#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.Reflection; using System.Runtime.ExceptionServices; using System.Threading; using UniRx.Async.Internal; using UnityEngine; namespace UniRx.Async { public static class EnumeratorAsyncExtensions { public static UniTask.Awaiter GetAwaiter(this IEnumerator enumerator) { return new UniTask(EnumeratorPromise.Create(enumerator, PlayerLoopTiming.Update, CancellationToken.None, out var token), token).GetAwaiter(); } public static UniTask ToUniTask(this IEnumerator enumerator) { return new UniTask(EnumeratorPromise.Create(enumerator, PlayerLoopTiming.Update, CancellationToken.None, out var token), token); } public static UniTask ConfigureAwait(this IEnumerator enumerator, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) { return new UniTask(EnumeratorPromise.Create(enumerator, timing, cancellationToken, out var token), token); } class EnumeratorPromise : IUniTaskSource, IPlayerLoopItem, IPromisePoolItem { static readonly PromisePool pool = new PromisePool(); IEnumerator innerEnumerator; CancellationToken cancellationToken; UniTaskCompletionSourceCore core; EnumeratorPromise() { } public static IUniTaskSource Create(IEnumerator innerEnumerator, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token) { if (cancellationToken.IsCancellationRequested) { return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token); } var result = pool.TryRent() ?? new EnumeratorPromise(); result.innerEnumerator = ConsumeEnumerator(innerEnumerator); 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) { core.TrySetCanceled(cancellationToken); return false; } try { if (innerEnumerator.MoveNext()) { return true; } } catch (Exception ex) { core.TrySetException(ex); return false; } core.TrySetResult(null); return false; } public void Reset() { core.Reset(); innerEnumerator = default; cancellationToken = default; } ~EnumeratorPromise() { if (pool.TryReturn(this)) { GC.ReRegisterForFinalize(this); } } // Unwrap YieldInstructions static IEnumerator ConsumeEnumerator(IEnumerator enumerator) { while (enumerator.MoveNext()) { var current = enumerator.Current; if (current == null) { yield return null; } else if (current is CustomYieldInstruction) { // WWW, WaitForSecondsRealtime var e2 = UnwrapWaitCustomYieldInstruction((CustomYieldInstruction)current); while (e2.MoveNext()) { yield return null; } } else if (current is YieldInstruction) { IEnumerator innerCoroutine = null; switch (current) { case AsyncOperation ao: innerCoroutine = UnwrapWaitAsyncOperation(ao); break; case WaitForSeconds wfs: innerCoroutine = UnwrapWaitForSeconds(wfs); break; } if (innerCoroutine != null) { while (innerCoroutine.MoveNext()) { yield return null; } } else { yield return null; } } else if (current is IEnumerator e3) { var e4 = ConsumeEnumerator(e3); while (e4.MoveNext()) { yield return null; } } else { // WaitForEndOfFrame, WaitForFixedUpdate, others. yield return null; } } } // WWW and others as CustomYieldInstruction. static IEnumerator UnwrapWaitCustomYieldInstruction(CustomYieldInstruction yieldInstruction) { while (yieldInstruction.keepWaiting) { yield return null; } } static readonly FieldInfo waitForSeconds_Seconds = typeof(WaitForSeconds).GetField("m_Seconds", BindingFlags.Instance | BindingFlags.GetField | BindingFlags.NonPublic); static IEnumerator UnwrapWaitForSeconds(WaitForSeconds waitForSeconds) { var second = (float)waitForSeconds_Seconds.GetValue(waitForSeconds); var startTime = DateTimeOffset.UtcNow; while (true) { yield return null; var elapsed = (DateTimeOffset.UtcNow - startTime).TotalSeconds; if (elapsed >= second) { break; } }; } static IEnumerator UnwrapWaitAsyncOperation(AsyncOperation asyncOperation) { while (!asyncOperation.isDone) { yield return null; } } } } } #endif