#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6)) #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member using System; using System.Collections.Generic; using System.Threading; using UniRx.Async.Internal; using UnityEngine; namespace UniRx.Async.Triggers { public abstract class AsyncTriggerBase : MonoBehaviour { bool calledAwake; bool calledDestroy; ICancelPromise triggerSlot; void Awake() { calledAwake = true; } protected TriggerEvent SetTriggerEvent(ref TriggerEvent field) { if (field == null) { field = new TriggerEvent(); if (triggerSlot == null) { triggerSlot = field; } else { throw new InvalidOperationException("triggerSlot is already filled."); } } if (!calledAwake) { PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, new AwakeMonitor(this)); } return field; } void OnDestroy() { if (calledDestroy) return; calledDestroy = true; triggerSlot?.TrySetCanceled(); triggerSlot = null; } 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, IResolvePromise, ICancelPromise, IDisposable { static Action cancellationCallback = CancellationCallback; readonly TriggerEvent trigger; CancellationToken cancellationToken; CancellationTokenRegistration registration; bool isDisposed; bool callOnce; UniTaskCompletionSourceCore core; internal CancellationToken CancellationToken => cancellationToken; public AsyncTriggerHandler(TriggerEvent trigger, bool callOnce) { if (cancellationToken.IsCancellationRequested) { isDisposed = true; return; } this.trigger = trigger; this.cancellationToken = default; this.registration = default; this.callOnce = callOnce; trigger.Add(this); TaskTracker.TrackActiveTask(this, 3); } public AsyncTriggerHandler(TriggerEvent trigger, CancellationToken cancellationToken, bool callOnce) { if (cancellationToken.IsCancellationRequested) { isDisposed = true; return; } this.trigger = trigger; this.cancellationToken = cancellationToken; this.callOnce = callOnce; trigger.Add(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.Remove(this); } } T IUniTaskSource.GetResult(short token) { try { return core.GetResult(token); } finally { if (callOnce) { Dispose(); } } } bool IResolvePromise.TrySetResult(T result) { return core.TrySetResult(result); } bool ICancelPromise.TrySetCanceled(CancellationToken cancellationToken) { return core.TrySetCanceled(cancellationToken); } 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); } } public sealed class TriggerEvent : IResolvePromise, ICancelPromise { // optimize: many cases, handler is single. AsyncTriggerHandler singleHandler; List> handlers; public bool TrySetResult(T value) { List exceptions = null; if (singleHandler != null) { try { ((IResolvePromise)singleHandler).TrySetResult(value); } catch (Exception ex) { if (handlers == null) { throw; } else { exceptions = new List(); exceptions.Add(ex); } } } if (handlers != null) { // make snapshot var rentArray = ArrayPoolUtil.CopyToRentArray(handlers); var clearArray = true; try { var array = rentArray.Array; var len = rentArray.Length; for (int i = 0; i < len; i++) { try { ((IResolvePromise)array[i]).TrySetResult(value); } catch (Exception ex) { if (exceptions == null) { exceptions = new List(); } exceptions.Add(ex); } finally { array[i] = null; } } } finally { rentArray.DisposeManually(clearArray); } } if (exceptions != null) { throw new AggregateException(exceptions); } return true; } public bool TrySetCanceled(CancellationToken cancellationToken) { List exceptions = null; if (singleHandler != null) { try { ((ICancelPromise)singleHandler).TrySetCanceled(cancellationToken); } catch (Exception ex) { if (handlers == null) { throw; } else { exceptions = new List(); exceptions.Add(ex); } } } if (handlers != null) { // make snapshot var rentArray = ArrayPoolUtil.CopyToRentArray(handlers); var clearArray = true; try { var array = rentArray.Array; var len = rentArray.Length; for (int i = 0; i < len; i++) { try { ((ICancelPromise)array[i]).TrySetCanceled(cancellationToken); } catch (Exception ex) { if (exceptions == null) { exceptions = new List(); } exceptions.Add(ex); } finally { array[i] = null; } } } finally { rentArray.DisposeManually(clearArray); } } if (exceptions != null) { throw new AggregateException(exceptions); } return true; } public void Add(AsyncTriggerHandler handler) { if (singleHandler == null) { singleHandler = handler; } else { if (handlers != null) { handlers = new List>(); handlers.Add(handler); } } } public void Remove(AsyncTriggerHandler handler) { if (singleHandler == handler) { singleHandler = null; } else { if (handlers != null) { handlers.Remove(handler); } } } } } #endif