|
- using System;
- using System.Threading;
-
- namespace Cysharp.Threading.Tasks
- {
- public interface IReadOnlyAsyncReactiveProperty<T> : IUniTaskAsyncEnumerable<T>
- {
- T Value { get; }
- IUniTaskAsyncEnumerable<T> WithoutCurrent();
- UniTask<T> WaitAsync(CancellationToken cancellationToken = default);
- }
-
- public interface IAsyncReactiveProperty<T> : IReadOnlyAsyncReactiveProperty<T>
- {
- new T Value { get; set; }
- }
-
- [Serializable]
- public class AsyncReactiveProperty<T> : IAsyncReactiveProperty<T>, IDisposable
- {
- TriggerEvent<T> triggerEvent;
-
- #if UNITY_2018_3_OR_NEWER
- [UnityEngine.SerializeField]
- #endif
- T latestValue;
-
- public T Value
- {
- get
- {
- return latestValue;
- }
- set
- {
- this.latestValue = value;
- triggerEvent.SetResult(value);
- }
- }
-
- public AsyncReactiveProperty(T value)
- {
- this.latestValue = value;
- this.triggerEvent = default;
- }
-
- public IUniTaskAsyncEnumerable<T> WithoutCurrent()
- {
- return new WithoutCurrentEnumerable(this);
- }
-
- public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken)
- {
- return new Enumerator(this, cancellationToken, true);
- }
-
- public void Dispose()
- {
- triggerEvent.SetCompleted();
- }
-
- public static implicit operator T(AsyncReactiveProperty<T> value)
- {
- return value.Value;
- }
-
- public override string ToString()
- {
- if (isValueType) return latestValue.ToString();
- return latestValue?.ToString();
- }
-
- public UniTask<T> WaitAsync(CancellationToken cancellationToken = default)
- {
- return new UniTask<T>(WaitAsyncSource.Create(this, cancellationToken, out var token), token);
- }
-
- static bool isValueType;
-
- static AsyncReactiveProperty()
- {
- isValueType = typeof(T).IsValueType;
- }
-
- sealed class WaitAsyncSource : IUniTaskSource<T>, ITriggerHandler<T>, ITaskPoolNode<WaitAsyncSource>
- {
- static Action<object> cancellationCallback = CancellationCallback;
-
- static TaskPool<WaitAsyncSource> pool;
- WaitAsyncSource nextNode;
- ref WaitAsyncSource ITaskPoolNode<WaitAsyncSource>.NextNode => ref nextNode;
-
- static WaitAsyncSource()
- {
- TaskPool.RegisterSizeGetter(typeof(WaitAsyncSource), () => pool.Size);
- }
-
- AsyncReactiveProperty<T> parent;
- CancellationToken cancellationToken;
- CancellationTokenRegistration cancellationTokenRegistration;
- UniTaskCompletionSourceCore<T> core;
-
- WaitAsyncSource()
- {
- }
-
- public static IUniTaskSource<T> Create(AsyncReactiveProperty<T> parent, CancellationToken cancellationToken, out short token)
- {
- if (cancellationToken.IsCancellationRequested)
- {
- return AutoResetUniTaskCompletionSource<T>.CreateFromCanceled(cancellationToken, out token);
- }
-
- if (!pool.TryPop(out var result))
- {
- result = new WaitAsyncSource();
- }
-
- result.parent = parent;
- result.cancellationToken = cancellationToken;
-
- if (cancellationToken.CanBeCanceled)
- {
- result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, result);
- }
-
- result.parent.triggerEvent.Add(result);
-
- TaskTracker.TrackActiveTask(result, 3);
-
- token = result.core.Version;
- return result;
- }
-
- bool TryReturn()
- {
- TaskTracker.RemoveTracking(this);
- core.Reset();
- cancellationTokenRegistration.Dispose();
- cancellationTokenRegistration = default;
- parent.triggerEvent.Remove(this);
- parent = null;
- cancellationToken = default;
- return pool.TryPush(this);
- }
-
- static void CancellationCallback(object state)
- {
- var self = (WaitAsyncSource)state;
- self.OnCanceled(self.cancellationToken);
- }
-
- // IUniTaskSource
-
- public T GetResult(short token)
- {
- try
- {
- return core.GetResult(token);
- }
- finally
- {
- TryReturn();
- }
- }
-
- void IUniTaskSource.GetResult(short token)
- {
- GetResult(token);
- }
-
- public void OnCompleted(Action<object> continuation, object state, short token)
- {
- core.OnCompleted(continuation, state, token);
- }
-
- public UniTaskStatus GetStatus(short token)
- {
- return core.GetStatus(token);
- }
-
- public UniTaskStatus UnsafeGetStatus()
- {
- return core.UnsafeGetStatus();
- }
-
- // ITriggerHandler
-
- ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; }
- ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; }
-
- public void OnCanceled(CancellationToken cancellationToken)
- {
- core.TrySetCanceled(cancellationToken);
- }
-
- public void OnCompleted()
- {
- // Complete as Cancel.
- core.TrySetCanceled(CancellationToken.None);
- }
-
- public void OnError(Exception ex)
- {
- core.TrySetException(ex);
- }
-
- public void OnNext(T value)
- {
- core.TrySetResult(value);
- }
- }
-
- sealed class WithoutCurrentEnumerable : IUniTaskAsyncEnumerable<T>
- {
- readonly AsyncReactiveProperty<T> parent;
-
- public WithoutCurrentEnumerable(AsyncReactiveProperty<T> parent)
- {
- this.parent = parent;
- }
-
- public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
- {
- return new Enumerator(parent, cancellationToken, false);
- }
- }
-
- sealed class Enumerator : MoveNextSource, IUniTaskAsyncEnumerator<T>, ITriggerHandler<T>
- {
- static Action<object> cancellationCallback = CancellationCallback;
-
- readonly AsyncReactiveProperty<T> parent;
- readonly CancellationToken cancellationToken;
- readonly CancellationTokenRegistration cancellationTokenRegistration;
- T value;
- bool isDisposed;
- bool firstCall;
-
- public Enumerator(AsyncReactiveProperty<T> parent, CancellationToken cancellationToken, bool publishCurrentValue)
- {
- this.parent = parent;
- this.cancellationToken = cancellationToken;
- this.firstCall = publishCurrentValue;
-
- parent.triggerEvent.Add(this);
- TaskTracker.TrackActiveTask(this, 3);
-
- if (cancellationToken.CanBeCanceled)
- {
- cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, this);
- }
- }
-
- public T Current => value;
-
- ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; }
- ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; }
-
- public UniTask<bool> MoveNextAsync()
- {
- // raise latest value on first call.
- if (firstCall)
- {
- firstCall = false;
- value = parent.Value;
- return CompletedTasks.True;
- }
-
- completionSource.Reset();
- return new UniTask<bool>(this, completionSource.Version);
- }
-
- public UniTask DisposeAsync()
- {
- if (!isDisposed)
- {
- isDisposed = true;
- TaskTracker.RemoveTracking(this);
- completionSource.TrySetCanceled(cancellationToken);
- parent.triggerEvent.Remove(this);
- }
- return default;
- }
-
- public void OnNext(T value)
- {
- this.value = value;
- completionSource.TrySetResult(true);
- }
-
- public void OnCanceled(CancellationToken cancellationToken)
- {
- DisposeAsync().Forget();
- }
-
- public void OnCompleted()
- {
- completionSource.TrySetResult(false);
- }
-
- public void OnError(Exception ex)
- {
- completionSource.TrySetException(ex);
- }
-
- static void CancellationCallback(object state)
- {
- var self = (Enumerator)state;
- self.DisposeAsync().Forget();
- }
- }
- }
-
- public class ReadOnlyAsyncReactiveProperty<T> : IReadOnlyAsyncReactiveProperty<T>, IDisposable
- {
- TriggerEvent<T> triggerEvent;
-
- T latestValue;
- IUniTaskAsyncEnumerator<T> enumerator;
-
- public T Value
- {
- get
- {
- return latestValue;
- }
- }
-
- public ReadOnlyAsyncReactiveProperty(T initialValue, IUniTaskAsyncEnumerable<T> source, CancellationToken cancellationToken)
- {
- latestValue = initialValue;
- ConsumeEnumerator(source, cancellationToken).Forget();
- }
-
- public ReadOnlyAsyncReactiveProperty(IUniTaskAsyncEnumerable<T> source, CancellationToken cancellationToken)
- {
- ConsumeEnumerator(source, cancellationToken).Forget();
- }
-
- async UniTaskVoid ConsumeEnumerator(IUniTaskAsyncEnumerable<T> source, CancellationToken cancellationToken)
- {
- enumerator = source.GetAsyncEnumerator(cancellationToken);
- try
- {
- while (await enumerator.MoveNextAsync())
- {
- var value = enumerator.Current;
- this.latestValue = value;
- triggerEvent.SetResult(value);
- }
- }
- finally
- {
- await enumerator.DisposeAsync();
- enumerator = null;
- }
- }
-
- public IUniTaskAsyncEnumerable<T> WithoutCurrent()
- {
- return new WithoutCurrentEnumerable(this);
- }
-
- public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken)
- {
- return new Enumerator(this, cancellationToken, true);
- }
-
- public void Dispose()
- {
- if (enumerator != null)
- {
- enumerator.DisposeAsync().Forget();
- }
-
- triggerEvent.SetCompleted();
- }
-
- public static implicit operator T(ReadOnlyAsyncReactiveProperty<T> value)
- {
- return value.Value;
- }
-
- public override string ToString()
- {
- if (isValueType) return latestValue.ToString();
- return latestValue?.ToString();
- }
-
- public UniTask<T> WaitAsync(CancellationToken cancellationToken = default)
- {
- return new UniTask<T>(WaitAsyncSource.Create(this, cancellationToken, out var token), token);
- }
-
- static bool isValueType;
-
- static ReadOnlyAsyncReactiveProperty()
- {
- isValueType = typeof(T).IsValueType;
- }
-
- sealed class WaitAsyncSource : IUniTaskSource<T>, ITriggerHandler<T>, ITaskPoolNode<WaitAsyncSource>
- {
- static Action<object> cancellationCallback = CancellationCallback;
-
- static TaskPool<WaitAsyncSource> pool;
- WaitAsyncSource nextNode;
- ref WaitAsyncSource ITaskPoolNode<WaitAsyncSource>.NextNode => ref nextNode;
-
- static WaitAsyncSource()
- {
- TaskPool.RegisterSizeGetter(typeof(WaitAsyncSource), () => pool.Size);
- }
-
- ReadOnlyAsyncReactiveProperty<T> parent;
- CancellationToken cancellationToken;
- CancellationTokenRegistration cancellationTokenRegistration;
- UniTaskCompletionSourceCore<T> core;
-
- WaitAsyncSource()
- {
- }
-
- public static IUniTaskSource<T> Create(ReadOnlyAsyncReactiveProperty<T> parent, CancellationToken cancellationToken, out short token)
- {
- if (cancellationToken.IsCancellationRequested)
- {
- return AutoResetUniTaskCompletionSource<T>.CreateFromCanceled(cancellationToken, out token);
- }
-
- if (!pool.TryPop(out var result))
- {
- result = new WaitAsyncSource();
- }
-
- result.parent = parent;
- result.cancellationToken = cancellationToken;
-
- if (cancellationToken.CanBeCanceled)
- {
- result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, result);
- }
-
- result.parent.triggerEvent.Add(result);
-
- TaskTracker.TrackActiveTask(result, 3);
-
- token = result.core.Version;
- return result;
- }
-
- bool TryReturn()
- {
- TaskTracker.RemoveTracking(this);
- core.Reset();
- cancellationTokenRegistration.Dispose();
- cancellationTokenRegistration = default;
- parent.triggerEvent.Remove(this);
- parent = null;
- cancellationToken = default;
- return pool.TryPush(this);
- }
-
- static void CancellationCallback(object state)
- {
- var self = (WaitAsyncSource)state;
- self.OnCanceled(self.cancellationToken);
- }
-
- // IUniTaskSource
-
- public T GetResult(short token)
- {
- try
- {
- return core.GetResult(token);
- }
- finally
- {
- TryReturn();
- }
- }
-
- void IUniTaskSource.GetResult(short token)
- {
- GetResult(token);
- }
-
- public void OnCompleted(Action<object> continuation, object state, short token)
- {
- core.OnCompleted(continuation, state, token);
- }
-
- public UniTaskStatus GetStatus(short token)
- {
- return core.GetStatus(token);
- }
-
- public UniTaskStatus UnsafeGetStatus()
- {
- return core.UnsafeGetStatus();
- }
-
- // ITriggerHandler
-
- ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; }
- ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; }
-
- public void OnCanceled(CancellationToken cancellationToken)
- {
- core.TrySetCanceled(cancellationToken);
- }
-
- public void OnCompleted()
- {
- // Complete as Cancel.
- core.TrySetCanceled(CancellationToken.None);
- }
-
- public void OnError(Exception ex)
- {
- core.TrySetException(ex);
- }
-
- public void OnNext(T value)
- {
- core.TrySetResult(value);
- }
- }
-
- sealed class WithoutCurrentEnumerable : IUniTaskAsyncEnumerable<T>
- {
- readonly ReadOnlyAsyncReactiveProperty<T> parent;
-
- public WithoutCurrentEnumerable(ReadOnlyAsyncReactiveProperty<T> parent)
- {
- this.parent = parent;
- }
-
- public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
- {
- return new Enumerator(parent, cancellationToken, false);
- }
- }
-
- sealed class Enumerator : MoveNextSource, IUniTaskAsyncEnumerator<T>, ITriggerHandler<T>
- {
- static Action<object> cancellationCallback = CancellationCallback;
-
- readonly ReadOnlyAsyncReactiveProperty<T> parent;
- readonly CancellationToken cancellationToken;
- readonly CancellationTokenRegistration cancellationTokenRegistration;
- T value;
- bool isDisposed;
- bool firstCall;
-
- public Enumerator(ReadOnlyAsyncReactiveProperty<T> parent, CancellationToken cancellationToken, bool publishCurrentValue)
- {
- this.parent = parent;
- this.cancellationToken = cancellationToken;
- this.firstCall = publishCurrentValue;
-
- parent.triggerEvent.Add(this);
- TaskTracker.TrackActiveTask(this, 3);
-
- if (cancellationToken.CanBeCanceled)
- {
- cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, this);
- }
- }
-
- public T Current => value;
- ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; }
- ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; }
-
- public UniTask<bool> MoveNextAsync()
- {
- // raise latest value on first call.
- if (firstCall)
- {
- firstCall = false;
- value = parent.Value;
- return CompletedTasks.True;
- }
-
- completionSource.Reset();
- return new UniTask<bool>(this, completionSource.Version);
- }
-
- public UniTask DisposeAsync()
- {
- if (!isDisposed)
- {
- isDisposed = true;
- TaskTracker.RemoveTracking(this);
- completionSource.TrySetCanceled(cancellationToken);
- parent.triggerEvent.Remove(this);
- }
- return default;
- }
-
- public void OnNext(T value)
- {
- this.value = value;
- completionSource.TrySetResult(true);
- }
-
- public void OnCanceled(CancellationToken cancellationToken)
- {
- DisposeAsync().Forget();
- }
-
- public void OnCompleted()
- {
- completionSource.TrySetResult(false);
- }
-
- public void OnError(Exception ex)
- {
- completionSource.TrySetException(ex);
- }
-
- static void CancellationCallback(object state)
- {
- var self = (Enumerator)state;
- self.DisposeAsync().Forget();
- }
- }
- }
-
- public static class StateExtensions
- {
- public static ReadOnlyAsyncReactiveProperty<T> ToReadOnlyAsyncReactiveProperty<T>(this IUniTaskAsyncEnumerable<T> source, CancellationToken cancellationToken)
- {
- return new ReadOnlyAsyncReactiveProperty<T>(source, cancellationToken);
- }
-
- public static ReadOnlyAsyncReactiveProperty<T> ToReadOnlyAsyncReactiveProperty<T>(this IUniTaskAsyncEnumerable<T> source, T initialValue, CancellationToken cancellationToken)
- {
- return new ReadOnlyAsyncReactiveProperty<T>(initialValue, source, cancellationToken);
- }
- }
- }
|