#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member using System; using System.Collections; using System.Runtime.ExceptionServices; using System.Threading; using System.Threading.Tasks; using Cysharp.Threading.Tasks.Internal; namespace Cysharp.Threading.Tasks { public static partial class UniTaskExtensions { /// /// Convert Task[T] -> UniTask[T]. /// public static UniTask AsUniTask(this Task task, bool useCurrentSynchronizationContext = true) { var promise = new UniTaskCompletionSource(); task.ContinueWith((x, state) => { var p = (UniTaskCompletionSource)state; switch (x.Status) { case TaskStatus.Canceled: p.TrySetCanceled(); break; case TaskStatus.Faulted: p.TrySetException(x.Exception); break; case TaskStatus.RanToCompletion: p.TrySetResult(x.Result); break; default: throw new NotSupportedException(); } }, promise, useCurrentSynchronizationContext ? TaskScheduler.FromCurrentSynchronizationContext() : TaskScheduler.Current); return promise.Task; } /// /// Convert Task -> UniTask. /// public static UniTask AsUniTask(this Task task, bool useCurrentSynchronizationContext = true) { var promise = new UniTaskCompletionSource(); task.ContinueWith((x, state) => { var p = (UniTaskCompletionSource)state; switch (x.Status) { case TaskStatus.Canceled: p.TrySetCanceled(); break; case TaskStatus.Faulted: p.TrySetException(x.Exception); break; case TaskStatus.RanToCompletion: p.TrySetResult(); break; default: throw new NotSupportedException(); } }, promise, useCurrentSynchronizationContext ? TaskScheduler.FromCurrentSynchronizationContext() : TaskScheduler.Current); return promise.Task; } public static Task AsTask(this UniTask task) { try { UniTask.Awaiter awaiter; try { awaiter = task.GetAwaiter(); } catch (Exception ex) { return Task.FromException(ex); } if (awaiter.IsCompleted) { try { var result = awaiter.GetResult(); return Task.FromResult(result); } catch (Exception ex) { return Task.FromException(ex); } } var tcs = new TaskCompletionSource(); awaiter.SourceOnCompleted(state => { using (var tuple = (StateTuple, UniTask.Awaiter>)state) { var (inTcs, inAwaiter) = tuple; try { var result = inAwaiter.GetResult(); inTcs.SetResult(result); } catch (Exception ex) { inTcs.SetException(ex); } } }, StateTuple.Create(tcs, awaiter)); return tcs.Task; } catch (Exception ex) { return Task.FromException(ex); } } public static Task AsTask(this UniTask task) { try { UniTask.Awaiter awaiter; try { awaiter = task.GetAwaiter(); } catch (Exception ex) { return Task.FromException(ex); } if (awaiter.IsCompleted) { try { awaiter.GetResult(); // check token valid on Succeeded return Task.CompletedTask; } catch (Exception ex) { return Task.FromException(ex); } } var tcs = new TaskCompletionSource(); awaiter.SourceOnCompleted(state => { using (var tuple = (StateTuple, UniTask.Awaiter>)state) { var (inTcs, inAwaiter) = tuple; try { inAwaiter.GetResult(); inTcs.SetResult(null); } catch (Exception ex) { inTcs.SetException(ex); } } }, StateTuple.Create(tcs, awaiter)); return tcs.Task; } catch (Exception ex) { return Task.FromException(ex); } } public static AsyncLazy ToAsyncLazy(this UniTask task) { return new AsyncLazy(task); } public static AsyncLazy ToAsyncLazy(this UniTask task) { return new AsyncLazy(task); } /// /// Ignore task result when cancel raised first. /// public static UniTask AttachExternalCancellation(this UniTask task, CancellationToken cancellationToken) { if (!cancellationToken.CanBeCanceled) { return task; } if (cancellationToken.IsCancellationRequested) { return UniTask.FromCanceled(cancellationToken); } if (task.Status.IsCompleted()) { return task; } return new UniTask(new AttachExternalCancellationSource(task, cancellationToken), 0); } /// /// Ignore task result when cancel raised first. /// public static UniTask AttachExternalCancellation(this UniTask task, CancellationToken cancellationToken) { if (!cancellationToken.CanBeCanceled) { return task; } if (cancellationToken.IsCancellationRequested) { return UniTask.FromCanceled(cancellationToken); } if (task.Status.IsCompleted()) { return task; } return new UniTask(new AttachExternalCancellationSource(task, cancellationToken), 0); } sealed class AttachExternalCancellationSource : IUniTaskSource { static readonly Action cancellationCallbackDelegate = CancellationCallback; CancellationToken cancellationToken; CancellationTokenRegistration tokenRegistration; UniTaskCompletionSourceCore core; public AttachExternalCancellationSource(UniTask task, CancellationToken cancellationToken) { this.cancellationToken = cancellationToken; this.tokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallbackDelegate, this); RunTask(task).Forget(); } async UniTaskVoid RunTask(UniTask task) { try { await task; core.TrySetResult(AsyncUnit.Default); } catch (Exception ex) { core.TrySetException(ex); } finally { tokenRegistration.Dispose(); } } static void CancellationCallback(object state) { var self = (AttachExternalCancellationSource)state; self.core.TrySetCanceled(self.cancellationToken); } public void GetResult(short token) { core.GetResult(token); } public UniTaskStatus GetStatus(short token) { return core.GetStatus(token); } public void OnCompleted(Action continuation, object state, short token) { core.OnCompleted(continuation, state, token); } public UniTaskStatus UnsafeGetStatus() { return core.UnsafeGetStatus(); } } sealed class AttachExternalCancellationSource : IUniTaskSource { static readonly Action cancellationCallbackDelegate = CancellationCallback; CancellationToken cancellationToken; CancellationTokenRegistration tokenRegistration; UniTaskCompletionSourceCore core; public AttachExternalCancellationSource(UniTask task, CancellationToken cancellationToken) { this.cancellationToken = cancellationToken; this.tokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallbackDelegate, this); RunTask(task).Forget(); } async UniTaskVoid RunTask(UniTask task) { try { core.TrySetResult(await task); } catch (Exception ex) { core.TrySetException(ex); } finally { tokenRegistration.Dispose(); } } static void CancellationCallback(object state) { var self = (AttachExternalCancellationSource)state; self.core.TrySetCanceled(self.cancellationToken); } void IUniTaskSource.GetResult(short token) { core.GetResult(token); } public T GetResult(short token) { return core.GetResult(token); } public UniTaskStatus GetStatus(short token) { return core.GetStatus(token); } public void OnCompleted(Action continuation, object state, short token) { core.OnCompleted(continuation, state, token); } public UniTaskStatus UnsafeGetStatus() { return core.UnsafeGetStatus(); } } #if UNITY_2018_3_OR_NEWER public static IEnumerator ToCoroutine(this UniTask task, Action resultHandler = null, Action exceptionHandler = null) { return new ToCoroutineEnumerator(task, resultHandler, exceptionHandler); } public static IEnumerator ToCoroutine(this UniTask task, Action exceptionHandler = null) { return new ToCoroutineEnumerator(task, exceptionHandler); } public static async UniTask Timeout(this UniTask task, TimeSpan timeout, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null) { var delayCancellationTokenSource = new CancellationTokenSource(); var timeoutTask = UniTask.Delay(timeout, delayType, timeoutCheckTiming, delayCancellationTokenSource.Token).SuppressCancellationThrow(); int winArgIndex; bool taskResultIsCanceled; try { (winArgIndex, taskResultIsCanceled, _) = await UniTask.WhenAny(task.SuppressCancellationThrow(), timeoutTask); } catch { delayCancellationTokenSource.Cancel(); delayCancellationTokenSource.Dispose(); throw; } // timeout if (winArgIndex == 1) { if (taskCancellationTokenSource != null) { taskCancellationTokenSource.Cancel(); taskCancellationTokenSource.Dispose(); } throw new TimeoutException("Exceed Timeout:" + timeout); } else { delayCancellationTokenSource.Cancel(); delayCancellationTokenSource.Dispose(); } if (taskResultIsCanceled) { Error.ThrowOperationCanceledException(); } } public static async UniTask Timeout(this UniTask task, TimeSpan timeout, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null) { var delayCancellationTokenSource = new CancellationTokenSource(); var timeoutTask = UniTask.Delay(timeout, delayType, timeoutCheckTiming, delayCancellationTokenSource.Token).SuppressCancellationThrow(); int winArgIndex; (bool IsCanceled, T Result) taskResult; try { (winArgIndex, taskResult, _) = await UniTask.WhenAny(task.SuppressCancellationThrow(), timeoutTask); } catch { delayCancellationTokenSource.Cancel(); delayCancellationTokenSource.Dispose(); throw; } // timeout if (winArgIndex == 1) { if (taskCancellationTokenSource != null) { taskCancellationTokenSource.Cancel(); taskCancellationTokenSource.Dispose(); } throw new TimeoutException("Exceed Timeout:" + timeout); } else { delayCancellationTokenSource.Cancel(); delayCancellationTokenSource.Dispose(); } if (taskResult.IsCanceled) { Error.ThrowOperationCanceledException(); } return taskResult.Result; } /// /// Timeout with suppress OperationCanceledException. Returns (bool, IsCacneled). /// public static async UniTask TimeoutWithoutException(this UniTask task, TimeSpan timeout, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null) { var delayCancellationTokenSource = new CancellationTokenSource(); var timeoutTask = UniTask.Delay(timeout, delayType, timeoutCheckTiming, delayCancellationTokenSource.Token).SuppressCancellationThrow(); int winArgIndex; bool taskResultIsCanceled; try { (winArgIndex, taskResultIsCanceled, _) = await UniTask.WhenAny(task.SuppressCancellationThrow(), timeoutTask); } catch { delayCancellationTokenSource.Cancel(); delayCancellationTokenSource.Dispose(); return true; } // timeout if (winArgIndex == 1) { if (taskCancellationTokenSource != null) { taskCancellationTokenSource.Cancel(); taskCancellationTokenSource.Dispose(); } return true; } else { delayCancellationTokenSource.Cancel(); delayCancellationTokenSource.Dispose(); } if (taskResultIsCanceled) { return true; } return false; } /// /// Timeout with suppress OperationCanceledException. Returns (bool IsTimeout, T Result). /// public static async UniTask<(bool IsTimeout, T Result)> TimeoutWithoutException(this UniTask task, TimeSpan timeout, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null) { var delayCancellationTokenSource = new CancellationTokenSource(); var timeoutTask = UniTask.Delay(timeout, delayType, timeoutCheckTiming, delayCancellationTokenSource.Token).SuppressCancellationThrow(); int winArgIndex; (bool IsCanceled, T Result) taskResult; try { (winArgIndex, taskResult, _) = await UniTask.WhenAny(task.SuppressCancellationThrow(), timeoutTask); } catch { delayCancellationTokenSource.Cancel(); delayCancellationTokenSource.Dispose(); return (true, default); } // timeout if (winArgIndex == 1) { if (taskCancellationTokenSource != null) { taskCancellationTokenSource.Cancel(); taskCancellationTokenSource.Dispose(); } return (true, default); } else { delayCancellationTokenSource.Cancel(); delayCancellationTokenSource.Dispose(); } if (taskResult.IsCanceled) { return (true, default); } return (false, taskResult.Result); } #endif public static void Forget(this UniTask task) { var awaiter = task.GetAwaiter(); if (awaiter.IsCompleted) { try { awaiter.GetResult(); } catch (Exception ex) { UniTaskScheduler.PublishUnobservedTaskException(ex); } } else { awaiter.SourceOnCompleted(state => { using (var t = (StateTuple)state) { try { t.Item1.GetResult(); } catch (Exception ex) { UniTaskScheduler.PublishUnobservedTaskException(ex); } } }, StateTuple.Create(awaiter)); } } public static void Forget(this UniTask task, Action exceptionHandler, bool handleExceptionOnMainThread = true) { if (exceptionHandler == null) { Forget(task); } else { ForgetCoreWithCatch(task, exceptionHandler, handleExceptionOnMainThread).Forget(); } } static async UniTaskVoid ForgetCoreWithCatch(UniTask task, Action exceptionHandler, bool handleExceptionOnMainThread) { try { await task; } catch (Exception ex) { try { if (handleExceptionOnMainThread) { #if UNITY_2018_3_OR_NEWER await UniTask.SwitchToMainThread(); #endif } exceptionHandler(ex); } catch (Exception ex2) { UniTaskScheduler.PublishUnobservedTaskException(ex2); } } } public static void Forget(this UniTask task) { var awaiter = task.GetAwaiter(); if (awaiter.IsCompleted) { try { awaiter.GetResult(); } catch (Exception ex) { UniTaskScheduler.PublishUnobservedTaskException(ex); } } else { awaiter.SourceOnCompleted(state => { using (var t = (StateTuple.Awaiter>)state) { try { t.Item1.GetResult(); } catch (Exception ex) { UniTaskScheduler.PublishUnobservedTaskException(ex); } } }, StateTuple.Create(awaiter)); } } public static void Forget(this UniTask task, Action exceptionHandler, bool handleExceptionOnMainThread = true) { if (exceptionHandler == null) { task.Forget(); } else { ForgetCoreWithCatch(task, exceptionHandler, handleExceptionOnMainThread).Forget(); } } static async UniTaskVoid ForgetCoreWithCatch(UniTask task, Action exceptionHandler, bool handleExceptionOnMainThread) { try { await task; } catch (Exception ex) { try { if (handleExceptionOnMainThread) { #if UNITY_2018_3_OR_NEWER await UniTask.SwitchToMainThread(); #endif } exceptionHandler(ex); } catch (Exception ex2) { UniTaskScheduler.PublishUnobservedTaskException(ex2); } } } public static async UniTask ContinueWith(this UniTask task, Action continuationFunction) { continuationFunction(await task); } public static async UniTask ContinueWith(this UniTask task, Func continuationFunction) { await continuationFunction(await task); } public static async UniTask ContinueWith(this UniTask task, Func continuationFunction) { return continuationFunction(await task); } public static async UniTask ContinueWith(this UniTask task, Func> continuationFunction) { return await continuationFunction(await task); } public static async UniTask ContinueWith(this UniTask task, Action continuationFunction) { await task; continuationFunction(); } public static async UniTask ContinueWith(this UniTask task, Func continuationFunction) { await task; await continuationFunction(); } public static async UniTask ContinueWith(this UniTask task, Func continuationFunction) { await task; return continuationFunction(); } public static async UniTask ContinueWith(this UniTask task, Func> continuationFunction) { await task; return await continuationFunction(); } public static async UniTask Unwrap(this UniTask> task) { return await await task; } public static async UniTask Unwrap(this UniTask task) { await await task; } public static async UniTask Unwrap(this Task> task) { return await await task; } public static async UniTask Unwrap(this Task> task, bool continueOnCapturedContext) { return await await task.ConfigureAwait(continueOnCapturedContext); } public static async UniTask Unwrap(this Task task) { await await task; } public static async UniTask Unwrap(this Task task, bool continueOnCapturedContext) { await await task.ConfigureAwait(continueOnCapturedContext); } public static async UniTask Unwrap(this UniTask> task) { return await await task; } public static async UniTask Unwrap(this UniTask> task, bool continueOnCapturedContext) { return await (await task).ConfigureAwait(continueOnCapturedContext); } public static async UniTask Unwrap(this UniTask task) { await await task; } public static async UniTask Unwrap(this UniTask task, bool continueOnCapturedContext) { await (await task).ConfigureAwait(continueOnCapturedContext); } #if UNITY_2018_3_OR_NEWER sealed class ToCoroutineEnumerator : IEnumerator { bool completed; UniTask task; Action exceptionHandler = null; bool isStarted = false; ExceptionDispatchInfo exception; public ToCoroutineEnumerator(UniTask task, Action exceptionHandler) { completed = false; this.exceptionHandler = exceptionHandler; this.task = task; } async UniTaskVoid RunTask(UniTask task) { try { await task; } catch (Exception ex) { if (exceptionHandler != null) { exceptionHandler(ex); } else { this.exception = ExceptionDispatchInfo.Capture(ex); } } finally { completed = true; } } public object Current => null; public bool MoveNext() { if (!isStarted) { isStarted = true; RunTask(task).Forget(); } if (exception != null) { exception.Throw(); return false; } return !completed; } void IEnumerator.Reset() { } } sealed class ToCoroutineEnumerator : IEnumerator { bool completed; Action resultHandler = null; Action exceptionHandler = null; bool isStarted = false; UniTask task; object current = null; ExceptionDispatchInfo exception; public ToCoroutineEnumerator(UniTask task, Action resultHandler, Action exceptionHandler) { completed = false; this.task = task; this.resultHandler = resultHandler; this.exceptionHandler = exceptionHandler; } async UniTaskVoid RunTask(UniTask task) { try { var value = await task; current = value; // boxed if T is struct... if (resultHandler != null) { resultHandler(value); } } catch (Exception ex) { if (exceptionHandler != null) { exceptionHandler(ex); } else { this.exception = ExceptionDispatchInfo.Capture(ex); } } finally { completed = true; } } public object Current => current; public bool MoveNext() { if (!isStarted) { isStarted = true; RunTask(task).Forget(); } if (exception != null) { exception.Throw(); return false; } return !completed; } void IEnumerator.Reset() { } } #endif } }