initial commit

This commit is contained in:
Jo 2025-01-07 02:06:59 +01:00
parent 6715289efe
commit 788c3389af
37645 changed files with 2526849 additions and 80 deletions

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 607e374d8e8eff9c2fb611fec8273ab5
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,269 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security;
namespace Cysharp.Threading.Tasks.CompilerServices
{
[StructLayout(LayoutKind.Auto)]
public struct AsyncUniTaskMethodBuilder
{
IStateMachineRunnerPromise runnerPromise;
Exception ex;
// 1. Static Create method.
[DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static AsyncUniTaskMethodBuilder Create()
{
return default;
}
// 2. TaskLike Task property.
public UniTask Task
{
[DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
if (runnerPromise != null)
{
return runnerPromise.Task;
}
else if (ex != null)
{
return UniTask.FromException(ex);
}
else
{
return UniTask.CompletedTask;
}
}
}
// 3. SetException
[DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetException(Exception exception)
{
if (runnerPromise == null)
{
ex = exception;
}
else
{
runnerPromise.SetException(exception);
}
}
// 4. SetResult
[DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetResult()
{
if (runnerPromise != null)
{
runnerPromise.SetResult();
}
}
// 5. AwaitOnCompleted
[DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
where TAwaiter : INotifyCompletion
where TStateMachine : IAsyncStateMachine
{
if (runnerPromise == null)
{
AsyncUniTask<TStateMachine>.SetStateMachine(ref stateMachine, ref runnerPromise);
}
awaiter.OnCompleted(runnerPromise.MoveNext);
}
// 6. AwaitUnsafeOnCompleted
[DebuggerHidden]
[SecuritySafeCritical]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
where TAwaiter : ICriticalNotifyCompletion
where TStateMachine : IAsyncStateMachine
{
if (runnerPromise == null)
{
AsyncUniTask<TStateMachine>.SetStateMachine(ref stateMachine, ref runnerPromise);
}
awaiter.UnsafeOnCompleted(runnerPromise.MoveNext);
}
// 7. Start
[DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Start<TStateMachine>(ref TStateMachine stateMachine)
where TStateMachine : IAsyncStateMachine
{
stateMachine.MoveNext();
}
// 8. SetStateMachine
[DebuggerHidden]
public void SetStateMachine(IAsyncStateMachine stateMachine)
{
// don't use boxed stateMachine.
}
#if DEBUG || !UNITY_2018_3_OR_NEWER
// Important for IDE debugger.
object debuggingId;
private object ObjectIdForDebugger
{
get
{
if (debuggingId == null)
{
debuggingId = new object();
}
return debuggingId;
}
}
#endif
}
[StructLayout(LayoutKind.Auto)]
public struct AsyncUniTaskMethodBuilder<T>
{
IStateMachineRunnerPromise<T> runnerPromise;
Exception ex;
T result;
// 1. Static Create method.
[DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static AsyncUniTaskMethodBuilder<T> Create()
{
return default;
}
// 2. TaskLike Task property.
public UniTask<T> Task
{
[DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
if (runnerPromise != null)
{
return runnerPromise.Task;
}
else if (ex != null)
{
return UniTask.FromException<T>(ex);
}
else
{
return UniTask.FromResult(result);
}
}
}
// 3. SetException
[DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetException(Exception exception)
{
if (runnerPromise == null)
{
ex = exception;
}
else
{
runnerPromise.SetException(exception);
}
}
// 4. SetResult
[DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetResult(T result)
{
if (runnerPromise == null)
{
this.result = result;
}
else
{
runnerPromise.SetResult(result);
}
}
// 5. AwaitOnCompleted
[DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
where TAwaiter : INotifyCompletion
where TStateMachine : IAsyncStateMachine
{
if (runnerPromise == null)
{
AsyncUniTask<TStateMachine, T>.SetStateMachine(ref stateMachine, ref runnerPromise);
}
awaiter.OnCompleted(runnerPromise.MoveNext);
}
// 6. AwaitUnsafeOnCompleted
[DebuggerHidden]
[SecuritySafeCritical]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
where TAwaiter : ICriticalNotifyCompletion
where TStateMachine : IAsyncStateMachine
{
if (runnerPromise == null)
{
AsyncUniTask<TStateMachine, T>.SetStateMachine(ref stateMachine, ref runnerPromise);
}
awaiter.UnsafeOnCompleted(runnerPromise.MoveNext);
}
// 7. Start
[DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Start<TStateMachine>(ref TStateMachine stateMachine)
where TStateMachine : IAsyncStateMachine
{
stateMachine.MoveNext();
}
// 8. SetStateMachine
[DebuggerHidden]
public void SetStateMachine(IAsyncStateMachine stateMachine)
{
// don't use boxed stateMachine.
}
#if DEBUG || !UNITY_2018_3_OR_NEWER
// Important for IDE debugger.
object debuggingId;
private object ObjectIdForDebugger
{
get
{
if (debuggingId == null)
{
debuggingId = new object();
}
return debuggingId;
}
}
#endif
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: becef0cdc85ec6944869f164c4aeb31b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,137 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security;
namespace Cysharp.Threading.Tasks.CompilerServices
{
[StructLayout(LayoutKind.Auto)]
public struct AsyncUniTaskVoidMethodBuilder
{
IStateMachineRunner runner;
// 1. Static Create method.
[DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static AsyncUniTaskVoidMethodBuilder Create()
{
return default;
}
// 2. TaskLike Task property(void)
public UniTaskVoid Task
{
[DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return default;
}
}
// 3. SetException
[DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetException(Exception exception)
{
// runner is finished, return first.
if (runner != null)
{
#if ENABLE_IL2CPP
// workaround for IL2CPP bug.
PlayerLoopHelper.AddContinuation(PlayerLoopTiming.LastPostLateUpdate, runner.ReturnAction);
#else
runner.Return();
#endif
runner = null;
}
UniTaskScheduler.PublishUnobservedTaskException(exception);
}
// 4. SetResult
[DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetResult()
{
// runner is finished, return.
if (runner != null)
{
#if ENABLE_IL2CPP
// workaround for IL2CPP bug.
PlayerLoopHelper.AddContinuation(PlayerLoopTiming.LastPostLateUpdate, runner.ReturnAction);
#else
runner.Return();
#endif
runner = null;
}
}
// 5. AwaitOnCompleted
[DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
where TAwaiter : INotifyCompletion
where TStateMachine : IAsyncStateMachine
{
if (runner == null)
{
AsyncUniTaskVoid<TStateMachine>.SetStateMachine(ref stateMachine, ref runner);
}
awaiter.OnCompleted(runner.MoveNext);
}
// 6. AwaitUnsafeOnCompleted
[DebuggerHidden]
[SecuritySafeCritical]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
where TAwaiter : ICriticalNotifyCompletion
where TStateMachine : IAsyncStateMachine
{
if (runner == null)
{
AsyncUniTaskVoid<TStateMachine>.SetStateMachine(ref stateMachine, ref runner);
}
awaiter.UnsafeOnCompleted(runner.MoveNext);
}
// 7. Start
[DebuggerHidden]
public void Start<TStateMachine>(ref TStateMachine stateMachine)
where TStateMachine : IAsyncStateMachine
{
stateMachine.MoveNext();
}
// 8. SetStateMachine
[DebuggerHidden]
public void SetStateMachine(IAsyncStateMachine stateMachine)
{
// don't use boxed stateMachine.
}
#if DEBUG || !UNITY_2018_3_OR_NEWER
// Important for IDE debugger.
object debuggingId;
private object ObjectIdForDebugger
{
get
{
if (debuggingId == null)
{
debuggingId = new object();
}
return debuggingId;
}
}
#endif
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0337fe6f56e5951418faec15d08176c1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,380 @@
#pragma warning disable CS1591
using Cysharp.Threading.Tasks.Internal;
using System;
using System.Linq;
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace Cysharp.Threading.Tasks.CompilerServices
{
// #ENABLE_IL2CPP in this file is to avoid bug of IL2CPP VM.
// Issue is tracked on https://issuetracker.unity3d.com/issues/il2cpp-incorrect-results-when-calling-a-method-from-outside-class-in-a-struct
// but currently it is labeled `Won't Fix`.
internal interface IStateMachineRunner
{
Action MoveNext { get; }
void Return();
#if ENABLE_IL2CPP
Action ReturnAction { get; }
#endif
}
internal interface IStateMachineRunnerPromise : IUniTaskSource
{
Action MoveNext { get; }
UniTask Task { get; }
void SetResult();
void SetException(Exception exception);
}
internal interface IStateMachineRunnerPromise<T> : IUniTaskSource<T>
{
Action MoveNext { get; }
UniTask<T> Task { get; }
void SetResult(T result);
void SetException(Exception exception);
}
internal static class StateMachineUtility
{
// Get AsyncStateMachine internal state to check IL2CPP bug
public static int GetState(IAsyncStateMachine stateMachine)
{
var info = stateMachine.GetType().GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
.First(x => x.Name.EndsWith("__state"));
return (int)info.GetValue(stateMachine);
}
}
internal sealed class AsyncUniTaskVoid<TStateMachine> : IStateMachineRunner, ITaskPoolNode<AsyncUniTaskVoid<TStateMachine>>, IUniTaskSource
where TStateMachine : IAsyncStateMachine
{
static TaskPool<AsyncUniTaskVoid<TStateMachine>> pool;
#if ENABLE_IL2CPP
public Action ReturnAction { get; }
#endif
TStateMachine stateMachine;
public Action MoveNext { get; }
public AsyncUniTaskVoid()
{
MoveNext = Run;
#if ENABLE_IL2CPP
ReturnAction = Return;
#endif
}
public static void SetStateMachine(ref TStateMachine stateMachine, ref IStateMachineRunner runnerFieldRef)
{
if (!pool.TryPop(out var result))
{
result = new AsyncUniTaskVoid<TStateMachine>();
}
TaskTracker.TrackActiveTask(result, 3);
runnerFieldRef = result; // set runner before copied.
result.stateMachine = stateMachine; // copy struct StateMachine(in release build).
}
static AsyncUniTaskVoid()
{
TaskPool.RegisterSizeGetter(typeof(AsyncUniTaskVoid<TStateMachine>), () => pool.Size);
}
AsyncUniTaskVoid<TStateMachine> nextNode;
public ref AsyncUniTaskVoid<TStateMachine> NextNode => ref nextNode;
public void Return()
{
TaskTracker.RemoveTracking(this);
stateMachine = default;
pool.TryPush(this);
}
[DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
void Run()
{
stateMachine.MoveNext();
}
// dummy interface implementation for TaskTracker.
UniTaskStatus IUniTaskSource.GetStatus(short token)
{
return UniTaskStatus.Pending;
}
UniTaskStatus IUniTaskSource.UnsafeGetStatus()
{
return UniTaskStatus.Pending;
}
void IUniTaskSource.OnCompleted(Action<object> continuation, object state, short token)
{
}
void IUniTaskSource.GetResult(short token)
{
}
}
internal sealed class AsyncUniTask<TStateMachine> : IStateMachineRunnerPromise, IUniTaskSource, ITaskPoolNode<AsyncUniTask<TStateMachine>>
where TStateMachine : IAsyncStateMachine
{
static TaskPool<AsyncUniTask<TStateMachine>> pool;
#if ENABLE_IL2CPP
readonly Action returnDelegate;
#endif
public Action MoveNext { get; }
TStateMachine stateMachine;
UniTaskCompletionSourceCore<AsyncUnit> core;
AsyncUniTask()
{
MoveNext = Run;
#if ENABLE_IL2CPP
returnDelegate = Return;
#endif
}
public static void SetStateMachine(ref TStateMachine stateMachine, ref IStateMachineRunnerPromise runnerPromiseFieldRef)
{
if (!pool.TryPop(out var result))
{
result = new AsyncUniTask<TStateMachine>();
}
TaskTracker.TrackActiveTask(result, 3);
runnerPromiseFieldRef = result; // set runner before copied.
result.stateMachine = stateMachine; // copy struct StateMachine(in release build).
}
AsyncUniTask<TStateMachine> nextNode;
public ref AsyncUniTask<TStateMachine> NextNode => ref nextNode;
static AsyncUniTask()
{
TaskPool.RegisterSizeGetter(typeof(AsyncUniTask<TStateMachine>), () => pool.Size);
}
void Return()
{
TaskTracker.RemoveTracking(this);
core.Reset();
stateMachine = default;
pool.TryPush(this);
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
stateMachine = default;
return pool.TryPush(this);
}
[DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
void Run()
{
stateMachine.MoveNext();
}
public UniTask Task
{
[DebuggerHidden]
get
{
return new UniTask(this, core.Version);
}
}
[DebuggerHidden]
public void SetResult()
{
core.TrySetResult(AsyncUnit.Default);
}
[DebuggerHidden]
public void SetException(Exception exception)
{
core.TrySetException(exception);
}
[DebuggerHidden]
public void GetResult(short token)
{
try
{
core.GetResult(token);
}
finally
{
#if ENABLE_IL2CPP
// workaround for IL2CPP bug.
PlayerLoopHelper.AddContinuation(PlayerLoopTiming.LastPostLateUpdate, returnDelegate);
#else
TryReturn();
#endif
}
}
[DebuggerHidden]
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
[DebuggerHidden]
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
[DebuggerHidden]
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
}
internal sealed class AsyncUniTask<TStateMachine, T> : IStateMachineRunnerPromise<T>, IUniTaskSource<T>, ITaskPoolNode<AsyncUniTask<TStateMachine, T>>
where TStateMachine : IAsyncStateMachine
{
static TaskPool<AsyncUniTask<TStateMachine, T>> pool;
#if ENABLE_IL2CPP
readonly Action returnDelegate;
#endif
public Action MoveNext { get; }
TStateMachine stateMachine;
UniTaskCompletionSourceCore<T> core;
AsyncUniTask()
{
MoveNext = Run;
#if ENABLE_IL2CPP
returnDelegate = Return;
#endif
}
public static void SetStateMachine(ref TStateMachine stateMachine, ref IStateMachineRunnerPromise<T> runnerPromiseFieldRef)
{
if (!pool.TryPop(out var result))
{
result = new AsyncUniTask<TStateMachine, T>();
}
TaskTracker.TrackActiveTask(result, 3);
runnerPromiseFieldRef = result; // set runner before copied.
result.stateMachine = stateMachine; // copy struct StateMachine(in release build).
}
AsyncUniTask<TStateMachine, T> nextNode;
public ref AsyncUniTask<TStateMachine, T> NextNode => ref nextNode;
static AsyncUniTask()
{
TaskPool.RegisterSizeGetter(typeof(AsyncUniTask<TStateMachine, T>), () => pool.Size);
}
void Return()
{
TaskTracker.RemoveTracking(this);
core.Reset();
stateMachine = default;
pool.TryPush(this);
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
stateMachine = default;
return pool.TryPush(this);
}
[DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
void Run()
{
// UnityEngine.Debug.Log($"MoveNext State:" + StateMachineUtility.GetState(stateMachine));
stateMachine.MoveNext();
}
public UniTask<T> Task
{
[DebuggerHidden]
get
{
return new UniTask<T>(this, core.Version);
}
}
[DebuggerHidden]
public void SetResult(T result)
{
core.TrySetResult(result);
}
[DebuggerHidden]
public void SetException(Exception exception)
{
core.TrySetException(exception);
}
[DebuggerHidden]
public T GetResult(short token)
{
try
{
return core.GetResult(token);
}
finally
{
#if ENABLE_IL2CPP
// workaround for IL2CPP bug.
PlayerLoopHelper.AddContinuation(PlayerLoopTiming.LastPostLateUpdate, returnDelegate);
#else
TryReturn();
#endif
}
}
[DebuggerHidden]
void IUniTaskSource.GetResult(short token)
{
GetResult(token);
}
[DebuggerHidden]
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
[DebuggerHidden]
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
[DebuggerHidden]
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 10cce0b578043e248ae397128f195286
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a24378f7d972d4cd7bb2db0df94aab93
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,150 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using System;
using System.Threading;
namespace Cysharp.Threading.Tasks.Internal
{
// Same interface as System.Buffers.ArrayPool<T> but only provides Shared.
internal sealed class ArrayPool<T>
{
// Same size as System.Buffers.DefaultArrayPool<T>
const int DefaultMaxNumberOfArraysPerBucket = 50;
static readonly T[] EmptyArray = new T[0];
public static readonly ArrayPool<T> Shared = new ArrayPool<T>();
readonly MinimumQueue<T[]>[] buckets;
readonly SpinLock[] locks;
ArrayPool()
{
// see: GetQueueIndex
buckets = new MinimumQueue<T[]>[18];
locks = new SpinLock[18];
for (int i = 0; i < buckets.Length; i++)
{
buckets[i] = new MinimumQueue<T[]>(4);
locks[i] = new SpinLock(false);
}
}
public T[] Rent(int minimumLength)
{
if (minimumLength < 0)
{
throw new ArgumentOutOfRangeException("minimumLength");
}
else if (minimumLength == 0)
{
return EmptyArray;
}
var size = CalculateSize(minimumLength);
var index = GetQueueIndex(size);
if (index != -1)
{
var q = buckets[index];
var lockTaken = false;
try
{
locks[index].Enter(ref lockTaken);
if (q.Count != 0)
{
return q.Dequeue();
}
}
finally
{
if (lockTaken) locks[index].Exit(false);
}
}
return new T[size];
}
public void Return(T[] array, bool clearArray = false)
{
if (array == null || array.Length == 0)
{
return;
}
var index = GetQueueIndex(array.Length);
if (index != -1)
{
if (clearArray)
{
Array.Clear(array, 0, array.Length);
}
var q = buckets[index];
var lockTaken = false;
try
{
locks[index].Enter(ref lockTaken);
if (q.Count > DefaultMaxNumberOfArraysPerBucket)
{
return;
}
q.Enqueue(array);
}
finally
{
if (lockTaken) locks[index].Exit(false);
}
}
}
static int CalculateSize(int size)
{
size--;
size |= size >> 1;
size |= size >> 2;
size |= size >> 4;
size |= size >> 8;
size |= size >> 16;
size += 1;
if (size < 8)
{
size = 8;
}
return size;
}
static int GetQueueIndex(int size)
{
switch (size)
{
case 8: return 0;
case 16: return 1;
case 32: return 2;
case 64: return 3;
case 128: return 4;
case 256: return 5;
case 512: return 6;
case 1024: return 7;
case 2048: return 8;
case 4096: return 9;
case 8192: return 10;
case 16384: return 11;
case 32768: return 12;
case 65536: return 13;
case 131072: return 14;
case 262144: return 15;
case 524288: return 16;
case 1048576: return 17; // max array length
default:
return -1;
}
}
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 57115252201e02e4fbffa7a1dbe168bb
timeCreated: 1532361008
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,115 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace Cysharp.Threading.Tasks.Internal
{
internal static class ArrayPoolUtil
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void EnsureCapacity<T>(ref T[] array, int index, ArrayPool<T> pool)
{
if (array.Length <= index)
{
EnsureCapacityCore(ref array, index, pool);
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
static void EnsureCapacityCore<T>(ref T[] array, int index, ArrayPool<T> pool)
{
if (array.Length <= index)
{
var newSize = array.Length * 2;
var newArray = pool.Rent((index < newSize) ? newSize : (index * 2));
Array.Copy(array, 0, newArray, 0, array.Length);
pool.Return(array, clearArray: !RuntimeHelpersAbstraction.IsWellKnownNoReferenceContainsType<T>());
array = newArray;
}
}
public static RentArray<T> Materialize<T>(IEnumerable<T> source)
{
if (source is T[] array)
{
return new RentArray<T>(array, array.Length, null);
}
var defaultCount = 32;
if (source is ICollection<T> coll)
{
if (coll.Count == 0)
{
return new RentArray<T>(Array.Empty<T>(), 0, null);
}
defaultCount = coll.Count;
var pool = ArrayPool<T>.Shared;
var buffer = pool.Rent(defaultCount);
coll.CopyTo(buffer, 0);
return new RentArray<T>(buffer, coll.Count, pool);
}
else if (source is IReadOnlyCollection<T> rcoll)
{
defaultCount = rcoll.Count;
}
if (defaultCount == 0)
{
return new RentArray<T>(Array.Empty<T>(), 0, null);
}
{
var pool = ArrayPool<T>.Shared;
var index = 0;
var buffer = pool.Rent(defaultCount);
foreach (var item in source)
{
EnsureCapacity(ref buffer, index, pool);
buffer[index++] = item;
}
return new RentArray<T>(buffer, index, pool);
}
}
public struct RentArray<T> : IDisposable
{
public readonly T[] Array;
public readonly int Length;
ArrayPool<T> pool;
public RentArray(T[] array, int length, ArrayPool<T> pool)
{
this.Array = array;
this.Length = length;
this.pool = pool;
}
public void Dispose()
{
DisposeManually(!RuntimeHelpersAbstraction.IsWellKnownNoReferenceContainsType<T>());
}
public void DisposeManually(bool clearArray)
{
if (pool != null)
{
if (clearArray)
{
System.Array.Clear(Array, 0, Length);
}
pool.Return(Array, clearArray: false);
pool = null;
}
}
}
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 4059fd8a02e00064a9e06f68cc4b1e5e
timeCreated: 1532361007
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,73 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace Cysharp.Threading.Tasks.Internal
{
internal static class ArrayUtil
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void EnsureCapacity<T>(ref T[] array, int index)
{
if (array.Length <= index)
{
EnsureCore(ref array, index);
}
}
// rare case, no inlining.
[MethodImpl(MethodImplOptions.NoInlining)]
static void EnsureCore<T>(ref T[] array, int index)
{
var newSize = array.Length * 2;
var newArray = new T[(index < newSize) ? newSize : (index * 2)];
Array.Copy(array, 0, newArray, 0, array.Length);
array = newArray;
}
/// <summary>
/// Optimizing utility to avoid .ToArray() that creates buffer copy(cut to just size).
/// </summary>
public static (T[] array, int length) Materialize<T>(IEnumerable<T> source)
{
if (source is T[] array)
{
return (array, array.Length);
}
var defaultCount = 4;
if (source is ICollection<T> coll)
{
defaultCount = coll.Count;
var buffer = new T[defaultCount];
coll.CopyTo(buffer, 0);
return (buffer, defaultCount);
}
else if (source is IReadOnlyCollection<T> rcoll)
{
defaultCount = rcoll.Count;
}
if (defaultCount == 0)
{
return (Array.Empty<T>(), 0);
}
{
var index = 0;
var buffer = new T[defaultCount];
foreach (var item in source)
{
EnsureCapacity(ref buffer, index);
buffer[index++] = item;
}
return (buffer, index);
}
}
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 8e5712a43cac848469fe3e0018c1c39a
timeCreated: 1532361007
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,225 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using System;
using System.Threading;
namespace Cysharp.Threading.Tasks.Internal
{
internal sealed class ContinuationQueue
{
const int MaxArrayLength = 0X7FEFFFFF;
const int InitialSize = 16;
readonly PlayerLoopTiming timing;
SpinLock gate = new SpinLock(false);
bool dequing = false;
int actionListCount = 0;
Action[] actionList = new Action[InitialSize];
int waitingListCount = 0;
Action[] waitingList = new Action[InitialSize];
public ContinuationQueue(PlayerLoopTiming timing)
{
this.timing = timing;
}
public void Enqueue(Action continuation)
{
bool lockTaken = false;
try
{
gate.Enter(ref lockTaken);
if (dequing)
{
// Ensure Capacity
if (waitingList.Length == waitingListCount)
{
var newLength = waitingListCount * 2;
if ((uint)newLength > MaxArrayLength) newLength = MaxArrayLength;
var newArray = new Action[newLength];
Array.Copy(waitingList, newArray, waitingListCount);
waitingList = newArray;
}
waitingList[waitingListCount] = continuation;
waitingListCount++;
}
else
{
// Ensure Capacity
if (actionList.Length == actionListCount)
{
var newLength = actionListCount * 2;
if ((uint)newLength > MaxArrayLength) newLength = MaxArrayLength;
var newArray = new Action[newLength];
Array.Copy(actionList, newArray, actionListCount);
actionList = newArray;
}
actionList[actionListCount] = continuation;
actionListCount++;
}
}
finally
{
if (lockTaken) gate.Exit(false);
}
}
public int Clear()
{
var rest = actionListCount + waitingListCount;
actionListCount = 0;
actionList = new Action[InitialSize];
waitingListCount = 0;
waitingList = new Action[InitialSize];
return rest;
}
// delegate entrypoint.
public void Run()
{
// for debugging, create named stacktrace.
#if DEBUG
switch (timing)
{
case PlayerLoopTiming.Initialization:
Initialization();
break;
case PlayerLoopTiming.LastInitialization:
LastInitialization();
break;
case PlayerLoopTiming.EarlyUpdate:
EarlyUpdate();
break;
case PlayerLoopTiming.LastEarlyUpdate:
LastEarlyUpdate();
break;
case PlayerLoopTiming.FixedUpdate:
FixedUpdate();
break;
case PlayerLoopTiming.LastFixedUpdate:
LastFixedUpdate();
break;
case PlayerLoopTiming.PreUpdate:
PreUpdate();
break;
case PlayerLoopTiming.LastPreUpdate:
LastPreUpdate();
break;
case PlayerLoopTiming.Update:
Update();
break;
case PlayerLoopTiming.LastUpdate:
LastUpdate();
break;
case PlayerLoopTiming.PreLateUpdate:
PreLateUpdate();
break;
case PlayerLoopTiming.LastPreLateUpdate:
LastPreLateUpdate();
break;
case PlayerLoopTiming.PostLateUpdate:
PostLateUpdate();
break;
case PlayerLoopTiming.LastPostLateUpdate:
LastPostLateUpdate();
break;
#if UNITY_2020_2_OR_NEWER
case PlayerLoopTiming.TimeUpdate:
TimeUpdate();
break;
case PlayerLoopTiming.LastTimeUpdate:
LastTimeUpdate();
break;
#endif
default:
break;
}
#else
RunCore();
#endif
}
void Initialization() => RunCore();
void LastInitialization() => RunCore();
void EarlyUpdate() => RunCore();
void LastEarlyUpdate() => RunCore();
void FixedUpdate() => RunCore();
void LastFixedUpdate() => RunCore();
void PreUpdate() => RunCore();
void LastPreUpdate() => RunCore();
void Update() => RunCore();
void LastUpdate() => RunCore();
void PreLateUpdate() => RunCore();
void LastPreLateUpdate() => RunCore();
void PostLateUpdate() => RunCore();
void LastPostLateUpdate() => RunCore();
#if UNITY_2020_2_OR_NEWER
void TimeUpdate() => RunCore();
void LastTimeUpdate() => RunCore();
#endif
[System.Diagnostics.DebuggerHidden]
void RunCore()
{
{
bool lockTaken = false;
try
{
gate.Enter(ref lockTaken);
if (actionListCount == 0) return;
dequing = true;
}
finally
{
if (lockTaken) gate.Exit(false);
}
}
for (int i = 0; i < actionListCount; i++)
{
var action = actionList[i];
actionList[i] = null;
try
{
action();
}
catch (Exception ex)
{
UnityEngine.Debug.LogException(ex);
}
}
{
bool lockTaken = false;
try
{
gate.Enter(ref lockTaken);
dequing = false;
var swapTempActionList = actionList;
actionListCount = waitingListCount;
actionList = waitingList;
waitingListCount = 0;
waitingList = swapTempActionList;
}
finally
{
if (lockTaken) gate.Exit(false);
}
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0d5eec5145ab154479913e91701a918e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,249 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using UnityEngine;
namespace Cysharp.Threading.Tasks.Internal
{
internal static class DiagnosticsExtensions
{
static bool displayFilenames = true;
static readonly Regex typeBeautifyRegex = new Regex("`.+$", RegexOptions.Compiled);
static readonly Dictionary<Type, string> builtInTypeNames = new Dictionary<Type, string>
{
{ typeof(void), "void" },
{ typeof(bool), "bool" },
{ typeof(byte), "byte" },
{ typeof(char), "char" },
{ typeof(decimal), "decimal" },
{ typeof(double), "double" },
{ typeof(float), "float" },
{ typeof(int), "int" },
{ typeof(long), "long" },
{ typeof(object), "object" },
{ typeof(sbyte), "sbyte" },
{ typeof(short), "short" },
{ typeof(string), "string" },
{ typeof(uint), "uint" },
{ typeof(ulong), "ulong" },
{ typeof(ushort), "ushort" },
{ typeof(Task), "Task" },
{ typeof(UniTask), "UniTask" },
{ typeof(UniTaskVoid), "UniTaskVoid" }
};
public static string CleanupAsyncStackTrace(this StackTrace stackTrace)
{
if (stackTrace == null) return "";
var sb = new StringBuilder();
for (int i = 0; i < stackTrace.FrameCount; i++)
{
var sf = stackTrace.GetFrame(i);
var mb = sf.GetMethod();
if (IgnoreLine(mb)) continue;
if (IsAsync(mb))
{
sb.Append("async ");
TryResolveStateMachineMethod(ref mb, out var decType);
}
// return type
if (mb is MethodInfo mi)
{
sb.Append(BeautifyType(mi.ReturnType, false));
sb.Append(" ");
}
// method name
sb.Append(BeautifyType(mb.DeclaringType, false));
if (!mb.IsConstructor)
{
sb.Append(".");
}
sb.Append(mb.Name);
if (mb.IsGenericMethod)
{
sb.Append("<");
foreach (var item in mb.GetGenericArguments())
{
sb.Append(BeautifyType(item, true));
}
sb.Append(">");
}
// parameter
sb.Append("(");
sb.Append(string.Join(", ", mb.GetParameters().Select(p => BeautifyType(p.ParameterType, true) + " " + p.Name)));
sb.Append(")");
// file name
if (displayFilenames && (sf.GetILOffset() != -1))
{
String fileName = null;
try
{
fileName = sf.GetFileName();
}
catch (NotSupportedException)
{
displayFilenames = false;
}
catch (SecurityException)
{
displayFilenames = false;
}
if (fileName != null)
{
sb.Append(' ');
sb.AppendFormat(CultureInfo.InvariantCulture, "(at {0})", AppendHyperLink(fileName, sf.GetFileLineNumber().ToString()));
}
}
sb.AppendLine();
}
return sb.ToString();
}
static bool IsAsync(MethodBase methodInfo)
{
var declareType = methodInfo.DeclaringType;
return typeof(IAsyncStateMachine).IsAssignableFrom(declareType);
}
// code from Ben.Demystifier/EnhancedStackTrace.Frame.cs
static bool TryResolveStateMachineMethod(ref MethodBase method, out Type declaringType)
{
declaringType = method.DeclaringType;
var parentType = declaringType.DeclaringType;
if (parentType == null)
{
return false;
}
var methods = parentType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly);
if (methods == null)
{
return false;
}
foreach (var candidateMethod in methods)
{
var attributes = candidateMethod.GetCustomAttributes<StateMachineAttribute>();
if (attributes == null)
{
continue;
}
foreach (var asma in attributes)
{
if (asma.StateMachineType == declaringType)
{
method = candidateMethod;
declaringType = candidateMethod.DeclaringType;
// Mark the iterator as changed; so it gets the + annotation of the original method
// async statemachines resolve directly to their builder methods so aren't marked as changed
return asma is IteratorStateMachineAttribute;
}
}
}
return false;
}
static string BeautifyType(Type t, bool shortName)
{
if (builtInTypeNames.TryGetValue(t, out var builtin))
{
return builtin;
}
if (t.IsGenericParameter) return t.Name;
if (t.IsArray) return BeautifyType(t.GetElementType(), shortName) + "[]";
if (t.FullName?.StartsWith("System.ValueTuple") ?? false)
{
return "(" + string.Join(", ", t.GetGenericArguments().Select(x => BeautifyType(x, true))) + ")";
}
if (!t.IsGenericType) return shortName ? t.Name : t.FullName.Replace("Cysharp.Threading.Tasks.Triggers.", "").Replace("Cysharp.Threading.Tasks.Internal.", "").Replace("Cysharp.Threading.Tasks.", "") ?? t.Name;
var innerFormat = string.Join(", ", t.GetGenericArguments().Select(x => BeautifyType(x, true)));
var genericType = t.GetGenericTypeDefinition().FullName;
if (genericType == "System.Threading.Tasks.Task`1")
{
genericType = "Task";
}
return typeBeautifyRegex.Replace(genericType, "").Replace("Cysharp.Threading.Tasks.Triggers.", "").Replace("Cysharp.Threading.Tasks.Internal.", "").Replace("Cysharp.Threading.Tasks.", "") + "<" + innerFormat + ">";
}
static bool IgnoreLine(MethodBase methodInfo)
{
var declareType = methodInfo.DeclaringType.FullName;
if (declareType == "System.Threading.ExecutionContext")
{
return true;
}
else if (declareType.StartsWith("System.Runtime.CompilerServices"))
{
return true;
}
else if (declareType.StartsWith("Cysharp.Threading.Tasks.CompilerServices"))
{
return true;
}
else if (declareType == "System.Threading.Tasks.AwaitTaskContinuation")
{
return true;
}
else if (declareType.StartsWith("System.Threading.Tasks.Task"))
{
return true;
}
else if (declareType.StartsWith("Cysharp.Threading.Tasks.UniTaskCompletionSourceCore"))
{
return true;
}
else if (declareType.StartsWith("Cysharp.Threading.Tasks.AwaiterActions"))
{
return true;
}
return false;
}
static string AppendHyperLink(string path, string line)
{
var fi = new FileInfo(path);
if (fi.Directory == null)
{
return fi.Name;
}
else
{
var fname = fi.FullName.Replace(Path.DirectorySeparatorChar, '/').Replace(PlayerLoopHelper.ApplicationDataPath, "");
var withAssetsPath = "Assets/" + fname;
return "<a href=\"" + withAssetsPath + "\" line=\"" + line + "\">" + withAssetsPath + ":" + line + "</a>";
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0b89ca75860355f448a629506a70c4f0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,79 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using System;
using System.Runtime.CompilerServices;
namespace Cysharp.Threading.Tasks.Internal
{
internal static class Error
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ThrowArgumentNullException<T>(T value, string paramName)
where T : class
{
if (value == null) ThrowArgumentNullExceptionCore(paramName);
}
[MethodImpl(MethodImplOptions.NoInlining)]
static void ThrowArgumentNullExceptionCore(string paramName)
{
throw new ArgumentNullException(paramName);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Exception ArgumentOutOfRange(string paramName)
{
return new ArgumentOutOfRangeException(paramName);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Exception NoElements()
{
return new InvalidOperationException("Source sequence doesn't contain any elements.");
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Exception MoreThanOneElement()
{
return new InvalidOperationException("Source sequence contains more than one element.");
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static void ThrowArgumentException<T>(string message)
{
throw new ArgumentException(message);
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static void ThrowNotYetCompleted()
{
throw new InvalidOperationException("Not yet completed.");
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static T ThrowNotYetCompleted<T>()
{
throw new InvalidOperationException("Not yet completed.");
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ThrowWhenContinuationIsAlreadyRegistered<T>(T continuationField)
where T : class
{
if (continuationField != null) ThrowInvalidOperationExceptionCore("continuation is already registered.");
}
[MethodImpl(MethodImplOptions.NoInlining)]
static void ThrowInvalidOperationExceptionCore(string message)
{
throw new InvalidOperationException(message);
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static void ThrowOperationCanceledException()
{
throw new OperationCanceledException();
}
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 43967ebac9ded3142b5e5f8cb8fdaf78
timeCreated: 1532361007
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,112 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using System;
using System.Runtime.CompilerServices;
namespace Cysharp.Threading.Tasks.Internal
{
// optimized version of Standard Queue<T>.
internal class MinimumQueue<T>
{
const int MinimumGrow = 4;
const int GrowFactor = 200;
T[] array;
int head;
int tail;
int size;
public MinimumQueue(int capacity)
{
if (capacity < 0) throw new ArgumentOutOfRangeException("capacity");
array = new T[capacity];
head = tail = size = 0;
}
public int Count
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return size; }
}
public T Peek()
{
if (size == 0) ThrowForEmptyQueue();
return array[head];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Enqueue(T item)
{
if (size == array.Length)
{
Grow();
}
array[tail] = item;
MoveNext(ref tail);
size++;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T Dequeue()
{
if (size == 0) ThrowForEmptyQueue();
int head = this.head;
T[] array = this.array;
T removed = array[head];
array[head] = default(T);
MoveNext(ref this.head);
size--;
return removed;
}
void Grow()
{
int newcapacity = (int)((long)array.Length * (long)GrowFactor / 100);
if (newcapacity < array.Length + MinimumGrow)
{
newcapacity = array.Length + MinimumGrow;
}
SetCapacity(newcapacity);
}
void SetCapacity(int capacity)
{
T[] newarray = new T[capacity];
if (size > 0)
{
if (head < tail)
{
Array.Copy(array, head, newarray, 0, size);
}
else
{
Array.Copy(array, head, newarray, 0, array.Length - head);
Array.Copy(array, 0, newarray, array.Length - head, tail);
}
}
array = newarray;
head = 0;
tail = (size == capacity) ? 0 : size;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
void MoveNext(ref int index)
{
int tmp = index + 1;
if (tmp == array.Length)
{
tmp = 0;
}
index = tmp;
}
void ThrowForEmptyQueue()
{
throw new InvalidOperationException("EmptyQueue");
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 12b749af9ddfa75448ecfab0a8455117
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,260 @@
using System;
using UnityEngine;
namespace Cysharp.Threading.Tasks.Internal
{
internal sealed class PlayerLoopRunner
{
const int InitialSize = 16;
readonly PlayerLoopTiming timing;
readonly object runningAndQueueLock = new object();
readonly object arrayLock = new object();
readonly Action<Exception> unhandledExceptionCallback;
int tail = 0;
bool running = false;
IPlayerLoopItem[] loopItems = new IPlayerLoopItem[InitialSize];
MinimumQueue<IPlayerLoopItem> waitQueue = new MinimumQueue<IPlayerLoopItem>(InitialSize);
public PlayerLoopRunner(PlayerLoopTiming timing)
{
this.unhandledExceptionCallback = ex => Debug.LogException(ex);
this.timing = timing;
}
public void AddAction(IPlayerLoopItem item)
{
lock (runningAndQueueLock)
{
if (running)
{
waitQueue.Enqueue(item);
return;
}
}
lock (arrayLock)
{
// Ensure Capacity
if (loopItems.Length == tail)
{
Array.Resize(ref loopItems, checked(tail * 2));
}
loopItems[tail++] = item;
}
}
public int Clear()
{
lock (arrayLock)
{
var rest = 0;
for (var index = 0; index < loopItems.Length; index++)
{
if (loopItems[index] != null)
{
rest++;
}
loopItems[index] = null;
}
tail = 0;
return rest;
}
}
// delegate entrypoint.
public void Run()
{
// for debugging, create named stacktrace.
#if DEBUG
switch (timing)
{
case PlayerLoopTiming.Initialization:
Initialization();
break;
case PlayerLoopTiming.LastInitialization:
LastInitialization();
break;
case PlayerLoopTiming.EarlyUpdate:
EarlyUpdate();
break;
case PlayerLoopTiming.LastEarlyUpdate:
LastEarlyUpdate();
break;
case PlayerLoopTiming.FixedUpdate:
FixedUpdate();
break;
case PlayerLoopTiming.LastFixedUpdate:
LastFixedUpdate();
break;
case PlayerLoopTiming.PreUpdate:
PreUpdate();
break;
case PlayerLoopTiming.LastPreUpdate:
LastPreUpdate();
break;
case PlayerLoopTiming.Update:
Update();
break;
case PlayerLoopTiming.LastUpdate:
LastUpdate();
break;
case PlayerLoopTiming.PreLateUpdate:
PreLateUpdate();
break;
case PlayerLoopTiming.LastPreLateUpdate:
LastPreLateUpdate();
break;
case PlayerLoopTiming.PostLateUpdate:
PostLateUpdate();
break;
case PlayerLoopTiming.LastPostLateUpdate:
LastPostLateUpdate();
break;
#if UNITY_2020_2_OR_NEWER
case PlayerLoopTiming.TimeUpdate:
TimeUpdate();
break;
case PlayerLoopTiming.LastTimeUpdate:
LastTimeUpdate();
break;
#endif
default:
break;
}
#else
RunCore();
#endif
}
void Initialization() => RunCore();
void LastInitialization() => RunCore();
void EarlyUpdate() => RunCore();
void LastEarlyUpdate() => RunCore();
void FixedUpdate() => RunCore();
void LastFixedUpdate() => RunCore();
void PreUpdate() => RunCore();
void LastPreUpdate() => RunCore();
void Update() => RunCore();
void LastUpdate() => RunCore();
void PreLateUpdate() => RunCore();
void LastPreLateUpdate() => RunCore();
void PostLateUpdate() => RunCore();
void LastPostLateUpdate() => RunCore();
#if UNITY_2020_2_OR_NEWER
void TimeUpdate() => RunCore();
void LastTimeUpdate() => RunCore();
#endif
[System.Diagnostics.DebuggerHidden]
void RunCore()
{
lock (runningAndQueueLock)
{
running = true;
}
lock (arrayLock)
{
var j = tail - 1;
for (int i = 0; i < loopItems.Length; i++)
{
var action = loopItems[i];
if (action != null)
{
try
{
if (!action.MoveNext())
{
loopItems[i] = null;
}
else
{
continue; // next i
}
}
catch (Exception ex)
{
loopItems[i] = null;
try
{
unhandledExceptionCallback(ex);
}
catch { }
}
}
// find null, loop from tail
while (i < j)
{
var fromTail = loopItems[j];
if (fromTail != null)
{
try
{
if (!fromTail.MoveNext())
{
loopItems[j] = null;
j--;
continue; // next j
}
else
{
// swap
loopItems[i] = fromTail;
loopItems[j] = null;
j--;
goto NEXT_LOOP; // next i
}
}
catch (Exception ex)
{
loopItems[j] = null;
j--;
try
{
unhandledExceptionCallback(ex);
}
catch { }
continue; // next j
}
}
else
{
j--;
}
}
tail = i; // loop end
break; // LOOP END
NEXT_LOOP:
continue;
}
lock (runningAndQueueLock)
{
running = false;
while (waitQueue.Count != 0)
{
if (loopItems.Length == tail)
{
Array.Resize(ref loopItems, checked(tail * 2));
}
loopItems[tail++] = waitQueue.Dequeue();
}
}
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 139766eb3c9a35b48ba6cbde17168599
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,50 @@
using System;
using System.Runtime.CompilerServices;
namespace Cysharp.Threading.Tasks.Internal
{
internal sealed class PooledDelegate<T> : ITaskPoolNode<PooledDelegate<T>>
{
static TaskPool<PooledDelegate<T>> pool;
PooledDelegate<T> nextNode;
public ref PooledDelegate<T> NextNode => ref nextNode;
static PooledDelegate()
{
TaskPool.RegisterSizeGetter(typeof(PooledDelegate<T>), () => pool.Size);
}
readonly Action<T> runDelegate;
Action continuation;
PooledDelegate()
{
runDelegate = Run;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Action<T> Create(Action continuation)
{
if (!pool.TryPop(out var item))
{
item = new PooledDelegate<T>();
}
item.continuation = continuation;
return item.runDelegate;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
void Run(T _)
{
var call = continuation;
continuation = null;
if (call != null)
{
pool.TryPush(this);
call.Invoke();
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9a80b075c09018f4e832c8b72af9d368
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,64 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using System;
#if UNITY_2018_3_OR_NEWER
using UnityEngine;
#endif
namespace Cysharp.Threading.Tasks.Internal
{
internal static class RuntimeHelpersAbstraction
{
// If we can use RuntimeHelpers.IsReferenceOrContainsReferences(.NET Core 2.0), use it.
public static bool IsWellKnownNoReferenceContainsType<T>()
{
return WellKnownNoReferenceContainsType<T>.IsWellKnownType;
}
static bool WellKnownNoReferenceContainsTypeInitialize(Type t)
{
// The primitive types are Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double, and Single.
if (t.IsPrimitive) return true;
if (t.IsEnum) return true;
if (t == typeof(DateTime)) return true;
if (t == typeof(DateTimeOffset)) return true;
if (t == typeof(Guid)) return true;
if (t == typeof(decimal)) return true;
// unwrap nullable
if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>))
{
return WellKnownNoReferenceContainsTypeInitialize(t.GetGenericArguments()[0]);
}
#if UNITY_2018_3_OR_NEWER
// or add other wellknown types(Vector, etc...) here
if (t == typeof(Vector2)) return true;
if (t == typeof(Vector3)) return true;
if (t == typeof(Vector4)) return true;
if (t == typeof(Color)) return true;
if (t == typeof(Rect)) return true;
if (t == typeof(Bounds)) return true;
if (t == typeof(Quaternion)) return true;
if (t == typeof(Vector2Int)) return true;
if (t == typeof(Vector3Int)) return true;
#endif
return false;
}
static class WellKnownNoReferenceContainsType<T>
{
public static readonly bool IsWellKnownType;
static WellKnownNoReferenceContainsType()
{
IsWellKnownType = WellKnownNoReferenceContainsTypeInitialize(typeof(T));
}
}
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 1ccc6cb5ba67921498cdbef7719f1ed2
timeCreated: 1532361007
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,153 @@
using System;
using System.Collections.Concurrent;
using System.Runtime.CompilerServices;
namespace Cysharp.Threading.Tasks.Internal
{
internal static class StateTuple
{
public static StateTuple<T1> Create<T1>(T1 item1)
{
return StatePool<T1>.Create(item1);
}
public static StateTuple<T1, T2> Create<T1, T2>(T1 item1, T2 item2)
{
return StatePool<T1, T2>.Create(item1, item2);
}
public static StateTuple<T1, T2, T3> Create<T1, T2, T3>(T1 item1, T2 item2, T3 item3)
{
return StatePool<T1, T2, T3>.Create(item1, item2, item3);
}
}
internal class StateTuple<T1> : IDisposable
{
public T1 Item1;
public void Deconstruct(out T1 item1)
{
item1 = this.Item1;
}
public void Dispose()
{
StatePool<T1>.Return(this);
}
}
internal static class StatePool<T1>
{
static readonly ConcurrentQueue<StateTuple<T1>> queue = new ConcurrentQueue<StateTuple<T1>>();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static StateTuple<T1> Create(T1 item1)
{
if (queue.TryDequeue(out var value))
{
value.Item1 = item1;
return value;
}
return new StateTuple<T1> { Item1 = item1 };
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Return(StateTuple<T1> tuple)
{
tuple.Item1 = default;
queue.Enqueue(tuple);
}
}
internal class StateTuple<T1, T2> : IDisposable
{
public T1 Item1;
public T2 Item2;
public void Deconstruct(out T1 item1, out T2 item2)
{
item1 = this.Item1;
item2 = this.Item2;
}
public void Dispose()
{
StatePool<T1, T2>.Return(this);
}
}
internal static class StatePool<T1, T2>
{
static readonly ConcurrentQueue<StateTuple<T1, T2>> queue = new ConcurrentQueue<StateTuple<T1, T2>>();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static StateTuple<T1, T2> Create(T1 item1, T2 item2)
{
if (queue.TryDequeue(out var value))
{
value.Item1 = item1;
value.Item2 = item2;
return value;
}
return new StateTuple<T1, T2> { Item1 = item1, Item2 = item2 };
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Return(StateTuple<T1, T2> tuple)
{
tuple.Item1 = default;
tuple.Item2 = default;
queue.Enqueue(tuple);
}
}
internal class StateTuple<T1, T2, T3> : IDisposable
{
public T1 Item1;
public T2 Item2;
public T3 Item3;
public void Deconstruct(out T1 item1, out T2 item2, out T3 item3)
{
item1 = this.Item1;
item2 = this.Item2;
item3 = this.Item3;
}
public void Dispose()
{
StatePool<T1, T2, T3>.Return(this);
}
}
internal static class StatePool<T1, T2, T3>
{
static readonly ConcurrentQueue<StateTuple<T1, T2, T3>> queue = new ConcurrentQueue<StateTuple<T1, T2, T3>>();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static StateTuple<T1, T2, T3> Create(T1 item1, T2 item2, T3 item3)
{
if (queue.TryDequeue(out var value))
{
value.Item1 = item1;
value.Item2 = item2;
value.Item3 = item3;
return value;
}
return new StateTuple<T1, T2, T3> { Item1 = item1, Item2 = item2, Item3 = item3 };
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Return(StateTuple<T1, T2, T3> tuple)
{
tuple.Item1 = default;
tuple.Item2 = default;
tuple.Item3 = default;
queue.Enqueue(tuple);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cbd926066666f724499bae246379967e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,267 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Cysharp.Threading.Tasks.Internal
{
internal static class UnityEqualityComparer
{
public static readonly IEqualityComparer<Vector2> Vector2 = new Vector2EqualityComparer();
public static readonly IEqualityComparer<Vector3> Vector3 = new Vector3EqualityComparer();
public static readonly IEqualityComparer<Vector4> Vector4 = new Vector4EqualityComparer();
public static readonly IEqualityComparer<Color> Color = new ColorEqualityComparer();
public static readonly IEqualityComparer<Color32> Color32 = new Color32EqualityComparer();
public static readonly IEqualityComparer<Rect> Rect = new RectEqualityComparer();
public static readonly IEqualityComparer<Bounds> Bounds = new BoundsEqualityComparer();
public static readonly IEqualityComparer<Quaternion> Quaternion = new QuaternionEqualityComparer();
static readonly RuntimeTypeHandle vector2Type = typeof(Vector2).TypeHandle;
static readonly RuntimeTypeHandle vector3Type = typeof(Vector3).TypeHandle;
static readonly RuntimeTypeHandle vector4Type = typeof(Vector4).TypeHandle;
static readonly RuntimeTypeHandle colorType = typeof(Color).TypeHandle;
static readonly RuntimeTypeHandle color32Type = typeof(Color32).TypeHandle;
static readonly RuntimeTypeHandle rectType = typeof(Rect).TypeHandle;
static readonly RuntimeTypeHandle boundsType = typeof(Bounds).TypeHandle;
static readonly RuntimeTypeHandle quaternionType = typeof(Quaternion).TypeHandle;
#if UNITY_2017_2_OR_NEWER
public static readonly IEqualityComparer<Vector2Int> Vector2Int = new Vector2IntEqualityComparer();
public static readonly IEqualityComparer<Vector3Int> Vector3Int = new Vector3IntEqualityComparer();
public static readonly IEqualityComparer<RangeInt> RangeInt = new RangeIntEqualityComparer();
public static readonly IEqualityComparer<RectInt> RectInt = new RectIntEqualityComparer();
public static readonly IEqualityComparer<BoundsInt> BoundsInt = new BoundsIntEqualityComparer();
static readonly RuntimeTypeHandle vector2IntType = typeof(Vector2Int).TypeHandle;
static readonly RuntimeTypeHandle vector3IntType = typeof(Vector3Int).TypeHandle;
static readonly RuntimeTypeHandle rangeIntType = typeof(RangeInt).TypeHandle;
static readonly RuntimeTypeHandle rectIntType = typeof(RectInt).TypeHandle;
static readonly RuntimeTypeHandle boundsIntType = typeof(BoundsInt).TypeHandle;
#endif
static class Cache<T>
{
public static readonly IEqualityComparer<T> Comparer;
static Cache()
{
var comparer = GetDefaultHelper(typeof(T));
if (comparer == null)
{
Comparer = EqualityComparer<T>.Default;
}
else
{
Comparer = (IEqualityComparer<T>)comparer;
}
}
}
public static IEqualityComparer<T> GetDefault<T>()
{
return Cache<T>.Comparer;
}
static object GetDefaultHelper(Type type)
{
var t = type.TypeHandle;
if (t.Equals(vector2Type)) return (object)UnityEqualityComparer.Vector2;
if (t.Equals(vector3Type)) return (object)UnityEqualityComparer.Vector3;
if (t.Equals(vector4Type)) return (object)UnityEqualityComparer.Vector4;
if (t.Equals(colorType)) return (object)UnityEqualityComparer.Color;
if (t.Equals(color32Type)) return (object)UnityEqualityComparer.Color32;
if (t.Equals(rectType)) return (object)UnityEqualityComparer.Rect;
if (t.Equals(boundsType)) return (object)UnityEqualityComparer.Bounds;
if (t.Equals(quaternionType)) return (object)UnityEqualityComparer.Quaternion;
#if UNITY_2017_2_OR_NEWER
if (t.Equals(vector2IntType)) return (object)UnityEqualityComparer.Vector2Int;
if (t.Equals(vector3IntType)) return (object)UnityEqualityComparer.Vector3Int;
if (t.Equals(rangeIntType)) return (object)UnityEqualityComparer.RangeInt;
if (t.Equals(rectIntType)) return (object)UnityEqualityComparer.RectInt;
if (t.Equals(boundsIntType)) return (object)UnityEqualityComparer.BoundsInt;
#endif
return null;
}
sealed class Vector2EqualityComparer : IEqualityComparer<Vector2>
{
public bool Equals(Vector2 self, Vector2 vector)
{
return self.x.Equals(vector.x) && self.y.Equals(vector.y);
}
public int GetHashCode(Vector2 obj)
{
return obj.x.GetHashCode() ^ obj.y.GetHashCode() << 2;
}
}
sealed class Vector3EqualityComparer : IEqualityComparer<Vector3>
{
public bool Equals(Vector3 self, Vector3 vector)
{
return self.x.Equals(vector.x) && self.y.Equals(vector.y) && self.z.Equals(vector.z);
}
public int GetHashCode(Vector3 obj)
{
return obj.x.GetHashCode() ^ obj.y.GetHashCode() << 2 ^ obj.z.GetHashCode() >> 2;
}
}
sealed class Vector4EqualityComparer : IEqualityComparer<Vector4>
{
public bool Equals(Vector4 self, Vector4 vector)
{
return self.x.Equals(vector.x) && self.y.Equals(vector.y) && self.z.Equals(vector.z) && self.w.Equals(vector.w);
}
public int GetHashCode(Vector4 obj)
{
return obj.x.GetHashCode() ^ obj.y.GetHashCode() << 2 ^ obj.z.GetHashCode() >> 2 ^ obj.w.GetHashCode() >> 1;
}
}
sealed class ColorEqualityComparer : IEqualityComparer<Color>
{
public bool Equals(Color self, Color other)
{
return self.r.Equals(other.r) && self.g.Equals(other.g) && self.b.Equals(other.b) && self.a.Equals(other.a);
}
public int GetHashCode(Color obj)
{
return obj.r.GetHashCode() ^ obj.g.GetHashCode() << 2 ^ obj.b.GetHashCode() >> 2 ^ obj.a.GetHashCode() >> 1;
}
}
sealed class RectEqualityComparer : IEqualityComparer<Rect>
{
public bool Equals(Rect self, Rect other)
{
return self.x.Equals(other.x) && self.width.Equals(other.width) && self.y.Equals(other.y) && self.height.Equals(other.height);
}
public int GetHashCode(Rect obj)
{
return obj.x.GetHashCode() ^ obj.width.GetHashCode() << 2 ^ obj.y.GetHashCode() >> 2 ^ obj.height.GetHashCode() >> 1;
}
}
sealed class BoundsEqualityComparer : IEqualityComparer<Bounds>
{
public bool Equals(Bounds self, Bounds vector)
{
return self.center.Equals(vector.center) && self.extents.Equals(vector.extents);
}
public int GetHashCode(Bounds obj)
{
return obj.center.GetHashCode() ^ obj.extents.GetHashCode() << 2;
}
}
sealed class QuaternionEqualityComparer : IEqualityComparer<Quaternion>
{
public bool Equals(Quaternion self, Quaternion vector)
{
return self.x.Equals(vector.x) && self.y.Equals(vector.y) && self.z.Equals(vector.z) && self.w.Equals(vector.w);
}
public int GetHashCode(Quaternion obj)
{
return obj.x.GetHashCode() ^ obj.y.GetHashCode() << 2 ^ obj.z.GetHashCode() >> 2 ^ obj.w.GetHashCode() >> 1;
}
}
sealed class Color32EqualityComparer : IEqualityComparer<Color32>
{
public bool Equals(Color32 self, Color32 vector)
{
return self.a.Equals(vector.a) && self.r.Equals(vector.r) && self.g.Equals(vector.g) && self.b.Equals(vector.b);
}
public int GetHashCode(Color32 obj)
{
return obj.a.GetHashCode() ^ obj.r.GetHashCode() << 2 ^ obj.g.GetHashCode() >> 2 ^ obj.b.GetHashCode() >> 1;
}
}
#if UNITY_2017_2_OR_NEWER
sealed class Vector2IntEqualityComparer : IEqualityComparer<Vector2Int>
{
public bool Equals(Vector2Int self, Vector2Int vector)
{
return self.x.Equals(vector.x) && self.y.Equals(vector.y);
}
public int GetHashCode(Vector2Int obj)
{
return obj.x.GetHashCode() ^ obj.y.GetHashCode() << 2;
}
}
sealed class Vector3IntEqualityComparer : IEqualityComparer<Vector3Int>
{
public static readonly Vector3IntEqualityComparer Default = new Vector3IntEqualityComparer();
public bool Equals(Vector3Int self, Vector3Int vector)
{
return self.x.Equals(vector.x) && self.y.Equals(vector.y) && self.z.Equals(vector.z);
}
public int GetHashCode(Vector3Int obj)
{
return obj.x.GetHashCode() ^ obj.y.GetHashCode() << 2 ^ obj.z.GetHashCode() >> 2;
}
}
sealed class RangeIntEqualityComparer : IEqualityComparer<RangeInt>
{
public bool Equals(RangeInt self, RangeInt vector)
{
return self.start.Equals(vector.start) && self.length.Equals(vector.length);
}
public int GetHashCode(RangeInt obj)
{
return obj.start.GetHashCode() ^ obj.length.GetHashCode() << 2;
}
}
sealed class RectIntEqualityComparer : IEqualityComparer<RectInt>
{
public bool Equals(RectInt self, RectInt other)
{
return self.x.Equals(other.x) && self.width.Equals(other.width) && self.y.Equals(other.y) && self.height.Equals(other.height);
}
public int GetHashCode(RectInt obj)
{
return obj.x.GetHashCode() ^ obj.width.GetHashCode() << 2 ^ obj.y.GetHashCode() >> 2 ^ obj.height.GetHashCode() >> 1;
}
}
sealed class BoundsIntEqualityComparer : IEqualityComparer<BoundsInt>
{
public bool Equals(BoundsInt self, BoundsInt vector)
{
return Vector3IntEqualityComparer.Default.Equals(self.position, vector.position)
&& Vector3IntEqualityComparer.Default.Equals(self.size, vector.size);
}
public int GetHashCode(BoundsInt obj)
{
return Vector3IntEqualityComparer.Default.GetHashCode(obj.position) ^ Vector3IntEqualityComparer.Default.GetHashCode(obj.size) << 2;
}
}
#endif
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 62c8aa35a9a3bb1449483710011e8882
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine.Networking;
namespace Cysharp.Threading.Tasks.Internal
{
#if ENABLE_UNITYWEBREQUEST && (!UNITY_2019_1_OR_NEWER || UNITASK_WEBREQUEST_SUPPORT)
internal static class UnityWebRequestResultExtensions
{
public static bool IsError(this UnityWebRequest unityWebRequest)
{
#if UNITY_2020_2_OR_NEWER
var result = unityWebRequest.result;
return (result == UnityWebRequest.Result.ConnectionError)
|| (result == UnityWebRequest.Result.DataProcessingError)
|| (result == UnityWebRequest.Result.ProtocolError);
#else
return unityWebRequest.isHttpError || unityWebRequest.isNetworkError;
#endif
}
}
#endif
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 982fde32db9f15443a46e8031b2d35a4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,37 @@
using System;
using System.Diagnostics;
namespace Cysharp.Threading.Tasks.Internal
{
internal readonly struct ValueStopwatch
{
static readonly double TimestampToTicks = TimeSpan.TicksPerSecond / (double)Stopwatch.Frequency;
readonly long startTimestamp;
public static ValueStopwatch StartNew() => new ValueStopwatch(Stopwatch.GetTimestamp());
ValueStopwatch(long startTimestamp)
{
this.startTimestamp = startTimestamp;
}
public TimeSpan Elapsed => TimeSpan.FromTicks(this.ElapsedTicks);
public bool IsInvalid => startTimestamp == 0;
public long ElapsedTicks
{
get
{
if (startTimestamp == 0)
{
throw new InvalidOperationException("Detected invalid initialization(use 'default'), only to create from StartNew().");
}
var delta = Stopwatch.GetTimestamp() - startTimestamp;
return (long)(delta * TimestampToTicks);
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: aa100fe45f92ded40a7a4e56ec8498ac
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,334 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using System;
using System.Collections.Generic;
using System.Threading;
namespace Cysharp.Threading.Tasks.Internal
{
// Add, Remove, Enumerate with sweep. All operations are thread safe(in spinlock).
internal class WeakDictionary<TKey, TValue>
where TKey : class
{
Entry[] buckets;
int size;
SpinLock gate; // mutable struct(not readonly)
readonly float loadFactor;
readonly IEqualityComparer<TKey> keyEqualityComparer;
public WeakDictionary(int capacity = 4, float loadFactor = 0.75f, IEqualityComparer<TKey> keyComparer = null)
{
var tableSize = CalculateCapacity(capacity, loadFactor);
this.buckets = new Entry[tableSize];
this.loadFactor = loadFactor;
this.gate = new SpinLock(false);
this.keyEqualityComparer = keyComparer ?? EqualityComparer<TKey>.Default;
}
public bool TryAdd(TKey key, TValue value)
{
bool lockTaken = false;
try
{
gate.Enter(ref lockTaken);
return TryAddInternal(key, value);
}
finally
{
if (lockTaken) gate.Exit(false);
}
}
public bool TryGetValue(TKey key, out TValue value)
{
bool lockTaken = false;
try
{
gate.Enter(ref lockTaken);
if (TryGetEntry(key, out _, out var entry))
{
value = entry.Value;
return true;
}
value = default(TValue);
return false;
}
finally
{
if (lockTaken) gate.Exit(false);
}
}
public bool TryRemove(TKey key)
{
bool lockTaken = false;
try
{
gate.Enter(ref lockTaken);
if (TryGetEntry(key, out var hashIndex, out var entry))
{
Remove(hashIndex, entry);
return true;
}
return false;
}
finally
{
if (lockTaken) gate.Exit(false);
}
}
bool TryAddInternal(TKey key, TValue value)
{
var nextCapacity = CalculateCapacity(size + 1, loadFactor);
TRY_ADD_AGAIN:
if (buckets.Length < nextCapacity)
{
// rehash
var nextBucket = new Entry[nextCapacity];
for (int i = 0; i < buckets.Length; i++)
{
var e = buckets[i];
while (e != null)
{
AddToBuckets(nextBucket, key, e.Value, e.Hash);
e = e.Next;
}
}
buckets = nextBucket;
goto TRY_ADD_AGAIN;
}
else
{
// add entry
var successAdd = AddToBuckets(buckets, key, value, keyEqualityComparer.GetHashCode(key));
if (successAdd) size++;
return successAdd;
}
}
bool AddToBuckets(Entry[] targetBuckets, TKey newKey, TValue value, int keyHash)
{
var h = keyHash;
var hashIndex = h & (targetBuckets.Length - 1);
TRY_ADD_AGAIN:
if (targetBuckets[hashIndex] == null)
{
targetBuckets[hashIndex] = new Entry
{
Key = new WeakReference<TKey>(newKey, false),
Value = value,
Hash = h
};
return true;
}
else
{
// add to last.
var entry = targetBuckets[hashIndex];
while (entry != null)
{
if (entry.Key.TryGetTarget(out var target))
{
if (keyEqualityComparer.Equals(newKey, target))
{
return false; // duplicate
}
}
else
{
Remove(hashIndex, entry);
if (targetBuckets[hashIndex] == null) goto TRY_ADD_AGAIN; // add new entry
}
if (entry.Next != null)
{
entry = entry.Next;
}
else
{
// found last
entry.Next = new Entry
{
Key = new WeakReference<TKey>(newKey, false),
Value = value,
Hash = h
};
entry.Next.Prev = entry;
}
}
return false;
}
}
bool TryGetEntry(TKey key, out int hashIndex, out Entry entry)
{
var table = buckets;
var hash = keyEqualityComparer.GetHashCode(key);
hashIndex = hash & table.Length - 1;
entry = table[hashIndex];
while (entry != null)
{
if (entry.Key.TryGetTarget(out var target))
{
if (keyEqualityComparer.Equals(key, target))
{
return true;
}
}
else
{
// sweap
Remove(hashIndex, entry);
}
entry = entry.Next;
}
return false;
}
void Remove(int hashIndex, Entry entry)
{
if (entry.Prev == null && entry.Next == null)
{
buckets[hashIndex] = null;
}
else
{
if (entry.Prev == null)
{
buckets[hashIndex] = entry.Next;
}
if (entry.Prev != null)
{
entry.Prev.Next = entry.Next;
}
if (entry.Next != null)
{
entry.Next.Prev = entry.Prev;
}
}
size--;
}
public List<KeyValuePair<TKey, TValue>> ToList()
{
var list = new List<KeyValuePair<TKey, TValue>>(size);
ToList(ref list, false);
return list;
}
// avoid allocate everytime.
public int ToList(ref List<KeyValuePair<TKey, TValue>> list, bool clear = true)
{
if (clear)
{
list.Clear();
}
var listIndex = 0;
bool lockTaken = false;
try
{
for (int i = 0; i < buckets.Length; i++)
{
var entry = buckets[i];
while (entry != null)
{
if (entry.Key.TryGetTarget(out var target))
{
var item = new KeyValuePair<TKey, TValue>(target, entry.Value);
if (listIndex < list.Count)
{
list[listIndex++] = item;
}
else
{
list.Add(item);
listIndex++;
}
}
else
{
// sweap
Remove(i, entry);
}
entry = entry.Next;
}
}
}
finally
{
if (lockTaken) gate.Exit(false);
}
return listIndex;
}
static int CalculateCapacity(int collectionSize, float loadFactor)
{
var size = (int)(((float)collectionSize) / loadFactor);
size--;
size |= size >> 1;
size |= size >> 2;
size |= size >> 4;
size |= size >> 8;
size |= size >> 16;
size += 1;
if (size < 8)
{
size = 8;
}
return size;
}
class Entry
{
public WeakReference<TKey> Key;
public TValue Value;
public int Hash;
public Entry Prev;
public Entry Next;
// debug only
public override string ToString()
{
if (Key.TryGetTarget(out var target))
{
return target + "(" + Count() + ")";
}
else
{
return "(Dead)";
}
}
int Count()
{
var count = 1;
var n = this;
while (n.Next != null)
{
count++;
n = n.Next;
}
return count;
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2e253d29fde70c84482142fb5170182f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e1613a84b5498e50d409a08030dee741
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,32 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using System.Threading;
using UnityEngine;
namespace Cysharp.Threading.Tasks.Triggers
{
public static partial class AsyncTriggerExtensions
{
public static AsyncAwakeTrigger GetAsyncAwakeTrigger(this GameObject gameObject)
{
return GetOrAddComponent<AsyncAwakeTrigger>(gameObject);
}
public static AsyncAwakeTrigger GetAsyncAwakeTrigger(this Component component)
{
return component.gameObject.GetAsyncAwakeTrigger();
}
}
[DisallowMultipleComponent]
public sealed class AsyncAwakeTrigger : AsyncTriggerBase<AsyncUnit>
{
public UniTask AwakeAsync()
{
if (calledAwake) return UniTask.CompletedTask;
return ((IAsyncOneShotTrigger)new AsyncTriggerHandler<AsyncUnit>(this, true)).OneShotAsync();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 912f60e8013aa8a48ab69e2145e3a79e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,97 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using System.Threading;
using UnityEngine;
namespace Cysharp.Threading.Tasks.Triggers
{
public static partial class AsyncTriggerExtensions
{
public static AsyncDestroyTrigger GetAsyncDestroyTrigger(this GameObject gameObject)
{
return GetOrAddComponent<AsyncDestroyTrigger>(gameObject);
}
public static AsyncDestroyTrigger GetAsyncDestroyTrigger(this Component component)
{
return component.gameObject.GetAsyncDestroyTrigger();
}
}
[DisallowMultipleComponent]
public sealed class AsyncDestroyTrigger : MonoBehaviour
{
bool awakeCalled = false;
bool called = false;
CancellationTokenSource cancellationTokenSource;
public CancellationToken CancellationToken
{
get
{
if (cancellationTokenSource == null)
{
cancellationTokenSource = new CancellationTokenSource();
}
if (!awakeCalled)
{
PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, new AwakeMonitor(this));
}
return cancellationTokenSource.Token;
}
}
void Awake()
{
awakeCalled = true;
}
void OnDestroy()
{
called = true;
cancellationTokenSource?.Cancel();
cancellationTokenSource?.Dispose();
}
public UniTask OnDestroyAsync()
{
if (called) return UniTask.CompletedTask;
var tcs = new UniTaskCompletionSource();
// OnDestroy = Called Cancel.
CancellationToken.RegisterWithoutCaptureExecutionContext(state =>
{
var tcs2 = (UniTaskCompletionSource)state;
tcs2.TrySetResult();
}, tcs);
return tcs.Task;
}
class AwakeMonitor : IPlayerLoopItem
{
readonly AsyncDestroyTrigger trigger;
public AwakeMonitor(AsyncDestroyTrigger trigger)
{
this.trigger = trigger;
}
public bool MoveNext()
{
if (trigger.called) return false;
if (trigger == null)
{
trigger.OnDestroy();
return false;
}
return true;
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5c356ea8df7376241976ae134790635c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,38 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using UnityEngine;
namespace Cysharp.Threading.Tasks.Triggers
{
public static partial class AsyncTriggerExtensions
{
public static AsyncStartTrigger GetAsyncStartTrigger(this GameObject gameObject)
{
return GetOrAddComponent<AsyncStartTrigger>(gameObject);
}
public static AsyncStartTrigger GetAsyncStartTrigger(this Component component)
{
return component.gameObject.GetAsyncStartTrigger();
}
}
[DisallowMultipleComponent]
public sealed class AsyncStartTrigger : AsyncTriggerBase<AsyncUnit>
{
bool called;
void Start()
{
called = true;
RaiseEvent(AsyncUnit.Default);
}
public UniTask StartAsync()
{
if (called) return UniTask.CompletedTask;
return ((IAsyncOneShotTrigger)new AsyncTriggerHandler<AsyncUnit>(this, true)).OneShotAsync();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 18b975300e009e440ada38ab80f5e35d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,310 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using System;
using System.Threading;
using UnityEngine;
namespace Cysharp.Threading.Tasks.Triggers
{
public abstract class AsyncTriggerBase<T> : MonoBehaviour, IUniTaskAsyncEnumerable<T>
{
TriggerEvent<T> triggerEvent;
internal protected bool calledAwake;
internal protected bool calledDestroy;
void Awake()
{
calledAwake = true;
}
void OnDestroy()
{
if (calledDestroy) return;
calledDestroy = true;
triggerEvent.SetCompleted();
}
internal void AddHandler(ITriggerHandler<T> handler)
{
if (!calledAwake)
{
PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, new AwakeMonitor(this));
}
triggerEvent.Add(handler);
}
internal void RemoveHandler(ITriggerHandler<T> handler)
{
if (!calledAwake)
{
PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, new AwakeMonitor(this));
}
triggerEvent.Remove(handler);
}
protected void RaiseEvent(T value)
{
triggerEvent.SetResult(value);
}
public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
return new AsyncTriggerEnumerator(this, cancellationToken);
}
sealed class AsyncTriggerEnumerator : MoveNextSource, IUniTaskAsyncEnumerator<T>, ITriggerHandler<T>
{
static Action<object> cancellationCallback = CancellationCallback;
readonly AsyncTriggerBase<T> parent;
CancellationToken cancellationToken;
CancellationTokenRegistration registration;
bool called;
bool isDisposed;
public AsyncTriggerEnumerator(AsyncTriggerBase<T> parent, CancellationToken cancellationToken)
{
this.parent = parent;
this.cancellationToken = cancellationToken;
}
public void OnCanceled(CancellationToken cancellationToken = default)
{
completionSource.TrySetCanceled(cancellationToken);
}
public void OnNext(T value)
{
Current = value;
completionSource.TrySetResult(true);
}
public void OnCompleted()
{
completionSource.TrySetResult(false);
}
public void OnError(Exception ex)
{
completionSource.TrySetException(ex);
}
static void CancellationCallback(object state)
{
var self = (AsyncTriggerEnumerator)state;
self.DisposeAsync().Forget(); // sync
self.completionSource.TrySetCanceled(self.cancellationToken);
}
public T Current { get; private set; }
ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; }
ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; }
public UniTask<bool> MoveNextAsync()
{
cancellationToken.ThrowIfCancellationRequested();
completionSource.Reset();
if (!called)
{
called = true;
TaskTracker.TrackActiveTask(this, 3);
parent.AddHandler(this);
if (cancellationToken.CanBeCanceled)
{
registration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, this);
}
}
return new UniTask<bool>(this, completionSource.Version);
}
public UniTask DisposeAsync()
{
if (!isDisposed)
{
isDisposed = true;
TaskTracker.RemoveTracking(this);
registration.Dispose();
parent.RemoveHandler(this);
}
return default;
}
}
class AwakeMonitor : IPlayerLoopItem
{
readonly AsyncTriggerBase<T> trigger;
public AwakeMonitor(AsyncTriggerBase<T> trigger)
{
this.trigger = trigger;
}
public bool MoveNext()
{
if (trigger.calledAwake) return false;
if (trigger == null)
{
trigger.OnDestroy();
return false;
}
return true;
}
}
}
public interface IAsyncOneShotTrigger
{
UniTask OneShotAsync();
}
public partial class AsyncTriggerHandler<T> : IAsyncOneShotTrigger
{
UniTask IAsyncOneShotTrigger.OneShotAsync()
{
core.Reset();
return new UniTask((IUniTaskSource)this, core.Version);
}
}
public sealed partial class AsyncTriggerHandler<T> : IUniTaskSource<T>, ITriggerHandler<T>, IDisposable
{
static Action<object> cancellationCallback = CancellationCallback;
readonly AsyncTriggerBase<T> trigger;
CancellationToken cancellationToken;
CancellationTokenRegistration registration;
bool isDisposed;
bool callOnce;
UniTaskCompletionSourceCore<T> core;
internal CancellationToken CancellationToken => cancellationToken;
ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; }
ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; }
internal AsyncTriggerHandler(AsyncTriggerBase<T> trigger, bool callOnce)
{
if (cancellationToken.IsCancellationRequested)
{
isDisposed = true;
return;
}
this.trigger = trigger;
this.cancellationToken = default;
this.registration = default;
this.callOnce = callOnce;
trigger.AddHandler(this);
TaskTracker.TrackActiveTask(this, 3);
}
internal AsyncTriggerHandler(AsyncTriggerBase<T> trigger, CancellationToken cancellationToken, bool callOnce)
{
if (cancellationToken.IsCancellationRequested)
{
isDisposed = true;
return;
}
this.trigger = trigger;
this.cancellationToken = cancellationToken;
this.callOnce = callOnce;
trigger.AddHandler(this);
if (cancellationToken.CanBeCanceled)
{
registration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, this);
}
TaskTracker.TrackActiveTask(this, 3);
}
static void CancellationCallback(object state)
{
var self = (AsyncTriggerHandler<T>)state;
self.Dispose();
self.core.TrySetCanceled(self.cancellationToken);
}
public void Dispose()
{
if (!isDisposed)
{
isDisposed = true;
TaskTracker.RemoveTracking(this);
registration.Dispose();
trigger.RemoveHandler(this);
}
}
T IUniTaskSource<T>.GetResult(short token)
{
try
{
return core.GetResult(token);
}
finally
{
if (callOnce)
{
Dispose();
}
}
}
void ITriggerHandler<T>.OnNext(T value)
{
core.TrySetResult(value);
}
void ITriggerHandler<T>.OnCanceled(CancellationToken cancellationToken)
{
core.TrySetCanceled(cancellationToken);
}
void ITriggerHandler<T>.OnCompleted()
{
core.TrySetCanceled(CancellationToken.None);
}
void ITriggerHandler<T>.OnError(Exception ex)
{
core.TrySetException(ex);
}
void IUniTaskSource.GetResult(short token)
{
((IUniTaskSource<T>)this).GetResult(token);
}
UniTaskStatus IUniTaskSource.GetStatus(short token)
{
return core.GetStatus(token);
}
UniTaskStatus IUniTaskSource.UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
void IUniTaskSource.OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: aabd3a47404896e48a366237171ca755
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e610276658863d244a4f0c911d46da47
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: bd927b49e3149c78d41c6cb45615d789
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,245 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using System;
using System.Threading;
namespace Cysharp.Threading.Tasks
{
public class AsyncLazy
{
static Action<object> continuation = SetCompletionSource;
Func<UniTask> taskFactory;
UniTaskCompletionSource completionSource;
UniTask.Awaiter awaiter;
object syncLock;
bool initialized;
public AsyncLazy(Func<UniTask> taskFactory)
{
this.taskFactory = taskFactory;
this.completionSource = new UniTaskCompletionSource();
this.syncLock = new object();
this.initialized = false;
}
internal AsyncLazy(UniTask task)
{
this.taskFactory = null;
this.completionSource = new UniTaskCompletionSource();
this.syncLock = null;
this.initialized = true;
var awaiter = task.GetAwaiter();
if (awaiter.IsCompleted)
{
SetCompletionSource(awaiter);
}
else
{
this.awaiter = awaiter;
awaiter.SourceOnCompleted(continuation, this);
}
}
public UniTask Task
{
get
{
EnsureInitialized();
return completionSource.Task;
}
}
public UniTask.Awaiter GetAwaiter() => Task.GetAwaiter();
void EnsureInitialized()
{
if (Volatile.Read(ref initialized))
{
return;
}
EnsureInitializedCore();
}
void EnsureInitializedCore()
{
lock (syncLock)
{
if (!Volatile.Read(ref initialized))
{
var f = Interlocked.Exchange(ref taskFactory, null);
if (f != null)
{
var task = f();
var awaiter = task.GetAwaiter();
if (awaiter.IsCompleted)
{
SetCompletionSource(awaiter);
}
else
{
this.awaiter = awaiter;
awaiter.SourceOnCompleted(continuation, this);
}
Volatile.Write(ref initialized, true);
}
}
}
}
void SetCompletionSource(in UniTask.Awaiter awaiter)
{
try
{
awaiter.GetResult();
completionSource.TrySetResult();
}
catch (Exception ex)
{
completionSource.TrySetException(ex);
}
}
static void SetCompletionSource(object state)
{
var self = (AsyncLazy)state;
try
{
self.awaiter.GetResult();
self.completionSource.TrySetResult();
}
catch (Exception ex)
{
self.completionSource.TrySetException(ex);
}
finally
{
self.awaiter = default;
}
}
}
public class AsyncLazy<T>
{
static Action<object> continuation = SetCompletionSource;
Func<UniTask<T>> taskFactory;
UniTaskCompletionSource<T> completionSource;
UniTask<T>.Awaiter awaiter;
object syncLock;
bool initialized;
public AsyncLazy(Func<UniTask<T>> taskFactory)
{
this.taskFactory = taskFactory;
this.completionSource = new UniTaskCompletionSource<T>();
this.syncLock = new object();
this.initialized = false;
}
internal AsyncLazy(UniTask<T> task)
{
this.taskFactory = null;
this.completionSource = new UniTaskCompletionSource<T>();
this.syncLock = null;
this.initialized = true;
var awaiter = task.GetAwaiter();
if (awaiter.IsCompleted)
{
SetCompletionSource(awaiter);
}
else
{
this.awaiter = awaiter;
awaiter.SourceOnCompleted(continuation, this);
}
}
public UniTask<T> Task
{
get
{
EnsureInitialized();
return completionSource.Task;
}
}
public UniTask<T>.Awaiter GetAwaiter() => Task.GetAwaiter();
void EnsureInitialized()
{
if (Volatile.Read(ref initialized))
{
return;
}
EnsureInitializedCore();
}
void EnsureInitializedCore()
{
lock (syncLock)
{
if (!Volatile.Read(ref initialized))
{
var f = Interlocked.Exchange(ref taskFactory, null);
if (f != null)
{
var task = f();
var awaiter = task.GetAwaiter();
if (awaiter.IsCompleted)
{
SetCompletionSource(awaiter);
}
else
{
this.awaiter = awaiter;
awaiter.SourceOnCompleted(continuation, this);
}
Volatile.Write(ref initialized, true);
}
}
}
}
void SetCompletionSource(in UniTask<T>.Awaiter awaiter)
{
try
{
var result = awaiter.GetResult();
completionSource.TrySetResult(result);
}
catch (Exception ex)
{
completionSource.TrySetException(ex);
}
}
static void SetCompletionSource(object state)
{
var self = (AsyncLazy<T>)state;
try
{
var result = self.awaiter.GetResult();
self.completionSource.TrySetResult(result);
}
catch (Exception ex)
{
self.completionSource.TrySetException(ex);
}
finally
{
self.awaiter = default;
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 32ec52743bb89e94b80c337ca7e1b98c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,644 @@
using System;
using System.Threading;
namespace Cysharp.Threading.Tasks
{
public interface IReadOnlyAsyncReactiveProperty<T> : IUniTaskAsyncEnumerable<T>
{
T Value { get; }
IUniTaskAsyncEnumerable<T> WithoutCurrent();
UniTask<T> WaitAsync(CancellationToken cancellationToken = default);
}
public interface IAsyncReactiveProperty<T> : IReadOnlyAsyncReactiveProperty<T>
{
new T Value { get; set; }
}
[Serializable]
public class AsyncReactiveProperty<T> : IAsyncReactiveProperty<T>, IDisposable
{
TriggerEvent<T> triggerEvent;
#if UNITY_2018_3_OR_NEWER
[UnityEngine.SerializeField]
#endif
T latestValue;
public T Value
{
get
{
return latestValue;
}
set
{
this.latestValue = value;
triggerEvent.SetResult(value);
}
}
public AsyncReactiveProperty(T value)
{
this.latestValue = value;
this.triggerEvent = default;
}
public IUniTaskAsyncEnumerable<T> WithoutCurrent()
{
return new WithoutCurrentEnumerable(this);
}
public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken)
{
return new Enumerator(this, cancellationToken, true);
}
public void Dispose()
{
triggerEvent.SetCompleted();
}
public static implicit operator T(AsyncReactiveProperty<T> value)
{
return value.Value;
}
public override string ToString()
{
if (isValueType) return latestValue.ToString();
return latestValue?.ToString();
}
public UniTask<T> WaitAsync(CancellationToken cancellationToken = default)
{
return new UniTask<T>(WaitAsyncSource.Create(this, cancellationToken, out var token), token);
}
static bool isValueType;
static AsyncReactiveProperty()
{
isValueType = typeof(T).IsValueType;
}
sealed class WaitAsyncSource : IUniTaskSource<T>, ITriggerHandler<T>, ITaskPoolNode<WaitAsyncSource>
{
static Action<object> cancellationCallback = CancellationCallback;
static TaskPool<WaitAsyncSource> pool;
WaitAsyncSource nextNode;
ref WaitAsyncSource ITaskPoolNode<WaitAsyncSource>.NextNode => ref nextNode;
static WaitAsyncSource()
{
TaskPool.RegisterSizeGetter(typeof(WaitAsyncSource), () => pool.Size);
}
AsyncReactiveProperty<T> parent;
CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
UniTaskCompletionSourceCore<T> core;
WaitAsyncSource()
{
}
public static IUniTaskSource<T> Create(AsyncReactiveProperty<T> parent, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource<T>.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new WaitAsyncSource();
}
result.parent = parent;
result.cancellationToken = cancellationToken;
if (cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, result);
}
result.parent.triggerEvent.Add(result);
TaskTracker.TrackActiveTask(result, 3);
token = result.core.Version;
return result;
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
cancellationTokenRegistration.Dispose();
cancellationTokenRegistration = default;
parent.triggerEvent.Remove(this);
parent = null;
cancellationToken = default;
return pool.TryPush(this);
}
static void CancellationCallback(object state)
{
var self = (WaitAsyncSource)state;
self.OnCanceled(self.cancellationToken);
}
// IUniTaskSource
public T GetResult(short token)
{
try
{
return core.GetResult(token);
}
finally
{
TryReturn();
}
}
void IUniTaskSource.GetResult(short token)
{
GetResult(token);
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
// ITriggerHandler
ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; }
ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; }
public void OnCanceled(CancellationToken cancellationToken)
{
core.TrySetCanceled(cancellationToken);
}
public void OnCompleted()
{
// Complete as Cancel.
core.TrySetCanceled(CancellationToken.None);
}
public void OnError(Exception ex)
{
core.TrySetException(ex);
}
public void OnNext(T value)
{
core.TrySetResult(value);
}
}
sealed class WithoutCurrentEnumerable : IUniTaskAsyncEnumerable<T>
{
readonly AsyncReactiveProperty<T> parent;
public WithoutCurrentEnumerable(AsyncReactiveProperty<T> parent)
{
this.parent = parent;
}
public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
return new Enumerator(parent, cancellationToken, false);
}
}
sealed class Enumerator : MoveNextSource, IUniTaskAsyncEnumerator<T>, ITriggerHandler<T>
{
static Action<object> cancellationCallback = CancellationCallback;
readonly AsyncReactiveProperty<T> parent;
readonly CancellationToken cancellationToken;
readonly CancellationTokenRegistration cancellationTokenRegistration;
T value;
bool isDisposed;
bool firstCall;
public Enumerator(AsyncReactiveProperty<T> parent, CancellationToken cancellationToken, bool publishCurrentValue)
{
this.parent = parent;
this.cancellationToken = cancellationToken;
this.firstCall = publishCurrentValue;
parent.triggerEvent.Add(this);
TaskTracker.TrackActiveTask(this, 3);
if (cancellationToken.CanBeCanceled)
{
cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, this);
}
}
public T Current => value;
ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; }
ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; }
public UniTask<bool> MoveNextAsync()
{
// raise latest value on first call.
if (firstCall)
{
firstCall = false;
value = parent.Value;
return CompletedTasks.True;
}
completionSource.Reset();
return new UniTask<bool>(this, completionSource.Version);
}
public UniTask DisposeAsync()
{
if (!isDisposed)
{
isDisposed = true;
TaskTracker.RemoveTracking(this);
completionSource.TrySetCanceled(cancellationToken);
parent.triggerEvent.Remove(this);
}
return default;
}
public void OnNext(T value)
{
this.value = value;
completionSource.TrySetResult(true);
}
public void OnCanceled(CancellationToken cancellationToken)
{
DisposeAsync().Forget();
}
public void OnCompleted()
{
completionSource.TrySetResult(false);
}
public void OnError(Exception ex)
{
completionSource.TrySetException(ex);
}
static void CancellationCallback(object state)
{
var self = (Enumerator)state;
self.DisposeAsync().Forget();
}
}
}
public class ReadOnlyAsyncReactiveProperty<T> : IReadOnlyAsyncReactiveProperty<T>, IDisposable
{
TriggerEvent<T> triggerEvent;
T latestValue;
IUniTaskAsyncEnumerator<T> enumerator;
public T Value
{
get
{
return latestValue;
}
}
public ReadOnlyAsyncReactiveProperty(T initialValue, IUniTaskAsyncEnumerable<T> source, CancellationToken cancellationToken)
{
latestValue = initialValue;
ConsumeEnumerator(source, cancellationToken).Forget();
}
public ReadOnlyAsyncReactiveProperty(IUniTaskAsyncEnumerable<T> source, CancellationToken cancellationToken)
{
ConsumeEnumerator(source, cancellationToken).Forget();
}
async UniTaskVoid ConsumeEnumerator(IUniTaskAsyncEnumerable<T> source, CancellationToken cancellationToken)
{
enumerator = source.GetAsyncEnumerator(cancellationToken);
try
{
while (await enumerator.MoveNextAsync())
{
var value = enumerator.Current;
this.latestValue = value;
triggerEvent.SetResult(value);
}
}
finally
{
await enumerator.DisposeAsync();
enumerator = null;
}
}
public IUniTaskAsyncEnumerable<T> WithoutCurrent()
{
return new WithoutCurrentEnumerable(this);
}
public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken)
{
return new Enumerator(this, cancellationToken, true);
}
public void Dispose()
{
if (enumerator != null)
{
enumerator.DisposeAsync().Forget();
}
triggerEvent.SetCompleted();
}
public static implicit operator T(ReadOnlyAsyncReactiveProperty<T> value)
{
return value.Value;
}
public override string ToString()
{
if (isValueType) return latestValue.ToString();
return latestValue?.ToString();
}
public UniTask<T> WaitAsync(CancellationToken cancellationToken = default)
{
return new UniTask<T>(WaitAsyncSource.Create(this, cancellationToken, out var token), token);
}
static bool isValueType;
static ReadOnlyAsyncReactiveProperty()
{
isValueType = typeof(T).IsValueType;
}
sealed class WaitAsyncSource : IUniTaskSource<T>, ITriggerHandler<T>, ITaskPoolNode<WaitAsyncSource>
{
static Action<object> cancellationCallback = CancellationCallback;
static TaskPool<WaitAsyncSource> pool;
WaitAsyncSource nextNode;
ref WaitAsyncSource ITaskPoolNode<WaitAsyncSource>.NextNode => ref nextNode;
static WaitAsyncSource()
{
TaskPool.RegisterSizeGetter(typeof(WaitAsyncSource), () => pool.Size);
}
ReadOnlyAsyncReactiveProperty<T> parent;
CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
UniTaskCompletionSourceCore<T> core;
WaitAsyncSource()
{
}
public static IUniTaskSource<T> Create(ReadOnlyAsyncReactiveProperty<T> parent, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource<T>.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new WaitAsyncSource();
}
result.parent = parent;
result.cancellationToken = cancellationToken;
if (cancellationToken.CanBeCanceled)
{
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, result);
}
result.parent.triggerEvent.Add(result);
TaskTracker.TrackActiveTask(result, 3);
token = result.core.Version;
return result;
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
cancellationTokenRegistration.Dispose();
cancellationTokenRegistration = default;
parent.triggerEvent.Remove(this);
parent = null;
cancellationToken = default;
return pool.TryPush(this);
}
static void CancellationCallback(object state)
{
var self = (WaitAsyncSource)state;
self.OnCanceled(self.cancellationToken);
}
// IUniTaskSource
public T GetResult(short token)
{
try
{
return core.GetResult(token);
}
finally
{
TryReturn();
}
}
void IUniTaskSource.GetResult(short token)
{
GetResult(token);
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
// ITriggerHandler
ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; }
ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; }
public void OnCanceled(CancellationToken cancellationToken)
{
core.TrySetCanceled(cancellationToken);
}
public void OnCompleted()
{
// Complete as Cancel.
core.TrySetCanceled(CancellationToken.None);
}
public void OnError(Exception ex)
{
core.TrySetException(ex);
}
public void OnNext(T value)
{
core.TrySetResult(value);
}
}
sealed class WithoutCurrentEnumerable : IUniTaskAsyncEnumerable<T>
{
readonly ReadOnlyAsyncReactiveProperty<T> parent;
public WithoutCurrentEnumerable(ReadOnlyAsyncReactiveProperty<T> parent)
{
this.parent = parent;
}
public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
return new Enumerator(parent, cancellationToken, false);
}
}
sealed class Enumerator : MoveNextSource, IUniTaskAsyncEnumerator<T>, ITriggerHandler<T>
{
static Action<object> cancellationCallback = CancellationCallback;
readonly ReadOnlyAsyncReactiveProperty<T> parent;
readonly CancellationToken cancellationToken;
readonly CancellationTokenRegistration cancellationTokenRegistration;
T value;
bool isDisposed;
bool firstCall;
public Enumerator(ReadOnlyAsyncReactiveProperty<T> parent, CancellationToken cancellationToken, bool publishCurrentValue)
{
this.parent = parent;
this.cancellationToken = cancellationToken;
this.firstCall = publishCurrentValue;
parent.triggerEvent.Add(this);
TaskTracker.TrackActiveTask(this, 3);
if (cancellationToken.CanBeCanceled)
{
cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, this);
}
}
public T Current => value;
ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; }
ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; }
public UniTask<bool> MoveNextAsync()
{
// raise latest value on first call.
if (firstCall)
{
firstCall = false;
value = parent.Value;
return CompletedTasks.True;
}
completionSource.Reset();
return new UniTask<bool>(this, completionSource.Version);
}
public UniTask DisposeAsync()
{
if (!isDisposed)
{
isDisposed = true;
TaskTracker.RemoveTracking(this);
completionSource.TrySetCanceled(cancellationToken);
parent.triggerEvent.Remove(this);
}
return default;
}
public void OnNext(T value)
{
this.value = value;
completionSource.TrySetResult(true);
}
public void OnCanceled(CancellationToken cancellationToken)
{
DisposeAsync().Forget();
}
public void OnCompleted()
{
completionSource.TrySetResult(false);
}
public void OnError(Exception ex)
{
completionSource.TrySetException(ex);
}
static void CancellationCallback(object state)
{
var self = (Enumerator)state;
self.DisposeAsync().Forget();
}
}
}
public static class StateExtensions
{
public static ReadOnlyAsyncReactiveProperty<T> ToReadOnlyAsyncReactiveProperty<T>(this IUniTaskAsyncEnumerable<T> source, CancellationToken cancellationToken)
{
return new ReadOnlyAsyncReactiveProperty<T>(source, cancellationToken);
}
public static ReadOnlyAsyncReactiveProperty<T> ToReadOnlyAsyncReactiveProperty<T>(this IUniTaskAsyncEnumerable<T> source, T initialValue, CancellationToken cancellationToken)
{
return new ReadOnlyAsyncReactiveProperty<T>(initialValue, source, cancellationToken);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6dfe8d027e137da49b1047d42f29f3f0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,85 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using System.Threading;
using UnityEngine;
using Cysharp.Threading.Tasks.Triggers;
namespace Cysharp.Threading.Tasks
{
public static class UniTaskCancellationExtensions
{
/// <summary>This CancellationToken is canceled when the MonoBehaviour will be destroyed.</summary>
public static CancellationToken GetCancellationTokenOnDestroy(this GameObject gameObject)
{
return gameObject.GetAsyncDestroyTrigger().CancellationToken;
}
/// <summary>This CancellationToken is canceled when the MonoBehaviour will be destroyed.</summary>
public static CancellationToken GetCancellationTokenOnDestroy(this Component component)
{
return component.GetAsyncDestroyTrigger().CancellationToken;
}
}
}
namespace Cysharp.Threading.Tasks.Triggers
{
public static partial class AsyncTriggerExtensions
{
// Util.
static T GetOrAddComponent<T>(GameObject gameObject)
where T : Component
{
#if UNITY_2019_2_OR_NEWER
if (!gameObject.TryGetComponent<T>(out var component))
{
component = gameObject.AddComponent<T>();
}
#else
var component = gameObject.GetComponent<T>();
if (component == null)
{
component = gameObject.AddComponent<T>();
}
#endif
return component;
}
// Special for single operation.
/// <summary>This function is called when the MonoBehaviour will be destroyed.</summary>
public static UniTask OnDestroyAsync(this GameObject gameObject)
{
return gameObject.GetAsyncDestroyTrigger().OnDestroyAsync();
}
/// <summary>This function is called when the MonoBehaviour will be destroyed.</summary>
public static UniTask OnDestroyAsync(this Component component)
{
return component.GetAsyncDestroyTrigger().OnDestroyAsync();
}
public static UniTask StartAsync(this GameObject gameObject)
{
return gameObject.GetAsyncStartTrigger().StartAsync();
}
public static UniTask StartAsync(this Component component)
{
return component.GetAsyncStartTrigger().StartAsync();
}
public static UniTask AwakeAsync(this GameObject gameObject)
{
return gameObject.GetAsyncAwakeTrigger().AwakeAsync();
}
public static UniTask AwakeAsync(this Component component)
{
return component.GetAsyncAwakeTrigger().AwakeAsync();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 56c1bd5f9078ef940b75475abaf75032
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,26 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or
using System;
namespace Cysharp.Threading.Tasks
{
public readonly struct AsyncUnit : IEquatable<AsyncUnit>
{
public static readonly AsyncUnit Default = new AsyncUnit();
public override int GetHashCode()
{
return 0;
}
public bool Equals(AsyncUnit other)
{
return true;
}
public override string ToString()
{
return "()";
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0ce7843f97df8e54ab03924b8dc6dc63
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,23 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using System.Collections.Generic;
using System.Threading;
namespace Cysharp.Threading.Tasks
{
public class CancellationTokenEqualityComparer : IEqualityComparer<CancellationToken>
{
public static readonly IEqualityComparer<CancellationToken> Default = new CancellationTokenEqualityComparer();
public bool Equals(CancellationToken x, CancellationToken y)
{
return x.Equals(y);
}
public int GetHashCode(CancellationToken obj)
{
return obj.GetHashCode();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 320441a0503e50e44bc1c73343bd81c9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,182 @@
#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);
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4af2ead288d770147b76ce339b070ca0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,44 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using System.Threading;
using UnityEngine;
using Cysharp.Threading.Tasks.Triggers;
using System;
using Cysharp.Threading.Tasks.Internal;
namespace Cysharp.Threading.Tasks
{
public static partial class CancellationTokenSourceExtensions
{
readonly static Action<object> CancelCancellationTokenSourceStateDelegate = new Action<object>(CancelCancellationTokenSourceState);
static void CancelCancellationTokenSourceState(object state)
{
var cts = (CancellationTokenSource)state;
cts.Cancel();
}
public static IDisposable CancelAfterSlim(this CancellationTokenSource cts, int millisecondsDelay, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update)
{
return CancelAfterSlim(cts, TimeSpan.FromMilliseconds(millisecondsDelay), delayType, delayTiming);
}
public static IDisposable CancelAfterSlim(this CancellationTokenSource cts, TimeSpan delayTimeSpan, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update)
{
return PlayerLoopTimer.StartNew(delayTimeSpan, false, delayType, delayTiming, cts.Token, CancelCancellationTokenSourceStateDelegate, cts);
}
public static void RegisterRaiseCancelOnDestroy(this CancellationTokenSource cts, Component component)
{
RegisterRaiseCancelOnDestroy(cts, component.gameObject);
}
public static void RegisterRaiseCancelOnDestroy(this CancellationTokenSource cts, GameObject gameObject)
{
var trigger = gameObject.GetAsyncDestroyTrigger();
trigger.CancellationToken.RegisterWithoutCaptureExecutionContext(CancelCancellationTokenSourceStateDelegate, cts);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0ab87a95e80b81e47b987abfa96f3071
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,450 @@
using System;
using System.Collections.Generic;
using System.Threading;
namespace Cysharp.Threading.Tasks
{
public static class Channel
{
public static Channel<T> CreateSingleConsumerUnbounded<T>()
{
return new SingleConsumerUnboundedChannel<T>();
}
}
public abstract class Channel<TWrite, TRead>
{
public ChannelReader<TRead> Reader { get; protected set; }
public ChannelWriter<TWrite> Writer { get; protected set; }
public static implicit operator ChannelReader<TRead>(Channel<TWrite, TRead> channel) => channel.Reader;
public static implicit operator ChannelWriter<TWrite>(Channel<TWrite, TRead> channel) => channel.Writer;
}
public abstract class Channel<T> : Channel<T, T>
{
}
public abstract class ChannelReader<T>
{
public abstract bool TryRead(out T item);
public abstract UniTask<bool> WaitToReadAsync(CancellationToken cancellationToken = default(CancellationToken));
public abstract UniTask Completion { get; }
public virtual UniTask<T> ReadAsync(CancellationToken cancellationToken = default(CancellationToken))
{
if (this.TryRead(out var item))
{
return UniTask.FromResult(item);
}
return ReadAsyncCore(cancellationToken);
}
async UniTask<T> ReadAsyncCore(CancellationToken cancellationToken = default(CancellationToken))
{
if (await WaitToReadAsync(cancellationToken))
{
if (TryRead(out var item))
{
return item;
}
}
throw new ChannelClosedException();
}
public abstract IUniTaskAsyncEnumerable<T> ReadAllAsync(CancellationToken cancellationToken = default(CancellationToken));
}
public abstract class ChannelWriter<T>
{
public abstract bool TryWrite(T item);
public abstract bool TryComplete(Exception error = null);
public void Complete(Exception error = null)
{
if (!TryComplete(error))
{
throw new ChannelClosedException();
}
}
}
public partial class ChannelClosedException : InvalidOperationException
{
public ChannelClosedException() :
base("Channel is already closed.")
{ }
public ChannelClosedException(string message) : base(message) { }
public ChannelClosedException(Exception innerException) :
base("Channel is already closed", innerException)
{ }
public ChannelClosedException(string message, Exception innerException) : base(message, innerException) { }
}
internal class SingleConsumerUnboundedChannel<T> : Channel<T>
{
readonly Queue<T> items;
readonly SingleConsumerUnboundedChannelReader readerSource;
UniTaskCompletionSource completedTaskSource;
UniTask completedTask;
Exception completionError;
bool closed;
public SingleConsumerUnboundedChannel()
{
items = new Queue<T>();
Writer = new SingleConsumerUnboundedChannelWriter(this);
readerSource = new SingleConsumerUnboundedChannelReader(this);
Reader = readerSource;
}
sealed class SingleConsumerUnboundedChannelWriter : ChannelWriter<T>
{
readonly SingleConsumerUnboundedChannel<T> parent;
public SingleConsumerUnboundedChannelWriter(SingleConsumerUnboundedChannel<T> parent)
{
this.parent = parent;
}
public override bool TryWrite(T item)
{
bool waiting;
lock (parent.items)
{
if (parent.closed) return false;
parent.items.Enqueue(item);
waiting = parent.readerSource.isWaiting;
}
if (waiting)
{
parent.readerSource.SingalContinuation();
}
return true;
}
public override bool TryComplete(Exception error = null)
{
bool waiting;
lock (parent.items)
{
if (parent.closed) return false;
parent.closed = true;
waiting = parent.readerSource.isWaiting;
if (parent.items.Count == 0)
{
if (error == null)
{
if (parent.completedTaskSource != null)
{
parent.completedTaskSource.TrySetResult();
}
else
{
parent.completedTask = UniTask.CompletedTask;
}
}
else
{
if (parent.completedTaskSource != null)
{
parent.completedTaskSource.TrySetException(error);
}
else
{
parent.completedTask = UniTask.FromException(error);
}
}
if (waiting)
{
parent.readerSource.SingalCompleted(error);
}
}
parent.completionError = error;
}
return true;
}
}
sealed class SingleConsumerUnboundedChannelReader : ChannelReader<T>, IUniTaskSource<bool>
{
readonly Action<object> CancellationCallbackDelegate = CancellationCallback;
readonly SingleConsumerUnboundedChannel<T> parent;
CancellationToken cancellationToken;
CancellationTokenRegistration cancellationTokenRegistration;
UniTaskCompletionSourceCore<bool> core;
internal bool isWaiting;
public SingleConsumerUnboundedChannelReader(SingleConsumerUnboundedChannel<T> parent)
{
this.parent = parent;
TaskTracker.TrackActiveTask(this, 4);
}
public override UniTask Completion
{
get
{
if (parent.completedTaskSource != null) return parent.completedTaskSource.Task;
if (parent.closed)
{
return parent.completedTask;
}
parent.completedTaskSource = new UniTaskCompletionSource();
return parent.completedTaskSource.Task;
}
}
public override bool TryRead(out T item)
{
lock (parent.items)
{
if (parent.items.Count != 0)
{
item = parent.items.Dequeue();
// complete when all value was consumed.
if (parent.closed && parent.items.Count == 0)
{
if (parent.completionError != null)
{
if (parent.completedTaskSource != null)
{
parent.completedTaskSource.TrySetException(parent.completionError);
}
else
{
parent.completedTask = UniTask.FromException(parent.completionError);
}
}
else
{
if (parent.completedTaskSource != null)
{
parent.completedTaskSource.TrySetResult();
}
else
{
parent.completedTask = UniTask.CompletedTask;
}
}
}
}
else
{
item = default;
return false;
}
}
return true;
}
public override UniTask<bool> WaitToReadAsync(CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
{
return UniTask.FromCanceled<bool>(cancellationToken);
}
lock (parent.items)
{
if (parent.items.Count != 0)
{
return CompletedTasks.True;
}
if (parent.closed)
{
if (parent.completionError == null)
{
return CompletedTasks.False;
}
else
{
return UniTask.FromException<bool>(parent.completionError);
}
}
cancellationTokenRegistration.Dispose();
core.Reset();
isWaiting = true;
this.cancellationToken = cancellationToken;
if (this.cancellationToken.CanBeCanceled)
{
cancellationTokenRegistration = this.cancellationToken.RegisterWithoutCaptureExecutionContext(CancellationCallbackDelegate, this);
}
return new UniTask<bool>(this, core.Version);
}
}
public void SingalContinuation()
{
core.TrySetResult(true);
}
public void SingalCancellation(CancellationToken cancellationToken)
{
TaskTracker.RemoveTracking(this);
core.TrySetCanceled(cancellationToken);
}
public void SingalCompleted(Exception error)
{
if (error != null)
{
TaskTracker.RemoveTracking(this);
core.TrySetException(error);
}
else
{
TaskTracker.RemoveTracking(this);
core.TrySetResult(false);
}
}
public override IUniTaskAsyncEnumerable<T> ReadAllAsync(CancellationToken cancellationToken = default)
{
return new ReadAllAsyncEnumerable(this, cancellationToken);
}
bool IUniTaskSource<bool>.GetResult(short token)
{
return core.GetResult(token);
}
void IUniTaskSource.GetResult(short token)
{
core.GetResult(token);
}
UniTaskStatus IUniTaskSource.GetStatus(short token)
{
return core.GetStatus(token);
}
void IUniTaskSource.OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
UniTaskStatus IUniTaskSource.UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
static void CancellationCallback(object state)
{
var self = (SingleConsumerUnboundedChannelReader)state;
self.SingalCancellation(self.cancellationToken);
}
sealed class ReadAllAsyncEnumerable : IUniTaskAsyncEnumerable<T>, IUniTaskAsyncEnumerator<T>
{
readonly Action<object> CancellationCallback1Delegate = CancellationCallback1;
readonly Action<object> CancellationCallback2Delegate = CancellationCallback2;
readonly SingleConsumerUnboundedChannelReader parent;
CancellationToken cancellationToken1;
CancellationToken cancellationToken2;
CancellationTokenRegistration cancellationTokenRegistration1;
CancellationTokenRegistration cancellationTokenRegistration2;
T current;
bool cacheValue;
bool running;
public ReadAllAsyncEnumerable(SingleConsumerUnboundedChannelReader parent, CancellationToken cancellationToken)
{
this.parent = parent;
this.cancellationToken1 = cancellationToken;
}
public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
if (running)
{
throw new InvalidOperationException("Enumerator is already running, does not allow call GetAsyncEnumerator twice.");
}
if (this.cancellationToken1 != cancellationToken)
{
this.cancellationToken2 = cancellationToken;
}
if (this.cancellationToken1.CanBeCanceled)
{
this.cancellationTokenRegistration1 = this.cancellationToken1.RegisterWithoutCaptureExecutionContext(CancellationCallback1Delegate, this);
}
if (this.cancellationToken2.CanBeCanceled)
{
this.cancellationTokenRegistration2 = this.cancellationToken2.RegisterWithoutCaptureExecutionContext(CancellationCallback2Delegate, this);
}
running = true;
return this;
}
public T Current
{
get
{
if (cacheValue)
{
return current;
}
parent.TryRead(out current);
return current;
}
}
public UniTask<bool> MoveNextAsync()
{
cacheValue = false;
return parent.WaitToReadAsync(CancellationToken.None); // ok to use None, registered in ctor.
}
public UniTask DisposeAsync()
{
cancellationTokenRegistration1.Dispose();
cancellationTokenRegistration2.Dispose();
return default;
}
static void CancellationCallback1(object state)
{
var self = (ReadAllAsyncEnumerable)state;
self.parent.SingalCancellation(self.cancellationToken1);
}
static void CancellationCallback2(object state)
{
var self = (ReadAllAsyncEnumerable)state;
self.parent.SingalCancellation(self.cancellationToken2);
}
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 04f4ee8933a36d947926d5a3ca558730
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,34 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using System;
using System.Collections.Generic;
namespace Cysharp.Threading.Tasks
{
public static class EnumerableAsyncExtensions
{
// overload resolver - .Select(async x => { }) : IEnumerable<UniTask<T>>
public static IEnumerable<UniTask> Select<T>(this IEnumerable<T> source, Func<T, UniTask> selector)
{
return System.Linq.Enumerable.Select(source, selector);
}
public static IEnumerable<UniTask<TR>> Select<T, TR>(this IEnumerable<T> source, Func<T, UniTask<TR>> selector)
{
return System.Linq.Enumerable.Select(source, selector);
}
public static IEnumerable<UniTask> Select<T>(this IEnumerable<T> source, Func<T, int, UniTask> selector)
{
return System.Linq.Enumerable.Select(source, selector);
}
public static IEnumerable<UniTask<TR>> Select<T, TR>(this IEnumerable<T> source, Func<T, int, UniTask<TR>> selector)
{
return System.Linq.Enumerable.Select(source, selector);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d259e96c99c95904491627237363fb42
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,287 @@
#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 Cysharp.Threading.Tasks.Internal;
using UnityEngine;
namespace Cysharp.Threading.Tasks
{
public static class EnumeratorAsyncExtensions
{
public static UniTask.Awaiter GetAwaiter<T>(this T enumerator)
where T : IEnumerator
{
var e = (IEnumerator)enumerator;
Error.ThrowArgumentNullException(e, nameof(enumerator));
return new UniTask(EnumeratorPromise.Create(e, PlayerLoopTiming.Update, CancellationToken.None, out var token), token).GetAwaiter();
}
public static UniTask WithCancellation(this IEnumerator enumerator, CancellationToken cancellationToken)
{
Error.ThrowArgumentNullException(enumerator, nameof(enumerator));
return new UniTask(EnumeratorPromise.Create(enumerator, PlayerLoopTiming.Update, cancellationToken, out var token), token);
}
public static UniTask ToUniTask(this IEnumerator enumerator, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
{
Error.ThrowArgumentNullException(enumerator, nameof(enumerator));
return new UniTask(EnumeratorPromise.Create(enumerator, timing, cancellationToken, out var token), token);
}
public static UniTask ToUniTask(this IEnumerator enumerator, MonoBehaviour coroutineRunner)
{
var source = AutoResetUniTaskCompletionSource.Create();
coroutineRunner.StartCoroutine(Core(enumerator, coroutineRunner, source));
return source.Task;
}
static IEnumerator Core(IEnumerator inner, MonoBehaviour coroutineRunner, AutoResetUniTaskCompletionSource source)
{
yield return coroutineRunner.StartCoroutine(inner);
source.TrySetResult();
}
sealed class EnumeratorPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<EnumeratorPromise>
{
static TaskPool<EnumeratorPromise> pool;
EnumeratorPromise nextNode;
public ref EnumeratorPromise NextNode => ref nextNode;
static EnumeratorPromise()
{
TaskPool.RegisterSizeGetter(typeof(EnumeratorPromise), () => pool.Size);
}
IEnumerator innerEnumerator;
CancellationToken cancellationToken;
int initialFrame;
bool loopRunning;
bool calledGetResult;
UniTaskCompletionSourceCore<object> core;
EnumeratorPromise()
{
}
public static IUniTaskSource Create(IEnumerator innerEnumerator, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new EnumeratorPromise();
}
TaskTracker.TrackActiveTask(result, 3);
result.innerEnumerator = ConsumeEnumerator(innerEnumerator);
result.cancellationToken = cancellationToken;
result.loopRunning = true;
result.calledGetResult = false;
result.initialFrame = -1;
token = result.core.Version;
// run immediately.
if (result.MoveNext())
{
PlayerLoopHelper.AddAction(timing, result);
}
return result;
}
public void GetResult(short token)
{
try
{
calledGetResult = true;
core.GetResult(token);
}
finally
{
if (!loopRunning)
{
TryReturn();
}
}
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
public bool MoveNext()
{
if (calledGetResult)
{
loopRunning = false;
TryReturn();
return false;
}
if (innerEnumerator == null) // invalid status, returned but loop running?
{
return false;
}
if (cancellationToken.IsCancellationRequested)
{
loopRunning = false;
core.TrySetCanceled(cancellationToken);
return false;
}
if (initialFrame == -1)
{
// Time can not touch in threadpool.
if (PlayerLoopHelper.IsMainThread)
{
initialFrame = Time.frameCount;
}
}
else if (initialFrame == Time.frameCount)
{
return true; // already executed in first frame, skip.
}
try
{
if (innerEnumerator.MoveNext())
{
return true;
}
}
catch (Exception ex)
{
loopRunning = false;
core.TrySetException(ex);
return false;
}
loopRunning = false;
core.TrySetResult(null);
return false;
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
innerEnumerator = default;
cancellationToken = default;
return pool.TryPush(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 cyi)
{
// WWW, WaitForSecondsRealtime
while (cyi.keepWaiting)
{
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
{
goto WARN;
}
}
else if (current is IEnumerator e3)
{
var e4 = ConsumeEnumerator(e3);
while (e4.MoveNext())
{
yield return null;
}
}
else
{
goto WARN;
}
continue;
WARN:
// WaitForEndOfFrame, WaitForFixedUpdate, others.
UnityEngine.Debug.LogWarning($"yield {current.GetType().Name} is not supported on await IEnumerator or IEnumerator.ToUniTask(), please use ToUniTask(MonoBehaviour coroutineRunner) instead.");
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 elapsed = 0.0f;
while (true)
{
yield return null;
elapsed += Time.deltaTime;
if (elapsed >= second)
{
break;
}
};
}
static IEnumerator UnwrapWaitAsyncOperation(AsyncOperation asyncOperation)
{
while (!asyncOperation.isDone)
{
yield return null;
}
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d82b8e708c1740140ba668397947320d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,14 @@
using System;
namespace Cysharp.Threading.Tasks
{
public static class ExceptionExtensions
{
public static bool IsOperationCanceledException(this Exception exception)
{
return exception is OperationCanceledException;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0341bf927da82b3479131f172030dd78
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,91 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading;
namespace Cysharp.Threading.Tasks
{
public interface IUniTaskAsyncEnumerable<out T>
{
IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default);
}
public interface IUniTaskAsyncEnumerator<out T> : IUniTaskAsyncDisposable
{
T Current { get; }
UniTask<bool> MoveNextAsync();
}
public interface IUniTaskAsyncDisposable
{
UniTask DisposeAsync();
}
public interface IUniTaskOrderedAsyncEnumerable<TElement> : IUniTaskAsyncEnumerable<TElement>
{
IUniTaskOrderedAsyncEnumerable<TElement> CreateOrderedEnumerable<TKey>(Func<TElement, TKey> keySelector, IComparer<TKey> comparer, bool descending);
IUniTaskOrderedAsyncEnumerable<TElement> CreateOrderedEnumerable<TKey>(Func<TElement, UniTask<TKey>> keySelector, IComparer<TKey> comparer, bool descending);
IUniTaskOrderedAsyncEnumerable<TElement> CreateOrderedEnumerable<TKey>(Func<TElement, CancellationToken, UniTask<TKey>> keySelector, IComparer<TKey> comparer, bool descending);
}
public interface IConnectableUniTaskAsyncEnumerable<out T> : IUniTaskAsyncEnumerable<T>
{
IDisposable Connect();
}
// don't use AsyncGrouping.
//public interface IUniTaskAsyncGrouping<out TKey, out TElement> : IUniTaskAsyncEnumerable<TElement>
//{
// TKey Key { get; }
//}
public static class UniTaskAsyncEnumerableExtensions
{
public static UniTaskCancelableAsyncEnumerable<T> WithCancellation<T>(this IUniTaskAsyncEnumerable<T> source, CancellationToken cancellationToken)
{
return new UniTaskCancelableAsyncEnumerable<T>(source, cancellationToken);
}
}
[StructLayout(LayoutKind.Auto)]
public readonly struct UniTaskCancelableAsyncEnumerable<T>
{
private readonly IUniTaskAsyncEnumerable<T> enumerable;
private readonly CancellationToken cancellationToken;
internal UniTaskCancelableAsyncEnumerable(IUniTaskAsyncEnumerable<T> enumerable, CancellationToken cancellationToken)
{
this.enumerable = enumerable;
this.cancellationToken = cancellationToken;
}
public Enumerator GetAsyncEnumerator()
{
return new Enumerator(enumerable.GetAsyncEnumerator(cancellationToken));
}
[StructLayout(LayoutKind.Auto)]
public readonly struct Enumerator
{
private readonly IUniTaskAsyncEnumerator<T> enumerator;
internal Enumerator(IUniTaskAsyncEnumerator<T> enumerator)
{
this.enumerator = enumerator;
}
public T Current => enumerator.Current;
public UniTask<bool> MoveNextAsync()
{
return enumerator.MoveNextAsync();
}
public UniTask DisposeAsync()
{
return enumerator.DisposeAsync();
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3cefc80c785273f44928731702f4976f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,124 @@
#pragma warning disable CS1591
using System;
using System.Runtime.CompilerServices;
namespace Cysharp.Threading.Tasks
{
public enum UniTaskStatus
{
/// <summary>The operation has not yet completed.</summary>
Pending = 0,
/// <summary>The operation completed successfully.</summary>
Succeeded = 1,
/// <summary>The operation completed with an error.</summary>
Faulted = 2,
/// <summary>The operation completed due to cancellation.</summary>
Canceled = 3
}
// similar as IValueTaskSource
public interface IUniTaskSource
#if !UNITY_2018_3_OR_NEWER && !NETSTANDARD2_0
: System.Threading.Tasks.Sources.IValueTaskSource
#pragma warning disable CS0108
#endif
{
UniTaskStatus GetStatus(short token);
void OnCompleted(Action<object> continuation, object state, short token);
void GetResult(short token);
UniTaskStatus UnsafeGetStatus(); // only for debug use.
#if !UNITY_2018_3_OR_NEWER && !NETSTANDARD2_0
#pragma warning restore CS0108
System.Threading.Tasks.Sources.ValueTaskSourceStatus System.Threading.Tasks.Sources.IValueTaskSource.GetStatus(short token)
{
return (System.Threading.Tasks.Sources.ValueTaskSourceStatus)(int)((IUniTaskSource)this).GetStatus(token);
}
void System.Threading.Tasks.Sources.IValueTaskSource.GetResult(short token)
{
((IUniTaskSource)this).GetResult(token);
}
void System.Threading.Tasks.Sources.IValueTaskSource.OnCompleted(Action<object> continuation, object state, short token, System.Threading.Tasks.Sources.ValueTaskSourceOnCompletedFlags flags)
{
// ignore flags, always none.
((IUniTaskSource)this).OnCompleted(continuation, state, token);
}
#endif
}
public interface IUniTaskSource<out T> : IUniTaskSource
#if !UNITY_2018_3_OR_NEWER && !NETSTANDARD2_0
, System.Threading.Tasks.Sources.IValueTaskSource<T>
#endif
{
new T GetResult(short token);
#if !UNITY_2018_3_OR_NEWER && !NETSTANDARD2_0
new public UniTaskStatus GetStatus(short token)
{
return ((IUniTaskSource)this).GetStatus(token);
}
new public void OnCompleted(Action<object> continuation, object state, short token)
{
((IUniTaskSource)this).OnCompleted(continuation, state, token);
}
System.Threading.Tasks.Sources.ValueTaskSourceStatus System.Threading.Tasks.Sources.IValueTaskSource<T>.GetStatus(short token)
{
return (System.Threading.Tasks.Sources.ValueTaskSourceStatus)(int)((IUniTaskSource)this).GetStatus(token);
}
T System.Threading.Tasks.Sources.IValueTaskSource<T>.GetResult(short token)
{
return ((IUniTaskSource<T>)this).GetResult(token);
}
void System.Threading.Tasks.Sources.IValueTaskSource<T>.OnCompleted(Action<object> continuation, object state, short token, System.Threading.Tasks.Sources.ValueTaskSourceOnCompletedFlags flags)
{
// ignore flags, always none.
((IUniTaskSource)this).OnCompleted(continuation, state, token);
}
#endif
}
public static class UniTaskStatusExtensions
{
/// <summary>status != Pending.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsCompleted(this UniTaskStatus status)
{
return status != UniTaskStatus.Pending;
}
/// <summary>status == Succeeded.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsCompletedSuccessfully(this UniTaskStatus status)
{
return status == UniTaskStatus.Succeeded;
}
/// <summary>status == Canceled.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsCanceled(this UniTaskStatus status)
{
return status == UniTaskStatus.Canceled;
}
/// <summary>status == Faulted.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsFaulted(this UniTaskStatus status)
{
return status == UniTaskStatus.Faulted;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 80c07e6125fa788428edc3ae533039a9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,63 @@
using System;
namespace Cysharp.Threading.Tasks
{
public abstract class MoveNextSource : IUniTaskSource<bool>
{
protected UniTaskCompletionSourceCore<bool> completionSource;
public bool GetResult(short token)
{
return completionSource.GetResult(token);
}
public UniTaskStatus GetStatus(short token)
{
return completionSource.GetStatus(token);
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
completionSource.OnCompleted(continuation, state, token);
}
public UniTaskStatus UnsafeGetStatus()
{
return completionSource.UnsafeGetStatus();
}
void IUniTaskSource.GetResult(short token)
{
completionSource.GetResult(token);
}
protected bool TryGetResult<T>(UniTask<T>.Awaiter awaiter, out T result)
{
try
{
result = awaiter.GetResult();
return true;
}
catch (Exception ex)
{
completionSource.TrySetException(ex);
result = default;
return false;
}
}
protected bool TryGetResult(UniTask.Awaiter awaiter)
{
try
{
awaiter.GetResult();
return true;
}
catch (Exception ex)
{
completionSource.TrySetException(ex);
return false;
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c3d3a83909152a0499817b79d697331b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,564 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using System;
using System.Linq;
using UnityEngine;
using Cysharp.Threading.Tasks.Internal;
using System.Threading;
#if UNITY_2019_3_OR_NEWER
using UnityEngine.LowLevel;
using PlayerLoopType = UnityEngine.PlayerLoop;
#else
using UnityEngine.Experimental.LowLevel;
using PlayerLoopType = UnityEngine.Experimental.PlayerLoop;
#endif
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace Cysharp.Threading.Tasks
{
public static class UniTaskLoopRunners
{
public struct UniTaskLoopRunnerInitialization { };
public struct UniTaskLoopRunnerEarlyUpdate { };
public struct UniTaskLoopRunnerFixedUpdate { };
public struct UniTaskLoopRunnerPreUpdate { };
public struct UniTaskLoopRunnerUpdate { };
public struct UniTaskLoopRunnerPreLateUpdate { };
public struct UniTaskLoopRunnerPostLateUpdate { };
// Last
public struct UniTaskLoopRunnerLastInitialization { };
public struct UniTaskLoopRunnerLastEarlyUpdate { };
public struct UniTaskLoopRunnerLastFixedUpdate { };
public struct UniTaskLoopRunnerLastPreUpdate { };
public struct UniTaskLoopRunnerLastUpdate { };
public struct UniTaskLoopRunnerLastPreLateUpdate { };
public struct UniTaskLoopRunnerLastPostLateUpdate { };
// Yield
public struct UniTaskLoopRunnerYieldInitialization { };
public struct UniTaskLoopRunnerYieldEarlyUpdate { };
public struct UniTaskLoopRunnerYieldFixedUpdate { };
public struct UniTaskLoopRunnerYieldPreUpdate { };
public struct UniTaskLoopRunnerYieldUpdate { };
public struct UniTaskLoopRunnerYieldPreLateUpdate { };
public struct UniTaskLoopRunnerYieldPostLateUpdate { };
// Yield Last
public struct UniTaskLoopRunnerLastYieldInitialization { };
public struct UniTaskLoopRunnerLastYieldEarlyUpdate { };
public struct UniTaskLoopRunnerLastYieldFixedUpdate { };
public struct UniTaskLoopRunnerLastYieldPreUpdate { };
public struct UniTaskLoopRunnerLastYieldUpdate { };
public struct UniTaskLoopRunnerLastYieldPreLateUpdate { };
public struct UniTaskLoopRunnerLastYieldPostLateUpdate { };
#if UNITY_2020_2_OR_NEWER
public struct UniTaskLoopRunnerTimeUpdate { };
public struct UniTaskLoopRunnerLastTimeUpdate { };
public struct UniTaskLoopRunnerYieldTimeUpdate { };
public struct UniTaskLoopRunnerLastYieldTimeUpdate { };
#endif
}
public enum PlayerLoopTiming
{
Initialization = 0,
LastInitialization = 1,
EarlyUpdate = 2,
LastEarlyUpdate = 3,
FixedUpdate = 4,
LastFixedUpdate = 5,
PreUpdate = 6,
LastPreUpdate = 7,
Update = 8,
LastUpdate = 9,
PreLateUpdate = 10,
LastPreLateUpdate = 11,
PostLateUpdate = 12,
LastPostLateUpdate = 13,
#if UNITY_2020_2_OR_NEWER
// Unity 2020.2 added TimeUpdate https://docs.unity3d.com/2020.2/Documentation/ScriptReference/PlayerLoop.TimeUpdate.html
TimeUpdate = 14,
LastTimeUpdate = 15,
#endif
}
[Flags]
public enum InjectPlayerLoopTimings
{
/// <summary>
/// Preset: All loops(default).
/// </summary>
All =
Initialization | LastInitialization |
EarlyUpdate | LastEarlyUpdate |
FixedUpdate | LastFixedUpdate |
PreUpdate | LastPreUpdate |
Update | LastUpdate |
PreLateUpdate | LastPreLateUpdate |
PostLateUpdate | LastPostLateUpdate
#if UNITY_2020_2_OR_NEWER
| TimeUpdate | LastTimeUpdate,
#else
,
#endif
/// <summary>
/// Preset: All without last except LastPostLateUpdate.
/// </summary>
Standard =
Initialization |
EarlyUpdate |
FixedUpdate |
PreUpdate |
Update |
PreLateUpdate |
PostLateUpdate | LastPostLateUpdate
#if UNITY_2020_2_OR_NEWER
| TimeUpdate
#endif
,
/// <summary>
/// Preset: Minimum pattern, Update | FixedUpdate | LastPostLateUpdate
/// </summary>
Minimum =
Update | FixedUpdate | LastPostLateUpdate,
// PlayerLoopTiming
Initialization = 1,
LastInitialization = 2,
EarlyUpdate = 4,
LastEarlyUpdate = 8,
FixedUpdate = 16,
LastFixedUpdate = 32,
PreUpdate = 64,
LastPreUpdate = 128,
Update = 256,
LastUpdate = 512,
PreLateUpdate = 1024,
LastPreLateUpdate = 2048,
PostLateUpdate = 4096,
LastPostLateUpdate = 8192
#if UNITY_2020_2_OR_NEWER
,
// Unity 2020.2 added TimeUpdate https://docs.unity3d.com/2020.2/Documentation/ScriptReference/PlayerLoop.TimeUpdate.html
TimeUpdate = 16384,
LastTimeUpdate = 32768
#endif
}
public interface IPlayerLoopItem
{
bool MoveNext();
}
public static class PlayerLoopHelper
{
static readonly ContinuationQueue ThrowMarkerContinuationQueue = new ContinuationQueue(PlayerLoopTiming.Initialization);
static readonly PlayerLoopRunner ThrowMarkerPlayerLoopRunner = new PlayerLoopRunner(PlayerLoopTiming.Initialization);
public static SynchronizationContext UnitySynchronizationContext => unitySynchronizationContext;
public static int MainThreadId => mainThreadId;
internal static string ApplicationDataPath => applicationDataPath;
public static bool IsMainThread => Thread.CurrentThread.ManagedThreadId == mainThreadId;
static int mainThreadId;
static string applicationDataPath;
static SynchronizationContext unitySynchronizationContext;
static ContinuationQueue[] yielders;
static PlayerLoopRunner[] runners;
internal static bool IsEditorApplicationQuitting { get; private set; }
static PlayerLoopSystem[] InsertRunner(PlayerLoopSystem loopSystem,
bool injectOnFirst,
Type loopRunnerYieldType, ContinuationQueue cq,
Type loopRunnerType, PlayerLoopRunner runner)
{
#if UNITY_EDITOR
EditorApplication.playModeStateChanged += (state) =>
{
if (state == PlayModeStateChange.EnteredEditMode || state == PlayModeStateChange.ExitingEditMode)
{
IsEditorApplicationQuitting = true;
// run rest action before clear.
if (runner != null)
{
runner.Run();
runner.Clear();
}
if (cq != null)
{
cq.Run();
cq.Clear();
}
IsEditorApplicationQuitting = false;
}
};
#endif
var yieldLoop = new PlayerLoopSystem
{
type = loopRunnerYieldType,
updateDelegate = cq.Run
};
var runnerLoop = new PlayerLoopSystem
{
type = loopRunnerType,
updateDelegate = runner.Run
};
// Remove items from previous initializations.
var source = RemoveRunner(loopSystem, loopRunnerYieldType, loopRunnerType);
var dest = new PlayerLoopSystem[source.Length + 2];
Array.Copy(source, 0, dest, injectOnFirst ? 2 : 0, source.Length);
if (injectOnFirst)
{
dest[0] = yieldLoop;
dest[1] = runnerLoop;
}
else
{
dest[dest.Length - 2] = yieldLoop;
dest[dest.Length - 1] = runnerLoop;
}
return dest;
}
static PlayerLoopSystem[] RemoveRunner(PlayerLoopSystem loopSystem, Type loopRunnerYieldType, Type loopRunnerType)
{
return loopSystem.subSystemList
.Where(ls => ls.type != loopRunnerYieldType && ls.type != loopRunnerType)
.ToArray();
}
static PlayerLoopSystem[] InsertUniTaskSynchronizationContext(PlayerLoopSystem loopSystem)
{
var loop = new PlayerLoopSystem
{
type = typeof(UniTaskSynchronizationContext),
updateDelegate = UniTaskSynchronizationContext.Run
};
// Remove items from previous initializations.
var source = loopSystem.subSystemList
.Where(ls => ls.type != typeof(UniTaskSynchronizationContext))
.ToArray();
var dest = new System.Collections.Generic.List<PlayerLoopSystem>(source);
var index = dest.FindIndex(x => x.type.Name == "ScriptRunDelayedTasks");
if (index == -1)
{
index = dest.FindIndex(x => x.type.Name == "UniTaskLoopRunnerUpdate");
}
dest.Insert(index + 1, loop);
return dest.ToArray();
}
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
static void Init()
{
// capture default(unity) sync-context.
unitySynchronizationContext = SynchronizationContext.Current;
mainThreadId = Thread.CurrentThread.ManagedThreadId;
try
{
applicationDataPath = Application.dataPath;
}
catch { }
#if UNITY_EDITOR && UNITY_2019_3_OR_NEWER
// When domain reload is disabled, re-initialization is required when entering play mode;
// otherwise, pending tasks will leak between play mode sessions.
var domainReloadDisabled = UnityEditor.EditorSettings.enterPlayModeOptionsEnabled &&
UnityEditor.EditorSettings.enterPlayModeOptions.HasFlag(UnityEditor.EnterPlayModeOptions.DisableDomainReload);
if (!domainReloadDisabled && runners != null) return;
#else
if (runners != null) return; // already initialized
#endif
var playerLoop =
#if UNITY_2019_3_OR_NEWER
PlayerLoop.GetCurrentPlayerLoop();
#else
PlayerLoop.GetDefaultPlayerLoop();
#endif
Initialize(ref playerLoop);
}
#if UNITY_EDITOR
[InitializeOnLoadMethod]
static void InitOnEditor()
{
// Execute the play mode init method
Init();
// register an Editor update delegate, used to forcing playerLoop update
EditorApplication.update += ForceEditorPlayerLoopUpdate;
}
private static void ForceEditorPlayerLoopUpdate()
{
if (EditorApplication.isPlayingOrWillChangePlaymode || EditorApplication.isCompiling || EditorApplication.isUpdating)
{
// Not in Edit mode, don't interfere
return;
}
// EditorApplication.QueuePlayerLoopUpdate causes performance issue, don't call directly.
// EditorApplication.QueuePlayerLoopUpdate();
if (yielders != null)
{
foreach (var item in yielders)
{
if (item != null) item.Run();
}
}
if (runners != null)
{
foreach (var item in runners)
{
if (item != null) item.Run();
}
}
UniTaskSynchronizationContext.Run();
}
#endif
private static int FindLoopSystemIndex(PlayerLoopSystem[] playerLoopList, Type systemType)
{
for (int i = 0; i < playerLoopList.Length; i++)
{
if (playerLoopList[i].type == systemType)
{
return i;
}
}
throw new Exception("Target PlayerLoopSystem does not found. Type:" + systemType.FullName);
}
static void InsertLoop(PlayerLoopSystem[] copyList, InjectPlayerLoopTimings injectTimings, Type loopType, InjectPlayerLoopTimings targetTimings,
int index, bool injectOnFirst, Type loopRunnerYieldType, Type loopRunnerType, PlayerLoopTiming playerLoopTiming)
{
var i = FindLoopSystemIndex(copyList, loopType);
if ((injectTimings & targetTimings) == targetTimings)
{
copyList[i].subSystemList = InsertRunner(copyList[i], injectOnFirst,
loopRunnerYieldType, yielders[index] = new ContinuationQueue(playerLoopTiming),
loopRunnerType, runners[index] = new PlayerLoopRunner(playerLoopTiming));
}
else
{
copyList[i].subSystemList = RemoveRunner(copyList[i], loopRunnerYieldType, loopRunnerType);
}
}
public static void Initialize(ref PlayerLoopSystem playerLoop, InjectPlayerLoopTimings injectTimings = InjectPlayerLoopTimings.All)
{
#if UNITY_2020_2_OR_NEWER
yielders = new ContinuationQueue[16];
runners = new PlayerLoopRunner[16];
#else
yielders = new ContinuationQueue[14];
runners = new PlayerLoopRunner[14];
#endif
var copyList = playerLoop.subSystemList.ToArray();
// Initialization
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.Initialization),
InjectPlayerLoopTimings.Initialization, 0, true,
typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldInitialization), typeof(UniTaskLoopRunners.UniTaskLoopRunnerInitialization), PlayerLoopTiming.Initialization);
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.Initialization),
InjectPlayerLoopTimings.LastInitialization, 1, false,
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldInitialization), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastInitialization), PlayerLoopTiming.LastInitialization);
// EarlyUpdate
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.EarlyUpdate),
InjectPlayerLoopTimings.EarlyUpdate, 2, true,
typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldEarlyUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerEarlyUpdate), PlayerLoopTiming.EarlyUpdate);
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.EarlyUpdate),
InjectPlayerLoopTimings.LastEarlyUpdate, 3, false,
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldEarlyUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastEarlyUpdate), PlayerLoopTiming.LastEarlyUpdate);
// FixedUpdate
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.FixedUpdate),
InjectPlayerLoopTimings.FixedUpdate, 4, true,
typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldFixedUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerFixedUpdate), PlayerLoopTiming.FixedUpdate);
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.FixedUpdate),
InjectPlayerLoopTimings.LastFixedUpdate, 5, false,
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldFixedUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastFixedUpdate), PlayerLoopTiming.LastFixedUpdate);
// PreUpdate
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.PreUpdate),
InjectPlayerLoopTimings.PreUpdate, 6, true,
typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldPreUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerPreUpdate), PlayerLoopTiming.PreUpdate);
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.PreUpdate),
InjectPlayerLoopTimings.LastPreUpdate, 7, false,
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldPreUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastPreUpdate), PlayerLoopTiming.LastPreUpdate);
// Update
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.Update),
InjectPlayerLoopTimings.Update, 8, true,
typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerUpdate), PlayerLoopTiming.Update);
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.Update),
InjectPlayerLoopTimings.LastUpdate, 9, false,
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastUpdate), PlayerLoopTiming.LastUpdate);
// PreLateUpdate
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.PreLateUpdate),
InjectPlayerLoopTimings.PreLateUpdate, 10, true,
typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldPreLateUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerPreLateUpdate), PlayerLoopTiming.PreLateUpdate);
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.PreLateUpdate),
InjectPlayerLoopTimings.LastPreLateUpdate, 11, false,
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldPreLateUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastPreLateUpdate), PlayerLoopTiming.LastPreLateUpdate);
// PostLateUpdate
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.PostLateUpdate),
InjectPlayerLoopTimings.PostLateUpdate, 12, true,
typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldPostLateUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerPostLateUpdate), PlayerLoopTiming.PostLateUpdate);
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.PostLateUpdate),
InjectPlayerLoopTimings.LastPostLateUpdate, 13, false,
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldPostLateUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastPostLateUpdate), PlayerLoopTiming.LastPostLateUpdate);
#if UNITY_2020_2_OR_NEWER
// TimeUpdate
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.TimeUpdate),
InjectPlayerLoopTimings.TimeUpdate, 14, true,
typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldTimeUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerTimeUpdate), PlayerLoopTiming.TimeUpdate);
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.TimeUpdate),
InjectPlayerLoopTimings.LastTimeUpdate, 15, false,
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldTimeUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastTimeUpdate), PlayerLoopTiming.LastTimeUpdate);
#endif
// Insert UniTaskSynchronizationContext to Update loop
var i = FindLoopSystemIndex(copyList, typeof(PlayerLoopType.Update));
copyList[i].subSystemList = InsertUniTaskSynchronizationContext(copyList[i]);
playerLoop.subSystemList = copyList;
PlayerLoop.SetPlayerLoop(playerLoop);
}
public static void AddAction(PlayerLoopTiming timing, IPlayerLoopItem action)
{
var runner = runners[(int)timing];
if (runner == null)
{
ThrowInvalidLoopTiming(timing);
}
runner.AddAction(action);
}
static void ThrowInvalidLoopTiming(PlayerLoopTiming playerLoopTiming)
{
throw new InvalidOperationException("Target playerLoopTiming is not injected. Please check PlayerLoopHelper.Initialize. PlayerLoopTiming:" + playerLoopTiming);
}
public static void AddContinuation(PlayerLoopTiming timing, Action continuation)
{
var q = yielders[(int)timing];
if (q == null)
{
ThrowInvalidLoopTiming(timing);
}
q.Enqueue(continuation);
}
// Diagnostics helper
#if UNITY_2019_3_OR_NEWER
public static void DumpCurrentPlayerLoop()
{
var playerLoop = UnityEngine.LowLevel.PlayerLoop.GetCurrentPlayerLoop();
var sb = new System.Text.StringBuilder();
sb.AppendLine($"PlayerLoop List");
foreach (var header in playerLoop.subSystemList)
{
sb.AppendFormat("------{0}------", header.type.Name);
sb.AppendLine();
foreach (var subSystem in header.subSystemList)
{
sb.AppendFormat("{0}", subSystem.type.Name);
sb.AppendLine();
if (subSystem.subSystemList != null)
{
UnityEngine.Debug.LogWarning("More Subsystem:" + subSystem.subSystemList.Length);
}
}
}
UnityEngine.Debug.Log(sb.ToString());
}
public static bool IsInjectedUniTaskPlayerLoop()
{
var playerLoop = UnityEngine.LowLevel.PlayerLoop.GetCurrentPlayerLoop();
foreach (var header in playerLoop.subSystemList)
{
foreach (var subSystem in header.subSystemList)
{
if (subSystem.type == typeof(UniTaskLoopRunners.UniTaskLoopRunnerInitialization))
{
return true;
}
}
}
return false;
}
#endif
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0fff5439b835ecb4d80e91638a8cd32e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,262 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using System.Threading;
using System;
using Cysharp.Threading.Tasks.Internal;
using UnityEngine;
namespace Cysharp.Threading.Tasks
{
public abstract class PlayerLoopTimer : IDisposable, IPlayerLoopItem
{
readonly CancellationToken cancellationToken;
readonly Action<object> timerCallback;
readonly object state;
readonly PlayerLoopTiming playerLoopTiming;
readonly bool periodic;
bool isRunning;
bool tryStop;
bool isDisposed;
protected PlayerLoopTimer(bool periodic, PlayerLoopTiming playerLoopTiming, CancellationToken cancellationToken, Action<object> timerCallback, object state)
{
this.periodic = periodic;
this.playerLoopTiming = playerLoopTiming;
this.cancellationToken = cancellationToken;
this.timerCallback = timerCallback;
this.state = state;
}
public static PlayerLoopTimer Create(TimeSpan interval, bool periodic, DelayType delayType, PlayerLoopTiming playerLoopTiming, CancellationToken cancellationToken, Action<object> timerCallback, object state)
{
#if UNITY_EDITOR
// force use Realtime.
if (PlayerLoopHelper.IsMainThread && !UnityEditor.EditorApplication.isPlaying)
{
delayType = DelayType.Realtime;
}
#endif
switch (delayType)
{
case DelayType.UnscaledDeltaTime:
return new IgnoreTimeScalePlayerLoopTimer(interval, periodic, playerLoopTiming, cancellationToken, timerCallback, state);
case DelayType.Realtime:
return new RealtimePlayerLoopTimer(interval, periodic, playerLoopTiming, cancellationToken, timerCallback, state);
case DelayType.DeltaTime:
default:
return new DeltaTimePlayerLoopTimer(interval, periodic, playerLoopTiming, cancellationToken, timerCallback, state);
}
}
public static PlayerLoopTimer StartNew(TimeSpan interval, bool periodic, DelayType delayType, PlayerLoopTiming playerLoopTiming, CancellationToken cancellationToken, Action<object> timerCallback, object state)
{
var timer = Create(interval, periodic, delayType, playerLoopTiming, cancellationToken, timerCallback, state);
timer.Restart();
return timer;
}
/// <summary>
/// Restart(Reset and Start) timer.
/// </summary>
public void Restart()
{
if (isDisposed) throw new ObjectDisposedException(null);
ResetCore(null); // init state
if (!isRunning)
{
isRunning = true;
PlayerLoopHelper.AddAction(playerLoopTiming, this);
}
tryStop = false;
}
/// <summary>
/// Restart(Reset and Start) and change interval.
/// </summary>
public void Restart(TimeSpan interval)
{
if (isDisposed) throw new ObjectDisposedException(null);
ResetCore(interval); // init state
if (!isRunning)
{
isRunning = true;
PlayerLoopHelper.AddAction(playerLoopTiming, this);
}
tryStop = false;
}
/// <summary>
/// Stop timer.
/// </summary>
public void Stop()
{
tryStop = true;
}
protected abstract void ResetCore(TimeSpan? newInterval);
public void Dispose()
{
isDisposed = true;
}
bool IPlayerLoopItem.MoveNext()
{
if (isDisposed)
{
isRunning = false;
return false;
}
if (tryStop)
{
isRunning = false;
return false;
}
if (cancellationToken.IsCancellationRequested)
{
isRunning = false;
return false;
}
if (!MoveNextCore())
{
timerCallback(state);
if (periodic)
{
ResetCore(null);
return true;
}
else
{
isRunning = false;
return false;
}
}
return true;
}
protected abstract bool MoveNextCore();
}
sealed class DeltaTimePlayerLoopTimer : PlayerLoopTimer
{
int initialFrame;
float elapsed;
float interval;
public DeltaTimePlayerLoopTimer(TimeSpan interval, bool periodic, PlayerLoopTiming playerLoopTiming, CancellationToken cancellationToken, Action<object> timerCallback, object state)
: base(periodic, playerLoopTiming, cancellationToken, timerCallback, state)
{
ResetCore(interval);
}
protected override bool MoveNextCore()
{
if (elapsed == 0.0f)
{
if (initialFrame == Time.frameCount)
{
return true;
}
}
elapsed += Time.deltaTime;
if (elapsed >= interval)
{
return false;
}
return true;
}
protected override void ResetCore(TimeSpan? interval)
{
this.elapsed = 0.0f;
this.initialFrame = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1;
if (interval != null)
{
this.interval = (float)interval.Value.TotalSeconds;
}
}
}
sealed class IgnoreTimeScalePlayerLoopTimer : PlayerLoopTimer
{
int initialFrame;
float elapsed;
float interval;
public IgnoreTimeScalePlayerLoopTimer(TimeSpan interval, bool periodic, PlayerLoopTiming playerLoopTiming, CancellationToken cancellationToken, Action<object> timerCallback, object state)
: base(periodic, playerLoopTiming, cancellationToken, timerCallback, state)
{
ResetCore(interval);
}
protected override bool MoveNextCore()
{
if (elapsed == 0.0f)
{
if (initialFrame == Time.frameCount)
{
return true;
}
}
elapsed += Time.unscaledDeltaTime;
if (elapsed >= interval)
{
return false;
}
return true;
}
protected override void ResetCore(TimeSpan? interval)
{
this.elapsed = 0.0f;
this.initialFrame = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1;
if (interval != null)
{
this.interval = (float)interval.Value.TotalSeconds;
}
}
}
sealed class RealtimePlayerLoopTimer : PlayerLoopTimer
{
ValueStopwatch stopwatch;
long intervalTicks;
public RealtimePlayerLoopTimer(TimeSpan interval, bool periodic, PlayerLoopTiming playerLoopTiming, CancellationToken cancellationToken, Action<object> timerCallback, object state)
: base(periodic, playerLoopTiming, cancellationToken, timerCallback, state)
{
ResetCore(interval);
}
protected override bool MoveNextCore()
{
if (stopwatch.ElapsedTicks >= intervalTicks)
{
return false;
}
return true;
}
protected override void ResetCore(TimeSpan? interval)
{
this.stopwatch = ValueStopwatch.StartNew();
if (interval != null)
{
this.intervalTicks = interval.Value.Ticks;
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 819de61352ff4224b9fc68ab4b8b7758
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using Cysharp.Threading.Tasks.Internal;
namespace Cysharp.Threading.Tasks
{
/// <summary>
/// Lightweight IProgress[T] factory.
/// </summary>
public static class Progress
{
public static IProgress<T> Create<T>(Action<T> handler)
{
if (handler == null) return NullProgress<T>.Instance;
return new AnonymousProgress<T>(handler);
}
public static IProgress<T> CreateOnlyValueChanged<T>(Action<T> handler, IEqualityComparer<T> comparer = null)
{
if (handler == null) return NullProgress<T>.Instance;
#if UNITY_2018_3_OR_NEWER
return new OnlyValueChangedProgress<T>(handler, comparer ?? UnityEqualityComparer.GetDefault<T>());
#else
return new OnlyValueChangedProgress<T>(handler, comparer ?? EqualityComparer<T>.Default);
#endif
}
sealed class NullProgress<T> : IProgress<T>
{
public static readonly IProgress<T> Instance = new NullProgress<T>();
NullProgress()
{
}
public void Report(T value)
{
}
}
sealed class AnonymousProgress<T> : IProgress<T>
{
readonly Action<T> action;
public AnonymousProgress(Action<T> action)
{
this.action = action;
}
public void Report(T value)
{
action(value);
}
}
sealed class OnlyValueChangedProgress<T> : IProgress<T>
{
readonly Action<T> action;
readonly IEqualityComparer<T> comparer;
bool isFirstCall;
T latestValue;
public OnlyValueChangedProgress(Action<T> action, IEqualityComparer<T> comparer)
{
this.action = action;
this.comparer = comparer;
this.isFirstCall = true;
}
public void Report(T value)
{
if (isFirstCall)
{
isFirstCall = false;
}
else if (comparer.Equals(value, latestValue))
{
return;
}
latestValue = value;
action(value);
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3c17e4ff8fca63949a0c14d17c9fb19e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,123 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
namespace Cysharp.Threading.Tasks
{
// internaly used but public, allow to user create custom operator with pooling.
public static class TaskPool
{
internal static int MaxPoolSize;
// avoid to use ConcurrentDictionary for safety of WebGL build.
static Dictionary<Type, Func<int>> sizes = new Dictionary<Type, Func<int>>();
static TaskPool()
{
try
{
var value = Environment.GetEnvironmentVariable("UNITASK_MAX_POOLSIZE");
if (value != null)
{
if (int.TryParse(value, out var size))
{
MaxPoolSize = size;
return;
}
}
}
catch { }
MaxPoolSize = int.MaxValue;
}
public static void SetMaxPoolSize(int maxPoolSize)
{
MaxPoolSize = maxPoolSize;
}
public static IEnumerable<(Type, int)> GetCacheSizeInfo()
{
lock (sizes)
{
foreach (var item in sizes)
{
yield return (item.Key, item.Value());
}
}
}
public static void RegisterSizeGetter(Type type, Func<int> getSize)
{
lock (sizes)
{
sizes[type] = getSize;
}
}
}
public interface ITaskPoolNode<T>
{
ref T NextNode { get; }
}
// mutable struct, don't mark readonly.
[StructLayout(LayoutKind.Auto)]
public struct TaskPool<T>
where T : class, ITaskPoolNode<T>
{
int gate;
int size;
T root;
public int Size => size;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryPop(out T result)
{
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
{
var v = root;
if (!(v is null))
{
ref var nextNode = ref v.NextNode;
root = nextNode;
nextNode = null;
size--;
result = v;
Volatile.Write(ref gate, 0);
return true;
}
Volatile.Write(ref gate, 0);
}
result = default;
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryPush(T item)
{
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
{
if (size < TaskPool.MaxPoolSize)
{
item.NextNode = root;
root = item;
size++;
Volatile.Write(ref gate, 0);
return true;
}
else
{
Volatile.Write(ref gate, 0);
}
}
return false;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 974e0e44f3cafa14587f9fe4bdd872c1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,178 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Threading;
using Cysharp.Threading.Tasks.Internal;
namespace Cysharp.Threading.Tasks
{
// public for add user custom.
public static class TaskTracker
{
#if UNITY_EDITOR
static int trackingId = 0;
public const string EnableAutoReloadKey = "UniTaskTrackerWindow_EnableAutoReloadKey";
public const string EnableTrackingKey = "UniTaskTrackerWindow_EnableTrackingKey";
public const string EnableStackTraceKey = "UniTaskTrackerWindow_EnableStackTraceKey";
public static class EditorEnableState
{
static bool enableAutoReload;
public static bool EnableAutoReload
{
get { return enableAutoReload; }
set
{
enableAutoReload = value;
UnityEditor.EditorPrefs.SetBool(EnableAutoReloadKey, value);
}
}
static bool enableTracking;
public static bool EnableTracking
{
get { return enableTracking; }
set
{
enableTracking = value;
UnityEditor.EditorPrefs.SetBool(EnableTrackingKey, value);
}
}
static bool enableStackTrace;
public static bool EnableStackTrace
{
get { return enableStackTrace; }
set
{
enableStackTrace = value;
UnityEditor.EditorPrefs.SetBool(EnableStackTraceKey, value);
}
}
}
#endif
static List<KeyValuePair<IUniTaskSource, (string formattedType, int trackingId, DateTime addTime, string stackTrace)>> listPool = new List<KeyValuePair<IUniTaskSource, (string formattedType, int trackingId, DateTime addTime, string stackTrace)>>();
static readonly WeakDictionary<IUniTaskSource, (string formattedType, int trackingId, DateTime addTime, string stackTrace)> tracking = new WeakDictionary<IUniTaskSource, (string formattedType, int trackingId, DateTime addTime, string stackTrace)>();
[Conditional("UNITY_EDITOR")]
public static void TrackActiveTask(IUniTaskSource task, int skipFrame)
{
#if UNITY_EDITOR
dirty = true;
if (!EditorEnableState.EnableTracking) return;
var stackTrace = EditorEnableState.EnableStackTrace ? new StackTrace(skipFrame, true).CleanupAsyncStackTrace() : "";
string typeName;
if (EditorEnableState.EnableStackTrace)
{
var sb = new StringBuilder();
TypeBeautify(task.GetType(), sb);
typeName = sb.ToString();
}
else
{
typeName = task.GetType().Name;
}
tracking.TryAdd(task, (typeName, Interlocked.Increment(ref trackingId), DateTime.UtcNow, stackTrace));
#endif
}
[Conditional("UNITY_EDITOR")]
public static void RemoveTracking(IUniTaskSource task)
{
#if UNITY_EDITOR
dirty = true;
if (!EditorEnableState.EnableTracking) return;
var success = tracking.TryRemove(task);
#endif
}
static bool dirty;
public static bool CheckAndResetDirty()
{
var current = dirty;
dirty = false;
return current;
}
/// <summary>(trackingId, awaiterType, awaiterStatus, createdTime, stackTrace)</summary>
public static void ForEachActiveTask(Action<int, string, UniTaskStatus, DateTime, string> action)
{
lock (listPool)
{
var count = tracking.ToList(ref listPool, clear: false);
try
{
for (int i = 0; i < count; i++)
{
action(listPool[i].Value.trackingId, listPool[i].Value.formattedType, listPool[i].Key.UnsafeGetStatus(), listPool[i].Value.addTime, listPool[i].Value.stackTrace);
listPool[i] = default;
}
}
catch
{
listPool.Clear();
throw;
}
}
}
static void TypeBeautify(Type type, StringBuilder sb)
{
if (type.IsNested)
{
// TypeBeautify(type.DeclaringType, sb);
sb.Append(type.DeclaringType.Name.ToString());
sb.Append(".");
}
if (type.IsGenericType)
{
var genericsStart = type.Name.IndexOf("`");
if (genericsStart != -1)
{
sb.Append(type.Name.Substring(0, genericsStart));
}
else
{
sb.Append(type.Name);
}
sb.Append("<");
var first = true;
foreach (var item in type.GetGenericArguments())
{
if (!first)
{
sb.Append(", ");
}
first = false;
TypeBeautify(item, sb);
}
sb.Append(">");
}
else
{
sb.Append(type.Name);
}
}
//static string RemoveUniTaskNamespace(string str)
//{
// return str.Replace("Cysharp.Threading.Tasks.CompilerServices", "")
// .Replace("Cysharp.Threading.Tasks.Linq", "")
// .Replace("Cysharp.Threading.Tasks", "");
//}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ed0c80652260deb4cb52536183b4f139
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,129 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using System;
using System.Threading;
namespace Cysharp.Threading.Tasks
{
// CancellationTokenSource itself can not reuse but CancelAfter(Timeout.InfiniteTimeSpan) allows reuse if did not reach timeout.
// Similar discussion:
// https://github.com/dotnet/runtime/issues/4694
// https://github.com/dotnet/runtime/issues/48492
// This TimeoutController emulate similar implementation, using CancelAfterSlim; to achieve zero allocation timeout.
public sealed class TimeoutController : IDisposable
{
readonly static Action<object> CancelCancellationTokenSourceStateDelegate = new Action<object>(CancelCancellationTokenSourceState);
static void CancelCancellationTokenSourceState(object state)
{
var cts = (CancellationTokenSource)state;
cts.Cancel();
}
CancellationTokenSource timeoutSource;
CancellationTokenSource linkedSource;
PlayerLoopTimer timer;
bool isDisposed;
readonly DelayType delayType;
readonly PlayerLoopTiming delayTiming;
readonly CancellationTokenSource originalLinkCancellationTokenSource;
public TimeoutController(DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update)
{
this.timeoutSource = new CancellationTokenSource();
this.originalLinkCancellationTokenSource = null;
this.linkedSource = null;
this.delayType = delayType;
this.delayTiming = delayTiming;
}
public TimeoutController(CancellationTokenSource linkCancellationTokenSource, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update)
{
this.timeoutSource = new CancellationTokenSource();
this.originalLinkCancellationTokenSource = linkCancellationTokenSource;
this.linkedSource = CancellationTokenSource.CreateLinkedTokenSource(timeoutSource.Token, linkCancellationTokenSource.Token);
this.delayType = delayType;
this.delayTiming = delayTiming;
}
public CancellationToken Timeout(int millisecondsTimeout)
{
return Timeout(TimeSpan.FromMilliseconds(millisecondsTimeout));
}
public CancellationToken Timeout(TimeSpan timeout)
{
if (originalLinkCancellationTokenSource != null && originalLinkCancellationTokenSource.IsCancellationRequested)
{
return originalLinkCancellationTokenSource.Token;
}
// Timeouted, create new source and timer.
if (timeoutSource.IsCancellationRequested)
{
timeoutSource.Dispose();
timeoutSource = new CancellationTokenSource();
if (linkedSource != null)
{
this.linkedSource.Cancel();
this.linkedSource.Dispose();
this.linkedSource = CancellationTokenSource.CreateLinkedTokenSource(timeoutSource.Token, originalLinkCancellationTokenSource.Token);
}
timer?.Dispose();
timer = null;
}
var useSource = (linkedSource != null) ? linkedSource : timeoutSource;
var token = useSource.Token;
if (timer == null)
{
// Timer complete => timeoutSource.Cancel() -> linkedSource will be canceled.
// (linked)token is canceled => stop timer
timer = PlayerLoopTimer.StartNew(timeout, false, delayType, delayTiming, token, CancelCancellationTokenSourceStateDelegate, timeoutSource);
}
else
{
timer.Restart(timeout);
}
return token;
}
public bool IsTimeout()
{
return timeoutSource.IsCancellationRequested;
}
public void Reset()
{
timer.Stop();
}
public void Dispose()
{
if (isDisposed) return;
try
{
// stop timer.
timer.Dispose();
// cancel and dispose.
timeoutSource.Cancel();
timeoutSource.Dispose();
if (linkedSource != null)
{
linkedSource.Cancel();
linkedSource.Dispose();
}
}
finally
{
isDisposed = true;
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8a866089859a94645b82874f1e56b67a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,311 @@
using System;
using System.Threading;
namespace Cysharp.Threading.Tasks
{
public interface ITriggerHandler<T>
{
void OnNext(T value);
void OnError(Exception ex);
void OnCompleted();
void OnCanceled(CancellationToken cancellationToken);
// set/get from TriggerEvent<T>
ITriggerHandler<T> Prev { get; set; }
ITriggerHandler<T> Next { get; set; }
}
// be careful to use, itself is struct.
public struct TriggerEvent<T>
{
ITriggerHandler<T> head; // head.prev is last
ITriggerHandler<T> iteratingHead;
bool preserveRemoveSelf;
ITriggerHandler<T> iteratingNode;
void LogError(Exception ex)
{
#if UNITY_2018_3_OR_NEWER
UnityEngine.Debug.LogException(ex);
#else
Console.WriteLine(ex);
#endif
}
public void SetResult(T value)
{
if (iteratingNode != null)
{
throw new InvalidOperationException("Can not trigger itself in iterating.");
}
var h = head;
while (h != null)
{
iteratingNode = h;
try
{
h.OnNext(value);
}
catch (Exception ex)
{
LogError(ex);
Remove(h);
}
if (preserveRemoveSelf)
{
preserveRemoveSelf = false;
iteratingNode = null;
var next = h.Next;
Remove(h);
h = next;
}
else
{
h = h.Next;
}
}
iteratingNode = null;
if (iteratingHead != null)
{
Add(iteratingHead);
iteratingHead = null;
}
}
public void SetCanceled(CancellationToken cancellationToken)
{
if (iteratingNode != null)
{
throw new InvalidOperationException("Can not trigger itself in iterating.");
}
var h = head;
while (h != null)
{
iteratingNode = h;
try
{
h.OnCanceled(cancellationToken);
}
catch (Exception ex)
{
LogError(ex);
}
preserveRemoveSelf = false;
iteratingNode = null;
var next = h.Next;
Remove(h);
h = next;
}
iteratingNode = null;
if (iteratingHead != null)
{
Add(iteratingHead);
iteratingHead = null;
}
}
public void SetCompleted()
{
if (iteratingNode != null)
{
throw new InvalidOperationException("Can not trigger itself in iterating.");
}
var h = head;
while (h != null)
{
iteratingNode = h;
try
{
h.OnCompleted();
}
catch (Exception ex)
{
LogError(ex);
}
preserveRemoveSelf = false;
iteratingNode = null;
var next = h.Next;
Remove(h);
h = next;
}
iteratingNode = null;
if (iteratingHead != null)
{
Add(iteratingHead);
iteratingHead = null;
}
}
public void SetError(Exception exception)
{
if (iteratingNode != null)
{
throw new InvalidOperationException("Can not trigger itself in iterating.");
}
var h = head;
while (h != null)
{
iteratingNode = h;
try
{
h.OnError(exception);
}
catch (Exception ex)
{
LogError(ex);
}
preserveRemoveSelf = false;
iteratingNode = null;
var next = h.Next;
Remove(h);
h = next;
}
iteratingNode = null;
if (iteratingHead != null)
{
Add(iteratingHead);
iteratingHead = null;
}
}
public void Add(ITriggerHandler<T> handler)
{
if (handler == null) throw new ArgumentNullException(nameof(handler));
// zero node.
if (head == null)
{
head = handler;
return;
}
if (iteratingNode != null)
{
if (iteratingHead == null)
{
iteratingHead = handler;
return;
}
var last = iteratingHead.Prev;
if (last == null)
{
// single node.
iteratingHead.Prev = handler;
iteratingHead.Next = handler;
handler.Prev = iteratingHead;
}
else
{
// multi node
iteratingHead.Prev = handler;
last.Next = handler;
handler.Prev = last;
}
}
else
{
var last = head.Prev;
if (last == null)
{
// single node.
head.Prev = handler;
head.Next = handler;
handler.Prev = head;
}
else
{
// multi node
head.Prev = handler;
last.Next = handler;
handler.Prev = last;
}
}
}
public void Remove(ITriggerHandler<T> handler)
{
if (handler == null) throw new ArgumentNullException(nameof(handler));
if (iteratingNode != null && iteratingNode == handler)
{
// if remove self, reserve remove self after invoke completed.
preserveRemoveSelf = true;
}
else
{
var prev = handler.Prev;
var next = handler.Next;
if (next != null)
{
next.Prev = prev;
}
if (handler == head)
{
head = next;
}
else if (handler == iteratingHead)
{
iteratingHead = next;
}
else
{
// when handler is head, prev indicate last so don't use it.
if (prev != null)
{
prev.Next = next;
}
}
if (head != null)
{
if (head.Prev == handler)
{
if (prev != head)
{
head.Prev = prev;
}
else
{
head.Prev = null;
}
}
}
if (iteratingHead != null)
{
if (iteratingHead.Prev == handler)
{
if (prev != iteratingHead.Prev)
{
iteratingHead.Prev = prev;
}
else
{
iteratingHead.Prev = null;
}
}
}
handler.Prev = null;
handler.Next = null;
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3b5a73f8edabcbf409857abff4476532
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,18 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using System;
using System.Collections;
namespace Cysharp.Threading.Tasks
{
// UnityEngine Bridges.
public partial struct UniTask
{
public static IEnumerator ToCoroutine(Func<UniTask> taskFactory)
{
return taskFactory().ToCoroutine();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2a94847b10168f8409352e410c1832e5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,842 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using Cysharp.Threading.Tasks.Internal;
using System;
using System.Runtime.CompilerServices;
using System.Threading;
using UnityEngine;
namespace Cysharp.Threading.Tasks
{
public enum DelayType
{
/// <summary>use Time.deltaTime.</summary>
DeltaTime,
/// <summary>Ignore timescale, use Time.unscaledDeltaTime.</summary>
UnscaledDeltaTime,
/// <summary>use Stopwatch.GetTimestamp().</summary>
Realtime
}
public partial struct UniTask
{
public static YieldAwaitable Yield()
{
// optimized for single continuation
return new YieldAwaitable(PlayerLoopTiming.Update);
}
public static YieldAwaitable Yield(PlayerLoopTiming timing)
{
// optimized for single continuation
return new YieldAwaitable(timing);
}
public static UniTask Yield(CancellationToken cancellationToken)
{
return new UniTask(YieldPromise.Create(PlayerLoopTiming.Update, cancellationToken, out var token), token);
}
public static UniTask Yield(PlayerLoopTiming timing, CancellationToken cancellationToken)
{
return new UniTask(YieldPromise.Create(timing, cancellationToken, out var token), token);
}
/// <summary>
/// Similar as UniTask.Yield but guaranteed run on next frame.
/// </summary>
public static UniTask NextFrame()
{
return new UniTask(NextFramePromise.Create(PlayerLoopTiming.Update, CancellationToken.None, out var token), token);
}
/// <summary>
/// Similar as UniTask.Yield but guaranteed run on next frame.
/// </summary>
public static UniTask NextFrame(PlayerLoopTiming timing)
{
return new UniTask(NextFramePromise.Create(timing, CancellationToken.None, out var token), token);
}
/// <summary>
/// Similar as UniTask.Yield but guaranteed run on next frame.
/// </summary>
public static UniTask NextFrame(CancellationToken cancellationToken)
{
return new UniTask(NextFramePromise.Create(PlayerLoopTiming.Update, cancellationToken, out var token), token);
}
/// <summary>
/// Similar as UniTask.Yield but guaranteed run on next frame.
/// </summary>
public static UniTask NextFrame(PlayerLoopTiming timing, CancellationToken cancellationToken)
{
return new UniTask(NextFramePromise.Create(timing, cancellationToken, out var token), token);
}
/// <summary>
/// Same as UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate).
/// </summary>
public static YieldAwaitable WaitForEndOfFrame()
{
return UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate);
}
/// <summary>
/// Same as UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate, cancellationToken).
/// </summary>
public static UniTask WaitForEndOfFrame(CancellationToken cancellationToken)
{
return UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate, cancellationToken);
}
/// <summary>
/// Same as UniTask.Yield(PlayerLoopTiming.FixedUpdate).
/// </summary>
public static YieldAwaitable WaitForFixedUpdate()
{
return UniTask.Yield(PlayerLoopTiming.FixedUpdate);
}
/// <summary>
/// Same as UniTask.Yield(PlayerLoopTiming.FixedUpdate, cancellationToken).
/// </summary>
public static UniTask WaitForFixedUpdate(CancellationToken cancellationToken)
{
return UniTask.Yield(PlayerLoopTiming.FixedUpdate, cancellationToken);
}
public static UniTask DelayFrame(int delayFrameCount, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
{
if (delayFrameCount < 0)
{
throw new ArgumentOutOfRangeException("Delay does not allow minus delayFrameCount. delayFrameCount:" + delayFrameCount);
}
return new UniTask(DelayFramePromise.Create(delayFrameCount, delayTiming, cancellationToken, out var token), token);
}
public static UniTask Delay(int millisecondsDelay, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
{
var delayTimeSpan = TimeSpan.FromMilliseconds(millisecondsDelay);
return Delay(delayTimeSpan, ignoreTimeScale, delayTiming, cancellationToken);
}
public static UniTask Delay(TimeSpan delayTimeSpan, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
{
var delayType = ignoreTimeScale ? DelayType.UnscaledDeltaTime : DelayType.DeltaTime;
return Delay(delayTimeSpan, delayType, delayTiming, cancellationToken);
}
public static UniTask Delay(int millisecondsDelay, DelayType delayType, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
{
var delayTimeSpan = TimeSpan.FromMilliseconds(millisecondsDelay);
return Delay(delayTimeSpan, delayType, delayTiming, cancellationToken);
}
public static UniTask Delay(TimeSpan delayTimeSpan, DelayType delayType, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
{
if (delayTimeSpan < TimeSpan.Zero)
{
throw new ArgumentOutOfRangeException("Delay does not allow minus delayTimeSpan. delayTimeSpan:" + delayTimeSpan);
}
#if UNITY_EDITOR
// force use Realtime.
if (PlayerLoopHelper.IsMainThread && !UnityEditor.EditorApplication.isPlaying)
{
delayType = DelayType.Realtime;
}
#endif
switch (delayType)
{
case DelayType.UnscaledDeltaTime:
{
return new UniTask(DelayIgnoreTimeScalePromise.Create(delayTimeSpan, delayTiming, cancellationToken, out var token), token);
}
case DelayType.Realtime:
{
return new UniTask(DelayRealtimePromise.Create(delayTimeSpan, delayTiming, cancellationToken, out var token), token);
}
case DelayType.DeltaTime:
default:
{
return new UniTask(DelayPromise.Create(delayTimeSpan, delayTiming, cancellationToken, out var token), token);
}
}
}
sealed class YieldPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<YieldPromise>
{
static TaskPool<YieldPromise> pool;
YieldPromise nextNode;
public ref YieldPromise NextNode => ref nextNode;
static YieldPromise()
{
TaskPool.RegisterSizeGetter(typeof(YieldPromise), () => pool.Size);
}
CancellationToken cancellationToken;
UniTaskCompletionSourceCore<object> core;
YieldPromise()
{
}
public static IUniTaskSource Create(PlayerLoopTiming timing, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new YieldPromise();
}
result.cancellationToken = cancellationToken;
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(timing, result);
token = result.core.Version;
return result;
}
public void GetResult(short token)
{
try
{
core.GetResult(token);
}
finally
{
TryReturn();
}
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
public bool MoveNext()
{
if (cancellationToken.IsCancellationRequested)
{
core.TrySetCanceled(cancellationToken);
return false;
}
core.TrySetResult(null);
return false;
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
cancellationToken = default;
return pool.TryPush(this);
}
}
sealed class NextFramePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<NextFramePromise>
{
static TaskPool<NextFramePromise> pool;
NextFramePromise nextNode;
public ref NextFramePromise NextNode => ref nextNode;
static NextFramePromise()
{
TaskPool.RegisterSizeGetter(typeof(NextFramePromise), () => pool.Size);
}
int frameCount;
CancellationToken cancellationToken;
UniTaskCompletionSourceCore<AsyncUnit> core;
NextFramePromise()
{
}
public static IUniTaskSource Create(PlayerLoopTiming timing, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new NextFramePromise();
}
result.frameCount = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1;
result.cancellationToken = cancellationToken;
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(timing, result);
token = result.core.Version;
return result;
}
public void GetResult(short token)
{
try
{
core.GetResult(token);
}
finally
{
TryReturn();
}
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
public bool MoveNext()
{
if (cancellationToken.IsCancellationRequested)
{
core.TrySetCanceled(cancellationToken);
return false;
}
if (frameCount == Time.frameCount)
{
return true;
}
core.TrySetResult(AsyncUnit.Default);
return false;
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
cancellationToken = default;
return pool.TryPush(this);
}
}
sealed class DelayFramePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<DelayFramePromise>
{
static TaskPool<DelayFramePromise> pool;
DelayFramePromise nextNode;
public ref DelayFramePromise NextNode => ref nextNode;
static DelayFramePromise()
{
TaskPool.RegisterSizeGetter(typeof(DelayFramePromise), () => pool.Size);
}
int initialFrame;
int delayFrameCount;
CancellationToken cancellationToken;
int currentFrameCount;
UniTaskCompletionSourceCore<AsyncUnit> core;
DelayFramePromise()
{
}
public static IUniTaskSource Create(int delayFrameCount, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new DelayFramePromise();
}
result.delayFrameCount = delayFrameCount;
result.cancellationToken = cancellationToken;
result.initialFrame = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1;
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(timing, result);
token = result.core.Version;
return result;
}
public void GetResult(short token)
{
try
{
core.GetResult(token);
}
finally
{
TryReturn();
}
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
public bool MoveNext()
{
if (cancellationToken.IsCancellationRequested)
{
core.TrySetCanceled(cancellationToken);
return false;
}
if (currentFrameCount == 0)
{
if (delayFrameCount == 0) // same as Yield
{
core.TrySetResult(AsyncUnit.Default);
return false;
}
// skip in initial frame.
if (initialFrame == Time.frameCount)
{
return true;
}
}
if (++currentFrameCount >= delayFrameCount)
{
core.TrySetResult(AsyncUnit.Default);
return false;
}
return true;
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
currentFrameCount = default;
delayFrameCount = default;
cancellationToken = default;
return pool.TryPush(this);
}
}
sealed class DelayPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<DelayPromise>
{
static TaskPool<DelayPromise> pool;
DelayPromise nextNode;
public ref DelayPromise NextNode => ref nextNode;
static DelayPromise()
{
TaskPool.RegisterSizeGetter(typeof(DelayPromise), () => pool.Size);
}
int initialFrame;
float delayTimeSpan;
float elapsed;
CancellationToken cancellationToken;
UniTaskCompletionSourceCore<object> core;
DelayPromise()
{
}
public static IUniTaskSource Create(TimeSpan delayTimeSpan, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new DelayPromise();
}
result.elapsed = 0.0f;
result.delayTimeSpan = (float)delayTimeSpan.TotalSeconds;
result.cancellationToken = cancellationToken;
result.initialFrame = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1;
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(timing, result);
token = result.core.Version;
return result;
}
public void GetResult(short token)
{
try
{
core.GetResult(token);
}
finally
{
TryReturn();
}
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
public bool MoveNext()
{
if (cancellationToken.IsCancellationRequested)
{
core.TrySetCanceled(cancellationToken);
return false;
}
if (elapsed == 0.0f)
{
if (initialFrame == Time.frameCount)
{
return true;
}
}
elapsed += Time.deltaTime;
if (elapsed >= delayTimeSpan)
{
core.TrySetResult(null);
return false;
}
return true;
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
delayTimeSpan = default;
elapsed = default;
cancellationToken = default;
return pool.TryPush(this);
}
}
sealed class DelayIgnoreTimeScalePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<DelayIgnoreTimeScalePromise>
{
static TaskPool<DelayIgnoreTimeScalePromise> pool;
DelayIgnoreTimeScalePromise nextNode;
public ref DelayIgnoreTimeScalePromise NextNode => ref nextNode;
static DelayIgnoreTimeScalePromise()
{
TaskPool.RegisterSizeGetter(typeof(DelayIgnoreTimeScalePromise), () => pool.Size);
}
float delayFrameTimeSpan;
float elapsed;
int initialFrame;
CancellationToken cancellationToken;
UniTaskCompletionSourceCore<object> core;
DelayIgnoreTimeScalePromise()
{
}
public static IUniTaskSource Create(TimeSpan delayFrameTimeSpan, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new DelayIgnoreTimeScalePromise();
}
result.elapsed = 0.0f;
result.delayFrameTimeSpan = (float)delayFrameTimeSpan.TotalSeconds;
result.initialFrame = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1;
result.cancellationToken = cancellationToken;
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(timing, result);
token = result.core.Version;
return result;
}
public void GetResult(short token)
{
try
{
core.GetResult(token);
}
finally
{
TryReturn();
}
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
public bool MoveNext()
{
if (cancellationToken.IsCancellationRequested)
{
core.TrySetCanceled(cancellationToken);
return false;
}
if (elapsed == 0.0f)
{
if (initialFrame == Time.frameCount)
{
return true;
}
}
elapsed += Time.unscaledDeltaTime;
if (elapsed >= delayFrameTimeSpan)
{
core.TrySetResult(null);
return false;
}
return true;
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
delayFrameTimeSpan = default;
elapsed = default;
cancellationToken = default;
return pool.TryPush(this);
}
}
sealed class DelayRealtimePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<DelayRealtimePromise>
{
static TaskPool<DelayRealtimePromise> pool;
DelayRealtimePromise nextNode;
public ref DelayRealtimePromise NextNode => ref nextNode;
static DelayRealtimePromise()
{
TaskPool.RegisterSizeGetter(typeof(DelayRealtimePromise), () => pool.Size);
}
long delayTimeSpanTicks;
ValueStopwatch stopwatch;
CancellationToken cancellationToken;
UniTaskCompletionSourceCore<AsyncUnit> core;
DelayRealtimePromise()
{
}
public static IUniTaskSource Create(TimeSpan delayTimeSpan, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new DelayRealtimePromise();
}
result.stopwatch = ValueStopwatch.StartNew();
result.delayTimeSpanTicks = delayTimeSpan.Ticks;
result.cancellationToken = cancellationToken;
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(timing, result);
token = result.core.Version;
return result;
}
public void GetResult(short token)
{
try
{
core.GetResult(token);
}
finally
{
TryReturn();
}
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
public bool MoveNext()
{
if (cancellationToken.IsCancellationRequested)
{
core.TrySetCanceled(cancellationToken);
return false;
}
if (stopwatch.IsInvalid)
{
core.TrySetResult(AsyncUnit.Default);
return false;
}
if (stopwatch.ElapsedTicks >= delayTimeSpanTicks)
{
core.TrySetResult(AsyncUnit.Default);
return false;
}
return true;
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
stopwatch = default;
cancellationToken = default;
return pool.TryPush(this);
}
}
}
public readonly struct YieldAwaitable
{
readonly PlayerLoopTiming timing;
public YieldAwaitable(PlayerLoopTiming timing)
{
this.timing = timing;
}
public Awaiter GetAwaiter()
{
return new Awaiter(timing);
}
public UniTask ToUniTask()
{
return UniTask.Yield(timing, CancellationToken.None);
}
public readonly struct Awaiter : ICriticalNotifyCompletion
{
readonly PlayerLoopTiming timing;
public Awaiter(PlayerLoopTiming timing)
{
this.timing = timing;
}
public bool IsCompleted => false;
public void GetResult() { }
public void OnCompleted(Action continuation)
{
PlayerLoopHelper.AddContinuation(timing, continuation);
}
public void UnsafeOnCompleted(Action continuation)
{
PlayerLoopHelper.AddContinuation(timing, continuation);
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 189117fbe89c1eb4996dc5567b3ef566
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,466 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using Cysharp.Threading.Tasks.Internal;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;
using System.Threading;
namespace Cysharp.Threading.Tasks
{
public partial struct UniTask
{
static readonly UniTask CanceledUniTask = new Func<UniTask>(() =>
{
return new UniTask(new CanceledResultSource(CancellationToken.None), 0);
})();
static class CanceledUniTaskCache<T>
{
public static readonly UniTask<T> Task;
static CanceledUniTaskCache()
{
Task = new UniTask<T>(new CanceledResultSource<T>(CancellationToken.None), 0);
}
}
public static readonly UniTask CompletedTask = new UniTask();
public static UniTask FromException(Exception ex)
{
if (ex is OperationCanceledException oce)
{
return FromCanceled(oce.CancellationToken);
}
return new UniTask(new ExceptionResultSource(ex), 0);
}
public static UniTask<T> FromException<T>(Exception ex)
{
if (ex is OperationCanceledException oce)
{
return FromCanceled<T>(oce.CancellationToken);
}
return new UniTask<T>(new ExceptionResultSource<T>(ex), 0);
}
public static UniTask<T> FromResult<T>(T value)
{
return new UniTask<T>(value);
}
public static UniTask FromCanceled(CancellationToken cancellationToken = default)
{
if (cancellationToken == CancellationToken.None)
{
return CanceledUniTask;
}
else
{
return new UniTask(new CanceledResultSource(cancellationToken), 0);
}
}
public static UniTask<T> FromCanceled<T>(CancellationToken cancellationToken = default)
{
if (cancellationToken == CancellationToken.None)
{
return CanceledUniTaskCache<T>.Task;
}
else
{
return new UniTask<T>(new CanceledResultSource<T>(cancellationToken), 0);
}
}
public static UniTask Create(Func<UniTask> factory)
{
return factory();
}
public static UniTask<T> Create<T>(Func<UniTask<T>> factory)
{
return factory();
}
public static AsyncLazy Lazy(Func<UniTask> factory)
{
return new AsyncLazy(factory);
}
public static AsyncLazy<T> Lazy<T>(Func<UniTask<T>> factory)
{
return new AsyncLazy<T>(factory);
}
/// <summary>
/// helper of fire and forget void action.
/// </summary>
public static void Void(Func<UniTaskVoid> asyncAction)
{
asyncAction().Forget();
}
/// <summary>
/// helper of fire and forget void action.
/// </summary>
public static void Void(Func<CancellationToken, UniTaskVoid> asyncAction, CancellationToken cancellationToken)
{
asyncAction(cancellationToken).Forget();
}
/// <summary>
/// helper of fire and forget void action.
/// </summary>
public static void Void<T>(Func<T, UniTaskVoid> asyncAction, T state)
{
asyncAction(state).Forget();
}
/// <summary>
/// helper of create add UniTaskVoid to delegate.
/// For example: FooAction = UniTask.Action(async () => { /* */ })
/// </summary>
public static Action Action(Func<UniTaskVoid> asyncAction)
{
return () => asyncAction().Forget();
}
/// <summary>
/// helper of create add UniTaskVoid to delegate.
/// </summary>
public static Action Action(Func<CancellationToken, UniTaskVoid> asyncAction, CancellationToken cancellationToken)
{
return () => asyncAction(cancellationToken).Forget();
}
#if UNITY_2018_3_OR_NEWER
/// <summary>
/// Create async void(UniTaskVoid) UnityAction.
/// For exampe: onClick.AddListener(UniTask.UnityAction(async () => { /* */ } ))
/// </summary>
public static UnityEngine.Events.UnityAction UnityAction(Func<UniTaskVoid> asyncAction)
{
return () => asyncAction().Forget();
}
/// <summary>
/// Create async void(UniTaskVoid) UnityAction.
/// For exampe: onClick.AddListener(UniTask.UnityAction(FooAsync, this.GetCancellationTokenOnDestroy()))
/// </summary>
public static UnityEngine.Events.UnityAction UnityAction(Func<CancellationToken, UniTaskVoid> asyncAction, CancellationToken cancellationToken)
{
return () => asyncAction(cancellationToken).Forget();
}
#endif
/// <summary>
/// Defer the task creation just before call await.
/// </summary>
public static UniTask Defer(Func<UniTask> factory)
{
return new UniTask(new DeferPromise(factory), 0);
}
/// <summary>
/// Defer the task creation just before call await.
/// </summary>
public static UniTask<T> Defer<T>(Func<UniTask<T>> factory)
{
return new UniTask<T>(new DeferPromise<T>(factory), 0);
}
/// <summary>
/// Never complete.
/// </summary>
public static UniTask Never(CancellationToken cancellationToken)
{
return new UniTask<AsyncUnit>(new NeverPromise<AsyncUnit>(cancellationToken), 0);
}
/// <summary>
/// Never complete.
/// </summary>
public static UniTask<T> Never<T>(CancellationToken cancellationToken)
{
return new UniTask<T>(new NeverPromise<T>(cancellationToken), 0);
}
sealed class ExceptionResultSource : IUniTaskSource
{
readonly ExceptionDispatchInfo exception;
public ExceptionResultSource(Exception exception)
{
this.exception = ExceptionDispatchInfo.Capture(exception);
}
public void GetResult(short token)
{
exception.Throw();
}
public UniTaskStatus GetStatus(short token)
{
return UniTaskStatus.Faulted;
}
public UniTaskStatus UnsafeGetStatus()
{
return UniTaskStatus.Faulted;
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
continuation(state);
}
}
sealed class ExceptionResultSource<T> : IUniTaskSource<T>
{
readonly ExceptionDispatchInfo exception;
public ExceptionResultSource(Exception exception)
{
this.exception = ExceptionDispatchInfo.Capture(exception);
}
public T GetResult(short token)
{
exception.Throw();
return default;
}
void IUniTaskSource.GetResult(short token)
{
exception.Throw();
}
public UniTaskStatus GetStatus(short token)
{
return UniTaskStatus.Faulted;
}
public UniTaskStatus UnsafeGetStatus()
{
return UniTaskStatus.Faulted;
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
continuation(state);
}
}
sealed class CanceledResultSource : IUniTaskSource
{
readonly CancellationToken cancellationToken;
public CanceledResultSource(CancellationToken cancellationToken)
{
this.cancellationToken = cancellationToken;
}
public void GetResult(short token)
{
throw new OperationCanceledException(cancellationToken);
}
public UniTaskStatus GetStatus(short token)
{
return UniTaskStatus.Canceled;
}
public UniTaskStatus UnsafeGetStatus()
{
return UniTaskStatus.Canceled;
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
continuation(state);
}
}
sealed class CanceledResultSource<T> : IUniTaskSource<T>
{
readonly CancellationToken cancellationToken;
public CanceledResultSource(CancellationToken cancellationToken)
{
this.cancellationToken = cancellationToken;
}
public T GetResult(short token)
{
throw new OperationCanceledException(cancellationToken);
}
void IUniTaskSource.GetResult(short token)
{
throw new OperationCanceledException(cancellationToken);
}
public UniTaskStatus GetStatus(short token)
{
return UniTaskStatus.Canceled;
}
public UniTaskStatus UnsafeGetStatus()
{
return UniTaskStatus.Canceled;
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
continuation(state);
}
}
sealed class DeferPromise : IUniTaskSource
{
Func<UniTask> factory;
UniTask task;
UniTask.Awaiter awaiter;
public DeferPromise(Func<UniTask> factory)
{
this.factory = factory;
}
public void GetResult(short token)
{
awaiter.GetResult();
}
public UniTaskStatus GetStatus(short token)
{
var f = Interlocked.Exchange(ref factory, null);
if (f != null)
{
task = f();
awaiter = task.GetAwaiter();
}
return task.Status;
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
awaiter.SourceOnCompleted(continuation, state);
}
public UniTaskStatus UnsafeGetStatus()
{
return task.Status;
}
}
sealed class DeferPromise<T> : IUniTaskSource<T>
{
Func<UniTask<T>> factory;
UniTask<T> task;
UniTask<T>.Awaiter awaiter;
public DeferPromise(Func<UniTask<T>> factory)
{
this.factory = factory;
}
public T GetResult(short token)
{
return awaiter.GetResult();
}
void IUniTaskSource.GetResult(short token)
{
awaiter.GetResult();
}
public UniTaskStatus GetStatus(short token)
{
var f = Interlocked.Exchange(ref factory, null);
if (f != null)
{
task = f();
awaiter = task.GetAwaiter();
}
return task.Status;
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
awaiter.SourceOnCompleted(continuation, state);
}
public UniTaskStatus UnsafeGetStatus()
{
return task.Status;
}
}
sealed class NeverPromise<T> : IUniTaskSource<T>
{
static readonly Action<object> cancellationCallback = CancellationCallback;
CancellationToken cancellationToken;
UniTaskCompletionSourceCore<T> core;
public NeverPromise(CancellationToken cancellationToken)
{
this.cancellationToken = cancellationToken;
if (this.cancellationToken.CanBeCanceled)
{
this.cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, this);
}
}
static void CancellationCallback(object state)
{
var self = (NeverPromise<T>)state;
self.core.TrySetCanceled(self.cancellationToken);
}
public T GetResult(short token)
{
return core.GetResult(token);
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
void IUniTaskSource.GetResult(short token)
{
core.GetResult(token);
}
}
}
internal static class CompletedTasks
{
public static readonly UniTask<AsyncUnit> AsyncUnit = UniTask.FromResult(Cysharp.Threading.Tasks.AsyncUnit.Default);
public static readonly UniTask<bool> True = UniTask.FromResult(true);
public static readonly UniTask<bool> False = UniTask.FromResult(false);
public static readonly UniTask<int> Zero = UniTask.FromResult(0);
public static readonly UniTask<int> MinusOne = UniTask.FromResult(-1);
public static readonly UniTask<int> One = UniTask.FromResult(1);
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 46572bee54e34424b87a3e7098f15295
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,470 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using System;
using System.Threading;
namespace Cysharp.Threading.Tasks
{
public partial struct UniTask
{
#region OBSOLETE_RUN
// Run is a confusing name, use only RunOnThreadPool in the future.
/// <summary>[Obsolete]recommend to use RunOnThreadPool(or UniTask.Void(async void), UniTask.Create(async UniTask)).</summary>
public static async UniTask Run(Action action, bool configureAwait = true, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait)
{
try
{
action();
}
finally
{
await UniTask.Yield();
}
}
else
{
action();
}
cancellationToken.ThrowIfCancellationRequested();
}
/// <summary>[Obsolete]recommend to use RunOnThreadPool(or UniTask.Void(async void), UniTask.Create(async UniTask)).</summary>
public static async UniTask Run(Action<object> action, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait)
{
try
{
action(state);
}
finally
{
await UniTask.Yield();
}
}
else
{
action(state);
}
cancellationToken.ThrowIfCancellationRequested();
}
/// <summary>[Obsolete]recommend to use RunOnThreadPool(or UniTask.Void(async void), UniTask.Create(async UniTask)).</summary>
public static async UniTask Run(Func<UniTask> action, bool configureAwait = true, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait)
{
try
{
await action();
}
finally
{
await UniTask.Yield();
}
}
else
{
await action();
}
cancellationToken.ThrowIfCancellationRequested();
}
/// <summary>[Obsolete]recommend to use RunOnThreadPool(or UniTask.Void(async void), UniTask.Create(async UniTask)).</summary>
public static async UniTask Run(Func<object, UniTask> action, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait)
{
try
{
await action(state);
}
finally
{
await UniTask.Yield();
}
}
else
{
await action(state);
}
cancellationToken.ThrowIfCancellationRequested();
}
/// <summary>[Obsolete]recommend to use RunOnThreadPool(or UniTask.Void(async void), UniTask.Create(async UniTask)).</summary>
public static async UniTask<T> Run<T>(Func<T> func, bool configureAwait = true, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait)
{
try
{
return func();
}
finally
{
await UniTask.Yield();
cancellationToken.ThrowIfCancellationRequested();
}
}
else
{
return func();
}
}
/// <summary>[Obsolete]recommend to use RunOnThreadPool(or UniTask.Void(async void), UniTask.Create(async UniTask)).</summary>
public static async UniTask<T> Run<T>(Func<UniTask<T>> func, bool configureAwait = true, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait)
{
try
{
return await func();
}
finally
{
cancellationToken.ThrowIfCancellationRequested();
await UniTask.Yield();
cancellationToken.ThrowIfCancellationRequested();
}
}
else
{
var result = await func();
cancellationToken.ThrowIfCancellationRequested();
return result;
}
}
/// <summary>[Obsolete]recommend to use RunOnThreadPool(or UniTask.Void(async void), UniTask.Create(async UniTask)).</summary>
public static async UniTask<T> Run<T>(Func<object, T> func, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait)
{
try
{
return func(state);
}
finally
{
await UniTask.Yield();
cancellationToken.ThrowIfCancellationRequested();
}
}
else
{
return func(state);
}
}
/// <summary>[Obsolete]recommend to use RunOnThreadPool(or UniTask.Void(async void), UniTask.Create(async UniTask)).</summary>
public static async UniTask<T> Run<T>(Func<object, UniTask<T>> func, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait)
{
try
{
return await func(state);
}
finally
{
cancellationToken.ThrowIfCancellationRequested();
await UniTask.Yield();
cancellationToken.ThrowIfCancellationRequested();
}
}
else
{
var result = await func(state);
cancellationToken.ThrowIfCancellationRequested();
return result;
}
}
#endregion
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
public static async UniTask RunOnThreadPool(Action action, bool configureAwait = true, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait)
{
try
{
action();
}
finally
{
await UniTask.Yield();
}
}
else
{
action();
}
cancellationToken.ThrowIfCancellationRequested();
}
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
public static async UniTask RunOnThreadPool(Action<object> action, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait)
{
try
{
action(state);
}
finally
{
await UniTask.Yield();
}
}
else
{
action(state);
}
cancellationToken.ThrowIfCancellationRequested();
}
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
public static async UniTask RunOnThreadPool(Func<UniTask> action, bool configureAwait = true, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait)
{
try
{
await action();
}
finally
{
await UniTask.Yield();
}
}
else
{
await action();
}
cancellationToken.ThrowIfCancellationRequested();
}
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
public static async UniTask RunOnThreadPool(Func<object, UniTask> action, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait)
{
try
{
await action(state);
}
finally
{
await UniTask.Yield();
}
}
else
{
await action(state);
}
cancellationToken.ThrowIfCancellationRequested();
}
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
public static async UniTask<T> RunOnThreadPool<T>(Func<T> func, bool configureAwait = true, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait)
{
try
{
return func();
}
finally
{
await UniTask.Yield();
cancellationToken.ThrowIfCancellationRequested();
}
}
else
{
return func();
}
}
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
public static async UniTask<T> RunOnThreadPool<T>(Func<UniTask<T>> func, bool configureAwait = true, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait)
{
try
{
return await func();
}
finally
{
cancellationToken.ThrowIfCancellationRequested();
await UniTask.Yield();
cancellationToken.ThrowIfCancellationRequested();
}
}
else
{
var result = await func();
cancellationToken.ThrowIfCancellationRequested();
return result;
}
}
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
public static async UniTask<T> RunOnThreadPool<T>(Func<object, T> func, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait)
{
try
{
return func(state);
}
finally
{
await UniTask.Yield();
cancellationToken.ThrowIfCancellationRequested();
}
}
else
{
return func(state);
}
}
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
public static async UniTask<T> RunOnThreadPool<T>(Func<object, UniTask<T>> func, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait)
{
try
{
return await func(state);
}
finally
{
cancellationToken.ThrowIfCancellationRequested();
await UniTask.Yield();
cancellationToken.ThrowIfCancellationRequested();
}
}
else
{
var result = await func(state);
cancellationToken.ThrowIfCancellationRequested();
return result;
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0bdd7fcbe89fab3459f61dab35f4dea5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Some files were not shown because too many files have changed in this diff Show more