Compare commits

..

33 Commits

Author SHA1 Message Date
neuecc
21bf08a6b3 replace all promisepool to new taskpool system 2020-05-29 03:43:18 +09:00
neuecc
d5db96b913 rename StackNode to TaskPool 2020-05-29 02:08:11 +09:00
neuecc
a8455af16d Improve pooling mechanism 2020-05-29 01:22:46 +09:00
neuecc
2290b14532 reduce AsyncBuilder heap allocation 2020-05-28 22:20:06 +09:00
neuecc
90c5a6311b check il2cpp generics limitation 2020-05-28 21:18:35 +09:00
neuecc
6e0ad3623b non public 2020-05-27 09:16:46 +09:00
neuecc
005e02a1fa sealed 2020-05-27 07:39:19 +09:00
neuecc
10fb8060fa use PooledDelegate to avoid convert Action to Action<AsyncOperation> allocation 2020-05-27 07:37:16 +09:00
neuecc
35b933730b 2.0.11-rc8 2020-05-25 19:44:57 +09:00
neuecc
7ab9467069 IObservable<T>.ToUniTask parameter order changed from CancellationToken cancellationToken, bool useFirst to bool useFirst, CancellationToken cancellationToken 2020-05-25 19:42:51 +09:00
neuecc
598312ba61 DOTween's WithCancellation remove = default 2020-05-25 19:38:08 +09:00
neuecc
985aa5c43a add allocationchecker in netcore sandbox project 2020-05-25 19:37:11 +09:00
neuecc
10eff95a42 fix: does not work zero-allocation mechanism in release build 2020-05-25 19:36:39 +09:00
neuecc
d27d6d5d9d UniTask.Yield in .NET Core becomes zero allocation 2020-05-25 19:33:54 +09:00
neuecc
b8c109848e UniTaskVoid can not await. 2020-05-25 19:33:11 +09:00
neuecc
8b7f832c0f JobHandle.WaitAsync accepts CancellationToken 2020-05-25 09:58:06 +09:00
neuecc
7cce0f48e5 call webRequest.Abort() on canceled 2020-05-25 09:54:26 +09:00
neuecc
8a56838111 modify cannot await twice message 2020-05-25 01:55:35 +09:00
neuecc
ff15e00003 f 2020-05-24 03:33:25 +09:00
neuecc
f60d2c51fb fix in UnityEditor performance issue 2020-05-24 03:27:05 +09:00
neuecc
6dfb969015 Add ValueTask.AsUniTask only for .NET Core 2020-05-24 01:30:52 +09:00
neuecc
da7e9fc4b3 UniTask marked StructLayout(LayoutKind.Auto) 2020-05-24 01:30:33 +09:00
neuecc
70385c4115 Fix channel's cancellationTokenRegistration does not handle correctly 2020-05-24 00:19:13 +09:00
neuecc
51ba740413 In .NET Core, IUniTaskSource implements IValueTaskSource and implicit, zero overhead conversion 2020-05-24 00:18:39 +09:00
neuecc
f3e3ba8864 fix DOTweenExt's TweenCancelBehaviour.CancelAwait 2020-05-23 02:53:48 +09:00
neuecc
07cf65c1ec lower support is 2019.1 2020-05-23 02:12:19 +09:00
neuecc
eca5b1c096 2.0.10-rc7 2020-05-23 02:11:00 +09:00
neuecc
c74ce14ad1 DoTween -> DOTween 2020-05-23 02:10:18 +09:00
neuecc
f59c56506f Remove ConfigureAwait method from all async object extensions(renamed to ToUniTask). Add WithCancellation method to all async object extensions. Improved performance when async object is done. 2020-05-23 02:07:46 +09:00
neuecc
896eef1ee4 Add DoTween Extension 2020-05-23 01:10:04 +09:00
neuecc
ec0123eec7 rename UniTask.VoidAction -> UniTask.Action, UniTask.VoidUnityAction -> UniTask.UnityAction, there return type Func<UniTask> -> Func<UniTaskVoid> 2020-05-22 21:42:23 +09:00
neuecc
78f56b9b33 OCS 2020-05-22 17:15:36 +09:00
neuecc
1d88ed85bc fix ComibineLatest 2020-05-22 11:08:03 +09:00
48 changed files with 3104 additions and 655 deletions

View File

@@ -1,123 +1,30 @@
#pragma warning disable 0649
using System;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using System.Threading.Tasks.Sources;
namespace Cysharp.Threading.Tasks
{
public static class UniTaskValueTaskExtensions
{
public static ValueTask AsValueTask(this UniTask task)
public static ValueTask AsValueTask(this in UniTask task)
{
ref var core = ref Unsafe.As<UniTask, UniTaskToValueTask>(ref task);
if (core.source == null)
{
return default;
}
return new ValueTask(new UniTaskValueTaskSource(core.source), core.token);
return task;
}
public static ValueTask<T> AsValueTask<T>(this UniTask<T> task)
public static ValueTask<T> AsValueTask<T>(this in UniTask<T> task)
{
ref var core = ref Unsafe.As<UniTask<T>, UniTaskToValueTask<T>>(ref task);
if (core.source == null)
{
return new ValueTask<T>(core.result);
}
return new ValueTask<T>(new UniTaskValueTaskSource<T>(core.source), core.token);
return task;
}
struct UniTaskToValueTask
public static UniTask<T> AsUniTask<T>(this ValueTask<T> task, bool useCurrentSynchronizationContext = true)
{
public IUniTaskSource source;
public short token;
// NOTE: get _obj and _token directly for low overhead conversion but not yet implemented.
return task.AsTask().AsUniTask(useCurrentSynchronizationContext);
}
class UniTaskValueTaskSource : IValueTaskSource
public static UniTask AsUniTask(this ValueTask task, bool useCurrentSynchronizationContext = true)
{
readonly IUniTaskSource source;
public UniTaskValueTaskSource(IUniTaskSource source)
{
this.source = source;
}
public void GetResult(short token)
{
source.GetResult(token);
}
public ValueTaskSourceStatus GetStatus(short token)
{
var status = source.GetStatus(token);
switch (status)
{
case UniTaskStatus.Pending:
return ValueTaskSourceStatus.Pending;
case UniTaskStatus.Succeeded:
return ValueTaskSourceStatus.Succeeded;
case UniTaskStatus.Faulted:
return ValueTaskSourceStatus.Faulted;
case UniTaskStatus.Canceled:
return ValueTaskSourceStatus.Canceled;
default:
return (ValueTaskSourceStatus)status;
}
}
public void OnCompleted(Action<object> continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags)
{
source.OnCompleted(continuation, state, token);
}
}
struct UniTaskToValueTask<T>
{
public IUniTaskSource<T> source;
public T result;
public short token;
}
class UniTaskValueTaskSource<T> : IValueTaskSource<T>
{
readonly IUniTaskSource<T> source;
public UniTaskValueTaskSource(IUniTaskSource<T> source)
{
this.source = source;
}
public T GetResult(short token)
{
return source.GetResult(token);
}
public ValueTaskSourceStatus GetStatus(short token)
{
var status = source.GetStatus(token);
switch (status)
{
case UniTaskStatus.Pending:
return ValueTaskSourceStatus.Pending;
case UniTaskStatus.Succeeded:
return ValueTaskSourceStatus.Succeeded;
case UniTaskStatus.Faulted:
return ValueTaskSourceStatus.Faulted;
case UniTaskStatus.Canceled:
return ValueTaskSourceStatus.Canceled;
default:
return (ValueTaskSourceStatus)status;
}
}
public void OnCompleted(Action<object> continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags)
{
source.OnCompleted(continuation, state, token);
}
return task.AsTask().AsUniTask(useCurrentSynchronizationContext);
}
}
}

View File

@@ -1,4 +1,6 @@
using System;
using Cysharp.Threading.Tasks.Internal;
using System;
using System.Collections.Concurrent;
using System.Runtime.CompilerServices;
using System.Threading;
@@ -41,7 +43,11 @@ namespace Cysharp.Threading.Tasks
}
else
{
#if NETCOREAPP3_1
ThreadPool.UnsafeQueueUserWorkItem(ThreadPoolWorkItem.Create(continuation), false);
#else
ThreadPool.UnsafeQueueUserWorkItem(WaitCallbackDelegate, continuation);
#endif
}
}
@@ -50,6 +56,47 @@ namespace Cysharp.Threading.Tasks
((Action)state).Invoke();
}
}
#if NETCOREAPP3_1
sealed class ThreadPoolWorkItem : IThreadPoolWorkItem, ITaskPoolNode<ThreadPoolWorkItem>
{
static TaskPool<ThreadPoolWorkItem> pool;
public ThreadPoolWorkItem NextNode { get; set; }
static ThreadPoolWorkItem()
{
TaskPoolMonitor.RegisterSizeGetter(typeof(ThreadPoolWorkItem), () => pool.Size);
}
Action continuation;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ThreadPoolWorkItem Create(Action continuation)
{
if (!pool.TryPop(out var item))
{
item = new ThreadPoolWorkItem();
}
item.continuation = continuation;
return item;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Execute()
{
var call = continuation;
continuation = null;
if (call != null)
{
pool.TryPush(this);
call.Invoke();
}
}
}
#endif
}
}
}

View File

@@ -1,8 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<TargetFrameworks>netcoreapp3.1;netstandard2.1</TargetFrameworks>
<AssemblyName>UniTask</AssemblyName>
<LangVersion>8.0</LangVersion>
<RootNamespace>Cysharp.Threading.Tasks</RootNamespace>
</PropertyGroup>

View File

@@ -0,0 +1,258 @@
using BenchmarkDotNet.Attributes;
using System.Linq;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Diagnosers;
using BenchmarkDotNet.Exporters;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Running;
using Cysharp.Threading.Tasks;
using PooledAwait;
using System;
using System.Runtime.ExceptionServices;
using System.Threading.Tasks;
using System.Threading;
using System.Runtime.CompilerServices;
using Cysharp.Threading.Tasks.CompilerServices;
using System.Collections.Concurrent;
[Config(typeof(BenchmarkConfig))]
public class AllocationCheck
{
// note: all the benchmarks use Task/Task<T> for the public API, because BenchmarkDotNet
// doesn't work reliably with more exotic task-types (even just ValueTask fails); instead,
// we'll obscure the cost of the outer awaitable by doing a relatively large number of
// iterations, so that we're only really measuring the inner loop
private const int InnerOps = 1000;
[Benchmark(OperationsPerInvoke = InnerOps)]
public async Task ViaUniTask()
{
for (int i = 0; i < InnerOps; i++)
{
var a = Core();
var b = Core();
var c = Core();
await a;
await b;
await c;
}
static async UniTask Core()
{
await new TestAwaiter(false, UniTaskStatus.Succeeded);
await new TestAwaiter(false, UniTaskStatus.Succeeded);
await new TestAwaiter(false, UniTaskStatus.Succeeded);
}
}
[Benchmark(OperationsPerInvoke = InnerOps)]
public async Task<int> ViaUniTaskT()
{
var sum = 0;
for (int i = 0; i < InnerOps; i++)
{
var a = Core();
var b = Core();
var c = Core();
sum += await a;
sum += await b;
sum += await c;
}
return sum;
static async UniTask<int> Core()
{
var a = await new TestAwaiter<int>(false, UniTaskStatus.Succeeded, 10);
var b = await new TestAwaiter<int>(false, UniTaskStatus.Succeeded, 10);
var c = await new TestAwaiter<int>(false, UniTaskStatus.Succeeded, 10);
return 10;
}
}
//[Benchmark(OperationsPerInvoke = InnerOps)]
//[Benchmark]
public void ViaUniTaskVoid()
{
for (int i = 0; i < InnerOps; i++)
{
Core().Forget();
Core().Forget();
Core().Forget();
}
static async UniTaskVoid Core()
{
await new TestAwaiter(false, UniTaskStatus.Succeeded);
await new TestAwaiter(false, UniTaskStatus.Succeeded);
await new TestAwaiter(false, UniTaskStatus.Succeeded);
}
}
struct Foo : IAsyncStateMachine
{
public AsyncUniTaskVoidMethodBuilder builder;
public TestAwaiter awaiter;
public TestAwaiter awaiterawaiter;
public int state;
public void MoveNext()
{
switch (state)
{
case -1:
awaiterawaiter = awaiter.GetAwaiter();
if (awaiterawaiter.IsCompleted)
{
goto case 0;
}
else
{
state = 0;
builder.AwaitUnsafeOnCompleted(ref awaiterawaiter, ref this);
return;
}
case 0:
default:
goto END;
}
END:
builder.SetResult();
}
public void SetStateMachine(IAsyncStateMachine stateMachine)
{
}
}
}
public class TaskTestException : Exception
{
}
public struct TestAwaiter : ICriticalNotifyCompletion
{
readonly UniTaskStatus status;
readonly bool isCompleted;
public TestAwaiter(bool isCompleted, UniTaskStatus status)
{
this.isCompleted = isCompleted;
this.status = status;
}
public TestAwaiter GetAwaiter() => this;
public bool IsCompleted => isCompleted;
public void GetResult()
{
switch (status)
{
case UniTaskStatus.Faulted:
throw new TaskTestException();
case UniTaskStatus.Canceled:
throw new OperationCanceledException();
case UniTaskStatus.Pending:
case UniTaskStatus.Succeeded:
default:
break;
}
}
public void OnCompleted(Action continuation)
{
ThreadPool.UnsafeQueueUserWorkItem(ThreadPoolWorkItem.Create(continuation), false);
}
public void UnsafeOnCompleted(Action continuation)
{
ThreadPool.UnsafeQueueUserWorkItem(ThreadPoolWorkItem.Create(continuation), false);
}
}
public struct TestAwaiter<T> : ICriticalNotifyCompletion
{
readonly UniTaskStatus status;
readonly bool isCompleted;
readonly T value;
public TestAwaiter(bool isCompleted, UniTaskStatus status, T value)
{
this.isCompleted = isCompleted;
this.status = status;
this.value = value;
}
public TestAwaiter<T> GetAwaiter() => this;
public bool IsCompleted => isCompleted;
public T GetResult()
{
switch (status)
{
case UniTaskStatus.Faulted:
throw new TaskTestException();
case UniTaskStatus.Canceled:
throw new OperationCanceledException();
case UniTaskStatus.Pending:
case UniTaskStatus.Succeeded:
default:
return value;
}
}
public void OnCompleted(Action continuation)
{
ThreadPool.UnsafeQueueUserWorkItem(ThreadPoolWorkItem.Create(continuation), false);
}
public void UnsafeOnCompleted(Action continuation)
{
ThreadPool.UnsafeQueueUserWorkItem(ThreadPoolWorkItem.Create(continuation), false);
}
}
public sealed class ThreadPoolWorkItem : IThreadPoolWorkItem
{
public static readonly ConcurrentQueue<ThreadPoolWorkItem> pool = new ConcurrentQueue<ThreadPoolWorkItem>();
public static void CreatePoolItems(int count)
{
for (int i = 0; i < count; i++)
{
pool.Enqueue(new ThreadPoolWorkItem());
}
}
Action continuation;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ThreadPoolWorkItem Create(Action continuation)
{
if (!pool.TryDequeue(out var item))
{
item = new ThreadPoolWorkItem();
}
item.continuation = continuation;
return item;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Execute()
{
var call = continuation;
continuation = null;
pool.Enqueue(this);
call.Invoke();
}
}

View File

@@ -0,0 +1,283 @@
using BenchmarkDotNet.Attributes;
using System.Linq;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Diagnosers;
using BenchmarkDotNet.Exporters;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Running;
using Cysharp.Threading.Tasks;
using PooledAwait;
using System;
using System.Runtime.ExceptionServices;
using System.Threading.Tasks;
using System.Threading;
using System.Runtime.CompilerServices;
using Cysharp.Threading.Tasks.CompilerServices;
//class Program
//{
// static void Main(string[] args)
// {
// var switcher = new BenchmarkSwitcher(new[]
// {
// typeof(StandardBenchmark)
// });
//#if DEBUG
// var b = new StandardBenchmark();
//#else
// switcher.Run(args);
//#endif
// }
//}
public class BenchmarkConfig : ManualConfig
{
public BenchmarkConfig()
{
AddDiagnoser(MemoryDiagnoser.Default);
AddJob(Job.ShortRun.WithLaunchCount(1).WithIterationCount(1).WithWarmupCount(1)/*.RunOncePerIteration()*/);
}
}
// borrowed from PooledAwait
[Config(typeof(BenchmarkConfig))]
[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)]
[CategoriesColumn]
public class ComparisonBenchmarks
{
// note: all the benchmarks use Task/Task<T> for the public API, because BenchmarkDotNet
// doesn't work reliably with more exotic task-types (even just ValueTask fails); instead,
// we'll obscure the cost of the outer awaitable by doing a relatively large number of
// iterations, so that we're only really measuring the inner loop
private const int InnerOps = 1000;
public bool ConfigureAwait { get; set; } = false;
[Benchmark(OperationsPerInvoke = InnerOps, Description = ".NET")]
[BenchmarkCategory("Task<T>")]
public async Task<int> ViaTaskT()
{
int sum = 0;
for (int i = 0; i < InnerOps; i++)
sum += await Inner(1, 2).ConfigureAwait(ConfigureAwait);
return sum;
static async Task<int> Inner(int x, int y)
{
int i = x;
await Task.Yield();
i *= y;
await Task.Yield();
return 5 * i;
}
}
[Benchmark(OperationsPerInvoke = InnerOps, Description = ".NET")]
[BenchmarkCategory("Task")]
public async Task ViaTask()
{
for (int i = 0; i < InnerOps; i++)
await Inner().ConfigureAwait(ConfigureAwait);
static async Task Inner()
{
await Task.Yield();
await Task.Yield();
}
}
[Benchmark(OperationsPerInvoke = InnerOps, Description = ".NET")]
[BenchmarkCategory("ValueTask<T>")]
public async Task<int> ViaValueTaskT()
{
int sum = 0;
for (int i = 0; i < InnerOps; i++)
sum += await Inner(1, 2).ConfigureAwait(ConfigureAwait);
return sum;
static async ValueTask<int> Inner(int x, int y)
{
int i = x;
await Task.Yield();
i *= y;
await Task.Yield();
return 5 * i;
}
}
[Benchmark(OperationsPerInvoke = InnerOps, Description = ".NET")]
[BenchmarkCategory("ValueTask")]
public async Task ViaValueTask()
{
for (int i = 0; i < InnerOps; i++)
await Inner().ConfigureAwait(ConfigureAwait);
static async ValueTask Inner()
{
await Task.Yield();
await Task.Yield();
}
}
[Benchmark(OperationsPerInvoke = InnerOps, Description = "Pooled")]
[BenchmarkCategory("ValueTask<T>")]
public async Task<int> ViaPooledValueTaskT()
{
int sum = 0;
for (int i = 0; i < InnerOps; i++)
sum += await Inner(1, 2).ConfigureAwait(ConfigureAwait);
return sum;
static async PooledValueTask<int> Inner(int x, int y)
{
int i = x;
await Task.Yield();
i *= y;
await Task.Yield();
return 5 * i;
}
}
[Benchmark(OperationsPerInvoke = InnerOps, Description = "Pooled")]
[BenchmarkCategory("ValueTask")]
public async Task ViaPooledValueTask()
{
for (int i = 0; i < InnerOps; i++)
await Inner().ConfigureAwait(ConfigureAwait);
static async PooledValueTask Inner()
{
await Task.Yield();
await Task.Yield();
}
}
[Benchmark(OperationsPerInvoke = InnerOps, Description = "Pooled")]
[BenchmarkCategory("Task<T>")]
public async Task<int> ViaPooledTaskT()
{
int sum = 0;
for (int i = 0; i < InnerOps; i++)
sum += await Inner(1, 2).ConfigureAwait(ConfigureAwait);
return sum;
static async PooledTask<int> Inner(int x, int y)
{
int i = x;
await Task.Yield();
i *= y;
await Task.Yield();
return 5 * i;
}
}
[Benchmark(OperationsPerInvoke = InnerOps, Description = "Pooled")]
[BenchmarkCategory("Task")]
public async Task ViaPooledTask()
{
for (int i = 0; i < InnerOps; i++)
await Inner().ConfigureAwait(ConfigureAwait);
static async PooledTask Inner()
{
await Task.Yield();
await Task.Yield();
}
}
// ---
//[Benchmark(OperationsPerInvoke = InnerOps, Description = "UniTaskVoid")]
//[BenchmarkCategory("UniTask")]
//public async Task ViaUniTaskVoid()
//{
// for (int i = 0; i < InnerOps; i++)
// {
// await Inner();
// }
// static async UniTaskVoid Inner()
// {
// await UniTask.Yield();
// await UniTask.Yield();
// }
//}
[Benchmark(OperationsPerInvoke = InnerOps, Description = "UniTask")]
[BenchmarkCategory("UniTask")]
public async Task ViaUniTask()
{
for (int i = 0; i < InnerOps; i++)
{
await Inner();
}
static async UniTask Inner()
{
await UniTask.Yield();
await UniTask.Yield();
}
}
[Benchmark(OperationsPerInvoke = InnerOps, Description = "UniTaskT")]
[BenchmarkCategory("UniTask")]
public async Task<int> ViaUniTaskT()
{
var sum = 0;
for (int i = 0; i < InnerOps; i++)
{
sum += await Inner(1, 2);
}
return sum;
static async UniTask<int> Inner(int x, int y)
{
int i = x;
await UniTask.Yield();
i *= y;
await UniTask.Yield();
return 5 * i;
}
}
}
public struct MyAwaiter : ICriticalNotifyCompletion
{
public MyAwaiter GetAwaiter() => this;
public bool IsCompleted => false;
public void GetResult()
{
}
public void OnCompleted(Action continuation)
{
continuation();
}
public void UnsafeOnCompleted(Action continuation)
{
continuation();
}
}
public struct MyTestStateMachine : IAsyncStateMachine
{
public void MoveNext()
{
//throw new NotImplementedException();
}
public void SetStateMachine(IAsyncStateMachine stateMachine)
{
//throw new NotImplementedException();
}
}

