TriggerEvent becomes linkedlist

master
neuecc 2020-06-01 13:40:35 +09:00
parent a9baa52309
commit 130286e8c2
7 changed files with 925 additions and 244 deletions

View File

@ -0,0 +1,39 @@
//using Cysharp.Threading.Tasks.Internal;
//using System;
//using System.Collections.Concurrent;
//using System.Runtime.CompilerServices;
//using System.Threading;
//namespace Cysharp.Threading.Tasks
//{
// public partial struct UniTask
// {
// public static UniTask Delay()
// {
// return default;
// }
// sealed class DelayPromise : IUniTaskSource
// {
// public void GetResult(short token)
// {
// throw new NotImplementedException();
// }
// public UniTaskStatus GetStatus(short token)
// {
// throw new NotImplementedException();
// }
// public void OnCompleted(Action<object> continuation, object state, short token)
// {
// throw new NotImplementedException();
// }
// public UniTaskStatus UnsafeGetStatus()
// {
// throw new NotImplementedException();
// }
// }
// }
//}

View File

@ -0,0 +1,637 @@
using Cysharp.Threading.Tasks;
using FluentAssertions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Channels;
using Cysharp.Threading.Tasks.Linq;
using System.Threading.Tasks;
using Xunit;
namespace NetCoreTests
{
public class TriggerEventTest
{
[Fact]
public void SimpleAdd()
{
var ev = new TriggerEvent<int>();
// do nothing
ev.SetResult(0);
ev.SetError(null);
ev.SetCompleted();
ev.SetCanceled(default);
{
var one = new TestEvent(1);
ev.Add(one);
ev.SetResult(10);
ev.SetResult(20);
ev.SetResult(30);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
ev.SetCompleted();
one.CompletedCalled.Count.Should().Be(1);
// do nothing
ev.SetResult(0);
ev.SetError(null);
ev.SetCompleted();
ev.SetCanceled(default);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
one.CompletedCalled.Count.Should().Be(1);
}
// after removed, onemore
{
var one = new TestEvent(1);
ev.Add(one);
ev.SetResult(10);
ev.SetResult(20);
ev.SetResult(30);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
ev.SetCompleted();
one.CompletedCalled.Count.Should().Be(1);
// do nothing
ev.SetResult(0);
ev.SetError(null);
ev.SetCompleted();
ev.SetCanceled(default);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
one.CompletedCalled.Count.Should().Be(1);
}
}
[Fact]
public void AddFour()
{
var ev = new TriggerEvent<int>();
// do nothing
ev.SetResult(0);
ev.SetError(null);
ev.SetCompleted();
ev.SetCanceled(default);
{
var one = new TestEvent(1);
var two = new TestEvent(2);
var three = new TestEvent(3);
var four = new TestEvent(4);
ev.Add(one);
ev.Add(two);
ev.Add(three);
ev.Add(four);
ev.SetResult(10);
ev.SetResult(20);
ev.SetResult(30);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
four.NextCalled.Should().BeEquivalentTo(10, 20, 30);
ev.SetCompleted();
one.CompletedCalled.Count.Should().Be(1);
two.CompletedCalled.Count.Should().Be(1);
three.CompletedCalled.Count.Should().Be(1);
// do nothing
ev.SetResult(0);
ev.SetError(null);
ev.SetCompleted();
ev.SetCanceled(default);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
one.CompletedCalled.Count.Should().Be(1);
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
three.CompletedCalled.Count.Should().Be(1);
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
three.CompletedCalled.Count.Should().Be(1);
}
// after removed, onemore.
{
var one = new TestEvent(1);
var two = new TestEvent(2);
var three = new TestEvent(3);
var four = new TestEvent(4);
ev.Add(one);
ev.Add(two);
ev.Add(three);
ev.Add(four);
ev.SetResult(10);
ev.SetResult(20);
ev.SetResult(30);
ev.Add(four);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
four.NextCalled.Should().BeEquivalentTo(10, 20, 30);
ev.SetCompleted();
one.CompletedCalled.Count.Should().Be(1);
two.CompletedCalled.Count.Should().Be(1);
three.CompletedCalled.Count.Should().Be(1);
// do nothing
ev.SetResult(0);
ev.SetError(null);
ev.SetCompleted();
ev.SetCanceled(default);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
one.CompletedCalled.Count.Should().Be(1);
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
three.CompletedCalled.Count.Should().Be(1);
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
three.CompletedCalled.Count.Should().Be(1);
}
}
[Fact]
public void OneRemove()
{
var ev = new TriggerEvent<int>();
{
var one = new TestEvent(1);
var two = new TestEvent(2);
var three = new TestEvent(3);
ev.Add(one);
ev.Add(two);
ev.Add(three);
ev.SetResult(10);
ev.SetResult(20);
ev.SetResult(30);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
ev.Remove(one);
ev.SetResult(40);
ev.SetResult(50);
ev.SetResult(60);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
two.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60);
three.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60);
}
}
[Fact]
public void TwoRemove()
{
var ev = new TriggerEvent<int>();
{
var one = new TestEvent(1);
var two = new TestEvent(2);
var three = new TestEvent(3);
ev.Add(one);
ev.Add(two);
ev.Add(three);
ev.SetResult(10);
ev.SetResult(20);
ev.SetResult(30);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
ev.Remove(two);
ev.SetResult(40);
ev.SetResult(50);
ev.SetResult(60);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60);
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
three.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60);
}
}
[Fact]
public void ThreeRemove()
{
var ev = new TriggerEvent<int>();
{
var one = new TestEvent(1);
var two = new TestEvent(2);
var three = new TestEvent(3);
ev.Add(one);
ev.Add(two);
ev.Add(three);
ev.SetResult(10);
ev.SetResult(20);
ev.SetResult(30);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
ev.Remove(three);
ev.SetResult(40);
ev.SetResult(50);
ev.SetResult(60);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60);
two.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60);
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
}
}
[Fact]
public void RemoveSelf()
{
new RemoveMe().Run1();
new RemoveMe().Run2();
new RemoveMe().Run3();
}
[Fact]
public void RemoveNextInIterating()
{
new RemoveNext().Run1();
new RemoveNext().Run2();
new RemoveNext().Run3();
}
[Fact]
public void RemoveNextNextTest()
{
new RemoveNextNext().Run1();
new RemoveNextNext().Run2();
}
[Fact]
public void AddTest()
{
new AddMe().Run1();
new AddMe().Run2();
}
public class RemoveMe
{
TriggerEvent<int> ev;
public void Run1()
{
TestEvent one = default;
one = new TestEvent(1, () => ev.Remove(one));
var two = new TestEvent(2);
var three = new TestEvent(3);
ev.Add(one);
ev.Add(two);
ev.Add(three);
ev.SetResult(10);
ev.SetResult(20);
ev.SetResult(30);
one.NextCalled.Should().BeEquivalentTo(10);
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
}
public void Run2()
{
TestEvent one = default;
one = new TestEvent(1, () => ev.Remove(one));
var two = new TestEvent(2);
var three = new TestEvent(3);
ev.Add(two);
ev.Add(one); // add second.
ev.Add(three);
ev.SetResult(10);
ev.SetResult(20);
ev.SetResult(30);
one.NextCalled.Should().BeEquivalentTo(10);
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
}
public void Run3()
{
TestEvent one = default;
one = new TestEvent(1, () => ev.Remove(one));
var two = new TestEvent(2);
var three = new TestEvent(3);
ev.Add(two);
ev.Add(three);
ev.Add(one); // add thired.
ev.SetResult(10);
ev.SetResult(20);
ev.SetResult(30);
one.NextCalled.Should().BeEquivalentTo(10);
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
}
}
public class RemoveNext
{
TriggerEvent<int> ev;
public void Run1()
{
TestEvent one = default;
TestEvent two = default;
TestEvent three = default;
one = new TestEvent(1, () => ev.Remove(two));
two = new TestEvent(2);
three = new TestEvent(3);
ev.Add(one);
ev.Add(two);
ev.Add(three);
ev.SetResult(10);
ev.SetResult(20);
ev.SetResult(30);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
two.NextCalled.Count.Should().Be(0);
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
}
public void Run2()
{
TestEvent one = default;
TestEvent two = default;
TestEvent three = default;
one = new TestEvent(1, () => ev.Remove(two));
two = new TestEvent(2);
three = new TestEvent(3);
ev.Add(two);
ev.Add(one); // add second
ev.Add(three);
ev.SetResult(10);
ev.SetResult(20);
ev.SetResult(30);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
two.NextCalled.Should().BeEquivalentTo(10);
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
}
public void Run3()
{
TestEvent one = default;
TestEvent two = default;
TestEvent three = default;
one = new TestEvent(1, () => ev.Remove(two));
two = new TestEvent(2);
three = new TestEvent(3);
ev.Add(two);
ev.Add(three);
ev.Add(one); // add thired.
ev.SetResult(10);
ev.SetResult(20);
ev.SetResult(30);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
two.NextCalled.Should().BeEquivalentTo(10);
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
}
}
public class RemoveNextNext
{
TriggerEvent<int> ev;
public void Run1()
{
TestEvent one = default;
TestEvent two = default;
TestEvent three = default;
TestEvent four = default;
one = new TestEvent(1, () => { ev.Remove(two); ev.Remove(three); });
two = new TestEvent(2);
three = new TestEvent(3);
four = new TestEvent(4);
ev.Add(one);
ev.Add(two);
ev.Add(three);
ev.Add(four);
ev.SetResult(10);
ev.SetResult(20);
ev.SetResult(30);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
two.NextCalled.Count.Should().Be(0);
three.NextCalled.Count.Should().Be(0);
four.NextCalled.Should().BeEquivalentTo(10, 20, 30);
}
public void Run2()
{
TestEvent one = default;
TestEvent two = default;
TestEvent three = default;
TestEvent four = default;
one = new TestEvent(1, () => { ev.Remove(one); ev.Remove(two); ev.Remove(three); });
two = new TestEvent(2);
three = new TestEvent(3);
four = new TestEvent(4);
ev.Add(one);
ev.Add(two);
ev.Add(three);
ev.Add(four);
ev.SetResult(10);
ev.SetResult(20);
ev.SetResult(30);
one.NextCalled.Should().BeEquivalentTo(10);
two.NextCalled.Count.Should().Be(0);
three.NextCalled.Count.Should().Be(0);
four.NextCalled.Should().BeEquivalentTo(10, 20, 30);
}
}
public class AddMe
{
TriggerEvent<int> ev;
public void Run1()
{
TestEvent one = default;
TestEvent two = default;
TestEvent three = default;
TestEvent four = default;
one = new TestEvent(1, () =>
{
if (two == null)
{
ev.Add(two = new TestEvent(2));
}
else if (three == null)
{
ev.Add(three = new TestEvent(3));
}
else if (four == null)
{
ev.Add(four = new TestEvent(4));
}
});
ev.Add(one);
ev.SetResult(10);
ev.SetResult(20);
ev.SetResult(30);
ev.SetResult(40);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40);
two.NextCalled.Should().BeEquivalentTo(20, 30, 40);
three.NextCalled.Should().BeEquivalentTo(30, 40);
four.NextCalled.Should().BeEquivalentTo(40);
}
public void Run2()
{
TestEvent one = default;
TestEvent two = default;
TestEvent three = default;
TestEvent four = default;
one = new TestEvent(1, () =>
{
if (two == null)
{
ev.Add(two = new TestEvent(2, () =>
{
if (three == null)
{
ev.Add(three = new TestEvent(3, () =>
{
if (four == null)
{
ev.Add(four = new TestEvent(4));
}
}));
}
}));
}
});
ev.Add(one);
ev.SetResult(10);
ev.SetResult(20);
ev.SetResult(30);
ev.SetResult(40);
one.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40);
two.NextCalled.Should().BeEquivalentTo(20, 30, 40);
three.NextCalled.Should().BeEquivalentTo(30, 40);
four.NextCalled.Should().BeEquivalentTo(40);
}
}
}
public class TestEvent : ITriggerHandler<int>
{
public readonly int Id;
readonly Action iteratingEvent;
public TestEvent(int id)
{
this.Id = id;
}
public TestEvent(int id, Action iteratingEvent)
{
this.Id = id;
this.iteratingEvent = iteratingEvent;
}
public List<int> NextCalled = new List<int>();
public List<Exception> ErrorCalled = new List<Exception>();
public List<object> CompletedCalled = new List<object>();
public List<CancellationToken> CancelCalled = new List<CancellationToken>();
public ITriggerHandler<int> Prev { get; set; }
public ITriggerHandler<int> Next { get; set; }
public void OnCanceled(CancellationToken cancellationToken)
{
CancelCalled.Add(cancellationToken);
}
public void OnCompleted()
{
CompletedCalled.Add(new object());
}
public void OnError(Exception ex)
{
ErrorCalled.Add(ex);
}
public void OnNext(int value)
{
NextCalled.Add(value);
iteratingEvent?.Invoke();
}
public override string ToString()
{
return Id.ToString();
}
}
}

