AsyncTrigger returns MoveNext -> false when destroyed

master
neuecc 2020-05-19 01:19:46 +09:00
parent efaf3ee8f5
commit 7a306118f5
3 changed files with 104 additions and 42 deletions

View File

@ -4,26 +4,25 @@ using System.Threading;
namespace Cysharp.Threading.Tasks namespace Cysharp.Threading.Tasks
{ {
public interface ITriggerEvent<T> public interface ITriggerHandler<T>
{ {
void SetResult(T value); void OnNext(T value);
void SetCanceled(CancellationToken cancellationToken); void OnCanceled(CancellationToken cancellationToken);
void Add(IResolveCancelPromise<T> handler); void OnCompleted();
void Remove(IResolveCancelPromise<T> handler);
} }
// be careful to use, itself is struct. // be careful to use, itself is struct.
public struct TriggerEvent<T> : ITriggerEvent<T> public struct TriggerEvent<T>
{ {
// optimize: many cases, handler is single. // optimize: many cases, handler is single.
IResolveCancelPromise<T> singleHandler; ITriggerHandler<T> singleHandler;
IResolveCancelPromise<T>[] handlers; ITriggerHandler<T>[] handlers;
// when running(in TrySetResult), does not add immediately. // when running(in TrySetResult), does not add immediately(trampoline).
bool isRunning; bool isRunning;
IResolveCancelPromise<T> waitHandler; ITriggerHandler<T> waitHandler;
MinimumQueue<IResolveCancelPromise<T>> waitQueue; MinimumQueue<ITriggerHandler<T>> waitQueue;
public void SetResult(T value) public void SetResult(T value)
{ {
@ -33,7 +32,7 @@ namespace Cysharp.Threading.Tasks
{ {
try try
{ {
singleHandler.TrySetResult(value); singleHandler.OnNext(value);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -53,7 +52,7 @@ namespace Cysharp.Threading.Tasks
{ {
try try
{ {
handlers[i].TrySetResult(value); handlers[i].OnNext(value);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -94,7 +93,7 @@ namespace Cysharp.Threading.Tasks
{ {
try try
{ {
((ICancelPromise)singleHandler).TrySetCanceled(cancellationToken); (singleHandler).OnCanceled(cancellationToken);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -114,7 +113,7 @@ namespace Cysharp.Threading.Tasks
{ {
try try
{ {
((ICancelPromise)handlers[i]).TrySetCanceled(cancellationToken); (handlers[i]).OnCanceled(cancellationToken);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -147,7 +146,68 @@ namespace Cysharp.Threading.Tasks
} }
} }
public void Add(IResolveCancelPromise<T> handler) public void SetCompleted()
{
isRunning = true;
if (singleHandler != null)
{
try
{
(singleHandler).OnCompleted();
}
catch (Exception ex)
{
#if UNITY_2018_3_OR_NEWER
UnityEngine.Debug.LogException(ex);
#else
Console.WriteLine(ex);
#endif
}
}
if (handlers != null)
{
for (int i = 0; i < handlers.Length; i++)
{
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 Add(ITriggerHandler<T> handler)
{ {
if (isRunning) if (isRunning)
{ {
@ -159,7 +219,7 @@ namespace Cysharp.Threading.Tasks
if (waitQueue == null) if (waitQueue == null)
{ {
waitQueue = new MinimumQueue<IResolveCancelPromise<T>>(4); waitQueue = new MinimumQueue<ITriggerHandler<T>>(4);
} }
waitQueue.Enqueue(handler); waitQueue.Enqueue(handler);
return; return;
@ -173,7 +233,7 @@ namespace Cysharp.Threading.Tasks
{ {
if (handlers == null) if (handlers == null)
{ {
handlers = new IResolveCancelPromise<T>[4]; handlers = new ITriggerHandler<T>[4];
} }
// check empty // check empty
@ -195,15 +255,15 @@ namespace Cysharp.Threading.Tasks
} }
} }
static void EnsureCapacity(ref IResolveCancelPromise<T>[] array) static void EnsureCapacity(ref ITriggerHandler<T>[] array)
{ {
var newSize = array.Length * 2; var newSize = array.Length * 2;
var newArray = new IResolveCancelPromise<T>[newSize]; var newArray = new ITriggerHandler<T>[newSize];
Array.Copy(array, 0, newArray, 0, array.Length); Array.Copy(array, 0, newArray, 0, array.Length);
array = newArray; array = newArray;
} }
public void Remove(IResolveCancelPromise<T> handler) public void Remove(ITriggerHandler<T> handler)
{ {
if (singleHandler == handler) if (singleHandler == handler)
{ {

View File

@ -24,10 +24,10 @@ namespace Cysharp.Threading.Tasks.Triggers
if (calledDestroy) return; if (calledDestroy) return;
calledDestroy = true; calledDestroy = true;
triggerEvent.SetCanceled(CancellationToken.None); triggerEvent.SetCompleted();
} }
internal void AddHandler(IResolveCancelPromise<T> handler) internal void AddHandler(ITriggerHandler<T> handler)
{ {
if (!calledAwake) if (!calledAwake)
{ {
@ -37,7 +37,7 @@ namespace Cysharp.Threading.Tasks.Triggers
triggerEvent.Add(handler); triggerEvent.Add(handler);
} }
internal void RemoveHandler(IResolveCancelPromise<T> handler) internal void RemoveHandler(ITriggerHandler<T> handler)
{ {
if (!calledAwake) if (!calledAwake)
{ {
@ -57,7 +57,7 @@ namespace Cysharp.Threading.Tasks.Triggers
return new AsyncTriggerEnumerator(this, cancellationToken); return new AsyncTriggerEnumerator(this, cancellationToken);
} }
sealed class AsyncTriggerEnumerator : MoveNextSource, IUniTaskAsyncEnumerator<T>, IResolveCancelPromise<T> sealed class AsyncTriggerEnumerator : MoveNextSource, IUniTaskAsyncEnumerator<T>, ITriggerHandler<T>
{ {
static Action<object> cancellationCallback = CancellationCallback; static Action<object> cancellationCallback = CancellationCallback;
@ -73,15 +73,20 @@ namespace Cysharp.Threading.Tasks.Triggers
this.cancellationToken = cancellationToken; this.cancellationToken = cancellationToken;
} }
public bool TrySetCanceled(CancellationToken cancellationToken = default) public void OnCanceled(CancellationToken cancellationToken = default)
{ {
return completionSource.TrySetCanceled(cancellationToken); completionSource.TrySetCanceled(cancellationToken);
} }
public bool TrySetResult(T value) public void OnNext(T value)
{ {
Current = value; Current = value;
return completionSource.TrySetResult(true); completionSource.TrySetResult(true);
}
public void OnCompleted()
{
completionSource.TrySetResult(false);
} }
static void CancellationCallback(object state) static void CancellationCallback(object state)
@ -164,7 +169,7 @@ namespace Cysharp.Threading.Tasks.Triggers
} }
} }
public sealed partial class AsyncTriggerHandler<T> : IUniTaskSource<T>, IResolveCancelPromise<T>, IDisposable public sealed partial class AsyncTriggerHandler<T> : IUniTaskSource<T>, ITriggerHandler<T>, IDisposable
{ {
static Action<object> cancellationCallback = CancellationCallback; static Action<object> cancellationCallback = CancellationCallback;
@ -253,14 +258,19 @@ namespace Cysharp.Threading.Tasks.Triggers
} }
} }
bool IResolvePromise<T>.TrySetResult(T result) void ITriggerHandler<T>.OnNext(T value)
{ {
return core.TrySetResult(result); core.TrySetResult(value);
} }
bool ICancelPromise.TrySetCanceled(CancellationToken cancellationToken) void ITriggerHandler<T>.OnCanceled(CancellationToken cancellationToken)
{ {
return core.TrySetCanceled(cancellationToken); core.TrySetCanceled(cancellationToken);
}
void ITriggerHandler<T>.OnCompleted()
{
core.TrySetCanceled(CancellationToken.None);
} }
void IUniTaskSource.GetResult(short token) void IUniTaskSource.GetResult(short token)

View File

@ -38,14 +38,6 @@ namespace Cysharp.Threading.Tasks
{ {
} }
public interface IResolveCancelPromise : IResolvePromise, ICancelPromise
{
}
public interface IResolveCancelPromise<T> : IResolvePromise<T>, ICancelPromise
{
}
[StructLayout(LayoutKind.Auto)] [StructLayout(LayoutKind.Auto)]
public struct UniTaskCompletionSourceCore<TResult> public struct UniTaskCompletionSourceCore<TResult>
{ {