Compare commits

...

16 Commits

Author SHA1 Message Date
dvsilch
12df2b2e35 Merge 16895054f1 into 06067cd4c8 2025-04-09 14:08:44 +00:00
dvsilch
16895054f1 fix: async lazy cannot use utcs auto reset version 2025-04-09 22:08:32 +08:00
dvsilch
d3e71502d6 perf: using AutoResetUniTaskCompletionSource instead of UniTaskCompletionSource 2025-04-09 21:20:36 +08:00
dvsilch
63708841f5 fix: typo 2025-04-09 21:14:13 +08:00
Ikiru Yoshizaki
06067cd4c8 ci: use Cysharp/Actions checkout instead of 3rd party directly 2025-03-19 15:44:16 +09:00
Ikiru Yoshizaki
d9983cfe27 Merge pull request #654 from Cysharp/feature/pin_action
ci: Pinning third party GitHub Actions sha
2025-03-18 17:33:39 +09:00
Ikiru Yoshizaki
70eb7cd3ee ci: Pinning third party GitHub Actions sha 2025-03-18 16:58:33 +09:00
Ikiru Yoshizaki
cc3c70af90 ci: update vault 2025-03-11 12:55:51 +09:00
Ikiru Yoshizaki
8042b29ff8 ci: extend timeout 2025-01-07 12:29:34 +09:00
Yoshifumi Kawai
b0d01ca75f Merge pull request #641 from hmkc/dev
Update README_CN.md
2024-12-12 16:35:32 +09:00
hmkc
7a63ab7088 Update README_CN.md
Re-translated the documentation.
2024-12-11 17:13:32 +08:00
Yoshifumi Kawai
bdf102f145 Merge pull request #639 from hmkc/dev
Update README_CN.md
2024-12-09 20:18:43 +09:00
hmkc
41cea030ab Update README_CN.md 2024-12-09 18:39:08 +08:00
Yoshifumi Kawai
f9fd769be7 Update README.md 2024-10-09 14:46:23 +09:00
neuecc
579304fe47 chore(docs): update TOC 2024-10-08 14:02:41 +00:00
Yoshifumi Kawai
740ca7ef01 Awaitable notes 2024-10-08 23:02:26 +09:00
13 changed files with 312 additions and 260 deletions

View File

@@ -5,3 +5,8 @@ updates:
directory: "/" directory: "/"
schedule: schedule:
interval: "weekly" # Check for updates to GitHub Actions every week interval: "weekly" # Check for updates to GitHub Actions every week
ignore:
# I just want update action when major/minor version is updated. patch updates are too noisy.
- dependency-name: '*'
update-types:
- version-update:semver-patch

View File

