Compare commits

..

1 Commits

Author SHA1 Message Date
Ikiru Yoshizaki
51cc6e71e9 chore: add demo script which follows README 2021-04-27 10:43:58 +09:00
66 changed files with 3732 additions and 2448 deletions

1
.github/FUNDING.yml vendored
View File

@@ -1 +0,0 @@
github: [neuecc]

View File

@@ -4,6 +4,8 @@ on:
push: push:
branches: branches:
- "master" - "master"
tags:
- "!*" # not a tag push
pull_request: pull_request:
branches: branches:
- "master" - "master"
@@ -11,60 +13,70 @@ on:
jobs: jobs:
build-dotnet: build-dotnet:
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 10 env:
DOTNET_CLI_TELEMETRY_OPTOUT: 1
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
NUGET_XMLDOC_MODE: skip
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v2
- uses: Cysharp/Actions/.github/actions/setup-dotnet@main - uses: actions/setup-dotnet@v1
with: with:
dotnet-version: | dotnet-version: 3.1.x
6.0.x - run: dotnet test -c Debug ./src/UniTask.NetCoreTests/UniTask.NetCoreTests.csproj
7.0.x
- run: dotnet build -c Debug
- run: dotnet test -c Debug
build-unity: build-unity:
if: "((github.event_name == 'push' && github.repository_owner == 'Cysharp') || startsWith(github.event.pull_request.head.label, 'Cysharp:'))" if: "((github.event_name == 'push' && github.repository_owner == 'Cysharp') || startsWith(github.event.pull_request.head.label, 'Cysharp:'))"
strategy: strategy:
matrix: matrix:
unity: ["2019.3.9f1", "2019.4.13f1", "2020.1.12f1"] unity: ["2019.3.9f1", "2019.4.13f1", "2020.1.12f1"]
include:
- unity: 2019.3.9f1
license: UNITY_LICENSE_2019
- unity: 2019.4.13f1
license: UNITY_LICENSE_2019
- unity: 2020.1.12f1
license: UNITY_LICENSE_2020
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 15
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v2
# Execute scripts: RuntimeUnitTestToolkit # Execute scripts: RuntimeUnitTestToolkit
# /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod UnitTestBuilder.BuildUnitTest /headless /ScriptBackend mono /BuildTarget StandaloneLinux64 # /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod UnitTestBuilder.BuildUnitTest /headless /ScriptBackend mono /BuildTarget StandaloneLinux64
- name: Build UnitTest(Linux64, mono) - name: Build UnitTest(Linux64, mono)
uses: Cysharp/Actions/.github/actions/unity-builder@main uses: game-ci/unity-builder@v2.0-alpha-6
env: env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} UNITY_LICENSE: ${{ secrets[matrix.license] }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
with: with:
projectPath: src/UniTask projectPath: src/UniTask
unityVersion: ${{ matrix.unity }} unityVersion: ${{ matrix.unity }}
targetPlatform: StandaloneLinux64 targetPlatform: StandaloneLinux64
buildMethod: UnitTestBuilder.BuildUnitTest buildMethod: UnitTestBuilder.BuildUnitTest
customParameters: /headless /ScriptBackend mono customParameters: /headless /ScriptBackend mono
versioning: None
- name: Execute UnitTest - name: Execute UnitTest
run: ./src/UniTask/bin/UnitTest/StandaloneLinux64_Mono2x/test run: ./src/UniTask/bin/UnitTest/StandaloneLinux64_Mono2x/test
# 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
- name: Build Unity (.unitypacakge) - name: Export unitypackage
uses: Cysharp/Actions/.github/actions/unity-builder@main uses: game-ci/unity-builder@v2.0-alpha-6
env: env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} UNITY_LICENSE: ${{ secrets[matrix.license] }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
with: with:
projectPath: src/UniTask projectPath: src/UniTask
unityVersion: ${{ matrix.unity }} unityVersion: ${{ matrix.unity }}
targetPlatform: StandaloneLinux64 targetPlatform: StandaloneLinux64
buildMethod: PackageExporter.Export buildMethod: PackageExporter.Export
versioning: None
- uses: Cysharp/Actions/.github/actions/check-metas@main # check meta files - name: check all .meta is commited
with: run: |
directory: src/UniTask if git ls-files --others --exclude-standard -t | grep --regexp='[.]meta$'; then
echo "Detected .meta file generated. Do you forgot commit a .meta file?"
exit 1
else
echo "Great, all .meta files are commited."
fi
working-directory: src/UniTask
# Store artifacts. # Store artifacts.
- uses: actions/upload-artifact@v2 - uses: actions/upload-artifact@v2

View File

@@ -9,10 +9,9 @@ on:
jobs: jobs:
run-docfx: run-docfx:
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 10
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v2
- uses: actions/checkout@v3 - uses: actions/checkout@v2
with: with:
repository: Cysharp/DocfxTemplate repository: Cysharp/DocfxTemplate
path: docs/_DocfxTemplate path: docs/_DocfxTemplate

View File

@@ -6,44 +6,94 @@ on:
tag: tag:
description: "tag: git tag you want create. (sample 1.0.0)" description: "tag: git tag you want create. (sample 1.0.0)"
required: true required: true
dry-run: dry_run:
description: "dry-run: true will never create relase/nuget." description: "dry_run: true will never create relase/nuget."
required: true required: true
default: false default: "false"
type: boolean
env: env:
GIT_TAG: ${{ github.event.inputs.tag }} GIT_TAG: ${{ github.event.inputs.tag }}
DRY_RUN: ${{ github.event.inputs.dry-run }} DRY_RUN: ${{ github.event.inputs.dry_run }}
DRY_RUN_BRANCH_PREFIX: "test_release"
DOTNET_SDK_VERISON_3: 3.1.x
jobs: jobs:
update-packagejson: update-packagejson:
uses: Cysharp/Actions/.github/workflows/update-packagejson.yaml@main runs-on: ubuntu-latest
with: env:
file-path: ./src/UniTask/Assets/Plugins/UniTask/package.json TARGET_FILE: ./src/UniTask/Assets/Plugins/UniTask/package.json
tag: ${{ github.event.inputs.tag }} outputs:
dry-run: ${{ fromJson(github.event.inputs.dry-run) }} sha: ${{ steps.commit.outputs.sha }}
steps:
- uses: actions/checkout@v2
- name: Output package.json (Before)
run: cat ${{ env.TARGET_FILE}}
- name: Update package.json to version ${{ env.GIT_TAG }}
run: sed -i -e "s/\(\"version\":\) \"\(.*\)\",/\1 \"${{ env.GIT_TAG }}\",/" ${{ env.TARGET_FILE }}
- name: Check update
id: check_update
run: |
cat ${{ env.TARGET_FILE}}
git diff --exit-code || echo "::set-output name=changed::1"
- name: Commit files
id: commit
if: steps.check_update.outputs.changed == '1'
run: |
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git commit -m "feat: Update package.json to ${{ env.GIT_TAG }}" -a
echo "::set-output name=sha::$(git rev-parse HEAD)"
- name: Check sha
run: echo "SHA ${SHA}"
env:
SHA: ${{ steps.commit.outputs.sha }}
- name: Create Tag
if: steps.check_update.outputs.changed == '1'
run: git tag ${{ env.GIT_TAG }}
- name: Push changes
if: env.DRY_RUN == 'false' && steps.check_update.outputs.changed == '1'
uses: ad-m/github-push-action@master
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
branch: ${{ github.ref }}
tags: true
- name: Push changes (dry_run)
if: env.DRY_RUN == 'true' && steps.check_update.outputs.changed == '1'
uses: ad-m/github-push-action@master
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
branch: ${{ env.DRY_RUN_BRANCH_PREFIX }}-${{ env.GIT_TAG }}
tags: false
build-dotnet: build-dotnet:
needs: [update-packagejson] needs: [update-packagejson]
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 10 timeout-minutes: 10
env:
DOTNET_CLI_TELEMETRY_OPTOUT: 1
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
NUGET_XMLDOC_MODE: skip
steps: steps:
- run: echo ${{ needs.update-packagejson.outputs.sha }} - run: echo ${{ needs.update-packagejson.outputs.sha }}
- uses: actions/checkout@v3 - uses: actions/checkout@v2
with: with:
ref: ${{ needs.update-packagejson.outputs.sha }} ref: ${{ needs.update-packagejson.outputs.sha }}
- uses: Cysharp/Actions/.github/actions/setup-dotnet@main - uses: actions/setup-dotnet@v1
with: with:
dotnet-version: | dotnet-version: "${{ env.DOTNET_SDK_VERSION_3 }}"
3.1.x
6.0.x
# build and pack # build and pack
- run: dotnet build -c Release -p:Version=${{ env.GIT_TAG }} - run: dotnet build -c Release -p:Version=${{ env.GIT_TAG }}
- run: dotnet test -c Release --no-build - run: dotnet test -c Release --no-build
- run: dotnet pack ./src/UniTask.NetCore/UniTask.NetCore.csproj -c Release --no-build -p:Version=${{ env.GIT_TAG }} -o ./publish - run: dotnet pack ./src/UniTask.NetCore/UniTask.NetCore.csproj -c Release --no-build -p:Version=${{ env.GIT_TAG }} -o ./publish
# Store artifacts. # Store artifacts.
- uses: actions/upload-artifact@v2 - uses: actions/upload-artifact@v1
with: with:
name: nuget name: nuget
path: ./publish/ path: ./publish/
@@ -53,30 +103,38 @@ jobs:
strategy: strategy:
matrix: matrix:
unity: ["2019.3.9f1"] unity: ["2019.3.9f1"]
include:
- unity: 2019.3.9f1
license: UNITY_LICENSE_2019
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 15 timeout-minutes: 15
steps: steps:
- run: echo ${{ needs.update-packagejson.outputs.sha }} - run: echo ${{ needs.update-packagejson.outputs.sha }}
- uses: actions/checkout@v3 - uses: actions/checkout@v2
with: with:
ref: ${{ needs.update-packagejson.outputs.sha }} ref: ${{ needs.update-packagejson.outputs.sha }}
# 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
- name: Build Unity (.unitypacakge) - name: Export unitypackage
uses: Cysharp/Actions/.github/actions/unity-builder@main uses: game-ci/unity-builder@v2.0-alpha-6
env: env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} UNITY_LICENSE: ${{ secrets[matrix.license] }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
with: with:
projectPath: src/UniTask projectPath: src/UniTask
unityVersion: ${{ matrix.unity }} unityVersion: ${{ matrix.unity }}
targetPlatform: StandaloneLinux64 targetPlatform: StandaloneLinux64
buildMethod: PackageExporter.Export buildMethod: PackageExporter.Export
versioning: None
- uses: Cysharp/Actions/.github/actions/check-metas@main # check meta files - name: check all .meta is commited
with: run: |
directory: src/UniTask if git ls-files --others --exclude-standard -t | grep --regexp='[.]meta$'; then
echo "Detected .meta file generated. Do you forgot commit a .meta file?"
exit 1
else
echo "Great, all .meta files are commited."
fi
working-directory: src/UniTask
# Store artifacts. # Store artifacts.
- uses: actions/upload-artifact@v2 - uses: actions/upload-artifact@v2
@@ -85,12 +143,18 @@ jobs:
path: ./src/UniTask/UniTask.${{ env.GIT_TAG }}.unitypackage path: ./src/UniTask/UniTask.${{ env.GIT_TAG }}.unitypackage
create-release: create-release:
if: github.event.inputs.dry-run == 'false' if: github.event.inputs.dry_run == 'false'
needs: [update-packagejson, build-dotnet, build-unity] needs: [update-packagejson, build-dotnet, build-unity]
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 10 env:
DOTNET_CLI_TELEMETRY_OPTOUT: 1
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
NUGET_XMLDOC_MODE: skip
steps: steps:
- uses: Cysharp/Actions/.github/actions/setup-dotnet@main # setup dotnet for nuget push
- uses: actions/setup-dotnet@v1
with:
dotnet-version: "${{ env.DOTNET_SDK_VERSION_3 }}"
# Create Releases # Create Releases
- uses: actions/create-release@v1 - uses: actions/create-release@v1
id: create_release id: create_release
@@ -117,8 +181,12 @@ jobs:
asset_content_type: application/octet-stream asset_content_type: application/octet-stream
cleanup: cleanup:
if: needs.update-packagejson.outputs.is-branch-created == 'true' if: github.event.inputs.dry_run == 'true'
needs: [update-packagejson, build-dotnet, build-unity] needs: [build-dotnet, build-unity]
uses: Cysharp/Actions/.github/workflows/clean-packagejson-branch.yaml@main runs-on: ubuntu-latest
with: steps:
branch: ${{ needs.update-packagejson.outputs.branch-name }} - name: Delete branch
uses: dawidd6/action-delete-branch@v3
with:
github_token: ${{ github.token }}
branches: ${{ env.DRY_RUN_BRANCH_PREFIX }}-${{ env.GIT_TAG }}

View File

@@ -1,10 +0,0 @@
name: Prevent github change
on:
pull_request:
paths:
- ".github/**/*.yaml"
- ".github/**/*.yml"
jobs:
detect:
uses: Cysharp/Actions/.github/workflows/prevent-github-change.yaml@main

View File

@@ -1,10 +1,24 @@
name: "Close stale issues" name: "Close stale issues"
on: on:
workflow_dispatch:
schedule: schedule:
- cron: "0 0 * * *" - cron: "0 0 * * *"
jobs: jobs:
stale: stale:
uses: Cysharp/Actions/.github/workflows/stale-issue.yaml@main runs-on: ubuntu-latest
steps:
- uses: actions/stale@v3
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
# enable issue
stale-issue-message: "This issue is stale because it has been open 90 days with no activity. Remove stale label or comment or this will be closed in 7 days."
stale-issue-label: "stale"
# enable pr
stale-pr-message: "This PR is stale because it has been open 90 days with no activity. Remove stale label or comment or this will be closed in 7 days."
stale-pr-label: "stale"
days-before-stale: 90
days-before-close: 7
exempt-issue-labels: "wip"
exempt-pr-labels: "wip"
remove-stale-when-updated: true

View File

@@ -1,6 +1,6 @@
UniTask UniTask
=== ===
[![GitHub Actions](https://github.com/Cysharp/UniTask/workflows/Build-Debug/badge.svg)](https://github.com/Cysharp/UniTask/actions) [![Releases](https://img.shields.io/github/release/Cysharp/UniTask.svg)](https://github.com/Cysharp/UniTask/releases) [![Readme_CN](https://img.shields.io/badge/UniTask-%E4%B8%AD%E6%96%87%E6%96%87%E6%A1%A3-red)](https://github.com/Cysharp/UniTask/blob/master/README_CN.md) [![GitHub Actions](https://github.com/Cysharp/UniTask/workflows/Build-Debug/badge.svg)](https://github.com/Cysharp/UniTask/actions) [![Releases](https://img.shields.io/github/release/Cysharp/UniTask.svg)](https://github.com/Cysharp/UniTask/releases)
Provides an efficient allocation free async/await integration for Unity. Provides an efficient allocation free async/await integration for Unity.
@@ -44,6 +44,7 @@ For advanced tips, see blog post: [Extends UnityWebRequest via async decorator p
- [API References](#api-references) - [API References](#api-references)
- [UPM Package](#upm-package) - [UPM Package](#upm-package)
- [Install via git URL](#install-via-git-url) - [Install via git URL](#install-via-git-url)
- [Install via OpenUPM](#install-via-openupm)
- [.NET Core](#net-core) - [.NET Core](#net-core)
- [License](#license) - [License](#license)
@@ -85,13 +86,8 @@ async UniTask<string> DemoAsync()
await UniTask.Yield(); await UniTask.Yield();
await UniTask.NextFrame(); await UniTask.NextFrame();
// replacement of WaitForEndOfFrame // replacement of WaitForEndOfFrame(same as UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate))
#if UNITY_2023_1_OR_NEWER
await UniTask.WaitForEndOfFrame(); await UniTask.WaitForEndOfFrame();
#else
// requires MonoBehaviour(CoroutineRunner))
await UniTask.WaitForEndOfFrame(this); // this is MonoBehaviour
#endif
// replacement of yield return new WaitForFixedUpdate(same as UniTask.Yield(PlayerLoopTiming.FixedUpdate)) // replacement of yield return new WaitForFixedUpdate(same as UniTask.Yield(PlayerLoopTiming.FixedUpdate))
await UniTask.WaitForFixedUpdate(); await UniTask.WaitForFixedUpdate();
@@ -500,19 +496,17 @@ public enum PlayerLoopTiming
It indicates when to run, you can check [PlayerLoopList.md](https://gist.github.com/neuecc/bc3a1cfd4d74501ad057e49efcd7bdae) to Unity's default playerloop and injected UniTask's custom loop. It indicates when to run, you can check [PlayerLoopList.md](https://gist.github.com/neuecc/bc3a1cfd4d74501ad057e49efcd7bdae) to Unity's default playerloop and injected UniTask's custom loop.
`PlayerLoopTiming.Update` is similar to `yield return null` in a coroutine, but it is called before Update(Update and uGUI events(button.onClick, etc...) are called on `ScriptRunBehaviourUpdate`, yield return null is called on `ScriptRunDelayedDynamicFrameRate`). `PlayerLoopTiming.FixedUpdate` is similar to `WaitForFixedUpdate`. `PlayerLoopTiming.Update` is similar to `yield return null` in a coroutine, but it is called before Update(Update and uGUI events(button.onClick, etc...) are called on `ScriptRunBehaviourUpdate`, yield return null is called on `ScriptRunDelayedDynamicFrameRate`). `PlayerLoopTiming.FixedUpdate` is similar to `WaitForFixedUpdate`, `PlayerLoopTiming.LastPostLateUpdate` is similar to `WaitForEndOfFrame` in coroutine.
> `PlayerLoopTiming.LastPostLateUpdate` is not equivalent to coroutine's `yield return new WaitForEndOfFrame()`. Coroutine's WaitForEndOfFrame seems to run after the PlayerLoop is done. Some methods that require coroutine's end of frame(`Texture2D.ReadPixels`, `ScreenCapture.CaptureScreenshotAsTexture`, `CommandBuffer`, etc) do not work correctly when replaced with async/await. In these cases, pass MonoBehaviour(coroutine runnner) to `UniTask.WaitForEndOfFrame`. For example, `await UniTask.WaitForEndOfFrame(this);` is lightweight allocation free alternative of `yield return new WaitForEndOfFrame()`. > `await UniTask.WaitForEndOfFrame()` is not equivalent to coroutine's `yield return new WaitForEndOfFrame()`. Coroutine's WaitForEndOfFrame seems to run after the PlayerLoop is done. Some methods that require coroutine's end of frame(`ScreenCapture.CaptureScreenshotAsTexture`, `CommandBuffer`, etc) do not work correctly when replaced with async/await. In these cases, use a coroutine instead.
>
> Note: In Unity 2023.1 or newer, `await UniTask.WaitForEndOfFrame();` no longer requires MonoBehaviour. It uses `UnityEngine.Awaitable.EndOfFrameAsync`.
`yield return null` and `UniTask.Yield` are similar but different. `yield return null` always returns next frame but `UniTask.Yield` returns next called. That is, call `UniTask.Yield(PlayerLoopTiming.Update)` on `PreUpdate`, it returns same frame. `UniTask.NextFrame()` guarantees return next frame, you can expect this to behave exactly the same as `yield return null`. `yield return null` and `UniTask.Yield` are similar but different. `yield return null` always returns next frame but `UniTask.Yield` returns next called. That is, call `UniTask.Yield(PlayerLoopTiming.Update)` on `PreUpdate`, it returns same frame. `UniTask.NextFrame()` guarantees return next frame, you can expect this to behave exactly the same as `yield return null`.
> UniTask.Yield(without CancellationToken) is a special type, returns `YieldAwaitable` and runs on YieldRunner. It is the most lightweight and fastest. > UniTask.Yield(without CancellationToken) is a special type, returns `YieldAwaitable` and run on YieldRunner. It is most lightweight and fastest.
`AsyncOperation` is returned from native timing. For example, await `SceneManager.LoadSceneAsync` is returned from `EarlyUpdate.UpdatePreloading` and after being called, the loaded scene's `Start` is called from `EarlyUpdate.ScriptRunDelayedStartupFrame`. Also `await UnityWebRequest` is returned from `EarlyUpdate.ExecuteMainThreadJobs`. `AsyncOperation` is returned from native timing. For example, await `SceneManager.LoadSceneAsync` is returned from `EarlyUpdate.UpdatePreloading` and after being called, the loaded scene's `Start` is called from `EarlyUpdate.ScriptRunDelayedStartupFrame`. Also `await UnityWebRequest` is returned from `EarlyUpdate.ExecuteMainThreadJobs`.
In UniTask, await directly uses native timing, while `WithCancellation` and `ToUniTask` use specified timing. This is usually not a particular problem, but with `LoadSceneAsync`, it causes a different order of Start and continuation after await. So it is recommended not to use `LoadSceneAsync.ToUniTask`. In UniTask, await directly uses native timing, `WithCancellation` and `ToUniTask` use specified timing. This is usually not a particular problem, but with `LoadSceneAsync`, it causes a different order of Start and continuation after await. So it is recommended not to use `LoadSceneAsync.ToUniTask`.
In the stacktrace, you can check where it is running in playerloop. In the stacktrace, you can check where it is running in playerloop.
@@ -664,8 +658,7 @@ By default, UniTask supports TextMeshPro(`BindTo(TMP_Text)` and `TMP_InputField`
There are defined in separated asmdefs like `UniTask.TextMeshPro`, `UniTask.DOTween`, `UniTask.Addressables`. There are defined in separated asmdefs like `UniTask.TextMeshPro`, `UniTask.DOTween`, `UniTask.Addressables`.
TextMeshPro and Addressables support are automatically enabled when importing their packages from package manager. TextMeshPro and Addressables support are automatically enabled when importing their packages from package manager. However for DOTween support, it is required to import `com.demigiant.dotween` from [OpenUPM](https://openupm.com/packages/com.demigiant.dotween/) or to define `UNITASK_DOTWEEN_SUPPORT` to enable it.
However for DOTween support, after importing from the [DOTWeen assets](https://assetstore.unity.com/packages/tools/animation/dotween-hotween-v2-27676r) and define the scripting define symbol `UNITASK_DOTWEEN_SUPPORT` to enable it.
```csharp ```csharp
// sequential // sequential
@@ -724,7 +717,7 @@ Async LINQ is enabled when `using Cysharp.Threading.Tasks.Linq;`, and `UniTaskAs
It's closer to UniRx (Reactive Extensions), but UniTaskAsyncEnumerable is a pull-based asynchronous stream, whereas Rx was a push-based asynchronous stream. Note that although similar, the characteristics are different and the details behave differently along with them. It's closer to UniRx (Reactive Extensions), but UniTaskAsyncEnumerable is a pull-based asynchronous stream, whereas Rx was a push-based asynchronous stream. Note that although similar, the characteristics are different and the details behave differently along with them.
`UniTaskAsyncEnumerable` is the entry point like `Enumerable`. In addition to the standard query operators, there are other generators for Unity such as `EveryUpdate`, `Timer`, `TimerFrame`, `Interval`, `IntervalFrame`, and `EveryValueChanged`. And also added additional UniTask original query operators like `Append`, `Prepend`, `DistinctUntilChanged`, `ToHashSet`, `Buffer`, `CombineLatest`,`Merge` `Do`, `Never`, `ForEachAsync`, `Pairwise`, `Publish`, `Queue`, `Return`, `SkipUntil`, `TakeUntil`, `SkipUntilCanceled`, `TakeUntilCanceled`, `TakeLast`, `Subscribe`. `UniTaskAsyncEnumerable` is the entry point like `Enumerable`. In addition to the standard query operators, there are other generators for Unity such as `EveryUpdate`, `Timer`, `TimerFrame`, `Interval`, `IntervalFrame`, and `EveryValueChanged`. And also added additional UniTask original query operators like `Append`, `Prepend`, `DistinctUntilChanged`, `ToHashSet`, `Buffer`, `CombineLatest`, `Do`, `Never`, `ForEachAsync`, `Pairwise`, `Publish`, `Queue`, `Return`, `SkipUntil`, `TakeUntil`, `SkipUntilCanceled`, `TakeUntilCanceled`, `TakeLast`, `Subscribe`.
The method with Func as an argument has three additional overloads, `***Await`, `***AwaitWithCancellation`. The method with Func as an argument has three additional overloads, `***Await`, `***AwaitWithCancellation`.
@@ -812,13 +805,32 @@ async UniTask TripleClick(CancellationToken token)
} }
``` ```
All MonoBehaviour message events can convert async-streams by `AsyncTriggers` that can be enabled by `using Cysharp.Threading.Tasks.Triggers;`. AsyncTrigger can be created using `GetAsync***Trigger` and triggers itself as UniTaskAsyncEnumerable. All MonoBehaviour message events can convert async-streams by `AsyncTriggers` that can be enabled by `using Cysharp.Threading.Tasks.Triggers;`.
```csharp ```csharp
var trigger = this.GetOnCollisionEnterAsyncHandler(); using Cysharp.Threading.Tasks.Triggers;
await trigger.OnCollisionEnterAsync();
await trigger.OnCollisionEnterAsync(); async UniTaskVoid MonitorCollision()
await trigger.OnCollisionEnterAsync(); {
await gameObject.OnCollisionEnterAsync();
Debug.Log("Collision Enter");
/* do anything */
await gameObject.OnCollisionExitAsync();
Debug.Log("Collision Exit");
}
```
Similar to uGUI event, AsyncTrigger can be created using `GetAsync***Trigger` and triggers itself as UniTaskAsyncEnumerable.
```csharp
// use await multiple times, get AsyncTriggerHandler is more efficient.
using(var trigger = this.GetOnCollisionEnterAsyncHandler())
{
await OnCollisionEnterAsync();
await OnCollisionEnterAsync();
await OnCollisionEnterAsync();
}
// every moves. // every moves.
await this.GetAsyncMoveTrigger().ForEachAsync(axisEventData => await this.GetAsyncMoveTrigger().ForEachAsync(axisEventData =>
@@ -846,9 +858,9 @@ rp.WithoutCurrent().BindTo(this.textComponent);
await rp.WaitAsync(); // wait until next value set await rp.WaitAsync(); // wait until next value set
// also exists ToReadOnlyAsyncReactiveProperty // also exists ToReadOnlyReactiveProperty
var rp2 = new AsyncReactiveProperty<int>(99); var rp2 = new AsyncReactiveProperty<int>(99);
var rorp = rp.CombineLatest(rp2, (x, y) => (x, y)).ToReadOnlyAsyncReactiveProperty(CancellationToken.None); var rorp = rp.CombineLatest(rp2, (x, y) => (x, y)).ToReadOnlyReactiveProperty();
``` ```
A pull-type asynchronous stream does not get the next values until the asynchronous processing in the sequence is complete. This could spill data from push-type events such as buttons. A pull-type asynchronous stream does not get the next values until the asynchronous processing in the sequence is complete. This could spill data from push-type events such as buttons.
@@ -954,9 +966,9 @@ UniTask's own unit tests are written using Unity Test Runner and [Cysharp/Runtim
ThreadPool limitation ThreadPool limitation
--- ---
Most UniTask methods run on a single thread (PlayerLoop), with only `UniTask.Run`(`Task.Run` equivalent) and `UniTask.SwitchToThreadPool` running on a thread pool. If you use a thread pool, it won't work with WebGL and so on. Most UniTask methods run on a single thread (PlayerLoop), with only `UniTask.Run` and `UniTask.SwitchToThreadPool` running on a thread pool. If you use a thread pool, it won't work with WebGL and so on.
`UniTask.Run` is now deprecated. You can use `UniTask.RunOnThreadPool` instead. And also consider whether you can use `UniTask.Create` or `UniTask.Void`. `UniTask.Run` will be deprecated in the future (marked with an Obsolete) and only `RunOnThreadPool` will be used. If you use `UniTask.Run`, consider whether you can use `UniTask.Create` or `UniTask.Void`.
IEnumerator.ToUniTask limitation IEnumerator.ToUniTask limitation
--- ---
@@ -1011,7 +1023,7 @@ Use UniTask type.
| `IAsyncDisposable` | `IUniTaskAsyncDisposable` | | `IAsyncDisposable` | `IUniTaskAsyncDisposable` |
| `Task.Delay` | `UniTask.Delay` | | `Task.Delay` | `UniTask.Delay` |
| `Task.Yield` | `UniTask.Yield` | | `Task.Yield` | `UniTask.Yield` |
| `Task.Run` | `UniTask.RunOnThreadPool` | | `Task.Run` | `UniTask.Run` |
| `Task.WhenAll` | `UniTask.WhenAll` | | `Task.WhenAll` | `UniTask.WhenAll` |
| `Task.WhenAny` | `UniTask.WhenAny` | | `Task.WhenAny` | `UniTask.WhenAny` |
| `Task.CompletedTask` | `UniTask.CompletedTask` | | `Task.CompletedTask` | `UniTask.CompletedTask` |
@@ -1079,6 +1091,13 @@ or add `"com.cysharp.unitask": "https://github.com/Cysharp/UniTask.git?path=src/
If you want to set a target version, UniTask uses the `*.*.*` release tag so you can specify a version like `#2.1.0`. For example `https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask#2.1.0`. If you want to set a target version, UniTask uses the `*.*.*` release tag so you can specify a version like `#2.1.0`. For example `https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask#2.1.0`.
### Install via OpenUPM
The package is available on the [openupm registry](https://openupm.com). It's recommended to install it via [openupm-cli](https://github.com/openupm/openupm-cli).
```
openupm add com.cysharp.unitask
```
.NET Core .NET Core
--- ---
@@ -1088,7 +1107,7 @@ For .NET Core, use NuGet.
UniTask of .NET Core version is a subset of Unity UniTask with PlayerLoop dependent methods removed. UniTask of .NET Core version is a subset of Unity UniTask with PlayerLoop dependent methods removed.
It runs at higher performance than the standard Task/ValueTask, but you should be careful to ignore the ExecutionContext/SynchronizationContext when using it. `AsyncLocal` also does not work because it ignores ExecutionContext. It runs at higher performance than the standard Task/ValueTask, but you should be careful to ignore the ExecutionContext/SynchronizationContext when using it. `AysncLocal` also does not work because it ignores ExecutionContext.
If you use UniTask internally, but provide ValueTask as an external API, you can write it like the following(Inspired by [PooledAwait](https://github.com/mgravell/PooledAwait)). If you use UniTask internally, but provide ValueTask as an external API, you can write it like the following(Inspired by [PooledAwait](https://github.com/mgravell/PooledAwait)).

File diff suppressed because it is too large Load Diff

View File

@@ -1,15 +1,13 @@
 
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17 # Visual Studio Version 16
VisualStudioVersion = 17.0.31606.5 VisualStudioVersion = 16.0.29613.14
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniTask.NetCoreTests", "src\UniTask.NetCoreTests\UniTask.NetCoreTests.csproj", "{B3E311A4-70D8-4131-9965-C073A99D201A}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniTask.NetCoreTests", "src\UniTask.NetCoreTests\UniTask.NetCoreTests.csproj", "{B3E311A4-70D8-4131-9965-C073A99D201A}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniTask.NetCore", "src\UniTask.NetCore\UniTask.NetCore.csproj", "{16EE20D0-7FB1-483A-8467-A5EEDBF1F5BF}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniTask.NetCore", "src\UniTask.NetCore\UniTask.NetCore.csproj", "{16EE20D0-7FB1-483A-8467-A5EEDBF1F5BF}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniTask.NetCoreSandbox", "src\UniTask.NetCoreSandbox\UniTask.NetCoreSandbox.csproj", "{3915E72E-33E0-4A14-A6D8-872702200E58}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniTask.NetCoreSandbox", "src\UniTask.NetCoreSandbox\UniTask.NetCoreSandbox.csproj", "{3915E72E-33E0-4A14-A6D8-872702200E58}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniTask.Analyzer", "src\UniTask.Analyzer\UniTask.Analyzer.csproj", "{0AC6F052-A255-4EE3-9E05-1C02D49AB1C2}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -29,10 +27,6 @@ Global
{3915E72E-33E0-4A14-A6D8-872702200E58}.Debug|Any CPU.Build.0 = Debug|Any CPU {3915E72E-33E0-4A14-A6D8-872702200E58}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3915E72E-33E0-4A14-A6D8-872702200E58}.Release|Any CPU.ActiveCfg = Release|Any CPU {3915E72E-33E0-4A14-A6D8-872702200E58}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3915E72E-33E0-4A14-A6D8-872702200E58}.Release|Any CPU.Build.0 = Release|Any CPU {3915E72E-33E0-4A14-A6D8-872702200E58}.Release|Any CPU.Build.0 = Release|Any CPU
{0AC6F052-A255-4EE3-9E05-1C02D49AB1C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0AC6F052-A255-4EE3-9E05-1C02D49AB1C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0AC6F052-A255-4EE3-9E05-1C02D49AB1C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0AC6F052-A255-4EE3-9E05-1C02D49AB1C2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@@ -1,8 +0,0 @@
{
"profiles": {
"UniTask.Analyzer": {
"commandName": "DebugRoslynComponent",
"targetProject": "..\\UniTask.NetCoreSandbox\\UniTask.NetCoreSandbox.csproj"
}
}
}

View File

@@ -1,29 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>library</OutputType>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<IsRoslynComponent>true</IsRoslynComponent>
<TargetsForTfmSpecificContentInPackage>$(TargetsForTfmSpecificContentInPackage);PackBuildOutputs</TargetsForTfmSpecificContentInPackage>
<IncludeBuildOutput>false</IncludeBuildOutput>
<IncludeSymbols>false</IncludeSymbols>
<SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
<DevelopmentDependency>true</DevelopmentDependency>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.8.0" />
</ItemGroup>
<Target Name="PackBuildOutputs" DependsOnTargets="SatelliteDllsProjectOutputGroup;DebugSymbolsProjectOutputGroup">
<ItemGroup>
<TfmSpecificPackageFile Include="$(TargetDir)\*.dll" PackagePath="analyzers\dotnet\cs" />
<TfmSpecificPackageFile Include="@(SatelliteDllsProjectOutputGroupOutput->'%(FinalOutputPath)')" PackagePath="analyzers\dotnet\cs\%(SatelliteDllsProjectOutputGroupOutput.Culture)\" />
</ItemGroup>
</Target>
</Project>

View File

@@ -1,54 +0,0 @@
#pragma warning disable RS2008
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
using System.Collections.Immutable;
using System.Threading;
namespace UniTask.Analyzer
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class UniTaskAnalyzer : DiagnosticAnalyzer
{
private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(
id: "UNITASK001",
title: "UniTaskAnalyzer001: Must pass CancellationToken",
messageFormat: "Must pass CancellationToken",
category: "Usage",
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true,
description: "Pass CancellationToken or CancellationToken.None.");
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(Rule); } }
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
context.RegisterOperationAction(AnalyzeOperation, OperationKind.Invocation);
}
private static void AnalyzeOperation(OperationAnalysisContext context)
{
var token = context.Compilation.GetTypeByMetadataName(typeof(CancellationToken).FullName);
if (token == null) return;
if (context.Operation is IInvocationOperation invocation)
{
foreach (var arg in invocation.Arguments)
{
if (arg.ArgumentKind == ArgumentKind.DefaultValue)
{
if (SymbolEqualityComparer.Default.Equals(arg.Parameter.Type, token))
{
var diagnostic = Diagnostic.Create(Rule, arg.Syntax.GetLocation());
context.ReportDiagnostic(diagnostic);
}
}
}
}
}
}
}

View File

@@ -1,12 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net6.0;net7.0;netstandard2.1;netstandard2.0</TargetFrameworks> <TargetFrameworks>netcoreapp3.1;netstandard2.1;netstandard2.0</TargetFrameworks>
<AssemblyName>UniTask</AssemblyName> <AssemblyName>UniTask</AssemblyName>
<LangVersion>8.0</LangVersion> <LangVersion>8.0</LangVersion>
<RootNamespace>Cysharp.Threading.Tasks</RootNamespace> <RootNamespace>Cysharp.Threading.Tasks</RootNamespace>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<!-- NuGet Packaging --> <!-- NuGet Packaging -->
<Id>UniTask</Id> <Id>UniTask</Id>
@@ -23,7 +21,6 @@
<PackageIcon>Icon.png</PackageIcon> <PackageIcon>Icon.png</PackageIcon>
<SignAssembly>true</SignAssembly> <SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>opensource.snk</AssemblyOriginatorKeyFile> <AssemblyOriginatorKeyFile>opensource.snk</AssemblyOriginatorKeyFile>
<IsPackable>true</IsPackable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

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

View File

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

View File

@@ -17,34 +17,499 @@ using System.Reactive.Concurrency;
namespace NetCoreSandbox namespace NetCoreSandbox
{ {
public class Program public class MySyncContext : SynchronizationContext
{ {
static async Task Main(string[] args) public MySyncContext()
{ {
var cts = new CancellationTokenSource();
// OK.
await FooAsync(10, cts.Token);
// NG(Compiler Error)
// await FooAsync(10);
} }
static async UniTask FooAsync(int x, CancellationToken cancellationToken = default) public override void Post(SendOrPostCallback d, object state)
{ {
await UniTask.Yield(); Console.WriteLine("Called SyncContext Post!");
base.Post(d, state);
} }
} }
public class Text
{
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
{
}
class Foo
{
public async UniTask MethodFooAsync()
{
await MethodBarAsync();
}
private async UniTask MethodBarAsync()
{
Throw();
}
private void Throw()
{
throw new Exception();
}
}
public struct TestAwaiter : ICriticalNotifyCompletion
{
readonly UniTaskStatus status;
readonly bool isCompleted;
public TestAwaiter(bool isCompleted, UniTaskStatus status)
{
this.isCompleted = isCompleted;
this.status = status;
}
public TestAwaiter GetAwaiter() => this;
public bool IsCompleted => isCompleted;
public void GetResult()
{
switch (status)
{
case UniTaskStatus.Faulted:
throw new TaskTestException();
case UniTaskStatus.Canceled:
throw new OperationCanceledException();
case UniTaskStatus.Pending:
case UniTaskStatus.Succeeded:
default:
break;
}
}
public void OnCompleted(Action continuation)
{
ThreadPool.QueueUserWorkItem(_ => continuation(), null);
}
public void UnsafeOnCompleted(Action continuation)
{
ThreadPool.UnsafeQueueUserWorkItem(_ => continuation(), null);
}
}
public struct TestAwaiter<T> : ICriticalNotifyCompletion
{
readonly UniTaskStatus status;
readonly bool isCompleted;
readonly T value;
public TestAwaiter(bool isCompleted, UniTaskStatus status, T value)
{
this.isCompleted = isCompleted;
this.status = status;
this.value = value;
}
public TestAwaiter<T> GetAwaiter() => this;
public bool IsCompleted => isCompleted;
public T GetResult()
{
switch (status)
{
case UniTaskStatus.Faulted:
throw new TaskTestException();
case UniTaskStatus.Canceled:
throw new OperationCanceledException();
case UniTaskStatus.Pending:
case UniTaskStatus.Succeeded:
default:
return value;
}
}
public void OnCompleted(Action continuation)
{
ThreadPool.QueueUserWorkItem(_ => continuation(), null);
}
public void UnsafeOnCompleted(Action continuation)
{
ThreadPool.UnsafeQueueUserWorkItem(_ => continuation(), null);
}
}
public static partial class UnityUIComponentExtensions
{
public static void BindTo(this IUniTaskAsyncEnumerable<string> source, Text text)
{
AAAACORECORE(source, text).Forget();
async UniTaskVoid AAAACORECORE(IUniTaskAsyncEnumerable<string> source2, Text text2)
{
var e = source2.GetAsyncEnumerator();
try
{
while (await e.MoveNextAsync())
{
text2.text = e.Current;
// action(e.Current);
}
}
finally
{
if (e != null)
{
await e.DisposeAsync();
}
}
}
}
//public static IDisposable SubscribeToText<T>(this IObservable<T> source, Text text)
//{
// return source.SubscribeWithState(text, (x, t) => t.text = x.ToString());
//}
//public static IDisposable SubscribeToText<T>(this IObservable<T> source, Text text, Func<T, string> selector)
//{
// return source.SubscribeWithState2(text, selector, (x, t, s) => t.text = s(x));
//}
//public static IDisposable SubscribeToInteractable(this IObservable<bool> source, Selectable selectable)
//{
// return source.SubscribeWithState(selectable, (x, s) => s.interactable = x);
//}
}
class Program
{
static string FlattenGenArgs(Type type)
{
if (type.IsGenericType)
{
var t = string.Join(", ", type.GetGenericArguments().Select(x => FlattenGenArgs(x)));
return Regex.Replace(type.Name, "`.+", "") + "<" + t + ">";
}
//x.ReturnType.GetGenericArguments()
else
{
return type.Name;
}
}
static async IAsyncEnumerable<int> FooAsync([EnumeratorCancellation]CancellationToken cancellationToken = default)
{
yield return 1;
await Task.Delay(10, cancellationToken);
}
public class MyDisposable : IDisposable
{
public void Dispose()
{
}
}
static void Test()
{
var disp = new MyDisposable();
using var _ = new MyDisposable();
Console.WriteLine("tako");
}
static async UniTask FooBarAsync()
{
await using (UniTask.ReturnToCurrentSynchronizationContext())
{
await UniTask.SwitchToThreadPool();
}
}
static async UniTask Aaa()
{
await FooBarAsync();
Console.WriteLine("FooBarAsync End");
}
static async UniTask WhereSelect()
{
await foreach (var item in UniTaskAsyncEnumerable.Range(1, 10)
.SelectAwait(async x =>
{
await UniTask.Yield();
return x;
})
.Where(x => x % 2 == 0))
{
Console.WriteLine(item);
}
}
static async Task Main(string[] args)
{
#if !DEBUG
//await new AllocationCheck().ViaUniTaskVoid();
//Console.ReadLine();
BenchmarkDotNet.Running.BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);
//await new ComparisonBenchmarks().ViaUniTaskT();
return;
#endif
var e = UniTaskAsyncEnumerable.Create<int>(async (writer, token) =>
{
for (int i = 0; i < 5; i++)
{
Console.WriteLine($"Start {i}");
await writer.YieldAsync(i);
Console.WriteLine($"End {i}");
}
});
var ee = e.GetAsyncEnumerator();
while (await ee.MoveNextAsync())
{
Console.WriteLine("ForEach " + ee.Current);
}
}
static async UniTask YieldCore()
{
await UniTask.Yield();
}
#pragma warning disable CS1998
static async UniTask<int> AsyncTest()
{
// empty
await new TestAwaiter(false, UniTaskStatus.Succeeded);
await new TestAwaiter(true, UniTaskStatus.Succeeded);
await new TestAwaiter(false, UniTaskStatus.Succeeded);
return 10;
}
#pragma warning restore CS1998
void Foo()
{
// AsyncEnumerable.Range(1,10).Do(
// AsyncEnumerable.t
var sb = new StringBuilder();
sb.AppendLine(@"using System;
using System.Threading;
namespace Cysharp.Threading.Tasks.Linq
{
");
var chako = typeof(AsyncEnumerable).GetMethods()
.OrderBy(x => x.Name)
.Select(x =>
{
var ret = FlattenGenArgs(x.ReturnType);
var generics = string.Join(", ", x.GetGenericArguments().Select(x => x.Name));
if (x.GetParameters().Length == 0) return "";
var self = x.GetParameters().First();
if (x.GetCustomAttributes(typeof(ExtensionAttribute), true).Length == 0)
{
return "";
}
var arg1Type = FlattenGenArgs(x.GetParameters().First().ParameterType);
var others = string.Join(", ", x.GetParameters().Skip(1).Select(y => FlattenGenArgs(y.ParameterType) + " " + y.Name));
if (!string.IsNullOrEmpty(others))
{
others = ", " + others;
}
var template = $"public static {ret} {x.Name}<{generics}>(this {arg1Type} {self.Name}{others})";
return template.Replace("ValueTask", "UniTask").Replace("IAsyncEnumerable", "IUniTaskAsyncEnumerable").Replace("<>", "");
})
.Where(x => x != "")
.Select(x => x + "\r\n{\r\n throw new NotImplementedException();\r\n}")
.ToArray();
var huga = string.Join("\r\n\r\n", chako);
foreach (var item in typeof(AsyncEnumerable).GetMethods().Select(x => x.Name).Distinct())
{
if (item.EndsWith("AwaitAsync") || item.EndsWith("AwaitWithCancellationAsync") || item.EndsWith("WithCancellation"))
{
continue;
}
var item2 = item.Replace("Async", "");
item2 = item2.Replace("Await", "");
var format = @"
internal sealed class {0}
{{
}}
";
sb.Append(string.Format(format, item2));
}
sb.Append("}");
Console.WriteLine(sb.ToString());
}
public static async IAsyncEnumerable<int> AsyncGen()
{
await UniTask.SwitchToThreadPool();
yield return 10;
await UniTask.SwitchToThreadPool();
yield return 100;
}
}
class MyEnumerable : IEnumerable<int>
{
public IEnumerator<int> GetEnumerator()
{
return new MyEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
}
class MyEnumerator : IEnumerator<int>
{
public int Current => throw new NotImplementedException();
object IEnumerator.Current => throw new NotImplementedException();
public void Dispose()
{
Console.WriteLine("Called Dispose");
}
public bool MoveNext()
{
throw new NotImplementedException();
}
public void Reset()
{
throw new NotImplementedException();
}
}
public class MyClass<T>
{
public CustomAsyncEnumerator<T> GetAsyncEnumerator()
{
//IAsyncEnumerable
return new CustomAsyncEnumerator<T>();
}
}
public struct CustomAsyncEnumerator<T>
{
int count;
public T Current
{
get
{
return default;
}
}
public UniTask<bool> MoveNextAsync()
{
if (count++ == 3)
{
return UniTask.FromResult(false);
//return false;
}
return UniTask.FromResult(true);
}
public UniTask DisposeAsync()
{
return default;
}
}
} }

View File

@@ -0,0 +1,473 @@
using BenchmarkDotNet.Attributes;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
[Config(typeof(BenchmarkConfig))]
public class QueueCheck
{
Node node1 = new Node();
Node node2 = new Node();
RefNode refNode1 = new RefNode();
RefNode refNode2 = new RefNode();
Queue<Node> q1 = new Queue<Node>();
Stack<Node> s1 = new Stack<Node>();
ConcurrentQueue<Node> cq = new ConcurrentQueue<Node>();
ConcurrentStack<Node> cs = new ConcurrentStack<Node>();
static TaskPool<Node> pool;
static TaskPoolRefNode<RefNode> poolRefNode;
static TaskPoolEqualNull<Node> poolEqualNull;
static TaskPoolClass<Node> poolClass = new TaskPoolClass<Node>();
static TaskPoolWithoutSize<Node> poolWithoutSize;
static TaskPoolWithoutLock<Node> poolWithoutLock;
[Benchmark]
public void Queue()
{
q1.Enqueue(node1);
q1.Enqueue(node1);
q1.TryDequeue(out _);
q1.TryDequeue(out _);
}
[Benchmark]
public void QueueLock()
{
lock (q1) { q1.Enqueue(node1); }
lock (q1) { q1.Enqueue(node1); }
lock (q1) { q1.TryDequeue(out _); }
lock (q1) { q1.TryDequeue(out _); }
}
[Benchmark]
public void Stack()
{
s1.Push(node1);
s1.Push(node2);
s1.TryPop(out _);
s1.TryPop(out _);
}
[Benchmark]
public void StackLock()
{
lock (s1) { s1.Push(node1); }
lock (s1) { s1.Push(node2); }
lock (s1) { s1.TryPop(out _); }
lock (s1) { s1.TryPop(out _); }
}
[Benchmark]
public void ConcurrentQueue()
{
cq.Enqueue(node1);
cq.Enqueue(node1);
cq.TryDequeue(out _);
cq.TryDequeue(out _);
}
[Benchmark]
public void ConcurrentStack()
{
cs.Push(node1);
cs.Push(node2);
cs.TryPop(out _);
cs.TryPop(out _);
}
[Benchmark]
public void TaskPool()
{
pool.TryPush(node1);
pool.TryPush(node2);
pool.TryPop(out _);
pool.TryPop(out _);
}
[Benchmark]
public void TaskPoolRefNode()
{
poolRefNode.TryPush(refNode1);
poolRefNode.TryPush(refNode2);
poolRefNode.TryPop(out _);
poolRefNode.TryPop(out _);
}
[Benchmark]
public void TaskPoolEqualNull()
{
poolEqualNull.TryPush(node1);
poolEqualNull.TryPush(node2);
poolEqualNull.TryPop(out _);
poolEqualNull.TryPop(out _);
}
[Benchmark]
public void TaskPoolClass()
{
poolClass.TryPush(node1);
poolClass.TryPush(node2);
poolClass.TryPop(out _);
poolClass.TryPop(out _);
}
[Benchmark]
public void TaskPoolWithoutSize()
{
poolWithoutSize.TryPush(node1);
poolWithoutSize.TryPush(node2);
poolWithoutSize.TryPop(out _);
poolWithoutSize.TryPop(out _);
}
[Benchmark]
public void TaskPoolWithoutLock()
{
poolWithoutLock.TryPush(node1);
poolWithoutLock.TryPush(node2);
poolWithoutLock.TryPop(out _);
poolWithoutLock.TryPop(out _);
}
}
public sealed class Node : ITaskPoolNode<Node>
{
public Node NextNode { get; set; }
}
public interface ITaskPoolNode<T>
{
T NextNode { get; set; }
}
public sealed class RefNode :ITaskPoolRefNode<RefNode>
{
RefNode nextNode;
public ref RefNode NextNode => ref nextNode;
}
public interface ITaskPoolRefNode<T>
{
ref T NextNode { get; }
}
// mutable struct, don't mark readonly.
[StructLayout(LayoutKind.Auto)]
public struct TaskPoolWithoutLock<T>
where T : class, ITaskPoolNode<T>
{
int size;
T root;
public int Size => size;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryPop(out T result)
{
//if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
{
var v = root;
if (!(v is null))
{
root = v.NextNode;
v.NextNode = null;
size--;
result = v;
// Volatile.Write(ref gate, 0);
return true;
}
//Volatile.Write(ref gate, 0);
}
result = default;
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryPush(T item)
{
//if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
{
//if (size < TaskPool.MaxPoolSize)
{
item.NextNode = root;
root = item;
size++;
// Volatile.Write(ref gate, 0);
return true;
}
//else
{
// Volatile.Write(ref gate, 0);
}
}
//return false;
}
}
[StructLayout(LayoutKind.Auto)]
public struct TaskPool<T>
where T : class, ITaskPoolNode<T>
{
int gate;
int size;
T root;
public int Size => size;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryPop(out T result)
{
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
{
var v = root;
if (!(v is null))
{
root = v.NextNode;
v.NextNode = null;
size--;
result = v;
Volatile.Write(ref gate, 0);
return true;
}
Volatile.Write(ref gate, 0);
}
result = default;
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryPush(T item)
{
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
{
//if (size < TaskPool.MaxPoolSize)
{
item.NextNode = root;
root = item;
size++;
Volatile.Write(ref gate, 0);
return true;
}
//else
{
// Volatile.Write(ref gate, 0);
}
}
return false;
}
}
[StructLayout(LayoutKind.Auto)]
public struct TaskPoolRefNode<T>
where T : class, ITaskPoolRefNode<T>
{
int gate;
int size;
T root;
public int Size => size;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryPop(out T result)
{
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
{
var v = root;
if (!(v is null))
{
ref var nextNode = ref v.NextNode;
root = nextNode;
nextNode = null;
size--;
result = v;
Volatile.Write(ref gate, 0);
return true;
}
Volatile.Write(ref gate, 0);
}
result = default;
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryPush(T item)
{
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
{
//if (size < TaskPool.MaxPoolSize)
{
item.NextNode = root;
root = item;
size++;
Volatile.Write(ref gate, 0);
return true;
}
//else
{
// Volatile.Write(ref gate, 0);
}
}
return false;
}
}
[StructLayout(LayoutKind.Auto)]
public struct TaskPoolEqualNull<T>
where T : class, ITaskPoolNode<T>
{
int gate;
int size;
T root;
public int Size => size;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryPop(out T result)
{
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
{
var v = root;
if (v != null)
{
root = v.NextNode;
v.NextNode = null;
size--;
result = v;
Volatile.Write(ref gate, 0);
return true;
}
Volatile.Write(ref gate, 0);
}
result = default;
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryPush(T item)
{
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
{
//if (size < TaskPool.MaxPoolSize)
{
item.NextNode = root;
root = item;
size++;
Volatile.Write(ref gate, 0);
return true;
}
//else
{
// Volatile.Write(ref gate, 0);
}
}
return false;
}
}
public class TaskPoolClass<T>
where T : class, ITaskPoolNode<T>
{
int gate;
int size;
T root;
public int Size => size;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryPop(out T result)
{
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
{
var v = root;
if (!(v is null))
{
root = v.NextNode;
v.NextNode = null;
size--;
result = v;
Volatile.Write(ref gate, 0);
return true;
}
Volatile.Write(ref gate, 0);
}
result = default;
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryPush(T item)
{
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
{
//if (size < TaskPool.MaxPoolSize)
{
item.NextNode = root;
root = item;
size++;
Volatile.Write(ref gate, 0);
return true;
}
//else
{
// Volatile.Write(ref gate, 0);
}
}
return false;
}
}
[StructLayout(LayoutKind.Auto)]
public struct TaskPoolWithoutSize<T>
where T : class, ITaskPoolNode<T>
{
int gate;
T root;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryPop(out T result)
{
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
{
var v = root;
if (!(v is null))
{
root = v.NextNode;
v.NextNode = null;
result = v;
Volatile.Write(ref gate, 0);
return true;
}
Volatile.Write(ref gate, 0);
}
result = default;
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryPush(T item)
{
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
{
//if (size < TaskPool.MaxPoolSize)
{
item.NextNode = root;
root = item;
Volatile.Write(ref gate, 0);
return true;
}
//else
{
// Volatile.Write(ref gate, 0);
}
}
return false;
}
}

View File

@@ -2,9 +2,8 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework> <TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace>NetCoreSandbox</RootNamespace> <RootNamespace>NetCoreSandbox</RootNamespace>
<IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -16,12 +15,6 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\UniTask.NetCore\UniTask.NetCore.csproj" /> <ProjectReference Include="..\UniTask.NetCore\UniTask.NetCore.csproj" />
<ProjectReference Include="..\UniTask.Analyzer\UniTask.Analyzer.csproj">
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
<OutputItemType>Analyzer</OutputItemType>
</ProjectReference>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -159,30 +159,6 @@ namespace NetCoreTests.Linq
list.Should().Equal(100, 200, 300, 400); list.Should().Equal(100, 200, 300, 400);
} }
[Fact]
public async Task AwaitForeachBreak()
{
var finallyCalled = false;
var enumerable = UniTaskAsyncEnumerable.Create<int>(async (writer, _) =>
{
try
{
await writer.YieldAsync(1);
}
finally
{
finallyCalled = true;
}
});
await foreach (var x in enumerable)
{
x.Should().Be(1);
break;
}
finallyCalled.Should().BeTrue();
}
async IAsyncEnumerable<int> Range(int from, int count) async IAsyncEnumerable<int> Range(int from, int count)
{ {
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)

View File

@@ -1,137 +0,0 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Cysharp.Threading.Tasks;
using Cysharp.Threading.Tasks.Linq;
using FluentAssertions;
using Xunit;
namespace NetCoreTests.Linq
{
public class MergeTest
{
[Fact]
public async Task TwoSource()
{
var semaphore = new SemaphoreSlim(1, 1);
var a = UniTaskAsyncEnumerable.Create<string>(async (writer, _) =>
{
await UniTask.SwitchToThreadPool();
await semaphore.WaitAsync();
await writer.YieldAsync("A1");
semaphore.Release();
await semaphore.WaitAsync();
await writer.YieldAsync("A2");
semaphore.Release();
});
var b = UniTaskAsyncEnumerable.Create<string>(async (writer, _) =>
{
await UniTask.SwitchToThreadPool();
await semaphore.WaitAsync();
await writer.YieldAsync("B1");
await writer.YieldAsync("B2");
semaphore.Release();
await semaphore.WaitAsync();
await writer.YieldAsync("B3");
semaphore.Release();
});
var result = await a.Merge(b).ToArrayAsync();
result.Should().Equal("A1", "B1", "B2", "A2", "B3");
}
[Fact]
public async Task ThreeSource()
{
var semaphore = new SemaphoreSlim(0, 1);
var a = UniTaskAsyncEnumerable.Create<string>(async (writer, _) =>
{
await UniTask.SwitchToThreadPool();
await semaphore.WaitAsync();
await writer.YieldAsync("A1");
semaphore.Release();
await semaphore.WaitAsync();
await writer.YieldAsync("A2");
semaphore.Release();
});
var b = UniTaskAsyncEnumerable.Create<string>(async (writer, _) =>
{
await UniTask.SwitchToThreadPool();
await semaphore.WaitAsync();
await writer.YieldAsync("B1");
await writer.YieldAsync("B2");
semaphore.Release();
await semaphore.WaitAsync();
await writer.YieldAsync("B3");
semaphore.Release();
});
var c = UniTaskAsyncEnumerable.Create<string>(async (writer, _) =>
{
await UniTask.SwitchToThreadPool();
await writer.YieldAsync("C1");
semaphore.Release();
});
var result = await a.Merge(b, c).ToArrayAsync();
result.Should().Equal("C1", "A1", "B1", "B2", "A2", "B3");
}
[Fact]
public async Task Throw()
{
var a = UniTaskAsyncEnumerable.Create<string>(async (writer, _) =>
{
await writer.YieldAsync("A1");
});
var b = UniTaskAsyncEnumerable.Create<string>(async (writer, _) =>
{
throw new UniTaskTestException();
});
var enumerator = a.Merge(b).GetAsyncEnumerator();
(await enumerator.MoveNextAsync()).Should().Be(true);
enumerator.Current.Should().Be("A1");
await Assert.ThrowsAsync<UniTaskTestException>(async () => await enumerator.MoveNextAsync());
}
[Fact]
public async Task Cancel()
{
var cts = new CancellationTokenSource();
var a = UniTaskAsyncEnumerable.Create<string>(async (writer, _) =>
{
await writer.YieldAsync("A1");
});
var b = UniTaskAsyncEnumerable.Create<string>(async (writer, _) =>
{
await writer.YieldAsync("B1");
});
var enumerator = a.Merge(b).GetAsyncEnumerator(cts.Token);
(await enumerator.MoveNextAsync()).Should().Be(true);
enumerator.Current.Should().Be("A1");
cts.Cancel();
await Assert.ThrowsAsync<OperationCanceledException>(async () => await enumerator.MoveNextAsync());
}
}
}

View File

@@ -1,43 +0,0 @@
using System;
using System.Threading.Tasks;
using Cysharp.Threading.Tasks;
using Xunit;
namespace NetCoreTests
{
public class TaskExtensionsTest
{
[Fact]
public async Task PropagateException()
{
await Assert.ThrowsAsync<InvalidOperationException>(async () =>
{
await ThrowAsync().AsUniTask();
});
await Assert.ThrowsAsync<InvalidOperationException>(async () =>
{
await ThrowOrValueAsync().AsUniTask();
});
}
[Fact]
public async Task PropagateExceptionWhenAll()
{
await Assert.ThrowsAsync<InvalidOperationException>(async () =>
{
await Task.WhenAll(ThrowAsync(), ThrowAsync()).AsUniTask();
});
}
async Task ThrowAsync()
{
throw new InvalidOperationException();
}
async Task<int> ThrowOrValueAsync()
{
throw new InvalidOperationException();
}
}
}

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0</TargetFramework> <TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>

View File

@@ -1,80 +0,0 @@
using System.Threading.Tasks;
using Cysharp.Threading.Tasks;
using FluentAssertions;
using NetCoreTests.Linq;
using Xunit;
namespace NetCoreTests
{
public class AutoResetUniTaskCompletionSourceTest
{
[Fact]
public async Task SetResultAfterReturn()
{
var source1 = AutoResetUniTaskCompletionSource.Create();
source1.TrySetResult();
await source1.Task;
source1.TrySetResult().Should().BeFalse();
var source2 = AutoResetUniTaskCompletionSource.Create();
source2.TrySetResult();
await source2.Task;
source2.TrySetResult().Should().BeFalse();
}
[Fact]
public async Task SetCancelAfterReturn()
{
var source = AutoResetUniTaskCompletionSource.Create();
source.TrySetResult();
await source.Task;
source.TrySetCanceled().Should().BeFalse();
}
[Fact]
public async Task SetExceptionAfterReturn()
{
var source = AutoResetUniTaskCompletionSource.Create();
source.TrySetResult();
await source.Task;
source.TrySetException(new UniTaskTestException()).Should().BeFalse();
}
[Fact]
public async Task SetResultWithValueAfterReturn()
{
var source1 = AutoResetUniTaskCompletionSource<int>.Create();
source1.TrySetResult(100);
(await source1.Task).Should().Be(100);
source1.TrySetResult(100).Should().BeFalse();
var source2 = AutoResetUniTaskCompletionSource.Create();
source2.TrySetResult();
await source2.Task;
source2.TrySetResult().Should().BeFalse();
}
[Fact]
public async Task SetCancelWithValueAfterReturn()
{
var source = AutoResetUniTaskCompletionSource<int>.Create();
source.TrySetResult(100);
(await source.Task).Should().Be(100);
source.TrySetCanceled().Should().BeFalse();
}
[Fact]
public async Task SetExceptionWithValueAfterReturn()
{
var source = AutoResetUniTaskCompletionSource<int>.Create();
source.TrySetResult(100);
(await source.Task).Should().Be(100);
source.TrySetException(new UniTaskTestException()).Should().BeFalse();
}
}
}

View File

@@ -22,9 +22,7 @@ public static class EditorRunnerChecker
//var r = await UnityWebRequest.Get("https://bing.com/").SendWebRequest().ToUniTask(); //var r = await UnityWebRequest.Get("https://bing.com/").SendWebRequest().ToUniTask();
//Debug.Log(r.downloadHandler.text.Substring(0, 100)); //Debug.Log(r.downloadHandler.text.Substring(0, 100));
//await UniTask.Yield(); await UniTask.Yield();
await UniTask.DelayFrame(30);
Debug.Log("End"); Debug.Log("End");
} }

View File

@@ -16,12 +16,7 @@
"name": "com.unity.addressables", "name": "com.unity.addressables",
"expression": "", "expression": "",
"define": "UNITASK_ADDRESSABLE_SUPPORT" "define": "UNITASK_ADDRESSABLE_SUPPORT"
},
{
"name": "com.unity.addressables.cn",
"expression": "",
"define": "UNITASK_ADDRESSABLE_SUPPORT"
} }
], ],
"noEngineReferences": false "noEngineReferences": false
} }

View File

@@ -16,14 +16,14 @@ namespace Cysharp.Threading.Tasks
Kill, Kill,
KillWithCompleteCallback, KillWithCompleteCallback,
Complete, Complete,
CompleteWithSequenceCallback, CompleteWithSeqeunceCallback,
CancelAwait, CancelAwait,
// AndCancelAwait // AndCancelAwait
KillAndCancelAwait, KillAndCancelAwait,
KillWithCompleteCallbackAndCancelAwait, KillWithCompleteCallbackAndCancelAwait,
CompleteAndCancelAwait, CompleteAndCancelAwait,
CompleteWithSequenceCallbackAndCancelAwait CompleteWithSeqeunceCallbackAndCancelAwait
} }
public static class DOTweenAsyncExtensions public static class DOTweenAsyncExtensions
@@ -143,21 +143,24 @@ namespace Cysharp.Threading.Tasks
TaskPool.RegisterSizeGetter(typeof(TweenConfiguredSource), () => pool.Size); TaskPool.RegisterSizeGetter(typeof(TweenConfiguredSource), () => pool.Size);
} }
static readonly TweenCallback EmptyTweenCallback = () => { };
readonly TweenCallback onCompleteCallbackDelegate; readonly TweenCallback onCompleteCallbackDelegate;
readonly TweenCallback onUpdateDelegate;
Tween tween; Tween tween;
TweenCancelBehaviour cancelBehaviour; TweenCancelBehaviour cancelBehaviour;
CancellationToken cancellationToken; CancellationToken cancellationToken;
CancellationTokenRegistration cancellationRegistration;
CallbackType callbackType; CallbackType callbackType;
bool canceled; bool canceled;
TweenCallback originalCompleteAction; TweenCallback originalUpdateAction;
UniTaskCompletionSourceCore<AsyncUnit> core; UniTaskCompletionSourceCore<AsyncUnit> core;
TweenConfiguredSource() TweenConfiguredSource()
{ {
onCompleteCallbackDelegate = OnCompleteCallbackDelegate; onCompleteCallbackDelegate = OnCompleteCallbackDelegate;
onUpdateDelegate = OnUpdate;
} }
public static IUniTaskSource Create(Tween tween, TweenCancelBehaviour cancelBehaviour, CancellationToken cancellationToken, CallbackType callbackType, out short token) public static IUniTaskSource Create(Tween tween, TweenCancelBehaviour cancelBehaviour, CancellationToken cancellationToken, CallbackType callbackType, out short token)
@@ -177,87 +180,41 @@ namespace Cysharp.Threading.Tasks
result.cancelBehaviour = cancelBehaviour; result.cancelBehaviour = cancelBehaviour;
result.cancellationToken = cancellationToken; result.cancellationToken = cancellationToken;
result.callbackType = callbackType; result.callbackType = callbackType;
result.originalUpdateAction = tween.onUpdate;
result.canceled = false; result.canceled = false;
if (result.originalUpdateAction == result.onUpdateDelegate)
{
result.originalUpdateAction = null;
}
tween.onUpdate = result.onUpdateDelegate;
switch (callbackType) switch (callbackType)
{ {
case CallbackType.Kill: case CallbackType.Kill:
result.originalCompleteAction = tween.onKill;
tween.onKill = result.onCompleteCallbackDelegate; tween.onKill = result.onCompleteCallbackDelegate;
break; break;
case CallbackType.Complete: case CallbackType.Complete:
result.originalCompleteAction = tween.onComplete;
tween.onComplete = result.onCompleteCallbackDelegate; tween.onComplete = result.onCompleteCallbackDelegate;
break; break;
case CallbackType.Pause: case CallbackType.Pause:
result.originalCompleteAction = tween.onPause;
tween.onPause = result.onCompleteCallbackDelegate; tween.onPause = result.onCompleteCallbackDelegate;
break; break;
case CallbackType.Play: case CallbackType.Play:
result.originalCompleteAction = tween.onPlay;
tween.onPlay = result.onCompleteCallbackDelegate; tween.onPlay = result.onCompleteCallbackDelegate;
break; break;
case CallbackType.Rewind: case CallbackType.Rewind:
result.originalCompleteAction = tween.onRewind;
tween.onRewind = result.onCompleteCallbackDelegate; tween.onRewind = result.onCompleteCallbackDelegate;
break; break;
case CallbackType.StepComplete: case CallbackType.StepComplete:
result.originalCompleteAction = tween.onStepComplete;
tween.onStepComplete = result.onCompleteCallbackDelegate; tween.onStepComplete = result.onCompleteCallbackDelegate;
break; break;
default: default:
break; break;
} }
if (result.originalCompleteAction == result.onCompleteCallbackDelegate)
{
result.originalCompleteAction = null;
}
if (cancellationToken.CanBeCanceled)
{
result.cancellationRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(x =>
{
var source = (TweenConfiguredSource)x;
switch (source.cancelBehaviour)
{
case TweenCancelBehaviour.Kill:
default:
source.tween.Kill(false);
break;
case TweenCancelBehaviour.KillAndCancelAwait:
source.canceled = true;
source.tween.Kill(false);
break;
case TweenCancelBehaviour.KillWithCompleteCallback:
source.tween.Kill(true);
break;
case TweenCancelBehaviour.KillWithCompleteCallbackAndCancelAwait:
source.canceled = true;
source.tween.Kill(true);
break;
case TweenCancelBehaviour.Complete:
source.tween.Complete(false);
break;
case TweenCancelBehaviour.CompleteAndCancelAwait:
source.canceled = true;
source.tween.Complete(false);
break;
case TweenCancelBehaviour.CompleteWithSequenceCallback:
source.tween.Complete(true);
break;
case TweenCancelBehaviour.CompleteWithSequenceCallbackAndCancelAwait:
source.canceled = true;
source.tween.Complete(true);
break;
case TweenCancelBehaviour.CancelAwait:
source.RestoreOriginalCallback();
source.core.TrySetCanceled(source.cancellationToken);
break;
}
}, result);
}
TaskTracker.TrackActiveTask(result, 3); TaskTracker.TrackActiveTask(result, 3);
token = result.core.Version; token = result.core.Version;
@@ -271,7 +228,7 @@ namespace Cysharp.Threading.Tasks
if (this.cancelBehaviour == TweenCancelBehaviour.KillAndCancelAwait if (this.cancelBehaviour == TweenCancelBehaviour.KillAndCancelAwait
|| this.cancelBehaviour == TweenCancelBehaviour.KillWithCompleteCallbackAndCancelAwait || this.cancelBehaviour == TweenCancelBehaviour.KillWithCompleteCallbackAndCancelAwait
|| this.cancelBehaviour == TweenCancelBehaviour.CompleteAndCancelAwait || this.cancelBehaviour == TweenCancelBehaviour.CompleteAndCancelAwait
|| this.cancelBehaviour == TweenCancelBehaviour.CompleteWithSequenceCallbackAndCancelAwait || this.cancelBehaviour == TweenCancelBehaviour.CompleteWithSeqeunceCallbackAndCancelAwait
|| this.cancelBehaviour == TweenCancelBehaviour.CancelAwait) || this.cancelBehaviour == TweenCancelBehaviour.CancelAwait)
{ {
canceled = true; canceled = true;
@@ -283,11 +240,81 @@ namespace Cysharp.Threading.Tasks
} }
else else
{ {
originalCompleteAction?.Invoke();
core.TrySetResult(AsyncUnit.Default); core.TrySetResult(AsyncUnit.Default);
} }
} }
void OnUpdate()
{
originalUpdateAction?.Invoke();
if (!cancellationToken.IsCancellationRequested)
{
return;
}
switch (this.cancelBehaviour)
{
case TweenCancelBehaviour.Kill:
default:
this.tween.Kill(false);
break;
case TweenCancelBehaviour.KillAndCancelAwait:
this.canceled = true;
this.tween.Kill(false);
break;
case TweenCancelBehaviour.KillWithCompleteCallback:
this.tween.Kill(true);
break;
case TweenCancelBehaviour.KillWithCompleteCallbackAndCancelAwait:
this.canceled = true;
this.tween.Kill(true);
break;
case TweenCancelBehaviour.Complete:
this.tween.Complete(false);
break;
case TweenCancelBehaviour.CompleteAndCancelAwait:
this.canceled = true;
this.tween.Complete(false);
break;
case TweenCancelBehaviour.CompleteWithSeqeunceCallback:
this.tween.Complete(true);
break;
case TweenCancelBehaviour.CompleteWithSeqeunceCallbackAndCancelAwait:
this.canceled = true;
this.tween.Complete(true);
break;
case TweenCancelBehaviour.CancelAwait:
// replace to empty(avoid callback after Canceled(instance is returned to pool.)
switch (callbackType)
{
case CallbackType.Kill:
tween.onKill = EmptyTweenCallback;
break;
case CallbackType.Complete:
tween.onComplete = EmptyTweenCallback;
break;
case CallbackType.Pause:
tween.onPause = EmptyTweenCallback;
break;
case CallbackType.Play:
tween.onPlay = EmptyTweenCallback;
break;
case CallbackType.Rewind:
tween.onRewind = EmptyTweenCallback;
break;
case CallbackType.StepComplete:
tween.onStepComplete = EmptyTweenCallback;
break;
default:
break;
}
this.core.TrySetCanceled(this.cancellationToken);
break;
}
}
static void DoCancelBeforeCreate(Tween tween, TweenCancelBehaviour tweenCancelBehaviour) static void DoCancelBeforeCreate(Tween tween, TweenCancelBehaviour tweenCancelBehaviour)
{ {
@@ -312,10 +339,10 @@ namespace Cysharp.Threading.Tasks
case TweenCancelBehaviour.CompleteAndCancelAwait: case TweenCancelBehaviour.CompleteAndCancelAwait:
tween.Complete(false); tween.Complete(false);
break; break;
case TweenCancelBehaviour.CompleteWithSequenceCallback: case TweenCancelBehaviour.CompleteWithSeqeunceCallback:
tween.Complete(true); tween.Complete(true);
break; break;
case TweenCancelBehaviour.CompleteWithSequenceCallbackAndCancelAwait: case TweenCancelBehaviour.CompleteWithSeqeunceCallbackAndCancelAwait:
tween.Complete(true); tween.Complete(true);
break; break;
case TweenCancelBehaviour.CancelAwait: case TweenCancelBehaviour.CancelAwait:
@@ -354,41 +381,36 @@ namespace Cysharp.Threading.Tasks
{ {
TaskTracker.RemoveTracking(this); TaskTracker.RemoveTracking(this);
core.Reset(); core.Reset();
cancellationRegistration.Dispose(); tween.onUpdate = originalUpdateAction;
RestoreOriginalCallback();
tween = default;
cancellationToken = default;
originalCompleteAction = default;
return pool.TryPush(this);
}
void RestoreOriginalCallback()
{
switch (callbackType) switch (callbackType)
{ {
case CallbackType.Kill: case CallbackType.Kill:
tween.onKill = originalCompleteAction; tween.onKill = null;
break; break;
case CallbackType.Complete: case CallbackType.Complete:
tween.onComplete = originalCompleteAction; tween.onComplete = null;
break; break;
case CallbackType.Pause: case CallbackType.Pause:
tween.onPause = originalCompleteAction; tween.onPause = null;
break; break;
case CallbackType.Play: case CallbackType.Play:
tween.onPlay = originalCompleteAction; tween.onPlay = null;
break; break;
case CallbackType.Rewind: case CallbackType.Rewind:
tween.onRewind = originalCompleteAction; tween.onRewind = null;
break; break;
case CallbackType.StepComplete: case CallbackType.StepComplete:
tween.onStepComplete = originalCompleteAction; tween.onStepComplete = null;
break; break;
default: default:
break; break;
} }
tween = default;
cancellationToken = default;
originalUpdateAction = default;
return pool.TryPush(this);
} }
} }
} }

View File

@@ -148,7 +148,7 @@ namespace Cysharp.Threading.Tasks.Internal
foreach (var candidateMethod in methods) foreach (var candidateMethod in methods)
{ {
var attributes = candidateMethod.GetCustomAttributes<StateMachineAttribute>(false); var attributes = candidateMethod.GetCustomAttributes<StateMachineAttribute>();
if (attributes == null) if (attributes == null)
{ {
continue; continue;

View File

@@ -39,7 +39,7 @@ namespace Cysharp.Threading.Tasks.Internal
} }
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
public static void ThrowArgumentException(string message) public static void ThrowArgumentException<T>(string message)
{ {
throw new ArgumentException(message); throw new ArgumentException(message);
} }

View File

@@ -52,7 +52,6 @@ namespace Cysharp.Threading.Tasks.Linq
public UniTask DisposeAsync() public UniTask DisposeAsync()
{ {
TaskTracker.RemoveTracking(this); TaskTracker.RemoveTracking(this);
writer.Dispose();
return default; return default;
} }
@@ -128,7 +127,7 @@ namespace Cysharp.Threading.Tasks.Linq
} }
} }
sealed class AsyncWriter : IUniTaskSource, IAsyncWriter<T>, IDisposable sealed class AsyncWriter : IUniTaskSource, IAsyncWriter<T>
{ {
readonly _Create enumerator; readonly _Create enumerator;
@@ -138,15 +137,6 @@ namespace Cysharp.Threading.Tasks.Linq
{ {
this.enumerator = enumerator; this.enumerator = enumerator;
} }
public void Dispose()
{
var status = core.GetStatus(core.Version);
if (status == UniTaskStatus.Pending)
{
core.TrySetCanceled();
}
}
public void GetResult(short token) public void GetResult(short token)
{ {

View File

@@ -1,232 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Cysharp.Threading.Tasks.Internal;
namespace Cysharp.Threading.Tasks.Linq
{
public static partial class UniTaskAsyncEnumerable
{
public static IUniTaskAsyncEnumerable<T> Merge<T>(this IUniTaskAsyncEnumerable<T> first, IUniTaskAsyncEnumerable<T> second)
{
Error.ThrowArgumentNullException(first, nameof(first));
Error.ThrowArgumentNullException(second, nameof(second));
return new Merge<T>(new [] { first, second });
}
public static IUniTaskAsyncEnumerable<T> Merge<T>(this IUniTaskAsyncEnumerable<T> first, IUniTaskAsyncEnumerable<T> second, IUniTaskAsyncEnumerable<T> third)
{
Error.ThrowArgumentNullException(first, nameof(first));
Error.ThrowArgumentNullException(second, nameof(second));
Error.ThrowArgumentNullException(third, nameof(third));
return new Merge<T>(new[] { first, second, third });
}
public static IUniTaskAsyncEnumerable<T> Merge<T>(this IEnumerable<IUniTaskAsyncEnumerable<T>> sources)
{
return new Merge<T>(sources.ToArray());
}
public static IUniTaskAsyncEnumerable<T> Merge<T>(params IUniTaskAsyncEnumerable<T>[] sources)
{
return new Merge<T>(sources);
}
}
internal sealed class Merge<T> : IUniTaskAsyncEnumerable<T>
{
readonly IUniTaskAsyncEnumerable<T>[] sources;
public Merge(IUniTaskAsyncEnumerable<T>[] sources)
{
if (sources.Length <= 0)
{
Error.ThrowArgumentException("No source async enumerable to merge");
}
this.sources = sources;
}
public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
=> new _Merge(sources, cancellationToken);
enum MergeSourceState
{
Pending,
Running,
Completed,
}
sealed class _Merge : MoveNextSource, IUniTaskAsyncEnumerator<T>
{
static readonly Action<object> GetResultAtAction = GetResultAt;
readonly int length;
readonly IUniTaskAsyncEnumerator<T>[] enumerators;
readonly MergeSourceState[] states;
readonly Queue<(T, Exception, bool)> queuedResult = new Queue<(T, Exception, bool)>();
readonly CancellationToken cancellationToken;
int moveNextCompleted;
public T Current { get; private set; }
public _Merge(IUniTaskAsyncEnumerable<T>[] sources, CancellationToken cancellationToken)
{
this.cancellationToken = cancellationToken;
length = sources.Length;
states = ArrayPool<MergeSourceState>.Shared.Rent(length);
enumerators = ArrayPool<IUniTaskAsyncEnumerator<T>>.Shared.Rent(length);
for (var i = 0; i < length; i++)
{
enumerators[i] = sources[i].GetAsyncEnumerator(cancellationToken);
states[i] = (int)MergeSourceState.Pending;;
}
}
public UniTask<bool> MoveNextAsync()
{
cancellationToken.ThrowIfCancellationRequested();
completionSource.Reset();
Interlocked.Exchange(ref moveNextCompleted, 0);
if (HasQueuedResult() && Interlocked.CompareExchange(ref moveNextCompleted, 1, 0) == 0)
{
(T, Exception, bool) value;
lock (states)
{
value = queuedResult.Dequeue();
}
var resultValue = value.Item1;
var exception = value.Item2;
var hasNext = value.Item3;
if (exception != null)
{
completionSource.TrySetException(exception);
}
else
{
Current = resultValue;
completionSource.TrySetResult(hasNext);
}
return new UniTask<bool>(this, completionSource.Version);
}
for (var i = 0; i < length; i++)
{
lock (states)
{
if (states[i] == MergeSourceState.Pending)
{
states[i] = MergeSourceState.Running;
}
else
{
continue;
}
}
var awaiter = enumerators[i].MoveNextAsync().GetAwaiter();
if (awaiter.IsCompleted)
{
GetResultAt(i, awaiter);
}
else
{
awaiter.SourceOnCompleted(GetResultAtAction, StateTuple.Create(this, i, awaiter));
}
}
return new UniTask<bool>(this, completionSource.Version);
}
public async UniTask DisposeAsync()
{
for (var i = 0; i < length; i++)
{
await enumerators[i].DisposeAsync();
}
ArrayPool<MergeSourceState>.Shared.Return(states, true);
ArrayPool<IUniTaskAsyncEnumerator<T>>.Shared.Return(enumerators, true);
}
static void GetResultAt(object state)
{
using (var tuple = (StateTuple<_Merge, int, UniTask<bool>.Awaiter>)state)
{
tuple.Item1.GetResultAt(tuple.Item2, tuple.Item3);
}
}
void GetResultAt(int index, UniTask<bool>.Awaiter awaiter)
{
bool hasNext;
bool completedAll;
try
{
hasNext = awaiter.GetResult();
}
catch (Exception ex)
{
if (Interlocked.CompareExchange(ref moveNextCompleted, 1, 0) == 0)
{
completionSource.TrySetException(ex);
}
else
{
lock (states)
{
queuedResult.Enqueue((default, ex, default));
}
}
return;
}
lock (states)
{
states[index] = hasNext ? MergeSourceState.Pending : MergeSourceState.Completed;
completedAll = !hasNext && IsCompletedAll();
}
if (hasNext || completedAll)
{
if (Interlocked.CompareExchange(ref moveNextCompleted, 1, 0) == 0)
{
Current = enumerators[index].Current;
completionSource.TrySetResult(!completedAll);
}
else
{
lock (states)
{
queuedResult.Enqueue((enumerators[index].Current, null, !completedAll));
}
}
}
}
bool HasQueuedResult()
{
lock (states)
{
return queuedResult.Count > 0;
}
}
bool IsCompletedAll()
{
lock (states)
{
for (var i = 0; i < length; i++)
{
if (states[i] != MergeSourceState.Completed)
{
return false;
}
}
}
return true;
}
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: ca56812f160c45d0bacb4339819edf1a
timeCreated: 1694133666

View File

@@ -285,11 +285,7 @@ namespace Cysharp.Threading.Tasks
return dest.ToArray(); return dest.ToArray();
} }
#if UNITY_2020_1_OR_NEWER [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]
#else
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
#endif
static void Init() static void Init()
{ {
// capture default(unity) sync-context. // capture default(unity) sync-context.
@@ -528,14 +524,6 @@ namespace Cysharp.Threading.Tasks
{ {
sb.AppendFormat("------{0}------", header.type.Name); sb.AppendFormat("------{0}------", header.type.Name);
sb.AppendLine(); sb.AppendLine();
if (header.subSystemList is null)
{
sb.AppendFormat("{0} has no subsystems!", header.ToString());
sb.AppendLine();
continue;
}
foreach (var subSystem in header.subSystemList) foreach (var subSystem in header.subSystemList)
{ {
sb.AppendFormat("{0}", subSystem.type.Name); sb.AppendFormat("{0}", subSystem.type.Name);
@@ -557,11 +545,6 @@ namespace Cysharp.Threading.Tasks
foreach (var header in playerLoop.subSystemList) foreach (var header in playerLoop.subSystemList)
{ {
if (header.subSystemList is null)
{
continue;
}
foreach (var subSystem in header.subSystemList) foreach (var subSystem in header.subSystemList)
{ {
if (subSystem.type == typeof(UniTaskLoopRunners.UniTaskLoopRunnerInitialization)) if (subSystem.type == typeof(UniTaskLoopRunners.UniTaskLoopRunnerInitialization))

View File

@@ -7,7 +7,7 @@ using System.Threading;
namespace Cysharp.Threading.Tasks namespace Cysharp.Threading.Tasks
{ {
// internally used but public, allow to user create custom operator with pooling. // internaly used but public, allow to user create custom operator with pooling.
public static class TaskPool public static class TaskPool
{ {

View File

@@ -99,7 +99,7 @@ namespace Cysharp.Threading.Tasks
public void Reset() public void Reset()
{ {
timer?.Stop(); timer.Stop();
} }
public void Dispose() public void Dispose()
@@ -109,7 +109,7 @@ namespace Cysharp.Threading.Tasks
try try
{ {
// stop timer. // stop timer.
timer?.Dispose(); timer.Dispose();
// cancel and dispose. // cancel and dispose.
timeoutSource.Cancel(); timeoutSource.Cancel();

View File

@@ -20,6 +20,8 @@ namespace Cysharp.Threading.Tasks
{ {
ITriggerHandler<T> head; // head.prev is last ITriggerHandler<T> head; // head.prev is last
ITriggerHandler<T> iteratingHead; ITriggerHandler<T> iteratingHead;
bool preserveRemoveSelf;
ITriggerHandler<T> iteratingNode; ITriggerHandler<T> iteratingNode;
void LogError(Exception ex) void LogError(Exception ex)
@@ -53,9 +55,18 @@ namespace Cysharp.Threading.Tasks
Remove(h); Remove(h);
} }
// If `h` itself is removed by OnNext, h.Next is null. if (preserveRemoveSelf)
// Therefore, instead of looking at h.Next, the `iteratingNode` reference itself is replaced. {
h = h == iteratingNode ? h.Next : iteratingNode; preserveRemoveSelf = false;
iteratingNode = null;
var next = h.Next;
Remove(h);
h = next;
}
else
{
h = h.Next;
}
} }
iteratingNode = null; iteratingNode = null;
@@ -86,8 +97,9 @@ namespace Cysharp.Threading.Tasks
LogError(ex); LogError(ex);
} }
var next = h == iteratingNode ? h.Next : iteratingNode; preserveRemoveSelf = false;
iteratingNode = null; iteratingNode = null;
var next = h.Next;
Remove(h); Remove(h);
h = next; h = next;
} }
@@ -120,8 +132,9 @@ namespace Cysharp.Threading.Tasks
LogError(ex); LogError(ex);
} }
var next = h == iteratingNode ? h.Next : iteratingNode; preserveRemoveSelf = false;
iteratingNode = null; iteratingNode = null;
var next = h.Next;
Remove(h); Remove(h);
h = next; h = next;
} }
@@ -154,8 +167,9 @@ namespace Cysharp.Threading.Tasks
LogError(ex); LogError(ex);
} }
var next = h == iteratingNode ? h.Next : iteratingNode; preserveRemoveSelf = false;
iteratingNode = null; iteratingNode = null;
var next = h.Next;
Remove(h); Remove(h);
h = next; h = next;
} }
@@ -227,65 +241,71 @@ namespace Cysharp.Threading.Tasks
{ {
if (handler == null) throw new ArgumentNullException(nameof(handler)); if (handler == null) throw new ArgumentNullException(nameof(handler));
var prev = handler.Prev; if (iteratingNode != null && iteratingNode == handler)
var next = handler.Next; {
// if remove self, reserve remove self after invoke completed.
preserveRemoveSelf = true;
}
else
{
var prev = handler.Prev;
var next = handler.Next;
if (next != null) if (next != null)
{
next.Prev = prev;
}
if (handler == head)
{
head = next;
}
// when handler is head, prev indicate last so don't use it.
else if (prev != null)
{
prev.Next = next;
}
if (handler == iteratingNode)
{
iteratingNode = next;
}
if (handler == iteratingHead)
{
iteratingHead = next;
}
if (head != null)
{
if (head.Prev == handler)
{ {
if (prev != head) next.Prev = prev;
}
if (handler == head)
{
head = next;
}
else if (handler == iteratingHead)
{
iteratingHead = next;
}
else
{
// when handler is head, prev indicate last so don't use it.
if (prev != null)
{ {
head.Prev = prev; prev.Next = next;
}
else
{
head.Prev = null;
} }
} }
}
if (iteratingHead != null) if (head != null)
{
if (iteratingHead.Prev == handler)
{ {
if (prev != iteratingHead.Prev) if (head.Prev == handler)
{ {
iteratingHead.Prev = prev; if (prev != head)
} {
else head.Prev = prev;
{ }
iteratingHead.Prev = null; else
{
head.Prev = null;
}
} }
} }
}
handler.Prev = null; if (iteratingHead != null)
handler.Next = null; {
if (iteratingHead.Prev == handler)
{
if (prev != iteratingHead.Prev)
{
iteratingHead.Prev = prev;
}
else
{
iteratingHead.Prev = null;
}
}
}
handler.Prev = null;
handler.Next = null;
}
} }
} }
} }

View File

@@ -32,11 +32,13 @@ namespace Cysharp.Threading.Tasks.Triggers
if (cancellationTokenSource == null) if (cancellationTokenSource == null)
{ {
cancellationTokenSource = new CancellationTokenSource(); cancellationTokenSource = new CancellationTokenSource();
if (!awakeCalled)
{
PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, new AwakeMonitor(this));
}
} }
if (!awakeCalled)
{
PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, new AwakeMonitor(this));
}
return cancellationTokenSource.Token; return cancellationTokenSource.Token;
} }
} }
@@ -81,7 +83,7 @@ namespace Cysharp.Threading.Tasks.Triggers
public bool MoveNext() public bool MoveNext()
{ {
if (trigger.called || trigger.awakeCalled) return false; if (trigger.called) return false;
if (trigger == null) if (trigger == null)
{ {
trigger.OnDestroy(); trigger.OnDestroy();

View File

@@ -8,16 +8,6 @@ namespace Cysharp.Threading.Tasks
{ {
public static class UniTaskCancellationExtensions public static class UniTaskCancellationExtensions
{ {
#if UNITY_2022_2_OR_NEWER
/// <summary>This CancellationToken is canceled when the MonoBehaviour will be destroyed.</summary>
public static CancellationToken GetCancellationTokenOnDestroy(this MonoBehaviour monoBehaviour)
{
return monoBehaviour.destroyCancellationToken;
}
#endif
/// <summary>This CancellationToken is canceled when the MonoBehaviour will be destroyed.</summary> /// <summary>This CancellationToken is canceled when the MonoBehaviour will be destroyed.</summary>
public static CancellationToken GetCancellationTokenOnDestroy(this GameObject gameObject) public static CancellationToken GetCancellationTokenOnDestroy(this GameObject gameObject)
{ {
@@ -27,13 +17,6 @@ namespace Cysharp.Threading.Tasks
/// <summary>This CancellationToken is canceled when the MonoBehaviour will be destroyed.</summary> /// <summary>This CancellationToken is canceled when the MonoBehaviour 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 (component is MonoBehaviour mb)
{
return mb.destroyCancellationToken;
}
#endif
return component.GetAsyncDestroyTrigger().CancellationToken; return component.GetAsyncDestroyTrigger().CancellationToken;
} }
} }

View File

@@ -2,7 +2,6 @@
using Cysharp.Threading.Tasks.Internal; using Cysharp.Threading.Tasks.Internal;
using System; using System;
using System.Collections;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading; using System.Threading;
using UnityEngine; using UnityEngine;
@@ -75,60 +74,40 @@ namespace Cysharp.Threading.Tasks
return new UniTask(NextFramePromise.Create(timing, cancellationToken, out var token), token); return new UniTask(NextFramePromise.Create(timing, cancellationToken, out var token), token);
} }
#if UNITY_2023_1_OR_NEWER
public static async UniTask WaitForEndOfFrame(CancellationToken cancellationToken = default) /// <summary>
{ /// Same as UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate).
await Awaitable.EndOfFrameAsync(cancellationToken); /// </summary>
}
#else
[Obsolete("Use WaitForEndOfFrame(MonoBehaviour) instead or UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate). Equivalent for coroutine's WaitForEndOfFrame requires MonoBehaviour(runner of Coroutine).")]
public static YieldAwaitable WaitForEndOfFrame() public static YieldAwaitable WaitForEndOfFrame()
{ {
return UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate); return UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate);
} }
[Obsolete("Use WaitForEndOfFrame(MonoBehaviour) instead or UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate). Equivalent for coroutine's WaitForEndOfFrame requires MonoBehaviour(runner of Coroutine).")] /// <summary>
/// Same as UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate, cancellationToken).
/// </summary>
public static UniTask WaitForEndOfFrame(CancellationToken cancellationToken) public static UniTask WaitForEndOfFrame(CancellationToken cancellationToken)
{ {
return UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate, cancellationToken); return UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate, cancellationToken);
} }
#endif
public static UniTask WaitForEndOfFrame(MonoBehaviour coroutineRunner, CancellationToken cancellationToken = default)
{
var source = WaitForEndOfFramePromise.Create(coroutineRunner, cancellationToken, out var token);
return new UniTask(source, token);
}
/// <summary> /// <summary>
/// Same as UniTask.Yield(PlayerLoopTiming.LastFixedUpdate). /// Same as UniTask.Yield(PlayerLoopTiming.FixedUpdate).
/// </summary> /// </summary>
public static YieldAwaitable WaitForFixedUpdate() public static YieldAwaitable WaitForFixedUpdate()
{ {
// use LastFixedUpdate instead of FixedUpdate return UniTask.Yield(PlayerLoopTiming.FixedUpdate);
// https://github.com/Cysharp/UniTask/issues/377
return UniTask.Yield(PlayerLoopTiming.LastFixedUpdate);
} }
/// <summary> /// <summary>
/// Same as UniTask.Yield(PlayerLoopTiming.LastFixedUpdate, cancellationToken). /// Same as UniTask.Yield(PlayerLoopTiming.FixedUpdate, cancellationToken).
/// </summary> /// </summary>
public static UniTask WaitForFixedUpdate(CancellationToken cancellationToken) public static UniTask WaitForFixedUpdate(CancellationToken cancellationToken)
{ {
return UniTask.Yield(PlayerLoopTiming.LastFixedUpdate, cancellationToken); return UniTask.Yield(PlayerLoopTiming.FixedUpdate, cancellationToken);
} }
public static UniTask WaitForSeconds(float duration, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) public static UniTask DelayFrame(int delayFrameCount, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
{
return Delay(Mathf.RoundToInt(1000 * duration), ignoreTimeScale, delayTiming, cancellationToken);
}
public static UniTask WaitForSeconds(int duration, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
{
return Delay(1000 * duration, ignoreTimeScale, delayTiming, cancellationToken);
}
public static UniTask DelayFrame(int delayFrameCount, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
{ {
if (delayFrameCount < 0) if (delayFrameCount < 0)
{ {
@@ -373,113 +352,6 @@ namespace Cysharp.Threading.Tasks
} }
} }
sealed class WaitForEndOfFramePromise : IUniTaskSource, ITaskPoolNode<WaitForEndOfFramePromise>, System.Collections.IEnumerator
{
static TaskPool<WaitForEndOfFramePromise> pool;
WaitForEndOfFramePromise nextNode;
public ref WaitForEndOfFramePromise NextNode => ref nextNode;
static WaitForEndOfFramePromise()
{
TaskPool.RegisterSizeGetter(typeof(WaitForEndOfFramePromise), () => pool.Size);
}
CancellationToken cancellationToken;
UniTaskCompletionSourceCore<object> core;
WaitForEndOfFramePromise()
{
}
public static IUniTaskSource Create(MonoBehaviour coroutineRunner, CancellationToken cancellationToken, out short token)
{
if (cancellationToken.IsCancellationRequested)
{
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
}
if (!pool.TryPop(out var result))
{
result = new WaitForEndOfFramePromise();
}
result.cancellationToken = cancellationToken;
TaskTracker.TrackActiveTask(result, 3);
coroutineRunner.StartCoroutine(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);
}
bool TryReturn()
{
TaskTracker.RemoveTracking(this);
core.Reset();
Reset(); // Reset Enumerator
cancellationToken = default;
return pool.TryPush(this);
}
// Coroutine Runner implementation
static readonly WaitForEndOfFrame waitForEndOfFrameYieldInstruction = new WaitForEndOfFrame();
bool isFirst = true;
object IEnumerator.Current => waitForEndOfFrameYieldInstruction;
bool IEnumerator.MoveNext()
{
if (isFirst)
{
isFirst = false;
return true; // start WaitForEndOfFrame
}
if (cancellationToken.IsCancellationRequested)
{
core.TrySetCanceled(cancellationToken);
return false;
}
core.TrySetResult(null);
return false;
}
public void Reset()
{
isFirst = true;
}
}
sealed class DelayFramePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<DelayFramePromise> sealed class DelayFramePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<DelayFramePromise>
{ {
static TaskPool<DelayFramePromise> pool; static TaskPool<DelayFramePromise> pool;
@@ -572,19 +444,7 @@ namespace Cysharp.Threading.Tasks
// skip in initial frame. // skip in initial frame.
if (initialFrame == Time.frameCount) if (initialFrame == Time.frameCount)
{ {
#if UNITY_EDITOR
// force use Realtime.
if (PlayerLoopHelper.IsMainThread && !UnityEditor.EditorApplication.isPlaying)
{
//goto ++currentFrameCount
}
else
{
return true;
}
#else
return true; return true;
#endif
} }
} }

View File

@@ -194,7 +194,6 @@ namespace Cysharp.Threading.Tasks
sealed class ExceptionResultSource : IUniTaskSource sealed class ExceptionResultSource : IUniTaskSource
{ {
readonly ExceptionDispatchInfo exception; readonly ExceptionDispatchInfo exception;
bool calledGet;
public ExceptionResultSource(Exception exception) public ExceptionResultSource(Exception exception)
{ {
@@ -203,11 +202,6 @@ namespace Cysharp.Threading.Tasks
public void GetResult(short token) public void GetResult(short token)
{ {
if (!calledGet)
{
calledGet = true;
GC.SuppressFinalize(this);
}
exception.Throw(); exception.Throw();
} }
@@ -225,20 +219,11 @@ namespace Cysharp.Threading.Tasks
{ {
continuation(state); continuation(state);
} }
~ExceptionResultSource()
{
if (!calledGet)
{
UniTaskScheduler.PublishUnobservedTaskException(exception.SourceException);
}
}
} }
sealed class ExceptionResultSource<T> : IUniTaskSource<T> sealed class ExceptionResultSource<T> : IUniTaskSource<T>
{ {
readonly ExceptionDispatchInfo exception; readonly ExceptionDispatchInfo exception;
bool calledGet;
public ExceptionResultSource(Exception exception) public ExceptionResultSource(Exception exception)
{ {
@@ -247,22 +232,12 @@ namespace Cysharp.Threading.Tasks
public T GetResult(short token) public T GetResult(short token)
{ {
if (!calledGet)
{
calledGet = true;
GC.SuppressFinalize(this);
}
exception.Throw(); exception.Throw();
return default; return default;
} }
void IUniTaskSource.GetResult(short token) void IUniTaskSource.GetResult(short token)
{ {
if (!calledGet)
{
calledGet = true;
GC.SuppressFinalize(this);
}
exception.Throw(); exception.Throw();
} }
@@ -280,14 +255,6 @@ namespace Cysharp.Threading.Tasks
{ {
continuation(state); continuation(state);
} }
~ExceptionResultSource()
{
if (!calledGet)
{
UniTaskScheduler.PublishUnobservedTaskException(exception.SourceException);
}
}
} }
sealed class CanceledResultSource : IUniTaskSource sealed class CanceledResultSource : IUniTaskSource

View File

@@ -9,56 +9,237 @@ namespace Cysharp.Threading.Tasks
{ {
#region OBSOLETE_RUN #region OBSOLETE_RUN
[Obsolete("UniTask.Run is similar as Task.Run, it uses ThreadPool. For equivalent behaviour, use UniTask.RunOnThreadPool instead. If you don't want to use ThreadPool, you can use UniTask.Void(async void) or UniTask.Create(async UniTask) too.")] // Run is a confusing name, use only RunOnThreadPool in the future.
public static UniTask Run(Action action, bool configureAwait = true, CancellationToken cancellationToken = default)
/// <summary>[Obsolete]recommend to use RunOnThreadPool(or UniTask.Void(async void), UniTask.Create(async UniTask)).</summary>
public static async UniTask Run(Action action, bool configureAwait = true, CancellationToken cancellationToken = default)
{ {
return RunOnThreadPool(action, configureAwait, cancellationToken); cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait)
{
try
{
action();
}
finally
{
await UniTask.Yield();
}
}
else
{
action();
}
cancellationToken.ThrowIfCancellationRequested();
} }
[Obsolete("UniTask.Run is similar as Task.Run, it uses ThreadPool. For equivalent behaviour, use UniTask.RunOnThreadPool instead. If you don't want to use ThreadPool, you can use UniTask.Void(async void) or UniTask.Create(async UniTask) too.")] /// <summary>[Obsolete]recommend to use RunOnThreadPool(or UniTask.Void(async void), UniTask.Create(async UniTask)).</summary>
public static UniTask Run(Action<object> action, object state, bool configureAwait = true, CancellationToken cancellationToken = default) public static async UniTask Run(Action<object> action, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
{ {
return RunOnThreadPool(action, state, configureAwait, cancellationToken); cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait)
{
try
{
action(state);
}
finally
{
await UniTask.Yield();
}
}
else
{
action(state);
}
cancellationToken.ThrowIfCancellationRequested();
} }
[Obsolete("UniTask.Run is similar as Task.Run, it uses ThreadPool. For equivalent behaviour, use UniTask.RunOnThreadPool instead. If you don't want to use ThreadPool, you can use UniTask.Void(async void) or UniTask.Create(async UniTask) too.")] /// <summary>[Obsolete]recommend to use RunOnThreadPool(or UniTask.Void(async void), UniTask.Create(async UniTask)).</summary>
public static UniTask Run(Func<UniTask> action, bool configureAwait = true, CancellationToken cancellationToken = default) public static async UniTask Run(Func<UniTask> action, bool configureAwait = true, CancellationToken cancellationToken = default)
{ {
return RunOnThreadPool(action, configureAwait, cancellationToken); cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait)
{
try
{
await action();
}
finally
{
await UniTask.Yield();
}
}
else
{
await action();
}
cancellationToken.ThrowIfCancellationRequested();
} }
[Obsolete("UniTask.Run is similar as Task.Run, it uses ThreadPool. For equivalent behaviour, use UniTask.RunOnThreadPool instead. If you don't want to use ThreadPool, you can use UniTask.Void(async void) or UniTask.Create(async UniTask) too.")] /// <summary>[Obsolete]recommend to use RunOnThreadPool(or UniTask.Void(async void), UniTask.Create(async UniTask)).</summary>
public static UniTask Run(Func<object, UniTask> action, object state, bool configureAwait = true, CancellationToken cancellationToken = default) public static async UniTask Run(Func<object, UniTask> action, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
{ {
return RunOnThreadPool(action, state, configureAwait, cancellationToken); cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait)
{
try
{
await action(state);
}
finally
{
await UniTask.Yield();
}
}
else
{
await action(state);
}
cancellationToken.ThrowIfCancellationRequested();
} }
[Obsolete("UniTask.Run is similar as Task.Run, it uses ThreadPool. For equivalent behaviour, use UniTask.RunOnThreadPool instead. If you don't want to use ThreadPool, you can use UniTask.Void(async void) or UniTask.Create(async UniTask) too.")] /// <summary>[Obsolete]recommend to use RunOnThreadPool(or UniTask.Void(async void), UniTask.Create(async UniTask)).</summary>
public static UniTask<T> Run<T>(Func<T> func, bool configureAwait = true, CancellationToken cancellationToken = default) public static async UniTask<T> Run<T>(Func<T> func, bool configureAwait = true, CancellationToken cancellationToken = default)
{ {
return RunOnThreadPool(func, configureAwait, cancellationToken); cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait)
{
try
{
return func();
}
finally
{
await UniTask.Yield();
cancellationToken.ThrowIfCancellationRequested();
}
}
else
{
return func();
}
} }
[Obsolete("UniTask.Run is similar as Task.Run, it uses ThreadPool. For equivalent behaviour, use UniTask.RunOnThreadPool instead. If you don't want to use ThreadPool, you can use UniTask.Void(async void) or UniTask.Create(async UniTask) too.")] /// <summary>[Obsolete]recommend to use RunOnThreadPool(or UniTask.Void(async void), UniTask.Create(async UniTask)).</summary>
public static UniTask<T> Run<T>(Func<UniTask<T>> func, bool configureAwait = true, CancellationToken cancellationToken = default) public static async UniTask<T> Run<T>(Func<UniTask<T>> func, bool configureAwait = true, CancellationToken cancellationToken = default)
{ {
return RunOnThreadPool(func, configureAwait, cancellationToken); cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait)
{
try
{
return await func();
}
finally
{
cancellationToken.ThrowIfCancellationRequested();
await UniTask.Yield();
cancellationToken.ThrowIfCancellationRequested();
}
}
else
{
var result = await func();
cancellationToken.ThrowIfCancellationRequested();
return result;
}
} }
[Obsolete("UniTask.Run is similar as Task.Run, it uses ThreadPool. For equivalent behaviour, use UniTask.RunOnThreadPool instead. If you don't want to use ThreadPool, you can use UniTask.Void(async void) or UniTask.Create(async UniTask) too.")] /// <summary>[Obsolete]recommend to use RunOnThreadPool(or UniTask.Void(async void), UniTask.Create(async UniTask)).</summary>
public static UniTask<T> Run<T>(Func<object, T> func, object state, bool configureAwait = true, CancellationToken cancellationToken = default) public static async UniTask<T> Run<T>(Func<object, T> func, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
{ {
return RunOnThreadPool(func, state, configureAwait, cancellationToken); cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait)
{
try
{
return func(state);
}
finally
{
await UniTask.Yield();
cancellationToken.ThrowIfCancellationRequested();
}
}
else
{
return func(state);
}
} }
[Obsolete("UniTask.Run is similar as Task.Run, it uses ThreadPool. For equivalent behaviour, use UniTask.RunOnThreadPool instead. If you don't want to use ThreadPool, you can use UniTask.Void(async void) or UniTask.Create(async UniTask) too.")] /// <summary>[Obsolete]recommend to use RunOnThreadPool(or UniTask.Void(async void), UniTask.Create(async UniTask)).</summary>
public static UniTask<T> Run<T>(Func<object, UniTask<T>> func, object state, bool configureAwait = true, CancellationToken cancellationToken = default) public static async UniTask<T> Run<T>(Func<object, UniTask<T>> func, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
{ {
return RunOnThreadPool(func, state, configureAwait, cancellationToken); cancellationToken.ThrowIfCancellationRequested();
await UniTask.SwitchToThreadPool();
cancellationToken.ThrowIfCancellationRequested();
if (configureAwait)
{
try
{
return await func(state);
}
finally
{
cancellationToken.ThrowIfCancellationRequested();
await UniTask.Yield();
cancellationToken.ThrowIfCancellationRequested();
}
}
else
{
var result = await func(state);
cancellationToken.ThrowIfCancellationRequested();
return result;
}
} }
#endregion #endregion
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary> /// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
public static async UniTask RunOnThreadPool(Action action, bool configureAwait = true, CancellationToken cancellationToken = default) public static async UniTask RunOnThreadPool(Action action, bool configureAwait = true, CancellationToken cancellationToken = default)
{ {

View File

@@ -137,8 +137,8 @@ namespace Cysharp.Threading.Tasks
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)
{ {
continuation(continuationState); continuation(continuationState);
return true;
} }
return true;
} }
return false; return false;
@@ -165,8 +165,8 @@ namespace Cysharp.Threading.Tasks
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)
{ {
continuation(continuationState); continuation(continuationState);
return true;
} }
return true;
} }
return false; return false;
@@ -184,8 +184,8 @@ namespace Cysharp.Threading.Tasks
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)
{ {
continuation(continuationState); continuation(continuationState);
return true;
} }
return true;
} }
return false; return false;
@@ -328,7 +328,6 @@ namespace Cysharp.Threading.Tasks
} }
UniTaskCompletionSourceCore<AsyncUnit> core; UniTaskCompletionSourceCore<AsyncUnit> core;
short version;
AutoResetUniTaskCompletionSource() AutoResetUniTaskCompletionSource()
{ {
@@ -341,7 +340,6 @@ namespace Cysharp.Threading.Tasks
{ {
result = new AutoResetUniTaskCompletionSource(); result = new AutoResetUniTaskCompletionSource();
} }
result.version = result.core.Version;
TaskTracker.TrackActiveTask(result, 2); TaskTracker.TrackActiveTask(result, 2);
return result; return result;
} }
@@ -385,19 +383,19 @@ namespace Cysharp.Threading.Tasks
[DebuggerHidden] [DebuggerHidden]
public bool TrySetResult() public bool TrySetResult()
{ {
return version == core.Version && core.TrySetResult(AsyncUnit.Default); return core.TrySetResult(AsyncUnit.Default);
} }
[DebuggerHidden] [DebuggerHidden]
public bool TrySetCanceled(CancellationToken cancellationToken = default) public bool TrySetCanceled(CancellationToken cancellationToken = default)
{ {
return version == core.Version && core.TrySetCanceled(cancellationToken); return core.TrySetCanceled(cancellationToken);
} }
[DebuggerHidden] [DebuggerHidden]
public bool TrySetException(Exception exception) public bool TrySetException(Exception exception)
{ {
return version == core.Version && core.TrySetException(exception); return core.TrySetException(exception);
} }
[DebuggerHidden] [DebuggerHidden]
@@ -411,6 +409,7 @@ namespace Cysharp.Threading.Tasks
{ {
TryReturn(); TryReturn();
} }
} }
[DebuggerHidden] [DebuggerHidden]
@@ -452,7 +451,6 @@ namespace Cysharp.Threading.Tasks
} }
UniTaskCompletionSourceCore<T> core; UniTaskCompletionSourceCore<T> core;
short version;
AutoResetUniTaskCompletionSource() AutoResetUniTaskCompletionSource()
{ {
@@ -465,7 +463,6 @@ namespace Cysharp.Threading.Tasks
{ {
result = new AutoResetUniTaskCompletionSource<T>(); result = new AutoResetUniTaskCompletionSource<T>();
} }
result.version = result.core.Version;
TaskTracker.TrackActiveTask(result, 2); TaskTracker.TrackActiveTask(result, 2);
return result; return result;
} }
@@ -509,19 +506,19 @@ namespace Cysharp.Threading.Tasks
[DebuggerHidden] [DebuggerHidden]
public bool TrySetResult(T result) public bool TrySetResult(T result)
{ {
return version == core.Version && core.TrySetResult(result); return core.TrySetResult(result);
} }
[DebuggerHidden] [DebuggerHidden]
public bool TrySetCanceled(CancellationToken cancellationToken = default) public bool TrySetCanceled(CancellationToken cancellationToken = default)
{ {
return version == core.Version && core.TrySetCanceled(cancellationToken); return core.TrySetCanceled(cancellationToken);
} }
[DebuggerHidden] [DebuggerHidden]
public bool TrySetException(Exception exception) public bool TrySetException(Exception exception)
{ {
return version == core.Version && core.TrySetException(exception); return core.TrySetException(exception);
} }
[DebuggerHidden] [DebuggerHidden]
@@ -940,5 +937,5 @@ namespace Cysharp.Threading.Tasks
} }
return false; return false;
} }
} }
} }