View File

@@ -21,6 +21,113 @@ namespace NetCoreSandbox
public string text { get; set; }
}
public class ZeroAllocAsyncAwaitInDotNetCore
{
public ValueTask<int> NanikaAsync(int x, int y)
{
return Core(this, x, y);
static async UniTask<int> Core(ZeroAllocAsyncAwaitInDotNetCore self, int x, int y)
{
// nanika suru...
await Task.Delay(TimeSpan.FromSeconds(x + y));
return 10;
}
}
}
public class TaskTestException : Exception
{
}
public struct TestAwaiter : ICriticalNotifyCompletion
{
readonly UniTaskStatus status;
readonly bool isCompleted;
public TestAwaiter(bool isCompleted, UniTaskStatus status)
{
this.isCompleted = isCompleted;
this.status = status;
}
public TestAwaiter GetAwaiter() => this;
public bool IsCompleted => isCompleted;
public void GetResult()
{
switch (status)
{
case UniTaskStatus.Faulted:
throw new TaskTestException();
case UniTaskStatus.Canceled:
throw new OperationCanceledException();
case UniTaskStatus.Pending:
case UniTaskStatus.Succeeded:
default:
break;
}
}
public void OnCompleted(Action continuation)
{
ThreadPool.QueueUserWorkItem(_ => continuation(), null);
}
public void UnsafeOnCompleted(Action continuation)
{
ThreadPool.UnsafeQueueUserWorkItem(_ => continuation(), null);
}
}
public struct TestAwaiter<T> : ICriticalNotifyCompletion
{
readonly UniTaskStatus status;
readonly bool isCompleted;
readonly T value;
public TestAwaiter(bool isCompleted, UniTaskStatus status, T value)
{
this.isCompleted = isCompleted;
this.status = status;
this.value = value;
}
public TestAwaiter<T> GetAwaiter() => this;
public bool IsCompleted => isCompleted;
public T GetResult()
{
switch (status)
{
case UniTaskStatus.Faulted:
throw new TaskTestException();
case UniTaskStatus.Canceled:
throw new OperationCanceledException();
case UniTaskStatus.Pending:
case UniTaskStatus.Succeeded:
default:
return value;
}
}
public void OnCompleted(Action continuation)
{
ThreadPool.QueueUserWorkItem(_ => continuation(), null);
}
public void UnsafeOnCompleted(Action continuation)
{
ThreadPool.UnsafeQueueUserWorkItem(_ => continuation(), null);
}
}
public static partial class UnityUIComponentExtensions
{
public static void BindTo(this IUniTaskAsyncEnumerable<string> source, Text text)
@@ -88,26 +195,76 @@ namespace NetCoreSandbox
static void Main(string[] args)
static async Task Main(string[] args)
{
#if !DEBUG
//await new AllocationCheck().ViaUniTaskVoid();
//Console.ReadLine();
BenchmarkDotNet.Running.BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);
var channel = Channel.CreateSingleConsumerUnbounded<int>();
//await new ComparisonBenchmarks().ViaUniTaskT();
return;
#endif
// await new AllocationCheck().ViaUniTaskVoid();
// Observable.Range(1,10).CombineLatest(
var cts = new CancellationTokenSource();
var token = cts.Token;
FooAsync(token).ForEachAsync(x => { }, token);
// AsyncTest().Forget();
// Observable.Range(1,10).CombineLatest(
//AsyncTest().Forget();
// AsyncTest().Forget();
ThreadPool.SetMinThreads(100, 100);
//List<UniTask<int>> list = new List<UniTask<int>>();
for (int i = 0; i < short.MaxValue; i++)
{
//// list.Add(AsyncTest());
await YieldCore();
}
//await UniTask.WhenAll(list);
//Console.WriteLine("TOGO");
//var a = await AsyncTest();
//var b = AsyncTest();
//var c = AsyncTest();
await YieldCore();
//await b;
//await c;
foreach (var item in Cysharp.Threading.Tasks.Internal.TaskPoolMonitor.GetCacheSizeInfo())
{
Console.WriteLine(item);
}
Console.ReadLine();
}
static async UniTask YieldCore()
{
await UniTask.Yield();
}
#pragma warning disable CS1998
static async UniTask<int> AsyncTest()
{
// empty
await new TestAwaiter(false, UniTaskStatus.Succeeded);
await new TestAwaiter(true, UniTaskStatus.Succeeded);
await new TestAwaiter(false, UniTaskStatus.Succeeded);
return 10;
}
#pragma warning restore CS1998
void Foo()
{

View File

@@ -7,6 +7,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
<PackageReference Include="PooledAwait" Version="1.0.49" />
<PackageReference Include="System.Interactive.Async" Version="4.1.1" />
<PackageReference Include="System.Reactive" Version="4.4.1" />
</ItemGroup>

View File

@@ -0,0 +1,301 @@
#pragma warning disable CS1998
using Cysharp.Threading.Tasks;
using FluentAssertions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Channels;
using Cysharp.Threading.Tasks.Linq;
using System.Threading.Tasks;
using Xunit;
using System.Runtime.CompilerServices;
namespace NetCoreTests
{
public class UniTaskBuilderTest
{
[Fact]
public async Task Empty()
{
await Core();
static async UniTask Core()
{
}
}
[Fact]
public async Task EmptyThrow()
{
await Assert.ThrowsAsync<TaskTestException>(async () => await Core());
static async UniTask Core()
{
throw new TaskTestException();
}
}
[Fact]
public async Task Task_Done()
{
await Core();
static async UniTask Core()
{
await new TestAwaiter(true, UniTaskStatus.Succeeded);
}
}
[Fact]
public async Task Task_Fail()
{
await Assert.ThrowsAsync<TaskTestException>(async () => await Core());
static async UniTask Core()
{
await new TestAwaiter(true, UniTaskStatus.Faulted);
}
}
[Fact]
public async Task Task_Cancel()
{
await Assert.ThrowsAsync<OperationCanceledException>(async () => await Core());
static async UniTask Core()
{
await new TestAwaiter(true, UniTaskStatus.Canceled);
}
}
[Fact]
public async Task AwaitUnsafeOnCompletedCall_Task_SetResult()
{
await Core();
static async UniTask Core()
{
await new TestAwaiter(false, UniTaskStatus.Succeeded);
await new TestAwaiter(false, UniTaskStatus.Succeeded);
await new TestAwaiter(false, UniTaskStatus.Succeeded);
}
}
[Fact]
public async Task AwaitUnsafeOnCompletedCall_Task_SetException()
{
await Assert.ThrowsAsync<TaskTestException>(async () => await Core());
static async UniTask Core()
{
await new TestAwaiter(false, UniTaskStatus.Succeeded);
await new TestAwaiter(false, UniTaskStatus.Faulted);
throw new InvalidOperationException();
}
}
[Fact]
public async Task AwaitUnsafeOnCompletedCall_Task_SetCancelException()
{
await Assert.ThrowsAsync<OperationCanceledException>(async () => await Core());
static async UniTask Core()
{
await new TestAwaiter(false, UniTaskStatus.Succeeded);
await new TestAwaiter(false, UniTaskStatus.Canceled);
throw new InvalidOperationException();
}
}
}
public class UniTask_T_BuilderTest
{
[Fact]
public async Task Empty()
{
(await Core()).Should().Be(10);
static async UniTask<int> Core()
{
return 10;
}
}
[Fact]
public async Task EmptyThrow()
{
await Assert.ThrowsAsync<TaskTestException>(async () => await Core());
static async UniTask<int> Core()
{
throw new TaskTestException();
}
}
[Fact]
public async Task Task_Done()
{
(await Core()).Should().Be(10);
static async UniTask<int> Core()
{
return await new TestAwaiter<int>(true, UniTaskStatus.Succeeded, 10);
}
}
[Fact]
public async Task Task_Fail()
{
await Assert.ThrowsAsync<TaskTestException>(async () => await Core());
static async UniTask<int> Core()
{
return await new TestAwaiter<int>(true, UniTaskStatus.Faulted, 10);
}
}
[Fact]
public async Task Task_Cancel()
{
await Assert.ThrowsAsync<OperationCanceledException>(async () => await Core());
static async UniTask<int> Core()
{
return await new TestAwaiter<int>(true, UniTaskStatus.Canceled, 10);
}
}
[Fact]
public async Task AwaitUnsafeOnCompletedCall_Task_SetResult()
{
(await Core()).Should().Be(6);
static async UniTask<int> Core()
{
var sum = 0;
sum += await new TestAwaiter<int>(false, UniTaskStatus.Succeeded, 1);
sum += await new TestAwaiter<int>(false, UniTaskStatus.Succeeded, 2);
sum += await new TestAwaiter<int>(false, UniTaskStatus.Succeeded, 3);
return sum;
}
}
[Fact]
public async Task AwaitUnsafeOnCompletedCall_Task_SetException()
{
await Assert.ThrowsAsync<TaskTestException>(async () => await Core());
static async UniTask<int> Core()
{
await new TestAwaiter<int>(false, UniTaskStatus.Succeeded, 10);
await new TestAwaiter<int>(false, UniTaskStatus.Faulted, 10);
throw new InvalidOperationException();
}
}
[Fact]
public async Task AwaitUnsafeOnCompletedCall_Task_SetCancelException()
{
await Assert.ThrowsAsync<OperationCanceledException>(async () => await Core());
static async UniTask<int> Core()
{
await new TestAwaiter<int>(false, UniTaskStatus.Succeeded, 10);
await new TestAwaiter<int>(false, UniTaskStatus.Canceled, 10);
throw new InvalidOperationException();
}
}
}
public class TaskTestException : Exception
{
}
public struct TestAwaiter : ICriticalNotifyCompletion
{
readonly UniTaskStatus status;
readonly bool isCompleted;
public TestAwaiter(bool isCompleted, UniTaskStatus status)
{
this.isCompleted = isCompleted;
this.status = status;
}
public TestAwaiter GetAwaiter() => this;
public bool IsCompleted => isCompleted;
public void GetResult()
{
switch (status)
{
case UniTaskStatus.Faulted:
throw new TaskTestException();
case UniTaskStatus.Canceled:
throw new OperationCanceledException();
case UniTaskStatus.Pending:
case UniTaskStatus.Succeeded:
default:
break;
}
}
public void OnCompleted(Action continuation)
{
ThreadPool.QueueUserWorkItem(_ => continuation(), null);
}
public void UnsafeOnCompleted(Action continuation)
{
ThreadPool.UnsafeQueueUserWorkItem(_ => continuation(), null);
}
}
public struct TestAwaiter<T> : ICriticalNotifyCompletion
{
readonly UniTaskStatus status;
readonly bool isCompleted;
readonly T value;
public TestAwaiter(bool isCompleted, UniTaskStatus status, T value)
{
this.isCompleted = isCompleted;
this.status = status;
this.value = value;
}
public TestAwaiter<T> GetAwaiter() => this;
public bool IsCompleted => isCompleted;
public T GetResult()
{
switch (status)
{
case UniTaskStatus.Faulted:
throw new TaskTestException();
case UniTaskStatus.Canceled:
throw new OperationCanceledException();
case UniTaskStatus.Pending:
case UniTaskStatus.Succeeded:
default:
return value;
}
}
public void OnCompleted(Action continuation)
{
ThreadPool.QueueUserWorkItem(_ => continuation(), null);
}
public void UnsafeOnCompleted(Action continuation)
{
ThreadPool.UnsafeQueueUserWorkItem(_ => continuation(), null);
}
}
}

View File

@@ -0,0 +1,30 @@
#if UNITY_EDITOR
using Cysharp.Threading.Tasks;
using System;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.Networking;
public static class EditorRunnerChecker
{
[MenuItem("Tools/UniTaskEditorRunnerChecker")]
public static void RunUniTaskAsync()
{
RunCore().Forget();
}
static async UniTaskVoid RunCore()
{
Debug.Log("Start");
var r = await UnityWebRequest.Get("https://bing.com/").SendWebRequest().ToUniTask();
Debug.Log(r.downloadHandler.text.Substring(0, 100));
Debug.Log("End");
}
}
#endif

View File

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

View File

@@ -4,7 +4,7 @@ using System;
namespace Cysharp.Threading.Tasks
{
public struct AsyncUnit : IEquatable<AsyncUnit>
public readonly struct AsyncUnit : IEquatable<AsyncUnit>
{
public static readonly AsyncUnit Default = new AsyncUnit();

View File

@@ -368,8 +368,8 @@ namespace Cysharp.Threading.Tasks
readonly SingleConsumerUnboundedChannelReader parent;
CancellationToken cancellationToken1;
CancellationToken cancellationToken2;
CancellationTokenRegistration CancellationTokenRegistration1;
CancellationTokenRegistration CancellationTokenRegistration2;
CancellationTokenRegistration cancellationTokenRegistration1;
CancellationTokenRegistration cancellationTokenRegistration2;
T current;
bool cacheValue;
@@ -395,12 +395,12 @@ namespace Cysharp.Threading.Tasks
if (this.cancellationToken1.CanBeCanceled)
{
this.cancellationToken1.RegisterWithoutCaptureExecutionContext(CancellationCallback1Delegate, this);
this.cancellationTokenRegistration1 = this.cancellationToken1.RegisterWithoutCaptureExecutionContext(CancellationCallback1Delegate, this);
}
if (this.cancellationToken2.CanBeCanceled)
{
this.cancellationToken2.RegisterWithoutCaptureExecutionContext(CancellationCallback2Delegate, this);
this.cancellationTokenRegistration2 = this.cancellationToken2.RegisterWithoutCaptureExecutionContext(CancellationCallback2Delegate, this);
}
running = true;
@@ -428,8 +428,8 @@ namespace Cysharp.Threading.Tasks
public UniTask DisposeAsync()
{
CancellationTokenRegistration1.Dispose();
CancellationTokenRegistration2.Dispose();
cancellationTokenRegistration1.Dispose();
cancellationTokenRegistration2.Dispose();
return default;
}

View File

@@ -12,9 +12,8 @@ namespace Cysharp.Threading.Tasks.CompilerServices
[StructLayout(LayoutKind.Auto)]
public struct AsyncUniTaskMethodBuilder
{
// cache items.
AutoResetUniTaskCompletionSource promise;
IMoveNextRunner runner;
internal IMoveNextRunnerPromise runnerPromise;
Exception ex;
// 1. Static Create method.
[DebuggerHidden]
@@ -31,18 +30,18 @@ namespace Cysharp.Threading.Tasks.CompilerServices
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
if (promise != null)
if (runnerPromise != null)
{
return promise.Task;
return runnerPromise.Task;
}
if (runner == null)
else if (ex != null)
{
return UniTask.FromException(ex);
}
else
{
return UniTask.CompletedTask;
}
promise = AutoResetUniTaskCompletionSource.Create();
return promise.Task;
}
}
@@ -50,20 +49,13 @@ namespace Cysharp.Threading.Tasks.CompilerServices
[DebuggerHidden]
public void SetException(Exception exception)
{
// runner is finished, return first.
if (runner != null)
if (runnerPromise == null)
{
runner.Return();
runner = null;
}
if (promise != null)
{
promise.TrySetException(exception);
ex = exception;
}
else
{
promise = AutoResetUniTaskCompletionSource.CreateFromException(exception, out _);
runnerPromise.SetException(exception);
}
}
@@ -71,16 +63,9 @@ namespace Cysharp.Threading.Tasks.CompilerServices
[DebuggerHidden]
public void SetResult()
{
// runner is finished, return first.
if (runner != null)
if (runnerPromise != null)
{
runner.Return();
runner = null;
}
if (promise != null)
{
promise.TrySetResult();
runnerPromise.SetResult();
}
}
@@ -90,16 +75,12 @@ namespace Cysharp.Threading.Tasks.CompilerServices
where TAwaiter : INotifyCompletion
where TStateMachine : IAsyncStateMachine
{
if (promise == null)
if (runnerPromise == null)
{
promise = AutoResetUniTaskCompletionSource.Create();
}
if (runner == null)
{
runner = MoveNextRunner<TStateMachine>.Create(ref stateMachine);
AsyncUniTask<TStateMachine>.SetStateMachine(ref this, ref stateMachine);
}
awaiter.OnCompleted(runner.CallMoveNext);
awaiter.OnCompleted(runnerPromise.MoveNext);
}
// 6. AwaitUnsafeOnCompleted
@@ -109,16 +90,12 @@ namespace Cysharp.Threading.Tasks.CompilerServices
where TAwaiter : ICriticalNotifyCompletion
where TStateMachine : IAsyncStateMachine
{
if (promise == null)
if (runnerPromise == null)
{
promise = AutoResetUniTaskCompletionSource.Create();
}
if (runner == null)
{
runner = MoveNextRunner<TStateMachine>.Create(ref stateMachine);
AsyncUniTask<TStateMachine>.SetStateMachine(ref this, ref stateMachine);
}
awaiter.OnCompleted(runner.CallMoveNext);
awaiter.UnsafeOnCompleted(runnerPromise.MoveNext);
}
// 7. Start
@@ -156,9 +133,8 @@ namespace Cysharp.Threading.Tasks.CompilerServices
[StructLayout(LayoutKind.Auto)]
public struct AsyncUniTaskMethodBuilder<T>
{
// cache items.
AutoResetUniTaskCompletionSource<T> promise;
IMoveNextRunner runner;
internal IMoveNextRunnerPromise<T> runnerPromise;
Exception ex;
T result;
// 1. Static Create method.
@@ -175,18 +151,18 @@ namespace Cysharp.Threading.Tasks.CompilerServices
{
get
{
if (promise != null)
if (runnerPromise != null)
{
return promise.Task;
return runnerPromise.Task;
}
if (runner == null)
else if (ex != null)
{
return UniTask.FromException<T>(ex);
}
else
{
return UniTask.FromResult(result);
}
promise = AutoResetUniTaskCompletionSource<T>.Create();
return promise.Task;
}
}
@@ -194,20 +170,13 @@ namespace Cysharp.Threading.Tasks.CompilerServices
[DebuggerHidden]
public void SetException(Exception exception)
{
// runner is finished, return first.
if (runner != null)
if (runnerPromise == null)
{
runner.Return();
runner = null;
}
if (promise == null)
{
promise = AutoResetUniTaskCompletionSource<T>.CreateFromException(exception, out _);
ex = exception;
}
else
{
promise.TrySetException(exception);
runnerPromise.SetException(exception);
}
}
@@ -215,20 +184,14 @@ namespace Cysharp.Threading.Tasks.CompilerServices
[DebuggerHidden]
public void SetResult(T result)
{
// runner is finished, return first.
if (runner != null)
{
runner.Return();
runner = null;
}
if (promise == null)
if (runnerPromise == null)
{
this.result = result;
return;
}
promise.TrySetResult(result);
else
{
runnerPromise.SetResult(result);
}
}
// 5. AwaitOnCompleted
@@ -237,16 +200,12 @@ namespace Cysharp.Threading.Tasks.CompilerServices
where TAwaiter : INotifyCompletion
where TStateMachine : IAsyncStateMachine
{
if (promise == null)
if (runnerPromise == null)
{
promise = AutoResetUniTaskCompletionSource<T>.Create();
}
if (runner == null)
{
runner = MoveNextRunner<TStateMachine>.Create(ref stateMachine);
AsyncUniTask<TStateMachine, T>.SetStateMachine(ref this, ref stateMachine);
}
awaiter.OnCompleted(runner.CallMoveNext);
awaiter.OnCompleted(runnerPromise.MoveNext);
}
// 6. AwaitUnsafeOnCompleted
@@ -256,16 +215,12 @@ namespace Cysharp.Threading.Tasks.CompilerServices
where TAwaiter : ICriticalNotifyCompletion
where TStateMachine : IAsyncStateMachine
{
if (promise == null)
if (runnerPromise == null)
{
promise = AutoResetUniTaskCompletionSource<T>.Create();
}
if (runner == null)
{
runner = MoveNextRunner<TStateMachine>.Create(ref stateMachine);
AsyncUniTask<TStateMachine, T>.SetStateMachine(ref this, ref stateMachine);
}
awaiter.OnCompleted(runner.CallMoveNext);
awaiter.UnsafeOnCompleted(runnerPromise.MoveNext);
}
// 7. Start

View File

@@ -4,13 +4,15 @@
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
{
IMoveNextRunner runner;
internal IMoveNextRunner runner;
// 1. Static Create method.
[DebuggerHidden]
@@ -65,10 +67,10 @@ namespace Cysharp.Threading.Tasks.CompilerServices
{
if (runner == null)
{
runner = MoveNextRunner<TStateMachine>.Create(ref stateMachine);
AsyncUniTaskVoid<TStateMachine>.SetStateMachine(ref this, ref stateMachine);
}
awaiter.OnCompleted(runner.CallMoveNext);
awaiter.OnCompleted(runner.MoveNext);
}
// 6. AwaitUnsafeOnCompleted
@@ -80,10 +82,10 @@ namespace Cysharp.Threading.Tasks.CompilerServices
{
if (runner == null)
{
runner = MoveNextRunner<TStateMachine>.Create(ref stateMachine);
AsyncUniTaskVoid<TStateMachine>.SetStateMachine(ref this, ref stateMachine);
}
awaiter.OnCompleted(runner.CallMoveNext);
awaiter.UnsafeOnCompleted(runner.MoveNext);
}
// 7. Start

View File

@@ -1,56 +1,327 @@

#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using Cysharp.Threading.Tasks.Internal;
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using Cysharp.Threading.Tasks.Internal;
namespace Cysharp.Threading.Tasks.CompilerServices
{
internal interface IMoveNextRunner
public interface IMoveNextRunner
{
Action CallMoveNext { get; }
Action MoveNext { get; }
void Return();
}
internal sealed class MoveNextRunner<TStateMachine> : IMoveNextRunner, IPromisePoolItem
internal interface IMoveNextRunnerPromise : IUniTaskSource
{
Action MoveNext { get; }
UniTask Task { get; }
void SetResult();
void SetException(Exception exception);
}
internal interface IMoveNextRunnerPromise<T> : IUniTaskSource<T>
{
Action MoveNext { get; }
UniTask<T> Task { get; }
void SetResult(T result);
void SetException(Exception exception);
}
internal sealed class AsyncUniTaskVoid<TStateMachine> : IMoveNextRunner, ITaskPoolNode<AsyncUniTaskVoid<TStateMachine>>, IUniTaskSource
where TStateMachine : IAsyncStateMachine
{
static PromisePool<MoveNextRunner<TStateMachine>> pool = new PromisePool<MoveNextRunner<TStateMachine>>();
static TaskPool<AsyncUniTaskVoid<TStateMachine>> pool;
TStateMachine stateMachine;
internal readonly Action callMoveNext;
public Action CallMoveNext => callMoveNext;
public Action MoveNext { get; }
MoveNextRunner()
public AsyncUniTaskVoid()
{
callMoveNext = MoveNext;
MoveNext = Run;
}
public static MoveNextRunner<TStateMachine> Create(ref TStateMachine stateMachine)
public static void SetStateMachine(ref AsyncUniTaskVoidMethodBuilder builder, ref TStateMachine stateMachine)
{
var result = pool.TryRent() ?? new MoveNextRunner<TStateMachine>();
result.stateMachine = stateMachine;
return result;
if (!pool.TryPop(out var result))
{
result = new AsyncUniTaskVoid<TStateMachine>();
}
TaskTracker.TrackActiveTask(result, 3);
builder.runner = result; // set runner before copied.
result.stateMachine = stateMachine; // copy struct StateMachine(in release build).
}
static AsyncUniTaskVoid()
{
TaskPoolMonitor.RegisterSizeGetter(typeof(AsyncUniTaskVoid<TStateMachine>), () => pool.Size);
}
public AsyncUniTaskVoid<TStateMachine> NextNode { get; set; }
public void Return()
{
TaskTracker.RemoveTracking(this);
stateMachine = default;
pool.TryPush(this);
}
[DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
void MoveNext()
void Run()
{
stateMachine.MoveNext();
}
public void Return()
// dummy interface implementation for TaskTracker.
UniTaskStatus IUniTaskSource.GetStatus(short token)
{
pool.TryReturn(this);
return UniTaskStatus.Pending;
}
void IPromisePoolItem.Reset()
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> : IMoveNextRunnerPromise, IUniTaskSource, ITaskPoolNode<AsyncUniTask<TStateMachine>>
where TStateMachine : IAsyncStateMachine
{
static TaskPool<AsyncUniTask<TStateMachine>> pool;
TStateMachine stateMachine;
public Action MoveNext { get; }
UniTaskCompletionSourceCore<AsyncUnit> core;
AsyncUniTask()
{
MoveNext = Run;
}
public static void SetStateMachine(ref AsyncUniTaskMethodBuilder builder, ref TStateMachine stateMachine)
{
if (!pool.TryPop(out var result))
{
result = new AsyncUniTask<TStateMachine>();
}
TaskTracker.TrackActiveTask(result, 3);
builder.runnerPromise = result; // set runner before copied.
result.stateMachine = stateMachine; // copy struct StateMachine(in release build).
}
public AsyncUniTask<TStateMachine> NextNode { get; set; }
static AsyncUniTask()
{
TaskPoolMonitor.RegisterSizeGetter(typeof(AsyncUniTask<TStateMachine>), () => pool.Size);
}
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
{
TryReturn();
}
}
[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);
}
~AsyncUniTask()
{
if (TryReturn())
{
GC.ReRegisterForFinalize(this);
}
}
}
internal sealed class AsyncUniTask<TStateMachine, T> : IMoveNextRunnerPromise<T>, IUniTaskSource<T>, ITaskPoolNode<AsyncUniTask<TStateMachine, T>>
where TStateMachine : IAsyncStateMachine
{
static TaskPool<AsyncUniTask<TStateMachine, T>> pool;
TStateMachine stateMachine;
public Action MoveNext { get; }
UniTaskCompletionSourceCore<T> core;
AsyncUniTask()
{
MoveNext = Run;
}
public static void SetStateMachine(ref AsyncUniTaskMethodBuilder<T> builder, ref TStateMachine stateMachine)
{
if (!pool.TryPop(out var result))
{
result = new AsyncUniTask<TStateMachine, T>();
}
TaskTracker.TrackActiveTask(result, 3);
builder.runnerPromise = result; // set runner before copied.
result.stateMachine = stateMachine; // copy struct StateMachine(in release build).
}
public AsyncUniTask<TStateMachine, T> NextNode { get; set; }
static AsyncUniTask()
{
TaskPoolMonitor.RegisterSizeGetter(typeof(AsyncUniTask<TStateMachine, T>), () => pool.Size);
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
stateMachine = default;
return pool.TryPush(this);
}
[DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
void Run()
{
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
{
TryReturn();
}
}
[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);
}
~AsyncUniTask()
{
if (TryReturn())
{
GC.ReRegisterForFinalize(this);
}
}
}
}

View File

@@ -14,22 +14,31 @@ namespace Cysharp.Threading.Tasks
{
public static UniTask.Awaiter GetAwaiter(this IEnumerator enumerator)
{
Error.ThrowArgumentNullException(enumerator, nameof(enumerator));
return new UniTask(EnumeratorPromise.Create(enumerator, PlayerLoopTiming.Update, CancellationToken.None, out var token), token).GetAwaiter();
}
public static UniTask ToUniTask(this IEnumerator enumerator)
public static UniTask WithCancellation(this IEnumerator enumerator, CancellationToken cancellationToken)
{
return new UniTask(EnumeratorPromise.Create(enumerator, PlayerLoopTiming.Update, CancellationToken.None, out var token), token);
Error.ThrowArgumentNullException(enumerator, nameof(enumerator));
return new UniTask(EnumeratorPromise.Create(enumerator, PlayerLoopTiming.Update, cancellationToken, out var token), token);
}
public static UniTask ConfigureAwait(this IEnumerator enumerator, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
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);
}
class EnumeratorPromise : IUniTaskSource, IPlayerLoopItem, IPromisePoolItem
sealed class EnumeratorPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<EnumeratorPromise>
{
static readonly PromisePool<EnumeratorPromise> pool = new PromisePool<EnumeratorPromise>();
static TaskPool<EnumeratorPromise> pool;
public EnumeratorPromise NextNode { get; set; }
static EnumeratorPromise()
{
TaskPoolMonitor.RegisterSizeGetter(typeof(EnumeratorPromise), () => pool.Size);
}
IEnumerator innerEnumerator;
CancellationToken cancellationToken;
@@ -47,13 +56,15 @@ namespace Cysharp.Threading.Tasks
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
}
var result = pool.TryRent() ?? new EnumeratorPromise();
if (!pool.TryPop(out var result))
{
result = new EnumeratorPromise();
}
TaskTracker.TrackActiveTask(result, 3);
result.innerEnumerator = ConsumeEnumerator(innerEnumerator);
result.cancellationToken = cancellationToken;
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(timing, result);
token = result.core.Version;
@@ -64,12 +75,11 @@ namespace Cysharp.Threading.Tasks
{
try
{
TaskTracker.RemoveTracking(this);
core.GetResult(token);
}
finally
{
pool.TryReturn(this);
TryReturn();
}
}
@@ -113,16 +123,18 @@ namespace Cysharp.Threading.Tasks
return false;
}
public void Reset()
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
innerEnumerator = default;
cancellationToken = default;
return pool.TryPush(this);
}
~EnumeratorPromise()
{
if (pool.TryReturn(this))
if (TryReturn())
{
GC.ReRegisterForFinalize(this);
}

View File

@@ -13,21 +13,23 @@ namespace Cysharp.Threading.Tasks
{
public static class AddressableAsyncExtensions
{
#region AsyncOperationHandle
#region AsyncOperationHandle
public static AsyncOperationHandleAwaiter GetAwaiter(this AsyncOperationHandle handle)
{
return new AsyncOperationHandleAwaiter(handle);
}
public static UniTask ToUniTask(this AsyncOperationHandle handle)
public static UniTask WithCancellation(this AsyncOperationHandle handle, CancellationToken cancellationToken)
{
return new UniTask(AsyncOperationHandleConfiguredSource.Create(handle, PlayerLoopTiming.Update, null, CancellationToken.None, out var token), token);
if (handle.IsDone) return UniTask.CompletedTask;
return new UniTask(AsyncOperationHandleConfiguredSource.Create(handle, PlayerLoopTiming.Update, null, cancellationToken, out var token), token);
}
public static UniTask ConfigureAwait(this AsyncOperationHandle handle, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellation = default(CancellationToken))
public static UniTask ToUniTask(this AsyncOperationHandle handle, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
{
return new UniTask(AsyncOperationHandleConfiguredSource.Create(handle, timing, progress, cancellation, out var token), token);
if (handle.IsDone) return UniTask.CompletedTask;
return new UniTask(AsyncOperationHandleConfiguredSource.Create(handle, timing, progress, cancellationToken, out var token), token);
}
public struct AsyncOperationHandleAwaiter : ICriticalNotifyCompletion
@@ -70,14 +72,20 @@ namespace Cysharp.Threading.Tasks
public void UnsafeOnCompleted(Action continuation)
{
Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction);
continuationAction = continuation.AsFuncOfT<AsyncOperationHandle>(); // allocate delegate.
continuationAction = PooledDelegate<AsyncOperationHandle>.Create(continuation);
handle.Completed += continuationAction;
}
}
class AsyncOperationHandleConfiguredSource : IUniTaskSource, IPlayerLoopItem, IPromisePoolItem
sealed class AsyncOperationHandleConfiguredSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleConfiguredSource>
{
static readonly PromisePool<AsyncOperationHandleConfiguredSource> pool = new PromisePool<AsyncOperationHandleConfiguredSource>();
static TaskPool<AsyncOperationHandleConfiguredSource> pool;
public AsyncOperationHandleConfiguredSource NextNode { get; set; }
static AsyncOperationHandleConfiguredSource()
{
TaskPoolMonitor.RegisterSizeGetter(typeof(AsyncOperationHandleConfiguredSource), () => pool.Size);
}
AsyncOperationHandle handle;
IProgress<float> progress;
@@ -97,7 +105,10 @@ namespace Cysharp.Threading.Tasks
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
}
var result = pool.TryRent() ?? new AsyncOperationHandleConfiguredSource();
if (!pool.TryPop(out var result))
{
result = new AsyncOperationHandleConfiguredSource();
}
result.handle = handle;
result.progress = progress;
@@ -115,12 +126,12 @@ namespace Cysharp.Threading.Tasks
{
try
{
TaskTracker.RemoveTracking(this);
core.GetResult(token);
}
finally
{
pool.TryReturn(this);
TryReturn();
}
}
@@ -168,40 +179,44 @@ namespace Cysharp.Threading.Tasks
return true;
}
public void Reset()
bool TryReturn()
{
core.Reset();
TaskTracker.RemoveTracking(this);
handle = default;
progress = default;
cancellationToken = default;
return pool.TryPush(this);
}
~AsyncOperationHandleConfiguredSource()
{
if (pool.TryReturn(this))
if (TryReturn())
{
GC.ReRegisterForFinalize(this);
}
}
}
#endregion
#endregion
#region AsyncOperationHandle_T
#region AsyncOperationHandle_T
public static AsyncOperationHandleAwaiter<T> GetAwaiter<T>(this AsyncOperationHandle<T> handle)
{
return new AsyncOperationHandleAwaiter<T>(handle);
}
public static UniTask<T> ToUniTask<T>(this AsyncOperationHandle<T> handle)
public static UniTask<T> WithCancellation<T>(this AsyncOperationHandle<T> handle, CancellationToken cancellationToken)
{
return new UniTask<T>(AsyncOperationHandleConfiguredSource<T>.Create(handle, PlayerLoopTiming.Update, null, CancellationToken.None, out var token), token);
if (handle.IsDone) return UniTask.FromResult(handle.Result);
return new UniTask<T>(AsyncOperationHandleConfiguredSource<T>.Create(handle, PlayerLoopTiming.Update, null, cancellationToken, out var token), token);
}
public static UniTask<T> ConfigureAwait<T>(this AsyncOperationHandle<T> handle, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellation = default(CancellationToken))
public static UniTask<T> ToUniTask<T>(this AsyncOperationHandle<T> handle, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
{
return new UniTask<T>(AsyncOperationHandleConfiguredSource<T>.Create(handle, timing, progress, cancellation, out var token), token);
if (handle.IsDone) return UniTask.FromResult(handle.Result);
return new UniTask<T>(AsyncOperationHandleConfiguredSource<T>.Create(handle, timing, progress, cancellationToken, out var token), token);
}
public struct AsyncOperationHandleAwaiter<T> : ICriticalNotifyCompletion
@@ -245,14 +260,20 @@ namespace Cysharp.Threading.Tasks
public void UnsafeOnCompleted(Action continuation)
{
Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction);
continuationAction = continuation.AsFuncOfT<AsyncOperationHandle>(); // allocate delegate.
continuationAction = PooledDelegate<AsyncOperationHandle>.Create(continuation);
handle.CompletedTypeless += continuationAction;
}
}
class AsyncOperationHandleConfiguredSource<T> : IUniTaskSource<T>, IPlayerLoopItem, IPromisePoolItem
sealed class AsyncOperationHandleConfiguredSource<T> : IUniTaskSource<T>, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleConfiguredSource<T>>
{
static readonly PromisePool<AsyncOperationHandleConfiguredSource<T>> pool = new PromisePool<AsyncOperationHandleConfiguredSource<T>>();
static TaskPool<AsyncOperationHandleConfiguredSource<T>> pool;
public AsyncOperationHandleConfiguredSource<T> NextNode { get; set; }
static AsyncOperationHandleConfiguredSource()
{
TaskPoolMonitor.RegisterSizeGetter(typeof(AsyncOperationHandleConfiguredSource<T>), () => pool.Size);
}
AsyncOperationHandle<T> handle;
IProgress<float> progress;
@@ -272,7 +293,10 @@ namespace Cysharp.Threading.Tasks
return AutoResetUniTaskCompletionSource<T>.CreateFromCanceled(cancellationToken, out token);
}
var result = pool.TryRent() ?? new AsyncOperationHandleConfiguredSource<T>();
if (!pool.TryPop(out var result))
{
result = new AsyncOperationHandleConfiguredSource<T>();
}
result.handle = handle;
result.progress = progress;
@@ -290,13 +314,11 @@ namespace Cysharp.Threading.Tasks
{
try
{
TaskTracker.RemoveTracking(this);
return core.GetResult(token);
}
finally
{
pool.TryReturn(this);
TryReturn();
}
}
@@ -349,24 +371,26 @@ namespace Cysharp.Threading.Tasks
return true;
}
public void Reset()
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
handle = default;
progress = default;
cancellationToken = default;
return pool.TryPush(this);
}
~AsyncOperationHandleConfiguredSource()
{
if (pool.TryReturn(this))
if (TryReturn())
{
GC.ReRegisterForFinalize(this);
}
}
}
#endregion
#endregion
}
}

