Compare commits

...

30 Commits

Author SHA1 Message Date
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
neuecc
2b7986da19 2.0.9-rc6 2020-05-22 10:45:16 +09:00
neuecc
c3d22968e1 State rename to AsyncReadOnlyReactiveProperty and removed setter and implicit conversion. 2020-05-22 10:44:41 +09:00
neuecc
0e25122ee2 Add Pairwise 2020-05-22 03:19:54 +09:00
neuecc
4504d84aa8 cl2 2020-05-22 02:34:04 +09:00
neuecc
2b87cadba3 Add CombineLatest 2020-05-22 02:25:36 +09:00
45 changed files with 13623 additions and 434 deletions

View File

@@ -1,123 +1,30 @@
#pragma warning disable 0649 #pragma warning disable 0649
using System;
using System.Runtime.CompilerServices;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Threading.Tasks.Sources;
namespace Cysharp.Threading.Tasks namespace Cysharp.Threading.Tasks
{ {
public static class UniTaskValueTaskExtensions 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); return task;
if (core.source == null)
{
return default;
}
return new ValueTask(new UniTaskValueTaskSource(core.source), core.token);
} }
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); return task;
if (core.source == null)
{
return new ValueTask<T>(core.result);
}
return new ValueTask<T>(new UniTaskValueTaskSource<T>(core.source), core.token);
} }
struct UniTaskToValueTask public static UniTask<T> AsUniTask<T>(this ValueTask<T> task, bool useCurrentSynchronizationContext = true)
{ {
public IUniTaskSource source; // NOTE: get _obj and _token directly for low overhead conversion but not yet implemented.
public short token; return task.AsTask().AsUniTask(useCurrentSynchronizationContext);
} }
class UniTaskValueTaskSource : IValueTaskSource public static UniTask AsUniTask(this ValueTask task, bool useCurrentSynchronizationContext = true)
{ {
readonly IUniTaskSource source; return task.AsTask().AsUniTask(useCurrentSynchronizationContext);
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);
}
} }
} }
} }

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.Runtime.CompilerServices;
using System.Threading; using System.Threading;
@@ -41,7 +43,11 @@ namespace Cysharp.Threading.Tasks
} }
else else
{ {
#if NETCOREAPP3_1
ThreadPool.UnsafeQueueUserWorkItem(ThreadPoolWorkItem.Create(continuation), false);
#else
ThreadPool.UnsafeQueueUserWorkItem(WaitCallbackDelegate, continuation); ThreadPool.UnsafeQueueUserWorkItem(WaitCallbackDelegate, continuation);
#endif
} }
} }
@@ -50,6 +56,39 @@ namespace Cysharp.Threading.Tasks
((Action)state).Invoke(); ((Action)state).Invoke();
} }
} }
#if NETCOREAPP3_1
public sealed class ThreadPoolWorkItem : IThreadPoolWorkItem
{
static readonly ConcurrentQueue<ThreadPoolWorkItem> pool = new ConcurrentQueue<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();
}
}
#endif
} }
} }
} }

View File

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

View File

@@ -0,0 +1,198 @@
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++)
{
await Core();
}
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++)
{
sum += await Core();
}
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)]
public Task ViaUniTaskVoid()
{
for (int i = 0; i < InnerOps; i++)
{
Core().Forget();
}
return Task.CompletedTask;
static async UniTaskVoid Core()
{
await new TestAwaiter(false, UniTaskStatus.Succeeded);
await new TestAwaiter(false, UniTaskStatus.Succeeded);
await new TestAwaiter(false, UniTaskStatus.Succeeded);
}
}
}
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
{
static readonly ConcurrentQueue<ThreadPoolWorkItem> pool = new ConcurrentQueue<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));
}
}
// 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 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 partial class UnityUIComponentExtensions
{ {
public static void BindTo(this IUniTaskAsyncEnumerable<string> source, Text text) public static void BindTo(this IUniTaskAsyncEnumerable<string> source, Text text)
@@ -88,20 +195,47 @@ namespace NetCoreSandbox
static void Main(string[] args) static async Task Main(string[] args)
{ {
#if !DEBUG
BenchmarkDotNet.Running.BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);
var channel = Channel.CreateSingleConsumerUnbounded<int>(); //await new ComparisonBenchmarks().ViaUniTaskT();
return;
#endif
AsyncTest().Forget();
//AsyncTest().Forget();
// AsyncTest().Forget();
await UniTask.Yield();
Console.ReadLine();
}
#pragma warning disable CS1998
static async UniTaskVoid AsyncTest()
{
// empty
await new TestAwaiter(false, UniTaskStatus.Succeeded);
await new TestAwaiter(true, UniTaskStatus.Succeeded);
await new TestAwaiter(false, UniTaskStatus.Succeeded);
Console.WriteLine("foo");
//return 10;
} }
#pragma warning restore CS1998
void Foo() void Foo()
{ {

View File

@@ -7,6 +7,8 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <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.Interactive.Async" Version="4.1.1" />
<PackageReference Include="System.Reactive" Version="4.4.1" /> <PackageReference Include="System.Reactive" Version="4.4.1" />
</ItemGroup> </ItemGroup>

View File

@@ -52,44 +52,44 @@ namespace NetCoreTests
ar.Should().BeEquivalentTo(new[] { 100, 100, 100, 131, 191 }); ar.Should().BeEquivalentTo(new[] { 100, 100, 100, 131, 191 });
} }
[Fact] //[Fact]
public async Task StateIteration() //public async Task StateIteration()
{ //{
var rp = new State<int>(99); // var rp = new ReadOnlyAsyncReactiveProperty<int>(99);
var setter = rp.GetSetter(); // var setter = rp.GetSetter();
var f = await rp.FirstAsync(); // var f = await rp.FirstAsync();
f.Should().Be(99); // f.Should().Be(99);
var array = rp.Take(5).ToArrayAsync(); // var array = rp.Take(5).ToArrayAsync();
setter(100); // setter(100);
setter(100); // setter(100);
setter(100); // setter(100);
setter(131); // setter(131);
var ar = await array; // var ar = await array;
ar.Should().BeEquivalentTo(new[] { 99, 100, 100, 100, 131 }); // ar.Should().BeEquivalentTo(new[] { 99, 100, 100, 100, 131 });
} //}
[Fact] //[Fact]
public async Task StateWithoutCurrent() //public async Task StateWithoutCurrent()
{ //{
var rp = new State<int>(99); // var rp = new ReadOnlyAsyncReactiveProperty<int>(99);
var setter = rp.GetSetter(); // var setter = rp.GetSetter();
var array = rp.WithoutCurrent().Take(5).ToArrayAsync(); // var array = rp.WithoutCurrent().Take(5).ToArrayAsync();
setter(100); // setter(100);
setter(100); // setter(100);
setter(100); // setter(100);
setter(131); // setter(131);
setter(191); // setter(191);
var ar = await array; // var ar = await array;
ar.Should().BeEquivalentTo(new[] { 100, 100, 100, 131, 191 }); // ar.Should().BeEquivalentTo(new[] { 100, 100, 100, 131, 191 });
} //}
@@ -98,7 +98,7 @@ namespace NetCoreTests
{ {
var rp = new AsyncReactiveProperty<int>(10); var rp = new AsyncReactiveProperty<int>(10);
var state = rp.ToState(CancellationToken.None); var state = rp.ToReadOnlyAsyncReactiveProperty(CancellationToken.None);
rp.Value = 10; rp.Value = 10;
state.Value.Should().Be(10); state.Value.Should().Be(10);

View File

@@ -319,5 +319,119 @@ namespace NetCoreTests.Linq
await Assert.ThrowsAsync<UniTaskTestException>(async () => await ys); await Assert.ThrowsAsync<UniTaskTestException>(async () => await ys);
} }
} }
[Fact]
public async Task CombineLatestOK()
{
var a = new AsyncReactiveProperty<int>(0);
var b = new AsyncReactiveProperty<int>(0);
var list = new List<(int, int)>();
var complete = a.WithoutCurrent().CombineLatest(b.WithoutCurrent(), (x, y) => (x, y)).ForEachAsync(x => list.Add(x));
list.Count.Should().Be(0);
a.Value = 10;
list.Count.Should().Be(0);
a.Value = 20;
list.Count.Should().Be(0);
b.Value = 1;
list.Count.Should().Be(1);
list[0].Should().Be((20, 1));
a.Value = 30;
list.Last().Should().Be((30, 1));
b.Value = 2;
list.Last().Should().Be((30, 2));
a.Dispose();
b.Value = 3;
list.Last().Should().Be((30, 3));
b.Dispose();
await complete;
}
[Fact]
public async Task CombineLatestLong()
{
var a = UniTaskAsyncEnumerable.Range(1, 100000);
var b = new AsyncReactiveProperty<int>(0);
var list = new List<(int, int)>();
var complete = a.CombineLatest(b.WithoutCurrent(), (x, y) => (x, y)).ForEachAsync(x => list.Add(x));
b.Value = 1;
list[0].Should().Be((100000, 1));
b.Dispose();
await complete;
}
[Fact]
public async Task CombineLatestError()
{
var a = new AsyncReactiveProperty<int>(0);
var b = new AsyncReactiveProperty<int>(0);
var list = new List<(int, int)>();
var complete = a.WithoutCurrent()
.Select(x => { if (x == 0) { throw new MyException(); } return x; })
.CombineLatest(b.WithoutCurrent(), (x, y) => (x, y)).ForEachAsync(x => list.Add(x));
a.Value = 10;
b.Value = 1;
list.Last().Should().Be((10, 1));
a.Value = 0;
await Assert.ThrowsAsync<MyException>(async () => await complete);
}
[Fact]
public async Task PariwiseImmediate()
{
var xs = await UniTaskAsyncEnumerable.Range(1, 5).Pairwise().ToArrayAsync();
xs.Should().BeEquivalentTo((1, 2), (2, 3), (3, 4), (4, 5));
}
[Fact]
public async Task Pariwise()
{
var a = new AsyncReactiveProperty<int>(0);
var list = new List<(int, int)>();
var complete = a.WithoutCurrent().Pairwise().ForEachAsync(x => list.Add(x));
list.Count.Should().Be(0);
a.Value = 10;
list.Count.Should().Be(0);
a.Value = 20;
list.Count.Should().Be(1);
a.Value = 30;
a.Value = 40;
a.Value = 50;
a.Dispose();
await complete;
list.Should().BeEquivalentTo((10, 20), (20, 30), (30, 40), (40, 50));
}
class MyException : Exception
{
}
} }
} }

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