View File

@@ -28,7 +28,7 @@ namespace Cysharp.Threading.Tasks
p.TrySetCanceled(); p.TrySetCanceled();
break; break;
case TaskStatus.Faulted: case TaskStatus.Faulted:
p.TrySetException(x.Exception.InnerException ?? x.Exception); p.TrySetException(x.Exception);
break; break;
case TaskStatus.RanToCompletion: case TaskStatus.RanToCompletion:
p.TrySetResult(x.Result); p.TrySetResult(x.Result);
@@ -58,7 +58,7 @@ namespace Cysharp.Threading.Tasks
p.TrySetCanceled(); p.TrySetCanceled();
break; break;
case TaskStatus.Faulted: case TaskStatus.Faulted:
p.TrySetException(x.Exception.InnerException ?? x.Exception); p.TrySetException(x.Exception);
break; break;
case TaskStatus.RanToCompletion: case TaskStatus.RanToCompletion:
p.TrySetResult(); p.TrySetResult();

View File

@@ -74,19 +74,16 @@ namespace Cysharp.Threading.Tasks
static async UniTaskVoid Fire<T>(AsyncSubject<T> subject, UniTask<T> task) static async UniTaskVoid Fire<T>(AsyncSubject<T> subject, UniTask<T> task)
{ {
T value;
try try
{ {
value = await task; var value = await task;
subject.OnNext(value);
subject.OnCompleted();
} }
catch (Exception ex) catch (Exception ex)
{ {
subject.OnError(ex); subject.OnError(ex);
return;
} }
subject.OnNext(value);
subject.OnCompleted();
} }
static async UniTaskVoid Fire(AsyncSubject<AsyncUnit> subject, UniTask task) static async UniTaskVoid Fire(AsyncSubject<AsyncUnit> subject, UniTask task)
@@ -94,15 +91,13 @@ namespace Cysharp.Threading.Tasks
try try
{ {
await task; await task;
subject.OnNext(AsyncUnit.Default);
subject.OnCompleted();
} }
catch (Exception ex) catch (Exception ex)
{ {
subject.OnError(ex); subject.OnError(ex);
return;
} }
subject.OnNext(AsyncUnit.Default);
subject.OnCompleted();
} }
class ToUniTaskObserver<T> : IObserver<T> class ToUniTaskObserver<T> : IObserver<T>
@@ -133,7 +128,7 @@ namespace Cysharp.Threading.Tasks
{ {
var self = (ToUniTaskObserver<T>)state; var self = (ToUniTaskObserver<T>)state;
self.disposable.Dispose(); self.disposable.Dispose();
self.promise.TrySetCanceled(self.cancellationToken); self.promise.TrySetCanceled();
} }
public void OnNext(T value) public void OnNext(T value)
@@ -203,7 +198,7 @@ namespace Cysharp.Threading.Tasks
{ {
var self = (FirstValueToUniTaskObserver<T>)state; var self = (FirstValueToUniTaskObserver<T>)state;
self.disposable.Dispose(); self.disposable.Dispose();
self.promise.TrySetCanceled(self.cancellationToken); self.promise.TrySetCanceled();
} }
public void OnNext(T value) public void OnNext(T value)

