mirror of
https://github.com/Cysharp/UniTask.git
synced 2026-05-15 11:30:09 +00:00
Compare commits
139 Commits
chore/add_
...
2.4.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cfe509a556 | ||
|
|
a46a4cac01 | ||
|
|
3ed28e534a | ||
|
|
be8dbe8804 | ||
|
|
64f7eec4e9 | ||
|
|
3a93f4a49f | ||
|
|
71958adc3d | ||
|
|
acc71550c9 | ||
|
|
3ba64412f8 | ||
|
|
90c5e5a6ad | ||
|
|
8a022ee02d | ||
|
|
6a89ea8139 | ||
|
|
90c81613ac | ||
|
|
7c62904a74 | ||
|
|
4f6344a12f | ||
|
|
937d3adf66 | ||
|
|
3bac16229f | ||
|
|
ea57847c97 | ||
|
|
730d68132d | ||
|
|
6db872236e | ||
|
|
ba7e676c6f | ||
|
|
6e99accf99 | ||
|
|
b195df9773 | ||
|
|
f303d9d7e8 | ||
|
|
62a2a2e8f9 | ||
|
|
ffbadbcc4c | ||
|
|
50ad2ee9d6 | ||
|
|
c170af5642 | ||
|
|
242bceecd3 | ||
|
|
06346b8a2a | ||
|
|
b071eeadfb | ||
|
|
e7f23d8328 | ||
|
|
47a3f09abf | ||
|
|
a7a6af0a68 | ||
|
|
f203b6c051 | ||
|
|
07211f1fc3 | ||
|
|
bc27f6c0d8 | ||
|
|
c65f9c3497 | ||
|
|
afe5f57adc | ||
|
|
9135c7ce56 | ||
|
|
7fae415689 | ||
|
|
e5cc8667ac | ||
|
|
0ea18d0e16 | ||
|
|
2d674999f0 | ||
|
|
3121903fa3 | ||
|
|
af2e49aa29 | ||
|
|
22940635fe | ||
|
|
c1042b32b7 | ||
|
|
29a144694d | ||
|
|
548d56e654 | ||
|
|
6fb4f2d6d2 | ||
|
|
8eac07ad24 | ||
|
|
716402a180 | ||
|
|
4c3d6938ed | ||
|
|
b4486802f2 | ||
|
|
d210e3d76a | ||
|
|
f2773f585e | ||
|
|
305695ad5d | ||
|
|
418ab36a72 | ||
|
|
563b4fbbd5 | ||
|
|
878d33115f | ||
|
|
356a4ee62e | ||
|
|
eb32ae25e0 | ||
|
|
3ca4062536 | ||
|
|
019f8aaf30 | ||
|
|
663fa737f3 | ||
|
|
73d86259ce | ||
|
|
c7eedf85c7 | ||
|
|
8dc3ffd552 | ||
|
|
b992a061fb | ||
|
|
4fc09a6f61 | ||
|
|
e57176a43c | ||
|
|
710d0d9012 | ||
|
|
039de3ef65 | ||
|
|
de38f63a55 | ||
|
|
38f8193199 | ||
|
|
15cffb7357 | ||
|
|
d55748e05b | ||
|
|
5602861dd4 | ||
|
|
d2245bc38b | ||
|
|
f092f6a112 | ||
|
|
ed617a04a6 | ||
|
|
340736795c | ||
|
|
dc804ffb13 | ||
|
|
a879989d1c | ||
|
|
ee54559532 | ||
|
|
4a72ec2a1a | ||
|
|
e999268305 | ||
|
|
8300c1b1e6 | ||
|
|
daa0c7b9a0 | ||
|
|
9b9a4ec76a | ||
|
|
3f8e43b83f | ||
|
|
cf6f0799e9 | ||
|
|
91ba4f003a | ||
|
|
f48cb4b03e | ||
|
|
70a243d978 | ||
|
|
9b95f3b9f6 | ||
|
|
a25adb601b | ||
|
|
e6240879c4 | ||
|
|
a94e8cceac | ||
|
|
0595a4182a | ||
|
|
df96c119c1 | ||
|
|
534f4a2588 | ||
|
|
873485ad1a | ||
|
|
f6037d6c9b | ||
|
|
78db78c7bd | ||
|
|
226e272787 | ||
|
|
9e2163616b | ||
|
|
52df6fbf3f | ||
|
|
33d32baea4 | ||
|
|
b0250cfe75 | ||
|
|
7a35f121cd | ||
|
|
79f1566fdb | ||
|
|
364b67805d | ||
|
|
62ca0c6e92 | ||
|
|
f63212aa17 | ||
|
|
768fd16e60 | ||
|
|
3c99010ba0 | ||
|
|
5c668717d8 | ||
|
|
b089f74c65 | ||
|
|
fcf4f21cc1 | ||
|
|
ee2fd3e91d | ||
|
|
27604496ca | ||
|
|
0d01034a57 | ||
|
|
50a67d8f41 | ||
|
|
2a23a85cdd | ||
|
|
e127d9976e | ||
|
|
c31b78e45e | ||
|
|
b6b0b4000d | ||
|
|
69be818a46 | ||
|
|
a1dee8b54f | ||
|
|
4f6166102d | ||
|
|
6b1d2c231a | ||
|
|
0715dd31bf | ||
|
|
26dbfa3655 | ||
|
|
6ac55e37a1 | ||
|
|
60bfbae787 | ||
|
|
fc9ddeb15c | ||
|
|
dfe18d11ff |
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
github: [neuecc]
|
||||||
56
.github/workflows/build-debug.yml
vendored
56
.github/workflows/build-debug.yml
vendored
@@ -4,8 +4,6 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- "master"
|
- "master"
|
||||||
tags:
|
|
||||||
- "!*" # not a tag push
|
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- "master"
|
- "master"
|
||||||
@@ -13,70 +11,60 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
build-dotnet:
|
build-dotnet:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
env:
|
timeout-minutes: 10
|
||||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
|
||||||
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
|
|
||||||
NUGET_XMLDOC_MODE: skip
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-dotnet@v1
|
- uses: Cysharp/Actions/.github/actions/setup-dotnet@main
|
||||||
with:
|
with:
|
||||||
dotnet-version: 3.1.x
|
dotnet-version: |
|
||||||
- run: dotnet test -c Debug ./src/UniTask.NetCoreTests/UniTask.NetCoreTests.csproj
|
6.0.x
|
||||||
|
7.0.x
|
||||||
|
- run: dotnet build -c Debug
|
||||||
|
- run: dotnet test -c Debug
|
||||||
|
|
||||||
build-unity:
|
build-unity:
|
||||||
if: "((github.event_name == 'push' && github.repository_owner == 'Cysharp') || startsWith(github.event.pull_request.head.label, 'Cysharp:'))"
|
if: "((github.event_name == 'push' && github.repository_owner == 'Cysharp') || startsWith(github.event.pull_request.head.label, 'Cysharp:'))"
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
unity: ["2019.3.9f1", "2019.4.13f1", "2020.1.12f1"]
|
unity: ["2019.3.9f1", "2019.4.13f1", "2020.1.12f1"]
|
||||||
include:
|
|
||||||
- unity: 2019.3.9f1
|
|
||||||
license: UNITY_LICENSE_2019
|
|
||||||
- unity: 2019.4.13f1
|
|
||||||
license: UNITY_LICENSE_2019
|
|
||||||
- unity: 2020.1.12f1
|
|
||||||
license: UNITY_LICENSE_2020
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 15
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
# Execute scripts: RuntimeUnitTestToolkit
|
# Execute scripts: RuntimeUnitTestToolkit
|
||||||
# /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod UnitTestBuilder.BuildUnitTest /headless /ScriptBackend mono /BuildTarget StandaloneLinux64
|
# /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod UnitTestBuilder.BuildUnitTest /headless /ScriptBackend mono /BuildTarget StandaloneLinux64
|
||||||
- name: Build UnitTest(Linux64, mono)
|
- name: Build UnitTest(Linux64, mono)
|
||||||
uses: game-ci/unity-builder@v2.0-alpha-6
|
uses: Cysharp/Actions/.github/actions/unity-builder@main
|
||||||
env:
|
env:
|
||||||
UNITY_LICENSE: ${{ secrets[matrix.license] }}
|
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
|
||||||
|
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
|
||||||
|
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
|
||||||
with:
|
with:
|
||||||
projectPath: src/UniTask
|
projectPath: src/UniTask
|
||||||
unityVersion: ${{ matrix.unity }}
|
unityVersion: ${{ matrix.unity }}
|
||||||
targetPlatform: StandaloneLinux64
|
targetPlatform: StandaloneLinux64
|
||||||
buildMethod: UnitTestBuilder.BuildUnitTest
|
buildMethod: UnitTestBuilder.BuildUnitTest
|
||||||
customParameters: /headless /ScriptBackend mono
|
customParameters: /headless /ScriptBackend mono
|
||||||
versioning: None
|
|
||||||
- name: Execute UnitTest
|
- name: Execute UnitTest
|
||||||
run: ./src/UniTask/bin/UnitTest/StandaloneLinux64_Mono2x/test
|
run: ./src/UniTask/bin/UnitTest/StandaloneLinux64_Mono2x/test
|
||||||
|
|
||||||
# Execute scripts: Export Package
|
# Execute scripts: Export Package
|
||||||
# /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod PackageExporter.Export
|
# /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod PackageExporter.Export
|
||||||
- name: Export unitypackage
|
- name: Build Unity (.unitypacakge)
|
||||||
uses: game-ci/unity-builder@v2.0-alpha-6
|
uses: Cysharp/Actions/.github/actions/unity-builder@main
|
||||||
env:
|
env:
|
||||||
UNITY_LICENSE: ${{ secrets[matrix.license] }}
|
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
|
||||||
|
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
|
||||||
|
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
|
||||||
with:
|
with:
|
||||||
projectPath: src/UniTask
|
projectPath: src/UniTask
|
||||||
unityVersion: ${{ matrix.unity }}
|
unityVersion: ${{ matrix.unity }}
|
||||||
targetPlatform: StandaloneLinux64
|
targetPlatform: StandaloneLinux64
|
||||||
buildMethod: PackageExporter.Export
|
buildMethod: PackageExporter.Export
|
||||||
versioning: None
|
|
||||||
|
|
||||||
- name: check all .meta is commited
|
- uses: Cysharp/Actions/.github/actions/check-metas@main # check meta files
|
||||||
run: |
|
with:
|
||||||
if git ls-files --others --exclude-standard -t | grep --regexp='[.]meta$'; then
|
directory: src/UniTask
|
||||||
echo "Detected .meta file generated. Do you forgot commit a .meta file?"
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
echo "Great, all .meta files are commited."
|
|
||||||
fi
|
|
||||||
working-directory: src/UniTask
|
|
||||||
|
|
||||||
# Store artifacts.
|
# Store artifacts.
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v2
|
||||||
|
|||||||
5
.github/workflows/build-docs.yml
vendored
5
.github/workflows/build-docs.yml
vendored
@@ -9,9 +9,10 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
run-docfx:
|
run-docfx:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 10
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
repository: Cysharp/DocfxTemplate
|
repository: Cysharp/DocfxTemplate
|
||||||
path: docs/_DocfxTemplate
|
path: docs/_DocfxTemplate
|
||||||
|
|||||||
134
.github/workflows/build-release.yml
vendored
134
.github/workflows/build-release.yml
vendored
@@ -6,94 +6,44 @@ on:
|
|||||||
tag:
|
tag:
|
||||||
description: "tag: git tag you want create. (sample 1.0.0)"
|
description: "tag: git tag you want create. (sample 1.0.0)"
|
||||||
required: true
|
required: true
|
||||||
dry_run:
|
dry-run:
|
||||||
description: "dry_run: true will never create relase/nuget."
|
description: "dry-run: true will never create relase/nuget."
|
||||||
required: true
|
required: true
|
||||||
default: "false"
|
default: false
|
||||||
|
type: boolean
|
||||||
|
|
||||||
env:
|
env:
|
||||||
GIT_TAG: ${{ github.event.inputs.tag }}
|
GIT_TAG: ${{ github.event.inputs.tag }}
|
||||||
DRY_RUN: ${{ github.event.inputs.dry_run }}
|
DRY_RUN: ${{ github.event.inputs.dry-run }}
|
||||||
DRY_RUN_BRANCH_PREFIX: "test_release"
|
|
||||||
DOTNET_SDK_VERISON_3: 3.1.x
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
update-packagejson:
|
update-packagejson:
|
||||||
runs-on: ubuntu-latest
|
uses: Cysharp/Actions/.github/workflows/update-packagejson.yaml@main
|
||||||
env:
|
with:
|
||||||
TARGET_FILE: ./src/UniTask/Assets/Plugins/UniTask/package.json
|
file-path: ./src/UniTask/Assets/Plugins/UniTask/package.json
|
||||||
outputs:
|
tag: ${{ github.event.inputs.tag }}
|
||||||
sha: ${{ steps.commit.outputs.sha }}
|
dry-run: ${{ fromJson(github.event.inputs.dry-run) }}
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- name: Output package.json (Before)
|
|
||||||
run: cat ${{ env.TARGET_FILE}}
|
|
||||||
|
|
||||||
- name: Update package.json to version ${{ env.GIT_TAG }}
|
|
||||||
run: sed -i -e "s/\(\"version\":\) \"\(.*\)\",/\1 \"${{ env.GIT_TAG }}\",/" ${{ env.TARGET_FILE }}
|
|
||||||
|
|
||||||
- name: Check update
|
|
||||||
id: check_update
|
|
||||||
run: |
|
|
||||||
cat ${{ env.TARGET_FILE}}
|
|
||||||
git diff --exit-code || echo "::set-output name=changed::1"
|
|
||||||
|
|
||||||
- name: Commit files
|
|
||||||
id: commit
|
|
||||||
if: steps.check_update.outputs.changed == '1'
|
|
||||||
run: |
|
|
||||||
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
||||||
git config --local user.name "github-actions[bot]"
|
|
||||||
git commit -m "feat: Update package.json to ${{ env.GIT_TAG }}" -a
|
|
||||||
echo "::set-output name=sha::$(git rev-parse HEAD)"
|
|
||||||
|
|
||||||
- name: Check sha
|
|
||||||
run: echo "SHA ${SHA}"
|
|
||||||
env:
|
|
||||||
SHA: ${{ steps.commit.outputs.sha }}
|
|
||||||
|
|
||||||
- name: Create Tag
|
|
||||||
if: steps.check_update.outputs.changed == '1'
|
|
||||||
run: git tag ${{ env.GIT_TAG }}
|
|
||||||
|
|
||||||
- name: Push changes
|
|
||||||
if: env.DRY_RUN == 'false' && steps.check_update.outputs.changed == '1'
|
|
||||||
uses: ad-m/github-push-action@master
|
|
||||||
with:
|
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
branch: ${{ github.ref }}
|
|
||||||
tags: true
|
|
||||||
|
|
||||||
- name: Push changes (dry_run)
|
|
||||||
if: env.DRY_RUN == 'true' && steps.check_update.outputs.changed == '1'
|
|
||||||
uses: ad-m/github-push-action@master
|
|
||||||
with:
|
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
branch: ${{ env.DRY_RUN_BRANCH_PREFIX }}-${{ env.GIT_TAG }}
|
|
||||||
tags: false
|
|
||||||
|
|
||||||
build-dotnet:
|
build-dotnet:
|
||||||
needs: [update-packagejson]
|
needs: [update-packagejson]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
env:
|
|
||||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
|
||||||
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
|
|
||||||
NUGET_XMLDOC_MODE: skip
|
|
||||||
steps:
|
steps:
|
||||||
- run: echo ${{ needs.update-packagejson.outputs.sha }}
|
- run: echo ${{ needs.update-packagejson.outputs.sha }}
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
ref: ${{ needs.update-packagejson.outputs.sha }}
|
ref: ${{ needs.update-packagejson.outputs.sha }}
|
||||||
- uses: actions/setup-dotnet@v1
|
- uses: Cysharp/Actions/.github/actions/setup-dotnet@main
|
||||||
with:
|
with:
|
||||||
dotnet-version: "${{ env.DOTNET_SDK_VERSION_3 }}"
|
dotnet-version: |
|
||||||
|
3.1.x
|
||||||
|
6.0.x
|
||||||
# build and pack
|
# build and pack
|
||||||
- run: dotnet build -c Release -p:Version=${{ env.GIT_TAG }}
|
- run: dotnet build -c Release -p:Version=${{ env.GIT_TAG }}
|
||||||
- run: dotnet test -c Release --no-build
|
- run: dotnet test -c Release --no-build
|
||||||
- run: dotnet pack ./src/UniTask.NetCore/UniTask.NetCore.csproj -c Release --no-build -p:Version=${{ env.GIT_TAG }} -o ./publish
|
- run: dotnet pack ./src/UniTask.NetCore/UniTask.NetCore.csproj -c Release --no-build -p:Version=${{ env.GIT_TAG }} -o ./publish
|
||||||
# Store artifacts.
|
# Store artifacts.
|
||||||
- uses: actions/upload-artifact@v1
|
- uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: nuget
|
name: nuget
|
||||||
path: ./publish/
|
path: ./publish/
|
||||||
@@ -103,38 +53,30 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
unity: ["2019.3.9f1"]
|
unity: ["2019.3.9f1"]
|
||||||
include:
|
|
||||||
- unity: 2019.3.9f1
|
|
||||||
license: UNITY_LICENSE_2019
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 15
|
timeout-minutes: 15
|
||||||
steps:
|
steps:
|
||||||
- run: echo ${{ needs.update-packagejson.outputs.sha }}
|
- run: echo ${{ needs.update-packagejson.outputs.sha }}
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
ref: ${{ needs.update-packagejson.outputs.sha }}
|
ref: ${{ needs.update-packagejson.outputs.sha }}
|
||||||
# Execute scripts: Export Package
|
# Execute scripts: Export Package
|
||||||
# /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod PackageExporter.Export
|
# /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod PackageExporter.Export
|
||||||
- name: Export unitypackage
|
- name: Build Unity (.unitypacakge)
|
||||||
uses: game-ci/unity-builder@v2.0-alpha-6
|
uses: Cysharp/Actions/.github/actions/unity-builder@main
|
||||||
env:
|
env:
|
||||||
UNITY_LICENSE: ${{ secrets[matrix.license] }}
|
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
|
||||||
|
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
|
||||||
|
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
|
||||||
with:
|
with:
|
||||||
projectPath: src/UniTask
|
projectPath: src/UniTask
|
||||||
unityVersion: ${{ matrix.unity }}
|
unityVersion: ${{ matrix.unity }}
|
||||||
targetPlatform: StandaloneLinux64
|
targetPlatform: StandaloneLinux64
|
||||||
buildMethod: PackageExporter.Export
|
buildMethod: PackageExporter.Export
|
||||||
versioning: None
|
|
||||||
|
|
||||||
- name: check all .meta is commited
|
- uses: Cysharp/Actions/.github/actions/check-metas@main # check meta files
|
||||||
run: |
|
with:
|
||||||
if git ls-files --others --exclude-standard -t | grep --regexp='[.]meta$'; then
|
directory: src/UniTask
|
||||||
echo "Detected .meta file generated. Do you forgot commit a .meta file?"
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
echo "Great, all .meta files are commited."
|
|
||||||
fi
|
|
||||||
working-directory: src/UniTask
|
|
||||||
|
|
||||||
# Store artifacts.
|
# Store artifacts.
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v2
|
||||||
@@ -143,18 +85,12 @@ jobs:
|
|||||||
path: ./src/UniTask/UniTask.${{ env.GIT_TAG }}.unitypackage
|
path: ./src/UniTask/UniTask.${{ env.GIT_TAG }}.unitypackage
|
||||||
|
|
||||||
create-release:
|
create-release:
|
||||||
if: github.event.inputs.dry_run == 'false'
|
if: github.event.inputs.dry-run == 'false'
|
||||||
needs: [update-packagejson, build-dotnet, build-unity]
|
needs: [update-packagejson, build-dotnet, build-unity]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
env:
|
timeout-minutes: 10
|
||||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
|
||||||
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
|
|
||||||
NUGET_XMLDOC_MODE: skip
|
|
||||||
steps:
|
steps:
|
||||||
# setup dotnet for nuget push
|
- uses: Cysharp/Actions/.github/actions/setup-dotnet@main
|
||||||
- uses: actions/setup-dotnet@v1
|
|
||||||
with:
|
|
||||||
dotnet-version: "${{ env.DOTNET_SDK_VERSION_3 }}"
|
|
||||||
# Create Releases
|
# Create Releases
|
||||||
- uses: actions/create-release@v1
|
- uses: actions/create-release@v1
|
||||||
id: create_release
|
id: create_release
|
||||||
@@ -181,12 +117,8 @@ jobs:
|
|||||||
asset_content_type: application/octet-stream
|
asset_content_type: application/octet-stream
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
if: github.event.inputs.dry_run == 'true'
|
if: needs.update-packagejson.outputs.is-branch-created == 'true'
|
||||||
needs: [build-dotnet, build-unity]
|
needs: [update-packagejson, build-dotnet, build-unity]
|
||||||
runs-on: ubuntu-latest
|
uses: Cysharp/Actions/.github/workflows/clean-packagejson-branch.yaml@main
|
||||||
steps:
|
with:
|
||||||
- name: Delete branch
|
branch: ${{ needs.update-packagejson.outputs.branch-name }}
|
||||||
uses: dawidd6/action-delete-branch@v3
|
|
||||||
with:
|
|
||||||
github_token: ${{ github.token }}
|
|
||||||
branches: ${{ env.DRY_RUN_BRANCH_PREFIX }}-${{ env.GIT_TAG }}
|
|
||||||
|
|||||||
10
.github/workflows/prevent-github-change.yml
vendored
Normal file
10
.github/workflows/prevent-github-change.yml
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
name: Prevent github change
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- ".github/**/*.yaml"
|
||||||
|
- ".github/**/*.yml"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
detect:
|
||||||
|
uses: Cysharp/Actions/.github/workflows/prevent-github-change.yaml@main
|
||||||
18
.github/workflows/stale.yml
vendored
18
.github/workflows/stale.yml
vendored
@@ -1,24 +1,10 @@
|
|||||||
name: "Close stale issues"
|
name: "Close stale issues"
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: "0 0 * * *"
|
- cron: "0 0 * * *"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
stale:
|
stale:
|
||||||
runs-on: ubuntu-latest
|
uses: Cysharp/Actions/.github/workflows/stale-issue.yaml@main
|
||||||
steps:
|
|
||||||
- uses: actions/stale@v3
|
|
||||||
with:
|
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
# enable issue
|
|
||||||
stale-issue-message: "This issue is stale because it has been open 90 days with no activity. Remove stale label or comment or this will be closed in 7 days."
|
|
||||||
stale-issue-label: "stale"
|
|
||||||
# enable pr
|
|
||||||
stale-pr-message: "This PR is stale because it has been open 90 days with no activity. Remove stale label or comment or this will be closed in 7 days."
|
|
||||||
stale-pr-label: "stale"
|
|
||||||
days-before-stale: 90
|
|
||||||
days-before-close: 7
|
|
||||||
exempt-issue-labels: "wip"
|
|
||||||
exempt-pr-labels: "wip"
|
|
||||||
remove-stale-when-updated: true
|
|
||||||
|
|||||||
73
README.md
73
README.md
@@ -1,6 +1,6 @@
|
|||||||
UniTask
|
UniTask
|
||||||
===
|
===
|
||||||
[](https://github.com/Cysharp/UniTask/actions) [](https://github.com/Cysharp/UniTask/releases)
|
[](https://github.com/Cysharp/UniTask/actions) [](https://github.com/Cysharp/UniTask/releases) [](https://github.com/Cysharp/UniTask/blob/master/README_CN.md)
|
||||||
|
|
||||||
Provides an efficient allocation free async/await integration for Unity.
|
Provides an efficient allocation free async/await integration for Unity.
|
||||||
|
|
||||||
@@ -44,7 +44,6 @@ For advanced tips, see blog post: [Extends UnityWebRequest via async decorator p
|
|||||||
- [API References](#api-references)
|
- [API References](#api-references)
|
||||||
- [UPM Package](#upm-package)
|
- [UPM Package](#upm-package)
|
||||||
- [Install via git URL](#install-via-git-url)
|
- [Install via git URL](#install-via-git-url)
|
||||||
- [Install via OpenUPM](#install-via-openupm)
|
|
||||||
- [.NET Core](#net-core)
|
- [.NET Core](#net-core)
|
||||||
- [License](#license)
|
- [License](#license)
|
||||||
|
|
||||||
@@ -86,8 +85,13 @@ async UniTask<string> DemoAsync()
|
|||||||
await UniTask.Yield();
|
await UniTask.Yield();
|
||||||
await UniTask.NextFrame();
|
await UniTask.NextFrame();
|
||||||
|
|
||||||
// replacement of WaitForEndOfFrame(same as UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate))
|
// replacement of WaitForEndOfFrame
|
||||||
|
#if UNITY_2023_1_OR_NEWER
|
||||||
await UniTask.WaitForEndOfFrame();
|
await UniTask.WaitForEndOfFrame();
|
||||||
|
#else
|
||||||
|
// requires MonoBehaviour(CoroutineRunner))
|
||||||
|
await UniTask.WaitForEndOfFrame(this); // this is MonoBehaviour
|
||||||
|
#endif
|
||||||
|
|
||||||
// replacement of yield return new WaitForFixedUpdate(same as UniTask.Yield(PlayerLoopTiming.FixedUpdate))
|
// replacement of yield return new WaitForFixedUpdate(same as UniTask.Yield(PlayerLoopTiming.FixedUpdate))
|
||||||
await UniTask.WaitForFixedUpdate();
|
await UniTask.WaitForFixedUpdate();
|
||||||
@@ -496,17 +500,19 @@ public enum PlayerLoopTiming
|
|||||||
|
|
||||||
It indicates when to run, you can check [PlayerLoopList.md](https://gist.github.com/neuecc/bc3a1cfd4d74501ad057e49efcd7bdae) to Unity's default playerloop and injected UniTask's custom loop.
|
It indicates when to run, you can check [PlayerLoopList.md](https://gist.github.com/neuecc/bc3a1cfd4d74501ad057e49efcd7bdae) to Unity's default playerloop and injected UniTask's custom loop.
|
||||||
|
|
||||||
`PlayerLoopTiming.Update` is similar to `yield return null` in a coroutine, but it is called before Update(Update and uGUI events(button.onClick, etc...) are called on `ScriptRunBehaviourUpdate`, yield return null is called on `ScriptRunDelayedDynamicFrameRate`). `PlayerLoopTiming.FixedUpdate` is similar to `WaitForFixedUpdate`, `PlayerLoopTiming.LastPostLateUpdate` is similar to `WaitForEndOfFrame` in coroutine.
|
`PlayerLoopTiming.Update` is similar to `yield return null` in a coroutine, but it is called before Update(Update and uGUI events(button.onClick, etc...) are called on `ScriptRunBehaviourUpdate`, yield return null is called on `ScriptRunDelayedDynamicFrameRate`). `PlayerLoopTiming.FixedUpdate` is similar to `WaitForFixedUpdate`.
|
||||||
|
|
||||||
> `await UniTask.WaitForEndOfFrame()` is not equivalent to coroutine's `yield return new WaitForEndOfFrame()`. Coroutine's WaitForEndOfFrame seems to run after the PlayerLoop is done. Some methods that require coroutine's end of frame(`ScreenCapture.CaptureScreenshotAsTexture`, `CommandBuffer`, etc) do not work correctly when replaced with async/await. In these cases, use a coroutine instead.
|
> `PlayerLoopTiming.LastPostLateUpdate` is not equivalent to coroutine's `yield return new WaitForEndOfFrame()`. Coroutine's WaitForEndOfFrame seems to run after the PlayerLoop is done. Some methods that require coroutine's end of frame(`Texture2D.ReadPixels`, `ScreenCapture.CaptureScreenshotAsTexture`, `CommandBuffer`, etc) do not work correctly when replaced with async/await. In these cases, pass MonoBehaviour(coroutine runnner) to `UniTask.WaitForEndOfFrame`. For example, `await UniTask.WaitForEndOfFrame(this);` is lightweight allocation free alternative of `yield return new WaitForEndOfFrame()`.
|
||||||
|
>
|
||||||
|
> Note: In Unity 2023.1 or newer, `await UniTask.WaitForEndOfFrame();` no longer requires MonoBehaviour. It uses `UnityEngine.Awaitable.EndOfFrameAsync`.
|
||||||
|
|
||||||
`yield return null` and `UniTask.Yield` are similar but different. `yield return null` always returns next frame but `UniTask.Yield` returns next called. That is, call `UniTask.Yield(PlayerLoopTiming.Update)` on `PreUpdate`, it returns same frame. `UniTask.NextFrame()` guarantees return next frame, you can expect this to behave exactly the same as `yield return null`.
|
`yield return null` and `UniTask.Yield` are similar but different. `yield return null` always returns next frame but `UniTask.Yield` returns next called. That is, call `UniTask.Yield(PlayerLoopTiming.Update)` on `PreUpdate`, it returns same frame. `UniTask.NextFrame()` guarantees return next frame, you can expect this to behave exactly the same as `yield return null`.
|
||||||
|
|
||||||
> UniTask.Yield(without CancellationToken) is a special type, returns `YieldAwaitable` and run on YieldRunner. It is most lightweight and fastest.
|
> UniTask.Yield(without CancellationToken) is a special type, returns `YieldAwaitable` and runs on YieldRunner. It is the most lightweight and fastest.
|
||||||
|
|
||||||
`AsyncOperation` is returned from native timing. For example, await `SceneManager.LoadSceneAsync` is returned from `EarlyUpdate.UpdatePreloading` and after being called, the loaded scene's `Start` is called from `EarlyUpdate.ScriptRunDelayedStartupFrame`. Also `await UnityWebRequest` is returned from `EarlyUpdate.ExecuteMainThreadJobs`.
|
`AsyncOperation` is returned from native timing. For example, await `SceneManager.LoadSceneAsync` is returned from `EarlyUpdate.UpdatePreloading` and after being called, the loaded scene's `Start` is called from `EarlyUpdate.ScriptRunDelayedStartupFrame`. Also `await UnityWebRequest` is returned from `EarlyUpdate.ExecuteMainThreadJobs`.
|
||||||
|
|
||||||
In UniTask, await directly uses native timing, `WithCancellation` and `ToUniTask` use specified timing. This is usually not a particular problem, but with `LoadSceneAsync`, it causes a different order of Start and continuation after await. So it is recommended not to use `LoadSceneAsync.ToUniTask`.
|
In UniTask, await directly uses native timing, while `WithCancellation` and `ToUniTask` use specified timing. This is usually not a particular problem, but with `LoadSceneAsync`, it causes a different order of Start and continuation after await. So it is recommended not to use `LoadSceneAsync.ToUniTask`.
|
||||||
|
|
||||||
In the stacktrace, you can check where it is running in playerloop.
|
In the stacktrace, you can check where it is running in playerloop.
|
||||||
|
|
||||||
@@ -658,7 +664,8 @@ By default, UniTask supports TextMeshPro(`BindTo(TMP_Text)` and `TMP_InputField`
|
|||||||
|
|
||||||
There are defined in separated asmdefs like `UniTask.TextMeshPro`, `UniTask.DOTween`, `UniTask.Addressables`.
|
There are defined in separated asmdefs like `UniTask.TextMeshPro`, `UniTask.DOTween`, `UniTask.Addressables`.
|
||||||
|
|
||||||
TextMeshPro and Addressables support are automatically enabled when importing their packages from package manager. However for DOTween support, it is required to import `com.demigiant.dotween` from [OpenUPM](https://openupm.com/packages/com.demigiant.dotween/) or to define `UNITASK_DOTWEEN_SUPPORT` to enable it.
|
TextMeshPro and Addressables support are automatically enabled when importing their packages from package manager.
|
||||||
|
However for DOTween support, after importing from the [DOTWeen assets](https://assetstore.unity.com/packages/tools/animation/dotween-hotween-v2-27676r) and define the scripting define symbol `UNITASK_DOTWEEN_SUPPORT` to enable it.
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
// sequential
|
// sequential
|
||||||
@@ -717,7 +724,7 @@ Async LINQ is enabled when `using Cysharp.Threading.Tasks.Linq;`, and `UniTaskAs
|
|||||||
|
|
||||||
It's closer to UniRx (Reactive Extensions), but UniTaskAsyncEnumerable is a pull-based asynchronous stream, whereas Rx was a push-based asynchronous stream. Note that although similar, the characteristics are different and the details behave differently along with them.
|
It's closer to UniRx (Reactive Extensions), but UniTaskAsyncEnumerable is a pull-based asynchronous stream, whereas Rx was a push-based asynchronous stream. Note that although similar, the characteristics are different and the details behave differently along with them.
|
||||||
|
|
||||||
`UniTaskAsyncEnumerable` is the entry point like `Enumerable`. In addition to the standard query operators, there are other generators for Unity such as `EveryUpdate`, `Timer`, `TimerFrame`, `Interval`, `IntervalFrame`, and `EveryValueChanged`. And also added additional UniTask original query operators like `Append`, `Prepend`, `DistinctUntilChanged`, `ToHashSet`, `Buffer`, `CombineLatest`, `Do`, `Never`, `ForEachAsync`, `Pairwise`, `Publish`, `Queue`, `Return`, `SkipUntil`, `TakeUntil`, `SkipUntilCanceled`, `TakeUntilCanceled`, `TakeLast`, `Subscribe`.
|
`UniTaskAsyncEnumerable` is the entry point like `Enumerable`. In addition to the standard query operators, there are other generators for Unity such as `EveryUpdate`, `Timer`, `TimerFrame`, `Interval`, `IntervalFrame`, and `EveryValueChanged`. And also added additional UniTask original query operators like `Append`, `Prepend`, `DistinctUntilChanged`, `ToHashSet`, `Buffer`, `CombineLatest`,`Merge` `Do`, `Never`, `ForEachAsync`, `Pairwise`, `Publish`, `Queue`, `Return`, `SkipUntil`, `TakeUntil`, `SkipUntilCanceled`, `TakeUntilCanceled`, `TakeLast`, `Subscribe`.
|
||||||
|
|
||||||
The method with Func as an argument has three additional overloads, `***Await`, `***AwaitWithCancellation`.
|
The method with Func as an argument has three additional overloads, `***Await`, `***AwaitWithCancellation`.
|
||||||
|
|
||||||
@@ -805,32 +812,13 @@ async UniTask TripleClick(CancellationToken token)
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
All MonoBehaviour message events can convert async-streams by `AsyncTriggers` that can be enabled by `using Cysharp.Threading.Tasks.Triggers;`.
|
All MonoBehaviour message events can convert async-streams by `AsyncTriggers` that can be enabled by `using Cysharp.Threading.Tasks.Triggers;`. AsyncTrigger can be created using `GetAsync***Trigger` and triggers itself as UniTaskAsyncEnumerable.
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
using Cysharp.Threading.Tasks.Triggers;
|
var trigger = this.GetOnCollisionEnterAsyncHandler();
|
||||||
|
await trigger.OnCollisionEnterAsync();
|
||||||
async UniTaskVoid MonitorCollision()
|
await trigger.OnCollisionEnterAsync();
|
||||||
{
|
await trigger.OnCollisionEnterAsync();
|
||||||
await gameObject.OnCollisionEnterAsync();
|
|
||||||
Debug.Log("Collision Enter");
|
|
||||||
/* do anything */
|
|
||||||
|
|
||||||
await gameObject.OnCollisionExitAsync();
|
|
||||||
Debug.Log("Collision Exit");
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Similar to uGUI event, AsyncTrigger can be created using `GetAsync***Trigger` and triggers itself as UniTaskAsyncEnumerable.
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
// use await multiple times, get AsyncTriggerHandler is more efficient.
|
|
||||||
using(var trigger = this.GetOnCollisionEnterAsyncHandler())
|
|
||||||
{
|
|
||||||
await OnCollisionEnterAsync();
|
|
||||||
await OnCollisionEnterAsync();
|
|
||||||
await OnCollisionEnterAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
// every moves.
|
// every moves.
|
||||||
await this.GetAsyncMoveTrigger().ForEachAsync(axisEventData =>
|
await this.GetAsyncMoveTrigger().ForEachAsync(axisEventData =>
|
||||||
@@ -858,9 +846,9 @@ rp.WithoutCurrent().BindTo(this.textComponent);
|
|||||||
|
|
||||||
await rp.WaitAsync(); // wait until next value set
|
await rp.WaitAsync(); // wait until next value set
|
||||||
|
|
||||||
// also exists ToReadOnlyReactiveProperty
|
// also exists ToReadOnlyAsyncReactiveProperty
|
||||||
var rp2 = new AsyncReactiveProperty<int>(99);
|
var rp2 = new AsyncReactiveProperty<int>(99);
|
||||||
var rorp = rp.CombineLatest(rp2, (x, y) => (x, y)).ToReadOnlyReactiveProperty();
|
var rorp = rp.CombineLatest(rp2, (x, y) => (x, y)).ToReadOnlyAsyncReactiveProperty(CancellationToken.None);
|
||||||
```
|
```
|
||||||
|
|
||||||
A pull-type asynchronous stream does not get the next values until the asynchronous processing in the sequence is complete. This could spill data from push-type events such as buttons.
|
A pull-type asynchronous stream does not get the next values until the asynchronous processing in the sequence is complete. This could spill data from push-type events such as buttons.
|
||||||
@@ -966,9 +954,9 @@ UniTask's own unit tests are written using Unity Test Runner and [Cysharp/Runtim
|
|||||||
|
|
||||||
ThreadPool limitation
|
ThreadPool limitation
|
||||||
---
|
---
|
||||||
Most UniTask methods run on a single thread (PlayerLoop), with only `UniTask.Run` and `UniTask.SwitchToThreadPool` running on a thread pool. If you use a thread pool, it won't work with WebGL and so on.
|
Most UniTask methods run on a single thread (PlayerLoop), with only `UniTask.Run`(`Task.Run` equivalent) and `UniTask.SwitchToThreadPool` running on a thread pool. If you use a thread pool, it won't work with WebGL and so on.
|
||||||
|
|
||||||
`UniTask.Run` will be deprecated in the future (marked with an Obsolete) and only `RunOnThreadPool` will be used. If you use `UniTask.Run`, consider whether you can use `UniTask.Create` or `UniTask.Void`.
|
`UniTask.Run` is now deprecated. You can use `UniTask.RunOnThreadPool` instead. And also consider whether you can use `UniTask.Create` or `UniTask.Void`.
|
||||||
|
|
||||||
IEnumerator.ToUniTask limitation
|
IEnumerator.ToUniTask limitation
|
||||||
---
|
---
|
||||||
@@ -1023,7 +1011,7 @@ Use UniTask type.
|
|||||||
| `IAsyncDisposable` | `IUniTaskAsyncDisposable` |
|
| `IAsyncDisposable` | `IUniTaskAsyncDisposable` |
|
||||||
| `Task.Delay` | `UniTask.Delay` |
|
| `Task.Delay` | `UniTask.Delay` |
|
||||||
| `Task.Yield` | `UniTask.Yield` |
|
| `Task.Yield` | `UniTask.Yield` |
|
||||||
| `Task.Run` | `UniTask.Run` |
|
| `Task.Run` | `UniTask.RunOnThreadPool` |
|
||||||
| `Task.WhenAll` | `UniTask.WhenAll` |
|
| `Task.WhenAll` | `UniTask.WhenAll` |
|
||||||
| `Task.WhenAny` | `UniTask.WhenAny` |
|
| `Task.WhenAny` | `UniTask.WhenAny` |
|
||||||
| `Task.CompletedTask` | `UniTask.CompletedTask` |
|
| `Task.CompletedTask` | `UniTask.CompletedTask` |
|
||||||
@@ -1091,13 +1079,6 @@ or add `"com.cysharp.unitask": "https://github.com/Cysharp/UniTask.git?path=src/
|
|||||||
|
|
||||||
If you want to set a target version, UniTask uses the `*.*.*` release tag so you can specify a version like `#2.1.0`. For example `https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask#2.1.0`.
|
If you want to set a target version, UniTask uses the `*.*.*` release tag so you can specify a version like `#2.1.0`. For example `https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask#2.1.0`.
|
||||||
|
|
||||||
### Install via OpenUPM
|
|
||||||
|
|
||||||
The package is available on the [openupm registry](https://openupm.com). It's recommended to install it via [openupm-cli](https://github.com/openupm/openupm-cli).
|
|
||||||
|
|
||||||
```
|
|
||||||
openupm add com.cysharp.unitask
|
|
||||||
```
|
|
||||||
|
|
||||||
.NET Core
|
.NET Core
|
||||||
---
|
---
|
||||||
@@ -1107,7 +1088,7 @@ For .NET Core, use NuGet.
|
|||||||
|
|
||||||
UniTask of .NET Core version is a subset of Unity UniTask with PlayerLoop dependent methods removed.
|
UniTask of .NET Core version is a subset of Unity UniTask with PlayerLoop dependent methods removed.
|
||||||
|
|
||||||
It runs at higher performance than the standard Task/ValueTask, but you should be careful to ignore the ExecutionContext/SynchronizationContext when using it. `AysncLocal` also does not work because it ignores ExecutionContext.
|
It runs at higher performance than the standard Task/ValueTask, but you should be careful to ignore the ExecutionContext/SynchronizationContext when using it. `AsyncLocal` also does not work because it ignores ExecutionContext.
|
||||||
|
|
||||||
If you use UniTask internally, but provide ValueTask as an external API, you can write it like the following(Inspired by [PooledAwait](https://github.com/mgravell/PooledAwait)).
|
If you use UniTask internally, but provide ValueTask as an external API, you can write it like the following(Inspired by [PooledAwait](https://github.com/mgravell/PooledAwait)).
|
||||||
|
|
||||||
|
|||||||
1134
README_CN.md
Normal file
1134
README_CN.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,13 +1,15 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio Version 16
|
# Visual Studio Version 17
|
||||||
VisualStudioVersion = 16.0.29613.14
|
VisualStudioVersion = 17.0.31606.5
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniTask.NetCoreTests", "src\UniTask.NetCoreTests\UniTask.NetCoreTests.csproj", "{B3E311A4-70D8-4131-9965-C073A99D201A}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniTask.NetCoreTests", "src\UniTask.NetCoreTests\UniTask.NetCoreTests.csproj", "{B3E311A4-70D8-4131-9965-C073A99D201A}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniTask.NetCore", "src\UniTask.NetCore\UniTask.NetCore.csproj", "{16EE20D0-7FB1-483A-8467-A5EEDBF1F5BF}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniTask.NetCore", "src\UniTask.NetCore\UniTask.NetCore.csproj", "{16EE20D0-7FB1-483A-8467-A5EEDBF1F5BF}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniTask.NetCoreSandbox", "src\UniTask.NetCoreSandbox\UniTask.NetCoreSandbox.csproj", "{3915E72E-33E0-4A14-A6D8-872702200E58}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniTask.NetCoreSandbox", "src\UniTask.NetCoreSandbox\UniTask.NetCoreSandbox.csproj", "{3915E72E-33E0-4A14-A6D8-872702200E58}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniTask.Analyzer", "src\UniTask.Analyzer\UniTask.Analyzer.csproj", "{0AC6F052-A255-4EE3-9E05-1C02D49AB1C2}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
@@ -27,6 +29,10 @@ Global
|
|||||||
{3915E72E-33E0-4A14-A6D8-872702200E58}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{3915E72E-33E0-4A14-A6D8-872702200E58}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{3915E72E-33E0-4A14-A6D8-872702200E58}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{3915E72E-33E0-4A14-A6D8-872702200E58}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{3915E72E-33E0-4A14-A6D8-872702200E58}.Release|Any CPU.Build.0 = Release|Any CPU
|
{3915E72E-33E0-4A14-A6D8-872702200E58}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{0AC6F052-A255-4EE3-9E05-1C02D49AB1C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{0AC6F052-A255-4EE3-9E05-1C02D49AB1C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{0AC6F052-A255-4EE3-9E05-1C02D49AB1C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{0AC6F052-A255-4EE3-9E05-1C02D49AB1C2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|||||||
8
src/UniTask.Analyzer/Properties/launchSettings.json
Normal file
8
src/UniTask.Analyzer/Properties/launchSettings.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"profiles": {
|
||||||
|
"UniTask.Analyzer": {
|
||||||
|
"commandName": "DebugRoslynComponent",
|
||||||
|
"targetProject": "..\\UniTask.NetCoreSandbox\\UniTask.NetCoreSandbox.csproj"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
src/UniTask.Analyzer/UniTask.Analyzer.csproj
Normal file
29
src/UniTask.Analyzer/UniTask.Analyzer.csproj
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>library</OutputType>
|
||||||
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<IsRoslynComponent>true</IsRoslynComponent>
|
||||||
|
<TargetsForTfmSpecificContentInPackage>$(TargetsForTfmSpecificContentInPackage);PackBuildOutputs</TargetsForTfmSpecificContentInPackage>
|
||||||
|
<IncludeBuildOutput>false</IncludeBuildOutput>
|
||||||
|
<IncludeSymbols>false</IncludeSymbols>
|
||||||
|
<SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
|
||||||
|
<DevelopmentDependency>true</DevelopmentDependency>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.2">
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.8.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<Target Name="PackBuildOutputs" DependsOnTargets="SatelliteDllsProjectOutputGroup;DebugSymbolsProjectOutputGroup">
|
||||||
|
<ItemGroup>
|
||||||
|
<TfmSpecificPackageFile Include="$(TargetDir)\*.dll" PackagePath="analyzers\dotnet\cs" />
|
||||||
|
<TfmSpecificPackageFile Include="@(SatelliteDllsProjectOutputGroupOutput->'%(FinalOutputPath)')" PackagePath="analyzers\dotnet\cs\%(SatelliteDllsProjectOutputGroupOutput.Culture)\" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Target>
|
||||||
|
</Project>
|
||||||
54
src/UniTask.Analyzer/UniTaskAnalyzer.cs
Normal file
54
src/UniTask.Analyzer/UniTaskAnalyzer.cs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
#pragma warning disable RS2008
|
||||||
|
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis.Diagnostics;
|
||||||
|
using Microsoft.CodeAnalysis.Operations;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace UniTask.Analyzer
|
||||||
|
{
|
||||||
|
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||||
|
public class UniTaskAnalyzer : DiagnosticAnalyzer
|
||||||
|
{
|
||||||
|
private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(
|
||||||
|
id: "UNITASK001",
|
||||||
|
title: "UniTaskAnalyzer001: Must pass CancellationToken",
|
||||||
|
messageFormat: "Must pass CancellationToken",
|
||||||
|
category: "Usage",
|
||||||
|
defaultSeverity: DiagnosticSeverity.Error,
|
||||||
|
isEnabledByDefault: true,
|
||||||
|
description: "Pass CancellationToken or CancellationToken.None.");
|
||||||
|
|
||||||
|
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(Rule); } }
|
||||||
|
|
||||||
|
public override void Initialize(AnalysisContext context)
|
||||||
|
{
|
||||||
|
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
|
||||||
|
context.EnableConcurrentExecution();
|
||||||
|
|
||||||
|
context.RegisterOperationAction(AnalyzeOperation, OperationKind.Invocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AnalyzeOperation(OperationAnalysisContext context)
|
||||||
|
{
|
||||||
|
var token = context.Compilation.GetTypeByMetadataName(typeof(CancellationToken).FullName);
|
||||||
|
if (token == null) return;
|
||||||
|
|
||||||
|
if (context.Operation is IInvocationOperation invocation)
|
||||||
|
{
|
||||||
|
foreach (var arg in invocation.Arguments)
|
||||||
|
{
|
||||||
|
if (arg.ArgumentKind == ArgumentKind.DefaultValue)
|
||||||
|
{
|
||||||
|
if (SymbolEqualityComparer.Default.Equals(arg.Parameter.Type, token))
|
||||||
|
{
|
||||||
|
var diagnostic = Diagnostic.Create(Rule, arg.Syntax.GetLocation());
|
||||||
|
context.ReportDiagnostic(diagnostic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netcoreapp3.1;netstandard2.1;netstandard2.0</TargetFrameworks>
|
<TargetFrameworks>net6.0;net7.0;netstandard2.1;netstandard2.0</TargetFrameworks>
|
||||||
<AssemblyName>UniTask</AssemblyName>
|
<AssemblyName>UniTask</AssemblyName>
|
||||||
<LangVersion>8.0</LangVersion>
|
<LangVersion>8.0</LangVersion>
|
||||||
<RootNamespace>Cysharp.Threading.Tasks</RootNamespace>
|
<RootNamespace>Cysharp.Threading.Tasks</RootNamespace>
|
||||||
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
|
<NoWarn>$(NoWarn);CS1591</NoWarn>
|
||||||
|
|
||||||
<!-- NuGet Packaging -->
|
<!-- NuGet Packaging -->
|
||||||
<Id>UniTask</Id>
|
<Id>UniTask</Id>
|
||||||
@@ -21,6 +23,7 @@
|
|||||||
<PackageIcon>Icon.png</PackageIcon>
|
<PackageIcon>Icon.png</PackageIcon>
|
||||||
<SignAssembly>true</SignAssembly>
|
<SignAssembly>true</SignAssembly>
|
||||||
<AssemblyOriginatorKeyFile>opensource.snk</AssemblyOriginatorKeyFile>
|
<AssemblyOriginatorKeyFile>opensource.snk</AssemblyOriginatorKeyFile>
|
||||||
|
<IsPackable>true</IsPackable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -1,258 +0,0 @@
|
|||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,283 +0,0 @@
|
|||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -17,499 +17,34 @@ using System.Reactive.Concurrency;
|
|||||||
|
|
||||||
namespace NetCoreSandbox
|
namespace NetCoreSandbox
|
||||||
{
|
{
|
||||||
public class MySyncContext : SynchronizationContext
|
public class Program
|
||||||
{
|
{
|
||||||
public MySyncContext()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Post(SendOrPostCallback d, object state)
|
|
||||||
{
|
|
||||||
Console.WriteLine("Called SyncContext Post!");
|
|
||||||
base.Post(d, state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Text
|
|
||||||
{
|
|
||||||
|
|
||||||
public string text { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ZeroAllocAsyncAwaitInDotNetCore
|
|
||||||
{
|
|
||||||
public ValueTask<int> NanikaAsync(int x, int y)
|
|
||||||
{
|
|
||||||
return Core(this, x, y);
|
|
||||||
|
|
||||||
static async UniTask<int> Core(ZeroAllocAsyncAwaitInDotNetCore self, int x, int y)
|
|
||||||
{
|
|
||||||
// nanika suru...
|
|
||||||
await Task.Delay(TimeSpan.FromSeconds(x + y));
|
|
||||||
|
|
||||||
return 10;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public class TaskTestException : Exception
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class Foo
|
|
||||||
{
|
|
||||||
public async UniTask MethodFooAsync()
|
|
||||||
{
|
|
||||||
await MethodBarAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async UniTask MethodBarAsync()
|
|
||||||
|
|
||||||
{
|
|
||||||
Throw();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Throw()
|
|
||||||
{
|
|
||||||
throw new Exception();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct TestAwaiter : ICriticalNotifyCompletion
|
|
||||||
{
|
|
||||||
readonly UniTaskStatus status;
|
|
||||||
readonly bool isCompleted;
|
|
||||||
|
|
||||||
public TestAwaiter(bool isCompleted, UniTaskStatus status)
|
|
||||||
{
|
|
||||||
this.isCompleted = isCompleted;
|
|
||||||
this.status = status;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TestAwaiter GetAwaiter() => this;
|
|
||||||
|
|
||||||
public bool IsCompleted => isCompleted;
|
|
||||||
|
|
||||||
public void GetResult()
|
|
||||||
{
|
|
||||||
switch (status)
|
|
||||||
{
|
|
||||||
case UniTaskStatus.Faulted:
|
|
||||||
throw new TaskTestException();
|
|
||||||
case UniTaskStatus.Canceled:
|
|
||||||
throw new OperationCanceledException();
|
|
||||||
case UniTaskStatus.Pending:
|
|
||||||
case UniTaskStatus.Succeeded:
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnCompleted(Action continuation)
|
|
||||||
{
|
|
||||||
ThreadPool.QueueUserWorkItem(_ => continuation(), null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UnsafeOnCompleted(Action continuation)
|
|
||||||
{
|
|
||||||
ThreadPool.UnsafeQueueUserWorkItem(_ => continuation(), null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public struct TestAwaiter<T> : ICriticalNotifyCompletion
|
|
||||||
{
|
|
||||||
readonly UniTaskStatus status;
|
|
||||||
readonly bool isCompleted;
|
|
||||||
readonly T value;
|
|
||||||
|
|
||||||
public TestAwaiter(bool isCompleted, UniTaskStatus status, T value)
|
|
||||||
{
|
|
||||||
this.isCompleted = isCompleted;
|
|
||||||
this.status = status;
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TestAwaiter<T> GetAwaiter() => this;
|
|
||||||
|
|
||||||
public bool IsCompleted => isCompleted;
|
|
||||||
|
|
||||||
public T GetResult()
|
|
||||||
{
|
|
||||||
switch (status)
|
|
||||||
{
|
|
||||||
case UniTaskStatus.Faulted:
|
|
||||||
throw new TaskTestException();
|
|
||||||
case UniTaskStatus.Canceled:
|
|
||||||
throw new OperationCanceledException();
|
|
||||||
case UniTaskStatus.Pending:
|
|
||||||
case UniTaskStatus.Succeeded:
|
|
||||||
default:
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnCompleted(Action continuation)
|
|
||||||
{
|
|
||||||
ThreadPool.QueueUserWorkItem(_ => continuation(), null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UnsafeOnCompleted(Action continuation)
|
|
||||||
{
|
|
||||||
ThreadPool.UnsafeQueueUserWorkItem(_ => continuation(), null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static partial class UnityUIComponentExtensions
|
|
||||||
{
|
|
||||||
public static void BindTo(this IUniTaskAsyncEnumerable<string> source, Text text)
|
|
||||||
{
|
|
||||||
AAAACORECORE(source, text).Forget();
|
|
||||||
|
|
||||||
async UniTaskVoid AAAACORECORE(IUniTaskAsyncEnumerable<string> source2, Text text2)
|
|
||||||
{
|
|
||||||
var e = source2.GetAsyncEnumerator();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
while (await e.MoveNextAsync())
|
|
||||||
{
|
|
||||||
text2.text = e.Current;
|
|
||||||
// action(e.Current);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (e != null)
|
|
||||||
{
|
|
||||||
await e.DisposeAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//public static IDisposable SubscribeToText<T>(this IObservable<T> source, Text text)
|
|
||||||
//{
|
|
||||||
// return source.SubscribeWithState(text, (x, t) => t.text = x.ToString());
|
|
||||||
//}
|
|
||||||
|
|
||||||
//public static IDisposable SubscribeToText<T>(this IObservable<T> source, Text text, Func<T, string> selector)
|
|
||||||
//{
|
|
||||||
// return source.SubscribeWithState2(text, selector, (x, t, s) => t.text = s(x));
|
|
||||||
//}
|
|
||||||
|
|
||||||
//public static IDisposable SubscribeToInteractable(this IObservable<bool> source, Selectable selectable)
|
|
||||||
//{
|
|
||||||
// return source.SubscribeWithState(selectable, (x, s) => s.interactable = x);
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Program
|
|
||||||
{
|
|
||||||
static string FlattenGenArgs(Type type)
|
|
||||||
{
|
|
||||||
if (type.IsGenericType)
|
|
||||||
{
|
|
||||||
var t = string.Join(", ", type.GetGenericArguments().Select(x => FlattenGenArgs(x)));
|
|
||||||
return Regex.Replace(type.Name, "`.+", "") + "<" + t + ">";
|
|
||||||
}
|
|
||||||
//x.ReturnType.GetGenericArguments()
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return type.Name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static async IAsyncEnumerable<int> FooAsync([EnumeratorCancellation]CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
yield return 1;
|
|
||||||
await Task.Delay(10, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class MyDisposable : IDisposable
|
|
||||||
{
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Test()
|
|
||||||
{
|
|
||||||
var disp = new MyDisposable();
|
|
||||||
|
|
||||||
using var _ = new MyDisposable();
|
|
||||||
|
|
||||||
Console.WriteLine("tako");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static async UniTask FooBarAsync()
|
|
||||||
{
|
|
||||||
await using (UniTask.ReturnToCurrentSynchronizationContext())
|
|
||||||
{
|
|
||||||
await UniTask.SwitchToThreadPool();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static async UniTask Aaa()
|
|
||||||
{
|
|
||||||
await FooBarAsync();
|
|
||||||
|
|
||||||
Console.WriteLine("FooBarAsync End");
|
|
||||||
}
|
|
||||||
|
|
||||||
static async UniTask WhereSelect()
|
|
||||||
{
|
|
||||||
await foreach (var item in UniTaskAsyncEnumerable.Range(1, 10)
|
|
||||||
.SelectAwait(async x =>
|
|
||||||
{
|
|
||||||
await UniTask.Yield();
|
|
||||||
return x;
|
|
||||||
})
|
|
||||||
.Where(x => x % 2 == 0))
|
|
||||||
{
|
|
||||||
Console.WriteLine(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static async Task Main(string[] args)
|
static async Task Main(string[] args)
|
||||||
{
|
{
|
||||||
#if !DEBUG
|
var cts = new CancellationTokenSource();
|
||||||
|
|
||||||
|
|
||||||
|
// OK.
|
||||||
|
await FooAsync(10, cts.Token);
|
||||||
|
|
||||||
|
// NG(Compiler Error)
|
||||||
|
// await FooAsync(10);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//await new AllocationCheck().ViaUniTaskVoid();
|
|
||||||
//Console.ReadLine();
|
|
||||||
BenchmarkDotNet.Running.BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);
|
|
||||||
|
|
||||||
//await new ComparisonBenchmarks().ViaUniTaskT();
|
|
||||||
return;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
var e = UniTaskAsyncEnumerable.Create<int>(async (writer, token) =>
|
|
||||||
{
|
|
||||||
for (int i = 0; i < 5; i++)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Start {i}");
|
|
||||||
await writer.YieldAsync(i);
|
|
||||||
Console.WriteLine($"End {i}");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var ee = e.GetAsyncEnumerator();
|
|
||||||
while (await ee.MoveNextAsync())
|
|
||||||
{
|
|
||||||
Console.WriteLine("ForEach " + ee.Current);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static async UniTask YieldCore()
|
static async UniTask FooAsync(int x, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
await UniTask.Yield();
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,473 +0,0 @@
|
|||||||
using BenchmarkDotNet.Attributes;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
[Config(typeof(BenchmarkConfig))]
|
|
||||||
public class QueueCheck
|
|
||||||
{
|
|
||||||
Node node1 = new Node();
|
|
||||||
Node node2 = new Node();
|
|
||||||
RefNode refNode1 = new RefNode();
|
|
||||||
RefNode refNode2 = new RefNode();
|
|
||||||
Queue<Node> q1 = new Queue<Node>();
|
|
||||||
Stack<Node> s1 = new Stack<Node>();
|
|
||||||
ConcurrentQueue<Node> cq = new ConcurrentQueue<Node>();
|
|
||||||
ConcurrentStack<Node> cs = new ConcurrentStack<Node>();
|
|
||||||
static TaskPool<Node> pool;
|
|
||||||
static TaskPoolRefNode<RefNode> poolRefNode;
|
|
||||||
static TaskPoolEqualNull<Node> poolEqualNull;
|
|
||||||
static TaskPoolClass<Node> poolClass = new TaskPoolClass<Node>();
|
|
||||||
static TaskPoolWithoutSize<Node> poolWithoutSize;
|
|
||||||
static TaskPoolWithoutLock<Node> poolWithoutLock;
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void Queue()
|
|
||||||
{
|
|
||||||
q1.Enqueue(node1);
|
|
||||||
q1.Enqueue(node1);
|
|
||||||
q1.TryDequeue(out _);
|
|
||||||
q1.TryDequeue(out _);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void QueueLock()
|
|
||||||
{
|
|
||||||
lock (q1) { q1.Enqueue(node1); }
|
|
||||||
lock (q1) { q1.Enqueue(node1); }
|
|
||||||
lock (q1) { q1.TryDequeue(out _); }
|
|
||||||
lock (q1) { q1.TryDequeue(out _); }
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void Stack()
|
|
||||||
{
|
|
||||||
s1.Push(node1);
|
|
||||||
s1.Push(node2);
|
|
||||||
s1.TryPop(out _);
|
|
||||||
s1.TryPop(out _);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void StackLock()
|
|
||||||
{
|
|
||||||
lock (s1) { s1.Push(node1); }
|
|
||||||
lock (s1) { s1.Push(node2); }
|
|
||||||
lock (s1) { s1.TryPop(out _); }
|
|
||||||
lock (s1) { s1.TryPop(out _); }
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void ConcurrentQueue()
|
|
||||||
{
|
|
||||||
cq.Enqueue(node1);
|
|
||||||
cq.Enqueue(node1);
|
|
||||||
cq.TryDequeue(out _);
|
|
||||||
cq.TryDequeue(out _);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void ConcurrentStack()
|
|
||||||
{
|
|
||||||
cs.Push(node1);
|
|
||||||
cs.Push(node2);
|
|
||||||
cs.TryPop(out _);
|
|
||||||
cs.TryPop(out _);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void TaskPool()
|
|
||||||
{
|
|
||||||
pool.TryPush(node1);
|
|
||||||
pool.TryPush(node2);
|
|
||||||
pool.TryPop(out _);
|
|
||||||
pool.TryPop(out _);
|
|
||||||
}
|
|
||||||
[Benchmark]
|
|
||||||
public void TaskPoolRefNode()
|
|
||||||
{
|
|
||||||
poolRefNode.TryPush(refNode1);
|
|
||||||
poolRefNode.TryPush(refNode2);
|
|
||||||
poolRefNode.TryPop(out _);
|
|
||||||
poolRefNode.TryPop(out _);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void TaskPoolEqualNull()
|
|
||||||
{
|
|
||||||
poolEqualNull.TryPush(node1);
|
|
||||||
poolEqualNull.TryPush(node2);
|
|
||||||
poolEqualNull.TryPop(out _);
|
|
||||||
poolEqualNull.TryPop(out _);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void TaskPoolClass()
|
|
||||||
{
|
|
||||||
poolClass.TryPush(node1);
|
|
||||||
poolClass.TryPush(node2);
|
|
||||||
poolClass.TryPop(out _);
|
|
||||||
poolClass.TryPop(out _);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void TaskPoolWithoutSize()
|
|
||||||
{
|
|
||||||
poolWithoutSize.TryPush(node1);
|
|
||||||
poolWithoutSize.TryPush(node2);
|
|
||||||
poolWithoutSize.TryPop(out _);
|
|
||||||
poolWithoutSize.TryPop(out _);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void TaskPoolWithoutLock()
|
|
||||||
{
|
|
||||||
poolWithoutLock.TryPush(node1);
|
|
||||||
poolWithoutLock.TryPush(node2);
|
|
||||||
poolWithoutLock.TryPop(out _);
|
|
||||||
poolWithoutLock.TryPop(out _);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class Node : ITaskPoolNode<Node>
|
|
||||||
{
|
|
||||||
public Node NextNode { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface ITaskPoolNode<T>
|
|
||||||
{
|
|
||||||
T NextNode { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class RefNode :ITaskPoolRefNode<RefNode>
|
|
||||||
{
|
|
||||||
RefNode nextNode;
|
|
||||||
public ref RefNode NextNode => ref nextNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface ITaskPoolRefNode<T>
|
|
||||||
{
|
|
||||||
ref T NextNode { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// mutable struct, don't mark readonly.
|
|
||||||
[StructLayout(LayoutKind.Auto)]
|
|
||||||
public struct TaskPoolWithoutLock<T>
|
|
||||||
where T : class, ITaskPoolNode<T>
|
|
||||||
{
|
|
||||||
int size;
|
|
||||||
T root;
|
|
||||||
|
|
||||||
public int Size => size;
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public bool TryPop(out T result)
|
|
||||||
{
|
|
||||||
//if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
|
||||||
{
|
|
||||||
var v = root;
|
|
||||||
if (!(v is null))
|
|
||||||
{
|
|
||||||
root = v.NextNode;
|
|
||||||
v.NextNode = null;
|
|
||||||
size--;
|
|
||||||
result = v;
|
|
||||||
// Volatile.Write(ref gate, 0);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Volatile.Write(ref gate, 0);
|
|
||||||
}
|
|
||||||
result = default;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public bool TryPush(T item)
|
|
||||||
{
|
|
||||||
//if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
|
||||||
{
|
|
||||||
//if (size < TaskPool.MaxPoolSize)
|
|
||||||
{
|
|
||||||
item.NextNode = root;
|
|
||||||
root = item;
|
|
||||||
size++;
|
|
||||||
// Volatile.Write(ref gate, 0);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
//else
|
|
||||||
{
|
|
||||||
// Volatile.Write(ref gate, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Auto)]
|
|
||||||
public struct TaskPool<T>
|
|
||||||
where T : class, ITaskPoolNode<T>
|
|
||||||
{
|
|
||||||
int gate;
|
|
||||||
int size;
|
|
||||||
T root;
|
|
||||||
|
|
||||||
public int Size => size;
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public bool TryPop(out T result)
|
|
||||||
{
|
|
||||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
|
||||||
{
|
|
||||||
var v = root;
|
|
||||||
if (!(v is null))
|
|
||||||
{
|
|
||||||
root = v.NextNode;
|
|
||||||
v.NextNode = null;
|
|
||||||
size--;
|
|
||||||
result = v;
|
|
||||||
Volatile.Write(ref gate, 0);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Volatile.Write(ref gate, 0);
|
|
||||||
}
|
|
||||||
result = default;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public bool TryPush(T item)
|
|
||||||
{
|
|
||||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
|
||||||
{
|
|
||||||
//if (size < TaskPool.MaxPoolSize)
|
|
||||||
{
|
|
||||||
item.NextNode = root;
|
|
||||||
root = item;
|
|
||||||
size++;
|
|
||||||
Volatile.Write(ref gate, 0);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
//else
|
|
||||||
{
|
|
||||||
// Volatile.Write(ref gate, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
[StructLayout(LayoutKind.Auto)]
|
|
||||||
public struct TaskPoolRefNode<T>
|
|
||||||
where T : class, ITaskPoolRefNode<T>
|
|
||||||
{
|
|
||||||
int gate;
|
|
||||||
int size;
|
|
||||||
T root;
|
|
||||||
|
|
||||||
public int Size => size;
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public bool TryPop(out T result)
|
|
||||||
{
|
|
||||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
|
||||||
{
|
|
||||||
var v = root;
|
|
||||||
if (!(v is null))
|
|
||||||
{
|
|
||||||
ref var nextNode = ref v.NextNode;
|
|
||||||
root = nextNode;
|
|
||||||
nextNode = null;
|
|
||||||
size--;
|
|
||||||
result = v;
|
|
||||||
Volatile.Write(ref gate, 0);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Volatile.Write(ref gate, 0);
|
|
||||||
}
|
|
||||||
result = default;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public bool TryPush(T item)
|
|
||||||
{
|
|
||||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
|
||||||
{
|
|
||||||
//if (size < TaskPool.MaxPoolSize)
|
|
||||||
{
|
|
||||||
item.NextNode = root;
|
|
||||||
root = item;
|
|
||||||
size++;
|
|
||||||
Volatile.Write(ref gate, 0);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
//else
|
|
||||||
{
|
|
||||||
// Volatile.Write(ref gate, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Auto)]
|
|
||||||
public struct TaskPoolEqualNull<T>
|
|
||||||
where T : class, ITaskPoolNode<T>
|
|
||||||
{
|
|
||||||
int gate;
|
|
||||||
int size;
|
|
||||||
T root;
|
|
||||||
|
|
||||||
public int Size => size;
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public bool TryPop(out T result)
|
|
||||||
{
|
|
||||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
|
||||||
{
|
|
||||||
var v = root;
|
|
||||||
if (v != null)
|
|
||||||
{
|
|
||||||
root = v.NextNode;
|
|
||||||
v.NextNode = null;
|
|
||||||
size--;
|
|
||||||
result = v;
|
|
||||||
Volatile.Write(ref gate, 0);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Volatile.Write(ref gate, 0);
|
|
||||||
}
|
|
||||||
result = default;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public bool TryPush(T item)
|
|
||||||
{
|
|
||||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
|
||||||
{
|
|
||||||
//if (size < TaskPool.MaxPoolSize)
|
|
||||||
{
|
|
||||||
item.NextNode = root;
|
|
||||||
root = item;
|
|
||||||
size++;
|
|
||||||
Volatile.Write(ref gate, 0);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
//else
|
|
||||||
{
|
|
||||||
// Volatile.Write(ref gate, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class TaskPoolClass<T>
|
|
||||||
where T : class, ITaskPoolNode<T>
|
|
||||||
{
|
|
||||||
int gate;
|
|
||||||
int size;
|
|
||||||
T root;
|
|
||||||
|
|
||||||
public int Size => size;
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public bool TryPop(out T result)
|
|
||||||
{
|
|
||||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
|
||||||
{
|
|
||||||
var v = root;
|
|
||||||
if (!(v is null))
|
|
||||||
{
|
|
||||||
root = v.NextNode;
|
|
||||||
v.NextNode = null;
|
|
||||||
size--;
|
|
||||||
result = v;
|
|
||||||
Volatile.Write(ref gate, 0);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Volatile.Write(ref gate, 0);
|
|
||||||
}
|
|
||||||
result = default;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public bool TryPush(T item)
|
|
||||||
{
|
|
||||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
|
||||||
{
|
|
||||||
//if (size < TaskPool.MaxPoolSize)
|
|
||||||
{
|
|
||||||
item.NextNode = root;
|
|
||||||
root = item;
|
|
||||||
size++;
|
|
||||||
Volatile.Write(ref gate, 0);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
//else
|
|
||||||
{
|
|
||||||
// Volatile.Write(ref gate, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Auto)]
|
|
||||||
public struct TaskPoolWithoutSize<T>
|
|
||||||
where T : class, ITaskPoolNode<T>
|
|
||||||
{
|
|
||||||
int gate;
|
|
||||||
T root;
|
|
||||||
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public bool TryPop(out T result)
|
|
||||||
{
|
|
||||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
|
||||||
{
|
|
||||||
var v = root;
|
|
||||||
if (!(v is null))
|
|
||||||
{
|
|
||||||
root = v.NextNode;
|
|
||||||
v.NextNode = null;
|
|
||||||
result = v;
|
|
||||||
Volatile.Write(ref gate, 0);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Volatile.Write(ref gate, 0);
|
|
||||||
}
|
|
||||||
result = default;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public bool TryPush(T item)
|
|
||||||
{
|
|
||||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
|
||||||
{
|
|
||||||
//if (size < TaskPool.MaxPoolSize)
|
|
||||||
{
|
|
||||||
item.NextNode = root;
|
|
||||||
root = item;
|
|
||||||
Volatile.Write(ref gate, 0);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
//else
|
|
||||||
{
|
|
||||||
// Volatile.Write(ref gate, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,8 +2,9 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
<RootNamespace>NetCoreSandbox</RootNamespace>
|
<RootNamespace>NetCoreSandbox</RootNamespace>
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -15,6 +16,12 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\UniTask.NetCore\UniTask.NetCore.csproj" />
|
<ProjectReference Include="..\UniTask.NetCore\UniTask.NetCore.csproj" />
|
||||||
|
|
||||||
|
|
||||||
|
<ProjectReference Include="..\UniTask.Analyzer\UniTask.Analyzer.csproj">
|
||||||
|
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
|
||||||
|
<OutputItemType>Analyzer</OutputItemType>
|
||||||
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -159,6 +159,30 @@ namespace NetCoreTests.Linq
|
|||||||
list.Should().Equal(100, 200, 300, 400);
|
list.Should().Equal(100, 200, 300, 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task AwaitForeachBreak()
|
||||||
|
{
|
||||||
|
var finallyCalled = false;
|
||||||
|
var enumerable = UniTaskAsyncEnumerable.Create<int>(async (writer, _) =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await writer.YieldAsync(1);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
finallyCalled = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await foreach (var x in enumerable)
|
||||||
|
{
|
||||||
|
x.Should().Be(1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
finallyCalled.Should().BeTrue();
|
||||||
|
}
|
||||||
|
|
||||||
async IAsyncEnumerable<int> Range(int from, int count)
|
async IAsyncEnumerable<int> Range(int from, int count)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
|
|||||||
137
src/UniTask.NetCoreTests/Linq/Merge.cs
Normal file
137
src/UniTask.NetCoreTests/Linq/Merge.cs
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Cysharp.Threading.Tasks;
|
||||||
|
using Cysharp.Threading.Tasks.Linq;
|
||||||
|
using FluentAssertions;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace NetCoreTests.Linq
|
||||||
|
{
|
||||||
|
public class MergeTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task TwoSource()
|
||||||
|
{
|
||||||
|
var semaphore = new SemaphoreSlim(1, 1);
|
||||||
|
|
||||||
|
var a = UniTaskAsyncEnumerable.Create<string>(async (writer, _) =>
|
||||||
|
{
|
||||||
|
await UniTask.SwitchToThreadPool();
|
||||||
|
|
||||||
|
await semaphore.WaitAsync();
|
||||||
|
await writer.YieldAsync("A1");
|
||||||
|
semaphore.Release();
|
||||||
|
|
||||||
|
await semaphore.WaitAsync();
|
||||||
|
await writer.YieldAsync("A2");
|
||||||
|
semaphore.Release();
|
||||||
|
});
|
||||||
|
|
||||||
|
var b = UniTaskAsyncEnumerable.Create<string>(async (writer, _) =>
|
||||||
|
{
|
||||||
|
await UniTask.SwitchToThreadPool();
|
||||||
|
|
||||||
|
await semaphore.WaitAsync();
|
||||||
|
await writer.YieldAsync("B1");
|
||||||
|
await writer.YieldAsync("B2");
|
||||||
|
semaphore.Release();
|
||||||
|
|
||||||
|
await semaphore.WaitAsync();
|
||||||
|
await writer.YieldAsync("B3");
|
||||||
|
semaphore.Release();
|
||||||
|
});
|
||||||
|
|
||||||
|
var result = await a.Merge(b).ToArrayAsync();
|
||||||
|
result.Should().Equal("A1", "B1", "B2", "A2", "B3");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ThreeSource()
|
||||||
|
{
|
||||||
|
var semaphore = new SemaphoreSlim(0, 1);
|
||||||
|
|
||||||
|
var a = UniTaskAsyncEnumerable.Create<string>(async (writer, _) =>
|
||||||
|
{
|
||||||
|
await UniTask.SwitchToThreadPool();
|
||||||
|
|
||||||
|
await semaphore.WaitAsync();
|
||||||
|
await writer.YieldAsync("A1");
|
||||||
|
semaphore.Release();
|
||||||
|
|
||||||
|
await semaphore.WaitAsync();
|
||||||
|
await writer.YieldAsync("A2");
|
||||||
|
semaphore.Release();
|
||||||
|
});
|
||||||
|
|
||||||
|
var b = UniTaskAsyncEnumerable.Create<string>(async (writer, _) =>
|
||||||
|
{
|
||||||
|
await UniTask.SwitchToThreadPool();
|
||||||
|
|
||||||
|
await semaphore.WaitAsync();
|
||||||
|
await writer.YieldAsync("B1");
|
||||||
|
await writer.YieldAsync("B2");
|
||||||
|
semaphore.Release();
|
||||||
|
|
||||||
|
await semaphore.WaitAsync();
|
||||||
|
await writer.YieldAsync("B3");
|
||||||
|
semaphore.Release();
|
||||||
|
});
|
||||||
|
|
||||||
|
var c = UniTaskAsyncEnumerable.Create<string>(async (writer, _) =>
|
||||||
|
{
|
||||||
|
await UniTask.SwitchToThreadPool();
|
||||||
|
|
||||||
|
await writer.YieldAsync("C1");
|
||||||
|
semaphore.Release();
|
||||||
|
});
|
||||||
|
|
||||||
|
var result = await a.Merge(b, c).ToArrayAsync();
|
||||||
|
result.Should().Equal("C1", "A1", "B1", "B2", "A2", "B3");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Throw()
|
||||||
|
{
|
||||||
|
var a = UniTaskAsyncEnumerable.Create<string>(async (writer, _) =>
|
||||||
|
{
|
||||||
|
await writer.YieldAsync("A1");
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
var b = UniTaskAsyncEnumerable.Create<string>(async (writer, _) =>
|
||||||
|
{
|
||||||
|
throw new UniTaskTestException();
|
||||||
|
});
|
||||||
|
|
||||||
|
var enumerator = a.Merge(b).GetAsyncEnumerator();
|
||||||
|
(await enumerator.MoveNextAsync()).Should().Be(true);
|
||||||
|
enumerator.Current.Should().Be("A1");
|
||||||
|
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await enumerator.MoveNextAsync());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Cancel()
|
||||||
|
{
|
||||||
|
var cts = new CancellationTokenSource();
|
||||||
|
|
||||||
|
var a = UniTaskAsyncEnumerable.Create<string>(async (writer, _) =>
|
||||||
|
{
|
||||||
|
await writer.YieldAsync("A1");
|
||||||
|
});
|
||||||
|
|
||||||
|
var b = UniTaskAsyncEnumerable.Create<string>(async (writer, _) =>
|
||||||
|
{
|
||||||
|
await writer.YieldAsync("B1");
|
||||||
|
});
|
||||||
|
|
||||||
|
var enumerator = a.Merge(b).GetAsyncEnumerator(cts.Token);
|
||||||
|
(await enumerator.MoveNextAsync()).Should().Be(true);
|
||||||
|
enumerator.Current.Should().Be("A1");
|
||||||
|
|
||||||
|
cts.Cancel();
|
||||||
|
await Assert.ThrowsAsync<OperationCanceledException>(async () => await enumerator.MoveNextAsync());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
43
src/UniTask.NetCoreTests/TaskExtensionsTest.cs
Normal file
43
src/UniTask.NetCoreTests/TaskExtensionsTest.cs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Cysharp.Threading.Tasks;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace NetCoreTests
|
||||||
|
{
|
||||||
|
public class TaskExtensionsTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task PropagateException()
|
||||||
|
{
|
||||||
|
await Assert.ThrowsAsync<InvalidOperationException>(async () =>
|
||||||
|
{
|
||||||
|
await ThrowAsync().AsUniTask();
|
||||||
|
});
|
||||||
|
|
||||||
|
await Assert.ThrowsAsync<InvalidOperationException>(async () =>
|
||||||
|
{
|
||||||
|
await ThrowOrValueAsync().AsUniTask();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task PropagateExceptionWhenAll()
|
||||||
|
{
|
||||||
|
await Assert.ThrowsAsync<InvalidOperationException>(async () =>
|
||||||
|
{
|
||||||
|
await Task.WhenAll(ThrowAsync(), ThrowAsync()).AsUniTask();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task ThrowAsync()
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task<int> ThrowOrValueAsync()
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
|
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
|
|
||||||
|
|||||||
80
src/UniTask.NetCoreTests/UniTaskCompletionSourceTest.cs
Normal file
80
src/UniTask.NetCoreTests/UniTaskCompletionSourceTest.cs
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using Cysharp.Threading.Tasks;
|
||||||
|
using FluentAssertions;
|
||||||
|
using NetCoreTests.Linq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace NetCoreTests
|
||||||
|
{
|
||||||
|
public class AutoResetUniTaskCompletionSourceTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task SetResultAfterReturn()
|
||||||
|
{
|
||||||
|
var source1 = AutoResetUniTaskCompletionSource.Create();
|
||||||
|
source1.TrySetResult();
|
||||||
|
await source1.Task;
|
||||||
|
|
||||||
|
source1.TrySetResult().Should().BeFalse();
|
||||||
|
|
||||||
|
var source2 = AutoResetUniTaskCompletionSource.Create();
|
||||||
|
source2.TrySetResult();
|
||||||
|
await source2.Task;
|
||||||
|
|
||||||
|
source2.TrySetResult().Should().BeFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SetCancelAfterReturn()
|
||||||
|
{
|
||||||
|
var source = AutoResetUniTaskCompletionSource.Create();
|
||||||
|
source.TrySetResult();
|
||||||
|
await source.Task;
|
||||||
|
|
||||||
|
source.TrySetCanceled().Should().BeFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SetExceptionAfterReturn()
|
||||||
|
{
|
||||||
|
var source = AutoResetUniTaskCompletionSource.Create();
|
||||||
|
source.TrySetResult();
|
||||||
|
await source.Task;
|
||||||
|
|
||||||
|
source.TrySetException(new UniTaskTestException()).Should().BeFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SetResultWithValueAfterReturn()
|
||||||
|
{
|
||||||
|
var source1 = AutoResetUniTaskCompletionSource<int>.Create();
|
||||||
|
source1.TrySetResult(100);
|
||||||
|
(await source1.Task).Should().Be(100);
|
||||||
|
|
||||||
|
source1.TrySetResult(100).Should().BeFalse();
|
||||||
|
|
||||||
|
var source2 = AutoResetUniTaskCompletionSource.Create();
|
||||||
|
source2.TrySetResult();
|
||||||
|
await source2.Task;
|
||||||
|
source2.TrySetResult().Should().BeFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SetCancelWithValueAfterReturn()
|
||||||
|
{
|
||||||
|
var source = AutoResetUniTaskCompletionSource<int>.Create();
|
||||||
|
source.TrySetResult(100);
|
||||||
|
(await source.Task).Should().Be(100);
|
||||||
|
source.TrySetCanceled().Should().BeFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SetExceptionWithValueAfterReturn()
|
||||||
|
{
|
||||||
|
var source = AutoResetUniTaskCompletionSource<int>.Create();
|
||||||
|
source.TrySetResult(100);
|
||||||
|
(await source.Task).Should().Be(100);
|
||||||
|
source.TrySetException(new UniTaskTestException()).Should().BeFalse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,7 +22,9 @@ public static class EditorRunnerChecker
|
|||||||
|
|
||||||
//var r = await UnityWebRequest.Get("https://bing.com/").SendWebRequest().ToUniTask();
|
//var r = await UnityWebRequest.Get("https://bing.com/").SendWebRequest().ToUniTask();
|
||||||
//Debug.Log(r.downloadHandler.text.Substring(0, 100));
|
//Debug.Log(r.downloadHandler.text.Substring(0, 100));
|
||||||
await UniTask.Yield();
|
//await UniTask.Yield();
|
||||||
|
|
||||||
|
await UniTask.DelayFrame(30);
|
||||||
|
|
||||||
Debug.Log("End");
|
Debug.Log("End");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,12 @@
|
|||||||
"name": "com.unity.addressables",
|
"name": "com.unity.addressables",
|
||||||
"expression": "",
|
"expression": "",
|
||||||
"define": "UNITASK_ADDRESSABLE_SUPPORT"
|
"define": "UNITASK_ADDRESSABLE_SUPPORT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "com.unity.addressables.cn",
|
||||||
|
"expression": "",
|
||||||
|
"define": "UNITASK_ADDRESSABLE_SUPPORT"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"noEngineReferences": false
|
"noEngineReferences": false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,14 +16,14 @@ namespace Cysharp.Threading.Tasks
|
|||||||
Kill,
|
Kill,
|
||||||
KillWithCompleteCallback,
|
KillWithCompleteCallback,
|
||||||
Complete,
|
Complete,
|
||||||
CompleteWithSeqeunceCallback,
|
CompleteWithSequenceCallback,
|
||||||
CancelAwait,
|
CancelAwait,
|
||||||
|
|
||||||
// AndCancelAwait
|
// AndCancelAwait
|
||||||
KillAndCancelAwait,
|
KillAndCancelAwait,
|
||||||
KillWithCompleteCallbackAndCancelAwait,
|
KillWithCompleteCallbackAndCancelAwait,
|
||||||
CompleteAndCancelAwait,
|
CompleteAndCancelAwait,
|
||||||
CompleteWithSeqeunceCallbackAndCancelAwait
|
CompleteWithSequenceCallbackAndCancelAwait
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class DOTweenAsyncExtensions
|
public static class DOTweenAsyncExtensions
|
||||||
@@ -143,24 +143,21 @@ namespace Cysharp.Threading.Tasks
|
|||||||
TaskPool.RegisterSizeGetter(typeof(TweenConfiguredSource), () => pool.Size);
|
TaskPool.RegisterSizeGetter(typeof(TweenConfiguredSource), () => pool.Size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static readonly TweenCallback EmptyTweenCallback = () => { };
|
|
||||||
|
|
||||||
readonly TweenCallback onCompleteCallbackDelegate;
|
readonly TweenCallback onCompleteCallbackDelegate;
|
||||||
readonly TweenCallback onUpdateDelegate;
|
|
||||||
|
|
||||||
Tween tween;
|
Tween tween;
|
||||||
TweenCancelBehaviour cancelBehaviour;
|
TweenCancelBehaviour cancelBehaviour;
|
||||||
CancellationToken cancellationToken;
|
CancellationToken cancellationToken;
|
||||||
|
CancellationTokenRegistration cancellationRegistration;
|
||||||
CallbackType callbackType;
|
CallbackType callbackType;
|
||||||
bool canceled;
|
bool canceled;
|
||||||
|
|
||||||
TweenCallback originalUpdateAction;
|
TweenCallback originalCompleteAction;
|
||||||
UniTaskCompletionSourceCore<AsyncUnit> core;
|
UniTaskCompletionSourceCore<AsyncUnit> core;
|
||||||
|
|
||||||
TweenConfiguredSource()
|
TweenConfiguredSource()
|
||||||
{
|
{
|
||||||
onCompleteCallbackDelegate = OnCompleteCallbackDelegate;
|
onCompleteCallbackDelegate = OnCompleteCallbackDelegate;
|
||||||
onUpdateDelegate = OnUpdate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IUniTaskSource Create(Tween tween, TweenCancelBehaviour cancelBehaviour, CancellationToken cancellationToken, CallbackType callbackType, out short token)
|
public static IUniTaskSource Create(Tween tween, TweenCancelBehaviour cancelBehaviour, CancellationToken cancellationToken, CallbackType callbackType, out short token)
|
||||||
@@ -180,41 +177,87 @@ namespace Cysharp.Threading.Tasks
|
|||||||
result.cancelBehaviour = cancelBehaviour;
|
result.cancelBehaviour = cancelBehaviour;
|
||||||
result.cancellationToken = cancellationToken;
|
result.cancellationToken = cancellationToken;
|
||||||
result.callbackType = callbackType;
|
result.callbackType = callbackType;
|
||||||
|
|
||||||
result.originalUpdateAction = tween.onUpdate;
|
|
||||||
result.canceled = false;
|
result.canceled = false;
|
||||||
|
|
||||||
if (result.originalUpdateAction == result.onUpdateDelegate)
|
|
||||||
{
|
|
||||||
result.originalUpdateAction = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
tween.onUpdate = result.onUpdateDelegate;
|
|
||||||
|
|
||||||
switch (callbackType)
|
switch (callbackType)
|
||||||
{
|
{
|
||||||
case CallbackType.Kill:
|
case CallbackType.Kill:
|
||||||
|
result.originalCompleteAction = tween.onKill;
|
||||||
tween.onKill = result.onCompleteCallbackDelegate;
|
tween.onKill = result.onCompleteCallbackDelegate;
|
||||||
break;
|
break;
|
||||||
case CallbackType.Complete:
|
case CallbackType.Complete:
|
||||||
|
result.originalCompleteAction = tween.onComplete;
|
||||||
tween.onComplete = result.onCompleteCallbackDelegate;
|
tween.onComplete = result.onCompleteCallbackDelegate;
|
||||||
break;
|
break;
|
||||||
case CallbackType.Pause:
|
case CallbackType.Pause:
|
||||||
|
result.originalCompleteAction = tween.onPause;
|
||||||
tween.onPause = result.onCompleteCallbackDelegate;
|
tween.onPause = result.onCompleteCallbackDelegate;
|
||||||
break;
|
break;
|
||||||
case CallbackType.Play:
|
case CallbackType.Play:
|
||||||
|
result.originalCompleteAction = tween.onPlay;
|
||||||
tween.onPlay = result.onCompleteCallbackDelegate;
|
tween.onPlay = result.onCompleteCallbackDelegate;
|
||||||
break;
|
break;
|
||||||
case CallbackType.Rewind:
|
case CallbackType.Rewind:
|
||||||
|
result.originalCompleteAction = tween.onRewind;
|
||||||
tween.onRewind = result.onCompleteCallbackDelegate;
|
tween.onRewind = result.onCompleteCallbackDelegate;
|
||||||
break;
|
break;
|
||||||
case CallbackType.StepComplete:
|
case CallbackType.StepComplete:
|
||||||
|
result.originalCompleteAction = tween.onStepComplete;
|
||||||
tween.onStepComplete = result.onCompleteCallbackDelegate;
|
tween.onStepComplete = result.onCompleteCallbackDelegate;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (result.originalCompleteAction == result.onCompleteCallbackDelegate)
|
||||||
|
{
|
||||||
|
result.originalCompleteAction = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cancellationToken.CanBeCanceled)
|
||||||
|
{
|
||||||
|
result.cancellationRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(x =>
|
||||||
|
{
|
||||||
|
var source = (TweenConfiguredSource)x;
|
||||||
|
switch (source.cancelBehaviour)
|
||||||
|
{
|
||||||
|
case TweenCancelBehaviour.Kill:
|
||||||
|
default:
|
||||||
|
source.tween.Kill(false);
|
||||||
|
break;
|
||||||
|
case TweenCancelBehaviour.KillAndCancelAwait:
|
||||||
|
source.canceled = true;
|
||||||
|
source.tween.Kill(false);
|
||||||
|
break;
|
||||||
|
case TweenCancelBehaviour.KillWithCompleteCallback:
|
||||||
|
source.tween.Kill(true);
|
||||||
|
break;
|
||||||
|
case TweenCancelBehaviour.KillWithCompleteCallbackAndCancelAwait:
|
||||||
|
source.canceled = true;
|
||||||
|
source.tween.Kill(true);
|
||||||
|
break;
|
||||||
|
case TweenCancelBehaviour.Complete:
|
||||||
|
source.tween.Complete(false);
|
||||||
|
break;
|
||||||
|
case TweenCancelBehaviour.CompleteAndCancelAwait:
|
||||||
|
source.canceled = true;
|
||||||
|
source.tween.Complete(false);
|
||||||
|
break;
|
||||||
|
case TweenCancelBehaviour.CompleteWithSequenceCallback:
|
||||||
|
source.tween.Complete(true);
|
||||||
|
break;
|
||||||
|
case TweenCancelBehaviour.CompleteWithSequenceCallbackAndCancelAwait:
|
||||||
|
source.canceled = true;
|
||||||
|
source.tween.Complete(true);
|
||||||
|
break;
|
||||||
|
case TweenCancelBehaviour.CancelAwait:
|
||||||
|
source.RestoreOriginalCallback();
|
||||||
|
source.core.TrySetCanceled(source.cancellationToken);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}, result);
|
||||||
|
}
|
||||||
|
|
||||||
TaskTracker.TrackActiveTask(result, 3);
|
TaskTracker.TrackActiveTask(result, 3);
|
||||||
|
|
||||||
token = result.core.Version;
|
token = result.core.Version;
|
||||||
@@ -228,7 +271,7 @@ namespace Cysharp.Threading.Tasks
|
|||||||
if (this.cancelBehaviour == TweenCancelBehaviour.KillAndCancelAwait
|
if (this.cancelBehaviour == TweenCancelBehaviour.KillAndCancelAwait
|
||||||
|| this.cancelBehaviour == TweenCancelBehaviour.KillWithCompleteCallbackAndCancelAwait
|
|| this.cancelBehaviour == TweenCancelBehaviour.KillWithCompleteCallbackAndCancelAwait
|
||||||
|| this.cancelBehaviour == TweenCancelBehaviour.CompleteAndCancelAwait
|
|| this.cancelBehaviour == TweenCancelBehaviour.CompleteAndCancelAwait
|
||||||
|| this.cancelBehaviour == TweenCancelBehaviour.CompleteWithSeqeunceCallbackAndCancelAwait
|
|| this.cancelBehaviour == TweenCancelBehaviour.CompleteWithSequenceCallbackAndCancelAwait
|
||||||
|| this.cancelBehaviour == TweenCancelBehaviour.CancelAwait)
|
|| this.cancelBehaviour == TweenCancelBehaviour.CancelAwait)
|
||||||
{
|
{
|
||||||
canceled = true;
|
canceled = true;
|
||||||
@@ -240,81 +283,11 @@ namespace Cysharp.Threading.Tasks
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
originalCompleteAction?.Invoke();
|
||||||
core.TrySetResult(AsyncUnit.Default);
|
core.TrySetResult(AsyncUnit.Default);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnUpdate()
|
|
||||||
{
|
|
||||||
originalUpdateAction?.Invoke();
|
|
||||||
|
|
||||||
if (!cancellationToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (this.cancelBehaviour)
|
|
||||||
{
|
|
||||||
case TweenCancelBehaviour.Kill:
|
|
||||||
default:
|
|
||||||
this.tween.Kill(false);
|
|
||||||
break;
|
|
||||||
case TweenCancelBehaviour.KillAndCancelAwait:
|
|
||||||
this.canceled = true;
|
|
||||||
this.tween.Kill(false);
|
|
||||||
break;
|
|
||||||
case TweenCancelBehaviour.KillWithCompleteCallback:
|
|
||||||
this.tween.Kill(true);
|
|
||||||
break;
|
|
||||||
case TweenCancelBehaviour.KillWithCompleteCallbackAndCancelAwait:
|
|
||||||
this.canceled = true;
|
|
||||||
this.tween.Kill(true);
|
|
||||||
break;
|
|
||||||
case TweenCancelBehaviour.Complete:
|
|
||||||
this.tween.Complete(false);
|
|
||||||
break;
|
|
||||||
case TweenCancelBehaviour.CompleteAndCancelAwait:
|
|
||||||
this.canceled = true;
|
|
||||||
this.tween.Complete(false);
|
|
||||||
break;
|
|
||||||
case TweenCancelBehaviour.CompleteWithSeqeunceCallback:
|
|
||||||
this.tween.Complete(true);
|
|
||||||
break;
|
|
||||||
case TweenCancelBehaviour.CompleteWithSeqeunceCallbackAndCancelAwait:
|
|
||||||
this.canceled = true;
|
|
||||||
this.tween.Complete(true);
|
|
||||||
break;
|
|
||||||
case TweenCancelBehaviour.CancelAwait:
|
|
||||||
// replace to empty(avoid callback after Canceled(instance is returned to pool.)
|
|
||||||
switch (callbackType)
|
|
||||||
{
|
|
||||||
case CallbackType.Kill:
|
|
||||||
tween.onKill = EmptyTweenCallback;
|
|
||||||
break;
|
|
||||||
case CallbackType.Complete:
|
|
||||||
tween.onComplete = EmptyTweenCallback;
|
|
||||||
break;
|
|
||||||
case CallbackType.Pause:
|
|
||||||
tween.onPause = EmptyTweenCallback;
|
|
||||||
break;
|
|
||||||
case CallbackType.Play:
|
|
||||||
tween.onPlay = EmptyTweenCallback;
|
|
||||||
break;
|
|
||||||
case CallbackType.Rewind:
|
|
||||||
tween.onRewind = EmptyTweenCallback;
|
|
||||||
break;
|
|
||||||
case CallbackType.StepComplete:
|
|
||||||
tween.onStepComplete = EmptyTweenCallback;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.core.TrySetCanceled(this.cancellationToken);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void DoCancelBeforeCreate(Tween tween, TweenCancelBehaviour tweenCancelBehaviour)
|
static void DoCancelBeforeCreate(Tween tween, TweenCancelBehaviour tweenCancelBehaviour)
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -339,10 +312,10 @@ namespace Cysharp.Threading.Tasks
|
|||||||
case TweenCancelBehaviour.CompleteAndCancelAwait:
|
case TweenCancelBehaviour.CompleteAndCancelAwait:
|
||||||
tween.Complete(false);
|
tween.Complete(false);
|
||||||
break;
|
break;
|
||||||
case TweenCancelBehaviour.CompleteWithSeqeunceCallback:
|
case TweenCancelBehaviour.CompleteWithSequenceCallback:
|
||||||
tween.Complete(true);
|
tween.Complete(true);
|
||||||
break;
|
break;
|
||||||
case TweenCancelBehaviour.CompleteWithSeqeunceCallbackAndCancelAwait:
|
case TweenCancelBehaviour.CompleteWithSequenceCallbackAndCancelAwait:
|
||||||
tween.Complete(true);
|
tween.Complete(true);
|
||||||
break;
|
break;
|
||||||
case TweenCancelBehaviour.CancelAwait:
|
case TweenCancelBehaviour.CancelAwait:
|
||||||
@@ -381,36 +354,41 @@ namespace Cysharp.Threading.Tasks
|
|||||||
{
|
{
|
||||||
TaskTracker.RemoveTracking(this);
|
TaskTracker.RemoveTracking(this);
|
||||||
core.Reset();
|
core.Reset();
|
||||||
tween.onUpdate = originalUpdateAction;
|
cancellationRegistration.Dispose();
|
||||||
|
|
||||||
|
RestoreOriginalCallback();
|
||||||
|
|
||||||
|
tween = default;
|
||||||
|
cancellationToken = default;
|
||||||
|
originalCompleteAction = default;
|
||||||
|
return pool.TryPush(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RestoreOriginalCallback()
|
||||||
|
{
|
||||||
switch (callbackType)
|
switch (callbackType)
|
||||||
{
|
{
|
||||||
case CallbackType.Kill:
|
case CallbackType.Kill:
|
||||||
tween.onKill = null;
|
tween.onKill = originalCompleteAction;
|
||||||
break;
|
break;
|
||||||
case CallbackType.Complete:
|
case CallbackType.Complete:
|
||||||
tween.onComplete = null;
|
tween.onComplete = originalCompleteAction;
|
||||||
break;
|
break;
|
||||||
case CallbackType.Pause:
|
case CallbackType.Pause:
|
||||||
tween.onPause = null;
|
tween.onPause = originalCompleteAction;
|
||||||
break;
|
break;
|
||||||
case CallbackType.Play:
|
case CallbackType.Play:
|
||||||
tween.onPlay = null;
|
tween.onPlay = originalCompleteAction;
|
||||||
break;
|
break;
|
||||||
case CallbackType.Rewind:
|
case CallbackType.Rewind:
|
||||||
tween.onRewind = null;
|
tween.onRewind = originalCompleteAction;
|
||||||
break;
|
break;
|
||||||
case CallbackType.StepComplete:
|
case CallbackType.StepComplete:
|
||||||
tween.onStepComplete = null;
|
tween.onStepComplete = originalCompleteAction;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
tween = default;
|
|
||||||
cancellationToken = default;
|
|
||||||
originalUpdateAction = default;
|
|
||||||
return pool.TryPush(this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ namespace Cysharp.Threading.Tasks.Internal
|
|||||||
|
|
||||||
foreach (var candidateMethod in methods)
|
foreach (var candidateMethod in methods)
|
||||||
{
|
{
|
||||||
var attributes = candidateMethod.GetCustomAttributes<StateMachineAttribute>();
|
var attributes = candidateMethod.GetCustomAttributes<StateMachineAttribute>(false);
|
||||||
if (attributes == null)
|
if (attributes == null)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ namespace Cysharp.Threading.Tasks.Internal
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
public static void ThrowArgumentException<T>(string message)
|
public static void ThrowArgumentException(string message)
|
||||||
{
|
{
|
||||||
throw new ArgumentException(message);
|
throw new ArgumentException(message);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ namespace Cysharp.Threading.Tasks.Linq
|
|||||||
public UniTask DisposeAsync()
|
public UniTask DisposeAsync()
|
||||||
{
|
{
|
||||||
TaskTracker.RemoveTracking(this);
|
TaskTracker.RemoveTracking(this);
|
||||||
|
writer.Dispose();
|
||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,7 +128,7 @@ namespace Cysharp.Threading.Tasks.Linq
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class AsyncWriter : IUniTaskSource, IAsyncWriter<T>
|
sealed class AsyncWriter : IUniTaskSource, IAsyncWriter<T>, IDisposable
|
||||||
{
|
{
|
||||||
readonly _Create enumerator;
|
readonly _Create enumerator;
|
||||||
|
|
||||||
@@ -137,6 +138,15 @@ namespace Cysharp.Threading.Tasks.Linq
|
|||||||
{
|
{
|
||||||
this.enumerator = enumerator;
|
this.enumerator = enumerator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
var status = core.GetStatus(core.Version);
|
||||||
|
if (status == UniTaskStatus.Pending)
|
||||||
|
{
|
||||||
|
core.TrySetCanceled();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void GetResult(short token)
|
public void GetResult(short token)
|
||||||
{
|
{
|
||||||
|
|||||||
232
src/UniTask/Assets/Plugins/UniTask/Runtime/Linq/Merge.cs
Normal file
232
src/UniTask/Assets/Plugins/UniTask/Runtime/Linq/Merge.cs
Normal file
@@ -0,0 +1,232 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using Cysharp.Threading.Tasks.Internal;
|
||||||
|
|
||||||
|
namespace Cysharp.Threading.Tasks.Linq
|
||||||
|
{
|
||||||
|
public static partial class UniTaskAsyncEnumerable
|
||||||
|
{
|
||||||
|
public static IUniTaskAsyncEnumerable<T> Merge<T>(this IUniTaskAsyncEnumerable<T> first, IUniTaskAsyncEnumerable<T> second)
|
||||||
|
{
|
||||||
|
Error.ThrowArgumentNullException(first, nameof(first));
|
||||||
|
Error.ThrowArgumentNullException(second, nameof(second));
|
||||||
|
|
||||||
|
return new Merge<T>(new [] { first, second });
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IUniTaskAsyncEnumerable<T> Merge<T>(this IUniTaskAsyncEnumerable<T> first, IUniTaskAsyncEnumerable<T> second, IUniTaskAsyncEnumerable<T> third)
|
||||||
|
{
|
||||||
|
Error.ThrowArgumentNullException(first, nameof(first));
|
||||||
|
Error.ThrowArgumentNullException(second, nameof(second));
|
||||||
|
Error.ThrowArgumentNullException(third, nameof(third));
|
||||||
|
|
||||||
|
return new Merge<T>(new[] { first, second, third });
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IUniTaskAsyncEnumerable<T> Merge<T>(this IEnumerable<IUniTaskAsyncEnumerable<T>> sources)
|
||||||
|
{
|
||||||
|
return new Merge<T>(sources.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IUniTaskAsyncEnumerable<T> Merge<T>(params IUniTaskAsyncEnumerable<T>[] sources)
|
||||||
|
{
|
||||||
|
return new Merge<T>(sources);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class Merge<T> : IUniTaskAsyncEnumerable<T>
|
||||||
|
{
|
||||||
|
readonly IUniTaskAsyncEnumerable<T>[] sources;
|
||||||
|
|
||||||
|
public Merge(IUniTaskAsyncEnumerable<T>[] sources)
|
||||||
|
{
|
||||||
|
if (sources.Length <= 0)
|
||||||
|
{
|
||||||
|
Error.ThrowArgumentException("No source async enumerable to merge");
|
||||||
|
}
|
||||||
|
this.sources = sources;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
||||||
|
=> new _Merge(sources, cancellationToken);
|
||||||
|
|
||||||
|
enum MergeSourceState
|
||||||
|
{
|
||||||
|
Pending,
|
||||||
|
Running,
|
||||||
|
Completed,
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class _Merge : MoveNextSource, IUniTaskAsyncEnumerator<T>
|
||||||
|
{
|
||||||
|
static readonly Action<object> GetResultAtAction = GetResultAt;
|
||||||
|
|
||||||
|
readonly int length;
|
||||||
|
readonly IUniTaskAsyncEnumerator<T>[] enumerators;
|
||||||
|
readonly MergeSourceState[] states;
|
||||||
|
readonly Queue<(T, Exception, bool)> queuedResult = new Queue<(T, Exception, bool)>();
|
||||||
|
readonly CancellationToken cancellationToken;
|
||||||
|
|
||||||
|
int moveNextCompleted;
|
||||||
|
|
||||||
|
public T Current { get; private set; }
|
||||||
|
|
||||||
|
public _Merge(IUniTaskAsyncEnumerable<T>[] sources, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
this.cancellationToken = cancellationToken;
|
||||||
|
length = sources.Length;
|
||||||
|
states = ArrayPool<MergeSourceState>.Shared.Rent(length);
|
||||||
|
enumerators = ArrayPool<IUniTaskAsyncEnumerator<T>>.Shared.Rent(length);
|
||||||
|
for (var i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
enumerators[i] = sources[i].GetAsyncEnumerator(cancellationToken);
|
||||||
|
states[i] = (int)MergeSourceState.Pending;;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public UniTask<bool> MoveNextAsync()
|
||||||
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
completionSource.Reset();
|
||||||
|
Interlocked.Exchange(ref moveNextCompleted, 0);
|
||||||
|
|
||||||
|
if (HasQueuedResult() && Interlocked.CompareExchange(ref moveNextCompleted, 1, 0) == 0)
|
||||||
|
{
|
||||||
|
(T, Exception, bool) value;
|
||||||
|
lock (states)
|
||||||
|
{
|
||||||
|
value = queuedResult.Dequeue();
|
||||||
|
}
|
||||||
|
var resultValue = value.Item1;
|
||||||
|
var exception = value.Item2;
|
||||||
|
var hasNext = value.Item3;
|
||||||
|
if (exception != null)
|
||||||
|
{
|
||||||
|
completionSource.TrySetException(exception);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Current = resultValue;
|
||||||
|
completionSource.TrySetResult(hasNext);
|
||||||
|
}
|
||||||
|
return new UniTask<bool>(this, completionSource.Version);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
lock (states)
|
||||||
|
{
|
||||||
|
if (states[i] == MergeSourceState.Pending)
|
||||||
|
{
|
||||||
|
states[i] = MergeSourceState.Running;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var awaiter = enumerators[i].MoveNextAsync().GetAwaiter();
|
||||||
|
if (awaiter.IsCompleted)
|
||||||
|
{
|
||||||
|
GetResultAt(i, awaiter);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
awaiter.SourceOnCompleted(GetResultAtAction, StateTuple.Create(this, i, awaiter));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new UniTask<bool>(this, completionSource.Version);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async UniTask DisposeAsync()
|
||||||
|
{
|
||||||
|
for (var i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
await enumerators[i].DisposeAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayPool<MergeSourceState>.Shared.Return(states, true);
|
||||||
|
ArrayPool<IUniTaskAsyncEnumerator<T>>.Shared.Return(enumerators, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GetResultAt(object state)
|
||||||
|
{
|
||||||
|
using (var tuple = (StateTuple<_Merge, int, UniTask<bool>.Awaiter>)state)
|
||||||
|
{
|
||||||
|
tuple.Item1.GetResultAt(tuple.Item2, tuple.Item3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetResultAt(int index, UniTask<bool>.Awaiter awaiter)
|
||||||
|
{
|
||||||
|
bool hasNext;
|
||||||
|
bool completedAll;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
hasNext = awaiter.GetResult();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
if (Interlocked.CompareExchange(ref moveNextCompleted, 1, 0) == 0)
|
||||||
|
{
|
||||||
|
completionSource.TrySetException(ex);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lock (states)
|
||||||
|
{
|
||||||
|
queuedResult.Enqueue((default, ex, default));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (states)
|
||||||
|
{
|
||||||
|
states[index] = hasNext ? MergeSourceState.Pending : MergeSourceState.Completed;
|
||||||
|
completedAll = !hasNext && IsCompletedAll();
|
||||||
|
}
|
||||||
|
if (hasNext || completedAll)
|
||||||
|
{
|
||||||
|
if (Interlocked.CompareExchange(ref moveNextCompleted, 1, 0) == 0)
|
||||||
|
{
|
||||||
|
Current = enumerators[index].Current;
|
||||||
|
completionSource.TrySetResult(!completedAll);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lock (states)
|
||||||
|
{
|
||||||
|
queuedResult.Enqueue((enumerators[index].Current, null, !completedAll));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HasQueuedResult()
|
||||||
|
{
|
||||||
|
lock (states)
|
||||||
|
{
|
||||||
|
return queuedResult.Count > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsCompletedAll()
|
||||||
|
{
|
||||||
|
lock (states)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
if (states[i] != MergeSourceState.Completed)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ca56812f160c45d0bacb4339819edf1a
|
||||||
|
timeCreated: 1694133666
|
||||||
@@ -285,7 +285,11 @@ namespace Cysharp.Threading.Tasks
|
|||||||
return dest.ToArray();
|
return dest.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
|
#if UNITY_2020_1_OR_NEWER
|
||||||
|
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]
|
||||||
|
#else
|
||||||
|
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
|
||||||
|
#endif
|
||||||
static void Init()
|
static void Init()
|
||||||
{
|
{
|
||||||
// capture default(unity) sync-context.
|
// capture default(unity) sync-context.
|
||||||
@@ -524,6 +528,14 @@ namespace Cysharp.Threading.Tasks
|
|||||||
{
|
{
|
||||||
sb.AppendFormat("------{0}------", header.type.Name);
|
sb.AppendFormat("------{0}------", header.type.Name);
|
||||||
sb.AppendLine();
|
sb.AppendLine();
|
||||||
|
|
||||||
|
if (header.subSystemList is null)
|
||||||
|
{
|
||||||
|
sb.AppendFormat("{0} has no subsystems!", header.ToString());
|
||||||
|
sb.AppendLine();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var subSystem in header.subSystemList)
|
foreach (var subSystem in header.subSystemList)
|
||||||
{
|
{
|
||||||
sb.AppendFormat("{0}", subSystem.type.Name);
|
sb.AppendFormat("{0}", subSystem.type.Name);
|
||||||
@@ -545,6 +557,11 @@ namespace Cysharp.Threading.Tasks
|
|||||||
|
|
||||||
foreach (var header in playerLoop.subSystemList)
|
foreach (var header in playerLoop.subSystemList)
|
||||||
{
|
{
|
||||||
|
if (header.subSystemList is null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var subSystem in header.subSystemList)
|
foreach (var subSystem in header.subSystemList)
|
||||||
{
|
{
|
||||||
if (subSystem.type == typeof(UniTaskLoopRunners.UniTaskLoopRunnerInitialization))
|
if (subSystem.type == typeof(UniTaskLoopRunners.UniTaskLoopRunnerInitialization))
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ using System.Threading;
|
|||||||
|
|
||||||
namespace Cysharp.Threading.Tasks
|
namespace Cysharp.Threading.Tasks
|
||||||
{
|
{
|
||||||
// internaly used but public, allow to user create custom operator with pooling.
|
// internally used but public, allow to user create custom operator with pooling.
|
||||||
|
|
||||||
public static class TaskPool
|
public static class TaskPool
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ namespace Cysharp.Threading.Tasks
|
|||||||
|
|
||||||
public void Reset()
|
public void Reset()
|
||||||
{
|
{
|
||||||
timer.Stop();
|
timer?.Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
@@ -109,7 +109,7 @@ namespace Cysharp.Threading.Tasks
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
// stop timer.
|
// stop timer.
|
||||||
timer.Dispose();
|
timer?.Dispose();
|
||||||
|
|
||||||
// cancel and dispose.
|
// cancel and dispose.
|
||||||
timeoutSource.Cancel();
|
timeoutSource.Cancel();
|
||||||
|
|||||||
@@ -20,8 +20,6 @@ namespace Cysharp.Threading.Tasks
|
|||||||
{
|
{
|
||||||
ITriggerHandler<T> head; // head.prev is last
|
ITriggerHandler<T> head; // head.prev is last
|
||||||
ITriggerHandler<T> iteratingHead;
|
ITriggerHandler<T> iteratingHead;
|
||||||
|
|
||||||
bool preserveRemoveSelf;
|
|
||||||
ITriggerHandler<T> iteratingNode;
|
ITriggerHandler<T> iteratingNode;
|
||||||
|
|
||||||
void LogError(Exception ex)
|
void LogError(Exception ex)
|
||||||
@@ -55,18 +53,9 @@ namespace Cysharp.Threading.Tasks
|
|||||||
Remove(h);
|
Remove(h);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (preserveRemoveSelf)
|
// If `h` itself is removed by OnNext, h.Next is null.
|
||||||
{
|
// Therefore, instead of looking at h.Next, the `iteratingNode` reference itself is replaced.
|
||||||
preserveRemoveSelf = false;
|
h = h == iteratingNode ? h.Next : iteratingNode;
|
||||||
iteratingNode = null;
|
|
||||||
var next = h.Next;
|
|
||||||
Remove(h);
|
|
||||||
h = next;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
h = h.Next;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
iteratingNode = null;
|
iteratingNode = null;
|
||||||
@@ -97,9 +86,8 @@ namespace Cysharp.Threading.Tasks
|
|||||||
LogError(ex);
|
LogError(ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
preserveRemoveSelf = false;
|
var next = h == iteratingNode ? h.Next : iteratingNode;
|
||||||
iteratingNode = null;
|
iteratingNode = null;
|
||||||
var next = h.Next;
|
|
||||||
Remove(h);
|
Remove(h);
|
||||||
h = next;
|
h = next;
|
||||||
}
|
}
|
||||||
@@ -132,9 +120,8 @@ namespace Cysharp.Threading.Tasks
|
|||||||
LogError(ex);
|
LogError(ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
preserveRemoveSelf = false;
|
var next = h == iteratingNode ? h.Next : iteratingNode;
|
||||||
iteratingNode = null;
|
iteratingNode = null;
|
||||||
var next = h.Next;
|
|
||||||
Remove(h);
|
Remove(h);
|
||||||
h = next;
|
h = next;
|
||||||
}
|
}
|
||||||
@@ -167,9 +154,8 @@ namespace Cysharp.Threading.Tasks
|
|||||||
LogError(ex);
|
LogError(ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
preserveRemoveSelf = false;
|
var next = h == iteratingNode ? h.Next : iteratingNode;
|
||||||
iteratingNode = null;
|
iteratingNode = null;
|
||||||
var next = h.Next;
|
|
||||||
Remove(h);
|
Remove(h);
|
||||||
h = next;
|
h = next;
|
||||||
}
|
}
|
||||||
@@ -241,71 +227,65 @@ namespace Cysharp.Threading.Tasks
|
|||||||
{
|
{
|
||||||
if (handler == null) throw new ArgumentNullException(nameof(handler));
|
if (handler == null) throw new ArgumentNullException(nameof(handler));
|
||||||
|
|
||||||
if (iteratingNode != null && iteratingNode == handler)
|
var prev = handler.Prev;
|
||||||
|
var next = handler.Next;
|
||||||
|
|
||||||
|
if (next != null)
|
||||||
{
|
{
|
||||||
// if remove self, reserve remove self after invoke completed.
|
next.Prev = prev;
|
||||||
preserveRemoveSelf = true;
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (handler == head)
|
||||||
{
|
{
|
||||||
var prev = handler.Prev;
|
head = next;
|
||||||
var next = handler.Next;
|
|
||||||
|
|
||||||
if (next != null)
|
|
||||||
{
|
|
||||||
next.Prev = prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (handler == head)
|
|
||||||
{
|
|
||||||
head = next;
|
|
||||||
}
|
|
||||||
else if (handler == iteratingHead)
|
|
||||||
{
|
|
||||||
iteratingHead = next;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// when handler is head, prev indicate last so don't use it.
|
|
||||||
if (prev != null)
|
|
||||||
{
|
|
||||||
prev.Next = next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (head != null)
|
|
||||||
{
|
|
||||||
if (head.Prev == handler)
|
|
||||||
{
|
|
||||||
if (prev != head)
|
|
||||||
{
|
|
||||||
head.Prev = prev;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
head.Prev = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (iteratingHead != null)
|
|
||||||
{
|
|
||||||
if (iteratingHead.Prev == handler)
|
|
||||||
{
|
|
||||||
if (prev != iteratingHead.Prev)
|
|
||||||
{
|
|
||||||
iteratingHead.Prev = prev;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
iteratingHead.Prev = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handler.Prev = null;
|
|
||||||
handler.Next = null;
|
|
||||||
}
|
}
|
||||||
|
// when handler is head, prev indicate last so don't use it.
|
||||||
|
else if (prev != null)
|
||||||
|
{
|
||||||
|
prev.Next = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handler == iteratingNode)
|
||||||
|
{
|
||||||
|
iteratingNode = next;
|
||||||
|
}
|
||||||
|
if (handler == iteratingHead)
|
||||||
|
{
|
||||||
|
iteratingHead = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (head != null)
|
||||||
|
{
|
||||||
|
if (head.Prev == handler)
|
||||||
|
{
|
||||||
|
if (prev != head)
|
||||||
|
{
|
||||||
|
head.Prev = prev;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
head.Prev = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iteratingHead != null)
|
||||||
|
{
|
||||||
|
if (iteratingHead.Prev == handler)
|
||||||
|
{
|
||||||
|
if (prev != iteratingHead.Prev)
|
||||||
|
{
|
||||||
|
iteratingHead.Prev = prev;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
iteratingHead.Prev = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handler.Prev = null;
|
||||||
|
handler.Next = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,13 +32,11 @@ namespace Cysharp.Threading.Tasks.Triggers
|
|||||||
if (cancellationTokenSource == null)
|
if (cancellationTokenSource == null)
|
||||||
{
|
{
|
||||||
cancellationTokenSource = new CancellationTokenSource();
|
cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
if (!awakeCalled)
|
||||||
|
{
|
||||||
|
PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, new AwakeMonitor(this));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!awakeCalled)
|
|
||||||
{
|
|
||||||
PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, new AwakeMonitor(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
return cancellationTokenSource.Token;
|
return cancellationTokenSource.Token;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -83,7 +81,7 @@ namespace Cysharp.Threading.Tasks.Triggers
|
|||||||
|
|
||||||
public bool MoveNext()
|
public bool MoveNext()
|
||||||
{
|
{
|
||||||
if (trigger.called) return false;
|
if (trigger.called || trigger.awakeCalled) return false;
|
||||||
if (trigger == null)
|
if (trigger == null)
|
||||||
{
|
{
|
||||||
trigger.OnDestroy();
|
trigger.OnDestroy();
|
||||||
|
|||||||
@@ -8,6 +8,16 @@ namespace Cysharp.Threading.Tasks
|
|||||||
{
|
{
|
||||||
public static class UniTaskCancellationExtensions
|
public static class UniTaskCancellationExtensions
|
||||||
{
|
{
|
||||||
|
#if UNITY_2022_2_OR_NEWER
|
||||||
|
|
||||||
|
/// <summary>This CancellationToken is canceled when the MonoBehaviour will be destroyed.</summary>
|
||||||
|
public static CancellationToken GetCancellationTokenOnDestroy(this MonoBehaviour monoBehaviour)
|
||||||
|
{
|
||||||
|
return monoBehaviour.destroyCancellationToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>This CancellationToken is canceled when the MonoBehaviour will be destroyed.</summary>
|
/// <summary>This CancellationToken is canceled when the MonoBehaviour will be destroyed.</summary>
|
||||||
public static CancellationToken GetCancellationTokenOnDestroy(this GameObject gameObject)
|
public static CancellationToken GetCancellationTokenOnDestroy(this GameObject gameObject)
|
||||||
{
|
{
|
||||||
@@ -17,6 +27,13 @@ namespace Cysharp.Threading.Tasks
|
|||||||
/// <summary>This CancellationToken is canceled when the MonoBehaviour will be destroyed.</summary>
|
/// <summary>This CancellationToken is canceled when the MonoBehaviour will be destroyed.</summary>
|
||||||
public static CancellationToken GetCancellationTokenOnDestroy(this Component component)
|
public static CancellationToken GetCancellationTokenOnDestroy(this Component component)
|
||||||
{
|
{
|
||||||
|
#if UNITY_2022_2_OR_NEWER
|
||||||
|
if (component is MonoBehaviour mb)
|
||||||
|
{
|
||||||
|
return mb.destroyCancellationToken;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
return component.GetAsyncDestroyTrigger().CancellationToken;
|
return component.GetAsyncDestroyTrigger().CancellationToken;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
using Cysharp.Threading.Tasks.Internal;
|
using Cysharp.Threading.Tasks.Internal;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
@@ -74,40 +75,60 @@ namespace Cysharp.Threading.Tasks
|
|||||||
return new UniTask(NextFramePromise.Create(timing, cancellationToken, out var token), token);
|
return new UniTask(NextFramePromise.Create(timing, cancellationToken, out var token), token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if UNITY_2023_1_OR_NEWER
|
||||||
/// <summary>
|
public static async UniTask WaitForEndOfFrame(CancellationToken cancellationToken = default)
|
||||||
/// Same as UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate).
|
{
|
||||||
/// </summary>
|
await Awaitable.EndOfFrameAsync(cancellationToken);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
[Obsolete("Use WaitForEndOfFrame(MonoBehaviour) instead or UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate). Equivalent for coroutine's WaitForEndOfFrame requires MonoBehaviour(runner of Coroutine).")]
|
||||||
public static YieldAwaitable WaitForEndOfFrame()
|
public static YieldAwaitable WaitForEndOfFrame()
|
||||||
{
|
{
|
||||||
return UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate);
|
return UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
[Obsolete("Use WaitForEndOfFrame(MonoBehaviour) instead or UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate). Equivalent for coroutine's WaitForEndOfFrame requires MonoBehaviour(runner of Coroutine).")]
|
||||||
/// Same as UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate, cancellationToken).
|
|
||||||
/// </summary>
|
|
||||||
public static UniTask WaitForEndOfFrame(CancellationToken cancellationToken)
|
public static UniTask WaitForEndOfFrame(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate, cancellationToken);
|
return UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate, cancellationToken);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public static UniTask WaitForEndOfFrame(MonoBehaviour coroutineRunner, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
var source = WaitForEndOfFramePromise.Create(coroutineRunner, cancellationToken, out var token);
|
||||||
|
return new UniTask(source, token);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Same as UniTask.Yield(PlayerLoopTiming.FixedUpdate).
|
/// Same as UniTask.Yield(PlayerLoopTiming.LastFixedUpdate).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static YieldAwaitable WaitForFixedUpdate()
|
public static YieldAwaitable WaitForFixedUpdate()
|
||||||
{
|
{
|
||||||
return UniTask.Yield(PlayerLoopTiming.FixedUpdate);
|
// use LastFixedUpdate instead of FixedUpdate
|
||||||
|
// https://github.com/Cysharp/UniTask/issues/377
|
||||||
|
return UniTask.Yield(PlayerLoopTiming.LastFixedUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Same as UniTask.Yield(PlayerLoopTiming.FixedUpdate, cancellationToken).
|
/// Same as UniTask.Yield(PlayerLoopTiming.LastFixedUpdate, cancellationToken).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static UniTask WaitForFixedUpdate(CancellationToken cancellationToken)
|
public static UniTask WaitForFixedUpdate(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return UniTask.Yield(PlayerLoopTiming.FixedUpdate, cancellationToken);
|
return UniTask.Yield(PlayerLoopTiming.LastFixedUpdate, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static UniTask DelayFrame(int delayFrameCount, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
|
public static UniTask WaitForSeconds(float duration, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
|
||||||
|
{
|
||||||
|
return Delay(Mathf.RoundToInt(1000 * duration), ignoreTimeScale, delayTiming, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UniTask WaitForSeconds(int duration, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
|
||||||
|
{
|
||||||
|
return Delay(1000 * duration, ignoreTimeScale, delayTiming, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UniTask DelayFrame(int delayFrameCount, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
|
||||||
{
|
{
|
||||||
if (delayFrameCount < 0)
|
if (delayFrameCount < 0)
|
||||||
{
|
{
|
||||||
@@ -352,6 +373,113 @@ namespace Cysharp.Threading.Tasks
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sealed class WaitForEndOfFramePromise : IUniTaskSource, ITaskPoolNode<WaitForEndOfFramePromise>, System.Collections.IEnumerator
|
||||||
|
{
|
||||||
|
static TaskPool<WaitForEndOfFramePromise> pool;
|
||||||
|
WaitForEndOfFramePromise nextNode;
|
||||||
|
public ref WaitForEndOfFramePromise NextNode => ref nextNode;
|
||||||
|
|
||||||
|
static WaitForEndOfFramePromise()
|
||||||
|
{
|
||||||
|
TaskPool.RegisterSizeGetter(typeof(WaitForEndOfFramePromise), () => pool.Size);
|
||||||
|
}
|
||||||
|
|
||||||
|
CancellationToken cancellationToken;
|
||||||
|
UniTaskCompletionSourceCore<object> core;
|
||||||
|
|
||||||
|
WaitForEndOfFramePromise()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IUniTaskSource Create(MonoBehaviour coroutineRunner, CancellationToken cancellationToken, out short token)
|
||||||
|
{
|
||||||
|
if (cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pool.TryPop(out var result))
|
||||||
|
{
|
||||||
|
result = new WaitForEndOfFramePromise();
|
||||||
|
}
|
||||||
|
|
||||||
|
result.cancellationToken = cancellationToken;
|
||||||
|
|
||||||
|
TaskTracker.TrackActiveTask(result, 3);
|
||||||
|
|
||||||
|
coroutineRunner.StartCoroutine(result);
|
||||||
|
|
||||||
|
token = result.core.Version;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetResult(short token)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
core.GetResult(token);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
TryReturn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public UniTaskStatus GetStatus(short token)
|
||||||
|
{
|
||||||
|
return core.GetStatus(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UniTaskStatus UnsafeGetStatus()
|
||||||
|
{
|
||||||
|
return core.UnsafeGetStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||||
|
{
|
||||||
|
core.OnCompleted(continuation, state, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TryReturn()
|
||||||
|
{
|
||||||
|
TaskTracker.RemoveTracking(this);
|
||||||
|
core.Reset();
|
||||||
|
Reset(); // Reset Enumerator
|
||||||
|
cancellationToken = default;
|
||||||
|
return pool.TryPush(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Coroutine Runner implementation
|
||||||
|
|
||||||
|
static readonly WaitForEndOfFrame waitForEndOfFrameYieldInstruction = new WaitForEndOfFrame();
|
||||||
|
bool isFirst = true;
|
||||||
|
|
||||||
|
object IEnumerator.Current => waitForEndOfFrameYieldInstruction;
|
||||||
|
|
||||||
|
bool IEnumerator.MoveNext()
|
||||||
|
{
|
||||||
|
if (isFirst)
|
||||||
|
{
|
||||||
|
isFirst = false;
|
||||||
|
return true; // start WaitForEndOfFrame
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
core.TrySetCanceled(cancellationToken);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
core.TrySetResult(null);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
isFirst = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sealed class DelayFramePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<DelayFramePromise>
|
sealed class DelayFramePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<DelayFramePromise>
|
||||||
{
|
{
|
||||||
static TaskPool<DelayFramePromise> pool;
|
static TaskPool<DelayFramePromise> pool;
|
||||||
@@ -444,7 +572,19 @@ namespace Cysharp.Threading.Tasks
|
|||||||
// skip in initial frame.
|
// skip in initial frame.
|
||||||
if (initialFrame == Time.frameCount)
|
if (initialFrame == Time.frameCount)
|
||||||
{
|
{
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
// force use Realtime.
|
||||||
|
if (PlayerLoopHelper.IsMainThread && !UnityEditor.EditorApplication.isPlaying)
|
||||||
|
{
|
||||||
|
//goto ++currentFrameCount
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#else
|
||||||
return true;
|
return true;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -194,6 +194,7 @@ namespace Cysharp.Threading.Tasks
|
|||||||
sealed class ExceptionResultSource : IUniTaskSource
|
sealed class ExceptionResultSource : IUniTaskSource
|
||||||
{
|
{
|
||||||
readonly ExceptionDispatchInfo exception;
|
readonly ExceptionDispatchInfo exception;
|
||||||
|
bool calledGet;
|
||||||
|
|
||||||
public ExceptionResultSource(Exception exception)
|
public ExceptionResultSource(Exception exception)
|
||||||
{
|
{
|
||||||
@@ -202,6 +203,11 @@ namespace Cysharp.Threading.Tasks
|
|||||||
|
|
||||||
public void GetResult(short token)
|
public void GetResult(short token)
|
||||||
{
|
{
|
||||||
|
if (!calledGet)
|
||||||
|
{
|
||||||
|
calledGet = true;
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
exception.Throw();
|
exception.Throw();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,11 +225,20 @@ namespace Cysharp.Threading.Tasks
|
|||||||
{
|
{
|
||||||
continuation(state);
|
continuation(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~ExceptionResultSource()
|
||||||
|
{
|
||||||
|
if (!calledGet)
|
||||||
|
{
|
||||||
|
UniTaskScheduler.PublishUnobservedTaskException(exception.SourceException);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class ExceptionResultSource<T> : IUniTaskSource<T>
|
sealed class ExceptionResultSource<T> : IUniTaskSource<T>
|
||||||
{
|
{
|
||||||
readonly ExceptionDispatchInfo exception;
|
readonly ExceptionDispatchInfo exception;
|
||||||
|
bool calledGet;
|
||||||
|
|
||||||
public ExceptionResultSource(Exception exception)
|
public ExceptionResultSource(Exception exception)
|
||||||
{
|
{
|
||||||
@@ -232,12 +247,22 @@ namespace Cysharp.Threading.Tasks
|
|||||||
|
|
||||||
public T GetResult(short token)
|
public T GetResult(short token)
|
||||||
{
|
{
|
||||||
|
if (!calledGet)
|
||||||
|
{
|
||||||
|
calledGet = true;
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
exception.Throw();
|
exception.Throw();
|
||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
void IUniTaskSource.GetResult(short token)
|
void IUniTaskSource.GetResult(short token)
|
||||||
{
|
{
|
||||||
|
if (!calledGet)
|
||||||
|
{
|
||||||
|
calledGet = true;
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
exception.Throw();
|
exception.Throw();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -255,6 +280,14 @@ namespace Cysharp.Threading.Tasks
|
|||||||
{
|
{
|
||||||
continuation(state);
|
continuation(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~ExceptionResultSource()
|
||||||
|
{
|
||||||
|
if (!calledGet)
|
||||||
|
{
|
||||||
|
UniTaskScheduler.PublishUnobservedTaskException(exception.SourceException);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class CanceledResultSource : IUniTaskSource
|
sealed class CanceledResultSource : IUniTaskSource
|
||||||
|
|||||||
@@ -9,237 +9,56 @@ namespace Cysharp.Threading.Tasks
|
|||||||
{
|
{
|
||||||
#region OBSOLETE_RUN
|
#region OBSOLETE_RUN
|
||||||
|
|
||||||
// Run is a confusing name, use only RunOnThreadPool in the future.
|
[Obsolete("UniTask.Run is similar as Task.Run, it uses ThreadPool. For equivalent behaviour, use UniTask.RunOnThreadPool instead. If you don't want to use ThreadPool, you can use UniTask.Void(async void) or UniTask.Create(async UniTask) too.")]
|
||||||
|
public static UniTask Run(Action action, bool configureAwait = true, CancellationToken cancellationToken = default)
|
||||||
/// <summary>[Obsolete]recommend to use RunOnThreadPool(or UniTask.Void(async void), UniTask.Create(async UniTask)).</summary>
|
|
||||||
public static async UniTask Run(Action action, bool configureAwait = true, CancellationToken cancellationToken = default)
|
|
||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
return RunOnThreadPool(action, configureAwait, cancellationToken);
|
||||||
|
|
||||||
await UniTask.SwitchToThreadPool();
|
|
||||||
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
|
||||||
|
|
||||||
if (configureAwait)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
action();
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
await UniTask.Yield();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
action();
|
|
||||||
}
|
|
||||||
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>[Obsolete]recommend to use RunOnThreadPool(or UniTask.Void(async void), UniTask.Create(async UniTask)).</summary>
|
[Obsolete("UniTask.Run is similar as Task.Run, it uses ThreadPool. For equivalent behaviour, use UniTask.RunOnThreadPool instead. If you don't want to use ThreadPool, you can use UniTask.Void(async void) or UniTask.Create(async UniTask) too.")]
|
||||||
public static async UniTask Run(Action<object> action, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
|
public static UniTask Run(Action<object> action, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
return RunOnThreadPool(action, state, configureAwait, cancellationToken);
|
||||||
|
|
||||||
await UniTask.SwitchToThreadPool();
|
|
||||||
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
|
||||||
|
|
||||||
if (configureAwait)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
action(state);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
await UniTask.Yield();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
action(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>[Obsolete]recommend to use RunOnThreadPool(or UniTask.Void(async void), UniTask.Create(async UniTask)).</summary>
|
[Obsolete("UniTask.Run is similar as Task.Run, it uses ThreadPool. For equivalent behaviour, use UniTask.RunOnThreadPool instead. If you don't want to use ThreadPool, you can use UniTask.Void(async void) or UniTask.Create(async UniTask) too.")]
|
||||||
public static async UniTask Run(Func<UniTask> action, bool configureAwait = true, CancellationToken cancellationToken = default)
|
public static UniTask Run(Func<UniTask> action, bool configureAwait = true, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
return RunOnThreadPool(action, configureAwait, cancellationToken);
|
||||||
|
|
||||||
await UniTask.SwitchToThreadPool();
|
|
||||||
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
|
||||||
|
|
||||||
if (configureAwait)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await action();
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
await UniTask.Yield();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await action();
|
|
||||||
}
|
|
||||||
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>[Obsolete]recommend to use RunOnThreadPool(or UniTask.Void(async void), UniTask.Create(async UniTask)).</summary>
|
[Obsolete("UniTask.Run is similar as Task.Run, it uses ThreadPool. For equivalent behaviour, use UniTask.RunOnThreadPool instead. If you don't want to use ThreadPool, you can use UniTask.Void(async void) or UniTask.Create(async UniTask) too.")]
|
||||||
public static async UniTask Run(Func<object, UniTask> action, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
|
public static UniTask Run(Func<object, UniTask> action, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
return RunOnThreadPool(action, state, configureAwait, cancellationToken);
|
||||||
|
|
||||||
await UniTask.SwitchToThreadPool();
|
|
||||||
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
|
||||||
|
|
||||||
if (configureAwait)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await action(state);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
await UniTask.Yield();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await action(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>[Obsolete]recommend to use RunOnThreadPool(or UniTask.Void(async void), UniTask.Create(async UniTask)).</summary>
|
[Obsolete("UniTask.Run is similar as Task.Run, it uses ThreadPool. For equivalent behaviour, use UniTask.RunOnThreadPool instead. If you don't want to use ThreadPool, you can use UniTask.Void(async void) or UniTask.Create(async UniTask) too.")]
|
||||||
public static async UniTask<T> Run<T>(Func<T> func, bool configureAwait = true, CancellationToken cancellationToken = default)
|
public static UniTask<T> Run<T>(Func<T> func, bool configureAwait = true, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
return RunOnThreadPool(func, configureAwait, cancellationToken);
|
||||||
|
|
||||||
await UniTask.SwitchToThreadPool();
|
|
||||||
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
|
||||||
|
|
||||||
if (configureAwait)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return func();
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
await UniTask.Yield();
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return func();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>[Obsolete]recommend to use RunOnThreadPool(or UniTask.Void(async void), UniTask.Create(async UniTask)).</summary>
|
[Obsolete("UniTask.Run is similar as Task.Run, it uses ThreadPool. For equivalent behaviour, use UniTask.RunOnThreadPool instead. If you don't want to use ThreadPool, you can use UniTask.Void(async void) or UniTask.Create(async UniTask) too.")]
|
||||||
public static async UniTask<T> Run<T>(Func<UniTask<T>> func, bool configureAwait = true, CancellationToken cancellationToken = default)
|
public static UniTask<T> Run<T>(Func<UniTask<T>> func, bool configureAwait = true, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
return RunOnThreadPool(func, configureAwait, cancellationToken);
|
||||||
|
|
||||||
await UniTask.SwitchToThreadPool();
|
|
||||||
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
|
||||||
|
|
||||||
if (configureAwait)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return await func();
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
|
||||||
await UniTask.Yield();
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var result = await func();
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>[Obsolete]recommend to use RunOnThreadPool(or UniTask.Void(async void), UniTask.Create(async UniTask)).</summary>
|
[Obsolete("UniTask.Run is similar as Task.Run, it uses ThreadPool. For equivalent behaviour, use UniTask.RunOnThreadPool instead. If you don't want to use ThreadPool, you can use UniTask.Void(async void) or UniTask.Create(async UniTask) too.")]
|
||||||
public static async UniTask<T> Run<T>(Func<object, T> func, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
|
public static UniTask<T> Run<T>(Func<object, T> func, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
return RunOnThreadPool(func, state, configureAwait, cancellationToken);
|
||||||
|
|
||||||
await UniTask.SwitchToThreadPool();
|
|
||||||
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
|
||||||
|
|
||||||
if (configureAwait)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return func(state);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
await UniTask.Yield();
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return func(state);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>[Obsolete]recommend to use RunOnThreadPool(or UniTask.Void(async void), UniTask.Create(async UniTask)).</summary>
|
[Obsolete("UniTask.Run is similar as Task.Run, it uses ThreadPool. For equivalent behaviour, use UniTask.RunOnThreadPool instead. If you don't want to use ThreadPool, you can use UniTask.Void(async void) or UniTask.Create(async UniTask) too.")]
|
||||||
public static async UniTask<T> Run<T>(Func<object, UniTask<T>> func, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
|
public static UniTask<T> Run<T>(Func<object, UniTask<T>> func, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
return RunOnThreadPool(func, state, configureAwait, cancellationToken);
|
||||||
|
|
||||||
await UniTask.SwitchToThreadPool();
|
|
||||||
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
|
||||||
|
|
||||||
if (configureAwait)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return await func(state);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
|
||||||
await UniTask.Yield();
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var result = await func(state);
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
|
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
|
||||||
public static async UniTask RunOnThreadPool(Action action, bool configureAwait = true, CancellationToken cancellationToken = default)
|
public static async UniTask RunOnThreadPool(Action action, bool configureAwait = true, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -137,8 +137,8 @@ namespace Cysharp.Threading.Tasks
|
|||||||
if (continuation != null || Interlocked.CompareExchange(ref this.continuation, UniTaskCompletionSourceCoreShared.s_sentinel, null) != null)
|
if (continuation != null || Interlocked.CompareExchange(ref this.continuation, UniTaskCompletionSourceCoreShared.s_sentinel, null) != null)
|
||||||
{
|
{
|
||||||
continuation(continuationState);
|
continuation(continuationState);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -165,8 +165,8 @@ namespace Cysharp.Threading.Tasks
|
|||||||
if (continuation != null || Interlocked.CompareExchange(ref this.continuation, UniTaskCompletionSourceCoreShared.s_sentinel, null) != null)
|
if (continuation != null || Interlocked.CompareExchange(ref this.continuation, UniTaskCompletionSourceCoreShared.s_sentinel, null) != null)
|
||||||
{
|
{
|
||||||
continuation(continuationState);
|
continuation(continuationState);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -184,8 +184,8 @@ namespace Cysharp.Threading.Tasks
|
|||||||
if (continuation != null || Interlocked.CompareExchange(ref this.continuation, UniTaskCompletionSourceCoreShared.s_sentinel, null) != null)
|
if (continuation != null || Interlocked.CompareExchange(ref this.continuation, UniTaskCompletionSourceCoreShared.s_sentinel, null) != null)
|
||||||
{
|
{
|
||||||
continuation(continuationState);
|
continuation(continuationState);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -328,6 +328,7 @@ namespace Cysharp.Threading.Tasks
|
|||||||
}
|
}
|
||||||
|
|
||||||
UniTaskCompletionSourceCore<AsyncUnit> core;
|
UniTaskCompletionSourceCore<AsyncUnit> core;
|
||||||
|
short version;
|
||||||
|
|
||||||
AutoResetUniTaskCompletionSource()
|
AutoResetUniTaskCompletionSource()
|
||||||
{
|
{
|
||||||
@@ -340,6 +341,7 @@ namespace Cysharp.Threading.Tasks
|
|||||||
{
|
{
|
||||||
result = new AutoResetUniTaskCompletionSource();
|
result = new AutoResetUniTaskCompletionSource();
|
||||||
}
|
}
|
||||||
|
result.version = result.core.Version;
|
||||||
TaskTracker.TrackActiveTask(result, 2);
|
TaskTracker.TrackActiveTask(result, 2);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -383,19 +385,19 @@ namespace Cysharp.Threading.Tasks
|
|||||||
[DebuggerHidden]
|
[DebuggerHidden]
|
||||||
public bool TrySetResult()
|
public bool TrySetResult()
|
||||||
{
|
{
|
||||||
return core.TrySetResult(AsyncUnit.Default);
|
return version == core.Version && core.TrySetResult(AsyncUnit.Default);
|
||||||
}
|
}
|
||||||
|
|
||||||
[DebuggerHidden]
|
[DebuggerHidden]
|
||||||
public bool TrySetCanceled(CancellationToken cancellationToken = default)
|
public bool TrySetCanceled(CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
return core.TrySetCanceled(cancellationToken);
|
return version == core.Version && core.TrySetCanceled(cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
[DebuggerHidden]
|
[DebuggerHidden]
|
||||||
public bool TrySetException(Exception exception)
|
public bool TrySetException(Exception exception)
|
||||||
{
|
{
|
||||||
return core.TrySetException(exception);
|
return version == core.Version && core.TrySetException(exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
[DebuggerHidden]
|
[DebuggerHidden]
|
||||||
@@ -409,7 +411,6 @@ namespace Cysharp.Threading.Tasks
|
|||||||
{
|
{
|
||||||
TryReturn();
|
TryReturn();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[DebuggerHidden]
|
[DebuggerHidden]
|
||||||
@@ -451,6 +452,7 @@ namespace Cysharp.Threading.Tasks
|
|||||||
}
|
}
|
||||||
|
|
||||||
UniTaskCompletionSourceCore<T> core;
|
UniTaskCompletionSourceCore<T> core;
|
||||||
|
short version;
|
||||||
|
|
||||||
AutoResetUniTaskCompletionSource()
|
AutoResetUniTaskCompletionSource()
|
||||||
{
|
{
|
||||||
@@ -463,6 +465,7 @@ namespace Cysharp.Threading.Tasks
|
|||||||
{
|
{
|
||||||
result = new AutoResetUniTaskCompletionSource<T>();
|
result = new AutoResetUniTaskCompletionSource<T>();
|
||||||
}
|
}
|
||||||
|
result.version = result.core.Version;
|
||||||
TaskTracker.TrackActiveTask(result, 2);
|
TaskTracker.TrackActiveTask(result, 2);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -506,19 +509,19 @@ namespace Cysharp.Threading.Tasks
|
|||||||
[DebuggerHidden]
|
[DebuggerHidden]
|
||||||
public bool TrySetResult(T result)
|
public bool TrySetResult(T result)
|
||||||
{
|
{
|
||||||
return core.TrySetResult(result);
|
return version == core.Version && core.TrySetResult(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
[DebuggerHidden]
|
[DebuggerHidden]
|
||||||
public bool TrySetCanceled(CancellationToken cancellationToken = default)
|
public bool TrySetCanceled(CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
return core.TrySetCanceled(cancellationToken);
|
return version == core.Version && core.TrySetCanceled(cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
[DebuggerHidden]
|
[DebuggerHidden]
|
||||||
public bool TrySetException(Exception exception)
|
public bool TrySetException(Exception exception)
|
||||||
{
|
{
|
||||||
return core.TrySetException(exception);
|
return version == core.Version && core.TrySetException(exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
[DebuggerHidden]
|
[DebuggerHidden]
|
||||||
@@ -937,5 +940,5 @@ namespace Cysharp.Threading.Tasks
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -28,7 +28,7 @@ namespace Cysharp.Threading.Tasks
|
|||||||
p.TrySetCanceled();
|
p.TrySetCanceled();
|
||||||
break;
|
break;
|
||||||
case TaskStatus.Faulted:
|
case TaskStatus.Faulted:
|
||||||
p.TrySetException(x.Exception);
|
p.TrySetException(x.Exception.InnerException ?? x.Exception);
|
||||||
break;
|
break;
|
||||||
case TaskStatus.RanToCompletion:
|
case TaskStatus.RanToCompletion:
|
||||||
p.TrySetResult(x.Result);
|
p.TrySetResult(x.Result);
|
||||||
@@ -58,7 +58,7 @@ namespace Cysharp.Threading.Tasks
|
|||||||
p.TrySetCanceled();
|
p.TrySetCanceled();
|
||||||
break;
|
break;
|
||||||
case TaskStatus.Faulted:
|
case TaskStatus.Faulted:
|
||||||
p.TrySetException(x.Exception);
|
p.TrySetException(x.Exception.InnerException ?? x.Exception);
|
||||||
break;
|
break;
|
||||||
case TaskStatus.RanToCompletion:
|
case TaskStatus.RanToCompletion:
|
||||||
p.TrySetResult();
|
p.TrySetResult();
|
||||||
|
|||||||
@@ -74,16 +74,19 @@ namespace Cysharp.Threading.Tasks
|
|||||||
|
|
||||||
static async UniTaskVoid Fire<T>(AsyncSubject<T> subject, UniTask<T> task)
|
static async UniTaskVoid Fire<T>(AsyncSubject<T> subject, UniTask<T> task)
|
||||||
{
|
{
|
||||||
|
T value;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var value = await task;
|
value = await task;
|
||||||
subject.OnNext(value);
|
|
||||||
subject.OnCompleted();
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
subject.OnError(ex);
|
subject.OnError(ex);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
subject.OnNext(value);
|
||||||
|
subject.OnCompleted();
|
||||||
}
|
}
|
||||||
|
|
||||||
static async UniTaskVoid Fire(AsyncSubject<AsyncUnit> subject, UniTask task)
|
static async UniTaskVoid Fire(AsyncSubject<AsyncUnit> subject, UniTask task)
|
||||||
@@ -91,13 +94,15 @@ namespace Cysharp.Threading.Tasks
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
await task;
|
await task;
|
||||||
subject.OnNext(AsyncUnit.Default);
|
|
||||||
subject.OnCompleted();
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
subject.OnError(ex);
|
subject.OnError(ex);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
subject.OnNext(AsyncUnit.Default);
|
||||||
|
subject.OnCompleted();
|
||||||
}
|
}
|
||||||
|
|
||||||
class ToUniTaskObserver<T> : IObserver<T>
|
class ToUniTaskObserver<T> : IObserver<T>
|
||||||
@@ -128,7 +133,7 @@ namespace Cysharp.Threading.Tasks
|
|||||||
{
|
{
|
||||||
var self = (ToUniTaskObserver<T>)state;
|
var self = (ToUniTaskObserver<T>)state;
|
||||||
self.disposable.Dispose();
|
self.disposable.Dispose();
|
||||||
self.promise.TrySetCanceled();
|
self.promise.TrySetCanceled(self.cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnNext(T value)
|
public void OnNext(T value)
|
||||||
@@ -198,7 +203,7 @@ namespace Cysharp.Threading.Tasks
|
|||||||
{
|
{
|
||||||
var self = (FirstValueToUniTaskObserver<T>)state;
|
var self = (FirstValueToUniTaskObserver<T>)state;
|
||||||
self.disposable.Dispose();
|
self.disposable.Dispose();
|
||||||
self.promise.TrySetCanceled();
|
self.promise.TrySetCanceled(self.cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnNext(T value)
|
public void OnNext(T value)
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ namespace Cysharp.Threading.Tasks
|
|||||||
|
|
||||||
public static UniTask<UnityEngine.Object[]> AwaitForAllAssets(this AssetBundleRequest asyncOperation, CancellationToken cancellationToken)
|
public static UniTask<UnityEngine.Object[]> AwaitForAllAssets(this AssetBundleRequest asyncOperation, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return AwaitForAllAssets(asyncOperation, cancellationToken: cancellationToken);
|
return AwaitForAllAssets(asyncOperation, null, PlayerLoopTiming.Update, cancellationToken: cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static UniTask<UnityEngine.Object[]> AwaitForAllAssets(this AssetBundleRequest asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
|
public static UniTask<UnityEngine.Object[]> AwaitForAllAssets(this AssetBundleRequest asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
|
||||||
|
|||||||
@@ -15,11 +15,15 @@ namespace Cysharp.Threading.Tasks
|
|||||||
{
|
{
|
||||||
#region AsyncOperation
|
#region AsyncOperation
|
||||||
|
|
||||||
|
#if !UNITY_2023_1_OR_NEWER
|
||||||
|
// from Unity2023.1.0a15, AsyncOperationAwaitableExtensions.GetAwaiter is defined in UnityEngine.
|
||||||
|
|
||||||
public static AsyncOperationAwaiter GetAwaiter(this AsyncOperation asyncOperation)
|
public static AsyncOperationAwaiter GetAwaiter(this AsyncOperation asyncOperation)
|
||||||
{
|
{
|
||||||
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
|
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
|
||||||
return new AsyncOperationAwaiter(asyncOperation);
|
return new AsyncOperationAwaiter(asyncOperation);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
public static UniTask WithCancellation(this AsyncOperation asyncOperation, CancellationToken cancellationToken)
|
public static UniTask WithCancellation(this AsyncOperation asyncOperation, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
@@ -896,7 +900,11 @@ namespace Cysharp.Threading.Tasks
|
|||||||
|
|
||||||
if (asyncOperation.isDone)
|
if (asyncOperation.isDone)
|
||||||
{
|
{
|
||||||
if (asyncOperation.webRequest.IsError())
|
if (asyncOperation.webRequest == null)
|
||||||
|
{
|
||||||
|
core.TrySetException(new ObjectDisposedException("The webRequest has been destroyed."));
|
||||||
|
}
|
||||||
|
else if (asyncOperation.webRequest.IsError())
|
||||||
{
|
{
|
||||||
core.TrySetException(new UnityWebRequestException(asyncOperation.webRequest));
|
core.TrySetException(new UnityWebRequestException(asyncOperation.webRequest));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -673,7 +673,7 @@ namespace Cysharp.Threading.Tasks
|
|||||||
}
|
}
|
||||||
if (cancellationToken2.CanBeCanceled)
|
if (cancellationToken2.CanBeCanceled)
|
||||||
{
|
{
|
||||||
registration2 = cancellationToken1.RegisterWithoutCaptureExecutionContext(cancel2, this);
|
registration2 = cancellationToken2.RegisterWithoutCaptureExecutionContext(cancel2, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -688,13 +688,27 @@ namespace Cysharp.Threading.Tasks
|
|||||||
static void OnCanceled1(object state)
|
static void OnCanceled1(object state)
|
||||||
{
|
{
|
||||||
var self = (UnityEventHandlerAsyncEnumerator)state;
|
var self = (UnityEventHandlerAsyncEnumerator)state;
|
||||||
self.DisposeAsync().Forget();
|
try
|
||||||
|
{
|
||||||
|
self.completionSource.TrySetCanceled(self.cancellationToken1);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
self.DisposeAsync().Forget();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void OnCanceled2(object state)
|
static void OnCanceled2(object state)
|
||||||
{
|
{
|
||||||
var self = (UnityEventHandlerAsyncEnumerator)state;
|
var self = (UnityEventHandlerAsyncEnumerator)state;
|
||||||
self.DisposeAsync().Forget();
|
try
|
||||||
|
{
|
||||||
|
self.completionSource.TrySetCanceled(self.cancellationToken2);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
self.DisposeAsync().Forget();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public UniTask DisposeAsync()
|
public UniTask DisposeAsync()
|
||||||
@@ -706,6 +720,8 @@ namespace Cysharp.Threading.Tasks
|
|||||||
registration1.Dispose();
|
registration1.Dispose();
|
||||||
registration2.Dispose();
|
registration2.Dispose();
|
||||||
unityEvent.RemoveListener(unityAction);
|
unityEvent.RemoveListener(unityAction);
|
||||||
|
|
||||||
|
completionSource.TrySetCanceled();
|
||||||
}
|
}
|
||||||
|
|
||||||
return default;
|
return default;
|
||||||
@@ -777,7 +793,7 @@ namespace Cysharp.Threading.Tasks
|
|||||||
}
|
}
|
||||||
if (cancellationToken2.CanBeCanceled)
|
if (cancellationToken2.CanBeCanceled)
|
||||||
{
|
{
|
||||||
registration2 = cancellationToken1.RegisterWithoutCaptureExecutionContext(cancel2, this);
|
registration2 = cancellationToken2.RegisterWithoutCaptureExecutionContext(cancel2, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -793,13 +809,27 @@ namespace Cysharp.Threading.Tasks
|
|||||||
static void OnCanceled1(object state)
|
static void OnCanceled1(object state)
|
||||||
{
|
{
|
||||||
var self = (UnityEventHandlerAsyncEnumerator)state;
|
var self = (UnityEventHandlerAsyncEnumerator)state;
|
||||||
self.DisposeAsync().Forget();
|
try
|
||||||
|
{
|
||||||
|
self.completionSource.TrySetCanceled(self.cancellationToken1);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
self.DisposeAsync().Forget();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void OnCanceled2(object state)
|
static void OnCanceled2(object state)
|
||||||
{
|
{
|
||||||
var self = (UnityEventHandlerAsyncEnumerator)state;
|
var self = (UnityEventHandlerAsyncEnumerator)state;
|
||||||
self.DisposeAsync().Forget();
|
try
|
||||||
|
{
|
||||||
|
self.completionSource.TrySetCanceled(self.cancellationToken2);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
self.DisposeAsync().Forget();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public UniTask DisposeAsync()
|
public UniTask DisposeAsync()
|
||||||
@@ -815,6 +845,8 @@ namespace Cysharp.Threading.Tasks
|
|||||||
disp.Dispose();
|
disp.Dispose();
|
||||||
}
|
}
|
||||||
unityEvent.RemoveListener(unityAction);
|
unityEvent.RemoveListener(unityAction);
|
||||||
|
|
||||||
|
completionSource.TrySetCanceled();
|
||||||
}
|
}
|
||||||
|
|
||||||
return default;
|
return default;
|
||||||
|
|||||||
@@ -12,17 +12,17 @@ namespace Cysharp.Threading.Tasks
|
|||||||
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
|
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
|
||||||
// <string> -> Text
|
// <string> -> Text
|
||||||
|
|
||||||
public static void BindTo(this IUniTaskAsyncEnumerable<string> source, Text text, bool rebindOnError = true)
|
public static void BindTo(this IUniTaskAsyncEnumerable<string> source, UnityEngine.UI.Text text, bool rebindOnError = true)
|
||||||
{
|
{
|
||||||
BindToCore(source, text, text.GetCancellationTokenOnDestroy(), rebindOnError).Forget();
|
BindToCore(source, text, text.GetCancellationTokenOnDestroy(), rebindOnError).Forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void BindTo(this IUniTaskAsyncEnumerable<string> source, Text text, CancellationToken cancellationToken, bool rebindOnError = true)
|
public static void BindTo(this IUniTaskAsyncEnumerable<string> source, UnityEngine.UI.Text text, CancellationToken cancellationToken, bool rebindOnError = true)
|
||||||
{
|
{
|
||||||
BindToCore(source, text, cancellationToken, rebindOnError).Forget();
|
BindToCore(source, text, cancellationToken, rebindOnError).Forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
static async UniTaskVoid BindToCore(IUniTaskAsyncEnumerable<string> source, Text text, CancellationToken cancellationToken, bool rebindOnError)
|
static async UniTaskVoid BindToCore(IUniTaskAsyncEnumerable<string> source, UnityEngine.UI.Text text, CancellationToken cancellationToken, bool rebindOnError)
|
||||||
{
|
{
|
||||||
var repeat = false;
|
var repeat = false;
|
||||||
BIND_AGAIN:
|
BIND_AGAIN:
|
||||||
@@ -68,22 +68,22 @@ namespace Cysharp.Threading.Tasks
|
|||||||
|
|
||||||
// <T> -> Text
|
// <T> -> Text
|
||||||
|
|
||||||
public static void BindTo<T>(this IUniTaskAsyncEnumerable<T> source, Text text, bool rebindOnError = true)
|
public static void BindTo<T>(this IUniTaskAsyncEnumerable<T> source, UnityEngine.UI.Text text, bool rebindOnError = true)
|
||||||
{
|
{
|
||||||
BindToCore(source, text, text.GetCancellationTokenOnDestroy(), rebindOnError).Forget();
|
BindToCore(source, text, text.GetCancellationTokenOnDestroy(), rebindOnError).Forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void BindTo<T>(this IUniTaskAsyncEnumerable<T> source, Text text, CancellationToken cancellationToken, bool rebindOnError = true)
|
public static void BindTo<T>(this IUniTaskAsyncEnumerable<T> source, UnityEngine.UI.Text text, CancellationToken cancellationToken, bool rebindOnError = true)
|
||||||
{
|
{
|
||||||
BindToCore(source, text, cancellationToken, rebindOnError).Forget();
|
BindToCore(source, text, cancellationToken, rebindOnError).Forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void BindTo<T>(this AsyncReactiveProperty<T> source, Text text, bool rebindOnError = true)
|
public static void BindTo<T>(this AsyncReactiveProperty<T> source, UnityEngine.UI.Text text, bool rebindOnError = true)
|
||||||
{
|
{
|
||||||
BindToCore(source, text, text.GetCancellationTokenOnDestroy(), rebindOnError).Forget();
|
BindToCore(source, text, text.GetCancellationTokenOnDestroy(), rebindOnError).Forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
static async UniTaskVoid BindToCore<T>(IUniTaskAsyncEnumerable<T> source, Text text, CancellationToken cancellationToken, bool rebindOnError)
|
static async UniTaskVoid BindToCore<T>(IUniTaskAsyncEnumerable<T> source, UnityEngine.UI.Text text, CancellationToken cancellationToken, bool rebindOnError)
|
||||||
{
|
{
|
||||||
var repeat = false;
|
var repeat = false;
|
||||||
BIND_AGAIN:
|
BIND_AGAIN:
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#if ENABLE_UNITYWEBREQUEST && (!UNITY_2019_1_OR_NEWER || UNITASK_WEBREQUEST_SUPPORT)
|
#if ENABLE_UNITYWEBREQUEST && (!UNITY_2019_1_OR_NEWER || UNITASK_WEBREQUEST_SUPPORT)
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -49,7 +49,7 @@ namespace Cysharp.Threading.Tasks
|
|||||||
{
|
{
|
||||||
if (msg == null)
|
if (msg == null)
|
||||||
{
|
{
|
||||||
if (Text != null)
|
if(!string.IsNullOrWhiteSpace(Text))
|
||||||
{
|
{
|
||||||
msg = Error + Environment.NewLine + Text;
|
msg = Error + Environment.NewLine + Text;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "com.cysharp.unitask",
|
"name": "com.cysharp.unitask",
|
||||||
"displayName": "UniTask",
|
"displayName": "UniTask",
|
||||||
"version": "2.2.5",
|
"author": { "name": "Cysharp, Inc.", "url": "https://cysharp.co.jp/en/" },
|
||||||
|
"version": "2.4.1",
|
||||||
"unity": "2018.4",
|
"unity": "2018.4",
|
||||||
"description": "Provides an efficient async/await integration to Unity.",
|
"description": "Provides an efficient async/await integration to Unity.",
|
||||||
"keywords": [ "async/await", "async", "Task", "UniTask" ],
|
"keywords": [ "async/await", "async", "Task", "UniTask" ],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"category": "Task",
|
"category": "Task",
|
||||||
"dependencies": {}
|
"dependencies": {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -543,7 +543,7 @@ public class SandboxMain : MonoBehaviour
|
|||||||
Debug.LogError(e);
|
Debug.LogError(e);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Debug.Log("TestAsync Finished.");
|
Debug.Log("TestAsync Finished.");
|
||||||
@@ -555,6 +555,7 @@ public class SandboxMain : MonoBehaviour
|
|||||||
async UniTaskVoid Start()
|
async UniTaskVoid Start()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
// UniTask.Delay(TimeSpan.FromSeconds(1)).TimeoutWithoutException
|
// UniTask.Delay(TimeSpan.FromSeconds(1)).TimeoutWithoutException
|
||||||
|
|
||||||
|
|
||||||
@@ -562,61 +563,34 @@ public class SandboxMain : MonoBehaviour
|
|||||||
PlayerLoopHelper.Initialize(ref currentLoop, InjectPlayerLoopTimings.Minimum); // minimum is Update | FixedUpdate | LastPostLateUpdate
|
PlayerLoopHelper.Initialize(ref currentLoop, InjectPlayerLoopTimings.Minimum); // minimum is Update | FixedUpdate | LastPostLateUpdate
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var cancelToken = new CancellationTokenSource();
|
|
||||||
cancelButton.onClick.AddListener(()=>
|
|
||||||
{
|
|
||||||
cancelToken.Cancel(); // cancel from button click.
|
|
||||||
});
|
|
||||||
|
|
||||||
var timeoutToken = new CancellationTokenSource();
|
|
||||||
timeoutToken.CancelAfterSlim(TimeSpan.FromSeconds(5)); // 5sec timeout.
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// combine token
|
|
||||||
var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancelToken.Token, timeoutToken.Token);
|
|
||||||
|
|
||||||
await UnityWebRequest.Get("http://foo").SendWebRequest().WithCancellation(linkedTokenSource.Token);
|
|
||||||
}
|
|
||||||
catch (OperationCanceledException ex)
|
|
||||||
{
|
|
||||||
if (timeoutToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
UnityEngine.Debug.Log("Timeout.");
|
|
||||||
}
|
|
||||||
else if (cancelToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
UnityEngine.Debug.Log("Cancel clicked.");
|
|
||||||
}
|
|
||||||
_ = ex;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// TestAsync(cts.Token).Forget();
|
// TestAsync(cts.Token).Forget();
|
||||||
|
|
||||||
okButton.onClick.AddListener(UniTask.UnityAction(async () =>
|
okButton.onClick.AddListener(UniTask.UnityAction(async () =>
|
||||||
{
|
{
|
||||||
// try timeout
|
await UniTask.WaitForEndOfFrame(this);
|
||||||
try
|
var texture = new Texture2D(Screen.width, Screen.height);
|
||||||
{
|
texture.ReadPixels(new Rect(0, 0, Screen.width, Screen.height), 0, 0);
|
||||||
//await UniTask.Delay(TimeSpan.FromSeconds(2), cancellationToken: timeoutController.Timeout(TimeSpan.FromSeconds(3)));
|
texture.Apply();
|
||||||
UnityEngine.Debug.Log("Delay Complete, Reset(and reuse).");
|
|
||||||
//timeoutController.Reset();
|
|
||||||
}
|
|
||||||
catch (OperationCanceledException ex)
|
|
||||||
{
|
|
||||||
//UnityEngine.Debug.Log("Timeout! FromTimeout?:" + timeoutController.IsTimeout());
|
|
||||||
_ = ex;
|
|
||||||
}
|
|
||||||
|
|
||||||
await UniTask.Yield();
|
var jpg = texture.EncodeToJPG();
|
||||||
|
File.WriteAllBytes("testscreencapture.jpg", jpg);
|
||||||
|
Debug.Log("ok?");
|
||||||
|
|
||||||
|
//var texture = ScreenCapture.CaptureScreenshotAsTexture();
|
||||||
|
//if (texture == null)
|
||||||
|
//{
|
||||||
|
// Debug.Log("fail");
|
||||||
|
//}
|
||||||
|
//else
|
||||||
|
//{
|
||||||
|
// var jpg = texture.EncodeToJPG();
|
||||||
|
// File.WriteAllBytes("testscreencapture.jpg", jpg);
|
||||||
|
// Debug.Log("ok?");
|
||||||
|
//}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
cancelButton.onClick.AddListener(UniTask.UnityAction(async () =>
|
cancelButton.onClick.AddListener(UniTask.UnityAction(async () =>
|
||||||
|
|||||||
@@ -310,33 +310,33 @@ namespace Cysharp.Threading.TasksTests
|
|||||||
yield return null;
|
yield return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnityTest]
|
//[UnityTest]
|
||||||
public IEnumerator ExceptionUnobserved1() => UniTask.ToCoroutine(async () =>
|
//public IEnumerator ExceptionUnobserved1() => UniTask.ToCoroutine(async () =>
|
||||||
{
|
//{
|
||||||
bool calledEx = false;
|
// bool calledEx = false;
|
||||||
Action<Exception> action = exx =>
|
// Action<Exception> action = exx =>
|
||||||
{
|
// {
|
||||||
calledEx = true;
|
// calledEx = true;
|
||||||
exx.Message.Should().Be("MyException");
|
// exx.Message.Should().Be("MyException");
|
||||||
};
|
// };
|
||||||
|
|
||||||
UniTaskScheduler.UnobservedTaskException += action;
|
// UniTaskScheduler.UnobservedTaskException += action;
|
||||||
|
|
||||||
var ex = InException1();
|
// var ex = InException1();
|
||||||
ex = default(UniTask);
|
// ex = default(UniTask);
|
||||||
|
|
||||||
await UniTask.DelayFrame(3);
|
// await UniTask.DelayFrame(3);
|
||||||
|
|
||||||
GC.Collect();
|
// GC.Collect();
|
||||||
GC.WaitForPendingFinalizers();
|
// GC.WaitForPendingFinalizers();
|
||||||
GC.Collect();
|
// GC.Collect();
|
||||||
|
|
||||||
await UniTask.DelayFrame(1);
|
// await UniTask.DelayFrame(1);
|
||||||
|
|
||||||
calledEx.Should().BeTrue();
|
// calledEx.Should().BeTrue();
|
||||||
|
|
||||||
UniTaskScheduler.UnobservedTaskException -= action;
|
// UniTaskScheduler.UnobservedTaskException -= action;
|
||||||
});
|
//});
|
||||||
|
|
||||||
[UnityTest]
|
[UnityTest]
|
||||||
public IEnumerator ExceptionUnobserved2() => UniTask.ToCoroutine(async () =>
|
public IEnumerator ExceptionUnobserved2() => UniTask.ToCoroutine(async () =>
|
||||||
@@ -366,6 +366,29 @@ namespace Cysharp.Threading.TasksTests
|
|||||||
UniTaskScheduler.UnobservedTaskException -= action;
|
UniTaskScheduler.UnobservedTaskException -= action;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// can not run on RuntimeUnitTestToolkit so ignore...
|
||||||
|
// [UnityTest]
|
||||||
|
// public IEnumerator ThrowExceptionUnawaited() => UniTask.ToCoroutine(async () =>
|
||||||
|
// {
|
||||||
|
// LogAssert.Expect(LogType.Exception, "Exception: MyException");
|
||||||
|
|
||||||
|
//#pragma warning disable 1998
|
||||||
|
// async UniTask Throw() => throw new Exception("MyException");
|
||||||
|
//#pragma warning restore 1998
|
||||||
|
|
||||||
|
//#pragma warning disable 4014
|
||||||
|
// Throw();
|
||||||
|
//#pragma warning restore 4014
|
||||||
|
|
||||||
|
// await UniTask.DelayFrame(3);
|
||||||
|
|
||||||
|
// GC.Collect();
|
||||||
|
// GC.WaitForPendingFinalizers();
|
||||||
|
// GC.Collect();
|
||||||
|
|
||||||
|
// await UniTask.DelayFrame(1);
|
||||||
|
// });
|
||||||
|
|
||||||
async UniTask InException1()
|
async UniTask InException1()
|
||||||
{
|
{
|
||||||
await UniTask.Yield();
|
await UniTask.Yield();
|
||||||
|
|||||||
Reference in New Issue
Block a user