#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; using System.Runtime.ExceptionServices; using System.Threading; using System.Threading.Tasks; using UniRx.Async.Internal; namespace UniRx.Async { 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 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, bool ignoreTimeScale = true, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null) { var delayCancellationTokenSource = new CancellationTokenSource(); var timeoutTask = UniTask.Delay(timeout, ignoreTimeScale, 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, bool ignoreTimeScale = true, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null) { var delayCancellationTokenSource = new CancellationTokenSource(); var timeoutTask = UniTask.Delay(timeout, ignoreTimeScale, 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, bool ignoreTimeScale = true, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null) { var delayCancellationTokenSource = new CancellationTokenSource(); var timeoutTask = UniTask.Delay(timeout, ignoreTimeScale, 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(); } throw new TimeoutException("Exceed Timeout:" + timeout); } else { delayCancellationTokenSource.Cancel(); delayCancellationTokenSource.Dispose(); } if (taskResultIsCanceled) { Error.ThrowOperationCanceledException(); } 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, bool ignoreTimeScale = true, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null) { var delayCancellationTokenSource = new CancellationTokenSource(); var timeoutTask = UniTask.Delay(timeout, ignoreTimeScale, 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(); } throw new TimeoutException("Exceed Timeout:" + timeout); } else { delayCancellationTokenSource.Cancel(); delayCancellationTokenSource.Dispose(); } if (taskResult.IsCanceled) { Error.ThrowOperationCanceledException(); } return (false, taskResult.Result); } public static void Forget(this UniTask task) { var awaiter = task.GetAwaiter(); if (awaiter.IsCompleted) { try { awaiter.GetResult(); } catch (Exception ex) { UniTaskScheduler.PublishUnobservedTaskException(ex); } } 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) { await UniTask.SwitchToMainThread(); } 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); } } 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) { await UniTask.SwitchToMainThread(); } 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 ConfigureAwait(this Task task, PlayerLoopTiming timing) { await task.ConfigureAwait(false); await UniTask.Yield(timing); } public static async UniTask ConfigureAwait(this Task task, PlayerLoopTiming timing) { var v = await task.ConfigureAwait(false); await UniTask.Yield(timing); return v; } public static async UniTask ConfigureAwait(this UniTask task, PlayerLoopTiming timing) { await task; await UniTask.Yield(timing); } public static async UniTask ConfigureAwait(this UniTask task, PlayerLoopTiming timing) { var v = await task; await UniTask.Yield(timing); return v; } public static async UniTask Unwrap(this UniTask> task) { return await await task; } public static async UniTask Unwrap(this UniTask task) { await await task; } 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) { // throw exception on iterator (main)thread. // unfortunately unity test-runner can not handle throw exception on hand-write IEnumerator.MoveNext. UnityEngine.Debug.LogException(exception.SourceException); } return !completed; } public void Reset() { } } 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) { // throw exception on iterator (main)thread. // unfortunately unity test-runner can not handle throw exception on hand-write IEnumerator.MoveNext. UnityEngine.Debug.LogException(exception.SourceException); } return !completed; } public void Reset() { } } } } #endif