View File

@@ -21,7 +21,7 @@ namespace Cysharp.Threading.Tasks
public static UniTask<UnityEngine.Object[]> AwaitForAllAssets(this AssetBundleRequest asyncOperation, CancellationToken cancellationToken) public static UniTask<UnityEngine.Object[]> AwaitForAllAssets(this AssetBundleRequest asyncOperation, CancellationToken cancellationToken)
{ {
return AwaitForAllAssets(asyncOperation, null, PlayerLoopTiming.Update, cancellationToken: cancellationToken); return AwaitForAllAssets(asyncOperation, cancellationToken: cancellationToken);
} }
public static UniTask<UnityEngine.Object[]> AwaitForAllAssets(this AssetBundleRequest asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) public static UniTask<UnityEngine.Object[]> AwaitForAllAssets(this AssetBundleRequest asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))

View File

@@ -15,15 +15,11 @@ namespace Cysharp.Threading.Tasks
{ {
#region AsyncOperation #region AsyncOperation
#if !UNITY_2023_1_OR_NEWER
// from Unity2023.1.0a15, AsyncOperationAwaitableExtensions.GetAwaiter is defined in UnityEngine.
public static AsyncOperationAwaiter GetAwaiter(this AsyncOperation asyncOperation) public static AsyncOperationAwaiter GetAwaiter(this AsyncOperation asyncOperation)
{ {
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
return new AsyncOperationAwaiter(asyncOperation); return new AsyncOperationAwaiter(asyncOperation);
} }
#endif
public static UniTask WithCancellation(this AsyncOperation asyncOperation, CancellationToken cancellationToken) public static UniTask WithCancellation(this AsyncOperation asyncOperation, CancellationToken cancellationToken)
{ {
@@ -900,11 +896,7 @@ namespace Cysharp.Threading.Tasks
if (asyncOperation.isDone) if (asyncOperation.isDone)
{ {
if (asyncOperation.webRequest == null) if (asyncOperation.webRequest.IsError())
{
core.TrySetException(new ObjectDisposedException("The webRequest has been destroyed."));
}
else if (asyncOperation.webRequest.IsError())
{ {
core.TrySetException(new UnityWebRequestException(asyncOperation.webRequest)); core.TrySetException(new UnityWebRequestException(asyncOperation.webRequest));
} }

View File

@@ -673,7 +673,7 @@ namespace Cysharp.Threading.Tasks
} }
if (cancellationToken2.CanBeCanceled) if (cancellationToken2.CanBeCanceled)
{ {
registration2 = cancellationToken2.RegisterWithoutCaptureExecutionContext(cancel2, this); registration2 = cancellationToken1.RegisterWithoutCaptureExecutionContext(cancel2, this);
} }
} }
@@ -688,27 +688,13 @@ namespace Cysharp.Threading.Tasks
static void OnCanceled1(object state) static void OnCanceled1(object state)
{ {
var self = (UnityEventHandlerAsyncEnumerator)state; var self = (UnityEventHandlerAsyncEnumerator)state;
try self.DisposeAsync().Forget();
{
self.completionSource.TrySetCanceled(self.cancellationToken1);
}
finally
{
self.DisposeAsync().Forget();
}
} }
static void OnCanceled2(object state) static void OnCanceled2(object state)
{ {
var self = (UnityEventHandlerAsyncEnumerator)state; var self = (UnityEventHandlerAsyncEnumerator)state;
try self.DisposeAsync().Forget();
{
self.completionSource.TrySetCanceled(self.cancellationToken2);
}
finally
{
self.DisposeAsync().Forget();
}
} }
public UniTask DisposeAsync() public UniTask DisposeAsync()
@@ -720,8 +706,6 @@ namespace Cysharp.Threading.Tasks
registration1.Dispose(); registration1.Dispose();
registration2.Dispose(); registration2.Dispose();
unityEvent.RemoveListener(unityAction); unityEvent.RemoveListener(unityAction);
completionSource.TrySetCanceled();
} }
return default; return default;
@@ -793,7 +777,7 @@ namespace Cysharp.Threading.Tasks
} }
if (cancellationToken2.CanBeCanceled) if (cancellationToken2.CanBeCanceled)
{ {
registration2 = cancellationToken2.RegisterWithoutCaptureExecutionContext(cancel2, this); registration2 = cancellationToken1.RegisterWithoutCaptureExecutionContext(cancel2, this);
} }
} }
@@ -809,27 +793,13 @@ namespace Cysharp.Threading.Tasks
static void OnCanceled1(object state) static void OnCanceled1(object state)
{ {
var self = (UnityEventHandlerAsyncEnumerator)state; var self = (UnityEventHandlerAsyncEnumerator)state;
try self.DisposeAsync().Forget();
{
self.completionSource.TrySetCanceled(self.cancellationToken1);
}
finally
{
self.DisposeAsync().Forget();
}
} }
static void OnCanceled2(object state) static void OnCanceled2(object state)
{ {
var self = (UnityEventHandlerAsyncEnumerator)state; var self = (UnityEventHandlerAsyncEnumerator)state;
try self.DisposeAsync().Forget();
{
self.completionSource.TrySetCanceled(self.cancellationToken2);
}
finally
{
self.DisposeAsync().Forget();
}
} }
public UniTask DisposeAsync() public UniTask DisposeAsync()
@@ -845,8 +815,6 @@ namespace Cysharp.Threading.Tasks
disp.Dispose(); disp.Dispose();
} }
unityEvent.RemoveListener(unityAction); unityEvent.RemoveListener(unityAction);
completionSource.TrySetCanceled();
} }
return default; return default;

