#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; namespace UniRx.Async { 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 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 = !object.ReferenceEquals(target, null); // 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, IPromisePoolItem { static readonly PromisePool pool = new PromisePool(); 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); } var result = pool.TryRent() ?? 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 { TaskTracker.RemoveTracking(this); core.GetResult(token); } finally { pool.TryReturn(this); } } 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; } public void Reset() { core.Reset(); predicate = default; cancellationToken = default; } ~WaitUntilPromise() { if (pool.TryReturn(this)) { GC.ReRegisterForFinalize(this); } } } sealed class WaitWhilePromise : IUniTaskSource, IPlayerLoopItem, IPromisePoolItem { static readonly PromisePool pool = new PromisePool(); 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); } var result = pool.TryRent() ?? 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 { TaskTracker.RemoveTracking(this); core.GetResult(token); } finally { pool.TryReturn(this); } } 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; } public void Reset() { core.Reset(); predicate = default; cancellationToken = default; } ~WaitWhilePromise() { if (pool.TryReturn(this)) { GC.ReRegisterForFinalize(this); } } } // where T : UnityEngine.Object, can not add constraint sealed class WaitUntilValueChangedUnityObjectPromise : IUniTaskSource, IPlayerLoopItem, IPromisePoolItem { static readonly PromisePool> pool = new PromisePool>(); T target; 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); } var result = pool.TryRent() ?? new WaitUntilValueChangedUnityObjectPromise(); result.target = target; 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 { TaskTracker.RemoveTracking(this); return core.GetResult(token); } finally { pool.TryReturn(this); } } 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 == 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; } public void Reset() { core.Reset(); target = default; currentValue = default; monitorFunction = default; equalityComparer = default; cancellationToken = default; } ~WaitUntilValueChangedUnityObjectPromise() { if (pool.TryReturn(this)) { GC.ReRegisterForFinalize(this); } } } sealed class WaitUntilValueChangedStandardObjectPromise : IUniTaskSource, IPlayerLoopItem, IPromisePoolItem where T : class { static readonly PromisePool> pool = new PromisePool>(); 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); } var result = pool.TryRent() ?? 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 { TaskTracker.RemoveTracking(this); return core.GetResult(token); } finally { pool.TryReturn(this); } } 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; } public void Reset() { core.Reset(); target = default; currentValue = default; monitorFunction = default; equalityComparer = default; cancellationToken = default; } ~WaitUntilValueChangedStandardObjectPromise() { if (pool.TryReturn(this)) { GC.ReRegisterForFinalize(this); } } } } } #endif