View File

@@ -0,0 +1,290 @@
// asmdef Version Defines, enabled when com.demigiant.dotween is imported.
#if UNITASK_DOTWEEN_SUPPORT
using Cysharp.Threading.Tasks.Internal;
using DG.Tweening;
using System;
using System.Collections.Concurrent;
using System.Runtime.CompilerServices;
using System.Threading;
namespace Cysharp.Threading.Tasks
{
// The idea of TweenCancelBehaviour is borrowed from https://www.shibuya24.info/entry/dotween_async_await
public enum TweenCancelBehaviour
{
Kill,
KillWithCompleteCallback,
Complete,
CompleteWithSeqeunceCallback,
CancelAwait,
// AndCancelAwait
KillAndCancelAwait,
KillWithCompleteCallbackAndCancelAwait,
CompleteAndCancelAwait,
CompleteWithSeqeunceCallbackAndCancelAwait
}
public static class DOTweenAsyncExtensions
{
public static TweenAwaiter GetAwaiter(this Tween tween)
{
return new TweenAwaiter(tween);
}
public static UniTask WithCancellation(this Tween tween, CancellationToken cancellationToken)
{
Error.ThrowArgumentNullException(tween, nameof(tween));
if (!tween.IsActive()) return UniTask.CompletedTask;
return new UniTask(TweenConfiguredSource.Create(tween, TweenCancelBehaviour.Kill, cancellationToken, out var token), token);
}
public static UniTask ToUniTask(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default)
{
Error.ThrowArgumentNullException(tween, nameof(tween));
if (!tween.IsActive()) return UniTask.CompletedTask;
return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, out var token), token);
}
public struct TweenAwaiter : ICriticalNotifyCompletion
{
readonly Tween tween;
// killed(non active) as completed.
public bool IsCompleted => !tween.IsActive();
public TweenAwaiter(Tween tween)
{
this.tween = tween;
}
public TweenAwaiter GetAwaiter()
{
return this;
}
public void GetResult()
{
}
public void OnCompleted(System.Action continuation)
{
UnsafeOnCompleted(continuation);
}
public void UnsafeOnCompleted(System.Action continuation)
{
// onKill is called after OnCompleted, both Complete(false/true) and Kill(false/true).
tween.onKill = PooledTweenCallback.Create(continuation);
}
}
sealed class TweenConfiguredSource : IUniTaskSource, ITaskPoolNode<TweenConfiguredSource>
{
static TaskPool<TweenConfiguredSource> pool;
public TweenConfiguredSource NextNode { get; set; }
static TweenConfiguredSource()
{
TaskPoolMonitor.RegisterSizeGetter(typeof(TweenConfiguredSource), () => pool.Size);
}
static readonly Action<object> CancellationCallbackDelegate = CancellationCallback;
static readonly TweenCallback EmptyTweenCallback = () => { };
readonly TweenCallback onKillDelegate;
Tween tween;
TweenCancelBehaviour cancelBehaviour;
CancellationToken cancellationToken;
bool canceled;
CancellationTokenRegistration cancellationTokenRegistration;
UniTaskCompletionSourceCore<AsyncUnit> core;
TweenConfiguredSource()
{
onKillDelegate = OnKill;
}
public static IUniTaskSource Create(Tween tween, TweenCancelBehaviour cancelBehaviour, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new TweenConfiguredSource();
}
result.tween = tween;
result.cancelBehaviour = cancelBehaviour;
result.cancellationToken = cancellationToken;
TaskTracker.TrackActiveTask(result, 3);
result.RegisterEvent();
token = result.core.Version;
return result;
}
void RegisterEvent()
{
if (cancellationToken.CanBeCanceled)
{
cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(CancellationCallbackDelegate, this);
}
tween.OnKill(onKillDelegate);
}
void OnKill()
{
cancellationTokenRegistration.Dispose();
if (canceled)
{
core.TrySetCanceled(cancellationToken);
}
else
{
core.TrySetResult(AsyncUnit.Default);
}
}
static void CancellationCallback(object state)
{
var self = (TweenConfiguredSource)state;
switch (self.cancelBehaviour)
{
case TweenCancelBehaviour.Kill:
default:
self.tween.Kill(false);
break;
case TweenCancelBehaviour.KillAndCancelAwait:
self.canceled = true;
self.tween.Kill(false);
break;
case TweenCancelBehaviour.KillWithCompleteCallback:
self.tween.Kill(true);
break;
case TweenCancelBehaviour.KillWithCompleteCallbackAndCancelAwait:
self.canceled = true;
self.tween.Kill(true);
break;
case TweenCancelBehaviour.Complete:
self.tween.Complete(false);
break;
case TweenCancelBehaviour.CompleteAndCancelAwait:
self.canceled = true;
self.tween.Complete(false);
break;
case TweenCancelBehaviour.CompleteWithSeqeunceCallback:
self.tween.Complete(true);
break;
case TweenCancelBehaviour.CompleteWithSeqeunceCallbackAndCancelAwait:
self.canceled = true;
self.tween.Complete(true);
break;
case TweenCancelBehaviour.CancelAwait:
self.tween.onKill = EmptyTweenCallback; // replace to empty(avoid callback after Caceled(instance is returned to pool.)
self.core.TrySetCanceled(self.cancellationToken);
break;
}
}
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);
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
tween = default;
cancellationToken = default;
return pool.TryPush(this);
}
~TweenConfiguredSource()
{
if (TryReturn())
{
GC.ReRegisterForFinalize(this);
}
}
}
}
sealed class PooledTweenCallback
{
static readonly ConcurrentQueue<PooledTweenCallback> pool = new ConcurrentQueue<PooledTweenCallback>();
readonly TweenCallback runDelegate;
Action continuation;
PooledTweenCallback()
{
runDelegate = Run;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TweenCallback Create(Action continuation)
{
if (!pool.TryDequeue(out var item))
{
item = new PooledTweenCallback();
}
item.continuation = continuation;
return item.runDelegate;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
void Run()
{
var call = continuation;
continuation = null;
if (call != null)
{
pool.Enqueue(this);
call.Invoke();
}
}
}
}
#endif

View File

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

View File

@@ -19,17 +19,75 @@ namespace Cysharp.Threading.Tasks
// similar as IValueTaskSource
public interface IUniTaskSource
#if !UNITY_2018_3_OR_NEWER
: 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
#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
, System.Threading.Tasks.Sources.IValueTaskSource<T>
#endif
{
new T GetResult(short token);
#if !UNITY_2018_3_OR_NEWER
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

View File

@@ -0,0 +1,49 @@
using System;
using System.Runtime.CompilerServices;
namespace Cysharp.Threading.Tasks.Internal
{
internal sealed class PooledDelegate<T> : ITaskPoolNode<PooledDelegate<T>>
{
static TaskPool<PooledDelegate<T>> pool;
public PooledDelegate<T> NextNode { get; set; }
static PooledDelegate()
{
TaskPoolMonitor.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: 8932579438742fa40b010edd412dbfba
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,55 +0,0 @@
using System.Collections.Concurrent;
using System.Runtime.CompilerServices;
using System.Threading;
namespace Cysharp.Threading.Tasks.Internal
{
// public, allow to user create custom operator with pool.
public interface IPromisePoolItem
{
void Reset();
}
public class PromisePool<T>
where T : class, IPromisePoolItem
{
int count = 0;
readonly ConcurrentQueue<T> queue = new ConcurrentQueue<T>();
readonly int maxSize;
public PromisePool(int maxSize = 256)
{
this.maxSize = maxSize;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T TryRent()
{
if (queue.TryDequeue(out var value))
{
Interlocked.Decrement(ref count);
return value;
}
return null;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryReturn(T value)
{
value.Reset(); // reset when return.
if (count < maxSize)
{
queue.Enqueue(value);
Interlocked.Increment(ref count);
return true;
}
else
{
return false;
}
}
}
}

View File

@@ -0,0 +1,119 @@
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.Internal
{
// internaly used but public, allow to user create custom operator with pooling.
public static class TaskPool
{
internal static int MaxPoolSize;
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 interface ITaskPoolNode<T>
{
T NextNode { get; set; }
}
// 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))
{
root = v.NextNode;
v.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;
}
}
public static class TaskPoolMonitor
{
static ConcurrentDictionary<Type, Func<int>> sizes = new ConcurrentDictionary<Type, Func<int>>();
public static IEnumerable<(Type, int)> GetCacheSizeInfo()
{
foreach (var item in sizes)
{
yield return (item.Key, item.Value());
}
}
public static void RegisterSizeGetter(Type type, Func<int> getSize)
{
sizes[type] = getSize;
}
}
}

View File

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

View File

@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Threading;
using Cysharp.Threading.Tasks.Internal;
@@ -103,16 +104,11 @@ namespace Cysharp.Threading.Tasks
{
for (int i = 0; i < count; i++)
{
string typeName = null;
var keyType = listPool[i].Key.GetType();
if (keyType.IsNested)
{
typeName = keyType.DeclaringType.Name + "." + keyType.Name;
}
else
{
typeName = keyType.Name;
}
var sb = new StringBuilder();
TypeBeautify(keyType, sb);
var typeName = sb.ToString();
action(listPool[i].Value.trackingId, typeName, listPool[i].Key.UnsafeGetStatus(), listPool[i].Value.addTime, listPool[i].Value.stackTrace);
listPool[i] = new KeyValuePair<IUniTaskSource, (int trackingId, DateTime addTime, string stackTrace)>(null, (0, default(DateTime), null)); // clear
@@ -125,6 +121,31 @@ namespace Cysharp.Threading.Tasks
}
}
}
static void TypeBeautify(Type type, StringBuilder sb)
{
if (type.IsNested)
{
TypeBeautify(type.DeclaringType, sb);
sb.Append(".");
}
if (type.IsGenericType)
{
var genericsStart = type.Name.IndexOf("`");
sb.Append(type.Name.Substring(0, genericsStart));
sb.Append("<");
foreach (var item in type.GetGenericArguments())
{
TypeBeautify(item, sb);
}
sb.Append(">");
}
else
{
sb.Append(type.Name);
}
}
}
}

View File

@@ -159,6 +159,7 @@ namespace Cysharp.Threading.Tasks.Linq
}
catch (Exception ex)
{
self.running<#= j #> = true; // as complete, no more call MoveNextAsync.
self.completedCount = CompleteCount;
self.completionSource.TrySetException(ex);
return;
@@ -168,6 +169,7 @@ namespace Cysharp.Threading.Tasks.Linq
if (!self.TrySetResult())
{
if (self.syncRunning) return;
self.running<#= j #> = true; // as complete, no more call MoveNextAsync.
try
{
self.awaiter<#= j #> = self.enumerator<#= j #>.MoveNextAsync().GetAwaiter();

View File

@@ -200,27 +200,43 @@ namespace Cysharp.Threading.Tasks
#if UNITY_EDITOR
[InitializeOnLoadMethod]
static void InitOnEditor()
{
//Execute the play mode init method
// Execute the play mode init method
Init();
//register an Editor update delegate, used to forcing playerLoop update
// register an Editor update delegate, used to forcing playerLoop update
EditorApplication.update += ForceEditorPlayerLoopUpdate;
}
private static void ForceEditorPlayerLoopUpdate()
{
if (EditorApplication.isPlayingOrWillChangePlaymode || EditorApplication.isCompiling ||
EditorApplication.isUpdating)
if (EditorApplication.isPlayingOrWillChangePlaymode || EditorApplication.isCompiling || EditorApplication.isUpdating)
{
// Not in Edit mode, don't interfere
return;
}
//force unity to update PlayerLoop callbacks
EditorApplication.QueuePlayerLoopUpdate();
// 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();
}
}
}
#endif

View File

@@ -56,9 +56,15 @@ namespace Cysharp.Threading.Tasks
: new UniTask(DelayPromise.Create(delayTimeSpan, delayTiming, cancellationToken, out token), token);
}
sealed class YieldPromise : IUniTaskSource, IPlayerLoopItem, IPromisePoolItem
sealed class YieldPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<YieldPromise>
{
static readonly PromisePool<YieldPromise> pool = new PromisePool<YieldPromise>();
static TaskPool<YieldPromise> pool;
public YieldPromise NextNode { get; set; }
static YieldPromise()
{
TaskPoolMonitor.RegisterSizeGetter(typeof(YieldPromise), () => pool.Size);
}
CancellationToken cancellationToken;
UniTaskCompletionSourceCore<object> core;
@@ -74,7 +80,11 @@ namespace Cysharp.Threading.Tasks
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
}
var result = pool.TryRent() ?? new YieldPromise();
if (!pool.TryPop(out var result))
{
result = new YieldPromise();
}
result.cancellationToken = cancellationToken;
@@ -90,12 +100,11 @@ namespace Cysharp.Threading.Tasks
{
try
{
TaskTracker.RemoveTracking(this);
core.GetResult(token);
}
finally
{
pool.TryReturn(this);
TryReturn();
}
}
@@ -126,24 +135,32 @@ namespace Cysharp.Threading.Tasks
return false;
}
public void Reset()
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
cancellationToken = default;
return pool.TryPush(this);
}
~YieldPromise()
{
if (pool.TryReturn(this))
if (TryReturn())
{
GC.ReRegisterForFinalize(this);
}
}
}
sealed class DelayFramePromise : IUniTaskSource, IPlayerLoopItem, IPromisePoolItem
sealed class DelayFramePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<DelayFramePromise>
{
static readonly PromisePool<DelayFramePromise> pool = new PromisePool<DelayFramePromise>();
static TaskPool<DelayFramePromise> pool;
public DelayFramePromise NextNode { get; set; }
static DelayFramePromise()
{
TaskPoolMonitor.RegisterSizeGetter(typeof(DelayFramePromise), () => pool.Size);
}
int delayFrameCount;
CancellationToken cancellationToken;
@@ -162,7 +179,10 @@ namespace Cysharp.Threading.Tasks
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
}
var result = pool.TryRent() ?? new DelayFramePromise();
if (!pool.TryPop(out var result))
{
result = new DelayFramePromise();
}
result.delayFrameCount = delayFrameCount;
result.cancellationToken = cancellationToken;
@@ -179,12 +199,11 @@ namespace Cysharp.Threading.Tasks
{
try
{
TaskTracker.RemoveTracking(this);
core.GetResult(token);
}
finally
{
pool.TryReturn(this);
TryReturn();
}
}
@@ -221,26 +240,34 @@ namespace Cysharp.Threading.Tasks
return true;
}
public void Reset()
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
currentFrameCount = default;
delayFrameCount = default;
cancellationToken = default;
return pool.TryPush(this);
}
~DelayFramePromise()
{
if (pool.TryReturn(this))
if (TryReturn())
{
GC.ReRegisterForFinalize(this);
}
}
}
sealed class DelayPromise : IUniTaskSource, IPlayerLoopItem, IPromisePoolItem
sealed class DelayPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<DelayPromise>
{
static readonly PromisePool<DelayPromise> pool = new PromisePool<DelayPromise>();
static TaskPool<DelayPromise> pool;
public DelayPromise NextNode { get; set; }
static DelayPromise()
{
TaskPoolMonitor.RegisterSizeGetter(typeof(DelayPromise), () => pool.Size);
}
float delayFrameTimeSpan;
float elapsed;
@@ -259,7 +286,10 @@ namespace Cysharp.Threading.Tasks
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
}
var result = pool.TryRent() ?? new DelayPromise();
if (!pool.TryPop(out var result))
{
result = new DelayPromise();
}
result.elapsed = 0.0f;
result.delayFrameTimeSpan = (float)delayFrameTimeSpan.TotalSeconds;
@@ -277,12 +307,11 @@ namespace Cysharp.Threading.Tasks
{
try
{
TaskTracker.RemoveTracking(this);
core.GetResult(token);
}
finally
{
pool.TryReturn(this);
TryReturn();
}
}
@@ -319,26 +348,34 @@ namespace Cysharp.Threading.Tasks
return true;
}
public void Reset()
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
delayFrameTimeSpan = default;
elapsed = default;
cancellationToken = default;
return pool.TryPush(this);
}
~DelayPromise()
{
if (pool.TryReturn(this))
if (TryReturn())
{
GC.ReRegisterForFinalize(this);
}
}
}
sealed class DelayIgnoreTimeScalePromise : IUniTaskSource, IPlayerLoopItem, IPromisePoolItem
sealed class DelayIgnoreTimeScalePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<DelayIgnoreTimeScalePromise>
{
static readonly PromisePool<DelayIgnoreTimeScalePromise> pool = new PromisePool<DelayIgnoreTimeScalePromise>();
static TaskPool<DelayIgnoreTimeScalePromise> pool;
public DelayIgnoreTimeScalePromise NextNode { get; set; }
static DelayIgnoreTimeScalePromise()
{
TaskPoolMonitor.RegisterSizeGetter(typeof(DelayIgnoreTimeScalePromise), () => pool.Size);
}
float delayFrameTimeSpan;
float elapsed;
@@ -357,7 +394,10 @@ namespace Cysharp.Threading.Tasks
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
}
var result = pool.TryRent() ?? new DelayIgnoreTimeScalePromise();
if (!pool.TryPop(out var result))
{
result = new DelayIgnoreTimeScalePromise();
}
result.elapsed = 0.0f;
result.delayFrameTimeSpan = (float)delayFrameTimeSpan.TotalSeconds;
@@ -375,12 +415,11 @@ namespace Cysharp.Threading.Tasks
{
try
{
TaskTracker.RemoveTracking(this);
core.GetResult(token);
}
finally
{
pool.TryReturn(this);
TryReturn();
}
}
@@ -417,17 +456,19 @@ namespace Cysharp.Threading.Tasks
return true;
}
public void Reset()
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
delayFrameTimeSpan = default;
elapsed = default;
cancellationToken = default;
return pool.TryPush(this);
}
~DelayIgnoreTimeScalePromise()
{
if (pool.TryReturn(this))
if (TryReturn())
{
GC.ReRegisterForFinalize(this);
}

View File

@@ -105,34 +105,67 @@ namespace Cysharp.Threading.Tasks
/// helper of create add UniTaskVoid to delegate.
/// For example: FooEvent += () => UniTask.Void(async () => { /* */ })
/// </summary>
public static void Void(Func<UniTask> asyncAction)
public static void Void(Func<UniTaskVoid> asyncAction)
{
asyncAction().Forget();
}
public static Action VoidAction(Func<UniTask> asyncAction)
/// <summary>
/// helper of create add UniTaskVoid to delegate.
/// </summary>
public static void Void(Func<CancellationToken, UniTaskVoid> asyncAction, CancellationToken cancellationToken)
{
return () => Void(asyncAction);
asyncAction(cancellationToken).Forget();
}
#if UNITY_2018_3_OR_NEWER
public static UnityEngine.Events.UnityAction VoidUnityAction(Func<UniTask> asyncAction)
{
return () => Void(asyncAction);
}
#endif
/// <summary>
/// helper of create add UniTaskVoid to delegate.
/// For example: FooEvent += (sender, e) => UniTask.Void(async arg => { /* */ }, (sender, e))
/// </summary>
public static void Void<T>(Func<T, UniTask> asyncAction, T state)
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>

View File

@@ -35,9 +35,15 @@ namespace Cysharp.Threading.Tasks
: WaitUntilValueChangedStandardObjectPromise<T, U>.Create(target, monitorFunction, equalityComparer, monitorTiming, cancellationToken, out token), token);
}
sealed class WaitUntilPromise : IUniTaskSource, IPlayerLoopItem, IPromisePoolItem
sealed class WaitUntilPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<WaitUntilPromise>
{
static readonly PromisePool<WaitUntilPromise> pool = new PromisePool<WaitUntilPromise>();
static TaskPool<WaitUntilPromise> pool;
public WaitUntilPromise NextNode { get; set; }
static WaitUntilPromise()
{
TaskPoolMonitor.RegisterSizeGetter(typeof(WaitUntilPromise), () => pool.Size);
}
Func<bool> predicate;
CancellationToken cancellationToken;
@@ -55,7 +61,10 @@ namespace Cysharp.Threading.Tasks
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
}
var result = pool.TryRent() ?? new WaitUntilPromise();
if (!pool.TryPop(out var result))
{
result = new WaitUntilPromise();
}
result.predicate = predicate;
result.cancellationToken = cancellationToken;
@@ -72,12 +81,11 @@ namespace Cysharp.Threading.Tasks
{
try
{
TaskTracker.RemoveTracking(this);
core.GetResult(token);
}
finally
{
pool.TryReturn(this);
TryReturn();
}
}
@@ -121,25 +129,33 @@ namespace Cysharp.Threading.Tasks
return false;
}
public void Reset()
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
predicate = default;
cancellationToken = default;
return pool.TryPush(this);
}
~WaitUntilPromise()
{
if (pool.TryReturn(this))
if (TryReturn())
{
GC.ReRegisterForFinalize(this);
}
}
}
sealed class WaitWhilePromise : IUniTaskSource, IPlayerLoopItem, IPromisePoolItem
sealed class WaitWhilePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<WaitWhilePromise>
{
static readonly PromisePool<WaitWhilePromise> pool = new PromisePool<WaitWhilePromise>();
static TaskPool<WaitWhilePromise> pool;
public WaitWhilePromise NextNode { get; set; }
static WaitWhilePromise()
{
TaskPoolMonitor.RegisterSizeGetter(typeof(WaitWhilePromise), () => pool.Size);
}
Func<bool> predicate;
CancellationToken cancellationToken;
@@ -157,7 +173,10 @@ namespace Cysharp.Threading.Tasks
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
}
var result = pool.TryRent() ?? new WaitWhilePromise();
if (!pool.TryPop(out var result))
{
result = new WaitWhilePromise();
}
result.predicate = predicate;
result.cancellationToken = cancellationToken;
@@ -174,12 +193,11 @@ namespace Cysharp.Threading.Tasks
{
try
{
TaskTracker.RemoveTracking(this);
core.GetResult(token);
}
finally
{
pool.TryReturn(this);
TryReturn();
}
}
@@ -223,25 +241,33 @@ namespace Cysharp.Threading.Tasks
return false;
}
public void Reset()
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
predicate = default;
cancellationToken = default;
return pool.TryPush(this);
}
~WaitWhilePromise()
{
if (pool.TryReturn(this))
if (TryReturn())
{
GC.ReRegisterForFinalize(this);
}
}
}
sealed class WaitUntilCanceledPromise : IUniTaskSource, IPlayerLoopItem, IPromisePoolItem
sealed class WaitUntilCanceledPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<WaitUntilCanceledPromise>
{
static readonly PromisePool<WaitUntilCanceledPromise> pool = new PromisePool<WaitUntilCanceledPromise>();
static TaskPool<WaitUntilCanceledPromise> pool;
public WaitUntilCanceledPromise NextNode { get; set; }
static WaitUntilCanceledPromise()
{
TaskPoolMonitor.RegisterSizeGetter(typeof(WaitUntilCanceledPromise), () => pool.Size);
}
CancellationToken cancellationToken;
@@ -258,7 +284,10 @@ namespace Cysharp.Threading.Tasks
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
}
var result = pool.TryRent() ?? new WaitUntilCanceledPromise();
if (!pool.TryPop(out var result))
{
result = new WaitUntilCanceledPromise();
}
result.cancellationToken = cancellationToken;
@@ -274,12 +303,11 @@ namespace Cysharp.Threading.Tasks
{
try
{
TaskTracker.RemoveTracking(this);
core.GetResult(token);
}
finally
{
pool.TryReturn(this);
TryReturn();
}
}
@@ -309,15 +337,17 @@ namespace Cysharp.Threading.Tasks
return true;
}
public void Reset()
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
cancellationToken = default;
return pool.TryPush(this);
}
~WaitUntilCanceledPromise()
{
if (pool.TryReturn(this))
if (TryReturn())
{
GC.ReRegisterForFinalize(this);
}
@@ -325,9 +355,15 @@ namespace Cysharp.Threading.Tasks
}
// where T : UnityEngine.Object, can not add constraint
sealed class WaitUntilValueChangedUnityObjectPromise<T, U> : IUniTaskSource<U>, IPlayerLoopItem, IPromisePoolItem
sealed class WaitUntilValueChangedUnityObjectPromise<T, U> : IUniTaskSource<U>, IPlayerLoopItem, ITaskPoolNode<WaitUntilValueChangedUnityObjectPromise<T, U>>
{
static readonly PromisePool<WaitUntilValueChangedUnityObjectPromise<T, U>> pool = new PromisePool<WaitUntilValueChangedUnityObjectPromise<T, U>>();
static TaskPool<WaitUntilValueChangedUnityObjectPromise<T, U>> pool;
public WaitUntilValueChangedUnityObjectPromise<T, U> NextNode { get; set; }
static WaitUntilValueChangedUnityObjectPromise()
{
TaskPoolMonitor.RegisterSizeGetter(typeof(WaitUntilValueChangedUnityObjectPromise<T, U>), () => pool.Size);
}
T target;
UnityEngine.Object targetAsUnityObject;
@@ -349,7 +385,10 @@ namespace Cysharp.Threading.Tasks
return AutoResetUniTaskCompletionSource<U>.CreateFromCanceled(cancellationToken, out token);
}
var result = pool.TryRent() ?? new WaitUntilValueChangedUnityObjectPromise<T, U>();
if (!pool.TryPop(out var result))
{
result = new WaitUntilValueChangedUnityObjectPromise<T, U>();
}
result.target = target;
result.targetAsUnityObject = target as UnityEngine.Object;
@@ -370,12 +409,11 @@ namespace Cysharp.Threading.Tasks
{
try
{
TaskTracker.RemoveTracking(this);
return core.GetResult(token);
}
finally
{
pool.TryReturn(this);
TryReturn();
}
}
@@ -426,29 +464,37 @@ namespace Cysharp.Threading.Tasks
return false;
}
public void Reset()
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
target = default;
currentValue = default;
monitorFunction = default;
equalityComparer = default;
cancellationToken = default;
return pool.TryPush(this);
}
~WaitUntilValueChangedUnityObjectPromise()
{
if (pool.TryReturn(this))
if (TryReturn())
{
GC.ReRegisterForFinalize(this);
}
}
}
sealed class WaitUntilValueChangedStandardObjectPromise<T, U> : IUniTaskSource<U>, IPlayerLoopItem, IPromisePoolItem
sealed class WaitUntilValueChangedStandardObjectPromise<T, U> : IUniTaskSource<U>, IPlayerLoopItem, ITaskPoolNode<WaitUntilValueChangedStandardObjectPromise<T, U>>
where T : class
{
static readonly PromisePool<WaitUntilValueChangedStandardObjectPromise<T, U>> pool = new PromisePool<WaitUntilValueChangedStandardObjectPromise<T, U>>();
static TaskPool<WaitUntilValueChangedStandardObjectPromise<T, U>> pool;
public WaitUntilValueChangedStandardObjectPromise<T, U> NextNode { get; set; }
static WaitUntilValueChangedStandardObjectPromise()
{
TaskPoolMonitor.RegisterSizeGetter(typeof(WaitUntilValueChangedStandardObjectPromise<T, U>), () => pool.Size);
}
WeakReference<T> target;
U currentValue;
@@ -469,7 +515,10 @@ namespace Cysharp.Threading.Tasks
return AutoResetUniTaskCompletionSource<U>.CreateFromCanceled(cancellationToken, out token);
}
var result = pool.TryRent() ?? new WaitUntilValueChangedStandardObjectPromise<T, U>();
if (!pool.TryPop(out var result))
{
result = new WaitUntilValueChangedStandardObjectPromise<T, U>();
}
result.target = new WeakReference<T>(target, false); // wrap in WeakReference.
result.monitorFunction = monitorFunction;
@@ -489,12 +538,11 @@ namespace Cysharp.Threading.Tasks
{
try
{
TaskTracker.RemoveTracking(this);
return core.GetResult(token);
}
finally
{
pool.TryReturn(this);
TryReturn();
}
}
@@ -545,19 +593,21 @@ namespace Cysharp.Threading.Tasks
return false;
}
public void Reset()
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
target = default;
currentValue = default;
monitorFunction = default;
equalityComparer = default;
cancellationToken = default;
return pool.TryPush(this);
}
~WaitUntilValueChangedStandardObjectPromise()
{
if (pool.TryReturn(this))
if (TryReturn())
{
GC.ReRegisterForFinalize(this);
}

View File

@@ -2,7 +2,8 @@
"name": "UniTask",
"references": [
"Unity.ResourceManager",
"Unity.TextMeshPro"
"Unity.TextMeshPro",
"DOTween.Modules"
],
"includePlatforms": [],
"excludePlatforms": [],
@@ -21,6 +22,11 @@
"name": "com.unity.textmeshpro",
"expression": "",
"define": "UNITASK_TEXTMESHPRO_SUPPORT"
},
{
"name": "com.demigiant.dotween",
"expression": "",
"define": "UNITASK_DOTWEEN_SUPPORT"
}
],
"noEngineReferences": false