View File

@@ -12,17 +12,17 @@ namespace Cysharp.Threading.Tasks
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT #if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
// <string> -> Text // <string> -> Text
public static void BindTo(this IUniTaskAsyncEnumerable<string> source, UnityEngine.UI.Text text, bool rebindOnError = true) public static void BindTo(this IUniTaskAsyncEnumerable<string> source, Text text, bool rebindOnError = true)
{ {
BindToCore(source, text, text.GetCancellationTokenOnDestroy(), rebindOnError).Forget(); BindToCore(source, text, text.GetCancellationTokenOnDestroy(), rebindOnError).Forget();
} }
public static void BindTo(this IUniTaskAsyncEnumerable<string> source, UnityEngine.UI.Text text, CancellationToken cancellationToken, bool rebindOnError = true) public static void BindTo(this IUniTaskAsyncEnumerable<string> source, Text text, CancellationToken cancellationToken, bool rebindOnError = true)
{ {
BindToCore(source, text, cancellationToken, rebindOnError).Forget(); BindToCore(source, text, cancellationToken, rebindOnError).Forget();
} }
static async UniTaskVoid BindToCore(IUniTaskAsyncEnumerable<string> source, UnityEngine.UI.Text text, CancellationToken cancellationToken, bool rebindOnError) static async UniTaskVoid BindToCore(IUniTaskAsyncEnumerable<string> source, Text text, CancellationToken cancellationToken, bool rebindOnError)
{ {
var repeat = false; var repeat = false;
BIND_AGAIN: BIND_AGAIN:
@@ -68,22 +68,22 @@ namespace Cysharp.Threading.Tasks
// <T> -> Text // <T> -> Text
public static void BindTo<T>(this IUniTaskAsyncEnumerable<T> source, UnityEngine.UI.Text text, bool rebindOnError = true) public static void BindTo<T>(this IUniTaskAsyncEnumerable<T> source, Text text, bool rebindOnError = true)
{ {
BindToCore(source, text, text.GetCancellationTokenOnDestroy(), rebindOnError).Forget(); BindToCore(source, text, text.GetCancellationTokenOnDestroy(), rebindOnError).Forget();
} }
public static void BindTo<T>(this IUniTaskAsyncEnumerable<T> source, UnityEngine.UI.Text text, CancellationToken cancellationToken, bool rebindOnError = true) public static void BindTo<T>(this IUniTaskAsyncEnumerable<T> source, Text text, CancellationToken cancellationToken, bool rebindOnError = true)
{ {
BindToCore(source, text, cancellationToken, rebindOnError).Forget(); BindToCore(source, text, cancellationToken, rebindOnError).Forget();
} }
public static void BindTo<T>(this AsyncReactiveProperty<T> source, UnityEngine.UI.Text text, bool rebindOnError = true) public static void BindTo<T>(this AsyncReactiveProperty<T> source, Text text, bool rebindOnError = true)
{ {
BindToCore(source, text, text.GetCancellationTokenOnDestroy(), rebindOnError).Forget(); BindToCore(source, text, text.GetCancellationTokenOnDestroy(), rebindOnError).Forget();
} }
static async UniTaskVoid BindToCore<T>(IUniTaskAsyncEnumerable<T> source, UnityEngine.UI.Text text, CancellationToken cancellationToken, bool rebindOnError) static async UniTaskVoid BindToCore<T>(IUniTaskAsyncEnumerable<T> source, Text text, CancellationToken cancellationToken, bool rebindOnError)
{ {
var repeat = false; var repeat = false;
BIND_AGAIN: BIND_AGAIN:

View File

@@ -1,4 +1,4 @@
#if ENABLE_UNITYWEBREQUEST && (!UNITY_2019_1_OR_NEWER || UNITASK_WEBREQUEST_SUPPORT) #if ENABLE_UNITYWEBREQUEST && (!UNITY_2019_1_OR_NEWER || UNITASK_WEBREQUEST_SUPPORT)
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@@ -49,7 +49,7 @@ namespace Cysharp.Threading.Tasks
{ {
if (msg == null) if (msg == null)
{ {
if(!string.IsNullOrWhiteSpace(Text)) if (Text != null)
{ {
msg = Error + Environment.NewLine + Text; msg = Error + Environment.NewLine + Text;
} }

View File

@@ -1,12 +1,11 @@
{ {
"name": "com.cysharp.unitask", "name": "com.cysharp.unitask",
"displayName": "UniTask", "displayName": "UniTask",
"author": { "name": "Cysharp, Inc.", "url": "https://cysharp.co.jp/en/" }, "version": "2.2.5",
"version": "2.4.1",
"unity": "2018.4", "unity": "2018.4",
"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",
"category": "Task", "category": "Task",
"dependencies": {} "dependencies": {}
} }

View File

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

View File

@@ -0,0 +1 @@
bar

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 9df64c494bee73b40ad3679f44bbc8c5
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1 @@
baz

View File

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

View File

@@ -0,0 +1 @@
foo

View File

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

View File

@@ -0,0 +1,129 @@
using Cysharp.Threading.Tasks;
using System;
using System.Collections;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class DemoScript : MonoBehaviour
{
public Toggle toggle = null;
public Text text = null;
async Task Start()
{
text.text = "";
var result = await DemoAsync();
text.text = $"RESULT is: {result}";
}
async UniTask<string> DemoAsync()
{
// You can await Unity's AsyncObject
text.text = "loading assets";
var asset = await Resources.LoadAsync<TextAsset>("foo");
text.text = "get data from github.com";
var txt = (await UnityWebRequest.Get("https://github.com").SendWebRequest()).downloadHandler.text;
text.text = "load scene2";
await SceneManager.LoadSceneAsync("scene2", LoadSceneMode.Additive);
// .WithCancellation enables Cancel, GetCancellationTokenOnDestroy synchornizes with lifetime of GameObject
text.text = "load text asset bar";
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>
text.text = "load text asset baz";
var asset3 = await Resources.LoadAsync<TextAsset>("baz").ToUniTask(Progress.Create<float>(x => Debug.Log(x)));
// await frame-based operation like a coroutine
text.text = "delay frame";
await UniTask.DelayFrame(100);
// replacement of yield return new WaitForSeconds/WaitForSecondsRealtime
text.text = "wait 3sec.";
await UniTask.Delay(TimeSpan.FromSeconds(3), ignoreTimeScale: false);
// yield any playerloop timing(PreUpdate, Update, LateUpdate, etc...)
text.text = "wait for PreLateUpdate";
await UniTask.Yield(PlayerLoopTiming.PreLateUpdate);
// replacement of yield return null
text.text = "yield";
await UniTask.Yield();
text.text = "wait nextframe";
await UniTask.NextFrame();
// replacement of WaitForEndOfFrame(same as UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate))
text.text = "wait end of frame";
await UniTask.WaitForEndOfFrame();
// replacement of yield return new WaitForFixedUpdate(same as UniTask.Yield(PlayerLoopTiming.FixedUpdate))
text.text = "wait fixed update";
await UniTask.WaitForFixedUpdate();
// replacement of yield return WaitUntil
text.text = "wait for toggle => false";
await UniTask.WaitUntil(() => toggle.isOn == false);
// special helper of WaitUntil
text.text = "wait for toggle";
await UniTask.WaitUntilValueChanged(this, x => toggle.isOn);
// You can await IEnumerator coroutines
text.text = "corouting";
await FooCoroutineEnumerator();
// You can await a standard task
text.text = "run standard task";
await Task.Run(() => 100);
// Multithreading, run on ThreadPool under this code
text.text = "switch to thread pool";
await UniTask.SwitchToThreadPool();
/* work on ThreadPool */
// return to MainThread(same as `ObserveOnMainThread` in UniRx)
await UniTask.SwitchToMainThread();
text.text = "switch to main pool";
// get async webrequest
async UniTask<string> GetTextAsync(UnityWebRequest req)
{
var op = await req.SendWebRequest();
return op.downloadHandler.text;
}
var task1 = GetTextAsync(UnityWebRequest.Get("https://google.com/"));
var task2 = GetTextAsync(UnityWebRequest.Get("https://bing.com/"));
var task3 = GetTextAsync(UnityWebRequest.Get("https://yahoo.com/"));
// concurrent async-wait and get results easily by tuple syntax
text.text = "get data from google,bing,yahoo (concurrent)";
var (google, bing, yahoo) = await UniTask.WhenAll(task1, task2, task3);
// can not call same task twice. let's define again for simple demo.
var tasknew1 = GetTextAsync(UnityWebRequest.Get("https://google.com/"));
var tasknew2 = GetTextAsync(UnityWebRequest.Get("https://bing.com/"));
var tasknew3 = GetTextAsync(UnityWebRequest.Get("https://yahoo.com/"));
// shorthand of WhenAll, tuple can await directly.
text.text = "get data from google,bing,yahoo (shorthand)";
var (google2, bing2, yahoo2) = await (tasknew1, tasknew2, tasknew3);
// return async-value.(or you can use `UniTask`(no result), `UniTaskVoid`(fire and forget)).
text.text = "return asset result";
return (asset as TextAsset)?.text ?? throw new InvalidOperationException("Asset not found");
}
private IEnumerator FooCoroutineEnumerator()
{
Debug.Log("cotoutine 1");
yield return new WaitForSeconds(1);
Debug.Log("cotoutine 2");
yield return new WaitForSeconds(1);
Debug.Log("cotoutine end");
}
}

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -543,7 +543,7 @@ public class SandboxMain : MonoBehaviour
Debug.LogError(e); Debug.LogError(e);
return; return;
} }
} }
Debug.Log("TestAsync Finished."); Debug.Log("TestAsync Finished.");
@@ -555,7 +555,6 @@ public class SandboxMain : MonoBehaviour
async UniTaskVoid Start() async UniTaskVoid Start()
{ {
// UniTask.Delay(TimeSpan.FromSeconds(1)).TimeoutWithoutException // UniTask.Delay(TimeSpan.FromSeconds(1)).TimeoutWithoutException
@@ -563,34 +562,61 @@ public class SandboxMain : MonoBehaviour
PlayerLoopHelper.Initialize(ref currentLoop, InjectPlayerLoopTimings.Minimum); // minimum is Update | FixedUpdate | LastPostLateUpdate PlayerLoopHelper.Initialize(ref currentLoop, InjectPlayerLoopTimings.Minimum); // minimum is Update | FixedUpdate | LastPostLateUpdate
var cancelToken = new CancellationTokenSource();
cancelButton.onClick.AddListener(()=>
{
cancelToken.Cancel(); // cancel from button click.
});
var timeoutToken = new CancellationTokenSource();
timeoutToken.CancelAfterSlim(TimeSpan.FromSeconds(5)); // 5sec timeout.
try
{
// combine token
var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancelToken.Token, timeoutToken.Token);
await UnityWebRequest.Get("http://foo").SendWebRequest().WithCancellation(linkedTokenSource.Token);
}
catch (OperationCanceledException ex)
{
if (timeoutToken.IsCancellationRequested)
{
UnityEngine.Debug.Log("Timeout.");
}
else if (cancelToken.IsCancellationRequested)
{
UnityEngine.Debug.Log("Cancel clicked.");
}
_ = ex;
}
// TestAsync(cts.Token).Forget(); // TestAsync(cts.Token).Forget();
okButton.onClick.AddListener(UniTask.UnityAction(async () => okButton.onClick.AddListener(UniTask.UnityAction(async () =>
{ {
await UniTask.WaitForEndOfFrame(this); // try timeout
var texture = new Texture2D(Screen.width, Screen.height); try
texture.ReadPixels(new Rect(0, 0, Screen.width, Screen.height), 0, 0); {
texture.Apply(); //await UniTask.Delay(TimeSpan.FromSeconds(2), cancellationToken: timeoutController.Timeout(TimeSpan.FromSeconds(3)));
UnityEngine.Debug.Log("Delay Complete, Reset(and reuse).");
//timeoutController.Reset();
}
catch (OperationCanceledException ex)
{
//UnityEngine.Debug.Log("Timeout! FromTimeout?:" + timeoutController.IsTimeout());
_ = ex;
}
var jpg = texture.EncodeToJPG(); await UniTask.Yield();
File.WriteAllBytes("testscreencapture.jpg", jpg);
Debug.Log("ok?");
//var texture = ScreenCapture.CaptureScreenshotAsTexture();
//if (texture == null)
//{
// Debug.Log("fail");
//}
//else
//{
// var jpg = texture.EncodeToJPG();
// File.WriteAllBytes("testscreencapture.jpg", jpg);
// Debug.Log("ok?");
//}
})); }));
cancelButton.onClick.AddListener(UniTask.UnityAction(async () => cancelButton.onClick.AddListener(UniTask.UnityAction(async () =>

View File

@@ -0,0 +1,299 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!29 &1
OcclusionCullingSettings:
m_ObjectHideFlags: 0
serializedVersion: 2
m_OcclusionBakeSettings:
smallestOccluder: 5
smallestHole: 0.25
backfaceThreshold: 100
m_SceneGUID: 00000000000000000000000000000000
m_OcclusionCullingData: {fileID: 0}
--- !u!104 &2
RenderSettings:
m_ObjectHideFlags: 0
serializedVersion: 9
m_Fog: 0
m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
m_FogMode: 3
m_FogDensity: 0.01
m_LinearFogStart: 0
m_LinearFogEnd: 300
m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
m_AmbientIntensity: 1
m_AmbientMode: 0
m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0}
m_HaloStrength: 0.5
m_FlareStrength: 1
m_FlareFadeSpeed: 3
m_HaloTexture: {fileID: 0}
m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
m_DefaultReflectionMode: 0
m_DefaultReflectionResolution: 128
m_ReflectionBounces: 1
m_ReflectionIntensity: 1
m_CustomReflection: {fileID: 0}
m_Sun: {fileID: 0}
m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1}
m_UseRadianceAmbientProbe: 0
--- !u!157 &3
LightmapSettings:
m_ObjectHideFlags: 0
serializedVersion: 11
m_GIWorkflowMode: 1
m_GISettings:
serializedVersion: 2
m_BounceScale: 1
m_IndirectOutputScale: 1
m_AlbedoBoost: 1
m_EnvironmentLightingMode: 0
m_EnableBakedLightmaps: 1
m_EnableRealtimeLightmaps: 0
m_LightmapEditorSettings:
serializedVersion: 12
m_Resolution: 2
m_BakeResolution: 40
m_AtlasSize: 1024
m_AO: 0
m_AOMaxDistance: 1
m_CompAOExponent: 1
m_CompAOExponentDirect: 0
m_ExtractAmbientOcclusion: 0
m_Padding: 2
m_LightmapParameters: {fileID: 0}
m_LightmapsBakeMode: 1
m_TextureCompression: 1
m_FinalGather: 0
m_FinalGatherFiltering: 1
m_FinalGatherRayCount: 256
m_ReflectionCompression: 2
m_MixedBakeMode: 2
m_BakeBackend: 1
m_PVRSampling: 1
m_PVRDirectSampleCount: 32
m_PVRSampleCount: 512
m_PVRBounces: 2
m_PVREnvironmentSampleCount: 256
m_PVREnvironmentReferencePointCount: 2048
m_PVRFilteringMode: 1
m_PVRDenoiserTypeDirect: 1
m_PVRDenoiserTypeIndirect: 1
m_PVRDenoiserTypeAO: 1
m_PVRFilterTypeDirect: 0
m_PVRFilterTypeIndirect: 0
m_PVRFilterTypeAO: 0
m_PVREnvironmentMIS: 1
m_PVRCulling: 1
m_PVRFilteringGaussRadiusDirect: 1
m_PVRFilteringGaussRadiusIndirect: 5
m_PVRFilteringGaussRadiusAO: 2
m_PVRFilteringAtrousPositionSigmaDirect: 0.5
m_PVRFilteringAtrousPositionSigmaIndirect: 2
m_PVRFilteringAtrousPositionSigmaAO: 1
m_ExportTrainingData: 0
m_TrainingDataDestination: TrainingData
m_LightProbeSampleCountMultiplier: 4
m_LightingDataAsset: {fileID: 0}
m_UseShadowmask: 1
--- !u!196 &4
NavMeshSettings:
serializedVersion: 2
m_ObjectHideFlags: 0
m_BuildSettings:
serializedVersion: 2
agentTypeID: 0
agentRadius: 0.5
agentHeight: 2
agentSlope: 45
agentClimb: 0.4
ledgeDropHeight: 0
maxJumpAcrossDistance: 0
minRegionArea: 2
manualCellSize: 0
cellSize: 0.16666667
manualTileSize: 0
tileSize: 256
accuratePlacement: 0
debug:
m_Flags: 0
m_NavMeshData: {fileID: 0}
--- !u!1 &386714
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 386717}
- component: {fileID: 386716}
- component: {fileID: 386715}
m_Layer: 0
m_Name: Main Camera
m_TagString: MainCamera
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 0
--- !u!81 &386715
AudioListener:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 386714}
m_Enabled: 1
--- !u!20 &386716
Camera:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 386714}
m_Enabled: 1
serializedVersion: 2
m_ClearFlags: 1
m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
m_projectionMatrixMode: 1
m_GateFitMode: 2
m_FOVAxisMode: 0
m_SensorSize: {x: 36, y: 24}
m_LensShift: {x: 0, y: 0}
m_FocalLength: 50
m_NormalizedViewPortRect:
serializedVersion: 2
x: 0
y: 0
width: 1
height: 1
near clip plane: 0.3
far clip plane: 1000
field of view: 60
orthographic: 0
orthographic size: 5
m_Depth: -1
m_CullingMask:
serializedVersion: 2
m_Bits: 4294967295
m_RenderingPath: -1
m_TargetTexture: {fileID: 0}
m_TargetDisplay: 0
m_TargetEye: 3
m_HDR: 1
m_AllowMSAA: 1
m_AllowDynamicResolution: 0
m_ForceIntoRT: 0
m_OcclusionCulling: 1
m_StereoConvergence: 10
m_StereoSeparation: 0.022
--- !u!4 &386717
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 386714}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 1, z: -10}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &172792889
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 172792893}
- component: {fileID: 172792892}
- component: {fileID: 172792891}
- component: {fileID: 172792890}
m_Layer: 0
m_Name: Cube
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!65 &172792890
BoxCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 172792889}
m_Material: {fileID: 0}
m_IsTrigger: 0
m_Enabled: 1
serializedVersion: 2
m_Size: {x: 1, y: 1, z: 1}
m_Center: {x: 0, y: 0, z: 0}
--- !u!23 &172792891
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 172792889}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
--- !u!33 &172792892
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 172792889}
m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
--- !u!4 &172792893
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 172792889}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}