@@ -175,13 +175,11 @@ namespace Cysharp.Threading.Tasks
} }
} }
public class State<T> : IReadOnlyAsyncReactiveProperty<T>, IDisposable public class ReadOnlyAsyncReactiveProperty<T> : IReadOnlyAsyncReactiveProperty<T>, IDisposable
{ {
TriggerEvent<T> triggerEvent; TriggerEvent<T> triggerEvent;
T latestValue; T latestValue;
Action<T> setter;
IUniTaskAsyncEnumerator<T> enumerator; IUniTaskAsyncEnumerator<T> enumerator;
public T Value public T Value
@@ -192,19 +190,13 @@ namespace Cysharp.Threading.Tasks
} }
} }
public State(T value) public ReadOnlyAsyncReactiveProperty(T initialValue, IUniTaskAsyncEnumerable<T> source, CancellationToken cancellationToken)
{
this.latestValue = value;
this.triggerEvent = default;
}
public State(T initialValue, IUniTaskAsyncEnumerable<T> source, CancellationToken cancellationToken)
{ {
latestValue = initialValue; latestValue = initialValue;
ConsumeEnumerator(source, cancellationToken).Forget(); ConsumeEnumerator(source, cancellationToken).Forget();
} }
public State(IUniTaskAsyncEnumerable<T> source, CancellationToken cancellationToken) public ReadOnlyAsyncReactiveProperty(IUniTaskAsyncEnumerable<T> source, CancellationToken cancellationToken)
{ {
ConsumeEnumerator(source, cancellationToken).Forget(); ConsumeEnumerator(source, cancellationToken).Forget();
} }
@@ -216,7 +208,9 @@ namespace Cysharp.Threading.Tasks
{ {
while (await enumerator.MoveNextAsync()) while (await enumerator.MoveNextAsync())
{ {
SetValue(enumerator.Current); var value = enumerator.Current;
this.latestValue = value;
triggerEvent.SetResult(value);
} }
} }
finally finally
@@ -226,28 +220,6 @@ namespace Cysharp.Threading.Tasks
} }
} }
public Action<T> GetSetter()
{
if (enumerator != null)
{
throw new InvalidOperationException("Can not get setter when create from IUniTaskAsyncEnumerable source.");
}
if (setter != null)
{
throw new InvalidOperationException("GetSetter can only call once.");
}
setter = SetValue;
return setter;
}
void SetValue(T value)
{
this.latestValue = value;
triggerEvent.SetResult(value);
}
public IUniTaskAsyncEnumerable<T> WithoutCurrent() public IUniTaskAsyncEnumerable<T> WithoutCurrent()
{ {
return new WithoutCurrentEnumerable(this); return new WithoutCurrentEnumerable(this);
@@ -268,12 +240,7 @@ namespace Cysharp.Threading.Tasks
triggerEvent.SetCompleted(); triggerEvent.SetCompleted();
} }
public static implicit operator State<T>(T value) public static implicit operator T(ReadOnlyAsyncReactiveProperty<T> value)
{
return new State<T>(value);
}
public static implicit operator T(State<T> value)
{ {
return value.Value; return value.Value;
} }
@@ -286,16 +253,16 @@ namespace Cysharp.Threading.Tasks
static bool isValueType; static bool isValueType;
static State() static ReadOnlyAsyncReactiveProperty()
{ {
isValueType = typeof(T).IsValueType; isValueType = typeof(T).IsValueType;
} }
class WithoutCurrentEnumerable : IUniTaskAsyncEnumerable<T> class WithoutCurrentEnumerable : IUniTaskAsyncEnumerable<T>
{ {
readonly State<T> parent; readonly ReadOnlyAsyncReactiveProperty<T> parent;
public WithoutCurrentEnumerable(State<T> parent) public WithoutCurrentEnumerable(ReadOnlyAsyncReactiveProperty<T> parent)
{ {
this.parent = parent; this.parent = parent;
} }
@@ -310,14 +277,14 @@ namespace Cysharp.Threading.Tasks
{ {
static Action<object> cancellationCallback = CancellationCallback; static Action<object> cancellationCallback = CancellationCallback;
readonly State<T> parent; readonly ReadOnlyAsyncReactiveProperty<T> parent;
readonly CancellationToken cancellationToken; readonly CancellationToken cancellationToken;
readonly CancellationTokenRegistration cancellationTokenRegistration; readonly CancellationTokenRegistration cancellationTokenRegistration;
T value; T value;
bool isDisposed; bool isDisposed;
bool firstCall; bool firstCall;
public Enumerator(State<T> parent, CancellationToken cancellationToken, bool publishCurrentValue) public Enumerator(ReadOnlyAsyncReactiveProperty<T> parent, CancellationToken cancellationToken, bool publishCurrentValue)
{ {
this.parent = parent; this.parent = parent;
this.cancellationToken = cancellationToken; this.cancellationToken = cancellationToken;
@@ -391,14 +358,14 @@ namespace Cysharp.Threading.Tasks
public static class StateExtensions public static class StateExtensions
{ {
public static State<T> ToState<T>(this IUniTaskAsyncEnumerable<T> source, CancellationToken cancellationToken) public static ReadOnlyAsyncReactiveProperty<T> ToReadOnlyAsyncReactiveProperty<T>(this IUniTaskAsyncEnumerable<T> source, CancellationToken cancellationToken)
{ {
return new State<T>(source, cancellationToken); return new ReadOnlyAsyncReactiveProperty<T>(source, cancellationToken);
} }
public static State<T> ToState<T>(this IUniTaskAsyncEnumerable<T> source, T initialValue, CancellationToken cancellationToken) public static ReadOnlyAsyncReactiveProperty<T> ToReadOnlyAsyncReactiveProperty<T>(this IUniTaskAsyncEnumerable<T> source, T initialValue, CancellationToken cancellationToken)
{ {
return new State<T>(initialValue, source, cancellationToken); return new ReadOnlyAsyncReactiveProperty<T>(initialValue, source, cancellationToken);
} }
} }
} }

View File

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

View File

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

View File

@@ -14,7 +14,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
{ {
// cache items. // cache items.
AutoResetUniTaskCompletionSource promise; AutoResetUniTaskCompletionSource promise;
IMoveNextRunner runner; internal IMoveNextRunner runner;
// 1. Static Create method. // 1. Static Create method.
[DebuggerHidden] [DebuggerHidden]
@@ -50,6 +50,8 @@ namespace Cysharp.Threading.Tasks.CompilerServices
[DebuggerHidden] [DebuggerHidden]
public void SetException(Exception exception) public void SetException(Exception exception)
{ {
var p = promise; // after return, promise will clear so require to store local.
// runner is finished, return first. // runner is finished, return first.
if (runner != null) if (runner != null)
{ {
@@ -57,9 +59,9 @@ namespace Cysharp.Threading.Tasks.CompilerServices
runner = null; runner = null;
} }
if (promise != null) if (p != null)
{ {
promise.TrySetException(exception); p.TrySetException(exception);
} }
else else
{ {
@@ -71,6 +73,8 @@ namespace Cysharp.Threading.Tasks.CompilerServices
[DebuggerHidden] [DebuggerHidden]
public void SetResult() public void SetResult()
{ {
var p = promise; // after return, promise will clear so require to store local.
// runner is finished, return first. // runner is finished, return first.
if (runner != null) if (runner != null)
{ {
@@ -78,9 +82,9 @@ namespace Cysharp.Threading.Tasks.CompilerServices
runner = null; runner = null;
} }
if (promise != null) if (p != null)
{ {
promise.TrySetResult(); p.TrySetResult();
} }
} }
@@ -96,7 +100,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
} }
if (runner == null) if (runner == null)
{ {
runner = MoveNextRunner<TStateMachine>.Create(ref stateMachine); MoveNextRunner<TStateMachine>.SetRunner(ref this, ref stateMachine);
} }
awaiter.OnCompleted(runner.CallMoveNext); awaiter.OnCompleted(runner.CallMoveNext);
@@ -115,10 +119,10 @@ namespace Cysharp.Threading.Tasks.CompilerServices
} }
if (runner == null) if (runner == null)
{ {
runner = MoveNextRunner<TStateMachine>.Create(ref stateMachine); MoveNextRunner<TStateMachine>.SetRunner(ref this, ref stateMachine);
} }
awaiter.OnCompleted(runner.CallMoveNext); awaiter.UnsafeOnCompleted(runner.CallMoveNext);
} }
// 7. Start // 7. Start
@@ -158,7 +162,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
{ {
// cache items. // cache items.
AutoResetUniTaskCompletionSource<T> promise; AutoResetUniTaskCompletionSource<T> promise;
IMoveNextRunner runner; internal IMoveNextRunner runner;
T result; T result;
// 1. Static Create method. // 1. Static Create method.
@@ -194,6 +198,8 @@ namespace Cysharp.Threading.Tasks.CompilerServices
[DebuggerHidden] [DebuggerHidden]
public void SetException(Exception exception) public void SetException(Exception exception)
{ {
var p = promise; // after return, promise will clear so require to store local.
// runner is finished, return first. // runner is finished, return first.
if (runner != null) if (runner != null)
{ {
@@ -201,13 +207,13 @@ namespace Cysharp.Threading.Tasks.CompilerServices
runner = null; runner = null;
} }
if (promise == null) if (p == null)
{ {
promise = AutoResetUniTaskCompletionSource<T>.CreateFromException(exception, out _); promise = AutoResetUniTaskCompletionSource<T>.CreateFromException(exception, out _);
} }
else else
{ {
promise.TrySetException(exception); p.TrySetException(exception);
} }
} }
@@ -215,6 +221,8 @@ namespace Cysharp.Threading.Tasks.CompilerServices
[DebuggerHidden] [DebuggerHidden]
public void SetResult(T result) public void SetResult(T result)
{ {
var p = promise; // after return, promise will clear so require to store local.
// runner is finished, return first. // runner is finished, return first.
if (runner != null) if (runner != null)
{ {
@@ -222,13 +230,14 @@ namespace Cysharp.Threading.Tasks.CompilerServices
runner = null; runner = null;
} }
if (promise == null) if (p == null)
{ {
this.result = result; this.result = result;
return;
} }
else
promise.TrySetResult(result); {
p.TrySetResult(result);
}
} }
// 5. AwaitOnCompleted // 5. AwaitOnCompleted
@@ -243,7 +252,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
} }
if (runner == null) if (runner == null)
{ {
runner = MoveNextRunner<TStateMachine>.Create(ref stateMachine); MoveNextRunner<TStateMachine>.SetRunner(ref this, ref stateMachine);
} }
awaiter.OnCompleted(runner.CallMoveNext); awaiter.OnCompleted(runner.CallMoveNext);
@@ -262,10 +271,10 @@ namespace Cysharp.Threading.Tasks.CompilerServices
} }
if (runner == null) if (runner == null)
{ {
runner = MoveNextRunner<TStateMachine>.Create(ref stateMachine); MoveNextRunner<TStateMachine>.SetRunner(ref this, ref stateMachine);
} }
awaiter.OnCompleted(runner.CallMoveNext); awaiter.UnsafeOnCompleted(runner.CallMoveNext);
} }
// 7. Start // 7. Start

