From 7bc9ef90f1044b8a983c3cbff966623592640430 Mon Sep 17 00:00:00 2001 From: neuecc Date: Mon, 4 May 2020 01:59:22 +0900 Subject: [PATCH] complete except trigger --- Assets/Scenes/SandboxMain.cs | 13 +- .../LazyPromise.cs.meta => AsyncLazy.cs.meta} | 2 +- .../UniRx.Async/EnumeratorAsyncExtensions.cs | 5 - Assets/UniRx.Async/Internal/LazyPromise.cs | 130 -- Assets/UniRx.Async/Internal/MinimumQueue.cs | 5 +- .../StatePool.cs.meta} | 2 +- .../UniRx.Async/Triggers/AsyncTriggerBase.cs | 46 +- .../Triggers/AsyncUpdateTrigger.cs | 159 +++ .../UniTask.WhenAll.Generated.cs.meta | 11 + .../UniTask.WhenAll.Generated.tt.meta | 7 + .../UniTask.WhenAny.Generated.cs.meta | 11 + .../UniTask.WhenAny.Generated.tt.meta | 7 + .../UniTaskExtensions.Shorthand.cs.meta | 11 + .../UniTaskExtensions.Shorthand.tt.meta | 7 + .../UniTaskObservableExtensions.cs | 4 +- ...UnityAsyncExtensions.MonoBehaviour.cs.meta | 11 + Assets/UniRx.Async/UnityAsyncExtensions.cs | 1094 ++++++----------- Assets/UniRx.Async/UnityAsyncExtensions.tt | 234 ++++ .../UniRx.Async/UnityAsyncExtensions.uGUI.cs | 225 ++-- 19 files changed, 960 insertions(+), 1024 deletions(-) rename Assets/UniRx.Async/{Internal/LazyPromise.cs.meta => AsyncLazy.cs.meta} (83%) delete mode 100644 Assets/UniRx.Async/Internal/LazyPromise.cs rename Assets/UniRx.Async/{UnityAsyncExtensions.cs.meta => Internal/StatePool.cs.meta} (83%) create mode 100644 Assets/UniRx.Async/UniTask.WhenAll.Generated.cs.meta create mode 100644 Assets/UniRx.Async/UniTask.WhenAll.Generated.tt.meta create mode 100644 Assets/UniRx.Async/UniTask.WhenAny.Generated.cs.meta create mode 100644 Assets/UniRx.Async/UniTask.WhenAny.Generated.tt.meta create mode 100644 Assets/UniRx.Async/UniTaskExtensions.Shorthand.cs.meta create mode 100644 Assets/UniRx.Async/UniTaskExtensions.Shorthand.tt.meta create mode 100644 Assets/UniRx.Async/UnityAsyncExtensions.MonoBehaviour.cs.meta create mode 100644 Assets/UniRx.Async/UnityAsyncExtensions.tt diff --git a/Assets/Scenes/SandboxMain.cs b/Assets/Scenes/SandboxMain.cs index 8d6fc4a..5ddbaab 100644 --- a/Assets/Scenes/SandboxMain.cs +++ b/Assets/Scenes/SandboxMain.cs @@ -22,17 +22,6 @@ public class SandboxMain : MonoBehaviour void Start() { - // Setup unobserverd tskexception handling - TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; - - // Optional: disable ExecutionContext if you don't use AsyncLocal. - //if (!ExecutionContext.IsFlowSuppressed()) - //{ - // ExecutionContext.SuppressFlow(); - //} - - //// Optional: disable SynchronizationContext(to boostup performance) if you completely use UniTask only - //SynchronizationContext.SetSynchronizationContext(null); // ----- @@ -50,7 +39,7 @@ public class SandboxMain : MonoBehaviour { text.text = ""; - ucs.TrySetResult(); + // ucs.TrySetResult(); await ucs.Task; }); diff --git a/Assets/UniRx.Async/Internal/LazyPromise.cs.meta b/Assets/UniRx.Async/AsyncLazy.cs.meta similarity index 83% rename from Assets/UniRx.Async/Internal/LazyPromise.cs.meta rename to Assets/UniRx.Async/AsyncLazy.cs.meta index ee8f3f7..554d162 100644 --- a/Assets/UniRx.Async/Internal/LazyPromise.cs.meta +++ b/Assets/UniRx.Async/AsyncLazy.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: fe7a4187b7f89f84582fd1e466a7f27e +guid: 01d1404ca421466419a7db7340ff5e77 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Assets/UniRx.Async/EnumeratorAsyncExtensions.cs b/Assets/UniRx.Async/EnumeratorAsyncExtensions.cs index 4438f5b..f5f4b33 100644 --- a/Assets/UniRx.Async/EnumeratorAsyncExtensions.cs +++ b/Assets/UniRx.Async/EnumeratorAsyncExtensions.cs @@ -35,9 +35,6 @@ namespace UniRx.Async IEnumerator innerEnumerator; CancellationToken cancellationToken; - Action continuation; - ExceptionDispatchInfo exception; - UniTaskCompletionSourceCore core; EnumeratorPromise() @@ -122,8 +119,6 @@ namespace UniRx.Async core.Reset(); innerEnumerator = default; cancellationToken = default; - continuation = default; - exception = default; } ~EnumeratorPromise() diff --git a/Assets/UniRx.Async/Internal/LazyPromise.cs b/Assets/UniRx.Async/Internal/LazyPromise.cs deleted file mode 100644 index dfea432..0000000 --- a/Assets/UniRx.Async/Internal/LazyPromise.cs +++ /dev/null @@ -1,130 +0,0 @@ -#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.Threading; - -namespace UniRx.Async.Internal -{ - internal sealed class LazyPromise : IAwaiter - { - Func factory; - UniTask value; - - public LazyPromise(Func factory) - { - this.factory = factory; - } - - void Create() - { - var f = Interlocked.Exchange(ref factory, null); - if (f != null) - { - value = f(); - } - } - - public bool IsCompleted - { - get - { - Create(); - return value.IsCompleted; - } - } - - public UniTaskStatus Status - { - get - { - Create(); - return value.Status; - } - } - - public void GetResult() - { - Create(); - value.GetResult(); - } - - void IAwaiter.GetResult() - { - GetResult(); - } - - public void UnsafeOnCompleted(Action continuation) - { - Create(); - value.GetAwaiter().UnsafeOnCompleted(continuation); - } - - public void OnCompleted(Action continuation) - { - UnsafeOnCompleted(continuation); - } - } - - internal sealed class LazyPromise : IAwaiter - { - Func> factory; - UniTask value; - - public LazyPromise(Func> factory) - { - this.factory = factory; - } - - void Create() - { - var f = Interlocked.Exchange(ref factory, null); - if (f != null) - { - value = f(); - } - } - - public bool IsCompleted - { - get - { - Create(); - return value.IsCompleted; - } - } - - public UniTaskStatus Status - { - get - { - Create(); - return value.Status; - } - } - - public T GetResult() - { - Create(); - return value.Result; - } - - void IAwaiter.GetResult() - { - GetResult(); - } - - public void UnsafeOnCompleted(Action continuation) - { - Create(); - value.GetAwaiter().UnsafeOnCompleted(continuation); - } - - public void OnCompleted(Action continuation) - { - UnsafeOnCompleted(continuation); - } - } -} - -#endif \ No newline at end of file diff --git a/Assets/UniRx.Async/Internal/MinimumQueue.cs b/Assets/UniRx.Async/Internal/MinimumQueue.cs index acf4962..5f3b589 100644 --- a/Assets/UniRx.Async/Internal/MinimumQueue.cs +++ b/Assets/UniRx.Async/Internal/MinimumQueue.cs @@ -8,7 +8,8 @@ using System.Runtime.CompilerServices; namespace UniRx.Async.Internal { // optimized version of Standard Queue. - internal class MinimumQueue + // TODO: to internal. + public class MinimumQueue { const int MinimumGrow = 4; const int GrowFactor = 200; @@ -28,7 +29,7 @@ namespace UniRx.Async.Internal public int Count { #if NET_4_6 || NET_STANDARD_2_0 - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] #endif get { return size; } } diff --git a/Assets/UniRx.Async/UnityAsyncExtensions.cs.meta b/Assets/UniRx.Async/Internal/StatePool.cs.meta similarity index 83% rename from Assets/UniRx.Async/UnityAsyncExtensions.cs.meta rename to Assets/UniRx.Async/Internal/StatePool.cs.meta index 6dfab81..6779aa1 100644 --- a/Assets/UniRx.Async/UnityAsyncExtensions.cs.meta +++ b/Assets/UniRx.Async/Internal/StatePool.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 8cc7fd65dd1433e419be4764aeb51391 +guid: 60cdf0bcaea36b444a7ae7263ae7598f MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Assets/UniRx.Async/Triggers/AsyncTriggerBase.cs b/Assets/UniRx.Async/Triggers/AsyncTriggerBase.cs index b0ffa57..a7b0d7a 100644 --- a/Assets/UniRx.Async/Triggers/AsyncTriggerBase.cs +++ b/Assets/UniRx.Async/Triggers/AsyncTriggerBase.cs @@ -15,46 +15,6 @@ namespace UniRx.Async.Triggers bool TrySetCanceled(); } - public class AsyncTriggerPromise : ReusablePromise, IPromise, ICancelablePromise - { - public CancellationToken RegisteredCancellationToken { get; private set; } - - public AsyncTriggerPromise() - : this(CancellationToken.None) - { - } - - public AsyncTriggerPromise(CancellationToken cancellationToken) - { - this.RegisteredCancellationToken = cancellationToken; - TaskTracker.TrackActiveTask(this); - } - - public override T GetResult() - { - if (Status == UniTaskStatus.Pending) return RawResult; - return base.GetResult(); - } - - public override bool TrySetResult(T result) - { - if (Status == UniTaskStatus.Pending) - { - // keep status as Pending. - this.ForceSetResult(result); - TryInvokeContinuation(); - return true; - } - return false; - } - - public override bool TrySetCanceled() - { - if (Status == UniTaskStatus.Canceled) return false; - TaskTracker.RemoveTracking(this); - return base.TrySetCanceled(); - } - } public interface ICancellationTokenKeyDictionary { @@ -62,7 +22,7 @@ namespace UniRx.Async.Triggers } public class AsyncTriggerPromiseDictionary : - Dictionary>, + Dictionary>, ICancellationTokenKeyDictionary, IEnumerable { @@ -73,7 +33,9 @@ namespace UniRx.Async.Triggers IEnumerator IEnumerable.GetEnumerator() { - return Values.GetEnumerator(); + // TODO: + throw new NotImplementedException(); + //return Values.GetEnumerator(); } void ICancellationTokenKeyDictionary.Remove(CancellationToken token) diff --git a/Assets/UniRx.Async/Triggers/AsyncUpdateTrigger.cs b/Assets/UniRx.Async/Triggers/AsyncUpdateTrigger.cs index 258eec9..20df476 100644 --- a/Assets/UniRx.Async/Triggers/AsyncUpdateTrigger.cs +++ b/Assets/UniRx.Async/Triggers/AsyncUpdateTrigger.cs @@ -2,8 +2,11 @@ #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.Linq; using System.Threading; +using UniRx.Async.Internal; using UnityEngine; using UnityEngine.EventSystems; @@ -35,6 +38,162 @@ namespace UniRx.Async.Triggers } + + + + // TODO:remove 2. + public abstract class AsyncTriggerBase2 : MonoBehaviour + { + static readonly Action Callback = CancelCallback; + + bool calledAwake = false; + bool destroyCalled = false; + CancellationTokenRegistration[] registeredCancellations; + int registeredCancellationsCount; + + protected abstract IEnumerable GetPromises(); + + void Awake() + { + calledAwake = true; + } + + void OnDestroy() + { + if (destroyCalled) return; + destroyCalled = true; + foreach (var item in GetPromises()) + { + item.TrySetCanceled(); + } + if (registeredCancellations != null) + { + for (int i = 0; i < registeredCancellationsCount; i++) + { + registeredCancellations[i].Dispose(); + registeredCancellations[i] = default(CancellationTokenRegistration); + } + ArrayPool.Shared.Return(registeredCancellations); + } + } + + protected void TrySetResult(MinimumQueue> promise, AsyncTriggerPromiseDictionary promises, T result) + { + if (promise != null) + { + // TODO: + } + + if (promises != null) + { + PromiseHelper.TrySetResultAll(promises.Values, result); + } + } + + public UniTask CreatePromise(ref MinimumQueue> promise, ref AsyncTriggerPromiseDictionary promises, CancellationToken cancellationToken) + { + if (destroyCalled) return UniTask.FromCanceled(); + + if (!calledAwake) + { + PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, new AwakeMonitor(this)); + } + + if (!cancellationToken.CanBeCanceled) + { + if (promise == null) + { + promise = new MinimumQueue>(4); // kakko kari.(ArrayPool?) + } + + var tcs = AutoResetUniTaskCompletionSource.Create(); + promise.Enqueue(tcs); + return tcs.Task; + } + + + CancellationTokenRegistration registrationToken = default; + // TODO:atode. + // var registrationToken = cancellationToken.RegisterWithoutCaptureExecutionContext(Callback, Tuple.Create((ICancellationTokenKeyDictionary)promises, (ICancelablePromise)cancellablePromise)); + if (registeredCancellations == null) + { + registeredCancellations = ArrayPool.Shared.Rent(4); + } + + ArrayPoolUtil.EnsureCapacity(ref registeredCancellations, registeredCancellationsCount + 1, ArrayPool.Shared); + registeredCancellations[registeredCancellationsCount++] = registrationToken; + + // TODO:use at registration + { + if (promises == null) + { + promises = new AsyncTriggerPromiseDictionary(); + } + + var tcs = AutoResetUniTaskCompletionSource.Create(); + promises.Add(cancellationToken, tcs); + return tcs.Task; + } + } + + static void CancelCallback(object state) + { + // TODO:nantokasuru. + + //var tuple = (Tuple)state; + //var dict = tuple.Item1; + //var promise = tuple.Item2; + + //promise.TrySetCanceled(); + //dict.Remove(promise.RegisteredCancellationToken); + } + + class AwakeMonitor : IPlayerLoopItem + { + readonly AsyncTriggerBase2 trigger; + + public AwakeMonitor(AsyncTriggerBase2 trigger) + { + this.trigger = trigger; + } + + public bool MoveNext() + { + if (trigger.calledAwake) return false; + if (trigger == null) + { + trigger.OnDestroy(); + return false; + } + return true; + } + } + } + + + // TODO:remove 2. + [DisallowMultipleComponent] + public class AsyncUpdateTrigger2 : AsyncTriggerBase2 + { + MinimumQueue> promise; + AsyncTriggerPromiseDictionary promises; + + protected override IEnumerable GetPromises() + { + // TODO: + throw new NotImplementedException(); + } + + void Update() + { + // TrySetResult + } + + public UniTask UpdateAsync(CancellationToken cancellationToken = default(CancellationToken)) + { + return CreatePromise(ref promise, ref promises, cancellationToken).AsUniTask(); + } + } } #endif diff --git a/Assets/UniRx.Async/UniTask.WhenAll.Generated.cs.meta b/Assets/UniRx.Async/UniTask.WhenAll.Generated.cs.meta new file mode 100644 index 0000000..40ed46c --- /dev/null +++ b/Assets/UniRx.Async/UniTask.WhenAll.Generated.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5110117231c8a6d4095fd0cbd3f4c142 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/UniRx.Async/UniTask.WhenAll.Generated.tt.meta b/Assets/UniRx.Async/UniTask.WhenAll.Generated.tt.meta new file mode 100644 index 0000000..f64cefd --- /dev/null +++ b/Assets/UniRx.Async/UniTask.WhenAll.Generated.tt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 2c2ecfd98ee1b9a4c9fae1b398c32d75 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/UniRx.Async/UniTask.WhenAny.Generated.cs.meta b/Assets/UniRx.Async/UniTask.WhenAny.Generated.cs.meta new file mode 100644 index 0000000..49a2c3f --- /dev/null +++ b/Assets/UniRx.Async/UniTask.WhenAny.Generated.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 13d604ac281570c4eac9962429f19ca9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/UniRx.Async/UniTask.WhenAny.Generated.tt.meta b/Assets/UniRx.Async/UniTask.WhenAny.Generated.tt.meta new file mode 100644 index 0000000..389cfd2 --- /dev/null +++ b/Assets/UniRx.Async/UniTask.WhenAny.Generated.tt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 71c2c0bce7543454c8ef545083e18170 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/UniRx.Async/UniTaskExtensions.Shorthand.cs.meta b/Assets/UniRx.Async/UniTaskExtensions.Shorthand.cs.meta new file mode 100644 index 0000000..e2dcc14 --- /dev/null +++ b/Assets/UniRx.Async/UniTaskExtensions.Shorthand.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4b4ff020f73dc6d4b8ebd4760d61fb43 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/UniRx.Async/UniTaskExtensions.Shorthand.tt.meta b/Assets/UniRx.Async/UniTaskExtensions.Shorthand.tt.meta new file mode 100644 index 0000000..4b7ff9e --- /dev/null +++ b/Assets/UniRx.Async/UniTaskExtensions.Shorthand.tt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 9a75b5d7e55a5a34fb6476586df37c72 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/UniRx.Async/UniTaskObservableExtensions.cs b/Assets/UniRx.Async/UniTaskObservableExtensions.cs index 2276bd4..cb7dc03 100644 --- a/Assets/UniRx.Async/UniTaskObservableExtensions.cs +++ b/Assets/UniRx.Async/UniTaskObservableExtensions.cs @@ -86,12 +86,12 @@ namespace UniRx.Async } } - static async UniTaskVoid Fire(AsyncSubject subject, UniTask task) + static async UniTaskVoid Fire(AsyncSubject subject, UniTask task) { try { await task; - subject.OnNext(null); + subject.OnNext(AsyncUnit.Default); subject.OnCompleted(); } catch (Exception ex) diff --git a/Assets/UniRx.Async/UnityAsyncExtensions.MonoBehaviour.cs.meta b/Assets/UniRx.Async/UnityAsyncExtensions.MonoBehaviour.cs.meta new file mode 100644 index 0000000..6e45863 --- /dev/null +++ b/Assets/UniRx.Async/UnityAsyncExtensions.MonoBehaviour.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2edd588bb09eb0a4695d039d6a1f02b2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/UniRx.Async/UnityAsyncExtensions.cs b/Assets/UniRx.Async/UnityAsyncExtensions.cs index 285eda5..385581f 100644 --- a/Assets/UniRx.Async/UnityAsyncExtensions.cs +++ b/Assets/UniRx.Async/UnityAsyncExtensions.cs @@ -1,17 +1,20 @@ -#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 +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member using System; using System.Runtime.CompilerServices; using System.Threading; -using UniRx.Async.Internal; using UnityEngine; +using UniRx.Async.Internal; +#if ENABLE_UNITYWEBREQUEST using UnityEngine.Networking; +#endif namespace UniRx.Async { public static partial class UnityAsyncExtensions { +#region AsyncOperation + public static AsyncOperationAwaiter GetAwaiter(this AsyncOperation asyncOperation) { Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); @@ -21,179 +24,25 @@ namespace UniRx.Async public static UniTask ToUniTask(this AsyncOperation asyncOperation) { Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); - return new UniTask(new AsyncOperationAwaiter(asyncOperation)); + + return new UniTask(AsyncOperationConfiguredSource.Create(asyncOperation, PlayerLoopTiming.Update, null, CancellationToken.None, out var token), token); } public static UniTask ConfigureAwait(this AsyncOperation asyncOperation, IProgress progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellation = default(CancellationToken)) { Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); - var awaiter = new AsyncOperationConfiguredAwaiter(asyncOperation, progress, cancellation); - if (!awaiter.IsCompleted) - { - PlayerLoopHelper.AddAction(timing, awaiter); - } - return new UniTask(awaiter); + return new UniTask(AsyncOperationConfiguredSource.Create(asyncOperation, timing, progress, cancellation, out var token), token); } - public static ResourceRequestAwaiter GetAwaiter(this ResourceRequest resourceRequest) - { - Error.ThrowArgumentNullException(resourceRequest, nameof(resourceRequest)); - return new ResourceRequestAwaiter(resourceRequest); - } - - public static UniTask ToUniTask(this ResourceRequest resourceRequest) - { - Error.ThrowArgumentNullException(resourceRequest, nameof(resourceRequest)); - return new UniTask(new ResourceRequestAwaiter(resourceRequest)); - } - - public static UniTask ConfigureAwait(this ResourceRequest resourceRequest, IProgress progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellation = default(CancellationToken)) - { - Error.ThrowArgumentNullException(resourceRequest, nameof(resourceRequest)); - - var awaiter = new ResourceRequestConfiguredAwaiter(resourceRequest, progress, cancellation); - if (!awaiter.IsCompleted) - { - PlayerLoopHelper.AddAction(timing, awaiter); - } - return new UniTask(awaiter); - } - - public static AssetBundleRequestAwaiter GetAwaiter(this AssetBundleRequest resourceRequest) - { - Error.ThrowArgumentNullException(resourceRequest, nameof(resourceRequest)); - return new AssetBundleRequestAwaiter(resourceRequest); - } - - public static UniTask ToUniTask(this AssetBundleRequest resourceRequest) - { - Error.ThrowArgumentNullException(resourceRequest, nameof(resourceRequest)); - return new UniTask(new AssetBundleRequestAwaiter(resourceRequest)); - } - - public static UniTask ConfigureAwait(this AssetBundleRequest resourceRequest, IProgress progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellation = default(CancellationToken)) - { - Error.ThrowArgumentNullException(resourceRequest, nameof(resourceRequest)); - - var awaiter = new AssetBundleRequestConfiguredAwaiter(resourceRequest, progress, cancellation); - if (!awaiter.IsCompleted) - { - PlayerLoopHelper.AddAction(timing, awaiter); - } - return new UniTask(awaiter); - } - - public static AssetBundleCreateRequestAwaiter GetAwaiter(this AssetBundleCreateRequest resourceRequest) - { - Error.ThrowArgumentNullException(resourceRequest, nameof(resourceRequest)); - return new AssetBundleCreateRequestAwaiter(resourceRequest); - } - - public static UniTask ToUniTask(this AssetBundleCreateRequest resourceRequest) - { - Error.ThrowArgumentNullException(resourceRequest, nameof(resourceRequest)); - return new UniTask(new AssetBundleCreateRequestAwaiter(resourceRequest)); - } - - public static UniTask ConfigureAwait(this AssetBundleCreateRequest resourceRequest, IProgress progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellation = default(CancellationToken)) - { - Error.ThrowArgumentNullException(resourceRequest, nameof(resourceRequest)); - - var awaiter = new AssetBundleCreateRequestConfiguredAwaiter(resourceRequest, progress, cancellation); - if (!awaiter.IsCompleted) - { - PlayerLoopHelper.AddAction(timing, awaiter); - } - return new UniTask(awaiter); - } - -#if ENABLE_WWW - -#if UNITY_2018_3_OR_NEWER -#pragma warning disable CS0618 -#endif - - public static IAwaiter GetAwaiter(this WWW www) - { - Error.ThrowArgumentNullException(www, nameof(www)); - - var awaiter = new WWWConfiguredAwaiter(www, null, CancellationToken.None); - if (!awaiter.IsCompleted) - { - PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, awaiter); - } - return awaiter; - } - - public static UniTask ToUniTask(this WWW www) - { - Error.ThrowArgumentNullException(www, nameof(www)); - - var awaiter = new WWWConfiguredAwaiter(www, null, CancellationToken.None); - if (!awaiter.IsCompleted) - { - PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, awaiter); - } - return new UniTask(awaiter); - } - - public static UniTask ConfigureAwait(this WWW www, IProgress progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellation = default(CancellationToken)) - { - Error.ThrowArgumentNullException(www, nameof(www)); - - var awaiter = new WWWConfiguredAwaiter(www, progress, cancellation); - if (!awaiter.IsCompleted) - { - PlayerLoopHelper.AddAction(timing, awaiter); - } - return new UniTask(awaiter); - } - -#if UNITY_2018_3_OR_NEWER -#pragma warning restore CS0618 -#endif - -#endif - -#if ENABLE_UNITYWEBREQUEST - - public static UnityWebRequestAsyncOperationAwaiter GetAwaiter(this UnityWebRequestAsyncOperation asyncOperation) - { - Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); - return new UnityWebRequestAsyncOperationAwaiter(asyncOperation); - } - - public static UniTask ToUniTask(this UnityWebRequestAsyncOperation asyncOperation) - { - Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); - return new UniTask(new UnityWebRequestAsyncOperationAwaiter(asyncOperation)); - } - - public static UniTask ConfigureAwait(this UnityWebRequestAsyncOperation asyncOperation, IProgress progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellation = default(CancellationToken)) - { - Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); - - var awaiter = new UnityWebRequestAsyncOperationConfiguredAwaiter(asyncOperation, progress, cancellation); - if (!awaiter.IsCompleted) - { - PlayerLoopHelper.AddAction(timing, awaiter); - } - return new UniTask(awaiter); - } - -#endif - public struct AsyncOperationAwaiter : ICriticalNotifyCompletion { AsyncOperation asyncOperation; Action continuationAction; - // UniTaskStatus status; public AsyncOperationAwaiter(AsyncOperation asyncOperation) { - this.status = asyncOperation.isDone ? UniTaskStatus.Succeeded : UniTaskStatus.Pending; - this.asyncOperation = (this.status.IsCompleted()) ? null : asyncOperation; + this.asyncOperation = asyncOperation; this.continuationAction = null; } @@ -201,22 +50,6 @@ namespace UniRx.Async public void GetResult() { - //*/ if (status == UniTaskStatus.Succeeded) return; - /* - if (status == UniTaskStatus.Pending) - { - // first timing of call - if (asyncOperation.isDone) - { - status = UniTaskStatus.Succeeded; - } - else - { - Error.ThrowNotYetCompleted(); - } - } - */ - if (continuationAction != null) { asyncOperation.completed -= continuationAction; @@ -227,6 +60,7 @@ namespace UniRx.Async { asyncOperation = null; // remove reference. } + } public void OnCompleted(Action continuation) @@ -242,52 +76,77 @@ namespace UniRx.Async } } - class AsyncOperationConfiguredAwaiter : IAwaiter, IPlayerLoopItem + class AsyncOperationConfiguredSource : IUniTaskSource, IPlayerLoopItem, IPromisePoolItem { + static readonly PromisePool pool = new PromisePool(); + AsyncOperation asyncOperation; IProgress progress; CancellationToken cancellationToken; - UniTaskStatus status; - Action continuation; - public AsyncOperationConfiguredAwaiter(AsyncOperation asyncOperation, IProgress progress, CancellationToken cancellationToken) + UniTaskCompletionSourceCore core; + + AsyncOperationConfiguredSource() { - this.status = cancellationToken.IsCancellationRequested ? UniTaskStatus.Canceled - : asyncOperation.isDone ? UniTaskStatus.Succeeded - : UniTaskStatus.Pending; - if (this.status.IsCompleted()) return; - - this.asyncOperation = asyncOperation; - this.progress = progress; - this.cancellationToken = cancellationToken; - this.continuation = null; - - TaskTracker.TrackActiveTask(this, 2); } - public bool IsCompleted => status.IsCompleted(); - public UniTaskStatus Status => status; - - public void GetResult() + public static IUniTaskSource Create(AsyncOperation asyncOperation, PlayerLoopTiming timing, IProgress progress, CancellationToken cancellationToken, out short token) { - if (status == UniTaskStatus.Succeeded) + if (cancellationToken.IsCancellationRequested) { - return; - } - else if (status == UniTaskStatus.Canceled) - { - Error.ThrowOperationCanceledException(); + return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token); } - Error.ThrowNotYetCompleted(); + var result = pool.TryRent() ?? new AsyncOperationConfiguredSource(); + + result.asyncOperation = asyncOperation; + result.progress = progress; + result.cancellationToken = cancellationToken; + + TaskTracker2.TrackActiveTask(result, 3); + + PlayerLoopHelper.AddAction(timing, result); + + token = result.core.Version; + return result; + } + + public void GetResult(short token) + { + try + { + TaskTracker2.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) { - InvokeContinuation(UniTaskStatus.Canceled); + core.TrySetCanceled(cancellationToken); return false; } @@ -298,77 +157,71 @@ namespace UniRx.Async if (asyncOperation.isDone) { - InvokeContinuation(UniTaskStatus.Succeeded); + core.TrySetResult(AsyncUnit.Default); return false; } return true; } - void InvokeContinuation(UniTaskStatus status) + public void Reset() { - this.status = status; - var cont = this.continuation; - - // cleanup - TaskTracker.RemoveTracking(this); - this.continuation = null; - this.cancellationToken = CancellationToken.None; - this.progress = null; - this.asyncOperation = null; - - if (cont != null) cont.Invoke(); + core.Reset(); + asyncOperation = default; + progress = default; + cancellationToken = default; } - public void OnCompleted(Action continuation) + ~AsyncOperationConfiguredSource() { - UnsafeOnCompleted(continuation); - } - - public void UnsafeOnCompleted(Action continuation) - { - Error.ThrowWhenContinuationIsAlreadyRegistered(this.continuation); - this.continuation = continuation; + if (pool.TryReturn(this)) + { + GC.ReRegisterForFinalize(this); + } } } - public struct ResourceRequestAwaiter : IAwaiter +# endregion + +#region ResourceRequest + + public static ResourceRequestAwaiter GetAwaiter(this ResourceRequest asyncOperation) + { + Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); + return new ResourceRequestAwaiter(asyncOperation); + } + + public static UniTask ToUniTask(this ResourceRequest asyncOperation) + { + Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); + + return new UniTask(ResourceRequestConfiguredSource.Create(asyncOperation, PlayerLoopTiming.Update, null, CancellationToken.None, out var token), token); + } + + public static UniTask ConfigureAwait(this ResourceRequest asyncOperation, IProgress progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellation = default(CancellationToken)) + { + Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); + + return new UniTask(ResourceRequestConfiguredSource.Create(asyncOperation, timing, progress, cancellation, out var token), token); + } + + public struct ResourceRequestAwaiter : ICriticalNotifyCompletion { ResourceRequest asyncOperation; Action continuationAction; - UniTaskStatus status; UnityEngine.Object result; public ResourceRequestAwaiter(ResourceRequest asyncOperation) { - this.status = asyncOperation.isDone ? UniTaskStatus.Succeeded : UniTaskStatus.Pending; - this.asyncOperation = (this.status.IsCompleted()) ? null : asyncOperation; - this.result = (this.status.IsCompletedSuccessfully()) ? asyncOperation.asset : null; + this.asyncOperation = asyncOperation; this.continuationAction = null; + this.result = default; } - public bool IsCompleted => status.IsCompleted(); - public UniTaskStatus Status => status; + public bool IsCompleted => asyncOperation.isDone; public UnityEngine.Object GetResult() { - if (status == UniTaskStatus.Succeeded) return this.result; - - if (status == UniTaskStatus.Pending) - { - // first timing of call - if (asyncOperation.isDone) - { - status = UniTaskStatus.Succeeded; - } - else - { - Error.ThrowNotYetCompleted(); - } - } - - this.result = asyncOperation.asset; - if (continuationAction != null) { asyncOperation.completed -= continuationAction; @@ -383,8 +236,6 @@ namespace UniRx.Async return this.result; } - void IAwaiter.GetResult() => GetResult(); - public void OnCompleted(Action continuation) { UnsafeOnCompleted(continuation); @@ -393,108 +244,11 @@ namespace UniRx.Async public void UnsafeOnCompleted(Action continuation) { Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction); - continuationAction = continuation.AsFuncOfT(); + continuationAction = continuation.AsFuncOfT(); // allocate delegate. asyncOperation.completed += continuationAction; } } - class ResourceRequestConfiguredAwaiter : IAwaiter, IPlayerLoopItem - { - ResourceRequest asyncOperation; - IProgress progress; - CancellationToken cancellationToken; - UniTaskStatus status; - Action continuation; - UnityEngine.Object result; - - public ResourceRequestConfiguredAwaiter(ResourceRequest asyncOperation, IProgress progress, CancellationToken cancellationToken) - { - this.status = cancellationToken.IsCancellationRequested ? UniTaskStatus.Canceled - : asyncOperation.isDone ? UniTaskStatus.Succeeded - : UniTaskStatus.Pending; - - if (this.status.IsCompletedSuccessfully()) this.result = asyncOperation.asset; - if (this.status.IsCompleted()) return; - - this.asyncOperation = asyncOperation; - this.progress = progress; - this.cancellationToken = cancellationToken; - this.continuation = null; - this.result = null; - - TaskTracker.TrackActiveTask(this, 2); - } - - public bool IsCompleted => status.IsCompleted(); - public UniTaskStatus Status => status; - void IAwaiter.GetResult() => GetResult(); - - public UnityEngine.Object GetResult() - { - if (status == UniTaskStatus.Succeeded) return this.result; - - if (status == UniTaskStatus.Canceled) - { - Error.ThrowOperationCanceledException(); - } - - return Error.ThrowNotYetCompleted(); - } - - public bool MoveNext() - { - if (cancellationToken.IsCancellationRequested) - { - InvokeContinuation(UniTaskStatus.Canceled); - return false; - } - - if (progress != null) - { - progress.Report(asyncOperation.progress); - } - - if (asyncOperation.isDone) - { - this.result = asyncOperation.asset; - InvokeContinuation(UniTaskStatus.Succeeded); - return false; - } - - return true; - } - - void InvokeContinuation(UniTaskStatus status) - { - this.status = status; - var cont = this.continuation; - - // cleanup - TaskTracker.RemoveTracking(this); - this.continuation = null; - this.cancellationToken = CancellationToken.None; - this.progress = null; - this.asyncOperation = null; - - if (cont != null) cont.Invoke(); - } - - public void OnCompleted(Action continuation) - { - Error.ThrowWhenContinuationIsAlreadyRegistered(this.continuation); - this.continuation = continuation; - } - - public void UnsafeOnCompleted(Action continuation) - { - Error.ThrowWhenContinuationIsAlreadyRegistered(this.continuation); - this.continuation = continuation; - } - } - - - - // TODO: try to check API. class ResourceRequestConfiguredSource : IUniTaskSource, IPlayerLoopItem, IPromisePoolItem { static readonly PromisePool pool = new PromisePool(); @@ -523,12 +277,7 @@ namespace UniRx.Async result.progress = progress; result.cancellationToken = cancellationToken; -#if UNITY_EDITOR - // TODO:capture??? - //var capturedStackTraceForDebugging = TaskTracker.CaptureStackTrace(2); - // TODO:Add ActiveTask? - // TaskTracker.TrackActiveTask( -#endif + TaskTracker2.TrackActiveTask(result, 3); PlayerLoopHelper.AddAction(timing, result); @@ -540,6 +289,8 @@ namespace UniRx.Async { try { + TaskTracker2.RemoveTracking(this); + return core.GetResult(token); } finally @@ -572,8 +323,6 @@ namespace UniRx.Async { if (cancellationToken.IsCancellationRequested) { - // TODO:Remove Tracking - // TaskTracker.RemoveTracking(); core.TrySetCanceled(cancellationToken); return false; } @@ -585,8 +334,6 @@ namespace UniRx.Async if (asyncOperation.isDone) { - // TODO:Remove Tracking - // TaskTracker.RemoveTracking(); core.TrySetResult(asyncOperation.asset); return false; } @@ -601,55 +348,57 @@ namespace UniRx.Async progress = default; cancellationToken = default; } + + ~ResourceRequestConfiguredSource() + { + if (pool.TryReturn(this)) + { + GC.ReRegisterForFinalize(this); + } + } } +# endregion +#region AssetBundleRequest + public static AssetBundleRequestAwaiter GetAwaiter(this AssetBundleRequest asyncOperation) + { + Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); + return new AssetBundleRequestAwaiter(asyncOperation); + } + public static UniTask ToUniTask(this AssetBundleRequest asyncOperation) + { + Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); + return new UniTask(AssetBundleRequestConfiguredSource.Create(asyncOperation, PlayerLoopTiming.Update, null, CancellationToken.None, out var token), token); + } + public static UniTask ConfigureAwait(this AssetBundleRequest asyncOperation, IProgress progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellation = default(CancellationToken)) + { + Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); + return new UniTask(AssetBundleRequestConfiguredSource.Create(asyncOperation, timing, progress, cancellation, out var token), token); + } - - - - public struct AssetBundleRequestAwaiter : IAwaiter + public struct AssetBundleRequestAwaiter : ICriticalNotifyCompletion { AssetBundleRequest asyncOperation; Action continuationAction; - UniTaskStatus status; UnityEngine.Object result; public AssetBundleRequestAwaiter(AssetBundleRequest asyncOperation) { - this.status = asyncOperation.isDone ? UniTaskStatus.Succeeded : UniTaskStatus.Pending; - this.asyncOperation = (this.status.IsCompleted()) ? null : asyncOperation; - this.result = (this.status.IsCompletedSuccessfully()) ? asyncOperation.asset : null; + this.asyncOperation = asyncOperation; this.continuationAction = null; + this.result = default; } - public bool IsCompleted => status.IsCompleted(); - public UniTaskStatus Status => status; + public bool IsCompleted => asyncOperation.isDone; public UnityEngine.Object GetResult() { - if (status == UniTaskStatus.Succeeded) return this.result; - - if (status == UniTaskStatus.Pending) - { - // first timing of call - if (asyncOperation.isDone) - { - status = UniTaskStatus.Succeeded; - } - else - { - Error.ThrowNotYetCompleted(); - } - } - - this.result = asyncOperation.asset; - if (continuationAction != null) { asyncOperation.completed -= continuationAction; @@ -664,8 +413,6 @@ namespace UniRx.Async return this.result; } - void IAwaiter.GetResult() => GetResult(); - public void OnCompleted(Action continuation) { UnsafeOnCompleted(continuation); @@ -674,59 +421,86 @@ namespace UniRx.Async public void UnsafeOnCompleted(Action continuation) { Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction); - continuationAction = continuation.AsFuncOfT(); + continuationAction = continuation.AsFuncOfT(); // allocate delegate. asyncOperation.completed += continuationAction; } } - class AssetBundleRequestConfiguredAwaiter : IAwaiter, IPlayerLoopItem + class AssetBundleRequestConfiguredSource : IUniTaskSource, IPlayerLoopItem, IPromisePoolItem { + static readonly PromisePool pool = new PromisePool(); + AssetBundleRequest asyncOperation; IProgress progress; CancellationToken cancellationToken; - UniTaskStatus status; - Action continuation; - UnityEngine.Object result; - public AssetBundleRequestConfiguredAwaiter(AssetBundleRequest asyncOperation, IProgress progress, CancellationToken cancellationToken) + UniTaskCompletionSourceCore core; + + AssetBundleRequestConfiguredSource() { - this.status = cancellationToken.IsCancellationRequested ? UniTaskStatus.Canceled - : asyncOperation.isDone ? UniTaskStatus.Succeeded - : UniTaskStatus.Pending; - if (this.status.IsCompletedSuccessfully()) this.result = asyncOperation.asset; - if (this.status.IsCompleted()) return; - - this.asyncOperation = asyncOperation; - this.progress = progress; - this.cancellationToken = cancellationToken; - this.continuation = null; - this.result = null; - - TaskTracker.TrackActiveTask(this, 2); } - public bool IsCompleted => status.IsCompleted(); - public UniTaskStatus Status => status; - void IAwaiter.GetResult() => GetResult(); - - public UnityEngine.Object GetResult() + public static IUniTaskSource Create(AssetBundleRequest asyncOperation, PlayerLoopTiming timing, IProgress progress, CancellationToken cancellationToken, out short token) { - if (status == UniTaskStatus.Succeeded) return this.result; - - if (status == UniTaskStatus.Canceled) + if (cancellationToken.IsCancellationRequested) { - Error.ThrowOperationCanceledException(); + return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token); } - return Error.ThrowNotYetCompleted(); + var result = pool.TryRent() ?? new AssetBundleRequestConfiguredSource(); + + result.asyncOperation = asyncOperation; + result.progress = progress; + result.cancellationToken = cancellationToken; + + TaskTracker2.TrackActiveTask(result, 3); + + PlayerLoopHelper.AddAction(timing, result); + + token = result.core.Version; + return result; + } + + public UnityEngine.Object GetResult(short token) + { + try + { + TaskTracker2.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) { - InvokeContinuation(UniTaskStatus.Canceled); + core.TrySetCanceled(cancellationToken); return false; } @@ -737,79 +511,71 @@ namespace UniRx.Async if (asyncOperation.isDone) { - this.result = asyncOperation.asset; - InvokeContinuation(UniTaskStatus.Succeeded); + core.TrySetResult(asyncOperation.asset); return false; } return true; } - void InvokeContinuation(UniTaskStatus status) + public void Reset() { - this.status = status; - var cont = this.continuation; - - // cleanup - TaskTracker.RemoveTracking(this); - this.continuation = null; - this.cancellationToken = CancellationToken.None; - this.progress = null; - this.asyncOperation = null; - - if (cont != null) cont.Invoke(); + core.Reset(); + asyncOperation = default; + progress = default; + cancellationToken = default; } - public void OnCompleted(Action continuation) + ~AssetBundleRequestConfiguredSource() { - Error.ThrowWhenContinuationIsAlreadyRegistered(this.continuation); - this.continuation = continuation; - } - - public void UnsafeOnCompleted(Action continuation) - { - Error.ThrowWhenContinuationIsAlreadyRegistered(this.continuation); - this.continuation = continuation; + if (pool.TryReturn(this)) + { + GC.ReRegisterForFinalize(this); + } } } - public struct AssetBundleCreateRequestAwaiter : IAwaiter +# endregion + +#region AssetBundleCreateRequest + + public static AssetBundleCreateRequestAwaiter GetAwaiter(this AssetBundleCreateRequest asyncOperation) + { + Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); + return new AssetBundleCreateRequestAwaiter(asyncOperation); + } + + public static UniTask ToUniTask(this AssetBundleCreateRequest asyncOperation) + { + Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); + + return new UniTask(AssetBundleCreateRequestConfiguredSource.Create(asyncOperation, PlayerLoopTiming.Update, null, CancellationToken.None, out var token), token); + } + + public static UniTask ConfigureAwait(this AssetBundleCreateRequest asyncOperation, IProgress progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellation = default(CancellationToken)) + { + Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); + + return new UniTask(AssetBundleCreateRequestConfiguredSource.Create(asyncOperation, timing, progress, cancellation, out var token), token); + } + + public struct AssetBundleCreateRequestAwaiter : ICriticalNotifyCompletion { AssetBundleCreateRequest asyncOperation; Action continuationAction; - UniTaskStatus status; AssetBundle result; public AssetBundleCreateRequestAwaiter(AssetBundleCreateRequest asyncOperation) { - this.status = asyncOperation.isDone ? UniTaskStatus.Succeeded : UniTaskStatus.Pending; - this.asyncOperation = (this.status.IsCompleted()) ? null : asyncOperation; - this.result = (this.status.IsCompletedSuccessfully()) ? asyncOperation.assetBundle : null; + this.asyncOperation = asyncOperation; this.continuationAction = null; + this.result = default; } - public bool IsCompleted => status.IsCompleted(); - public UniTaskStatus Status => status; + public bool IsCompleted => asyncOperation.isDone; public AssetBundle GetResult() { - if (status == UniTaskStatus.Succeeded) return this.result; - - if (status == UniTaskStatus.Pending) - { - // first timing of call - if (asyncOperation.isDone) - { - status = UniTaskStatus.Succeeded; - } - else - { - Error.ThrowNotYetCompleted(); - } - } - - this.result = asyncOperation.assetBundle; - if (continuationAction != null) { asyncOperation.completed -= continuationAction; @@ -824,8 +590,6 @@ namespace UniRx.Async return this.result; } - void IAwaiter.GetResult() => GetResult(); - public void OnCompleted(Action continuation) { UnsafeOnCompleted(continuation); @@ -834,59 +598,86 @@ namespace UniRx.Async public void UnsafeOnCompleted(Action continuation) { Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction); - continuationAction = continuation.AsFuncOfT(); + continuationAction = continuation.AsFuncOfT(); // allocate delegate. asyncOperation.completed += continuationAction; } } - class AssetBundleCreateRequestConfiguredAwaiter : IAwaiter, IPlayerLoopItem + class AssetBundleCreateRequestConfiguredSource : IUniTaskSource, IPlayerLoopItem, IPromisePoolItem { + static readonly PromisePool pool = new PromisePool(); + AssetBundleCreateRequest asyncOperation; IProgress progress; CancellationToken cancellationToken; - UniTaskStatus status; - Action continuation; - AssetBundle result; - public AssetBundleCreateRequestConfiguredAwaiter(AssetBundleCreateRequest asyncOperation, IProgress progress, CancellationToken cancellationToken) + UniTaskCompletionSourceCore core; + + AssetBundleCreateRequestConfiguredSource() { - this.status = cancellationToken.IsCancellationRequested ? UniTaskStatus.Canceled - : asyncOperation.isDone ? UniTaskStatus.Succeeded - : UniTaskStatus.Pending; - if (this.status.IsCompletedSuccessfully()) this.result = asyncOperation.assetBundle; - if (this.status.IsCompleted()) return; - - this.asyncOperation = asyncOperation; - this.progress = progress; - this.cancellationToken = cancellationToken; - this.continuation = null; - this.result = null; - - TaskTracker.TrackActiveTask(this, 2); } - public bool IsCompleted => status.IsCompleted(); - public UniTaskStatus Status => status; - void IAwaiter.GetResult() => GetResult(); - - public AssetBundle GetResult() + public static IUniTaskSource Create(AssetBundleCreateRequest asyncOperation, PlayerLoopTiming timing, IProgress progress, CancellationToken cancellationToken, out short token) { - if (status == UniTaskStatus.Succeeded) return this.result; - - if (status == UniTaskStatus.Canceled) + if (cancellationToken.IsCancellationRequested) { - Error.ThrowOperationCanceledException(); + return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token); } - return Error.ThrowNotYetCompleted(); + var result = pool.TryRent() ?? new AssetBundleCreateRequestConfiguredSource(); + + result.asyncOperation = asyncOperation; + result.progress = progress; + result.cancellationToken = cancellationToken; + + TaskTracker2.TrackActiveTask(result, 3); + + PlayerLoopHelper.AddAction(timing, result); + + token = result.core.Version; + return result; + } + + public AssetBundle GetResult(short token) + { + try + { + TaskTracker2.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) { - InvokeContinuation(UniTaskStatus.Canceled); + core.TrySetCanceled(cancellationToken); return false; } @@ -897,182 +688,72 @@ namespace UniRx.Async if (asyncOperation.isDone) { - this.result = asyncOperation.assetBundle; - InvokeContinuation(UniTaskStatus.Succeeded); + core.TrySetResult(asyncOperation.assetBundle); return false; } return true; } - void InvokeContinuation(UniTaskStatus status) + public void Reset() { - this.status = status; - var cont = this.continuation; - - // cleanup - TaskTracker.RemoveTracking(this); - this.continuation = null; - this.cancellationToken = CancellationToken.None; - this.progress = null; - this.asyncOperation = null; - - if (cont != null) cont.Invoke(); + core.Reset(); + asyncOperation = default; + progress = default; + cancellationToken = default; } - public void OnCompleted(Action continuation) + ~AssetBundleCreateRequestConfiguredSource() { - Error.ThrowWhenContinuationIsAlreadyRegistered(this.continuation); - this.continuation = continuation; - } - - public void UnsafeOnCompleted(Action continuation) - { - Error.ThrowWhenContinuationIsAlreadyRegistered(this.continuation); - this.continuation = continuation; - } - } -#if ENABLE_WWW - -#if UNITY_2018_3_OR_NEWER -#pragma warning disable CS0618 -#endif - - class WWWConfiguredAwaiter : IAwaiter, IPlayerLoopItem - { - WWW asyncOperation; - IProgress progress; - CancellationToken cancellationToken; - UniTaskStatus status; - Action continuation; - - public WWWConfiguredAwaiter(WWW asyncOperation, IProgress progress, CancellationToken cancellationToken) - { - this.status = cancellationToken.IsCancellationRequested ? UniTaskStatus.Canceled - : asyncOperation.isDone ? UniTaskStatus.Succeeded - : UniTaskStatus.Pending; - - if (this.status.IsCompleted()) return; - - this.asyncOperation = asyncOperation; - this.progress = progress; - this.cancellationToken = cancellationToken; - this.continuation = null; - - TaskTracker.TrackActiveTask(this, 2); - } - - public bool IsCompleted => status.IsCompleted(); - public UniTaskStatus Status => status; - - public void GetResult() - { - if (status == UniTaskStatus.Succeeded) + if (pool.TryReturn(this)) { - return; + GC.ReRegisterForFinalize(this); } - else if (status == UniTaskStatus.Canceled) - { - Error.ThrowOperationCanceledException(); - } - - Error.ThrowNotYetCompleted(); - } - - public bool MoveNext() - { - if (cancellationToken.IsCancellationRequested) - { - InvokeContinuation(UniTaskStatus.Canceled); - return false; - } - - if (progress != null) - { - progress.Report(asyncOperation.progress); - } - - if (asyncOperation.isDone) - { - InvokeContinuation(UniTaskStatus.Succeeded); - return false; - } - - return true; - } - - void InvokeContinuation(UniTaskStatus status) - { - this.status = status; - var cont = this.continuation; - - // cleanup - TaskTracker.RemoveTracking(this); - this.continuation = null; - this.cancellationToken = CancellationToken.None; - this.progress = null; - this.asyncOperation = null; - - if (cont != null) cont.Invoke(); - } - - public void OnCompleted(Action continuation) - { - UnsafeOnCompleted(continuation); - } - - public void UnsafeOnCompleted(Action continuation) - { - Error.ThrowWhenContinuationIsAlreadyRegistered(this.continuation); - this.continuation = continuation; } } -#if UNITY_2018_3_OR_NEWER -#pragma warning restore CS0618 -#endif - -#endif +# endregion #if ENABLE_UNITYWEBREQUEST +#region UnityWebRequestAsyncOperation - public struct UnityWebRequestAsyncOperationAwaiter : IAwaiter + public static UnityWebRequestAsyncOperationAwaiter GetAwaiter(this UnityWebRequestAsyncOperation asyncOperation) + { + Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); + return new UnityWebRequestAsyncOperationAwaiter(asyncOperation); + } + + public static UniTask ToUniTask(this UnityWebRequestAsyncOperation asyncOperation) + { + Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); + + return new UniTask(UnityWebRequestAsyncOperationConfiguredSource.Create(asyncOperation, PlayerLoopTiming.Update, null, CancellationToken.None, out var token), token); + } + + public static UniTask ConfigureAwait(this UnityWebRequestAsyncOperation asyncOperation, IProgress progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellation = default(CancellationToken)) + { + Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); + + return new UniTask(UnityWebRequestAsyncOperationConfiguredSource.Create(asyncOperation, timing, progress, cancellation, out var token), token); + } + + public struct UnityWebRequestAsyncOperationAwaiter : ICriticalNotifyCompletion { UnityWebRequestAsyncOperation asyncOperation; Action continuationAction; - UniTaskStatus status; UnityWebRequest result; public UnityWebRequestAsyncOperationAwaiter(UnityWebRequestAsyncOperation asyncOperation) { - this.status = asyncOperation.isDone ? UniTaskStatus.Succeeded : UniTaskStatus.Pending; - this.asyncOperation = (this.status.IsCompleted()) ? null : asyncOperation; - this.result = (this.status.IsCompletedSuccessfully()) ? asyncOperation.webRequest : null; + this.asyncOperation = asyncOperation; this.continuationAction = null; + this.result = default; } - public bool IsCompleted => status.IsCompleted(); - public UniTaskStatus Status => status; + public bool IsCompleted => asyncOperation.isDone; public UnityWebRequest GetResult() { - if (status == UniTaskStatus.Succeeded) return this.result; - - if (status == UniTaskStatus.Pending) - { - // first timing of call - if (asyncOperation.isDone) - { - status = UniTaskStatus.Succeeded; - } - else - { - Error.ThrowNotYetCompleted(); - } - } - - this.result = asyncOperation.webRequest; - if (continuationAction != null) { asyncOperation.completed -= continuationAction; @@ -1084,12 +765,9 @@ namespace UniRx.Async asyncOperation = null; // remove reference. } - return this.result; } - void IAwaiter.GetResult() => GetResult(); - public void OnCompleted(Action continuation) { UnsafeOnCompleted(continuation); @@ -1098,59 +776,86 @@ namespace UniRx.Async public void UnsafeOnCompleted(Action continuation) { Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction); - continuationAction = continuation.AsFuncOfT(); + continuationAction = continuation.AsFuncOfT(); // allocate delegate. asyncOperation.completed += continuationAction; } } - class UnityWebRequestAsyncOperationConfiguredAwaiter : IAwaiter, IPlayerLoopItem + class UnityWebRequestAsyncOperationConfiguredSource : IUniTaskSource, IPlayerLoopItem, IPromisePoolItem { + static readonly PromisePool pool = new PromisePool(); + UnityWebRequestAsyncOperation asyncOperation; IProgress progress; CancellationToken cancellationToken; - UniTaskStatus status; - Action continuation; - UnityWebRequest result; - public UnityWebRequestAsyncOperationConfiguredAwaiter(UnityWebRequestAsyncOperation asyncOperation, IProgress progress, CancellationToken cancellationToken) + UniTaskCompletionSourceCore core; + + UnityWebRequestAsyncOperationConfiguredSource() { - this.status = cancellationToken.IsCancellationRequested ? UniTaskStatus.Canceled - : asyncOperation.isDone ? UniTaskStatus.Succeeded - : UniTaskStatus.Pending; - if (this.status.IsCompletedSuccessfully()) this.result = asyncOperation.webRequest; - if (this.status.IsCompleted()) return; - - this.asyncOperation = asyncOperation; - this.progress = progress; - this.cancellationToken = cancellationToken; - this.continuation = null; - this.result = null; - - TaskTracker.TrackActiveTask(this, 2); } - public bool IsCompleted => status.IsCompleted(); - public UniTaskStatus Status => status; - void IAwaiter.GetResult() => GetResult(); - - public UnityWebRequest GetResult() + public static IUniTaskSource Create(UnityWebRequestAsyncOperation asyncOperation, PlayerLoopTiming timing, IProgress progress, CancellationToken cancellationToken, out short token) { - if (status == UniTaskStatus.Succeeded) return this.result; - - if (status == UniTaskStatus.Canceled) + if (cancellationToken.IsCancellationRequested) { - Error.ThrowOperationCanceledException(); + return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token); } - return Error.ThrowNotYetCompleted(); + var result = pool.TryRent() ?? new UnityWebRequestAsyncOperationConfiguredSource(); + + result.asyncOperation = asyncOperation; + result.progress = progress; + result.cancellationToken = cancellationToken; + + TaskTracker2.TrackActiveTask(result, 3); + + PlayerLoopHelper.AddAction(timing, result); + + token = result.core.Version; + return result; + } + + public UnityWebRequest GetResult(short token) + { + try + { + TaskTracker2.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) { - InvokeContinuation(UniTaskStatus.Canceled); + core.TrySetCanceled(cancellationToken); return false; } @@ -1161,43 +866,32 @@ namespace UniRx.Async if (asyncOperation.isDone) { - this.result = asyncOperation.webRequest; - InvokeContinuation(UniTaskStatus.Succeeded); + core.TrySetResult(asyncOperation.webRequest); return false; } return true; } - void InvokeContinuation(UniTaskStatus status) + public void Reset() { - this.status = status; - var cont = this.continuation; - - // cleanup - TaskTracker.RemoveTracking(this); - this.continuation = null; - this.cancellationToken = CancellationToken.None; - this.progress = null; - this.asyncOperation = null; - - if (cont != null) cont.Invoke(); + core.Reset(); + asyncOperation = default; + progress = default; + cancellationToken = default; } - public void OnCompleted(Action continuation) + ~UnityWebRequestAsyncOperationConfiguredSource() { - Error.ThrowWhenContinuationIsAlreadyRegistered(this.continuation); - this.continuation = continuation; - } - - public void UnsafeOnCompleted(Action continuation) - { - Error.ThrowWhenContinuationIsAlreadyRegistered(this.continuation); - this.continuation = continuation; + if (pool.TryReturn(this)) + { + GC.ReRegisterForFinalize(this); + } } } +# endregion #endif + } -} -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/Assets/UniRx.Async/UnityAsyncExtensions.tt b/Assets/UniRx.Async/UnityAsyncExtensions.tt new file mode 100644 index 0000000..e4d6e27 --- /dev/null +++ b/Assets/UniRx.Async/UnityAsyncExtensions.tt @@ -0,0 +1,234 @@ +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ assembly name="System.Core" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Text" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ output extension=".cs" #> +<# + var types = new (string typeName, string returnType, string returnField)[] + { + ("AsyncOperation", "void", null), + ("ResourceRequest", "UnityEngine.Object", "asset"), + ("AssetBundleRequest", "UnityEngine.Object", "asset"), // allAssets? + ("AssetBundleCreateRequest", "AssetBundle", "assetBundle"), + ("UnityWebRequestAsyncOperation", "UnityWebRequest", "webRequest") // -> #if ENABLE_UNITYWEBREQUEST + }; + + Func ToUniTaskReturnType = x => (x == "void") ? "UniTask" : $"UniTask<{x}>"; + Func ToIUniTaskSourceReturnType = x => (x == "void") ? "IUniTaskSource" : $"IUniTaskSource<{x}>"; + Func<(string typeName, string returnType, string returnField), bool> IsVoid = x => x.returnType == "void"; +#> +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member + +using System; +using System.Runtime.CompilerServices; +using System.Threading; +using UnityEngine; +using UniRx.Async.Internal; +#if ENABLE_UNITYWEBREQUEST +using UnityEngine.Networking; +#endif + +namespace UniRx.Async +{ + public static partial class UnityAsyncExtensions + { +<# foreach(var t in types) { #> +<# if(t.returnType == "UnityWebRequest") { #> +#if ENABLE_UNITYWEBREQUEST +<# } #> +#region <#= t.typeName #> + + public static <#= t.typeName #>Awaiter GetAwaiter(this <#= t.typeName #> asyncOperation) + { + Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); + return new <#= t.typeName #>Awaiter(asyncOperation); + } + + public static <#= ToUniTaskReturnType(t.returnType) #> ToUniTask(this <#= t.typeName #> asyncOperation) + { + Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); + + return new <#= ToUniTaskReturnType(t.returnType) #>(<#= t.typeName #>ConfiguredSource.Create(asyncOperation, PlayerLoopTiming.Update, null, CancellationToken.None, out var token), token); + } + + public static <#= ToUniTaskReturnType(t.returnType) #> ConfigureAwait(this <#= t.typeName #> asyncOperation, IProgress progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellation = default(CancellationToken)) + { + Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); + + return new <#= ToUniTaskReturnType(t.returnType) #>(<#= t.typeName #>ConfiguredSource.Create(asyncOperation, timing, progress, cancellation, out var token), token); + } + + public struct <#= t.typeName #>Awaiter : ICriticalNotifyCompletion + { + <#= t.typeName #> asyncOperation; + Action continuationAction; +<# if (!IsVoid(t)) { #> + <#= t.returnType #> result; +<# } #> + + public <#= t.typeName #>Awaiter(<#= t.typeName #> asyncOperation) + { + this.asyncOperation = asyncOperation; + this.continuationAction = null; +<# if (!IsVoid(t)) { #> + this.result = default; +<# } #> + } + + public bool IsCompleted => asyncOperation.isDone; + + public <#= t.returnType #> GetResult() + { + if (continuationAction != null) + { + asyncOperation.completed -= continuationAction; + asyncOperation = null; // remove reference. + continuationAction = null; + } + else + { + asyncOperation = null; // remove reference. + } + +<# if (!IsVoid(t)) { #> + return this.result; +<# } #> + } + + public void OnCompleted(Action continuation) + { + UnsafeOnCompleted(continuation); + } + + public void UnsafeOnCompleted(Action continuation) + { + Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction); + continuationAction = continuation.AsFuncOfT(); // allocate delegate. + asyncOperation.completed += continuationAction; + } + } + + class <#= t.typeName #>ConfiguredSource : <#= ToIUniTaskSourceReturnType(t.returnType) #>, IPlayerLoopItem, IPromisePoolItem + { + static readonly PromisePool<<#= t.typeName #>ConfiguredSource> pool = new PromisePool<<#= t.typeName #>ConfiguredSource>(); + + <#= t.typeName #> asyncOperation; + IProgress progress; + CancellationToken cancellationToken; + + UniTaskCompletionSourceCore<<#= IsVoid(t) ? "AsyncUnit" : t.returnType #>> core; + + <#= t.typeName #>ConfiguredSource() + { + + } + + public static <#= ToIUniTaskSourceReturnType(t.returnType) #> Create(<#= t.typeName #> asyncOperation, PlayerLoopTiming timing, IProgress progress, CancellationToken cancellationToken, out short token) + { + if (cancellationToken.IsCancellationRequested) + { + return AutoResetUniTaskCompletionSource<#= IsVoid(t) ? "" : $"<{t.returnType}>" #>.CreateFromCanceled(cancellationToken, out token); + } + + var result = pool.TryRent() ?? new <#= t.typeName #>ConfiguredSource(); + + result.asyncOperation = asyncOperation; + result.progress = progress; + result.cancellationToken = cancellationToken; + + TaskTracker2.TrackActiveTask(result, 3); + + PlayerLoopHelper.AddAction(timing, result); + + token = result.core.Version; + return result; + } + + public <#= t.returnType #> GetResult(short token) + { + try + { + TaskTracker2.RemoveTracking(this); + +<# if (!IsVoid(t)) { #> + return core.GetResult(token); +<# } else { #> + core.GetResult(token); +<# } #> + } + finally + { + pool.TryReturn(this); + } + } + +<# if (!IsVoid(t)) { #> + 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) + { + core.TrySetCanceled(cancellationToken); + return false; + } + + if (progress != null) + { + progress.Report(asyncOperation.progress); + } + + if (asyncOperation.isDone) + { + core.TrySetResult(<#= IsVoid(t) ? "AsyncUnit.Default" : $"asyncOperation.{t.returnField}" #>); + return false; + } + + return true; + } + + public void Reset() + { + core.Reset(); + asyncOperation = default; + progress = default; + cancellationToken = default; + } + + ~<#= t.typeName #>ConfiguredSource() + { + if (pool.TryReturn(this)) + { + GC.ReRegisterForFinalize(this); + } + } + } + +# endregion +<# if(t.returnType == "UnityWebRequest") { #> +#endif +<# } #> + +<# } #> + } +} \ No newline at end of file diff --git a/Assets/UniRx.Async/UnityAsyncExtensions.uGUI.cs b/Assets/UniRx.Async/UnityAsyncExtensions.uGUI.cs index 45d9da9..09658f7 100644 --- a/Assets/UniRx.Async/UnityAsyncExtensions.uGUI.cs +++ b/Assets/UniRx.Async/UnityAsyncExtensions.uGUI.cs @@ -1,14 +1,11 @@ -#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 +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member using System; -using System.Runtime.CompilerServices; using System.Threading; using UniRx.Async.Internal; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; -using UniRx.Async.Triggers; namespace UniRx.Async { @@ -168,86 +165,72 @@ namespace UniRx.Async public interface IAsyncClickEventHandler : IDisposable { UniTask OnClickAsync(); - UniTask OnClickAsyncSuppressCancellationThrow(); } public interface IAsyncValueChangedEventHandler : IDisposable { UniTask OnValueChangedAsync(); - UniTask<(bool IsCanceled, T Result)> OnValueChangedAsyncSuppressCancellationThrow(); } public interface IAsyncEndEditEventHandler : IDisposable { UniTask OnEndEditAsync(); - UniTask<(bool IsCanceled, T Result)> OnEndEditAsyncSuppressCancellationThrow(); } - // event handler is reusable when callOnce = false. - public class AsyncUnityEventHandler : IAwaiter, IDisposable, IAsyncClickEventHandler + public class AsyncUnityEventHandler : IUniTaskSource, IDisposable, IAsyncClickEventHandler { static Action cancellationCallback = CancellationCallback; readonly UnityAction action; readonly UnityEvent unityEvent; - Action continuation; + + CancellationToken cancellationToken; CancellationTokenRegistration registration; bool isDisposed; bool callOnce; - UniTask? suppressCancellationThrowTask; + + UniTaskCompletionSourceCore core; public AsyncUnityEventHandler(UnityEvent unityEvent, CancellationToken cancellationToken, bool callOnce) { - this.callOnce = callOnce; - if (cancellationToken.IsCancellationRequested) { isDisposed = true; return; } - action = Invoke; - unityEvent.AddListener(action); + this.action = Invoke; this.unityEvent = unityEvent; + this.cancellationToken = cancellationToken; + this.callOnce = callOnce; + + unityEvent.AddListener(action); if (cancellationToken.CanBeCanceled) { registration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, this); } - TaskTracker.TrackActiveTask(this, 3); + TaskTracker2.TrackActiveTask(this, 3); } public UniTask OnInvokeAsync() { - // zero allocation wait handler. - return new UniTask(this); - } - - public UniTask OnInvokeAsyncSuppressCancellationThrow() - { - if (suppressCancellationThrowTask == null) - { - suppressCancellationThrowTask = OnInvokeAsync().SuppressCancellationThrow(); - } - return suppressCancellationThrowTask.Value; + core.Reset(); + return new UniTask(this, core.Version); } void Invoke() { - var c = continuation; - continuation = null; - if (c != null) - { - c.Invoke(); - } + core.TrySetResult(AsyncUnit.Default); } static void CancellationCallback(object state) { var self = (AsyncUnityEventHandler)state; self.Dispose(); - self.Invoke(); // call continuation if exists yet(GetResult -> throw OperationCanceledException). + + self.core.TrySetCanceled(self.cancellationToken); } public void Dispose() @@ -255,7 +238,7 @@ namespace UniRx.Async if (!isDisposed) { isDisposed = true; - TaskTracker.RemoveTracking(this); + TaskTracker2.RemoveTracking(this); registration.Dispose(); if (unityEvent != null) { @@ -264,106 +247,96 @@ namespace UniRx.Async } } - bool IAwaiter.IsCompleted => isDisposed ? true : false; - UniTaskStatus IAwaiter.Status => isDisposed ? UniTaskStatus.Canceled : UniTaskStatus.Pending; - void IAwaiter.GetResult() - { - if (isDisposed) throw new OperationCanceledException(); - if (callOnce) Dispose(); - } - - void INotifyCompletion.OnCompleted(Action action) - { - ((ICriticalNotifyCompletion)this).UnsafeOnCompleted(action); - } - - void ICriticalNotifyCompletion.UnsafeOnCompleted(Action action) - { - Error.ThrowWhenContinuationIsAlreadyRegistered(this.continuation); - this.continuation = action; - } - - // Interface events. - UniTask IAsyncClickEventHandler.OnClickAsync() { return OnInvokeAsync(); } - UniTask IAsyncClickEventHandler.OnClickAsyncSuppressCancellationThrow() + void IUniTaskSource.GetResult(short token) { - return OnInvokeAsyncSuppressCancellationThrow(); + try + { + core.GetResult(token); + } + finally + { + if (callOnce) + { + Dispose(); + } + } + } + + 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); } } - // event handler is reusable when callOnce = false. - public class AsyncUnityEventHandler : IAwaiter, IDisposable, IAsyncValueChangedEventHandler, IAsyncEndEditEventHandler + public class AsyncUnityEventHandler : IUniTaskSource, IDisposable, IAsyncValueChangedEventHandler, IAsyncEndEditEventHandler { static Action cancellationCallback = CancellationCallback; readonly UnityAction action; readonly UnityEvent unityEvent; - Action continuation; + + CancellationToken cancellationToken; CancellationTokenRegistration registration; bool isDisposed; - T eventValue; bool callOnce; - UniTask<(bool, T)>? suppressCancellationThrowTask; + + UniTaskCompletionSourceCore core; public AsyncUnityEventHandler(UnityEvent unityEvent, CancellationToken cancellationToken, bool callOnce) { - this.callOnce = callOnce; - if (cancellationToken.IsCancellationRequested) { isDisposed = true; return; } - action = Invoke; - unityEvent.AddListener(action); + this.action = Invoke; this.unityEvent = unityEvent; + this.cancellationToken = cancellationToken; + this.callOnce = callOnce; + + unityEvent.AddListener(action); if (cancellationToken.CanBeCanceled) { registration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, this); } - TaskTracker.TrackActiveTask(this, 3); + TaskTracker2.TrackActiveTask(this, 3); } public UniTask OnInvokeAsync() { - // zero allocation wait handler. - return new UniTask(this); + core.Reset(); + return new UniTask(this, core.Version); } - public UniTask<(bool IsCanceled, T Result)> OnInvokeAsyncSuppressCancellationThrow() + void Invoke(T result) { - if (suppressCancellationThrowTask == null) - { - suppressCancellationThrowTask = OnInvokeAsync().SuppressCancellationThrow(); - } - return suppressCancellationThrowTask.Value; - } - - void Invoke(T value) - { - this.eventValue = value; - - var c = continuation; - continuation = null; - if (c != null) - { - c.Invoke(); - } + core.TrySetResult(result); } static void CancellationCallback(object state) { var self = (AsyncUnityEventHandler)state; self.Dispose(); - self.Invoke(default(T)); // call continuation if exists yet(GetResult -> throw OperationCanceledException). + + self.core.TrySetCanceled(self.cancellationToken); } public void Dispose() @@ -371,7 +344,7 @@ namespace UniRx.Async if (!isDisposed) { isDisposed = true; - TaskTracker.RemoveTracking(this); + TaskTracker2.RemoveTracking(this); registration.Dispose(); if (unityEvent != null) { @@ -380,55 +353,49 @@ namespace UniRx.Async } } - bool IAwaiter.IsCompleted => isDisposed ? true : false; - UniTaskStatus IAwaiter.Status => isDisposed ? UniTaskStatus.Canceled : UniTaskStatus.Pending; - - T IAwaiter.GetResult() - { - if (isDisposed) throw new OperationCanceledException(); - if (callOnce) Dispose(); - return eventValue; - } - - void IAwaiter.GetResult() - { - if (isDisposed) throw new OperationCanceledException(); - if (callOnce) Dispose(); - } - - void INotifyCompletion.OnCompleted(Action action) - { - ((ICriticalNotifyCompletion)this).UnsafeOnCompleted(action); - } - - void ICriticalNotifyCompletion.UnsafeOnCompleted(Action action) - { - Error.ThrowWhenContinuationIsAlreadyRegistered(this.continuation); - this.continuation = action; - } - - // Interface events. - UniTask IAsyncValueChangedEventHandler.OnValueChangedAsync() { return OnInvokeAsync(); } - UniTask<(bool IsCanceled, T Result)> IAsyncValueChangedEventHandler.OnValueChangedAsyncSuppressCancellationThrow() - { - return OnInvokeAsyncSuppressCancellationThrow(); - } - UniTask IAsyncEndEditEventHandler.OnEndEditAsync() { return OnInvokeAsync(); } - UniTask<(bool IsCanceled, T Result)> IAsyncEndEditEventHandler.OnEndEditAsyncSuppressCancellationThrow() + T IUniTaskSource.GetResult(short token) { - return OnInvokeAsyncSuppressCancellationThrow(); + try + { + return core.GetResult(token); + } + finally + { + if (callOnce) + { + Dispose(); + } + } + } + + 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); } } -} - -#endif \ No newline at end of file +} \ No newline at end of file