@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 10 timeout-minutes: 10
steps: steps:
- uses: actions/checkout@v4 - uses: Cysharp/Actions/.github/actions/checkout@main
- uses: Cysharp/Actions/.github/actions/setup-dotnet@main - uses: Cysharp/Actions/.github/actions/setup-dotnet@main
- run: dotnet build -c Debug - run: dotnet build -c Debug
- run: dotnet test -c Debug - run: dotnet test -c Debug
@@ -26,20 +26,20 @@ jobs:
matrix: matrix:
unity: ["2022.3.39f1", "6000.0.12f1"] # Test with LTS unity: ["2022.3.39f1", "6000.0.12f1"] # Test with LTS
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 20 timeout-minutes: 30 # Unity build takes more than 20min.
steps: steps:
- name: Load secrets - name: Load secrets
id: op-load-secret id: op-load-secret
uses: 1password/load-secrets-action@v2 uses: 1password/load-secrets-action@581a835fb51b8e7ec56b71cf2ffddd7e68bb25e0 # v2.0.0
with: with:
export-env: false export-env: false
env: env:
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN_PUBLIC }} OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN_PUBLIC }}
UNITY_EMAIL: "op://GitHubActionsPublic/UNITY_LICENSE/username" UNITY_EMAIL: "op://${{ vars.OP_VAULT_ACTIONS_PUBLIC }}/UNITY_LICENSE/username"
UNITY_PASSWORD: "op://GitHubActionsPublic/UNITY_LICENSE/credential" UNITY_PASSWORD: "op://${{ vars.OP_VAULT_ACTIONS_PUBLIC }}/UNITY_LICENSE/credential"
UNITY_SERIAL: "op://GitHubActionsPublic/UNITY_LICENSE/serial" UNITY_SERIAL: "op://${{ vars.OP_VAULT_ACTIONS_PUBLIC }}/UNITY_LICENSE/serial"
- uses: actions/checkout@v4 - uses: Cysharp/Actions/.github/actions/checkout@main
# Execute scripts: Export Package # Execute scripts: Export Package
# /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod PackageExporter.Export # /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod PackageExporter.Export
@@ -56,10 +56,6 @@ jobs:
targetPlatform: StandaloneLinux64 targetPlatform: StandaloneLinux64
buildMethod: PackageExporter.Export buildMethod: PackageExporter.Export
- uses: Cysharp/Actions/.github/actions/check-metas@main # check meta files
with:
directory: src/UniTask
# Execute UnitTest # Execute UnitTest
# /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod UnitTestBuilder.BuildUnitTest /headless /ScriptBackend IL2CPP /BuildTarget StandaloneLinux64 # /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod UnitTestBuilder.BuildUnitTest /headless /ScriptBackend IL2CPP /BuildTarget StandaloneLinux64
- name: Build UnitTest (IL2CPP) - name: Build UnitTest (IL2CPP)
@@ -79,6 +75,10 @@ jobs:
- name: Execute UnitTest - name: Execute UnitTest
run: ./src/UniTask/bin/UnitTest/StandaloneLinux64_IL2CPP/test run: ./src/UniTask/bin/UnitTest/StandaloneLinux64_IL2CPP/test
- uses: Cysharp/Actions/.github/actions/check-metas@main # check meta files
with:
directory: src/UniTask
# Store artifacts. # Store artifacts.
- uses: Cysharp/Actions/.github/actions/upload-artifact@main - uses: Cysharp/Actions/.github/actions/upload-artifact@main
if: ${{ startsWith(matrix.unity, '2021') }} # only execute 2021 if: ${{ startsWith(matrix.unity, '2021') }} # only execute 2021

View File

