You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
582 lines
19 KiB
582 lines
19 KiB
#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<bool> 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<bool> 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<U> WaitUntilValueChanged<T, U>(T target, Func<T, U> monitorFunction, PlayerLoopTiming monitorTiming = PlayerLoopTiming.Update, IEqualityComparer<U> 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<U>(isUnityObject |
|
? WaitUntilValueChangedUnityObjectPromise<T, U>.Create(target, monitorFunction, equalityComparer, monitorTiming, cancellationToken, out var token) |
|
: WaitUntilValueChangedStandardObjectPromise<T, U>.Create(target, monitorFunction, equalityComparer, monitorTiming, cancellationToken, out token), token); |
|
} |
|
|
|
sealed class WaitUntilPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<WaitUntilPromise> |
|
{ |
|
static TaskPool<WaitUntilPromise> pool; |
|
WaitUntilPromise nextNode; |
|
public ref WaitUntilPromise NextNode => ref nextNode; |
|
|
|
static WaitUntilPromise() |
|
{ |
|
TaskPool.RegisterSizeGetter(typeof(WaitUntilPromise), () => pool.Size); |
|
} |
|
|
|
Func<bool> predicate; |
|
CancellationToken cancellationToken; |
|
|
|
UniTaskCompletionSourceCore<object> core; |
|
|
|
WaitUntilPromise() |
|
{ |
|
} |
|
|
|
public static IUniTaskSource Create(Func<bool> 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<object> 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<WaitWhilePromise> |
|
{ |
|
static TaskPool<WaitWhilePromise> pool; |
|
WaitWhilePromise nextNode; |
|
public ref WaitWhilePromise NextNode => ref nextNode; |
|
|
|
static WaitWhilePromise() |
|
{ |
|
TaskPool.RegisterSizeGetter(typeof(WaitWhilePromise), () => pool.Size); |
|
} |
|
|
|
Func<bool> predicate; |
|
CancellationToken cancellationToken; |
|
|
|
UniTaskCompletionSourceCore<object> core; |
|
|
|
WaitWhilePromise() |
|
{ |
|
} |
|
|
|
public static IUniTaskSource Create(Func<bool> 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<object> 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<WaitUntilCanceledPromise> |
|
{ |
|
static TaskPool<WaitUntilCanceledPromise> pool; |
|
WaitUntilCanceledPromise nextNode; |
|
public ref WaitUntilCanceledPromise NextNode => ref nextNode; |
|
|
|
static WaitUntilCanceledPromise() |
|
{ |
|
TaskPool.RegisterSizeGetter(typeof(WaitUntilCanceledPromise), () => pool.Size); |
|
} |
|
|
|
CancellationToken cancellationToken; |
|
|
|
UniTaskCompletionSourceCore<object> 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<object> 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<T, U> : IUniTaskSource<U>, IPlayerLoopItem, ITaskPoolNode<WaitUntilValueChangedUnityObjectPromise<T, U>> |
|
{ |
|
static TaskPool<WaitUntilValueChangedUnityObjectPromise<T, U>> pool; |
|
WaitUntilValueChangedUnityObjectPromise<T, U> nextNode; |
|
public ref WaitUntilValueChangedUnityObjectPromise<T, U> NextNode => ref nextNode; |
|
|
|
static WaitUntilValueChangedUnityObjectPromise() |
|
{ |
|
TaskPool.RegisterSizeGetter(typeof(WaitUntilValueChangedUnityObjectPromise<T, U>), () => pool.Size); |
|
} |
|
|
|
T target; |
|
UnityEngine.Object targetAsUnityObject; |
|
U currentValue; |
|
Func<T, U> monitorFunction; |
|
IEqualityComparer<U> equalityComparer; |
|
CancellationToken cancellationToken; |
|
|
|
UniTaskCompletionSourceCore<U> core; |
|
|
|
WaitUntilValueChangedUnityObjectPromise() |
|
{ |
|
} |
|
|
|
public static IUniTaskSource<U> Create(T target, Func<T, U> monitorFunction, IEqualityComparer<U> equalityComparer, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token) |
|
{ |
|
if (cancellationToken.IsCancellationRequested) |
|
{ |
|
return AutoResetUniTaskCompletionSource<U>.CreateFromCanceled(cancellationToken, out token); |
|
} |
|
|
|
if (!pool.TryPop(out var result)) |
|
{ |
|
result = new WaitUntilValueChangedUnityObjectPromise<T, U>(); |
|
} |
|
|
|
result.target = target; |
|
result.targetAsUnityObject = target as UnityEngine.Object; |
|
result.monitorFunction = monitorFunction; |
|
result.currentValue = monitorFunction(target); |
|
result.equalityComparer = equalityComparer ?? UnityEqualityComparer.GetDefault<U>(); |
|
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<object> 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<T, U> : IUniTaskSource<U>, IPlayerLoopItem, ITaskPoolNode<WaitUntilValueChangedStandardObjectPromise<T, U>> |
|
where T : class |
|
{ |
|
static TaskPool<WaitUntilValueChangedStandardObjectPromise<T, U>> pool; |
|
WaitUntilValueChangedStandardObjectPromise<T, U> nextNode; |
|
public ref WaitUntilValueChangedStandardObjectPromise<T, U> NextNode => ref nextNode; |
|
|
|
static WaitUntilValueChangedStandardObjectPromise() |
|
{ |
|
TaskPool.RegisterSizeGetter(typeof(WaitUntilValueChangedStandardObjectPromise<T, U>), () => pool.Size); |
|
} |
|
|
|
WeakReference<T> target; |
|
U currentValue; |
|
Func<T, U> monitorFunction; |
|
IEqualityComparer<U> equalityComparer; |
|
CancellationToken cancellationToken; |
|
|
|
UniTaskCompletionSourceCore<U> core; |
|
|
|
WaitUntilValueChangedStandardObjectPromise() |
|
{ |
|
} |
|
|
|
public static IUniTaskSource<U> Create(T target, Func<T, U> monitorFunction, IEqualityComparer<U> equalityComparer, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token) |
|
{ |
|
if (cancellationToken.IsCancellationRequested) |
|
{ |
|
return AutoResetUniTaskCompletionSource<U>.CreateFromCanceled(cancellationToken, out token); |
|
} |
|
|
|
if (!pool.TryPop(out var result)) |
|
{ |
|
result = new WaitUntilValueChangedStandardObjectPromise<T, U>(); |
|
} |
|
|
|
result.target = new WeakReference<T>(target, false); // wrap in WeakReference. |
|
result.monitorFunction = monitorFunction; |
|
result.currentValue = monitorFunction(target); |
|
result.equalityComparer = equalityComparer ?? UnityEqualityComparer.GetDefault<U>(); |
|
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<object> 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); |
|
} |
|
} |
|
} |
|
}
|
|
|