292 lines
7.6 KiB
C#
292 lines
7.6 KiB
C#
|
using System;
|
|||
|
using System.Threading;
|
|||
|
|
|||
|
namespace Cysharp.Threading.Tasks
|
|||
|
{
|
|||
|
public interface ITriggerHandler<T>
|
|||
|
{
|
|||
|
void OnNext(T value);
|
|||
|
void OnError(Exception ex);
|
|||
|
void OnCompleted();
|
|||
|
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.
|
|||
|
public struct TriggerEvent<T>
|
|||
|
{
|
|||
|
ITriggerHandler<T> head; // head.prev is last
|
|||
|
ITriggerHandler<T> iteratingHead;
|
|||
|
ITriggerHandler<T> iteratingNode;
|
|||
|
|
|||
|
void LogError(Exception ex)
|
|||
|
{
|
|||
|
#if UNITY_2018_3_OR_NEWER
|
|||
|
UnityEngine.Debug.LogException(ex);
|
|||
|
#else
|
|||
|
Console.WriteLine(ex);
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
public void SetResult(T value)
|
|||
|
{
|
|||
|
if (iteratingNode != null)
|
|||
|
{
|
|||
|
throw new InvalidOperationException("Can not trigger itself in iterating.");
|
|||
|
}
|
|||
|
|
|||
|
var h = head;
|
|||
|
while (h != null)
|
|||
|
{
|
|||
|
iteratingNode = h;
|
|||
|
|
|||
|
try
|
|||
|
{
|
|||
|
h.OnNext(value);
|
|||
|
}
|
|||
|
catch (Exception ex)
|
|||
|
{
|
|||
|
LogError(ex);
|
|||
|
Remove(h);
|
|||
|
}
|
|||
|
|
|||
|
// If `h` itself is removed by OnNext, h.Next is null.
|
|||
|
// Therefore, instead of looking at h.Next, the `iteratingNode` reference itself is replaced.
|
|||
|
h = h == iteratingNode ? h.Next : iteratingNode;
|
|||
|
}
|
|||
|
|
|||
|
iteratingNode = null;
|
|||
|
if (iteratingHead != null)
|
|||
|
{
|
|||
|
Add(iteratingHead);
|
|||
|
iteratingHead = null;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public void SetCanceled(CancellationToken cancellationToken)
|
|||
|
{
|
|||
|
if (iteratingNode != null)
|
|||
|
{
|
|||
|
throw new InvalidOperationException("Can not trigger itself in iterating.");
|
|||
|
}
|
|||
|
|
|||
|
var h = head;
|
|||
|
while (h != null)
|
|||
|
{
|
|||
|
iteratingNode = h;
|
|||
|
try
|
|||
|
{
|
|||
|
h.OnCanceled(cancellationToken);
|
|||
|
}
|
|||
|
catch (Exception ex)
|
|||
|
{
|
|||
|
LogError(ex);
|
|||
|
}
|
|||
|
|
|||
|
var next = h == iteratingNode ? h.Next : iteratingNode;
|
|||
|
iteratingNode = null;
|
|||
|
Remove(h);
|
|||
|
h = next;
|
|||
|
}
|
|||
|
|
|||
|
iteratingNode = null;
|
|||
|
if (iteratingHead != null)
|
|||
|
{
|
|||
|
Add(iteratingHead);
|
|||
|
iteratingHead = null;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public void SetCompleted()
|
|||
|
{
|
|||
|
if (iteratingNode != null)
|
|||
|
{
|
|||
|
throw new InvalidOperationException("Can not trigger itself in iterating.");
|
|||
|
}
|
|||
|
|
|||
|
var h = head;
|
|||
|
while (h != null)
|
|||
|
{
|
|||
|
iteratingNode = h;
|
|||
|
try
|
|||
|
{
|
|||
|
h.OnCompleted();
|
|||
|
}
|
|||
|
catch (Exception ex)
|
|||
|
{
|
|||
|
LogError(ex);
|
|||
|
}
|
|||
|
|
|||
|
var next = h == iteratingNode ? h.Next : iteratingNode;
|
|||
|
iteratingNode = null;
|
|||
|
Remove(h);
|
|||
|
h = next;
|
|||
|
}
|
|||
|
|
|||
|
iteratingNode = null;
|
|||
|
if (iteratingHead != null)
|
|||
|
{
|
|||
|
Add(iteratingHead);
|
|||
|
iteratingHead = null;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public void SetError(Exception exception)
|
|||
|
{
|
|||
|
if (iteratingNode != null)
|
|||
|
{
|
|||
|
throw new InvalidOperationException("Can not trigger itself in iterating.");
|
|||
|
}
|
|||
|
|
|||
|
var h = head;
|
|||
|
while (h != null)
|
|||
|
{
|
|||
|
iteratingNode = h;
|
|||
|
try
|
|||
|
{
|
|||
|
h.OnError(exception);
|
|||
|
}
|
|||
|
catch (Exception ex)
|
|||
|
{
|
|||
|
LogError(ex);
|
|||
|
}
|
|||
|
|
|||
|
var next = h == iteratingNode ? h.Next : iteratingNode;
|
|||
|
iteratingNode = null;
|
|||
|
Remove(h);
|
|||
|
h = next;
|
|||
|
}
|
|||
|
|
|||
|
iteratingNode = null;
|
|||
|
if (iteratingHead != null)
|
|||
|
{
|
|||
|
Add(iteratingHead);
|
|||
|
iteratingHead = null;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public void Add(ITriggerHandler<T> handler)
|
|||
|
{
|
|||
|
if (handler == null) throw new ArgumentNullException(nameof(handler));
|
|||
|
|
|||
|
// zero node.
|
|||
|
if (head == null)
|
|||
|
{
|
|||
|
head = handler;
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
if (iteratingNode != null)
|
|||
|
{
|
|||
|
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
|
|||
|
{
|
|||
|
var last = head.Prev;
|
|||
|
if (last == null)
|
|||
|
{
|
|||
|
// single node.
|
|||
|
head.Prev = handler;
|
|||
|
head.Next = handler;
|
|||
|
handler.Prev = head;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// multi node
|
|||
|
head.Prev = handler;
|
|||
|
last.Next = handler;
|
|||
|
handler.Prev = last;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public void Remove(ITriggerHandler<T> handler)
|
|||
|
{
|
|||
|
if (handler == null) throw new ArgumentNullException(nameof(handler));
|
|||
|
|
|||
|
var prev = handler.Prev;
|
|||
|
var next = handler.Next;
|
|||
|
|
|||
|
if (next != null)
|
|||
|
{
|
|||
|
next.Prev = prev;
|
|||
|
}
|
|||
|
|
|||
|
if (handler == head)
|
|||
|
{
|
|||
|
head = next;
|
|||
|
}
|
|||
|
// when handler is head, prev indicate last so don't use it.
|
|||
|
else if (prev != null)
|
|||
|
{
|
|||
|
prev.Next = next;
|
|||
|
}
|
|||
|
|
|||
|
if (handler == iteratingNode)
|
|||
|
{
|
|||
|
iteratingNode = next;
|
|||
|
}
|
|||
|
if (handler == iteratingHead)
|
|||
|
{
|
|||
|
iteratingHead = next;
|
|||
|
}
|
|||
|
|
|||
|
if (head != null)
|
|||
|
{
|
|||
|
if (head.Prev == handler)
|
|||
|
{
|
|||
|
if (prev != head)
|
|||
|
{
|
|||
|
head.Prev = prev;
|
|||
|
}
|
|||
|
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;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|