@@ -11,21 +11,21 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 10 timeout-minutes: 10
steps: steps:
- uses: actions/checkout@v4 - uses: Cysharp/Actions/.github/actions/checkout@main
- uses: actions/checkout@v4 - uses: Cysharp/Actions/.github/actions/checkout@main
with: with:
repository: Cysharp/DocfxTemplate repository: Cysharp/DocfxTemplate
path: docs/_DocfxTemplate path: docs/_DocfxTemplate
- uses: Kirbyrawr/docfx-action@master - uses: Kirbyrawr/docfx-action@db9a22c8fe1e8693a2a21be54cb0b87dfaa72cc4
name: Docfx metadata name: Docfx metadata
with: with:
args: metadata docs/docfx.json args: metadata docs/docfx.json
- uses: Kirbyrawr/docfx-action@master - uses: Kirbyrawr/docfx-action@db9a22c8fe1e8693a2a21be54cb0b87dfaa72cc4
name: Docfx build name: Docfx build
with: with:
args: build docs/docfx.json args: build docs/docfx.json
- name: Publish to GitHub Pages - name: Publish to GitHub Pages
uses: peaceiris/actions-gh-pages@v4 uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4.0.0
with: with:
github_token: ${{ secrets.GITHUB_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: docs/_site publish_dir: docs/_site

View File

@@ -26,7 +26,7 @@ jobs:
timeout-minutes: 10 timeout-minutes: 10
steps: steps:
- run: echo ${{ needs.update-packagejson.outputs.sha }} - run: echo ${{ needs.update-packagejson.outputs.sha }}
- uses: actions/checkout@v4 - uses: Cysharp/Actions/.github/actions/checkout@main
with: with:
ref: ${{ needs.update-packagejson.outputs.sha }} ref: ${{ needs.update-packagejson.outputs.sha }}
- uses: Cysharp/Actions/.github/actions/setup-dotnet@main - uses: Cysharp/Actions/.github/actions/setup-dotnet@main
@@ -51,17 +51,17 @@ jobs:
steps: steps:
- name: Load secrets - name: Load secrets
id: op-load-secret id: op-load-secret
uses: 1password/load-secrets-action@v2 uses: 1password/load-secrets-action@581a835fb51b8e7ec56b71cf2ffddd7e68bb25e0 # v2.0.0
with: with:
export-env: false export-env: false
env: env:
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN_PUBLIC }} OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN_PUBLIC }}
UNITY_EMAIL: "op://GitHubActionsPublic/UNITY_LICENSE/username" UNITY_EMAIL: "op://${{ vars.OP_VAULT_ACTIONS_PUBLIC }}/UNITY_LICENSE/username"
UNITY_PASSWORD: "op://GitHubActionsPublic/UNITY_LICENSE/credential" UNITY_PASSWORD: "op://${{ vars.OP_VAULT_ACTIONS_PUBLIC }}/UNITY_LICENSE/credential"
UNITY_SERIAL: "op://GitHubActionsPublic/UNITY_LICENSE/serial" UNITY_SERIAL: "op://${{ vars.OP_VAULT_ACTIONS_PUBLIC }}/UNITY_LICENSE/serial"
- run: echo ${{ needs.update-packagejson.outputs.sha }} - run: echo ${{ needs.update-packagejson.outputs.sha }}
- uses: actions/checkout@v4 - uses: Cysharp/Actions/.github/actions/checkout@main
with: with:
ref: ${{ needs.update-packagejson.outputs.sha }} ref: ${{ needs.update-packagejson.outputs.sha }}
# Execute scripts: Export Package # Execute scripts: Export Package

View File

@@ -8,8 +8,7 @@ on:
jobs: jobs:
generateTOC: generateTOC:
name: TOC Generator name: TOC Generator
runs-on: ubuntu-latest uses: Cysharp/Actions/.github/workflows/toc-generator.yaml@main
steps: with:
- uses: technote-space/toc-generator@v4.3.1 TOC_TITLE: "## Table of Contents"
with: secrets: inherit
TOC_TITLE: "## Table of Contents"

View File

