Compare commits

..

7 Commits

Author SHA1 Message Date
neuecc
6ec0ed8d61 docs: update TOC 2020-07-29 23:18:59 +00:00
neuecc
2e35324403 ready for 2.0.27 2020-07-30 08:12:32 +09:00
neuecc
e9474649c4 Add AssetBundleRequest.AwaitForAllAssets 2020-07-30 08:12:24 +09:00
neuecc
a8e0ce50c8 Fix Addressables async extensions when autoReleaseHandle: true 2020-07-30 08:12:03 +09:00
neuecc
db7ddba735 Add UniTaskSynchronizationContext, PlayerLoopHelper.IsInjectedUniTaskPlayerLoop, DumpCurrentPlayerLoop 2020-07-30 08:11:07 +09:00
neuecc
1999d94b33 Add UniTask.WithCancellation 2020-07-30 08:10:16 +09:00
neuecc
44af123b6c Update project version to 2019.4.5f1 2020-07-30 07:01:29 +09:00
14 changed files with 943 additions and 323 deletions

40
.gitignore vendored
View File

@@ -213,3 +213,43 @@ src/UniTask/UniTask.Addressables.csproj
src/UniTask/UniTask.DOTween.csproj
src/UniTask/UniTask.TextMeshPro.csproj
src/UniTask/RuntimeUnitTestToolkit.Player.csproj
src/UniTask/TempAsm.Player.csproj
src/UniTask/UniTask.Addressables.Player.csproj
src/UniTask/UniTask.DOTween.Player.csproj
src/UniTask/UniTask.Linq.Player.csproj
src/UniTask/UniTask.Player.csproj
src/UniTask/UniTask.Tests.Player.csproj
src/UniTask/UniTask.TextMeshPro.Player.csproj
src/UniTask/Unity.Addressables.Player.csproj
src/UniTask/Unity.Analytics.DataPrivacy.Player.csproj
src/UniTask/Unity.ResourceManager.Player.csproj
src/UniTask/Unity.ScriptableBuildPipeline.Player.csproj
src/UniTask/Unity.TextMeshPro.Player.csproj
src/UniTask/Unity.Timeline.Player.csproj
src/UniTask/UnityEngine.Advertisements.Player.csproj
src/UniTask/UnityEngine.Monetization.Player.csproj
src/UniTask/UnityEngine.TestRunner.Player.csproj
src/UniTask/UnityEngine.UI.Player.csproj
src/UniTask/DOTween.Modules.Player.csproj
src/UniTask/Assembly-CSharp.Player.csproj

View File