View File

@@ -10,7 +10,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
{ {
public struct AsyncUniTaskVoidMethodBuilder public struct AsyncUniTaskVoidMethodBuilder
{ {
IMoveNextRunner runner; internal IMoveNextRunner runner;
// 1. Static Create method. // 1. Static Create method.
[DebuggerHidden] [DebuggerHidden]
@@ -65,7 +65,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
{ {
if (runner == null) if (runner == null)
{ {
runner = MoveNextRunner<TStateMachine>.Create(ref stateMachine); MoveNextRunner<TStateMachine>.SetRunner(ref this, ref stateMachine);
} }
awaiter.OnCompleted(runner.CallMoveNext); awaiter.OnCompleted(runner.CallMoveNext);
@@ -80,10 +80,10 @@ namespace Cysharp.Threading.Tasks.CompilerServices
{ {
if (runner == null) if (runner == null)
{ {
runner = MoveNextRunner<TStateMachine>.Create(ref stateMachine); MoveNextRunner<TStateMachine>.SetRunner(ref this, ref stateMachine);
} }
awaiter.OnCompleted(runner.CallMoveNext); awaiter.UnsafeOnCompleted(runner.CallMoveNext);
} }
// 7. Start // 7. Start

View File

@@ -26,19 +26,48 @@ namespace Cysharp.Threading.Tasks.CompilerServices
MoveNextRunner() MoveNextRunner()
{ {
callMoveNext = MoveNext; callMoveNext = Run;
} }
public static MoveNextRunner<TStateMachine> Create(ref TStateMachine stateMachine) public static void SetRunner(ref AsyncUniTaskMethodBuilder builder, ref TStateMachine stateMachine)
{ {
var result = pool.TryRent() ?? new MoveNextRunner<TStateMachine>(); var result = pool.TryRent();
result.stateMachine = stateMachine; if (result == null)
return result; {
result = new MoveNextRunner<TStateMachine>();
}
builder.runner = result; // set runner before copied.
result.stateMachine = stateMachine; // copy struct StateMachine(in release build).
}
public static void SetRunner<T>(ref AsyncUniTaskMethodBuilder<T> builder, ref TStateMachine stateMachine)
{
var result = pool.TryRent();
if (result == null)
{
result = new MoveNextRunner<TStateMachine>();
}
builder.runner = result; // set runner before copied.
result.stateMachine = stateMachine; // copy struct StateMachine(in release build).
}
public static void SetRunner(ref AsyncUniTaskVoidMethodBuilder builder, ref TStateMachine stateMachine)
{
var result = pool.TryRent();
if (result == null)
{
result = new MoveNextRunner<TStateMachine>();
}
builder.runner = result; // set runner before copied.
result.stateMachine = stateMachine; // copy struct StateMachine(in release build).
} }
[DebuggerHidden] [DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
void MoveNext() void Run()
{ {
stateMachine.MoveNext(); stateMachine.MoveNext();
} }

View File

@@ -14,16 +14,19 @@ namespace Cysharp.Threading.Tasks
{ {
public static UniTask.Awaiter GetAwaiter(this IEnumerator enumerator) 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(); 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); return new UniTask(EnumeratorPromise.Create(enumerator, timing, cancellationToken, out var token), token);
} }

View File

@@ -13,21 +13,23 @@ namespace Cysharp.Threading.Tasks
{ {
public static class AddressableAsyncExtensions public static class AddressableAsyncExtensions
{ {
#region AsyncOperationHandle #region AsyncOperationHandle
public static AsyncOperationHandleAwaiter GetAwaiter(this AsyncOperationHandle handle) public static AsyncOperationHandleAwaiter GetAwaiter(this AsyncOperationHandle handle)
{ {
return new AsyncOperationHandleAwaiter(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 public struct AsyncOperationHandleAwaiter : ICriticalNotifyCompletion
@@ -75,7 +77,7 @@ namespace Cysharp.Threading.Tasks
} }
} }
class AsyncOperationHandleConfiguredSource : IUniTaskSource, IPlayerLoopItem, IPromisePoolItem sealed class AsyncOperationHandleConfiguredSource : IUniTaskSource, IPlayerLoopItem, IPromisePoolItem
{ {
static readonly PromisePool<AsyncOperationHandleConfiguredSource> pool = new PromisePool<AsyncOperationHandleConfiguredSource>(); static readonly PromisePool<AsyncOperationHandleConfiguredSource> pool = new PromisePool<AsyncOperationHandleConfiguredSource>();
@@ -185,23 +187,25 @@ namespace Cysharp.Threading.Tasks
} }
} }
#endregion #endregion
#region AsyncOperationHandle_T #region AsyncOperationHandle_T
public static AsyncOperationHandleAwaiter<T> GetAwaiter<T>(this AsyncOperationHandle<T> handle) public static AsyncOperationHandleAwaiter<T> GetAwaiter<T>(this AsyncOperationHandle<T> handle)
{ {
return new AsyncOperationHandleAwaiter<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 public struct AsyncOperationHandleAwaiter<T> : ICriticalNotifyCompletion
@@ -250,7 +254,7 @@ namespace Cysharp.Threading.Tasks
} }
} }
class AsyncOperationHandleConfiguredSource<T> : IUniTaskSource<T>, IPlayerLoopItem, IPromisePoolItem sealed class AsyncOperationHandleConfiguredSource<T> : IUniTaskSource<T>, IPlayerLoopItem, IPromisePoolItem
{ {
static readonly PromisePool<AsyncOperationHandleConfiguredSource<T>> pool = new PromisePool<AsyncOperationHandleConfiguredSource<T>>(); static readonly PromisePool<AsyncOperationHandleConfiguredSource<T>> pool = new PromisePool<AsyncOperationHandleConfiguredSource<T>>();
@@ -366,7 +370,7 @@ namespace Cysharp.Threading.Tasks
} }
} }
#endregion #endregion
} }
} }

