#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 : MonoBehaviour, IUniTaskAsyncEnumerable { TriggerEvent 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 handler) { if (!calledAwake) { PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, new AwakeMonitor(this)); } triggerEvent.Add(handler); } internal void RemoveHandler(ITriggerHandler handler) { if (!calledAwake) { PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, new AwakeMonitor(this)); } triggerEvent.Remove(handler); } protected void RaiseEvent(T value) { triggerEvent.SetResult(value); } public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) { return new AsyncTriggerEnumerator(this, cancellationToken); } sealed class AsyncTriggerEnumerator : MoveNextSource, IUniTaskAsyncEnumerator, ITriggerHandler { static Action cancellationCallback = CancellationCallback; readonly AsyncTriggerBase parent; CancellationToken cancellationToken; CancellationTokenRegistration registration; bool called; bool isDisposed; public AsyncTriggerEnumerator(AsyncTriggerBase 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 ITriggerHandler.Prev { get; set; } ITriggerHandler ITriggerHandler.Next { get; set; } public UniTask 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(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 trigger; public AwakeMonitor(AsyncTriggerBase 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 : IAsyncOneShotTrigger { UniTask IAsyncOneShotTrigger.OneShotAsync() { core.Reset(); return new UniTask((IUniTaskSource)this, core.Version); } } public sealed partial class AsyncTriggerHandler : IUniTaskSource, ITriggerHandler, IDisposable { static Action cancellationCallback = CancellationCallback; readonly AsyncTriggerBase trigger; CancellationToken cancellationToken; CancellationTokenRegistration registration; bool isDisposed; bool callOnce; UniTaskCompletionSourceCore core; internal CancellationToken CancellationToken => cancellationToken; ITriggerHandler ITriggerHandler.Prev { get; set; } ITriggerHandler ITriggerHandler.Next { get; set; } internal AsyncTriggerHandler(AsyncTriggerBase 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 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)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.GetResult(short token) { try { return core.GetResult(token); } finally { if (callOnce) { Dispose(); } } } void ITriggerHandler.OnNext(T value) { core.TrySetResult(value); } void ITriggerHandler.OnCanceled(CancellationToken cancellationToken) { core.TrySetCanceled(cancellationToken); } void ITriggerHandler.OnCompleted() { core.TrySetCanceled(CancellationToken.None); } void ITriggerHandler.OnError(Exception ex) { core.TrySetException(ex); } void IUniTaskSource.GetResult(short token) { ((IUniTaskSource)this).GetResult(token); } UniTaskStatus IUniTaskSource.GetStatus(short token) { return core.GetStatus(token); } UniTaskStatus IUniTaskSource.UnsafeGetStatus() { return core.UnsafeGetStatus(); } void IUniTaskSource.OnCompleted(Action continuation, object state, short token) { core.OnCompleted(continuation, state, token); } } }