@@ -34,6 +34,7 @@ Techinical details, see blog post: [UniTask v2 — Zero Allocation async/await f
- [For Unit Testing](#for-unit-testing)
- [Compare with Standard Task API](#compare-with-standard-task-api)
- [Pooling Configuration](#pooling-configuration)
- [UniTaskSynchronizationContext](#unitasksynchronizationcontext)
- [API References](#api-references)
- [UPM Package](#upm-package)
- [Install via git URL](#install-via-git-url)
@@ -384,6 +385,16 @@ var playerLoop = ScriptBehaviourUpdateOrder.CurrentPlayerLoop;
PlayerLoopHelper.Initialize(ref playerLoop);
```
You can diagnostic UniTask's player loop is ready by `PlayerLoopHelper.IsInjectedUniTaskPlayerLoop()`. And also `PlayerLoopHelper.DumpCurrentPlayerLoop` shows current all playerloop to console.
```csharp
void Start()
{
UnityEngine.Debug.Log("UniTaskPlayerLoop ready? " + PlayerLoopHelper.IsInjectedUniTaskPlayerLoop());
PlayerLoopHelper.DumpCurrentPlayerLoop();
}
```
async void vs async UniTaskVoid
---
`async void` is a standard C# task system so does not run on UniTask systems. It is better not to use. `async UniTaskVoid` is a lightweight version of `async UniTask` because it does not have awaitable completion and report error immediately to `UniTaskScheduler.UnobservedTaskException`. If you don't require to await it(fire and forget), use `UniTaskVoid` is better. Unfortunately to dismiss warning, require to using with `Forget()`.
@@ -826,6 +837,23 @@ foreach (var (type, size) in TaskPool.GetCacheSizeInfo())
> In UnityEditor profiler shows allocation of compiler generated AsyncStateMachine but it only occurs in debug(development) build. C# Compiler generate AsyncStateMachine as class on Debug build and as struct on Release build.
UniTaskSynchronizationContext
---
Unity's default SynchronizationContext(`UnitySynchronizationContext`) is poor implementation for performance. UniTask itself is bypass `SynchronizationContext`(and `ExecutionContext`) so does not use it but if exists in `async Task`, still used it. `UniTaskSynchronizationContext` is replacement of `UnitySynchronizationContext`, it is better for performance.
```csharp
public class SyncContextInjecter
{
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
public static void Inject()
{
SynchronizationContext.SetSynchronizationContext(new UniTaskSynchronizationContext());
}
}
```
This is an optional choice and is not always recommended; `UniTaskSynchronizationContext` is less performance than `async UniTask` and is not a complete UniTask replacement. It also does not guarantee full behavioral compatibility with the `UnitySynchronizationContext`.
API References
---
UniTask's API References is hosted at [cysharp.github.io/UniTask](https://cysharp.github.io/UniTask/api/Cysharp.Threading.Tasks.html) by [DocFX](https://dotnet.github.io/docfx/) and [Cysharp/DocfXTemplate](https://github.com/Cysharp/DocfxTemplate).

View File

@@ -38,6 +38,7 @@
..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\ContinuationQueue.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\UnityWebRequestExtensions.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\UniTaskSynchronizationContext.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\CancellationTokenSourceExtensions.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\EnumeratorAsyncExtensions.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\PlayerLoopHelper.cs;

View File

@@ -0,0 +1,40 @@
using Cysharp.Threading.Tasks;
using FluentAssertions;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
namespace NetCoreTests
{
public class WithCancellationTest
{
[Fact]
public async Task Standard()
{
CancellationTokenSource cts = new CancellationTokenSource();
var v = await UniTask.Run(() => 10).WithCancellation(cts.Token);
v.Should().Be(10);
}
[Fact]
public async Task Cancel()
{
CancellationTokenSource cts = new CancellationTokenSource();
var t = UniTask.Create(async () =>
{
await Task.Delay(TimeSpan.FromSeconds(1));
return 10;
}).WithCancellation(cts.Token);
cts.Cancel();
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await t)).CancellationToken.Should().Be(cts.Token);
}
}
}

View File

@@ -15,29 +15,25 @@ namespace Cysharp.Threading.Tasks
{
#region AsyncOperationHandle
public static AsyncOperationHandleAwaiter GetAwaiter(this AsyncOperationHandle handle)
public static UniTask.Awaiter GetAwaiter(this AsyncOperationHandle handle)
{
return new AsyncOperationHandleAwaiter(handle);
return ToUniTask(handle).GetAwaiter();
}
public static UniTask WithCancellation(this AsyncOperationHandle handle, CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled(cancellationToken);
if (handle.IsDone)
{
if (handle.Status == AsyncOperationStatus.Failed)
{
return UniTask.FromException(handle.OperationException);
}
return UniTask.CompletedTask;
}
return new UniTask(AsyncOperationHandleWithCancellationSource.Create(handle, cancellationToken, out var token), token);
return ToUniTask(handle, cancellationToken: cancellationToken);
}
public static UniTask ToUniTask(this AsyncOperationHandle handle, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
{
if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled(cancellationToken);
if (!handle.IsValid())
{
throw new Exception("Attempting to use an invalid operation handle");
}
if (handle.IsDone)
{
if (handle.Status == AsyncOperationStatus.Failed)
@@ -95,29 +91,30 @@ namespace Cysharp.Threading.Tasks
}
}
sealed class AsyncOperationHandleWithCancellationSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleWithCancellationSource>
sealed class AsyncOperationHandleConfiguredSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleConfiguredSource>
{
static TaskPool<AsyncOperationHandleWithCancellationSource> pool;
public AsyncOperationHandleWithCancellationSource NextNode { get; set; }
static TaskPool<AsyncOperationHandleConfiguredSource> pool;
public AsyncOperationHandleConfiguredSource NextNode { get; set; }
static AsyncOperationHandleWithCancellationSource()
static AsyncOperationHandleConfiguredSource()
{
TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleWithCancellationSource), () => pool.Size);
TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleConfiguredSource), () => pool.Size);
}
readonly Action<AsyncOperationHandle> continuationAction;
AsyncOperationHandle handle;
CancellationToken cancellationToken;
IProgress<float> progress;
bool completed;
UniTaskCompletionSourceCore<AsyncUnit> core;
AsyncOperationHandleWithCancellationSource()
AsyncOperationHandleConfiguredSource()
{
continuationAction = Continuation;
}
public static IUniTaskSource Create(AsyncOperationHandle handle, CancellationToken cancellationToken, out short token)
public static IUniTaskSource Create(AsyncOperationHandle handle, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
@@ -126,16 +123,17 @@ namespace Cysharp.Threading.Tasks
if (!pool.TryPop(out var result))
{
result = new AsyncOperationHandleWithCancellationSource();
result = new AsyncOperationHandleConfiguredSource();
}
result.handle = handle;
result.progress = progress;
result.cancellationToken = cancellationToken;
result.completed = false;
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, result);
PlayerLoopHelper.AddAction(timing, result);
handle.Completed += result.continuationAction;
@@ -204,125 +202,18 @@ namespace Cysharp.Threading.Tasks
return false;
}
return true;
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
handle = default;
cancellationToken = default;
return pool.TryPush(this);
}
}
sealed class AsyncOperationHandleConfiguredSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleConfiguredSource>
{
static TaskPool<AsyncOperationHandleConfiguredSource> pool;
public AsyncOperationHandleConfiguredSource NextNode { get; set; }
static AsyncOperationHandleConfiguredSource()
{
TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleConfiguredSource), () => pool.Size);
}
AsyncOperationHandle handle;
IProgress<float> progress;
CancellationToken cancellationToken;
UniTaskCompletionSourceCore<AsyncUnit> core;
AsyncOperationHandleConfiguredSource()
{
}
public static IUniTaskSource Create(AsyncOperationHandle handle, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new AsyncOperationHandleConfiguredSource();
}
result.handle = handle;
result.progress = progress;
result.cancellationToken = cancellationToken;
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(timing, result);
token = result.core.Version;
return result;
}
public void GetResult(short token)
{
try
{
core.GetResult(token);
}
finally
{
TryReturn();
}
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
public bool MoveNext()
{
if (cancellationToken.IsCancellationRequested)
{
core.TrySetCanceled(cancellationToken);
return false;
}
if (progress != null)
if (progress != null && handle.IsValid())
{
progress.Report(handle.PercentComplete);
}
if (handle.IsDone)
{
if (handle.Status == AsyncOperationStatus.Failed)
{
core.TrySetException(handle.OperationException);
}
else
{
core.TrySetResult(AsyncUnit.Default);
}
return false;
}
return true;
}
bool TryReturn()
{
core.Reset();
TaskTracker.RemoveTracking(this);
core.Reset();
handle = default;
progress = default;
cancellationToken = default;
@@ -334,28 +225,25 @@ namespace Cysharp.Threading.Tasks
#region AsyncOperationHandle_T
public static AsyncOperationHandleAwaiter<T> GetAwaiter<T>(this AsyncOperationHandle<T> handle)
public static UniTask<T>.Awaiter GetAwaiter<T>(this AsyncOperationHandle<T> handle)
{
return new AsyncOperationHandleAwaiter<T>(handle);
return ToUniTask(handle).GetAwaiter();
}
public static UniTask<T> WithCancellation<T>(this AsyncOperationHandle<T> handle, CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled<T>(cancellationToken);
if (handle.IsDone)
{
if (handle.Status == AsyncOperationStatus.Failed)
{
return UniTask.FromException<T>(handle.OperationException);
}
return UniTask.FromResult(handle.Result);
}
return new UniTask<T>(AsyncOperationHandleWithCancellationSource<T>.Create(handle, cancellationToken, out var token), token);
return ToUniTask(handle, cancellationToken: cancellationToken);
}
public static UniTask<T> ToUniTask<T>(this AsyncOperationHandle<T> handle, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
{
if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled<T>(cancellationToken);
if (!handle.IsValid())
{
throw new Exception("Attempting to use an invalid operation handle");
}
if (handle.IsDone)
{
if (handle.Status == AsyncOperationStatus.Failed)
@@ -368,75 +256,30 @@ namespace Cysharp.Threading.Tasks
return new UniTask<T>(AsyncOperationHandleConfiguredSource<T>.Create(handle, timing, progress, cancellationToken, out var token), token);
}
public struct AsyncOperationHandleAwaiter<T> : ICriticalNotifyCompletion
sealed class AsyncOperationHandleConfiguredSource<T> : IUniTaskSource<T>, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleConfiguredSource<T>>
{
AsyncOperationHandle<T> handle;
Action<AsyncOperationHandle> continuationAction;
static TaskPool<AsyncOperationHandleConfiguredSource<T>> pool;
public AsyncOperationHandleConfiguredSource<T> NextNode { get; set; }
public AsyncOperationHandleAwaiter(AsyncOperationHandle<T> handle)
static AsyncOperationHandleConfiguredSource()
{
this.handle = handle;
this.continuationAction = null;
}
public bool IsCompleted => handle.IsDone;
public T GetResult()
{
if (continuationAction != null)
{
handle.CompletedTypeless -= continuationAction;
continuationAction = null;
}
if (handle.Status == AsyncOperationStatus.Failed)
{
var e = handle.OperationException;
handle = default;
ExceptionDispatchInfo.Capture(e).Throw();
}
var result = handle.Result;
handle = default;
return result;
}
public void OnCompleted(Action continuation)
{
UnsafeOnCompleted(continuation);
}
public void UnsafeOnCompleted(Action continuation)
{
Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction);
continuationAction = PooledDelegate<AsyncOperationHandle>.Create(continuation);
handle.CompletedTypeless += continuationAction;
}
}
sealed class AsyncOperationHandleWithCancellationSource<T> : IUniTaskSource<T>, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleWithCancellationSource<T>>
{
static TaskPool<AsyncOperationHandleWithCancellationSource<T>> pool;
public AsyncOperationHandleWithCancellationSource<T> NextNode { get; set; }
static AsyncOperationHandleWithCancellationSource()
{
TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleWithCancellationSource<T>), () => pool.Size);
TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleConfiguredSource<T>), () => pool.Size);
}
readonly Action<AsyncOperationHandle<T>> continuationAction;
AsyncOperationHandle<T> handle;
CancellationToken cancellationToken;
IProgress<float> progress;
bool completed;
UniTaskCompletionSourceCore<T> core;
AsyncOperationHandleWithCancellationSource()
AsyncOperationHandleConfiguredSource()
{
continuationAction = Continuation;
}
public static IUniTaskSource<T> Create(AsyncOperationHandle<T> handle, CancellationToken cancellationToken, out short token)
public static IUniTaskSource<T> Create(AsyncOperationHandle<T> handle, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
@@ -445,16 +288,17 @@ namespace Cysharp.Threading.Tasks
if (!pool.TryPop(out var result))
{
result = new AsyncOperationHandleWithCancellationSource<T>();
result = new AsyncOperationHandleConfiguredSource<T>();
}
result.handle = handle;
result.cancellationToken = cancellationToken;
result.completed = false;
result.progress = progress;
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, result);
PlayerLoopHelper.AddAction(timing, result);
handle.Completed += result.continuationAction;
@@ -462,7 +306,7 @@ namespace Cysharp.Threading.Tasks
return result;
}
void Continuation(AsyncOperationHandle<T> _)
void Continuation(AsyncOperationHandle<T> argHandle)
{
handle.Completed -= continuationAction;
@@ -477,13 +321,13 @@ namespace Cysharp.Threading.Tasks
{
core.TrySetCanceled(cancellationToken);
}
else if (handle.Status == AsyncOperationStatus.Failed)
else if (argHandle.Status == AsyncOperationStatus.Failed)
{
core.TrySetException(handle.OperationException);
core.TrySetException(argHandle.OperationException);
}
else
{
core.TrySetResult(handle.Result);
core.TrySetResult(argHandle.Result);
}
}
}
@@ -528,122 +372,11 @@ namespace Cysharp.Threading.Tasks
return false;
}
return true;
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
handle = default;
cancellationToken = default;
return pool.TryPush(this);
}
}
sealed class AsyncOperationHandleConfiguredSource<T> : IUniTaskSource<T>, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleConfiguredSource<T>>
{
static TaskPool<AsyncOperationHandleConfiguredSource<T>> pool;
public AsyncOperationHandleConfiguredSource<T> NextNode { get; set; }
static AsyncOperationHandleConfiguredSource()
{
TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleConfiguredSource<T>), () => pool.Size);
}
AsyncOperationHandle<T> handle;
IProgress<float> progress;
CancellationToken cancellationToken;
UniTaskCompletionSourceCore<T> core;
AsyncOperationHandleConfiguredSource()
{
}
public static IUniTaskSource<T> Create(AsyncOperationHandle<T> handle, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource<T>.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new AsyncOperationHandleConfiguredSource<T>();
}
result.handle = handle;
result.progress = progress;
result.cancellationToken = cancellationToken;
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(timing, result);
token = result.core.Version;
return result;
}
public T GetResult(short token)
{
try
{
return core.GetResult(token);
}
finally
{
TryReturn();
}
}
void IUniTaskSource.GetResult(short token)
{
GetResult(token);
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
public bool MoveNext()
{
if (cancellationToken.IsCancellationRequested)
{
core.TrySetCanceled(cancellationToken);
return false;
}
if (progress != null)
if (progress != null && handle.IsValid())
{
progress.Report(handle.PercentComplete);
}
if (handle.IsDone)
{
if (handle.Status == AsyncOperationStatus.Failed)
{
core.TrySetException(handle.OperationException);
}
else
{
core.TrySetResult(handle.Result);
}
return false;
}
return true;
}

View File

@@ -175,6 +175,32 @@ namespace Cysharp.Threading.Tasks
return dest;
}
static PlayerLoopSystem[] InsertUniTaskSynchronizationContext(PlayerLoopSystem loopSystem)
{
var loop = new PlayerLoopSystem
{
type = typeof(UniTaskSynchronizationContext),
updateDelegate = UniTaskSynchronizationContext.Run
};
// Remove items from previous initializations.
var source = loopSystem.subSystemList
.Where(ls => ls.type != typeof(UniTaskSynchronizationContext))
.ToArray();
var dest = new System.Collections.Generic.List<PlayerLoopSystem>(source);
var index = dest.FindIndex(x => x.type.Name == "ScriptRunDelayedTasks");
if (index == -1)
{
index = dest.FindIndex(x => x.type.Name == "UniTaskLoopRunnerUpdate");
}
dest.Insert(index + 1, loop);
return dest.ToArray();
}
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
static void Init()
{
@@ -246,6 +272,8 @@ namespace Cysharp.Threading.Tasks
if (item != null) item.Run();
}
}
UniTaskSynchronizationContext.Run();
}
#endif
@@ -293,6 +321,9 @@ namespace Cysharp.Threading.Tasks
typeof(UniTaskLoopRunners.UniTaskLoopRunnerPostLateUpdate), runners[12] = new PlayerLoopRunner(PlayerLoopTiming.PostLateUpdate),
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastPostLateUpdate), runners[13] = new PlayerLoopRunner(PlayerLoopTiming.LastPostLateUpdate));
// Insert UniTaskSynchronizationContext to Update loop
copyList[4].subSystemList = InsertUniTaskSynchronizationContext(copyList[4]);
playerLoop.subSystemList = copyList;
PlayerLoop.SetPlayerLoop(playerLoop);
}
@@ -306,6 +337,56 @@ namespace Cysharp.Threading.Tasks
{
yielders[(int)timing].Enqueue(continuation);
}
// Diagnostics helper
#if UNITY_2019_3_OR_NEWER
public static void DumpCurrentPlayerLoop()
{
var playerLoop = UnityEngine.LowLevel.PlayerLoop.GetCurrentPlayerLoop();
var sb = new System.Text.StringBuilder();
sb.AppendLine($"PlayerLoop List");
foreach (var header in playerLoop.subSystemList)
{
sb.AppendFormat("------{0}------", header.type.Name);
sb.AppendLine();
foreach (var subSystem in header.subSystemList)
{
sb.AppendFormat("{0}", subSystem.type.Name);
sb.AppendLine();
if (subSystem.subSystemList != null)
{
UnityEngine.Debug.LogWarning("More Subsystem:" + subSystem.subSystemList.Length);
}
}
}
UnityEngine.Debug.Log(sb.ToString());
}
public static bool IsInjectedUniTaskPlayerLoop()
{
var playerLoop = UnityEngine.LowLevel.PlayerLoop.GetCurrentPlayerLoop();
foreach (var header in playerLoop.subSystemList)
{
foreach (var subSystem in header.subSystemList)
{
if (subSystem.type == typeof(UniTaskLoopRunners.UniTaskLoopRunnerInitialization))
{
return true;
}
}
}
return false;
}
#endif
}
}

View File

@@ -189,6 +189,174 @@ namespace Cysharp.Threading.Tasks
return new AsyncLazy<T>(task);
}
/// <summary>
/// Ignore task result when cancel raised first.
/// </summary>
public static UniTask WithCancellation(this UniTask task, CancellationToken cancellationToken)
{
if (!cancellationToken.CanBeCanceled)
{
return task;
}
if (cancellationToken.IsCancellationRequested)
{
return UniTask.FromCanceled(cancellationToken);
}
if (task.Status.IsCompleted())
{
return task;
}
return new UniTask(new WithCancellationSource(task, cancellationToken), 0);
}
/// <summary>
/// Ignore task result when cancel raised first.
/// </summary>
public static UniTask<T> WithCancellation<T>(this UniTask<T> task, CancellationToken cancellationToken)
{
if (!cancellationToken.CanBeCanceled)
{
return task;
}
if (cancellationToken.IsCancellationRequested)
{
return UniTask.FromCanceled<T>(cancellationToken);
}
if (task.Status.IsCompleted())
{
return task;
}
return new UniTask<T>(new WithCancellationSource<T>(task, cancellationToken), 0);
}
sealed class WithCancellationSource : IUniTaskSource
{
static readonly Action<object> cancellationCallbackDelegate = CancellationCallback;
CancellationToken cancellationToken;
CancellationTokenRegistration tokenRegistration;
UniTaskCompletionSourceCore<AsyncUnit> core;
public WithCancellationSource(UniTask task, CancellationToken cancellationToken)
{
this.cancellationToken = cancellationToken;
this.tokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallbackDelegate, this);
RunTask(task).Forget();
}
async UniTaskVoid RunTask(UniTask task)
{
try
{
await task;
core.TrySetResult(AsyncUnit.Default);
}
catch (Exception ex)
{
core.TrySetException(ex);
}
finally
{
tokenRegistration.Dispose();
}
}
static void CancellationCallback(object state)
{
var self = (WithCancellationSource)state;
self.core.TrySetCanceled(self.cancellationToken);
}
public void GetResult(short token)
{
core.GetResult(token);
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
}
sealed class WithCancellationSource<T> : IUniTaskSource<T>
{
static readonly Action<object> cancellationCallbackDelegate = CancellationCallback;
CancellationToken cancellationToken;
CancellationTokenRegistration tokenRegistration;
UniTaskCompletionSourceCore<T> core;
public WithCancellationSource(UniTask<T> task, CancellationToken cancellationToken)
{
this.cancellationToken = cancellationToken;
this.tokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallbackDelegate, this);
RunTask(task).Forget();
}
async UniTaskVoid RunTask(UniTask<T> task)
{
try
{
core.TrySetResult(await task);
}
catch (Exception ex)
{
core.TrySetException(ex);
}
finally
{
tokenRegistration.Dispose();
}
}
static void CancellationCallback(object state)
{
var self = (WithCancellationSource<T>)state;
self.core.TrySetCanceled(self.cancellationToken);
}
void IUniTaskSource.GetResult(short token)
{
core.GetResult(token);
}
public T GetResult(short token)
{
return core.GetResult(token);
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
}
#if UNITY_2018_3_OR_NEWER
public static IEnumerator ToCoroutine<T>(this UniTask<T> task, Action<T> resultHandler = null, Action<Exception> exceptionHandler = null)

View File

@@ -0,0 +1,158 @@
using System;
using System.Runtime.InteropServices;
using System.Threading;
namespace Cysharp.Threading.Tasks
{
public class UniTaskSynchronizationContext : SynchronizationContext
{
const int MaxArrayLength = 0X7FEFFFFF;
const int InitialSize = 16;
static SpinLock gate = new SpinLock();
static bool dequing = false;
static int actionListCount = 0;
static Callback[] actionList = new Callback[InitialSize];
static int waitingListCount = 0;
static Callback[] waitingList = new Callback[InitialSize];
static int opCount;
public override void Send(SendOrPostCallback d, object state)
{
d(state);
}
public override void Post(SendOrPostCallback d, object state)
{
bool lockTaken = false;
try
{
gate.Enter(ref lockTaken);
if (dequing)
{
// Ensure Capacity
if (waitingList.Length == waitingListCount)
{
var newLength = waitingListCount * 2;
if ((uint)newLength > MaxArrayLength) newLength = MaxArrayLength;
var newArray = new Callback[newLength];
Array.Copy(waitingList, newArray, waitingListCount);
waitingList = newArray;
}
waitingList[waitingListCount] = new Callback(d, state);
waitingListCount++;
}
else
{
// Ensure Capacity
if (actionList.Length == actionListCount)
{
var newLength = actionListCount * 2;
if ((uint)newLength > MaxArrayLength) newLength = MaxArrayLength;
var newArray = new Callback[newLength];
Array.Copy(actionList, newArray, actionListCount);
actionList = newArray;
}
actionList[actionListCount] = new Callback(d, state);
actionListCount++;
}
}
finally
{
if (lockTaken) gate.Exit(false);
}
}
public override void OperationStarted()
{
Interlocked.Increment(ref opCount);
}
public override void OperationCompleted()
{
Interlocked.Decrement(ref opCount);
}
public override SynchronizationContext CreateCopy()
{
return this;
}
// delegate entrypoint.
internal static void Run()
{
{
bool lockTaken = false;
try
{
gate.Enter(ref lockTaken);
if (actionListCount == 0) return;
dequing = true;
}
finally
{
if (lockTaken) gate.Exit(false);
}
}
for (int i = 0; i < actionListCount; i++)
{
var action = actionList[i];
actionList[i] = default;
action.Invoke();
}
{
bool lockTaken = false;
try
{
gate.Enter(ref lockTaken);
dequing = false;
var swapTempActionList = actionList;
actionListCount = waitingListCount;
actionList = waitingList;
waitingListCount = 0;
waitingList = swapTempActionList;
}
finally
{
if (lockTaken) gate.Exit(false);
}
}
}
[StructLayout(LayoutKind.Auto)]
readonly struct Callback
{
readonly SendOrPostCallback callback;
readonly object state;
public Callback(SendOrPostCallback callback, object state)
{
this.callback = callback;
this.state = state;
}
public void Invoke()
{
try
{
callback(state);
}
catch (Exception ex)
{
UnityEngine.Debug.LogException(ex);
}
}
}
}
}

View File

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

View File

@@ -0,0 +1,315 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
#if UNITY_2018_4 || UNITY_2019_4_OR_NEWER
using Cysharp.Threading.Tasks.Internal;
using System;
using System.Runtime.CompilerServices;
using System.Threading;
using UnityEngine;
namespace Cysharp.Threading.Tasks
{
public static partial class UnityAsyncExtensions
{
public static AssetBundleRequestAllAssetsAwaiter AwaitForAllAssets(this AssetBundleRequest asyncOperation)
{
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
return new AssetBundleRequestAllAssetsAwaiter(asyncOperation);
}
public static UniTask<UnityEngine.Object[]> AwaitForAllAssets(this AssetBundleRequest asyncOperation, CancellationToken cancellationToken)
{
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled<UnityEngine.Object[]>(cancellationToken);
if (asyncOperation.isDone) return UniTask.FromResult<UnityEngine.Object[]>(asyncOperation.allAssets);
return new UniTask<UnityEngine.Object[]>(AssetBundleRequestAllAssetsWithCancellationSource.Create(asyncOperation, cancellationToken, out var token), token);
}
public static UniTask<UnityEngine.Object[]> AwaitForAllAssets(this AssetBundleRequest asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
{
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled<UnityEngine.Object[]>(cancellationToken);
if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.allAssets);
return new UniTask<UnityEngine.Object[]>(AssetBundleRequestAllAssetsConfiguredSource.Create(asyncOperation, timing, progress, cancellationToken, out var token), token);
}
public struct AssetBundleRequestAllAssetsAwaiter : ICriticalNotifyCompletion
{
AssetBundleRequest asyncOperation;
Action<AsyncOperation> continuationAction;
public AssetBundleRequestAllAssetsAwaiter(AssetBundleRequest asyncOperation)
{
this.asyncOperation = asyncOperation;
this.continuationAction = null;
}
public bool IsCompleted => asyncOperation.isDone;
public UnityEngine.Object[] GetResult()
{
if (continuationAction != null)
{
asyncOperation.completed -= continuationAction;
continuationAction = null;
var result = asyncOperation.allAssets;
asyncOperation = null;
return result;
}
else
{
var result = asyncOperation.allAssets;
asyncOperation = null;
return result;
}
}
public void OnCompleted(Action continuation)
{
UnsafeOnCompleted(continuation);
}
public void UnsafeOnCompleted(Action continuation)
{
Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction);
continuationAction = PooledDelegate<AsyncOperation>.Create(continuation);
asyncOperation.completed += continuationAction;
}
}
sealed class AssetBundleRequestAllAssetsWithCancellationSource : IUniTaskSource<UnityEngine.Object[]>, IPlayerLoopItem, ITaskPoolNode<AssetBundleRequestAllAssetsWithCancellationSource>
{
static TaskPool<AssetBundleRequestAllAssetsWithCancellationSource> pool;
public AssetBundleRequestAllAssetsWithCancellationSource NextNode { get; set; }
static AssetBundleRequestAllAssetsWithCancellationSource()
{
TaskPool.RegisterSizeGetter(typeof(AssetBundleRequestAllAssetsWithCancellationSource), () => pool.Size);
}
readonly Action<AsyncOperation> continuationAction;
AssetBundleRequest asyncOperation;
CancellationToken cancellationToken;
bool completed;
UniTaskCompletionSourceCore<UnityEngine.Object[]> core;
AssetBundleRequestAllAssetsWithCancellationSource()
{
continuationAction = Continuation;
}
public static IUniTaskSource<UnityEngine.Object[]> Create(AssetBundleRequest asyncOperation, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource<UnityEngine.Object[]>.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new AssetBundleRequestAllAssetsWithCancellationSource();
}
result.asyncOperation = asyncOperation;
result.cancellationToken = cancellationToken;
result.completed = false;
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, result);
asyncOperation.completed += result.continuationAction;
token = result.core.Version;
return result;
}
void Continuation(AsyncOperation _)
{
asyncOperation.completed -= continuationAction;
if (completed)
{
TryReturn();
}
else
{
completed = true;
if (cancellationToken.IsCancellationRequested)
{
core.TrySetCanceled(cancellationToken);
return;
}
core.TrySetResult(asyncOperation.allAssets);
}
}
public UnityEngine.Object[] GetResult(short token)
{
return core.GetResult(token);
}
void IUniTaskSource.GetResult(short token)
{
GetResult(token);
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
public bool MoveNext()
{
if (completed)
{
TryReturn();
return false;
}
if (cancellationToken.IsCancellationRequested)
{
completed = true;
core.TrySetCanceled(cancellationToken);
return false;
}
return true;
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
asyncOperation = default;
cancellationToken = default;
return pool.TryPush(this);
}
}
sealed class AssetBundleRequestAllAssetsConfiguredSource : IUniTaskSource<UnityEngine.Object[]>, IPlayerLoopItem, ITaskPoolNode<AssetBundleRequestAllAssetsConfiguredSource>
{
static TaskPool<AssetBundleRequestAllAssetsConfiguredSource> pool;
public AssetBundleRequestAllAssetsConfiguredSource NextNode { get; set; }
static AssetBundleRequestAllAssetsConfiguredSource()
{
TaskPool.RegisterSizeGetter(typeof(AssetBundleRequestAllAssetsConfiguredSource), () => pool.Size);
}
AssetBundleRequest asyncOperation;
IProgress<float> progress;
CancellationToken cancellationToken;
UniTaskCompletionSourceCore<UnityEngine.Object[]> core;
AssetBundleRequestAllAssetsConfiguredSource()
{
}
public static IUniTaskSource<UnityEngine.Object[]> Create(AssetBundleRequest asyncOperation, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource<UnityEngine.Object[]>.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new AssetBundleRequestAllAssetsConfiguredSource();
}
result.asyncOperation = asyncOperation;
result.progress = progress;
result.cancellationToken = cancellationToken;
TaskTracker.TrackActiveTask(result, 3);
PlayerLoopHelper.AddAction(timing, result);
token = result.core.Version;
return result;
}
public UnityEngine.Object[] GetResult(short token)
{
try
{
return core.GetResult(token);
}
finally
{
TryReturn();
}
}
void IUniTaskSource.GetResult(short token)
{
GetResult(token);
}
public UniTaskStatus GetStatus(short token)
{
return core.GetStatus(token);
}
public UniTaskStatus UnsafeGetStatus()
{
return core.UnsafeGetStatus();
}
public void OnCompleted(Action<object> continuation, object state, short token)
{
core.OnCompleted(continuation, state, token);
}
public bool MoveNext()
{
if (cancellationToken.IsCancellationRequested)
{
core.TrySetCanceled(cancellationToken);
return false;
}
if (progress != null)
{
progress.Report(asyncOperation.progress);
}
if (asyncOperation.isDone)
{
core.TrySetResult(asyncOperation.allAssets);
return false;
}
return true;
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
asyncOperation = default;
progress = default;
cancellationToken = default;
return pool.TryPush(this);
}
}
}
}
#endif

View File

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

View File

@@ -1,7 +1,7 @@
{
"name": "com.cysharp.unitask",
"displayName": "UniTask",
"version": "2.0.26",
"version": "2.0.27",
"unity": "2018.4",
"description": "Provides an efficient async/await integration to Unity.",
"keywords": [ "async/await", "async", "Task", "UniTask" ],

View File

@@ -133,6 +133,7 @@ public class SandboxMain : MonoBehaviour
UniTaskCompletionSource ucs;
async UniTask<int> FooAsync()
{
// use F10, will crash.
var loop = int.Parse("9");
await UniTask.DelayFrame(loop);
@@ -190,6 +191,9 @@ public class SandboxMain : MonoBehaviour
async UniTask RunStandardDelayAsync()
{
UnityEngine.Debug.Log("DEB");
await UniTask.DelayFrame(30);
@@ -220,6 +224,11 @@ public class SandboxMain : MonoBehaviour
async UniTaskVoid Update2()
{
UnityEngine.Debug.Log("async linq!");
await UniTaskAsyncEnumerable.Range(1, 10)
@@ -455,7 +464,7 @@ public class SandboxMain : MonoBehaviour
private void Awake()
{
PlayerLoopInfo.Inject();
// PlayerLoopInfo.Inject();
PrepareCamera();
}
@@ -473,12 +482,24 @@ public class SandboxMain : MonoBehaviour
});
}
async void RunStandardTaskAsync()
{
Debug.Log("Wait 3 seconds");
await Task.Delay(TimeSpan.FromSeconds(3));
Debug.Log("Current SyncContext:" + SynchronizationContext.Current.GetType().FullName);
}
async UniTaskVoid Start()
{
var url = "http://google.com/404";
var webRequestAsyncOperation = UnityWebRequest.Get(url).SendWebRequest();
await webRequestAsyncOperation.ToUniTask();
RunStandardTaskAsync();
UnityEngine.Debug.Log("UniTaskPlayerLoop ready? " + PlayerLoopHelper.IsInjectedUniTaskPlayerLoop());
//var url = "http://google.com/404";
//var webRequestAsyncOperation = UnityWebRequest.Get(url).SendWebRequest();
//await webRequestAsyncOperation.ToUniTask();
//PlayerLoopInfo.Inject();
@@ -685,7 +706,9 @@ public class SandboxMain : MonoBehaviour
//StartCoroutine(Coroutine());
//await UniTask.Delay(TimeSpan.FromSeconds(1));
// PlayerLoopInfo.Inject();
await UniTask.Delay(TimeSpan.FromSeconds(1));
PlayerLoopInfo.DumpPlayerLoop("current", PlayerLoop.GetCurrentPlayerLoop());
// _ = ReturnToMainThreadTest();
@@ -1051,6 +1074,17 @@ public class SandboxMain : MonoBehaviour
}
}
public class SyncContextInjecter
{
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
public static void Inject()
{
SynchronizationContext.SetSynchronizationContext(new UniTaskSynchronizationContext());
}
}
public class PlayerLoopInfo
{
// [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
@@ -1085,7 +1119,7 @@ public class PlayerLoopInfo
public static Type CurrentLoopType { get; private set; }
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
// [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
public static void Inject()
{
var system = PlayerLoop.GetCurrentPlayerLoop();

View File

@@ -1,2 +1,2 @@
m_EditorVersion: 2019.3.9f1
m_EditorVersionWithRevision: 2019.3.9f1 (e6e740a1c473)
m_EditorVersion: 2019.4.5f1
m_EditorVersionWithRevision: 2019.4.5f1 (81610f64359c)