View File

@@ -1,22 +1,22 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
#pragma warning disable CS0436
using Cysharp.Threading.Tasks.CompilerServices;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;
using Cysharp.Threading.Tasks.CompilerServices;
using Cysharp.Threading.Tasks.Internal;
using System.Runtime.InteropServices;
namespace Cysharp.Threading.Tasks
{
internal static class AwaiterActions
{
internal static readonly Action<object> InvokeActionDelegate = InvokeAction;
internal static readonly Action<object> InvokeContinuationDelegate = Continuation;
[DebuggerHidden]
static void InvokeAction(object state)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static void Continuation(object state)
{
((Action)state).Invoke();
}
@@ -26,6 +26,7 @@ namespace Cysharp.Threading.Tasks
/// Lightweight unity specified task-like object.
/// </summary>
[AsyncMethodBuilder(typeof(AsyncUniTaskMethodBuilder))]
[StructLayout(LayoutKind.Auto)]
public readonly partial struct UniTask
{
readonly IUniTaskSource source;
@@ -68,6 +69,20 @@ namespace Cysharp.Threading.Tasks
return new UniTask<bool>(new IsCanceledSource(source), token);
}
#if !UNITY_2018_3_OR_NEWER
public static implicit operator System.Threading.Tasks.ValueTask(in UniTask self)
{
if (self.source == null)
{
return default;
}
return new System.Threading.Tasks.ValueTask(self.source, self.token);
}
#endif
public override string ToString()
{
if (source == null) return "()";
@@ -298,7 +313,7 @@ namespace Cysharp.Threading.Tasks
}
else
{
task.source.OnCompleted(AwaiterActions.InvokeActionDelegate, continuation, task.token);
task.source.OnCompleted(AwaiterActions.InvokeContinuationDelegate, continuation, task.token);
}
}
@@ -312,7 +327,7 @@ namespace Cysharp.Threading.Tasks
}
else
{
task.source.OnCompleted(AwaiterActions.InvokeActionDelegate, continuation, task.token);
task.source.OnCompleted(AwaiterActions.InvokeContinuationDelegate, continuation, task.token);
}
}
@@ -339,6 +354,7 @@ namespace Cysharp.Threading.Tasks
/// Lightweight unity specified task-like object.
/// </summary>
[AsyncMethodBuilder(typeof(AsyncUniTaskMethodBuilder<>))]
[StructLayout(LayoutKind.Auto)]
public readonly struct UniTask<T>
{
readonly IUniTaskSource<T> source;
@@ -414,6 +430,20 @@ namespace Cysharp.Threading.Tasks
return self.AsUniTask();
}
#if !UNITY_2018_3_OR_NEWER
public static implicit operator System.Threading.Tasks.ValueTask<T>(in UniTask<T> self)
{
if (self.source == null)
{
return new System.Threading.Tasks.ValueTask<T>(self.result);
}
return new System.Threading.Tasks.ValueTask<T>(self.source, self.token);
}
#endif
/// <summary>
/// returns (bool IsCanceled, T Result) instead of throws OperationCanceledException.
/// </summary>
@@ -621,7 +651,7 @@ namespace Cysharp.Threading.Tasks
}
else
{
s.OnCompleted(AwaiterActions.InvokeActionDelegate, continuation, task.token);
s.OnCompleted(AwaiterActions.InvokeContinuationDelegate, continuation, task.token);
}
}
@@ -636,7 +666,7 @@ namespace Cysharp.Threading.Tasks
}
else
{
s.OnCompleted(AwaiterActions.InvokeActionDelegate, continuation, task.token);
s.OnCompleted(AwaiterActions.InvokeContinuationDelegate, continuation, task.token);
}
}

