#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); } } }