#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member using System; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using Cysharp.Threading.Tasks.Internal; namespace Cysharp.Threading.Tasks { public partial struct UniTask { #if UNITY_2018_3_OR_NEWER /// /// If running on mainthread, do nothing. Otherwise, same as UniTask.Yield(PlayerLoopTiming.Update). /// public static SwitchToMainThreadAwaitable SwitchToMainThread(CancellationToken cancellationToken = default) { return new SwitchToMainThreadAwaitable(PlayerLoopTiming.Update, cancellationToken); } /// /// If running on mainthread, do nothing. Otherwise, same as UniTask.Yield(timing). /// public static SwitchToMainThreadAwaitable SwitchToMainThread(PlayerLoopTiming timing, CancellationToken cancellationToken = default) { return new SwitchToMainThreadAwaitable(timing, cancellationToken); } /// /// Return to mainthread(same as await SwitchToMainThread) after using scope is closed. /// public static ReturnToMainThread ReturnToMainThread(CancellationToken cancellationToken = default) { return new ReturnToMainThread(PlayerLoopTiming.Update, cancellationToken); } /// /// Return to mainthread(same as await SwitchToMainThread) after using scope is closed. /// public static ReturnToMainThread ReturnToMainThread(PlayerLoopTiming timing, CancellationToken cancellationToken = default) { return new ReturnToMainThread(timing, cancellationToken); } /// /// Queue the action to PlayerLoop. /// public static void Post(Action action, PlayerLoopTiming timing = PlayerLoopTiming.Update) { PlayerLoopHelper.AddContinuation(timing, action); } #endif public static SwitchToThreadPoolAwaitable SwitchToThreadPool() { return new SwitchToThreadPoolAwaitable(); } /// /// Note: use SwitchToThreadPool is recommended. /// public static SwitchToTaskPoolAwaitable SwitchToTaskPool() { return new SwitchToTaskPoolAwaitable(); } public static SwitchToSynchronizationContextAwaitable SwitchToSynchronizationContext(SynchronizationContext synchronizationContext, CancellationToken cancellationToken = default) { Error.ThrowArgumentNullException(synchronizationContext, nameof(synchronizationContext)); return new SwitchToSynchronizationContextAwaitable(synchronizationContext, cancellationToken); } public static ReturnToSynchronizationContext ReturnToSynchronizationContext(SynchronizationContext synchronizationContext, CancellationToken cancellationToken = default) { return new ReturnToSynchronizationContext(synchronizationContext, false, cancellationToken); } public static ReturnToSynchronizationContext ReturnToCurrentSynchronizationContext(bool dontPostWhenSameContext = true, CancellationToken cancellationToken = default) { return new ReturnToSynchronizationContext(SynchronizationContext.Current, dontPostWhenSameContext, cancellationToken); } } #if UNITY_2018_3_OR_NEWER public struct SwitchToMainThreadAwaitable { readonly PlayerLoopTiming playerLoopTiming; readonly CancellationToken cancellationToken; public SwitchToMainThreadAwaitable(PlayerLoopTiming playerLoopTiming, CancellationToken cancellationToken) { this.playerLoopTiming = playerLoopTiming; this.cancellationToken = cancellationToken; } public Awaiter GetAwaiter() => new Awaiter(playerLoopTiming, cancellationToken); public struct Awaiter : ICriticalNotifyCompletion { readonly PlayerLoopTiming playerLoopTiming; readonly CancellationToken cancellationToken; public Awaiter(PlayerLoopTiming playerLoopTiming, CancellationToken cancellationToken) { this.playerLoopTiming = playerLoopTiming; this.cancellationToken = cancellationToken; } public bool IsCompleted { get { var currentThreadId = System.Threading.Thread.CurrentThread.ManagedThreadId; if (PlayerLoopHelper.MainThreadId == currentThreadId) { return true; // run immediate. } else { return false; // register continuation. } } } public void GetResult() { cancellationToken.ThrowIfCancellationRequested(); } public void OnCompleted(Action continuation) { PlayerLoopHelper.AddContinuation(playerLoopTiming, continuation); } public void UnsafeOnCompleted(Action continuation) { PlayerLoopHelper.AddContinuation(playerLoopTiming, continuation); } } } public struct ReturnToMainThread { readonly PlayerLoopTiming playerLoopTiming; readonly CancellationToken cancellationToken; public ReturnToMainThread(PlayerLoopTiming playerLoopTiming, CancellationToken cancellationToken) { this.playerLoopTiming = playerLoopTiming; this.cancellationToken = cancellationToken; } public Awaiter DisposeAsync() { return new Awaiter(playerLoopTiming, cancellationToken); // run immediate. } public readonly struct Awaiter : ICriticalNotifyCompletion { readonly PlayerLoopTiming timing; readonly CancellationToken cancellationToken; public Awaiter(PlayerLoopTiming timing, CancellationToken cancellationToken) { this.timing = timing; this.cancellationToken = cancellationToken; } public Awaiter GetAwaiter() => this; public bool IsCompleted => PlayerLoopHelper.MainThreadId == System.Threading.Thread.CurrentThread.ManagedThreadId; public void GetResult() { cancellationToken.ThrowIfCancellationRequested(); } public void OnCompleted(Action continuation) { PlayerLoopHelper.AddContinuation(timing, continuation); } public void UnsafeOnCompleted(Action continuation) { PlayerLoopHelper.AddContinuation(timing, continuation); } } } #endif public struct SwitchToThreadPoolAwaitable { public Awaiter GetAwaiter() => new Awaiter(); public struct Awaiter : ICriticalNotifyCompletion { static readonly WaitCallback switchToCallback = Callback; public bool IsCompleted => false; public void GetResult() { } public void OnCompleted(Action continuation) { ThreadPool.QueueUserWorkItem(switchToCallback, continuation); } public void UnsafeOnCompleted(Action continuation) { #if NETCOREAPP3_1 ThreadPool.UnsafeQueueUserWorkItem(ThreadPoolWorkItem.Create(continuation), false); #else ThreadPool.UnsafeQueueUserWorkItem(switchToCallback, continuation); #endif } static void Callback(object state) { var continuation = (Action)state; continuation(); } } #if NETCOREAPP3_1 sealed class ThreadPoolWorkItem : IThreadPoolWorkItem, ITaskPoolNode { static TaskPool pool; ThreadPoolWorkItem nextNode; public ref ThreadPoolWorkItem NextNode => ref nextNode; static ThreadPoolWorkItem() { TaskPool.RegisterSizeGetter(typeof(ThreadPoolWorkItem), () => pool.Size); } Action continuation; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ThreadPoolWorkItem Create(Action continuation) { if (!pool.TryPop(out var item)) { item = new ThreadPoolWorkItem(); } item.continuation = continuation; return item; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Execute() { var call = continuation; continuation = null; if (call != null) { pool.TryPush(this); call.Invoke(); } } } #endif } public struct SwitchToTaskPoolAwaitable { public Awaiter GetAwaiter() => new Awaiter(); public struct Awaiter : ICriticalNotifyCompletion { static readonly Action switchToCallback = Callback; public bool IsCompleted => false; public void GetResult() { } public void OnCompleted(Action continuation) { Task.Factory.StartNew(switchToCallback, continuation, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); } public void UnsafeOnCompleted(Action continuation) { Task.Factory.StartNew(switchToCallback, continuation, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); } static void Callback(object state) { var continuation = (Action)state; continuation(); } } } public struct SwitchToSynchronizationContextAwaitable { readonly SynchronizationContext synchronizationContext; readonly CancellationToken cancellationToken; public SwitchToSynchronizationContextAwaitable(SynchronizationContext synchronizationContext, CancellationToken cancellationToken) { this.synchronizationContext = synchronizationContext; this.cancellationToken = cancellationToken; } public Awaiter GetAwaiter() => new Awaiter(synchronizationContext, cancellationToken); public struct Awaiter : ICriticalNotifyCompletion { static readonly SendOrPostCallback switchToCallback = Callback; readonly SynchronizationContext synchronizationContext; readonly CancellationToken cancellationToken; public Awaiter(SynchronizationContext synchronizationContext, CancellationToken cancellationToken) { this.synchronizationContext = synchronizationContext; this.cancellationToken = cancellationToken; } public bool IsCompleted => false; public void GetResult() { cancellationToken.ThrowIfCancellationRequested(); } public void OnCompleted(Action continuation) { synchronizationContext.Post(switchToCallback, continuation); } public void UnsafeOnCompleted(Action continuation) { synchronizationContext.Post(switchToCallback, continuation); } static void Callback(object state) { var continuation = (Action)state; continuation(); } } } public struct ReturnToSynchronizationContext { readonly SynchronizationContext syncContext; readonly bool dontPostWhenSameContext; readonly CancellationToken cancellationToken; public ReturnToSynchronizationContext(SynchronizationContext syncContext, bool dontPostWhenSameContext, CancellationToken cancellationToken) { this.syncContext = syncContext; this.dontPostWhenSameContext = dontPostWhenSameContext; this.cancellationToken = cancellationToken; } public Awaiter DisposeAsync() { return new Awaiter(syncContext, dontPostWhenSameContext, cancellationToken); } public struct Awaiter : ICriticalNotifyCompletion { static readonly SendOrPostCallback switchToCallback = Callback; readonly SynchronizationContext synchronizationContext; readonly bool dontPostWhenSameContext; readonly CancellationToken cancellationToken; public Awaiter(SynchronizationContext synchronizationContext, bool dontPostWhenSameContext, CancellationToken cancellationToken) { this.synchronizationContext = synchronizationContext; this.dontPostWhenSameContext = dontPostWhenSameContext; this.cancellationToken = cancellationToken; } public Awaiter GetAwaiter() => this; public bool IsCompleted { get { if (!dontPostWhenSameContext) return false; var current = SynchronizationContext.Current; if (current == synchronizationContext) { return true; } else { return false; } } } public void GetResult() { cancellationToken.ThrowIfCancellationRequested(); } public void OnCompleted(Action continuation) { synchronizationContext.Post(switchToCallback, continuation); } public void UnsafeOnCompleted(Action continuation) { synchronizationContext.Post(switchToCallback, continuation); } static void Callback(object state) { var continuation = (Action)state; continuation(); } } } }