@@ -33,6 +33,7 @@ For advanced tips, see blog post: [Extends UnityWebRequest via async decorator p
- [AsyncEnumerable and Async LINQ](#asyncenumerable-and-async-linq) - [AsyncEnumerable and Async LINQ](#asyncenumerable-and-async-linq)
- [Awaitable Events](#awaitable-events) - [Awaitable Events](#awaitable-events)
- [Channel](#channel) - [Channel](#channel)
- [vs Awaitable](#vs-awaitable)
- [For Unit Testing](#for-unit-testing) - [For Unit Testing](#for-unit-testing)
- [ThreadPool limitation](#threadpool-limitation) - [ThreadPool limitation](#threadpool-limitation)
- [IEnumerator.ToUniTask limitation](#ienumeratortounitask-limitation) - [IEnumerator.ToUniTask limitation](#ienumeratortounitask-limitation)
@@ -67,6 +68,7 @@ async UniTask<string> DemoAsync()
await SceneManager.LoadSceneAsync("scene2"); await SceneManager.LoadSceneAsync("scene2");
// .WithCancellation enables Cancel, GetCancellationTokenOnDestroy synchornizes with lifetime of GameObject // .WithCancellation enables Cancel, GetCancellationTokenOnDestroy synchornizes with lifetime of GameObject
// after Unity 2022.2, you can use `destroyCancellationToken` in MonoBehaviour
var asset2 = await Resources.LoadAsync<TextAsset>("bar").WithCancellation(this.GetCancellationTokenOnDestroy()); var asset2 = await Resources.LoadAsync<TextAsset>("bar").WithCancellation(this.GetCancellationTokenOnDestroy());
// .ToUniTask accepts progress callback(and all options), Progress.Create is a lightweight alternative of IProgress<T> // .ToUniTask accepts progress callback(and all options), Progress.Create is a lightweight alternative of IProgress<T>
@@ -293,6 +295,8 @@ public class MyBehaviour : MonoBehaviour
} }
``` ```
After Unity 2022.2, Unity adds CancellationToken in [MonoBehaviour.destroyCancellationToken](https://docs.unity3d.com/ScriptReference/MonoBehaviour-destroyCancellationToken.html) and [Application.exitCancellationToken](https://docs.unity3d.com/ScriptReference/Application-exitCancellationToken.html).
When cancellation is detected, all methods throw `OperationCanceledException` and propagate upstream. When exception(not limited to `OperationCanceledException`) is not handled in async method, it is propagated finally to `UniTaskScheduler.UnobservedTaskException`. The default behaviour of received unhandled exception is to write log as exception. Log level can be changed using `UniTaskScheduler.UnobservedExceptionWriteLogType`. If you want to use custom behaviour, set an action to `UniTaskScheduler.UnobservedTaskException.` When cancellation is detected, all methods throw `OperationCanceledException` and propagate upstream. When exception(not limited to `OperationCanceledException`) is not handled in async method, it is propagated finally to `UniTaskScheduler.UnobservedTaskException`. The default behaviour of received unhandled exception is to write log as exception. Log level can be changed using `UniTaskScheduler.UnobservedExceptionWriteLogType`. If you want to use custom behaviour, set an action to `UniTaskScheduler.UnobservedTaskException.`
And also `OperationCanceledException` is a special exception, this is silently ignored at `UnobservedTaskException`. And also `OperationCanceledException` is a special exception, this is silently ignored at `UnobservedTaskException`.
@@ -953,6 +957,14 @@ public class AsyncMessageBroker<T> : IDisposable
} }
``` ```
vs Awaitable
---
Unity 6 introduces the awaitable type, [Awaitable](https://docs.unity3d.com/6000.0/Documentation/ScriptReference/Awaitable.html). To put it simply, Awaitable can be considered a subset of UniTask, and in fact, Awaitable's design was influenced by UniTask. It should be able to handle PlayerLoop-based awaits, pooled Tasks, and support for cancellation with `CancellationToken` in a similar way. With its inclusion in the standard library, you may wonder whether to continue using UniTask or migrate to Awaitable. Here's a brief guide.
First, the functionality provided by Awaitable is equivalent to what coroutines offer. Instead of `yield return`, you use await; `await NextFrameAsync()` replaces `yield return null`; and there are equivalents for `WaitForSeconds` and `EndOfFrame`. However, that's the extent of it. Being coroutine-based in terms of functionality, it lacks Task-based features. In practical application development using async/await, operations like `WhenAll` are essential. Additionally, UniTask enables many frame-based operations (such as `DelayFrame`) and more flexible PlayerLoopTiming control, which are not available in Awaitable. Of course, there's no Tracker Window either.
Therefore, I recommend using UniTask for application development. UniTask is a superset of Awaitable and includes many essential features. For library development, where you want to avoid external dependencies, using Awaitable as a return type for methods would be appropriate. Awaitable can be converted to UniTask using `AsUniTask`, so there's no issue in handling Awaitable-based functionality within the UniTask library. Of course, if you don't need to worry about dependencies, using UniTask would be the best choice even for library development.
For Unit Testing For Unit Testing
--- ---
Unity's `[UnityTest]` attribute can test coroutine(IEnumerator) but can not test async. `UniTask.ToCoroutine` bridges async/await to coroutine so you can test async methods. Unity's `[UnityTest]` attribute can test coroutine(IEnumerator) but can not test async. `UniTask.ToCoroutine` bridges async/await to coroutine so you can test async methods.

File diff suppressed because it is too large Load Diff

View File

@@ -67,13 +67,13 @@ namespace Cysharp.Threading.Tasks
return (UniTask.FromCanceled(cancellationToken), default(CancellationTokenRegistration)); return (UniTask.FromCanceled(cancellationToken), default(CancellationTokenRegistration));
} }
var promise = new UniTaskCompletionSource(); var promise = AutoResetUniTaskCompletionSource.Create();
return (promise.Task, cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationTokenCallback, promise)); return (promise.Task, cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationTokenCallback, promise));
} }
static void Callback(object state) static void Callback(object state)
{ {
var promise = (UniTaskCompletionSource)state; var promise = (AutoResetUniTaskCompletionSource)state;
promise.TrySetResult(); promise.TrySetResult();
} }

