#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 Cysharp.Threading.Tasks.Internal; #if ENABLE_UNITYWEBREQUEST && (!UNITY_2019_1_OR_NEWER || UNITASK_WEBREQUEST_SUPPORT) using UnityEngine.Networking; #endif namespace Cysharp.Threading.Tasks { public static partial class UnityAsyncExtensions { #region AsyncOperation #if !UNITY_2023_1_OR_NEWER // from Unity2023.1.0a15, AsyncOperationAwaitableExtensions.GetAwaiter is defined in UnityEngine. public static AsyncOperationAwaiter GetAwaiter(this AsyncOperation asyncOperation) { Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); return new AsyncOperationAwaiter(asyncOperation); } #endif public static UniTask WithCancellation(this AsyncOperation asyncOperation, CancellationToken cancellationToken) { return ToUniTask(asyncOperation, cancellationToken: cancellationToken); } public static UniTask ToUniTask(this AsyncOperation asyncOperation, IProgress progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) { Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled(cancellationToken); if (asyncOperation.isDone) return UniTask.CompletedTask; return new UniTask(AsyncOperationConfiguredSource.Create(asyncOperation, timing, progress, cancellationToken, out var token), token); } public struct AsyncOperationAwaiter : ICriticalNotifyCompletion { AsyncOperation asyncOperation; Action continuationAction; public AsyncOperationAwaiter(AsyncOperation asyncOperation) { this.asyncOperation = asyncOperation; this.continuationAction = null; } public bool IsCompleted => asyncOperation.isDone; public void GetResult() { if (continuationAction != null) { asyncOperation.completed -= continuationAction; continuationAction = null; asyncOperation = null; } else { asyncOperation = null; } } public void OnCompleted(Action continuation) { UnsafeOnCompleted(continuation); } public void UnsafeOnCompleted(Action continuation) { Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction); continuationAction = PooledDelegate.Create(continuation); asyncOperation.completed += continuationAction; } } sealed class AsyncOperationConfiguredSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode { static TaskPool pool; AsyncOperationConfiguredSource nextNode; public ref AsyncOperationConfiguredSource NextNode => ref nextNode; static AsyncOperationConfiguredSource() { TaskPool.RegisterSizeGetter(typeof(AsyncOperationConfiguredSource), () => pool.Size); } AsyncOperation asyncOperation; IProgress progress; CancellationToken cancellationToken; UniTaskCompletionSourceCore core; AsyncOperationConfiguredSource() { } public static IUniTaskSource Create(AsyncOperation asyncOperation, PlayerLoopTiming timing, IProgress progress, CancellationToken cancellationToken, out short token) { if (cancellationToken.IsCancellationRequested) { return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token); } if (!pool.TryPop(out var result)) { result = new AsyncOperationConfiguredSource(); } result.asyncOperation = asyncOperation; result.progress = progress; result.cancellationToken = cancellationToken; TaskTracker.TrackActiveTask(result, 3); PlayerLoopHelper.AddAction(timing, result); token = result.core.Version; return result; } public void GetResult(short token) { try { core.GetResult(token); } finally { TryReturn(); } } public UniTaskStatus GetStatus(short token) { return core.GetStatus(token); } public UniTaskStatus UnsafeGetStatus() { return core.UnsafeGetStatus(); } public void OnCompleted(Action continuation, object state, short token) { core.OnCompleted(continuation, state, token); } public bool MoveNext() { if (cancellationToken.IsCancellationRequested) { core.TrySetCanceled(cancellationToken); return false; } if (progress != null) { progress.Report(asyncOperation.progress); } if (asyncOperation.isDone) { core.TrySetResult(AsyncUnit.Default); return false; } return true; } bool TryReturn() { TaskTracker.RemoveTracking(this); core.Reset(); asyncOperation = default; progress = default; cancellationToken = default; return pool.TryPush(this); } } #endregion #region ResourceRequest public static ResourceRequestAwaiter GetAwaiter(this ResourceRequest asyncOperation) { Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); return new ResourceRequestAwaiter(asyncOperation); } public static UniTask WithCancellation(this ResourceRequest asyncOperation, CancellationToken cancellationToken) { return ToUniTask(asyncOperation, cancellationToken: cancellationToken); } public static UniTask ToUniTask(this ResourceRequest asyncOperation, IProgress progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) { Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled(cancellationToken); if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.asset); return new UniTask(ResourceRequestConfiguredSource.Create(asyncOperation, timing, progress, cancellationToken, out var token), token); } public struct ResourceRequestAwaiter : ICriticalNotifyCompletion { ResourceRequest asyncOperation; Action continuationAction; public ResourceRequestAwaiter(ResourceRequest asyncOperation) { this.asyncOperation = asyncOperation; this.continuationAction = null; } public bool IsCompleted => asyncOperation.isDone; public UnityEngine.Object GetResult() { if (continuationAction != null) { asyncOperation.completed -= continuationAction; continuationAction = null; var result = asyncOperation.asset; asyncOperation = null; return result; } else { var result = asyncOperation.asset; asyncOperation = null; return result; } } public void OnCompleted(Action continuation) { UnsafeOnCompleted(continuation); } public void UnsafeOnCompleted(Action continuation) { Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction); continuationAction = PooledDelegate.Create(continuation); asyncOperation.completed += continuationAction; } } sealed class ResourceRequestConfiguredSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode { static TaskPool pool; ResourceRequestConfiguredSource nextNode; public ref ResourceRequestConfiguredSource NextNode => ref nextNode; static ResourceRequestConfiguredSource() { TaskPool.RegisterSizeGetter(typeof(ResourceRequestConfiguredSource), () => pool.Size); } ResourceRequest asyncOperation; IProgress progress; CancellationToken cancellationToken; UniTaskCompletionSourceCore core; ResourceRequestConfiguredSource() { } public static IUniTaskSource Create(ResourceRequest asyncOperation, PlayerLoopTiming timing, IProgress progress, CancellationToken cancellationToken, out short token) { if (cancellationToken.IsCancellationRequested) { return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token); } if (!pool.TryPop(out var result)) { result = new ResourceRequestConfiguredSource(); } result.asyncOperation = asyncOperation; result.progress = progress; result.cancellationToken = cancellationToken; TaskTracker.TrackActiveTask(result, 3); PlayerLoopHelper.AddAction(timing, result); token = result.core.Version; return result; } public UnityEngine.Object GetResult(short token) { try { return core.GetResult(token); } finally { TryReturn(); } } void IUniTaskSource.GetResult(short token) { GetResult(token); } public UniTaskStatus GetStatus(short token) { return core.GetStatus(token); } public UniTaskStatus UnsafeGetStatus() { return core.UnsafeGetStatus(); } public void OnCompleted(Action continuation, object state, short token) { core.OnCompleted(continuation, state, token); } public bool MoveNext() { if (cancellationToken.IsCancellationRequested) { core.TrySetCanceled(cancellationToken); return false; } if (progress != null) { progress.Report(asyncOperation.progress); } if (asyncOperation.isDone) { core.TrySetResult(asyncOperation.asset); return false; } return true; } bool TryReturn() { TaskTracker.RemoveTracking(this); core.Reset(); asyncOperation = default; progress = default; cancellationToken = default; return pool.TryPush(this); } } #endregion #if UNITASK_ASSETBUNDLE_SUPPORT #region AssetBundleRequest public static AssetBundleRequestAwaiter GetAwaiter(this AssetBundleRequest asyncOperation) { Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); return new AssetBundleRequestAwaiter(asyncOperation); } public static UniTask WithCancellation(this AssetBundleRequest asyncOperation, CancellationToken cancellationToken) { return ToUniTask(asyncOperation, cancellationToken: cancellationToken); } public static UniTask ToUniTask(this AssetBundleRequest asyncOperation, IProgress progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) { Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled(cancellationToken); if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.asset); return new UniTask(AssetBundleRequestConfiguredSource.Create(asyncOperation, timing, progress, cancellationToken, out var token), token); } public struct AssetBundleRequestAwaiter : ICriticalNotifyCompletion { AssetBundleRequest asyncOperation; Action continuationAction; public AssetBundleRequestAwaiter(AssetBundleRequest asyncOperation) { this.asyncOperation = asyncOperation; this.continuationAction = null; } public bool IsCompleted => asyncOperation.isDone; public UnityEngine.Object GetResult() { if (continuationAction != null) { asyncOperation.completed -= continuationAction; continuationAction = null; var result = asyncOperation.asset; asyncOperation = null; return result; } else { var result = asyncOperation.asset; asyncOperation = null; return result; } } public void OnCompleted(Action continuation) { UnsafeOnCompleted(continuation); } public void UnsafeOnCompleted(Action continuation) { Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction); continuationAction = PooledDelegate.Create(continuation); asyncOperation.completed += continuationAction; } } sealed class AssetBundleRequestConfiguredSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode { static TaskPool pool; AssetBundleRequestConfiguredSource nextNode; public ref AssetBundleRequestConfiguredSource NextNode => ref nextNode; static AssetBundleRequestConfiguredSource() { TaskPool.RegisterSizeGetter(typeof(AssetBundleRequestConfiguredSource), () => pool.Size); } AssetBundleRequest asyncOperation; IProgress progress; CancellationToken cancellationToken; UniTaskCompletionSourceCore core; AssetBundleRequestConfiguredSource() { } public static IUniTaskSource Create(AssetBundleRequest asyncOperation, PlayerLoopTiming timing, IProgress progress, CancellationToken cancellationToken, out short token) { if (cancellationToken.IsCancellationRequested) { return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token); } if (!pool.TryPop(out var result)) { result = new AssetBundleRequestConfiguredSource(); } result.asyncOperation = asyncOperation; result.progress = progress; result.cancellationToken = cancellationToken; TaskTracker.TrackActiveTask(result, 3); PlayerLoopHelper.AddAction(timing, result); token = result.core.Version; return result; } public UnityEngine.Object GetResult(short token) { try { return core.GetResult(token); } finally { TryReturn(); } } void IUniTaskSource.GetResult(short token) { GetResult(token); } public UniTaskStatus GetStatus(short token) { return core.GetStatus(token); } public UniTaskStatus UnsafeGetStatus() { return core.UnsafeGetStatus(); } public void OnCompleted(Action continuation, object state, short token) { core.OnCompleted(continuation, state, token); } public bool MoveNext() { if (cancellationToken.IsCancellationRequested) { core.TrySetCanceled(cancellationToken); return false; } if (progress != null) { progress.Report(asyncOperation.progress); } if (asyncOperation.isDone) { core.TrySetResult(asyncOperation.asset); return false; } return true; } bool TryReturn() { TaskTracker.RemoveTracking(this); core.Reset(); asyncOperation = default; progress = default; cancellationToken = default; return pool.TryPush(this); } } #endregion #endif #if UNITASK_ASSETBUNDLE_SUPPORT #region AssetBundleCreateRequest public static AssetBundleCreateRequestAwaiter GetAwaiter(this AssetBundleCreateRequest asyncOperation) { Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); return new AssetBundleCreateRequestAwaiter(asyncOperation); } public static UniTask WithCancellation(this AssetBundleCreateRequest asyncOperation, CancellationToken cancellationToken) { return ToUniTask(asyncOperation, cancellationToken: cancellationToken); } public static UniTask ToUniTask(this AssetBundleCreateRequest asyncOperation, IProgress progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) { Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled(cancellationToken); if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.assetBundle); return new UniTask(AssetBundleCreateRequestConfiguredSource.Create(asyncOperation, timing, progress, cancellationToken, out var token), token); } public struct AssetBundleCreateRequestAwaiter : ICriticalNotifyCompletion { AssetBundleCreateRequest asyncOperation; Action continuationAction; public AssetBundleCreateRequestAwaiter(AssetBundleCreateRequest asyncOperation) { this.asyncOperation = asyncOperation; this.continuationAction = null; } public bool IsCompleted => asyncOperation.isDone; public AssetBundle GetResult() { if (continuationAction != null) { asyncOperation.completed -= continuationAction; continuationAction = null; var result = asyncOperation.assetBundle; asyncOperation = null; return result; } else { var result = asyncOperation.assetBundle; asyncOperation = null; return result; } } public void OnCompleted(Action continuation) { UnsafeOnCompleted(continuation); } public void UnsafeOnCompleted(Action continuation) { Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction); continuationAction = PooledDelegate.Create(continuation); asyncOperation.completed += continuationAction; } } sealed class AssetBundleCreateRequestConfiguredSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode { static TaskPool pool; AssetBundleCreateRequestConfiguredSource nextNode; public ref AssetBundleCreateRequestConfiguredSource NextNode => ref nextNode; static AssetBundleCreateRequestConfiguredSource() { TaskPool.RegisterSizeGetter(typeof(AssetBundleCreateRequestConfiguredSource), () => pool.Size); } AssetBundleCreateRequest asyncOperation; IProgress progress; CancellationToken cancellationToken; UniTaskCompletionSourceCore core; AssetBundleCreateRequestConfiguredSource() { } public static IUniTaskSource Create(AssetBundleCreateRequest asyncOperation, PlayerLoopTiming timing, IProgress progress, CancellationToken cancellationToken, out short token) { if (cancellationToken.IsCancellationRequested) { return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token); } if (!pool.TryPop(out var result)) { result = new AssetBundleCreateRequestConfiguredSource(); } result.asyncOperation = asyncOperation; result.progress = progress; result.cancellationToken = cancellationToken; TaskTracker.TrackActiveTask(result, 3); PlayerLoopHelper.AddAction(timing, result); token = result.core.Version; return result; } public AssetBundle GetResult(short token) { try { return core.GetResult(token); } finally { TryReturn(); } } void IUniTaskSource.GetResult(short token) { GetResult(token); } public UniTaskStatus GetStatus(short token) { return core.GetStatus(token); } public UniTaskStatus UnsafeGetStatus() { return core.UnsafeGetStatus(); } public void OnCompleted(Action continuation, object state, short token) { core.OnCompleted(continuation, state, token); } public bool MoveNext() { if (cancellationToken.IsCancellationRequested) { core.TrySetCanceled(cancellationToken); return false; } if (progress != null) { progress.Report(asyncOperation.progress); } if (asyncOperation.isDone) { core.TrySetResult(asyncOperation.assetBundle); return false; } return true; } bool TryReturn() { TaskTracker.RemoveTracking(this); core.Reset(); asyncOperation = default; progress = default; cancellationToken = default; return pool.TryPush(this); } } #endregion #endif #if ENABLE_UNITYWEBREQUEST && (!UNITY_2019_1_OR_NEWER || UNITASK_WEBREQUEST_SUPPORT) #region UnityWebRequestAsyncOperation public static UnityWebRequestAsyncOperationAwaiter GetAwaiter(this UnityWebRequestAsyncOperation asyncOperation) { Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); return new UnityWebRequestAsyncOperationAwaiter(asyncOperation); } public static UniTask WithCancellation(this UnityWebRequestAsyncOperation asyncOperation, CancellationToken cancellationToken) { return ToUniTask(asyncOperation, cancellationToken: cancellationToken); } public static UniTask ToUniTask(this UnityWebRequestAsyncOperation asyncOperation, IProgress progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) { Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled(cancellationToken); if (asyncOperation.isDone) { if (asyncOperation.webRequest.IsError()) { return UniTask.FromException(new UnityWebRequestException(asyncOperation.webRequest)); } return UniTask.FromResult(asyncOperation.webRequest); } return new UniTask(UnityWebRequestAsyncOperationConfiguredSource.Create(asyncOperation, timing, progress, cancellationToken, out var token), token); } public struct UnityWebRequestAsyncOperationAwaiter : ICriticalNotifyCompletion { UnityWebRequestAsyncOperation asyncOperation; Action continuationAction; public UnityWebRequestAsyncOperationAwaiter(UnityWebRequestAsyncOperation asyncOperation) { this.asyncOperation = asyncOperation; this.continuationAction = null; } public bool IsCompleted => asyncOperation.isDone; public UnityWebRequest GetResult() { if (continuationAction != null) { asyncOperation.completed -= continuationAction; continuationAction = null; var result = asyncOperation.webRequest; asyncOperation = null; if (result.IsError()) { throw new UnityWebRequestException(result); } return result; } else { var result = asyncOperation.webRequest; asyncOperation = null; if (result.IsError()) { throw new UnityWebRequestException(result); } return result; } } public void OnCompleted(Action continuation) { UnsafeOnCompleted(continuation); } public void UnsafeOnCompleted(Action continuation) { Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction); continuationAction = PooledDelegate.Create(continuation); asyncOperation.completed += continuationAction; } } sealed class UnityWebRequestAsyncOperationConfiguredSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode { static TaskPool pool; UnityWebRequestAsyncOperationConfiguredSource nextNode; public ref UnityWebRequestAsyncOperationConfiguredSource NextNode => ref nextNode; static UnityWebRequestAsyncOperationConfiguredSource() { TaskPool.RegisterSizeGetter(typeof(UnityWebRequestAsyncOperationConfiguredSource), () => pool.Size); } UnityWebRequestAsyncOperation asyncOperation; IProgress progress; CancellationToken cancellationToken; UniTaskCompletionSourceCore core; UnityWebRequestAsyncOperationConfiguredSource() { } public static IUniTaskSource Create(UnityWebRequestAsyncOperation asyncOperation, PlayerLoopTiming timing, IProgress progress, CancellationToken cancellationToken, out short token) { if (cancellationToken.IsCancellationRequested) { return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token); } if (!pool.TryPop(out var result)) { result = new UnityWebRequestAsyncOperationConfiguredSource(); } result.asyncOperation = asyncOperation; result.progress = progress; result.cancellationToken = cancellationToken; TaskTracker.TrackActiveTask(result, 3); PlayerLoopHelper.AddAction(timing, result); token = result.core.Version; return result; } public UnityWebRequest GetResult(short token) { try { return core.GetResult(token); } finally { TryReturn(); } } void IUniTaskSource.GetResult(short token) { GetResult(token); } public UniTaskStatus GetStatus(short token) { return core.GetStatus(token); } public UniTaskStatus UnsafeGetStatus() { return core.UnsafeGetStatus(); } public void OnCompleted(Action continuation, object state, short token) { core.OnCompleted(continuation, state, token); } public bool MoveNext() { if (cancellationToken.IsCancellationRequested) { asyncOperation.webRequest.Abort(); core.TrySetCanceled(cancellationToken); return false; } if (progress != null) { progress.Report(asyncOperation.progress); } if (asyncOperation.isDone) { if (asyncOperation.webRequest.IsError()) { core.TrySetException(new UnityWebRequestException(asyncOperation.webRequest)); } else { core.TrySetResult(asyncOperation.webRequest); } return false; } return true; } bool TryReturn() { TaskTracker.RemoveTracking(this); core.Reset(); asyncOperation = default; progress = default; cancellationToken = default; return pool.TryPush(this); } } #endregion #endif } }