|
|
|
|
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
|
|
|
|
|
|
|
|
|
using System;
|
|
|
|
|
using System.Runtime.CompilerServices;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
|
|
|
|
|
namespace Cysharp.Threading.Tasks
|
|
|
|
|
{
|
|
|
|
|
public static class CancellationTokenExtensions
|
|
|
|
|
{
|
|
|
|
|
static readonly Action<object> cancellationTokenCallback = Callback;
|
|
|
|
|
static readonly Action<object> disposeCallback = DisposeCallback;
|
|
|
|
|
|
|
|
|
|
public static CancellationToken ToCancellationToken(this UniTask task)
|
|
|
|
|
{
|
|
|
|
|
var cts = new CancellationTokenSource();
|
|
|
|
|
ToCancellationTokenCore(task, cts).Forget();
|
|
|
|
|
return cts.Token;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static CancellationToken ToCancellationToken(this UniTask task, CancellationToken linkToken)
|
|
|
|
|
{
|
|
|
|
|
if (linkToken.IsCancellationRequested)
|
|
|
|
|
{
|
|
|
|
|
return linkToken;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!linkToken.CanBeCanceled)
|
|
|
|
|
{
|
|
|
|
|
return ToCancellationToken(task);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var cts = CancellationTokenSource.CreateLinkedTokenSource(linkToken);
|
|
|
|
|
ToCancellationTokenCore(task, cts).Forget();
|
|
|
|
|
|
|
|
|
|
return cts.Token;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static CancellationToken ToCancellationToken<T>(this UniTask<T> task)
|
|
|
|
|
{
|
|
|
|
|
return ToCancellationToken(task.AsUniTask());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static CancellationToken ToCancellationToken<T>(this UniTask<T> task, CancellationToken linkToken)
|
|
|
|
|
{
|
|
|
|
|
return ToCancellationToken(task.AsUniTask(), linkToken);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static async UniTaskVoid ToCancellationTokenCore(UniTask task, CancellationTokenSource cts)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
await task;
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
UniTaskScheduler.PublishUnobservedTaskException(ex);
|
|
|
|
|
}
|
|
|
|
|
cts.Cancel();
|
|
|
|
|
cts.Dispose();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static (UniTask, CancellationTokenRegistration) ToUniTask(this CancellationToken cancellationToken)
|
|
|
|
|
{
|
|
|
|
|
if (cancellationToken.IsCancellationRequested)
|
|
|
|
|
{
|
|
|
|
|
return (UniTask.FromCanceled(cancellationToken), default(CancellationTokenRegistration));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var promise = new UniTaskCompletionSource();
|
|
|
|
|
return (promise.Task, cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationTokenCallback, promise));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void Callback(object state)
|
|
|
|
|
{
|
|
|
|
|
var promise = (UniTaskCompletionSource)state;
|
|
|
|
|
promise.TrySetResult();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static CancellationTokenAwaitable WaitUntilCanceled(this CancellationToken cancellationToken)
|
|
|
|
|
{
|
|
|
|
|
return new CancellationTokenAwaitable(cancellationToken);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static CancellationTokenRegistration RegisterWithoutCaptureExecutionContext(this CancellationToken cancellationToken, Action callback)
|
|
|
|
|
{
|
|
|
|
|
var restoreFlow = false;
|
|
|
|
|
if (!ExecutionContext.IsFlowSuppressed())
|
|
|
|
|
{
|
|
|
|
|
ExecutionContext.SuppressFlow();
|
|
|
|
|
restoreFlow = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
return cancellationToken.Register(callback, false);
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
if (restoreFlow)
|
|
|
|
|
{
|
|
|
|
|
ExecutionContext.RestoreFlow();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static CancellationTokenRegistration RegisterWithoutCaptureExecutionContext(this CancellationToken cancellationToken, Action<object> callback, object state)
|
|
|
|
|
{
|
|
|
|
|
var restoreFlow = false;
|
|
|
|
|
if (!ExecutionContext.IsFlowSuppressed())
|
|
|
|
|
{
|
|
|
|
|
ExecutionContext.SuppressFlow();
|
|
|
|
|
restoreFlow = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
return cancellationToken.Register(callback, state, false);
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
if (restoreFlow)
|
|
|
|
|
{
|
|
|
|
|
ExecutionContext.RestoreFlow();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static CancellationTokenRegistration AddTo(this IDisposable disposable, CancellationToken cancellationToken)
|
|
|
|
|
{
|
|
|
|
|
return cancellationToken.RegisterWithoutCaptureExecutionContext(disposeCallback, disposable);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void DisposeCallback(object state)
|
|
|
|
|
{
|
|
|
|
|
var d = (IDisposable)state;
|
|
|
|
|
d.Dispose();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public struct CancellationTokenAwaitable
|
|
|
|
|
{
|
|
|
|
|
CancellationToken cancellationToken;
|
|
|
|
|
|
|
|
|
|
public CancellationTokenAwaitable(CancellationToken cancellationToken)
|
|
|
|
|
{
|
|
|
|
|
this.cancellationToken = cancellationToken;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Awaiter GetAwaiter()
|
|
|
|
|
{
|
|
|
|
|
return new Awaiter(cancellationToken);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public struct Awaiter : ICriticalNotifyCompletion
|
|
|
|
|
{
|
|
|
|
|
CancellationToken cancellationToken;
|
|
|
|
|
|
|
|
|
|
public Awaiter(CancellationToken cancellationToken)
|
|
|
|
|
{
|
|
|
|
|
this.cancellationToken = cancellationToken;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool IsCompleted => !cancellationToken.CanBeCanceled || cancellationToken.IsCancellationRequested;
|
|
|
|
|
|
|
|
|
|
public void GetResult()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void OnCompleted(Action continuation)
|
|
|
|
|
{
|
|
|
|
|
UnsafeOnCompleted(continuation);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void UnsafeOnCompleted(Action continuation)
|
|
|
|
|
{
|
|
|
|
|
cancellationToken.RegisterWithoutCaptureExecutionContext(continuation);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|