View File

@@ -91,7 +91,7 @@ namespace Cysharp.Threading.Tasks
{ {
readonly Queue<T> items; readonly Queue<T> items;
readonly SingleConsumerUnboundedChannelReader readerSource; readonly SingleConsumerUnboundedChannelReader readerSource;
UniTaskCompletionSource completedTaskSource; AutoResetUniTaskCompletionSource completedTaskSource;
UniTask completedTask; UniTask completedTask;
Exception completionError; Exception completionError;
@@ -208,7 +208,7 @@ namespace Cysharp.Threading.Tasks
return parent.completedTask; return parent.completedTask;
} }
parent.completedTaskSource = new UniTaskCompletionSource(); parent.completedTaskSource = AutoResetUniTaskCompletionSource.Create();
return parent.completedTaskSource.Task; return parent.completedTaskSource.Task;
} }
} }

View File

@@ -36,11 +36,11 @@ namespace Cysharp.Threading.Tasks.Linq
public UniTask<bool> MoveNextAsync() public UniTask<bool> MoveNextAsync()
{ {
var tcs = new UniTaskCompletionSource<bool>(); var tcs = AutoResetUniTaskCompletionSource<bool>.Create();
cancellationToken.Register(state => cancellationToken.Register(state =>
{ {
var task = (UniTaskCompletionSource<bool>)state; var task = (AutoResetUniTaskCompletionSource<bool>)state;
task.TrySetCanceled(cancellationToken); task.TrySetCanceled(cancellationToken);
}, tcs); }, tcs);

View File

@@ -18,13 +18,13 @@ namespace Cysharp.Threading.Tasks
#endif #endif
/// <summary>This CancellationToken is canceled when the MonoBehaviour will be destroyed.</summary> /// <summary>This CancellationToken is canceled when the GameObject will be destroyed.</summary>
public static CancellationToken GetCancellationTokenOnDestroy(this GameObject gameObject) public static CancellationToken GetCancellationTokenOnDestroy(this GameObject gameObject)
{ {
return gameObject.GetAsyncDestroyTrigger().CancellationToken; return gameObject.GetAsyncDestroyTrigger().CancellationToken;
} }
/// <summary>This CancellationToken is canceled when the MonoBehaviour will be destroyed.</summary> /// <summary>This CancellationToken is canceled when the Component will be destroyed.</summary>
public static CancellationToken GetCancellationTokenOnDestroy(this Component component) public static CancellationToken GetCancellationTokenOnDestroy(this Component component)
{ {
#if UNITY_2022_2_OR_NEWER #if UNITY_2022_2_OR_NEWER

View File

@@ -16,11 +16,11 @@ namespace Cysharp.Threading.Tasks
/// </summary> /// </summary>
public static UniTask<T> AsUniTask<T>(this Task<T> task, bool useCurrentSynchronizationContext = true) public static UniTask<T> AsUniTask<T>(this Task<T> task, bool useCurrentSynchronizationContext = true)
{ {
var promise = new UniTaskCompletionSource<T>(); var promise = AutoResetUniTaskCompletionSource<T>.Create();
task.ContinueWith((x, state) => task.ContinueWith((x, state) =>
{ {
var p = (UniTaskCompletionSource<T>)state; var p = (AutoResetUniTaskCompletionSource<T>)state;
switch (x.Status) switch (x.Status)
{ {
@@ -46,11 +46,11 @@ namespace Cysharp.Threading.Tasks
/// </summary> /// </summary>
public static UniTask AsUniTask(this Task task, bool useCurrentSynchronizationContext = true) public static UniTask AsUniTask(this Task task, bool useCurrentSynchronizationContext = true)
{ {
var promise = new UniTaskCompletionSource(); var promise = AutoResetUniTaskCompletionSource.Create();
task.ContinueWith((x, state) => task.ContinueWith((x, state) =>
{ {
var p = (UniTaskCompletionSource)state; var p = (AutoResetUniTaskCompletionSource)state;
switch (x.Status) switch (x.Status)
{ {

View File

@@ -11,7 +11,7 @@ namespace Cysharp.Threading.Tasks
{ {
public static UniTask<T> ToUniTask<T>(this IObservable<T> source, bool useFirstValue = false, CancellationToken cancellationToken = default) public static UniTask<T> ToUniTask<T>(this IObservable<T> source, bool useFirstValue = false, CancellationToken cancellationToken = default)
{ {
var promise = new UniTaskCompletionSource<T>(); var promise = AutoResetUniTaskCompletionSource<T>.Create();
var disposable = new SingleAssignmentDisposable(); var disposable = new SingleAssignmentDisposable();
var observer = useFirstValue var observer = useFirstValue
@@ -109,7 +109,7 @@ namespace Cysharp.Threading.Tasks
{ {
static readonly Action<object> callback = OnCanceled; static readonly Action<object> callback = OnCanceled;
readonly UniTaskCompletionSource<T> promise; readonly IPromise<T> promise;
readonly SingleAssignmentDisposable disposable; readonly SingleAssignmentDisposable disposable;
readonly CancellationToken cancellationToken; readonly CancellationToken cancellationToken;
readonly CancellationTokenRegistration registration; readonly CancellationTokenRegistration registration;
@@ -117,7 +117,7 @@ namespace Cysharp.Threading.Tasks
bool hasValue; bool hasValue;
T latestValue; T latestValue;
public ToUniTaskObserver(UniTaskCompletionSource<T> promise, SingleAssignmentDisposable disposable, CancellationToken cancellationToken) public ToUniTaskObserver(IPromise<T> promise, SingleAssignmentDisposable disposable, CancellationToken cancellationToken)
{ {
this.promise = promise; this.promise = promise;
this.disposable = disposable; this.disposable = disposable;
@@ -180,14 +180,14 @@ namespace Cysharp.Threading.Tasks
{ {
static readonly Action<object> callback = OnCanceled; static readonly Action<object> callback = OnCanceled;
readonly UniTaskCompletionSource<T> promise; readonly IPromise<T> promise;
readonly SingleAssignmentDisposable disposable; readonly SingleAssignmentDisposable disposable;
readonly CancellationToken cancellationToken; readonly CancellationToken cancellationToken;
readonly CancellationTokenRegistration registration; readonly CancellationTokenRegistration registration;
bool hasValue; bool hasValue;
public FirstValueToUniTaskObserver(UniTaskCompletionSource<T> promise, SingleAssignmentDisposable disposable, CancellationToken cancellationToken) public FirstValueToUniTaskObserver(IPromise<T> promise, SingleAssignmentDisposable disposable, CancellationToken cancellationToken)
{ {
this.promise = promise; this.promise = promise;
this.disposable = disposable; this.disposable = disposable;