|
|
|
|
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
|
|
|
|
|
|
|
|
|
using System;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
|
|
|
|
|
namespace Cysharp.Threading.Tasks.Triggers
|
|
|
|
|
{
|
|
|
|
|
public abstract class AsyncTriggerBase<T> : MonoBehaviour, IUniTaskAsyncEnumerable<T>
|
|
|
|
|
{
|
|
|
|
|
TriggerEvent<T> triggerEvent;
|
|
|
|
|
|
|
|
|
|
internal protected bool calledAwake;
|
|
|
|
|
internal protected bool calledDestroy;
|
|
|
|
|
|
|
|
|
|
void Awake()
|
|
|
|
|
{
|
|
|
|
|
calledAwake = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OnDestroy()
|
|
|
|
|
{
|
|
|
|
|
if (calledDestroy) return;
|
|
|
|
|
calledDestroy = true;
|
|
|
|
|
|
|
|
|
|
triggerEvent.SetCompleted();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal void AddHandler(ITriggerHandler<T> handler)
|
|
|
|
|
{
|
|
|
|
|
if (!calledAwake)
|
|
|
|
|
{
|
|
|
|
|
PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, new AwakeMonitor(this));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
triggerEvent.Add(handler);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal void RemoveHandler(ITriggerHandler<T> handler)
|
|
|
|
|
{
|
|
|
|
|
if (!calledAwake)
|
|
|
|
|
{
|
|
|
|
|
PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, new AwakeMonitor(this));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
triggerEvent.Remove(handler);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected void RaiseEvent(T value)
|
|
|
|
|
{
|
|
|
|
|
triggerEvent.SetResult(value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
|
|
|
|
{
|
|
|
|
|
return new AsyncTriggerEnumerator(this, cancellationToken);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sealed class AsyncTriggerEnumerator : MoveNextSource, IUniTaskAsyncEnumerator<T>, ITriggerHandler<T>
|
|
|
|
|
{
|
|
|
|
|
static Action<object> cancellationCallback = CancellationCallback;
|
|
|
|
|
|
|
|
|
|
readonly AsyncTriggerBase<T> parent;
|
|
|
|
|
CancellationToken cancellationToken;
|
|
|
|
|
CancellationTokenRegistration registration;
|
|
|
|
|
bool called;
|
|
|
|
|
bool isDisposed;
|
|
|
|
|
|
|
|
|
|
public AsyncTriggerEnumerator(AsyncTriggerBase<T> parent, CancellationToken cancellationToken)
|
|
|
|
|
{
|
|
|
|
|
this.parent = parent;
|
|
|
|
|
this.cancellationToken = cancellationToken;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void OnCanceled(CancellationToken cancellationToken = default)
|
|
|
|
|
{
|
|
|
|
|
completionSource.TrySetCanceled(cancellationToken);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void OnNext(T value)
|
|
|
|
|
{
|
|
|
|
|
Current = value;
|
|
|
|
|
completionSource.TrySetResult(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void OnCompleted()
|
|
|
|
|
{
|
|
|
|
|
completionSource.TrySetResult(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void OnError(Exception ex)
|
|
|
|
|
{
|
|
|
|
|
completionSource.TrySetException(ex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void CancellationCallback(object state)
|
|
|
|
|
{
|
|
|
|
|
var self = (AsyncTriggerEnumerator)state;
|
|
|
|
|
self.DisposeAsync().Forget(); // sync
|
|
|
|
|
|
|
|
|
|
self.completionSource.TrySetCanceled(self.cancellationToken);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public T Current { get; private set; }
|
|
|
|
|
ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; }
|
|
|
|
|
ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; }
|
|
|
|
|
|
|
|
|
|
public UniTask<bool> MoveNextAsync()
|
|
|
|
|
{
|
|
|
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
|
|
|
completionSource.Reset();
|
|
|
|
|
|
|
|
|
|
if (!called)
|
|
|
|
|
{
|
|
|
|
|
called = true;
|
|
|
|
|
|
|
|
|
|
TaskTracker.TrackActiveTask(this, 3);
|
|
|
|
|
parent.AddHandler(this);
|
|
|
|
|
if (cancellationToken.CanBeCanceled)
|
|
|
|
|
{
|
|
|
|
|
registration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, this);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return new UniTask<bool>(this, completionSource.Version);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public UniTask DisposeAsync()
|
|
|
|
|
{
|
|
|
|
|
if (!isDisposed)
|
|
|
|
|
{
|
|
|
|
|
isDisposed = true;
|
|
|
|
|
TaskTracker.RemoveTracking(this);
|
|
|
|
|
registration.Dispose();
|
|
|
|
|
parent.RemoveHandler(this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return default;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class AwakeMonitor : IPlayerLoopItem
|
|
|
|
|
{
|
|
|
|
|
readonly AsyncTriggerBase<T> trigger;
|
|
|
|
|
|
|
|
|
|
public AwakeMonitor(AsyncTriggerBase<T> trigger)
|
|
|
|
|
{
|
|
|
|
|
this.trigger = trigger;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool MoveNext()
|
|
|
|
|
{
|
|
|
|
|
if (trigger.calledAwake) return false;
|
|
|
|
|
if (trigger == null)
|
|
|
|
|
{
|
|
|
|
|
trigger.OnDestroy();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public interface IAsyncOneShotTrigger
|
|
|
|
|
{
|
|
|
|
|
UniTask OneShotAsync();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public partial class AsyncTriggerHandler<T> : IAsyncOneShotTrigger
|
|
|
|
|
{
|
|
|
|
|
UniTask IAsyncOneShotTrigger.OneShotAsync()
|
|
|
|
|
{
|
|
|
|
|
core.Reset();
|
|
|
|
|
return new UniTask((IUniTaskSource)this, core.Version);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public sealed partial class AsyncTriggerHandler<T> : IUniTaskSource<T>, ITriggerHandler<T>, IDisposable
|
|
|
|
|
{
|
|
|
|
|
static Action<object> cancellationCallback = CancellationCallback;
|
|
|
|
|
|
|
|
|
|
readonly AsyncTriggerBase<T> trigger;
|
|
|
|
|
|
|
|
|
|
CancellationToken cancellationToken;
|
|
|
|
|
CancellationTokenRegistration registration;
|
|
|
|
|
bool isDisposed;
|
|
|
|
|
bool callOnce;
|
|
|
|
|
|
|
|
|
|
UniTaskCompletionSourceCore<T> core;
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
if (cancellationToken.IsCancellationRequested)
|
|
|
|
|
{
|
|
|
|
|
isDisposed = true;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.trigger = trigger;
|
|
|
|
|
this.cancellationToken = default;
|
|
|
|
|
this.registration = default;
|
|
|
|
|
this.callOnce = callOnce;
|
|
|
|
|
|
|
|
|
|
trigger.AddHandler(this);
|
|
|
|
|
|
|
|
|
|
TaskTracker.TrackActiveTask(this, 3);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal AsyncTriggerHandler(AsyncTriggerBase<T> trigger, CancellationToken cancellationToken, bool callOnce)
|
|
|
|
|
{
|
|
|
|
|
if (cancellationToken.IsCancellationRequested)
|
|
|
|
|
{
|
|
|
|
|
isDisposed = true;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.trigger = trigger;
|
|
|
|
|
this.cancellationToken = cancellationToken;
|
|
|
|
|
this.callOnce = callOnce;
|
|
|
|
|
|
|
|
|
|
trigger.AddHandler(this);
|
|
|
|
|
|
|
|
|
|
if (cancellationToken.CanBeCanceled)
|
|
|
|
|
{
|
|
|
|
|
registration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TaskTracker.TrackActiveTask(this, 3);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void CancellationCallback(object state)
|
|
|
|
|
{
|
|
|
|
|
var self = (AsyncTriggerHandler<T>)state;
|
|
|
|
|
self.Dispose();
|
|
|
|
|
|
|
|
|
|
self.core.TrySetCanceled(self.cancellationToken);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
|
{
|
|
|
|
|
if (!isDisposed)
|
|
|
|
|
{
|
|
|
|
|
isDisposed = true;
|
|
|
|
|
TaskTracker.RemoveTracking(this);
|
|
|
|
|
registration.Dispose();
|
|
|
|
|
trigger.RemoveHandler(this);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
T IUniTaskSource<T>.GetResult(short token)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
return core.GetResult(token);
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
if (callOnce)
|
|
|
|
|
{
|
|
|
|
|
Dispose();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ITriggerHandler<T>.OnNext(T value)
|
|
|
|
|
{
|
|
|
|
|
core.TrySetResult(value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ITriggerHandler<T>.OnCanceled(CancellationToken cancellationToken)
|
|
|
|
|
{
|
|
|
|
|
core.TrySetCanceled(cancellationToken);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ITriggerHandler<T>.OnCompleted()
|
|
|
|
|
{
|
|
|
|
|
core.TrySetCanceled(CancellationToken.None);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ITriggerHandler<T>.OnError(Exception ex)
|
|
|
|
|
{
|
|
|
|
|
core.TrySetException(ex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void IUniTaskSource.GetResult(short token)
|
|
|
|
|
{
|
|
|
|
|
((IUniTaskSource<T>)this).GetResult(token);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UniTaskStatus IUniTaskSource.GetStatus(short token)
|
|
|
|
|
{
|
|
|
|
|
return core.GetStatus(token);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UniTaskStatus IUniTaskSource.UnsafeGetStatus()
|
|
|
|
|
{
|
|
|
|
|
return core.UnsafeGetStatus();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void IUniTaskSource.OnCompleted(Action<object> continuation, object state, short token)
|
|
|
|
|
{
|
|
|
|
|
core.OnCompleted(continuation, state, token);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|