View File

@ -120,6 +120,9 @@ namespace Cysharp.Threading.Tasks
public T Current => value; public T Current => value;
ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; }
ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; }
public UniTask<bool> MoveNextAsync() public UniTask<bool> MoveNextAsync()
{ {
// raise latest value on first call. // raise latest value on first call.
@ -300,6 +303,8 @@ namespace Cysharp.Threading.Tasks
} }
public T Current => value; public T Current => value;
ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; }
ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; }
public UniTask<bool> MoveNextAsync() public UniTask<bool> MoveNextAsync()
{ {

View File

@ -115,6 +115,8 @@ namespace Cysharp.Threading.Tasks.Linq
} }
public TSource Current { get; private set; } public TSource Current { get; private set; }
ITriggerHandler<TSource> ITriggerHandler<TSource>.Prev { get; set; }
ITriggerHandler<TSource> ITriggerHandler<TSource>.Next { get; set; }
public UniTask<bool> MoveNextAsync() public UniTask<bool> MoveNextAsync()
{ {

View File

@ -1,5 +1,4 @@
using Cysharp.Threading.Tasks.Internal; using System;
using System;
using System.Threading; using System.Threading;
namespace Cysharp.Threading.Tasks namespace Cysharp.Threading.Tasks
@ -10,341 +9,283 @@ namespace Cysharp.Threading.Tasks
void OnError(Exception ex); void OnError(Exception ex);
void OnCompleted(); void OnCompleted();
void OnCanceled(CancellationToken cancellationToken); void OnCanceled(CancellationToken cancellationToken);
// set/get from TriggerEvent<T>
ITriggerHandler<T> Prev { get; set; }
ITriggerHandler<T> Next { get; set; }
} }
// be careful to use, itself is struct. // be careful to use, itself is struct.
public struct TriggerEvent<T> public struct TriggerEvent<T>
{ {
// optimize: many cases, handler is single. ITriggerHandler<T> head; // head.prev is last.
ITriggerHandler<T> singleHandler; ITriggerHandler<T> iteratingHead;
ITriggerHandler<T>[] handlers; bool preserveRemoveSelf;
ITriggerHandler<T> iteratingNode;
// when running(in TrySetResult), does not add immediately(trampoline).
bool isRunning; void LogError(Exception ex)
ITriggerHandler<T> waitHandler; {
MinimumQueue<ITriggerHandler<T>> waitQueue; #if UNITY_2018_3_OR_NEWER
UnityEngine.Debug.LogException(ex);
#else
Console.WriteLine(ex);
#endif
}
public void SetResult(T value) public void SetResult(T value)
{ {
isRunning = true; var h = head;
while (h != null)
if (singleHandler != null)
{ {
iteratingNode = h;
try try
{ {
singleHandler.OnNext(value); h.OnNext(value);
} }
catch (Exception ex) catch (Exception ex)
{ {
#if UNITY_2018_3_OR_NEWER LogError(ex);
UnityEngine.Debug.LogException(ex); Remove(h);
#else
Console.WriteLine(ex);
#endif
} }
}
if (handlers != null) if (preserveRemoveSelf)
{
for (int i = 0; i < handlers.Length; i++)
{ {
if (handlers[i] != null) preserveRemoveSelf = false;
{ iteratingNode = null;
try var next = h.Next;
{ Remove(h);
handlers[i].OnNext(value); h = next;
}
catch (Exception ex)
{
handlers[i] = null;
#if UNITY_2018_3_OR_NEWER
UnityEngine.Debug.LogException(ex);
#else
Console.WriteLine(ex);
#endif
}
}
} }
} else
isRunning = false;
if (waitHandler != null)
{
var h = waitHandler;
waitHandler = null;
Add(h);
}
if (waitQueue != null)
{
while (waitQueue.Count != 0)
{ {
Add(waitQueue.Dequeue()); h = h.Next;
} }
} }
iteratingNode = null;
if (iteratingHead != null)
{
Add(iteratingHead);
iteratingHead = null;
}
} }
public void SetCanceled(CancellationToken cancellationToken) public void SetCanceled(CancellationToken cancellationToken)
{ {
isRunning = true; var h = head;
while (h != null)
if (singleHandler != null)
{ {
iteratingNode = h;
try try
{ {
(singleHandler).OnCanceled(cancellationToken); h.OnCanceled(cancellationToken);
} }
catch (Exception ex) catch (Exception ex)
{ {
#if UNITY_2018_3_OR_NEWER LogError(ex);
UnityEngine.Debug.LogException(ex);
#else
Console.WriteLine(ex);
#endif
} }
preserveRemoveSelf = false;
iteratingNode = null;
var next = h.Next;
Remove(h);
h = next;
} }
if (handlers != null) iteratingNode = null;
if (iteratingHead != null)
{ {
for (int i = 0; i < handlers.Length; i++) Add(iteratingHead);
{ iteratingHead = null;
if (handlers[i] != null)
{
try
{
(handlers[i]).OnCanceled(cancellationToken);
}
catch (Exception ex)
{
#if UNITY_2018_3_OR_NEWER
UnityEngine.Debug.LogException(ex);
#else
Console.WriteLine(ex);
#endif
handlers[i] = null;
}
}
}
}
isRunning = false;
if (waitHandler != null)
{
var h = waitHandler;
waitHandler = null;
Add(h);
}
if (waitQueue != null)
{
while (waitQueue.Count != 0)
{
Add(waitQueue.Dequeue());
}
} }
} }
public void SetCompleted() public void SetCompleted()
{ {
isRunning = true; var h = head;
while (h != null)
if (singleHandler != null)
{ {
iteratingNode = h;
try try
{ {
(singleHandler).OnCompleted(); h.OnCompleted();
} }
catch (Exception ex) catch (Exception ex)
{ {
#if UNITY_2018_3_OR_NEWER LogError(ex);
UnityEngine.Debug.LogException(ex);
#else
Console.WriteLine(ex);
#endif
} }
preserveRemoveSelf = false;
iteratingNode = null;
var next = h.Next;
Remove(h);
h = next;
} }
if (handlers != null) iteratingNode = null;
if (iteratingHead != null)
{ {
for (int i = 0; i < handlers.Length; i++) Add(iteratingHead);
{ iteratingHead = null;
if (handlers[i] != null)
{
try
{
(handlers[i]).OnCompleted();
}
catch (Exception ex)
{
#if UNITY_2018_3_OR_NEWER
UnityEngine.Debug.LogException(ex);
#else
Console.WriteLine(ex);
#endif
handlers[i] = null;
}
}
}
}
isRunning = false;
if (waitHandler != null)
{
var h = waitHandler;
waitHandler = null;
Add(h);
}
if (waitQueue != null)
{
while (waitQueue.Count != 0)
{
Add(waitQueue.Dequeue());
}
} }
} }
public void SetError(Exception exception) public void SetError(Exception exception)
{ {
isRunning = true; var h = head;
while (h != null)
if (singleHandler != null)
{ {
iteratingNode = h;
try try
{ {
singleHandler.OnError(exception); h.OnError(exception);
} }
catch (Exception ex) catch (Exception ex)
{ {
#if UNITY_2018_3_OR_NEWER LogError(ex);
UnityEngine.Debug.LogException(ex);
#else
Console.WriteLine(ex);
#endif
} }
preserveRemoveSelf = false;
iteratingNode = null;
var next = h.Next;
Remove(h);
h = next;
} }
if (handlers != null) iteratingNode = null;
if (iteratingHead != null)
{ {
for (int i = 0; i < handlers.Length; i++) Add(iteratingHead);
{ iteratingHead = null;
if (handlers[i] != null)
{
try
{
handlers[i].OnError(exception);
}
catch (Exception ex)
{
handlers[i] = null;
#if UNITY_2018_3_OR_NEWER
UnityEngine.Debug.LogException(ex);
#else
Console.WriteLine(ex);
#endif
}
}
}
}
isRunning = false;
if (waitHandler != null)
{
var h = waitHandler;
waitHandler = null;
Add(h);
}
if (waitQueue != null)
{
while (waitQueue.Count != 0)
{
Add(waitQueue.Dequeue());
}
} }
} }
public void Add(ITriggerHandler<T> handler) public void Add(ITriggerHandler<T> handler)
{ {
if (isRunning) if (handler == null) throw new ArgumentNullException(nameof(handler));
{
if (waitHandler == null)
{
waitHandler = handler;
return;
}
if (waitQueue == null) // zero node.
{ if (head == null)
waitQueue = new MinimumQueue<ITriggerHandler<T>>(4); {
} head = handler;
waitQueue.Enqueue(handler);
return; return;
} }
if (singleHandler == null) if (iteratingNode != null)
{ {
singleHandler = handler; if (iteratingHead == null)
{
iteratingHead = handler;
return;
}
var last = iteratingHead.Prev;
if (last == null)
{
// single node.
iteratingHead.Prev = handler;
iteratingHead.Next = handler;
handler.Prev = iteratingHead;
}
else
{
// multi node
iteratingHead.Prev = handler;
last.Next = handler;
handler.Prev = last;
}
} }
else else
{ {
if (handlers == null) var last = head.Prev;
if (last == null)
{ {
handlers = new ITriggerHandler<T>[4]; // single node.
head.Prev = handler;
head.Next = handler;
handler.Prev = head;
} }
else
// check empty
for (int i = 0; i < handlers.Length; i++)
{ {
if (handlers[i] == null) // multi node
{ head.Prev = handler;
handlers[i] = handler; last.Next = handler;
return; handler.Prev = last;
}
}
// full, ensure capacity
var last = handlers.Length;
{
EnsureCapacity(ref handlers);
handlers[last] = handler;
} }
} }
} }
static void EnsureCapacity(ref ITriggerHandler<T>[] array)
{
var newSize = array.Length * 2;
var newArray = new ITriggerHandler<T>[newSize];
Array.Copy(array, 0, newArray, 0, array.Length);
array = newArray;
}
public void Remove(ITriggerHandler<T> handler) public void Remove(ITriggerHandler<T> handler)
{ {
if (singleHandler == handler) if (handler == null) throw new ArgumentNullException(nameof(handler));
if (iteratingNode != null && iteratingNode == handler)
{ {
singleHandler = null; // if remove self, reserve remove self after invoke completed.
preserveRemoveSelf = true;
} }
else else
{ {
if (handlers != null) var prev = handler.Prev;
var next = handler.Next;
if (next != null)
{ {
for (int i = 0; i < handlers.Length; i++) next.Prev = prev;
}
if (handler == head)
{
head = next;
}
else if (handler == iteratingHead)
{
iteratingHead = next;
}
else
{
// when handler is head, prev indicate last so don't use it.
if (prev != null)
{ {
if (handlers[i] == handler) prev.Next = next;
}
}
if (head != null)
{
if (head.Prev == handler)
{
if (prev != head)
{ {
// fill null. head.Prev = prev;
handlers[i] = null; }
return; else
{
head.Prev = null;
} }
} }
} }
if (iteratingHead != null)
{
if (iteratingHead.Prev == handler)
{
if (prev != iteratingHead.Prev)
{
iteratingHead.Prev = prev;
}
else
{
iteratingHead.Prev = null;
}
}
}
handler.Prev = null;
handler.Next = null;
} }
} }
} }