View File

@@ -123,7 +123,14 @@ namespace Cysharp.Threading.Tasks
{
// setup result
this.hasUnhandledError = true;
this.error = ExceptionDispatchInfo.Capture(error);
if (error is OperationCanceledException)
{
this.error = error;
}
else
{
this.error = ExceptionDispatchInfo.Capture(error);
}
if (continuation != null || Interlocked.CompareExchange(ref this.continuation, UniTaskCompletionSourceCoreShared.s_sentinel, null) != null)
{
@@ -264,7 +271,7 @@ namespace Cysharp.Threading.Tasks
{
if (token != version)
{
throw new InvalidOperationException("token version is not matched, can not await twice.");
throw new InvalidOperationException("Token version is not matched, can not await twice or get Status after await.");
}
}
}
@@ -368,9 +375,15 @@ namespace Cysharp.Threading.Tasks
}
}
public class AutoResetUniTaskCompletionSource : IUniTaskSource, IPromisePoolItem, IPromise
public class AutoResetUniTaskCompletionSource : IUniTaskSource, ITaskPoolNode<AutoResetUniTaskCompletionSource>, IPromise
{
static readonly PromisePool<AutoResetUniTaskCompletionSource> pool = new PromisePool<AutoResetUniTaskCompletionSource>();
static TaskPool<AutoResetUniTaskCompletionSource> pool;
public AutoResetUniTaskCompletionSource NextNode { get; set; }
static AutoResetUniTaskCompletionSource()
{
TaskPoolMonitor.RegisterSizeGetter(typeof(AutoResetUniTaskCompletionSource), () => pool.Size);
}
UniTaskCompletionSourceCore<AsyncUnit> core;
@@ -381,9 +394,12 @@ namespace Cysharp.Threading.Tasks
[DebuggerHidden]
public static AutoResetUniTaskCompletionSource Create()
{
var value = pool.TryRent() ?? new AutoResetUniTaskCompletionSource();
TaskTracker.TrackActiveTask(value, 2);
return value;
if (!pool.TryPop(out var result))
{
result = new AutoResetUniTaskCompletionSource();
}
TaskTracker.TrackActiveTask(result, 2);
return result;
}
[DebuggerHidden]
@@ -445,12 +461,11 @@ namespace Cysharp.Threading.Tasks
{
try
{
TaskTracker.RemoveTracking(this);
core.GetResult(token);
}
finally
{
pool.TryReturn(this);
TryReturn();
}
}
@@ -474,14 +489,16 @@ namespace Cysharp.Threading.Tasks
}
[DebuggerHidden]
void IPromisePoolItem.Reset()
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
return pool.TryPush(this);
}
~AutoResetUniTaskCompletionSource()
{
if (pool.TryReturn(this))
if (TryReturn())
{
GC.ReRegisterForFinalize(this);
return;
@@ -584,9 +601,15 @@ namespace Cysharp.Threading.Tasks
}
}
public class AutoResetUniTaskCompletionSource<T> : IUniTaskSource<T>, IPromisePoolItem, IPromise<T>
public class AutoResetUniTaskCompletionSource<T> : IUniTaskSource<T>, ITaskPoolNode<AutoResetUniTaskCompletionSource<T>>, IPromise<T>
{
static readonly PromisePool<AutoResetUniTaskCompletionSource<T>> pool = new PromisePool<AutoResetUniTaskCompletionSource<T>>();
static TaskPool<AutoResetUniTaskCompletionSource<T>> pool;
public AutoResetUniTaskCompletionSource<T> NextNode { get; set; }
static AutoResetUniTaskCompletionSource()
{
TaskPoolMonitor.RegisterSizeGetter(typeof(AutoResetUniTaskCompletionSource<T>), () => pool.Size);
}
UniTaskCompletionSourceCore<T> core;
@@ -597,7 +620,10 @@ namespace Cysharp.Threading.Tasks
[DebuggerHidden]
public static AutoResetUniTaskCompletionSource<T> Create()
{
var result = pool.TryRent() ?? new AutoResetUniTaskCompletionSource<T>();
if (!pool.TryPop(out var result))
{
result = new AutoResetUniTaskCompletionSource<T>();
}
TaskTracker.TrackActiveTask(result, 2);
return result;
}
@@ -661,12 +687,11 @@ namespace Cysharp.Threading.Tasks
{
try
{
TaskTracker.RemoveTracking(this);
return core.GetResult(token);
}
finally
{
pool.TryReturn(this);
TryReturn();
}
}
@@ -695,14 +720,17 @@ namespace Cysharp.Threading.Tasks
}
[DebuggerHidden]
void IPromisePoolItem.Reset()
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
return pool.TryPush(this);
}
~AutoResetUniTaskCompletionSource()
{
if (pool.TryReturn(this))
if (TryReturn())
{
GC.ReRegisterForFinalize(this);
}

View File

@@ -559,36 +559,6 @@ namespace Cysharp.Threading.Tasks
return await continuationFunction();
}
#if UNITY_2018_3_OR_NEWER
public static async UniTask ConfigureAwait(this Task task, PlayerLoopTiming timing)
{
await task.ConfigureAwait(false);
await UniTask.Yield(timing);
}
public static async UniTask<T> ConfigureAwait<T>(this Task<T> task, PlayerLoopTiming timing)
{
var v = await task.ConfigureAwait(false);
await UniTask.Yield(timing);
return v;
}
public static async UniTask ConfigureAwait(this UniTask task, PlayerLoopTiming timing)
{
await task;
await UniTask.Yield(timing);
}
public static async UniTask<T> ConfigureAwait<T>(this UniTask<T> task, PlayerLoopTiming timing)
{
var v = await task;
await UniTask.Yield(timing);
return v;
}
#endif
public static async UniTask<T> Unwrap<T>(this UniTask<UniTask<T>> task)
{
return await await task;
@@ -601,7 +571,7 @@ namespace Cysharp.Threading.Tasks
#if UNITY_2018_3_OR_NEWER
class ToCoroutineEnumerator : IEnumerator
sealed class ToCoroutineEnumerator : IEnumerator
{
bool completed;
UniTask task;
@@ -659,12 +629,12 @@ namespace Cysharp.Threading.Tasks
return !completed;
}
public void Reset()
void IEnumerator.Reset()
{
}
}
class ToCoroutineEnumerator<T> : IEnumerator
sealed class ToCoroutineEnumerator<T> : IEnumerator
{
bool completed;
Action<T> resultHandler = null;
@@ -730,11 +700,11 @@ namespace Cysharp.Threading.Tasks
return !completed;
}
public void Reset()
void IEnumerator.Reset()
{
}
}
#endif
}
}

