mirror of
https://github.com/Cysharp/UniTask.git
synced 2026-05-15 11:30:09 +00:00
Compare commits
173 Commits
2.0.1-prev
...
fix-il2cpp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
37cd00d347 | ||
|
|
859c4d706f | ||
|
|
7289fe6e25 | ||
|
|
0c33977f5a | ||
|
|
4d4466e801 | ||
|
|
79330d7cdb | ||
|
|
680ce1098b | ||
|
|
2337d705ec | ||
|
|
d2880a818f | ||
|
|
86ea128bf4 | ||
|
|
a66f378622 | ||
|
|
265f88584b | ||
|
|
686394c861 | ||
|
|
8bad158ab4 | ||
|
|
5e59e7ec86 | ||
|
|
8bb0a48720 | ||
|
|
b4468b4eba | ||
|
|
0725bd1b30 | ||
|
|
ebd80243e0 | ||
|
|
f1ac469058 | ||
|
|
2e0b603d25 | ||
|
|
1d90a40f66 | ||
|
|
1bec3f507e | ||
|
|
1d5ecbb3ab | ||
|
|
e1a4aeb9da | ||
|
|
43f1bb4d85 | ||
|
|
8b7a0e9b15 | ||
|
|
da329e19d1 | ||
|
|
b8260d4e91 | ||
|
|
345e32aaf0 | ||
|
|
d7bef8c5b5 | ||
|
|
b2f82df4d3 | ||
|
|
83596b3d1f | ||
|
|
0022598a1c | ||
|
|
f4294d3752 | ||
|
|
a1444c0b39 | ||
|
|
e1d5359d73 | ||
|
|
239bf749b6 | ||
|
|
2bf3b1e172 | ||
|
|
d225de201f | ||
|
|
c3b8a3852d | ||
|
|
c31dab888e | ||
|
|
d4cf59bd2f | ||
|
|
d5edc3acd3 | ||
|
|
130286e8c2 | ||
|
|
a9baa52309 | ||
|
|
3001996298 | ||
|
|
bfcd18aabb | ||
|
|
96aa299e7e | ||
|
|
24faa34418 | ||
|
|
21bf08a6b3 | ||
|
|
d5db96b913 | ||
|
|
a8455af16d | ||
|
|
2290b14532 | ||
|
|
90c5a6311b | ||
|
|
6e0ad3623b | ||
|
|
005e02a1fa | ||
|
|
10fb8060fa | ||
|
|
35b933730b | ||
|
|
7ab9467069 | ||
|
|
598312ba61 | ||
|
|
985aa5c43a | ||
|
|
10eff95a42 | ||
|
|
d27d6d5d9d | ||
|
|
b8c109848e | ||
|
|
8b7f832c0f | ||
|
|
7cce0f48e5 | ||
|
|
8a56838111 | ||
|
|
ff15e00003 | ||
|
|
f60d2c51fb | ||
|
|
6dfb969015 | ||
|
|
da7e9fc4b3 | ||
|
|
70385c4115 | ||
|
|
51ba740413 | ||
|
|
f3e3ba8864 | ||
|
|
07cf65c1ec | ||
|
|
eca5b1c096 | ||
|
|
c74ce14ad1 | ||
|
|
f59c56506f | ||
|
|
896eef1ee4 | ||
|
|
ec0123eec7 | ||
|
|
78f56b9b33 | ||
|
|
1d88ed85bc | ||
|
|
2b7986da19 | ||
|
|
c3d22968e1 | ||
|
|
0e25122ee2 | ||
|
|
4504d84aa8 | ||
|
|
2b87cadba3 | ||
|
|
21dc83c641 | ||
|
|
3b593f349c | ||
|
|
962c215e3b | ||
|
|
42dcfdbcdc | ||
|
|
6d7e6ec871 | ||
|
|
36d53a3bcb | ||
|
|
ea9e61c2e1 | ||
|
|
a52c26102b | ||
|
|
e31c87b8a8 | ||
|
|
cc165a6897 | ||
|
|
f99910d802 | ||
|
|
997b0b3710 | ||
|
|
ec7064083a | ||
|
|
07cccfddd6 | ||
|
|
f07527cd06 | ||
|
|
7b273c4bd1 | ||
|
|
d36e7987b3 | ||
|
|
bbd5686816 | ||
|
|
fb1152d8f4 | ||
|
|
7a306118f5 | ||
|
|
efaf3ee8f5 | ||
|
|
2e4fe90956 | ||
|
|
e33d572104 | ||
|
|
2b2af9e455 | ||
|
|
d003597662 | ||
|
|
ec0a8f5a8b | ||
|
|
49ba57f20a | ||
|
|
6f4d1183cc | ||
|
|
dd18c9fff8 | ||
|
|
21f5f78ff1 | ||
|
|
1729f389db | ||
|
|
6d37bb7bac | ||
|
|
957adfad7a | ||
|
|
3ef889e17d | ||
|
|
c73af7390f | ||
|
|
c5b4376486 | ||
|
|
ba65049dd8 | ||
|
|
ee58aab0a9 | ||
|
|
859eaa2278 | ||
|
|
79f770e687 | ||
|
|
8ff4de67a1 | ||
|
|
6a7a6fde5c | ||
|
|
6a5e259006 | ||
|
|
f6622ad29c | ||
|
|
3de29a181d | ||
|
|
090cacece5 | ||
|
|
354fd65d58 | ||
|
|
d3538bdc8f | ||
|
|
bd6906792d | ||
|
|
7fc6c6bd36 | ||
|
|
c23b9ca480 | ||
|
|
cda59ba9c2 | ||
|
|
61a3744fdd | ||
|
|
72efadd0a2 | ||
|
|
57c414a6e0 | ||
|
|
85dc70a3ab | ||
|
|
7298686d5a | ||
|
|
418586fbfb | ||
|
|
12c507574e | ||
|
|
b20b37e7a5 | ||
|
|
8ef7a66081 | ||
|
|
a5f47d4095 | ||
|
|
1316328766 | ||
|
|
c0da316cb4 | ||
|
|
4d13523df7 | ||
|
|
16c527fa89 | ||
|
|
5db5beab29 | ||
|
|
3f082f1923 | ||
|
|
93dd82e3d4 | ||
|
|
af6dbd8868 | ||
|
|
716decd199 | ||
|
|
f37cd703a9 | ||
|
|
31b788a2c9 | ||
|
|
e93bcbf564 | ||
|
|
aa8cb80866 | ||
|
|
dd6a8da96f | ||
|
|
d4511c0f67 | ||
|
|
c16433e0fe | ||
|
|
ed0990e402 | ||
|
|
856a049dd0 | ||
|
|
61b798b6e9 | ||
|
|
be45066773 | ||
|
|
5d0d29dffd | ||
|
|
be539fdb10 | ||
|
|
66fa203f7c |
@@ -1,114 +0,0 @@
|
||||
version: 2.1
|
||||
executors:
|
||||
unity:
|
||||
# https://hub.docker.com/r/gableroux/unity3d/tags
|
||||
parameters:
|
||||
version: {type: string}
|
||||
docker:
|
||||
- image: gableroux/unity3d:<< parameters.version >>
|
||||
go:
|
||||
docker:
|
||||
- image: circleci/golang
|
||||
commands:
|
||||
unity_activate:
|
||||
parameters:
|
||||
unity_version: {type: string}
|
||||
unity_license: {type: string}
|
||||
steps:
|
||||
# get activation file, if fail to activate unity, use this key and activate from https://license.unity3d.com/manual
|
||||
- run: apt update && apt install libunwind8 -y
|
||||
- run: /opt/Unity/Editor/Unity -quit -batchmode -nographics -logFile -createManualActivationFile || exit 0
|
||||
- run: cat Unity_v<< parameters.unity_version >>.alf
|
||||
# get from UNITY_LICENSE envvar(base64 encoded(cat foo.ulf | base64 )), this file is generated from above manual activation
|
||||
- run: echo << parameters.unity_license >> | base64 -di >> .circleci/Unity.ulf
|
||||
- run: /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -manualLicenseFile .circleci/Unity.ulf || exit 0
|
||||
jobs:
|
||||
build-and-test:
|
||||
parameters:
|
||||
unity_version: {type: string}
|
||||
unity_license: {type: string}
|
||||
executor:
|
||||
name: unity
|
||||
version: << parameters.unity_version >>
|
||||
steps:
|
||||
- checkout
|
||||
- unity_activate:
|
||||
unity_version: << parameters.unity_version >>
|
||||
unity_license: << parameters.unity_license >>
|
||||
- run:
|
||||
name: Build Linux(Mono)
|
||||
command: /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod UnitTestBuilder.BuildUnitTest /headless /ScriptBackend Mono2x /BuildTarget StandaloneLinux64
|
||||
working_directory: .
|
||||
# TODO:check unity version and packages...
|
||||
# - run: ./bin/UnitTest/StandaloneLinux64_Mono2x/test
|
||||
build-and-create-package:
|
||||
parameters:
|
||||
unity_version: {type: string}
|
||||
unity_license: {type: string}
|
||||
executor:
|
||||
name: unity
|
||||
version: << parameters.unity_version >>
|
||||
steps:
|
||||
- checkout
|
||||
- unity_activate:
|
||||
unity_version: << parameters.unity_version >>
|
||||
unity_license: << parameters.unity_license >>
|
||||
- run:
|
||||
name: Export unitypackage
|
||||
command: /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod PackageExporter.Export
|
||||
working_directory: .
|
||||
- store_artifacts:
|
||||
path: ./UniRx.Async.unitypackage
|
||||
destination: /UniRx.Async.unitypackage
|
||||
# upload to github by ghr
|
||||
upload-github:
|
||||
executor: go
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- run: go get github.com/tcnksm/ghr
|
||||
- run: ghr -t ${GITHUB_TOKEN} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} ${CIRCLE_TAG} .
|
||||
- store_artifacts:
|
||||
path: UniRx.Async.unitypackage
|
||||
destination: UniRx.Async.unitypackage
|
||||
workflows:
|
||||
version: 2
|
||||
build-unity:
|
||||
jobs:
|
||||
# does not exists yet.
|
||||
# - build-and-test:
|
||||
# unity_version: 2019.3.0a2
|
||||
# unity_license: ${UNITY_LICENSE_2019_3}
|
||||
# - build-and-test:
|
||||
# unity_version: 2019.2.0b2
|
||||
# unity_license: ${UNITY_LICENSE_2019_2}
|
||||
- build-and-test:
|
||||
unity_version: 2019.1.2f1
|
||||
unity_license: ${UNITY_LICENSE_2019_1}
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
# test asmdef will not found.
|
||||
# - build-and-test:
|
||||
# unity_version: 2018.4.0f1
|
||||
# unity_license: ${UNITY_LICENSE_2018_4}
|
||||
# # UniTask minimum support version is 2018.3(C# 7.x)
|
||||
# - build-and-test:
|
||||
# unity_version: 2018.3.12f1
|
||||
# unity_license: ${UNITY_LICENSE_2018_3}
|
||||
- build-and-create-package:
|
||||
unity_version: 2019.1.2f1
|
||||
unity_license: ${UNITY_LICENSE_2019_1}
|
||||
filters:
|
||||
tags:
|
||||
only: /^\d\.\d\.\d.*/
|
||||
branches:
|
||||
ignore: /.*/
|
||||
- upload-github:
|
||||
requires:
|
||||
- build-and-create-package
|
||||
filters:
|
||||
tags:
|
||||
only: /^\d\.\d\.\d.*/
|
||||
branches:
|
||||
ignore: /.*/
|
||||
73
.github/workflows/build-debug.yml
vendored
Normal file
73
.github/workflows/build-debug.yml
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
name: Build-Debug
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "**"
|
||||
tags:
|
||||
- "!*" # not a tag push
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
|
||||
jobs:
|
||||
build-dotnet:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
|
||||
NUGET_XMLDOC_MODE: skip
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
dotnet-version: 3.1.101
|
||||
- run: dotnet test -c Debug ./src/UniTask.NetCoreTests/UniTask.NetCoreTests.csproj
|
||||
|
||||
build-unity:
|
||||
strategy:
|
||||
matrix:
|
||||
unity: ['2019.3.9f1', '2020.1.0b5']
|
||||
include:
|
||||
- unity: 2019.3.9f1
|
||||
license: UNITY_2019_3
|
||||
- unity: 2020.1.0b5
|
||||
license: UNITY_2020_1
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
# with linux-il2cpp. image from https://hub.docker.com/r/gableroux/unity3d/tags
|
||||
image: gableroux/unity3d:${{ matrix.unity }}-linux-il2cpp
|
||||
steps:
|
||||
- run: apt update && apt install git -y
|
||||
- uses: actions/checkout@v2
|
||||
# create unity activation file and store to artifacts.
|
||||
- run: /opt/Unity/Editor/Unity -quit -batchmode -nographics -logFile -createManualActivationFile || exit 0
|
||||
- uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: Unity_v${{ matrix.unity }}.alf
|
||||
path: ./Unity_v${{ matrix.unity }}.alf
|
||||
# activate Unity from manual license file(ulf)
|
||||
- run: echo -n "$UNITY_LICENSE" >> .Unity.ulf
|
||||
env:
|
||||
UNITY_LICENSE: ${{ secrets[matrix.license] }}
|
||||
- name: Activate Unity, always returns a success. But if a subsequent run fails, the activation may have failed(if succeeded, shows `Next license update check is after` and not shows other message(like GUID != GUID). If fails not). In that case, upload the artifact's .alf file to https://license.unity3d.com/manual to get the .ulf file and set it to secrets.
|
||||
run: /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -manualLicenseFile .Unity.ulf || exit 0
|
||||
|
||||
# Execute scripts: RuntimeUnitTestToolkit
|
||||
- name: Build UnitTest(Linux64, mono)
|
||||
run: /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod UnitTestBuilder.BuildUnitTest /headless /ScriptBackend mono /BuildTarget StandaloneLinux64
|
||||
working-directory: src/UniTask
|
||||
- name: Execute UnitTest
|
||||
run: ./src/UniTask/bin/UnitTest/StandaloneLinux64_Mono2x/test
|
||||
|
||||
# Execute scripts: Export Package
|
||||
- name: Export unitypackage
|
||||
run: /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod PackageExporter.Export
|
||||
working-directory: src/UniTask
|
||||
|
||||
# Store artifacts.
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: UniTask.unitypackage.zip
|
||||
path: ./src/UniTask/*.unitypackage
|
||||
31
.github/workflows/build-docs.yml
vendored
Normal file
31
.github/workflows/build-docs.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
name: build-docs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- feature/docs
|
||||
|
||||
jobs:
|
||||
run-docfx:
|
||||
if: "!(contains(github.event.head_commit.message, '[skip ci]') || contains(github.event.head_commit.message, '[ci skip]'))"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
repository: Cysharp/DocfxTemplate
|
||||
path: docs/_DocfxTemplate
|
||||
- uses: Kirbyrawr/docfx-action@master
|
||||
name: Docfx metadata
|
||||
with:
|
||||
args: metadata docs/docfx.json
|
||||
- uses: Kirbyrawr/docfx-action@master
|
||||
name: Docfx build
|
||||
with:
|
||||
args: build docs/docfx.json
|
||||
- name: Publish to GitHub Pages
|
||||
uses: peaceiris/actions-gh-pages@v3
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: docs/_site
|
||||
105
.github/workflows/build-release.yml
vendored
Normal file
105
.github/workflows/build-release.yml
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
name: Build-Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "[0-9]+.[0-9]+.[0-9]+*"
|
||||
|
||||
jobs:
|
||||
build-dotnet:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
|
||||
NUGET_XMLDOC_MODE: skip
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
dotnet-version: 3.1.101
|
||||
# set release tag(*.*.*) to env.GIT_TAG
|
||||
- run: echo ::set-env name=GIT_TAG::${GITHUB_REF#refs/tags/}
|
||||
|
||||
# build and pack
|
||||
- run: dotnet build -c Release -p:Version=${{ env.GIT_TAG }}
|
||||
- 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 }}
|
||||
|
||||
# Store artifacts.
|
||||
- uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: nuget
|
||||
path: ./src/UniTask.NetCore/bin/Release/UniTask.${{ env.GIT_TAG }}.nupkg
|
||||
|
||||
build-unity:
|
||||
strategy:
|
||||
matrix:
|
||||
unity: ['2019.3.9f1']
|
||||
include:
|
||||
- unity: 2019.3.9f1
|
||||
license: UNITY_2019_3
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
# with linux-il2cpp. image from https://hub.docker.com/r/gableroux/unity3d/tags
|
||||
image: gableroux/unity3d:${{ matrix.unity }}-linux-il2cpp
|
||||
steps:
|
||||
- run: apt update && apt install git -y
|
||||
- uses: actions/checkout@v2
|
||||
- run: echo -n "$UNITY_LICENSE" >> .Unity.ulf
|
||||
env:
|
||||
UNITY_LICENSE: ${{ secrets[matrix.license] }}
|
||||
- run: /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -manualLicenseFile .Unity.ulf || exit 0
|
||||
|
||||
# set release tag(*.*.*) to env.GIT_TAG
|
||||
- run: echo ::set-env name=GIT_TAG::${GITHUB_REF#refs/tags/}
|
||||
|
||||
# Execute scripts: Export Package
|
||||
- name: Export unitypackage
|
||||
run: /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod PackageExporter.Export
|
||||
working-directory: src/UniTask
|
||||
|
||||
# Store artifacts.
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: UniTask.${{ env.GIT_TAG }}.unitypackage
|
||||
path: ./src/UniTask/UniTask.${{ env.GIT_TAG }}.unitypackage
|
||||
|
||||
create-release:
|
||||
needs: [build-dotnet, build-unity]
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
|
||||
NUGET_XMLDOC_MODE: skip
|
||||
steps:
|
||||
# setup dotnet for nuget push
|
||||
- uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
dotnet-version: 3.1.101
|
||||
# set release tag(*.*.*) to env.GIT_TAG
|
||||
- run: echo ::set-env name=GIT_TAG::${GITHUB_REF#refs/tags/}
|
||||
|
||||
# Create Releases
|
||||
- uses: actions/create-release@v1
|
||||
id: create_release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: Ver.${{ github.ref }}
|
||||
|
||||
# Download(All) Artifacts to current directory
|
||||
- uses: actions/download-artifact@v2-preview
|
||||
|
||||
# Upload to NuGet
|
||||
- run: dotnet nuget push "./nuget/*.nupkg" -s https://www.nuget.org/api/v2/package -k ${{ secrets.NUGET_KEY }}
|
||||
|
||||
# Upload to Releases(unitypackage)
|
||||
- uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./UniTask.${{ env.GIT_TAG }}.unitypackage/UniTask.${{ env.GIT_TAG }}.unitypackage
|
||||
asset_name: UniTask.${{ env.GIT_TAG }}.unitypackage
|
||||
asset_content_type: application/octet-stream
|
||||
15
.github/workflows/toc.yml
vendored
Normal file
15
.github/workflows/toc.yml
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
name: TOC Generator
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'README.md'
|
||||
|
||||
jobs:
|
||||
generateTOC:
|
||||
name: TOC Generator
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: technote-space/toc-generator@v2.4.0
|
||||
with:
|
||||
TOC_TITLE: "## Table of Contents"
|
||||
50
.gitignore
vendored
50
.gitignore
vendored
@@ -156,4 +156,52 @@ src/UniTask/UniTask.Tests.csproj
|
||||
|
||||
src/UniTask/UniTask.Tests.Editor.csproj
|
||||
|
||||
src/UniTask/UniTask.unitypackage
|
||||
src/UniTask/UniTask.*.unitypackage
|
||||
|
||||
src/UniTask/UniTask.Linq.csproj
|
||||
|
||||
src/UniTask/DOTween.Modules.csproj
|
||||
|
||||
src/UniTask/Unity.Addressables.csproj
|
||||
|
||||
src/UniTask/Unity.Addressables.Editor.csproj
|
||||
|
||||
src/UniTask/Unity.Analytics.DataPrivacy.csproj
|
||||
|
||||
src/UniTask/Unity.Recorder.csproj
|
||||
|
||||
src/UniTask/Unity.Recorder.Editor.csproj
|
||||
|
||||
src/UniTask/Unity.ResourceManager.csproj
|
||||
|
||||
src/UniTask/Unity.Rider.Editor.csproj
|
||||
|
||||
src/UniTask/Unity.ScriptableBuildPipeline.csproj
|
||||
|
||||
src/UniTask/Unity.ScriptableBuildPipeline.Editor.csproj
|
||||
|
||||
src/UniTask/Unity.TextMeshPro.csproj
|
||||
|
||||
src/UniTask/Unity.TextMeshPro.Editor.csproj
|
||||
|
||||
src/UniTask/Unity.Timeline.csproj
|
||||
|
||||
src/UniTask/Unity.Timeline.Editor.csproj
|
||||
|
||||
src/UniTask/Unity.VisualStudio.Editor.csproj
|
||||
|
||||
src/UniTask/Unity.VSCode.Editor.csproj
|
||||
|
||||
src/UniTask/UnityEditor.CacheServer.csproj
|
||||
|
||||
src/UniTask/UnityEditor.TestRunner.csproj
|
||||
|
||||
src/UniTask/UnityEditor.UI.csproj
|
||||
|
||||
src/UniTask/UnityEngine.Advertisements.csproj
|
||||
|
||||
src/UniTask/UnityEngine.Monetization.csproj
|
||||
|
||||
src/UniTask/UnityEngine.TestRunner.csproj
|
||||
|
||||
src/UniTask/UnityEngine.UI.csproj
|
||||
|
||||
591
README.md
591
README.md
@@ -1,50 +1,106 @@
|
||||
# UniTask
|
||||
UniTask
|
||||
===
|
||||
[](https://github.com/Cysharp/UniTask/actions) [](https://github.com/Cysharp/UniTask/releases)
|
||||
|
||||
[](https://circleci.com/gh/Cysharp/UniTask)
|
||||
Provides an efficient async/await integration to Unity.
|
||||
|
||||
Provides an efficient async/await integration to Unity.
|
||||
* Struct based `UniTask<T>` and custom AsyncMethodBuilder to achive zero allocation
|
||||
* All Unity AsyncOperations and Coroutine to awaitable
|
||||
* PlayerLoop based task(`UniTask.Yield`, `UniTask.Delay`, `UniTask.DelayFrame`, etc..) that enable to replace all coroutine operation
|
||||
* MonoBehaviour Message Events and uGUI Events as awaitable/async-enumerable
|
||||
* Completely run on Unity's PlayerLoop so don't use thread and run on WebGL, wasm, etc.
|
||||
* Asynchronous LINQ, with Channel and AsyncReactiveProperty
|
||||
* TaskTracker window to prevent memory leak
|
||||
* Highly compatible behaviour with Task/ValueTask/IValueTaskSource
|
||||
|
||||
> UniTask was included in UniRx before v7 but now completely separated, it no dependent each other.
|
||||
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
||||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
||||
## Table of Contents
|
||||
|
||||
- [Getting started](#getting-started)
|
||||
- [Basics of UniTask and AsyncOperation](#basics-of-unitask-and-asyncoperation)
|
||||
- [Cancellation and Exception handling](#cancellation-and-exception-handling)
|
||||
- [Progress](#progress)
|
||||
- [PlayerLoop](#playerloop)
|
||||
- [async void vs async UniTaskVoid](#async-void-vs-async-unitaskvoid)
|
||||
- [UniTaskTracker](#unitasktracker)
|
||||
- [External Assets](#external-assets)
|
||||
- [AsyncEnumerable and Async LINQ](#asyncenumerable-and-async-linq)
|
||||
- [Awaitable Events](#awaitable-events)
|
||||
- [Channel](#channel)
|
||||
- [For Unit Testing](#for-unit-testing)
|
||||
- [Pooling Configuration](#pooling-configuration)
|
||||
- [API References](#api-references)
|
||||
- [UPM Package](#upm-package)
|
||||
- [Install via git URL](#install-via-git-url)
|
||||
- [Install via OpenUPM](#install-via-openupm)
|
||||
- [.NET Core](#net-core)
|
||||
- [License](#license)
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
|
||||
Getting started
|
||||
---
|
||||
Install package(`UniRx.Async.unitypackage`) is available in [UniTask/releases](https://github.com/Cysharp/UniTask/releases) page.
|
||||
Install via [UPM package](#upm-package) or asset package(`UniTask.*.*.*.unitypackage`) available in [UniTask/releases](https://github.com/Cysharp/UniTask/releases) page.
|
||||
|
||||
```csharp
|
||||
// extension awaiter/methods can be used by this namespace
|
||||
using UniRx.Async;
|
||||
using Cysharp.Threading.Tasks;
|
||||
|
||||
// You can return type as struct UniTask<T>(or UniTask), it is unity specialized lightweight alternative of Task<T>
|
||||
// no(or less) allocation and fast excution for zero overhead async/await integrate with Unity
|
||||
// zero allocation and fast excution for zero overhead async/await integrate with Unity
|
||||
async UniTask<string> DemoAsync()
|
||||
{
|
||||
// You can await Unity's AsyncObject
|
||||
var asset = await Resources.LoadAsync<TextAsset>("foo");
|
||||
|
||||
// .ConfigureAwait accepts progress callback
|
||||
await SceneManager.LoadSceneAsync("scene2").ConfigureAwait(Progress.Create<float>(x => Debug.Log(x)));
|
||||
|
||||
var txt = (await UnityWebRequest.Get("https://...").SendWebRequest()).downloadHandler.text;
|
||||
await SceneManager.LoadSceneAsync("scene2");
|
||||
|
||||
// .WithCancellation enables Cancel, GetCancellationTokenOnDestroy synchornizes with lifetime of GameObject
|
||||
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>
|
||||
var asset3 = await Resources.LoadAsync<TextAsset>("baz").ToUniTask(Progress.Create<float>(x => Debug.Log(x)));
|
||||
|
||||
// await frame-based operation like coroutine
|
||||
await UniTask.DelayFrame(100);
|
||||
|
||||
// replacement of WaitForSeconds/WaitForSecondsRealtime
|
||||
// replacement of yield return new WaitForSeconds/WaitForSecondsRealtime
|
||||
await UniTask.Delay(TimeSpan.FromSeconds(10), ignoreTimeScale: false);
|
||||
|
||||
// replacement of WaitForEndOfFrame(or other timing like yield return null, yield return WaitForFixedUpdate)
|
||||
await UniTask.Yield(PlayerLoopTiming.PostLateUpdate);
|
||||
// yield any playerloop timing(PreUpdate, Update, LateUpdate, etc...)
|
||||
await UniTask.Yield(PlayerLoopTiming.PreLateUpdate);
|
||||
|
||||
// replacement of yield return null
|
||||
await UniTask.Yield();
|
||||
await UniTask.NextFrame();
|
||||
|
||||
// replacement of WaitForEndOfFrame(same as UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate))
|
||||
await UniTask.WaitForEndOfFrame();
|
||||
|
||||
// replacement of yield return new WaitForFixedUpdate(same as UniTask.Yield(PlayerLoopTiming.FixedUpdate))
|
||||
await UniTask.WaitForFixedUpdate();
|
||||
|
||||
// replacement of yield return WaitUntil
|
||||
await UniTask.WaitUntil(() => isActive == false);
|
||||
|
||||
// special helper of WaitUntil
|
||||
await UniTask.WaitUntilValueChanged(this, x => x.isActive);
|
||||
|
||||
// You can await IEnumerator coroutine
|
||||
await FooCoroutineEnumerator();
|
||||
|
||||
// You can await standard task
|
||||
await Task.Run(() => 100);
|
||||
|
||||
// Multithreading, run on ThreadPool under this code(use SwitchToMainThread, same as `ObserveOnMainThreadDispatcher`)
|
||||
// Multithreading, run on ThreadPool under this code
|
||||
await UniTask.SwitchToThreadPool();
|
||||
|
||||
/* work on ThreadPool */
|
||||
|
||||
// return to MainThread(same as `ObserveOnMainThread` in UniRx)
|
||||
await UniTask.SwitchToMainThread();
|
||||
|
||||
// get async webrequest
|
||||
async UniTask<string> GetTextAsync(UnityWebRequest req)
|
||||
{
|
||||
@@ -58,6 +114,9 @@ async UniTask<string> DemoAsync()
|
||||
|
||||
// concurrent async-wait and get result easily by tuple syntax
|
||||
var (google, bing, yahoo) = await UniTask.WhenAll(task1, task2, task3);
|
||||
|
||||
// shorthand of WhenAll, tuple can await directly
|
||||
var (google2, bing2, yahoo2) = await (task1, task2, task3);
|
||||
|
||||
// You can handle timeout easily
|
||||
await GetTextAsync(UnityWebRequest.Get("http://unity.com")).Timeout(TimeSpan.FromMilliseconds(300));
|
||||
@@ -67,47 +126,38 @@ async UniTask<string> DemoAsync()
|
||||
}
|
||||
```
|
||||
|
||||
`UniTask<T>`
|
||||
Basics of UniTask and AsyncOperation
|
||||
---
|
||||
UniTask feature rely on C# 7.0([task-like custom async method builder feature](https://github.com/dotnet/roslyn/blob/master/docs/features/task-types.md)) so required Unity version is after `Unity 2018.3`.
|
||||
UniTask feature rely on C# 7.0([task-like custom async method builder feature](https://github.com/dotnet/roslyn/blob/master/docs/features/task-types.md)) so required Unity version is after `Unity 2018.3`, officialy lower support version is `Unity 2018.4.13f1`.
|
||||
|
||||
Why UniTask(custom task-like object) is required? Because Task is too heavy, not matched to Unity threading(single-thread). UniTask does not use thread and SynchronizationContext because almost Unity's asynchronous object is automaticaly dispatched by Unity's engine layer. It acquires more fast and more less allocation, completely integrated with Unity.
|
||||
Why UniTask(custom task-like object) is required? Because Task is too heavy, not matched to Unity threading(single-thread). UniTask does not use thread and SynchronizationContext/ExecutionContext because almost Unity's asynchronous object is automaticaly dispatched by Unity's engine layer. It acquires more fast and more less allocation, completely integrated with Unity.
|
||||
|
||||
> More details, please see this slide: [Deep Dive async/await in Unity with UniTask(EN)
|
||||
](https://www.slideshare.net/neuecc/deep-dive-asyncawait-in-unity-with-unitasken)
|
||||
You can await `AsyncOperation`, `ResourceRequest`, `AssetBundleRequest`, `AssetBundleCreateRequest`, `UnityWebRequestAsyncOperation`, `IEnumerator` and others when `using Cysharp.Threading.Tasks;`.
|
||||
|
||||
You can await `AsyncOperation`, `ResourceRequest`, `UnityWebRequestAsyncOperation`, `IEnumerator` and others when using `UniRx.Async`.
|
||||
|
||||
`UniTask.Delay`, `UniTask.Yield`, `UniTask.Timeout` that is frame-based timer operators(no uses thread so works on WebGL publish) driven by custom PlayerLoop(Unity 2018 experimental feature). In default, UniTask initialize automatically when application begin, but it is override all. If you want to append PlayerLoop, please call `PlayerLoopHelper.Initialize(ref yourCustomizedPlayerLoop)` manually.
|
||||
|
||||
> Before Unity 2019.3, Unity does not have `PlayerLooop.GetCurrentPlayerLoop` so you can't use with Unity ECS package in default. If you want to use with ECS and before Unity 2019.3, you can use this hack below.
|
||||
UniTask provides three pattern of extension methods.
|
||||
|
||||
```csharp
|
||||
// Get ECS Loop.
|
||||
var playerLoop = ScriptBehaviourUpdateOrder.CurrentPlayerLoop;
|
||||
|
||||
// Setup UniTask's PlayerLoop.
|
||||
PlayerLoopHelper.Initialize(ref playerLoop);
|
||||
* await asyncOperation;
|
||||
* .WithCancellation(CancellationToken);
|
||||
* .ToUniTask(IProgress, PlayerLoopTiming, CancellationToken);
|
||||
```
|
||||
|
||||
`UniTask.WhenAll`, `UniTask.WhenAny` is like Task.WhenAll/WhenAny but return type is more useful.
|
||||
`WithCancellation` is a simple version of `ToUniTask`, both returns `UniTask`. Details of cancellation, see: [Cancellation and Exception handling](#cancellation-and-exception-handling) section.
|
||||
|
||||
`UniTask.ctor(Func<UniTask>)` is like the embeded [`AsyncLazy<T>`](https://blogs.msdn.microsoft.com/pfxteam/2011/01/15/asynclazyt/)
|
||||
> Note: WithCancellation is returned from native timing of PlayerLoop but ToUniTask is returned from specified PlayerLoopTiming. Details of timing, see: [PlayerLoop](#playerloop) section.
|
||||
|
||||
The type of `UniTask` can use utility like `UniTask.WhenAll`, `UniTask.WhenAny`. It is like Task.WhenAll/WhenAny but return type is more useful, returns value tuple so can deconsrtuct each result and pass multiple type.
|
||||
|
||||
```csharp
|
||||
public class SceneAssets
|
||||
{
|
||||
public readonly UniTask<Sprite> Front;
|
||||
public readonly UniTask<Sprite> Background;
|
||||
public readonly UniTask<Sprite> Effect;
|
||||
|
||||
public SceneAssets()
|
||||
{
|
||||
// ctor(Func) overload is AsyncLazy, initialized once when await.
|
||||
// and after it, await returns zero-allocation value immediately.
|
||||
Front = new UniTask<Sprite>(() => LoadAsSprite("foo"));
|
||||
Background = new UniTask<Sprite>(() => LoadAsSprite("bar"));
|
||||
Effect = new UniTask<Sprite>(() => LoadAsSprite("baz"));
|
||||
// parallel load.
|
||||
var (a, b, c) = await UniTask.WhenAll(
|
||||
LoadAsSprite("foo"),
|
||||
LoadAsSprite("bar"),
|
||||
LoadAsSprite("baz"));
|
||||
}
|
||||
|
||||
async UniTask<Sprite> LoadAsSprite(string path)
|
||||
@@ -133,13 +183,32 @@ public UniTask<int> WrapByUniTaskCompletionSource()
|
||||
}
|
||||
```
|
||||
|
||||
You can convert Task -> UniTask: `AsUniTask`, `UniTask` -> `UniTask<AsyncUnit>`: `AsAsyncUnitUniTask`(this is useful to use WhenAll/WhenAny), `UniTask<T>` -> `UniTask`: `AsUniTask`.
|
||||
You can convert Task -> UniTask: `AsUniTask`, `UniTask` -> `UniTask<AsyncUnit>`: `AsAsyncUnitUniTask`, `UniTask<T>` -> `UniTask`: `AsUniTask`. `UniTask<T>` -> `UniTask`'s conversion cost is free.
|
||||
|
||||
If you want to convert async to coroutine, you can use `UniTask.ToCoroutine`, this is useful to use only allow coroutine system.
|
||||
If you want to convert async to coroutine, you can use `.ToCoroutine()`, this is useful to use only allow coroutine system.
|
||||
|
||||
UniTask can not await twice. This is a similar constraint to the [ValueTask/IValueTaskSource](https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.valuetask-1?view=netcore-3.1) introduced in .NET Standard 2.1.
|
||||
|
||||
> The following operations should never be performed on a ValueTask<TResult> instance:
|
||||
>
|
||||
> * Awaiting the instance multiple times.
|
||||
> * Calling AsTask multiple times.
|
||||
> * Using .Result or .GetAwaiter().GetResult() when the operation hasn't yet completed, or using them multiple times.
|
||||
> * Using more than one of these techniques to consume the instance.
|
||||
>
|
||||
> If you do any of the above, the results are undefined.
|
||||
|
||||
```csharp
|
||||
var task = UniTask.DelayFrame(10);
|
||||
await task;
|
||||
await task; // NG, throws Exception
|
||||
```
|
||||
|
||||
Store to the class field, you can use `UniTask.Lazy` that gurantee call multipletimes. `.Prevent()` allows for multiple calls (internally cached results). This is useful when multiple calls in a function scope.
|
||||
|
||||
Cancellation and Exception handling
|
||||
---
|
||||
Some UniTask factory methods have `CancellationToken cancellation = default(CancellationToken)` parameter. Andalso some async operation for unity have `ConfigureAwait(..., CancellationToken cancellation = default(CancellationToken))` extension methods.
|
||||
Some UniTask factory methods have `CancellationToken cancellationToken = default` parameter. Andalso some async operation for unity have `WithCancellation(CancellationToken)` and `ToUniTask(..., CancellationToken cancellation = default)` extension methods.
|
||||
|
||||
You can pass `CancellationToken` to parameter by standard [`CancellationTokenSource`](https://docs.microsoft.com/en-us/dotnet/api/system.threading.cancellationtokensource).
|
||||
|
||||
@@ -151,7 +220,7 @@ cancelButton.onClick.AddListener(() =>
|
||||
cts.Cancel();
|
||||
});
|
||||
|
||||
await UnityWebRequest.Get("http://google.co.jp").SendWebRequest().ConfigureAwait(cancellation: cts.Token);
|
||||
await UnityWebRequest.Get("http://google.co.jp").SendWebRequest().WithCancellation(cts.Token);
|
||||
|
||||
await UniTask.DelayFrame(1000, cancellationToken: cts.Token);
|
||||
```
|
||||
@@ -165,7 +234,7 @@ await UniTask.DelayFrame(1000, cancellationToken: this.GetCancellationTokenOnDes
|
||||
|
||||
When detect cancellation, all methods throws `OperationCanceledException` and propagate to upstream. `OperationCanceledException` is special exception, if not handled this exception, finally it is propagated to `UniTaskScheduler.UnobservedTaskException`.
|
||||
|
||||
Default behaviour of received unhandled exception is write log as warning. Log level can change by `UniTaskScheduler.UnobservedExceptionWriteLogType`. If you want to change custom beavhiour, set action to `UniTaskScheduler.UnobservedTaskException.`
|
||||
Default behaviour of received unhandled exception is write log as exception. Log level can change by `UniTaskScheduler.UnobservedExceptionWriteLogType`. If you want to change custom beavhiour, set action to `UniTaskScheduler.UnobservedTaskException.`
|
||||
|
||||
If you want to cancel behaviour in async UniTask method, throws `OperationCanceledException` manually.
|
||||
|
||||
@@ -204,23 +273,23 @@ if (isCanceled)
|
||||
}
|
||||
```
|
||||
|
||||
Note: Only suppress throws if you call it directly into the most source method.
|
||||
Note: Only suppress throws if you call it directly into the most source method. Otherwise, the return value will be converted, but the entire pipeline will not be suppressed throws.
|
||||
|
||||
Progress
|
||||
---
|
||||
Some async operation for unity have `ConfigureAwait(IProgress<float> progress = null, ...)` extension methods.
|
||||
Some async operation for unity have `ToUniTask(IProgress<float> progress = null, ...)` extension methods.
|
||||
|
||||
```csharp
|
||||
var progress = Progress.Create<float>(x => Debug.Log(x));
|
||||
|
||||
var request = await UnityWebRequest.Get("http://google.co.jp")
|
||||
.SendWebRequest()
|
||||
.ConfigureAwait(progress: progress);
|
||||
.ToUniTask(progress: progress);
|
||||
```
|
||||
|
||||
Should not use `new System.Progress<T>`, because it allocate every times. Use `UniRx.Async.Progress` instead. Progress factory has two methods, `Create` and `CreateOnlyValueChanged`. `CreateOnlyValueChanged` calls only when progress value changed. Should not use `new System.Progress<T>`, it allocate every times.
|
||||
You should not use standard `new System.Progress<T>`, because it causes allocation every times. Use `Cysharp.Threading.Tasks.Progress` instead. This progress factory has two methods, `Create` and `CreateOnlyValueChanged`. `CreateOnlyValueChanged` calls only when progress value changed.
|
||||
|
||||
Implements interface is more better.
|
||||
Implements IProgress interface to caller is more better, there is no allocation of lambda.
|
||||
|
||||
```csharp
|
||||
public class Foo : MonoBehaviour, IProgress<float>
|
||||
@@ -234,16 +303,135 @@ public class Foo : MonoBehaviour, IProgress<float>
|
||||
{
|
||||
var request = await UnityWebRequest.Get("http://google.co.jp")
|
||||
.SendWebRequest()
|
||||
.ConfigureAwait(progress: this); // pass this
|
||||
.ToUniTask(progress: this); // pass this
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
PlayerLoop
|
||||
---
|
||||
UniTask is run on custom [PlayerLoop](https://docs.unity3d.com/ScriptReference/LowLevel.PlayerLoop.html). UniTask's playerloop based method(such as `Delay`, `DelayFrame`, `asyncOperation.ToUniTask`, etc...) accepts this `PlayerLoopTiming`.
|
||||
|
||||
```csharp
|
||||
public enum PlayerLoopTiming
|
||||
{
|
||||
Initialization = 0,
|
||||
LastInitialization = 1,
|
||||
|
||||
EarlyUpdate = 2,
|
||||
LastEarlyUpdate = 3,
|
||||
|
||||
FixedUpdate = 4,
|
||||
LastFixedUpdate = 5,
|
||||
|
||||
PreUpdate = 6,
|
||||
LastPreUpdate = 7,
|
||||
|
||||
Update = 8,
|
||||
LastUpdate = 9,
|
||||
|
||||
PreLateUpdate = 10,
|
||||
LastPreLateUpdate = 11,
|
||||
|
||||
PostLateUpdate = 12,
|
||||
LastPostLateUpdate = 13
|
||||
}
|
||||
```
|
||||
|
||||
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 as `yield return null` in 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 as `WaitForFixedUpdate`, `PlayerLoopTiming.LastPostLateUpdate` is similar as `WaitForEndOfFrame` in coroutine.
|
||||
|
||||
`yield return null` and `UniTask.Yield` is similar but different. `yield return null` always return next frame but `UniTask.Yield` return next called, that is, call `UniTask.Yield(PlayerLoopTiming.Update)` on `PreUpdate`, it returns same frame. `UniTask.NextFrame()` gurantees return next frame, this would be expected to behave exactly the same as `yield return null`.
|
||||
|
||||
> UniTask.Yield(without CancellationToken) is a special type, returns `YieldAwaitable` and run on YieldRunner. It is most lightweight and faster.
|
||||
|
||||
AsyncOperation is returned from native timing. For example, await `SceneManager.LoadSceneAsync` is returned from `EarlyUpdate.UpdatePreloading` and after called, loaded scene called from `EarlyUpdate.ScriptRunDelayedStartupFrame`. Also `await UnityWebRequest` is returned from `EarlyUpdate.ExecuteMainThreadJobs`.
|
||||
|
||||
In UniTask, await directly and `WithCancellation` use native timing, `ToUniTask` use specified timing. This is usually not a particular problem, but with `LoadSceneAsync`, causes different order of Start and continuation after await. so recommend not to use `LoadSceneAsync.ToUniTask`.
|
||||
|
||||
In stacktrace, you can check where is running in playerloop.
|
||||
|
||||

|
||||
|
||||
In default, UniTask's PlayerLoop is initialized at `[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]`.
|
||||
|
||||
The order in which methods are called in BeforeSceneLoad is indeterminate, so if you want to use UniTask in other BeforeSceneLoad methods, you should try to initialize it before this.
|
||||
|
||||
```csharp
|
||||
// AfterAssembliesLoaded is called before BeforeSceneLoad
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]
|
||||
public static void InitUniTaskLoop()
|
||||
{
|
||||
var loop = PlayerLoop.GetCurrentPlayerLoop();
|
||||
Cysharp.Threading.Tasks.PlayerLoopHelper.Initialize(ref loop);
|
||||
}
|
||||
```
|
||||
|
||||
If you import Unity's `Entities` package, that reset custom player loop to default at `BeforeSceneLoad` and inject ECS's loop. When Unity call ECS's inject method after UniTask's initialize method, UniTask will no longer work.
|
||||
|
||||
To solve this issue, you can re-initialize UniTask PlayerLoop after ECS initialized.
|
||||
|
||||
```csharp
|
||||
// Get ECS Loop.
|
||||
var playerLoop = ScriptBehaviourUpdateOrder.CurrentPlayerLoop;
|
||||
|
||||
// Setup UniTask's PlayerLoop.
|
||||
PlayerLoopHelper.Initialize(ref playerLoop);
|
||||
```
|
||||
|
||||
async void vs async UniTaskVoid
|
||||
---
|
||||
`async void` is a standard C# taks system so does not run on UniTask systems. It is better not to use. `async UniTaskVoid` is a lightweight version of `async UniTask` because it does not have awaitable completion and report error immediately to `UniTaskScheduler.UnobservedTaskException`. If you don't require to await it(fire and forget), use `UniTaskVoid` is better. Unfortunately to dismiss warning, require to using with `Forget()`.
|
||||
|
||||
```csharp
|
||||
public async UniTaskVoid FireAndForgetMethod()
|
||||
{
|
||||
// do anything...
|
||||
await UniTask.Yield();
|
||||
}
|
||||
|
||||
public void Caller()
|
||||
{
|
||||
FireAndForgetMethod().Forget();
|
||||
}
|
||||
```
|
||||
|
||||
Also UniTask have `Forget` method, it is similar with UniTaskVoid and same effects with it. However still UniTaskVoid is more efficient if completely do not use await。
|
||||
|
||||
```csharp
|
||||
public async UniTask DoAsync()
|
||||
{
|
||||
// do anything...
|
||||
await UniTask.Yield();
|
||||
}
|
||||
|
||||
public void Caller()
|
||||
{
|
||||
DoAsync().Forget();
|
||||
}
|
||||
```
|
||||
|
||||
Using async lambda in register event, it is used `async void`. To avoid it, you can use `UniTask.Action` or `UniTask.UnityAction` that creates delegate via `async UniTaskVoid` lambda.
|
||||
|
||||
```csharp
|
||||
Action actEvent;
|
||||
UnityAction unityEvent; // especially used in uGUI
|
||||
|
||||
// Bad: async void
|
||||
actEvent += async () => { };
|
||||
unityEvent += async () => { };
|
||||
|
||||
// Ok: create Action delegate by lambda
|
||||
actEvent += UniTask.Action(async () => { await UniTask.Yield(); });
|
||||
unityEvent += UniTask.UnityAction(async () => { await UniTask.Yield(); });
|
||||
```
|
||||
|
||||
UniTaskTracker
|
||||
---
|
||||
useful for check(leak) UniTasks. You can open tracker window in `Window -> UniTask Tracker`.
|
||||
|
||||

|
||||

|
||||
|
||||
* Enable AutoReload(Toggle) - Reload automatically.
|
||||
* Reload - Reload view.
|
||||
@@ -253,38 +441,89 @@ useful for check(leak) UniTasks. You can open tracker window in `Window -> UniTa
|
||||
|
||||
For debug use, enable tracking and capture stacktrace is useful but it it decline performance. Recommended usage is enable both to find task leak, and when done, finally disable both.
|
||||
|
||||
Reusable Promises
|
||||
External Assets
|
||||
---
|
||||
Some UniTask factory can reuse to reduce allocation. The list is `Yield`, `Delay`, `DelayFrame`, `WaitUntil`, `WaitWhile`, `WaitUntilValueChanged`.
|
||||
In default, UniTask supports DOTween and Addressables(`AsyncOperationHandle` and `AsyncOpereationHandle<T>` as awaitable).
|
||||
|
||||
For DOTween support, require to `com.demigiant.dotween` import from [OpenUPM](https://openupm.com/packages/com.demigiant.dotween/) or define `UNITASK_DOTWEEN_SUPPORT` to enable it.
|
||||
|
||||
```csharp
|
||||
var reusePromise = UniTask.DelayFrame(10);
|
||||
// sequential
|
||||
await transform.DOMoveX(2, 10);
|
||||
await transform.DOMoveZ(5, 20);
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
// parallel with cancellation
|
||||
var ct = this.GetCancellationTokenOnDestroy();
|
||||
|
||||
await UniTask.WhenAll(
|
||||
transform.DOMoveX(10, 3).WithCancellation(ct),
|
||||
transform.DOScale(10, 3).WithCancellation(ct));
|
||||
```
|
||||
|
||||
AsyncEnumerable and Async LINQ
|
||||
---
|
||||
Unity 2020.2.0a12 supports C# 8.0 so you can use `await foreach`. This is the new Update notation in async era.
|
||||
|
||||
```csharp
|
||||
// Unity 2020.2.0a12, C# 8.0
|
||||
await foreach (var _ in UniTaskAsyncEnumerable.EveryUpdate(token))
|
||||
{
|
||||
await reusePromise;
|
||||
Debug.Log("Update() " + Time.frameCount);
|
||||
}
|
||||
```
|
||||
|
||||
awaitable Events
|
||||
---
|
||||
Unity events can await like `OnClickAsync`, `OnCollisionEnterAsync`. It can use by `UniRx.Async.Triggers`.
|
||||
In a C# 7.3 environment, you can use the `ForEachAsync` method to work in almost the same way.
|
||||
|
||||
```csharp
|
||||
using UniRx.Async.Triggers;
|
||||
|
||||
async UniTask TripleClick(CancellationToken token)
|
||||
// C# 7.3(Unity 2018.3~)
|
||||
await UniTaskAsyncEnumerable.EveryUpdate(token).ForEachAsync(_ =>
|
||||
{
|
||||
await button.OnClickAsync(token);
|
||||
await button.OnClickAsync(token);
|
||||
await button.OnClickAsync(token);
|
||||
Debug.Log("Update() " + Time.frameCount);
|
||||
});
|
||||
```
|
||||
|
||||
UniTaskAsyncEnumerable implements asynchronous LINQ, similar to LINQ in `IEnumerable<T>` or Rx in `IObservable<T>`. All standard LINQ query operators can be applied to asynchronous streams. For example, the following code shows how to apply a Where filter to a button-click asynchronous stream that runs once every two clicks.
|
||||
|
||||
```csharp
|
||||
await okButton.OnClickAsAsyncEnumerable().Where((x, i) => i % 2 == 0).ForEachAsync(_ =>
|
||||
{
|
||||
});
|
||||
```
|
||||
|
||||
Async LINQ is enabled when `using Cysharp.Threading.Tasks.Linq;`, and `UniTaskAsyncEnumerable` is defined in `UniTask.Linq` asmdef.
|
||||
|
||||
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 `Enumerbale`. 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`, `SkipUntilCanceled`, `TakeUntilCanceled`, `TakeLast`.
|
||||
|
||||
The method with Func as an argument has three additional overloads, `***Await`, `***AwaitWithCancellation`.
|
||||
|
||||
```csharp
|
||||
Select(Func<T, TR> selector)
|
||||
SelectAwait(Func<T, UniTask<TR>> selector)
|
||||
SelectAwaitWithCancellation(Func<T, CancellationToken, UniTask<TR>> selector)
|
||||
```
|
||||
|
||||
If you want to use the `async` method inside the func, use the `***Await` or `***AwaitWithCancellation`.
|
||||
|
||||
Awaitable Events
|
||||
---
|
||||
All uGUI component implements `***AsAsyncEnumerable` to convert asynchronous streams of events.
|
||||
|
||||
```csharp
|
||||
async UniTask TripleClick()
|
||||
{
|
||||
// In default, used button.GetCancellationTokenOnDestroy to manage lieftime of async
|
||||
await button.OnClickAsync();
|
||||
await button.OnClickAsync();
|
||||
await button.OnClickAsync();
|
||||
Debug.Log("Three times clicked");
|
||||
}
|
||||
|
||||
// more efficient way
|
||||
async UniTask TripleClick(CancellationToken token)
|
||||
async UniTask TripleClick()
|
||||
{
|
||||
using (var handler = button.GetAsyncClickEventHandler(token))
|
||||
using (var handler = button.GetAsyncClickEventHandler())
|
||||
{
|
||||
await handler.OnClickAsync();
|
||||
await handler.OnClickAsync();
|
||||
@@ -292,11 +531,126 @@ async UniTask TripleClick(CancellationToken token)
|
||||
Debug.Log("Three times clicked");
|
||||
}
|
||||
}
|
||||
|
||||
// use async LINQ
|
||||
async UniTask TripleClick(CancellationToken token)
|
||||
{
|
||||
await button.OnClickAsAsyncEnumerable().Take(3).Last();
|
||||
Debug.Log("Three times clicked");
|
||||
}
|
||||
|
||||
// use async LINQ2
|
||||
async UniTask TripleClick(CancellationToken token)
|
||||
{
|
||||
await button.OnClickAsAsyncEnumerable().Take(3).ForEachAsync(_ =>
|
||||
{
|
||||
Debug.Log("Every clicked");
|
||||
});
|
||||
Debug.Log("Three times clicked, complete.");
|
||||
}
|
||||
```
|
||||
|
||||
async void vs async UniTask/UniTaskVoid
|
||||
All MonoBehaviour message events can convert async-streams by `AsyncTriggers` that can enable by `using Cysharp.Threading.Tasks.Triggers;`.
|
||||
|
||||
```csharp
|
||||
using Cysharp.Threading.Tasks.Triggers;
|
||||
|
||||
async UniTaskVoid MonitorCollision()
|
||||
{
|
||||
await gameObject.OnCollisionEnterAsync();
|
||||
Debug.Log("Collision Enter");
|
||||
/* do anything */
|
||||
|
||||
await gameObject.OnCollisionExitAsync();
|
||||
Debug.Log("Collision Exit");
|
||||
}
|
||||
```
|
||||
|
||||
Similar as uGUI event, AsyncTrigger can get by `GetAsync***Trigger` and trigger it self is UniTaskAsyncEnumerable.
|
||||
|
||||
```csharp
|
||||
// use await multiple times, get AsyncTriggerHandler is more efficient.
|
||||
using(var trigger = this.GetOnCollisionEnterAsyncHandler())
|
||||
{
|
||||
await OnCollisionEnterAsync();
|
||||
await OnCollisionEnterAsync();
|
||||
await OnCollisionEnterAsync();
|
||||
}
|
||||
|
||||
// every moves.
|
||||
await this.GetAsyncMoveTrigger().ForEachAsync(axisEventData =>
|
||||
{
|
||||
});
|
||||
```
|
||||
|
||||
`AsyncReactiveProperty`, `AsyncReadOnlyReactiveProperty` is UniTask version of UniTask's ReactiveProperty.
|
||||
|
||||
`BindTo` extension method of `IUniTaskAsyncEnumerable<T>` for binding asynchronous stream values to Unity components(Text/Selectable/TMP/Text).
|
||||
|
||||
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.
|
||||
|
||||
```csharp
|
||||
// can not get click event during 3 seconds complete.
|
||||
await button.OnClickAsAsyncEnumerable().ForEachAwaitAsync()
|
||||
{
|
||||
await UniTask.Delay(TimeSpan.FromSeconds(3));
|
||||
}
|
||||
```
|
||||
|
||||
It is useful(prevent double-click) but not useful in sometimes.
|
||||
|
||||
Using `Queue()` method, which will also queue events during asynchronous processing.
|
||||
|
||||
```csharp
|
||||
// queued message in asynchronous processing
|
||||
await button.OnClickAsAsyncEnumerable().Queue().ForEachAwaitAsync()
|
||||
{
|
||||
await UniTask.Delay(TimeSpan.FromSeconds(3));
|
||||
}
|
||||
```
|
||||
|
||||
Channel
|
||||
---
|
||||
`async void` is standard C# system so does not run on UniTask systems. It is better not to use. `async UniTaskVoid` is lightweight version of `async UniTask` because it does not have awaitable completion. If you don't require to await it(fire and forget), use `UniTaskVoid` is better. Unfortunately to dismiss warning, require to using with `Forget()`.
|
||||
`Channel` is same as [System.Threading.Tasks.Channels](https://docs.microsoft.com/ja-jp/dotnet/api/system.threading.channels?view=netcore-3.1) that is similar as GoLang Channel.
|
||||
|
||||
Currently only supports multiple-producer, single-consumer unbounded channel. It can create by `Channel.CreateSingleConsumerUnbounded<T>()`.
|
||||
|
||||
For producer(`.Writer`), `TryWrite` to push value and `TryComplete` to complete channel. For consumer(`.Reader`), `TryRead`, `WaitToReadAsync`, `ReadAsync`, `Completion` and `ReadAllAsync` to read queued messages.
|
||||
|
||||
`ReadAllAsync` returns `IUniTaskAsyncEnumerable<T>` so query LINQ operators. Reader only allows single-consumer but use `.Publish()` query operator to enable multicast message. For example, make pub/sub utility.
|
||||
|
||||
```csharp
|
||||
public class AsyncMessageBroker<T> : IDisposable
|
||||
{
|
||||
Channel<T> channel;
|
||||
|
||||
IConnectableUniTaskAsyncEnumerable<T> multicastSource;
|
||||
IDisposable connection;
|
||||
|
||||
public AsyncMessageBroker()
|
||||
{
|
||||
channel = Channel.CreateSingleConsumerUnbounded<T>();
|
||||
multicastSource = channel.Reader.ReadAllAsync().Publish();
|
||||
connection = multicastSource.Connect(); // Publish returns IConnectableUniTaskAsyncEnumerable.
|
||||
}
|
||||
|
||||
public void Publish(T value)
|
||||
{
|
||||
channel.Writer.TryWrite(value);
|
||||
}
|
||||
|
||||
public IUniTaskAsyncEnumerable<T> Subscribe()
|
||||
{
|
||||
return multicastSource;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
channel.Writer.TryComplete();
|
||||
connection.Dispose();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For Unit Testing
|
||||
---
|
||||
@@ -323,37 +677,92 @@ public IEnumerator DelayIgnore() => UniTask.ToCoroutine(async () =>
|
||||
});
|
||||
```
|
||||
|
||||
Method List
|
||||
UniTask itself's unit test is written by Unity Test Runner and [Cysharp/RuntimeUnitTestToolkit](https://github.com/Cysharp/RuntimeUnitTestToolkit) to check on CI and IL2CPP working.
|
||||
|
||||
Pooling Configuration
|
||||
---
|
||||
UniTask is aggressively caching async promise object to achive zero allocation. In default, cache all promises but you can configure `TaskPool.SetMaxPoolSize` to your value, the value indicates cache size per type. `TaskPool.GetCacheSizeInfo` returns current cached object in pool.
|
||||
|
||||
```csharp
|
||||
UniTask.WaitUntil
|
||||
UniTask.WaitWhile
|
||||
UniTask.WaitUntilValueChanged
|
||||
UniTask.SwitchToThreadPool
|
||||
UniTask.SwitchToTaskPool
|
||||
UniTask.SwitchToMainThread
|
||||
UniTask.SwitchToSynchronizationContext
|
||||
UniTask.Yield
|
||||
UniTask.Run
|
||||
UniTask.Lazy
|
||||
UniTask.Void
|
||||
UniTask.ConfigureAwait
|
||||
UniTask.DelayFrame
|
||||
UniTask.Delay(..., bool ignoreTimeScale = false, ...) parameter
|
||||
foreach (var (type, size) in TaskPool.GetCacheSizeInfo())
|
||||
{
|
||||
Debug.Log(type + ":" + size);
|
||||
}
|
||||
```
|
||||
|
||||
API References
|
||||
---
|
||||
UniTask's API References is hosted at [cysharp.github.io/UniTask](https://cysharp.github.io/UniTask/api/Cysharp.Threading.Tasks.html) by [DocFX](https://dotnet.github.io/docfx/) and [Cysharp/DocfXTemplate](https://github.com/Cysharp/DocfxTemplate).
|
||||
|
||||
For example, UniTask's factory methods can see at [UniTask#methods](https://cysharp.github.io/UniTask/api/Cysharp.Threading.Tasks.UniTask.html#methods-1). UniTaskAsyncEnumerable's factory/extension methods can see at [UniTaskAsyncEnumerable#methods](https://cysharp.github.io/UniTask/api/Cysharp.Threading.Tasks.Linq.UniTaskAsyncEnumerable.html#methods-1).
|
||||
|
||||
UPM Package
|
||||
---
|
||||
After Unity 2019.3.4f1, Unity 2020.1a21, that support path query parameter of git package. You can add `https://github.com/Cysharp/UniTask.git?path=Assets/UniRx.Async` to Package Manager
|
||||
### Install via git URL
|
||||
|
||||
After Unity 2019.3.4f1, Unity 2020.1a21, that support path query parameter of git package. You can add `https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask` to Package Manager
|
||||
|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
or add `"com.cysharp.unitask": "https://github.com/Cysharp/UniTask.git?path=Assets/UniRx.Async"` to `Packages/manifest.json`.
|
||||
or add `"com.cysharp.unitask": "https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask"` to `Packages/manifest.json`.
|
||||
|
||||
If you want to set a target version, UniTask is using `*.*.*` release tag so you can specify a version like `#1.3.0`. For example `https://github.com/Cysharp/UniTask.git?path=Assets/UniRx.Async#1.3.1`.
|
||||
If you want to set a target version, UniTask is using `*.*.*` release tag so you can specify a version like `#2.0.13`. For example `https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask#2.0.13`.
|
||||
|
||||
### 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
|
||||
---
|
||||
For .NET Core, use NuGet.
|
||||
|
||||
> PM> Install-Package [UniTask](https://www.nuget.org/packages/UniTask)
|
||||
|
||||
UniTask of .NET Core version is a subset of Unity UniTask, removed PlayerLoop dependent methods.
|
||||
|
||||
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 like the following(Inspired by [PooledAwait](https://github.com/mgravell/PooledAwait)).
|
||||
|
||||
```csharp
|
||||
public class ZeroAllocAsyncAwaitInDotNetCore
|
||||
{
|
||||
public ValueTask<int> DoAsync(int x, int y)
|
||||
{
|
||||
return Core(this, x, y);
|
||||
|
||||
static async UniTask<int> Core(ZeroAllocAsyncAwaitInDotNetCore self, int x, int y)
|
||||
{
|
||||
// do anything...
|
||||
await Task.Delay(TimeSpan.FromSeconds(x + y));
|
||||
await UniTask.Yield();
|
||||
|
||||
return 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// UniTask does not return to original SynchronizationContext but you can use helper `ReturnToCurrentSynchronizationContext`.
|
||||
public ValueTask TestAsync()
|
||||
{
|
||||
await using (UniTask.ReturnToCurrentSynchronizationContext())
|
||||
{
|
||||
await UniTask.SwitchToThreadPool();
|
||||
// do anything..
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
.NET Core version is intended to allow users to use UniTask as an interface when sharing code with Unity (such as [Cysharp/MagicOnion](https://github.com/Cysharp/MagicOnion/)). .NET Core version of UniTask enables smooth code sharing.
|
||||
|
||||
Utility methods such as WhenAll which is equivalent to UniTask are provided as [Cysharp/ValueTaskSupplement](https://github.com/Cysharp/ValueTaskSupplement).
|
||||
|
||||
License
|
||||
---
|
||||
This library is under the MIT License.
|
||||
This library is under the MIT License.
|
||||
|
||||
10
docs/.gitignore
vendored
Normal file
10
docs/.gitignore
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
###############
|
||||
# folder #
|
||||
###############
|
||||
/**/DROP/
|
||||
/**/TEMP/
|
||||
/**/packages/
|
||||
/**/bin/
|
||||
/**/obj/
|
||||
_site
|
||||
_DocfxTemplate
|
||||
5
docs/api/.gitignore
vendored
Normal file
5
docs/api/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
###############
|
||||
# temp file #
|
||||
###############
|
||||
*.yml
|
||||
.manifest
|
||||
70
docs/docfx.json
Normal file
70
docs/docfx.json
Normal file
@@ -0,0 +1,70 @@
|
||||
{
|
||||
"metadata": [
|
||||
{
|
||||
"src": [
|
||||
{
|
||||
"files": [
|
||||
"UniTask/Assets/Plugins/UniTask/Runtime/**/*.cs"
|
||||
],
|
||||
"src": "../src"
|
||||
}
|
||||
],
|
||||
"dest": "api",
|
||||
"disableGitFeatures": false,
|
||||
"disableDefaultFilter": false
|
||||
}
|
||||
],
|
||||
"build": {
|
||||
"globalMetadata": {
|
||||
"_disableContribution": true,
|
||||
"_appTitle": "UniTask"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"files": [
|
||||
"api/**.yml",
|
||||
"api/index.md"
|
||||
]
|
||||
},
|
||||
{
|
||||
"files": [
|
||||
"articles/**.md",
|
||||
"articles/**/toc.yml",
|
||||
"toc.yml",
|
||||
"*.md"
|
||||
]
|
||||
}
|
||||
],
|
||||
"resource": [
|
||||
{
|
||||
"files": [
|
||||
"images/**"
|
||||
]
|
||||
}
|
||||
],
|
||||
"overwrite": [
|
||||
{
|
||||
"files": [
|
||||
"apidoc/**.md"
|
||||
],
|
||||
"exclude": [
|
||||
"obj/**",
|
||||
"_site/**"
|
||||
]
|
||||
}
|
||||
],
|
||||
"dest": "_site",
|
||||
|
||||
"globalMetadataFiles": [],
|
||||
"fileMetadataFiles": [],
|
||||
"template": [
|
||||
"_DocfxTemplate/templates/default-v2.5.2",
|
||||
"_DocfxTemplate/templates/cysharp"
|
||||
],
|
||||
"postProcessors": [],
|
||||
"markdownEngineName": "markdig",
|
||||
"noLangKeyword": false,
|
||||
"keepFileLink": false,
|
||||
"cleanupCacheHistory": false
|
||||
}
|
||||
}
|
||||
8
docs/index.md
Normal file
8
docs/index.md
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
title: Home
|
||||
---
|
||||
# UniTask
|
||||
|
||||
Provides an efficient async/await integration to Unity.
|
||||
|
||||
https://github.com/Cysharp/UniTask
|
||||
11
docs/toc.yml
Normal file
11
docs/toc.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
- name: API Documentation
|
||||
href: api/
|
||||
homepage: api/Cysharp.Threading.Tasks.html
|
||||
|
||||
- name: Repository
|
||||
href: https://github.com/Cysharp/UniTask
|
||||
homepage: https://github.com/Cysharp/UniTask
|
||||
|
||||
- name: Releases
|
||||
href: https://github.com/Cysharp/UniTask/releases
|
||||
homepage: https://github.com/Cysharp/UniTask/releases
|
||||
BIN
src/UniTask.NetCore/Icon.png
Normal file
BIN
src/UniTask.NetCore/Icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.1 KiB |
98
src/UniTask.NetCore/NetCore/UniTask.AsValueTask.cs
Normal file
98
src/UniTask.NetCore/NetCore/UniTask.AsValueTask.cs
Normal file
@@ -0,0 +1,98 @@
|
||||
#pragma warning disable 0649
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks.Sources;
|
||||
|
||||
namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
public static class UniTaskValueTaskExtensions
|
||||
{
|
||||
public static ValueTask AsValueTask(this in UniTask task)
|
||||
{
|
||||
#if NETSTANDARD2_0
|
||||
return new ValueTask(new UniTaskValueTaskSource(task), 0);
|
||||
#else
|
||||
return task;
|
||||
#endif
|
||||
}
|
||||
|
||||
public static ValueTask<T> AsValueTask<T>(this in UniTask<T> task)
|
||||
{
|
||||
#if NETSTANDARD2_0
|
||||
return new ValueTask<T>(new UniTaskValueTaskSource<T>(task), 0);
|
||||
#else
|
||||
return task;
|
||||
#endif
|
||||
}
|
||||
|
||||
public static UniTask<T> AsUniTask<T>(this ValueTask<T> task, bool useCurrentSynchronizationContext = true)
|
||||
{
|
||||
// NOTE: get _obj and _token directly for low overhead conversion but not yet implemented.
|
||||
return task.AsTask().AsUniTask(useCurrentSynchronizationContext);
|
||||
}
|
||||
|
||||
public static UniTask AsUniTask(this ValueTask task, bool useCurrentSynchronizationContext = true)
|
||||
{
|
||||
return task.AsTask().AsUniTask(useCurrentSynchronizationContext);
|
||||
}
|
||||
|
||||
#if NETSTANDARD2_0
|
||||
|
||||
class UniTaskValueTaskSource : IValueTaskSource
|
||||
{
|
||||
readonly UniTask task;
|
||||
readonly UniTask.Awaiter awaiter;
|
||||
|
||||
public UniTaskValueTaskSource(UniTask task)
|
||||
{
|
||||
this.task = task;
|
||||
this.awaiter = task.GetAwaiter();
|
||||
}
|
||||
|
||||
public void GetResult(short token)
|
||||
{
|
||||
awaiter.GetResult();
|
||||
}
|
||||
|
||||
public ValueTaskSourceStatus GetStatus(short token)
|
||||
{
|
||||
return (ValueTaskSourceStatus)task.Status;
|
||||
}
|
||||
|
||||
public void OnCompleted(Action<object> continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags)
|
||||
{
|
||||
awaiter.SourceOnCompleted(continuation, state);
|
||||
}
|
||||
}
|
||||
|
||||
class UniTaskValueTaskSource<T> : IValueTaskSource<T>
|
||||
{
|
||||
readonly UniTask<T> task;
|
||||
readonly UniTask<T>.Awaiter awaiter;
|
||||
|
||||
public UniTaskValueTaskSource(UniTask<T> task)
|
||||
{
|
||||
this.task = task;
|
||||
this.awaiter = task.GetAwaiter();
|
||||
}
|
||||
|
||||
public T GetResult(short token)
|
||||
{
|
||||
return awaiter.GetResult();
|
||||
}
|
||||
|
||||
public ValueTaskSourceStatus GetStatus(short token)
|
||||
{
|
||||
return (ValueTaskSourceStatus)task.Status;
|
||||
}
|
||||
|
||||
public void OnCompleted(Action<object> continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags)
|
||||
{
|
||||
awaiter.SourceOnCompleted(continuation, state);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
39
src/UniTask.NetCore/NetCore/UniTask.Delay.cs
Normal file
39
src/UniTask.NetCore/NetCore/UniTask.Delay.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
//using Cysharp.Threading.Tasks.Internal;
|
||||
//using System;
|
||||
//using System.Collections.Concurrent;
|
||||
//using System.Runtime.CompilerServices;
|
||||
//using System.Threading;
|
||||
|
||||
//namespace Cysharp.Threading.Tasks
|
||||
//{
|
||||
// public partial struct UniTask
|
||||
// {
|
||||
// public static UniTask Delay()
|
||||
// {
|
||||
// return default;
|
||||
// }
|
||||
|
||||
// sealed class DelayPromise : IUniTaskSource
|
||||
// {
|
||||
// public void GetResult(short token)
|
||||
// {
|
||||
// throw new NotImplementedException();
|
||||
// }
|
||||
|
||||
// public UniTaskStatus GetStatus(short token)
|
||||
// {
|
||||
// throw new NotImplementedException();
|
||||
// }
|
||||
|
||||
// public void OnCompleted(Action<object> continuation, object state, short token)
|
||||
// {
|
||||
// throw new NotImplementedException();
|
||||
// }
|
||||
|
||||
// public UniTaskStatus UnsafeGetStatus()
|
||||
// {
|
||||
// throw new NotImplementedException();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
112
src/UniTask.NetCore/NetCore/UniTask.Run.cs
Normal file
112
src/UniTask.NetCore/NetCore/UniTask.Run.cs
Normal file
@@ -0,0 +1,112 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
public partial struct UniTask
|
||||
{
|
||||
/// <summary>Run action on the threadPool and return to current SynchronizationContext if configureAwait = true.</summary>
|
||||
public static async UniTask Run(Action action, bool configureAwait = true)
|
||||
{
|
||||
if (configureAwait)
|
||||
{
|
||||
var current = SynchronizationContext.Current;
|
||||
await UniTask.SwitchToThreadPool();
|
||||
try
|
||||
{
|
||||
action();
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (current != null)
|
||||
{
|
||||
await UniTask.SwitchToSynchronizationContext(current);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await UniTask.SwitchToThreadPool();
|
||||
action();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Run action on the threadPool and return to current SynchronizationContext if configureAwait = true.</summary>
|
||||
public static async UniTask Run(Action<object> action, object state, bool configureAwait = true)
|
||||
{
|
||||
if (configureAwait)
|
||||
{
|
||||
var current = SynchronizationContext.Current;
|
||||
await UniTask.SwitchToThreadPool();
|
||||
try
|
||||
{
|
||||
action(state);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (current != null)
|
||||
{
|
||||
await UniTask.SwitchToSynchronizationContext(current);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await UniTask.SwitchToThreadPool();
|
||||
action(state);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Run action on the threadPool and return to current SynchronizationContext if configureAwait = true.</summary>
|
||||
public static async UniTask<T> Run<T>(Func<T> func, bool configureAwait = true)
|
||||
{
|
||||
if (configureAwait)
|
||||
{
|
||||
var current = SynchronizationContext.Current;
|
||||
await UniTask.SwitchToThreadPool();
|
||||
try
|
||||
{
|
||||
return func();
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (current != null)
|
||||
{
|
||||
await UniTask.SwitchToSynchronizationContext(current);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await UniTask.SwitchToThreadPool();
|
||||
return func();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Run action on the threadPool and return to current SynchronizationContext if configureAwait = true.</summary>
|
||||
public static async UniTask<T> Run<T>(Func<object, T> func, object state, bool configureAwait = true)
|
||||
{
|
||||
if (configureAwait)
|
||||
{
|
||||
var current = SynchronizationContext.Current;
|
||||
await UniTask.SwitchToThreadPool();
|
||||
try
|
||||
{
|
||||
return func(state);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (current != null)
|
||||
{
|
||||
await UniTask.SwitchToSynchronizationContext(current);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await UniTask.SwitchToThreadPool();
|
||||
return func(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
102
src/UniTask.NetCore/NetCore/UniTask.Yield.cs
Normal file
102
src/UniTask.NetCore/NetCore/UniTask.Yield.cs
Normal file
@@ -0,0 +1,102 @@
|
||||
using Cysharp.Threading.Tasks.Internal;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
public partial struct UniTask
|
||||
{
|
||||
public static UniTask.YieldAwaitable Yield()
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
public readonly struct YieldAwaitable
|
||||
{
|
||||
public Awaiter GetAwaiter()
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
public readonly struct Awaiter : ICriticalNotifyCompletion
|
||||
{
|
||||
static readonly SendOrPostCallback SendOrPostCallbackDelegate = Continuation;
|
||||
static readonly WaitCallback WaitCallbackDelegate = Continuation;
|
||||
|
||||
public bool IsCompleted => false;
|
||||
|
||||
public void GetResult() { }
|
||||
|
||||
public void OnCompleted(Action continuation)
|
||||
{
|
||||
UnsafeOnCompleted(continuation);
|
||||
}
|
||||
|
||||
public void UnsafeOnCompleted(Action continuation)
|
||||
{
|
||||
var syncContext = SynchronizationContext.Current;
|
||||
if (syncContext != null)
|
||||
{
|
||||
syncContext.Post(SendOrPostCallbackDelegate, continuation);
|
||||
}
|
||||
else
|
||||
{
|
||||
#if NETCOREAPP3_1
|
||||
ThreadPool.UnsafeQueueUserWorkItem(ThreadPoolWorkItem.Create(continuation), false);
|
||||
#else
|
||||
ThreadPool.UnsafeQueueUserWorkItem(WaitCallbackDelegate, continuation);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static void Continuation(object state)
|
||||
{
|
||||
((Action)state).Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
#if NETCOREAPP3_1
|
||||
|
||||
sealed class ThreadPoolWorkItem : IThreadPoolWorkItem, ITaskPoolNode<ThreadPoolWorkItem>
|
||||
{
|
||||
static TaskPool<ThreadPoolWorkItem> pool;
|
||||
public ThreadPoolWorkItem NextNode { get; set; }
|
||||
|
||||
static ThreadPoolWorkItem()
|
||||
{
|
||||
TaskPool.RegisterSizeGetter(typeof(ThreadPoolWorkItem), () => pool.Size);
|
||||
}
|
||||
|
||||
Action continuation;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ThreadPoolWorkItem Create(Action continuation)
|
||||
{
|
||||
if (!pool.TryPop(out var item))
|
||||
{
|
||||
item = new ThreadPoolWorkItem();
|
||||
}
|
||||
|
||||
item.continuation = continuation;
|
||||
return item;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Execute()
|
||||
{
|
||||
var call = continuation;
|
||||
continuation = null;
|
||||
if (call != null)
|
||||
{
|
||||
pool.TryPush(this);
|
||||
call.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +1,58 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<TargetFrameworks>netcoreapp3.1;netstandard2.1;netstandard2.0</TargetFrameworks>
|
||||
<AssemblyName>UniTask</AssemblyName>
|
||||
<LangVersion>8.0</LangVersion>
|
||||
<RootNamespace>Cysharp.Threading.Tasks</RootNamespace>
|
||||
|
||||
<!-- NuGet Packaging -->
|
||||
<Id>UniTask</Id>
|
||||
<PackageVersion>$(Version)</PackageVersion>
|
||||
<Company>Cysharp</Company>
|
||||
<Authors>Cysharp</Authors>
|
||||
<Copyright>© Cysharp, Inc.</Copyright>
|
||||
<PackageTags>task;async</PackageTags>
|
||||
<Description>Provides an efficient async/await integration to Unity and .NET Core.</Description>
|
||||
<PackageProjectUrl>https://github.com/Cysharp/UniTask</PackageProjectUrl>
|
||||
<RepositoryUrl>$(PackageProjectUrl)</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<PackageIcon>Icon.png</PackageIcon>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\UniTask\Assets\Plugins\UniTask\**\*.cs"
|
||||
Exclude="..\UniTask\Assets\Plugins\UniTask\Triggers\*.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Editor\*.cs;
|
||||
|
||||
..\UniTask\Assets\Plugins\UniTask\Internal\UnityEqualityComparer.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Internal\DiagnosticsExtensions.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Internal\PlayerLoopRunner.cs;
|
||||
|
||||
..\UniTask\Assets\Plugins\UniTask\CancellationTokenSourceExtensions.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\EnumeratorAsyncExtensions.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\PlayerLoopHelper.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\UniTask.Delay.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\UniTask.Run.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\UniTask.Bridge.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\UniTask.WaitUntil.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\UnityAsyncExtensions.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\UnityAsyncExtensions.uGUI.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\UnityAsyncExtensions.MonoBehaviour.cs;
|
||||
"/>
|
||||
<None Include="Icon.png" Pack="true" PackagePath="/" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\UniTask\Assets\Plugins\UniTask\Runtime\**\*.cs"
|
||||
Exclude="
|
||||
..\UniTask\Assets\Plugins\UniTask\Editor\*.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\Triggers\*.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\Linq\UnityExtensions\*.cs;
|
||||
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\UnityEqualityComparer.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\DiagnosticsExtensions.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\PlayerLoopRunner.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\ContinuationQueue.cs;
|
||||
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\CancellationTokenSourceExtensions.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\EnumeratorAsyncExtensions.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\PlayerLoopHelper.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.Delay.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.Run.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.Bridge.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.WaitUntil.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\UnityAsyncExtensions.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\UnityAsyncExtensions.uGUI.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\UnityAsyncExtensions.MonoBehaviour.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\UnityBindingExtensions.cs;
|
||||
" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
258
src/UniTask.NetCoreSandbox/AllocationCheck.cs
Normal file
258
src/UniTask.NetCoreSandbox/AllocationCheck.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
283
src/UniTask.NetCoreSandbox/Benchmark.cs
Normal file
283
src/UniTask.NetCoreSandbox/Benchmark.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -1,38 +1,523 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
|
||||
using System.Linq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Cysharp.Threading.Tasks.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Concurrency;
|
||||
|
||||
namespace NetCoreSandbox
|
||||
{
|
||||
class Program
|
||||
public class MySyncContext : SynchronizationContext
|
||||
{
|
||||
static async Task Main(string[] args)
|
||||
public MySyncContext()
|
||||
{
|
||||
Console.WriteLine("Foo");
|
||||
var v = await outer().AsTask();
|
||||
|
||||
Console.WriteLine("Bar:" + v);
|
||||
}
|
||||
|
||||
static async UniTask<int> outer()
|
||||
public override void Post(SendOrPostCallback d, object state)
|
||||
{
|
||||
var v = await DoAsync();
|
||||
return v;
|
||||
Console.WriteLine("Called SyncContext Post!");
|
||||
base.Post(d, state);
|
||||
}
|
||||
}
|
||||
|
||||
public class Text
|
||||
{
|
||||
|
||||
static async UniTask<int> DoAsync()
|
||||
public string text { get; set; }
|
||||
}
|
||||
|
||||
public class ZeroAllocAsyncAwaitInDotNetCore
|
||||
{
|
||||
public ValueTask<int> NanikaAsync(int x, int y)
|
||||
{
|
||||
var tcs = new UniTaskCompletionSource<int>();
|
||||
return Core(this, x, y);
|
||||
|
||||
tcs.TrySetResult(100);
|
||||
static async UniTask<int> Core(ZeroAllocAsyncAwaitInDotNetCore self, int x, int y)
|
||||
{
|
||||
// nanika suru...
|
||||
await Task.Delay(TimeSpan.FromSeconds(x + y));
|
||||
|
||||
|
||||
var v = await tcs.Task;
|
||||
|
||||
return v;
|
||||
return 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class TaskTestException : Exception
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
public struct TestAwaiter : ICriticalNotifyCompletion
|
||||
{
|
||||
readonly UniTaskStatus status;
|
||||
readonly bool isCompleted;
|
||||
|
||||
public TestAwaiter(bool isCompleted, UniTaskStatus status)
|
||||
{
|
||||
this.isCompleted = isCompleted;
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public TestAwaiter GetAwaiter() => this;
|
||||
|
||||
public bool IsCompleted => isCompleted;
|
||||
|
||||
public void GetResult()
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case UniTaskStatus.Faulted:
|
||||
throw new TaskTestException();
|
||||
case UniTaskStatus.Canceled:
|
||||
throw new OperationCanceledException();
|
||||
case UniTaskStatus.Pending:
|
||||
case UniTaskStatus.Succeeded:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnCompleted(Action continuation)
|
||||
{
|
||||
ThreadPool.QueueUserWorkItem(_ => continuation(), null);
|
||||
}
|
||||
|
||||
public void UnsafeOnCompleted(Action continuation)
|
||||
{
|
||||
ThreadPool.UnsafeQueueUserWorkItem(_ => continuation(), null);
|
||||
}
|
||||
}
|
||||
public struct TestAwaiter<T> : ICriticalNotifyCompletion
|
||||
{
|
||||
readonly UniTaskStatus status;
|
||||
readonly bool isCompleted;
|
||||
readonly T value;
|
||||
|
||||
public TestAwaiter(bool isCompleted, UniTaskStatus status, T value)
|
||||
{
|
||||
this.isCompleted = isCompleted;
|
||||
this.status = status;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public TestAwaiter<T> GetAwaiter() => this;
|
||||
|
||||
public bool IsCompleted => isCompleted;
|
||||
|
||||
public T GetResult()
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case UniTaskStatus.Faulted:
|
||||
throw new TaskTestException();
|
||||
case UniTaskStatus.Canceled:
|
||||
throw new OperationCanceledException();
|
||||
case UniTaskStatus.Pending:
|
||||
case UniTaskStatus.Succeeded:
|
||||
default:
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnCompleted(Action continuation)
|
||||
{
|
||||
ThreadPool.QueueUserWorkItem(_ => continuation(), null);
|
||||
}
|
||||
|
||||
public void UnsafeOnCompleted(Action continuation)
|
||||
{
|
||||
ThreadPool.UnsafeQueueUserWorkItem(_ => continuation(), null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static partial class UnityUIComponentExtensions
|
||||
{
|
||||
public static void BindTo(this IUniTaskAsyncEnumerable<string> source, Text text)
|
||||
{
|
||||
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
|
||||
// await new AllocationCheck().ViaUniTaskVoid();
|
||||
|
||||
// AsyncTest().Forge
|
||||
|
||||
Console.WriteLine("A?");
|
||||
var a = await new ZeroAllocAsyncAwaitInDotNetCore().NanikaAsync(1, 2);
|
||||
Console.WriteLine("RET:" + a);
|
||||
await WhereSelect();
|
||||
|
||||
SynchronizationContext.SetSynchronizationContext(new MySyncContext());
|
||||
|
||||
await Aaa();
|
||||
|
||||
|
||||
|
||||
|
||||
//AsyncTest().Forget();
|
||||
|
||||
// AsyncTest().Forget();
|
||||
|
||||
ThreadPool.SetMinThreads(100, 100);
|
||||
|
||||
//List<UniTask<int>> list = new List<UniTask<int>>();
|
||||
for (int i = 0; i < short.MaxValue; i++)
|
||||
{
|
||||
//// list.Add(AsyncTest());
|
||||
await YieldCore();
|
||||
}
|
||||
//await UniTask.WhenAll(list);
|
||||
|
||||
//Console.WriteLine("TOGO");
|
||||
|
||||
//var a = await AsyncTest();
|
||||
//var b = AsyncTest();
|
||||
//var c = AsyncTest();
|
||||
await YieldCore();
|
||||
|
||||
//await b;
|
||||
//await c;
|
||||
|
||||
|
||||
//foreach (var item in Cysharp.Threading.Tasks.Internal.TaskPool.GetCacheSizeInfo())
|
||||
//{
|
||||
// Console.WriteLine(item);
|
||||
//}
|
||||
|
||||
Console.ReadLine();
|
||||
}
|
||||
|
||||
static async UniTask YieldCore()
|
||||
{
|
||||
await UniTask.Yield();
|
||||
}
|
||||
|
||||
#pragma warning disable CS1998
|
||||
|
||||
|
||||
static async UniTask<int> AsyncTest()
|
||||
{
|
||||
// empty
|
||||
await new TestAwaiter(false, UniTaskStatus.Succeeded);
|
||||
await new TestAwaiter(true, UniTaskStatus.Succeeded);
|
||||
await new TestAwaiter(false, UniTaskStatus.Succeeded);
|
||||
return 10;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#pragma warning restore CS1998
|
||||
|
||||
void Foo()
|
||||
{
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
396
src/UniTask.NetCoreSandbox/QueueCheck.cs
Normal file
396
src/UniTask.NetCoreSandbox/QueueCheck.cs
Normal file
@@ -0,0 +1,396 @@
|
||||
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();
|
||||
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 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 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; }
|
||||
}
|
||||
|
||||
// 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 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;
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,13 @@
|
||||
<RootNamespace>NetCoreSandbox</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
|
||||
<PackageReference Include="PooledAwait" Version="1.0.49" />
|
||||
<PackageReference Include="System.Interactive.Async" Version="4.1.1" />
|
||||
<PackageReference Include="System.Reactive" Version="4.4.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\UniTask.NetCore\UniTask.NetCore.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
118
src/UniTask.NetCoreTests/AsyncReactivePropertyTest.cs
Normal file
118
src/UniTask.NetCoreTests/AsyncReactivePropertyTest.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Channels;
|
||||
using Cysharp.Threading.Tasks.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace NetCoreTests
|
||||
{
|
||||
public class AsyncReactivePropertyTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task Iteration()
|
||||
{
|
||||
var rp = new AsyncReactiveProperty<int>(99);
|
||||
|
||||
var f = await rp.FirstAsync();
|
||||
f.Should().Be(99);
|
||||
|
||||
var array = rp.Take(5).ToArrayAsync();
|
||||
|
||||
rp.Value = 100;
|
||||
rp.Value = 100;
|
||||
rp.Value = 100;
|
||||
rp.Value = 131;
|
||||
|
||||
var ar = await array;
|
||||
|
||||
ar.Should().BeEquivalentTo(new[] { 99, 100, 100, 100, 131 });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WithoutCurrent()
|
||||
{
|
||||
var rp = new AsyncReactiveProperty<int>(99);
|
||||
|
||||
var array = rp.WithoutCurrent().Take(5).ToArrayAsync();
|
||||
|
||||
rp.Value = 100;
|
||||
rp.Value = 100;
|
||||
rp.Value = 100;
|
||||
rp.Value = 131;
|
||||
rp.Value = 191;
|
||||
|
||||
var ar = await array;
|
||||
|
||||
ar.Should().BeEquivalentTo(new[] { 100, 100, 100, 131, 191 });
|
||||
}
|
||||
|
||||
//[Fact]
|
||||
//public async Task StateIteration()
|
||||
//{
|
||||
// var rp = new ReadOnlyAsyncReactiveProperty<int>(99);
|
||||
// var setter = rp.GetSetter();
|
||||
|
||||
// var f = await rp.FirstAsync();
|
||||
// f.Should().Be(99);
|
||||
|
||||
// var array = rp.Take(5).ToArrayAsync();
|
||||
|
||||
// setter(100);
|
||||
// setter(100);
|
||||
// setter(100);
|
||||
// setter(131);
|
||||
|
||||
// var ar = await array;
|
||||
|
||||
// ar.Should().BeEquivalentTo(new[] { 99, 100, 100, 100, 131 });
|
||||
//}
|
||||
|
||||
//[Fact]
|
||||
//public async Task StateWithoutCurrent()
|
||||
//{
|
||||
// var rp = new ReadOnlyAsyncReactiveProperty<int>(99);
|
||||
// var setter = rp.GetSetter();
|
||||
|
||||
// var array = rp.WithoutCurrent().Take(5).ToArrayAsync();
|
||||
// setter(100);
|
||||
// setter(100);
|
||||
// setter(100);
|
||||
// setter(131);
|
||||
// setter(191);
|
||||
|
||||
// var ar = await array;
|
||||
|
||||
// ar.Should().BeEquivalentTo(new[] { 100, 100, 100, 131, 191 });
|
||||
//}
|
||||
|
||||
|
||||
|
||||
[Fact]
|
||||
public void StateFromEnumeration()
|
||||
{
|
||||
var rp = new AsyncReactiveProperty<int>(10);
|
||||
|
||||
var state = rp.ToReadOnlyAsyncReactiveProperty(CancellationToken.None);
|
||||
|
||||
rp.Value = 10;
|
||||
state.Value.Should().Be(10);
|
||||
|
||||
rp.Value = 20;
|
||||
state.Value.Should().Be(20);
|
||||
|
||||
state.Dispose();
|
||||
|
||||
rp.Value = 30;
|
||||
state.Value.Should().Be(20);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
51
src/UniTask.NetCoreTests/CancellationTokenTest.cs
Normal file
51
src/UniTask.NetCoreTests/CancellationTokenTest.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Channels;
|
||||
using Cysharp.Threading.Tasks.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace NetCoreTests
|
||||
{
|
||||
public class CancellationTokenTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task WaitUntilCanceled()
|
||||
{
|
||||
var cts = new CancellationTokenSource();
|
||||
|
||||
cts.CancelAfter(TimeSpan.FromSeconds(1.5));
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
await cts.Token.WaitUntilCanceled();
|
||||
|
||||
var elapsed = DateTime.UtcNow - now;
|
||||
|
||||
elapsed.Should().BeGreaterThan(TimeSpan.FromSeconds(1));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AlreadyCanceled()
|
||||
{
|
||||
var cts = new CancellationTokenSource();
|
||||
|
||||
cts.Cancel();
|
||||
|
||||
cts.Token.WaitUntilCanceled().GetAwaiter().IsCompleted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void None()
|
||||
{
|
||||
CancellationToken.None.WaitUntilCanceled().GetAwaiter().IsCompleted.Should().BeTrue();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
370
src/UniTask.NetCoreTests/ChannelTest.cs
Normal file
370
src/UniTask.NetCoreTests/ChannelTest.cs
Normal file
@@ -0,0 +1,370 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Channels;
|
||||
using Cysharp.Threading.Tasks.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace NetCoreTests
|
||||
{
|
||||
public class ChannelTest
|
||||
{
|
||||
(System.Threading.Channels.Channel<int>, Cysharp.Threading.Tasks.Channel<int>) CreateChannel()
|
||||
{
|
||||
var reference = System.Threading.Channels.Channel.CreateUnbounded<int>(new UnboundedChannelOptions
|
||||
{
|
||||
AllowSynchronousContinuations = true,
|
||||
SingleReader = true,
|
||||
SingleWriter = false
|
||||
});
|
||||
|
||||
var channel = Cysharp.Threading.Tasks.Channel.CreateSingleConsumerUnbounded<int>();
|
||||
|
||||
return (reference, channel);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SingleWriteSingleRead()
|
||||
{
|
||||
var (reference, channel) = CreateChannel();
|
||||
|
||||
foreach (var item in new[] { 10, 20, 30 })
|
||||
{
|
||||
var t1 = reference.Reader.WaitToReadAsync();
|
||||
var t2 = channel.Reader.WaitToReadAsync();
|
||||
|
||||
t1.IsCompleted.Should().BeFalse();
|
||||
t2.Status.IsCompleted().Should().BeFalse();
|
||||
|
||||
reference.Writer.TryWrite(item);
|
||||
channel.Writer.TryWrite(item);
|
||||
|
||||
(await t1).Should().BeTrue();
|
||||
(await t2).Should().BeTrue();
|
||||
|
||||
reference.Reader.TryRead(out var refitem).Should().BeTrue();
|
||||
channel.Reader.TryRead(out var chanitem).Should().BeTrue();
|
||||
refitem.Should().Be(item);
|
||||
chanitem.Should().Be(item);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task MultiWrite()
|
||||
{
|
||||
var (reference, channel) = CreateChannel();
|
||||
|
||||
foreach (var item in new[] { 10, 20, 30 })
|
||||
{
|
||||
var t1 = reference.Reader.WaitToReadAsync();
|
||||
var t2 = channel.Reader.WaitToReadAsync();
|
||||
|
||||
t1.IsCompleted.Should().BeFalse();
|
||||
t2.Status.IsCompleted().Should().BeFalse();
|
||||
|
||||
foreach (var i in Enumerable.Range(1, 3))
|
||||
{
|
||||
reference.Writer.TryWrite(item * i);
|
||||
channel.Writer.TryWrite(item * i);
|
||||
}
|
||||
|
||||
(await t1).Should().BeTrue();
|
||||
(await t2).Should().BeTrue();
|
||||
|
||||
foreach (var i in Enumerable.Range(1, 3))
|
||||
{
|
||||
(await reference.Reader.WaitToReadAsync()).Should().BeTrue();
|
||||
(await channel.Reader.WaitToReadAsync()).Should().BeTrue();
|
||||
|
||||
reference.Reader.TryRead(out var refitem).Should().BeTrue();
|
||||
channel.Reader.TryRead(out var chanitem).Should().BeTrue();
|
||||
refitem.Should().Be(item * i);
|
||||
chanitem.Should().Be(item * i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CompleteOnEmpty()
|
||||
{
|
||||
var (reference, channel) = CreateChannel();
|
||||
|
||||
foreach (var item in new[] { 10, 20, 30 })
|
||||
{
|
||||
reference.Writer.TryWrite(item);
|
||||
channel.Writer.TryWrite(item);
|
||||
reference.Reader.TryRead(out var refitem);
|
||||
channel.Reader.TryRead(out var chanitem);
|
||||
}
|
||||
|
||||
// Empty.
|
||||
|
||||
var completion1 = reference.Reader.Completion;
|
||||
var wait1 = reference.Reader.WaitToReadAsync();
|
||||
|
||||
var completion2 = channel.Reader.Completion;
|
||||
var wait2 = channel.Reader.WaitToReadAsync();
|
||||
|
||||
reference.Writer.TryComplete();
|
||||
channel.Writer.TryComplete();
|
||||
|
||||
completion1.Status.Should().Be(TaskStatus.RanToCompletion);
|
||||
completion2.Status.Should().Be(UniTaskStatus.Succeeded);
|
||||
|
||||
(await wait1).Should().BeFalse();
|
||||
(await wait2).Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CompleteErrorOnEmpty()
|
||||
{
|
||||
var (reference, channel) = CreateChannel();
|
||||
|
||||
foreach (var item in new[] { 10, 20, 30 })
|
||||
{
|
||||
reference.Writer.TryWrite(item);
|
||||
channel.Writer.TryWrite(item);
|
||||
reference.Reader.TryRead(out var refitem);
|
||||
channel.Reader.TryRead(out var chanitem);
|
||||
}
|
||||
|
||||
// Empty.
|
||||
|
||||
var completion1 = reference.Reader.Completion;
|
||||
var wait1 = reference.Reader.WaitToReadAsync();
|
||||
|
||||
var completion2 = channel.Reader.Completion;
|
||||
var wait2 = channel.Reader.WaitToReadAsync();
|
||||
|
||||
var ex = new Exception();
|
||||
reference.Writer.TryComplete(ex);
|
||||
channel.Writer.TryComplete(ex);
|
||||
|
||||
completion1.Status.Should().Be(TaskStatus.Faulted);
|
||||
completion2.Status.Should().Be(UniTaskStatus.Faulted);
|
||||
|
||||
(await Assert.ThrowsAsync<Exception>(async () => await wait1)).Should().Be(ex);
|
||||
(await Assert.ThrowsAsync<Exception>(async () => await wait2)).Should().Be(ex);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CompleteWithRest()
|
||||
{
|
||||
var (reference, channel) = CreateChannel();
|
||||
|
||||
foreach (var item in new[] { 10, 20, 30 })
|
||||
{
|
||||
reference.Writer.TryWrite(item);
|
||||
channel.Writer.TryWrite(item);
|
||||
}
|
||||
|
||||
// Three Item2.
|
||||
|
||||
var completion1 = reference.Reader.Completion;
|
||||
var wait1 = reference.Reader.WaitToReadAsync();
|
||||
|
||||
var completion2 = channel.Reader.Completion;
|
||||
var wait2 = channel.Reader.WaitToReadAsync();
|
||||
|
||||
reference.Writer.TryComplete();
|
||||
channel.Writer.TryComplete();
|
||||
|
||||
// completion1.Status.Should().Be(TaskStatus.WaitingForActivation);
|
||||
completion2.Status.Should().Be(UniTaskStatus.Pending);
|
||||
|
||||
(await wait1).Should().BeTrue();
|
||||
(await wait2).Should().BeTrue();
|
||||
|
||||
foreach (var item in new[] { 10, 20, 30 })
|
||||
{
|
||||
reference.Reader.TryRead(out var i1).Should().BeTrue();
|
||||
channel.Reader.TryRead(out var i2).Should().BeTrue();
|
||||
i1.Should().Be(item);
|
||||
i2.Should().Be(item);
|
||||
}
|
||||
|
||||
(await reference.Reader.WaitToReadAsync()).Should().BeFalse();
|
||||
(await channel.Reader.WaitToReadAsync()).Should().BeFalse();
|
||||
|
||||
completion1.Status.Should().Be(TaskStatus.RanToCompletion);
|
||||
completion2.Status.Should().Be(UniTaskStatus.Succeeded);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task CompleteErrorWithRest()
|
||||
{
|
||||
var (reference, channel) = CreateChannel();
|
||||
|
||||
foreach (var item in new[] { 10, 20, 30 })
|
||||
{
|
||||
reference.Writer.TryWrite(item);
|
||||
channel.Writer.TryWrite(item);
|
||||
}
|
||||
|
||||
// Three Item2.
|
||||
|
||||
var completion1 = reference.Reader.Completion;
|
||||
var wait1 = reference.Reader.WaitToReadAsync();
|
||||
|
||||
var completion2 = channel.Reader.Completion;
|
||||
var wait2 = channel.Reader.WaitToReadAsync();
|
||||
|
||||
var ex = new Exception();
|
||||
reference.Writer.TryComplete(ex);
|
||||
channel.Writer.TryComplete(ex);
|
||||
|
||||
// completion1.Status.Should().Be(TaskStatus.WaitingForActivation);
|
||||
completion2.Status.Should().Be(UniTaskStatus.Pending);
|
||||
|
||||
(await wait1).Should().BeTrue();
|
||||
(await wait2).Should().BeTrue();
|
||||
|
||||
foreach (var item in new[] { 10, 20, 30 })
|
||||
{
|
||||
reference.Reader.TryRead(out var i1).Should().BeTrue();
|
||||
channel.Reader.TryRead(out var i2).Should().BeTrue();
|
||||
i1.Should().Be(item);
|
||||
i2.Should().Be(item);
|
||||
}
|
||||
|
||||
wait1 = reference.Reader.WaitToReadAsync();
|
||||
wait2 = channel.Reader.WaitToReadAsync();
|
||||
|
||||
(await Assert.ThrowsAsync<Exception>(async () => await wait1)).Should().Be(ex);
|
||||
(await Assert.ThrowsAsync<Exception>(async () => await wait2)).Should().Be(ex);
|
||||
|
||||
completion1.Status.Should().Be(TaskStatus.Faulted);
|
||||
completion2.Status.Should().Be(UniTaskStatus.Faulted);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Cancellation()
|
||||
{
|
||||
var (reference, channel) = CreateChannel();
|
||||
|
||||
var cts = new CancellationTokenSource();
|
||||
|
||||
var wait1 = reference.Reader.WaitToReadAsync(cts.Token);
|
||||
var wait2 = channel.Reader.WaitToReadAsync(cts.Token);
|
||||
|
||||
cts.Cancel();
|
||||
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await wait1)).CancellationToken.Should().Be(cts.Token);
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await wait2)).CancellationToken.Should().Be(cts.Token);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AsyncEnumerator()
|
||||
{
|
||||
var (reference, channel) = CreateChannel();
|
||||
|
||||
var ta1 = reference.Reader.ReadAllAsync().ToArrayAsync();
|
||||
var ta2 = channel.Reader.ReadAllAsync().ToArrayAsync();
|
||||
|
||||
foreach (var item in new[] { 10, 20, 30 })
|
||||
{
|
||||
reference.Writer.TryWrite(item);
|
||||
channel.Writer.TryWrite(item);
|
||||
}
|
||||
|
||||
reference.Writer.TryComplete();
|
||||
channel.Writer.TryComplete();
|
||||
|
||||
(await ta1).Should().BeEquivalentTo(new[] { 10, 20, 30 });
|
||||
(await ta2).Should().BeEquivalentTo(new[] { 10, 20, 30 });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AsyncEnumeratorCancellation()
|
||||
{
|
||||
// Token1, Token2 and Cancel1
|
||||
{
|
||||
var cts1 = new CancellationTokenSource();
|
||||
var cts2 = new CancellationTokenSource();
|
||||
|
||||
var (reference, channel) = CreateChannel();
|
||||
|
||||
var ta1 = reference.Reader.ReadAllAsync(cts1.Token).ToArrayAsync(cts2.Token);
|
||||
var ta2 = channel.Reader.ReadAllAsync(cts1.Token).ToArrayAsync(cts2.Token);
|
||||
|
||||
foreach (var item in new[] { 10, 20, 30 })
|
||||
{
|
||||
reference.Writer.TryWrite(item);
|
||||
channel.Writer.TryWrite(item);
|
||||
}
|
||||
|
||||
cts1.Cancel();
|
||||
|
||||
await Assert.ThrowsAsync<OperationCanceledException>(async () => await ta1);
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await ta2)).CancellationToken.Should().Be(cts1.Token);
|
||||
}
|
||||
// Token1, Token2 and Cancel2
|
||||
{
|
||||
var cts1 = new CancellationTokenSource();
|
||||
var cts2 = new CancellationTokenSource();
|
||||
|
||||
var (reference, channel) = CreateChannel();
|
||||
|
||||
var ta1 = reference.Reader.ReadAllAsync(cts1.Token).ToArrayAsync(cts2.Token);
|
||||
var ta2 = channel.Reader.ReadAllAsync(cts1.Token).ToArrayAsync(cts2.Token);
|
||||
|
||||
foreach (var item in new[] { 10, 20, 30 })
|
||||
{
|
||||
reference.Writer.TryWrite(item);
|
||||
channel.Writer.TryWrite(item);
|
||||
}
|
||||
|
||||
cts2.Cancel();
|
||||
|
||||
await Assert.ThrowsAsync<OperationCanceledException>(async () => await ta1);
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await ta2)).CancellationToken.Should().Be(cts2.Token);
|
||||
}
|
||||
// Token1 and Cancel1
|
||||
{
|
||||
var cts1 = new CancellationTokenSource();
|
||||
|
||||
var (reference, channel) = CreateChannel();
|
||||
|
||||
var ta1 = reference.Reader.ReadAllAsync(cts1.Token).ToArrayAsync();
|
||||
var ta2 = channel.Reader.ReadAllAsync(cts1.Token).ToArrayAsync();
|
||||
|
||||
foreach (var item in new[] { 10, 20, 30 })
|
||||
{
|
||||
reference.Writer.TryWrite(item);
|
||||
channel.Writer.TryWrite(item);
|
||||
}
|
||||
|
||||
cts1.Cancel();
|
||||
|
||||
await Assert.ThrowsAsync<OperationCanceledException>(async () => await ta1);
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await ta2)).CancellationToken.Should().Be(cts1.Token);
|
||||
}
|
||||
// Token2 and Cancel2
|
||||
{
|
||||
var cts2 = new CancellationTokenSource();
|
||||
|
||||
var (reference, channel) = CreateChannel();
|
||||
|
||||
var ta1 = reference.Reader.ReadAllAsync().ToArrayAsync(cts2.Token);
|
||||
var ta2 = channel.Reader.ReadAllAsync().ToArrayAsync(cts2.Token);
|
||||
|
||||
foreach (var item in new[] { 10, 20, 30 })
|
||||
{
|
||||
reference.Writer.TryWrite(item);
|
||||
channel.Writer.TryWrite(item);
|
||||
}
|
||||
|
||||
cts2.Cancel();
|
||||
|
||||
await Assert.ThrowsAsync<OperationCanceledException>(async () => await ta1);
|
||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await ta2)).CancellationToken.Should().Be(cts2.Token);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
47
src/UniTask.NetCoreTests/DeferTest.cs
Normal file
47
src/UniTask.NetCoreTests/DeferTest.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Channels;
|
||||
using Cysharp.Threading.Tasks.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace NetCoreTests
|
||||
{
|
||||
public class DeferTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task D()
|
||||
{
|
||||
var created = false;
|
||||
var v = UniTask.Defer(() => { created = true; return UniTask.Run(() => 10); });
|
||||
|
||||
created.Should().BeFalse();
|
||||
|
||||
var t = await v;
|
||||
|
||||
created.Should().BeTrue();
|
||||
|
||||
t.Should().Be(10);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task D2()
|
||||
{
|
||||
var created = false;
|
||||
var v = UniTask.Defer(() => { created = true; return UniTask.Run(() => 10).AsUniTask(); });
|
||||
|
||||
created.Should().BeFalse();
|
||||
|
||||
await v;
|
||||
|
||||
created.Should().BeTrue();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
496
src/UniTask.NetCoreTests/Linq/Aggregate.cs
Normal file
496
src/UniTask.NetCoreTests/Linq/Aggregate.cs
Normal file
@@ -0,0 +1,496 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Cysharp.Threading.Tasks.Linq;
|
||||
using FluentAssertions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace NetCoreTests.Linq
|
||||
{
|
||||
public class Aggregate
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(0, 10)]
|
||||
[InlineData(0, 1)]
|
||||
[InlineData(10, 0)]
|
||||
[InlineData(1, 11)]
|
||||
public async Task Sum(int start, int count)
|
||||
{
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(start, count).SumAsync();
|
||||
var ys = Enumerable.Range(start, count).Sum();
|
||||
xs.Should().Be(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(start, count).SumAsync(x => x * 2);
|
||||
var ys = Enumerable.Range(start, count).Sum(x => x * 2);
|
||||
xs.Should().Be(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(start, count).SumAwaitAsync(x => UniTask.Run(() => x));
|
||||
var ys = Enumerable.Range(start, count).Sum(x => x);
|
||||
xs.Should().Be(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(start, count).SumAwaitWithCancellationAsync((x, _) => UniTask.Run(() => x));
|
||||
var ys = Enumerable.Range(start, count).Sum(x => x);
|
||||
xs.Should().Be(ys);
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> array1 = new object[][]
|
||||
{
|
||||
new object[]{new int[] { 1, 10, 100 } },
|
||||
new object[]{new int?[] { 1, null, 100 } },
|
||||
new object[]{new float[] { 1, 10, 100 } },
|
||||
new object[]{new float?[] { 1, null, 100 } },
|
||||
new object[]{new double[] { 1, 10, 100 } },
|
||||
new object[]{new double?[] { 1, null, 100 } },
|
||||
new object[]{new decimal[] { 1, 10, 100 } },
|
||||
new object[]{new decimal?[] { 1, null, 100 } },
|
||||
};
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(array1))]
|
||||
public async Task Average<T>(T arr)
|
||||
{
|
||||
switch (arr)
|
||||
{
|
||||
case int[] array:
|
||||
{
|
||||
var xs = await array.ToUniTaskAsyncEnumerable().AverageAsync();
|
||||
var ys = array.Average();
|
||||
xs.Should().Be(ys);
|
||||
}
|
||||
break;
|
||||
case int?[] array:
|
||||
{
|
||||
var xs = await array.ToUniTaskAsyncEnumerable().AverageAsync();
|
||||
var ys = array.Average();
|
||||
xs.Should().Be(ys);
|
||||
}
|
||||
break;
|
||||
case float[] array:
|
||||
{
|
||||
var xs = await array.ToUniTaskAsyncEnumerable().AverageAsync();
|
||||
var ys = array.Average();
|
||||
xs.Should().Be(ys);
|
||||
}
|
||||
break;
|
||||
case float?[] array:
|
||||
{
|
||||
var xs = await array.ToUniTaskAsyncEnumerable().AverageAsync();
|
||||
var ys = array.Average();
|
||||
xs.Should().Be(ys);
|
||||
}
|
||||
break;
|
||||
case double[] array:
|
||||
{
|
||||
var xs = await array.ToUniTaskAsyncEnumerable().AverageAsync();
|
||||
var ys = array.Average();
|
||||
xs.Should().Be(ys);
|
||||
}
|
||||
break;
|
||||
case double?[] array:
|
||||
{
|
||||
var xs = await array.ToUniTaskAsyncEnumerable().AverageAsync();
|
||||
var ys = array.Average();
|
||||
xs.Should().Be(ys);
|
||||
}
|
||||
break;
|
||||
case decimal[] array:
|
||||
{
|
||||
var xs = await array.ToUniTaskAsyncEnumerable().AverageAsync();
|
||||
var ys = array.Average();
|
||||
xs.Should().Be(ys);
|
||||
}
|
||||
break;
|
||||
case decimal?[] array:
|
||||
{
|
||||
var xs = await array.ToUniTaskAsyncEnumerable().AverageAsync();
|
||||
var ys = array.Average();
|
||||
xs.Should().Be(ys);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static IEnumerable<object[]> array2 = new object[][]
|
||||
{
|
||||
new object[]{new int[] { } },
|
||||
new object[]{new int[] { 5 } },
|
||||
new object[]{new int[] { 5, 10, 100 } },
|
||||
new object[]{new int[] { 10, 5,100 } },
|
||||
new object[]{new int[] { 100, 10, 5 } },
|
||||
|
||||
new object[]{new int?[] { } },
|
||||
new object[]{new int?[] { 5 } },
|
||||
new object[]{new int?[] { null, null, null } },
|
||||
new object[]{new int?[] { null, 5, 10, 100 } },
|
||||
new object[]{new int?[] { 10, 5,100, null } },
|
||||
new object[]{new int?[] { 100, 10, 5 } },
|
||||
|
||||
new object[]{new X[] { } },
|
||||
new object[]{new X[] { new X(5) } },
|
||||
new object[]{new X[] { new X(5), new X(10), new X(100) } },
|
||||
new object[]{new X[] { new X(10),new X( 5),new X(100) } },
|
||||
new object[]{new X[] { new X(100), new X(10),new X(5) } },
|
||||
|
||||
new object[]{new XX[] { } },
|
||||
new object[]{new XX[] { new XX(new X(5)) } },
|
||||
new object[]{new XX[] { new XX(new X(5)), new XX(new X(10)), new XX(new X(100)) } },
|
||||
new object[]{new XX[] { new XX(new X(10)),new XX(new X( 5)),new XX(new X(100)) } },
|
||||
new object[]{new XX[] { new XX(new X(100)), new XX(new X(10)),new XX(new X(5)) } },
|
||||
};
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(array2))]
|
||||
public async Task Min<T>(T arr)
|
||||
{
|
||||
switch (arr)
|
||||
{
|
||||
case int[] array:
|
||||
{
|
||||
{
|
||||
if (array.Length == 0)
|
||||
{
|
||||
await Assert.ThrowsAsync<InvalidOperationException>(async () => await array.ToUniTaskAsyncEnumerable().MinAsync());
|
||||
Assert.Throws<InvalidOperationException>(() => array.Min());
|
||||
}
|
||||
else
|
||||
{
|
||||
var xs = await array.ToUniTaskAsyncEnumerable().MinAsync();
|
||||
var ys = array.Min();
|
||||
xs.Should().Be(ys);
|
||||
}
|
||||
}
|
||||
{
|
||||
if (array.Length == 0)
|
||||
{
|
||||
await Assert.ThrowsAsync<InvalidOperationException>(async () => await array.ToUniTaskAsyncEnumerable().MinAsync(x => x * 2));
|
||||
Assert.Throws<InvalidOperationException>(() => array.Min(x => x * 2));
|
||||
}
|
||||
else
|
||||
{
|
||||
var xs = await array.ToUniTaskAsyncEnumerable().MinAsync(x => x * 2);
|
||||
var ys = array.Min(x => x * 2);
|
||||
xs.Should().Be(ys);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case int?[] array:
|
||||
{
|
||||
{
|
||||
var xs = await array.ToUniTaskAsyncEnumerable().MinAsync();
|
||||
var ys = array.Min();
|
||||
xs.Should().Be(ys);
|
||||
}
|
||||
{
|
||||
var xs = await array.ToUniTaskAsyncEnumerable().MinAsync(x => x);
|
||||
var ys = array.Min(x => x);
|
||||
xs.Should().Be(ys);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case X[] array:
|
||||
{
|
||||
{
|
||||
var xs = await array.ToUniTaskAsyncEnumerable().MinAsync();
|
||||
var ys = array.Min();
|
||||
xs.Should().Be(ys);
|
||||
}
|
||||
{
|
||||
|
||||
if (array.Length == 0)
|
||||
{
|
||||
await Assert.ThrowsAsync<InvalidOperationException>(async () => await array.ToUniTaskAsyncEnumerable().MinAsync(x => x.Value));
|
||||
Assert.Throws<InvalidOperationException>(() => array.Min(x => x.Value));
|
||||
}
|
||||
else
|
||||
{
|
||||
var xs = await array.ToUniTaskAsyncEnumerable().MinAsync(x => x.Value);
|
||||
var ys = array.Min(x => x.Value);
|
||||
xs.Should().Be(ys);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case XX[] array:
|
||||
{
|
||||
var xs = await array.ToUniTaskAsyncEnumerable().MinAsync(x => x.Value);
|
||||
var ys = array.Min(x => x.Value);
|
||||
xs.Should().Be(ys);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(array2))]
|
||||
public async Task Max<T>(T arr)
|
||||
{
|
||||
switch (arr)
|
||||
{
|
||||
case int[] array:
|
||||
{
|
||||
{
|
||||
if (array.Length == 0)
|
||||
{
|
||||
await Assert.ThrowsAsync<InvalidOperationException>(async () => await array.ToUniTaskAsyncEnumerable().MaxAsync());
|
||||
Assert.Throws<InvalidOperationException>(() => array.Max());
|
||||
}
|
||||
else
|
||||
{
|
||||
var xs = await array.ToUniTaskAsyncEnumerable().MaxAsync();
|
||||
var ys = array.Max();
|
||||
xs.Should().Be(ys);
|
||||
}
|
||||
}
|
||||
{
|
||||
if (array.Length == 0)
|
||||
{
|
||||
await Assert.ThrowsAsync<InvalidOperationException>(async () => await array.ToUniTaskAsyncEnumerable().MaxAsync(x => x * 2));
|
||||
Assert.Throws<InvalidOperationException>(() => array.Max(x => x * 2));
|
||||
}
|
||||
else
|
||||
{
|
||||
var xs = await array.ToUniTaskAsyncEnumerable().MaxAsync(x => x * 2);
|
||||
var ys = array.Max(x => x * 2);
|
||||
xs.Should().Be(ys);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case int?[] array:
|
||||
{
|
||||
{
|
||||
var xs = await array.ToUniTaskAsyncEnumerable().MaxAsync();
|
||||
var ys = array.Max();
|
||||
xs.Should().Be(ys);
|
||||
}
|
||||
{
|
||||
var xs = await array.ToUniTaskAsyncEnumerable().MaxAsync(x => x);
|
||||
var ys = array.Max(x => x);
|
||||
xs.Should().Be(ys);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case X[] array:
|
||||
{
|
||||
{
|
||||
var xs = await array.ToUniTaskAsyncEnumerable().MaxAsync();
|
||||
var ys = array.Max();
|
||||
xs.Should().Be(ys);
|
||||
}
|
||||
{
|
||||
|
||||
if (array.Length == 0)
|
||||
{
|
||||
await Assert.ThrowsAsync<InvalidOperationException>(async () => await array.ToUniTaskAsyncEnumerable().MaxAsync(x => x.Value));
|
||||
Assert.Throws<InvalidOperationException>(() => array.Max(x => x.Value));
|
||||
}
|
||||
else
|
||||
{
|
||||
var xs = await array.ToUniTaskAsyncEnumerable().MaxAsync(x => x.Value);
|
||||
var ys = array.Max(x => x.Value);
|
||||
xs.Should().Be(ys);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case XX[] array:
|
||||
{
|
||||
var xs = await array.ToUniTaskAsyncEnumerable().MaxAsync(x => x.Value);
|
||||
var ys = array.Max(x => x.Value);
|
||||
xs.Should().Be(ys);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public class XX
|
||||
{
|
||||
public readonly X Value;
|
||||
|
||||
public XX(X value)
|
||||
{
|
||||
this.Value = value;
|
||||
}
|
||||
}
|
||||
|
||||
public class X : IComparable<X>
|
||||
{
|
||||
public readonly int Value;
|
||||
|
||||
public X(int value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public int CompareTo([AllowNull] X other)
|
||||
{
|
||||
return Comparer<int>.Default.Compare(Value, other.Value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, 10)]
|
||||
[InlineData(0, 1)]
|
||||
[InlineData(10, 0)]
|
||||
[InlineData(1, 11)]
|
||||
public async Task Count(int start, int count)
|
||||
{
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(start, count).CountAsync();
|
||||
var ys = Enumerable.Range(start, count).Count();
|
||||
xs.Should().Be(ys);
|
||||
}
|
||||
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(start, count).CountAsync(x => x % 2 == 0);
|
||||
var ys = Enumerable.Range(start, count).Count(x => x % 2 == 0);
|
||||
xs.Should().Be(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(start, count).LongCountAsync();
|
||||
var ys = Enumerable.Range(start, count).LongCount();
|
||||
xs.Should().Be(ys);
|
||||
}
|
||||
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(start, count).LongCountAsync(x => x % 2 == 0);
|
||||
var ys = Enumerable.Range(start, count).LongCount(x => x % 2 == 0);
|
||||
xs.Should().Be(ys);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task AggregateTest1()
|
||||
{
|
||||
// 0
|
||||
await Assert.ThrowsAsync<InvalidOperationException>(async () => await new int[] { }.ToUniTaskAsyncEnumerable().AggregateAsync((x, y) => x + y));
|
||||
Assert.Throws<InvalidOperationException>(() => new int[] { }.Aggregate((x, y) => x + y));
|
||||
|
||||
// 1
|
||||
{
|
||||
var a = await Enumerable.Range(1, 1).ToUniTaskAsyncEnumerable().AggregateAsync((x, y) => x + y);
|
||||
var b = Enumerable.Range(1, 1).Aggregate((x, y) => x + y);
|
||||
a.Should().Be(b);
|
||||
}
|
||||
|
||||
// 2
|
||||
{
|
||||
var a = await Enumerable.Range(1, 2).ToUniTaskAsyncEnumerable().AggregateAsync((x, y) => x + y);
|
||||
var b = Enumerable.Range(1, 2).Aggregate((x, y) => x + y);
|
||||
a.Should().Be(b);
|
||||
}
|
||||
|
||||
// 10
|
||||
{
|
||||
var a = await Enumerable.Range(1, 10).ToUniTaskAsyncEnumerable().AggregateAsync((x, y) => x + y);
|
||||
var b = Enumerable.Range(1, 10).Aggregate((x, y) => x + y);
|
||||
a.Should().Be(b);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AggregateTest2()
|
||||
{
|
||||
// 0
|
||||
{
|
||||
var a = await Enumerable.Range(1, 1).ToUniTaskAsyncEnumerable().AggregateAsync(1000, (x, y) => x + y);
|
||||
var b = Enumerable.Range(1, 1).Aggregate(1000, (x, y) => x + y);
|
||||
a.Should().Be(b);
|
||||
}
|
||||
|
||||
// 1
|
||||
{
|
||||
var a = await Enumerable.Range(1, 1).ToUniTaskAsyncEnumerable().AggregateAsync(1000, (x, y) => x + y);
|
||||
var b = Enumerable.Range(1, 1).Aggregate(1000, (x, y) => x + y);
|
||||
a.Should().Be(b);
|
||||
}
|
||||
|
||||
// 2
|
||||
{
|
||||
var a = await Enumerable.Range(1, 2).ToUniTaskAsyncEnumerable().AggregateAsync(1000, (x, y) => x + y);
|
||||
var b = Enumerable.Range(1, 2).Aggregate(1000, (x, y) => x + y);
|
||||
a.Should().Be(b);
|
||||
}
|
||||
|
||||
// 10
|
||||
{
|
||||
var a = await Enumerable.Range(1, 10).ToUniTaskAsyncEnumerable().AggregateAsync(1000, (x, y) => x + y);
|
||||
var b = Enumerable.Range(1, 10).Aggregate(1000, (x, y) => x + y);
|
||||
a.Should().Be(b);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AggregateTest3()
|
||||
{
|
||||
// 0
|
||||
{
|
||||
var a = await Enumerable.Range(1, 1).ToUniTaskAsyncEnumerable().AggregateAsync(1000, (x, y) => x + y, x => (x * 99).ToString());
|
||||
var b = Enumerable.Range(1, 1).Aggregate(1000, (x, y) => x + y, x => (x * 99).ToString());
|
||||
a.Should().Be(b);
|
||||
}
|
||||
|
||||
// 1
|
||||
{
|
||||
var a = await Enumerable.Range(1, 1).ToUniTaskAsyncEnumerable().AggregateAsync(1000, (x, y) => x + y, x => (x * 99).ToString());
|
||||
var b = Enumerable.Range(1, 1).Aggregate(1000, (x, y) => x + y, x => (x * 99).ToString());
|
||||
a.Should().Be(b);
|
||||
}
|
||||
|
||||
// 2
|
||||
{
|
||||
var a = await Enumerable.Range(1, 2).ToUniTaskAsyncEnumerable().AggregateAsync(1000, (x, y) => x + y, x => (x * 99).ToString());
|
||||
var b = Enumerable.Range(1, 2).Aggregate(1000, (x, y) => x + y, x => (x * 99).ToString());
|
||||
a.Should().Be(b);
|
||||
}
|
||||
|
||||
// 10
|
||||
{
|
||||
var a = await Enumerable.Range(1, 10).ToUniTaskAsyncEnumerable().AggregateAsync(1000, (x, y) => x + y, x => (x * 99).ToString());
|
||||
var b = Enumerable.Range(1, 10).Aggregate(1000, (x, y) => x + y, x => (x * 99).ToString());
|
||||
a.Should().Be(b);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ForEach()
|
||||
{
|
||||
var list = new List<int>();
|
||||
await Enumerable.Range(1, 10).ToUniTaskAsyncEnumerable().ForEachAsync(x =>
|
||||
{
|
||||
list.Add(x);
|
||||
});
|
||||
|
||||
list.Should().BeEquivalentTo(Enumerable.Range(1, 10));
|
||||
|
||||
var list2 = new List<(int, int)>();
|
||||
await Enumerable.Range(5, 10).ToUniTaskAsyncEnumerable().ForEachAsync((index, x) =>
|
||||
{
|
||||
list2.Add((index, x));
|
||||
});
|
||||
|
||||
var list3 = Enumerable.Range(5, 10).Select((index, x) => (index, x)).ToArray();
|
||||
list2.Should().BeEquivalentTo(list3);
|
||||
}
|
||||
}
|
||||
}
|
||||
112
src/UniTask.NetCoreTests/Linq/AllAny.cs
Normal file
112
src/UniTask.NetCoreTests/Linq/AllAny.cs
Normal file
@@ -0,0 +1,112 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Cysharp.Threading.Tasks.Linq;
|
||||
using FluentAssertions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Concurrency;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
|
||||
namespace NetCoreTests.Linq
|
||||
{
|
||||
public class AllAny
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(0, 0)]
|
||||
[InlineData(1, 1)]
|
||||
[InlineData(1, 2)]
|
||||
[InlineData(1, 3)]
|
||||
[InlineData(0, 10)]
|
||||
[InlineData(0, 11)]
|
||||
public async Task AllTest(int start, int count)
|
||||
{
|
||||
var range = Enumerable.Range(start, count);
|
||||
var x = await range.ToUniTaskAsyncEnumerable().AllAsync(x => x % 2 == 0);
|
||||
var y = range.All(x => x % 2 == 0);
|
||||
|
||||
x.Should().Be(y);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, 0)]
|
||||
[InlineData(1, 1)]
|
||||
[InlineData(1, 2)]
|
||||
[InlineData(1, 3)]
|
||||
[InlineData(0, 10)]
|
||||
[InlineData(0, 11)]
|
||||
public async Task AnyTest(int start, int count)
|
||||
{
|
||||
var range = Enumerable.Range(start, count);
|
||||
{
|
||||
var x = await range.ToUniTaskAsyncEnumerable().AnyAsync();
|
||||
var y = range.Any();
|
||||
|
||||
x.Should().Be(y);
|
||||
}
|
||||
{
|
||||
var x = await range.ToUniTaskAsyncEnumerable().AnyAsync(x => x % 2 == 0);
|
||||
var y = range.Any(x => x % 2 == 0);
|
||||
|
||||
x.Should().Be(y);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, 0)]
|
||||
[InlineData(1, 1)]
|
||||
[InlineData(1, 2)]
|
||||
[InlineData(1, 3)]
|
||||
[InlineData(0, 10)]
|
||||
[InlineData(0, 11)]
|
||||
public async Task ContainsTest(int start, int count)
|
||||
{
|
||||
var range = Enumerable.Range(start, count);
|
||||
foreach (var c in Enumerable.Range(0, 15))
|
||||
{
|
||||
var x = await range.ToUniTaskAsyncEnumerable().ContainsAsync(c);
|
||||
var y = range.Contains(c);
|
||||
x.Should().Be(y);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SequenceEqual()
|
||||
{
|
||||
// empty and empty
|
||||
(await new int[0].ToUniTaskAsyncEnumerable().SequenceEqualAsync(new int[0].ToUniTaskAsyncEnumerable())).Should().BeTrue();
|
||||
(new int[0].SequenceEqual(new int[0])).Should().BeTrue();
|
||||
|
||||
// empty and exists
|
||||
(await new int[0].ToUniTaskAsyncEnumerable().SequenceEqualAsync(new int[] { 1 }.ToUniTaskAsyncEnumerable())).Should().BeFalse();
|
||||
(new int[0].SequenceEqual(new int[] { 1 })).Should().BeFalse();
|
||||
|
||||
// exists and empty
|
||||
(await new int[] { 1 }.ToUniTaskAsyncEnumerable().SequenceEqualAsync(new int[0].ToUniTaskAsyncEnumerable())).Should().BeFalse();
|
||||
(new int[] { 1 }.SequenceEqual(new int[] { })).Should().BeFalse();
|
||||
|
||||
// samelength same value
|
||||
(await new int[] { 1, 2, 3 }.ToUniTaskAsyncEnumerable().SequenceEqualAsync(new int[] { 1, 2, 3 }.ToUniTaskAsyncEnumerable())).Should().BeTrue();
|
||||
(new int[] { 1, 2, 3 }.SequenceEqual(new int[] { 1, 2, 3 })).Should().BeTrue();
|
||||
|
||||
// samelength different value(first)
|
||||
(await new int[] { 5, 2, 3 }.ToUniTaskAsyncEnumerable().SequenceEqualAsync(new int[] { 1, 2, 3 }.ToUniTaskAsyncEnumerable())).Should().BeFalse();
|
||||
|
||||
// samelength different value(middle)
|
||||
(await new int[] { 1, 2, 3 }.ToUniTaskAsyncEnumerable().SequenceEqualAsync(new int[] { 1, 5, 3 }.ToUniTaskAsyncEnumerable())).Should().BeFalse();
|
||||
|
||||
// samelength different value(last)
|
||||
(await new int[] { 1, 2, 3 }.ToUniTaskAsyncEnumerable().SequenceEqualAsync(new int[] { 1, 2, 5 }.ToUniTaskAsyncEnumerable())).Should().BeFalse();
|
||||
|
||||
// left is long
|
||||
(await new int[] { 1, 2, 3, 4 }.ToUniTaskAsyncEnumerable().SequenceEqualAsync(new int[] { 1, 2, 3 }.ToUniTaskAsyncEnumerable())).Should().BeFalse();
|
||||
(new int[] { 1, 2, 3, 4 }.SequenceEqual(new int[] { 1, 2, 3 })).Should().BeFalse();
|
||||
|
||||
// right is long
|
||||
(await new int[] { 1, 2, 3 }.ToUniTaskAsyncEnumerable().SequenceEqualAsync(new int[] { 1, 2, 3, 4 }.ToUniTaskAsyncEnumerable())).Should().BeFalse();
|
||||
(new int[] { 1, 2, 3 }.SequenceEqual(new int[] { 1, 2, 3, 4 })).Should().BeFalse();
|
||||
}
|
||||
}
|
||||
}
|
||||
144
src/UniTask.NetCoreTests/Linq/Concat.cs
Normal file
144
src/UniTask.NetCoreTests/Linq/Concat.cs
Normal file
@@ -0,0 +1,144 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Cysharp.Threading.Tasks.Linq;
|
||||
using FluentAssertions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Concurrency;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
|
||||
namespace NetCoreTests.Linq
|
||||
{
|
||||
public class Concat
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(0, 0)]
|
||||
[InlineData(0, 1)]
|
||||
[InlineData(0, 2)]
|
||||
[InlineData(0, 10)]
|
||||
public async Task Append(int start, int count)
|
||||
{
|
||||
var xs = await Enumerable.Range(start, count).ToUniTaskAsyncEnumerable().Append(99).ToArrayAsync();
|
||||
var ys = Enumerable.Range(start, count).Append(99).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AppendThrow()
|
||||
{
|
||||
var xs = UniTaskTestException.ThrowImmediate().Append(99).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
|
||||
var ys = UniTaskTestException.ThrowAfter().Append(99).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await ys);
|
||||
|
||||
var zs = UniTaskTestException.ThrowInMoveNext().Append(99).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await zs);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, 0)]
|
||||
[InlineData(0, 1)]
|
||||
[InlineData(0, 2)]
|
||||
[InlineData(0, 10)]
|
||||
public async Task Prepend(int start, int count)
|
||||
{
|
||||
var xs = await Enumerable.Range(start, count).ToUniTaskAsyncEnumerable().Prepend(99).ToArrayAsync();
|
||||
var ys = Enumerable.Range(start, count).Prepend(99).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PrependThrow()
|
||||
{
|
||||
var xs = UniTaskTestException.ThrowImmediate().Prepend(99).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
|
||||
var ys = UniTaskTestException.ThrowAfter().Prepend(99).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await ys);
|
||||
|
||||
var zs = UniTaskTestException.ThrowInMoveNext().Prepend(99).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await zs);
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> array1 = new object[][]
|
||||
{
|
||||
new object[] { (0, 0), (0, 0) }, // empty + empty
|
||||
new object[] { (0, 1), (0, 0) }, // 1 + empty
|
||||
new object[] { (0, 0), (0, 1) }, // empty + 1
|
||||
new object[] { (0, 5), (0, 0) }, // 5 + empty
|
||||
new object[] { (0, 0), (0, 5) }, // empty + 5
|
||||
new object[] { (0, 5), (0, 5) }, // 5 + 5
|
||||
};
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(array1))]
|
||||
public async Task ConcatTest((int, int) left, (int, int) right)
|
||||
{
|
||||
var l = Enumerable.Range(left.Item1, left.Item2);
|
||||
var r = Enumerable.Range(right.Item1, right.Item2);
|
||||
|
||||
var xs = await l.ToUniTaskAsyncEnumerable().Concat(r.ToUniTaskAsyncEnumerable()).ToArrayAsync();
|
||||
var ys = l.Concat(r).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ConcatThrow()
|
||||
{
|
||||
{
|
||||
var xs = UniTaskTestException.ThrowImmediate().Concat(UniTaskAsyncEnumerable.Range(1, 10)).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
|
||||
var ys = UniTaskTestException.ThrowAfter().Concat(UniTaskAsyncEnumerable.Range(1, 10)).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await ys);
|
||||
|
||||
var zs = UniTaskTestException.ThrowInMoveNext().Concat(UniTaskAsyncEnumerable.Range(1, 10)).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await zs);
|
||||
}
|
||||
{
|
||||
var xs = UniTaskAsyncEnumerable.Range(1, 10).Concat(UniTaskTestException.ThrowImmediate()).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
|
||||
var ys = UniTaskAsyncEnumerable.Range(1, 10).Concat(UniTaskTestException.ThrowAfter()).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await ys);
|
||||
|
||||
var zs = UniTaskAsyncEnumerable.Range(1, 10).Concat(UniTaskTestException.ThrowInMoveNext()).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await zs);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DefaultIfEmpty()
|
||||
{
|
||||
{
|
||||
var xs = await Enumerable.Range(1, 0).ToUniTaskAsyncEnumerable().DefaultIfEmpty(99).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, 0).DefaultIfEmpty(99).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
{
|
||||
var xs = await Enumerable.Range(1, 1).ToUniTaskAsyncEnumerable().DefaultIfEmpty(99).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, 1).DefaultIfEmpty(99).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
{
|
||||
var xs = await Enumerable.Range(1, 10).ToUniTaskAsyncEnumerable().DefaultIfEmpty(99).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, 10).DefaultIfEmpty(99).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
// Throw
|
||||
{
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = item.DefaultIfEmpty().ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
196
src/UniTask.NetCoreTests/Linq/Convert.cs
Normal file
196
src/UniTask.NetCoreTests/Linq/Convert.cs
Normal file
@@ -0,0 +1,196 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Cysharp.Threading.Tasks.Linq;
|
||||
using FluentAssertions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Concurrency;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace NetCoreTests.Linq
|
||||
{
|
||||
public class Convert
|
||||
{
|
||||
[Fact]
|
||||
public async Task ToAsyncEnumerable()
|
||||
{
|
||||
{
|
||||
var xs = await Enumerable.Range(1, 100).ToUniTaskAsyncEnumerable().ToArrayAsync();
|
||||
|
||||
xs.Length.Should().Be(100);
|
||||
}
|
||||
{
|
||||
var xs = await Enumerable.Empty<int>().ToUniTaskAsyncEnumerable().ToArrayAsync();
|
||||
|
||||
xs.Length.Should().Be(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task ToObservable()
|
||||
{
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, 10).ToObservable().ToArray();
|
||||
xs.Should().BeEquivalentTo(Enumerable.Range(1, 10));
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, 0).ToObservable().ToArray();
|
||||
xs.Should().BeEquivalentTo(Enumerable.Range(1, 0));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task ToAsyncEnumerableTask()
|
||||
{
|
||||
var t = Task.FromResult(100);
|
||||
var xs = await t.ToUniTaskAsyncEnumerable().ToArrayAsync();
|
||||
|
||||
xs.Length.Should().Be(1);
|
||||
xs[0].Should().Be(100);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ToAsyncEnumerableUniTask()
|
||||
{
|
||||
var t = UniTask.FromResult(100);
|
||||
var xs = await t.ToUniTaskAsyncEnumerable().ToArrayAsync();
|
||||
|
||||
xs.Length.Should().Be(1);
|
||||
xs[0].Should().Be(100);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ToAsyncEnumerableObservable()
|
||||
{
|
||||
{
|
||||
var xs = await Observable.Range(1, 100).ToUniTaskAsyncEnumerable().ToArrayAsync();
|
||||
var ys = await Observable.Range(1, 100).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
|
||||
{
|
||||
var xs = await Observable.Range(1, 100, ThreadPoolScheduler.Instance).ToUniTaskAsyncEnumerable().ToArrayAsync();
|
||||
var ys = await Observable.Range(1, 100, ThreadPoolScheduler.Instance).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
|
||||
{
|
||||
var xs = await Observable.Empty<int>(ThreadPoolScheduler.Instance).ToUniTaskAsyncEnumerable().ToArrayAsync();
|
||||
var ys = await Observable.Empty<int>(ThreadPoolScheduler.Instance).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ToDictionary()
|
||||
{
|
||||
{
|
||||
var xs = await Enumerable.Range(1, 100).ToUniTaskAsyncEnumerable().ToDictionaryAsync(x => x);
|
||||
var ys = Enumerable.Range(1, 100).ToDictionary(x => x);
|
||||
|
||||
xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key));
|
||||
}
|
||||
{
|
||||
var xs = await Enumerable.Range(1, 0).ToUniTaskAsyncEnumerable().ToDictionaryAsync(x => x);
|
||||
var ys = Enumerable.Range(1, 0).ToDictionary(x => x);
|
||||
|
||||
xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key));
|
||||
}
|
||||
{
|
||||
var xs = await Enumerable.Range(1, 100).ToUniTaskAsyncEnumerable().ToDictionaryAsync(x => x, x => x * 2);
|
||||
var ys = Enumerable.Range(1, 100).ToDictionary(x => x, x => x * 2);
|
||||
|
||||
xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key));
|
||||
}
|
||||
{
|
||||
var xs = await Enumerable.Range(1, 0).ToUniTaskAsyncEnumerable().ToDictionaryAsync(x => x, x => x * 2);
|
||||
var ys = Enumerable.Range(1, 0).ToDictionary(x => x, x => x * 2);
|
||||
|
||||
xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ToLookup()
|
||||
{
|
||||
var arr = new[] { 1, 4, 10, 10, 4, 5, 10, 9 };
|
||||
{
|
||||
var xs = await arr.ToUniTaskAsyncEnumerable().ToLookupAsync(x => x);
|
||||
var ys = arr.ToLookup(x => x);
|
||||
|
||||
xs.Count.Should().Be(ys.Count);
|
||||
xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key));
|
||||
foreach (var key in xs.Select(x => x.Key))
|
||||
{
|
||||
xs[key].Should().BeEquivalentTo(ys[key]);
|
||||
}
|
||||
}
|
||||
{
|
||||
var xs = await Enumerable.Range(1, 0).ToUniTaskAsyncEnumerable().ToLookupAsync(x => x);
|
||||
var ys = Enumerable.Range(1, 0).ToLookup(x => x);
|
||||
|
||||
xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key));
|
||||
}
|
||||
{
|
||||
var xs = await arr.ToUniTaskAsyncEnumerable().ToLookupAsync(x => x, x => x * 2);
|
||||
var ys = arr.ToLookup(x => x, x => x * 2);
|
||||
|
||||
xs.Count.Should().Be(ys.Count);
|
||||
xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key));
|
||||
foreach (var key in xs.Select(x => x.Key))
|
||||
{
|
||||
xs[key].Should().BeEquivalentTo(ys[key]);
|
||||
}
|
||||
}
|
||||
{
|
||||
var xs = await Enumerable.Range(1, 0).ToUniTaskAsyncEnumerable().ToLookupAsync(x => x, x => x * 2);
|
||||
var ys = Enumerable.Range(1, 0).ToLookup(x => x, x => x * 2);
|
||||
|
||||
xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ToList()
|
||||
{
|
||||
{
|
||||
var xs = await Enumerable.Range(1, 100).ToUniTaskAsyncEnumerable().ToListAsync();
|
||||
var ys = Enumerable.Range(1, 100).ToList();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
{
|
||||
var xs = await Enumerable.Empty<int>().ToUniTaskAsyncEnumerable().ToListAsync();
|
||||
var ys = Enumerable.Empty<int>().ToList();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ToHashSet()
|
||||
{
|
||||
{
|
||||
var xs = await new[] { 1, 20, 4, 5, 20, 4, 6 }.ToUniTaskAsyncEnumerable().ToHashSetAsync();
|
||||
var ys = new[] { 1, 20, 4, 5, 20, 4, 6 }.ToHashSet();
|
||||
|
||||
xs.OrderBy(x => x).Should().BeEquivalentTo(ys.OrderBy(x => x));
|
||||
}
|
||||
{
|
||||
var xs = await Enumerable.Empty<int>().ToUniTaskAsyncEnumerable().ToHashSetAsync();
|
||||
var ys = Enumerable.Empty<int>().ToHashSet();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
66
src/UniTask.NetCoreTests/Linq/Factory.cs
Normal file
66
src/UniTask.NetCoreTests/Linq/Factory.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
using Cysharp.Threading.Tasks.Linq;
|
||||
using FluentAssertions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace NetCoreTests.Linq
|
||||
{
|
||||
public class Factory
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(0, 10)]
|
||||
[InlineData(0, 0)]
|
||||
[InlineData(1, 5)]
|
||||
[InlineData(1, 0)]
|
||||
[InlineData(0, 11)]
|
||||
[InlineData(1, 11)]
|
||||
public async Task RangeTest(int start, int count)
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(start, count).ToArrayAsync();
|
||||
var ys = Enumerable.Range(start, count).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("foo", 0)]
|
||||
[InlineData("bar", 1)]
|
||||
[InlineData("baz", 3)]
|
||||
[InlineData("foobar", 10)]
|
||||
[InlineData("foobarbaz", 11)]
|
||||
public async Task RepeatTest(string value, int count)
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Repeat(value, count).ToArrayAsync();
|
||||
var ys = Enumerable.Repeat(value, count).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EmptyTest()
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Empty<int>().ToArrayAsync();
|
||||
var ys = Enumerable.Empty<int>().ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(100)]
|
||||
[InlineData((string)null)]
|
||||
[InlineData("foo")]
|
||||
public async Task ReturnTest<T>(T value)
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Return(value).ToArrayAsync();
|
||||
|
||||
xs.Length.Should().Be(1);
|
||||
xs[0].Should().Be(value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
118
src/UniTask.NetCoreTests/Linq/Filtering.cs
Normal file
118
src/UniTask.NetCoreTests/Linq/Filtering.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Cysharp.Threading.Tasks.Linq;
|
||||
using FluentAssertions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Concurrency;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
|
||||
namespace NetCoreTests.Linq
|
||||
{
|
||||
public class Filtering
|
||||
{
|
||||
[Fact]
|
||||
public async Task Where()
|
||||
{
|
||||
var range = Enumerable.Range(1, 10);
|
||||
var src = range.ToUniTaskAsyncEnumerable();
|
||||
|
||||
{
|
||||
var a = await src.Where(x => x % 2 == 0).ToArrayAsync();
|
||||
var expected = range.Where(x => x % 2 == 0).ToArray();
|
||||
a.Should().BeEquivalentTo(expected);
|
||||
}
|
||||
{
|
||||
var a = await src.Where((x, i) => (x + i) % 2 == 0).ToArrayAsync();
|
||||
var expected = range.Where((x, i) => (x + i) % 2 == 0).ToArray();
|
||||
a.Should().BeEquivalentTo(expected);
|
||||
}
|
||||
{
|
||||
var a = await src.WhereAwait(x => UniTask.Run(() => x % 2 == 0)).ToArrayAsync();
|
||||
var b = await src.WhereAwait(x => UniTask.FromResult(x % 2 == 0)).ToArrayAsync();
|
||||
var expected = range.Where(x => x % 2 == 0).ToArray();
|
||||
a.Should().BeEquivalentTo(expected);
|
||||
b.Should().BeEquivalentTo(expected);
|
||||
}
|
||||
{
|
||||
var a = await src.WhereAwait((x, i) => UniTask.Run(() => (x + i) % 2 == 0)).ToArrayAsync();
|
||||
var b = await src.WhereAwait((x, i) => UniTask.FromResult((x + i) % 2 == 0)).ToArrayAsync();
|
||||
var expected = range.Where((x, i) => (x + i) % 2 == 0).ToArray();
|
||||
a.Should().BeEquivalentTo(expected);
|
||||
b.Should().BeEquivalentTo(expected);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task WhereException()
|
||||
{
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
{
|
||||
var xs = item.Where(x => x % 2 == 0).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
{
|
||||
var xs = item.Where((x, i) => x % 2 == 0).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
{
|
||||
var xs = item.WhereAwait(x => UniTask.FromResult(x % 2 == 0)).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
{
|
||||
var xs = item.WhereAwait((x, i) => UniTask.FromResult(x % 2 == 0)).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task OfType()
|
||||
{
|
||||
var data = new object[] { 0, null, 10, 30, null, "foo", 99 };
|
||||
|
||||
var a = await data.ToUniTaskAsyncEnumerable().OfType<int>().ToArrayAsync();
|
||||
var b = data.OfType<int>().ToArray();
|
||||
|
||||
a.Should().BeEquivalentTo(b);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task OfTypeException()
|
||||
{
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = item.Select(x => (object)x).OfType<int>().ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Cast()
|
||||
{
|
||||
var data = new object[] { 0, 10, 30, 99 };
|
||||
|
||||
var a = await data.ToUniTaskAsyncEnumerable().Cast<int>().ToArrayAsync();
|
||||
var b = data.Cast<int>().ToArray();
|
||||
|
||||
a.Should().BeEquivalentTo(b);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task CastException()
|
||||
{
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = item.Select(x => (object)x).Cast<int>().ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
258
src/UniTask.NetCoreTests/Linq/FirstLast.cs
Normal file
258
src/UniTask.NetCoreTests/Linq/FirstLast.cs
Normal file
@@ -0,0 +1,258 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Cysharp.Threading.Tasks.Linq;
|
||||
using FluentAssertions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Concurrency;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace NetCoreTests.Linq
|
||||
{
|
||||
public class FirstLast
|
||||
{
|
||||
[Fact]
|
||||
public async Task FirstTest()
|
||||
{
|
||||
{
|
||||
await Assert.ThrowsAsync<InvalidOperationException>(async () => await Enumerable.Empty<int>().ToUniTaskAsyncEnumerable().FirstAsync());
|
||||
Assert.Throws<InvalidOperationException>(() => Enumerable.Empty<int>().First());
|
||||
|
||||
var x = await new[] { 99 }.ToUniTaskAsyncEnumerable().FirstAsync();
|
||||
var y = new[] { 99 }.First();
|
||||
x.Should().Be(y);
|
||||
}
|
||||
{
|
||||
var array = new[] { 99, 11, 135, 10, 144, 800 };
|
||||
await Assert.ThrowsAsync<InvalidOperationException>(async () => await array.ToUniTaskAsyncEnumerable().FirstAsync(x => x % 98 == 0));
|
||||
Assert.Throws<InvalidOperationException>(() => array.First(x => x % 98 == 0));
|
||||
|
||||
var x = await array.ToUniTaskAsyncEnumerable().FirstAsync(x => x % 2 == 0);
|
||||
var y = array.First(x => x % 2 == 0);
|
||||
x.Should().Be(y);
|
||||
}
|
||||
|
||||
{
|
||||
var x = await Enumerable.Empty<int>().ToUniTaskAsyncEnumerable().FirstOrDefaultAsync();
|
||||
var y = Enumerable.Empty<int>().FirstOrDefault();
|
||||
x.Should().Be(y);
|
||||
}
|
||||
{
|
||||
var x = await new[] { 99 }.ToUniTaskAsyncEnumerable().FirstOrDefaultAsync();
|
||||
var y = new[] { 99 }.FirstOrDefault();
|
||||
x.Should().Be(y);
|
||||
}
|
||||
{
|
||||
var array = new[] { 99, 11, 135, 10, 144, 800 };
|
||||
var x = await array.ToUniTaskAsyncEnumerable().FirstOrDefaultAsync(x => x % 98 == 0);
|
||||
var y = array.FirstOrDefault(x => x % 98 == 0);
|
||||
x.Should().Be(y);
|
||||
}
|
||||
{
|
||||
var array = new[] { 99, 11, 135, 10, 144, 800 };
|
||||
var x = await array.ToUniTaskAsyncEnumerable().FirstAsync(x => x % 2 == 0);
|
||||
var y = array.FirstOrDefault(x => x % 2 == 0);
|
||||
x.Should().Be(y);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task LastTest()
|
||||
{
|
||||
{
|
||||
await Assert.ThrowsAsync<InvalidOperationException>(async () => await Enumerable.Empty<int>().ToUniTaskAsyncEnumerable().LastAsync());
|
||||
Assert.Throws<InvalidOperationException>(() => Enumerable.Empty<int>().Last());
|
||||
|
||||
var x = await new[] { 99 }.ToUniTaskAsyncEnumerable().LastAsync();
|
||||
var y = new[] { 99 }.Last();
|
||||
x.Should().Be(y);
|
||||
}
|
||||
{
|
||||
var array = new[] { 99, 11, 135, 10, 144, 800 };
|
||||
await Assert.ThrowsAsync<InvalidOperationException>(async () => await array.ToUniTaskAsyncEnumerable().LastAsync(x => x % 98 == 0));
|
||||
Assert.Throws<InvalidOperationException>(() => array.Last(x => x % 98 == 0));
|
||||
|
||||
var x = await array.ToUniTaskAsyncEnumerable().LastAsync(x => x % 2 == 0);
|
||||
var y = array.Last(x => x % 2 == 0);
|
||||
x.Should().Be(y);
|
||||
}
|
||||
|
||||
{
|
||||
var x = await Enumerable.Empty<int>().ToUniTaskAsyncEnumerable().LastOrDefaultAsync();
|
||||
var y = Enumerable.Empty<int>().LastOrDefault();
|
||||
x.Should().Be(y);
|
||||
}
|
||||
{
|
||||
var x = await new[] { 99 }.ToUniTaskAsyncEnumerable().LastOrDefaultAsync();
|
||||
var y = new[] { 99 }.LastOrDefault();
|
||||
x.Should().Be(y);
|
||||
}
|
||||
{
|
||||
var array = new[] { 99, 11, 135, 10, 144, 800 };
|
||||
var x = await array.ToUniTaskAsyncEnumerable().LastOrDefaultAsync(x => x % 98 == 0);
|
||||
var y = array.LastOrDefault(x => x % 98 == 0);
|
||||
x.Should().Be(y);
|
||||
}
|
||||
{
|
||||
var array = new[] { 99, 11, 135, 10, 144, 800 };
|
||||
var x = await array.ToUniTaskAsyncEnumerable().LastOrDefaultAsync(x => x % 2 == 0);
|
||||
var y = array.LastOrDefault(x => x % 2 == 0);
|
||||
x.Should().Be(y);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SingleTest()
|
||||
{
|
||||
{
|
||||
await Assert.ThrowsAsync<InvalidOperationException>(async () => await Enumerable.Empty<int>().ToUniTaskAsyncEnumerable().SingleAsync());
|
||||
Assert.Throws<InvalidOperationException>(() => Enumerable.Empty<int>().Single());
|
||||
|
||||
var x = await new[] { 99 }.ToUniTaskAsyncEnumerable().SingleAsync();
|
||||
var y = new[] { 99 }.Single();
|
||||
x.Should().Be(y);
|
||||
|
||||
var array = new[] { 99, 11, 135, 10, 144, 800 };
|
||||
await Assert.ThrowsAsync<InvalidOperationException>(async () => await array.ToUniTaskAsyncEnumerable().SingleAsync());
|
||||
Assert.Throws<InvalidOperationException>(() => array.Single());
|
||||
}
|
||||
{
|
||||
var array = new[] { 99, 11, 135, 10, 144, 800 };
|
||||
// not found
|
||||
await Assert.ThrowsAsync<InvalidOperationException>(async () => await array.ToUniTaskAsyncEnumerable().SingleAsync(x => x % 999 == 0));
|
||||
Assert.Throws<InvalidOperationException>(() => array.Single(x => x % 999 == 0));
|
||||
// found multi
|
||||
await Assert.ThrowsAsync<InvalidOperationException>(async () => await array.ToUniTaskAsyncEnumerable().SingleAsync(x => x % 2 == 0));
|
||||
Assert.Throws<InvalidOperationException>(() => array.Single(x => x % 2 == 0));
|
||||
|
||||
{
|
||||
var x = await array.ToUniTaskAsyncEnumerable().SingleAsync(x => x % 144 == 0);
|
||||
var y = array.Single(x => x % 144 == 0);
|
||||
x.Should().Be(y);
|
||||
}
|
||||
{
|
||||
var x = await array.ToUniTaskAsyncEnumerable().SingleAsync(x => x % 800 == 0);
|
||||
var y = array.Single(x => x % 800 == 0);
|
||||
x.Should().Be(y);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
{
|
||||
var x = await Enumerable.Empty<int>().ToUniTaskAsyncEnumerable().SingleOrDefaultAsync();
|
||||
var y = Enumerable.Empty<int>().SingleOrDefault();
|
||||
x.Should().Be(y);
|
||||
}
|
||||
{
|
||||
var x = await new[] { 99 }.ToUniTaskAsyncEnumerable().SingleOrDefaultAsync();
|
||||
var y = new[] { 99 }.SingleOrDefault();
|
||||
x.Should().Be(y);
|
||||
|
||||
var array = new[] { 99, 11, 135, 10, 144, 800 };
|
||||
await Assert.ThrowsAsync<InvalidOperationException>(async () => await array.ToUniTaskAsyncEnumerable().SingleOrDefaultAsync());
|
||||
Assert.Throws<InvalidOperationException>(() => array.SingleOrDefault());
|
||||
}
|
||||
{
|
||||
var array = new[] { 99, 11, 135, 10, 144, 800 };
|
||||
// not found
|
||||
{
|
||||
var x = await array.ToUniTaskAsyncEnumerable().SingleOrDefaultAsync(x => x % 999 == 0);
|
||||
var y = array.SingleOrDefault(x => x % 999 == 0);
|
||||
x.Should().Be(y);
|
||||
}
|
||||
// found multi
|
||||
await Assert.ThrowsAsync<InvalidOperationException>(async () => await array.ToUniTaskAsyncEnumerable().SingleOrDefaultAsync(x => x % 2 == 0));
|
||||
Assert.Throws<InvalidOperationException>(() => array.SingleOrDefault(x => x % 2 == 0));
|
||||
|
||||
// normal
|
||||
{
|
||||
var x = await array.ToUniTaskAsyncEnumerable().SingleOrDefaultAsync(x => x % 144 == 0);
|
||||
var y = array.SingleOrDefault(x => x % 144 == 0);
|
||||
x.Should().Be(y);
|
||||
}
|
||||
{
|
||||
var x = await array.ToUniTaskAsyncEnumerable().SingleOrDefaultAsync(x => x % 800 == 0);
|
||||
var y = array.SingleOrDefault(x => x % 800 == 0);
|
||||
x.Should().Be(y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task ElementAtTest()
|
||||
{
|
||||
{
|
||||
await Assert.ThrowsAsync<ArgumentOutOfRangeException>(async () => await Enumerable.Empty<int>().ToUniTaskAsyncEnumerable().ElementAtAsync(0));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => Enumerable.Empty<int>().ElementAt(0));
|
||||
|
||||
var x = await new[] { 99 }.ToUniTaskAsyncEnumerable().ElementAtAsync(0);
|
||||
var y = new[] { 99 }.ElementAt(0);
|
||||
x.Should().Be(y);
|
||||
}
|
||||
{
|
||||
var array = new[] { 99, 11, 135, 10, 144, 800 };
|
||||
await Assert.ThrowsAsync<ArgumentOutOfRangeException>(async () => await array.ToUniTaskAsyncEnumerable().ElementAtAsync(10));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => array.ElementAt(10));
|
||||
|
||||
{
|
||||
var x = await array.ToUniTaskAsyncEnumerable().ElementAtAsync(0);
|
||||
var y = array.ElementAt(0);
|
||||
x.Should().Be(y);
|
||||
}
|
||||
{
|
||||
var x = await array.ToUniTaskAsyncEnumerable().ElementAtAsync(3);
|
||||
var y = array.ElementAt(3);
|
||||
x.Should().Be(y);
|
||||
}
|
||||
{
|
||||
var x = await array.ToUniTaskAsyncEnumerable().ElementAtAsync(5);
|
||||
var y = array.ElementAt(5);
|
||||
x.Should().Be(y);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
{
|
||||
var x = await Enumerable.Empty<int>().ToUniTaskAsyncEnumerable().ElementAtOrDefaultAsync(0);
|
||||
var y = Enumerable.Empty<int>().ElementAtOrDefault(0);
|
||||
x.Should().Be(y);
|
||||
}
|
||||
{
|
||||
var x = await new[] { 99 }.ToUniTaskAsyncEnumerable().ElementAtOrDefaultAsync(0);
|
||||
var y = new[] { 99 }.ElementAtOrDefault(0);
|
||||
x.Should().Be(y);
|
||||
}
|
||||
}
|
||||
{
|
||||
var array = new[] { 99, 11, 135, 10, 144, 800 };
|
||||
{
|
||||
var x = await array.ToUniTaskAsyncEnumerable().ElementAtOrDefaultAsync(10);
|
||||
var y = array.ElementAtOrDefault(10);
|
||||
x.Should().Be(y);
|
||||
}
|
||||
{
|
||||
var x = await array.ToUniTaskAsyncEnumerable().ElementAtOrDefaultAsync(0);
|
||||
var y = array.ElementAtOrDefault(0);
|
||||
x.Should().Be(y);
|
||||
}
|
||||
{
|
||||
var x = await array.ToUniTaskAsyncEnumerable().ElementAtOrDefaultAsync(3);
|
||||
var y = array.ElementAtOrDefault(3);
|
||||
x.Should().Be(y);
|
||||
}
|
||||
{
|
||||
var x = await array.ToUniTaskAsyncEnumerable().ElementAtOrDefaultAsync(5);
|
||||
var y = array.ElementAtOrDefault(5);
|
||||
x.Should().Be(y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
253
src/UniTask.NetCoreTests/Linq/Joins.cs
Normal file
253
src/UniTask.NetCoreTests/Linq/Joins.cs
Normal file
@@ -0,0 +1,253 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Cysharp.Threading.Tasks.Linq;
|
||||
using FluentAssertions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Concurrency;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
|
||||
namespace NetCoreTests.Linq
|
||||
{
|
||||
public class Joins
|
||||
{
|
||||
static int rd;
|
||||
|
||||
static UniTask<T> RandomRun<T>(T value)
|
||||
{
|
||||
if (Interlocked.Increment(ref rd) % 2 == 0)
|
||||
{
|
||||
return UniTask.Run(() => value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return UniTask.FromResult(value);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Join()
|
||||
{
|
||||
var outer = new[] { 1, 2, 4, 5, 8, 10, 14, 4, 8, 1, 2, 10 };
|
||||
var inner = new[] { 1, 2, 1, 2, 1, 14, 2 };
|
||||
|
||||
var xs = await outer.ToUniTaskAsyncEnumerable().Join(inner.ToUniTaskAsyncEnumerable(), x => x, x => x, (x, y) => (x, y)).ToArrayAsync();
|
||||
var ys = outer.Join(inner, x => x, x => x, (x, y) => (x, y)).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task JoinThrow()
|
||||
{
|
||||
var outer = new[] { 1, 2, 4, 5, 8, 10, 14, 4, 8, 1, 2, 10 };
|
||||
var inner = new[] { 1, 2, 1, 2, 1, 14, 2 };
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = outer.ToUniTaskAsyncEnumerable().Join(item, x => x, x => x, (x, y) => x + y).ToArrayAsync();
|
||||
var ys = item.Join(inner.ToUniTaskAsyncEnumerable(), x => x, x => x, (x, y) => x + y).ToArrayAsync();
|
||||
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await ys);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task JoinAwait()
|
||||
{
|
||||
var outer = new[] { 1, 2, 4, 5, 8, 10, 14, 4, 8, 1, 2, 10 };
|
||||
var inner = new[] { 1, 2, 1, 2, 1, 14, 2 };
|
||||
|
||||
var xs = await outer.ToUniTaskAsyncEnumerable().JoinAwait(inner.ToUniTaskAsyncEnumerable(), x => RandomRun(x), x => RandomRun(x), (x, y) => RandomRun((x, y))).ToArrayAsync();
|
||||
var ys = outer.Join(inner, x => x, x => x, (x, y) => (x, y)).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task JoinAwaitThrow()
|
||||
{
|
||||
var outer = new[] { 1, 2, 4, 5, 8, 10, 14, 4, 8, 1, 2, 10 };
|
||||
var inner = new[] { 1, 2, 1, 2, 1, 14, 2 };
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = outer.ToUniTaskAsyncEnumerable().JoinAwait(item, x => RandomRun(x), x => RandomRun(x), (x, y) => RandomRun(x + y)).ToArrayAsync();
|
||||
var ys = item.Join(inner.ToUniTaskAsyncEnumerable(), x => x, x => x, (x, y) => x + y).ToArrayAsync();
|
||||
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await ys);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task JoinAwaitCt()
|
||||
{
|
||||
var outer = new[] { 1, 2, 4, 5, 8, 10, 14, 4, 8, 1, 2, 10 };
|
||||
var inner = new[] { 1, 2, 1, 2, 1, 14, 2 };
|
||||
|
||||
var xs = await outer.ToUniTaskAsyncEnumerable().JoinAwaitWithCancellation(inner.ToUniTaskAsyncEnumerable(), (x, _) => RandomRun(x), (x, _) => RandomRun(x), (x, y, _) => RandomRun((x, y))).ToArrayAsync();
|
||||
var ys = outer.Join(inner, x => x, x => x, (x, y) => (x, y)).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task JoinAwaitCtThrow()
|
||||
{
|
||||
var outer = new[] { 1, 2, 4, 5, 8, 10, 14, 4, 8, 1, 2, 10 };
|
||||
var inner = new[] { 1, 2, 1, 2, 1, 14, 2 };
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = outer.ToUniTaskAsyncEnumerable().JoinAwaitWithCancellation(item, (x, _) => RandomRun(x), (x, _) => RandomRun(x), (x, y, _) => RandomRun(x + y)).ToArrayAsync();
|
||||
var ys = item.JoinAwaitWithCancellation(inner.ToUniTaskAsyncEnumerable(), (x, _) => RandomRun(x), (x, _) => RandomRun(x), (x, y, _) => RandomRun(x + y)).ToArrayAsync();
|
||||
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await ys);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task GroupBy()
|
||||
{
|
||||
var arr = new[] { 1, 4, 10, 10, 4, 5, 10, 9 };
|
||||
{
|
||||
var xs = await arr.ToUniTaskAsyncEnumerable().GroupBy(x => x).ToArrayAsync();
|
||||
var ys = arr.GroupBy(x => x).ToArray();
|
||||
|
||||
xs.Length.Should().Be(ys.Length);
|
||||
xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key));
|
||||
}
|
||||
|
||||
{
|
||||
var xs = await arr.ToUniTaskAsyncEnumerable().GroupBy(x => x, (key, xs) => (key, xs.ToArray())).ToArrayAsync();
|
||||
var ys = arr.GroupBy(x => x, (key, xs) => (key, xs.ToArray())).ToArray();
|
||||
|
||||
xs.Length.Should().Be(ys.Length);
|
||||
xs.OrderBy(x => x.key).SelectMany(x => x.Item2).Should().BeEquivalentTo(ys.OrderBy(x => x.key).SelectMany(x => x.Item2));
|
||||
}
|
||||
|
||||
{
|
||||
var xs = await arr.ToUniTaskAsyncEnumerable().GroupByAwait(x => RandomRun(x)).ToArrayAsync();
|
||||
var ys = arr.GroupBy(x => x).ToArray();
|
||||
|
||||
xs.Length.Should().Be(ys.Length);
|
||||
xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key));
|
||||
}
|
||||
|
||||
{
|
||||
var xs = await arr.ToUniTaskAsyncEnumerable().GroupByAwait(x => RandomRun(x), (key, xs) => RandomRun((key, xs.ToArray()))).ToArrayAsync();
|
||||
var ys = arr.GroupBy(x => x, (key, xs) => (key, xs.ToArray())).ToArray();
|
||||
|
||||
xs.Length.Should().Be(ys.Length);
|
||||
xs.OrderBy(x => x.key).SelectMany(x => x.Item2).Should().BeEquivalentTo(ys.OrderBy(x => x.key).SelectMany(x => x.Item2));
|
||||
}
|
||||
|
||||
{
|
||||
var xs = await arr.ToUniTaskAsyncEnumerable().GroupByAwaitWithCancellation((x, _) => RandomRun(x)).ToArrayAsync();
|
||||
var ys = arr.GroupBy(x => x).ToArray();
|
||||
|
||||
xs.Length.Should().Be(ys.Length);
|
||||
xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key));
|
||||
}
|
||||
|
||||
{
|
||||
var xs = await arr.ToUniTaskAsyncEnumerable().GroupByAwaitWithCancellation((x, _) => RandomRun(x), (key, xs, _) => RandomRun((key, xs.ToArray()))).ToArrayAsync();
|
||||
var ys = arr.GroupBy(x => x, (key, xs) => (key, xs.ToArray())).ToArray();
|
||||
|
||||
xs.Length.Should().Be(ys.Length);
|
||||
xs.OrderBy(x => x.key).SelectMany(x => x.Item2).Should().BeEquivalentTo(ys.OrderBy(x => x.key).SelectMany(x => x.Item2));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task GroupByThrow()
|
||||
{
|
||||
var arr = new[] { 1, 4, 10, 10, 4, 5, 10, 9 };
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = item.GroupBy(x => x).ToArrayAsync();
|
||||
var ys = item.GroupByAwait(x => RandomRun(x)).ToArrayAsync();
|
||||
var zs = item.GroupByAwaitWithCancellation((x, _) => RandomRun(x)).ToArrayAsync();
|
||||
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await ys);
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await zs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task GroupJoin()
|
||||
{
|
||||
var outer = new[] { 1, 2, 4, 5, 8, 10, 14, 4, 8, 1, 2, 10 };
|
||||
var inner = new[] { 1, 2, 1, 2, 1, 14, 2 };
|
||||
|
||||
{
|
||||
var xs = await outer.ToUniTaskAsyncEnumerable().GroupJoin(inner.ToUniTaskAsyncEnumerable(), x => x, x => x, (x, y) => (x, string.Join(", ", y))).ToArrayAsync();
|
||||
var ys = outer.GroupJoin(inner, x => x, x => x, (x, y) => (x, string.Join(", ", y))).ToArray();
|
||||
|
||||
xs.Length.Should().Be(ys.Length);
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
{
|
||||
var xs = await outer.ToUniTaskAsyncEnumerable().GroupJoinAwait(inner.ToUniTaskAsyncEnumerable(), x => RandomRun(x), x => RandomRun(x), (x, y) => RandomRun((x, string.Join(", ", y)))).ToArrayAsync();
|
||||
var ys = outer.GroupJoin(inner, x => x, x => x, (x, y) => (x, string.Join(", ", y))).ToArray();
|
||||
|
||||
xs.Length.Should().Be(ys.Length);
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
{
|
||||
var xs = await outer.ToUniTaskAsyncEnumerable().GroupJoinAwaitWithCancellation(inner.ToUniTaskAsyncEnumerable(), (x, _) => RandomRun(x), (x, _) => RandomRun(x), (x, y, _) => RandomRun((x, string.Join(", ", y)))).ToArrayAsync();
|
||||
var ys = outer.GroupJoin(inner, x => x, x => x, (x, y) => (x, string.Join(", ", y))).ToArray();
|
||||
|
||||
xs.Length.Should().Be(ys.Length);
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task GroupJoinThrow()
|
||||
{
|
||||
|
||||
var outer = new[] { 1, 2, 4, 5, 8, 10, 14, 4, 8, 1, 2, 10 }.ToUniTaskAsyncEnumerable();
|
||||
var inner = new[] { 1, 2, 1, 2, 1, 14, 2 }.ToUniTaskAsyncEnumerable();
|
||||
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
{
|
||||
var xs = item.GroupJoin(outer, x => x, x => x, (x, y) => x).ToArrayAsync();
|
||||
var ys = inner.GroupJoin(item, x => x, x => x, (x, y) => x).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await ys);
|
||||
}
|
||||
{
|
||||
var xs = item.GroupJoinAwait(outer, x => RandomRun(x), x => RandomRun(x), (x, y) => RandomRun(x)).ToArrayAsync();
|
||||
var ys = inner.GroupJoinAwait(item, x => RandomRun(x), x => RandomRun(x), (x, y) => RandomRun(x)).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await ys);
|
||||
}
|
||||
{
|
||||
var xs = item.GroupJoinAwaitWithCancellation(outer, (x, _) => RandomRun(x), (x, _) => RandomRun(x), (x, y, _) => RandomRun(x)).ToArrayAsync();
|
||||
var ys = inner.GroupJoinAwaitWithCancellation(item, (x, _) => RandomRun(x), (x, _) => RandomRun(x), (x, y, _) => RandomRun(x)).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await ys);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
285
src/UniTask.NetCoreTests/Linq/Paging.cs
Normal file
285
src/UniTask.NetCoreTests/Linq/Paging.cs
Normal file
@@ -0,0 +1,285 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Cysharp.Threading.Tasks.Linq;
|
||||
using FluentAssertions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Concurrency;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
|
||||
namespace NetCoreTests.Linq
|
||||
{
|
||||
public class Paging
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(0, 0)]
|
||||
[InlineData(0, 1)]
|
||||
[InlineData(9, 0)]
|
||||
[InlineData(9, 1)]
|
||||
[InlineData(9, 5)]
|
||||
[InlineData(9, 9)]
|
||||
[InlineData(9, 15)]
|
||||
public async Task Skip(int collection, int skipCount)
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).Skip(skipCount).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, collection).Skip(skipCount).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SkipException()
|
||||
{
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = item.Skip(5).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
}
|
||||
[Theory]
|
||||
[InlineData(0, 0)]
|
||||
[InlineData(0, 1)]
|
||||
[InlineData(9, 0)]
|
||||
[InlineData(9, 1)]
|
||||
[InlineData(9, 5)]
|
||||
[InlineData(9, 9)]
|
||||
[InlineData(9, 15)]
|
||||
public async Task SkipLast(int collection, int skipCount)
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipLast(skipCount).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, collection).SkipLast(skipCount).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SkipLastException()
|
||||
{
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = item.SkipLast(5).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
}
|
||||
[Theory]
|
||||
[InlineData(0, 0)]
|
||||
[InlineData(0, 1)]
|
||||
[InlineData(9, 0)]
|
||||
[InlineData(9, 1)]
|
||||
[InlineData(9, 5)]
|
||||
[InlineData(9, 9)]
|
||||
[InlineData(9, 15)]
|
||||
public async Task TakeLast(int collection, int takeCount)
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeLast(takeCount).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, collection).TakeLast(takeCount).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TakeLastException()
|
||||
{
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = item.TakeLast(5).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, 0)]
|
||||
[InlineData(0, 1)]
|
||||
[InlineData(9, 0)]
|
||||
[InlineData(9, 1)]
|
||||
[InlineData(9, 5)]
|
||||
[InlineData(9, 9)]
|
||||
[InlineData(9, 15)]
|
||||
public async Task Take(int collection, int takeCount)
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).Take(takeCount).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, collection).Take(takeCount).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TakeException()
|
||||
{
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = item.Take(5).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, 0)]
|
||||
[InlineData(0, 1)]
|
||||
[InlineData(9, 0)]
|
||||
[InlineData(9, 1)]
|
||||
[InlineData(9, 5)]
|
||||
[InlineData(9, 9)]
|
||||
[InlineData(9, 15)]
|
||||
public async Task SkipWhile(int collection, int skipCount)
|
||||
{
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipWhile(x => x < skipCount).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, collection).SkipWhile(x => x < skipCount).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipWhile((x, i) => x < (skipCount - i)).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, collection).SkipWhile((x, i) => x < (skipCount - i)).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipWhileAwait(x => UniTask.Run(() => x < skipCount)).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, collection).SkipWhile(x => x < skipCount).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipWhileAwait((x, i) => UniTask.Run(() => x < (skipCount - i))).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, collection).SkipWhile((x, i) => x < (skipCount - i)).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipWhileAwaitWithCancellation((x, _) => UniTask.Run(() => x < skipCount)).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, collection).SkipWhile(x => x < skipCount).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipWhileAwaitWithCancellation((x, i, _) => UniTask.Run(() => x < (skipCount - i))).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, collection).SkipWhile((x, i) => x < (skipCount - i)).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SkipWhileException()
|
||||
{
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = item.SkipWhile(x => x < 2).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = item.SkipWhile((x, i) => x < 2).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = item.SkipWhileAwait((x) => UniTask.Run(() => x < 2)).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = item.SkipWhileAwait((x, i) => UniTask.Run(() => x < 2)).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = item.SkipWhileAwaitWithCancellation((x, _) => UniTask.Run(() => x < 2)).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = item.SkipWhileAwaitWithCancellation((x, i, _) => UniTask.Run(() => x < 2)).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, 0)]
|
||||
[InlineData(0, 1)]
|
||||
[InlineData(9, 0)]
|
||||
[InlineData(9, 1)]
|
||||
[InlineData(9, 5)]
|
||||
[InlineData(9, 9)]
|
||||
[InlineData(9, 15)]
|
||||
public async Task TakeWhile(int collection, int skipCount)
|
||||
{
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeWhile(x => x < skipCount).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, collection).TakeWhile(x => x < skipCount).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeWhile((x, i) => x < (skipCount - i)).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, collection).TakeWhile((x, i) => x < (skipCount - i)).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeWhileAwait(x => UniTask.Run(() => x < skipCount)).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, collection).TakeWhile(x => x < skipCount).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeWhileAwait((x, i) => UniTask.Run(() => x < (skipCount - i))).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, collection).TakeWhile((x, i) => x < (skipCount - i)).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeWhileAwaitWithCancellation((x, _) => UniTask.Run(() => x < skipCount)).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, collection).TakeWhile(x => x < skipCount).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeWhileAwaitWithCancellation((x, i, _) => UniTask.Run(() => x < (skipCount - i))).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, collection).TakeWhile((x, i) => x < (skipCount - i)).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TakeWhileException()
|
||||
{
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = item.TakeWhile(x => x < 5).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = item.TakeWhile((x, i) => x < 5).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = item.TakeWhileAwait((x) => UniTask.Run(() => x < 5)).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = item.TakeWhileAwait((x, i) => UniTask.Run(() => x < 5)).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = item.TakeWhileAwaitWithCancellation((x, _) => UniTask.Run(() => x < 5)).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = item.TakeWhileAwaitWithCancellation((x, i, _) => UniTask.Run(() => x < 5)).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
437
src/UniTask.NetCoreTests/Linq/Projection.cs
Normal file
437
src/UniTask.NetCoreTests/Linq/Projection.cs
Normal file
@@ -0,0 +1,437 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Cysharp.Threading.Tasks.Linq;
|
||||
using FluentAssertions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Concurrency;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
|
||||
namespace NetCoreTests.Linq
|
||||
{
|
||||
public class Projection
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(0, 0)]
|
||||
[InlineData(0, 1)]
|
||||
[InlineData(0, 2)]
|
||||
[InlineData(0, 10)]
|
||||
public async Task Reverse(int start, int count)
|
||||
{
|
||||
var xs = await Enumerable.Range(start, count).ToUniTaskAsyncEnumerable().Reverse().ToArrayAsync();
|
||||
var ys = Enumerable.Range(start, count).Reverse().ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ReverseException()
|
||||
{
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = item.Reverse().ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0)]
|
||||
[InlineData(1)]
|
||||
[InlineData(9)]
|
||||
public async Task Select(int count)
|
||||
{
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, count).Select(x => x * x).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, count).Select(x => x * x).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
|
||||
var zs = await UniTaskAsyncEnumerable.Range(1, count).SelectAwait((x) => UniTask.Run(() => x * x)).ToArrayAsync();
|
||||
zs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, count).Select((x, i) => x * x * i).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, count).Select((x, i) => x * x * i).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
|
||||
var zs = await UniTaskAsyncEnumerable.Range(1, count).SelectAwait((x, i) => UniTask.Run(() => x * x * i)).ToArrayAsync();
|
||||
zs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task SelectException()
|
||||
{
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = item.Select(x => UniTaskAsyncEnumerable.Range(0, 1)).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
|
||||
// await
|
||||
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = item.SelectAwait(x => UniTask.Run(() => UniTaskAsyncEnumerable.Range(0, 1))).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
|
||||
// cancel
|
||||
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = item.SelectAwaitWithCancellation((x, _) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(0, 1))).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, 9)] // empty + exists
|
||||
[InlineData(9, 0)] // exists + empty
|
||||
[InlineData(9, 9)] // exists + exists
|
||||
public async Task SelectMany(int leftCount, int rightCount)
|
||||
{
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectMany(x => UniTaskAsyncEnumerable.Range(99, rightCount * x)).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, leftCount).SelectMany(x => Enumerable.Range(99, rightCount * x)).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectMany((i, x) => UniTaskAsyncEnumerable.Range(99 * i, rightCount * x)).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, leftCount).SelectMany((i, x) => Enumerable.Range(99 * i, rightCount * x)).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectMany(x => UniTaskAsyncEnumerable.Range(99, rightCount * x), (x, y) => x * y).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, leftCount).SelectMany(x => Enumerable.Range(99, rightCount * x), (x, y) => x * y).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectMany((i, x) => UniTaskAsyncEnumerable.Range(99 * i, rightCount * x), (x, y) => x * y).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, leftCount).SelectMany((i, x) => Enumerable.Range(99 * i, rightCount * x), (x, y) => x * y).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
|
||||
// await
|
||||
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwait(x => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99, rightCount * x))).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, leftCount).SelectMany(x => Enumerable.Range(99, rightCount * x)).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwait((i, x) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99 * i, rightCount * x))).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, leftCount).SelectMany((i, x) => Enumerable.Range(99 * i, rightCount * x)).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwait(x => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99, rightCount * x)), (x, y) => UniTask.Run(() => x * y)).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, leftCount).SelectMany(x => Enumerable.Range(99, rightCount * x), (x, y) => x * y).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwait((i, x) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99 * i, rightCount * x)), (x, y) => UniTask.Run(() => x * y)).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, leftCount).SelectMany((i, x) => Enumerable.Range(99 * i, rightCount * x), (x, y) => x * y).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
|
||||
// with cancel
|
||||
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwaitWithCancellation((x, _) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99, rightCount * x))).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, leftCount).SelectMany(x => Enumerable.Range(99, rightCount * x)).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwaitWithCancellation((i, x, _) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99 * i, rightCount * x))).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, leftCount).SelectMany((i, x) => Enumerable.Range(99 * i, rightCount * x)).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwaitWithCancellation((x, _) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99, rightCount * x)), (x, y, _) => UniTask.Run(() => x * y)).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, leftCount).SelectMany(x => Enumerable.Range(99, rightCount * x), (x, y) => x * y).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwaitWithCancellation((i, x, _) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99 * i, rightCount * x)), (x, y, _) => UniTask.Run(() => x * y)).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, leftCount).SelectMany((i, x) => Enumerable.Range(99 * i, rightCount * x), (x, y) => x * y).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SelectManyException()
|
||||
{
|
||||
// error + exists
|
||||
// exists + error
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = item.SelectMany(x => UniTaskAsyncEnumerable.Range(0, 1)).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = UniTaskAsyncEnumerable.Range(0, 1).SelectMany(x => item).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
|
||||
// await
|
||||
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = item.SelectManyAwait(x => UniTask.Run(() => UniTaskAsyncEnumerable.Range(0, 1))).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = UniTaskAsyncEnumerable.Range(0, 1).SelectManyAwait(x => UniTask.Run(() => item)).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
|
||||
// with c
|
||||
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = item.SelectManyAwaitWithCancellation((x, _) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(0, 1))).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = UniTaskAsyncEnumerable.Range(0, 1).SelectManyAwaitWithCancellation((x, _) => UniTask.Run(() => item)).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, 9)] // empty + exists
|
||||
[InlineData(9, 0)] // exists + empty
|
||||
[InlineData(9, 9)] // same
|
||||
[InlineData(9, 4)] // leftlong
|
||||
[InlineData(4, 9)] // rightlong
|
||||
public async Task Zip(int leftCount, int rightCount)
|
||||
{
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).Zip(UniTaskAsyncEnumerable.Range(99, rightCount)).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, leftCount).Zip(Enumerable.Range(99, rightCount)).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).ZipAwait(UniTaskAsyncEnumerable.Range(99, rightCount), (x, y) => UniTask.Run(() => (x, y))).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, leftCount).Zip(Enumerable.Range(99, rightCount)).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).ZipAwaitWithCancellation(UniTaskAsyncEnumerable.Range(99, rightCount), (x, y, _) => UniTask.Run(() => (x, y))).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, leftCount).Zip(Enumerable.Range(99, rightCount)).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
}
|
||||
[Fact]
|
||||
public async Task ZipException()
|
||||
{
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = item.Zip(UniTaskAsyncEnumerable.Range(1, 10)).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = UniTaskAsyncEnumerable.Range(1, 10).Zip(item).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
|
||||
// a
|
||||
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = item.ZipAwait(UniTaskAsyncEnumerable.Range(1, 10), (x, y) => UniTask.Run(() => (x, y))).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = UniTaskAsyncEnumerable.Range(1, 10).ZipAwait(item, (x, y) => UniTask.Run(() => (x, y))).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
|
||||
// c
|
||||
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = item.ZipAwaitWithCancellation(UniTaskAsyncEnumerable.Range(1, 10), (x, y, c) => UniTask.Run(() => (x, y))).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = UniTaskAsyncEnumerable.Range(1, 10).ZipAwaitWithCancellation(item, (x, y, c) => UniTask.Run(() => (x, y))).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
// [InlineData(0, 0)]
|
||||
[InlineData(0, 3)]
|
||||
[InlineData(9, 1)]
|
||||
[InlineData(9, 2)]
|
||||
[InlineData(9, 3)]
|
||||
[InlineData(17, 3)]
|
||||
[InlineData(17, 16)]
|
||||
[InlineData(17, 17)]
|
||||
[InlineData(17, 27)]
|
||||
public async Task Buffer(int rangeCount, int bufferCount)
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(0, rangeCount).Buffer(bufferCount).Select(x => string.Join(",", x)).ToArrayAsync();
|
||||
var ys = await AsyncEnumerable.Range(0, rangeCount).Buffer(bufferCount).Select(x => string.Join(",", x)).ToArrayAsync();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
// [InlineData(0, 0)]
|
||||
[InlineData(0, 3, 2)]
|
||||
[InlineData(9, 1, 1)]
|
||||
[InlineData(9, 2, 3)]
|
||||
[InlineData(9, 3, 4)]
|
||||
[InlineData(17, 3, 3)]
|
||||
[InlineData(17, 16, 5)]
|
||||
[InlineData(17, 17, 19)]
|
||||
public async Task BufferSkip(int rangeCount, int bufferCount, int skipCount)
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(0, rangeCount).Buffer(bufferCount, skipCount).Select(x => string.Join(",", x)).ToArrayAsync();
|
||||
var ys = await AsyncEnumerable.Range(0, rangeCount).Buffer(bufferCount, skipCount).Select(x => string.Join(",", x)).ToArrayAsync();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task BufferError()
|
||||
{
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = item.Buffer(3).ToArrayAsync();
|
||||
var ys = item.Buffer(3, 2).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await ys);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CombineLatestOK()
|
||||
{
|
||||
var a = new AsyncReactiveProperty<int>(0);
|
||||
var b = new AsyncReactiveProperty<int>(0);
|
||||
|
||||
var list = new List<(int, int)>();
|
||||
var complete = a.WithoutCurrent().CombineLatest(b.WithoutCurrent(), (x, y) => (x, y)).ForEachAsync(x => list.Add(x));
|
||||
|
||||
list.Count.Should().Be(0);
|
||||
|
||||
a.Value = 10;
|
||||
list.Count.Should().Be(0);
|
||||
|
||||
a.Value = 20;
|
||||
list.Count.Should().Be(0);
|
||||
|
||||
b.Value = 1;
|
||||
list.Count.Should().Be(1);
|
||||
|
||||
list[0].Should().Be((20, 1));
|
||||
|
||||
a.Value = 30;
|
||||
list.Last().Should().Be((30, 1));
|
||||
|
||||
b.Value = 2;
|
||||
list.Last().Should().Be((30, 2));
|
||||
|
||||
a.Dispose();
|
||||
b.Value = 3;
|
||||
list.Last().Should().Be((30, 3));
|
||||
|
||||
b.Dispose();
|
||||
|
||||
await complete;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CombineLatestLong()
|
||||
{
|
||||
var a = UniTaskAsyncEnumerable.Range(1, 100000);
|
||||
var b = new AsyncReactiveProperty<int>(0);
|
||||
|
||||
var list = new List<(int, int)>();
|
||||
var complete = a.CombineLatest(b.WithoutCurrent(), (x, y) => (x, y)).ForEachAsync(x => list.Add(x));
|
||||
|
||||
b.Value = 1;
|
||||
|
||||
list[0].Should().Be((100000, 1));
|
||||
|
||||
b.Dispose();
|
||||
|
||||
await complete;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CombineLatestError()
|
||||
{
|
||||
var a = new AsyncReactiveProperty<int>(0);
|
||||
var b = new AsyncReactiveProperty<int>(0);
|
||||
|
||||
var list = new List<(int, int)>();
|
||||
var complete = a.WithoutCurrent()
|
||||
.Select(x => { if (x == 0) { throw new MyException(); } return x; })
|
||||
.CombineLatest(b.WithoutCurrent(), (x, y) => (x, y)).ForEachAsync(x => list.Add(x));
|
||||
|
||||
|
||||
a.Value = 10;
|
||||
b.Value = 1;
|
||||
list.Last().Should().Be((10, 1));
|
||||
|
||||
a.Value = 0;
|
||||
|
||||
await Assert.ThrowsAsync<MyException>(async () => await complete);
|
||||
}
|
||||
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task PariwiseImmediate()
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, 5).Pairwise().ToArrayAsync();
|
||||
xs.Should().BeEquivalentTo((1, 2), (2, 3), (3, 4), (4, 5));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Pariwise()
|
||||
{
|
||||
var a = new AsyncReactiveProperty<int>(0);
|
||||
|
||||
var list = new List<(int, int)>();
|
||||
var complete = a.WithoutCurrent().Pairwise().ForEachAsync(x => list.Add(x));
|
||||
|
||||
list.Count.Should().Be(0);
|
||||
a.Value = 10;
|
||||
list.Count.Should().Be(0);
|
||||
a.Value = 20;
|
||||
list.Count.Should().Be(1);
|
||||
a.Value = 30;
|
||||
a.Value = 40;
|
||||
a.Value = 50;
|
||||
|
||||
a.Dispose();
|
||||
|
||||
await complete;
|
||||
|
||||
list.Should().BeEquivalentTo((10, 20), (20, 30), (30, 40), (40, 50));
|
||||
}
|
||||
|
||||
class MyException : Exception
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
78
src/UniTask.NetCoreTests/Linq/PulbishTest.cs
Normal file
78
src/UniTask.NetCoreTests/Linq/PulbishTest.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Cysharp.Threading.Tasks.Linq;
|
||||
using FluentAssertions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace NetCoreTests.Linq
|
||||
{
|
||||
public class PublishTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task Normal()
|
||||
{
|
||||
var rp = new AsyncReactiveProperty<int>(1);
|
||||
|
||||
var multicast = rp.Publish();
|
||||
|
||||
var a = multicast.ToArrayAsync();
|
||||
var b = multicast.Take(2).ToArrayAsync();
|
||||
|
||||
var disp = multicast.Connect();
|
||||
|
||||
rp.Value = 2;
|
||||
|
||||
(await b).Should().BeEquivalentTo(1, 2);
|
||||
|
||||
var c = multicast.ToArrayAsync();
|
||||
|
||||
rp.Value = 3;
|
||||
rp.Value = 4;
|
||||
rp.Value = 5;
|
||||
|
||||
rp.Dispose();
|
||||
|
||||
(await a).Should().BeEquivalentTo(1, 2, 3, 4, 5);
|
||||
(await c).Should().BeEquivalentTo(3, 4, 5);
|
||||
|
||||
disp.Dispose();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Cancel()
|
||||
{
|
||||
var rp = new AsyncReactiveProperty<int>(1);
|
||||
|
||||
var multicast = rp.Publish();
|
||||
|
||||
var a = multicast.ToArrayAsync();
|
||||
var b = multicast.Take(2).ToArrayAsync();
|
||||
|
||||
var disp = multicast.Connect();
|
||||
|
||||
rp.Value = 2;
|
||||
|
||||
(await b).Should().BeEquivalentTo(1, 2);
|
||||
|
||||
var c = multicast.ToArrayAsync();
|
||||
|
||||
rp.Value = 3;
|
||||
|
||||
disp.Dispose();
|
||||
|
||||
rp.Value = 4;
|
||||
rp.Value = 5;
|
||||
|
||||
rp.Dispose();
|
||||
|
||||
await Assert.ThrowsAsync<OperationCanceledException>(async () => await a);
|
||||
await Assert.ThrowsAsync<OperationCanceledException>(async () => await c);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
29
src/UniTask.NetCoreTests/Linq/QueueTest.cs
Normal file
29
src/UniTask.NetCoreTests/Linq/QueueTest.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Cysharp.Threading.Tasks.Linq;
|
||||
using FluentAssertions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace NetCoreTests.Linq
|
||||
{
|
||||
public class QueueTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task Q()
|
||||
{
|
||||
var rp = new AsyncReactiveProperty<int>(100);
|
||||
|
||||
var l = new List<int>();
|
||||
await rp.Take(10).Queue().ForEachAsync(x =>
|
||||
{
|
||||
rp.Value += 10;
|
||||
l.Add(x);
|
||||
});
|
||||
|
||||
l.Should().BeEquivalentTo(100, 110, 120, 130, 140, 150, 160, 170, 180, 190);
|
||||
}
|
||||
}
|
||||
}
|
||||
193
src/UniTask.NetCoreTests/Linq/Sets.cs
Normal file
193
src/UniTask.NetCoreTests/Linq/Sets.cs
Normal file
@@ -0,0 +1,193 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Cysharp.Threading.Tasks.Linq;
|
||||
using FluentAssertions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Concurrency;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
|
||||
namespace NetCoreTests.Linq
|
||||
{
|
||||
public class Sets
|
||||
{
|
||||
public static IEnumerable<object[]> array1 = new object[][]
|
||||
{
|
||||
new object[] { new int[] { } }, // empty
|
||||
new object[] { new int[] { 1, 2, 3 } }, // no dup
|
||||
new object[] { new int[] { 1, 2, 3, 3, 4, 5, 2 } }, // dup
|
||||
};
|
||||
|
||||
public static IEnumerable<object[]> array2 = new object[][]
|
||||
{
|
||||
new object[] { new int[] { } }, // empty
|
||||
new object[] { new int[] { 1, 2 } },
|
||||
new object[] { new int[] { 1, 2, 4, 5, 9 } }, // dup
|
||||
};
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(array1))]
|
||||
public async Task Distinct(int[] array)
|
||||
{
|
||||
var ys = array.Distinct().ToArray();
|
||||
{
|
||||
(await array.ToUniTaskAsyncEnumerable().Distinct().ToArrayAsync()).Should().BeEquivalentTo(ys);
|
||||
(await array.ToUniTaskAsyncEnumerable().Distinct(x => x).ToArrayAsync()).Should().BeEquivalentTo(ys);
|
||||
(await array.ToUniTaskAsyncEnumerable().DistinctAwait(x => UniTask.Run(() => x)).ToArrayAsync()).Should().BeEquivalentTo(ys);
|
||||
(await array.ToUniTaskAsyncEnumerable().DistinctAwaitWithCancellation((x, _) => UniTask.Run(() => x)).ToArrayAsync()).Should().BeEquivalentTo(ys);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DistinctThrow()
|
||||
{
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
{
|
||||
var xs = item.Distinct().ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
{
|
||||
var xs = item.Distinct(x => x).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
{
|
||||
var xs = item.DistinctAwait(x => UniTask.Run(() => x)).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
{
|
||||
var xs = item.DistinctAwaitWithCancellation((x, _) => UniTask.Run(() => x)).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(array1))]
|
||||
public async Task DistinctUntilChanged(int[] array)
|
||||
{
|
||||
var ys = await array.ToAsyncEnumerable().DistinctUntilChanged().ToArrayAsync();
|
||||
{
|
||||
(await array.ToUniTaskAsyncEnumerable().DistinctUntilChanged().ToArrayAsync()).Should().BeEquivalentTo(ys);
|
||||
(await array.ToUniTaskAsyncEnumerable().DistinctUntilChanged(x => x).ToArrayAsync()).Should().BeEquivalentTo(ys);
|
||||
(await array.ToUniTaskAsyncEnumerable().DistinctUntilChangedAwait(x => UniTask.Run(() => x)).ToArrayAsync()).Should().BeEquivalentTo(ys);
|
||||
(await array.ToUniTaskAsyncEnumerable().DistinctUntilChangedAwaitWithCancellation((x, _) => UniTask.Run(() => x)).ToArrayAsync()).Should().BeEquivalentTo(ys);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DistinctUntilChangedThrow()
|
||||
{
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
{
|
||||
var xs = item.DistinctUntilChanged().ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
{
|
||||
var xs = item.DistinctUntilChanged(x => x).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
{
|
||||
var xs = item.DistinctUntilChangedAwait(x => UniTask.Run(() => x)).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
{
|
||||
var xs = item.DistinctUntilChangedAwaitWithCancellation((x, _) => UniTask.Run(() => x)).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task Except()
|
||||
{
|
||||
foreach (var a1 in array1.First().Cast<int[]>())
|
||||
{
|
||||
foreach (var a2 in array2.First().Cast<int[]>())
|
||||
{
|
||||
var xs = await a1.ToUniTaskAsyncEnumerable().Except(a2.ToUniTaskAsyncEnumerable()).ToArrayAsync();
|
||||
var ys = a1.Except(a2).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ExceptThrow()
|
||||
{
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = item.Except(UniTaskAsyncEnumerable.Return(10)).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = UniTaskAsyncEnumerable.Return(10).Except(item).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Intersect()
|
||||
{
|
||||
foreach (var a1 in array1.First().Cast<int[]>())
|
||||
{
|
||||
foreach (var a2 in array2.First().Cast<int[]>())
|
||||
{
|
||||
var xs = await a1.ToUniTaskAsyncEnumerable().Intersect(a2.ToUniTaskAsyncEnumerable()).ToArrayAsync();
|
||||
var ys = a1.Intersect(a2).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task IntersectThrow()
|
||||
{
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = item.Intersect(UniTaskAsyncEnumerable.Return(10)).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = UniTaskAsyncEnumerable.Return(10).Intersect(item).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Union()
|
||||
{
|
||||
foreach (var a1 in array1.First().Cast<int[]>())
|
||||
{
|
||||
foreach (var a2 in array2.First().Cast<int[]>())
|
||||
{
|
||||
var xs = await a1.ToUniTaskAsyncEnumerable().Union(a2.ToUniTaskAsyncEnumerable()).ToArrayAsync();
|
||||
var ys = a1.Union(a2).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UnionThrow()
|
||||
{
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = item.Union(UniTaskAsyncEnumerable.Return(10)).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
var xs = UniTaskAsyncEnumerable.Return(10).Union(item).ToArrayAsync();
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
221
src/UniTask.NetCoreTests/Linq/Sort.cs
Normal file
221
src/UniTask.NetCoreTests/Linq/Sort.cs
Normal file
@@ -0,0 +1,221 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Cysharp.Threading.Tasks.Linq;
|
||||
using FluentAssertions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Concurrency;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
|
||||
namespace NetCoreTests.Linq
|
||||
{
|
||||
public class SortCheck
|
||||
{
|
||||
public int Age { get; set; }
|
||||
public string FirstName { get; set; }
|
||||
public string LastName { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return (Age, FirstName, LastName).ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public class Sort
|
||||
{
|
||||
static int rd;
|
||||
|
||||
static UniTask<T> RandomRun<T>(T value)
|
||||
{
|
||||
if (Interlocked.Increment(ref rd) % 2 == 0)
|
||||
{
|
||||
return UniTask.Run(() => value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return UniTask.FromResult(value);
|
||||
}
|
||||
}
|
||||
static UniTask<T> RandomRun<T>(T value, CancellationToken ct)
|
||||
{
|
||||
if (Interlocked.Increment(ref rd) % 2 == 0)
|
||||
{
|
||||
return UniTask.Run(() => value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return UniTask.FromResult(value);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task OrderBy()
|
||||
{
|
||||
var array = new[] { 1, 99, 32, 4, 536, 7, 8 };
|
||||
{
|
||||
var xs = await array.ToUniTaskAsyncEnumerable().OrderBy(x => x).ToArrayAsync();
|
||||
var ys = array.OrderBy(x => x).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
{
|
||||
var xs = await array.ToUniTaskAsyncEnumerable().OrderByDescending(x => x).ToArrayAsync();
|
||||
var ys = array.OrderByDescending(x => x).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
{
|
||||
var xs = await array.ToUniTaskAsyncEnumerable().OrderByAwait(RandomRun).ToArrayAsync();
|
||||
var ys = array.OrderBy(x => x).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
{
|
||||
var xs = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwait(RandomRun).ToArrayAsync();
|
||||
var ys = array.OrderByDescending(x => x).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
{
|
||||
var xs = await array.ToUniTaskAsyncEnumerable().OrderByAwaitWithCancellation(RandomRun).ToArrayAsync();
|
||||
var ys = array.OrderBy(x => x).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
{
|
||||
var xs = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwaitWithCancellation(RandomRun).ToArrayAsync();
|
||||
var ys = array.OrderByDescending(x => x).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ThenBy()
|
||||
{
|
||||
var array = new[]
|
||||
{
|
||||
new SortCheck { Age = 99, FirstName = "ABC", LastName = "DEF" },
|
||||
new SortCheck { Age = 49, FirstName = "ABC", LastName = "DEF" },
|
||||
new SortCheck { Age = 49, FirstName = "ABC", LastName = "ZKH" },
|
||||
new SortCheck { Age = 12, FirstName = "ABC", LastName = "DEF" },
|
||||
new SortCheck { Age = 49, FirstName = "ABC", LastName = "MEF" },
|
||||
new SortCheck { Age = 12, FirstName = "QQQ", LastName = "DEF" },
|
||||
new SortCheck { Age = 19, FirstName = "ZKN", LastName = "DEF" },
|
||||
new SortCheck { Age = 39, FirstName = "APO", LastName = "REF" },
|
||||
new SortCheck { Age = 59, FirstName = "ABC", LastName = "DEF" },
|
||||
new SortCheck { Age = 99, FirstName = "DBC", LastName = "DEF" },
|
||||
new SortCheck { Age = 99, FirstName = "DBC", LastName = "MEF" },
|
||||
};
|
||||
{
|
||||
var a = array.OrderBy(x => x.Age).ThenBy(x => x.FirstName).ThenBy(x => x.LastName).ToArray();
|
||||
var b = array.OrderBy(x => x.Age).ThenBy(x => x.FirstName).ThenByDescending(x => x.LastName).ToArray();
|
||||
var c = array.OrderBy(x => x.Age).ThenByDescending(x => x.FirstName).ThenBy(x => x.LastName).ToArray();
|
||||
var d = array.OrderBy(x => x.Age).ThenByDescending(x => x.FirstName).ThenByDescending(x => x.LastName).ToArray();
|
||||
var e = array.OrderByDescending(x => x.Age).ThenBy(x => x.FirstName).ThenBy(x => x.LastName).ToArray();
|
||||
var f = array.OrderByDescending(x => x.Age).ThenBy(x => x.FirstName).ThenByDescending(x => x.LastName).ToArray();
|
||||
var g = array.OrderByDescending(x => x.Age).ThenByDescending(x => x.FirstName).ThenBy(x => x.LastName).ToArray();
|
||||
var h = array.OrderByDescending(x => x.Age).ThenByDescending(x => x.FirstName).ThenByDescending(x => x.LastName).ToArray();
|
||||
|
||||
{
|
||||
var a2 = await array.ToUniTaskAsyncEnumerable().OrderBy(x => x.Age).ThenBy(x => x.FirstName).ThenBy(x => x.LastName).ToArrayAsync();
|
||||
var b2 = await array.ToUniTaskAsyncEnumerable().OrderBy(x => x.Age).ThenBy(x => x.FirstName).ThenByDescending(x => x.LastName).ToArrayAsync();
|
||||
var c2 = await array.ToUniTaskAsyncEnumerable().OrderBy(x => x.Age).ThenByDescending(x => x.FirstName).ThenBy(x => x.LastName).ToArrayAsync();
|
||||
var d2 = await array.ToUniTaskAsyncEnumerable().OrderBy(x => x.Age).ThenByDescending(x => x.FirstName).ThenByDescending(x => x.LastName).ToArrayAsync();
|
||||
var e2 = await array.ToUniTaskAsyncEnumerable().OrderByDescending(x => x.Age).ThenBy(x => x.FirstName).ThenBy(x => x.LastName).ToArrayAsync();
|
||||
var f2 = await array.ToUniTaskAsyncEnumerable().OrderByDescending(x => x.Age).ThenBy(x => x.FirstName).ThenByDescending(x => x.LastName).ToArrayAsync();
|
||||
var g2 = await array.ToUniTaskAsyncEnumerable().OrderByDescending(x => x.Age).ThenByDescending(x => x.FirstName).ThenBy(x => x.LastName).ToArrayAsync();
|
||||
var h2 = await array.ToUniTaskAsyncEnumerable().OrderByDescending(x => x.Age).ThenByDescending(x => x.FirstName).ThenByDescending(x => x.LastName).ToArrayAsync();
|
||||
|
||||
a.Should().BeEquivalentTo(a2);
|
||||
b.Should().BeEquivalentTo(b2);
|
||||
c.Should().BeEquivalentTo(c2);
|
||||
d.Should().BeEquivalentTo(d2);
|
||||
e.Should().BeEquivalentTo(e2);
|
||||
f.Should().BeEquivalentTo(f2);
|
||||
g.Should().BeEquivalentTo(g2);
|
||||
h.Should().BeEquivalentTo(h2);
|
||||
}
|
||||
{
|
||||
var a2 = await array.ToUniTaskAsyncEnumerable().OrderByAwait(x => RandomRun(x.Age)).ThenByAwait(x => RandomRun(x.FirstName)).ThenByAwait(x => RandomRun(x.LastName)).ToArrayAsync();
|
||||
var b2 = await array.ToUniTaskAsyncEnumerable().OrderByAwait(x => RandomRun(x.Age)).ThenByAwait(x => RandomRun(x.FirstName)).ThenByDescendingAwait(x => RandomRun(x.LastName)).ToArrayAsync();
|
||||
var c2 = await array.ToUniTaskAsyncEnumerable().OrderByAwait(x => RandomRun(x.Age)).ThenByDescendingAwait(x => RandomRun(x.FirstName)).ThenByAwait(x => RandomRun(x.LastName)).ToArrayAsync();
|
||||
var d2 = await array.ToUniTaskAsyncEnumerable().OrderByAwait(x => RandomRun(x.Age)).ThenByDescendingAwait(x => RandomRun(x.FirstName)).ThenByDescendingAwait(x => RandomRun(x.LastName)).ToArrayAsync();
|
||||
var e2 = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwait(x => RandomRun(x.Age)).ThenByAwait(x => RandomRun(x.FirstName)).ThenByAwait(x => RandomRun(x.LastName)).ToArrayAsync();
|
||||
var f2 = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwait(x => RandomRun(x.Age)).ThenByAwait(x => RandomRun(x.FirstName)).ThenByDescendingAwait(x => RandomRun(x.LastName)).ToArrayAsync();
|
||||
var g2 = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwait(x => RandomRun(x.Age)).ThenByDescendingAwait(x => RandomRun(x.FirstName)).ThenByAwait(x => RandomRun(x.LastName)).ToArrayAsync();
|
||||
var h2 = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwait(x => RandomRun(x.Age)).ThenByDescendingAwait(x => RandomRun(x.FirstName)).ThenByDescendingAwait(x => RandomRun(x.LastName)).ToArrayAsync();
|
||||
|
||||
a.Should().BeEquivalentTo(a2);
|
||||
b.Should().BeEquivalentTo(b2);
|
||||
c.Should().BeEquivalentTo(c2);
|
||||
d.Should().BeEquivalentTo(d2);
|
||||
e.Should().BeEquivalentTo(e2);
|
||||
f.Should().BeEquivalentTo(f2);
|
||||
g.Should().BeEquivalentTo(g2);
|
||||
h.Should().BeEquivalentTo(h2);
|
||||
}
|
||||
{
|
||||
var a2 = await array.ToUniTaskAsyncEnumerable().OrderByAwaitWithCancellation((x, ct) => RandomRun(x.Age)).ThenByAwaitWithCancellation((x, ct) => RandomRun(x.FirstName)).ThenByAwaitWithCancellation((x, ct) => RandomRun(x.LastName)).ToArrayAsync();
|
||||
var b2 = await array.ToUniTaskAsyncEnumerable().OrderByAwaitWithCancellation((x, ct) => RandomRun(x.Age)).ThenByAwaitWithCancellation((x, ct) => RandomRun(x.FirstName)).ThenByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.LastName)).ToArrayAsync();
|
||||
var c2 = await array.ToUniTaskAsyncEnumerable().OrderByAwaitWithCancellation((x, ct) => RandomRun(x.Age)).ThenByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.FirstName)).ThenByAwaitWithCancellation((x, ct) => RandomRun(x.LastName)).ToArrayAsync();
|
||||
var d2 = await array.ToUniTaskAsyncEnumerable().OrderByAwaitWithCancellation((x, ct) => RandomRun(x.Age)).ThenByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.FirstName)).ThenByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.LastName)).ToArrayAsync();
|
||||
var e2 = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.Age)).ThenByAwaitWithCancellation((x, ct) => RandomRun(x.FirstName)).ThenByAwaitWithCancellation((x, ct) => RandomRun(x.LastName)).ToArrayAsync();
|
||||
var f2 = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.Age)).ThenByAwaitWithCancellation((x, ct) => RandomRun(x.FirstName)).ThenByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.LastName)).ToArrayAsync();
|
||||
var g2 = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.Age)).ThenByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.FirstName)).ThenByAwaitWithCancellation((x, ct) => RandomRun(x.LastName)).ToArrayAsync();
|
||||
var h2 = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.Age)).ThenByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.FirstName)).ThenByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.LastName)).ToArrayAsync();
|
||||
|
||||
a.Should().BeEquivalentTo(a2);
|
||||
b.Should().BeEquivalentTo(b2);
|
||||
c.Should().BeEquivalentTo(c2);
|
||||
d.Should().BeEquivalentTo(d2);
|
||||
e.Should().BeEquivalentTo(e2);
|
||||
f.Should().BeEquivalentTo(f2);
|
||||
g.Should().BeEquivalentTo(g2);
|
||||
h.Should().BeEquivalentTo(h2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task Throws()
|
||||
{
|
||||
foreach (var item in UniTaskTestException.Throws())
|
||||
{
|
||||
{
|
||||
var a = item.OrderBy(x => x).ToArrayAsync();
|
||||
var b = item.OrderByDescending(x => x).ToArrayAsync();
|
||||
var c = item.OrderBy(x => x).ThenBy(x => x).ToArrayAsync();
|
||||
var d = item.OrderBy(x => x).ThenByDescending(x => x).ToArrayAsync();
|
||||
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await a);
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await b);
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await c);
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await d);
|
||||
}
|
||||
{
|
||||
var a = item.OrderByAwait(RandomRun).ToArrayAsync();
|
||||
var b = item.OrderByDescendingAwait(RandomRun).ToArrayAsync();
|
||||
var c = item.OrderByAwait(RandomRun).ThenByAwait(RandomRun).ToArrayAsync();
|
||||
var d = item.OrderByAwait(RandomRun).ThenByDescendingAwait(RandomRun).ToArrayAsync();
|
||||
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await a);
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await b);
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await c);
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await d);
|
||||
}
|
||||
{
|
||||
var a = item.OrderByAwaitWithCancellation(RandomRun).ToArrayAsync();
|
||||
var b = item.OrderByDescendingAwaitWithCancellation(RandomRun).ToArrayAsync();
|
||||
var c = item.OrderByAwaitWithCancellation(RandomRun).ThenByAwaitWithCancellation(RandomRun).ToArrayAsync();
|
||||
var d = item.OrderByAwaitWithCancellation(RandomRun).ThenByDescendingAwaitWithCancellation(RandomRun).ToArrayAsync();
|
||||
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await a);
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await b);
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await c);
|
||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await d);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
106
src/UniTask.NetCoreTests/Linq/TakeInfinityTest.cs
Normal file
106
src/UniTask.NetCoreTests/Linq/TakeInfinityTest.cs
Normal file
@@ -0,0 +1,106 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Cysharp.Threading.Tasks.Linq;
|
||||
using FluentAssertions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace NetCoreTests.Linq
|
||||
{
|
||||
public class TakeInfinityTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task Take()
|
||||
{
|
||||
var rp = new AsyncReactiveProperty<int>(1);
|
||||
|
||||
var xs = rp.Take(5).ToArrayAsync();
|
||||
|
||||
rp.Value = 2;
|
||||
rp.Value = 3;
|
||||
rp.Value = 4;
|
||||
rp.Value = 5;
|
||||
|
||||
(await xs).Should().BeEquivalentTo(1, 2, 3, 4, 5);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TakeWhile()
|
||||
{
|
||||
var rp = new AsyncReactiveProperty<int>(1);
|
||||
|
||||
var xs = rp.TakeWhile(x => x != 5).ToArrayAsync();
|
||||
|
||||
rp.Value = 2;
|
||||
rp.Value = 3;
|
||||
rp.Value = 4;
|
||||
rp.Value = 5;
|
||||
|
||||
(await xs).Should().BeEquivalentTo(1, 2, 3, 4);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TakeUntil()
|
||||
{
|
||||
var cts = new CancellationTokenSource();
|
||||
|
||||
var rp = new AsyncReactiveProperty<int>(1);
|
||||
|
||||
var xs = rp.TakeUntilCanceled(cts.Token).ToArrayAsync();
|
||||
|
||||
var c = CancelAsync();
|
||||
|
||||
await c;
|
||||
var foo = await xs;
|
||||
|
||||
foo.Should().BeEquivalentTo(new[] { 1, 10, 20 });
|
||||
|
||||
async Task CancelAsync()
|
||||
{
|
||||
rp.Value = 10;
|
||||
await Task.Yield();
|
||||
rp.Value = 20;
|
||||
await Task.Yield();
|
||||
cts.Cancel();
|
||||
rp.Value = 30;
|
||||
await Task.Yield();
|
||||
rp.Value = 40;
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SkipUntil()
|
||||
{
|
||||
var cts = new CancellationTokenSource();
|
||||
|
||||
var rp = new AsyncReactiveProperty<int>(1);
|
||||
|
||||
var xs = rp.SkipUntilCanceled(cts.Token).ToArrayAsync();
|
||||
|
||||
var c = CancelAsync();
|
||||
|
||||
await c;
|
||||
var foo = await xs;
|
||||
|
||||
foo.Should().BeEquivalentTo(new[] { 30, 40 });
|
||||
|
||||
async Task CancelAsync()
|
||||
{
|
||||
rp.Value = 10;
|
||||
await Task.Yield();
|
||||
rp.Value = 20;
|
||||
await Task.Yield();
|
||||
cts.Cancel();
|
||||
rp.Value = 30;
|
||||
await Task.Yield();
|
||||
rp.Value = 40;
|
||||
|
||||
rp.Dispose(); // complete.
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
126
src/UniTask.NetCoreTests/Linq/_Exception.cs
Normal file
126
src/UniTask.NetCoreTests/Linq/_Exception.cs
Normal file
@@ -0,0 +1,126 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Cysharp.Threading.Tasks.Linq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace NetCoreTests.Linq
|
||||
{
|
||||
public class UniTaskTestException : Exception
|
||||
{
|
||||
public static IUniTaskAsyncEnumerable<int> ThrowImmediate()
|
||||
{
|
||||
return UniTaskAsyncEnumerable.Throw<int>(new UniTaskTestException());
|
||||
}
|
||||
public static IUniTaskAsyncEnumerable<int> ThrowAfter()
|
||||
{
|
||||
return new ThrowAfter<int>(new UniTaskTestException());
|
||||
}
|
||||
public static IUniTaskAsyncEnumerable<int> ThrowInMoveNext()
|
||||
{
|
||||
return new ThrowIn<int>(new UniTaskTestException());
|
||||
}
|
||||
|
||||
|
||||
public static IEnumerable<IUniTaskAsyncEnumerable<int>> Throws(int count = 3)
|
||||
{
|
||||
yield return ThrowImmediate();
|
||||
yield return ThrowAfter();
|
||||
yield return ThrowInMoveNext();
|
||||
yield return UniTaskAsyncEnumerable.Range(1, count).Concat(ThrowImmediate());
|
||||
yield return UniTaskAsyncEnumerable.Range(1, count).Concat(ThrowAfter());
|
||||
yield return UniTaskAsyncEnumerable.Range(1, count).Concat(ThrowInMoveNext());
|
||||
}
|
||||
}
|
||||
|
||||
internal class ThrowIn<TValue> : IUniTaskAsyncEnumerable<TValue>
|
||||
{
|
||||
readonly Exception exception;
|
||||
|
||||
public ThrowIn(Exception exception)
|
||||
{
|
||||
this.exception = exception;
|
||||
}
|
||||
|
||||
public IUniTaskAsyncEnumerator<TValue> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return new Enumerator(exception, cancellationToken);
|
||||
}
|
||||
|
||||
class Enumerator : IUniTaskAsyncEnumerator<TValue>
|
||||
{
|
||||
readonly Exception exception;
|
||||
CancellationToken cancellationToken;
|
||||
|
||||
public Enumerator(Exception exception, CancellationToken cancellationToken)
|
||||
{
|
||||
this.exception = exception;
|
||||
this.cancellationToken = cancellationToken;
|
||||
}
|
||||
|
||||
public TValue Current => default;
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
ExceptionDispatchInfo.Capture(exception).Throw();
|
||||
return new UniTask<bool>(false);
|
||||
}
|
||||
|
||||
public UniTask DisposeAsync()
|
||||
{
|
||||
return default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class ThrowAfter<TValue> : IUniTaskAsyncEnumerable<TValue>
|
||||
{
|
||||
readonly Exception exception;
|
||||
|
||||
public ThrowAfter(Exception exception)
|
||||
{
|
||||
this.exception = exception;
|
||||
}
|
||||
|
||||
public IUniTaskAsyncEnumerator<TValue> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return new Enumerator(exception, cancellationToken);
|
||||
}
|
||||
|
||||
class Enumerator : IUniTaskAsyncEnumerator<TValue>
|
||||
{
|
||||
readonly Exception exception;
|
||||
CancellationToken cancellationToken;
|
||||
|
||||
public Enumerator(Exception exception, CancellationToken cancellationToken)
|
||||
{
|
||||
this.exception = exception;
|
||||
this.cancellationToken = cancellationToken;
|
||||
}
|
||||
|
||||
public TValue Current => default;
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var tcs = new UniTaskCompletionSource<bool>();
|
||||
|
||||
var awaiter = UniTask.Yield().GetAwaiter();
|
||||
awaiter.UnsafeOnCompleted(() =>
|
||||
{
|
||||
Thread.Sleep(1);
|
||||
tcs.TrySetException(exception);
|
||||
});
|
||||
|
||||
return tcs.Task;
|
||||
}
|
||||
|
||||
public UniTask DisposeAsync()
|
||||
{
|
||||
return default;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
301
src/UniTask.NetCoreTests/TaskBuilderCases.cs
Normal file
301
src/UniTask.NetCoreTests/TaskBuilderCases.cs
Normal file
@@ -0,0 +1,301 @@
|
||||
#pragma warning disable CS1998
|
||||
|
||||
using Cysharp.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Channels;
|
||||
using Cysharp.Threading.Tasks.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace NetCoreTests
|
||||
{
|
||||
public class UniTaskBuilderTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task Empty()
|
||||
{
|
||||
await Core();
|
||||
|
||||
static async UniTask Core()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EmptyThrow()
|
||||
{
|
||||
await Assert.ThrowsAsync<TaskTestException>(async () => await Core());
|
||||
|
||||
static async UniTask Core()
|
||||
{
|
||||
throw new TaskTestException();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Task_Done()
|
||||
{
|
||||
await Core();
|
||||
|
||||
static async UniTask Core()
|
||||
{
|
||||
await new TestAwaiter(true, UniTaskStatus.Succeeded);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Task_Fail()
|
||||
{
|
||||
await Assert.ThrowsAsync<TaskTestException>(async () => await Core());
|
||||
|
||||
static async UniTask Core()
|
||||
{
|
||||
await new TestAwaiter(true, UniTaskStatus.Faulted);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Task_Cancel()
|
||||
{
|
||||
await Assert.ThrowsAsync<OperationCanceledException>(async () => await Core());
|
||||
|
||||
static async UniTask Core()
|
||||
{
|
||||
await new TestAwaiter(true, UniTaskStatus.Canceled);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AwaitUnsafeOnCompletedCall_Task_SetResult()
|
||||
{
|
||||
await Core();
|
||||
|
||||
static async UniTask Core()
|
||||
{
|
||||
await new TestAwaiter(false, UniTaskStatus.Succeeded);
|
||||
await new TestAwaiter(false, UniTaskStatus.Succeeded);
|
||||
await new TestAwaiter(false, UniTaskStatus.Succeeded);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AwaitUnsafeOnCompletedCall_Task_SetException()
|
||||
{
|
||||
await Assert.ThrowsAsync<TaskTestException>(async () => await Core());
|
||||
|
||||
static async UniTask Core()
|
||||
{
|
||||
await new TestAwaiter(false, UniTaskStatus.Succeeded);
|
||||
await new TestAwaiter(false, UniTaskStatus.Faulted);
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AwaitUnsafeOnCompletedCall_Task_SetCancelException()
|
||||
{
|
||||
await Assert.ThrowsAsync<OperationCanceledException>(async () => await Core());
|
||||
|
||||
static async UniTask Core()
|
||||
{
|
||||
await new TestAwaiter(false, UniTaskStatus.Succeeded);
|
||||
await new TestAwaiter(false, UniTaskStatus.Canceled);
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class UniTask_T_BuilderTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task Empty()
|
||||
{
|
||||
(await Core()).Should().Be(10);
|
||||
|
||||
static async UniTask<int> Core()
|
||||
{
|
||||
return 10;
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EmptyThrow()
|
||||
{
|
||||
await Assert.ThrowsAsync<TaskTestException>(async () => await Core());
|
||||
|
||||
static async UniTask<int> Core()
|
||||
{
|
||||
throw new TaskTestException();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Task_Done()
|
||||
{
|
||||
(await Core()).Should().Be(10);
|
||||
|
||||
static async UniTask<int> Core()
|
||||
{
|
||||
return await new TestAwaiter<int>(true, UniTaskStatus.Succeeded, 10);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Task_Fail()
|
||||
{
|
||||
await Assert.ThrowsAsync<TaskTestException>(async () => await Core());
|
||||
|
||||
static async UniTask<int> Core()
|
||||
{
|
||||
return await new TestAwaiter<int>(true, UniTaskStatus.Faulted, 10);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Task_Cancel()
|
||||
{
|
||||
await Assert.ThrowsAsync<OperationCanceledException>(async () => await Core());
|
||||
|
||||
static async UniTask<int> Core()
|
||||
{
|
||||
return await new TestAwaiter<int>(true, UniTaskStatus.Canceled, 10);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AwaitUnsafeOnCompletedCall_Task_SetResult()
|
||||
{
|
||||
(await Core()).Should().Be(6);
|
||||
|
||||
static async UniTask<int> Core()
|
||||
{
|
||||
var sum = 0;
|
||||
sum += await new TestAwaiter<int>(false, UniTaskStatus.Succeeded, 1);
|
||||
sum += await new TestAwaiter<int>(false, UniTaskStatus.Succeeded, 2);
|
||||
sum += await new TestAwaiter<int>(false, UniTaskStatus.Succeeded, 3);
|
||||
return sum;
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AwaitUnsafeOnCompletedCall_Task_SetException()
|
||||
{
|
||||
await Assert.ThrowsAsync<TaskTestException>(async () => await Core());
|
||||
|
||||
static async UniTask<int> Core()
|
||||
{
|
||||
await new TestAwaiter<int>(false, UniTaskStatus.Succeeded, 10);
|
||||
await new TestAwaiter<int>(false, UniTaskStatus.Faulted, 10);
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AwaitUnsafeOnCompletedCall_Task_SetCancelException()
|
||||
{
|
||||
await Assert.ThrowsAsync<OperationCanceledException>(async () => await Core());
|
||||
|
||||
static async UniTask<int> Core()
|
||||
{
|
||||
await new TestAwaiter<int>(false, UniTaskStatus.Succeeded, 10);
|
||||
await new TestAwaiter<int>(false, UniTaskStatus.Canceled, 10);
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class TaskTestException : Exception
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public struct TestAwaiter : ICriticalNotifyCompletion
|
||||
{
|
||||
readonly UniTaskStatus status;
|
||||
readonly bool isCompleted;
|
||||
|
||||
public TestAwaiter(bool isCompleted, UniTaskStatus status)
|
||||
{
|
||||
this.isCompleted = isCompleted;
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public TestAwaiter GetAwaiter() => this;
|
||||
|
||||
public bool IsCompleted => isCompleted;
|
||||
|
||||
public void GetResult()
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case UniTaskStatus.Faulted:
|
||||
throw new TaskTestException();
|
||||
case UniTaskStatus.Canceled:
|
||||
throw new OperationCanceledException();
|
||||
case UniTaskStatus.Pending:
|
||||
case UniTaskStatus.Succeeded:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnCompleted(Action continuation)
|
||||
{
|
||||
ThreadPool.QueueUserWorkItem(_ => continuation(), null);
|
||||
}
|
||||
|
||||
public void UnsafeOnCompleted(Action continuation)
|
||||
{
|
||||
ThreadPool.UnsafeQueueUserWorkItem(_ => continuation(), null);
|
||||
}
|
||||
}
|
||||
|
||||
public struct TestAwaiter<T> : ICriticalNotifyCompletion
|
||||
{
|
||||
readonly UniTaskStatus status;
|
||||
readonly bool isCompleted;
|
||||
readonly T value;
|
||||
|
||||
public TestAwaiter(bool isCompleted, UniTaskStatus status, T value)
|
||||
{
|
||||
this.isCompleted = isCompleted;
|
||||
this.status = status;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public TestAwaiter<T> GetAwaiter() => this;
|
||||
|
||||
public bool IsCompleted => isCompleted;
|
||||
|
||||
public T GetResult()
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case UniTaskStatus.Faulted:
|
||||
throw new TaskTestException();
|
||||
case UniTaskStatus.Canceled:
|
||||
throw new OperationCanceledException();
|
||||
case UniTaskStatus.Pending:
|
||||
case UniTaskStatus.Succeeded:
|
||||
default:
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnCompleted(Action continuation)
|
||||
{
|
||||
ThreadPool.QueueUserWorkItem(_ => continuation(), null);
|
||||
}
|
||||
|
||||
public void UnsafeOnCompleted(Action continuation)
|
||||
{
|
||||
ThreadPool.UnsafeQueueUserWorkItem(_ => continuation(), null);
|
||||
}
|
||||
}
|
||||
}
|
||||
637
src/UniTask.NetCoreTests/TriggerEventTest.cs
Normal file
637
src/UniTask.NetCoreTests/TriggerEventTest.cs
Normal file
@@ -0,0 +1,637 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Channels;
|
||||
using Cysharp.Threading.Tasks.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace NetCoreTests
|
||||
{
|
||||
public class TriggerEventTest
|
||||
{
|
||||
[Fact]
|
||||
public void SimpleAdd()
|
||||
{
|
||||
var ev = new TriggerEvent<int>();
|
||||
|
||||
// do nothing
|
||||
ev.SetResult(0);
|
||||
ev.SetError(null);
|
||||
ev.SetCompleted();
|
||||
ev.SetCanceled(default);
|
||||
|
||||
{
|
||||
var one = new TestEvent(1);
|
||||
|
||||
ev.Add(one);
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
|
||||
ev.SetCompleted();
|
||||
|
||||
one.CompletedCalled.Count.Should().Be(1);
|
||||
|
||||
// do nothing
|
||||
ev.SetResult(0);
|
||||
ev.SetError(null);
|
||||
ev.SetCompleted();
|
||||
ev.SetCanceled(default);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
one.CompletedCalled.Count.Should().Be(1);
|
||||
}
|
||||
// after removed, onemore
|
||||
{
|
||||
var one = new TestEvent(1);
|
||||
|
||||
ev.Add(one);
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
|
||||
ev.SetCompleted();
|
||||
|
||||
one.CompletedCalled.Count.Should().Be(1);
|
||||
|
||||
// do nothing
|
||||
ev.SetResult(0);
|
||||
ev.SetError(null);
|
||||
ev.SetCompleted();
|
||||
ev.SetCanceled(default);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
one.CompletedCalled.Count.Should().Be(1);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddFour()
|
||||
{
|
||||
var ev = new TriggerEvent<int>();
|
||||
|
||||
// do nothing
|
||||
ev.SetResult(0);
|
||||
ev.SetError(null);
|
||||
ev.SetCompleted();
|
||||
ev.SetCanceled(default);
|
||||
|
||||
{
|
||||
var one = new TestEvent(1);
|
||||
var two = new TestEvent(2);
|
||||
var three = new TestEvent(3);
|
||||
var four = new TestEvent(4);
|
||||
|
||||
ev.Add(one);
|
||||
ev.Add(two);
|
||||
ev.Add(three);
|
||||
ev.Add(four);
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
four.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
|
||||
ev.SetCompleted();
|
||||
|
||||
one.CompletedCalled.Count.Should().Be(1);
|
||||
two.CompletedCalled.Count.Should().Be(1);
|
||||
three.CompletedCalled.Count.Should().Be(1);
|
||||
|
||||
|
||||
// do nothing
|
||||
ev.SetResult(0);
|
||||
ev.SetError(null);
|
||||
ev.SetCompleted();
|
||||
ev.SetCanceled(default);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
one.CompletedCalled.Count.Should().Be(1);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.CompletedCalled.Count.Should().Be(1);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.CompletedCalled.Count.Should().Be(1);
|
||||
}
|
||||
|
||||
// after removed, onemore.
|
||||
{
|
||||
var one = new TestEvent(1);
|
||||
var two = new TestEvent(2);
|
||||
var three = new TestEvent(3);
|
||||
var four = new TestEvent(4);
|
||||
|
||||
ev.Add(one);
|
||||
ev.Add(two);
|
||||
ev.Add(three);
|
||||
ev.Add(four);
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
ev.Add(four);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
four.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
|
||||
ev.SetCompleted();
|
||||
|
||||
one.CompletedCalled.Count.Should().Be(1);
|
||||
two.CompletedCalled.Count.Should().Be(1);
|
||||
three.CompletedCalled.Count.Should().Be(1);
|
||||
|
||||
|
||||
// do nothing
|
||||
ev.SetResult(0);
|
||||
ev.SetError(null);
|
||||
ev.SetCompleted();
|
||||
ev.SetCanceled(default);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
one.CompletedCalled.Count.Should().Be(1);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.CompletedCalled.Count.Should().Be(1);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.CompletedCalled.Count.Should().Be(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void OneRemove()
|
||||
{
|
||||
var ev = new TriggerEvent<int>();
|
||||
{
|
||||
var one = new TestEvent(1);
|
||||
var two = new TestEvent(2);
|
||||
var three = new TestEvent(3);
|
||||
|
||||
ev.Add(one);
|
||||
ev.Add(two);
|
||||
ev.Add(three);
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
|
||||
ev.Remove(one);
|
||||
|
||||
ev.SetResult(40);
|
||||
ev.SetResult(50);
|
||||
ev.SetResult(60);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60);
|
||||
}
|
||||
}
|
||||
[Fact]
|
||||
public void TwoRemove()
|
||||
{
|
||||
var ev = new TriggerEvent<int>();
|
||||
{
|
||||
var one = new TestEvent(1);
|
||||
var two = new TestEvent(2);
|
||||
var three = new TestEvent(3);
|
||||
|
||||
ev.Add(one);
|
||||
ev.Add(two);
|
||||
ev.Add(three);
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
|
||||
ev.Remove(two);
|
||||
|
||||
ev.SetResult(40);
|
||||
ev.SetResult(50);
|
||||
ev.SetResult(60);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60);
|
||||
}
|
||||
}
|
||||
[Fact]
|
||||
public void ThreeRemove()
|
||||
{
|
||||
var ev = new TriggerEvent<int>();
|
||||
{
|
||||
var one = new TestEvent(1);
|
||||
var two = new TestEvent(2);
|
||||
var three = new TestEvent(3);
|
||||
|
||||
ev.Add(one);
|
||||
ev.Add(two);
|
||||
ev.Add(three);
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
|
||||
ev.Remove(three);
|
||||
|
||||
ev.SetResult(40);
|
||||
ev.SetResult(50);
|
||||
ev.SetResult(60);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RemoveSelf()
|
||||
{
|
||||
new RemoveMe().Run1();
|
||||
new RemoveMe().Run2();
|
||||
new RemoveMe().Run3();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RemoveNextInIterating()
|
||||
{
|
||||
new RemoveNext().Run1();
|
||||
new RemoveNext().Run2();
|
||||
new RemoveNext().Run3();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RemoveNextNextTest()
|
||||
{
|
||||
new RemoveNextNext().Run1();
|
||||
new RemoveNextNext().Run2();
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void AddTest()
|
||||
{
|
||||
new AddMe().Run1();
|
||||
new AddMe().Run2();
|
||||
}
|
||||
|
||||
public class RemoveMe
|
||||
{
|
||||
TriggerEvent<int> ev;
|
||||
|
||||
public void Run1()
|
||||
{
|
||||
TestEvent one = default;
|
||||
one = new TestEvent(1, () => ev.Remove(one));
|
||||
|
||||
var two = new TestEvent(2);
|
||||
var three = new TestEvent(3);
|
||||
|
||||
ev.Add(one);
|
||||
ev.Add(two);
|
||||
ev.Add(three);
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
}
|
||||
|
||||
public void Run2()
|
||||
{
|
||||
TestEvent one = default;
|
||||
one = new TestEvent(1, () => ev.Remove(one));
|
||||
|
||||
var two = new TestEvent(2);
|
||||
var three = new TestEvent(3);
|
||||
|
||||
ev.Add(two);
|
||||
ev.Add(one); // add second.
|
||||
ev.Add(three);
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
}
|
||||
|
||||
public void Run3()
|
||||
{
|
||||
TestEvent one = default;
|
||||
one = new TestEvent(1, () => ev.Remove(one));
|
||||
|
||||
var two = new TestEvent(2);
|
||||
var three = new TestEvent(3);
|
||||
|
||||
ev.Add(two);
|
||||
ev.Add(three);
|
||||
ev.Add(one); // add thired.
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
}
|
||||
}
|
||||
|
||||
public class RemoveNext
|
||||
{
|
||||
TriggerEvent<int> ev;
|
||||
|
||||
public void Run1()
|
||||
{
|
||||
TestEvent one = default;
|
||||
TestEvent two = default;
|
||||
TestEvent three = default;
|
||||
one = new TestEvent(1, () => ev.Remove(two));
|
||||
two = new TestEvent(2);
|
||||
three = new TestEvent(3);
|
||||
|
||||
ev.Add(one);
|
||||
ev.Add(two);
|
||||
ev.Add(three);
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
two.NextCalled.Count.Should().Be(0);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
}
|
||||
|
||||
public void Run2()
|
||||
{
|
||||
TestEvent one = default;
|
||||
TestEvent two = default;
|
||||
TestEvent three = default;
|
||||
one = new TestEvent(1, () => ev.Remove(two));
|
||||
two = new TestEvent(2);
|
||||
three = new TestEvent(3);
|
||||
|
||||
ev.Add(two);
|
||||
ev.Add(one); // add second
|
||||
ev.Add(three);
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
two.NextCalled.Should().BeEquivalentTo(10);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
}
|
||||
|
||||
public void Run3()
|
||||
{
|
||||
TestEvent one = default;
|
||||
TestEvent two = default;
|
||||
TestEvent three = default;
|
||||
one = new TestEvent(1, () => ev.Remove(two));
|
||||
two = new TestEvent(2);
|
||||
three = new TestEvent(3);
|
||||
|
||||
ev.Add(two);
|
||||
ev.Add(three);
|
||||
ev.Add(one); // add thired.
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
two.NextCalled.Should().BeEquivalentTo(10);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
}
|
||||
}
|
||||
|
||||
public class RemoveNextNext
|
||||
{
|
||||
TriggerEvent<int> ev;
|
||||
|
||||
public void Run1()
|
||||
{
|
||||
TestEvent one = default;
|
||||
TestEvent two = default;
|
||||
TestEvent three = default;
|
||||
TestEvent four = default;
|
||||
one = new TestEvent(1, () => { ev.Remove(two); ev.Remove(three); });
|
||||
two = new TestEvent(2);
|
||||
three = new TestEvent(3);
|
||||
four = new TestEvent(4);
|
||||
|
||||
ev.Add(one);
|
||||
ev.Add(two);
|
||||
ev.Add(three);
|
||||
ev.Add(four);
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
two.NextCalled.Count.Should().Be(0);
|
||||
three.NextCalled.Count.Should().Be(0);
|
||||
four.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
}
|
||||
|
||||
public void Run2()
|
||||
{
|
||||
TestEvent one = default;
|
||||
TestEvent two = default;
|
||||
TestEvent three = default;
|
||||
TestEvent four = default;
|
||||
one = new TestEvent(1, () => { ev.Remove(one); ev.Remove(two); ev.Remove(three); });
|
||||
two = new TestEvent(2);
|
||||
three = new TestEvent(3);
|
||||
four = new TestEvent(4);
|
||||
|
||||
ev.Add(one);
|
||||
ev.Add(two);
|
||||
ev.Add(three);
|
||||
ev.Add(four);
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10);
|
||||
two.NextCalled.Count.Should().Be(0);
|
||||
three.NextCalled.Count.Should().Be(0);
|
||||
four.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public class AddMe
|
||||
{
|
||||
TriggerEvent<int> ev;
|
||||
|
||||
public void Run1()
|
||||
{
|
||||
TestEvent one = default;
|
||||
TestEvent two = default;
|
||||
TestEvent three = default;
|
||||
TestEvent four = default;
|
||||
|
||||
one = new TestEvent(1, () =>
|
||||
{
|
||||
if (two == null)
|
||||
{
|
||||
ev.Add(two = new TestEvent(2));
|
||||
}
|
||||
else if (three == null)
|
||||
{
|
||||
ev.Add(three = new TestEvent(3));
|
||||
}
|
||||
else if (four == null)
|
||||
{
|
||||
ev.Add(four = new TestEvent(4));
|
||||
}
|
||||
});
|
||||
|
||||
ev.Add(one);
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
ev.SetResult(40);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40);
|
||||
two.NextCalled.Should().BeEquivalentTo(20, 30, 40);
|
||||
three.NextCalled.Should().BeEquivalentTo(30, 40);
|
||||
four.NextCalled.Should().BeEquivalentTo(40);
|
||||
}
|
||||
|
||||
public void Run2()
|
||||
{
|
||||
TestEvent one = default;
|
||||
TestEvent two = default;
|
||||
TestEvent three = default;
|
||||
TestEvent four = default;
|
||||
|
||||
one = new TestEvent(1, () =>
|
||||
{
|
||||
if (two == null)
|
||||
{
|
||||
ev.Add(two = new TestEvent(2, () =>
|
||||
{
|
||||
if (three == null)
|
||||
{
|
||||
ev.Add(three = new TestEvent(3, () =>
|
||||
{
|
||||
if (four == null)
|
||||
{
|
||||
ev.Add(four = new TestEvent(4));
|
||||
}
|
||||
}));
|
||||
}
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
ev.Add(one);
|
||||
|
||||
ev.SetResult(10);
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
ev.SetResult(40);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40);
|
||||
two.NextCalled.Should().BeEquivalentTo(20, 30, 40);
|
||||
three.NextCalled.Should().BeEquivalentTo(30, 40);
|
||||
four.NextCalled.Should().BeEquivalentTo(40);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class TestEvent : ITriggerHandler<int>
|
||||
{
|
||||
public readonly int Id;
|
||||
readonly Action iteratingEvent;
|
||||
|
||||
public TestEvent(int id)
|
||||
{
|
||||
this.Id = id;
|
||||
}
|
||||
|
||||
public TestEvent(int id, Action iteratingEvent)
|
||||
{
|
||||
this.Id = id;
|
||||
this.iteratingEvent = iteratingEvent;
|
||||
}
|
||||
|
||||
public List<int> NextCalled = new List<int>();
|
||||
public List<Exception> ErrorCalled = new List<Exception>();
|
||||
public List<object> CompletedCalled = new List<object>();
|
||||
public List<CancellationToken> CancelCalled = new List<CancellationToken>();
|
||||
|
||||
public ITriggerHandler<int> Prev { get; set; }
|
||||
public ITriggerHandler<int> Next { get; set; }
|
||||
|
||||
public void OnCanceled(CancellationToken cancellationToken)
|
||||
{
|
||||
CancelCalled.Add(cancellationToken);
|
||||
|
||||
}
|
||||
|
||||
public void OnCompleted()
|
||||
{
|
||||
CompletedCalled.Add(new object());
|
||||
}
|
||||
|
||||
public void OnError(Exception ex)
|
||||
{
|
||||
ErrorCalled.Add(ex);
|
||||
}
|
||||
|
||||
public void OnNext(int value)
|
||||
{
|
||||
NextCalled.Add(value);
|
||||
iteratingEvent?.Invoke();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Id.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
@@ -9,10 +9,20 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.0" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
|
||||
<PackageReference Include="coverlet.collector" Version="1.0.1" />
|
||||
<PackageReference Include="FluentAssertions" Version="5.10.3" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
|
||||
<PackageReference Include="System.Interactive.Async" Version="4.1.1" />
|
||||
<PackageReference Include="System.Linq.Async" Version="4.1.1" />
|
||||
<PackageReference Include="System.Reactive" Version="4.4.1" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\UniTask.NetCore\UniTask.NetCore.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
using System;
|
||||
using Xunit;
|
||||
|
||||
namespace UniTask.NetCoreTests
|
||||
{
|
||||
public class UnitTest1
|
||||
{
|
||||
[Fact]
|
||||
public void Test1()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
30
src/UniTask/Assets/Editor/EditorRunnerChecker.cs
Normal file
30
src/UniTask/Assets/Editor/EditorRunnerChecker.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using Cysharp.Threading.Tasks;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Networking;
|
||||
|
||||
public static class EditorRunnerChecker
|
||||
{
|
||||
[MenuItem("Tools/UniTaskEditorRunnerChecker")]
|
||||
public static void RunUniTaskAsync()
|
||||
{
|
||||
RunCore().Forget();
|
||||
}
|
||||
|
||||
static async UniTaskVoid RunCore()
|
||||
{
|
||||
Debug.Log("Start");
|
||||
|
||||
var r = await UnityWebRequest.Get("https://bing.com/").SendWebRequest().ToUniTask();
|
||||
Debug.Log(r.downloadHandler.text.Substring(0, 100));
|
||||
|
||||
Debug.Log("End");
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4d5a9a3e1f0f069478969f752fde29a9
|
||||
guid: e51b78c06cb410f42b36e0af9de3b065
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
@@ -11,9 +11,11 @@ public static class PackageExporter
|
||||
[MenuItem("Tools/Export Unitypackage")]
|
||||
public static void Export()
|
||||
{
|
||||
// configure
|
||||
var root = "Plugins/UniTask";
|
||||
var exportPath = "./UniTask.unitypackage";
|
||||
var version = GetVersion(root);
|
||||
|
||||
var fileName = string.IsNullOrEmpty(version) ? "UniTask.unitypackage" : $"UniTask.{version}.unitypackage";
|
||||
var exportPath = "./" + fileName;
|
||||
|
||||
var path = Path.Combine(Application.dataPath, root);
|
||||
var assets = Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories)
|
||||
@@ -30,6 +32,42 @@ public static class PackageExporter
|
||||
|
||||
UnityEngine.Debug.Log("Export complete: " + Path.GetFullPath(exportPath));
|
||||
}
|
||||
|
||||
static string GetVersion(string root)
|
||||
{
|
||||
var version = Environment.GetEnvironmentVariable("UNITY_PACKAGE_VERSION");
|
||||
var versionJson = Path.Combine(Application.dataPath, root, "package.json");
|
||||
|
||||
if (File.Exists(versionJson))
|
||||
{
|
||||
var v = JsonUtility.FromJson<Version>(File.ReadAllText(versionJson));
|
||||
|
||||
if (!string.IsNullOrEmpty(version))
|
||||
{
|
||||
if (v.version != version)
|
||||
{
|
||||
var msg = $"package.json and env version are mismatched. UNITY_PACKAGE_VERSION:{version}, package.json:{v.version}";
|
||||
|
||||
if (Application.isBatchMode)
|
||||
{
|
||||
Console.WriteLine(msg);
|
||||
Application.Quit(1);
|
||||
}
|
||||
|
||||
throw new Exception("package.json and env version are mismatched.");
|
||||
}
|
||||
}
|
||||
|
||||
version = v.version;
|
||||
}
|
||||
|
||||
return version;
|
||||
}
|
||||
|
||||
public class Version
|
||||
{
|
||||
public string version;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,57 +0,0 @@
|
||||
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Cysharp.Threading.Tasks.Internal;
|
||||
|
||||
namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
{
|
||||
internal interface IMoveNextRunner
|
||||
{
|
||||
Action CallMoveNext { get; }
|
||||
void Return();
|
||||
}
|
||||
|
||||
internal sealed class MoveNextRunner<TStateMachine> : IMoveNextRunner, IPromisePoolItem
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
static PromisePool<MoveNextRunner<TStateMachine>> pool = new PromisePool<MoveNextRunner<TStateMachine>>();
|
||||
|
||||
TStateMachine stateMachine;
|
||||
internal readonly Action callMoveNext;
|
||||
|
||||
public Action CallMoveNext => callMoveNext;
|
||||
|
||||
MoveNextRunner()
|
||||
{
|
||||
callMoveNext = MoveNext;
|
||||
}
|
||||
|
||||
public static MoveNextRunner<TStateMachine> Create(ref TStateMachine stateMachine)
|
||||
{
|
||||
var result = pool.TryRent() ?? new MoveNextRunner<TStateMachine>();
|
||||
result.stateMachine = stateMachine;
|
||||
return result;
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
void MoveNext()
|
||||
{
|
||||
stateMachine.MoveNext();
|
||||
}
|
||||
|
||||
public void Return()
|
||||
{
|
||||
pool.TryReturn(this);
|
||||
}
|
||||
|
||||
void IPromisePoolItem.Reset()
|
||||
{
|
||||
stateMachine = default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,373 +0,0 @@
|
||||
// asmdef Version Defines, enabled when com.unity.addressables is imported.
|
||||
|
||||
#if UNITASK_ADDRESSABLE_SUPPORT
|
||||
|
||||
using Cysharp.Threading.Tasks.Internal;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using System.Threading;
|
||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
|
||||
namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
public static class AddressableAsyncExtensions
|
||||
{
|
||||
#region AsyncOperationHandle
|
||||
|
||||
public static AsyncOperationHandleAwaiter GetAwaiter(this AsyncOperationHandle handle)
|
||||
{
|
||||
return new AsyncOperationHandleAwaiter(handle);
|
||||
}
|
||||
|
||||
public static UniTask ToUniTask(this AsyncOperationHandle handle)
|
||||
{
|
||||
return new UniTask(AsyncOperationHandleConfiguredSource.Create(handle, PlayerLoopTiming.Update, null, CancellationToken.None, out var token), token);
|
||||
}
|
||||
|
||||
public static UniTask ConfigureAwait(this AsyncOperationHandle handle, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellation = default(CancellationToken))
|
||||
{
|
||||
return new UniTask(AsyncOperationHandleConfiguredSource.Create(handle, timing, progress, cancellation, out var token), token);
|
||||
}
|
||||
|
||||
public struct AsyncOperationHandleAwaiter : ICriticalNotifyCompletion
|
||||
{
|
||||
AsyncOperationHandle handle;
|
||||
Action<AsyncOperationHandle> continuationAction;
|
||||
|
||||
public AsyncOperationHandleAwaiter(AsyncOperationHandle handle)
|
||||
{
|
||||
this.handle = handle;
|
||||
this.continuationAction = null;
|
||||
}
|
||||
|
||||
public bool IsCompleted => handle.IsDone;
|
||||
|
||||
public void GetResult()
|
||||
{
|
||||
if (continuationAction != null)
|
||||
{
|
||||
handle.Completed -= continuationAction;
|
||||
continuationAction = null;
|
||||
}
|
||||
|
||||
if (handle.Status == AsyncOperationStatus.Failed)
|
||||
{
|
||||
var e = handle.OperationException;
|
||||
handle = default;
|
||||
ExceptionDispatchInfo.Capture(e).Throw();
|
||||
}
|
||||
|
||||
var result = handle.Result;
|
||||
handle = default;
|
||||
}
|
||||
|
||||
public void OnCompleted(Action continuation)
|
||||
{
|
||||
UnsafeOnCompleted(continuation);
|
||||
}
|
||||
|
||||
public void UnsafeOnCompleted(Action continuation)
|
||||
{
|
||||
Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction);
|
||||
continuationAction = continuation.AsFuncOfT<AsyncOperationHandle>(); // allocate delegate.
|
||||
handle.Completed += continuationAction;
|
||||
}
|
||||
}
|
||||
|
||||
class AsyncOperationHandleConfiguredSource : IUniTaskSource, IPlayerLoopItem, IPromisePoolItem
|
||||
{
|
||||
static readonly PromisePool<AsyncOperationHandleConfiguredSource> pool = new PromisePool<AsyncOperationHandleConfiguredSource>();
|
||||
|
||||
AsyncOperationHandle handle;
|
||||
IProgress<float> progress;
|
||||
CancellationToken cancellationToken;
|
||||
|
||||
UniTaskCompletionSourceCore<AsyncUnit> core;
|
||||
|
||||
AsyncOperationHandleConfiguredSource()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public static IUniTaskSource Create(AsyncOperationHandle handle, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, out short token)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
|
||||
}
|
||||
|
||||
var result = pool.TryRent() ?? new AsyncOperationHandleConfiguredSource();
|
||||
|
||||
result.handle = handle;
|
||||
result.progress = progress;
|
||||
result.cancellationToken = cancellationToken;
|
||||
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
PlayerLoopHelper.AddAction(timing, result);
|
||||
|
||||
token = result.core.Version;
|
||||
return result;
|
||||
}
|
||||
|
||||
public void GetResult(short token)
|
||||
{
|
||||
try
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.GetResult(token);
|
||||
}
|
||||
finally
|
||||
{
|
||||
pool.TryReturn(this);
|
||||
}
|
||||
}
|
||||
|
||||
public UniTaskStatus GetStatus(short token)
|
||||
{
|
||||
return core.GetStatus(token);
|
||||
}
|
||||
|
||||
public UniTaskStatus UnsafeGetStatus()
|
||||
{
|
||||
return core.UnsafeGetStatus();
|
||||
}
|
||||
|
||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||
{
|
||||
core.OnCompleted(continuation, state, token);
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
core.TrySetCanceled(cancellationToken);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (progress != null)
|
||||
{
|
||||
progress.Report(handle.PercentComplete);
|
||||
}
|
||||
|
||||
if (handle.IsDone)
|
||||
{
|
||||
if (handle.Status == AsyncOperationStatus.Failed)
|
||||
{
|
||||
core.TrySetException(handle.OperationException);
|
||||
}
|
||||
else
|
||||
{
|
||||
core.TrySetResult(AsyncUnit.Default);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
core.Reset();
|
||||
handle = default;
|
||||
progress = default;
|
||||
cancellationToken = default;
|
||||
}
|
||||
|
||||
~AsyncOperationHandleConfiguredSource()
|
||||
{
|
||||
if (pool.TryReturn(this))
|
||||
{
|
||||
GC.ReRegisterForFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region AsyncOperationHandle_T
|
||||
|
||||
public static AsyncOperationHandleAwaiter<T> GetAwaiter<T>(this AsyncOperationHandle<T> handle)
|
||||
{
|
||||
return new AsyncOperationHandleAwaiter<T>(handle);
|
||||
}
|
||||
|
||||
public static UniTask<T> ToUniTask<T>(this AsyncOperationHandle<T> handle)
|
||||
{
|
||||
return new UniTask<T>(AsyncOperationHandleConfiguredSource<T>.Create(handle, PlayerLoopTiming.Update, null, CancellationToken.None, out var token), token);
|
||||
}
|
||||
|
||||
public static UniTask<T> ConfigureAwait<T>(this AsyncOperationHandle<T> handle, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellation = default(CancellationToken))
|
||||
{
|
||||
return new UniTask<T>(AsyncOperationHandleConfiguredSource<T>.Create(handle, timing, progress, cancellation, out var token), token);
|
||||
}
|
||||
|
||||
public struct AsyncOperationHandleAwaiter<T> : ICriticalNotifyCompletion
|
||||
{
|
||||
AsyncOperationHandle<T> handle;
|
||||
Action<AsyncOperationHandle> continuationAction;
|
||||
|
||||
public AsyncOperationHandleAwaiter(AsyncOperationHandle<T> handle)
|
||||
{
|
||||
this.handle = handle;
|
||||
this.continuationAction = null;
|
||||
}
|
||||
|
||||
public bool IsCompleted => handle.IsDone;
|
||||
|
||||
public T GetResult()
|
||||
{
|
||||
if (continuationAction != null)
|
||||
{
|
||||
handle.CompletedTypeless -= continuationAction;
|
||||
continuationAction = null;
|
||||
}
|
||||
|
||||
if (handle.Status == AsyncOperationStatus.Failed)
|
||||
{
|
||||
var e = handle.OperationException;
|
||||
handle = default;
|
||||
ExceptionDispatchInfo.Capture(e).Throw();
|
||||
}
|
||||
|
||||
var result = handle.Result;
|
||||
handle = default;
|
||||
return result;
|
||||
}
|
||||
|
||||
public void OnCompleted(Action continuation)
|
||||
{
|
||||
UnsafeOnCompleted(continuation);
|
||||
}
|
||||
|
||||
public void UnsafeOnCompleted(Action continuation)
|
||||
{
|
||||
Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction);
|
||||
continuationAction = continuation.AsFuncOfT<AsyncOperationHandle>(); // allocate delegate.
|
||||
handle.CompletedTypeless += continuationAction;
|
||||
}
|
||||
}
|
||||
|
||||
class AsyncOperationHandleConfiguredSource<T> : IUniTaskSource<T>, IPlayerLoopItem, IPromisePoolItem
|
||||
{
|
||||
static readonly PromisePool<AsyncOperationHandleConfiguredSource<T>> pool = new PromisePool<AsyncOperationHandleConfiguredSource<T>>();
|
||||
|
||||
AsyncOperationHandle<T> handle;
|
||||
IProgress<float> progress;
|
||||
CancellationToken cancellationToken;
|
||||
|
||||
UniTaskCompletionSourceCore<T> core;
|
||||
|
||||
AsyncOperationHandleConfiguredSource()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public static IUniTaskSource<T> Create(AsyncOperationHandle<T> handle, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, out short token)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return AutoResetUniTaskCompletionSource<T>.CreateFromCanceled(cancellationToken, out token);
|
||||
}
|
||||
|
||||
var result = pool.TryRent() ?? new AsyncOperationHandleConfiguredSource<T>();
|
||||
|
||||
result.handle = handle;
|
||||
result.progress = progress;
|
||||
result.cancellationToken = cancellationToken;
|
||||
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
PlayerLoopHelper.AddAction(timing, result);
|
||||
|
||||
token = result.core.Version;
|
||||
return result;
|
||||
}
|
||||
|
||||
public T GetResult(short token)
|
||||
{
|
||||
try
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
|
||||
return core.GetResult(token);
|
||||
}
|
||||
finally
|
||||
{
|
||||
pool.TryReturn(this);
|
||||
}
|
||||
}
|
||||
|
||||
void IUniTaskSource.GetResult(short token)
|
||||
{
|
||||
GetResult(token);
|
||||
}
|
||||
|
||||
public UniTaskStatus GetStatus(short token)
|
||||
{
|
||||
return core.GetStatus(token);
|
||||
}
|
||||
|
||||
public UniTaskStatus UnsafeGetStatus()
|
||||
{
|
||||
return core.UnsafeGetStatus();
|
||||
}
|
||||
|
||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||
{
|
||||
core.OnCompleted(continuation, state, token);
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
core.TrySetCanceled(cancellationToken);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (progress != null)
|
||||
{
|
||||
progress.Report(handle.PercentComplete);
|
||||
}
|
||||
|
||||
if (handle.IsDone)
|
||||
{
|
||||
if (handle.Status == AsyncOperationStatus.Failed)
|
||||
{
|
||||
core.TrySetException(handle.OperationException);
|
||||
}
|
||||
else
|
||||
{
|
||||
core.TrySetResult(handle.Result);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
core.Reset();
|
||||
handle = default;
|
||||
progress = default;
|
||||
cancellationToken = default;
|
||||
}
|
||||
|
||||
~AsyncOperationHandleConfiguredSource()
|
||||
{
|
||||
if (pool.TryReturn(this))
|
||||
{
|
||||
GC.ReRegisterForFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,66 +0,0 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
public enum UniTaskStatus
|
||||
{
|
||||
/// <summary>The operation has not yet completed.</summary>
|
||||
Pending = 0,
|
||||
/// <summary>The operation completed successfully.</summary>
|
||||
Succeeded = 1,
|
||||
/// <summary>The operation completed with an error.</summary>
|
||||
Faulted = 2,
|
||||
/// <summary>The operation completed due to cancellation.</summary>
|
||||
Canceled = 3
|
||||
}
|
||||
|
||||
// similar as IValueTaskSource
|
||||
public interface IUniTaskSource
|
||||
{
|
||||
UniTaskStatus GetStatus(short token);
|
||||
void OnCompleted(Action<object> continuation, object state, short token);
|
||||
void GetResult(short token);
|
||||
|
||||
UniTaskStatus UnsafeGetStatus(); // only for debug use.
|
||||
}
|
||||
|
||||
public interface IUniTaskSource<out T> : IUniTaskSource
|
||||
{
|
||||
new T GetResult(short token);
|
||||
}
|
||||
|
||||
public static class UniTaskStatusExtensions
|
||||
{
|
||||
/// <summary>status != Pending.</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsCompleted(this UniTaskStatus status)
|
||||
{
|
||||
return status != UniTaskStatus.Pending;
|
||||
}
|
||||
|
||||
/// <summary>status == Succeeded.</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsCompletedSuccessfully(this UniTaskStatus status)
|
||||
{
|
||||
return status == UniTaskStatus.Succeeded;
|
||||
}
|
||||
|
||||
/// <summary>status == Canceled.</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsCanceled(this UniTaskStatus status)
|
||||
{
|
||||
return status == UniTaskStatus.Canceled;
|
||||
}
|
||||
|
||||
/// <summary>status == Faulted.</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsFaulted(this UniTaskStatus status)
|
||||
{
|
||||
return status == UniTaskStatus.Faulted;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
#if NET_4_6 || NET_STANDARD_2_0 || CSHARP_7_OR_LATER
|
||||
|
||||
using System;
|
||||
|
||||
namespace Cysharp.Threading.Tasks.Internal
|
||||
{
|
||||
internal static class FuncExtensions
|
||||
{
|
||||
// avoid lambda capture
|
||||
|
||||
internal static Action<T> AsFuncOfT<T>(this Action action)
|
||||
{
|
||||
return new Action<T>(action.Invoke);
|
||||
}
|
||||
|
||||
static void Invoke<T>(this Action action, T unused)
|
||||
{
|
||||
action();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,53 +0,0 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace Cysharp.Threading.Tasks.Internal
|
||||
{
|
||||
internal interface IPromisePoolItem
|
||||
{
|
||||
void Reset();
|
||||
}
|
||||
|
||||
internal class PromisePool<T>
|
||||
where T : class, IPromisePoolItem
|
||||
{
|
||||
int count = 0;
|
||||
readonly ConcurrentQueue<T> queue = new ConcurrentQueue<T>();
|
||||
readonly int maxSize;
|
||||
|
||||
public PromisePool(int maxSize = 256)
|
||||
{
|
||||
this.maxSize = maxSize;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public T TryRent()
|
||||
{
|
||||
if (queue.TryDequeue(out var value))
|
||||
{
|
||||
Interlocked.Decrement(ref count);
|
||||
return value;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryReturn(T value)
|
||||
{
|
||||
value.Reset(); // reset when return.
|
||||
|
||||
if (count < maxSize)
|
||||
{
|
||||
queue.Enqueue(value);
|
||||
Interlocked.Increment(ref count);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
8
src/UniTask/Assets/Plugins/UniTask/Runtime.meta
Normal file
8
src/UniTask/Assets/Plugins/UniTask/Runtime.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aa765154468d4b34eb34304100d39e64
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,375 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
public interface IReadOnlyAsyncReactiveProperty<T> : IUniTaskAsyncEnumerable<T>
|
||||
{
|
||||
T Value { get; }
|
||||
IUniTaskAsyncEnumerable<T> WithoutCurrent();
|
||||
}
|
||||
|
||||
public interface IAsyncReactiveProperty<T> : IReadOnlyAsyncReactiveProperty<T>
|
||||
{
|
||||
new T Value { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class AsyncReactiveProperty<T> : IAsyncReactiveProperty<T>, IDisposable
|
||||
{
|
||||
TriggerEvent<T> triggerEvent;
|
||||
|
||||
#if UNITY_2018_3_OR_NEWER
|
||||
[UnityEngine.SerializeField]
|
||||
#endif
|
||||
T latestValue;
|
||||
|
||||
public T Value
|
||||
{
|
||||
get
|
||||
{
|
||||
return latestValue;
|
||||
}
|
||||
set
|
||||
{
|
||||
this.latestValue = value;
|
||||
triggerEvent.SetResult(value);
|
||||
}
|
||||
}
|
||||
|
||||
public AsyncReactiveProperty(T value)
|
||||
{
|
||||
this.latestValue = value;
|
||||
this.triggerEvent = default;
|
||||
}
|
||||
|
||||
public IUniTaskAsyncEnumerable<T> WithoutCurrent()
|
||||
{
|
||||
return new WithoutCurrentEnumerable(this);
|
||||
}
|
||||
|
||||
public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken)
|
||||
{
|
||||
return new Enumerator(this, cancellationToken, true);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
triggerEvent.SetCompleted();
|
||||
}
|
||||
|
||||
public static implicit operator T(AsyncReactiveProperty<T> value)
|
||||
{
|
||||
return value.Value;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (isValueType) return latestValue.ToString();
|
||||
return latestValue?.ToString();
|
||||
}
|
||||
|
||||
static bool isValueType;
|
||||
|
||||
static AsyncReactiveProperty()
|
||||
{
|
||||
isValueType = typeof(T).IsValueType;
|
||||
}
|
||||
|
||||
class WithoutCurrentEnumerable : IUniTaskAsyncEnumerable<T>
|
||||
{
|
||||
readonly AsyncReactiveProperty<T> parent;
|
||||
|
||||
public WithoutCurrentEnumerable(AsyncReactiveProperty<T> parent)
|
||||
{
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return new Enumerator(parent, cancellationToken, false);
|
||||
}
|
||||
}
|
||||
|
||||
sealed class Enumerator : MoveNextSource, IUniTaskAsyncEnumerator<T>, ITriggerHandler<T>
|
||||
{
|
||||
static Action<object> cancellationCallback = CancellationCallback;
|
||||
|
||||
readonly AsyncReactiveProperty<T> parent;
|
||||
readonly CancellationToken cancellationToken;
|
||||
readonly CancellationTokenRegistration cancellationTokenRegistration;
|
||||
T value;
|
||||
bool isDisposed;
|
||||
bool firstCall;
|
||||
|
||||
public Enumerator(AsyncReactiveProperty<T> parent, CancellationToken cancellationToken, bool publishCurrentValue)
|
||||
{
|
||||
this.parent = parent;
|
||||
this.cancellationToken = cancellationToken;
|
||||
this.firstCall = publishCurrentValue;
|
||||
|
||||
parent.triggerEvent.Add(this);
|
||||
TaskTracker.TrackActiveTask(this, 3);
|
||||
|
||||
if (cancellationToken.CanBeCanceled)
|
||||
{
|
||||
cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, this);
|
||||
}
|
||||
}
|
||||
|
||||
public T Current => value;
|
||||
|
||||
ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; }
|
||||
ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; }
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
// raise latest value on first call.
|
||||
if (firstCall)
|
||||
{
|
||||
firstCall = false;
|
||||
value = parent.Value;
|
||||
return CompletedTasks.True;
|
||||
}
|
||||
|
||||
completionSource.Reset();
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
public UniTask DisposeAsync()
|
||||
{
|
||||
if (!isDisposed)
|
||||
{
|
||||
isDisposed = true;
|
||||
TaskTracker.RemoveTracking(this);
|
||||
completionSource.TrySetCanceled(cancellationToken);
|
||||
parent.triggerEvent.Remove(this);
|
||||
}
|
||||
return default;
|
||||
}
|
||||
|
||||
public void OnNext(T value)
|
||||
{
|
||||
this.value = value;
|
||||
completionSource.TrySetResult(true);
|
||||
}
|
||||
|
||||
public void OnCanceled(CancellationToken cancellationToken)
|
||||
{
|
||||
DisposeAsync().Forget();
|
||||
}
|
||||
|
||||
public void OnCompleted()
|
||||
{
|
||||
completionSource.TrySetResult(false);
|
||||
}
|
||||
|
||||
public void OnError(Exception ex)
|
||||
{
|
||||
completionSource.TrySetException(ex);
|
||||
}
|
||||
|
||||
static void CancellationCallback(object state)
|
||||
{
|
||||
var self = (Enumerator)state;
|
||||
self.DisposeAsync().Forget();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ReadOnlyAsyncReactiveProperty<T> : IReadOnlyAsyncReactiveProperty<T>, IDisposable
|
||||
{
|
||||
TriggerEvent<T> triggerEvent;
|
||||
|
||||
T latestValue;
|
||||
IUniTaskAsyncEnumerator<T> enumerator;
|
||||
|
||||
public T Value
|
||||
{
|
||||
get
|
||||
{
|
||||
return latestValue;
|
||||
}
|
||||
}
|
||||
|
||||
public ReadOnlyAsyncReactiveProperty(T initialValue, IUniTaskAsyncEnumerable<T> source, CancellationToken cancellationToken)
|
||||
{
|
||||
latestValue = initialValue;
|
||||
ConsumeEnumerator(source, cancellationToken).Forget();
|
||||
}
|
||||
|
||||
public ReadOnlyAsyncReactiveProperty(IUniTaskAsyncEnumerable<T> source, CancellationToken cancellationToken)
|
||||
{
|
||||
ConsumeEnumerator(source, cancellationToken).Forget();
|
||||
}
|
||||
|
||||
async UniTaskVoid ConsumeEnumerator(IUniTaskAsyncEnumerable<T> source, CancellationToken cancellationToken)
|
||||
{
|
||||
enumerator = source.GetAsyncEnumerator(cancellationToken);
|
||||
try
|
||||
{
|
||||
while (await enumerator.MoveNextAsync())
|
||||
{
|
||||
var value = enumerator.Current;
|
||||
this.latestValue = value;
|
||||
triggerEvent.SetResult(value);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
await enumerator.DisposeAsync();
|
||||
enumerator = null;
|
||||
}
|
||||
}
|
||||
|
||||
public IUniTaskAsyncEnumerable<T> WithoutCurrent()
|
||||
{
|
||||
return new WithoutCurrentEnumerable(this);
|
||||
}
|
||||
|
||||
public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken)
|
||||
{
|
||||
return new Enumerator(this, cancellationToken, true);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (enumerator != null)
|
||||
{
|
||||
enumerator.DisposeAsync().Forget();
|
||||
}
|
||||
|
||||
triggerEvent.SetCompleted();
|
||||
}
|
||||
|
||||
public static implicit operator T(ReadOnlyAsyncReactiveProperty<T> value)
|
||||
{
|
||||
return value.Value;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (isValueType) return latestValue.ToString();
|
||||
return latestValue?.ToString();
|
||||
}
|
||||
|
||||
static bool isValueType;
|
||||
|
||||
static ReadOnlyAsyncReactiveProperty()
|
||||
{
|
||||
isValueType = typeof(T).IsValueType;
|
||||
}
|
||||
|
||||
class WithoutCurrentEnumerable : IUniTaskAsyncEnumerable<T>
|
||||
{
|
||||
readonly ReadOnlyAsyncReactiveProperty<T> parent;
|
||||
|
||||
public WithoutCurrentEnumerable(ReadOnlyAsyncReactiveProperty<T> parent)
|
||||
{
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return new Enumerator(parent, cancellationToken, false);
|
||||
}
|
||||
}
|
||||
|
||||
sealed class Enumerator : MoveNextSource, IUniTaskAsyncEnumerator<T>, ITriggerHandler<T>
|
||||
{
|
||||
static Action<object> cancellationCallback = CancellationCallback;
|
||||
|
||||
readonly ReadOnlyAsyncReactiveProperty<T> parent;
|
||||
readonly CancellationToken cancellationToken;
|
||||
readonly CancellationTokenRegistration cancellationTokenRegistration;
|
||||
T value;
|
||||
bool isDisposed;
|
||||
bool firstCall;
|
||||
|
||||
public Enumerator(ReadOnlyAsyncReactiveProperty<T> parent, CancellationToken cancellationToken, bool publishCurrentValue)
|
||||
{
|
||||
this.parent = parent;
|
||||
this.cancellationToken = cancellationToken;
|
||||
this.firstCall = publishCurrentValue;
|
||||
|
||||
parent.triggerEvent.Add(this);
|
||||
TaskTracker.TrackActiveTask(this, 3);
|
||||
|
||||
if (cancellationToken.CanBeCanceled)
|
||||
{
|
||||
cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, this);
|
||||
}
|
||||
}
|
||||
|
||||
public T Current => value;
|
||||
ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; }
|
||||
ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; }
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
// raise latest value on first call.
|
||||
if (firstCall)
|
||||
{
|
||||
firstCall = false;
|
||||
value = parent.Value;
|
||||
return CompletedTasks.True;
|
||||
}
|
||||
|
||||
completionSource.Reset();
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
public UniTask DisposeAsync()
|
||||
{
|
||||
if (!isDisposed)
|
||||
{
|
||||
isDisposed = true;
|
||||
TaskTracker.RemoveTracking(this);
|
||||
completionSource.TrySetCanceled(cancellationToken);
|
||||
parent.triggerEvent.Remove(this);
|
||||
}
|
||||
return default;
|
||||
}
|
||||
|
||||
public void OnNext(T value)
|
||||
{
|
||||
this.value = value;
|
||||
completionSource.TrySetResult(true);
|
||||
}
|
||||
|
||||
public void OnCanceled(CancellationToken cancellationToken)
|
||||
{
|
||||
DisposeAsync().Forget();
|
||||
}
|
||||
|
||||
public void OnCompleted()
|
||||
{
|
||||
completionSource.TrySetResult(false);
|
||||
}
|
||||
|
||||
public void OnError(Exception ex)
|
||||
{
|
||||
completionSource.TrySetException(ex);
|
||||
}
|
||||
|
||||
static void CancellationCallback(object state)
|
||||
{
|
||||
var self = (Enumerator)state;
|
||||
self.DisposeAsync().Forget();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class StateExtensions
|
||||
{
|
||||
public static ReadOnlyAsyncReactiveProperty<T> ToReadOnlyAsyncReactiveProperty<T>(this IUniTaskAsyncEnumerable<T> source, CancellationToken cancellationToken)
|
||||
{
|
||||
return new ReadOnlyAsyncReactiveProperty<T>(source, cancellationToken);
|
||||
}
|
||||
|
||||
public static ReadOnlyAsyncReactiveProperty<T> ToReadOnlyAsyncReactiveProperty<T>(this IUniTaskAsyncEnumerable<T> source, T initialValue, CancellationToken cancellationToken)
|
||||
{
|
||||
return new ReadOnlyAsyncReactiveProperty<T>(initialValue, source, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fcb1f7467a3e2b64c8a016c8aee2f9b4
|
||||
guid: 8ef320b87f537ee4fb2282e765dc6166
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
@@ -4,7 +4,7 @@ using System;
|
||||
|
||||
namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
public struct AsyncUnit : IEquatable<AsyncUnit>
|
||||
public readonly struct AsyncUnit : IEquatable<AsyncUnit>
|
||||
{
|
||||
public static readonly AsyncUnit Default = new AsyncUnit();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace Cysharp.Threading.Tasks
|
||||
@@ -9,15 +10,15 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
static readonly Action<object> cancellationTokenCallback = Callback;
|
||||
|
||||
public static (UniTask, CancellationTokenRegistration) ToUniTask(this CancellationToken cts)
|
||||
public static (UniTask, CancellationTokenRegistration) ToUniTask(this CancellationToken cancellationToken)
|
||||
{
|
||||
if (cts.IsCancellationRequested)
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return (UniTask.FromCanceled(cts), default(CancellationTokenRegistration));
|
||||
return (UniTask.FromCanceled(cancellationToken), default(CancellationTokenRegistration));
|
||||
}
|
||||
|
||||
var promise = new UniTaskCompletionSource();
|
||||
return (promise.Task, cts.RegisterWithoutCaptureExecutionContext(cancellationTokenCallback, promise));
|
||||
return (promise.Task, cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationTokenCallback, promise));
|
||||
}
|
||||
|
||||
static void Callback(object state)
|
||||
@@ -26,6 +27,11 @@ namespace Cysharp.Threading.Tasks
|
||||
promise.TrySetResult();
|
||||
}
|
||||
|
||||
public static CancellationTokenAwaitable WaitUntilCanceled(this CancellationToken cancellationToken)
|
||||
{
|
||||
return new CancellationTokenAwaitable(cancellationToken);
|
||||
}
|
||||
|
||||
public static CancellationTokenRegistration RegisterWithoutCaptureExecutionContext(this CancellationToken cancellationToken, Action callback)
|
||||
{
|
||||
var restoreFlow = false;
|
||||
@@ -70,5 +76,46 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct CancellationTokenAwaitable
|
||||
{
|
||||
CancellationToken cancellationToken;
|
||||
|
||||
public CancellationTokenAwaitable(CancellationToken cancellationToken)
|
||||
{
|
||||
this.cancellationToken = cancellationToken;
|
||||
}
|
||||
|
||||
public Awaiter GetAwaiter()
|
||||
{
|
||||
return new Awaiter(cancellationToken);
|
||||
}
|
||||
|
||||
public struct Awaiter : ICriticalNotifyCompletion
|
||||
{
|
||||
CancellationToken cancellationToken;
|
||||
|
||||
public Awaiter(CancellationToken cancellationToken)
|
||||
{
|
||||
this.cancellationToken = cancellationToken;
|
||||
}
|
||||
|
||||
public bool IsCompleted => !cancellationToken.CanBeCanceled || cancellationToken.IsCancellationRequested;
|
||||
|
||||
public void GetResult()
|
||||
{
|
||||
}
|
||||
|
||||
public void OnCompleted(Action continuation)
|
||||
{
|
||||
UnsafeOnCompleted(continuation);
|
||||
}
|
||||
|
||||
public void UnsafeOnCompleted(Action continuation)
|
||||
{
|
||||
cancellationToken.RegisterWithoutCaptureExecutionContext(continuation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
450
src/UniTask/Assets/Plugins/UniTask/Runtime/Channel.cs
Normal file
450
src/UniTask/Assets/Plugins/UniTask/Runtime/Channel.cs
Normal file
@@ -0,0 +1,450 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
public static class Channel
|
||||
{
|
||||
public static Channel<T> CreateSingleConsumerUnbounded<T>()
|
||||
{
|
||||
return new SingleConsumerUnboundedChannel<T>();
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class Channel<TWrite, TRead>
|
||||
{
|
||||
public ChannelReader<TRead> Reader { get; protected set; }
|
||||
public ChannelWriter<TWrite> Writer { get; protected set; }
|
||||
|
||||
public static implicit operator ChannelReader<TRead>(Channel<TWrite, TRead> channel) => channel.Reader;
|
||||
public static implicit operator ChannelWriter<TWrite>(Channel<TWrite, TRead> channel) => channel.Writer;
|
||||
}
|
||||
|
||||
public abstract class Channel<T> : Channel<T, T>
|
||||
{
|
||||
}
|
||||
|
||||
public abstract class ChannelReader<T>
|
||||
{
|
||||
public abstract bool TryRead(out T item);
|
||||
public abstract UniTask<bool> WaitToReadAsync(CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
public abstract UniTask Completion { get; }
|
||||
|
||||
public virtual UniTask<T> ReadAsync(CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if (this.TryRead(out var item))
|
||||
{
|
||||
return UniTask.FromResult(item);
|
||||
}
|
||||
|
||||
return ReadAsyncCore(cancellationToken);
|
||||
}
|
||||
|
||||
async UniTask<T> ReadAsyncCore(CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if (await WaitToReadAsync(cancellationToken))
|
||||
{
|
||||
if (TryRead(out var item))
|
||||
{
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
throw new ChannelClosedException();
|
||||
}
|
||||
|
||||
public abstract IUniTaskAsyncEnumerable<T> ReadAllAsync(CancellationToken cancellationToken = default(CancellationToken));
|
||||
}
|
||||
|
||||
public abstract class ChannelWriter<T>
|
||||
{
|
||||
public abstract bool TryWrite(T item);
|
||||
public abstract bool TryComplete(Exception error = null);
|
||||
|
||||
public void Complete(Exception error = null)
|
||||
{
|
||||
if (!TryComplete(error))
|
||||
{
|
||||
throw new ChannelClosedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public partial class ChannelClosedException : InvalidOperationException
|
||||
{
|
||||
public ChannelClosedException() :
|
||||
base("Channel is already closed.")
|
||||
{ }
|
||||
|
||||
public ChannelClosedException(string message) : base(message) { }
|
||||
|
||||
public ChannelClosedException(Exception innerException) :
|
||||
base("Channel is already closed", innerException)
|
||||
{ }
|
||||
|
||||
public ChannelClosedException(string message, Exception innerException) : base(message, innerException) { }
|
||||
}
|
||||
|
||||
internal class SingleConsumerUnboundedChannel<T> : Channel<T>
|
||||
{
|
||||
readonly Queue<T> items;
|
||||
readonly SingleConsumerUnboundedChannelReader readerSource;
|
||||
UniTaskCompletionSource completedTaskSource;
|
||||
UniTask completedTask;
|
||||
|
||||
Exception completionError;
|
||||
bool closed;
|
||||
|
||||
public SingleConsumerUnboundedChannel()
|
||||
{
|
||||
items = new Queue<T>();
|
||||
Writer = new SingleConsumerUnboundedChannelWriter(this);
|
||||
readerSource = new SingleConsumerUnboundedChannelReader(this);
|
||||
Reader = readerSource;
|
||||
}
|
||||
|
||||
sealed class SingleConsumerUnboundedChannelWriter : ChannelWriter<T>
|
||||
{
|
||||
readonly SingleConsumerUnboundedChannel<T> parent;
|
||||
|
||||
public SingleConsumerUnboundedChannelWriter(SingleConsumerUnboundedChannel<T> parent)
|
||||
{
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public override bool TryWrite(T item)
|
||||
{
|
||||
bool waiting;
|
||||
lock (parent.items)
|
||||
{
|
||||
if (parent.closed) return false;
|
||||
|
||||
parent.items.Enqueue(item);
|
||||
waiting = parent.readerSource.isWaiting;
|
||||
}
|
||||
|
||||
if (waiting)
|
||||
{
|
||||
parent.readerSource.SingalContinuation();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool TryComplete(Exception error = null)
|
||||
{
|
||||
bool waiting;
|
||||
lock (parent.items)
|
||||
{
|
||||
if (parent.closed) return false;
|
||||
parent.closed = true;
|
||||
waiting = parent.readerSource.isWaiting;
|
||||
|
||||
if (parent.items.Count == 0)
|
||||
{
|
||||
if (error == null)
|
||||
{
|
||||
if (parent.completedTaskSource != null)
|
||||
{
|
||||
parent.completedTaskSource.TrySetResult();
|
||||
}
|
||||
else
|
||||
{
|
||||
parent.completedTask = UniTask.CompletedTask;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (parent.completedTaskSource != null)
|
||||
{
|
||||
parent.completedTaskSource.TrySetException(error);
|
||||
}
|
||||
else
|
||||
{
|
||||
parent.completedTask = UniTask.FromException(error);
|
||||
}
|
||||
}
|
||||
|
||||
if (waiting)
|
||||
{
|
||||
parent.readerSource.SingalCompleted(error);
|
||||
}
|
||||
}
|
||||
|
||||
parent.completionError = error;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
sealed class SingleConsumerUnboundedChannelReader : ChannelReader<T>, IUniTaskSource<bool>
|
||||
{
|
||||
readonly Action<object> CancellationCallbackDelegate = CancellationCallback;
|
||||
readonly SingleConsumerUnboundedChannel<T> parent;
|
||||
|
||||
CancellationToken cancellationToken;
|
||||
CancellationTokenRegistration cancellationTokenRegistration;
|
||||
UniTaskCompletionSourceCore<bool> core;
|
||||
internal bool isWaiting;
|
||||
|
||||
public SingleConsumerUnboundedChannelReader(SingleConsumerUnboundedChannel<T> parent)
|
||||
{
|
||||
this.parent = parent;
|
||||
|
||||
TaskTracker.TrackActiveTask(this, 4);
|
||||
}
|
||||
|
||||
public override UniTask Completion
|
||||
{
|
||||
get
|
||||
{
|
||||
if (parent.completedTaskSource != null) return parent.completedTaskSource.Task;
|
||||
|
||||
if (parent.closed)
|
||||
{
|
||||
return parent.completedTask;
|
||||
}
|
||||
|
||||
parent.completedTaskSource = new UniTaskCompletionSource();
|
||||
return parent.completedTaskSource.Task;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool TryRead(out T item)
|
||||
{
|
||||
lock (parent.items)
|
||||
{
|
||||
if (parent.items.Count != 0)
|
||||
{
|
||||
item = parent.items.Dequeue();
|
||||
|
||||
// complete when all value was consumed.
|
||||
if (parent.closed && parent.items.Count == 0)
|
||||
{
|
||||
if (parent.completionError != null)
|
||||
{
|
||||
if (parent.completedTaskSource != null)
|
||||
{
|
||||
parent.completedTaskSource.TrySetException(parent.completionError);
|
||||
}
|
||||
else
|
||||
{
|
||||
parent.completedTask = UniTask.FromException(parent.completionError);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (parent.completedTaskSource != null)
|
||||
{
|
||||
parent.completedTaskSource.TrySetResult();
|
||||
}
|
||||
else
|
||||
{
|
||||
parent.completedTask = UniTask.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
item = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override UniTask<bool> WaitToReadAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return UniTask.FromCanceled<bool>(cancellationToken);
|
||||
}
|
||||
|
||||
lock (parent.items)
|
||||
{
|
||||
if (parent.items.Count != 0)
|
||||
{
|
||||
return CompletedTasks.True;
|
||||
}
|
||||
|
||||
if (parent.closed)
|
||||
{
|
||||
if (parent.completionError == null)
|
||||
{
|
||||
return CompletedTasks.False;
|
||||
}
|
||||
else
|
||||
{
|
||||
return UniTask.FromException<bool>(parent.completionError);
|
||||
}
|
||||
}
|
||||
|
||||
cancellationTokenRegistration.Dispose();
|
||||
|
||||
core.Reset();
|
||||
isWaiting = true;
|
||||
|
||||
this.cancellationToken = cancellationToken;
|
||||
if (this.cancellationToken.CanBeCanceled)
|
||||
{
|
||||
cancellationTokenRegistration = this.cancellationToken.RegisterWithoutCaptureExecutionContext(CancellationCallbackDelegate, this);
|
||||
}
|
||||
|
||||
return new UniTask<bool>(this, core.Version);
|
||||
}
|
||||
}
|
||||
|
||||
public void SingalContinuation()
|
||||
{
|
||||
core.TrySetResult(true);
|
||||
}
|
||||
|
||||
public void SingalCancellation(CancellationToken cancellationToken)
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.TrySetCanceled(cancellationToken);
|
||||
}
|
||||
|
||||
public void SingalCompleted(Exception error)
|
||||
{
|
||||
if (error != null)
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.TrySetException(error);
|
||||
}
|
||||
else
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.TrySetResult(false);
|
||||
}
|
||||
}
|
||||
|
||||
public override IUniTaskAsyncEnumerable<T> ReadAllAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return new ReadAllAsyncEnumerable(this, cancellationToken);
|
||||
}
|
||||
|
||||
bool IUniTaskSource<bool>.GetResult(short token)
|
||||
{
|
||||
return core.GetResult(token);
|
||||
}
|
||||
|
||||
void IUniTaskSource.GetResult(short token)
|
||||
{
|
||||
core.GetResult(token);
|
||||
}
|
||||
|
||||
UniTaskStatus IUniTaskSource.GetStatus(short token)
|
||||
{
|
||||
return core.GetStatus(token);
|
||||
}
|
||||
|
||||
void IUniTaskSource.OnCompleted(Action<object> continuation, object state, short token)
|
||||
{
|
||||
core.OnCompleted(continuation, state, token);
|
||||
}
|
||||
|
||||
UniTaskStatus IUniTaskSource.UnsafeGetStatus()
|
||||
{
|
||||
return core.UnsafeGetStatus();
|
||||
}
|
||||
|
||||
static void CancellationCallback(object state)
|
||||
{
|
||||
var self = (SingleConsumerUnboundedChannelReader)state;
|
||||
self.SingalCancellation(self.cancellationToken);
|
||||
}
|
||||
|
||||
sealed class ReadAllAsyncEnumerable : IUniTaskAsyncEnumerable<T>, IUniTaskAsyncEnumerator<T>
|
||||
{
|
||||
readonly Action<object> CancellationCallback1Delegate = CancellationCallback1;
|
||||
readonly Action<object> CancellationCallback2Delegate = CancellationCallback2;
|
||||
|
||||
readonly SingleConsumerUnboundedChannelReader parent;
|
||||
CancellationToken cancellationToken1;
|
||||
CancellationToken cancellationToken2;
|
||||
CancellationTokenRegistration cancellationTokenRegistration1;
|
||||
CancellationTokenRegistration cancellationTokenRegistration2;
|
||||
|
||||
T current;
|
||||
bool cacheValue;
|
||||
bool running;
|
||||
|
||||
public ReadAllAsyncEnumerable(SingleConsumerUnboundedChannelReader parent, CancellationToken cancellationToken)
|
||||
{
|
||||
this.parent = parent;
|
||||
this.cancellationToken1 = cancellationToken;
|
||||
}
|
||||
|
||||
public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (running)
|
||||
{
|
||||
throw new InvalidOperationException("Enumerator is already running, does not allow call GetAsyncEnumerator twice.");
|
||||
}
|
||||
|
||||
if (this.cancellationToken1 != cancellationToken)
|
||||
{
|
||||
this.cancellationToken2 = cancellationToken;
|
||||
}
|
||||
|
||||
if (this.cancellationToken1.CanBeCanceled)
|
||||
{
|
||||
this.cancellationTokenRegistration1 = this.cancellationToken1.RegisterWithoutCaptureExecutionContext(CancellationCallback1Delegate, this);
|
||||
}
|
||||
|
||||
if (this.cancellationToken2.CanBeCanceled)
|
||||
{
|
||||
this.cancellationTokenRegistration2 = this.cancellationToken2.RegisterWithoutCaptureExecutionContext(CancellationCallback2Delegate, this);
|
||||
}
|
||||
|
||||
running = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public T Current
|
||||
{
|
||||
get
|
||||
{
|
||||
if (cacheValue)
|
||||
{
|
||||
return current;
|
||||
}
|
||||
parent.TryRead(out current);
|
||||
return current;
|
||||
}
|
||||
}
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
cacheValue = false;
|
||||
return parent.WaitToReadAsync(CancellationToken.None); // ok to use None, registered in ctor.
|
||||
}
|
||||
|
||||
public UniTask DisposeAsync()
|
||||
{
|
||||
cancellationTokenRegistration1.Dispose();
|
||||
cancellationTokenRegistration2.Dispose();
|
||||
return default;
|
||||
}
|
||||
|
||||
static void CancellationCallback1(object state)
|
||||
{
|
||||
var self = (ReadAllAsyncEnumerable)state;
|
||||
self.parent.SingalCancellation(self.cancellationToken1);
|
||||
}
|
||||
|
||||
static void CancellationCallback2(object state)
|
||||
{
|
||||
var self = (ReadAllAsyncEnumerable)state;
|
||||
self.parent.SingalCancellation(self.cancellationToken2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
src/UniTask/Assets/Plugins/UniTask/Runtime/Channel.cs.meta
Normal file
11
src/UniTask/Assets/Plugins/UniTask/Runtime/Channel.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5ceb3107bbdd1f14eb39091273798360
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -12,9 +12,8 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
public struct AsyncUniTaskMethodBuilder
|
||||
{
|
||||
// cache items.
|
||||
AutoResetUniTaskCompletionSource promise;
|
||||
IMoveNextRunner runner;
|
||||
IStateMachineRunnerPromise runnerPromise;
|
||||
Exception ex;
|
||||
|
||||
// 1. Static Create method.
|
||||
[DebuggerHidden]
|
||||
@@ -31,98 +30,81 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
if (promise != null)
|
||||
if (runnerPromise != null)
|
||||
{
|
||||
return promise.Task;
|
||||
return runnerPromise.Task;
|
||||
}
|
||||
|
||||
if (runner == null)
|
||||
else if (ex != null)
|
||||
{
|
||||
return UniTask.FromException(ex);
|
||||
}
|
||||
else
|
||||
{
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
|
||||
promise = AutoResetUniTaskCompletionSource.Create();
|
||||
return promise.Task;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. SetException
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetException(Exception exception)
|
||||
{
|
||||
// runner is finished, return first.
|
||||
if (runner != null)
|
||||
if (runnerPromise == null)
|
||||
{
|
||||
runner.Return();
|
||||
runner = null;
|
||||
}
|
||||
|
||||
if (promise != null)
|
||||
{
|
||||
promise.TrySetException(exception);
|
||||
ex = exception;
|
||||
}
|
||||
else
|
||||
{
|
||||
promise = AutoResetUniTaskCompletionSource.CreateFromException(exception, out _);
|
||||
runnerPromise.SetException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
// 4. SetResult
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetResult()
|
||||
{
|
||||
// runner is finished, return first.
|
||||
if (runner != null)
|
||||
if (runnerPromise != null)
|
||||
{
|
||||
runner.Return();
|
||||
runner = null;
|
||||
}
|
||||
|
||||
if (promise != null)
|
||||
{
|
||||
promise.TrySetResult();
|
||||
runnerPromise.SetResult();
|
||||
}
|
||||
}
|
||||
|
||||
// 5. AwaitOnCompleted
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
|
||||
where TAwaiter : INotifyCompletion
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
if (promise == null)
|
||||
if (runnerPromise == null)
|
||||
{
|
||||
promise = AutoResetUniTaskCompletionSource.Create();
|
||||
}
|
||||
if (runner == null)
|
||||
{
|
||||
runner = MoveNextRunner<TStateMachine>.Create(ref stateMachine);
|
||||
AsyncUniTask<TStateMachine>.SetStateMachine(ref stateMachine, ref runnerPromise);
|
||||
}
|
||||
|
||||
awaiter.OnCompleted(runner.CallMoveNext);
|
||||
awaiter.OnCompleted(runnerPromise.MoveNext);
|
||||
}
|
||||
|
||||
// 6. AwaitUnsafeOnCompleted
|
||||
[DebuggerHidden]
|
||||
[SecuritySafeCritical]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
|
||||
where TAwaiter : ICriticalNotifyCompletion
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
if (promise == null)
|
||||
if (runnerPromise == null)
|
||||
{
|
||||
promise = AutoResetUniTaskCompletionSource.Create();
|
||||
}
|
||||
if (runner == null)
|
||||
{
|
||||
runner = MoveNextRunner<TStateMachine>.Create(ref stateMachine);
|
||||
AsyncUniTask<TStateMachine>.SetStateMachine(ref stateMachine, ref runnerPromise);
|
||||
}
|
||||
|
||||
awaiter.OnCompleted(runner.CallMoveNext);
|
||||
awaiter.UnsafeOnCompleted(runnerPromise.MoveNext);
|
||||
}
|
||||
|
||||
// 7. Start
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Start<TStateMachine>(ref TStateMachine stateMachine)
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
@@ -156,9 +138,8 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
public struct AsyncUniTaskMethodBuilder<T>
|
||||
{
|
||||
// cache items.
|
||||
AutoResetUniTaskCompletionSource<T> promise;
|
||||
IMoveNextRunner runner;
|
||||
IStateMachineRunnerPromise<T> runnerPromise;
|
||||
Exception ex;
|
||||
T result;
|
||||
|
||||
// 1. Static Create method.
|
||||
@@ -170,106 +151,91 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
}
|
||||
|
||||
// 2. TaskLike Task property.
|
||||
[DebuggerHidden]
|
||||
public UniTask<T> Task
|
||||
{
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
if (promise != null)
|
||||
if (runnerPromise != null)
|
||||
{
|
||||
return promise.Task;
|
||||
return runnerPromise.Task;
|
||||
}
|
||||
|
||||
if (runner == null)
|
||||
else if (ex != null)
|
||||
{
|
||||
return UniTask.FromException<T>(ex);
|
||||
}
|
||||
else
|
||||
{
|
||||
return UniTask.FromResult(result);
|
||||
}
|
||||
|
||||
promise = AutoResetUniTaskCompletionSource<T>.Create();
|
||||
return promise.Task;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. SetException
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetException(Exception exception)
|
||||
{
|
||||
// runner is finished, return first.
|
||||
if (runner != null)
|
||||
if (runnerPromise == null)
|
||||
{
|
||||
runner.Return();
|
||||
runner = null;
|
||||
}
|
||||
|
||||
if (promise == null)
|
||||
{
|
||||
promise = AutoResetUniTaskCompletionSource<T>.CreateFromException(exception, out _);
|
||||
ex = exception;
|
||||
}
|
||||
else
|
||||
{
|
||||
promise.TrySetException(exception);
|
||||
runnerPromise.SetException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
// 4. SetResult
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetResult(T result)
|
||||
{
|
||||
// runner is finished, return first.
|
||||
if (runner != null)
|
||||
{
|
||||
runner.Return();
|
||||
runner = null;
|
||||
}
|
||||
|
||||
if (promise == null)
|
||||
if (runnerPromise == null)
|
||||
{
|
||||
this.result = result;
|
||||
return;
|
||||
}
|
||||
|
||||
promise.TrySetResult(result);
|
||||
else
|
||||
{
|
||||
runnerPromise.SetResult(result);
|
||||
}
|
||||
}
|
||||
|
||||
// 5. AwaitOnCompleted
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
|
||||
where TAwaiter : INotifyCompletion
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
if (promise == null)
|
||||
if (runnerPromise == null)
|
||||
{
|
||||
promise = AutoResetUniTaskCompletionSource<T>.Create();
|
||||
}
|
||||
if (runner == null)
|
||||
{
|
||||
runner = MoveNextRunner<TStateMachine>.Create(ref stateMachine);
|
||||
AsyncUniTask<TStateMachine, T>.SetStateMachine(ref stateMachine, ref runnerPromise);
|
||||
}
|
||||
|
||||
awaiter.OnCompleted(runner.CallMoveNext);
|
||||
awaiter.OnCompleted(runnerPromise.MoveNext);
|
||||
}
|
||||
|
||||
// 6. AwaitUnsafeOnCompleted
|
||||
[DebuggerHidden]
|
||||
[SecuritySafeCritical]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
|
||||
where TAwaiter : ICriticalNotifyCompletion
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
if (promise == null)
|
||||
if (runnerPromise == null)
|
||||
{
|
||||
promise = AutoResetUniTaskCompletionSource<T>.Create();
|
||||
}
|
||||
if (runner == null)
|
||||
{
|
||||
runner = MoveNextRunner<TStateMachine>.Create(ref stateMachine);
|
||||
AsyncUniTask<TStateMachine, T>.SetStateMachine(ref stateMachine, ref runnerPromise);
|
||||
}
|
||||
|
||||
awaiter.OnCompleted(runner.CallMoveNext);
|
||||
awaiter.UnsafeOnCompleted(runnerPromise.MoveNext);
|
||||
}
|
||||
|
||||
// 7. Start
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Start<TStateMachine>(ref TStateMachine stateMachine)
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
@@ -4,13 +4,15 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
|
||||
namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
{
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
public struct AsyncUniTaskVoidMethodBuilder
|
||||
{
|
||||
IMoveNextRunner runner;
|
||||
IStateMachineRunner runner;
|
||||
|
||||
// 1. Static Create method.
|
||||
[DebuggerHidden]
|
||||
@@ -33,6 +35,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
|
||||
// 3. SetException
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetException(Exception exception)
|
||||
{
|
||||
// runner is finished, return first.
|
||||
@@ -47,6 +50,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
|
||||
// 4. SetResult
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetResult()
|
||||
{
|
||||
// runner is finished, return.
|
||||
@@ -59,31 +63,33 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
|
||||
// 5. AwaitOnCompleted
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
|
||||
where TAwaiter : INotifyCompletion
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
if (runner == null)
|
||||
{
|
||||
runner = MoveNextRunner<TStateMachine>.Create(ref stateMachine);
|
||||
AsyncUniTaskVoid<TStateMachine>.SetStateMachine(ref stateMachine, ref runner);
|
||||
}
|
||||
|
||||
awaiter.OnCompleted(runner.CallMoveNext);
|
||||
awaiter.OnCompleted(runner.MoveNext);
|
||||
}
|
||||
|
||||
// 6. AwaitUnsafeOnCompleted
|
||||
[DebuggerHidden]
|
||||
[SecuritySafeCritical]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
|
||||
where TAwaiter : ICriticalNotifyCompletion
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
if (runner == null)
|
||||
{
|
||||
runner = MoveNextRunner<TStateMachine>.Create(ref stateMachine);
|
||||
AsyncUniTaskVoid<TStateMachine>.SetStateMachine(ref stateMachine, ref runner);
|
||||
}
|
||||
|
||||
awaiter.OnCompleted(runner.CallMoveNext);
|
||||
awaiter.UnsafeOnCompleted(runner.MoveNext);
|
||||
}
|
||||
|
||||
// 7. Start
|
||||
@@ -0,0 +1,343 @@
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
|
||||
using Cysharp.Threading.Tasks.Internal;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
{
|
||||
internal interface IStateMachineRunner
|
||||
{
|
||||
Action MoveNext { get; }
|
||||
void Return();
|
||||
}
|
||||
|
||||
internal interface IStateMachineRunnerPromise : IUniTaskSource
|
||||
{
|
||||
Action MoveNext { get; }
|
||||
UniTask Task { get; }
|
||||
void SetResult();
|
||||
void SetException(Exception exception);
|
||||
}
|
||||
|
||||
internal interface IStateMachineRunnerPromise<T> : IUniTaskSource<T>
|
||||
{
|
||||
Action MoveNext { get; }
|
||||
UniTask<T> Task { get; }
|
||||
void SetResult(T result);
|
||||
void SetException(Exception exception);
|
||||
}
|
||||
|
||||
internal static class StateMachineUtility
|
||||
{
|
||||
public static int GetState(IAsyncStateMachine stateMachine)
|
||||
{
|
||||
var info = stateMachine.GetType().GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
|
||||
.First(x => x.Name.EndsWith("__state"));
|
||||
return (int)info.GetValue(stateMachine);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class AsyncUniTaskVoid<TStateMachine> : IStateMachineRunner, ITaskPoolNode<AsyncUniTaskVoid<TStateMachine>>, IUniTaskSource
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
static TaskPool<AsyncUniTaskVoid<TStateMachine>> pool;
|
||||
|
||||
IAsyncStateMachine stateMachine; // unfortunatelly boxed to fix IL2CPP issue.
|
||||
|
||||
public Action MoveNext { get; }
|
||||
|
||||
public AsyncUniTaskVoid()
|
||||
{
|
||||
MoveNext = Run;
|
||||
}
|
||||
|
||||
public static void SetStateMachine(ref TStateMachine stateMachine, ref IStateMachineRunner runnerFieldRef)
|
||||
{
|
||||
if (!pool.TryPop(out var result))
|
||||
{
|
||||
result = new AsyncUniTaskVoid<TStateMachine>();
|
||||
}
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
runnerFieldRef = result; // set runner before copied.
|
||||
result.stateMachine = stateMachine; // copy struct StateMachine(in release build).
|
||||
}
|
||||
|
||||
static AsyncUniTaskVoid()
|
||||
{
|
||||
TaskPool.RegisterSizeGetter(typeof(AsyncUniTaskVoid<TStateMachine>), () => pool.Size);
|
||||
}
|
||||
|
||||
public AsyncUniTaskVoid<TStateMachine> NextNode { get; set; }
|
||||
|
||||
public void Return()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
stateMachine = default;
|
||||
pool.TryPush(this);
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
void Run()
|
||||
{
|
||||
stateMachine.MoveNext();
|
||||
}
|
||||
|
||||
// dummy interface implementation for TaskTracker.
|
||||
|
||||
UniTaskStatus IUniTaskSource.GetStatus(short token)
|
||||
{
|
||||
return UniTaskStatus.Pending;
|
||||
}
|
||||
|
||||
UniTaskStatus IUniTaskSource.UnsafeGetStatus()
|
||||
{
|
||||
return UniTaskStatus.Pending;
|
||||
}
|
||||
|
||||
void IUniTaskSource.OnCompleted(Action<object> continuation, object state, short token)
|
||||
{
|
||||
}
|
||||
|
||||
void IUniTaskSource.GetResult(short token)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class AsyncUniTask<TStateMachine> : IStateMachineRunnerPromise, IUniTaskSource, ITaskPoolNode<AsyncUniTask<TStateMachine>>
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
static TaskPool<AsyncUniTask<TStateMachine>> pool;
|
||||
|
||||
IAsyncStateMachine stateMachine; // unfortunatelly boxed to fix IL2CPP issue.
|
||||
|
||||
public Action MoveNext { get; }
|
||||
|
||||
UniTaskCompletionSourceCore<AsyncUnit> core;
|
||||
|
||||
AsyncUniTask()
|
||||
{
|
||||
MoveNext = Run;
|
||||
}
|
||||
|
||||
public static void SetStateMachine(ref TStateMachine stateMachine, ref IStateMachineRunnerPromise runnerPromiseFieldRef)
|
||||
{
|
||||
if (!pool.TryPop(out var result))
|
||||
{
|
||||
result = new AsyncUniTask<TStateMachine>();
|
||||
}
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
runnerPromiseFieldRef = result; // set runner before copied.
|
||||
result.stateMachine = stateMachine; // copy struct StateMachine(in release build).
|
||||
}
|
||||
|
||||
public AsyncUniTask<TStateMachine> NextNode { get; set; }
|
||||
|
||||
static AsyncUniTask()
|
||||
{
|
||||
TaskPool.RegisterSizeGetter(typeof(AsyncUniTask<TStateMachine>), () => pool.Size);
|
||||
}
|
||||
|
||||
bool TryReturn()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.Reset();
|
||||
stateMachine = default;
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
void Run()
|
||||
{
|
||||
stateMachine.MoveNext();
|
||||
}
|
||||
|
||||
public UniTask Task
|
||||
{
|
||||
[DebuggerHidden]
|
||||
get
|
||||
{
|
||||
return new UniTask(this, core.Version);
|
||||
}
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
public void SetResult()
|
||||
{
|
||||
core.TrySetResult(AsyncUnit.Default);
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
public void SetException(Exception exception)
|
||||
{
|
||||
core.TrySetException(exception);
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
public void GetResult(short token)
|
||||
{
|
||||
try
|
||||
{
|
||||
core.GetResult(token);
|
||||
}
|
||||
finally
|
||||
{
|
||||
TryReturn();
|
||||
}
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
public UniTaskStatus GetStatus(short token)
|
||||
{
|
||||
return core.GetStatus(token);
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
public UniTaskStatus UnsafeGetStatus()
|
||||
{
|
||||
return core.UnsafeGetStatus();
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||
{
|
||||
core.OnCompleted(continuation, state, token);
|
||||
}
|
||||
|
||||
~AsyncUniTask()
|
||||
{
|
||||
if (TryReturn())
|
||||
{
|
||||
GC.ReRegisterForFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class AsyncUniTask<TStateMachine, T> : IStateMachineRunnerPromise<T>, IUniTaskSource<T>, ITaskPoolNode<AsyncUniTask<TStateMachine, T>>
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
static TaskPool<AsyncUniTask<TStateMachine, T>> pool;
|
||||
|
||||
IAsyncStateMachine stateMachine; // unfortunatelly boxed to fix IL2CPP issue.
|
||||
|
||||
public Action MoveNext { get; }
|
||||
|
||||
UniTaskCompletionSourceCore<T> core;
|
||||
|
||||
AsyncUniTask()
|
||||
{
|
||||
MoveNext = Run;
|
||||
}
|
||||
|
||||
public static void SetStateMachine(ref TStateMachine stateMachine, ref IStateMachineRunnerPromise<T> runnerPromiseFieldRef)
|
||||
{
|
||||
if (!pool.TryPop(out var result))
|
||||
{
|
||||
result = new AsyncUniTask<TStateMachine, T>();
|
||||
}
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
runnerPromiseFieldRef = result; // set runner before copied.
|
||||
result.stateMachine = stateMachine; // copy struct StateMachine(in release build).
|
||||
|
||||
// UnityEngine.Debug.Log($"SetStateMachine State:" + StateMachineUtility.GetState(stateMachine));
|
||||
}
|
||||
|
||||
|
||||
public AsyncUniTask<TStateMachine, T> NextNode { get; set; }
|
||||
|
||||
static AsyncUniTask()
|
||||
{
|
||||
TaskPool.RegisterSizeGetter(typeof(AsyncUniTask<TStateMachine, T>), () => pool.Size);
|
||||
}
|
||||
|
||||
bool TryReturn()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.Reset();
|
||||
stateMachine = default;
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
void Run()
|
||||
{
|
||||
// UnityEngine.Debug.Log($"MoveNext State:" + StateMachineUtility.GetState(stateMachine));
|
||||
stateMachine.MoveNext();
|
||||
}
|
||||
|
||||
public UniTask<T> Task
|
||||
{
|
||||
[DebuggerHidden]
|
||||
get
|
||||
{
|
||||
return new UniTask<T>(this, core.Version);
|
||||
}
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
public void SetResult(T result)
|
||||
{
|
||||
core.TrySetResult(result);
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
public void SetException(Exception exception)
|
||||
{
|
||||
core.TrySetException(exception);
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
public T GetResult(short token)
|
||||
{
|
||||
try
|
||||
{
|
||||
return core.GetResult(token);
|
||||
}
|
||||
finally
|
||||
{
|
||||
TryReturn();
|
||||
}
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
void IUniTaskSource.GetResult(short token)
|
||||
{
|
||||
GetResult(token);
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
public UniTaskStatus GetStatus(short token)
|
||||
{
|
||||
return core.GetStatus(token);
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
public UniTaskStatus UnsafeGetStatus()
|
||||
{
|
||||
return core.UnsafeGetStatus();
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||
{
|
||||
core.OnCompleted(continuation, state, token);
|
||||
}
|
||||
|
||||
~AsyncUniTask()
|
||||
{
|
||||
if (TryReturn())
|
||||
{
|
||||
GC.ReRegisterForFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,24 +12,35 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
public static class EnumeratorAsyncExtensions
|
||||
{
|
||||
public static UniTask.Awaiter GetAwaiter(this IEnumerator enumerator)
|
||||
public static UniTask.Awaiter GetAwaiter<T>(this T enumerator)
|
||||
where T : IEnumerator
|
||||
{
|
||||
return new UniTask(EnumeratorPromise.Create(enumerator, PlayerLoopTiming.Update, CancellationToken.None, out var token), token).GetAwaiter();
|
||||
var e = (IEnumerator)enumerator;
|
||||
Error.ThrowArgumentNullException(e, nameof(enumerator));
|
||||
return new UniTask(EnumeratorPromise.Create(e, PlayerLoopTiming.Update, CancellationToken.None, out var token), token).GetAwaiter();
|
||||
}
|
||||
|
||||
public static UniTask ToUniTask(this IEnumerator enumerator)
|
||||
public static UniTask WithCancellation(this IEnumerator enumerator, CancellationToken cancellationToken)
|
||||
{
|
||||
return new UniTask(EnumeratorPromise.Create(enumerator, PlayerLoopTiming.Update, CancellationToken.None, out var token), token);
|
||||
Error.ThrowArgumentNullException(enumerator, nameof(enumerator));
|
||||
return new UniTask(EnumeratorPromise.Create(enumerator, PlayerLoopTiming.Update, cancellationToken, out var token), token);
|
||||
}
|
||||
|
||||
public static UniTask ConfigureAwait(this IEnumerator enumerator, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
|
||||
public static UniTask ToUniTask(this IEnumerator enumerator, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
Error.ThrowArgumentNullException(enumerator, nameof(enumerator));
|
||||
return new UniTask(EnumeratorPromise.Create(enumerator, timing, cancellationToken, out var token), token);
|
||||
}
|
||||
|
||||
class EnumeratorPromise : IUniTaskSource, IPlayerLoopItem, IPromisePoolItem
|
||||
sealed class EnumeratorPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<EnumeratorPromise>
|
||||
{
|
||||
static readonly PromisePool<EnumeratorPromise> pool = new PromisePool<EnumeratorPromise>();
|
||||
static TaskPool<EnumeratorPromise> pool;
|
||||
public EnumeratorPromise NextNode { get; set; }
|
||||
|
||||
static EnumeratorPromise()
|
||||
{
|
||||
TaskPool.RegisterSizeGetter(typeof(EnumeratorPromise), () => pool.Size);
|
||||
}
|
||||
|
||||
IEnumerator innerEnumerator;
|
||||
CancellationToken cancellationToken;
|
||||
@@ -47,13 +58,15 @@ namespace Cysharp.Threading.Tasks
|
||||
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
|
||||
}
|
||||
|
||||
var result = pool.TryRent() ?? new EnumeratorPromise();
|
||||
if (!pool.TryPop(out var result))
|
||||
{
|
||||
result = new EnumeratorPromise();
|
||||
}
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
result.innerEnumerator = ConsumeEnumerator(innerEnumerator);
|
||||
result.cancellationToken = cancellationToken;
|
||||
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
PlayerLoopHelper.AddAction(timing, result);
|
||||
|
||||
token = result.core.Version;
|
||||
@@ -64,12 +77,11 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
try
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.GetResult(token);
|
||||
}
|
||||
finally
|
||||
{
|
||||
pool.TryReturn(this);
|
||||
TryReturn();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,16 +125,18 @@ namespace Cysharp.Threading.Tasks
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
bool TryReturn()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.Reset();
|
||||
innerEnumerator = default;
|
||||
cancellationToken = default;
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
|
||||
~EnumeratorPromise()
|
||||
{
|
||||
if (pool.TryReturn(this))
|
||||
if (TryReturn())
|
||||
{
|
||||
GC.ReRegisterForFinalize(this);
|
||||
}
|
||||
654
src/UniTask/Assets/Plugins/UniTask/Runtime/External/AddressableAsyncExtensions.cs
vendored
Normal file
654
src/UniTask/Assets/Plugins/UniTask/Runtime/External/AddressableAsyncExtensions.cs
vendored
Normal file
@@ -0,0 +1,654 @@
|
||||
// asmdef Version Defines, enabled when com.unity.addressables is imported.
|
||||
|
||||
#if UNITASK_ADDRESSABLE_SUPPORT
|
||||
|
||||
using Cysharp.Threading.Tasks.Internal;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using System.Threading;
|
||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
|
||||
namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
public static class AddressableAsyncExtensions
|
||||
{
|
||||
#region AsyncOperationHandle
|
||||
|
||||
public static AsyncOperationHandleAwaiter GetAwaiter(this AsyncOperationHandle handle)
|
||||
{
|
||||
return new AsyncOperationHandleAwaiter(handle);
|
||||
}
|
||||
|
||||
public static UniTask WithCancellation(this AsyncOperationHandle handle, CancellationToken cancellationToken)
|
||||
{
|
||||
if (handle.IsDone) return UniTask.CompletedTask;
|
||||
return new UniTask(AsyncOperationHandleWithCancellationSource.Create(handle, cancellationToken, out var token), token);
|
||||
}
|
||||
|
||||
public static UniTask ToUniTask(this AsyncOperationHandle handle, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if (handle.IsDone) return UniTask.CompletedTask;
|
||||
return new UniTask(AsyncOperationHandleConfiguredSource.Create(handle, timing, progress, cancellationToken, out var token), token);
|
||||
}
|
||||
|
||||
public struct AsyncOperationHandleAwaiter : ICriticalNotifyCompletion
|
||||
{
|
||||
AsyncOperationHandle handle;
|
||||
Action<AsyncOperationHandle> continuationAction;
|
||||
|
||||
public AsyncOperationHandleAwaiter(AsyncOperationHandle handle)
|
||||
{
|
||||
this.handle = handle;
|
||||
this.continuationAction = null;
|
||||
}
|
||||
|
||||
public bool IsCompleted => handle.IsDone;
|
||||
|
||||
public void GetResult()
|
||||
{
|
||||
if (continuationAction != null)
|
||||
{
|
||||
handle.Completed -= continuationAction;
|
||||
continuationAction = null;
|
||||
}
|
||||
|
||||
if (handle.Status == AsyncOperationStatus.Failed)
|
||||
{
|
||||
var e = handle.OperationException;
|
||||
handle = default;
|
||||
ExceptionDispatchInfo.Capture(e).Throw();
|
||||
}
|
||||
|
||||
var result = handle.Result;
|
||||
handle = default;
|
||||
}
|
||||
|
||||
public void OnCompleted(Action continuation)
|
||||
{
|
||||
UnsafeOnCompleted(continuation);
|
||||
}
|
||||
|
||||
public void UnsafeOnCompleted(Action continuation)
|
||||
{
|
||||
Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction);
|
||||
continuationAction = PooledDelegate<AsyncOperationHandle>.Create(continuation);
|
||||
handle.Completed += continuationAction;
|
||||
}
|
||||
}
|
||||
|
||||
sealed class AsyncOperationHandleWithCancellationSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleWithCancellationSource>
|
||||
{
|
||||
static TaskPool<AsyncOperationHandleWithCancellationSource> pool;
|
||||
public AsyncOperationHandleWithCancellationSource NextNode { get; set; }
|
||||
|
||||
static AsyncOperationHandleWithCancellationSource()
|
||||
{
|
||||
TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleWithCancellationSource), () => pool.Size);
|
||||
}
|
||||
|
||||
readonly Action<AsyncOperationHandle> continuationAction;
|
||||
AsyncOperationHandle handle;
|
||||
CancellationToken cancellationToken;
|
||||
bool completed;
|
||||
|
||||
UniTaskCompletionSourceCore<AsyncUnit> core;
|
||||
|
||||
AsyncOperationHandleWithCancellationSource()
|
||||
{
|
||||
continuationAction = Continuation;
|
||||
}
|
||||
|
||||
public static IUniTaskSource Create(AsyncOperationHandle handle, CancellationToken cancellationToken, out short token)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
|
||||
}
|
||||
|
||||
if (!pool.TryPop(out var result))
|
||||
{
|
||||
result = new AsyncOperationHandleWithCancellationSource();
|
||||
}
|
||||
|
||||
result.handle = handle;
|
||||
result.cancellationToken = cancellationToken;
|
||||
result.completed = false;
|
||||
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, result);
|
||||
|
||||
handle.Completed += result.continuationAction;
|
||||
|
||||
token = result.core.Version;
|
||||
return result;
|
||||
}
|
||||
|
||||
void Continuation(AsyncOperationHandle _)
|
||||
{
|
||||
handle.Completed -= continuationAction;
|
||||
|
||||
if (completed)
|
||||
{
|
||||
TryReturn();
|
||||
}
|
||||
else
|
||||
{
|
||||
completed = true;
|
||||
if (handle.Status == AsyncOperationStatus.Failed)
|
||||
{
|
||||
core.TrySetException(handle.OperationException);
|
||||
}
|
||||
else
|
||||
{
|
||||
core.TrySetResult(AsyncUnit.Default);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void GetResult(short token)
|
||||
{
|
||||
core.GetResult(token);
|
||||
}
|
||||
|
||||
public UniTaskStatus GetStatus(short token)
|
||||
{
|
||||
return core.GetStatus(token);
|
||||
}
|
||||
|
||||
public UniTaskStatus UnsafeGetStatus()
|
||||
{
|
||||
return core.UnsafeGetStatus();
|
||||
}
|
||||
|
||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||
{
|
||||
core.OnCompleted(continuation, state, token);
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (completed)
|
||||
{
|
||||
TryReturn();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
completed = true;
|
||||
core.TrySetCanceled(cancellationToken);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TryReturn()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.Reset();
|
||||
handle = default;
|
||||
cancellationToken = default;
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
|
||||
~AsyncOperationHandleWithCancellationSource()
|
||||
{
|
||||
if (TryReturn())
|
||||
{
|
||||
GC.ReRegisterForFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class AsyncOperationHandleConfiguredSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleConfiguredSource>
|
||||
{
|
||||
static TaskPool<AsyncOperationHandleConfiguredSource> pool;
|
||||
public AsyncOperationHandleConfiguredSource NextNode { get; set; }
|
||||
|
||||
static AsyncOperationHandleConfiguredSource()
|
||||
{
|
||||
TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleConfiguredSource), () => pool.Size);
|
||||
}
|
||||
|
||||
AsyncOperationHandle handle;
|
||||
IProgress<float> progress;
|
||||
CancellationToken cancellationToken;
|
||||
|
||||
UniTaskCompletionSourceCore<AsyncUnit> core;
|
||||
|
||||
AsyncOperationHandleConfiguredSource()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public static IUniTaskSource Create(AsyncOperationHandle handle, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, out short token)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
|
||||
}
|
||||
|
||||
if (!pool.TryPop(out var result))
|
||||
{
|
||||
result = new AsyncOperationHandleConfiguredSource();
|
||||
}
|
||||
|
||||
result.handle = handle;
|
||||
result.progress = progress;
|
||||
result.cancellationToken = cancellationToken;
|
||||
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
PlayerLoopHelper.AddAction(timing, result);
|
||||
|
||||
token = result.core.Version;
|
||||
return result;
|
||||
}
|
||||
|
||||
public void GetResult(short token)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
core.GetResult(token);
|
||||
}
|
||||
finally
|
||||
{
|
||||
TryReturn();
|
||||
}
|
||||
}
|
||||
|
||||
public UniTaskStatus GetStatus(short token)
|
||||
{
|
||||
return core.GetStatus(token);
|
||||
}
|
||||
|
||||
public UniTaskStatus UnsafeGetStatus()
|
||||
{
|
||||
return core.UnsafeGetStatus();
|
||||
}
|
||||
|
||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||
{
|
||||
core.OnCompleted(continuation, state, token);
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
core.TrySetCanceled(cancellationToken);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (progress != null)
|
||||
{
|
||||
progress.Report(handle.PercentComplete);
|
||||
}
|
||||
|
||||
if (handle.IsDone)
|
||||
{
|
||||
if (handle.Status == AsyncOperationStatus.Failed)
|
||||
{
|
||||
core.TrySetException(handle.OperationException);
|
||||
}
|
||||
else
|
||||
{
|
||||
core.TrySetResult(AsyncUnit.Default);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TryReturn()
|
||||
{
|
||||
core.Reset();
|
||||
TaskTracker.RemoveTracking(this);
|
||||
handle = default;
|
||||
progress = default;
|
||||
cancellationToken = default;
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
|
||||
~AsyncOperationHandleConfiguredSource()
|
||||
{
|
||||
if (TryReturn())
|
||||
{
|
||||
GC.ReRegisterForFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region AsyncOperationHandle_T
|
||||
|
||||
public static AsyncOperationHandleAwaiter<T> GetAwaiter<T>(this AsyncOperationHandle<T> handle)
|
||||
{
|
||||
return new AsyncOperationHandleAwaiter<T>(handle);
|
||||
}
|
||||
|
||||
public static UniTask<T> WithCancellation<T>(this AsyncOperationHandle<T> handle, CancellationToken cancellationToken)
|
||||
{
|
||||
if (handle.IsDone) return UniTask.FromResult(handle.Result);
|
||||
return new UniTask<T>(AsyncOperationHandleWithCancellationSource<T>.Create(handle, cancellationToken, out var token), token);
|
||||
}
|
||||
|
||||
public static UniTask<T> ToUniTask<T>(this AsyncOperationHandle<T> handle, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if (handle.IsDone) return UniTask.FromResult(handle.Result);
|
||||
return new UniTask<T>(AsyncOperationHandleConfiguredSource<T>.Create(handle, timing, progress, cancellationToken, out var token), token);
|
||||
}
|
||||
|
||||
public struct AsyncOperationHandleAwaiter<T> : ICriticalNotifyCompletion
|
||||
{
|
||||
AsyncOperationHandle<T> handle;
|
||||
Action<AsyncOperationHandle> continuationAction;
|
||||
|
||||
public AsyncOperationHandleAwaiter(AsyncOperationHandle<T> handle)
|
||||
{
|
||||
this.handle = handle;
|
||||
this.continuationAction = null;
|
||||
}
|
||||
|
||||
public bool IsCompleted => handle.IsDone;
|
||||
|
||||
public T GetResult()
|
||||
{
|
||||
if (continuationAction != null)
|
||||
{
|
||||
handle.CompletedTypeless -= continuationAction;
|
||||
continuationAction = null;
|
||||
}
|
||||
|
||||
if (handle.Status == AsyncOperationStatus.Failed)
|
||||
{
|
||||
var e = handle.OperationException;
|
||||
handle = default;
|
||||
ExceptionDispatchInfo.Capture(e).Throw();
|
||||
}
|
||||
|
||||
var result = handle.Result;
|
||||
handle = default;
|
||||
return result;
|
||||
}
|
||||
|
||||
public void OnCompleted(Action continuation)
|
||||
{
|
||||
UnsafeOnCompleted(continuation);
|
||||
}
|
||||
|
||||
public void UnsafeOnCompleted(Action continuation)
|
||||
{
|
||||
Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction);
|
||||
continuationAction = PooledDelegate<AsyncOperationHandle>.Create(continuation);
|
||||
handle.CompletedTypeless += continuationAction;
|
||||
}
|
||||
}
|
||||
|
||||
sealed class AsyncOperationHandleWithCancellationSource<T> : IUniTaskSource<T>, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleWithCancellationSource<T>>
|
||||
{
|
||||
static TaskPool<AsyncOperationHandleWithCancellationSource<T>> pool;
|
||||
public AsyncOperationHandleWithCancellationSource<T> NextNode { get; set; }
|
||||
|
||||
static AsyncOperationHandleWithCancellationSource()
|
||||
{
|
||||
TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleWithCancellationSource<T>), () => pool.Size);
|
||||
}
|
||||
|
||||
readonly Action<AsyncOperationHandle<T>> continuationAction;
|
||||
AsyncOperationHandle<T> handle;
|
||||
CancellationToken cancellationToken;
|
||||
bool completed;
|
||||
|
||||
UniTaskCompletionSourceCore<T> core;
|
||||
|
||||
AsyncOperationHandleWithCancellationSource()
|
||||
{
|
||||
continuationAction = Continuation;
|
||||
}
|
||||
|
||||
public static IUniTaskSource<T> Create(AsyncOperationHandle<T> handle, CancellationToken cancellationToken, out short token)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return AutoResetUniTaskCompletionSource<T>.CreateFromCanceled(cancellationToken, out token);
|
||||
}
|
||||
|
||||
if (!pool.TryPop(out var result))
|
||||
{
|
||||
result = new AsyncOperationHandleWithCancellationSource<T>();
|
||||
}
|
||||
|
||||
result.handle = handle;
|
||||
result.cancellationToken = cancellationToken;
|
||||
result.completed = false;
|
||||
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, result);
|
||||
|
||||
handle.Completed += result.continuationAction;
|
||||
|
||||
token = result.core.Version;
|
||||
return result;
|
||||
}
|
||||
|
||||
void Continuation(AsyncOperationHandle<T> _)
|
||||
{
|
||||
handle.Completed -= continuationAction;
|
||||
|
||||
if (completed)
|
||||
{
|
||||
TryReturn();
|
||||
}
|
||||
else
|
||||
{
|
||||
completed = true;
|
||||
if (handle.Status == AsyncOperationStatus.Failed)
|
||||
{
|
||||
core.TrySetException(handle.OperationException);
|
||||
}
|
||||
else
|
||||
{
|
||||
core.TrySetResult(handle.Result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public T GetResult(short token)
|
||||
{
|
||||
return core.GetResult(token);
|
||||
}
|
||||
|
||||
void IUniTaskSource.GetResult(short token)
|
||||
{
|
||||
GetResult(token);
|
||||
}
|
||||
|
||||
public UniTaskStatus GetStatus(short token)
|
||||
{
|
||||
return core.GetStatus(token);
|
||||
}
|
||||
|
||||
public UniTaskStatus UnsafeGetStatus()
|
||||
{
|
||||
return core.UnsafeGetStatus();
|
||||
}
|
||||
|
||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||
{
|
||||
core.OnCompleted(continuation, state, token);
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (completed)
|
||||
{
|
||||
TryReturn();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
completed = true;
|
||||
core.TrySetCanceled(cancellationToken);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TryReturn()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.Reset();
|
||||
handle = default;
|
||||
cancellationToken = default;
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
|
||||
~AsyncOperationHandleWithCancellationSource()
|
||||
{
|
||||
if (TryReturn())
|
||||
{
|
||||
GC.ReRegisterForFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class AsyncOperationHandleConfiguredSource<T> : IUniTaskSource<T>, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleConfiguredSource<T>>
|
||||
{
|
||||
static TaskPool<AsyncOperationHandleConfiguredSource<T>> pool;
|
||||
public AsyncOperationHandleConfiguredSource<T> NextNode { get; set; }
|
||||
|
||||
static AsyncOperationHandleConfiguredSource()
|
||||
{
|
||||
TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleConfiguredSource<T>), () => pool.Size);
|
||||
}
|
||||
|
||||
AsyncOperationHandle<T> handle;
|
||||
IProgress<float> progress;
|
||||
CancellationToken cancellationToken;
|
||||
|
||||
UniTaskCompletionSourceCore<T> core;
|
||||
|
||||
AsyncOperationHandleConfiguredSource()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public static IUniTaskSource<T> Create(AsyncOperationHandle<T> handle, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, out short token)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return AutoResetUniTaskCompletionSource<T>.CreateFromCanceled(cancellationToken, out token);
|
||||
}
|
||||
|
||||
if (!pool.TryPop(out var result))
|
||||
{
|
||||
result = new AsyncOperationHandleConfiguredSource<T>();
|
||||
}
|
||||
|
||||
result.handle = handle;
|
||||
result.progress = progress;
|
||||
result.cancellationToken = cancellationToken;
|
||||
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
PlayerLoopHelper.AddAction(timing, result);
|
||||
|
||||
token = result.core.Version;
|
||||
return result;
|
||||
}
|
||||
|
||||
public T GetResult(short token)
|
||||
{
|
||||
try
|
||||
{
|
||||
return core.GetResult(token);
|
||||
}
|
||||
finally
|
||||
{
|
||||
TryReturn();
|
||||
}
|
||||
}
|
||||
|
||||
void IUniTaskSource.GetResult(short token)
|
||||
{
|
||||
GetResult(token);
|
||||
}
|
||||
|
||||
public UniTaskStatus GetStatus(short token)
|
||||
{
|
||||
return core.GetStatus(token);
|
||||
}
|
||||
|
||||
public UniTaskStatus UnsafeGetStatus()
|
||||
{
|
||||
return core.UnsafeGetStatus();
|
||||
}
|
||||
|
||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||
{
|
||||
core.OnCompleted(continuation, state, token);
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
core.TrySetCanceled(cancellationToken);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (progress != null)
|
||||
{
|
||||
progress.Report(handle.PercentComplete);
|
||||
}
|
||||
|
||||
if (handle.IsDone)
|
||||
{
|
||||
if (handle.Status == AsyncOperationStatus.Failed)
|
||||
{
|
||||
core.TrySetException(handle.OperationException);
|
||||
}
|
||||
else
|
||||
{
|
||||
core.TrySetResult(handle.Result);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TryReturn()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.Reset();
|
||||
handle = default;
|
||||
progress = default;
|
||||
cancellationToken = default;
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
|
||||
~AsyncOperationHandleConfiguredSource()
|
||||
{
|
||||
if (TryReturn())
|
||||
{
|
||||
GC.ReRegisterForFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
332
src/UniTask/Assets/Plugins/UniTask/Runtime/External/DoTweenAsyncExtensions.cs
vendored
Normal file
332
src/UniTask/Assets/Plugins/UniTask/Runtime/External/DoTweenAsyncExtensions.cs
vendored
Normal file
@@ -0,0 +1,332 @@
|
||||
// asmdef Version Defines, enabled when com.demigiant.dotween is imported.
|
||||
|
||||
#if UNITASK_DOTWEEN_SUPPORT
|
||||
|
||||
using Cysharp.Threading.Tasks.Internal;
|
||||
using DG.Tweening;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
// The idea of TweenCancelBehaviour is borrowed from https://www.shibuya24.info/entry/dotween_async_await
|
||||
public enum TweenCancelBehaviour
|
||||
{
|
||||
Kill,
|
||||
KillWithCompleteCallback,
|
||||
Complete,
|
||||
CompleteWithSeqeunceCallback,
|
||||
CancelAwait,
|
||||
|
||||
// AndCancelAwait
|
||||
KillAndCancelAwait,
|
||||
KillWithCompleteCallbackAndCancelAwait,
|
||||
CompleteAndCancelAwait,
|
||||
CompleteWithSeqeunceCallbackAndCancelAwait
|
||||
}
|
||||
|
||||
public static class DOTweenAsyncExtensions
|
||||
{
|
||||
public static TweenAwaiter GetAwaiter(this Tween tween)
|
||||
{
|
||||
return new TweenAwaiter(tween);
|
||||
}
|
||||
|
||||
public static UniTask WithCancellation(this Tween tween, CancellationToken cancellationToken)
|
||||
{
|
||||
Error.ThrowArgumentNullException(tween, nameof(tween));
|
||||
|
||||
if (!tween.IsActive()) return UniTask.CompletedTask;
|
||||
return new UniTask(TweenConfiguredSource.Create(tween, TweenCancelBehaviour.Kill, cancellationToken, out var token), token);
|
||||
}
|
||||
|
||||
public static UniTask ToUniTask(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default)
|
||||
{
|
||||
Error.ThrowArgumentNullException(tween, nameof(tween));
|
||||
|
||||
if (!tween.IsActive()) return UniTask.CompletedTask;
|
||||
return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, out var token), token);
|
||||
}
|
||||
|
||||
public struct TweenAwaiter : ICriticalNotifyCompletion
|
||||
{
|
||||
readonly Tween tween;
|
||||
|
||||
// killed(non active) as completed.
|
||||
public bool IsCompleted => !tween.IsActive();
|
||||
|
||||
public TweenAwaiter(Tween tween)
|
||||
{
|
||||
this.tween = tween;
|
||||
}
|
||||
|
||||
public TweenAwaiter GetAwaiter()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
public void GetResult()
|
||||
{
|
||||
}
|
||||
|
||||
public void OnCompleted(System.Action continuation)
|
||||
{
|
||||
UnsafeOnCompleted(continuation);
|
||||
}
|
||||
|
||||
public void UnsafeOnCompleted(System.Action continuation)
|
||||
{
|
||||
// onKill is called after OnCompleted, both Complete(false/true) and Kill(false/true).
|
||||
tween.onKill = PooledTweenCallback.Create(continuation);
|
||||
}
|
||||
}
|
||||
|
||||
sealed class TweenConfiguredSource : IUniTaskSource, ITaskPoolNode<TweenConfiguredSource>
|
||||
{
|
||||
static TaskPool<TweenConfiguredSource> pool;
|
||||
public TweenConfiguredSource NextNode { get; set; }
|
||||
|
||||
static TweenConfiguredSource()
|
||||
{
|
||||
TaskPool.RegisterSizeGetter(typeof(TweenConfiguredSource), () => pool.Size);
|
||||
}
|
||||
|
||||
static readonly TweenCallback EmptyTweenCallback = () => { };
|
||||
|
||||
readonly TweenCallback onKillDelegate;
|
||||
readonly TweenCallback onUpdateDelegate;
|
||||
|
||||
Tween tween;
|
||||
TweenCancelBehaviour cancelBehaviour;
|
||||
CancellationToken cancellationToken;
|
||||
bool canceled;
|
||||
|
||||
TweenCallback originalUpdateAction;
|
||||
UniTaskCompletionSourceCore<AsyncUnit> core;
|
||||
|
||||
TweenConfiguredSource()
|
||||
{
|
||||
onKillDelegate = OnKill;
|
||||
onUpdateDelegate = OnUpdate;
|
||||
}
|
||||
|
||||
public static IUniTaskSource Create(Tween tween, TweenCancelBehaviour cancelBehaviour, CancellationToken cancellationToken, out short token)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
DoCancelBeforeCreate(tween, cancelBehaviour);
|
||||
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
|
||||
}
|
||||
|
||||
if (!pool.TryPop(out var result))
|
||||
{
|
||||
result = new TweenConfiguredSource();
|
||||
}
|
||||
|
||||
result.tween = tween;
|
||||
result.cancelBehaviour = cancelBehaviour;
|
||||
result.cancellationToken = cancellationToken;
|
||||
|
||||
result.originalUpdateAction = tween.onUpdate;
|
||||
result.canceled = false;
|
||||
|
||||
if (result.originalUpdateAction == result.onUpdateDelegate)
|
||||
{
|
||||
result.originalUpdateAction = null;
|
||||
}
|
||||
|
||||
tween.onUpdate = result.onUpdateDelegate;
|
||||
tween.onKill = result.onKillDelegate;
|
||||
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
token = result.core.Version;
|
||||
return result;
|
||||
}
|
||||
|
||||
void OnKill()
|
||||
{
|
||||
if (canceled)
|
||||
{
|
||||
core.TrySetCanceled(cancellationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
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:
|
||||
this.tween.onKill = EmptyTweenCallback; // replace to empty(avoid callback after Canceled(instance is returned to pool.)
|
||||
this.core.TrySetCanceled(this.cancellationToken);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void DoCancelBeforeCreate(Tween tween, TweenCancelBehaviour tweenCancelBehaviour)
|
||||
{
|
||||
|
||||
switch (tweenCancelBehaviour)
|
||||
{
|
||||
case TweenCancelBehaviour.Kill:
|
||||
default:
|
||||
tween.Kill(false);
|
||||
break;
|
||||
case TweenCancelBehaviour.KillAndCancelAwait:
|
||||
tween.Kill(false);
|
||||
break;
|
||||
case TweenCancelBehaviour.KillWithCompleteCallback:
|
||||
tween.Kill(true);
|
||||
break;
|
||||
case TweenCancelBehaviour.KillWithCompleteCallbackAndCancelAwait:
|
||||
tween.Kill(true);
|
||||
break;
|
||||
case TweenCancelBehaviour.Complete:
|
||||
tween.Complete(false);
|
||||
break;
|
||||
case TweenCancelBehaviour.CompleteAndCancelAwait:
|
||||
tween.Complete(false);
|
||||
break;
|
||||
case TweenCancelBehaviour.CompleteWithSeqeunceCallback:
|
||||
tween.Complete(true);
|
||||
break;
|
||||
case TweenCancelBehaviour.CompleteWithSeqeunceCallbackAndCancelAwait:
|
||||
tween.Complete(true);
|
||||
break;
|
||||
case TweenCancelBehaviour.CancelAwait:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void GetResult(short token)
|
||||
{
|
||||
try
|
||||
{
|
||||
core.GetResult(token);
|
||||
}
|
||||
finally
|
||||
{
|
||||
TryReturn();
|
||||
}
|
||||
}
|
||||
|
||||
public UniTaskStatus GetStatus(short token)
|
||||
{
|
||||
return core.GetStatus(token);
|
||||
}
|
||||
|
||||
public UniTaskStatus UnsafeGetStatus()
|
||||
{
|
||||
return core.UnsafeGetStatus();
|
||||
}
|
||||
|
||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||
{
|
||||
core.OnCompleted(continuation, state, token);
|
||||
}
|
||||
|
||||
bool TryReturn()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.Reset();
|
||||
tween.onUpdate = originalUpdateAction;
|
||||
tween.onKill = null;
|
||||
tween = default;
|
||||
cancellationToken = default;
|
||||
originalUpdateAction = default;
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
|
||||
~TweenConfiguredSource()
|
||||
{
|
||||
if (TryReturn())
|
||||
{
|
||||
GC.ReRegisterForFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class PooledTweenCallback
|
||||
{
|
||||
static readonly ConcurrentQueue<PooledTweenCallback> pool = new ConcurrentQueue<PooledTweenCallback>();
|
||||
|
||||
readonly TweenCallback runDelegate;
|
||||
|
||||
Action continuation;
|
||||
|
||||
|
||||
PooledTweenCallback()
|
||||
{
|
||||
runDelegate = Run;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static TweenCallback Create(Action continuation)
|
||||
{
|
||||
if (!pool.TryDequeue(out var item))
|
||||
{
|
||||
item = new PooledTweenCallback();
|
||||
}
|
||||
|
||||
item.continuation = continuation;
|
||||
return item.runDelegate;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
void Run()
|
||||
{
|
||||
var call = continuation;
|
||||
continuation = null;
|
||||
if (call != null)
|
||||
{
|
||||
pool.Enqueue(this);
|
||||
call.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
11
src/UniTask/Assets/Plugins/UniTask/Runtime/External/DoTweenAsyncExtensions.cs.meta
vendored
Normal file
11
src/UniTask/Assets/Plugins/UniTask/Runtime/External/DoTweenAsyncExtensions.cs.meta
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1f448d5bc5b232e4f98d89d5d1832e8e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,91 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
public interface IUniTaskAsyncEnumerable<out T>
|
||||
{
|
||||
IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
public interface IUniTaskAsyncEnumerator<out T> : IUniTaskAsyncDisposable
|
||||
{
|
||||
T Current { get; }
|
||||
UniTask<bool> MoveNextAsync();
|
||||
}
|
||||
|
||||
public interface IUniTaskAsyncDisposable
|
||||
{
|
||||
UniTask DisposeAsync();
|
||||
}
|
||||
|
||||
public interface IUniTaskOrderedAsyncEnumerable<TElement> : IUniTaskAsyncEnumerable<TElement>
|
||||
{
|
||||
IUniTaskOrderedAsyncEnumerable<TElement> CreateOrderedEnumerable<TKey>(Func<TElement, TKey> keySelector, IComparer<TKey> comparer, bool descending);
|
||||
IUniTaskOrderedAsyncEnumerable<TElement> CreateOrderedEnumerable<TKey>(Func<TElement, UniTask<TKey>> keySelector, IComparer<TKey> comparer, bool descending);
|
||||
IUniTaskOrderedAsyncEnumerable<TElement> CreateOrderedEnumerable<TKey>(Func<TElement, CancellationToken, UniTask<TKey>> keySelector, IComparer<TKey> comparer, bool descending);
|
||||
}
|
||||
|
||||
public interface IConnectableUniTaskAsyncEnumerable<out T> : IUniTaskAsyncEnumerable<T>
|
||||
{
|
||||
IDisposable Connect();
|
||||
}
|
||||
|
||||
// don't use AsyncGrouping.
|
||||
//public interface IUniTaskAsyncGrouping<out TKey, out TElement> : IUniTaskAsyncEnumerable<TElement>
|
||||
//{
|
||||
// TKey Key { get; }
|
||||
//}
|
||||
|
||||
public static class UniTaskAsyncEnumerableExtensions
|
||||
{
|
||||
public static UniTaskCancelableAsyncEnumerable<T> WithCancellation<T>(this IUniTaskAsyncEnumerable<T> source, CancellationToken cancellationToken)
|
||||
{
|
||||
return new UniTaskCancelableAsyncEnumerable<T>(source, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
public readonly struct UniTaskCancelableAsyncEnumerable<T>
|
||||
{
|
||||
private readonly IUniTaskAsyncEnumerable<T> enumerable;
|
||||
private readonly CancellationToken cancellationToken;
|
||||
|
||||
internal UniTaskCancelableAsyncEnumerable(IUniTaskAsyncEnumerable<T> enumerable, CancellationToken cancellationToken)
|
||||
{
|
||||
this.enumerable = enumerable;
|
||||
this.cancellationToken = cancellationToken;
|
||||
}
|
||||
|
||||
public Enumerator GetAsyncEnumerator()
|
||||
{
|
||||
return new Enumerator(enumerable.GetAsyncEnumerator(cancellationToken));
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
public readonly struct Enumerator
|
||||
{
|
||||
private readonly IUniTaskAsyncEnumerator<T> enumerator;
|
||||
|
||||
internal Enumerator(IUniTaskAsyncEnumerator<T> enumerator)
|
||||
{
|
||||
this.enumerator = enumerator;
|
||||
}
|
||||
|
||||
public T Current => enumerator.Current;
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
return enumerator.MoveNextAsync();
|
||||
}
|
||||
|
||||
|
||||
public UniTask DisposeAsync()
|
||||
{
|
||||
return enumerator.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b20cf9f02ac585948a4372fa4ee06504
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
124
src/UniTask/Assets/Plugins/UniTask/Runtime/IUniTaskSource.cs
Normal file
124
src/UniTask/Assets/Plugins/UniTask/Runtime/IUniTaskSource.cs
Normal file
@@ -0,0 +1,124 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
public enum UniTaskStatus
|
||||
{
|
||||
/// <summary>The operation has not yet completed.</summary>
|
||||
Pending = 0,
|
||||
/// <summary>The operation completed successfully.</summary>
|
||||
Succeeded = 1,
|
||||
/// <summary>The operation completed with an error.</summary>
|
||||
Faulted = 2,
|
||||
/// <summary>The operation completed due to cancellation.</summary>
|
||||
Canceled = 3
|
||||
}
|
||||
|
||||
// similar as IValueTaskSource
|
||||
public interface IUniTaskSource
|
||||
#if !UNITY_2018_3_OR_NEWER && !NETSTANDARD2_0
|
||||
: System.Threading.Tasks.Sources.IValueTaskSource
|
||||
#pragma warning disable CS0108
|
||||
#endif
|
||||
{
|
||||
UniTaskStatus GetStatus(short token);
|
||||
void OnCompleted(Action<object> continuation, object state, short token);
|
||||
void GetResult(short token);
|
||||
|
||||
UniTaskStatus UnsafeGetStatus(); // only for debug use.
|
||||
|
||||
#if !UNITY_2018_3_OR_NEWER && !NETSTANDARD2_0
|
||||
#pragma warning restore CS0108
|
||||
|
||||
System.Threading.Tasks.Sources.ValueTaskSourceStatus System.Threading.Tasks.Sources.IValueTaskSource.GetStatus(short token)
|
||||
{
|
||||
return (System.Threading.Tasks.Sources.ValueTaskSourceStatus)(int)((IUniTaskSource)this).GetStatus(token);
|
||||
}
|
||||
|
||||
void System.Threading.Tasks.Sources.IValueTaskSource.GetResult(short token)
|
||||
{
|
||||
((IUniTaskSource)this).GetResult(token);
|
||||
}
|
||||
|
||||
void System.Threading.Tasks.Sources.IValueTaskSource.OnCompleted(Action<object> continuation, object state, short token, System.Threading.Tasks.Sources.ValueTaskSourceOnCompletedFlags flags)
|
||||
{
|
||||
// ignore flags, always none.
|
||||
((IUniTaskSource)this).OnCompleted(continuation, state, token);
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
public interface IUniTaskSource<out T> : IUniTaskSource
|
||||
#if !UNITY_2018_3_OR_NEWER && !NETSTANDARD2_0
|
||||
, System.Threading.Tasks.Sources.IValueTaskSource<T>
|
||||
#endif
|
||||
{
|
||||
new T GetResult(short token);
|
||||
|
||||
#if !UNITY_2018_3_OR_NEWER && !NETSTANDARD2_0
|
||||
|
||||
new public UniTaskStatus GetStatus(short token)
|
||||
{
|
||||
return ((IUniTaskSource)this).GetStatus(token);
|
||||
}
|
||||
|
||||
new public void OnCompleted(Action<object> continuation, object state, short token)
|
||||
{
|
||||
((IUniTaskSource)this).OnCompleted(continuation, state, token);
|
||||
}
|
||||
|
||||
System.Threading.Tasks.Sources.ValueTaskSourceStatus System.Threading.Tasks.Sources.IValueTaskSource<T>.GetStatus(short token)
|
||||
{
|
||||
return (System.Threading.Tasks.Sources.ValueTaskSourceStatus)(int)((IUniTaskSource)this).GetStatus(token);
|
||||
}
|
||||
|
||||
T System.Threading.Tasks.Sources.IValueTaskSource<T>.GetResult(short token)
|
||||
{
|
||||
return ((IUniTaskSource<T>)this).GetResult(token);
|
||||
}
|
||||
|
||||
void System.Threading.Tasks.Sources.IValueTaskSource<T>.OnCompleted(Action<object> continuation, object state, short token, System.Threading.Tasks.Sources.ValueTaskSourceOnCompletedFlags flags)
|
||||
{
|
||||
// ignore flags, always none.
|
||||
((IUniTaskSource)this).OnCompleted(continuation, state, token);
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
public static class UniTaskStatusExtensions
|
||||
{
|
||||
/// <summary>status != Pending.</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsCompleted(this UniTaskStatus status)
|
||||
{
|
||||
return status != UniTaskStatus.Pending;
|
||||
}
|
||||
|
||||
/// <summary>status == Succeeded.</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsCompletedSuccessfully(this UniTaskStatus status)
|
||||
{
|
||||
return status == UniTaskStatus.Succeeded;
|
||||
}
|
||||
|
||||
/// <summary>status == Canceled.</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsCanceled(this UniTaskStatus status)
|
||||
{
|
||||
return status == UniTaskStatus.Canceled;
|
||||
}
|
||||
|
||||
/// <summary>status == Faulted.</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsFaulted(this UniTaskStatus status)
|
||||
{
|
||||
return status == UniTaskStatus.Faulted;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,11 +32,21 @@ namespace Cysharp.Threading.Tasks.Internal
|
||||
}
|
||||
}
|
||||
|
||||
public static RentArray<T> CopyToRentArray<T>(IEnumerable<T> source)
|
||||
public static RentArray<T> Materialize<T>(IEnumerable<T> source)
|
||||
{
|
||||
if (source is T[] array)
|
||||
{
|
||||
return new RentArray<T>(array, array.Length, null);
|
||||
}
|
||||
|
||||
var defaultCount = 32;
|
||||
if (source is ICollection<T> coll)
|
||||
{
|
||||
if (coll.Count == 0)
|
||||
{
|
||||
return new RentArray<T>(Array.Empty<T>(), 0, null);
|
||||
}
|
||||
|
||||
defaultCount = coll.Count;
|
||||
var pool = ArrayPool<T>.Shared;
|
||||
var buffer = pool.Rent(defaultCount);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user