View File

@ -103,6 +103,8 @@ namespace Cysharp.Threading.Tasks.Triggers
} }
public T Current { get; private set; } public T Current { get; private set; }
ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; }
ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; }
public UniTask<bool> MoveNextAsync() public UniTask<bool> MoveNextAsync()
{ {
@ -189,6 +191,9 @@ namespace Cysharp.Threading.Tasks.Triggers
internal CancellationToken CancellationToken => cancellationToken; internal CancellationToken CancellationToken => cancellationToken;
ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; }
ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; }
internal AsyncTriggerHandler(AsyncTriggerBase<T> trigger, bool callOnce) internal AsyncTriggerHandler(AsyncTriggerBase<T> trigger, bool callOnce)
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)

View File

@ -67,12 +67,12 @@ namespace Cysharp.Threading.Tasks
public static ReturnToSynchronizationContext ReturnToSynchronizationContext(SynchronizationContext synchronizationContext) public static ReturnToSynchronizationContext ReturnToSynchronizationContext(SynchronizationContext synchronizationContext)
{ {
return new ReturnToSynchronizationContext(synchronizationContext); return new ReturnToSynchronizationContext(synchronizationContext, false);
} }
public static ReturnToSynchronizationContext ReturnToCurrentSynchronizationContext() public static ReturnToSynchronizationContext ReturnToCurrentSynchronizationContext(bool dontPostWhenSameContext = true)
{ {
return new ReturnToSynchronizationContext(SynchronizationContext.Current); return new ReturnToSynchronizationContext(SynchronizationContext.Current, dontPostWhenSameContext);
} }
} }
@ -319,15 +319,67 @@ namespace Cysharp.Threading.Tasks
public struct ReturnToSynchronizationContext public struct ReturnToSynchronizationContext
{ {
readonly SynchronizationContext syncContext; readonly SynchronizationContext syncContext;
readonly bool dontPostWhenSameContext;
public ReturnToSynchronizationContext(SynchronizationContext syncContext) public ReturnToSynchronizationContext(SynchronizationContext syncContext, bool dontPostWhenSameContext)
{ {
this.syncContext = syncContext; this.syncContext = syncContext;
this.dontPostWhenSameContext = dontPostWhenSameContext;
} }
public SwitchToSynchronizationContextAwaitable DisposeAsync() public Awaiter DisposeAsync()
{ {
return UniTask.SwitchToSynchronizationContext(syncContext); return new Awaiter(syncContext, dontPostWhenSameContext);
}
public struct Awaiter : ICriticalNotifyCompletion
{
static readonly SendOrPostCallback switchToCallback = Callback;
readonly SynchronizationContext synchronizationContext;
readonly bool dontPostWhenSameContext;
public Awaiter(SynchronizationContext synchronizationContext, bool dontPostWhenSameContext)
{
this.synchronizationContext = synchronizationContext;
this.dontPostWhenSameContext = dontPostWhenSameContext;
}
public Awaiter GetAwaiter() => this;
public bool IsCompleted
{
get
{
var current = SynchronizationContext.Current;
if (current == synchronizationContext)
{
return true;
}
else
{
return false;
}
}
}
public void GetResult() { }
public void OnCompleted(Action continuation)
{
synchronizationContext.Post(switchToCallback, continuation);
}
public void UnsafeOnCompleted(Action continuation)
{
synchronizationContext.Post(switchToCallback, continuation);
}
static void Callback(object state)
{
var continuation = (Action)state;
continuation();
}
} }
} }
} }