#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member using System; using System.Collections.Generic; using System.Threading; using Cysharp.Threading.Tasks.Internal; namespace Cysharp.Threading.Tasks { public partial struct UniTask { public static UniTask WaitUntil(Func predicate, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) { return new UniTask(WaitUntilPromise.Create(predicate, timing, cancellationToken, out var token), token); } public static UniTask WaitWhile(Func predicate, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) { return new UniTask(WaitWhilePromise.Create(predicate, timing, cancellationToken, out var token), token); } public static UniTask WaitUntilCanceled(CancellationToken cancellationToken, PlayerLoopTiming timing = PlayerLoopTiming.Update) { return new UniTask(WaitUntilCanceledPromise.Create(cancellationToken, timing, out var token), token); } public static UniTask WaitUntilValueChanged(T target, Func monitorFunction, PlayerLoopTiming monitorTiming = PlayerLoopTiming.Update, IEqualityComparer equalityComparer = null, CancellationToken cancellationToken = default(CancellationToken)) where T : class { var unityObject = target as UnityEngine.Object; var isUnityObject = target is UnityEngine.Object; // don't use (unityObject == null) return new UniTask(isUnityObject ? WaitUntilValueChangedUnityObjectPromise.Create(target, monitorFunction, equalityComparer, monitorTiming, cancellationToken, out var token) : WaitUntilValueChangedStandardObjectPromise.Create(target, monitorFunction, equalityComparer, monitorTiming, cancellationToken, out token), token); } sealed class WaitUntilPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode { static TaskPool pool; WaitUntilPromise nextNode; public ref WaitUntilPromise NextNode => ref nextNode; static WaitUntilPromise() { TaskPool.RegisterSizeGetter(typeof(WaitUntilPromise), () => pool.Size); } Func predicate; CancellationToken cancellationToken; UniTaskCompletionSourceCore core; WaitUntilPromise() { } public static IUniTaskSource Create(Func predicate, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token) { if (cancellationToken.IsCancellationRequested) { return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token); } if (!pool.TryPop(out var result)) { result = new WaitUntilPromise(); } result.predicate = predicate; result.cancellationToken = cancellationToken; TaskTracker.TrackActiveTask(result, 3); PlayerLoopHelper.AddAction(timing, result); token = result.core.Version; return result; } public void GetResult(short token) { try { core.GetResult(token); } finally { TryReturn(); } } public UniTaskStatus GetStatus(short token) { return core.GetStatus(token); } public UniTaskStatus UnsafeGetStatus() { return core.UnsafeGetStatus(); } public void OnCompleted(Action continuation, object state, short token) { core.OnCompleted(continuation, state, token); } public bool MoveNext() { if (cancellationToken.IsCancellationRequested) { core.TrySetCanceled(cancellationToken); return false; } try { if (!predicate()) { return true; } } catch (Exception ex) { core.TrySetException(ex); return false; } core.TrySetResult(null); return false; } bool TryReturn() { TaskTracker.RemoveTracking(this); core.Reset(); predicate = default; cancellationToken = default; return pool.TryPush(this); } } sealed class WaitWhilePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode { static TaskPool pool; WaitWhilePromise nextNode; public ref WaitWhilePromise NextNode => ref nextNode; static WaitWhilePromise() { TaskPool.RegisterSizeGetter(typeof(WaitWhilePromise), () => pool.Size); } Func predicate; CancellationToken cancellationToken; UniTaskCompletionSourceCore core; WaitWhilePromise() { } public static IUniTaskSource Create(Func predicate, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token) { if (cancellationToken.IsCancellationRequested) { return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token); } if (!pool.TryPop(out var result)) { result = new WaitWhilePromise(); } result.predicate = predicate; result.cancellationToken = cancellationToken; TaskTracker.TrackActiveTask(result, 3); PlayerLoopHelper.AddAction(timing, result); token = result.core.Version; return result; } public void GetResult(short token) { try { core.GetResult(token); } finally { TryReturn(); } } public UniTaskStatus GetStatus(short token) { return core.GetStatus(token); } public UniTaskStatus UnsafeGetStatus() { return core.UnsafeGetStatus(); } public void OnCompleted(Action continuation, object state, short token) { core.OnCompleted(continuation, state, token); } public bool MoveNext() { if (cancellationToken.IsCancellationRequested) { core.TrySetCanceled(cancellationToken); return false; } try { if (predicate()) { return true; } } catch (Exception ex) { core.TrySetException(ex); return false; } core.TrySetResult(null); return false; } bool TryReturn() { TaskTracker.RemoveTracking(this); core.Reset(); predicate = default; cancellationToken = default; return pool.TryPush(this); } } sealed class WaitUntilCanceledPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode { static TaskPool pool; WaitUntilCanceledPromise nextNode; public ref WaitUntilCanceledPromise NextNode => ref nextNode; static WaitUntilCanceledPromise() { TaskPool.RegisterSizeGetter(typeof(WaitUntilCanceledPromise), () => pool.Size); } CancellationToken cancellationToken; UniTaskCompletionSourceCore core; WaitUntilCanceledPromise() { } public static IUniTaskSource Create(CancellationToken cancellationToken, PlayerLoopTiming timing, out short token) { if (cancellationToken.IsCancellationRequested) { return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token); } if (!pool.TryPop(out var result)) { result = new WaitUntilCanceledPromise(); } result.cancellationToken = cancellationToken; TaskTracker.TrackActiveTask(result, 3); PlayerLoopHelper.AddAction(timing, result); token = result.core.Version; return result; } public void GetResult(short token) { try { core.GetResult(token); } finally { TryReturn(); } } public UniTaskStatus GetStatus(short token) { return core.GetStatus(token); } public UniTaskStatus UnsafeGetStatus() { return core.UnsafeGetStatus(); } public void OnCompleted(Action continuation, object state, short token) { core.OnCompleted(continuation, state, token); } public bool MoveNext() { if (cancellationToken.IsCancellationRequested) { core.TrySetResult(null); return false; } return true; } bool TryReturn() { TaskTracker.RemoveTracking(this); core.Reset(); cancellationToken = default; return pool.TryPush(this); } } // where T : UnityEngine.Object, can not add constraint sealed class WaitUntilValueChangedUnityObjectPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode> { static TaskPool> pool; WaitUntilValueChangedUnityObjectPromise nextNode; public ref WaitUntilValueChangedUnityObjectPromise NextNode => ref nextNode; static WaitUntilValueChangedUnityObjectPromise() { TaskPool.RegisterSizeGetter(typeof(WaitUntilValueChangedUnityObjectPromise), () => pool.Size); } T target; UnityEngine.Object targetAsUnityObject; U currentValue; Func monitorFunction; IEqualityComparer equalityComparer; CancellationToken cancellationToken; UniTaskCompletionSourceCore core; WaitUntilValueChangedUnityObjectPromise() { } public static IUniTaskSource Create(T target, Func monitorFunction, IEqualityComparer equalityComparer, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token) { if (cancellationToken.IsCancellationRequested) { return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token); } if (!pool.TryPop(out var result)) { result = new WaitUntilValueChangedUnityObjectPromise(); } result.target = target; result.targetAsUnityObject = target as UnityEngine.Object; result.monitorFunction = monitorFunction; result.currentValue = monitorFunction(target); result.equalityComparer = equalityComparer ?? UnityEqualityComparer.GetDefault(); result.cancellationToken = cancellationToken; TaskTracker.TrackActiveTask(result, 3); PlayerLoopHelper.AddAction(timing, result); token = result.core.Version; return result; } public U GetResult(short token) { try { return core.GetResult(token); } finally { TryReturn(); } } void IUniTaskSource.GetResult(short token) { GetResult(token); } public UniTaskStatus GetStatus(short token) { return core.GetStatus(token); } public UniTaskStatus UnsafeGetStatus() { return core.UnsafeGetStatus(); } public void OnCompleted(Action continuation, object state, short token) { core.OnCompleted(continuation, state, token); } public bool MoveNext() { if (cancellationToken.IsCancellationRequested || targetAsUnityObject == null) // destroyed = cancel. { core.TrySetCanceled(cancellationToken); return false; } U nextValue = default(U); try { nextValue = monitorFunction(target); if (equalityComparer.Equals(currentValue, nextValue)) { return true; } } catch (Exception ex) { core.TrySetException(ex); return false; } core.TrySetResult(nextValue); return false; } bool TryReturn() { TaskTracker.RemoveTracking(this); core.Reset(); target = default; currentValue = default; monitorFunction = default; equalityComparer = default; cancellationToken = default; return pool.TryPush(this); } } sealed class WaitUntilValueChangedStandardObjectPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode> where T : class { static TaskPool> pool; WaitUntilValueChangedStandardObjectPromise nextNode; public ref WaitUntilValueChangedStandardObjectPromise NextNode => ref nextNode; static WaitUntilValueChangedStandardObjectPromise() { TaskPool.RegisterSizeGetter(typeof(WaitUntilValueChangedStandardObjectPromise), () => pool.Size); } WeakReference target; U currentValue; Func monitorFunction; IEqualityComparer equalityComparer; CancellationToken cancellationToken; UniTaskCompletionSourceCore core; WaitUntilValueChangedStandardObjectPromise() { } public static IUniTaskSource Create(T target, Func monitorFunction, IEqualityComparer equalityComparer, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token) { if (cancellationToken.IsCancellationRequested) { return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token); } if (!pool.TryPop(out var result)) { result = new WaitUntilValueChangedStandardObjectPromise(); } result.target = new WeakReference(target, false); // wrap in WeakReference. result.monitorFunction = monitorFunction; result.currentValue = monitorFunction(target); result.equalityComparer = equalityComparer ?? UnityEqualityComparer.GetDefault(); result.cancellationToken = cancellationToken; TaskTracker.TrackActiveTask(result, 3); PlayerLoopHelper.AddAction(timing, result); token = result.core.Version; return result; } public U GetResult(short token) { try { return core.GetResult(token); } finally { TryReturn(); } } void IUniTaskSource.GetResult(short token) { GetResult(token); } public UniTaskStatus GetStatus(short token) { return core.GetStatus(token); } public UniTaskStatus UnsafeGetStatus() { return core.UnsafeGetStatus(); } public void OnCompleted(Action continuation, object state, short token) { core.OnCompleted(continuation, state, token); } public bool MoveNext() { if (cancellationToken.IsCancellationRequested || !target.TryGetTarget(out var t)) // doesn't find = cancel. { core.TrySetCanceled(cancellationToken); return false; } U nextValue = default(U); try { nextValue = monitorFunction(t); if (equalityComparer.Equals(currentValue, nextValue)) { return true; } } catch (Exception ex) { core.TrySetException(ex); return false; } core.TrySetResult(nextValue); return false; } bool TryReturn() { TaskTracker.RemoveTracking(this); core.Reset(); target = default; currentValue = default; monitorFunction = default; equalityComparer = default; cancellationToken = default; return pool.TryPush(this); } } } }