View File

@@ -9,7 +9,7 @@ namespace Cysharp.Threading.Tasks
{
public static class UniTaskObservableExtensions
{
public static UniTask<T> ToUniTask<T>(this IObservable<T> source, CancellationToken cancellationToken = default(CancellationToken), bool useFirstValue = false)
public static UniTask<T> ToUniTask<T>(this IObservable<T> source, bool useFirstValue = false, CancellationToken cancellationToken = default)
{
var promise = new UniTaskCompletionSource<T>();
var disposable = new SingleAssignmentDisposable();

View File

@@ -9,41 +9,11 @@ using Cysharp.Threading.Tasks.CompilerServices;
namespace Cysharp.Threading.Tasks
{
[AsyncMethodBuilder(typeof(AsyncUniTaskVoidMethodBuilder))]
public struct UniTaskVoid
public readonly struct UniTaskVoid
{
public void Forget()
{
}
[DebuggerHidden]
public Awaiter GetAwaiter()
{
return new Awaiter();
}
public struct Awaiter : ICriticalNotifyCompletion
{
[DebuggerHidden]
public bool IsCompleted => true;
[DebuggerHidden]
public void GetResult()
{
#if UNITY_2018_3_OR_NEWER
UnityEngine.Debug.LogWarning("UniTaskVoid can't await, always fire-and-forget. use Forget instead of await.");
#endif
}
[DebuggerHidden]
public void OnCompleted(Action continuation)
{
}
[DebuggerHidden]
public void UnsafeOnCompleted(Action continuation)
{
}
}
}
}

View File

@@ -9,10 +9,11 @@ namespace Cysharp.Threading.Tasks
{
public static partial class UnityAsyncExtensions
{
public static async UniTask WaitAsync(this JobHandle jobHandle, PlayerLoopTiming waitTiming)
public static async UniTask WaitAsync(this JobHandle jobHandle, PlayerLoopTiming waitTiming, CancellationToken cancellationToken = default)
{
await UniTask.Yield(waitTiming);
jobHandle.Complete();
cancellationToken.ThrowIfCancellationRequested(); // call cancel after Complete.
}
public static UniTask.Awaiter GetAwaiter(this JobHandle jobHandle)
@@ -29,21 +30,9 @@ namespace Cysharp.Threading.Tasks
return new UniTask(handler, token).GetAwaiter();
}
public static UniTask ToUniTask(this JobHandle jobHandle)
{
var handler = JobHandlePromise.Create(jobHandle, out var token);
{
PlayerLoopHelper.AddAction(PlayerLoopTiming.EarlyUpdate, handler);
PlayerLoopHelper.AddAction(PlayerLoopTiming.PreUpdate, handler);
PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, handler);
PlayerLoopHelper.AddAction(PlayerLoopTiming.PreLateUpdate, handler);
PlayerLoopHelper.AddAction(PlayerLoopTiming.PostLateUpdate, handler);
}
// can not pass CancellationToken because can't handle JobHandle's Complete and NativeArray.Dispose.
return new UniTask(handler, token);
}
public static UniTask ConfigureAwait(this JobHandle jobHandle, PlayerLoopTiming waitTiming)
public static UniTask ToUniTask(this JobHandle jobHandle, PlayerLoopTiming waitTiming)
{
var handler = JobHandlePromise.Create(jobHandle, out var token);
{

View File

@@ -13,7 +13,7 @@ namespace Cysharp.Threading.Tasks
{
public static partial class UnityAsyncExtensions
{
#region AsyncOperation
#region AsyncOperation
public static AsyncOperationAwaiter GetAwaiter(this AsyncOperation asyncOperation)
{
@@ -21,18 +21,18 @@ namespace Cysharp.Threading.Tasks
return new AsyncOperationAwaiter(asyncOperation);
}
public static UniTask ToUniTask(this AsyncOperation asyncOperation)
public static UniTask WithCancellation(this AsyncOperation asyncOperation, CancellationToken cancellationToken)
{
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
return new UniTask(AsyncOperationConfiguredSource.Create(asyncOperation, PlayerLoopTiming.Update, null, CancellationToken.None, out var token), token);
if (asyncOperation.isDone) return UniTask.CompletedTask;
return new UniTask(AsyncOperationConfiguredSource.Create(asyncOperation, PlayerLoopTiming.Update, null, cancellationToken, out var token), token);
}
public static UniTask ConfigureAwait(this AsyncOperation asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellation = default(CancellationToken))
public static UniTask ToUniTask(this AsyncOperation asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
{
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
return new UniTask(AsyncOperationConfiguredSource.Create(asyncOperation, timing, progress, cancellation, out var token), token);
if (asyncOperation.isDone) return UniTask.CompletedTask;
return new UniTask(AsyncOperationConfiguredSource.Create(asyncOperation, timing, progress, cancellationToken, out var token), token);
}
public struct AsyncOperationAwaiter : ICriticalNotifyCompletion
@@ -70,14 +70,20 @@ namespace Cysharp.Threading.Tasks
public void UnsafeOnCompleted(Action continuation)
{
Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction);
continuationAction = continuation.AsFuncOfT<AsyncOperation>(); // allocate delegate.
continuationAction = PooledDelegate<AsyncOperation>.Create(continuation);
asyncOperation.completed += continuationAction;
}
}
class AsyncOperationConfiguredSource : IUniTaskSource, IPlayerLoopItem, IPromisePoolItem
class AsyncOperationConfiguredSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<AsyncOperationConfiguredSource>
{
static readonly PromisePool<AsyncOperationConfiguredSource> pool = new PromisePool<AsyncOperationConfiguredSource>();
static TaskPool<AsyncOperationConfiguredSource> pool;
public AsyncOperationConfiguredSource NextNode { get; set; }
static AsyncOperationConfiguredSource()
{
TaskPoolMonitor.RegisterSizeGetter(typeof(AsyncOperationConfiguredSource), () => pool.Size);
}
AsyncOperation asyncOperation;
IProgress<float> progress;
@@ -97,7 +103,10 @@ namespace Cysharp.Threading.Tasks
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
}
var result = pool.TryRent() ?? new AsyncOperationConfiguredSource();
if (!pool.TryPop(out var result))
{
result = new AsyncOperationConfiguredSource();
}
result.asyncOperation = asyncOperation;
result.progress = progress;
@@ -115,17 +124,15 @@ namespace Cysharp.Threading.Tasks
{
try
{
TaskTracker.RemoveTracking(this);
core.GetResult(token);
}
finally
{
pool.TryReturn(this);
TryReturn();
}
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
@@ -163,26 +170,28 @@ namespace Cysharp.Threading.Tasks
return true;
}
public void Reset()
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
asyncOperation = default;
progress = default;
cancellationToken = default;
return pool.TryPush(this);
}
~AsyncOperationConfiguredSource()
{
if (pool.TryReturn(this))
if (TryReturn())
{
GC.ReRegisterForFinalize(this);
}
}
}
# endregion
#endregion
#region ResourceRequest
#region ResourceRequest
public static ResourceRequestAwaiter GetAwaiter(this ResourceRequest asyncOperation)
{
@@ -190,18 +199,18 @@ namespace Cysharp.Threading.Tasks
return new ResourceRequestAwaiter(asyncOperation);
}
public static UniTask<UnityEngine.Object> ToUniTask(this ResourceRequest asyncOperation)
public static UniTask<UnityEngine.Object> WithCancellation(this ResourceRequest asyncOperation, CancellationToken cancellationToken)
{
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
return new UniTask<UnityEngine.Object>(ResourceRequestConfiguredSource.Create(asyncOperation, PlayerLoopTiming.Update, null, CancellationToken.None, out var token), token);
if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.asset);
return new UniTask<UnityEngine.Object>(ResourceRequestConfiguredSource.Create(asyncOperation, PlayerLoopTiming.Update, null, cancellationToken, out var token), token);
}
public static UniTask<UnityEngine.Object> ConfigureAwait(this ResourceRequest asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellation = default(CancellationToken))
public static UniTask<UnityEngine.Object> ToUniTask(this ResourceRequest asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
{
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
return new UniTask<UnityEngine.Object>(ResourceRequestConfiguredSource.Create(asyncOperation, timing, progress, cancellation, out var token), token);
if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.asset);
return new UniTask<UnityEngine.Object>(ResourceRequestConfiguredSource.Create(asyncOperation, timing, progress, cancellationToken, out var token), token);
}
public struct ResourceRequestAwaiter : ICriticalNotifyCompletion
@@ -243,14 +252,20 @@ namespace Cysharp.Threading.Tasks
public void UnsafeOnCompleted(Action continuation)
{
Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction);
continuationAction = continuation.AsFuncOfT<AsyncOperation>(); // allocate delegate.
continuationAction = PooledDelegate<AsyncOperation>.Create(continuation);
asyncOperation.completed += continuationAction;
}
}
class ResourceRequestConfiguredSource : IUniTaskSource<UnityEngine.Object>, IPlayerLoopItem, IPromisePoolItem
class ResourceRequestConfiguredSource : IUniTaskSource<UnityEngine.Object>, IPlayerLoopItem, ITaskPoolNode<ResourceRequestConfiguredSource>
{
static readonly PromisePool<ResourceRequestConfiguredSource> pool = new PromisePool<ResourceRequestConfiguredSource>();
static TaskPool<ResourceRequestConfiguredSource> pool;
public ResourceRequestConfiguredSource NextNode { get; set; }
static ResourceRequestConfiguredSource()
{
TaskPoolMonitor.RegisterSizeGetter(typeof(ResourceRequestConfiguredSource), () => pool.Size);
}
ResourceRequest asyncOperation;
IProgress<float> progress;
@@ -270,7 +285,10 @@ namespace Cysharp.Threading.Tasks
return AutoResetUniTaskCompletionSource<UnityEngine.Object>.CreateFromCanceled(cancellationToken, out token);
}
var result = pool.TryRent() ?? new ResourceRequestConfiguredSource();
if (!pool.TryPop(out var result))
{
result = new ResourceRequestConfiguredSource();
}
result.asyncOperation = asyncOperation;
result.progress = progress;
@@ -288,13 +306,12 @@ namespace Cysharp.Threading.Tasks
{
try
{
TaskTracker.RemoveTracking(this);
return core.GetResult(token);
}
finally
{
pool.TryReturn(this);
TryReturn();
}
}
@@ -340,26 +357,28 @@ namespace Cysharp.Threading.Tasks
return true;
}
public void Reset()
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
asyncOperation = default;
progress = default;
cancellationToken = default;
return pool.TryPush(this);
}
~ResourceRequestConfiguredSource()
{
if (pool.TryReturn(this))
if (TryReturn())
{
GC.ReRegisterForFinalize(this);
}
}
}
# endregion
#endregion
#region AssetBundleRequest
#region AssetBundleRequest
public static AssetBundleRequestAwaiter GetAwaiter(this AssetBundleRequest asyncOperation)
{
@@ -367,18 +386,18 @@ namespace Cysharp.Threading.Tasks
return new AssetBundleRequestAwaiter(asyncOperation);
}
public static UniTask<UnityEngine.Object> ToUniTask(this AssetBundleRequest asyncOperation)
public static UniTask<UnityEngine.Object> WithCancellation(this AssetBundleRequest asyncOperation, CancellationToken cancellationToken)
{
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
return new UniTask<UnityEngine.Object>(AssetBundleRequestConfiguredSource.Create(asyncOperation, PlayerLoopTiming.Update, null, CancellationToken.None, out var token), token);
if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.asset);
return new UniTask<UnityEngine.Object>(AssetBundleRequestConfiguredSource.Create(asyncOperation, PlayerLoopTiming.Update, null, cancellationToken, out var token), token);
}
public static UniTask<UnityEngine.Object> ConfigureAwait(this AssetBundleRequest asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellation = default(CancellationToken))
public static UniTask<UnityEngine.Object> ToUniTask(this AssetBundleRequest asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
{
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
return new UniTask<UnityEngine.Object>(AssetBundleRequestConfiguredSource.Create(asyncOperation, timing, progress, cancellation, out var token), token);
if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.asset);
return new UniTask<UnityEngine.Object>(AssetBundleRequestConfiguredSource.Create(asyncOperation, timing, progress, cancellationToken, out var token), token);
}
public struct AssetBundleRequestAwaiter : ICriticalNotifyCompletion
@@ -420,14 +439,20 @@ namespace Cysharp.Threading.Tasks
public void UnsafeOnCompleted(Action continuation)
{
Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction);
continuationAction = continuation.AsFuncOfT<AsyncOperation>(); // allocate delegate.
continuationAction = PooledDelegate<AsyncOperation>.Create(continuation);
asyncOperation.completed += continuationAction;
}
}
class AssetBundleRequestConfiguredSource : IUniTaskSource<UnityEngine.Object>, IPlayerLoopItem, IPromisePoolItem
class AssetBundleRequestConfiguredSource : IUniTaskSource<UnityEngine.Object>, IPlayerLoopItem, ITaskPoolNode<AssetBundleRequestConfiguredSource>
{
static readonly PromisePool<AssetBundleRequestConfiguredSource> pool = new PromisePool<AssetBundleRequestConfiguredSource>();
static TaskPool<AssetBundleRequestConfiguredSource> pool;
public AssetBundleRequestConfiguredSource NextNode { get; set; }
static AssetBundleRequestConfiguredSource()
{
TaskPoolMonitor.RegisterSizeGetter(typeof(AssetBundleRequestConfiguredSource), () => pool.Size);
}
AssetBundleRequest asyncOperation;
IProgress<float> progress;
@@ -447,7 +472,10 @@ namespace Cysharp.Threading.Tasks
return AutoResetUniTaskCompletionSource<UnityEngine.Object>.CreateFromCanceled(cancellationToken, out token);
}
var result = pool.TryRent() ?? new AssetBundleRequestConfiguredSource();
if (!pool.TryPop(out var result))
{
result = new AssetBundleRequestConfiguredSource();
}
result.asyncOperation = asyncOperation;
result.progress = progress;
@@ -465,13 +493,11 @@ namespace Cysharp.Threading.Tasks
{
try
{
TaskTracker.RemoveTracking(this);
return core.GetResult(token);
}
finally
{
pool.TryReturn(this);
TryReturn();
}
}
@@ -517,26 +543,28 @@ namespace Cysharp.Threading.Tasks
return true;
}
public void Reset()
bool TryReturn()
{
core.Reset();
asyncOperation = default;
progress = default;
cancellationToken = default;
TaskTracker.RemoveTracking(this);
return pool.TryPush(this);
}
~AssetBundleRequestConfiguredSource()
{
if (pool.TryReturn(this))
if (TryReturn())
{
GC.ReRegisterForFinalize(this);
}
}
}
# endregion
#endregion
#region AssetBundleCreateRequest
#region AssetBundleCreateRequest
public static AssetBundleCreateRequestAwaiter GetAwaiter(this AssetBundleCreateRequest asyncOperation)
{
@@ -544,18 +572,18 @@ namespace Cysharp.Threading.Tasks
return new AssetBundleCreateRequestAwaiter(asyncOperation);
}
public static UniTask<AssetBundle> ToUniTask(this AssetBundleCreateRequest asyncOperation)
public static UniTask<AssetBundle> WithCancellation(this AssetBundleCreateRequest asyncOperation, CancellationToken cancellationToken)
{
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
return new UniTask<AssetBundle>(AssetBundleCreateRequestConfiguredSource.Create(asyncOperation, PlayerLoopTiming.Update, null, CancellationToken.None, out var token), token);
if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.assetBundle);
return new UniTask<AssetBundle>(AssetBundleCreateRequestConfiguredSource.Create(asyncOperation, PlayerLoopTiming.Update, null, cancellationToken, out var token), token);
}
public static UniTask<AssetBundle> ConfigureAwait(this AssetBundleCreateRequest asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellation = default(CancellationToken))
public static UniTask<AssetBundle> ToUniTask(this AssetBundleCreateRequest asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
{
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
return new UniTask<AssetBundle>(AssetBundleCreateRequestConfiguredSource.Create(asyncOperation, timing, progress, cancellation, out var token), token);
if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.assetBundle);
return new UniTask<AssetBundle>(AssetBundleCreateRequestConfiguredSource.Create(asyncOperation, timing, progress, cancellationToken, out var token), token);
}
public struct AssetBundleCreateRequestAwaiter : ICriticalNotifyCompletion
@@ -597,14 +625,20 @@ namespace Cysharp.Threading.Tasks
public void UnsafeOnCompleted(Action continuation)
{
Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction);
continuationAction = continuation.AsFuncOfT<AsyncOperation>(); // allocate delegate.
continuationAction = PooledDelegate<AsyncOperation>.Create(continuation);
asyncOperation.completed += continuationAction;
}
}
class AssetBundleCreateRequestConfiguredSource : IUniTaskSource<AssetBundle>, IPlayerLoopItem, IPromisePoolItem
class AssetBundleCreateRequestConfiguredSource : IUniTaskSource<AssetBundle>, IPlayerLoopItem, ITaskPoolNode<AssetBundleCreateRequestConfiguredSource>
{
static readonly PromisePool<AssetBundleCreateRequestConfiguredSource> pool = new PromisePool<AssetBundleCreateRequestConfiguredSource>();
static TaskPool<AssetBundleCreateRequestConfiguredSource> pool;
public AssetBundleCreateRequestConfiguredSource NextNode { get; set; }
static AssetBundleCreateRequestConfiguredSource()
{
TaskPoolMonitor.RegisterSizeGetter(typeof(AssetBundleCreateRequestConfiguredSource), () => pool.Size);
}
AssetBundleCreateRequest asyncOperation;
IProgress<float> progress;
@@ -624,7 +658,10 @@ namespace Cysharp.Threading.Tasks
return AutoResetUniTaskCompletionSource<AssetBundle>.CreateFromCanceled(cancellationToken, out token);
}
var result = pool.TryRent() ?? new AssetBundleCreateRequestConfiguredSource();
if (!pool.TryPop(out var result))
{
result = new AssetBundleCreateRequestConfiguredSource();
}
result.asyncOperation = asyncOperation;
result.progress = progress;
@@ -642,13 +679,11 @@ namespace Cysharp.Threading.Tasks
{
try
{
TaskTracker.RemoveTracking(this);
return core.GetResult(token);
}
finally
{
pool.TryReturn(this);
TryReturn();
}
}
@@ -694,27 +729,29 @@ namespace Cysharp.Threading.Tasks
return true;
}
public void Reset()
bool TryReturn()
{
core.Reset();
asyncOperation = default;
progress = default;
cancellationToken = default;
TaskTracker.RemoveTracking(this);
return pool.TryPush(this);
}
~AssetBundleCreateRequestConfiguredSource()
{
if (pool.TryReturn(this))
if (TryReturn())
{
GC.ReRegisterForFinalize(this);
}
}
}
# endregion
#endregion
#if ENABLE_UNITYWEBREQUEST
#region UnityWebRequestAsyncOperation
#region UnityWebRequestAsyncOperation
public static UnityWebRequestAsyncOperationAwaiter GetAwaiter(this UnityWebRequestAsyncOperation asyncOperation)
{
@@ -722,18 +759,18 @@ namespace Cysharp.Threading.Tasks
return new UnityWebRequestAsyncOperationAwaiter(asyncOperation);
}
public static UniTask<UnityWebRequest> ToUniTask(this UnityWebRequestAsyncOperation asyncOperation)
public static UniTask<UnityWebRequest> WithCancellation(this UnityWebRequestAsyncOperation asyncOperation, CancellationToken cancellationToken)
{
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
return new UniTask<UnityWebRequest>(UnityWebRequestAsyncOperationConfiguredSource.Create(asyncOperation, PlayerLoopTiming.Update, null, CancellationToken.None, out var token), token);
if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.webRequest);
return new UniTask<UnityWebRequest>(UnityWebRequestAsyncOperationConfiguredSource.Create(asyncOperation, PlayerLoopTiming.Update, null, cancellationToken, out var token), token);
}
public static UniTask<UnityWebRequest> ConfigureAwait(this UnityWebRequestAsyncOperation asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellation = default(CancellationToken))
public static UniTask<UnityWebRequest> ToUniTask(this UnityWebRequestAsyncOperation asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
{
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
return new UniTask<UnityWebRequest>(UnityWebRequestAsyncOperationConfiguredSource.Create(asyncOperation, timing, progress, cancellation, out var token), token);
if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.webRequest);
return new UniTask<UnityWebRequest>(UnityWebRequestAsyncOperationConfiguredSource.Create(asyncOperation, timing, progress, cancellationToken, out var token), token);
}
public struct UnityWebRequestAsyncOperationAwaiter : ICriticalNotifyCompletion
@@ -775,14 +812,20 @@ namespace Cysharp.Threading.Tasks
public void UnsafeOnCompleted(Action continuation)
{
Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction);
continuationAction = continuation.AsFuncOfT<AsyncOperation>(); // allocate delegate.
continuationAction = PooledDelegate<AsyncOperation>.Create(continuation);
asyncOperation.completed += continuationAction;
}
}
class UnityWebRequestAsyncOperationConfiguredSource : IUniTaskSource<UnityWebRequest>, IPlayerLoopItem, IPromisePoolItem
class UnityWebRequestAsyncOperationConfiguredSource : IUniTaskSource<UnityWebRequest>, IPlayerLoopItem, ITaskPoolNode<UnityWebRequestAsyncOperationConfiguredSource>
{
static readonly PromisePool<UnityWebRequestAsyncOperationConfiguredSource> pool = new PromisePool<UnityWebRequestAsyncOperationConfiguredSource>();
static TaskPool<UnityWebRequestAsyncOperationConfiguredSource> pool;
public UnityWebRequestAsyncOperationConfiguredSource NextNode { get; set; }
static UnityWebRequestAsyncOperationConfiguredSource()
{
TaskPoolMonitor.RegisterSizeGetter(typeof(UnityWebRequestAsyncOperationConfiguredSource), () => pool.Size);
}
UnityWebRequestAsyncOperation asyncOperation;
IProgress<float> progress;
@@ -802,7 +845,10 @@ namespace Cysharp.Threading.Tasks
return AutoResetUniTaskCompletionSource<UnityWebRequest>.CreateFromCanceled(cancellationToken, out token);
}
var result = pool.TryRent() ?? new UnityWebRequestAsyncOperationConfiguredSource();
if (!pool.TryPop(out var result))
{
result = new UnityWebRequestAsyncOperationConfiguredSource();
}
result.asyncOperation = asyncOperation;
result.progress = progress;
@@ -820,13 +866,12 @@ namespace Cysharp.Threading.Tasks
{
try
{
TaskTracker.RemoveTracking(this);
return core.GetResult(token);
}
finally
{
pool.TryReturn(this);
TryReturn();
}
}
@@ -854,6 +899,7 @@ namespace Cysharp.Threading.Tasks
{
if (cancellationToken.IsCancellationRequested)
{
asyncOperation.webRequest.Abort();
core.TrySetCanceled(cancellationToken);
return false;
}
@@ -872,24 +918,26 @@ namespace Cysharp.Threading.Tasks
return true;
}
public void Reset()
bool TryReturn()
{
core.Reset();
asyncOperation = default;
progress = default;
cancellationToken = default;
TaskTracker.RemoveTracking(this);
return pool.TryPush(this);
}
~UnityWebRequestAsyncOperationConfiguredSource()
{
if (pool.TryReturn(this))
if (TryReturn())
{
GC.ReRegisterForFinalize(this);
}
}
}
# endregion
#endregion
#endif
}