View File

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

View File

@@ -310,33 +310,33 @@ namespace Cysharp.Threading.TasksTests
yield return null; yield return null;
} }
//[UnityTest] [UnityTest]
//public IEnumerator ExceptionUnobserved1() => UniTask.ToCoroutine(async () => public IEnumerator ExceptionUnobserved1() => UniTask.ToCoroutine(async () =>
//{ {
// bool calledEx = false; bool calledEx = false;
// Action<Exception> action = exx => Action<Exception> action = exx =>
// { {
// calledEx = true; calledEx = true;
// exx.Message.Should().Be("MyException"); exx.Message.Should().Be("MyException");
// }; };
// UniTaskScheduler.UnobservedTaskException += action; UniTaskScheduler.UnobservedTaskException += action;
// var ex = InException1(); var ex = InException1();
// ex = default(UniTask); ex = default(UniTask);
// await UniTask.DelayFrame(3); await UniTask.DelayFrame(3);
// GC.Collect(); GC.Collect();
// GC.WaitForPendingFinalizers(); GC.WaitForPendingFinalizers();
// GC.Collect(); GC.Collect();
// await UniTask.DelayFrame(1); await UniTask.DelayFrame(1);
// calledEx.Should().BeTrue(); calledEx.Should().BeTrue();
// UniTaskScheduler.UnobservedTaskException -= action; UniTaskScheduler.UnobservedTaskException -= action;
//}); });
[UnityTest] [UnityTest]
public IEnumerator ExceptionUnobserved2() => UniTask.ToCoroutine(async () => public IEnumerator ExceptionUnobserved2() => UniTask.ToCoroutine(async () =>
@@ -366,29 +366,6 @@ namespace Cysharp.Threading.TasksTests
UniTaskScheduler.UnobservedTaskException -= action; UniTaskScheduler.UnobservedTaskException -= action;
}); });
// can not run on RuntimeUnitTestToolkit so ignore...
// [UnityTest]
// public IEnumerator ThrowExceptionUnawaited() => UniTask.ToCoroutine(async () =>
// {
// LogAssert.Expect(LogType.Exception, "Exception: MyException");
//#pragma warning disable 1998
// async UniTask Throw() => throw new Exception("MyException");
//#pragma warning restore 1998
//#pragma warning disable 4014
// Throw();
//#pragma warning restore 4014
// await UniTask.DelayFrame(3);
// GC.Collect();
// GC.WaitForPendingFinalizers();
// GC.Collect();
// await UniTask.DelayFrame(1);
// });
async UniTask InException1() async UniTask InException1()
{ {
await UniTask.Yield(); await UniTask.Yield();

View File

@@ -11,4 +11,10 @@ EditorBuildSettings:
- enabled: 1 - enabled: 1
path: Assets/Scenes/ExceptionExamples.unity path: Assets/Scenes/ExceptionExamples.unity
guid: b5fed17e3ece238439bc796d8747df5d guid: b5fed17e3ece238439bc796d8747df5d
- enabled: 1
path: Assets/Scenes/SampleScene.unity
guid: edc485cde0ff14046a21a7c2a51bb0ac
- enabled: 1
path: Assets/Scenes/scene2.unity
guid: 84d1cc7b4b33fa54f98a29e843b1db30
m_configObjects: {} m_configObjects: {}

View File

@@ -8,6 +8,9 @@ EditorUserSettings:
RecentlyUsedScenePath-0: RecentlyUsedScenePath-0:
value: 22424703114646680e0b0227036c6c1118131a25340527392367083debf42d value: 22424703114646680e0b0227036c6c1118131a25340527392367083debf42d
flags: 0 flags: 0
RecentlyUsedScenePath-1:
value: 22424703114646680e0b0227036c6c111b07142f1f2b233e2867083debf42d
flags: 0
vcSharedLogLevel: vcSharedLogLevel:
value: 0d5e400f0650 value: 0d5e400f0650
flags: 0 flags: 0