View File

@@ -0,0 +1,239 @@
// 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.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)
{
// convert Action -> TweenCallback allocation.
// onKill is called after OnCompleted, both Complete(false/true) and Kill(false/true).
tween.onKill = new TweenCallback(continuation);
}
}
sealed class TweenConfiguredSource : IUniTaskSource, IPromisePoolItem
{
static readonly PromisePool<TweenConfiguredSource> pool = new PromisePool<TweenConfiguredSource>();
static readonly Action<object> CancellationCallbackDelegate = CancellationCallback;
static readonly TweenCallback EmptyTweenCallback = () => { };
Tween tween;
TweenCancelBehaviour cancelBehaviour;
CancellationToken cancellationToken;
bool canceled;
CancellationTokenRegistration cancellationTokenRegistration;
UniTaskCompletionSourceCore<AsyncUnit> core;
TweenConfiguredSource()
{
}
public static IUniTaskSource Create(Tween tween, TweenCancelBehaviour cancelBehaviour, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
}
var result = pool.TryRent() ?? 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);
}
// allocate delegate.
tween.OnKill(new TweenCallback(OnKill));
}
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
{
TaskTracker.RemoveTracking(this);
core.GetResult(token);
}
finally
{
pool.TryReturn(this);
}
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
public void Reset()
{
core.Reset();
tween = default;
cancellationToken = default;
}
~TweenConfiguredSource()
{
if (pool.TryReturn(this))
{
GC.ReRegisterForFinalize(this);
}
}
}
}
}
#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 // similar as IValueTaskSource
public interface IUniTaskSource public interface IUniTaskSource
#if !UNITY_2018_3_OR_NEWER
: System.Threading.Tasks.Sources.IValueTaskSource
#pragma warning disable CS0108
#endif
{ {
UniTaskStatus GetStatus(short token); UniTaskStatus GetStatus(short token);
void OnCompleted(Action<object> continuation, object state, short token); void OnCompleted(Action<object> continuation, object state, short token);
void GetResult(short token); void GetResult(short token);
UniTaskStatus UnsafeGetStatus(); // only for debug use. 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 public interface IUniTaskSource<out T> : IUniTaskSource
#if !UNITY_2018_3_OR_NEWER
, System.Threading.Tasks.Sources.IValueTaskSource<T>
#endif
{ {
new T GetResult(short token); 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 public static class UniTaskStatusExtensions

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -0,0 +1,221 @@
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>
<#
var tMax = 15;
Func<int, string> typeArgs = x => string.Join(", ", Enumerable.Range(1, x).Select(x => $"T{x}")) + ", TResult";
Func<int, string> paramArgs = x => string.Join(", ", Enumerable.Range(1, x).Select(x => $"IUniTaskAsyncEnumerable<T{x}> source{x}"));
Func<int, string> parameters = x => string.Join(", ", Enumerable.Range(1, x).Select(x => $"source{x}"));
#>
using Cysharp.Threading.Tasks.Internal;
using System;
using System.Threading;
namespace Cysharp.Threading.Tasks.Linq
{
public static partial class UniTaskAsyncEnumerable
{
<# for(var i = 2; i <= tMax; i++) { #>
public static IUniTaskAsyncEnumerable<TResult> CombineLatest<<#= typeArgs(i) #>>(this <#= paramArgs(i) #>, Func<<#= typeArgs(i) #>> resultSelector)
{
<# for(var j = 1; j <= i; j++) { #>
Error.ThrowArgumentNullException(source<#= j #>, nameof(source<#= j #>));
<# } #>
Error.ThrowArgumentNullException(resultSelector, nameof(resultSelector));
return new CombineLatest<<#= typeArgs(i) #>>(<#= parameters(i) #>, resultSelector);
}
<# } #>
}
<# for(var i = 2; i <= tMax; i++) { #>
internal class CombineLatest<<#= typeArgs(i) #>> : IUniTaskAsyncEnumerable<TResult>
{
<# for(var j = 1; j <= i; j++) { #>
readonly IUniTaskAsyncEnumerable<T<#= j #>> source<#= j #>;
<# } #>
readonly Func<<#= typeArgs(i) #>> resultSelector;
public CombineLatest(<#= paramArgs(i) #>, Func<<#= typeArgs(i) #>> resultSelector)
{
<# for(var j = 1; j <= i; j++) { #>
this.source<#= j #> = source<#= j #>;
<# } #>
this.resultSelector = resultSelector;
}
public IUniTaskAsyncEnumerator<TResult> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
return new _CombineLatest(<#= parameters(i) #>, resultSelector, cancellationToken);
}
class _CombineLatest : MoveNextSource, IUniTaskAsyncEnumerator<TResult>
{
<# for(var j = 1; j <= i; j++) { #>
static readonly Action<object> Completed<#= j #>Delegate = Completed<#= j #>;
<# } #>
const int CompleteCount = <#= i #>;
<# for(var j = 1; j <= i; j++) { #>
readonly IUniTaskAsyncEnumerable<T<#= j #>> source<#= j #>;
<# } #>
readonly Func<<#= typeArgs(i) #>> resultSelector;
CancellationToken cancellationToken;
<# for(var j = 1; j <= i; j++) { #>
IUniTaskAsyncEnumerator<T<#= j #>> enumerator<#= j #>;
UniTask<bool>.Awaiter awaiter<#= j #>;
bool hasCurrent<#= j #>;
bool running<#= j #>;
T<#= j #> current<#= j #>;
<# } #>
int completedCount;
bool syncRunning;
TResult result;
public _CombineLatest(<#= paramArgs(i) #>, Func<<#= typeArgs(i) #>> resultSelector, CancellationToken cancellationToken)
{
<# for(var j = 1; j <= i; j++) { #>
this.source<#= j #> = source<#= j #>;
<# } #>
this.resultSelector = resultSelector;
this.cancellationToken = cancellationToken;
TaskTracker.TrackActiveTask(this, 3);
}
public TResult Current => result;
public UniTask<bool> MoveNextAsync()
{
cancellationToken.ThrowIfCancellationRequested();
if (completedCount == CompleteCount) return CompletedTasks.False;
if (enumerator1 == null)
{
<# for(var j = 1; j <= i; j++) { #>
enumerator<#= j #> = source<#= j #>.GetAsyncEnumerator(cancellationToken);
<# } #>
}
completionSource.Reset();
AGAIN:
syncRunning = true;
<# for(var j = 1; j <= i; j++) { #>
if (!running<#= j #>)
{
running<#= j #> = true;
awaiter<#= j #> = enumerator<#= j #>.MoveNextAsync().GetAwaiter();
if (awaiter<#= j #>.IsCompleted)
{
Completed<#= j #>(this);
}
else
{
awaiter<#= j #>.SourceOnCompleted(Completed<#= j #>Delegate, this);
}
}
<# } #>
if (<#= string.Join(" || ", Enumerable.Range(1, i).Select(x => $"!running{x}")) #>)
{
goto AGAIN;
}
syncRunning = false;
return new UniTask<bool>(this, completionSource.Version);
}
<# for(var j = 1; j <= i; j++) { #>
static void Completed<#= j #>(object state)
{
var self = (_CombineLatest)state;
self.running<#= j #> = false;
try
{
if (self.awaiter<#= j #>.GetResult())
{
self.hasCurrent<#= j #> = true;
self.current<#= j #> = self.enumerator<#= j #>.Current;
goto SUCCESS;
}
else
{
self.running<#= j #> = true; // as complete, no more call MoveNextAsync.
if (Interlocked.Increment(ref self.completedCount) == CompleteCount)
{
goto COMPLETE;
}
return;
}
}
catch (Exception ex)
{
self.running<#= j #> = true; // as complete, no more call MoveNextAsync.
self.completedCount = CompleteCount;
self.completionSource.TrySetException(ex);
return;
}
SUCCESS:
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();
}
catch (Exception ex)
{
self.completedCount = CompleteCount;
self.completionSource.TrySetException(ex);
return;
}
self.awaiter<#= j #>.SourceOnCompleted(Completed<#= j #>Delegate, self);
}
return;
COMPLETE:
self.completionSource.TrySetResult(false);
return;
}
<# } #>
bool TrySetResult()
{
if (<#= string.Join(" && ", Enumerable.Range(1, i).Select(x => $"hasCurrent{x}")) #>)
{
result = resultSelector(<#= string.Join(", ", Enumerable.Range(1, i).Select(x => $"current{x}")) #>);
completionSource.TrySetResult(true);
return true;
}
else
{
return false;
}
}
public async UniTask DisposeAsync()
{
TaskTracker.RemoveTracking(this);
<# for(var j = 1; j <= i; j++) { #>
if (enumerator<#= j #> != null)
{
await enumerator<#= j #>.DisposeAsync();
}
<# } #>
}
}
}
<# } #>
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: b1b8cfa9d17af814a971ee2224aaaaa2
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,128 @@
using Cysharp.Threading.Tasks.Internal;
using System;
using System.Threading;
namespace Cysharp.Threading.Tasks.Linq
{
public static partial class UniTaskAsyncEnumerable
{
public static IUniTaskAsyncEnumerable<(TSource, TSource)> Pairwise<TSource>(this IUniTaskAsyncEnumerable<TSource> source)
{
Error.ThrowArgumentNullException(source, nameof(source));
return new Pairwise<TSource>(source);
}
}
internal sealed class Pairwise<TSource> : IUniTaskAsyncEnumerable<(TSource, TSource)>
{
readonly IUniTaskAsyncEnumerable<TSource> source;
public Pairwise(IUniTaskAsyncEnumerable<TSource> source)
{
this.source = source;
}
public IUniTaskAsyncEnumerator<(TSource, TSource)> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
return new _Pairwise(source, cancellationToken);
}
sealed class _Pairwise : MoveNextSource, IUniTaskAsyncEnumerator<(TSource, TSource)>
{
static readonly Action<object> MoveNextCoreDelegate = MoveNextCore;
readonly IUniTaskAsyncEnumerable<TSource> source;
CancellationToken cancellationToken;
IUniTaskAsyncEnumerator<TSource> enumerator;
UniTask<bool>.Awaiter awaiter;
TSource prev;
bool isFirst;
public _Pairwise(IUniTaskAsyncEnumerable<TSource> source, CancellationToken cancellationToken)
{
this.source = source;
this.cancellationToken = cancellationToken;
TaskTracker.TrackActiveTask(this, 3);
}
public (TSource, TSource) Current { get; private set; }
public UniTask<bool> MoveNextAsync()
{
cancellationToken.ThrowIfCancellationRequested();
if (enumerator == null)
{
isFirst = true;
enumerator = source.GetAsyncEnumerator(cancellationToken);
}
completionSource.Reset();
SourceMoveNext();
return new UniTask<bool>(this, completionSource.Version);
}
void SourceMoveNext()
{
try
{
awaiter = enumerator.MoveNextAsync().GetAwaiter();
if (awaiter.IsCompleted)
{
MoveNextCore(this);
}
else
{
awaiter.SourceOnCompleted(MoveNextCoreDelegate, this);
}
}
catch (Exception ex)
{
completionSource.TrySetException(ex);
}
}
static void MoveNextCore(object state)
{
var self = (_Pairwise)state;
if (self.TryGetResult(self.awaiter, out var result))
{
if (result)
{
if (self.isFirst)
{
self.isFirst = false;
self.prev = self.enumerator.Current;
self.SourceMoveNext(); // run again. okay to use recursive(only one more).
}
else
{
var p = self.prev;
self.prev = self.enumerator.Current;
self.Current = (p, self.prev);
self.completionSource.TrySetResult(true);
}
}
else
{
self.completionSource.TrySetResult(false);
}
}
}
public UniTask DisposeAsync()
{
TaskTracker.RemoveTracking(this);
if (enumerator != null)
{
return enumerator.DisposeAsync();
}
return default;
}
}
}
}

View File

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

View File

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

View File

@@ -105,34 +105,67 @@ namespace Cysharp.Threading.Tasks
/// helper of create add UniTaskVoid to delegate. /// helper of create add UniTaskVoid to delegate.
/// For example: FooEvent += () => UniTask.Void(async () => { /* */ }) /// For example: FooEvent += () => UniTask.Void(async () => { /* */ })
/// </summary> /// </summary>
public static void Void(Func<UniTask> asyncAction) public static void Void(Func<UniTaskVoid> asyncAction)
{ {
asyncAction().Forget(); 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> /// <summary>
/// helper of create add UniTaskVoid to delegate. /// helper of create add UniTaskVoid to delegate.
/// For example: FooEvent += (sender, e) => UniTask.Void(async arg => { /* */ }, (sender, e)) /// For example: FooEvent += (sender, e) => UniTask.Void(async arg => { /* */ }, (sender, e))
/// </summary> /// </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(); 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> /// <summary>
/// Defer the task creation just before call await. /// Defer the task creation just before call await.
/// </summary> /// </summary>

View File

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

View File

@@ -1,13 +1,12 @@
#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
#pragma warning disable CS0436 #pragma warning disable CS0436
using Cysharp.Threading.Tasks.CompilerServices;
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices; using System.Runtime.ExceptionServices;
using Cysharp.Threading.Tasks.CompilerServices; using System.Runtime.InteropServices;
using Cysharp.Threading.Tasks.Internal;
namespace Cysharp.Threading.Tasks namespace Cysharp.Threading.Tasks
{ {
@@ -26,6 +25,7 @@ namespace Cysharp.Threading.Tasks
/// Lightweight unity specified task-like object. /// Lightweight unity specified task-like object.
/// </summary> /// </summary>
[AsyncMethodBuilder(typeof(AsyncUniTaskMethodBuilder))] [AsyncMethodBuilder(typeof(AsyncUniTaskMethodBuilder))]
[StructLayout(LayoutKind.Auto)]
public readonly partial struct UniTask public readonly partial struct UniTask
{ {
readonly IUniTaskSource source; readonly IUniTaskSource source;
@@ -68,6 +68,20 @@ namespace Cysharp.Threading.Tasks
return new UniTask<bool>(new IsCanceledSource(source), token); 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() public override string ToString()
{ {
if (source == null) return "()"; if (source == null) return "()";
@@ -339,6 +353,7 @@ namespace Cysharp.Threading.Tasks
/// Lightweight unity specified task-like object. /// Lightweight unity specified task-like object.
/// </summary> /// </summary>
[AsyncMethodBuilder(typeof(AsyncUniTaskMethodBuilder<>))] [AsyncMethodBuilder(typeof(AsyncUniTaskMethodBuilder<>))]
[StructLayout(LayoutKind.Auto)]
public readonly struct UniTask<T> public readonly struct UniTask<T>
{ {
readonly IUniTaskSource<T> source; readonly IUniTaskSource<T> source;
@@ -414,6 +429,20 @@ namespace Cysharp.Threading.Tasks
return self.AsUniTask(); 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> /// <summary>
/// returns (bool IsCanceled, T Result) instead of throws OperationCanceledException. /// returns (bool IsCanceled, T Result) instead of throws OperationCanceledException.
/// </summary> /// </summary>

View File

@@ -123,7 +123,14 @@ namespace Cysharp.Threading.Tasks
{ {
// setup result // setup result
this.hasUnhandledError = true; 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) if (continuation != null || Interlocked.CompareExchange(ref this.continuation, UniTaskCompletionSourceCoreShared.s_sentinel, null) != null)
{ {
@@ -264,7 +271,7 @@ namespace Cysharp.Threading.Tasks
{ {
if (token != version) 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.");
} }
} }
} }

View File

@@ -559,36 +559,6 @@ namespace Cysharp.Threading.Tasks
return await continuationFunction(); 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) public static async UniTask<T> Unwrap<T>(this UniTask<UniTask<T>> task)
{ {
return await await task; return await await task;

View File

@@ -9,7 +9,7 @@ namespace Cysharp.Threading.Tasks
{ {
public static class UniTaskObservableExtensions 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 promise = new UniTaskCompletionSource<T>();
var disposable = new SingleAssignmentDisposable(); var disposable = new SingleAssignmentDisposable();

View File

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

View File

@@ -9,10 +9,11 @@ namespace Cysharp.Threading.Tasks
{ {
public static partial class UnityAsyncExtensions 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); await UniTask.Yield(waitTiming);
jobHandle.Complete(); jobHandle.Complete();
cancellationToken.ThrowIfCancellationRequested(); // call cancel after Complete.
} }
public static UniTask.Awaiter GetAwaiter(this JobHandle jobHandle) public static UniTask.Awaiter GetAwaiter(this JobHandle jobHandle)
@@ -29,21 +30,9 @@ namespace Cysharp.Threading.Tasks
return new UniTask(handler, token).GetAwaiter(); return new UniTask(handler, token).GetAwaiter();
} }
public static UniTask ToUniTask(this JobHandle jobHandle) // can not pass CancellationToken because can't handle JobHandle's Complete and NativeArray.Dispose.
{
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);
}
return new UniTask(handler, token); public static UniTask ToUniTask(this JobHandle jobHandle, PlayerLoopTiming waitTiming)
}
public static UniTask ConfigureAwait(this JobHandle jobHandle, PlayerLoopTiming waitTiming)
{ {
var handler = JobHandlePromise.Create(jobHandle, out var token); var handler = JobHandlePromise.Create(jobHandle, out var token);
{ {

View File

@@ -13,7 +13,7 @@ namespace Cysharp.Threading.Tasks
{ {
public static partial class UnityAsyncExtensions public static partial class UnityAsyncExtensions
{ {
#region AsyncOperation #region AsyncOperation
public static AsyncOperationAwaiter GetAwaiter(this AsyncOperation asyncOperation) public static AsyncOperationAwaiter GetAwaiter(this AsyncOperation asyncOperation)
{ {
@@ -21,18 +21,18 @@ namespace Cysharp.Threading.Tasks
return new AsyncOperationAwaiter(asyncOperation); 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)); Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
if (asyncOperation.isDone) return UniTask.CompletedTask;
return new UniTask(AsyncOperationConfiguredSource.Create(asyncOperation, PlayerLoopTiming.Update, null, CancellationToken.None, out var token), token); 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)); Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
if (asyncOperation.isDone) return UniTask.CompletedTask;
return new UniTask(AsyncOperationConfiguredSource.Create(asyncOperation, timing, progress, cancellation, out var token), token); return new UniTask(AsyncOperationConfiguredSource.Create(asyncOperation, timing, progress, cancellationToken, out var token), token);
} }
public struct AsyncOperationAwaiter : ICriticalNotifyCompletion public struct AsyncOperationAwaiter : ICriticalNotifyCompletion
@@ -116,7 +116,7 @@ namespace Cysharp.Threading.Tasks
try try
{ {
TaskTracker.RemoveTracking(this); TaskTracker.RemoveTracking(this);
core.GetResult(token); core.GetResult(token);
} }
finally finally
@@ -125,7 +125,6 @@ namespace Cysharp.Threading.Tasks
} }
} }
public UniTaskStatus GetStatus(short token) public UniTaskStatus GetStatus(short token)
{ {
return core.GetStatus(token); return core.GetStatus(token);
@@ -180,9 +179,9 @@ namespace Cysharp.Threading.Tasks
} }
} }
# endregion #endregion
#region ResourceRequest #region ResourceRequest
public static ResourceRequestAwaiter GetAwaiter(this ResourceRequest asyncOperation) public static ResourceRequestAwaiter GetAwaiter(this ResourceRequest asyncOperation)
{ {
@@ -190,18 +189,18 @@ namespace Cysharp.Threading.Tasks
return new ResourceRequestAwaiter(asyncOperation); 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)); Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.asset);
return new UniTask<UnityEngine.Object>(ResourceRequestConfiguredSource.Create(asyncOperation, PlayerLoopTiming.Update, null, CancellationToken.None, out var token), token); 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)); Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.asset);
return new UniTask<UnityEngine.Object>(ResourceRequestConfiguredSource.Create(asyncOperation, timing, progress, cancellation, out var token), token); return new UniTask<UnityEngine.Object>(ResourceRequestConfiguredSource.Create(asyncOperation, timing, progress, cancellationToken, out var token), token);
} }
public struct ResourceRequestAwaiter : ICriticalNotifyCompletion public struct ResourceRequestAwaiter : ICriticalNotifyCompletion
@@ -289,7 +288,7 @@ namespace Cysharp.Threading.Tasks
try try
{ {
TaskTracker.RemoveTracking(this); TaskTracker.RemoveTracking(this);
return core.GetResult(token); return core.GetResult(token);
} }
finally finally
@@ -357,9 +356,9 @@ namespace Cysharp.Threading.Tasks
} }
} }
# endregion #endregion
#region AssetBundleRequest #region AssetBundleRequest
public static AssetBundleRequestAwaiter GetAwaiter(this AssetBundleRequest asyncOperation) public static AssetBundleRequestAwaiter GetAwaiter(this AssetBundleRequest asyncOperation)
{ {
@@ -367,18 +366,18 @@ namespace Cysharp.Threading.Tasks
return new AssetBundleRequestAwaiter(asyncOperation); 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)); Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.asset);
return new UniTask<UnityEngine.Object>(AssetBundleRequestConfiguredSource.Create(asyncOperation, PlayerLoopTiming.Update, null, CancellationToken.None, out var token), token); 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)); Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.asset);
return new UniTask<UnityEngine.Object>(AssetBundleRequestConfiguredSource.Create(asyncOperation, timing, progress, cancellation, out var token), token); return new UniTask<UnityEngine.Object>(AssetBundleRequestConfiguredSource.Create(asyncOperation, timing, progress, cancellationToken, out var token), token);
} }
public struct AssetBundleRequestAwaiter : ICriticalNotifyCompletion public struct AssetBundleRequestAwaiter : ICriticalNotifyCompletion
@@ -466,7 +465,7 @@ namespace Cysharp.Threading.Tasks
try try
{ {
TaskTracker.RemoveTracking(this); TaskTracker.RemoveTracking(this);
return core.GetResult(token); return core.GetResult(token);
} }
finally finally
@@ -534,9 +533,9 @@ namespace Cysharp.Threading.Tasks
} }
} }
# endregion #endregion
#region AssetBundleCreateRequest #region AssetBundleCreateRequest
public static AssetBundleCreateRequestAwaiter GetAwaiter(this AssetBundleCreateRequest asyncOperation) public static AssetBundleCreateRequestAwaiter GetAwaiter(this AssetBundleCreateRequest asyncOperation)
{ {
@@ -544,18 +543,18 @@ namespace Cysharp.Threading.Tasks
return new AssetBundleCreateRequestAwaiter(asyncOperation); 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)); Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.assetBundle);
return new UniTask<AssetBundle>(AssetBundleCreateRequestConfiguredSource.Create(asyncOperation, PlayerLoopTiming.Update, null, CancellationToken.None, out var token), token); 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)); Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.assetBundle);
return new UniTask<AssetBundle>(AssetBundleCreateRequestConfiguredSource.Create(asyncOperation, timing, progress, cancellation, out var token), token); return new UniTask<AssetBundle>(AssetBundleCreateRequestConfiguredSource.Create(asyncOperation, timing, progress, cancellationToken, out var token), token);
} }
public struct AssetBundleCreateRequestAwaiter : ICriticalNotifyCompletion public struct AssetBundleCreateRequestAwaiter : ICriticalNotifyCompletion
@@ -643,7 +642,7 @@ namespace Cysharp.Threading.Tasks
try try
{ {
TaskTracker.RemoveTracking(this); TaskTracker.RemoveTracking(this);
return core.GetResult(token); return core.GetResult(token);
} }
finally finally
@@ -711,10 +710,10 @@ namespace Cysharp.Threading.Tasks
} }
} }
# endregion #endregion
#if ENABLE_UNITYWEBREQUEST #if ENABLE_UNITYWEBREQUEST
#region UnityWebRequestAsyncOperation #region UnityWebRequestAsyncOperation
public static UnityWebRequestAsyncOperationAwaiter GetAwaiter(this UnityWebRequestAsyncOperation asyncOperation) public static UnityWebRequestAsyncOperationAwaiter GetAwaiter(this UnityWebRequestAsyncOperation asyncOperation)
{ {
@@ -722,18 +721,18 @@ namespace Cysharp.Threading.Tasks
return new UnityWebRequestAsyncOperationAwaiter(asyncOperation); 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)); Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.webRequest);
return new UniTask<UnityWebRequest>(UnityWebRequestAsyncOperationConfiguredSource.Create(asyncOperation, PlayerLoopTiming.Update, null, CancellationToken.None, out var token), token); 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)); Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.webRequest);
return new UniTask<UnityWebRequest>(UnityWebRequestAsyncOperationConfiguredSource.Create(asyncOperation, timing, progress, cancellation, out var token), token); return new UniTask<UnityWebRequest>(UnityWebRequestAsyncOperationConfiguredSource.Create(asyncOperation, timing, progress, cancellationToken, out var token), token);
} }
public struct UnityWebRequestAsyncOperationAwaiter : ICriticalNotifyCompletion public struct UnityWebRequestAsyncOperationAwaiter : ICriticalNotifyCompletion
@@ -821,7 +820,7 @@ namespace Cysharp.Threading.Tasks
try try
{ {
TaskTracker.RemoveTracking(this); TaskTracker.RemoveTracking(this);
return core.GetResult(token); return core.GetResult(token);
} }
finally finally
@@ -854,6 +853,7 @@ namespace Cysharp.Threading.Tasks
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
asyncOperation.webRequest.Abort();
core.TrySetCanceled(cancellationToken); core.TrySetCanceled(cancellationToken);
return false; return false;
} }
@@ -889,7 +889,7 @@ namespace Cysharp.Threading.Tasks
} }
} }
# endregion #endregion
#endif #endif
} }