View File

@@ -1,8 +1,8 @@
{
"name": "com.cysharp.unitask",
"displayName": "UniTask",
"version": "2.0.9-rc6",
"unity": "2018.3",
"version": "2.0.12-rc9",
"unity": "2019.1",
"description": "Provides an efficient async/await integration to Unity.",
"keywords": [ "async/await", "async", "Task", "UniTask" ],
"license": "MIT",

View File

@@ -10,8 +10,11 @@ using System.Threading.Tasks;
using Unity.Collections;
using Unity.Jobs;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;
// using DG.Tweening;
public struct MyJob : IJob
{
public int loopCount;
@@ -142,7 +145,7 @@ public class SandboxMain : MonoBehaviour
{
// State<int> Hp { get; }
public Model()
@@ -173,7 +176,7 @@ public class SandboxMain : MonoBehaviour
void Start2()
{
}
@@ -189,15 +192,20 @@ public class SandboxMain : MonoBehaviour
async UniTask RunJobAsync()
{
var job = new MyJob() { loopCount = 999, inOut = new NativeArray<int>(1, Allocator.TempJob) };
JobHandle.ScheduleBatchedJobs();
try
{
JobHandle.ScheduleBatchedJobs();
var scheduled = job.Schedule();
var scheduled = job.Schedule();
UnityEngine.Debug.Log("OK");
await scheduled; // .ConfigureAwait(PlayerLoopTiming.Update); // .WaitAsync(PlayerLoopTiming.Update);
UnityEngine.Debug.Log("OK2");
job.inOut.Dispose();
UnityEngine.Debug.Log("OK");
await scheduled; // .ConfigureAwait(PlayerLoopTiming.Update); // .WaitAsync(PlayerLoopTiming.Update);
UnityEngine.Debug.Log("OK2");
}
finally
{
job.inOut.Dispose();
}
}
@@ -240,16 +248,54 @@ public class SandboxMain : MonoBehaviour
public int MyProperty { get; set; }
}
MyClass mcc;
void Start()
async Task Test1()
{
this.mcc = new MyClass();
this.MyProperty = 999;
var r = await TcsAsync("https://bing.com/");
Debug.Log("TASKASYNC");
}
CheckDest().Forget();
async UniTaskVoid Test2()
{
try
{
//var cts = new CancellationTokenSource();
//var r = UniAsync("https://bing.com/", cts.Token);
//cts.Cancel();
//await r;
_ = await UnityWebRequest.Get("https://bing.com/").SendWebRequest();
Debug.Log("UNIASYNC1 ");
_ = await UnityWebRequest.Get("https://bing.com/").SendWebRequest();
Debug.Log("UNIASYNC2");
}
catch
{
Debug.Log("Canceled");
}
}
IEnumerator Test3(string url)
{
var req = UnityWebRequest.Get(url).SendWebRequest();
yield return req;
Debug.Log("COROUTINE");
}
static async Task<UnityWebRequest> TcsAsync(string url)
{
var req = await UnityWebRequest.Get(url).SendWebRequest();
return req;
}
static async UniTask<UnityWebRequest> UniAsync(string url, CancellationToken cancellationToken)
{
var req = await UnityWebRequest.Get(url).SendWebRequest().WithCancellation(cancellationToken);
return req;
}
async UniTaskVoid Start()
{
//UniTaskAsyncEnumerable.EveryValueChanged(mcc, x => x.MyProperty)
// .Do(_ => { }, () => Debug.Log("COMPLETED"))
// .ForEachAsync(x =>
@@ -258,38 +304,66 @@ public class SandboxMain : MonoBehaviour
// })
// .Forget();
//_ = Test1();
Test2().Forget();
//StartCoroutine(Test3("https://bing.com/"));
// DG.Tweening.Core.TweenerCore<int>
//Debug.Log("GO MOVEX");
//await okButton.GetComponent<RectTransform>().DOMoveX(-10.2f, 3).WithCancellation(CancellationToken.None);
//Debug.Log("END MOVEX");
//Debug.Log("AGAIN MOVE");
//await okButton.GetComponent<RectTransform>().DOMoveY(10.2f, 3).WithCancellation(CancellationToken.None);
//Debug.Log("AGAIN END MOVE");
await UniTask.Yield();
// DOTween.To(
var cts = new CancellationTokenSource();
//var tween = okButton.GetComponent<RectTransform>().DOLocalMoveX(100, 5.0f);
cancelButton.OnClickAsAsyncEnumerable().ForEachAsync(_ =>
{
cts.Cancel();
}).Forget();
// await tween.ToUniTask(TweenCancelBehaviour.KillAndCancelAwait, cts.Token);
//tween.SetRecyclable(true);
Debug.Log("END");
// tween.Play();
// DOTween.
// DOVirtual.Float(0, 1, 1, x => { }).ToUniTask();
okButton.OnClickAsAsyncEnumerable().ForEachAsync(_ =>
{
mcc.MyProperty += 10;
}).Forget();
cancelButton.OnClickAsAsyncEnumerable().ForEachAsync(_ =>
{
this.mcc = null;
});
CloseAsync(this.GetCancellationTokenOnDestroy()).Forget();
okButton.onClick.AddListener(UniTask.UnityAction(async () => await UniTask.Yield()));
}
async UniTaskVoid CheckDest()
async UniTaskVoid CloseAsync(CancellationToken cancellationToken = default)
{
try
while (true)
{
Debug.Log("WAIT");
await UniTask.WaitUntilValueChanged(mcc, x => x.MyProperty);
Debug.Log("CHANGED?");
}
finally
{
Debug.Log("END");
await UniTask.Yield(PlayerLoopTiming.Update, cancellationToken);
}
}
@@ -525,7 +599,7 @@ public class SandboxMain : MonoBehaviour
public class ShowPlayerLoop
{
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
// [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
static void Init()
{
var playerLoop = UnityEngine.LowLevel.PlayerLoop.GetDefaultPlayerLoop();

View File

@@ -124,7 +124,6 @@ namespace Cysharp.Threading.TasksTests
public IEnumerator BothEnumeratorCheck() => UniTask.ToCoroutine(async () =>
{
await ToaruCoroutineEnumerator(); // wait 5 frame:)
await ToaruCoroutineEnumerator().ConfigureAwait(PlayerLoopTiming.PostLateUpdate);
});
[UnityTest]

View File

@@ -124,7 +124,6 @@ namespace Cysharp.Threading.TasksTests
public IEnumerator BothEnumeratorCheck() => UniTask.ToCoroutine(async () =>
{
await ToaruCoroutineEnumerator(); // wait 5 frame:)
await ToaruCoroutineEnumerator().ConfigureAwait(PlayerLoopTiming.PostLateUpdate);
});
[UnityTest]

View File

@@ -5,7 +5,8 @@
"UnityEditor.TestRunner",
"UniTask.Tests",
"UniTask",
"Unity.ResourceManager"
"Unity.ResourceManager",
"DOTween.Modules"
],
"includePlatforms": [
"Editor"
@@ -14,7 +15,8 @@
"allowUnsafeCode": false,
"overrideReferences": true,
"precompiledReferences": [
"nunit.framework.dll"
"nunit.framework.dll",
"DOTween.dll"
],
"autoReferenced": false,
"defineConstraints": [

View File

@@ -0,0 +1,165 @@
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine.UI;
using UnityEngine.Scripting;
using Cysharp.Threading.Tasks;
using Unity.Collections;
using System.Threading;
using NUnit.Framework;
using UnityEngine.TestTools;
using FluentAssertions;
namespace Cysharp.Threading.TasksTests
{
public class GenericsWhenAllAny
{
[UnityTest]
public IEnumerator WhenAllT15() => UniTask.ToCoroutine(async () =>
{
var t01 = Tes<int>();
var t02 = Tes<int>();
var t03 = Tes<int>();
var t04 = Tes<int>();
var t05 = Tes<int>();
var t06 = Tes<int>();
var t07 = Tes<int>();
var t08 = Tes<int>();
var t09 = Tes<int>();
var t10 = Tes<int>();
var t11 = Tes<int>();
var t12 = Tes<int>();
var t13 = Tes<int>();
var t14 = Tes<int>();
var t15 = Tes<int>();
await UniTask.WhenAll(t01, t02, t03, t04, t05, t06, t07, t08, t09, t10, t11, t12, t13, t14, t15);
});
[UnityTest]
public IEnumerator WhenAllT01_Generics1() => UniTask.ToCoroutine(async () =>
{
var t01 = Tes<MyGenerics<int>>();
await UniTask.WhenAll(t01);
});
[UnityTest]
public IEnumerator WhenAllT02_Generics1() => UniTask.ToCoroutine(async () =>
{
var t01 = Tes<MyGenerics<int>>();
var t02 = Tes<MyGenerics<int>>();
await UniTask.WhenAll(t01, t02);
});
[UnityTest]
public IEnumerator WhenAllT03_Generics1() => UniTask.ToCoroutine(async () =>
{
var t01 = Tes<MyGenerics<int>>();
var t02 = Tes<MyGenerics<int>>();
var t03 = Tes<MyGenerics<int>>();
await UniTask.WhenAll(t01, t02, t03);
});
[UnityTest]
public IEnumerator WhenAllT04_Generics1() => UniTask.ToCoroutine(async () =>
{
var t01 = Tes<MyGenerics<int>>();
var t02 = Tes<MyGenerics<int>>();
var t03 = Tes<MyGenerics<int>>();
var t04 = Tes<MyGenerics<int>>();
await UniTask.WhenAll(t01, t02, t03, t04);
});
// will fail.
//[UnityTest]
//public IEnumerator WhenAllT05_Generics1() => UniTask.ToCoroutine(async () =>
//{
// var t01 = Tes<MyGenerics<int>>();
// var t02 = Tes<MyGenerics<int>>();
// var t03 = Tes<MyGenerics<int>>();
// var t04 = Tes<MyGenerics<int>>();
// var t05 = Tes<MyGenerics<int>>();
// await UniTask.WhenAll(t01, t02, t03, t04, t05);
//});
//[UnityTest]
//public IEnumerator WhenAllT06_Generics1() => UniTask.ToCoroutine(async () =>
//{
// var t01 = Tes<MyGenerics<int>>();
// var t02 = Tes<MyGenerics<int>>();
// var t03 = Tes<MyGenerics<int>>();
// var t04 = Tes<MyGenerics<int>>();
// var t05 = Tes<MyGenerics<int>>();
// var t06 = Tes<MyGenerics<int>>();
// await UniTask.WhenAll(t01, t02, t03, t04, t05, t06);
//});
//[UnityTest]
//public IEnumerator WhenAllT07_Generics1() => UniTask.ToCoroutine(async () =>
//{
// var t01 = Tes<MyGenerics<int>>();
// var t02 = Tes<MyGenerics<int>>();
// var t03 = Tes<MyGenerics<int>>();
// var t04 = Tes<MyGenerics<int>>();
// var t05 = Tes<MyGenerics<int>>();
// var t06 = Tes<MyGenerics<int>>();
// var t07 = Tes<MyGenerics<int>>();
// await UniTask.WhenAll(t01, t02, t03, t04, t05, t06, t07);
//});
//[UnityTest]
//public IEnumerator WhenAllT15_Generics1() => UniTask.ToCoroutine(async () =>
//{
// var t01 = Tes<MyGenerics<int>>();
// var t02 = Tes<MyGenerics<int>>();
// var t03 = Tes<MyGenerics<int>>();
// var t04 = Tes<MyGenerics<int>>();
// var t05 = Tes<MyGenerics<int>>();
// var t06 = Tes<MyGenerics<int>>();
// var t07 = Tes<MyGenerics<int>>();
// var t08 = Tes<MyGenerics<int>>();
// var t09 = Tes<MyGenerics<int>>();
// var t10 = Tes<MyGenerics<int>>();
// var t11 = Tes<MyGenerics<int>>();
// var t12 = Tes<MyGenerics<int>>();
// var t13 = Tes<MyGenerics<int>>();
// var t14 = Tes<MyGenerics<int>>();
// var t15 = Tes<MyGenerics<int>>();
// await UniTask.WhenAll(t01, t02, t03, t04, t05, t06, t07, t08, t09, t10, t11, t12, t13, t14, t15);
//});
async UniTask<T> Tes<T>()
{
await UniTask.Yield();
return default;
}
}
public class MyGenerics<T>
{
}
public class MyGenerics2
{
}
}
#endif

View File

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

View File

@@ -4,14 +4,16 @@
"UnityEngine.TestRunner",
"UnityEditor.TestRunner",
"UniTask",
"Unity.ResourceManager"
"Unity.ResourceManager",
"DOTween.Modules"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": true,
"precompiledReferences": [
"nunit.framework.dll"
"nunit.framework.dll",
"DOTween.dll"
],
"autoReferenced": false,
"defineConstraints": [