View File

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

View File

@@ -10,8 +10,12 @@ using System.Threading.Tasks;
using Unity.Collections; using Unity.Collections;
using Unity.Jobs; using Unity.Jobs;
using UnityEngine; using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI; using UnityEngine.UI;
// using DG.Tweening;
public struct MyJob : IJob public struct MyJob : IJob
{ {
public int loopCount; public int loopCount;
@@ -142,8 +146,6 @@ public class SandboxMain : MonoBehaviour
{ {
// State<int> Hp { get; } // State<int> Hp { get; }
AsyncReactiveProperty<int> hp;
IReadOnlyAsyncReactiveProperty<int> Hp => hp;
@@ -172,23 +174,10 @@ public class SandboxMain : MonoBehaviour
public Text text; public Text text;
public Button button; public Button button;
[SerializeField]
State<int> count;
void Start2() void Start2()
{ {
count = 10;
var countS = count.GetSetter();
count.BindTo(text);
button.OnClickAsAsyncEnumerable().ForEachAsync(_ =>
{
// int foo = countS;
//countS.Set(countS += 10);
// setter.SetValue(count.Value + 10);
});
} }
@@ -204,15 +193,20 @@ public class SandboxMain : MonoBehaviour
async UniTask RunJobAsync() async UniTask RunJobAsync()
{ {
var job = new MyJob() { loopCount = 999, inOut = new NativeArray<int>(1, Allocator.TempJob) }; 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"); UnityEngine.Debug.Log("OK");
await scheduled; // .ConfigureAwait(PlayerLoopTiming.Update); // .WaitAsync(PlayerLoopTiming.Update); await scheduled; // .ConfigureAwait(PlayerLoopTiming.Update); // .WaitAsync(PlayerLoopTiming.Update);
UnityEngine.Debug.Log("OK2"); UnityEngine.Debug.Log("OK2");
}
job.inOut.Dispose(); finally
{
job.inOut.Dispose();
}
} }
@@ -255,16 +249,50 @@ public class SandboxMain : MonoBehaviour
public int MyProperty { get; set; } public int MyProperty { get; set; }
} }
MyClass mcc; async Task Test1()
{
var r = await TcsAsync("https://bing.com/");
Debug.Log("TASKASYNC");
}
async UniTaskVoid Test2()
{
try
{
var cts = new CancellationTokenSource();
var r = UniAsync("https://bing.com/", cts.Token);
cts.Cancel();
await r;
Debug.Log("UNIASYNC");
}
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;
}
void Start() void Start()
{ {
this.mcc = new MyClass();
this.MyProperty = 999;
CheckDest().Forget();
//UniTaskAsyncEnumerable.EveryValueChanged(mcc, x => x.MyProperty) //UniTaskAsyncEnumerable.EveryValueChanged(mcc, x => x.MyProperty)
// .Do(_ => { }, () => Debug.Log("COMPLETED")) // .Do(_ => { }, () => Debug.Log("COMPLETED"))
// .ForEachAsync(x => // .ForEachAsync(x =>
@@ -273,37 +301,56 @@ public class SandboxMain : MonoBehaviour
// }) // })
// .Forget(); // .Forget();
_ = Test1();
Test2().Forget();
StartCoroutine(Test3("https://bing.com/"));
// DG.Tweening.Core.TweenerCore<int>
//okButton.GetComponent<RectTransform>().DOMoveX(10.2f, 30);
// 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(_ => okButton.OnClickAsAsyncEnumerable().ForEachAsync(_ =>
{ {
mcc.MyProperty += 10;
}).Forget(); }).Forget();
cancelButton.OnClickAsAsyncEnumerable().ForEachAsync(_ =>
{
this.mcc = null;
});
okButton.onClick.AddListener(UniTask.UnityAction(async () => await UniTask.Yield()));
} }
async UniTaskVoid CheckDest() async UniTaskVoid CloseAsync(CancellationToken cancellationToken = default)
{ {
try await UniTask.Yield();
{
Debug.Log("WAIT");
await UniTask.WaitUntilValueChanged(mcc, x => x.MyProperty);
Debug.Log("CHANGED?");
}
finally
{
Debug.Log("END");
}
} }
async UniTaskVoid Running(CancellationToken ct) async UniTaskVoid Running(CancellationToken ct)
@@ -538,7 +585,7 @@ public class SandboxMain : MonoBehaviour
public class ShowPlayerLoop public class ShowPlayerLoop
{ {
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] // [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
static void Init() static void Init()
{ {
var playerLoop = UnityEngine.LowLevel.PlayerLoop.GetDefaultPlayerLoop(); var playerLoop = UnityEngine.LowLevel.PlayerLoop.GetDefaultPlayerLoop();

View File

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

View File

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

View File

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

View File

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