mirror of
https://github.com/Cysharp/UniTask.git
synced 2026-05-15 11:30:09 +00:00
Compare commits
417 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5984b67ecb | ||
|
|
05fdf48058 | ||
|
|
647ed6ff82 | ||
|
|
0826b7e976 | ||
|
|
a51632cd4b | ||
|
|
bf945a7ef4 | ||
|
|
353f15e94f | ||
|
|
cf19f18662 | ||
|
|
fdb9d1cf95 | ||
|
|
2e0917428b | ||
|
|
0b16005f4b | ||
|
|
74bbe87b58 | ||
|
|
6f4131539b | ||
|
|
06283f0ffb | ||
|
|
dfe5ee43c2 | ||
|
|
83d8a2b424 | ||
|
|
4d204e4aa6 | ||
|
|
87e164e275 | ||
|
|
b63eb8d090 | ||
|
|
75119acb50 | ||
|
|
f7b3c2fbe1 | ||
|
|
b317ecfa01 | ||
|
|
7b05569ef7 | ||
|
|
e0465c6c2c | ||
|
|
9057452c86 | ||
|
|
a2f6f84bde | ||
|
|
f057abff0f | ||
|
|
c61a7d9961 | ||
|
|
9587f2eeec | ||
|
|
550784f31c | ||
|
|
11b3282b3d | ||
|
|
b2532b0798 | ||
|
|
4fc41ecb17 | ||
|
|
e52663cef6 | ||
|
|
1827be2de7 | ||
|
|
8560561ef3 | ||
|
|
2019f1fa7f | ||
|
|
9e2265d148 | ||
|
|
9d02279822 | ||
|
|
17ce06d93c | ||
|
|
7b810413fe | ||
|
|
d78c0d6c02 | ||
|
|
d248acc7d1 | ||
|
|
4c0b1f753a | ||
|
|
7bb6feda55 | ||
|
|
f8a501290a | ||
|
|
cb497c9eb5 | ||
|
|
342a37a074 | ||
|
|
c3146ec74f | ||
|
|
df16813fae | ||
|
|
8d98bbc7ba | ||
|
|
bb095697f7 | ||
|
|
cdf88c6a6a | ||
|
|
4eee2c9270 | ||
|
|
222b1401f5 | ||
|
|
fbe0bf8515 | ||
|
|
626685292c | ||
|
|
67794f5cdd | ||
|
|
2e225fb841 | ||
|
|
935523f25c | ||
|
|
3b3f7ebd3e | ||
|
|
88ecfa2992 | ||
|
|
aa70fe5c5a | ||
|
|
0c8057c668 | ||
|
|
28867646b0 | ||
|
|
ca60864021 | ||
|
|
1e4561da22 | ||
|
|
1496cc990a | ||
|
|
7bb3bf8d0e | ||
|
|
d057074f17 | ||
|
|
b724a2aa84 | ||
|
|
938ddd5356 | ||
|
|
911c37d4d8 | ||
|
|
caccccb0b5 | ||
|
|
a48f11d31b | ||
|
|
fcd93feb56 | ||
|
|
b472b23773 | ||
|
|
01c8fada1f | ||
|
|
10cd137126 | ||
|
|
809d23edae | ||
|
|
d38731bc44 | ||
|
|
64792b672d | ||
|
|
3892cc2299 | ||
|
|
5bfccaa3b6 | ||
|
|
b49b7332bb | ||
|
|
08184af737 | ||
|
|
0ef6c59385 | ||
|
|
4b0bd3b509 | ||
|
|
9a4720d180 | ||
|
|
b99646558c | ||
|
|
ee12dd9ae7 | ||
|
|
006e0f2c81 | ||
|
|
7d31299b5c | ||
|
|
fe8bf834e6 | ||
|
|
5843258e8c | ||
|
|
6cd002645e | ||
|
|
4fe0861714 | ||
|
|
fbbba061dd | ||
|
|
81f2e37ea5 | ||
|
|
66de0d3a58 | ||
|
|
beb10abbf7 | ||
|
|
d60f64761b | ||
|
|
36ac0863ad | ||
|
|
104f8e09ca | ||
|
|
cfbff008c4 | ||
|
|
5cc97c7f00 | ||
|
|
5666292496 | ||
|
|
1288cbc128 | ||
|
|
5f3aa18f38 | ||
|
|
0970ae8c31 | ||
|
|
ad23f7fb29 | ||
|
|
a4be8f316e | ||
|
|
370425578f | ||
|
|
1b76f77608 | ||
|
|
0579984355 | ||
|
|
39cf81d2ab | ||
|
|
0a203c8db9 | ||
|
|
55be4dba82 | ||
|
|
f0adf36633 | ||
|
|
24afc4f3eb | ||
|
|
2cf06af433 | ||
|
|
94be2e748b | ||
|
|
7f582e5e29 | ||
|
|
3f042c8886 | ||
|
|
6f5d818544 | ||
|
|
2ccb37cb02 | ||
|
|
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 | ||
|
|
958a8e11ab | ||
|
|
257186313b | ||
|
|
5bd508b31c | ||
|
|
9e18ba332e | ||
|
|
946b9003f0 | ||
|
|
ffa55becf3 | ||
|
|
72e620d169 | ||
|
|
10ebddf892 | ||
|
|
c51e45ee21 | ||
|
|
6968faf35b | ||
|
|
4fc9ca315e | ||
|
|
aaf1c0eaa1 | ||
|
|
44ce3c96bb | ||
|
|
03097f08e2 | ||
|
|
af82dd719e | ||
|
|
ecd3625a08 | ||
|
|
da8f599ccb | ||
|
|
aa3216e48d | ||
|
|
5f7148419f | ||
|
|
89ae106ea7 | ||
|
|
721a7d9e4e | ||
|
|
18f2746f0d | ||
|
|
8a10f2191f | ||
|
|
8b3c8d15c4 | ||
|
|
49ca9364f7 | ||
|
|
62f6429b60 | ||
|
|
b6a9836e81 | ||
|
|
be34d8abf4 | ||
|
|
9af15d7ab3 | ||
|
|
308fef2859 | ||
|
|
3cc0c80b1e | ||
|
|
cdda33a98e | ||
|
|
e57a4332ec | ||
|
|
e88e553cc9 | ||
|
|
a1a38d0d7c | ||
|
|
c9bebd6550 | ||
|
|
42047070dd | ||
|
|
316f3bd963 | ||
|
|
5f96e646d4 | ||
|
|
186114996c | ||
|
|
841b6e85ae | ||
|
|
7ac9853cf6 | ||
|
|
0ec45b9da6 | ||
|
|
dfd0fe9fe4 | ||
|
|
4710268e0a | ||
|
|
cae512e4de | ||
|
|
6351d4c5a4 | ||
|
|
1173bb4f34 | ||
|
|
e4272b5337 | ||
|
|
b660506e31 | ||
|
|
2c101aef08 | ||
|
|
498b2a4f81 | ||
|
|
a8c2fd420a | ||
|
|
dade7fdc76 | ||
|
|
1c8b16f798 | ||
|
|
89649d8777 | ||
|
|
9894bf875b | ||
|
|
45a800330b | ||
|
|
a35e5f929d | ||
|
|
9a3f10d4bf | ||
|
|
19b5f921a1 | ||
|
|
8cd577904e | ||
|
|
309d661bcc | ||
|
|
8736024d9b | ||
|
|
97680e57a8 | ||
|
|
638522600a | ||
|
|
081113a62c | ||
|
|
7535c84581 | ||
|
|
690f3a2ead | ||
|
|
88371d08b9 | ||
|
|
6d382450cf | ||
|
|
19f2a6f282 | ||
|
|
7ed0b016ea | ||
|
|
f72a51e13d | ||
|
|
0e2dbbe754 | ||
|
|
cff51a0425 | ||
|
|
7cac4bbbab | ||
|
|
3f4a46bca6 | ||
|
|
7d21a75ea8 | ||
|
|
88817b7093 | ||
|
|
52cdadc035 | ||
|
|
5401b9b227 | ||
|
|
38de930f81 | ||
|
|
9be6ef7ba6 | ||
|
|
739bc6e26c | ||
|
|
71879266ac | ||
|
|
5ced0a1d4b | ||
|
|
908e361985 | ||
|
|
797affae4d | ||
|
|
ae3b825e29 | ||
|
|
143d97a73b | ||
|
|
90631c54b1 | ||
|
|
478e2998a8 | ||
|
|
9406305b2e | ||
|
|
1c26c81b20 | ||
|
|
a455de88b0 | ||
|
|
fba6942d5f | ||
|
|
3115efb672 | ||
|
|
fd70c031cb | ||
|
|
a2eb75df68 | ||
|
|
4a62d7eba6 | ||
|
|
40d2d2fe06 | ||
|
|
d5d2cb5937 | ||
|
|
854100c075 | ||
|
|
5837b26208 | ||
|
|
da0e654e7d | ||
|
|
a3e9932be7 | ||
|
|
e82353b4d9 | ||
|
|
944b61f28c | ||
|
|
457c574865 | ||
|
|
089a509663 | ||
|
|
3bebaef969 | ||
|
|
37e8b4500e | ||
|
|
8537ddf8a6 | ||
|
|
346b1e0a6b | ||
|
|
fc7b9660a5 | ||
|
|
21e5cc22c7 | ||
|
|
3f18b37e5f | ||
|
|
5d4a90e9bd | ||
|
|
2bf9f4f062 | ||
|
|
d69490cb49 | ||
|
|
4e460c11ca | ||
|
|
9313969314 | ||
|
|
a40f89a922 | ||
|
|
e0d8410b62 | ||
|
|
bef1bd8ad1 | ||
|
|
81b4fcfac1 | ||
|
|
f1e4a3c65d | ||
|
|
65622b01f6 | ||
|
|
87dd5f13fd | ||
|
|
79cd2c17ba | ||
|
|
85fb08552e | ||
|
|
7bd4b6faf7 | ||
|
|
0b1ae7e295 | ||
|
|
35a893ad9e | ||
|
|
4955ed18f1 | ||
|
|
fe462328ab | ||
|
|
5136d92efa | ||
|
|
227f7872cb | ||
|
|
725b2fdc35 | ||
|
|
75abc8059f | ||
|
|
f1193743c8 | ||
|
|
109730eacd | ||
|
|
1f736afe86 | ||
|
|
4d554a6718 | ||
|
|
69c0c362e9 | ||
|
|
3bb446556a | ||
|
|
ea950d8cec | ||
|
|
a65f4da7a2 | ||
|
|
0bdc933c20 | ||
|
|
0c0f79c6db | ||
|
|
32f9b9d4ac |
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
github: [neuecc]
|
||||
7
.github/dependabot.yaml
vendored
Normal file
7
.github/dependabot.yaml
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
# ref: https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly" # Check for updates to GitHub Actions every week
|
||||
105
.github/workflows/build-debug.yml
vendored
105
.github/workflows/build-debug.yml
vendored
@@ -3,65 +3,86 @@ name: Build-Debug
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "**"
|
||||
tags:
|
||||
- "!*" # not a tag push
|
||||
- "master"
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
branches:
|
||||
- "master"
|
||||
|
||||
jobs:
|
||||
build-dotnet:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
|
||||
NUGET_XMLDOC_MODE: skip
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
dotnet-version: 3.1.101
|
||||
- run: dotnet test -c Debug ./src/UniTask.NetCoreTests/UniTask.NetCoreTests.csproj
|
||||
- uses: actions/checkout@v4
|
||||
- uses: Cysharp/Actions/.github/actions/setup-dotnet@main
|
||||
- run: dotnet build -c Debug
|
||||
- run: dotnet test -c Debug
|
||||
|
||||
build-unity:
|
||||
if: "(github.event == '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:')) && github.triggering_actor != 'dependabot[bot]' }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
max-parallel: 2
|
||||
matrix:
|
||||
unity: ["2019.3.9f1", "2020.1.0b5"]
|
||||
include:
|
||||
- unity: 2019.3.9f1
|
||||
license: UNITY_2019_3
|
||||
- unity: 2020.1.0b5
|
||||
license: UNITY_2020_1
|
||||
unity: ["2021.3.41f1", "2022.3.39f1", "6000.0.12f1"] # Test with LTS
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
# with linux-il2cpp. image from https://hub.docker.com/r/gableroux/unity3d/tags
|
||||
image: gableroux/unity3d:${{ matrix.unity }}-linux-il2cpp
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- run: apt update && apt install git -y
|
||||
- uses: actions/checkout@v2
|
||||
- run: echo -n "$UNITY_LICENSE" >> .Unity.ulf
|
||||
- name: Load secrets
|
||||
id: op-load-secret
|
||||
uses: 1password/load-secrets-action@v2
|
||||
with:
|
||||
export-env: false
|
||||
env:
|
||||
UNITY_LICENSE: ${{ secrets[matrix.license] }}
|
||||
- name: Activate Unity, always returns a success. But if a subsequent run fails, the activation may have failed(if succeeded, shows `Next license update check is after` and not shows other message(like GUID != GUID). If fails not). In that case, upload the artifact's .alf file to https://license.unity3d.com/manual to get the .ulf file and set it to secrets.
|
||||
run: /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -manualLicenseFile .Unity.ulf || exit 0
|
||||
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN_PUBLIC }}
|
||||
UNITY_EMAIL: "op://GitHubActionsPublic/UNITY_LICENSE/username"
|
||||
UNITY_PASSWORD: "op://GitHubActionsPublic/UNITY_LICENSE/credential"
|
||||
UNITY_SERIAL: "op://GitHubActionsPublic/UNITY_LICENSE/serial"
|
||||
|
||||
# Execute scripts: RuntimeUnitTestToolkit
|
||||
- name: Build UnitTest(Linux64, mono)
|
||||
run: /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod UnitTestBuilder.BuildUnitTest /headless /ScriptBackend mono /BuildTarget StandaloneLinux64
|
||||
working-directory: src/UniTask
|
||||
- name: Execute UnitTest
|
||||
run: ./src/UniTask/bin/UnitTest/StandaloneLinux64_Mono2x/test
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
# Execute scripts: Export Package
|
||||
- name: Export unitypackage
|
||||
run: /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod PackageExporter.Export
|
||||
working-directory: src/UniTask
|
||||
# /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod PackageExporter.Export
|
||||
- name: Build Unity (.unitypacakge)
|
||||
if: ${{ startsWith(matrix.unity, '2021') }} # only execute once
|
||||
uses: Cysharp/Actions/.github/actions/unity-builder@main
|
||||
env:
|
||||
UNITY_EMAIL: ${{ steps.op-load-secret.outputs.UNITY_EMAIL }}
|
||||
UNITY_PASSWORD: ${{ steps.op-load-secret.outputs.UNITY_PASSWORD }}
|
||||
UNITY_SERIAL: ${{ steps.op-load-secret.outputs.UNITY_SERIAL }}
|
||||
with:
|
||||
projectPath: src/UniTask
|
||||
unityVersion: ${{ matrix.unity }}
|
||||
targetPlatform: StandaloneLinux64
|
||||
buildMethod: PackageExporter.Export
|
||||
|
||||
- uses: Cysharp/Actions/.github/actions/check-metas@main # check meta files
|
||||
with:
|
||||
directory: src/UniTask
|
||||
|
||||
# Execute UnitTest
|
||||
# /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod UnitTestBuilder.BuildUnitTest /headless /ScriptBackend IL2CPP /BuildTarget StandaloneLinux64
|
||||
- name: Build UnitTest (IL2CPP)
|
||||
uses: Cysharp/Actions/.github/actions/unity-builder@main
|
||||
env:
|
||||
UNITY_EMAIL: ${{ steps.op-load-secret.outputs.UNITY_EMAIL }}
|
||||
UNITY_PASSWORD: ${{ steps.op-load-secret.outputs.UNITY_PASSWORD }}
|
||||
UNITY_SERIAL: ${{ steps.op-load-secret.outputs.UNITY_SERIAL }}
|
||||
with:
|
||||
projectPath: src/UniTask
|
||||
unityVersion: ${{ matrix.unity }}
|
||||
targetPlatform: StandaloneLinux64
|
||||
buildMethod: UnitTestBuilder.BuildUnitTest
|
||||
customParameters: "/headless /ScriptBackend IL2CPP"
|
||||
- name: Check UnitTest file is generated
|
||||
run: ls -lR ./src/UniTask/bin/UnitTest
|
||||
- name: Execute UnitTest
|
||||
run: ./src/UniTask/bin/UnitTest/StandaloneLinux64_IL2CPP/test
|
||||
|
||||
# Store artifacts.
|
||||
- uses: actions/upload-artifact@v2
|
||||
- uses: Cysharp/Actions/.github/actions/upload-artifact@main
|
||||
if: ${{ startsWith(matrix.unity, '2021') }} # only execute 2021
|
||||
with:
|
||||
name: UniTask.unitypackage.zip
|
||||
name: UniTask.unitypackage-${{ matrix.unity }}.zip
|
||||
path: ./src/UniTask/*.unitypackage
|
||||
retention-days: 1
|
||||
|
||||
10
.github/workflows/build-docs.yml
vendored
10
.github/workflows/build-docs.yml
vendored
@@ -8,11 +8,11 @@ on:
|
||||
|
||||
jobs:
|
||||
run-docfx:
|
||||
if: "!(contains(github.event.head_commit.message, '[skip ci]') || contains(github.event.head_commit.message, '[ci skip]'))"
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: Cysharp/DocfxTemplate
|
||||
path: docs/_DocfxTemplate
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
with:
|
||||
args: build docs/docfx.json
|
||||
- name: Publish to GitHub Pages
|
||||
uses: peaceiris/actions-gh-pages@v3
|
||||
uses: peaceiris/actions-gh-pages@v4
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: docs/_site
|
||||
publish_dir: docs/_site
|
||||
|
||||
169
.github/workflows/build-release.yml
vendored
169
.github/workflows/build-release.yml
vendored
@@ -1,105 +1,110 @@
|
||||
name: Build-Release
|
||||
name: build-release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "[0-9]+.[0-9]+.[0-9]+*"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: "tag: git tag you want create. (sample 1.0.0)"
|
||||
required: true
|
||||
dry-run:
|
||||
description: "dry-run: true will never create relase/nuget."
|
||||
required: true
|
||||
default: false
|
||||
type: boolean
|
||||
|
||||
jobs:
|
||||
update-packagejson:
|
||||
uses: Cysharp/Actions/.github/workflows/update-packagejson.yaml@main
|
||||
with:
|
||||
file-path: ./src/UniTask/Assets/Plugins/UniTask/package.json
|
||||
tag: ${{ inputs.tag }}
|
||||
dry-run: ${{ inputs.dry-run }}
|
||||
|
||||
build-dotnet:
|
||||
needs: [update-packagejson]
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
|
||||
NUGET_XMLDOC_MODE: skip
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-dotnet@v1
|
||||
- run: echo ${{ needs.update-packagejson.outputs.sha }}
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
dotnet-version: 3.1.101
|
||||
# set release tag(*.*.*) to env.GIT_TAG
|
||||
- run: echo ::set-env name=GIT_TAG::${GITHUB_REF#refs/tags/}
|
||||
|
||||
ref: ${{ needs.update-packagejson.outputs.sha }}
|
||||
- uses: Cysharp/Actions/.github/actions/setup-dotnet@main
|
||||
# build and pack
|
||||
- run: dotnet build -c Release -p:Version=${{ env.GIT_TAG }}
|
||||
- run: dotnet build -c Release -p:Version=${{ inputs.tag }}
|
||||
- run: dotnet test -c Release --no-build
|
||||
- run: dotnet pack ./src/UniTask.NetCore/UniTask.NetCore.csproj -c Release --no-build -p:Version=${{ env.GIT_TAG }}
|
||||
|
||||
- run: dotnet pack ./src/UniTask.NetCore/UniTask.NetCore.csproj -c Release --no-build -p:Version=${{ inputs.tag }} -o ./publish
|
||||
# Store artifacts.
|
||||
- uses: actions/upload-artifact@v1
|
||||
- uses: Cysharp/Actions/.github/actions/upload-artifact@main
|
||||
with:
|
||||
name: nuget
|
||||
path: ./src/UniTask.NetCore/bin/Release/UniTask.${{ env.GIT_TAG }}.nupkg
|
||||
path: ./publish/
|
||||
retention-days: 1
|
||||
|
||||
build-unity:
|
||||
needs: [update-packagejson]
|
||||
strategy:
|
||||
matrix:
|
||||
unity: ['2019.3.9f1']
|
||||
include:
|
||||
- unity: 2019.3.9f1
|
||||
license: UNITY_2019_3
|
||||
unity: ["2022.3.39f1"]
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
# with linux-il2cpp. image from https://hub.docker.com/r/gableroux/unity3d/tags
|
||||
image: gableroux/unity3d:${{ matrix.unity }}-linux-il2cpp
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- run: apt update && apt install git -y
|
||||
- uses: actions/checkout@v2
|
||||
- run: echo -n "$UNITY_LICENSE" >> .Unity.ulf
|
||||
env:
|
||||
UNITY_LICENSE: ${{ secrets[matrix.license] }}
|
||||
- run: /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -manualLicenseFile .Unity.ulf || exit 0
|
||||
|
||||
# set release tag(*.*.*) to env.GIT_TAG
|
||||
- run: echo ::set-env name=GIT_TAG::${GITHUB_REF#refs/tags/}
|
||||
|
||||
# Execute scripts: Export Package
|
||||
- name: Export unitypackage
|
||||
run: /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod PackageExporter.Export
|
||||
working-directory: src/UniTask
|
||||
|
||||
# Store artifacts.
|
||||
- uses: actions/upload-artifact@v2
|
||||
- name: Load secrets
|
||||
id: op-load-secret
|
||||
uses: 1password/load-secrets-action@v2
|
||||
with:
|
||||
name: UniTask.${{ env.GIT_TAG }}.unitypackage
|
||||
path: ./src/UniTask/UniTask.${{ env.GIT_TAG }}.unitypackage
|
||||
export-env: false
|
||||
env:
|
||||
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN_PUBLIC }}
|
||||
UNITY_EMAIL: "op://GitHubActionsPublic/UNITY_LICENSE/username"
|
||||
UNITY_PASSWORD: "op://GitHubActionsPublic/UNITY_LICENSE/credential"
|
||||
UNITY_SERIAL: "op://GitHubActionsPublic/UNITY_LICENSE/serial"
|
||||
|
||||
- run: echo ${{ needs.update-packagejson.outputs.sha }}
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ needs.update-packagejson.outputs.sha }}
|
||||
# Execute scripts: Export Package
|
||||
# /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod PackageExporter.Export
|
||||
- name: Build Unity (.unitypacakge)
|
||||
uses: Cysharp/Actions/.github/actions/unity-builder@main
|
||||
env:
|
||||
UNITY_EMAIL: ${{ steps.op-load-secret.outputs.UNITY_EMAIL }}
|
||||
UNITY_PASSWORD: ${{ steps.op-load-secret.outputs.UNITY_PASSWORD }}
|
||||
UNITY_SERIAL: ${{ steps.op-load-secret.outputs.UNITY_SERIAL }}
|
||||
with:
|
||||
projectPath: src/UniTask
|
||||
unityVersion: ${{ matrix.unity }}
|
||||
targetPlatform: StandaloneLinux64
|
||||
buildMethod: PackageExporter.Export
|
||||
|
||||
- uses: Cysharp/Actions/.github/actions/check-metas@main # check meta files
|
||||
with:
|
||||
directory: src/UniTask
|
||||
|
||||
# Store artifacts.
|
||||
- uses: Cysharp/Actions/.github/actions/upload-artifact@main
|
||||
with:
|
||||
name: UniTask.${{ inputs.tag }}.unitypackage
|
||||
path: ./src/UniTask/UniTask.${{ inputs.tag }}.unitypackage
|
||||
retention-days: 1
|
||||
|
||||
# release
|
||||
create-release:
|
||||
needs: [build-dotnet, build-unity]
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
|
||||
NUGET_XMLDOC_MODE: skip
|
||||
steps:
|
||||
# setup dotnet for nuget push
|
||||
- uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
dotnet-version: 3.1.101
|
||||
# set release tag(*.*.*) to env.GIT_TAG
|
||||
- run: echo ::set-env name=GIT_TAG::${GITHUB_REF#refs/tags/}
|
||||
needs: [update-packagejson, build-dotnet, build-unity]
|
||||
uses: Cysharp/Actions/.github/workflows/create-release.yaml@main
|
||||
with:
|
||||
commit-id: ${{ needs.update-packagejson.outputs.sha }}
|
||||
dry-run: ${{ inputs.dry-run }}
|
||||
tag: ${{ inputs.tag }}
|
||||
nuget-push: true
|
||||
release-upload: true
|
||||
release-asset-path: ./UniTask.${{ inputs.tag }}.unitypackage/UniTask.${{ inputs.tag }}.unitypackage
|
||||
secrets: inherit
|
||||
|
||||
# Create Releases
|
||||
- uses: actions/create-release@v1
|
||||
id: create_release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: Ver.${{ github.ref }}
|
||||
|
||||
# Download(All) Artifacts to current directory
|
||||
- uses: actions/download-artifact@v2-preview
|
||||
|
||||
# Upload to NuGet
|
||||
- run: dotnet nuget push "./nuget/*.nupkg" -s https://www.nuget.org/api/v2/package -k ${{ secrets.NUGET_KEY }}
|
||||
|
||||
# Upload to Releases(unitypackage)
|
||||
- uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./UniTask.${{ env.GIT_TAG }}.unitypackage/UniTask.${{ env.GIT_TAG }}.unitypackage
|
||||
asset_name: UniTask.${{ env.GIT_TAG }}.unitypackage
|
||||
asset_content_type: application/octet-stream
|
||||
cleanup:
|
||||
if: ${{ needs.update-packagejson.outputs.is-branch-created == 'true' }}
|
||||
needs: [update-packagejson, build-dotnet, build-unity]
|
||||
uses: Cysharp/Actions/.github/workflows/clean-packagejson-branch.yaml@main
|
||||
with:
|
||||
branch: ${{ needs.update-packagejson.outputs.branch-name }}
|
||||
|
||||
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
|
||||
10
.github/workflows/stale.yml
vendored
Normal file
10
.github/workflows/stale.yml
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
name: "Close stale issues"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
uses: Cysharp/Actions/.github/workflows/stale-issue.yaml@main
|
||||
2
.github/workflows/toc.yml
vendored
2
.github/workflows/toc.yml
vendored
@@ -10,6 +10,6 @@ jobs:
|
||||
name: TOC Generator
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: technote-space/toc-generator@v2.4.0
|
||||
- uses: technote-space/toc-generator@v4.3.1
|
||||
with:
|
||||
TOC_TITLE: "## Table of Contents"
|
||||
135
.gitignore
vendored
135
.gitignore
vendored
@@ -80,7 +80,7 @@ _ReSharper*
|
||||
*.ncrunch*
|
||||
.*crunch*.local.xml
|
||||
|
||||
# Installshield output folder
|
||||
# Installshield output folder
|
||||
[Ee]xpress
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
@@ -130,126 +130,15 @@ UpgradeLog*.XML
|
||||
Assets/WSATestCertificate.pfx
|
||||
.vs/
|
||||
|
||||
Assembly-CSharp\.csproj
|
||||
# Unity
|
||||
|
||||
UniRx\.Async\.csproj
|
||||
|
||||
UniRx\.Async\.Editor\.csproj
|
||||
|
||||
UniRx\.Async\.Tests\.csproj
|
||||
|
||||
UniTask\.sln
|
||||
|
||||
RuntimeUnitTestToolkit\.csproj
|
||||
|
||||
Assembly-CSharp-Editor\.csproj
|
||||
|
||||
UniRx\.Async\.unitypackage
|
||||
|
||||
UniRx.Async.Tests.Editor.csproj
|
||||
|
||||
src/UniTask/UniTask.csproj
|
||||
|
||||
src/UniTask/UniTask.Editor.csproj
|
||||
|
||||
src/UniTask/UniTask.Tests.csproj
|
||||
|
||||
src/UniTask/UniTask.Tests.Editor.csproj
|
||||
|
||||
src/UniTask/UniTask.*.unitypackage
|
||||
|
||||
src/UniTask/UniTask.Linq.csproj
|
||||
|
||||
src/UniTask/DOTween.Modules.csproj
|
||||
|
||||
src/UniTask/Unity.Addressables.csproj
|
||||
|
||||
src/UniTask/Unity.Addressables.Editor.csproj
|
||||
|
||||
src/UniTask/Unity.Analytics.DataPrivacy.csproj
|
||||
|
||||
src/UniTask/Unity.Recorder.csproj
|
||||
|
||||
src/UniTask/Unity.Recorder.Editor.csproj
|
||||
|
||||
src/UniTask/Unity.ResourceManager.csproj
|
||||
|
||||
src/UniTask/Unity.Rider.Editor.csproj
|
||||
|
||||
src/UniTask/Unity.ScriptableBuildPipeline.csproj
|
||||
|
||||
src/UniTask/Unity.ScriptableBuildPipeline.Editor.csproj
|
||||
|
||||
src/UniTask/Unity.TextMeshPro.csproj
|
||||
|
||||
src/UniTask/Unity.TextMeshPro.Editor.csproj
|
||||
|
||||
src/UniTask/Unity.Timeline.csproj
|
||||
|
||||
src/UniTask/Unity.Timeline.Editor.csproj
|
||||
|
||||
src/UniTask/Unity.VisualStudio.Editor.csproj
|
||||
|
||||
src/UniTask/Unity.VSCode.Editor.csproj
|
||||
|
||||
src/UniTask/UnityEditor.CacheServer.csproj
|
||||
|
||||
src/UniTask/UnityEditor.TestRunner.csproj
|
||||
|
||||
src/UniTask/UnityEditor.UI.csproj
|
||||
|
||||
src/UniTask/UnityEngine.Advertisements.csproj
|
||||
|
||||
src/UniTask/UnityEngine.Monetization.csproj
|
||||
|
||||
src/UniTask/UnityEngine.TestRunner.csproj
|
||||
|
||||
src/UniTask/UnityEngine.UI.csproj
|
||||
|
||||
src/UniTask/TempAsm.csproj
|
||||
|
||||
src/UniTask/UniTask.Addressables.csproj
|
||||
|
||||
src/UniTask/UniTask.DOTween.csproj
|
||||
|
||||
src/UniTask/UniTask.TextMeshPro.csproj
|
||||
|
||||
src/UniTask/RuntimeUnitTestToolkit.Player.csproj
|
||||
|
||||
src/UniTask/TempAsm.Player.csproj
|
||||
|
||||
src/UniTask/UniTask.Addressables.Player.csproj
|
||||
|
||||
src/UniTask/UniTask.DOTween.Player.csproj
|
||||
|
||||
src/UniTask/UniTask.Linq.Player.csproj
|
||||
|
||||
src/UniTask/UniTask.Player.csproj
|
||||
|
||||
src/UniTask/UniTask.Tests.Player.csproj
|
||||
|
||||
src/UniTask/UniTask.TextMeshPro.Player.csproj
|
||||
|
||||
src/UniTask/Unity.Addressables.Player.csproj
|
||||
|
||||
src/UniTask/Unity.Analytics.DataPrivacy.Player.csproj
|
||||
|
||||
src/UniTask/Unity.ResourceManager.Player.csproj
|
||||
|
||||
src/UniTask/Unity.ScriptableBuildPipeline.Player.csproj
|
||||
|
||||
src/UniTask/Unity.TextMeshPro.Player.csproj
|
||||
|
||||
src/UniTask/Unity.Timeline.Player.csproj
|
||||
|
||||
src/UniTask/UnityEngine.Advertisements.Player.csproj
|
||||
|
||||
src/UniTask/UnityEngine.Monetization.Player.csproj
|
||||
|
||||
src/UniTask/UnityEngine.TestRunner.Player.csproj
|
||||
|
||||
src/UniTask/UnityEngine.UI.Player.csproj
|
||||
|
||||
src/UniTask/DOTween.Modules.Player.csproj
|
||||
|
||||
src/UniTask/Assembly-CSharp.Player.csproj
|
||||
# Unity
|
||||
.vsconfig
|
||||
src/UniTask/Library/*
|
||||
src/UniTask/Temp/*
|
||||
src/UniTask/Logs/*
|
||||
src/UniTask/[Uu]ser[Ss]ettings/
|
||||
src/UniTask/*.sln
|
||||
src/UniTask/*.csproj
|
||||
src/UniTask/*.unitypackage
|
||||
!src/UniTask/Packages/
|
||||
|
||||
473
README.md
473
README.md
@@ -1,20 +1,21 @@
|
||||
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 to Unity.
|
||||
Provides an efficient allocation free async/await integration for Unity.
|
||||
|
||||
* Struct based `UniTask<T>` and custom AsyncMethodBuilder to achive zero allocation
|
||||
* All Unity AsyncOperations and Coroutine to awaitable
|
||||
* PlayerLoop based task(`UniTask.Yield`, `UniTask.Delay`, `UniTask.DelayFrame`, etc..) that enable to replace all coroutine operation
|
||||
* Struct based `UniTask<T>` and custom AsyncMethodBuilder to achieve zero allocation
|
||||
* Makes all Unity AsyncOperations and Coroutines awaitable
|
||||
* PlayerLoop based task(`UniTask.Yield`, `UniTask.Delay`, `UniTask.DelayFrame`, etc..) that enable replacing all coroutine operations
|
||||
* MonoBehaviour Message Events and uGUI Events as awaitable/async-enumerable
|
||||
* Completely run on Unity's PlayerLoop so don't use thread and run on WebGL, wasm, etc.
|
||||
* Runs completely on Unity's PlayerLoop so doesn't use threads and runs on WebGL, wasm, etc.
|
||||
* Asynchronous LINQ, with Channel and AsyncReactiveProperty
|
||||
* TaskTracker window to prevent memory leak
|
||||
* TaskTracker window to prevent memory leaks
|
||||
* Highly compatible behaviour with Task/ValueTask/IValueTaskSource
|
||||
|
||||
Techinical details, see blog post: [UniTask v2 — Zero Allocation async/await for Unity, with Asynchronous LINQ
|
||||
](https://medium.com/@neuecc/unitask-v2-zero-allocation-async-await-for-unity-with-asynchronous-linq-1aa9c96aa7dd)
|
||||
For technical details, see blog post: [UniTask v2 — Zero Allocation async/await for Unity, with Asynchronous LINQ
|
||||
](https://medium.com/@neuecc/unitask-v2-zero-allocation-async-await-for-unity-with-asynchronous-linq-1aa9c96aa7dd)
|
||||
For advanced tips, see blog post: [Extends UnityWebRequest via async decorator pattern — Advanced Techniques of UniTask](https://medium.com/@neuecc/extends-unitywebrequest-via-async-decorator-pattern-advanced-techniques-of-unitask-ceff9c5ee846)
|
||||
|
||||
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
||||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
||||
@@ -23,6 +24,7 @@ Techinical details, see blog post: [UniTask v2 — Zero Allocation async/await f
|
||||
- [Getting started](#getting-started)
|
||||
- [Basics of UniTask and AsyncOperation](#basics-of-unitask-and-asyncoperation)
|
||||
- [Cancellation and Exception handling](#cancellation-and-exception-handling)
|
||||
- [Timeout handling](#timeout-handling)
|
||||
- [Progress](#progress)
|
||||
- [PlayerLoop](#playerloop)
|
||||
- [async void vs async UniTaskVoid](#async-void-vs-async-unitaskvoid)
|
||||
@@ -32,6 +34,9 @@ Techinical details, see blog post: [UniTask v2 — Zero Allocation async/await f
|
||||
- [Awaitable Events](#awaitable-events)
|
||||
- [Channel](#channel)
|
||||
- [For Unit Testing](#for-unit-testing)
|
||||
- [ThreadPool limitation](#threadpool-limitation)
|
||||
- [IEnumerator.ToUniTask limitation](#ienumeratortounitask-limitation)
|
||||
- [For UnityEditor](#for-unityeditor)
|
||||
- [Compare with Standard Task API](#compare-with-standard-task-api)
|
||||
- [Pooling Configuration](#pooling-configuration)
|
||||
- [Allocation on Profiler](#allocation-on-profiler)
|
||||
@@ -39,7 +44,6 @@ Techinical details, see blog post: [UniTask v2 — Zero Allocation async/await f
|
||||
- [API References](#api-references)
|
||||
- [UPM Package](#upm-package)
|
||||
- [Install via git URL](#install-via-git-url)
|
||||
- [Install via OpenUPM](#install-via-openupm)
|
||||
- [.NET Core](#net-core)
|
||||
- [License](#license)
|
||||
|
||||
@@ -47,7 +51,7 @@ Techinical details, see blog post: [UniTask v2 — Zero Allocation async/await f
|
||||
|
||||
Getting started
|
||||
---
|
||||
Install via [UPM package](#upm-package) or asset package(`UniTask.*.*.*.unitypackage`) available in [UniTask/releases](https://github.com/Cysharp/UniTask/releases) page.
|
||||
Install via [UPM package](#upm-package) with git reference or asset package(`UniTask.*.*.*.unitypackage`) available in [UniTask/releases](https://github.com/Cysharp/UniTask/releases).
|
||||
|
||||
```csharp
|
||||
// extension awaiter/methods can be used by this namespace
|
||||
@@ -68,7 +72,7 @@ async UniTask<string> DemoAsync()
|
||||
// .ToUniTask accepts progress callback(and all options), Progress.Create is a lightweight alternative of IProgress<T>
|
||||
var asset3 = await Resources.LoadAsync<TextAsset>("baz").ToUniTask(Progress.Create<float>(x => Debug.Log(x)));
|
||||
|
||||
// await frame-based operation like coroutine
|
||||
// await frame-based operation like a coroutine
|
||||
await UniTask.DelayFrame(100);
|
||||
|
||||
// replacement of yield return new WaitForSeconds/WaitForSecondsRealtime
|
||||
@@ -81,8 +85,13 @@ async UniTask<string> DemoAsync()
|
||||
await UniTask.Yield();
|
||||
await UniTask.NextFrame();
|
||||
|
||||
// replacement of WaitForEndOfFrame(same as UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate))
|
||||
// replacement of WaitForEndOfFrame
|
||||
#if UNITY_2023_1_OR_NEWER
|
||||
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))
|
||||
await UniTask.WaitForFixedUpdate();
|
||||
@@ -93,10 +102,10 @@ async UniTask<string> DemoAsync()
|
||||
// special helper of WaitUntil
|
||||
await UniTask.WaitUntilValueChanged(this, x => x.isActive);
|
||||
|
||||
// You can await IEnumerator coroutine
|
||||
// You can await IEnumerator coroutines
|
||||
await FooCoroutineEnumerator();
|
||||
|
||||
// You can await standard task
|
||||
// You can await a standard task
|
||||
await Task.Run(() => 100);
|
||||
|
||||
// Multithreading, run on ThreadPool under this code
|
||||
@@ -118,14 +127,11 @@ async UniTask<string> DemoAsync()
|
||||
var task2 = GetTextAsync(UnityWebRequest.Get("http://bing.com"));
|
||||
var task3 = GetTextAsync(UnityWebRequest.Get("http://yahoo.com"));
|
||||
|
||||
// concurrent async-wait and get result easily by tuple syntax
|
||||
// concurrent async-wait and get results easily by tuple syntax
|
||||
var (google, bing, yahoo) = await UniTask.WhenAll(task1, task2, task3);
|
||||
|
||||
// shorthand of WhenAll, tuple can await directly
|
||||
var (google2, bing2, yahoo2) = await (task1, task2, task3);
|
||||
|
||||
// You can handle timeout easily
|
||||
await GetTextAsync(UnityWebRequest.Get("http://unity.com")).Timeout(TimeSpan.FromMilliseconds(300));
|
||||
|
||||
// return async-value.(or you can use `UniTask`(no result), `UniTaskVoid`(fire and forget)).
|
||||
return (asset as TextAsset)?.text ?? throw new InvalidOperationException("Asset not found");
|
||||
@@ -134,9 +140,9 @@ async UniTask<string> DemoAsync()
|
||||
|
||||
Basics of UniTask and AsyncOperation
|
||||
---
|
||||
UniTask feature rely on C# 7.0([task-like custom async method builder feature](https://github.com/dotnet/roslyn/blob/master/docs/features/task-types.md)) so required Unity version is after `Unity 2018.3`, officialy lower support version is `Unity 2018.4.13f1`.
|
||||
UniTask features rely on C# 7.0([task-like custom async method builder feature](https://github.com/dotnet/roslyn/blob/master/docs/features/task-types.md)) so the required Unity version is after `Unity 2018.3`, the official lowest version supported is `Unity 2018.4.13f1`.
|
||||
|
||||
Why UniTask(custom task-like object) is required? Because Task is too heavy, not matched to Unity threading(single-thread). UniTask does not use thread and SynchronizationContext/ExecutionContext because almost Unity's asynchronous object is automaticaly dispatched by Unity's engine layer. It acquires more fast and more less allocation, completely integrated with Unity.
|
||||
Why is UniTask(custom task-like object) required? Because Task is too heavy and not matched to Unity threading (single-thread). UniTask does not use threads and SynchronizationContext/ExecutionContext because Unity's asynchronous object is automaticaly dispatched by Unity's engine layer. It achieves faster and lower allocation, and is completely integrated with Unity.
|
||||
|
||||
You can await `AsyncOperation`, `ResourceRequest`, `AssetBundleRequest`, `AssetBundleCreateRequest`, `UnityWebRequestAsyncOperation`, `AsyncGPUReadbackRequest`, `IEnumerator` and others when `using Cysharp.Threading.Tasks;`.
|
||||
|
||||
@@ -148,13 +154,13 @@ UniTask provides three pattern of extension methods.
|
||||
* .ToUniTask(IProgress, PlayerLoopTiming, CancellationToken);
|
||||
```
|
||||
|
||||
`WithCancellation` is a simple version of `ToUniTask`, both returns `UniTask`. Details of cancellation, see: [Cancellation and Exception handling](#cancellation-and-exception-handling) section.
|
||||
`WithCancellation` is a simple version of `ToUniTask`, both return `UniTask`. For details of cancellation, see: [Cancellation and Exception handling](#cancellation-and-exception-handling) section.
|
||||
|
||||
> Note: WithCancellation is returned from native timing of PlayerLoop but ToUniTask is returned from specified PlayerLoopTiming. Details of timing, see: [PlayerLoop](#playerloop) section.
|
||||
> Note: await directly is returned from native timing of PlayerLoop but WithCancellation and ToUniTask are returned from specified PlayerLoopTiming. For details of timing, see: [PlayerLoop](#playerloop) section.
|
||||
|
||||
> Note: AssetBundleRequest has `asset` and `allAssets`, in default await returns `asset`. If you want to get `allAssets`, you can use `AwaitForAllAssets()` method.
|
||||
> Note: AssetBundleRequest has `asset` and `allAssets`, default await returns `asset`. If you want to get `allAssets`, you can use `AwaitForAllAssets()` method.
|
||||
|
||||
The type of `UniTask` can use utility like `UniTask.WhenAll`, `UniTask.WhenAny`. It is like Task.WhenAll/WhenAny but return type is more useful, returns value tuple so can deconsrtuct each result and pass multiple type.
|
||||
The type of `UniTask` can use utilities like `UniTask.WhenAll`, `UniTask.WhenAny`, `UniTask.WhenEach`. They are like `Task.WhenAll`/`Task.WhenAny` but the return type is more useful. They return value tuples so you can deconstruct each result and pass multiple types.
|
||||
|
||||
```csharp
|
||||
public async UniTaskVoid LoadManyAsync()
|
||||
@@ -173,7 +179,7 @@ async UniTask<Sprite> LoadAsSprite(string path)
|
||||
}
|
||||
```
|
||||
|
||||
If you want to convert callback to UniTask, you can use `UniTaskCompletionSource<T>` that is the lightweight edition of `TaskCompletionSource<T>`.
|
||||
If you want to convert a callback to UniTask, you can use `UniTaskCompletionSource<T>` which is a lightweight edition of `TaskCompletionSource<T>`.
|
||||
|
||||
```csharp
|
||||
public UniTask<int> WrapByUniTaskCompletionSource()
|
||||
@@ -190,7 +196,7 @@ public UniTask<int> WrapByUniTaskCompletionSource()
|
||||
|
||||
You can convert Task -> UniTask: `AsUniTask`, `UniTask` -> `UniTask<AsyncUnit>`: `AsAsyncUnitUniTask`, `UniTask<T>` -> `UniTask`: `AsUniTask`. `UniTask<T>` -> `UniTask`'s conversion cost is free.
|
||||
|
||||
If you want to convert async to coroutine, you can use `.ToCoroutine()`, this is useful to use only allow coroutine system.
|
||||
If you want to convert async to coroutine, you can use `.ToCoroutine()`, this is useful if you want to only allow using the coroutine system.
|
||||
|
||||
UniTask can not await twice. This is a similar constraint to the [ValueTask/IValueTaskSource](https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.valuetask-1?view=netcore-3.1) introduced in .NET Standard 2.1.
|
||||
|
||||
@@ -209,11 +215,13 @@ await task;
|
||||
await task; // NG, throws Exception
|
||||
```
|
||||
|
||||
Store to the class field, you can use `UniTask.Lazy` that gurantee call multipletimes. `.Preserve()` allows for multiple calls (internally cached results). This is useful when multiple calls in a function scope.
|
||||
Store to the class field, you can use `UniTask.Lazy` that supports calling multiple times. `.Preserve()` allows for multiple calls (internally cached results). This is useful when there are multiple calls in a function scope.
|
||||
|
||||
Also `UniTaskCompletionSource` can await multiple times and await from many callers.
|
||||
|
||||
Cancellation and Exception handling
|
||||
---
|
||||
Some UniTask factory methods have `CancellationToken cancellationToken = default` parameter. Andalso some async operation for unity have `WithCancellation(CancellationToken)` and `ToUniTask(..., CancellationToken cancellation = default)` extension methods.
|
||||
Some UniTask factory methods have a `CancellationToken cancellationToken = default` parameter. Also some async operations for Unity have `WithCancellation(CancellationToken)` and `ToUniTask(..., CancellationToken cancellation = default)` extension methods.
|
||||
|
||||
You can pass `CancellationToken` to parameter by standard [`CancellationTokenSource`](https://docs.microsoft.com/en-us/dotnet/api/system.threading.cancellationtokensource).
|
||||
|
||||
@@ -230,18 +238,66 @@ await UnityWebRequest.Get("http://google.co.jp").SendWebRequest().WithCancellati
|
||||
await UniTask.DelayFrame(1000, cancellationToken: cts.Token);
|
||||
```
|
||||
|
||||
CancellationToken can create by `CancellationTokenSource` or MonoBehaviour's extension method `GetCancellationTokenOnDestroy`.
|
||||
CancellationToken can be created by `CancellationTokenSource` or MonoBehaviour's extension method `GetCancellationTokenOnDestroy`.
|
||||
|
||||
```csharp
|
||||
// this CancellationToken lifecycle is same as GameObject.
|
||||
await UniTask.DelayFrame(1000, cancellationToken: this.GetCancellationTokenOnDestroy());
|
||||
```
|
||||
|
||||
When detect cancellation, all methods throws `OperationCanceledException` and propagate to upstream. `OperationCanceledException` is special exception, if not handled this exception, finally it is propagated to `UniTaskScheduler.UnobservedTaskException`.
|
||||
For propagate Cancellation, all async method recommend to accept `CancellationToken cancellationToken` at last argument, and pass `CancellationToken` from root to end.
|
||||
|
||||
Default behaviour of received unhandled exception is write log as exception. Log level can change by `UniTaskScheduler.UnobservedExceptionWriteLogType`. If you want to change custom beavhiour, set action to `UniTaskScheduler.UnobservedTaskException.`
|
||||
```csharp
|
||||
await FooAsync(this.GetCancellationTokenOnDestroy());
|
||||
|
||||
If you want to cancel behaviour in async UniTask method, throws `OperationCanceledException` manually.
|
||||
// ---
|
||||
|
||||
async UniTask FooAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
await BarAsync(cancellationToken);
|
||||
}
|
||||
|
||||
async UniTask BarAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
await UniTask.Delay(TimeSpan.FromSeconds(3), cancellationToken);
|
||||
}
|
||||
```
|
||||
|
||||
`CancellationToken` means lifecycle of async. You can hold your own lifecycle insteadof default CancellationTokenOnDestroy.
|
||||
|
||||
```csharp
|
||||
public class MyBehaviour : MonoBehaviour
|
||||
{
|
||||
CancellationTokenSource disableCancellation = new CancellationTokenSource();
|
||||
CancellationTokenSource destroyCancellation = new CancellationTokenSource();
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (disableCancellation != null)
|
||||
{
|
||||
disableCancellation.Dispose();
|
||||
}
|
||||
disableCancellation = new CancellationTokenSource();
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
disableCancellation.Cancel();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
destroyCancellation.Cancel();
|
||||
destroyCancellation.Dispose();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When cancellation is detected, all methods throw `OperationCanceledException` and propagate upstream. When exception(not limited to `OperationCanceledException`) is not handled in async method, it is propagated finally to `UniTaskScheduler.UnobservedTaskException`. The default behaviour of received unhandled exception is to write log as exception. Log level can be changed using `UniTaskScheduler.UnobservedExceptionWriteLogType`. If you want to use custom behaviour, set an action to `UniTaskScheduler.UnobservedTaskException.`
|
||||
|
||||
And also `OperationCanceledException` is a special exception, this is silently ignored at `UnobservedTaskException`.
|
||||
|
||||
If you want to cancel behaviour in an async UniTask method, throw `OperationCanceledException` manually.
|
||||
|
||||
```csharp
|
||||
public async UniTask<int> FooAsync()
|
||||
@@ -251,7 +307,7 @@ public async UniTask<int> FooAsync()
|
||||
}
|
||||
```
|
||||
|
||||
If you handle exception but want to ignore(propagete to global cancellation handling), use exception filter.
|
||||
If you handle an exception but want to ignore(propagate to global cancellation handling), use an exception filter.
|
||||
|
||||
```csharp
|
||||
public async UniTask<int> BarAsync()
|
||||
@@ -261,14 +317,14 @@ public async UniTask<int> BarAsync()
|
||||
var x = await FooAsync();
|
||||
return x * 2;
|
||||
}
|
||||
catch (Exception ex) when (!(ex is OperationCanceledException))
|
||||
catch (Exception ex) when (!(ex is OperationCanceledException)) // when (ex is not OperationCanceledException) at C# 9.0
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
throws/catch `OperationCanceledException` is slightly heavy, if you want to care performance, use `UniTask.SuppressCancellationThrow` to avoid OperationCanceledException throw. It returns `(bool IsCanceled, T Result)` instead of throw.
|
||||
throws/catch `OperationCanceledException` is slightly heavy, so if performance is a concern, use `UniTask.SuppressCancellationThrow` to avoid OperationCanceledException throw. It returns `(bool IsCanceled, T Result)` instead of throwing.
|
||||
|
||||
```csharp
|
||||
var (isCanceled, _) = await UniTask.DelayFrame(10, cancellationToken: cts.Token).SuppressCancellationThrow();
|
||||
@@ -278,11 +334,117 @@ if (isCanceled)
|
||||
}
|
||||
```
|
||||
|
||||
Note: Only suppress throws if you call it directly into the most source method. Otherwise, the return value will be converted, but the entire pipeline will not be suppressed throws.
|
||||
Note: Only suppress throws if you call directly into the most source method. Otherwise, the return value will be converted, but the entire pipeline will not suppress throws.
|
||||
|
||||
Some features that use Unity's player loop, such as `UniTask.Yield` and `UniTask.Delay` etc, determines CancellationToken state on the player loop.
|
||||
This means it does not cancel immediately upon `CancellationToken` fired.
|
||||
|
||||
If you want to change this behaviour, the cancellation to be immediate, set the `cancelImmediately` flag as an argument.
|
||||
|
||||
```csharp
|
||||
await UniTask.Yield(cancellationToken, cancelImmediately: true);
|
||||
```
|
||||
|
||||
Note: Setting `cancelImmediately` to true and detecting an immediate cancellation is more costly than the default behavior.
|
||||
This is because it uses `CancellationToken.Register`; it is heavier than checking CancellationToken on the player loop.
|
||||
|
||||
Timeout handling
|
||||
---
|
||||
Timeout is a variation of cancellation. You can set timeout by `CancellationTokenSouce.CancelAfterSlim(TimeSpan)` and pass CancellationToken to async methods.
|
||||
|
||||
```csharp
|
||||
var cts = new CancellationTokenSource();
|
||||
cts.CancelAfterSlim(TimeSpan.FromSeconds(5)); // 5sec timeout.
|
||||
|
||||
try
|
||||
{
|
||||
await UnityWebRequest.Get("http://foo").SendWebRequest().WithCancellation(cts.Token);
|
||||
}
|
||||
catch (OperationCanceledException ex)
|
||||
{
|
||||
if (ex.CancellationToken == cts.Token)
|
||||
{
|
||||
UnityEngine.Debug.Log("Timeout");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> `CancellationTokenSouce.CancelAfter` is a standard api. However in Unity you should not use it because it depends threading timer. `CancelAfterSlim` is UniTask's extension methods, it uses PlayerLoop instead.
|
||||
|
||||
If you want to use timeout with other source of cancellation, use `CancellationTokenSource.CreateLinkedTokenSource`.
|
||||
|
||||
```csharp
|
||||
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.");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Optimize for reduce allocation of CancellationTokenSource for timeout per call async method, you can use UniTask's `TimeoutController`.
|
||||
|
||||
```csharp
|
||||
TimeoutController timeoutController = new TimeoutController(); // setup to field for reuse.
|
||||
|
||||
async UniTask FooAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
// you can pass timeoutController.Timeout(TimeSpan) to cancellationToken.
|
||||
await UnityWebRequest.Get("http://foo").SendWebRequest()
|
||||
.WithCancellation(timeoutController.Timeout(TimeSpan.FromSeconds(5)));
|
||||
timeoutController.Reset(); // call Reset(Stop timeout timer and ready for reuse) when succeed.
|
||||
}
|
||||
catch (OperationCanceledException ex)
|
||||
{
|
||||
if (timeoutController.IsTimeout())
|
||||
{
|
||||
UnityEngine.Debug.Log("timeout");
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If you want to use timeout with other source of cancellation, use `new TimeoutController(CancellationToken)`.
|
||||
|
||||
```csharp
|
||||
TimeoutController timeoutController;
|
||||
CancellationTokenSource clickCancelSource;
|
||||
|
||||
void Start()
|
||||
{
|
||||
this.clickCancelSource = new CancellationTokenSource();
|
||||
this.timeoutController = new TimeoutController(clickCancelSource);
|
||||
}
|
||||
```
|
||||
|
||||
Note: UniTask has `.Timeout`, `.TimeoutWithoutException` methods however, if possible, do not use these, please pass `CancellationToken`. Because `.Timeout` work from external of task, can not stop timeoutted task. `.Timeout` means ignore result when timeout. If you pass a `CancellationToken` to the method, it will act from inside of the task, so it is possible to stop a running task.
|
||||
|
||||
Progress
|
||||
---
|
||||
Some async operation for unity have `ToUniTask(IProgress<float> progress = null, ...)` extension methods.
|
||||
Some async operations for unity have `ToUniTask(IProgress<float> progress = null, ...)` extension methods.
|
||||
|
||||
```csharp
|
||||
var progress = Progress.Create<float>(x => Debug.Log(x));
|
||||
@@ -292,9 +454,9 @@ var request = await UnityWebRequest.Get("http://google.co.jp")
|
||||
.ToUniTask(progress: progress);
|
||||
```
|
||||
|
||||
You should not use standard `new System.Progress<T>`, because it causes allocation every times. Use `Cysharp.Threading.Tasks.Progress` instead. This progress factory has two methods, `Create` and `CreateOnlyValueChanged`. `CreateOnlyValueChanged` calls only when progress value changed.
|
||||
You should not use standard `new System.Progress<T>`, because it causes allocation every time. Use `Cysharp.Threading.Tasks.Progress` instead. This progress factory has two methods, `Create` and `CreateOnlyValueChanged`. `CreateOnlyValueChanged` calls only when the progress value has changed.
|
||||
|
||||
Implements IProgress interface to caller is more better, there is no allocation of lambda.
|
||||
Implementing IProgress interface to caller is better as there is no lambda allocation.
|
||||
|
||||
```csharp
|
||||
public class Foo : MonoBehaviour, IProgress<float>
|
||||
@@ -315,7 +477,7 @@ public class Foo : MonoBehaviour, IProgress<float>
|
||||
|
||||
PlayerLoop
|
||||
---
|
||||
UniTask is run on custom [PlayerLoop](https://docs.unity3d.com/ScriptReference/LowLevel.PlayerLoop.html). UniTask's playerloop based method(such as `Delay`, `DelayFrame`, `asyncOperation.ToUniTask`, etc...) accepts this `PlayerLoopTiming`.
|
||||
UniTask is run on a custom [PlayerLoop](https://docs.unity3d.com/ScriptReference/LowLevel.PlayerLoop.html). UniTask's playerloop based methods (such as `Delay`, `DelayFrame`, `asyncOperation.ToUniTask`, etc...) accept this `PlayerLoopTiming`.
|
||||
|
||||
```csharp
|
||||
public enum PlayerLoopTiming
|
||||
@@ -340,28 +502,40 @@ public enum PlayerLoopTiming
|
||||
|
||||
PostLateUpdate = 12,
|
||||
LastPostLateUpdate = 13
|
||||
|
||||
#if UNITY_2020_2_OR_NEWER
|
||||
TimeUpdate = 14,
|
||||
LastTimeUpdate = 15,
|
||||
#endif
|
||||
}
|
||||
```
|
||||
|
||||
It indicates when to run, you can check [PlayerLoopList.md](https://gist.github.com/neuecc/bc3a1cfd4d74501ad057e49efcd7bdae) to Unity's default playerloop and injected UniTask's custom loop.
|
||||
|
||||
`PlayerLoopTiming.Update` is similar as `yield return null` in coroutine, but it is called before Update(Update and uGUI events(button.onClick, etc...) are called on `ScriptRunBehaviourUpdate`, yield return null is called on `ScriptRunDelayedDynamicFrameRate`). `PlayerLoopTiming.FixedUpdate` is similar as `WaitForFixedUpdate`, `PlayerLoopTiming.LastPostLateUpdate` is similar as `WaitForEndOfFrame` in coroutine.
|
||||
`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`.
|
||||
|
||||
`yield return null` and `UniTask.Yield` is similar but different. `yield return null` always return next frame but `UniTask.Yield` return next called, that is, call `UniTask.Yield(PlayerLoopTiming.Update)` on `PreUpdate`, it returns same frame. `UniTask.NextFrame()` gurantees return next frame, this would be expected to behave exactly the same as `yield return null`.
|
||||
> `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`.
|
||||
|
||||
> UniTask.Yield(without CancellationToken) is a special type, returns `YieldAwaitable` and run on YieldRunner. It is most lightweight and faster.
|
||||
`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`.
|
||||
|
||||
AsyncOperation is returned from native timing. For example, await `SceneManager.LoadSceneAsync` is returned from `EarlyUpdate.UpdatePreloading` and after called, loaded scene's `Start` called from `EarlyUpdate.ScriptRunDelayedStartupFrame`. Also `await UnityWebRequest` is returned from `EarlyUpdate.ExecuteMainThreadJobs`.
|
||||
> UniTask.Yield(without CancellationToken) is a special type, returns `YieldAwaitable` and runs on YieldRunner. It is the most lightweight and fastest.
|
||||
|
||||
In UniTask, await directly and `WithCancellation` use native timing, `ToUniTask` use specified timing. This is usually not a particular problem, but with `LoadSceneAsync`, causes different order of Start and continuation after await. so recommend not to use `LoadSceneAsync.ToUniTask`.
|
||||
`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 stacktrace, you can check where is running in playerloop.
|
||||
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`.
|
||||
|
||||
> Note: When using Unity 2023.1 or newer, ensure you have `using UnityEngine;` in the using statements of your file when working with new `UnityEngine.Awaitable` methods like `SceneManager.LoadSceneAsync`.
|
||||
> This prevents compilation errors by avoiding the use of the `UnityEngine.AsyncOperation` version.
|
||||
|
||||
In the stacktrace, you can check where it is running in playerloop.
|
||||
|
||||

|
||||
|
||||
In default, UniTask's PlayerLoop is initialized at `[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]`.
|
||||
By default, UniTask's PlayerLoop is initialized at `[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]`.
|
||||
|
||||
The order in which methods are called in BeforeSceneLoad is indeterminate, so if you want to use UniTask in other BeforeSceneLoad methods, you should try to initialize it before this.
|
||||
The order in which methods are called in BeforeSceneLoad is nondeterministic, so if you want to use UniTask in other BeforeSceneLoad methods, you should try to initialize it before this.
|
||||
|
||||
```csharp
|
||||
// AfterAssembliesLoaded is called before BeforeSceneLoad
|
||||
@@ -373,9 +547,9 @@ public static void InitUniTaskLoop()
|
||||
}
|
||||
```
|
||||
|
||||
If you import Unity's `Entities` package, that reset custom player loop to default at `BeforeSceneLoad` and inject ECS's loop. When Unity call ECS's inject method after UniTask's initialize method, UniTask will no longer work.
|
||||
If you import Unity's `Entities` package, that resets the custom player loop to default at `BeforeSceneLoad` and injects ECS's loop. When Unity calls ECS's inject method after UniTask's initialize method, UniTask will no longer work.
|
||||
|
||||
To solve this issue, you can re-initialize UniTask PlayerLoop after ECS initialized.
|
||||
To solve this issue, you can re-initialize the UniTask PlayerLoop after ECS is initialized.
|
||||
|
||||
```csharp
|
||||
// Get ECS Loop.
|
||||
@@ -385,7 +559,7 @@ var playerLoop = ScriptBehaviourUpdateOrder.CurrentPlayerLoop;
|
||||
PlayerLoopHelper.Initialize(ref playerLoop);
|
||||
```
|
||||
|
||||
You can diagnostic UniTask's player loop is ready by `PlayerLoopHelper.IsInjectedUniTaskPlayerLoop()`. And also `PlayerLoopHelper.DumpCurrentPlayerLoop` shows current all playerloop to console.
|
||||
You can diagnose whether UniTask's player loop is ready by calling `PlayerLoopHelper.IsInjectedUniTaskPlayerLoop()`. And also `PlayerLoopHelper.DumpCurrentPlayerLoop` logs all current playerloops to console.
|
||||
|
||||
```csharp
|
||||
void Start()
|
||||
@@ -395,9 +569,40 @@ void Start()
|
||||
}
|
||||
```
|
||||
|
||||
You can optimize loop cost slightly by remove unuse PlayerLoopTiming injection. You can call `PlayerLoopHelper.Initialize(InjectPlayerLoopTimings)` on initialize.
|
||||
|
||||
```csharp
|
||||
var loop = PlayerLoop.GetCurrentPlayerLoop();
|
||||
PlayerLoopHelper.Initialize(ref loop, InjectPlayerLoopTimings.Minimum); // minimum is Update | FixedUpdate | LastPostLateUpdate
|
||||
```
|
||||
|
||||
`InjectPlayerLoopTimings` has three preset, `All` and `Standard`(All without last except LastPostLateUpdate), `Minimum`(`Update | FixedUpdate | LastPostLateUpdate`). Default is All and you can combine custom inject timings like `InjectPlayerLoopTimings.Update | InjectPlayerLoopTimings.FixedUpdate | InjectPlayerLoopTimings.PreLateUpdate`.
|
||||
|
||||
You can make error to use uninjected `PlayerLoopTiming` by [Microsoft.CodeAnalysis.BannedApiAnalyzers](https://github.com/dotnet/roslyn-analyzers/blob/master/src/Microsoft.CodeAnalysis.BannedApiAnalyzers/BannedApiAnalyzers.Help.md). For example, you can setup `BannedSymbols.txt` like this for `InjectPlayerLoopTimings.Minimum`.
|
||||
|
||||
```txt
|
||||
F:Cysharp.Threading.Tasks.PlayerLoopTiming.Initialization; Isn't injected this PlayerLoop in this project.
|
||||
F:Cysharp.Threading.Tasks.PlayerLoopTiming.LastInitialization; Isn't injected this PlayerLoop in this project.
|
||||
F:Cysharp.Threading.Tasks.PlayerLoopTiming.EarlyUpdate; Isn't injected this PlayerLoop in this project.
|
||||
F:Cysharp.Threading.Tasks.PlayerLoopTiming.LastEarlyUpdate; Isn't injected this PlayerLoop in this project.d
|
||||
F:Cysharp.Threading.Tasks.PlayerLoopTiming.LastFixedUpdate; Isn't injected this PlayerLoop in this project.
|
||||
F:Cysharp.Threading.Tasks.PlayerLoopTiming.PreUpdate; Isn't injected this PlayerLoop in this project.
|
||||
F:Cysharp.Threading.Tasks.PlayerLoopTiming.LastPreUpdate; Isn't injected this PlayerLoop in this project.
|
||||
F:Cysharp.Threading.Tasks.PlayerLoopTiming.LastUpdate; Isn't injected this PlayerLoop in this project.
|
||||
F:Cysharp.Threading.Tasks.PlayerLoopTiming.PreLateUpdate; Isn't injected this PlayerLoop in this project.
|
||||
F:Cysharp.Threading.Tasks.PlayerLoopTiming.LastPreLateUpdate; Isn't injected this PlayerLoop in this project.
|
||||
F:Cysharp.Threading.Tasks.PlayerLoopTiming.PostLateUpdate; Isn't injected this PlayerLoop in this project.
|
||||
F:Cysharp.Threading.Tasks.PlayerLoopTiming.TimeUpdate; Isn't injected this PlayerLoop in this project.
|
||||
F:Cysharp.Threading.Tasks.PlayerLoopTiming.LastTimeUpdate; Isn't injected this PlayerLoop in this project.
|
||||
```
|
||||
|
||||
You can configure `RS0030` severity to error.
|
||||
|
||||

|
||||
|
||||
async void vs async UniTaskVoid
|
||||
---
|
||||
`async void` is a standard C# task system so does not run on UniTask systems. It is better not to use. `async UniTaskVoid` is a lightweight version of `async UniTask` because it does not have awaitable completion and report error immediately to `UniTaskScheduler.UnobservedTaskException`. If you don't require to await it(fire and forget), use `UniTaskVoid` is better. Unfortunately to dismiss warning, require to using with `Forget()`.
|
||||
`async void` is a standard C# task system so it does not run on UniTask systems. It is better not to use it. `async UniTaskVoid` is a lightweight version of `async UniTask` because it does not have awaitable completion and reports errors immediately to `UniTaskScheduler.UnobservedTaskException`. If you don't require awaiting (fire and forget), using `UniTaskVoid` is better. Unfortunately to dismiss warning, you're required to call `Forget()`.
|
||||
|
||||
```csharp
|
||||
public async UniTaskVoid FireAndForgetMethod()
|
||||
@@ -412,7 +617,7 @@ public void Caller()
|
||||
}
|
||||
```
|
||||
|
||||
Also UniTask have `Forget` method, it is similar with UniTaskVoid and same effects with it. However still UniTaskVoid is more efficient if completely do not use await。
|
||||
Also UniTask has the `Forget` method, it is similar to `UniTaskVoid` and has the same effects. However `UniTaskVoid` is more efficient if you completely don't use `await`。
|
||||
|
||||
```csharp
|
||||
public async UniTask DoAsync()
|
||||
@@ -427,7 +632,7 @@ public void Caller()
|
||||
}
|
||||
```
|
||||
|
||||
Using async lambda in register event, it is used `async void`. To avoid it, you can use `UniTask.Action` or `UniTask.UnityAction` that creates delegate via `async UniTaskVoid` lambda.
|
||||
To use an async lambda registered to an event, don't use `async void`. Instead you can use `UniTask.Action` or `UniTask.UnityAction`, both of which create a delegate via `async UniTaskVoid` lambda.
|
||||
|
||||
```csharp
|
||||
Action actEvent;
|
||||
@@ -442,7 +647,7 @@ actEvent += UniTask.Action(async () => { await UniTask.Yield(); });
|
||||
unityEvent += UniTask.UnityAction(async () => { await UniTask.Yield(); });
|
||||
```
|
||||
|
||||
`UniTaskVoid` can also use in MonoBehaviour's `Start` method.
|
||||
`UniTaskVoid` can also be used in MonoBehaviour's `Start` method.
|
||||
|
||||
```csharp
|
||||
class Sample : MonoBehaviour
|
||||
@@ -456,7 +661,7 @@ class Sample : MonoBehaviour
|
||||
|
||||
UniTaskTracker
|
||||
---
|
||||
useful for check(leak) UniTasks. You can open tracker window in `Window -> UniTask Tracker`.
|
||||
useful for checking (leaked) UniTasks. You can open tracker window in `Window -> UniTask Tracker`.
|
||||
|
||||

|
||||
|
||||
@@ -466,15 +671,16 @@ useful for check(leak) UniTasks. You can open tracker window in `Window -> UniTa
|
||||
* Enable Tracking(Toggle) - Start to track async/await UniTask. Performance impact: low.
|
||||
* Enable StackTrace(Toggle) - Capture StackTrace when task is started. Performance impact: high.
|
||||
|
||||
For debug use, enable tracking and capture stacktrace is useful but it it decline performance. Recommended usage is enable both to find task leak, and when done, finally disable both.
|
||||
UniTaskTracker is intended for debugging use only as enabling tracking and capturing stacktraces is useful but has a heavy performance impact. Recommended usage is to enable both tracking and stacktraces to find task leaks and to disable them both when done.
|
||||
|
||||
External Assets
|
||||
---
|
||||
In default, UniTask supports TextMeshPro(`BindTo(TMP_Text)` and `TMP_InputField` event extensions like standard uGUI `InputField`), DOTween(`Tween` as awaitable) and Addressables(`AsyncOperationHandle` and `AsyncOpereationHandle<T>` as awaitable).
|
||||
By default, UniTask supports TextMeshPro(`BindTo(TMP_Text)` and `TMP_InputField` event extensions like standard uGUI `InputField`), DOTween(`Tween` as awaitable) and Addressables(`AsyncOperationHandle` and `AsyncOperationHandle<T>` as awaitable).
|
||||
|
||||
There are defined in separated asmdef 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 import there package from package manager. However DOTween support, require to `com.demigiant.dotween` import from [OpenUPM](https://openupm.com/packages/com.demigiant.dotween/) or define `UNITASK_DOTWEEN_SUPPORT` to enable it.
|
||||
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
|
||||
// sequential
|
||||
@@ -489,15 +695,15 @@ await UniTask.WhenAll(
|
||||
transform.DOScale(10, 3).WithCancellation(ct));
|
||||
```
|
||||
|
||||
DOTween support's default behaviour(`await`, `WithCancellation`, `ToUniTask`) awaits tween is killed. It works both Complete(true/false) and Kill(true/false). But if you want to tween reuse(`SetAutoKill(false)`), it does not work you expected. Or, if you want to await for another timing, the following extension methods exist in Tween, `AwaitForComplete`, `AwaitForPause`, `AwaitForPlay`, `AwaitForRewind`, `AwaitForStepComplete`.
|
||||
DOTween support's default behaviour(`await`, `WithCancellation`, `ToUniTask`) awaits tween is killed. It works on both Complete(true/false) and Kill(true/false). But if you want to reuse tweens (`SetAutoKill(false)`), it does not work as expected. If you want to await for another timing, the following extension methods exist in Tween, `AwaitForComplete`, `AwaitForPause`, `AwaitForPlay`, `AwaitForRewind`, `AwaitForStepComplete`.
|
||||
|
||||
AsyncEnumerable and Async LINQ
|
||||
---
|
||||
Unity 2020.2.0a12 supports C# 8.0 so you can use `await foreach`. This is the new Update notation in async era.
|
||||
Unity 2020.2 supports C# 8.0 so you can use `await foreach`. This is the new Update notation in the async era.
|
||||
|
||||
```csharp
|
||||
// Unity 2020.2.0a12, C# 8.0
|
||||
await foreach (var _ in UniTaskAsyncEnumerable.EveryUpdate(token))
|
||||
// Unity 2020.2, C# 8.0
|
||||
await foreach (var _ in UniTaskAsyncEnumerable.EveryUpdate().WithCancellation(token))
|
||||
{
|
||||
Debug.Log("Update() " + Time.frameCount);
|
||||
}
|
||||
@@ -507,10 +713,23 @@ In a C# 7.3 environment, you can use the `ForEachAsync` method to work in almost
|
||||
|
||||
```csharp
|
||||
// C# 7.3(Unity 2018.3~)
|
||||
await UniTaskAsyncEnumerable.EveryUpdate(token).ForEachAsync(_ =>
|
||||
await UniTaskAsyncEnumerable.EveryUpdate().ForEachAsync(_ =>
|
||||
{
|
||||
Debug.Log("Update() " + Time.frameCount);
|
||||
});
|
||||
}, token);
|
||||
```
|
||||
|
||||
`UniTask.WhenEach` that is similar to .NET 9's `Task.WhenEach` can consume new way for await multiple tasks.
|
||||
|
||||
```csharp
|
||||
await foreach (var result in UniTask.WhenEach(task1, task2, task3))
|
||||
{
|
||||
// The result is of type WhenEachResult<T>.
|
||||
// It contains either `T Result` or `Exception Exception`.
|
||||
// You can check `IsCompletedSuccessfully` or `IsFaulted` to determine whether to access `.Result` or `.Exception`.
|
||||
// If you want to throw an exception when `IsFaulted` and retrieve the result when successful, use `GetResult()`.
|
||||
Debug.Log(result.GetResult());
|
||||
}
|
||||
```
|
||||
|
||||
UniTaskAsyncEnumerable implements asynchronous LINQ, similar to LINQ in `IEnumerable<T>` or Rx in `IObservable<T>`. All standard LINQ query operators can be applied to asynchronous streams. For example, the following code shows how to apply a Where filter to a button-click asynchronous stream that runs once every two clicks.
|
||||
@@ -521,7 +740,7 @@ await okButton.OnClickAsAsyncEnumerable().Where((x, i) => i % 2 == 0).ForEachAsy
|
||||
});
|
||||
```
|
||||
|
||||
Fire and Forget style(for example, event handling), also you can use `Subscribe`.
|
||||
Fire and Forget style(for example, event handling), you can also use `Subscribe`.
|
||||
|
||||
```csharp
|
||||
okButton.OnClickAsAsyncEnumerable().Where((x, i) => i % 2 == 0).Subscribe(_ =>
|
||||
@@ -533,7 +752,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.
|
||||
|
||||
`UniTaskAsyncEnumerable` is the entry point like `Enumerbale`. In addition to the standard query operators, there are other generators for Unity such as `EveryUpdate`, `Timer`, `TimerFrame`, `Interval`, `IntervalFrame`, and `EveryValueChanged`. And also added additional UniTask original query operators like `Append`, `Prepend`, `DistinctUntilChanged`, `ToHashSet`, `Buffer`, `CombineLatest`, `Do`, `Never`, `ForEachAsync`, `Pairwise`, `Publish`, `Queue`, `Return`, `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`.
|
||||
|
||||
@@ -545,7 +764,7 @@ SelectAwaitWithCancellation(Func<T, CancellationToken, UniTask<TR>> selector)
|
||||
|
||||
If you want to use the `async` method inside the func, use the `***Await` or `***AwaitWithCancellation`.
|
||||
|
||||
How to create async iterator, C# 8.0 supports async iterator(`async yield return`) but it only allows `IAsyncEnumerable<T>` and of course requires C# 8.0. UniTask supports `UniTaskAsyncEnumerable.Create` method to create custom async iterator.
|
||||
How to create an async iterator: C# 8.0 supports async iterator(`async yield return`) but it only allows `IAsyncEnumerable<T>` and of course requires C# 8.0. UniTask supports `UniTaskAsyncEnumerable.Create` method to create custom async iterator.
|
||||
|
||||
```csharp
|
||||
// IAsyncEnumerable, C# 8.0 version of async iterator. ( do not use this style, IAsyncEnumerable is not controled in UniTask).
|
||||
@@ -621,32 +840,13 @@ async UniTask TripleClick(CancellationToken token)
|
||||
}
|
||||
```
|
||||
|
||||
All MonoBehaviour message events can convert async-streams by `AsyncTriggers` that can enable 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
|
||||
using Cysharp.Threading.Tasks.Triggers;
|
||||
|
||||
async UniTaskVoid MonitorCollision()
|
||||
{
|
||||
await gameObject.OnCollisionEnterAsync();
|
||||
Debug.Log("Collision Enter");
|
||||
/* do anything */
|
||||
|
||||
await gameObject.OnCollisionExitAsync();
|
||||
Debug.Log("Collision Exit");
|
||||
}
|
||||
```
|
||||
|
||||
Similar as uGUI event, AsyncTrigger can get by `GetAsync***Trigger` and trigger it self is UniTaskAsyncEnumerable.
|
||||
|
||||
```csharp
|
||||
// use await multiple times, get AsyncTriggerHandler is more efficient.
|
||||
using(var trigger = this.GetOnCollisionEnterAsyncHandler())
|
||||
{
|
||||
await OnCollisionEnterAsync();
|
||||
await OnCollisionEnterAsync();
|
||||
await OnCollisionEnterAsync();
|
||||
}
|
||||
var trigger = this.GetOnCollisionEnterAsyncHandler();
|
||||
await trigger.OnCollisionEnterAsync();
|
||||
await trigger.OnCollisionEnterAsync();
|
||||
await trigger.OnCollisionEnterAsync();
|
||||
|
||||
// every moves.
|
||||
await this.GetAsyncMoveTrigger().ForEachAsync(axisEventData =>
|
||||
@@ -654,7 +854,7 @@ await this.GetAsyncMoveTrigger().ForEachAsync(axisEventData =>
|
||||
});
|
||||
```
|
||||
|
||||
`AsyncReactiveProperty`, `AsyncReadOnlyReactiveProperty` is UniTask version of UniTask's ReactiveProperty. `BindTo` extension method of `IUniTaskAsyncEnumerable<T>` for binding asynchronous stream values to Unity components(Text/Selectable/TMP/Text).
|
||||
`AsyncReactiveProperty`, `AsyncReadOnlyReactiveProperty` is UniTask's version of ReactiveProperty. `BindTo` extension method of `IUniTaskAsyncEnumerable<T>` for binding asynchronous stream values to Unity components(Text/Selectable/TMP/Text).
|
||||
|
||||
```csharp
|
||||
var rp = new AsyncReactiveProperty<int>(99);
|
||||
@@ -674,9 +874,9 @@ rp.WithoutCurrent().BindTo(this.textComponent);
|
||||
|
||||
await rp.WaitAsync(); // wait until next value set
|
||||
|
||||
// also exists ToReadOnlyReactiveProperty
|
||||
// also exists ToReadOnlyAsyncReactiveProperty
|
||||
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.
|
||||
@@ -689,9 +889,9 @@ await button.OnClickAsAsyncEnumerable().ForEachAwaitAsync(async x =>
|
||||
});
|
||||
```
|
||||
|
||||
It is useful(prevent double-click) but not useful in sometimes.
|
||||
It is useful (prevent double-click) but not useful sometimes.
|
||||
|
||||
Using `Queue()` method, which will also queue events during asynchronous processing.
|
||||
Using the `Queue()` method will also queue events during asynchronous processing.
|
||||
|
||||
```csharp
|
||||
// queued message in asynchronous processing
|
||||
@@ -712,13 +912,13 @@ button.OnClickAsAsyncEnumerable().Subscribe(async x =>
|
||||
|
||||
Channel
|
||||
---
|
||||
`Channel` is same as [System.Threading.Tasks.Channels](https://docs.microsoft.com/ja-jp/dotnet/api/system.threading.channels?view=netcore-3.1) that is similar as GoLang Channel.
|
||||
`Channel` is the same as [System.Threading.Tasks.Channels](https://docs.microsoft.com/en-us/dotnet/api/system.threading.channels?view=netcore-3.1) which is similar to a GoLang Channel.
|
||||
|
||||
Currently only supports multiple-producer, single-consumer unbounded channel. It can create by `Channel.CreateSingleConsumerUnbounded<T>()`.
|
||||
Currently it only supports multiple-producer, single-consumer unbounded channels. It can create by `Channel.CreateSingleConsumerUnbounded<T>()`.
|
||||
|
||||
For producer(`.Writer`), `TryWrite` to push value and `TryComplete` to complete channel. For consumer(`.Reader`), `TryRead`, `WaitToReadAsync`, `ReadAsync`, `Completion` and `ReadAllAsync` to read queued messages.
|
||||
For producer(`.Writer`), use `TryWrite` to push value and `TryComplete` to complete channel. For consumer(`.Reader`), use `TryRead`, `WaitToReadAsync`, `ReadAsync`, `Completion` and `ReadAllAsync` to read queued messages.
|
||||
|
||||
`ReadAllAsync` returns `IUniTaskAsyncEnumerable<T>` so query LINQ operators. Reader only allows single-consumer but use `.Publish()` query operator to enable multicast message. For example, make pub/sub utility.
|
||||
`ReadAllAsync` returns `IUniTaskAsyncEnumerable<T>` so query LINQ operators. Reader only allows single-consumer but uses `.Publish()` query operator to enable multicast message. For example, make pub/sub utility.
|
||||
|
||||
```csharp
|
||||
public class AsyncMessageBroker<T> : IDisposable
|
||||
@@ -755,7 +955,7 @@ public class AsyncMessageBroker<T> : IDisposable
|
||||
|
||||
For Unit Testing
|
||||
---
|
||||
Unity's `[UnityTest]` attribute can test coroutine(IEnumerator) but can not test async. `UniTask.ToCoroutine` bridges async/await to coroutine so you can test async method.
|
||||
Unity's `[UnityTest]` attribute can test coroutine(IEnumerator) but can not test async. `UniTask.ToCoroutine` bridges async/await to coroutine so you can test async methods.
|
||||
|
||||
```csharp
|
||||
[UnityTest]
|
||||
@@ -778,11 +978,34 @@ public IEnumerator DelayIgnore() => UniTask.ToCoroutine(async () =>
|
||||
});
|
||||
```
|
||||
|
||||
UniTask itself's unit test is written by Unity Test Runner and [Cysharp/RuntimeUnitTestToolkit](https://github.com/Cysharp/RuntimeUnitTestToolkit) to check on CI and IL2CPP working.
|
||||
UniTask's own unit tests are written using Unity Test Runner and [Cysharp/RuntimeUnitTestToolkit](https://github.com/Cysharp/RuntimeUnitTestToolkit) to integrate with CI and check if IL2CPP is working.
|
||||
|
||||
ThreadPool limitation
|
||||
---
|
||||
Most UniTask methods run on a single thread (PlayerLoop), with only `UniTask.Run`(`Task.Run` equivalent) and `UniTask.SwitchToThreadPool` running on a thread pool. If you use a thread pool, it won't work with WebGL and so on.
|
||||
|
||||
`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
|
||||
---
|
||||
You can convert coroutine(IEnumerator) to UniTask(or await directly) but it has some limitations.
|
||||
|
||||
* `WaitForEndOfFrame`/`WaitForFixedUpdate`/`Coroutine` is not supported.
|
||||
* Consuming loop timing is not the same as `StartCoroutine`, it uses the specified `PlayerLoopTiming` and the default `PlayerLoopTiming.Update` is run before MonoBehaviour's `Update` and `StartCoroutine`'s loop.
|
||||
|
||||
If you want fully compatible conversion from coroutine to async, use the `IEnumerator.ToUniTask(MonoBehaviour coroutineRunner)` overload. It executes StartCoroutine on an instance of the argument MonoBehaviour and waits for it to complete in UniTask.
|
||||
|
||||
For UnityEditor
|
||||
---
|
||||
UniTask can run on Unity Editor like an Editor Coroutine. However, there are some limitations.
|
||||
|
||||
* UniTask.Delay's DelayType.DeltaTime, UnscaledDeltaTime do not work correctly because they can not get deltaTime in editor. Therefore run on EditMode, automatically change DelayType to `DelayType.Realtime` that wait for the right time.
|
||||
* All PlayerLoopTiming run on the timing `EditorApplication.update`.
|
||||
* `-batchmode` with `-quit` does not work because Unity does not run `EditorApplication.update` and quit after a single frame. Instead, don't use `-quit` and quit manually with `EditorApplication.Exit(0)`.
|
||||
|
||||
Compare with Standard Task API
|
||||
---
|
||||
UniTask has many standard Task-like APIs. This table shows what is the alternative apis.
|
||||
UniTask has many standard Task-like APIs. This table shows what the alternative apis are.
|
||||
|
||||
Use standard type.
|
||||
|
||||
@@ -816,9 +1039,10 @@ Use UniTask type.
|
||||
| `IAsyncDisposable` | `IUniTaskAsyncDisposable` |
|
||||
| `Task.Delay` | `UniTask.Delay` |
|
||||
| `Task.Yield` | `UniTask.Yield` |
|
||||
| `Task.Run` | `UniTask.Run` |
|
||||
| `Task.Run` | `UniTask.RunOnThreadPool` |
|
||||
| `Task.WhenAll` | `UniTask.WhenAll` |
|
||||
| `Task.WhenAny` | `UniTask.WhenAny` |
|
||||
| `Task.WhenEach` | `UniTask.WhenEach` |
|
||||
| `Task.CompletedTask` | `UniTask.CompletedTask` |
|
||||
| `Task.FromException` | `UniTask.FromException` |
|
||||
| `Task.FromResult` | `UniTask.FromResult` |
|
||||
@@ -828,7 +1052,7 @@ Use UniTask type.
|
||||
|
||||
Pooling Configuration
|
||||
---
|
||||
UniTask is aggressively caching async promise object to achive zero allocation. In default, cache all promises but you can configure `TaskPool.SetMaxPoolSize` to your value, the value indicates cache size per type. `TaskPool.GetCacheSizeInfo` returns current cached object in pool.
|
||||
UniTask aggressively caches async promise objects to achieve zero allocation (for technical details, see blog post [UniTask v2 — Zero Allocation async/await for Unity, with Asynchronous LINQ](https://medium.com/@neuecc/unitask-v2-zero-allocation-async-await-for-unity-with-asynchronous-linq-1aa9c96aa7dd)). By default, it caches all promises but you can configure `TaskPool.SetMaxPoolSize` to your value, the value indicates cache size per type. `TaskPool.GetCacheSizeInfo` returns currently cached objects in pool.
|
||||
|
||||
```csharp
|
||||
foreach (var (type, size) in TaskPool.GetCacheSizeInfo())
|
||||
@@ -839,17 +1063,17 @@ foreach (var (type, size) in TaskPool.GetCacheSizeInfo())
|
||||
|
||||
Allocation on Profiler
|
||||
---
|
||||
In UnityEditor profiler shows allocation of compiler generated AsyncStateMachine but it only occurs in debug(development) build. C# Compiler generate AsyncStateMachine as class on Debug build and as struct on Release build.
|
||||
In UnityEditor the profiler shows allocation of compiler generated AsyncStateMachine but it only occurs in debug(development) build. C# Compiler generates AsyncStateMachine as class on Debug build and as struct on Release build.
|
||||
|
||||
After Unity 2020.1 supports Code Optimization option on UnityEditor(right, footer).
|
||||
Unity supports Code Optimization option starting in 2020.1 (right, footer).
|
||||
|
||||

|
||||
|
||||
You can change C# compiler optimization to release, it removes AsyncStateMachine allocation. Andalso optimization option can set via `Compilation.CompilationPipeline-codeOptimization`, and `Compilation.CodeOptimization`.
|
||||
You can change C# compiler optimization to release to remove AsyncStateMachine allocation in development builds. This optimization option can also be set via `Compilation.CompilationPipeline-codeOptimization`, and `Compilation.CodeOptimization`.
|
||||
|
||||
UniTaskSynchronizationContext
|
||||
---
|
||||
Unity's default SynchronizationContext(`UnitySynchronizationContext`) is poor implementation for performance. UniTask itself is bypass `SynchronizationContext`(and `ExecutionContext`) so does not use it but if exists in `async Task`, still used it. `UniTaskSynchronizationContext` is replacement of `UnitySynchronizationContext`, it is better for performance.
|
||||
Unity's default SynchronizationContext(`UnitySynchronizationContext`) is a poor implementation for performance. UniTask bypasses `SynchronizationContext`(and `ExecutionContext`) so it does not use it but if exists in `async Task`, still used it. `UniTaskSynchronizationContext` is a replacement of `UnitySynchronizationContext` which is better for performance.
|
||||
|
||||
```csharp
|
||||
public class SyncContextInjecter
|
||||
@@ -862,19 +1086,19 @@ public class SyncContextInjecter
|
||||
}
|
||||
```
|
||||
|
||||
This is an optional choice and is not always recommended; `UniTaskSynchronizationContext` is less performance than `async UniTask` and is not a complete UniTask replacement. It also does not guarantee full behavioral compatibility with the `UnitySynchronizationContext`.
|
||||
This is an optional choice and is not always recommended; `UniTaskSynchronizationContext` is less performant than `async UniTask` and is not a complete UniTask replacement. It also does not guarantee full behavioral compatibility with the `UnitySynchronizationContext`.
|
||||
|
||||
API References
|
||||
---
|
||||
UniTask's API References is hosted at [cysharp.github.io/UniTask](https://cysharp.github.io/UniTask/api/Cysharp.Threading.Tasks.html) by [DocFX](https://dotnet.github.io/docfx/) and [Cysharp/DocfXTemplate](https://github.com/Cysharp/DocfxTemplate).
|
||||
UniTask's API References are hosted at [cysharp.github.io/UniTask](https://cysharp.github.io/UniTask/api/Cysharp.Threading.Tasks.html) by [DocFX](https://dotnet.github.io/docfx/) and [Cysharp/DocfXTemplate](https://github.com/Cysharp/DocfxTemplate).
|
||||
|
||||
For example, UniTask's factory methods can see at [UniTask#methods](https://cysharp.github.io/UniTask/api/Cysharp.Threading.Tasks.UniTask.html#methods-1). UniTaskAsyncEnumerable's factory/extension methods can see at [UniTaskAsyncEnumerable#methods](https://cysharp.github.io/UniTask/api/Cysharp.Threading.Tasks.Linq.UniTaskAsyncEnumerable.html#methods-1).
|
||||
For example, UniTask's factory methods can be seen at [UniTask#methods](https://cysharp.github.io/UniTask/api/Cysharp.Threading.Tasks.UniTask.html#methods-1). UniTaskAsyncEnumerable's factory/extension methods can be seen at [UniTaskAsyncEnumerable#methods](https://cysharp.github.io/UniTask/api/Cysharp.Threading.Tasks.Linq.UniTaskAsyncEnumerable.html#methods-1).
|
||||
|
||||
UPM Package
|
||||
---
|
||||
### Install via git URL
|
||||
|
||||
After Unity 2019.3.4f1, Unity 2020.1a21, that support path query parameter of git package. You can add `https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask` to Package Manager
|
||||
Requires a version of unity that supports path query parameter for git packages (Unity >= 2019.3.4f1, Unity >= 2020.1a21). You can add `https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask` to Package Manager
|
||||
|
||||

|
||||
|
||||
@@ -882,15 +1106,8 @@ After Unity 2019.3.4f1, Unity 2020.1a21, that support path query parameter of gi
|
||||
|
||||
or add `"com.cysharp.unitask": "https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask"` to `Packages/manifest.json`.
|
||||
|
||||
If you want to set a target version, UniTask is using `*.*.*` release tag so you can specify a version like `#2.0.31`. For example `https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask#2.0.31`.
|
||||
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
|
||||
---
|
||||
@@ -898,11 +1115,11 @@ For .NET Core, use NuGet.
|
||||
|
||||
> PM> Install-Package [UniTask](https://www.nuget.org/packages/UniTask)
|
||||
|
||||
UniTask of .NET Core version is a subset of Unity UniTask, removed PlayerLoop dependent methods.
|
||||
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 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)).
|
||||
|
||||
```csharp
|
||||
public class ZeroAllocAsyncAwaitInDotNetCore
|
||||
@@ -935,7 +1152,7 @@ public ValueTask TestAsync()
|
||||
|
||||
.NET Core version is intended to allow users to use UniTask as an interface when sharing code with Unity (such as [Cysharp/MagicOnion](https://github.com/Cysharp/MagicOnion/)). .NET Core version of UniTask enables smooth code sharing.
|
||||
|
||||
Utility methods such as WhenAll which is equivalent to UniTask are provided as [Cysharp/ValueTaskSupplement](https://github.com/Cysharp/ValueTaskSupplement).
|
||||
Utility methods such as WhenAll which are equivalent to UniTask are provided as [Cysharp/ValueTaskSupplement](https://github.com/Cysharp/ValueTaskSupplement).
|
||||
|
||||
License
|
||||
---
|
||||
|
||||
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
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.29613.14
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.31606.5
|
||||
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
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniTask.NetCore", "src\UniTask.NetCore\UniTask.NetCore.csproj", "{16EE20D0-7FB1-483A-8467-A5EEDBF1F5BF}"
|
||||
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
|
||||
Global
|
||||
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}.Release|Any CPU.ActiveCfg = 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
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -62,7 +62,8 @@ namespace Cysharp.Threading.Tasks
|
||||
sealed class ThreadPoolWorkItem : IThreadPoolWorkItem, ITaskPoolNode<ThreadPoolWorkItem>
|
||||
{
|
||||
static TaskPool<ThreadPoolWorkItem> pool;
|
||||
public ThreadPoolWorkItem NextNode { get; set; }
|
||||
ThreadPoolWorkItem nextNode;
|
||||
public ref ThreadPoolWorkItem NextNode => ref nextNode;
|
||||
|
||||
static ThreadPoolWorkItem()
|
||||
{
|
||||
|
||||
@@ -1,58 +1,43 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netcoreapp3.1;netstandard2.1;netstandard2.0</TargetFrameworks>
|
||||
<AssemblyName>UniTask</AssemblyName>
|
||||
<LangVersion>8.0</LangVersion>
|
||||
<RootNamespace>Cysharp.Threading.Tasks</RootNamespace>
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0;net7.0;netstandard2.1;netstandard2.0</TargetFrameworks>
|
||||
<AssemblyName>UniTask</AssemblyName>
|
||||
<LangVersion>8.0</LangVersion>
|
||||
<RootNamespace>Cysharp.Threading.Tasks</RootNamespace>
|
||||
<DefineConstants>UNITASK_NETCORE</DefineConstants>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<NoWarn>$(NoWarn);CS1591</NoWarn>
|
||||
|
||||
<!-- NuGet Packaging -->
|
||||
<Id>UniTask</Id>
|
||||
<PackageVersion>$(Version)</PackageVersion>
|
||||
<Company>Cysharp</Company>
|
||||
<Authors>Cysharp</Authors>
|
||||
<Copyright>© Cysharp, Inc.</Copyright>
|
||||
<PackageTags>task;async</PackageTags>
|
||||
<Description>Provides an efficient async/await integration to Unity and .NET Core.</Description>
|
||||
<PackageProjectUrl>https://github.com/Cysharp/UniTask</PackageProjectUrl>
|
||||
<RepositoryUrl>$(PackageProjectUrl)</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<PackageIcon>Icon.png</PackageIcon>
|
||||
</PropertyGroup>
|
||||
<!-- NuGet Packaging -->
|
||||
<Id>UniTask</Id>
|
||||
<PackageVersion>$(Version)</PackageVersion>
|
||||
<Company>Cysharp</Company>
|
||||
<Authors>Cysharp</Authors>
|
||||
<Copyright>© Cysharp, Inc.</Copyright>
|
||||
<PackageTags>task;async</PackageTags>
|
||||
<Description>Provides an efficient async/await integration to Unity and .NET Core.</Description>
|
||||
<PackageProjectUrl>https://github.com/Cysharp/UniTask</PackageProjectUrl>
|
||||
<RepositoryUrl>$(PackageProjectUrl)</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<PackageIcon>Icon.png</PackageIcon>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>opensource.snk</AssemblyOriginatorKeyFile>
|
||||
<IsPackable>true</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="Icon.png" Pack="true" PackagePath="/" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Icon.png" Pack="true" PackagePath="/" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\UniTask\Assets\Plugins\UniTask\Runtime\**\*.cs"
|
||||
Exclude="
|
||||
..\UniTask\Assets\Plugins\UniTask\Editor\*.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\Triggers\*.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\Linq\UnityExtensions\*.cs;
|
||||
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\UnityEqualityComparer.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\DiagnosticsExtensions.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\PlayerLoopRunner.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\ContinuationQueue.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\UnityWebRequestExtensions.cs;
|
||||
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\UniTaskSynchronizationContext.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\CancellationTokenSourceExtensions.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\EnumeratorAsyncExtensions.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\PlayerLoopHelper.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.Delay.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.Run.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.Bridge.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.WaitUntil.cs;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\UnityAsyncExtensions.*;
|
||||
..\UniTask\Assets\Plugins\UniTask\Runtime\UnityBindingExtensions.cs;
|
||||
" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\UniTask\Assets\Plugins\UniTask\Runtime\**\*.cs" Exclude="
..\UniTask\Assets\Plugins\UniTask\Editor\*.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\Triggers\*.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\Linq\UnityExtensions\*.cs;
 
..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\UnityEqualityComparer.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\DiagnosticsExtensions.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\PlayerLoopRunner.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\ContinuationQueue.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\UnityWebRequestExtensions.cs;
 
..\UniTask\Assets\Plugins\UniTask\Runtime\UniTaskSynchronizationContext.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\CancellationTokenSourceExtensions.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\EnumeratorAsyncExtensions.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\TimeoutController.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\PlayerLoopHelper.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\PlayerLoopTimer.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.Delay.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.Run.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.Bridge.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.WaitUntil.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\UnityAsyncExtensions.*;
..\UniTask\Assets\Plugins\UniTask\Runtime\UnityBindingExtensions.cs;
" />
|
||||
<Compile Remove="..\UniTask\Assets\Plugins\UniTask\Runtime\_InternalVisibleTo.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
BIN
src/UniTask.NetCore/opensource.snk
Normal file
BIN
src/UniTask.NetCore/opensource.snk
Normal file
Binary file not shown.
@@ -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
|
||||
{
|
||||
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)
|
||||
{
|
||||
#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();
|
||||
}
|
||||
|
||||
#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,396 +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();
|
||||
Queue<Node> q1 = new Queue<Node>();
|
||||
Stack<Node> s1 = new Stack<Node>();
|
||||
ConcurrentQueue<Node> cq = new ConcurrentQueue<Node>();
|
||||
ConcurrentStack<Node> cs = new ConcurrentStack<Node>();
|
||||
static TaskPool<Node> pool;
|
||||
static TaskPoolEqualNull<Node> poolEqualNull;
|
||||
static TaskPoolClass<Node> poolClass = new TaskPoolClass<Node>();
|
||||
static TaskPoolWithoutSize<Node> poolWithoutSize;
|
||||
static TaskPoolWithoutLock<Node> poolWithoutLock;
|
||||
|
||||
[Benchmark]
|
||||
public void Queue()
|
||||
{
|
||||
q1.Enqueue(node1);
|
||||
q1.Enqueue(node1);
|
||||
q1.TryDequeue(out _);
|
||||
q1.TryDequeue(out _);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void QueueLock()
|
||||
{
|
||||
lock (q1) { q1.Enqueue(node1); }
|
||||
lock (q1) { q1.Enqueue(node1); }
|
||||
lock (q1) { q1.TryDequeue(out _); }
|
||||
lock (q1) { q1.TryDequeue(out _); }
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void Stack()
|
||||
{
|
||||
s1.Push(node1);
|
||||
s1.Push(node2);
|
||||
s1.TryPop(out _);
|
||||
s1.TryPop(out _);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void StackLock()
|
||||
{
|
||||
lock (s1) { s1.Push(node1); }
|
||||
lock (s1) { s1.Push(node2); }
|
||||
lock (s1) { s1.TryPop(out _); }
|
||||
lock (s1) { s1.TryPop(out _); }
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void ConcurrentQueue()
|
||||
{
|
||||
cq.Enqueue(node1);
|
||||
cq.Enqueue(node1);
|
||||
cq.TryDequeue(out _);
|
||||
cq.TryDequeue(out _);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void ConcurrentStack()
|
||||
{
|
||||
cs.Push(node1);
|
||||
cs.Push(node2);
|
||||
cs.TryPop(out _);
|
||||
cs.TryPop(out _);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void TaskPool()
|
||||
{
|
||||
pool.TryPush(node1);
|
||||
pool.TryPush(node2);
|
||||
pool.TryPop(out _);
|
||||
pool.TryPop(out _);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void TaskPoolEqualNull()
|
||||
{
|
||||
poolEqualNull.TryPush(node1);
|
||||
poolEqualNull.TryPush(node2);
|
||||
poolEqualNull.TryPop(out _);
|
||||
poolEqualNull.TryPop(out _);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void TaskPoolClass()
|
||||
{
|
||||
poolClass.TryPush(node1);
|
||||
poolClass.TryPush(node2);
|
||||
poolClass.TryPop(out _);
|
||||
poolClass.TryPop(out _);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void TaskPoolWithoutSize()
|
||||
{
|
||||
poolWithoutSize.TryPush(node1);
|
||||
poolWithoutSize.TryPush(node2);
|
||||
poolWithoutSize.TryPop(out _);
|
||||
poolWithoutSize.TryPop(out _);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void TaskPoolWithoutLock()
|
||||
{
|
||||
poolWithoutLock.TryPush(node1);
|
||||
poolWithoutLock.TryPush(node2);
|
||||
poolWithoutLock.TryPop(out _);
|
||||
poolWithoutLock.TryPop(out _);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class Node : ITaskPoolNode<Node>
|
||||
{
|
||||
public Node NextNode { get; set; }
|
||||
}
|
||||
|
||||
public interface ITaskPoolNode<T>
|
||||
{
|
||||
T NextNode { get; set; }
|
||||
}
|
||||
|
||||
// mutable struct, don't mark readonly.
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
public struct TaskPoolWithoutLock<T>
|
||||
where T : class, ITaskPoolNode<T>
|
||||
{
|
||||
int size;
|
||||
T root;
|
||||
|
||||
public int Size => size;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryPop(out T result)
|
||||
{
|
||||
//if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
||||
{
|
||||
var v = root;
|
||||
if (!(v is null))
|
||||
{
|
||||
root = v.NextNode;
|
||||
v.NextNode = null;
|
||||
size--;
|
||||
result = v;
|
||||
// Volatile.Write(ref gate, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
//Volatile.Write(ref gate, 0);
|
||||
}
|
||||
result = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryPush(T item)
|
||||
{
|
||||
//if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
||||
{
|
||||
//if (size < TaskPool.MaxPoolSize)
|
||||
{
|
||||
item.NextNode = root;
|
||||
root = item;
|
||||
size++;
|
||||
// Volatile.Write(ref gate, 0);
|
||||
return true;
|
||||
}
|
||||
//else
|
||||
{
|
||||
// Volatile.Write(ref gate, 0);
|
||||
}
|
||||
}
|
||||
//return false;
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
public struct TaskPool<T>
|
||||
where T : class, ITaskPoolNode<T>
|
||||
{
|
||||
int gate;
|
||||
int size;
|
||||
T root;
|
||||
|
||||
public int Size => size;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryPop(out T result)
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
||||
{
|
||||
var v = root;
|
||||
if (!(v is null))
|
||||
{
|
||||
root = v.NextNode;
|
||||
v.NextNode = null;
|
||||
size--;
|
||||
result = v;
|
||||
Volatile.Write(ref gate, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
Volatile.Write(ref gate, 0);
|
||||
}
|
||||
result = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryPush(T item)
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
||||
{
|
||||
//if (size < TaskPool.MaxPoolSize)
|
||||
{
|
||||
item.NextNode = root;
|
||||
root = item;
|
||||
size++;
|
||||
Volatile.Write(ref gate, 0);
|
||||
return true;
|
||||
}
|
||||
//else
|
||||
{
|
||||
// Volatile.Write(ref gate, 0);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
public struct TaskPoolEqualNull<T>
|
||||
where T : class, ITaskPoolNode<T>
|
||||
{
|
||||
int gate;
|
||||
int size;
|
||||
T root;
|
||||
|
||||
public int Size => size;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryPop(out T result)
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
||||
{
|
||||
var v = root;
|
||||
if (v != null)
|
||||
{
|
||||
root = v.NextNode;
|
||||
v.NextNode = null;
|
||||
size--;
|
||||
result = v;
|
||||
Volatile.Write(ref gate, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
Volatile.Write(ref gate, 0);
|
||||
}
|
||||
result = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryPush(T item)
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
||||
{
|
||||
//if (size < TaskPool.MaxPoolSize)
|
||||
{
|
||||
item.NextNode = root;
|
||||
root = item;
|
||||
size++;
|
||||
Volatile.Write(ref gate, 0);
|
||||
return true;
|
||||
}
|
||||
//else
|
||||
{
|
||||
// Volatile.Write(ref gate, 0);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public class TaskPoolClass<T>
|
||||
where T : class, ITaskPoolNode<T>
|
||||
{
|
||||
int gate;
|
||||
int size;
|
||||
T root;
|
||||
|
||||
public int Size => size;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryPop(out T result)
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
||||
{
|
||||
var v = root;
|
||||
if (!(v is null))
|
||||
{
|
||||
root = v.NextNode;
|
||||
v.NextNode = null;
|
||||
size--;
|
||||
result = v;
|
||||
Volatile.Write(ref gate, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
Volatile.Write(ref gate, 0);
|
||||
}
|
||||
result = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryPush(T item)
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
||||
{
|
||||
//if (size < TaskPool.MaxPoolSize)
|
||||
{
|
||||
item.NextNode = root;
|
||||
root = item;
|
||||
size++;
|
||||
Volatile.Write(ref gate, 0);
|
||||
return true;
|
||||
}
|
||||
//else
|
||||
{
|
||||
// Volatile.Write(ref gate, 0);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
public struct TaskPoolWithoutSize<T>
|
||||
where T : class, ITaskPoolNode<T>
|
||||
{
|
||||
int gate;
|
||||
T root;
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryPop(out T result)
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
||||
{
|
||||
var v = root;
|
||||
if (!(v is null))
|
||||
{
|
||||
root = v.NextNode;
|
||||
v.NextNode = null;
|
||||
result = v;
|
||||
Volatile.Write(ref gate, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
Volatile.Write(ref gate, 0);
|
||||
}
|
||||
result = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryPush(T item)
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
||||
{
|
||||
//if (size < TaskPool.MaxPoolSize)
|
||||
{
|
||||
item.NextNode = root;
|
||||
root = item;
|
||||
Volatile.Write(ref gate, 0);
|
||||
return true;
|
||||
}
|
||||
//else
|
||||
{
|
||||
// Volatile.Write(ref gate, 0);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,9 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<RootNamespace>NetCoreSandbox</RootNamespace>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -15,6 +16,12 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\UniTask.NetCore\UniTask.NetCore.csproj" />
|
||||
|
||||
|
||||
<ProjectReference Include="..\UniTask.Analyzer\UniTask.Analyzer.csproj">
|
||||
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
|
||||
<OutputItemType>Analyzer</OutputItemType>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace NetCoreTests
|
||||
|
||||
var ar = await array;
|
||||
|
||||
ar.Should().BeEquivalentTo(new[] { 99, 100, 100, 100, 131 });
|
||||
ar.Should().Equal(new[] { 99, 100, 100, 100, 131 });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -49,7 +49,7 @@ namespace NetCoreTests
|
||||
|
||||
var ar = await array;
|
||||
|
||||
ar.Should().BeEquivalentTo(new[] { 100, 100, 100, 131, 191 });
|
||||
ar.Should().Equal(new[] { 100, 100, 100, 131, 191 });
|
||||
}
|
||||
|
||||
//[Fact]
|
||||
@@ -70,7 +70,7 @@ namespace NetCoreTests
|
||||
|
||||
// var ar = await array;
|
||||
|
||||
// ar.Should().BeEquivalentTo(new[] { 99, 100, 100, 100, 131 });
|
||||
// ar.Should().Equal(new[] { 99, 100, 100, 100, 131 });
|
||||
//}
|
||||
|
||||
//[Fact]
|
||||
@@ -88,7 +88,7 @@ namespace NetCoreTests
|
||||
|
||||
// var ar = await array;
|
||||
|
||||
// ar.Should().BeEquivalentTo(new[] { 100, 100, 100, 131, 191 });
|
||||
// ar.Should().Equal(new[] { 100, 100, 100, 131, 191 });
|
||||
//}
|
||||
|
||||
|
||||
|
||||
@@ -276,8 +276,8 @@ namespace NetCoreTests
|
||||
reference.Writer.TryComplete();
|
||||
channel.Writer.TryComplete();
|
||||
|
||||
(await ta1).Should().BeEquivalentTo(new[] { 10, 20, 30 });
|
||||
(await ta2).Should().BeEquivalentTo(new[] { 10, 20, 30 });
|
||||
(await ta1).Should().Equal(new[] { 10, 20, 30 });
|
||||
(await ta2).Should().Equal(new[] { 10, 20, 30 });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -481,7 +481,7 @@ namespace NetCoreTests.Linq
|
||||
list.Add(x);
|
||||
});
|
||||
|
||||
list.Should().BeEquivalentTo(Enumerable.Range(1, 10));
|
||||
list.Should().Equal(Enumerable.Range(1, 10));
|
||||
|
||||
var list2 = new List<(int, int)>();
|
||||
await Enumerable.Range(5, 10).ToUniTaskAsyncEnumerable().ForEachAsync((index, x) =>
|
||||
@@ -490,7 +490,7 @@ namespace NetCoreTests.Linq
|
||||
});
|
||||
|
||||
var list3 = Enumerable.Range(5, 10).Select((index, x) => (index, x)).ToArray();
|
||||
list2.Should().BeEquivalentTo(list3);
|
||||
list2.Should().Equal(list3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace NetCoreTests.Linq
|
||||
var xs = await Enumerable.Range(start, count).ToUniTaskAsyncEnumerable().Append(99).ToArrayAsync();
|
||||
var ys = Enumerable.Range(start, count).Append(99).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -50,7 +50,7 @@ namespace NetCoreTests.Linq
|
||||
var xs = await Enumerable.Range(start, count).ToUniTaskAsyncEnumerable().Prepend(99).ToArrayAsync();
|
||||
var ys = Enumerable.Range(start, count).Prepend(99).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -85,7 +85,7 @@ namespace NetCoreTests.Linq
|
||||
|
||||
var xs = await l.ToUniTaskAsyncEnumerable().Concat(r.ToUniTaskAsyncEnumerable()).ToArrayAsync();
|
||||
var ys = l.Concat(r).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -119,17 +119,17 @@ namespace NetCoreTests.Linq
|
||||
{
|
||||
var xs = await Enumerable.Range(1, 0).ToUniTaskAsyncEnumerable().DefaultIfEmpty(99).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, 0).DefaultIfEmpty(99).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
{
|
||||
var xs = await Enumerable.Range(1, 1).ToUniTaskAsyncEnumerable().DefaultIfEmpty(99).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, 1).DefaultIfEmpty(99).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
{
|
||||
var xs = await Enumerable.Range(1, 10).ToUniTaskAsyncEnumerable().DefaultIfEmpty(99).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, 10).DefaultIfEmpty(99).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
// Throw
|
||||
{
|
||||
|
||||
@@ -34,11 +34,11 @@ namespace NetCoreTests.Linq
|
||||
{
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, 10).ToObservable().ToArray();
|
||||
xs.Should().BeEquivalentTo(Enumerable.Range(1, 10));
|
||||
xs.Should().Equal(Enumerable.Range(1, 10));
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, 0).ToObservable().ToArray();
|
||||
xs.Should().BeEquivalentTo(Enumerable.Range(1, 0));
|
||||
xs.Should().Equal(Enumerable.Range(1, 0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,21 +70,21 @@ namespace NetCoreTests.Linq
|
||||
var xs = await Observable.Range(1, 100).ToUniTaskAsyncEnumerable().ToArrayAsync();
|
||||
var ys = await Observable.Range(1, 100).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
|
||||
{
|
||||
var xs = await Observable.Range(1, 100, ThreadPoolScheduler.Instance).ToUniTaskAsyncEnumerable().ToArrayAsync();
|
||||
var ys = await Observable.Range(1, 100, ThreadPoolScheduler.Instance).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
|
||||
{
|
||||
var xs = await Observable.Empty<int>(ThreadPoolScheduler.Instance).ToUniTaskAsyncEnumerable().ToArrayAsync();
|
||||
var ys = await Observable.Empty<int>(ThreadPoolScheduler.Instance).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,25 +95,25 @@ namespace NetCoreTests.Linq
|
||||
var xs = await Enumerable.Range(1, 100).ToUniTaskAsyncEnumerable().ToDictionaryAsync(x => x);
|
||||
var ys = Enumerable.Range(1, 100).ToDictionary(x => x);
|
||||
|
||||
xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key));
|
||||
xs.OrderBy(x => x.Key).Should().Equal(ys.OrderBy(x => x.Key));
|
||||
}
|
||||
{
|
||||
var xs = await Enumerable.Range(1, 0).ToUniTaskAsyncEnumerable().ToDictionaryAsync(x => x);
|
||||
var ys = Enumerable.Range(1, 0).ToDictionary(x => x);
|
||||
|
||||
xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key));
|
||||
xs.OrderBy(x => x.Key).Should().Equal(ys.OrderBy(x => x.Key));
|
||||
}
|
||||
{
|
||||
var xs = await Enumerable.Range(1, 100).ToUniTaskAsyncEnumerable().ToDictionaryAsync(x => x, x => x * 2);
|
||||
var ys = Enumerable.Range(1, 100).ToDictionary(x => x, x => x * 2);
|
||||
|
||||
xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key));
|
||||
xs.OrderBy(x => x.Key).Should().Equal(ys.OrderBy(x => x.Key));
|
||||
}
|
||||
{
|
||||
var xs = await Enumerable.Range(1, 0).ToUniTaskAsyncEnumerable().ToDictionaryAsync(x => x, x => x * 2);
|
||||
var ys = Enumerable.Range(1, 0).ToDictionary(x => x, x => x * 2);
|
||||
|
||||
xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key));
|
||||
xs.OrderBy(x => x.Key).Should().Equal(ys.OrderBy(x => x.Key));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,34 +126,34 @@ namespace NetCoreTests.Linq
|
||||
var ys = arr.ToLookup(x => x);
|
||||
|
||||
xs.Count.Should().Be(ys.Count);
|
||||
xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key));
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
foreach (var key in xs.Select(x => x.Key))
|
||||
{
|
||||
xs[key].Should().BeEquivalentTo(ys[key]);
|
||||
xs[key].Should().Equal(ys[key]);
|
||||
}
|
||||
}
|
||||
{
|
||||
var xs = await Enumerable.Range(1, 0).ToUniTaskAsyncEnumerable().ToLookupAsync(x => x);
|
||||
var ys = Enumerable.Range(1, 0).ToLookup(x => x);
|
||||
|
||||
xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key));
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
{
|
||||
var xs = await arr.ToUniTaskAsyncEnumerable().ToLookupAsync(x => x, x => x * 2);
|
||||
var ys = arr.ToLookup(x => x, x => x * 2);
|
||||
|
||||
xs.Count.Should().Be(ys.Count);
|
||||
xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key));
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
foreach (var key in xs.Select(x => x.Key))
|
||||
{
|
||||
xs[key].Should().BeEquivalentTo(ys[key]);
|
||||
xs[key].Should().Equal(ys[key]);
|
||||
}
|
||||
}
|
||||
{
|
||||
var xs = await Enumerable.Range(1, 0).ToUniTaskAsyncEnumerable().ToLookupAsync(x => x, x => x * 2);
|
||||
var ys = Enumerable.Range(1, 0).ToLookup(x => x, x => x * 2);
|
||||
|
||||
xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key));
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,13 +164,13 @@ namespace NetCoreTests.Linq
|
||||
var xs = await Enumerable.Range(1, 100).ToUniTaskAsyncEnumerable().ToListAsync();
|
||||
var ys = Enumerable.Range(1, 100).ToList();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
{
|
||||
var xs = await Enumerable.Empty<int>().ToUniTaskAsyncEnumerable().ToListAsync();
|
||||
var ys = Enumerable.Empty<int>().ToList();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,13 +181,13 @@ namespace NetCoreTests.Linq
|
||||
var xs = await new[] { 1, 20, 4, 5, 20, 4, 6 }.ToUniTaskAsyncEnumerable().ToHashSetAsync();
|
||||
var ys = new[] { 1, 20, 4, 5, 20, 4, 6 }.ToHashSet();
|
||||
|
||||
xs.OrderBy(x => x).Should().BeEquivalentTo(ys.OrderBy(x => x));
|
||||
xs.OrderBy(x => x).Should().Equal(ys.OrderBy(x => x));
|
||||
}
|
||||
{
|
||||
var xs = await Enumerable.Empty<int>().ToUniTaskAsyncEnumerable().ToHashSetAsync();
|
||||
var ys = Enumerable.Empty<int>().ToHashSet();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace NetCoreTests.Linq
|
||||
|
||||
var ys = await Range(from, count).AsUniTaskAsyncEnumerable().ToArrayAsync();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -57,19 +57,19 @@ namespace NetCoreTests.Linq
|
||||
list.Should().BeEmpty();
|
||||
|
||||
await e.MoveNextAsync();
|
||||
list.Should().BeEquivalentTo(100);
|
||||
list.Should().Equal(100);
|
||||
e.Current.Should().Be(10);
|
||||
|
||||
await e.MoveNextAsync();
|
||||
list.Should().BeEquivalentTo(100, 200);
|
||||
list.Should().Equal(100, 200);
|
||||
e.Current.Should().Be(20);
|
||||
|
||||
await e.MoveNextAsync();
|
||||
list.Should().BeEquivalentTo(100, 200, 300);
|
||||
list.Should().Equal(100, 200, 300);
|
||||
e.Current.Should().Be(30);
|
||||
|
||||
(await e.MoveNextAsync()).Should().BeFalse();
|
||||
list.Should().BeEquivalentTo(100, 200, 300, 400);
|
||||
list.Should().Equal(100, 200, 300, 400);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -144,19 +144,43 @@ namespace NetCoreTests.Linq
|
||||
list.Should().BeEmpty();
|
||||
|
||||
await e.MoveNextAsync();
|
||||
list.Should().BeEquivalentTo(100);
|
||||
list.Should().Equal(100);
|
||||
e.Current.Should().Be(10);
|
||||
|
||||
await e.MoveNextAsync();
|
||||
list.Should().BeEquivalentTo(100, 200);
|
||||
list.Should().Equal(100, 200);
|
||||
e.Current.Should().Be(20);
|
||||
|
||||
await e.MoveNextAsync();
|
||||
list.Should().BeEquivalentTo(100, 200, 300);
|
||||
list.Should().Equal(100, 200, 300);
|
||||
e.Current.Should().Be(30);
|
||||
|
||||
(await e.MoveNextAsync()).Should().BeFalse();
|
||||
list.Should().BeEquivalentTo(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)
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace NetCoreTests.Linq
|
||||
var xs = await UniTaskAsyncEnumerable.Range(start, count).ToArrayAsync();
|
||||
var ys = Enumerable.Range(start, count).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
@@ -36,7 +36,7 @@ namespace NetCoreTests.Linq
|
||||
var xs = await UniTaskAsyncEnumerable.Repeat(value, count).ToArrayAsync();
|
||||
var ys = Enumerable.Repeat(value, count).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -45,7 +45,7 @@ namespace NetCoreTests.Linq
|
||||
var xs = await UniTaskAsyncEnumerable.Empty<int>().ToArrayAsync();
|
||||
var ys = Enumerable.Empty<int>().ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
||||
@@ -23,26 +23,26 @@ namespace NetCoreTests.Linq
|
||||
{
|
||||
var a = await src.Where(x => x % 2 == 0).ToArrayAsync();
|
||||
var expected = range.Where(x => x % 2 == 0).ToArray();
|
||||
a.Should().BeEquivalentTo(expected);
|
||||
a.Should().Equal(expected);
|
||||
}
|
||||
{
|
||||
var a = await src.Where((x, i) => (x + i) % 2 == 0).ToArrayAsync();
|
||||
var expected = range.Where((x, i) => (x + i) % 2 == 0).ToArray();
|
||||
a.Should().BeEquivalentTo(expected);
|
||||
a.Should().Equal(expected);
|
||||
}
|
||||
{
|
||||
var a = await src.WhereAwait(x => UniTask.Run(() => x % 2 == 0)).ToArrayAsync();
|
||||
var b = await src.WhereAwait(x => UniTask.FromResult(x % 2 == 0)).ToArrayAsync();
|
||||
var expected = range.Where(x => x % 2 == 0).ToArray();
|
||||
a.Should().BeEquivalentTo(expected);
|
||||
b.Should().BeEquivalentTo(expected);
|
||||
a.Should().Equal(expected);
|
||||
b.Should().Equal(expected);
|
||||
}
|
||||
{
|
||||
var a = await src.WhereAwait((x, i) => UniTask.Run(() => (x + i) % 2 == 0)).ToArrayAsync();
|
||||
var b = await src.WhereAwait((x, i) => UniTask.FromResult((x + i) % 2 == 0)).ToArrayAsync();
|
||||
var expected = range.Where((x, i) => (x + i) % 2 == 0).ToArray();
|
||||
a.Should().BeEquivalentTo(expected);
|
||||
b.Should().BeEquivalentTo(expected);
|
||||
a.Should().Equal(expected);
|
||||
b.Should().Equal(expected);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ namespace NetCoreTests.Linq
|
||||
var a = await data.ToUniTaskAsyncEnumerable().OfType<int>().ToArrayAsync();
|
||||
var b = data.OfType<int>().ToArray();
|
||||
|
||||
a.Should().BeEquivalentTo(b);
|
||||
a.Should().Equal(b);
|
||||
}
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ namespace NetCoreTests.Linq
|
||||
var a = await data.ToUniTaskAsyncEnumerable().Cast<int>().ToArrayAsync();
|
||||
var b = data.Cast<int>().ToArray();
|
||||
|
||||
a.Should().BeEquivalentTo(b);
|
||||
a.Should().Equal(b);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace NetCoreTests.Linq
|
||||
var xs = await outer.ToUniTaskAsyncEnumerable().Join(inner.ToUniTaskAsyncEnumerable(), x => x, x => x, (x, y) => (x, y)).ToArrayAsync();
|
||||
var ys = outer.Join(inner, x => x, x => x, (x, y) => (x, y)).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ namespace NetCoreTests.Linq
|
||||
var xs = await outer.ToUniTaskAsyncEnumerable().JoinAwait(inner.ToUniTaskAsyncEnumerable(), x => RandomRun(x), x => RandomRun(x), (x, y) => RandomRun((x, y))).ToArrayAsync();
|
||||
var ys = outer.Join(inner, x => x, x => x, (x, y) => (x, y)).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ namespace NetCoreTests.Linq
|
||||
var xs = await outer.ToUniTaskAsyncEnumerable().JoinAwaitWithCancellation(inner.ToUniTaskAsyncEnumerable(), (x, _) => RandomRun(x), (x, _) => RandomRun(x), (x, y, _) => RandomRun((x, y))).ToArrayAsync();
|
||||
var ys = outer.Join(inner, x => x, x => x, (x, y) => (x, y)).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
|
||||
|
||||
@@ -123,7 +123,7 @@ namespace NetCoreTests.Linq
|
||||
var ys = arr.GroupBy(x => x).ToArray();
|
||||
|
||||
xs.Length.Should().Be(ys.Length);
|
||||
xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key));
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -131,7 +131,7 @@ namespace NetCoreTests.Linq
|
||||
var ys = arr.GroupBy(x => x, (key, xs) => (key, xs.ToArray())).ToArray();
|
||||
|
||||
xs.Length.Should().Be(ys.Length);
|
||||
xs.OrderBy(x => x.key).SelectMany(x => x.Item2).Should().BeEquivalentTo(ys.OrderBy(x => x.key).SelectMany(x => x.Item2));
|
||||
xs.OrderBy(x => x.key).SelectMany(x => x.Item2).Should().Equal(ys.OrderBy(x => x.key).SelectMany(x => x.Item2));
|
||||
}
|
||||
|
||||
{
|
||||
@@ -139,7 +139,7 @@ namespace NetCoreTests.Linq
|
||||
var ys = arr.GroupBy(x => x).ToArray();
|
||||
|
||||
xs.Length.Should().Be(ys.Length);
|
||||
xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key));
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -147,7 +147,7 @@ namespace NetCoreTests.Linq
|
||||
var ys = arr.GroupBy(x => x, (key, xs) => (key, xs.ToArray())).ToArray();
|
||||
|
||||
xs.Length.Should().Be(ys.Length);
|
||||
xs.OrderBy(x => x.key).SelectMany(x => x.Item2).Should().BeEquivalentTo(ys.OrderBy(x => x.key).SelectMany(x => x.Item2));
|
||||
xs.OrderBy(x => x.key).SelectMany(x => x.Item2).Should().Equal(ys.OrderBy(x => x.key).SelectMany(x => x.Item2));
|
||||
}
|
||||
|
||||
{
|
||||
@@ -155,7 +155,7 @@ namespace NetCoreTests.Linq
|
||||
var ys = arr.GroupBy(x => x).ToArray();
|
||||
|
||||
xs.Length.Should().Be(ys.Length);
|
||||
xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key));
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -163,7 +163,7 @@ namespace NetCoreTests.Linq
|
||||
var ys = arr.GroupBy(x => x, (key, xs) => (key, xs.ToArray())).ToArray();
|
||||
|
||||
xs.Length.Should().Be(ys.Length);
|
||||
xs.OrderBy(x => x.key).SelectMany(x => x.Item2).Should().BeEquivalentTo(ys.OrderBy(x => x.key).SelectMany(x => x.Item2));
|
||||
xs.OrderBy(x => x.key).SelectMany(x => x.Item2).Should().Equal(ys.OrderBy(x => x.key).SelectMany(x => x.Item2));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,21 +199,21 @@ namespace NetCoreTests.Linq
|
||||
var ys = outer.GroupJoin(inner, x => x, x => x, (x, y) => (x, string.Join(", ", y))).ToArray();
|
||||
|
||||
xs.Length.Should().Be(ys.Length);
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
{
|
||||
var xs = await outer.ToUniTaskAsyncEnumerable().GroupJoinAwait(inner.ToUniTaskAsyncEnumerable(), x => RandomRun(x), x => RandomRun(x), (x, y) => RandomRun((x, string.Join(", ", y)))).ToArrayAsync();
|
||||
var ys = outer.GroupJoin(inner, x => x, x => x, (x, y) => (x, string.Join(", ", y))).ToArray();
|
||||
|
||||
xs.Length.Should().Be(ys.Length);
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
{
|
||||
var xs = await outer.ToUniTaskAsyncEnumerable().GroupJoinAwaitWithCancellation(inner.ToUniTaskAsyncEnumerable(), (x, _) => RandomRun(x), (x, _) => RandomRun(x), (x, y, _) => RandomRun((x, string.Join(", ", y)))).ToArrayAsync();
|
||||
var ys = outer.GroupJoin(inner, x => x, x => x, (x, y) => (x, string.Join(", ", y))).ToArray();
|
||||
|
||||
xs.Length.Should().Be(ys.Length);
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
139
src/UniTask.NetCoreTests/Linq/Merge.cs
Normal file
139
src/UniTask.NetCoreTests/Linq/Merge.cs
Normal file
@@ -0,0 +1,139 @@
|
||||
#pragma warning disable CS1998
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@ namespace NetCoreTests.Linq
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).Skip(skipCount).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, collection).Skip(skipCount).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -52,7 +52,7 @@ namespace NetCoreTests.Linq
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipLast(skipCount).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, collection).SkipLast(skipCount).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -77,7 +77,7 @@ namespace NetCoreTests.Linq
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeLast(takeCount).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, collection).TakeLast(takeCount).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -103,7 +103,7 @@ namespace NetCoreTests.Linq
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).Take(takeCount).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, collection).Take(takeCount).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -130,37 +130,37 @@ namespace NetCoreTests.Linq
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipWhile(x => x < skipCount).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, collection).SkipWhile(x => x < skipCount).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipWhile((x, i) => x < (skipCount - i)).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, collection).SkipWhile((x, i) => x < (skipCount - i)).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipWhileAwait(x => UniTask.Run(() => x < skipCount)).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, collection).SkipWhile(x => x < skipCount).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipWhileAwait((x, i) => UniTask.Run(() => x < (skipCount - i))).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, collection).SkipWhile((x, i) => x < (skipCount - i)).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipWhileAwaitWithCancellation((x, _) => UniTask.Run(() => x < skipCount)).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, collection).SkipWhile(x => x < skipCount).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipWhileAwaitWithCancellation((x, i, _) => UniTask.Run(() => x < (skipCount - i))).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, collection).SkipWhile((x, i) => x < (skipCount - i)).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,37 +213,37 @@ namespace NetCoreTests.Linq
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeWhile(x => x < skipCount).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, collection).TakeWhile(x => x < skipCount).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeWhile((x, i) => x < (skipCount - i)).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, collection).TakeWhile((x, i) => x < (skipCount - i)).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeWhileAwait(x => UniTask.Run(() => x < skipCount)).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, collection).TakeWhile(x => x < skipCount).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeWhileAwait((x, i) => UniTask.Run(() => x < (skipCount - i))).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, collection).TakeWhile((x, i) => x < (skipCount - i)).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeWhileAwaitWithCancellation((x, _) => UniTask.Run(() => x < skipCount)).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, collection).TakeWhile(x => x < skipCount).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeWhileAwaitWithCancellation((x, i, _) => UniTask.Run(() => x < (skipCount - i))).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, collection).TakeWhile((x, i) => x < (skipCount - i)).ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace NetCoreTests.Linq
|
||||
var xs = await Enumerable.Range(start, count).ToUniTaskAsyncEnumerable().Reverse().ToArrayAsync();
|
||||
var ys = Enumerable.Range(start, count).Reverse().ToArray();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -46,18 +46,18 @@ namespace NetCoreTests.Linq
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, count).Select(x => x * x).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, count).Select(x => x * x).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
|
||||
var zs = await UniTaskAsyncEnumerable.Range(1, count).SelectAwait((x) => UniTask.Run(() => x * x)).ToArrayAsync();
|
||||
zs.Should().BeEquivalentTo(ys);
|
||||
zs.Should().Equal(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, count).Select((x, i) => x * x * i).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, count).Select((x, i) => x * x * i).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
|
||||
var zs = await UniTaskAsyncEnumerable.Range(1, count).SelectAwait((x, i) => UniTask.Run(() => x * x * i)).ToArrayAsync();
|
||||
zs.Should().BeEquivalentTo(ys);
|
||||
zs.Should().Equal(ys);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,22 +98,22 @@ namespace NetCoreTests.Linq
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectMany(x => UniTaskAsyncEnumerable.Range(99, rightCount * x)).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, leftCount).SelectMany(x => Enumerable.Range(99, rightCount * x)).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectMany((i, x) => UniTaskAsyncEnumerable.Range(99 * i, rightCount * x)).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, leftCount).SelectMany((i, x) => Enumerable.Range(99 * i, rightCount * x)).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectMany(x => UniTaskAsyncEnumerable.Range(99, rightCount * x), (x, y) => x * y).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, leftCount).SelectMany(x => Enumerable.Range(99, rightCount * x), (x, y) => x * y).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectMany((i, x) => UniTaskAsyncEnumerable.Range(99 * i, rightCount * x), (x, y) => x * y).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, leftCount).SelectMany((i, x) => Enumerable.Range(99 * i, rightCount * x), (x, y) => x * y).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
|
||||
// await
|
||||
@@ -121,22 +121,22 @@ namespace NetCoreTests.Linq
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwait(x => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99, rightCount * x))).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, leftCount).SelectMany(x => Enumerable.Range(99, rightCount * x)).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwait((i, x) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99 * i, rightCount * x))).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, leftCount).SelectMany((i, x) => Enumerable.Range(99 * i, rightCount * x)).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwait(x => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99, rightCount * x)), (x, y) => UniTask.Run(() => x * y)).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, leftCount).SelectMany(x => Enumerable.Range(99, rightCount * x), (x, y) => x * y).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwait((i, x) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99 * i, rightCount * x)), (x, y) => UniTask.Run(() => x * y)).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, leftCount).SelectMany((i, x) => Enumerable.Range(99 * i, rightCount * x), (x, y) => x * y).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
|
||||
// with cancel
|
||||
@@ -144,22 +144,22 @@ namespace NetCoreTests.Linq
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwaitWithCancellation((x, _) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99, rightCount * x))).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, leftCount).SelectMany(x => Enumerable.Range(99, rightCount * x)).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwaitWithCancellation((i, x, _) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99 * i, rightCount * x))).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, leftCount).SelectMany((i, x) => Enumerable.Range(99 * i, rightCount * x)).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwaitWithCancellation((x, _) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99, rightCount * x)), (x, y, _) => UniTask.Run(() => x * y)).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, leftCount).SelectMany(x => Enumerable.Range(99, rightCount * x), (x, y) => x * y).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwaitWithCancellation((i, x, _) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99 * i, rightCount * x)), (x, y, _) => UniTask.Run(() => x * y)).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, leftCount).SelectMany((i, x) => Enumerable.Range(99 * i, rightCount * x), (x, y) => x * y).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,17 +219,17 @@ namespace NetCoreTests.Linq
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).Zip(UniTaskAsyncEnumerable.Range(99, rightCount)).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, leftCount).Zip(Enumerable.Range(99, rightCount)).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).ZipAwait(UniTaskAsyncEnumerable.Range(99, rightCount), (x, y) => UniTask.Run(() => (x, y))).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, leftCount).Zip(Enumerable.Range(99, rightCount)).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).ZipAwaitWithCancellation(UniTaskAsyncEnumerable.Range(99, rightCount), (x, y, _) => UniTask.Run(() => (x, y))).ToArrayAsync();
|
||||
var ys = Enumerable.Range(1, leftCount).Zip(Enumerable.Range(99, rightCount)).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
}
|
||||
[Fact]
|
||||
@@ -288,7 +288,7 @@ namespace NetCoreTests.Linq
|
||||
var xs = await UniTaskAsyncEnumerable.Range(0, rangeCount).Buffer(bufferCount).Select(x => string.Join(",", x)).ToArrayAsync();
|
||||
var ys = await AsyncEnumerable.Range(0, rangeCount).Buffer(bufferCount).Select(x => string.Join(",", x)).ToArrayAsync();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
@@ -305,7 +305,7 @@ namespace NetCoreTests.Linq
|
||||
var xs = await UniTaskAsyncEnumerable.Range(0, rangeCount).Buffer(bufferCount, skipCount).Select(x => string.Join(",", x)).ToArrayAsync();
|
||||
var ys = await AsyncEnumerable.Range(0, rangeCount).Buffer(bufferCount, skipCount).Select(x => string.Join(",", x)).ToArrayAsync();
|
||||
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -402,7 +402,7 @@ namespace NetCoreTests.Linq
|
||||
public async Task PariwiseImmediate()
|
||||
{
|
||||
var xs = await UniTaskAsyncEnumerable.Range(1, 5).Pairwise().ToArrayAsync();
|
||||
xs.Should().BeEquivalentTo((1, 2), (2, 3), (3, 4), (4, 5));
|
||||
xs.Should().Equal((1, 2), (2, 3), (3, 4), (4, 5));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -426,7 +426,7 @@ namespace NetCoreTests.Linq
|
||||
|
||||
await complete;
|
||||
|
||||
list.Should().BeEquivalentTo((10, 20), (20, 30), (30, 40), (40, 50));
|
||||
list.Should().Equal((10, 20), (20, 30), (30, 40), (40, 50));
|
||||
}
|
||||
|
||||
class MyException : Exception
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace NetCoreTests.Linq
|
||||
|
||||
rp.Value = 2;
|
||||
|
||||
(await b).Should().BeEquivalentTo(1, 2);
|
||||
(await b).Should().Equal(1, 2);
|
||||
|
||||
var c = multicast.ToArrayAsync();
|
||||
|
||||
@@ -36,8 +36,8 @@ namespace NetCoreTests.Linq
|
||||
|
||||
rp.Dispose();
|
||||
|
||||
(await a).Should().BeEquivalentTo(1, 2, 3, 4, 5);
|
||||
(await c).Should().BeEquivalentTo(3, 4, 5);
|
||||
(await a).Should().Equal(1, 2, 3, 4, 5);
|
||||
(await c).Should().Equal(3, 4, 5);
|
||||
|
||||
disp.Dispose();
|
||||
}
|
||||
@@ -56,7 +56,7 @@ namespace NetCoreTests.Linq
|
||||
|
||||
rp.Value = 2;
|
||||
|
||||
(await b).Should().BeEquivalentTo(1, 2);
|
||||
(await b).Should().Equal(1, 2);
|
||||
|
||||
var c = multicast.ToArrayAsync();
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace NetCoreTests.Linq
|
||||
l.Add(x);
|
||||
});
|
||||
|
||||
l.Should().BeEquivalentTo(100, 110, 120, 130, 140, 150, 160, 170, 180, 190);
|
||||
l.Should().Equal(100, 110, 120, 130, 140, 150, 160, 170, 180, 190);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,10 +34,10 @@ namespace NetCoreTests.Linq
|
||||
{
|
||||
var ys = array.Distinct().ToArray();
|
||||
{
|
||||
(await array.ToUniTaskAsyncEnumerable().Distinct().ToArrayAsync()).Should().BeEquivalentTo(ys);
|
||||
(await array.ToUniTaskAsyncEnumerable().Distinct(x => x).ToArrayAsync()).Should().BeEquivalentTo(ys);
|
||||
(await array.ToUniTaskAsyncEnumerable().DistinctAwait(x => UniTask.Run(() => x)).ToArrayAsync()).Should().BeEquivalentTo(ys);
|
||||
(await array.ToUniTaskAsyncEnumerable().DistinctAwaitWithCancellation((x, _) => UniTask.Run(() => x)).ToArrayAsync()).Should().BeEquivalentTo(ys);
|
||||
(await array.ToUniTaskAsyncEnumerable().Distinct().ToArrayAsync()).Should().Equal(ys);
|
||||
(await array.ToUniTaskAsyncEnumerable().Distinct(x => x).ToArrayAsync()).Should().Equal(ys);
|
||||
(await array.ToUniTaskAsyncEnumerable().DistinctAwait(x => UniTask.Run(() => x)).ToArrayAsync()).Should().Equal(ys);
|
||||
(await array.ToUniTaskAsyncEnumerable().DistinctAwaitWithCancellation((x, _) => UniTask.Run(() => x)).ToArrayAsync()).Should().Equal(ys);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,10 +71,10 @@ namespace NetCoreTests.Linq
|
||||
{
|
||||
var ys = await array.ToAsyncEnumerable().DistinctUntilChanged().ToArrayAsync();
|
||||
{
|
||||
(await array.ToUniTaskAsyncEnumerable().DistinctUntilChanged().ToArrayAsync()).Should().BeEquivalentTo(ys);
|
||||
(await array.ToUniTaskAsyncEnumerable().DistinctUntilChanged(x => x).ToArrayAsync()).Should().BeEquivalentTo(ys);
|
||||
(await array.ToUniTaskAsyncEnumerable().DistinctUntilChangedAwait(x => UniTask.Run(() => x)).ToArrayAsync()).Should().BeEquivalentTo(ys);
|
||||
(await array.ToUniTaskAsyncEnumerable().DistinctUntilChangedAwaitWithCancellation((x, _) => UniTask.Run(() => x)).ToArrayAsync()).Should().BeEquivalentTo(ys);
|
||||
(await array.ToUniTaskAsyncEnumerable().DistinctUntilChanged().ToArrayAsync()).Should().Equal(ys);
|
||||
(await array.ToUniTaskAsyncEnumerable().DistinctUntilChanged(x => x).ToArrayAsync()).Should().Equal(ys);
|
||||
(await array.ToUniTaskAsyncEnumerable().DistinctUntilChangedAwait(x => UniTask.Run(() => x)).ToArrayAsync()).Should().Equal(ys);
|
||||
(await array.ToUniTaskAsyncEnumerable().DistinctUntilChangedAwaitWithCancellation((x, _) => UniTask.Run(() => x)).ToArrayAsync()).Should().Equal(ys);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ namespace NetCoreTests.Linq
|
||||
{
|
||||
var xs = await a1.ToUniTaskAsyncEnumerable().Except(a2.ToUniTaskAsyncEnumerable()).ToArrayAsync();
|
||||
var ys = a1.Except(a2).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -141,7 +141,7 @@ namespace NetCoreTests.Linq
|
||||
{
|
||||
var xs = await a1.ToUniTaskAsyncEnumerable().Intersect(a2.ToUniTaskAsyncEnumerable()).ToArrayAsync();
|
||||
var ys = a1.Intersect(a2).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -170,7 +170,7 @@ namespace NetCoreTests.Linq
|
||||
{
|
||||
var xs = await a1.ToUniTaskAsyncEnumerable().Union(a2.ToUniTaskAsyncEnumerable()).ToArrayAsync();
|
||||
var ys = a1.Union(a2).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,32 +59,32 @@ namespace NetCoreTests.Linq
|
||||
{
|
||||
var xs = await array.ToUniTaskAsyncEnumerable().OrderBy(x => x).ToArrayAsync();
|
||||
var ys = array.OrderBy(x => x).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
{
|
||||
var xs = await array.ToUniTaskAsyncEnumerable().OrderByDescending(x => x).ToArrayAsync();
|
||||
var ys = array.OrderByDescending(x => x).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
{
|
||||
var xs = await array.ToUniTaskAsyncEnumerable().OrderByAwait(RandomRun).ToArrayAsync();
|
||||
var ys = array.OrderBy(x => x).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
{
|
||||
var xs = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwait(RandomRun).ToArrayAsync();
|
||||
var ys = array.OrderByDescending(x => x).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
{
|
||||
var xs = await array.ToUniTaskAsyncEnumerable().OrderByAwaitWithCancellation(RandomRun).ToArrayAsync();
|
||||
var ys = array.OrderBy(x => x).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
{
|
||||
var xs = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwaitWithCancellation(RandomRun).ToArrayAsync();
|
||||
var ys = array.OrderByDescending(x => x).ToArray();
|
||||
xs.Should().BeEquivalentTo(ys);
|
||||
xs.Should().Equal(ys);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,14 +125,14 @@ namespace NetCoreTests.Linq
|
||||
var g2 = await array.ToUniTaskAsyncEnumerable().OrderByDescending(x => x.Age).ThenByDescending(x => x.FirstName).ThenBy(x => x.LastName).ToArrayAsync();
|
||||
var h2 = await array.ToUniTaskAsyncEnumerable().OrderByDescending(x => x.Age).ThenByDescending(x => x.FirstName).ThenByDescending(x => x.LastName).ToArrayAsync();
|
||||
|
||||
a.Should().BeEquivalentTo(a2);
|
||||
b.Should().BeEquivalentTo(b2);
|
||||
c.Should().BeEquivalentTo(c2);
|
||||
d.Should().BeEquivalentTo(d2);
|
||||
e.Should().BeEquivalentTo(e2);
|
||||
f.Should().BeEquivalentTo(f2);
|
||||
g.Should().BeEquivalentTo(g2);
|
||||
h.Should().BeEquivalentTo(h2);
|
||||
a.Should().Equal(a2);
|
||||
b.Should().Equal(b2);
|
||||
c.Should().Equal(c2);
|
||||
d.Should().Equal(d2);
|
||||
e.Should().Equal(e2);
|
||||
f.Should().Equal(f2);
|
||||
g.Should().Equal(g2);
|
||||
h.Should().Equal(h2);
|
||||
}
|
||||
{
|
||||
var a2 = await array.ToUniTaskAsyncEnumerable().OrderByAwait(x => RandomRun(x.Age)).ThenByAwait(x => RandomRun(x.FirstName)).ThenByAwait(x => RandomRun(x.LastName)).ToArrayAsync();
|
||||
@@ -144,14 +144,14 @@ namespace NetCoreTests.Linq
|
||||
var g2 = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwait(x => RandomRun(x.Age)).ThenByDescendingAwait(x => RandomRun(x.FirstName)).ThenByAwait(x => RandomRun(x.LastName)).ToArrayAsync();
|
||||
var h2 = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwait(x => RandomRun(x.Age)).ThenByDescendingAwait(x => RandomRun(x.FirstName)).ThenByDescendingAwait(x => RandomRun(x.LastName)).ToArrayAsync();
|
||||
|
||||
a.Should().BeEquivalentTo(a2);
|
||||
b.Should().BeEquivalentTo(b2);
|
||||
c.Should().BeEquivalentTo(c2);
|
||||
d.Should().BeEquivalentTo(d2);
|
||||
e.Should().BeEquivalentTo(e2);
|
||||
f.Should().BeEquivalentTo(f2);
|
||||
g.Should().BeEquivalentTo(g2);
|
||||
h.Should().BeEquivalentTo(h2);
|
||||
a.Should().Equal(a2);
|
||||
b.Should().Equal(b2);
|
||||
c.Should().Equal(c2);
|
||||
d.Should().Equal(d2);
|
||||
e.Should().Equal(e2);
|
||||
f.Should().Equal(f2);
|
||||
g.Should().Equal(g2);
|
||||
h.Should().Equal(h2);
|
||||
}
|
||||
{
|
||||
var a2 = await array.ToUniTaskAsyncEnumerable().OrderByAwaitWithCancellation((x, ct) => RandomRun(x.Age)).ThenByAwaitWithCancellation((x, ct) => RandomRun(x.FirstName)).ThenByAwaitWithCancellation((x, ct) => RandomRun(x.LastName)).ToArrayAsync();
|
||||
@@ -163,14 +163,14 @@ namespace NetCoreTests.Linq
|
||||
var g2 = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.Age)).ThenByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.FirstName)).ThenByAwaitWithCancellation((x, ct) => RandomRun(x.LastName)).ToArrayAsync();
|
||||
var h2 = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.Age)).ThenByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.FirstName)).ThenByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.LastName)).ToArrayAsync();
|
||||
|
||||
a.Should().BeEquivalentTo(a2);
|
||||
b.Should().BeEquivalentTo(b2);
|
||||
c.Should().BeEquivalentTo(c2);
|
||||
d.Should().BeEquivalentTo(d2);
|
||||
e.Should().BeEquivalentTo(e2);
|
||||
f.Should().BeEquivalentTo(f2);
|
||||
g.Should().BeEquivalentTo(g2);
|
||||
h.Should().BeEquivalentTo(h2);
|
||||
a.Should().Equal(a2);
|
||||
b.Should().Equal(b2);
|
||||
c.Should().Equal(c2);
|
||||
d.Should().Equal(d2);
|
||||
e.Should().Equal(e2);
|
||||
f.Should().Equal(f2);
|
||||
g.Should().Equal(g2);
|
||||
h.Should().Equal(h2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace NetCoreTests.Linq
|
||||
rp.Value = 4;
|
||||
rp.Value = 5;
|
||||
|
||||
(await xs).Should().BeEquivalentTo(1, 2, 3, 4, 5);
|
||||
(await xs).Should().Equal(1, 2, 3, 4, 5);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -39,7 +39,7 @@ namespace NetCoreTests.Linq
|
||||
rp.Value = 4;
|
||||
rp.Value = 5;
|
||||
|
||||
(await xs).Should().BeEquivalentTo(1, 2, 3, 4);
|
||||
(await xs).Should().Equal(1, 2, 3, 4);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -56,7 +56,7 @@ namespace NetCoreTests.Linq
|
||||
await c;
|
||||
var foo = await xs;
|
||||
|
||||
foo.Should().BeEquivalentTo(new[] { 1, 10, 20 });
|
||||
foo.Should().Equal(new[] { 1, 10, 20 });
|
||||
|
||||
async Task CancelAsync()
|
||||
{
|
||||
@@ -85,7 +85,7 @@ namespace NetCoreTests.Linq
|
||||
await c;
|
||||
var foo = await xs;
|
||||
|
||||
foo.Should().BeEquivalentTo(new[] { 20, 30, 40 });
|
||||
foo.Should().Equal(new[] { 20, 30, 40 });
|
||||
|
||||
async Task CancelAsync()
|
||||
{
|
||||
@@ -116,7 +116,7 @@ namespace NetCoreTests.Linq
|
||||
await c;
|
||||
var foo = await xs;
|
||||
|
||||
foo.Should().BeEquivalentTo(new[] { 1, 10, 20 });
|
||||
foo.Should().Equal(new[] { 1, 10, 20 });
|
||||
|
||||
async Task CancelAsync()
|
||||
{
|
||||
@@ -145,7 +145,7 @@ namespace NetCoreTests.Linq
|
||||
await c;
|
||||
var foo = await xs;
|
||||
|
||||
foo.Should().BeEquivalentTo(new[] { 20, 30, 40 });
|
||||
foo.Should().Equal(new[] { 20, 30, 40 });
|
||||
|
||||
async Task CancelAsync()
|
||||
{
|
||||
|
||||
45
src/UniTask.NetCoreTests/TaskExtensionsTest.cs
Normal file
45
src/UniTask.NetCoreTests/TaskExtensionsTest.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma warning disable CS1998
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@ namespace NetCoreTests
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
one.NextCalled.Should().Equal(10, 20, 30);
|
||||
|
||||
ev.SetCompleted();
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace NetCoreTests
|
||||
ev.SetCompleted();
|
||||
ev.SetCanceled(default);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
one.NextCalled.Should().Equal(10, 20, 30);
|
||||
one.CompletedCalled.Count.Should().Be(1);
|
||||
}
|
||||
// after removed, onemore
|
||||
@@ -59,7 +59,7 @@ namespace NetCoreTests
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
one.NextCalled.Should().Equal(10, 20, 30);
|
||||
|
||||
ev.SetCompleted();
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace NetCoreTests
|
||||
ev.SetCompleted();
|
||||
ev.SetCanceled(default);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
one.NextCalled.Should().Equal(10, 20, 30);
|
||||
one.CompletedCalled.Count.Should().Be(1);
|
||||
}
|
||||
}
|
||||
@@ -102,10 +102,10 @@ namespace NetCoreTests
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
four.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
one.NextCalled.Should().Equal(10, 20, 30);
|
||||
two.NextCalled.Should().Equal(10, 20, 30);
|
||||
three.NextCalled.Should().Equal(10, 20, 30);
|
||||
four.NextCalled.Should().Equal(10, 20, 30);
|
||||
|
||||
ev.SetCompleted();
|
||||
|
||||
@@ -120,11 +120,11 @@ namespace NetCoreTests
|
||||
ev.SetCompleted();
|
||||
ev.SetCanceled(default);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
one.NextCalled.Should().Equal(10, 20, 30);
|
||||
one.CompletedCalled.Count.Should().Be(1);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
two.NextCalled.Should().Equal(10, 20, 30);
|
||||
three.CompletedCalled.Count.Should().Be(1);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
two.NextCalled.Should().Equal(10, 20, 30);
|
||||
three.CompletedCalled.Count.Should().Be(1);
|
||||
}
|
||||
|
||||
@@ -145,10 +145,10 @@ namespace NetCoreTests
|
||||
ev.SetResult(30);
|
||||
ev.Add(four);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
four.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
one.NextCalled.Should().Equal(10, 20, 30);
|
||||
two.NextCalled.Should().Equal(10, 20, 30);
|
||||
three.NextCalled.Should().Equal(10, 20, 30);
|
||||
four.NextCalled.Should().Equal(10, 20, 30);
|
||||
|
||||
ev.SetCompleted();
|
||||
|
||||
@@ -163,11 +163,11 @@ namespace NetCoreTests
|
||||
ev.SetCompleted();
|
||||
ev.SetCanceled(default);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
one.NextCalled.Should().Equal(10, 20, 30);
|
||||
one.CompletedCalled.Count.Should().Be(1);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
two.NextCalled.Should().Equal(10, 20, 30);
|
||||
three.CompletedCalled.Count.Should().Be(1);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
two.NextCalled.Should().Equal(10, 20, 30);
|
||||
three.CompletedCalled.Count.Should().Be(1);
|
||||
}
|
||||
}
|
||||
@@ -190,9 +190,9 @@ namespace NetCoreTests
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
one.NextCalled.Should().Equal(10, 20, 30);
|
||||
two.NextCalled.Should().Equal(10, 20, 30);
|
||||
three.NextCalled.Should().Equal(10, 20, 30);
|
||||
|
||||
ev.Remove(one);
|
||||
|
||||
@@ -200,9 +200,9 @@ namespace NetCoreTests
|
||||
ev.SetResult(50);
|
||||
ev.SetResult(60);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60);
|
||||
one.NextCalled.Should().Equal(10, 20, 30);
|
||||
two.NextCalled.Should().Equal(10, 20, 30, 40, 50, 60);
|
||||
three.NextCalled.Should().Equal(10, 20, 30, 40, 50, 60);
|
||||
}
|
||||
}
|
||||
[Fact]
|
||||
@@ -222,9 +222,9 @@ namespace NetCoreTests
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
one.NextCalled.Should().Equal(10, 20, 30);
|
||||
two.NextCalled.Should().Equal(10, 20, 30);
|
||||
three.NextCalled.Should().Equal(10, 20, 30);
|
||||
|
||||
ev.Remove(two);
|
||||
|
||||
@@ -232,9 +232,9 @@ namespace NetCoreTests
|
||||
ev.SetResult(50);
|
||||
ev.SetResult(60);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60);
|
||||
one.NextCalled.Should().Equal(10, 20, 30, 40, 50, 60);
|
||||
two.NextCalled.Should().Equal(10, 20, 30);
|
||||
three.NextCalled.Should().Equal(10, 20, 30, 40, 50, 60);
|
||||
}
|
||||
}
|
||||
[Fact]
|
||||
@@ -254,9 +254,9 @@ namespace NetCoreTests
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
one.NextCalled.Should().Equal(10, 20, 30);
|
||||
two.NextCalled.Should().Equal(10, 20, 30);
|
||||
three.NextCalled.Should().Equal(10, 20, 30);
|
||||
|
||||
ev.Remove(three);
|
||||
|
||||
@@ -264,9 +264,9 @@ namespace NetCoreTests
|
||||
ev.SetResult(50);
|
||||
ev.SetResult(60);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
one.NextCalled.Should().Equal(10, 20, 30, 40, 50, 60);
|
||||
two.NextCalled.Should().Equal(10, 20, 30, 40, 50, 60);
|
||||
three.NextCalled.Should().Equal(10, 20, 30);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -321,9 +321,9 @@ namespace NetCoreTests
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
one.NextCalled.Should().Equal(10);
|
||||
two.NextCalled.Should().Equal(10, 20, 30);
|
||||
three.NextCalled.Should().Equal(10, 20, 30);
|
||||
}
|
||||
|
||||
public void Run2()
|
||||
@@ -342,9 +342,9 @@ namespace NetCoreTests
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
one.NextCalled.Should().Equal(10);
|
||||
two.NextCalled.Should().Equal(10, 20, 30);
|
||||
three.NextCalled.Should().Equal(10, 20, 30);
|
||||
}
|
||||
|
||||
public void Run3()
|
||||
@@ -363,9 +363,9 @@ namespace NetCoreTests
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10);
|
||||
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
one.NextCalled.Should().Equal(10);
|
||||
two.NextCalled.Should().Equal(10, 20, 30);
|
||||
three.NextCalled.Should().Equal(10, 20, 30);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -390,9 +390,9 @@ namespace NetCoreTests
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
one.NextCalled.Should().Equal(10, 20, 30);
|
||||
two.NextCalled.Count.Should().Be(0);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
three.NextCalled.Should().Equal(10, 20, 30);
|
||||
}
|
||||
|
||||
public void Run2()
|
||||
@@ -412,9 +412,9 @@ namespace NetCoreTests
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
two.NextCalled.Should().BeEquivalentTo(10);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
one.NextCalled.Should().Equal(10, 20, 30);
|
||||
two.NextCalled.Should().Equal(10);
|
||||
three.NextCalled.Should().Equal(10, 20, 30);
|
||||
}
|
||||
|
||||
public void Run3()
|
||||
@@ -434,9 +434,9 @@ namespace NetCoreTests
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
two.NextCalled.Should().BeEquivalentTo(10);
|
||||
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
one.NextCalled.Should().Equal(10, 20, 30);
|
||||
two.NextCalled.Should().Equal(10);
|
||||
three.NextCalled.Should().Equal(10, 20, 30);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -464,10 +464,10 @@ namespace NetCoreTests
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
one.NextCalled.Should().Equal(10, 20, 30);
|
||||
two.NextCalled.Count.Should().Be(0);
|
||||
three.NextCalled.Count.Should().Be(0);
|
||||
four.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
four.NextCalled.Should().Equal(10, 20, 30);
|
||||
}
|
||||
|
||||
public void Run2()
|
||||
@@ -490,10 +490,10 @@ namespace NetCoreTests
|
||||
ev.SetResult(20);
|
||||
ev.SetResult(30);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10);
|
||||
one.NextCalled.Should().Equal(10);
|
||||
two.NextCalled.Count.Should().Be(0);
|
||||
three.NextCalled.Count.Should().Be(0);
|
||||
four.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||
four.NextCalled.Should().Equal(10, 20, 30);
|
||||
}
|
||||
|
||||
|
||||
@@ -533,10 +533,10 @@ namespace NetCoreTests
|
||||
ev.SetResult(30);
|
||||
ev.SetResult(40);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40);
|
||||
two.NextCalled.Should().BeEquivalentTo(20, 30, 40);
|
||||
three.NextCalled.Should().BeEquivalentTo(30, 40);
|
||||
four.NextCalled.Should().BeEquivalentTo(40);
|
||||
one.NextCalled.Should().Equal(10, 20, 30, 40);
|
||||
two.NextCalled.Should().Equal(20, 30, 40);
|
||||
three.NextCalled.Should().Equal(30, 40);
|
||||
four.NextCalled.Should().Equal(40);
|
||||
}
|
||||
|
||||
public void Run2()
|
||||
@@ -573,10 +573,10 @@ namespace NetCoreTests
|
||||
ev.SetResult(30);
|
||||
ev.SetResult(40);
|
||||
|
||||
one.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40);
|
||||
two.NextCalled.Should().BeEquivalentTo(20, 30, 40);
|
||||
three.NextCalled.Should().BeEquivalentTo(30, 40);
|
||||
four.NextCalled.Should().BeEquivalentTo(40);
|
||||
one.NextCalled.Should().Equal(10, 20, 30, 40);
|
||||
two.NextCalled.Should().Equal(20, 30, 40);
|
||||
three.NextCalled.Should().Equal(30, 40);
|
||||
four.NextCalled.Should().Equal(40);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
|
||||
<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();
|
||||
}
|
||||
}
|
||||
}
|
||||
69
src/UniTask.NetCoreTests/WhenEachTest.cs
Normal file
69
src/UniTask.NetCoreTests/WhenEachTest.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace NetCoreTests
|
||||
{
|
||||
public class WhenEachTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task Each()
|
||||
{
|
||||
var a = Delay(1, 3000);
|
||||
var b = Delay(2, 1000);
|
||||
var c = Delay(3, 2000);
|
||||
|
||||
var l = new List<int>();
|
||||
await foreach (var item in UniTask.WhenEach(a, b, c))
|
||||
{
|
||||
l.Add(item.Result);
|
||||
}
|
||||
|
||||
l.Should().Equal(2, 3, 1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Error()
|
||||
{
|
||||
var a = Delay2(1, 3000);
|
||||
var b = Delay2(2, 1000);
|
||||
var c = Delay2(3, 2000);
|
||||
|
||||
var l = new List<WhenEachResult<int>>();
|
||||
await foreach (var item in UniTask.WhenEach(a, b, c))
|
||||
{
|
||||
l.Add(item);
|
||||
}
|
||||
|
||||
l[0].IsCompletedSuccessfully.Should().BeTrue();
|
||||
l[0].IsFaulted.Should().BeFalse();
|
||||
l[0].Result.Should().Be(2);
|
||||
|
||||
l[1].IsCompletedSuccessfully.Should().BeFalse();
|
||||
l[1].IsFaulted.Should().BeTrue();
|
||||
l[1].Exception.Message.Should().Be("ERROR");
|
||||
|
||||
l[2].IsCompletedSuccessfully.Should().BeTrue();
|
||||
l[2].IsFaulted.Should().BeFalse();
|
||||
l[2].Result.Should().Be(1);
|
||||
}
|
||||
|
||||
async UniTask<int> Delay(int id, int sleep)
|
||||
{
|
||||
await Task.Delay(sleep);
|
||||
return id;
|
||||
}
|
||||
|
||||
async UniTask<int> Delay2(int id, int sleep)
|
||||
{
|
||||
await Task.Delay(sleep);
|
||||
if (id == 3) throw new Exception("ERROR");
|
||||
return id;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ namespace NetCoreTests
|
||||
{
|
||||
CancellationTokenSource cts = new CancellationTokenSource();
|
||||
|
||||
var v = await UniTask.Run(() => 10).WithCancellation(cts.Token);
|
||||
var v = await UniTask.Run(() => 10).AttachExternalCancellation(cts.Token);
|
||||
|
||||
v.Should().Be(10);
|
||||
}
|
||||
@@ -30,7 +30,7 @@ namespace NetCoreTests
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(1));
|
||||
return 10;
|
||||
}).WithCancellation(cts.Token);
|
||||
}).AttachExternalCancellation(cts.Token);
|
||||
|
||||
cts.Cancel();
|
||||
|
||||
|
||||
@@ -20,8 +20,11 @@ public static class EditorRunnerChecker
|
||||
{
|
||||
Debug.Log("Start");
|
||||
|
||||
var r = await UnityWebRequest.Get("https://bing.com/").SendWebRequest().ToUniTask();
|
||||
Debug.Log(r.downloadHandler.text.Substring(0, 100));
|
||||
//var r = await UnityWebRequest.Get("https://bing.com/").SendWebRequest().ToUniTask();
|
||||
//Debug.Log(r.downloadHandler.text.Substring(0, 100));
|
||||
//await UniTask.Yield();
|
||||
|
||||
await UniTask.DelayFrame(30);
|
||||
|
||||
Debug.Log("End");
|
||||
}
|
||||
|
||||
@@ -87,7 +87,8 @@ namespace Cysharp.Threading.Tasks
|
||||
static Action<object> cancellationCallback = CancellationCallback;
|
||||
|
||||
static TaskPool<WaitAsyncSource> pool;
|
||||
WaitAsyncSource ITaskPoolNode<WaitAsyncSource>.NextNode { get; set; }
|
||||
WaitAsyncSource nextNode;
|
||||
ref WaitAsyncSource ITaskPoolNode<WaitAsyncSource>.NextNode => ref nextNode;
|
||||
|
||||
static WaitAsyncSource()
|
||||
{
|
||||
@@ -404,7 +405,8 @@ namespace Cysharp.Threading.Tasks
|
||||
static Action<object> cancellationCallback = CancellationCallback;
|
||||
|
||||
static TaskPool<WaitAsyncSource> pool;
|
||||
WaitAsyncSource ITaskPoolNode<WaitAsyncSource>.NextNode { get; set; }
|
||||
WaitAsyncSource nextNode;
|
||||
ref WaitAsyncSource ITaskPoolNode<WaitAsyncSource>.NextNode => ref nextNode;
|
||||
|
||||
static WaitAsyncSource()
|
||||
{
|
||||
|
||||
@@ -4,31 +4,29 @@ using System.Threading;
|
||||
using UnityEngine;
|
||||
using Cysharp.Threading.Tasks.Triggers;
|
||||
using System;
|
||||
using Cysharp.Threading.Tasks.Internal;
|
||||
|
||||
namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
public static class CancellationTokenSourceExtensions
|
||||
|
||||
public static partial class CancellationTokenSourceExtensions
|
||||
{
|
||||
public static void CancelAfterSlim(this CancellationTokenSource cts, int millisecondsDelay, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update)
|
||||
readonly static Action<object> CancelCancellationTokenSourceStateDelegate = new Action<object>(CancelCancellationTokenSourceState);
|
||||
|
||||
static void CancelCancellationTokenSourceState(object state)
|
||||
{
|
||||
var delay = UniTask.Delay(millisecondsDelay, delayType, delayTiming, cts.Token);
|
||||
CancelAfterCore(cts, delay).Forget();
|
||||
var cts = (CancellationTokenSource)state;
|
||||
cts.Cancel();
|
||||
}
|
||||
|
||||
public static void CancelAfterSlim(this CancellationTokenSource cts, TimeSpan delayTimeSpan, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update)
|
||||
public static IDisposable CancelAfterSlim(this CancellationTokenSource cts, int millisecondsDelay, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update)
|
||||
{
|
||||
var delay = UniTask.Delay(delayTimeSpan, delayType, delayTiming, cts.Token);
|
||||
CancelAfterCore(cts, delay).Forget();
|
||||
return CancelAfterSlim(cts, TimeSpan.FromMilliseconds(millisecondsDelay), delayType, delayTiming);
|
||||
}
|
||||
|
||||
static async UniTaskVoid CancelAfterCore(CancellationTokenSource cts, UniTask delayTask)
|
||||
public static IDisposable CancelAfterSlim(this CancellationTokenSource cts, TimeSpan delayTimeSpan, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update)
|
||||
{
|
||||
var alreadyCanceled = await delayTask.SuppressCancellationThrow();
|
||||
if (!alreadyCanceled)
|
||||
{
|
||||
cts.Cancel();
|
||||
cts.Dispose();
|
||||
}
|
||||
return PlayerLoopTimer.StartNew(delayTimeSpan, false, delayType, delayTiming, cts.Token, CancelCancellationTokenSourceStateDelegate, cts);
|
||||
}
|
||||
|
||||
public static void RegisterRaiseCancelOnDestroy(this CancellationTokenSource cts, Component component)
|
||||
@@ -39,11 +37,7 @@ namespace Cysharp.Threading.Tasks
|
||||
public static void RegisterRaiseCancelOnDestroy(this CancellationTokenSource cts, GameObject gameObject)
|
||||
{
|
||||
var trigger = gameObject.GetAsyncDestroyTrigger();
|
||||
trigger.CancellationToken.RegisterWithoutCaptureExecutionContext(state =>
|
||||
{
|
||||
var cts2 = (CancellationTokenSource)state;
|
||||
cts2.Cancel();
|
||||
}, cts);
|
||||
trigger.CancellationToken.RegisterWithoutCaptureExecutionContext(CancelCancellationTokenSourceStateDelegate, cts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +87,8 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
TaskPool.RegisterSizeGetter(typeof(AsyncUniTaskVoid<TStateMachine>), () => pool.Size);
|
||||
}
|
||||
|
||||
public AsyncUniTaskVoid<TStateMachine> NextNode { get; set; }
|
||||
AsyncUniTaskVoid<TStateMachine> nextNode;
|
||||
public ref AsyncUniTaskVoid<TStateMachine> NextNode => ref nextNode;
|
||||
|
||||
public void Return()
|
||||
{
|
||||
@@ -157,7 +158,8 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
result.stateMachine = stateMachine; // copy struct StateMachine(in release build).
|
||||
}
|
||||
|
||||
public AsyncUniTask<TStateMachine> NextNode { get; set; }
|
||||
AsyncUniTask<TStateMachine> nextNode;
|
||||
public ref AsyncUniTask<TStateMachine> NextNode => ref nextNode;
|
||||
|
||||
static AsyncUniTask()
|
||||
{
|
||||
@@ -279,7 +281,8 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||
result.stateMachine = stateMachine; // copy struct StateMachine(in release build).
|
||||
}
|
||||
|
||||
public AsyncUniTask<TStateMachine, T> NextNode { get; set; }
|
||||
AsyncUniTask<TStateMachine, T> nextNode;
|
||||
public ref AsyncUniTask<TStateMachine, T> NextNode => ref nextNode;
|
||||
|
||||
static AsyncUniTask()
|
||||
{
|
||||
|
||||
@@ -32,10 +32,24 @@ namespace Cysharp.Threading.Tasks
|
||||
return new UniTask(EnumeratorPromise.Create(enumerator, timing, cancellationToken, out var token), token);
|
||||
}
|
||||
|
||||
public static UniTask ToUniTask(this IEnumerator enumerator, MonoBehaviour coroutineRunner)
|
||||
{
|
||||
var source = AutoResetUniTaskCompletionSource.Create();
|
||||
coroutineRunner.StartCoroutine(Core(enumerator, coroutineRunner, source));
|
||||
return source.Task;
|
||||
}
|
||||
|
||||
static IEnumerator Core(IEnumerator inner, MonoBehaviour coroutineRunner, AutoResetUniTaskCompletionSource source)
|
||||
{
|
||||
yield return coroutineRunner.StartCoroutine(inner);
|
||||
source.TrySetResult();
|
||||
}
|
||||
|
||||
sealed class EnumeratorPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<EnumeratorPromise>
|
||||
{
|
||||
static TaskPool<EnumeratorPromise> pool;
|
||||
public EnumeratorPromise NextNode { get; set; }
|
||||
EnumeratorPromise nextNode;
|
||||
public ref EnumeratorPromise NextNode => ref nextNode;
|
||||
|
||||
static EnumeratorPromise()
|
||||
{
|
||||
@@ -45,6 +59,8 @@ namespace Cysharp.Threading.Tasks
|
||||
IEnumerator innerEnumerator;
|
||||
CancellationToken cancellationToken;
|
||||
int initialFrame;
|
||||
bool loopRunning;
|
||||
bool calledGetResult;
|
||||
|
||||
UniTaskCompletionSourceCore<object> core;
|
||||
|
||||
@@ -67,13 +83,18 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
result.innerEnumerator = ConsumeEnumerator(innerEnumerator);
|
||||
result.cancellationToken = cancellationToken;
|
||||
result.loopRunning = true;
|
||||
result.calledGetResult = false;
|
||||
result.initialFrame = -1;
|
||||
|
||||
PlayerLoopHelper.AddAction(timing, result);
|
||||
|
||||
token = result.core.Version;
|
||||
|
||||
result.MoveNext(); // run immediately.
|
||||
// run immediately.
|
||||
if (result.MoveNext())
|
||||
{
|
||||
PlayerLoopHelper.AddAction(timing, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -81,11 +102,15 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
try
|
||||
{
|
||||
calledGetResult = true;
|
||||
core.GetResult(token);
|
||||
}
|
||||
finally
|
||||
{
|
||||
TryReturn();
|
||||
if (!loopRunning)
|
||||
{
|
||||
TryReturn();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,8 +131,21 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (calledGetResult)
|
||||
{
|
||||
loopRunning = false;
|
||||
TryReturn();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (innerEnumerator == null) // invalid status, returned but loop running?
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
loopRunning = false;
|
||||
core.TrySetCanceled(cancellationToken);
|
||||
return false;
|
||||
}
|
||||
@@ -134,10 +172,12 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
loopRunning = false;
|
||||
core.TrySetException(ex);
|
||||
return false;
|
||||
}
|
||||
|
||||
loopRunning = false;
|
||||
core.TrySetResult(null);
|
||||
return false;
|
||||
}
|
||||
@@ -148,6 +188,7 @@ namespace Cysharp.Threading.Tasks
|
||||
core.Reset();
|
||||
innerEnumerator = default;
|
||||
cancellationToken = default;
|
||||
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
|
||||
@@ -162,11 +203,10 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
else if (current is CustomYieldInstruction)
|
||||
else if (current is CustomYieldInstruction cyi)
|
||||
{
|
||||
// WWW, WaitForSecondsRealtime
|
||||
var e2 = UnwrapWaitCustomYieldInstruction((CustomYieldInstruction)current);
|
||||
while (e2.MoveNext())
|
||||
while (cyi.keepWaiting)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
@@ -192,7 +232,7 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return null;
|
||||
goto WARN;
|
||||
}
|
||||
}
|
||||
else if (current is IEnumerator e3)
|
||||
@@ -205,17 +245,14 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
else
|
||||
{
|
||||
// WaitForEndOfFrame, WaitForFixedUpdate, others.
|
||||
yield return null;
|
||||
goto WARN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WWW and others as CustomYieldInstruction.
|
||||
static IEnumerator UnwrapWaitCustomYieldInstruction(CustomYieldInstruction yieldInstruction)
|
||||
{
|
||||
while (yieldInstruction.keepWaiting)
|
||||
{
|
||||
continue;
|
||||
|
||||
WARN:
|
||||
// WaitForEndOfFrame, WaitForFixedUpdate, others.
|
||||
UnityEngine.Debug.LogWarning($"yield {current.GetType().Name} is not supported on await IEnumerator or IEnumerator.ToUniTask(), please use ToUniTask(MonoBehaviour coroutineRunner) instead.");
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
@@ -247,5 +284,4 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,31 +7,33 @@ using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using System.Threading;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
|
||||
namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
public static class AddressablesAsyncExtensions
|
||||
{
|
||||
#region AsyncOperationHandle
|
||||
#region AsyncOperationHandle
|
||||
|
||||
public static UniTask.Awaiter GetAwaiter(this AsyncOperationHandle handle)
|
||||
{
|
||||
return ToUniTask(handle).GetAwaiter();
|
||||
}
|
||||
|
||||
public static UniTask WithCancellation(this AsyncOperationHandle handle, CancellationToken cancellationToken)
|
||||
public static UniTask WithCancellation(this AsyncOperationHandle handle, CancellationToken cancellationToken, bool cancelImmediately = false, bool autoReleaseWhenCanceled = false)
|
||||
{
|
||||
return ToUniTask(handle, cancellationToken: cancellationToken);
|
||||
return ToUniTask(handle, cancellationToken: cancellationToken, cancelImmediately: cancelImmediately, autoReleaseWhenCanceled: autoReleaseWhenCanceled);
|
||||
}
|
||||
|
||||
public static UniTask ToUniTask(this AsyncOperationHandle handle, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
|
||||
public static UniTask ToUniTask(this AsyncOperationHandle handle, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false, bool autoReleaseWhenCanceled = false)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled(cancellationToken);
|
||||
|
||||
if (!handle.IsValid())
|
||||
{
|
||||
throw new Exception("Attempting to use an invalid operation handle");
|
||||
// autoReleaseHandle:true handle is invalid(immediately internal handle == null) so return completed.
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
|
||||
if (handle.IsDone)
|
||||
@@ -43,7 +45,7 @@ namespace Cysharp.Threading.Tasks
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
|
||||
return new UniTask(AsyncOperationHandleConfiguredSource.Create(handle, timing, progress, cancellationToken, out var token), token);
|
||||
return new UniTask(AsyncOperationHandleConfiguredSource.Create(handle, timing, progress, cancellationToken, cancelImmediately, autoReleaseWhenCanceled, out var token), token);
|
||||
}
|
||||
|
||||
public struct AsyncOperationHandleAwaiter : ICriticalNotifyCompletion
|
||||
@@ -94,27 +96,31 @@ namespace Cysharp.Threading.Tasks
|
||||
sealed class AsyncOperationHandleConfiguredSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleConfiguredSource>
|
||||
{
|
||||
static TaskPool<AsyncOperationHandleConfiguredSource> pool;
|
||||
public AsyncOperationHandleConfiguredSource NextNode { get; set; }
|
||||
AsyncOperationHandleConfiguredSource nextNode;
|
||||
public ref AsyncOperationHandleConfiguredSource NextNode => ref nextNode;
|
||||
|
||||
static AsyncOperationHandleConfiguredSource()
|
||||
{
|
||||
TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleConfiguredSource), () => pool.Size);
|
||||
}
|
||||
|
||||
readonly Action<AsyncOperationHandle> continuationAction;
|
||||
readonly Action<AsyncOperationHandle> completedCallback;
|
||||
AsyncOperationHandle handle;
|
||||
CancellationToken cancellationToken;
|
||||
CancellationTokenRegistration cancellationTokenRegistration;
|
||||
IProgress<float> progress;
|
||||
bool autoReleaseWhenCanceled;
|
||||
bool cancelImmediately;
|
||||
bool completed;
|
||||
|
||||
UniTaskCompletionSourceCore<AsyncUnit> core;
|
||||
|
||||
AsyncOperationHandleConfiguredSource()
|
||||
{
|
||||
continuationAction = Continuation;
|
||||
completedCallback = HandleCompleted;
|
||||
}
|
||||
|
||||
public static IUniTaskSource Create(AsyncOperationHandle handle, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, out short token)
|
||||
public static IUniTaskSource Create(AsyncOperationHandle handle, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, bool cancelImmediately, bool autoReleaseWhenCanceled, out short token)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
@@ -129,47 +135,81 @@ namespace Cysharp.Threading.Tasks
|
||||
result.handle = handle;
|
||||
result.progress = progress;
|
||||
result.cancellationToken = cancellationToken;
|
||||
result.cancelImmediately = cancelImmediately;
|
||||
result.autoReleaseWhenCanceled = autoReleaseWhenCanceled;
|
||||
result.completed = false;
|
||||
|
||||
if (cancelImmediately && cancellationToken.CanBeCanceled)
|
||||
{
|
||||
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
|
||||
{
|
||||
var promise = (AsyncOperationHandleConfiguredSource)state;
|
||||
if (promise.autoReleaseWhenCanceled && promise.handle.IsValid())
|
||||
{
|
||||
Addressables.Release(promise.handle);
|
||||
}
|
||||
promise.core.TrySetCanceled(promise.cancellationToken);
|
||||
}, result);
|
||||
}
|
||||
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
PlayerLoopHelper.AddAction(timing, result);
|
||||
|
||||
handle.Completed += result.continuationAction;
|
||||
handle.Completed += result.completedCallback;
|
||||
|
||||
token = result.core.Version;
|
||||
return result;
|
||||
}
|
||||
|
||||
void Continuation(AsyncOperationHandle _)
|
||||
void HandleCompleted(AsyncOperationHandle _)
|
||||
{
|
||||
handle.Completed -= continuationAction;
|
||||
if (handle.IsValid())
|
||||
{
|
||||
handle.Completed -= completedCallback;
|
||||
}
|
||||
|
||||
if (completed)
|
||||
{
|
||||
TryReturn();
|
||||
return;
|
||||
}
|
||||
|
||||
completed = true;
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
if (autoReleaseWhenCanceled && handle.IsValid())
|
||||
{
|
||||
Addressables.Release(handle);
|
||||
}
|
||||
core.TrySetCanceled(cancellationToken);
|
||||
}
|
||||
else if (handle.Status == AsyncOperationStatus.Failed)
|
||||
{
|
||||
core.TrySetException(handle.OperationException);
|
||||
}
|
||||
else
|
||||
{
|
||||
completed = true;
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
core.TrySetCanceled(cancellationToken);
|
||||
}
|
||||
else if (handle.Status == AsyncOperationStatus.Failed)
|
||||
{
|
||||
core.TrySetException(handle.OperationException);
|
||||
}
|
||||
else
|
||||
{
|
||||
core.TrySetResult(AsyncUnit.Default);
|
||||
}
|
||||
core.TrySetResult(AsyncUnit.Default);
|
||||
}
|
||||
}
|
||||
|
||||
public void GetResult(short token)
|
||||
{
|
||||
core.GetResult(token);
|
||||
try
|
||||
{
|
||||
core.GetResult(token);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
|
||||
{
|
||||
TryReturn();
|
||||
}
|
||||
else
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public UniTaskStatus GetStatus(short token)
|
||||
@@ -191,20 +231,23 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
if (completed)
|
||||
{
|
||||
TryReturn();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
completed = true;
|
||||
if (autoReleaseWhenCanceled && handle.IsValid())
|
||||
{
|
||||
Addressables.Release(handle);
|
||||
}
|
||||
core.TrySetCanceled(cancellationToken);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (progress != null && handle.IsValid())
|
||||
{
|
||||
progress.Report(handle.PercentComplete);
|
||||
progress.Report(handle.GetDownloadStatus().Percent);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -217,25 +260,26 @@ namespace Cysharp.Threading.Tasks
|
||||
handle = default;
|
||||
progress = default;
|
||||
cancellationToken = default;
|
||||
cancellationTokenRegistration.Dispose();
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region AsyncOperationHandle_T
|
||||
#region AsyncOperationHandle_T
|
||||
|
||||
public static UniTask<T>.Awaiter GetAwaiter<T>(this AsyncOperationHandle<T> handle)
|
||||
{
|
||||
return ToUniTask(handle).GetAwaiter();
|
||||
}
|
||||
|
||||
public static UniTask<T> WithCancellation<T>(this AsyncOperationHandle<T> handle, CancellationToken cancellationToken)
|
||||
public static UniTask<T> WithCancellation<T>(this AsyncOperationHandle<T> handle, CancellationToken cancellationToken, bool cancelImmediately = false, bool autoReleaseWhenCanceled = false)
|
||||
{
|
||||
return ToUniTask(handle, cancellationToken: cancellationToken);
|
||||
return ToUniTask(handle, cancellationToken: cancellationToken, cancelImmediately: cancelImmediately, autoReleaseWhenCanceled: autoReleaseWhenCanceled);
|
||||
}
|
||||
|
||||
public static UniTask<T> ToUniTask<T>(this AsyncOperationHandle<T> handle, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
|
||||
public static UniTask<T> ToUniTask<T>(this AsyncOperationHandle<T> handle, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false, bool autoReleaseWhenCanceled = false)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled<T>(cancellationToken);
|
||||
|
||||
@@ -253,33 +297,37 @@ namespace Cysharp.Threading.Tasks
|
||||
return UniTask.FromResult(handle.Result);
|
||||
}
|
||||
|
||||
return new UniTask<T>(AsyncOperationHandleConfiguredSource<T>.Create(handle, timing, progress, cancellationToken, out var token), token);
|
||||
return new UniTask<T>(AsyncOperationHandleConfiguredSource<T>.Create(handle, timing, progress, cancellationToken, cancelImmediately, autoReleaseWhenCanceled, out var token), token);
|
||||
}
|
||||
|
||||
sealed class AsyncOperationHandleConfiguredSource<T> : IUniTaskSource<T>, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleConfiguredSource<T>>
|
||||
{
|
||||
static TaskPool<AsyncOperationHandleConfiguredSource<T>> pool;
|
||||
public AsyncOperationHandleConfiguredSource<T> NextNode { get; set; }
|
||||
AsyncOperationHandleConfiguredSource<T> nextNode;
|
||||
public ref AsyncOperationHandleConfiguredSource<T> NextNode => ref nextNode;
|
||||
|
||||
static AsyncOperationHandleConfiguredSource()
|
||||
{
|
||||
TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleConfiguredSource<T>), () => pool.Size);
|
||||
}
|
||||
|
||||
readonly Action<AsyncOperationHandle<T>> continuationAction;
|
||||
readonly Action<AsyncOperationHandle<T>> completedCallback;
|
||||
AsyncOperationHandle<T> handle;
|
||||
CancellationToken cancellationToken;
|
||||
CancellationTokenRegistration cancellationTokenRegistration;
|
||||
IProgress<float> progress;
|
||||
bool autoReleaseWhenCanceled;
|
||||
bool cancelImmediately;
|
||||
bool completed;
|
||||
|
||||
UniTaskCompletionSourceCore<T> core;
|
||||
|
||||
AsyncOperationHandleConfiguredSource()
|
||||
{
|
||||
continuationAction = Continuation;
|
||||
completedCallback = HandleCompleted;
|
||||
}
|
||||
|
||||
public static IUniTaskSource<T> Create(AsyncOperationHandle<T> handle, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, out short token)
|
||||
public static IUniTaskSource<T> Create(AsyncOperationHandle<T> handle, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, bool cancelImmediately, bool autoReleaseWhenCanceled, out short token)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
@@ -295,46 +343,79 @@ namespace Cysharp.Threading.Tasks
|
||||
result.cancellationToken = cancellationToken;
|
||||
result.completed = false;
|
||||
result.progress = progress;
|
||||
result.autoReleaseWhenCanceled = autoReleaseWhenCanceled;
|
||||
result.cancelImmediately = cancelImmediately;
|
||||
|
||||
if (cancelImmediately && cancellationToken.CanBeCanceled)
|
||||
{
|
||||
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
|
||||
{
|
||||
var promise = (AsyncOperationHandleConfiguredSource<T>)state;
|
||||
if (promise.autoReleaseWhenCanceled && promise.handle.IsValid())
|
||||
{
|
||||
Addressables.Release(promise.handle);
|
||||
}
|
||||
promise.core.TrySetCanceled(promise.cancellationToken);
|
||||
}, result);
|
||||
}
|
||||
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
PlayerLoopHelper.AddAction(timing, result);
|
||||
|
||||
handle.Completed += result.continuationAction;
|
||||
handle.Completed += result.completedCallback;
|
||||
|
||||
token = result.core.Version;
|
||||
return result;
|
||||
}
|
||||
|
||||
void Continuation(AsyncOperationHandle<T> argHandle)
|
||||
void HandleCompleted(AsyncOperationHandle<T> argHandle)
|
||||
{
|
||||
handle.Completed -= continuationAction;
|
||||
if (handle.IsValid())
|
||||
{
|
||||
handle.Completed -= completedCallback;
|
||||
}
|
||||
|
||||
if (completed)
|
||||
{
|
||||
TryReturn();
|
||||
return;
|
||||
}
|
||||
completed = true;
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
if (autoReleaseWhenCanceled && handle.IsValid())
|
||||
{
|
||||
Addressables.Release(handle);
|
||||
}
|
||||
core.TrySetCanceled(cancellationToken);
|
||||
}
|
||||
else if (argHandle.Status == AsyncOperationStatus.Failed)
|
||||
{
|
||||
core.TrySetException(argHandle.OperationException);
|
||||
}
|
||||
else
|
||||
{
|
||||
completed = true;
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
core.TrySetCanceled(cancellationToken);
|
||||
}
|
||||
else if (argHandle.Status == AsyncOperationStatus.Failed)
|
||||
{
|
||||
core.TrySetException(argHandle.OperationException);
|
||||
}
|
||||
else
|
||||
{
|
||||
core.TrySetResult(argHandle.Result);
|
||||
}
|
||||
core.TrySetResult(argHandle.Result);
|
||||
}
|
||||
}
|
||||
|
||||
public T GetResult(short token)
|
||||
{
|
||||
return core.GetResult(token);
|
||||
try
|
||||
{
|
||||
return core.GetResult(token);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
|
||||
{
|
||||
TryReturn();
|
||||
}
|
||||
else
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IUniTaskSource.GetResult(short token)
|
||||
@@ -361,20 +442,23 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
if (completed)
|
||||
{
|
||||
TryReturn();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
completed = true;
|
||||
if (autoReleaseWhenCanceled && handle.IsValid())
|
||||
{
|
||||
Addressables.Release(handle);
|
||||
}
|
||||
core.TrySetCanceled(cancellationToken);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (progress != null && handle.IsValid())
|
||||
{
|
||||
progress.Report(handle.PercentComplete);
|
||||
progress.Report(handle.GetDownloadStatus().Percent);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -387,11 +471,12 @@ namespace Cysharp.Threading.Tasks
|
||||
handle = default;
|
||||
progress = default;
|
||||
cancellationToken = default;
|
||||
cancellationTokenRegistration.Dispose();
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
"name": "UniTask.Addressables",
|
||||
"references": [
|
||||
"UniTask",
|
||||
"Unity.ResourceManager"
|
||||
"Unity.ResourceManager",
|
||||
"Unity.Addressables"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
@@ -16,7 +17,12 @@
|
||||
"name": "com.unity.addressables",
|
||||
"expression": "",
|
||||
"define": "UNITASK_ADDRESSABLE_SUPPORT"
|
||||
},
|
||||
{
|
||||
"name": "com.unity.addressables.cn",
|
||||
"expression": "",
|
||||
"define": "UNITASK_ADDRESSABLE_SUPPORT"
|
||||
}
|
||||
],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,14 +16,14 @@ namespace Cysharp.Threading.Tasks
|
||||
Kill,
|
||||
KillWithCompleteCallback,
|
||||
Complete,
|
||||
CompleteWithSeqeunceCallback,
|
||||
CompleteWithSequenceCallback,
|
||||
CancelAwait,
|
||||
|
||||
// AndCancelAwait
|
||||
KillAndCancelAwait,
|
||||
KillWithCompleteCallbackAndCancelAwait,
|
||||
CompleteAndCancelAwait,
|
||||
CompleteWithSeqeunceCallbackAndCancelAwait
|
||||
CompleteWithSequenceCallbackAndCancelAwait
|
||||
}
|
||||
|
||||
public static class DOTweenAsyncExtensions
|
||||
@@ -135,31 +135,29 @@ namespace Cysharp.Threading.Tasks
|
||||
sealed class TweenConfiguredSource : IUniTaskSource, ITaskPoolNode<TweenConfiguredSource>
|
||||
{
|
||||
static TaskPool<TweenConfiguredSource> pool;
|
||||
public TweenConfiguredSource NextNode { get; set; }
|
||||
TweenConfiguredSource nextNode;
|
||||
public ref TweenConfiguredSource NextNode => ref nextNode;
|
||||
|
||||
static TweenConfiguredSource()
|
||||
{
|
||||
TaskPool.RegisterSizeGetter(typeof(TweenConfiguredSource), () => pool.Size);
|
||||
}
|
||||
|
||||
static readonly TweenCallback EmptyTweenCallback = () => { };
|
||||
|
||||
readonly TweenCallback onCompleteCallbackDelegate;
|
||||
readonly TweenCallback onUpdateDelegate;
|
||||
|
||||
Tween tween;
|
||||
TweenCancelBehaviour cancelBehaviour;
|
||||
CancellationToken cancellationToken;
|
||||
CancellationTokenRegistration cancellationRegistration;
|
||||
CallbackType callbackType;
|
||||
bool canceled;
|
||||
|
||||
TweenCallback originalUpdateAction;
|
||||
TweenCallback originalCompleteAction;
|
||||
UniTaskCompletionSourceCore<AsyncUnit> core;
|
||||
|
||||
TweenConfiguredSource()
|
||||
{
|
||||
onCompleteCallbackDelegate = OnCompleteCallbackDelegate;
|
||||
onUpdateDelegate = OnUpdate;
|
||||
}
|
||||
|
||||
public static IUniTaskSource Create(Tween tween, TweenCancelBehaviour cancelBehaviour, CancellationToken cancellationToken, CallbackType callbackType, out short token)
|
||||
@@ -179,41 +177,87 @@ namespace Cysharp.Threading.Tasks
|
||||
result.cancelBehaviour = cancelBehaviour;
|
||||
result.cancellationToken = cancellationToken;
|
||||
result.callbackType = callbackType;
|
||||
|
||||
result.originalUpdateAction = tween.onUpdate;
|
||||
result.canceled = false;
|
||||
|
||||
if (result.originalUpdateAction == result.onUpdateDelegate)
|
||||
{
|
||||
result.originalUpdateAction = null;
|
||||
}
|
||||
|
||||
tween.onUpdate = result.onUpdateDelegate;
|
||||
|
||||
switch (callbackType)
|
||||
{
|
||||
case CallbackType.Kill:
|
||||
result.originalCompleteAction = tween.onKill;
|
||||
tween.onKill = result.onCompleteCallbackDelegate;
|
||||
break;
|
||||
case CallbackType.Complete:
|
||||
result.originalCompleteAction = tween.onComplete;
|
||||
tween.onComplete = result.onCompleteCallbackDelegate;
|
||||
break;
|
||||
case CallbackType.Pause:
|
||||
result.originalCompleteAction = tween.onPause;
|
||||
tween.onPause = result.onCompleteCallbackDelegate;
|
||||
break;
|
||||
case CallbackType.Play:
|
||||
result.originalCompleteAction = tween.onPlay;
|
||||
tween.onPlay = result.onCompleteCallbackDelegate;
|
||||
break;
|
||||
case CallbackType.Rewind:
|
||||
result.originalCompleteAction = tween.onRewind;
|
||||
tween.onRewind = result.onCompleteCallbackDelegate;
|
||||
break;
|
||||
case CallbackType.StepComplete:
|
||||
result.originalCompleteAction = tween.onStepComplete;
|
||||
tween.onStepComplete = result.onCompleteCallbackDelegate;
|
||||
break;
|
||||
default:
|
||||
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);
|
||||
|
||||
token = result.core.Version;
|
||||
@@ -222,87 +266,28 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
void OnCompleteCallbackDelegate()
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
if (this.cancelBehaviour == TweenCancelBehaviour.KillAndCancelAwait
|
||||
|| this.cancelBehaviour == TweenCancelBehaviour.KillWithCompleteCallbackAndCancelAwait
|
||||
|| this.cancelBehaviour == TweenCancelBehaviour.CompleteAndCancelAwait
|
||||
|| this.cancelBehaviour == TweenCancelBehaviour.CompleteWithSequenceCallbackAndCancelAwait
|
||||
|| this.cancelBehaviour == TweenCancelBehaviour.CancelAwait)
|
||||
{
|
||||
canceled = true;
|
||||
}
|
||||
}
|
||||
if (canceled)
|
||||
{
|
||||
core.TrySetCanceled(cancellationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
originalCompleteAction?.Invoke();
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -327,10 +312,10 @@ namespace Cysharp.Threading.Tasks
|
||||
case TweenCancelBehaviour.CompleteAndCancelAwait:
|
||||
tween.Complete(false);
|
||||
break;
|
||||
case TweenCancelBehaviour.CompleteWithSeqeunceCallback:
|
||||
case TweenCancelBehaviour.CompleteWithSequenceCallback:
|
||||
tween.Complete(true);
|
||||
break;
|
||||
case TweenCancelBehaviour.CompleteWithSeqeunceCallbackAndCancelAwait:
|
||||
case TweenCancelBehaviour.CompleteWithSequenceCallbackAndCancelAwait:
|
||||
tween.Complete(true);
|
||||
break;
|
||||
case TweenCancelBehaviour.CancelAwait:
|
||||
@@ -369,36 +354,41 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.Reset();
|
||||
tween.onUpdate = originalUpdateAction;
|
||||
cancellationRegistration.Dispose();
|
||||
|
||||
RestoreOriginalCallback();
|
||||
|
||||
tween = default;
|
||||
cancellationToken = default;
|
||||
originalCompleteAction = default;
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
|
||||
void RestoreOriginalCallback()
|
||||
{
|
||||
switch (callbackType)
|
||||
{
|
||||
case CallbackType.Kill:
|
||||
tween.onKill = null;
|
||||
tween.onKill = originalCompleteAction;
|
||||
break;
|
||||
case CallbackType.Complete:
|
||||
tween.onComplete = null;
|
||||
tween.onComplete = originalCompleteAction;
|
||||
break;
|
||||
case CallbackType.Pause:
|
||||
tween.onPause = null;
|
||||
tween.onPause = originalCompleteAction;
|
||||
break;
|
||||
case CallbackType.Play:
|
||||
tween.onPlay = null;
|
||||
tween.onPlay = originalCompleteAction;
|
||||
break;
|
||||
case CallbackType.Rewind:
|
||||
tween.onRewind = null;
|
||||
tween.onRewind = originalCompleteAction;
|
||||
break;
|
||||
case CallbackType.StepComplete:
|
||||
tween.onStepComplete = null;
|
||||
tween.onStepComplete = originalCompleteAction;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
tween = default;
|
||||
cancellationToken = default;
|
||||
originalUpdateAction = default;
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,12 @@
|
||||
"name": "com.unity.textmeshpro",
|
||||
"expression": "",
|
||||
"define": "UNITASK_TEXTMESHPRO_SUPPORT"
|
||||
},
|
||||
{
|
||||
"name": "com.unity.ugui",
|
||||
"expression": "2.0.0",
|
||||
"define": "UNITASK_TEXTMESHPRO_SUPPORT"
|
||||
}
|
||||
],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable CS0108
|
||||
|
||||
#if (UNITASK_NETCORE && !NETSTANDARD2_0) || UNITY_2022_3_OR_NEWER
|
||||
#define SUPPORT_VALUETASK
|
||||
#endif
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
@@ -19,9 +24,8 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
// similar as IValueTaskSource
|
||||
public interface IUniTaskSource
|
||||
#if !UNITY_2018_3_OR_NEWER && !NETSTANDARD2_0
|
||||
#if SUPPORT_VALUETASK
|
||||
: System.Threading.Tasks.Sources.IValueTaskSource
|
||||
#pragma warning disable CS0108
|
||||
#endif
|
||||
{
|
||||
UniTaskStatus GetStatus(short token);
|
||||
@@ -30,8 +34,7 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
UniTaskStatus UnsafeGetStatus(); // only for debug use.
|
||||
|
||||
#if !UNITY_2018_3_OR_NEWER && !NETSTANDARD2_0
|
||||
#pragma warning restore CS0108
|
||||
#if SUPPORT_VALUETASK
|
||||
|
||||
System.Threading.Tasks.Sources.ValueTaskSourceStatus System.Threading.Tasks.Sources.IValueTaskSource.GetStatus(short token)
|
||||
{
|
||||
@@ -53,13 +56,13 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
|
||||
public interface IUniTaskSource<out T> : IUniTaskSource
|
||||
#if !UNITY_2018_3_OR_NEWER && !NETSTANDARD2_0
|
||||
#if SUPPORT_VALUETASK
|
||||
, System.Threading.Tasks.Sources.IValueTaskSource<T>
|
||||
#endif
|
||||
{
|
||||
new T GetResult(short token);
|
||||
|
||||
#if !UNITY_2018_3_OR_NEWER && !NETSTANDARD2_0
|
||||
#if SUPPORT_VALUETASK
|
||||
|
||||
new public UniTaskStatus GetStatus(short token)
|
||||
{
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Cysharp.Threading.Tasks.Internal
|
||||
|
||||
readonly PlayerLoopTiming timing;
|
||||
|
||||
SpinLock gate = new SpinLock();
|
||||
SpinLock gate = new SpinLock(false);
|
||||
bool dequing = false;
|
||||
|
||||
int actionListCount = 0;
|
||||
@@ -70,13 +70,17 @@ namespace Cysharp.Threading.Tasks.Internal
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
public int Clear()
|
||||
{
|
||||
var rest = actionListCount + waitingListCount;
|
||||
|
||||
actionListCount = 0;
|
||||
actionList = new Action[InitialSize];
|
||||
|
||||
waitingListCount = 0;
|
||||
waitingList = new Action[InitialSize];
|
||||
|
||||
return rest;
|
||||
}
|
||||
|
||||
// delegate entrypoint.
|
||||
@@ -128,6 +132,14 @@ namespace Cysharp.Threading.Tasks.Internal
|
||||
case PlayerLoopTiming.LastPostLateUpdate:
|
||||
LastPostLateUpdate();
|
||||
break;
|
||||
#if UNITY_2020_2_OR_NEWER
|
||||
case PlayerLoopTiming.TimeUpdate:
|
||||
TimeUpdate();
|
||||
break;
|
||||
case PlayerLoopTiming.LastTimeUpdate:
|
||||
LastTimeUpdate();
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -150,6 +162,10 @@ namespace Cysharp.Threading.Tasks.Internal
|
||||
void LastPreLateUpdate() => RunCore();
|
||||
void PostLateUpdate() => RunCore();
|
||||
void LastPostLateUpdate() => RunCore();
|
||||
#if UNITY_2020_2_OR_NEWER
|
||||
void TimeUpdate() => RunCore();
|
||||
void LastTimeUpdate() => RunCore();
|
||||
#endif
|
||||
|
||||
[System.Diagnostics.DebuggerHidden]
|
||||
void RunCore()
|
||||
@@ -170,9 +186,9 @@ namespace Cysharp.Threading.Tasks.Internal
|
||||
|
||||
for (int i = 0; i < actionListCount; i++)
|
||||
{
|
||||
|
||||
var action = actionList[i];
|
||||
actionList[i] = null;
|
||||
|
||||
try
|
||||
{
|
||||
action();
|
||||
|
||||
@@ -148,7 +148,7 @@ namespace Cysharp.Threading.Tasks.Internal
|
||||
|
||||
foreach (var candidateMethod in methods)
|
||||
{
|
||||
var attributes = candidateMethod.GetCustomAttributes<StateMachineAttribute>();
|
||||
var attributes = candidateMethod.GetCustomAttributes<StateMachineAttribute>(false);
|
||||
if (attributes == null)
|
||||
{
|
||||
continue;
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace Cysharp.Threading.Tasks.Internal
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void ThrowArgumentException<T>(string message)
|
||||
public static void ThrowArgumentException(string message)
|
||||
{
|
||||
throw new ArgumentException(message);
|
||||
}
|
||||
|
||||
@@ -48,14 +48,24 @@ namespace Cysharp.Threading.Tasks.Internal
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
public int Clear()
|
||||
{
|
||||
lock (arrayLock)
|
||||
{
|
||||
var rest = 0;
|
||||
|
||||
for (var index = 0; index < loopItems.Length; index++)
|
||||
{
|
||||
if (loopItems[index] != null)
|
||||
{
|
||||
rest++;
|
||||
}
|
||||
|
||||
loopItems[index] = null;
|
||||
}
|
||||
|
||||
tail = 0;
|
||||
return rest;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,6 +118,14 @@ namespace Cysharp.Threading.Tasks.Internal
|
||||
case PlayerLoopTiming.LastPostLateUpdate:
|
||||
LastPostLateUpdate();
|
||||
break;
|
||||
#if UNITY_2020_2_OR_NEWER
|
||||
case PlayerLoopTiming.TimeUpdate:
|
||||
TimeUpdate();
|
||||
break;
|
||||
case PlayerLoopTiming.LastTimeUpdate:
|
||||
LastTimeUpdate();
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -130,6 +148,10 @@ namespace Cysharp.Threading.Tasks.Internal
|
||||
void LastPreLateUpdate() => RunCore();
|
||||
void PostLateUpdate() => RunCore();
|
||||
void LastPostLateUpdate() => RunCore();
|
||||
#if UNITY_2020_2_OR_NEWER
|
||||
void TimeUpdate() => RunCore();
|
||||
void LastTimeUpdate() => RunCore();
|
||||
#endif
|
||||
|
||||
[System.Diagnostics.DebuggerHidden]
|
||||
void RunCore()
|
||||
@@ -143,7 +165,6 @@ namespace Cysharp.Threading.Tasks.Internal
|
||||
{
|
||||
var j = tail - 1;
|
||||
|
||||
// eliminate array-bound check for i
|
||||
for (int i = 0; i < loopItems.Length; i++)
|
||||
{
|
||||
var action = loopItems[i];
|
||||
|
||||
@@ -7,7 +7,8 @@ namespace Cysharp.Threading.Tasks.Internal
|
||||
{
|
||||
static TaskPool<PooledDelegate<T>> pool;
|
||||
|
||||
public PooledDelegate<T> NextNode { get; set; }
|
||||
PooledDelegate<T> nextNode;
|
||||
public ref PooledDelegate<T> NextNode => ref nextNode;
|
||||
|
||||
static PooledDelegate()
|
||||
{
|
||||
|
||||
@@ -7,6 +7,8 @@ using UnityEngine.Networking;
|
||||
|
||||
namespace Cysharp.Threading.Tasks.Internal
|
||||
{
|
||||
#if ENABLE_UNITYWEBREQUEST && (!UNITY_2019_1_OR_NEWER || UNITASK_WEBREQUEST_SUPPORT)
|
||||
|
||||
internal static class UnityWebRequestResultExtensions
|
||||
{
|
||||
public static bool IsError(this UnityWebRequest unityWebRequest)
|
||||
@@ -21,4 +23,6 @@ namespace Cysharp.Threading.Tasks.Internal
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
@@ -18,6 +18,8 @@ namespace Cysharp.Threading.Tasks.Internal
|
||||
|
||||
public TimeSpan Elapsed => TimeSpan.FromTicks(this.ElapsedTicks);
|
||||
|
||||
public bool IsInvalid => startTimestamp == 0;
|
||||
|
||||
public long ElapsedTicks
|
||||
{
|
||||
get
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
{
|
||||
Error.ThrowArgumentNullException(source, nameof(source));
|
||||
|
||||
return new AppendPrepend<TSource>(source, element, true);
|
||||
return new AppendPrepend<TSource>(source, element, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
|
||||
if (enumerator == null)
|
||||
{
|
||||
if (state == State.RequireAppend)
|
||||
if (state == State.RequirePrepend)
|
||||
{
|
||||
Current = element;
|
||||
state = State.None;
|
||||
|
||||
@@ -52,6 +52,7 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
public UniTask DisposeAsync()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
writer.Dispose();
|
||||
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;
|
||||
|
||||
@@ -137,6 +138,15 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
{
|
||||
this.enumerator = enumerator;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
var status = core.GetStatus(core.Version);
|
||||
if (status == UniTaskStatus.Pending)
|
||||
{
|
||||
core.TrySetCanceled();
|
||||
}
|
||||
}
|
||||
|
||||
public void GetResult(short token)
|
||||
{
|
||||
|
||||
234
src/UniTask/Assets/Plugins/UniTask/Runtime/Linq/Merge.cs
Normal file
234
src/UniTask/Assets/Plugins/UniTask/Runtime/Linq/Merge.cs
Normal file
@@ -0,0 +1,234 @@
|
||||
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 sources is IUniTaskAsyncEnumerable<T>[] array
|
||||
? new Merge<T>(array)
|
||||
: 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
|
||||
@@ -4,38 +4,50 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
{
|
||||
public static partial class UniTaskAsyncEnumerable
|
||||
{
|
||||
public static IUniTaskAsyncEnumerable<AsyncUnit> EveryUpdate(PlayerLoopTiming updateTiming = PlayerLoopTiming.Update)
|
||||
public static IUniTaskAsyncEnumerable<AsyncUnit> EveryUpdate(PlayerLoopTiming updateTiming = PlayerLoopTiming.Update, bool cancelImmediately = false)
|
||||
{
|
||||
return new EveryUpdate(updateTiming);
|
||||
return new EveryUpdate(updateTiming, cancelImmediately);
|
||||
}
|
||||
}
|
||||
|
||||
internal class EveryUpdate : IUniTaskAsyncEnumerable<AsyncUnit>
|
||||
{
|
||||
readonly PlayerLoopTiming updateTiming;
|
||||
readonly bool cancelImmediately;
|
||||
|
||||
public EveryUpdate(PlayerLoopTiming updateTiming)
|
||||
public EveryUpdate(PlayerLoopTiming updateTiming, bool cancelImmediately)
|
||||
{
|
||||
this.updateTiming = updateTiming;
|
||||
this.cancelImmediately = cancelImmediately;
|
||||
}
|
||||
|
||||
public IUniTaskAsyncEnumerator<AsyncUnit> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return new _EveryUpdate(updateTiming, cancellationToken);
|
||||
return new _EveryUpdate(updateTiming, cancellationToken, cancelImmediately);
|
||||
}
|
||||
|
||||
class _EveryUpdate : MoveNextSource, IUniTaskAsyncEnumerator<AsyncUnit>, IPlayerLoopItem
|
||||
{
|
||||
readonly PlayerLoopTiming updateTiming;
|
||||
CancellationToken cancellationToken;
|
||||
readonly CancellationToken cancellationToken;
|
||||
readonly CancellationTokenRegistration cancellationTokenRegistration;
|
||||
|
||||
bool disposed;
|
||||
|
||||
public _EveryUpdate(PlayerLoopTiming updateTiming, CancellationToken cancellationToken)
|
||||
public _EveryUpdate(PlayerLoopTiming updateTiming, CancellationToken cancellationToken, bool cancelImmediately)
|
||||
{
|
||||
this.updateTiming = updateTiming;
|
||||
this.cancellationToken = cancellationToken;
|
||||
|
||||
if (cancelImmediately && cancellationToken.CanBeCanceled)
|
||||
{
|
||||
cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
|
||||
{
|
||||
var source = (_EveryUpdate)state;
|
||||
source.completionSource.TrySetCanceled(source.cancellationToken);
|
||||
}, this);
|
||||
}
|
||||
|
||||
TaskTracker.TrackActiveTask(this, 2);
|
||||
PlayerLoopHelper.AddAction(updateTiming, this);
|
||||
}
|
||||
@@ -44,10 +56,14 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
// return false instead of throw
|
||||
if (disposed || cancellationToken.IsCancellationRequested) return CompletedTasks.False;
|
||||
|
||||
if (disposed) return CompletedTasks.False;
|
||||
|
||||
completionSource.Reset();
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
completionSource.TrySetCanceled(cancellationToken);
|
||||
}
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
@@ -55,6 +71,7 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
{
|
||||
if (!disposed)
|
||||
{
|
||||
cancellationTokenRegistration.Dispose();
|
||||
disposed = true;
|
||||
TaskTracker.RemoveTracking(this);
|
||||
}
|
||||
@@ -63,7 +80,13 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (disposed || cancellationToken.IsCancellationRequested)
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
completionSource.TrySetCanceled(cancellationToken);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (disposed)
|
||||
{
|
||||
completionSource.TrySetResult(false);
|
||||
return false;
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
{
|
||||
public static partial class UniTaskAsyncEnumerable
|
||||
{
|
||||
public static IUniTaskAsyncEnumerable<TProperty> EveryValueChanged<TTarget, TProperty>(TTarget target, Func<TTarget, TProperty> propertySelector, PlayerLoopTiming monitorTiming = PlayerLoopTiming.Update, IEqualityComparer<TProperty> equalityComparer = null)
|
||||
public static IUniTaskAsyncEnumerable<TProperty> EveryValueChanged<TTarget, TProperty>(TTarget target, Func<TTarget, TProperty> propertySelector, PlayerLoopTiming monitorTiming = PlayerLoopTiming.Update, IEqualityComparer<TProperty> equalityComparer = null, bool cancelImmediately = false)
|
||||
where TTarget : class
|
||||
{
|
||||
var unityObject = target as UnityEngine.Object;
|
||||
@@ -15,11 +15,11 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
|
||||
if (isUnityObject)
|
||||
{
|
||||
return new EveryValueChangedUnityObject<TTarget, TProperty>(target, propertySelector, equalityComparer ?? UnityEqualityComparer.GetDefault<TProperty>(), monitorTiming);
|
||||
return new EveryValueChangedUnityObject<TTarget, TProperty>(target, propertySelector, equalityComparer ?? UnityEqualityComparer.GetDefault<TProperty>(), monitorTiming, cancelImmediately);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new EveryValueChangedStandardObject<TTarget, TProperty>(target, propertySelector, equalityComparer ?? UnityEqualityComparer.GetDefault<TProperty>(), monitorTiming);
|
||||
return new EveryValueChangedStandardObject<TTarget, TProperty>(target, propertySelector, equalityComparer ?? UnityEqualityComparer.GetDefault<TProperty>(), monitorTiming, cancelImmediately);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,18 +30,20 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
readonly Func<TTarget, TProperty> propertySelector;
|
||||
readonly IEqualityComparer<TProperty> equalityComparer;
|
||||
readonly PlayerLoopTiming monitorTiming;
|
||||
readonly bool cancelImmediately;
|
||||
|
||||
public EveryValueChangedUnityObject(TTarget target, Func<TTarget, TProperty> propertySelector, IEqualityComparer<TProperty> equalityComparer, PlayerLoopTiming monitorTiming)
|
||||
public EveryValueChangedUnityObject(TTarget target, Func<TTarget, TProperty> propertySelector, IEqualityComparer<TProperty> equalityComparer, PlayerLoopTiming monitorTiming, bool cancelImmediately)
|
||||
{
|
||||
this.target = target;
|
||||
this.propertySelector = propertySelector;
|
||||
this.equalityComparer = equalityComparer;
|
||||
this.monitorTiming = monitorTiming;
|
||||
this.cancelImmediately = cancelImmediately;
|
||||
}
|
||||
|
||||
public IUniTaskAsyncEnumerator<TProperty> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return new _EveryValueChanged(target, propertySelector, equalityComparer, monitorTiming, cancellationToken);
|
||||
return new _EveryValueChanged(target, propertySelector, equalityComparer, monitorTiming, cancellationToken, cancelImmediately);
|
||||
}
|
||||
|
||||
sealed class _EveryValueChanged : MoveNextSource, IUniTaskAsyncEnumerator<TProperty>, IPlayerLoopItem
|
||||
@@ -50,13 +52,14 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
readonly UnityEngine.Object targetAsUnityObject;
|
||||
readonly IEqualityComparer<TProperty> equalityComparer;
|
||||
readonly Func<TTarget, TProperty> propertySelector;
|
||||
CancellationToken cancellationToken;
|
||||
readonly CancellationToken cancellationToken;
|
||||
readonly CancellationTokenRegistration cancellationTokenRegistration;
|
||||
|
||||
bool first;
|
||||
TProperty currentValue;
|
||||
bool disposed;
|
||||
|
||||
public _EveryValueChanged(TTarget target, Func<TTarget, TProperty> propertySelector, IEqualityComparer<TProperty> equalityComparer, PlayerLoopTiming monitorTiming, CancellationToken cancellationToken)
|
||||
public _EveryValueChanged(TTarget target, Func<TTarget, TProperty> propertySelector, IEqualityComparer<TProperty> equalityComparer, PlayerLoopTiming monitorTiming, CancellationToken cancellationToken, bool cancelImmediately)
|
||||
{
|
||||
this.target = target;
|
||||
this.targetAsUnityObject = target as UnityEngine.Object;
|
||||
@@ -64,6 +67,16 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
this.equalityComparer = equalityComparer;
|
||||
this.cancellationToken = cancellationToken;
|
||||
this.first = true;
|
||||
|
||||
if (cancelImmediately && cancellationToken.CanBeCanceled)
|
||||
{
|
||||
cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
|
||||
{
|
||||
var source = (_EveryValueChanged)state;
|
||||
source.completionSource.TrySetCanceled(source.cancellationToken);
|
||||
}, this);
|
||||
}
|
||||
|
||||
TaskTracker.TrackActiveTask(this, 2);
|
||||
PlayerLoopHelper.AddAction(monitorTiming, this);
|
||||
}
|
||||
@@ -72,8 +85,15 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
// return false instead of throw
|
||||
if (disposed || cancellationToken.IsCancellationRequested) return CompletedTasks.False;
|
||||
if (disposed) return CompletedTasks.False;
|
||||
|
||||
completionSource.Reset();
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
completionSource.TrySetCanceled(cancellationToken);
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
if (first)
|
||||
{
|
||||
@@ -86,7 +106,6 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
return CompletedTasks.True;
|
||||
}
|
||||
|
||||
completionSource.Reset();
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
@@ -94,6 +113,7 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
{
|
||||
if (!disposed)
|
||||
{
|
||||
cancellationTokenRegistration.Dispose();
|
||||
disposed = true;
|
||||
TaskTracker.RemoveTracking(this);
|
||||
}
|
||||
@@ -102,13 +122,18 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (disposed || cancellationToken.IsCancellationRequested || targetAsUnityObject == null) // destroyed = cancel.
|
||||
if (disposed || targetAsUnityObject == null)
|
||||
{
|
||||
completionSource.TrySetResult(false);
|
||||
DisposeAsync().Forget();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
completionSource.TrySetCanceled(cancellationToken);
|
||||
return false;
|
||||
}
|
||||
TProperty nextValue = default(TProperty);
|
||||
try
|
||||
{
|
||||
@@ -139,18 +164,20 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
readonly Func<TTarget, TProperty> propertySelector;
|
||||
readonly IEqualityComparer<TProperty> equalityComparer;
|
||||
readonly PlayerLoopTiming monitorTiming;
|
||||
readonly bool cancelImmediately;
|
||||
|
||||
public EveryValueChangedStandardObject(TTarget target, Func<TTarget, TProperty> propertySelector, IEqualityComparer<TProperty> equalityComparer, PlayerLoopTiming monitorTiming)
|
||||
public EveryValueChangedStandardObject(TTarget target, Func<TTarget, TProperty> propertySelector, IEqualityComparer<TProperty> equalityComparer, PlayerLoopTiming monitorTiming, bool cancelImmediately)
|
||||
{
|
||||
this.target = new WeakReference<TTarget>(target, false);
|
||||
this.propertySelector = propertySelector;
|
||||
this.equalityComparer = equalityComparer;
|
||||
this.monitorTiming = monitorTiming;
|
||||
this.cancelImmediately = cancelImmediately;
|
||||
}
|
||||
|
||||
public IUniTaskAsyncEnumerator<TProperty> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return new _EveryValueChanged(target, propertySelector, equalityComparer, monitorTiming, cancellationToken);
|
||||
return new _EveryValueChanged(target, propertySelector, equalityComparer, monitorTiming, cancellationToken, cancelImmediately);
|
||||
}
|
||||
|
||||
sealed class _EveryValueChanged : MoveNextSource, IUniTaskAsyncEnumerator<TProperty>, IPlayerLoopItem
|
||||
@@ -158,19 +185,30 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
readonly WeakReference<TTarget> target;
|
||||
readonly IEqualityComparer<TProperty> equalityComparer;
|
||||
readonly Func<TTarget, TProperty> propertySelector;
|
||||
CancellationToken cancellationToken;
|
||||
readonly CancellationToken cancellationToken;
|
||||
readonly CancellationTokenRegistration cancellationTokenRegistration;
|
||||
|
||||
bool first;
|
||||
TProperty currentValue;
|
||||
bool disposed;
|
||||
|
||||
public _EveryValueChanged(WeakReference<TTarget> target, Func<TTarget, TProperty> propertySelector, IEqualityComparer<TProperty> equalityComparer, PlayerLoopTiming monitorTiming, CancellationToken cancellationToken)
|
||||
public _EveryValueChanged(WeakReference<TTarget> target, Func<TTarget, TProperty> propertySelector, IEqualityComparer<TProperty> equalityComparer, PlayerLoopTiming monitorTiming, CancellationToken cancellationToken, bool cancelImmediately)
|
||||
{
|
||||
this.target = target;
|
||||
this.propertySelector = propertySelector;
|
||||
this.equalityComparer = equalityComparer;
|
||||
this.cancellationToken = cancellationToken;
|
||||
this.first = true;
|
||||
|
||||
if (cancelImmediately && cancellationToken.CanBeCanceled)
|
||||
{
|
||||
cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
|
||||
{
|
||||
var source = (_EveryValueChanged)state;
|
||||
source.completionSource.TrySetCanceled(source.cancellationToken);
|
||||
}, this);
|
||||
}
|
||||
|
||||
TaskTracker.TrackActiveTask(this, 2);
|
||||
PlayerLoopHelper.AddAction(monitorTiming, this);
|
||||
}
|
||||
@@ -179,8 +217,16 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
if (disposed || cancellationToken.IsCancellationRequested) return CompletedTasks.False;
|
||||
if (disposed) return CompletedTasks.False;
|
||||
|
||||
completionSource.Reset();
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
completionSource.TrySetCanceled(cancellationToken);
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
if (first)
|
||||
{
|
||||
first = false;
|
||||
@@ -192,7 +238,6 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
return CompletedTasks.True;
|
||||
}
|
||||
|
||||
completionSource.Reset();
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
@@ -200,6 +245,7 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
{
|
||||
if (!disposed)
|
||||
{
|
||||
cancellationTokenRegistration.Dispose();
|
||||
disposed = true;
|
||||
TaskTracker.RemoveTracking(this);
|
||||
}
|
||||
@@ -208,13 +254,19 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (disposed || cancellationToken.IsCancellationRequested || !target.TryGetTarget(out var t))
|
||||
if (disposed || !target.TryGetTarget(out var t))
|
||||
{
|
||||
completionSource.TrySetResult(false);
|
||||
DisposeAsync().Forget();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
completionSource.TrySetCanceled(cancellationToken);
|
||||
return false;
|
||||
}
|
||||
|
||||
TProperty nextValue = default(TProperty);
|
||||
try
|
||||
{
|
||||
|
||||
@@ -6,32 +6,32 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
{
|
||||
public static partial class UniTaskAsyncEnumerable
|
||||
{
|
||||
public static IUniTaskAsyncEnumerable<AsyncUnit> Timer(TimeSpan dueTime, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update, bool ignoreTimeScale = false)
|
||||
public static IUniTaskAsyncEnumerable<AsyncUnit> Timer(TimeSpan dueTime, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update, bool ignoreTimeScale = false, bool cancelImmediately = false)
|
||||
{
|
||||
return new Timer(dueTime, null, updateTiming, ignoreTimeScale);
|
||||
return new Timer(dueTime, null, updateTiming, ignoreTimeScale, cancelImmediately);
|
||||
}
|
||||
|
||||
public static IUniTaskAsyncEnumerable<AsyncUnit> Timer(TimeSpan dueTime, TimeSpan period, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update, bool ignoreTimeScale = false)
|
||||
public static IUniTaskAsyncEnumerable<AsyncUnit> Timer(TimeSpan dueTime, TimeSpan period, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update, bool ignoreTimeScale = false, bool cancelImmediately = false)
|
||||
{
|
||||
return new Timer(dueTime, period, updateTiming, ignoreTimeScale);
|
||||
return new Timer(dueTime, period, updateTiming, ignoreTimeScale, cancelImmediately);
|
||||
}
|
||||
|
||||
public static IUniTaskAsyncEnumerable<AsyncUnit> Interval(TimeSpan period, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update, bool ignoreTimeScale = false)
|
||||
public static IUniTaskAsyncEnumerable<AsyncUnit> Interval(TimeSpan period, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update, bool ignoreTimeScale = false, bool cancelImmediately = false)
|
||||
{
|
||||
return new Timer(period, period, updateTiming, ignoreTimeScale);
|
||||
return new Timer(period, period, updateTiming, ignoreTimeScale, cancelImmediately);
|
||||
}
|
||||
|
||||
public static IUniTaskAsyncEnumerable<AsyncUnit> TimerFrame(int dueTimeFrameCount, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update)
|
||||
public static IUniTaskAsyncEnumerable<AsyncUnit> TimerFrame(int dueTimeFrameCount, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update, bool cancelImmediately = false)
|
||||
{
|
||||
if (dueTimeFrameCount < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("Delay does not allow minus delayFrameCount. dueTimeFrameCount:" + dueTimeFrameCount);
|
||||
}
|
||||
|
||||
return new TimerFrame(dueTimeFrameCount, null, updateTiming);
|
||||
return new TimerFrame(dueTimeFrameCount, null, updateTiming, cancelImmediately);
|
||||
}
|
||||
|
||||
public static IUniTaskAsyncEnumerable<AsyncUnit> TimerFrame(int dueTimeFrameCount, int periodFrameCount, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update)
|
||||
public static IUniTaskAsyncEnumerable<AsyncUnit> TimerFrame(int dueTimeFrameCount, int periodFrameCount, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update, bool cancelImmediately = false)
|
||||
{
|
||||
if (dueTimeFrameCount < 0)
|
||||
{
|
||||
@@ -42,16 +42,16 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
throw new ArgumentOutOfRangeException("Delay does not allow minus periodFrameCount. periodFrameCount:" + dueTimeFrameCount);
|
||||
}
|
||||
|
||||
return new TimerFrame(dueTimeFrameCount, periodFrameCount, updateTiming);
|
||||
return new TimerFrame(dueTimeFrameCount, periodFrameCount, updateTiming, cancelImmediately);
|
||||
}
|
||||
|
||||
public static IUniTaskAsyncEnumerable<AsyncUnit> IntervalFrame(int intervalFrameCount, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update)
|
||||
public static IUniTaskAsyncEnumerable<AsyncUnit> IntervalFrame(int intervalFrameCount, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update, bool cancelImmediately = false)
|
||||
{
|
||||
if (intervalFrameCount < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("Delay does not allow minus intervalFrameCount. intervalFrameCount:" + intervalFrameCount);
|
||||
}
|
||||
return new TimerFrame(intervalFrameCount, intervalFrameCount, updateTiming);
|
||||
return new TimerFrame(intervalFrameCount, intervalFrameCount, updateTiming, cancelImmediately);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,18 +61,20 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
readonly TimeSpan dueTime;
|
||||
readonly TimeSpan? period;
|
||||
readonly bool ignoreTimeScale;
|
||||
readonly bool cancelImmediately;
|
||||
|
||||
public Timer(TimeSpan dueTime, TimeSpan? period, PlayerLoopTiming updateTiming, bool ignoreTimeScale)
|
||||
public Timer(TimeSpan dueTime, TimeSpan? period, PlayerLoopTiming updateTiming, bool ignoreTimeScale, bool cancelImmediately)
|
||||
{
|
||||
this.updateTiming = updateTiming;
|
||||
this.dueTime = dueTime;
|
||||
this.period = period;
|
||||
this.ignoreTimeScale = ignoreTimeScale;
|
||||
this.cancelImmediately = cancelImmediately;
|
||||
}
|
||||
|
||||
public IUniTaskAsyncEnumerator<AsyncUnit> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return new _Timer(dueTime, period, updateTiming, ignoreTimeScale, cancellationToken);
|
||||
return new _Timer(dueTime, period, updateTiming, ignoreTimeScale, cancellationToken, cancelImmediately);
|
||||
}
|
||||
|
||||
class _Timer : MoveNextSource, IUniTaskAsyncEnumerator<AsyncUnit>, IPlayerLoopItem
|
||||
@@ -81,7 +83,8 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
readonly float? period;
|
||||
readonly PlayerLoopTiming updateTiming;
|
||||
readonly bool ignoreTimeScale;
|
||||
CancellationToken cancellationToken;
|
||||
readonly CancellationToken cancellationToken;
|
||||
readonly CancellationTokenRegistration cancellationTokenRegistration;
|
||||
|
||||
int initialFrame;
|
||||
float elapsed;
|
||||
@@ -89,7 +92,7 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
bool completed;
|
||||
bool disposed;
|
||||
|
||||
public _Timer(TimeSpan dueTime, TimeSpan? period, PlayerLoopTiming updateTiming, bool ignoreTimeScale, CancellationToken cancellationToken)
|
||||
public _Timer(TimeSpan dueTime, TimeSpan? period, PlayerLoopTiming updateTiming, bool ignoreTimeScale, CancellationToken cancellationToken, bool cancelImmediately)
|
||||
{
|
||||
this.dueTime = (float)dueTime.TotalSeconds;
|
||||
this.period = (period == null) ? null : (float?)period.Value.TotalSeconds;
|
||||
@@ -105,6 +108,16 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
this.updateTiming = updateTiming;
|
||||
this.ignoreTimeScale = ignoreTimeScale;
|
||||
this.cancellationToken = cancellationToken;
|
||||
|
||||
if (cancelImmediately && cancellationToken.CanBeCanceled)
|
||||
{
|
||||
cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
|
||||
{
|
||||
var source = (_Timer)state;
|
||||
source.completionSource.TrySetCanceled(source.cancellationToken);
|
||||
}, this);
|
||||
}
|
||||
|
||||
TaskTracker.TrackActiveTask(this, 2);
|
||||
PlayerLoopHelper.AddAction(updateTiming, this);
|
||||
}
|
||||
@@ -114,12 +127,16 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
// return false instead of throw
|
||||
if (disposed || cancellationToken.IsCancellationRequested || completed) return CompletedTasks.False;
|
||||
if (disposed || completed) return CompletedTasks.False;
|
||||
|
||||
// reset value here.
|
||||
this.elapsed = 0;
|
||||
|
||||
completionSource.Reset();
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
completionSource.TrySetCanceled(cancellationToken);
|
||||
}
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
@@ -127,6 +144,7 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
{
|
||||
if (!disposed)
|
||||
{
|
||||
cancellationTokenRegistration.Dispose();
|
||||
disposed = true;
|
||||
TaskTracker.RemoveTracking(this);
|
||||
}
|
||||
@@ -135,11 +153,16 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (disposed || cancellationToken.IsCancellationRequested)
|
||||
if (disposed)
|
||||
{
|
||||
completionSource.TrySetResult(false);
|
||||
return false;
|
||||
}
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
completionSource.TrySetCanceled(cancellationToken);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dueTimePhase)
|
||||
{
|
||||
@@ -187,24 +210,27 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
readonly PlayerLoopTiming updateTiming;
|
||||
readonly int dueTimeFrameCount;
|
||||
readonly int? periodFrameCount;
|
||||
readonly bool cancelImmediately;
|
||||
|
||||
public TimerFrame(int dueTimeFrameCount, int? periodFrameCount, PlayerLoopTiming updateTiming)
|
||||
public TimerFrame(int dueTimeFrameCount, int? periodFrameCount, PlayerLoopTiming updateTiming, bool cancelImmediately)
|
||||
{
|
||||
this.updateTiming = updateTiming;
|
||||
this.dueTimeFrameCount = dueTimeFrameCount;
|
||||
this.periodFrameCount = periodFrameCount;
|
||||
this.cancelImmediately = cancelImmediately;
|
||||
}
|
||||
|
||||
public IUniTaskAsyncEnumerator<AsyncUnit> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return new _TimerFrame(dueTimeFrameCount, periodFrameCount, updateTiming, cancellationToken);
|
||||
return new _TimerFrame(dueTimeFrameCount, periodFrameCount, updateTiming, cancellationToken, cancelImmediately);
|
||||
}
|
||||
|
||||
class _TimerFrame : MoveNextSource, IUniTaskAsyncEnumerator<AsyncUnit>, IPlayerLoopItem
|
||||
{
|
||||
readonly int dueTimeFrameCount;
|
||||
readonly int? periodFrameCount;
|
||||
CancellationToken cancellationToken;
|
||||
readonly CancellationToken cancellationToken;
|
||||
readonly CancellationTokenRegistration cancellationTokenRegistration;
|
||||
|
||||
int initialFrame;
|
||||
int currentFrame;
|
||||
@@ -212,7 +238,7 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
bool completed;
|
||||
bool disposed;
|
||||
|
||||
public _TimerFrame(int dueTimeFrameCount, int? periodFrameCount, PlayerLoopTiming updateTiming, CancellationToken cancellationToken)
|
||||
public _TimerFrame(int dueTimeFrameCount, int? periodFrameCount, PlayerLoopTiming updateTiming, CancellationToken cancellationToken, bool cancelImmediately)
|
||||
{
|
||||
if (dueTimeFrameCount <= 0) dueTimeFrameCount = 0;
|
||||
if (periodFrameCount != null)
|
||||
@@ -225,6 +251,15 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
this.dueTimeFrameCount = dueTimeFrameCount;
|
||||
this.periodFrameCount = periodFrameCount;
|
||||
this.cancellationToken = cancellationToken;
|
||||
|
||||
if (cancelImmediately && cancellationToken.CanBeCanceled)
|
||||
{
|
||||
cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
|
||||
{
|
||||
var source = (_TimerFrame)state;
|
||||
source.completionSource.TrySetCanceled(source.cancellationToken);
|
||||
}, this);
|
||||
}
|
||||
|
||||
TaskTracker.TrackActiveTask(this, 2);
|
||||
PlayerLoopHelper.AddAction(updateTiming, this);
|
||||
@@ -234,13 +269,15 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
// return false instead of throw
|
||||
if (disposed || cancellationToken.IsCancellationRequested || completed) return CompletedTasks.False;
|
||||
if (disposed || completed) return CompletedTasks.False;
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
completionSource.TrySetCanceled(cancellationToken);
|
||||
}
|
||||
|
||||
// reset value here.
|
||||
this.currentFrame = 0;
|
||||
|
||||
completionSource.Reset();
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
@@ -249,6 +286,7 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
{
|
||||
if (!disposed)
|
||||
{
|
||||
cancellationTokenRegistration.Dispose();
|
||||
disposed = true;
|
||||
TaskTracker.RemoveTracking(this);
|
||||
}
|
||||
@@ -257,7 +295,12 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (disposed || cancellationToken.IsCancellationRequested)
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
completionSource.TrySetCanceled(cancellationToken);
|
||||
return false;
|
||||
}
|
||||
if (disposed)
|
||||
{
|
||||
completionSource.TrySetResult(false);
|
||||
return false;
|
||||
|
||||
@@ -8,8 +8,10 @@ using System.Threading;
|
||||
|
||||
#if UNITY_2019_3_OR_NEWER
|
||||
using UnityEngine.LowLevel;
|
||||
using PlayerLoopType = UnityEngine.PlayerLoop;
|
||||
#else
|
||||
using UnityEngine.Experimental.LowLevel;
|
||||
using PlayerLoopType = UnityEngine.Experimental.PlayerLoop;
|
||||
#endif
|
||||
|
||||
#if UNITY_EDITOR
|
||||
@@ -57,6 +59,13 @@ namespace Cysharp.Threading.Tasks
|
||||
public struct UniTaskLoopRunnerLastYieldUpdate { };
|
||||
public struct UniTaskLoopRunnerLastYieldPreLateUpdate { };
|
||||
public struct UniTaskLoopRunnerLastYieldPostLateUpdate { };
|
||||
|
||||
#if UNITY_2020_2_OR_NEWER
|
||||
public struct UniTaskLoopRunnerTimeUpdate { };
|
||||
public struct UniTaskLoopRunnerLastTimeUpdate { };
|
||||
public struct UniTaskLoopRunnerYieldTimeUpdate { };
|
||||
public struct UniTaskLoopRunnerLastYieldTimeUpdate { };
|
||||
#endif
|
||||
}
|
||||
|
||||
public enum PlayerLoopTiming
|
||||
@@ -80,7 +89,86 @@ namespace Cysharp.Threading.Tasks
|
||||
LastPreLateUpdate = 11,
|
||||
|
||||
PostLateUpdate = 12,
|
||||
LastPostLateUpdate = 13
|
||||
LastPostLateUpdate = 13,
|
||||
|
||||
#if UNITY_2020_2_OR_NEWER
|
||||
// Unity 2020.2 added TimeUpdate https://docs.unity3d.com/2020.2/Documentation/ScriptReference/PlayerLoop.TimeUpdate.html
|
||||
TimeUpdate = 14,
|
||||
LastTimeUpdate = 15,
|
||||
#endif
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum InjectPlayerLoopTimings
|
||||
{
|
||||
/// <summary>
|
||||
/// Preset: All loops(default).
|
||||
/// </summary>
|
||||
All =
|
||||
Initialization | LastInitialization |
|
||||
EarlyUpdate | LastEarlyUpdate |
|
||||
FixedUpdate | LastFixedUpdate |
|
||||
PreUpdate | LastPreUpdate |
|
||||
Update | LastUpdate |
|
||||
PreLateUpdate | LastPreLateUpdate |
|
||||
PostLateUpdate | LastPostLateUpdate
|
||||
#if UNITY_2020_2_OR_NEWER
|
||||
| TimeUpdate | LastTimeUpdate,
|
||||
#else
|
||||
,
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Preset: All without last except LastPostLateUpdate.
|
||||
/// </summary>
|
||||
Standard =
|
||||
Initialization |
|
||||
EarlyUpdate |
|
||||
FixedUpdate |
|
||||
PreUpdate |
|
||||
Update |
|
||||
PreLateUpdate |
|
||||
PostLateUpdate | LastPostLateUpdate
|
||||
#if UNITY_2020_2_OR_NEWER
|
||||
| TimeUpdate
|
||||
#endif
|
||||
,
|
||||
|
||||
/// <summary>
|
||||
/// Preset: Minimum pattern, Update | FixedUpdate | LastPostLateUpdate
|
||||
/// </summary>
|
||||
Minimum =
|
||||
Update | FixedUpdate | LastPostLateUpdate,
|
||||
|
||||
// PlayerLoopTiming
|
||||
|
||||
Initialization = 1,
|
||||
LastInitialization = 2,
|
||||
|
||||
EarlyUpdate = 4,
|
||||
LastEarlyUpdate = 8,
|
||||
|
||||
FixedUpdate = 16,
|
||||
LastFixedUpdate = 32,
|
||||
|
||||
PreUpdate = 64,
|
||||
LastPreUpdate = 128,
|
||||
|
||||
Update = 256,
|
||||
LastUpdate = 512,
|
||||
|
||||
PreLateUpdate = 1024,
|
||||
LastPreLateUpdate = 2048,
|
||||
|
||||
PostLateUpdate = 4096,
|
||||
LastPostLateUpdate = 8192
|
||||
|
||||
#if UNITY_2020_2_OR_NEWER
|
||||
,
|
||||
// Unity 2020.2 added TimeUpdate https://docs.unity3d.com/2020.2/Documentation/ScriptReference/PlayerLoop.TimeUpdate.html
|
||||
TimeUpdate = 16384,
|
||||
LastTimeUpdate = 32768
|
||||
#endif
|
||||
}
|
||||
|
||||
public interface IPlayerLoopItem
|
||||
@@ -90,7 +178,10 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
public static class PlayerLoopHelper
|
||||
{
|
||||
public static SynchronizationContext UnitySynchronizationContext => unitySynchronizationContetext;
|
||||
static readonly ContinuationQueue ThrowMarkerContinuationQueue = new ContinuationQueue(PlayerLoopTiming.Initialization);
|
||||
static readonly PlayerLoopRunner ThrowMarkerPlayerLoopRunner = new PlayerLoopRunner(PlayerLoopTiming.Initialization);
|
||||
|
||||
public static SynchronizationContext UnitySynchronizationContext => unitySynchronizationContext;
|
||||
public static int MainThreadId => mainThreadId;
|
||||
internal static string ApplicationDataPath => applicationDataPath;
|
||||
|
||||
@@ -98,39 +189,34 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
static int mainThreadId;
|
||||
static string applicationDataPath;
|
||||
static SynchronizationContext unitySynchronizationContetext;
|
||||
static SynchronizationContext unitySynchronizationContext;
|
||||
static ContinuationQueue[] yielders;
|
||||
static PlayerLoopRunner[] runners;
|
||||
|
||||
internal static bool IsEditorApplicationQuitting { get; private set; }
|
||||
static PlayerLoopSystem[] InsertRunner(PlayerLoopSystem loopSystem,
|
||||
Type loopRunnerYieldType, ContinuationQueue cq, Type lastLoopRunnerYieldType, ContinuationQueue lastCq,
|
||||
Type loopRunnerType, PlayerLoopRunner runner, Type lastLoopRunnerType, PlayerLoopRunner lastRunner)
|
||||
bool injectOnFirst,
|
||||
Type loopRunnerYieldType, ContinuationQueue cq,
|
||||
Type loopRunnerType, PlayerLoopRunner runner)
|
||||
{
|
||||
|
||||
#if UNITY_EDITOR
|
||||
EditorApplication.playModeStateChanged += (state) =>
|
||||
{
|
||||
if (state == PlayModeStateChange.EnteredEditMode || state == PlayModeStateChange.EnteredPlayMode)
|
||||
if (state == PlayModeStateChange.EnteredEditMode || state == PlayModeStateChange.ExitingEditMode)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (runner != null)
|
||||
{
|
||||
runner.Clear();
|
||||
}
|
||||
if (lastRunner != null)
|
||||
{
|
||||
lastRunner.Clear();
|
||||
}
|
||||
|
||||
if (cq != null)
|
||||
{
|
||||
cq.Clear();
|
||||
}
|
||||
if (lastCq != null)
|
||||
{
|
||||
lastCq.Clear();
|
||||
IsEditorApplicationQuitting = true;
|
||||
// run rest action before clear.
|
||||
if (runner != null)
|
||||
{
|
||||
runner.Run();
|
||||
runner.Clear();
|
||||
}
|
||||
if (cq != null)
|
||||
{
|
||||
cq.Run();
|
||||
cq.Clear();
|
||||
}
|
||||
IsEditorApplicationQuitting = false;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
@@ -141,40 +227,38 @@ namespace Cysharp.Threading.Tasks
|
||||
updateDelegate = cq.Run
|
||||
};
|
||||
|
||||
var lastYieldLoop = new PlayerLoopSystem
|
||||
{
|
||||
type = lastLoopRunnerYieldType,
|
||||
updateDelegate = lastCq.Run
|
||||
};
|
||||
|
||||
var runnerLoop = new PlayerLoopSystem
|
||||
{
|
||||
type = loopRunnerType,
|
||||
updateDelegate = runner.Run
|
||||
};
|
||||
|
||||
var lastRunnerLoop = new PlayerLoopSystem
|
||||
{
|
||||
type = lastLoopRunnerType,
|
||||
updateDelegate = lastRunner.Run
|
||||
};
|
||||
|
||||
// Remove items from previous initializations.
|
||||
var source = loopSystem.subSystemList
|
||||
.Where(ls => ls.type != loopRunnerYieldType && ls.type != loopRunnerType && ls.type != lastLoopRunnerYieldType && ls.type != lastLoopRunnerType)
|
||||
.ToArray();
|
||||
var source = RemoveRunner(loopSystem, loopRunnerYieldType, loopRunnerType);
|
||||
var dest = new PlayerLoopSystem[source.Length + 2];
|
||||
|
||||
var dest = new PlayerLoopSystem[source.Length + 4];
|
||||
|
||||
Array.Copy(source, 0, dest, 2, source.Length);
|
||||
dest[0] = yieldLoop;
|
||||
dest[1] = runnerLoop;
|
||||
dest[dest.Length - 2] = lastYieldLoop;
|
||||
dest[dest.Length - 1] = lastRunnerLoop;
|
||||
Array.Copy(source, 0, dest, injectOnFirst ? 2 : 0, source.Length);
|
||||
if (injectOnFirst)
|
||||
{
|
||||
dest[0] = yieldLoop;
|
||||
dest[1] = runnerLoop;
|
||||
}
|
||||
else
|
||||
{
|
||||
dest[dest.Length - 2] = yieldLoop;
|
||||
dest[dest.Length - 1] = runnerLoop;
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
static PlayerLoopSystem[] RemoveRunner(PlayerLoopSystem loopSystem, Type loopRunnerYieldType, Type loopRunnerType)
|
||||
{
|
||||
return loopSystem.subSystemList
|
||||
.Where(ls => ls.type != loopRunnerYieldType && ls.type != loopRunnerType)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
static PlayerLoopSystem[] InsertUniTaskSynchronizationContext(PlayerLoopSystem loopSystem)
|
||||
{
|
||||
var loop = new PlayerLoopSystem
|
||||
@@ -201,11 +285,15 @@ namespace Cysharp.Threading.Tasks
|
||||
return dest.ToArray();
|
||||
}
|
||||
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
|
||||
#if UNITY_2020_1_OR_NEWER
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]
|
||||
#else
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
|
||||
#endif
|
||||
static void Init()
|
||||
{
|
||||
// capture default(unity) sync-context.
|
||||
unitySynchronizationContetext = SynchronizationContext.Current;
|
||||
unitySynchronizationContext = SynchronizationContext.Current;
|
||||
mainThreadId = Thread.CurrentThread.ManagedThreadId;
|
||||
try
|
||||
{
|
||||
@@ -278,51 +366,124 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
#endif
|
||||
|
||||
public static void Initialize(ref PlayerLoopSystem playerLoop)
|
||||
private static int FindLoopSystemIndex(PlayerLoopSystem[] playerLoopList, Type systemType)
|
||||
{
|
||||
for (int i = 0; i < playerLoopList.Length; i++)
|
||||
{
|
||||
if (playerLoopList[i].type == systemType)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception("Target PlayerLoopSystem does not found. Type:" + systemType.FullName);
|
||||
}
|
||||
|
||||
static void InsertLoop(PlayerLoopSystem[] copyList, InjectPlayerLoopTimings injectTimings, Type loopType, InjectPlayerLoopTimings targetTimings,
|
||||
int index, bool injectOnFirst, Type loopRunnerYieldType, Type loopRunnerType, PlayerLoopTiming playerLoopTiming)
|
||||
{
|
||||
var i = FindLoopSystemIndex(copyList, loopType);
|
||||
if ((injectTimings & targetTimings) == targetTimings)
|
||||
{
|
||||
copyList[i].subSystemList = InsertRunner(copyList[i], injectOnFirst,
|
||||
loopRunnerYieldType, yielders[index] = new ContinuationQueue(playerLoopTiming),
|
||||
loopRunnerType, runners[index] = new PlayerLoopRunner(playerLoopTiming));
|
||||
}
|
||||
else
|
||||
{
|
||||
copyList[i].subSystemList = RemoveRunner(copyList[i], loopRunnerYieldType, loopRunnerType);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Initialize(ref PlayerLoopSystem playerLoop, InjectPlayerLoopTimings injectTimings = InjectPlayerLoopTimings.All)
|
||||
{
|
||||
#if UNITY_2020_2_OR_NEWER
|
||||
yielders = new ContinuationQueue[16];
|
||||
runners = new PlayerLoopRunner[16];
|
||||
#else
|
||||
yielders = new ContinuationQueue[14];
|
||||
runners = new PlayerLoopRunner[14];
|
||||
#endif
|
||||
|
||||
var copyList = playerLoop.subSystemList.ToArray();
|
||||
|
||||
// Initialization
|
||||
copyList[0].subSystemList = InsertRunner(copyList[0], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldInitialization), yielders[0] = new ContinuationQueue(PlayerLoopTiming.Initialization),
|
||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldInitialization), yielders[1] = new ContinuationQueue(PlayerLoopTiming.LastInitialization),
|
||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerInitialization), runners[1] = new PlayerLoopRunner(PlayerLoopTiming.Initialization),
|
||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastInitialization), runners[1] = new PlayerLoopRunner(PlayerLoopTiming.LastInitialization));
|
||||
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.Initialization),
|
||||
InjectPlayerLoopTimings.Initialization, 0, true,
|
||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldInitialization), typeof(UniTaskLoopRunners.UniTaskLoopRunnerInitialization), PlayerLoopTiming.Initialization);
|
||||
|
||||
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.Initialization),
|
||||
InjectPlayerLoopTimings.LastInitialization, 1, false,
|
||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldInitialization), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastInitialization), PlayerLoopTiming.LastInitialization);
|
||||
|
||||
// EarlyUpdate
|
||||
copyList[1].subSystemList = InsertRunner(copyList[1], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldEarlyUpdate), yielders[2] = new ContinuationQueue(PlayerLoopTiming.EarlyUpdate),
|
||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldEarlyUpdate), yielders[3] = new ContinuationQueue(PlayerLoopTiming.LastEarlyUpdate),
|
||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerEarlyUpdate), runners[2] = new PlayerLoopRunner(PlayerLoopTiming.EarlyUpdate),
|
||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastEarlyUpdate), runners[3] = new PlayerLoopRunner(PlayerLoopTiming.LastEarlyUpdate));
|
||||
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.EarlyUpdate),
|
||||
InjectPlayerLoopTimings.EarlyUpdate, 2, true,
|
||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldEarlyUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerEarlyUpdate), PlayerLoopTiming.EarlyUpdate);
|
||||
|
||||
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.EarlyUpdate),
|
||||
InjectPlayerLoopTimings.LastEarlyUpdate, 3, false,
|
||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldEarlyUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastEarlyUpdate), PlayerLoopTiming.LastEarlyUpdate);
|
||||
|
||||
// FixedUpdate
|
||||
copyList[2].subSystemList = InsertRunner(copyList[2], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldFixedUpdate), yielders[4] = new ContinuationQueue(PlayerLoopTiming.FixedUpdate),
|
||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldFixedUpdate), yielders[5] = new ContinuationQueue(PlayerLoopTiming.LastFixedUpdate),
|
||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerFixedUpdate), runners[4] = new PlayerLoopRunner(PlayerLoopTiming.FixedUpdate),
|
||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastFixedUpdate), runners[5] = new PlayerLoopRunner(PlayerLoopTiming.LastFixedUpdate));
|
||||
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.FixedUpdate),
|
||||
InjectPlayerLoopTimings.FixedUpdate, 4, true,
|
||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldFixedUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerFixedUpdate), PlayerLoopTiming.FixedUpdate);
|
||||
|
||||
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.FixedUpdate),
|
||||
InjectPlayerLoopTimings.LastFixedUpdate, 5, false,
|
||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldFixedUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastFixedUpdate), PlayerLoopTiming.LastFixedUpdate);
|
||||
|
||||
// PreUpdate
|
||||
copyList[3].subSystemList = InsertRunner(copyList[3], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldPreUpdate), yielders[6] = new ContinuationQueue(PlayerLoopTiming.PreUpdate),
|
||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldPreUpdate), yielders[7] = new ContinuationQueue(PlayerLoopTiming.LastPreUpdate),
|
||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerPreUpdate), runners[6] = new PlayerLoopRunner(PlayerLoopTiming.PreUpdate),
|
||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastPreUpdate), runners[7] = new PlayerLoopRunner(PlayerLoopTiming.LastPreUpdate));
|
||||
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.PreUpdate),
|
||||
InjectPlayerLoopTimings.PreUpdate, 6, true,
|
||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldPreUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerPreUpdate), PlayerLoopTiming.PreUpdate);
|
||||
|
||||
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.PreUpdate),
|
||||
InjectPlayerLoopTimings.LastPreUpdate, 7, false,
|
||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldPreUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastPreUpdate), PlayerLoopTiming.LastPreUpdate);
|
||||
|
||||
// Update
|
||||
copyList[4].subSystemList = InsertRunner(copyList[4], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldUpdate), yielders[8] = new ContinuationQueue(PlayerLoopTiming.Update),
|
||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldUpdate), yielders[9] = new ContinuationQueue(PlayerLoopTiming.LastUpdate),
|
||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerUpdate), runners[8] = new PlayerLoopRunner(PlayerLoopTiming.Update),
|
||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastUpdate), runners[9] = new PlayerLoopRunner(PlayerLoopTiming.LastUpdate));
|
||||
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.Update),
|
||||
InjectPlayerLoopTimings.Update, 8, true,
|
||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerUpdate), PlayerLoopTiming.Update);
|
||||
|
||||
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.Update),
|
||||
InjectPlayerLoopTimings.LastUpdate, 9, false,
|
||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastUpdate), PlayerLoopTiming.LastUpdate);
|
||||
|
||||
// PreLateUpdate
|
||||
copyList[5].subSystemList = InsertRunner(copyList[5], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldPreLateUpdate), yielders[10] = new ContinuationQueue(PlayerLoopTiming.PreLateUpdate),
|
||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldPreLateUpdate), yielders[11] = new ContinuationQueue(PlayerLoopTiming.LastPreLateUpdate),
|
||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerPreLateUpdate), runners[10] = new PlayerLoopRunner(PlayerLoopTiming.PreLateUpdate),
|
||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastPreLateUpdate), runners[11] = new PlayerLoopRunner(PlayerLoopTiming.LastPreLateUpdate));
|
||||
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.PreLateUpdate),
|
||||
InjectPlayerLoopTimings.PreLateUpdate, 10, true,
|
||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldPreLateUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerPreLateUpdate), PlayerLoopTiming.PreLateUpdate);
|
||||
|
||||
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.PreLateUpdate),
|
||||
InjectPlayerLoopTimings.LastPreLateUpdate, 11, false,
|
||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldPreLateUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastPreLateUpdate), PlayerLoopTiming.LastPreLateUpdate);
|
||||
|
||||
// PostLateUpdate
|
||||
copyList[6].subSystemList = InsertRunner(copyList[6], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldPostLateUpdate), yielders[12] = new ContinuationQueue(PlayerLoopTiming.PostLateUpdate),
|
||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldPostLateUpdate), yielders[13] = new ContinuationQueue(PlayerLoopTiming.LastPostLateUpdate),
|
||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerPostLateUpdate), runners[12] = new PlayerLoopRunner(PlayerLoopTiming.PostLateUpdate),
|
||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastPostLateUpdate), runners[13] = new PlayerLoopRunner(PlayerLoopTiming.LastPostLateUpdate));
|
||||
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.PostLateUpdate),
|
||||
InjectPlayerLoopTimings.PostLateUpdate, 12, true,
|
||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldPostLateUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerPostLateUpdate), PlayerLoopTiming.PostLateUpdate);
|
||||
|
||||
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.PostLateUpdate),
|
||||
InjectPlayerLoopTimings.LastPostLateUpdate, 13, false,
|
||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldPostLateUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastPostLateUpdate), PlayerLoopTiming.LastPostLateUpdate);
|
||||
|
||||
#if UNITY_2020_2_OR_NEWER
|
||||
// TimeUpdate
|
||||
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.TimeUpdate),
|
||||
InjectPlayerLoopTimings.TimeUpdate, 14, true,
|
||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldTimeUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerTimeUpdate), PlayerLoopTiming.TimeUpdate);
|
||||
|
||||
InsertLoop(copyList, injectTimings, typeof(PlayerLoopType.TimeUpdate),
|
||||
InjectPlayerLoopTimings.LastTimeUpdate, 15, false,
|
||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldTimeUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastTimeUpdate), PlayerLoopTiming.LastTimeUpdate);
|
||||
#endif
|
||||
|
||||
// Insert UniTaskSynchronizationContext to Update loop
|
||||
copyList[4].subSystemList = InsertUniTaskSynchronizationContext(copyList[4]);
|
||||
var i = FindLoopSystemIndex(copyList, typeof(PlayerLoopType.Update));
|
||||
copyList[i].subSystemList = InsertUniTaskSynchronizationContext(copyList[i]);
|
||||
|
||||
playerLoop.subSystemList = copyList;
|
||||
PlayerLoop.SetPlayerLoop(playerLoop);
|
||||
@@ -330,12 +491,27 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
public static void AddAction(PlayerLoopTiming timing, IPlayerLoopItem action)
|
||||
{
|
||||
runners[(int)timing].AddAction(action);
|
||||
var runner = runners[(int)timing];
|
||||
if (runner == null)
|
||||
{
|
||||
ThrowInvalidLoopTiming(timing);
|
||||
}
|
||||
runner.AddAction(action);
|
||||
}
|
||||
|
||||
static void ThrowInvalidLoopTiming(PlayerLoopTiming playerLoopTiming)
|
||||
{
|
||||
throw new InvalidOperationException("Target playerLoopTiming is not injected. Please check PlayerLoopHelper.Initialize. PlayerLoopTiming:" + playerLoopTiming);
|
||||
}
|
||||
|
||||
public static void AddContinuation(PlayerLoopTiming timing, Action continuation)
|
||||
{
|
||||
yielders[(int)timing].Enqueue(continuation);
|
||||
var q = yielders[(int)timing];
|
||||
if (q == null)
|
||||
{
|
||||
ThrowInvalidLoopTiming(timing);
|
||||
}
|
||||
q.Enqueue(continuation);
|
||||
}
|
||||
|
||||
// Diagnostics helper
|
||||
@@ -352,6 +528,14 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
sb.AppendFormat("------{0}------", header.type.Name);
|
||||
sb.AppendLine();
|
||||
|
||||
if (header.subSystemList is null)
|
||||
{
|
||||
sb.AppendFormat("{0} has no subsystems!", header.ToString());
|
||||
sb.AppendLine();
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var subSystem in header.subSystemList)
|
||||
{
|
||||
sb.AppendFormat("{0}", subSystem.type.Name);
|
||||
@@ -373,6 +557,11 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
foreach (var header in playerLoop.subSystemList)
|
||||
{
|
||||
if (header.subSystemList is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var subSystem in header.subSystemList)
|
||||
{
|
||||
if (subSystem.type == typeof(UniTaskLoopRunners.UniTaskLoopRunnerInitialization))
|
||||
|
||||
262
src/UniTask/Assets/Plugins/UniTask/Runtime/PlayerLoopTimer.cs
Normal file
262
src/UniTask/Assets/Plugins/UniTask/Runtime/PlayerLoopTimer.cs
Normal file
@@ -0,0 +1,262 @@
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
|
||||
using System.Threading;
|
||||
using System;
|
||||
using Cysharp.Threading.Tasks.Internal;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
public abstract class PlayerLoopTimer : IDisposable, IPlayerLoopItem
|
||||
{
|
||||
readonly CancellationToken cancellationToken;
|
||||
readonly Action<object> timerCallback;
|
||||
readonly object state;
|
||||
readonly PlayerLoopTiming playerLoopTiming;
|
||||
readonly bool periodic;
|
||||
|
||||
bool isRunning;
|
||||
bool tryStop;
|
||||
bool isDisposed;
|
||||
|
||||
protected PlayerLoopTimer(bool periodic, PlayerLoopTiming playerLoopTiming, CancellationToken cancellationToken, Action<object> timerCallback, object state)
|
||||
{
|
||||
this.periodic = periodic;
|
||||
this.playerLoopTiming = playerLoopTiming;
|
||||
this.cancellationToken = cancellationToken;
|
||||
this.timerCallback = timerCallback;
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public static PlayerLoopTimer Create(TimeSpan interval, bool periodic, DelayType delayType, PlayerLoopTiming playerLoopTiming, CancellationToken cancellationToken, Action<object> timerCallback, object state)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
// force use Realtime.
|
||||
if (PlayerLoopHelper.IsMainThread && !UnityEditor.EditorApplication.isPlaying)
|
||||
{
|
||||
delayType = DelayType.Realtime;
|
||||
}
|
||||
#endif
|
||||
|
||||
switch (delayType)
|
||||
{
|
||||
case DelayType.UnscaledDeltaTime:
|
||||
return new IgnoreTimeScalePlayerLoopTimer(interval, periodic, playerLoopTiming, cancellationToken, timerCallback, state);
|
||||
case DelayType.Realtime:
|
||||
return new RealtimePlayerLoopTimer(interval, periodic, playerLoopTiming, cancellationToken, timerCallback, state);
|
||||
case DelayType.DeltaTime:
|
||||
default:
|
||||
return new DeltaTimePlayerLoopTimer(interval, periodic, playerLoopTiming, cancellationToken, timerCallback, state);
|
||||
}
|
||||
}
|
||||
|
||||
public static PlayerLoopTimer StartNew(TimeSpan interval, bool periodic, DelayType delayType, PlayerLoopTiming playerLoopTiming, CancellationToken cancellationToken, Action<object> timerCallback, object state)
|
||||
{
|
||||
var timer = Create(interval, periodic, delayType, playerLoopTiming, cancellationToken, timerCallback, state);
|
||||
timer.Restart();
|
||||
return timer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restart(Reset and Start) timer.
|
||||
/// </summary>
|
||||
public void Restart()
|
||||
{
|
||||
if (isDisposed) throw new ObjectDisposedException(null);
|
||||
|
||||
ResetCore(null); // init state
|
||||
if (!isRunning)
|
||||
{
|
||||
isRunning = true;
|
||||
PlayerLoopHelper.AddAction(playerLoopTiming, this);
|
||||
}
|
||||
tryStop = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restart(Reset and Start) and change interval.
|
||||
/// </summary>
|
||||
public void Restart(TimeSpan interval)
|
||||
{
|
||||
if (isDisposed) throw new ObjectDisposedException(null);
|
||||
|
||||
ResetCore(interval); // init state
|
||||
if (!isRunning)
|
||||
{
|
||||
isRunning = true;
|
||||
PlayerLoopHelper.AddAction(playerLoopTiming, this);
|
||||
}
|
||||
tryStop = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop timer.
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
tryStop = true;
|
||||
}
|
||||
|
||||
protected abstract void ResetCore(TimeSpan? newInterval);
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
isDisposed = true;
|
||||
}
|
||||
|
||||
bool IPlayerLoopItem.MoveNext()
|
||||
{
|
||||
if (isDisposed)
|
||||
{
|
||||
isRunning = false;
|
||||
return false;
|
||||
}
|
||||
if (tryStop)
|
||||
{
|
||||
isRunning = false;
|
||||
return false;
|
||||
}
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
isRunning = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!MoveNextCore())
|
||||
{
|
||||
timerCallback(state);
|
||||
|
||||
if (periodic)
|
||||
{
|
||||
ResetCore(null);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
isRunning = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected abstract bool MoveNextCore();
|
||||
}
|
||||
|
||||
sealed class DeltaTimePlayerLoopTimer : PlayerLoopTimer
|
||||
{
|
||||
int initialFrame;
|
||||
float elapsed;
|
||||
float interval;
|
||||
|
||||
public DeltaTimePlayerLoopTimer(TimeSpan interval, bool periodic, PlayerLoopTiming playerLoopTiming, CancellationToken cancellationToken, Action<object> timerCallback, object state)
|
||||
: base(periodic, playerLoopTiming, cancellationToken, timerCallback, state)
|
||||
{
|
||||
ResetCore(interval);
|
||||
}
|
||||
|
||||
protected override bool MoveNextCore()
|
||||
{
|
||||
if (elapsed == 0.0f)
|
||||
{
|
||||
if (initialFrame == Time.frameCount)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
elapsed += Time.deltaTime;
|
||||
if (elapsed >= interval)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void ResetCore(TimeSpan? interval)
|
||||
{
|
||||
this.elapsed = 0.0f;
|
||||
this.initialFrame = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1;
|
||||
if (interval != null)
|
||||
{
|
||||
this.interval = (float)interval.Value.TotalSeconds;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class IgnoreTimeScalePlayerLoopTimer : PlayerLoopTimer
|
||||
{
|
||||
int initialFrame;
|
||||
float elapsed;
|
||||
float interval;
|
||||
|
||||
public IgnoreTimeScalePlayerLoopTimer(TimeSpan interval, bool periodic, PlayerLoopTiming playerLoopTiming, CancellationToken cancellationToken, Action<object> timerCallback, object state)
|
||||
: base(periodic, playerLoopTiming, cancellationToken, timerCallback, state)
|
||||
{
|
||||
ResetCore(interval);
|
||||
}
|
||||
|
||||
protected override bool MoveNextCore()
|
||||
{
|
||||
if (elapsed == 0.0f)
|
||||
{
|
||||
if (initialFrame == Time.frameCount)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
elapsed += Time.unscaledDeltaTime;
|
||||
if (elapsed >= interval)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void ResetCore(TimeSpan? interval)
|
||||
{
|
||||
this.elapsed = 0.0f;
|
||||
this.initialFrame = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1;
|
||||
if (interval != null)
|
||||
{
|
||||
this.interval = (float)interval.Value.TotalSeconds;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class RealtimePlayerLoopTimer : PlayerLoopTimer
|
||||
{
|
||||
ValueStopwatch stopwatch;
|
||||
long intervalTicks;
|
||||
|
||||
public RealtimePlayerLoopTimer(TimeSpan interval, bool periodic, PlayerLoopTiming playerLoopTiming, CancellationToken cancellationToken, Action<object> timerCallback, object state)
|
||||
: base(periodic, playerLoopTiming, cancellationToken, timerCallback, state)
|
||||
{
|
||||
ResetCore(interval);
|
||||
}
|
||||
|
||||
protected override bool MoveNextCore()
|
||||
{
|
||||
if (stopwatch.ElapsedTicks >= intervalTicks)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void ResetCore(TimeSpan? interval)
|
||||
{
|
||||
this.stopwatch = ValueStopwatch.StartNew();
|
||||
if (interval != null)
|
||||
{
|
||||
this.intervalTicks = interval.Value.Ticks;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8760bbbab905a534eb6fb7b61b736926
|
||||
guid: 57095a17fdca7ee4380450910afc7f26
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
@@ -7,12 +7,14 @@ using System.Threading;
|
||||
|
||||
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
|
||||
{
|
||||
internal static int MaxPoolSize;
|
||||
static ConcurrentDictionary<Type, Func<int>> sizes = new ConcurrentDictionary<Type, Func<int>>();
|
||||
|
||||
// avoid to use ConcurrentDictionary for safety of WebGL build.
|
||||
static Dictionary<Type, Func<int>> sizes = new Dictionary<Type, Func<int>>();
|
||||
|
||||
static TaskPool()
|
||||
{
|
||||
@@ -40,22 +42,27 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
public static IEnumerable<(Type, int)> GetCacheSizeInfo()
|
||||
{
|
||||
foreach (var item in sizes)
|
||||
lock (sizes)
|
||||
{
|
||||
yield return (item.Key, item.Value());
|
||||
foreach (var item in sizes)
|
||||
{
|
||||
yield return (item.Key, item.Value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void RegisterSizeGetter(Type type, Func<int> getSize)
|
||||
{
|
||||
sizes[type] = getSize;
|
||||
lock (sizes)
|
||||
{
|
||||
sizes[type] = getSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public interface ITaskPoolNode<T>
|
||||
{
|
||||
T NextNode { get; set; }
|
||||
ref T NextNode { get; }
|
||||
}
|
||||
|
||||
// mutable struct, don't mark readonly.
|
||||
@@ -77,8 +84,9 @@ namespace Cysharp.Threading.Tasks
|
||||
var v = root;
|
||||
if (!(v is null))
|
||||
{
|
||||
root = v.NextNode;
|
||||
v.NextNode = null;
|
||||
ref var nextNode = ref v.NextNode;
|
||||
root = nextNode;
|
||||
nextNode = null;
|
||||
size--;
|
||||
result = v;
|
||||
Volatile.Write(ref gate, 0);
|
||||
|
||||
129
src/UniTask/Assets/Plugins/UniTask/Runtime/TimeoutController.cs
Normal file
129
src/UniTask/Assets/Plugins/UniTask/Runtime/TimeoutController.cs
Normal file
@@ -0,0 +1,129 @@
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
// CancellationTokenSource itself can not reuse but CancelAfter(Timeout.InfiniteTimeSpan) allows reuse if did not reach timeout.
|
||||
// Similar discussion:
|
||||
// https://github.com/dotnet/runtime/issues/4694
|
||||
// https://github.com/dotnet/runtime/issues/48492
|
||||
// This TimeoutController emulate similar implementation, using CancelAfterSlim; to achieve zero allocation timeout.
|
||||
|
||||
public sealed class TimeoutController : IDisposable
|
||||
{
|
||||
readonly static Action<object> CancelCancellationTokenSourceStateDelegate = new Action<object>(CancelCancellationTokenSourceState);
|
||||
|
||||
static void CancelCancellationTokenSourceState(object state)
|
||||
{
|
||||
var cts = (CancellationTokenSource)state;
|
||||
cts.Cancel();
|
||||
}
|
||||
|
||||
CancellationTokenSource timeoutSource;
|
||||
CancellationTokenSource linkedSource;
|
||||
PlayerLoopTimer timer;
|
||||
bool isDisposed;
|
||||
|
||||
readonly DelayType delayType;
|
||||
readonly PlayerLoopTiming delayTiming;
|
||||
readonly CancellationTokenSource originalLinkCancellationTokenSource;
|
||||
|
||||
public TimeoutController(DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update)
|
||||
{
|
||||
this.timeoutSource = new CancellationTokenSource();
|
||||
this.originalLinkCancellationTokenSource = null;
|
||||
this.linkedSource = null;
|
||||
this.delayType = delayType;
|
||||
this.delayTiming = delayTiming;
|
||||
}
|
||||
|
||||
public TimeoutController(CancellationTokenSource linkCancellationTokenSource, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update)
|
||||
{
|
||||
this.timeoutSource = new CancellationTokenSource();
|
||||
this.originalLinkCancellationTokenSource = linkCancellationTokenSource;
|
||||
this.linkedSource = CancellationTokenSource.CreateLinkedTokenSource(timeoutSource.Token, linkCancellationTokenSource.Token);
|
||||
this.delayType = delayType;
|
||||
this.delayTiming = delayTiming;
|
||||
}
|
||||
|
||||
public CancellationToken Timeout(int millisecondsTimeout)
|
||||
{
|
||||
return Timeout(TimeSpan.FromMilliseconds(millisecondsTimeout));
|
||||
}
|
||||
|
||||
public CancellationToken Timeout(TimeSpan timeout)
|
||||
{
|
||||
if (originalLinkCancellationTokenSource != null && originalLinkCancellationTokenSource.IsCancellationRequested)
|
||||
{
|
||||
return originalLinkCancellationTokenSource.Token;
|
||||
}
|
||||
|
||||
// Timeouted, create new source and timer.
|
||||
if (timeoutSource.IsCancellationRequested)
|
||||
{
|
||||
timeoutSource.Dispose();
|
||||
timeoutSource = new CancellationTokenSource();
|
||||
if (linkedSource != null)
|
||||
{
|
||||
this.linkedSource.Cancel();
|
||||
this.linkedSource.Dispose();
|
||||
this.linkedSource = CancellationTokenSource.CreateLinkedTokenSource(timeoutSource.Token, originalLinkCancellationTokenSource.Token);
|
||||
}
|
||||
|
||||
timer?.Dispose();
|
||||
timer = null;
|
||||
}
|
||||
|
||||
var useSource = (linkedSource != null) ? linkedSource : timeoutSource;
|
||||
var token = useSource.Token;
|
||||
if (timer == null)
|
||||
{
|
||||
// Timer complete => timeoutSource.Cancel() -> linkedSource will be canceled.
|
||||
// (linked)token is canceled => stop timer
|
||||
timer = PlayerLoopTimer.StartNew(timeout, false, delayType, delayTiming, token, CancelCancellationTokenSourceStateDelegate, timeoutSource);
|
||||
}
|
||||
else
|
||||
{
|
||||
timer.Restart(timeout);
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
public bool IsTimeout()
|
||||
{
|
||||
return timeoutSource.IsCancellationRequested;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
timer?.Stop();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (isDisposed) return;
|
||||
|
||||
try
|
||||
{
|
||||
// stop timer.
|
||||
timer?.Dispose();
|
||||
|
||||
// cancel and dispose.
|
||||
timeoutSource.Cancel();
|
||||
timeoutSource.Dispose();
|
||||
if (linkedSource != null)
|
||||
{
|
||||
linkedSource.Cancel();
|
||||
linkedSource.Dispose();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
isDisposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 12bdad0556e999f4aa82da29415d361f
|
||||
guid: 6347ab34d2db6d744a654e8d62d96b96
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
@@ -20,8 +20,6 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
ITriggerHandler<T> head; // head.prev is last
|
||||
ITriggerHandler<T> iteratingHead;
|
||||
|
||||
bool preserveRemoveSelf;
|
||||
ITriggerHandler<T> iteratingNode;
|
||||
|
||||
void LogError(Exception ex)
|
||||
@@ -55,18 +53,9 @@ namespace Cysharp.Threading.Tasks
|
||||
Remove(h);
|
||||
}
|
||||
|
||||
if (preserveRemoveSelf)
|
||||
{
|
||||
preserveRemoveSelf = false;
|
||||
iteratingNode = null;
|
||||
var next = h.Next;
|
||||
Remove(h);
|
||||
h = next;
|
||||
}
|
||||
else
|
||||
{
|
||||
h = h.Next;
|
||||
}
|
||||
// If `h` itself is removed by OnNext, h.Next is null.
|
||||
// Therefore, instead of looking at h.Next, the `iteratingNode` reference itself is replaced.
|
||||
h = h == iteratingNode ? h.Next : iteratingNode;
|
||||
}
|
||||
|
||||
iteratingNode = null;
|
||||
@@ -97,9 +86,8 @@ namespace Cysharp.Threading.Tasks
|
||||
LogError(ex);
|
||||
}
|
||||
|
||||
preserveRemoveSelf = false;
|
||||
var next = h == iteratingNode ? h.Next : iteratingNode;
|
||||
iteratingNode = null;
|
||||
var next = h.Next;
|
||||
Remove(h);
|
||||
h = next;
|
||||
}
|
||||
@@ -132,9 +120,8 @@ namespace Cysharp.Threading.Tasks
|
||||
LogError(ex);
|
||||
}
|
||||
|
||||
preserveRemoveSelf = false;
|
||||
var next = h == iteratingNode ? h.Next : iteratingNode;
|
||||
iteratingNode = null;
|
||||
var next = h.Next;
|
||||
Remove(h);
|
||||
h = next;
|
||||
}
|
||||
@@ -167,9 +154,8 @@ namespace Cysharp.Threading.Tasks
|
||||
LogError(ex);
|
||||
}
|
||||
|
||||
preserveRemoveSelf = false;
|
||||
var next = h == iteratingNode ? h.Next : iteratingNode;
|
||||
iteratingNode = null;
|
||||
var next = h.Next;
|
||||
Remove(h);
|
||||
h = next;
|
||||
}
|
||||
@@ -241,71 +227,65 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
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.
|
||||
preserveRemoveSelf = true;
|
||||
next.Prev = prev;
|
||||
}
|
||||
else
|
||||
|
||||
if (handler == head)
|
||||
{
|
||||
var prev = handler.Prev;
|
||||
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;
|
||||
head = next;
|
||||
}
|
||||
// when handler is head, prev indicate last so don't use it.
|
||||
else if (prev != null)
|
||||
{
|
||||
prev.Next = next;
|
||||
}
|
||||
|
||||
if (handler == iteratingNode)
|
||||
{
|
||||
iteratingNode = next;
|
||||
}
|
||||
if (handler == iteratingHead)
|
||||
{
|
||||
iteratingHead = next;
|
||||
}
|
||||
|
||||
if (head != null)
|
||||
{
|
||||
if (head.Prev == handler)
|
||||
{
|
||||
if (prev != head)
|
||||
{
|
||||
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)
|
||||
{
|
||||
cancellationTokenSource = new CancellationTokenSource();
|
||||
if (!awakeCalled)
|
||||
{
|
||||
PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, new AwakeMonitor(this));
|
||||
}
|
||||
}
|
||||
|
||||
if (!awakeCalled)
|
||||
{
|
||||
PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, new AwakeMonitor(this));
|
||||
}
|
||||
|
||||
return cancellationTokenSource.Token;
|
||||
}
|
||||
}
|
||||
@@ -83,7 +81,7 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (trigger.called) return false;
|
||||
if (trigger.called || trigger.awakeCalled) return false;
|
||||
if (trigger == null)
|
||||
{
|
||||
trigger.OnDestroy();
|
||||
|
||||
@@ -8,6 +8,16 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
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>
|
||||
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>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
using System.Threading;
|
||||
using UnityEngine;
|
||||
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
|
||||
using UnityEngine.EventSystems;
|
||||
#endif
|
||||
|
||||
namespace Cysharp.Threading.Tasks.Triggers
|
||||
{
|
||||
@@ -715,6 +717,7 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
#endregion
|
||||
|
||||
#region CollisionEnter
|
||||
#if !UNITY_2019_1_OR_NEWER || UNITASK_PHYSICS_SUPPORT
|
||||
|
||||
public interface IAsyncOnCollisionEnterHandler
|
||||
{
|
||||
@@ -771,9 +774,11 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
return ((IAsyncOnCollisionEnterHandler)new AsyncTriggerHandler<Collision>(this, cancellationToken, true)).OnCollisionEnterAsync();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region CollisionEnter2D
|
||||
#if !UNITY_2019_1_OR_NEWER || UNITASK_PHYSICS2D_SUPPORT
|
||||
|
||||
public interface IAsyncOnCollisionEnter2DHandler
|
||||
{
|
||||
@@ -830,9 +835,11 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
return ((IAsyncOnCollisionEnter2DHandler)new AsyncTriggerHandler<Collision2D>(this, cancellationToken, true)).OnCollisionEnter2DAsync();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region CollisionExit
|
||||
#if !UNITY_2019_1_OR_NEWER || UNITASK_PHYSICS_SUPPORT
|
||||
|
||||
public interface IAsyncOnCollisionExitHandler
|
||||
{
|
||||
@@ -889,9 +896,11 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
return ((IAsyncOnCollisionExitHandler)new AsyncTriggerHandler<Collision>(this, cancellationToken, true)).OnCollisionExitAsync();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region CollisionExit2D
|
||||
#if !UNITY_2019_1_OR_NEWER || UNITASK_PHYSICS2D_SUPPORT
|
||||
|
||||
public interface IAsyncOnCollisionExit2DHandler
|
||||
{
|
||||
@@ -948,9 +957,11 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
return ((IAsyncOnCollisionExit2DHandler)new AsyncTriggerHandler<Collision2D>(this, cancellationToken, true)).OnCollisionExit2DAsync();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region CollisionStay
|
||||
#if !UNITY_2019_1_OR_NEWER || UNITASK_PHYSICS_SUPPORT
|
||||
|
||||
public interface IAsyncOnCollisionStayHandler
|
||||
{
|
||||
@@ -1007,9 +1018,11 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
return ((IAsyncOnCollisionStayHandler)new AsyncTriggerHandler<Collision>(this, cancellationToken, true)).OnCollisionStayAsync();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region CollisionStay2D
|
||||
#if !UNITY_2019_1_OR_NEWER || UNITASK_PHYSICS2D_SUPPORT
|
||||
|
||||
public interface IAsyncOnCollisionStay2DHandler
|
||||
{
|
||||
@@ -1066,9 +1079,11 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
return ((IAsyncOnCollisionStay2DHandler)new AsyncTriggerHandler<Collision2D>(this, cancellationToken, true)).OnCollisionStay2DAsync();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region ControllerColliderHit
|
||||
#if !UNITY_2019_1_OR_NEWER || UNITASK_PHYSICS_SUPPORT
|
||||
|
||||
public interface IAsyncOnControllerColliderHitHandler
|
||||
{
|
||||
@@ -1125,6 +1140,7 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
return ((IAsyncOnControllerColliderHitHandler)new AsyncTriggerHandler<ControllerColliderHit>(this, cancellationToken, true)).OnControllerColliderHitAsync();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region Disable
|
||||
@@ -1423,6 +1439,7 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
#endregion
|
||||
|
||||
#region JointBreak
|
||||
#if !UNITY_2019_1_OR_NEWER || UNITASK_PHYSICS_SUPPORT
|
||||
|
||||
public interface IAsyncOnJointBreakHandler
|
||||
{
|
||||
@@ -1479,9 +1496,11 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
return ((IAsyncOnJointBreakHandler)new AsyncTriggerHandler<float>(this, cancellationToken, true)).OnJointBreakAsync();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region JointBreak2D
|
||||
#if !UNITY_2019_1_OR_NEWER || UNITASK_PHYSICS2D_SUPPORT
|
||||
|
||||
public interface IAsyncOnJointBreak2DHandler
|
||||
{
|
||||
@@ -1538,9 +1557,11 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
return ((IAsyncOnJointBreak2DHandler)new AsyncTriggerHandler<Joint2D>(this, cancellationToken, true)).OnJointBreak2DAsync();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region MouseDown
|
||||
#if !(UNITY_IPHONE || UNITY_ANDROID || UNITY_METRO)
|
||||
|
||||
public interface IAsyncOnMouseDownHandler
|
||||
{
|
||||
@@ -1597,9 +1618,11 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
return ((IAsyncOnMouseDownHandler)new AsyncTriggerHandler<AsyncUnit>(this, cancellationToken, true)).OnMouseDownAsync();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region MouseDrag
|
||||
#if !(UNITY_IPHONE || UNITY_ANDROID || UNITY_METRO)
|
||||
|
||||
public interface IAsyncOnMouseDragHandler
|
||||
{
|
||||
@@ -1656,9 +1679,11 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
return ((IAsyncOnMouseDragHandler)new AsyncTriggerHandler<AsyncUnit>(this, cancellationToken, true)).OnMouseDragAsync();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region MouseEnter
|
||||
#if !(UNITY_IPHONE || UNITY_ANDROID || UNITY_METRO)
|
||||
|
||||
public interface IAsyncOnMouseEnterHandler
|
||||
{
|
||||
@@ -1715,9 +1740,11 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
return ((IAsyncOnMouseEnterHandler)new AsyncTriggerHandler<AsyncUnit>(this, cancellationToken, true)).OnMouseEnterAsync();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region MouseExit
|
||||
#if !(UNITY_IPHONE || UNITY_ANDROID || UNITY_METRO)
|
||||
|
||||
public interface IAsyncOnMouseExitHandler
|
||||
{
|
||||
@@ -1774,9 +1801,11 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
return ((IAsyncOnMouseExitHandler)new AsyncTriggerHandler<AsyncUnit>(this, cancellationToken, true)).OnMouseExitAsync();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region MouseOver
|
||||
#if !(UNITY_IPHONE || UNITY_ANDROID || UNITY_METRO)
|
||||
|
||||
public interface IAsyncOnMouseOverHandler
|
||||
{
|
||||
@@ -1833,9 +1862,11 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
return ((IAsyncOnMouseOverHandler)new AsyncTriggerHandler<AsyncUnit>(this, cancellationToken, true)).OnMouseOverAsync();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region MouseUp
|
||||
#if !(UNITY_IPHONE || UNITY_ANDROID || UNITY_METRO)
|
||||
|
||||
public interface IAsyncOnMouseUpHandler
|
||||
{
|
||||
@@ -1892,9 +1923,11 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
return ((IAsyncOnMouseUpHandler)new AsyncTriggerHandler<AsyncUnit>(this, cancellationToken, true)).OnMouseUpAsync();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region MouseUpAsButton
|
||||
#if !(UNITY_IPHONE || UNITY_ANDROID || UNITY_METRO)
|
||||
|
||||
public interface IAsyncOnMouseUpAsButtonHandler
|
||||
{
|
||||
@@ -1951,6 +1984,7 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
return ((IAsyncOnMouseUpAsButtonHandler)new AsyncTriggerHandler<AsyncUnit>(this, cancellationToken, true)).OnMouseUpAsButtonAsync();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region ParticleCollision
|
||||
@@ -2131,7 +2165,7 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
#endregion
|
||||
|
||||
#region ParticleUpdateJobScheduled
|
||||
#if UNITY_2019_3_OR_NEWER
|
||||
#if UNITY_2019_3_OR_NEWER && (!UNITY_2019_1_OR_NEWER || UNITASK_PARTICLESYSTEM_SUPPORT)
|
||||
|
||||
public interface IAsyncOnParticleUpdateJobScheduledHandler
|
||||
{
|
||||
@@ -2782,6 +2816,7 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
#endregion
|
||||
|
||||
#region TriggerEnter
|
||||
#if !UNITY_2019_1_OR_NEWER || UNITASK_PHYSICS_SUPPORT
|
||||
|
||||
public interface IAsyncOnTriggerEnterHandler
|
||||
{
|
||||
@@ -2838,9 +2873,11 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
return ((IAsyncOnTriggerEnterHandler)new AsyncTriggerHandler<Collider>(this, cancellationToken, true)).OnTriggerEnterAsync();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region TriggerEnter2D
|
||||
#if !UNITY_2019_1_OR_NEWER || UNITASK_PHYSICS2D_SUPPORT
|
||||
|
||||
public interface IAsyncOnTriggerEnter2DHandler
|
||||
{
|
||||
@@ -2897,9 +2934,11 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
return ((IAsyncOnTriggerEnter2DHandler)new AsyncTriggerHandler<Collider2D>(this, cancellationToken, true)).OnTriggerEnter2DAsync();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region TriggerExit
|
||||
#if !UNITY_2019_1_OR_NEWER || UNITASK_PHYSICS_SUPPORT
|
||||
|
||||
public interface IAsyncOnTriggerExitHandler
|
||||
{
|
||||
@@ -2956,9 +2995,11 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
return ((IAsyncOnTriggerExitHandler)new AsyncTriggerHandler<Collider>(this, cancellationToken, true)).OnTriggerExitAsync();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region TriggerExit2D
|
||||
#if !UNITY_2019_1_OR_NEWER || UNITASK_PHYSICS2D_SUPPORT
|
||||
|
||||
public interface IAsyncOnTriggerExit2DHandler
|
||||
{
|
||||
@@ -3015,9 +3056,11 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
return ((IAsyncOnTriggerExit2DHandler)new AsyncTriggerHandler<Collider2D>(this, cancellationToken, true)).OnTriggerExit2DAsync();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region TriggerStay
|
||||
#if !UNITY_2019_1_OR_NEWER || UNITASK_PHYSICS_SUPPORT
|
||||
|
||||
public interface IAsyncOnTriggerStayHandler
|
||||
{
|
||||
@@ -3074,9 +3117,11 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
return ((IAsyncOnTriggerStayHandler)new AsyncTriggerHandler<Collider>(this, cancellationToken, true)).OnTriggerStayAsync();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region TriggerStay2D
|
||||
#if !UNITY_2019_1_OR_NEWER || UNITASK_PHYSICS2D_SUPPORT
|
||||
|
||||
public interface IAsyncOnTriggerStay2DHandler
|
||||
{
|
||||
@@ -3133,6 +3178,7 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
return ((IAsyncOnTriggerStay2DHandler)new AsyncTriggerHandler<Collider2D>(this, cancellationToken, true)).OnTriggerStay2DAsync();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region Validate
|
||||
@@ -3372,6 +3418,7 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
#endregion
|
||||
|
||||
#region BeginDrag
|
||||
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
|
||||
|
||||
public interface IAsyncOnBeginDragHandler
|
||||
{
|
||||
@@ -3428,9 +3475,11 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
return ((IAsyncOnBeginDragHandler)new AsyncTriggerHandler<PointerEventData>(this, cancellationToken, true)).OnBeginDragAsync();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region Cancel
|
||||
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
|
||||
|
||||
public interface IAsyncOnCancelHandler
|
||||
{
|
||||
@@ -3487,9 +3536,11 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
return ((IAsyncOnCancelHandler)new AsyncTriggerHandler<BaseEventData>(this, cancellationToken, true)).OnCancelAsync();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region Deselect
|
||||
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
|
||||
|
||||
public interface IAsyncOnDeselectHandler
|
||||
{
|
||||
@@ -3546,9 +3597,11 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
return ((IAsyncOnDeselectHandler)new AsyncTriggerHandler<BaseEventData>(this, cancellationToken, true)).OnDeselectAsync();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region Drag
|
||||
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
|
||||
|
||||
public interface IAsyncOnDragHandler
|
||||
{
|
||||
@@ -3605,9 +3658,11 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
return ((IAsyncOnDragHandler)new AsyncTriggerHandler<PointerEventData>(this, cancellationToken, true)).OnDragAsync();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region Drop
|
||||
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
|
||||
|
||||
public interface IAsyncOnDropHandler
|
||||
{
|
||||
@@ -3664,9 +3719,11 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
return ((IAsyncOnDropHandler)new AsyncTriggerHandler<PointerEventData>(this, cancellationToken, true)).OnDropAsync();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region EndDrag
|
||||
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
|
||||
|
||||
public interface IAsyncOnEndDragHandler
|
||||
{
|
||||
@@ -3723,9 +3780,11 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
return ((IAsyncOnEndDragHandler)new AsyncTriggerHandler<PointerEventData>(this, cancellationToken, true)).OnEndDragAsync();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region InitializePotentialDrag
|
||||
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
|
||||
|
||||
public interface IAsyncOnInitializePotentialDragHandler
|
||||
{
|
||||
@@ -3782,9 +3841,11 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
return ((IAsyncOnInitializePotentialDragHandler)new AsyncTriggerHandler<PointerEventData>(this, cancellationToken, true)).OnInitializePotentialDragAsync();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region Move
|
||||
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
|
||||
|
||||
public interface IAsyncOnMoveHandler
|
||||
{
|
||||
@@ -3841,9 +3902,11 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
return ((IAsyncOnMoveHandler)new AsyncTriggerHandler<AxisEventData>(this, cancellationToken, true)).OnMoveAsync();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region PointerClick
|
||||
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
|
||||
|
||||
public interface IAsyncOnPointerClickHandler
|
||||
{
|
||||
@@ -3900,9 +3963,11 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
return ((IAsyncOnPointerClickHandler)new AsyncTriggerHandler<PointerEventData>(this, cancellationToken, true)).OnPointerClickAsync();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region PointerDown
|
||||
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
|
||||
|
||||
public interface IAsyncOnPointerDownHandler
|
||||
{
|
||||
@@ -3959,9 +4024,11 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
return ((IAsyncOnPointerDownHandler)new AsyncTriggerHandler<PointerEventData>(this, cancellationToken, true)).OnPointerDownAsync();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region PointerEnter
|
||||
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
|
||||
|
||||
public interface IAsyncOnPointerEnterHandler
|
||||
{
|
||||
@@ -4018,9 +4085,11 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
return ((IAsyncOnPointerEnterHandler)new AsyncTriggerHandler<PointerEventData>(this, cancellationToken, true)).OnPointerEnterAsync();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region PointerExit
|
||||
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
|
||||
|
||||
public interface IAsyncOnPointerExitHandler
|
||||
{
|
||||
@@ -4077,9 +4146,11 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
return ((IAsyncOnPointerExitHandler)new AsyncTriggerHandler<PointerEventData>(this, cancellationToken, true)).OnPointerExitAsync();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region PointerUp
|
||||
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
|
||||
|
||||
public interface IAsyncOnPointerUpHandler
|
||||
{
|
||||
@@ -4136,9 +4207,11 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
return ((IAsyncOnPointerUpHandler)new AsyncTriggerHandler<PointerEventData>(this, cancellationToken, true)).OnPointerUpAsync();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region Scroll
|
||||
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
|
||||
|
||||
public interface IAsyncOnScrollHandler
|
||||
{
|
||||
@@ -4195,9 +4268,11 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
return ((IAsyncOnScrollHandler)new AsyncTriggerHandler<PointerEventData>(this, cancellationToken, true)).OnScrollAsync();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region Select
|
||||
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
|
||||
|
||||
public interface IAsyncOnSelectHandler
|
||||
{
|
||||
@@ -4254,9 +4329,11 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
return ((IAsyncOnSelectHandler)new AsyncTriggerHandler<BaseEventData>(this, cancellationToken, true)).OnSelectAsync();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region Submit
|
||||
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
|
||||
|
||||
public interface IAsyncOnSubmitHandler
|
||||
{
|
||||
@@ -4313,9 +4390,11 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
return ((IAsyncOnSubmitHandler)new AsyncTriggerHandler<BaseEventData>(this, cancellationToken, true)).OnSubmitAsync();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region UpdateSelected
|
||||
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
|
||||
|
||||
public interface IAsyncOnUpdateSelectedHandler
|
||||
{
|
||||
@@ -4372,6 +4451,7 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
return ((IAsyncOnUpdateSelectedHandler)new AsyncTriggerHandler<BaseEventData>(this, cancellationToken, true)).OnUpdateSelectedAsync();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
}
|
||||
@@ -25,13 +25,7 @@
|
||||
("Update", "Update", "AsyncUnit", null, empty),
|
||||
("FixedUpdate", "FixedUpdate", "AsyncUnit", null, empty),
|
||||
("LateUpdate", "LateUpdate", "AsyncUnit", null, empty),
|
||||
("MouseDown", "OnMouseDown", "AsyncUnit", null, empty),
|
||||
("MouseDrag", "OnMouseDrag", "AsyncUnit", null, empty),
|
||||
("MouseEnter", "OnMouseEnter", "AsyncUnit", null, empty),
|
||||
("MouseExit", "OnMouseExit", "AsyncUnit", null, empty),
|
||||
("MouseOver", "OnMouseOver", "AsyncUnit", null, empty),
|
||||
("MouseUp", "OnMouseUp", "AsyncUnit", null, empty),
|
||||
("MouseUpAsButton", "OnMouseUpAsButton", "AsyncUnit", null, empty),
|
||||
|
||||
("ParticleCollision", "OnParticleCollision", "GameObject", null, new []{ ("GameObject", "other") }),
|
||||
("RectTransformDimensionsChange", "OnRectTransformDimensionsChange", "AsyncUnit", null, empty),
|
||||
("RectTransformRemoved", "OnRectTransformRemoved", "AsyncUnit", null, empty),
|
||||
@@ -47,6 +41,15 @@
|
||||
("BecameInvisible", "OnBecameInvisible", "AsyncUnit", null, empty),
|
||||
("BecameVisible", "OnBecameVisible", "AsyncUnit", null, empty),
|
||||
|
||||
// Mouse... #if !(UNITY_IPHONE || UNITY_ANDROID || UNITY_METRO)
|
||||
("MouseDown", "OnMouseDown", "AsyncUnit", null, empty),
|
||||
("MouseDrag", "OnMouseDrag", "AsyncUnit", null, empty),
|
||||
("MouseEnter", "OnMouseEnter", "AsyncUnit", null, empty),
|
||||
("MouseExit", "OnMouseExit", "AsyncUnit", null, empty),
|
||||
("MouseOver", "OnMouseOver", "AsyncUnit", null, empty),
|
||||
("MouseUp", "OnMouseUp", "AsyncUnit", null, empty),
|
||||
("MouseUpAsButton", "OnMouseUpAsButton", "AsyncUnit", null, empty),
|
||||
|
||||
// new in v2
|
||||
("ApplicationFocus", "OnApplicationFocus", "bool", null, new []{("bool", "hasFocus") }),
|
||||
("ApplicationPause", "OnApplicationPause", "bool", null, new []{("bool", "pauseStatus") }),
|
||||
@@ -103,20 +106,36 @@
|
||||
Func<(string argType, string argName)[], string> BuildMethodArgument = x => string.Join(", ", x.Select(y => y.argType + " " + y.argName));
|
||||
Func<(string argType, string argName)[], string> BuildResultParameter = x => x.Length == 0 ? "AsyncUnit.Default" : "(" + string.Join(", ", x.Select(y => y.argName)) + ")";
|
||||
|
||||
Func<string, bool> Is2019_3 = x => x == "ParticleUpdateJobScheduled";
|
||||
Func<string, bool> IsParticleSystem = x => x == "ParticleUpdateJobScheduled";
|
||||
Func<string, bool> IsMouseTrigger = x => x.StartsWith("Mouse");
|
||||
Func<string, string> RequirePhysicsModule = x => (x.StartsWith("Collision") || x.StartsWith("Collider") || x.StartsWith("ControllerCollider") || x.StartsWith("Joint") || x.StartsWith("Trigger"))
|
||||
? (x.Contains("2D") ? "UNITASK_PHYSICS2D_SUPPORT" : "UNITASK_PHYSICS_SUPPORT")
|
||||
: null;
|
||||
Func<string, bool> IsUguiSystem = x => x != null;
|
||||
#>
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
|
||||
using System.Threading;
|
||||
using UnityEngine;
|
||||
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
|
||||
using UnityEngine.EventSystems;
|
||||
#endif
|
||||
|
||||
namespace Cysharp.Threading.Tasks.Triggers
|
||||
{
|
||||
<# foreach(var t in triggers) { #>
|
||||
#region <#= t.triggerName #>
|
||||
<# if(Is2019_3(t.triggerName)) { #>
|
||||
#if UNITY_2019_3_OR_NEWER
|
||||
<# if(IsUguiSystem(t.handlerInterface)) { #>
|
||||
#if !UNITY_2019_1_OR_NEWER || UNITASK_UGUI_SUPPORT
|
||||
<# } #>
|
||||
<# if(IsParticleSystem(t.triggerName)) { #>
|
||||
#if UNITY_2019_3_OR_NEWER && (!UNITY_2019_1_OR_NEWER || UNITASK_PARTICLESYSTEM_SUPPORT)
|
||||
<# } #>
|
||||
<# if(IsMouseTrigger(t.triggerName)) { #>
|
||||
#if !(UNITY_IPHONE || UNITY_ANDROID || UNITY_METRO)
|
||||
<# } #>
|
||||
<# if(RequirePhysicsModule(t.triggerName) != null) { #>
|
||||
#if !UNITY_2019_1_OR_NEWER || <#= RequirePhysicsModule(t.triggerName) #>
|
||||
<# } #>
|
||||
|
||||
public interface <#= ToInterfaceName(t.methodName) #>
|
||||
@@ -174,7 +193,13 @@ namespace Cysharp.Threading.Tasks.Triggers
|
||||
return ((<#= ToInterfaceName(t.methodName) #>)new AsyncTriggerHandler<<#= t.returnType #>>(this, cancellationToken, true)).<#= t.methodName #>Async();
|
||||
}
|
||||
}
|
||||
<# if(Is2019_3(t.triggerName)) { #>
|
||||
<# if(IsUguiSystem(t.handlerInterface)) { #>
|
||||
#endif
|
||||
<# } #>
|
||||
<# if(RequirePhysicsModule(t.triggerName) != null) { #>
|
||||
#endif
|
||||
<# } #>
|
||||
<# if(IsParticleSystem(t.triggerName) || IsMouseTrigger(t.triggerName)) { #>
|
||||
#endif
|
||||
<# } #>
|
||||
#endregion
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
#pragma warning disable 0649
|
||||
|
||||
#if UNITASK_NETCORE || UNITY_2022_3_OR_NEWER
|
||||
#define SUPPORT_VALUETASK
|
||||
#endif
|
||||
|
||||
#if SUPPORT_VALUETASK
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks.Sources;
|
||||
@@ -10,7 +16,7 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
public static ValueTask AsValueTask(this in UniTask task)
|
||||
{
|
||||
#if NETSTANDARD2_0
|
||||
#if (UNITASK_NETCORE && NETSTANDARD2_0)
|
||||
return new ValueTask(new UniTaskValueTaskSource(task), 0);
|
||||
#else
|
||||
return task;
|
||||
@@ -19,7 +25,7 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
public static ValueTask<T> AsValueTask<T>(this in UniTask<T> task)
|
||||
{
|
||||
#if NETSTANDARD2_0
|
||||
#if (UNITASK_NETCORE && NETSTANDARD2_0)
|
||||
return new ValueTask<T>(new UniTaskValueTaskSource<T>(task), 0);
|
||||
#else
|
||||
return task;
|
||||
@@ -36,7 +42,7 @@ namespace Cysharp.Threading.Tasks
|
||||
await task;
|
||||
}
|
||||
|
||||
#if NETSTANDARD2_0
|
||||
#if (UNITASK_NETCORE && NETSTANDARD2_0)
|
||||
|
||||
class UniTaskValueTaskSource : IValueTaskSource
|
||||
{
|
||||
@@ -95,3 +101,4 @@ namespace Cysharp.Threading.Tasks
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,8 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3518da33b6245d341a0ef3670ee9268b
|
||||
timeCreated: 1488689723
|
||||
licenseType: Pro
|
||||
guid: d38f0478933be42d895c37b862540a1c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
using Cysharp.Threading.Tasks.Internal;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using UnityEngine;
|
||||
@@ -20,106 +21,176 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
public partial struct UniTask
|
||||
{
|
||||
public static YieldAwaitable Yield(PlayerLoopTiming timing = PlayerLoopTiming.Update)
|
||||
public static YieldAwaitable Yield()
|
||||
{
|
||||
// optimized for single continuation
|
||||
return new YieldAwaitable(PlayerLoopTiming.Update);
|
||||
}
|
||||
|
||||
public static YieldAwaitable Yield(PlayerLoopTiming timing)
|
||||
{
|
||||
// optimized for single continuation
|
||||
return new YieldAwaitable(timing);
|
||||
}
|
||||
|
||||
public static UniTask Yield(PlayerLoopTiming timing, CancellationToken cancellationToken)
|
||||
public static UniTask Yield(CancellationToken cancellationToken, bool cancelImmediately = false)
|
||||
{
|
||||
return new UniTask(YieldPromise.Create(timing, cancellationToken, out var token), token);
|
||||
return new UniTask(YieldPromise.Create(PlayerLoopTiming.Update, cancellationToken, cancelImmediately, out var token), token);
|
||||
}
|
||||
|
||||
public static UniTask Yield(PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately = false)
|
||||
{
|
||||
return new UniTask(YieldPromise.Create(timing, cancellationToken, cancelImmediately, out var token), token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Similar as UniTask.Yield but guaranteed run on next frame.
|
||||
/// </summary>
|
||||
public static UniTask NextFrame(PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default)
|
||||
public static UniTask NextFrame()
|
||||
{
|
||||
return new UniTask(NextFramePromise.Create(timing, cancellationToken, out var token), token);
|
||||
return new UniTask(NextFramePromise.Create(PlayerLoopTiming.Update, CancellationToken.None, false, out var token), token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Same as UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate).
|
||||
/// Similar as UniTask.Yield but guaranteed run on next frame.
|
||||
/// </summary>
|
||||
public static UniTask NextFrame(PlayerLoopTiming timing)
|
||||
{
|
||||
return new UniTask(NextFramePromise.Create(timing, CancellationToken.None, false, out var token), token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Similar as UniTask.Yield but guaranteed run on next frame.
|
||||
/// </summary>
|
||||
public static UniTask NextFrame(CancellationToken cancellationToken, bool cancelImmediately = false)
|
||||
{
|
||||
return new UniTask(NextFramePromise.Create(PlayerLoopTiming.Update, cancellationToken, cancelImmediately, out var token), token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Similar as UniTask.Yield but guaranteed run on next frame.
|
||||
/// </summary>
|
||||
public static UniTask NextFrame(PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately = false)
|
||||
{
|
||||
return new UniTask(NextFramePromise.Create(timing, cancellationToken, cancelImmediately, out var token), token);
|
||||
}
|
||||
|
||||
#if UNITY_2023_1_OR_NEWER
|
||||
public static async UniTask WaitForEndOfFrame(CancellationToken cancellationToken = default)
|
||||
{
|
||||
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()
|
||||
{
|
||||
return UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Same as UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate, cancellationToken).
|
||||
/// </summary>
|
||||
public static UniTask WaitForEndOfFrame(CancellationToken cancellationToken)
|
||||
[Obsolete("Use WaitForEndOfFrame(MonoBehaviour) instead or UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate). Equivalent for coroutine's WaitForEndOfFrame requires MonoBehaviour(runner of Coroutine).")]
|
||||
public static UniTask WaitForEndOfFrame(CancellationToken cancellationToken, bool cancelImmediately = false)
|
||||
{
|
||||
return UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate, cancellationToken);
|
||||
return UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate, cancellationToken, cancelImmediately);
|
||||
}
|
||||
#endif
|
||||
|
||||
public static UniTask WaitForEndOfFrame(MonoBehaviour coroutineRunner)
|
||||
{
|
||||
var source = WaitForEndOfFramePromise.Create(coroutineRunner, CancellationToken.None, false, out var token);
|
||||
return new UniTask(source, token);
|
||||
}
|
||||
|
||||
public static UniTask WaitForEndOfFrame(MonoBehaviour coroutineRunner, CancellationToken cancellationToken, bool cancelImmediately = false)
|
||||
{
|
||||
var source = WaitForEndOfFramePromise.Create(coroutineRunner, cancellationToken, cancelImmediately, out var token);
|
||||
return new UniTask(source, token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Same as UniTask.Yield(PlayerLoopTiming.FixedUpdate).
|
||||
/// Same as UniTask.Yield(PlayerLoopTiming.LastFixedUpdate).
|
||||
/// </summary>
|
||||
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>
|
||||
/// Same as UniTask.Yield(PlayerLoopTiming.FixedUpdate, cancellationToken).
|
||||
/// Same as UniTask.Yield(PlayerLoopTiming.LastFixedUpdate, cancellationToken).
|
||||
/// </summary>
|
||||
public static UniTask WaitForFixedUpdate(CancellationToken cancellationToken)
|
||||
public static UniTask WaitForFixedUpdate(CancellationToken cancellationToken, bool cancelImmediately = false)
|
||||
{
|
||||
return UniTask.Yield(PlayerLoopTiming.FixedUpdate, cancellationToken);
|
||||
return UniTask.Yield(PlayerLoopTiming.LastFixedUpdate, cancellationToken, cancelImmediately);
|
||||
}
|
||||
|
||||
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), bool cancelImmediately = false)
|
||||
{
|
||||
return Delay(Mathf.RoundToInt(1000 * duration), ignoreTimeScale, delayTiming, cancellationToken, cancelImmediately);
|
||||
}
|
||||
|
||||
public static UniTask WaitForSeconds(int duration, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
|
||||
{
|
||||
return Delay(1000 * duration, ignoreTimeScale, delayTiming, cancellationToken, cancelImmediately);
|
||||
}
|
||||
|
||||
public static UniTask DelayFrame(int delayFrameCount, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
|
||||
{
|
||||
if (delayFrameCount < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("Delay does not allow minus delayFrameCount. delayFrameCount:" + delayFrameCount);
|
||||
}
|
||||
|
||||
return new UniTask(DelayFramePromise.Create(delayFrameCount, delayTiming, cancellationToken, out var token), token);
|
||||
return new UniTask(DelayFramePromise.Create(delayFrameCount, delayTiming, cancellationToken, cancelImmediately, out var token), token);
|
||||
}
|
||||
|
||||
public static UniTask Delay(int millisecondsDelay, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
|
||||
public static UniTask Delay(int millisecondsDelay, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
|
||||
{
|
||||
var delayTimeSpan = TimeSpan.FromMilliseconds(millisecondsDelay);
|
||||
return Delay(delayTimeSpan, ignoreTimeScale, delayTiming, cancellationToken);
|
||||
return Delay(delayTimeSpan, ignoreTimeScale, delayTiming, cancellationToken, cancelImmediately);
|
||||
}
|
||||
|
||||
public static UniTask Delay(TimeSpan delayTimeSpan, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
|
||||
public static UniTask Delay(TimeSpan delayTimeSpan, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
|
||||
{
|
||||
var delayType = ignoreTimeScale ? DelayType.UnscaledDeltaTime : DelayType.DeltaTime;
|
||||
return Delay(delayTimeSpan, delayType, delayTiming, cancellationToken);
|
||||
return Delay(delayTimeSpan, delayType, delayTiming, cancellationToken, cancelImmediately);
|
||||
}
|
||||
|
||||
public static UniTask Delay(int millisecondsDelay, DelayType delayType, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
|
||||
public static UniTask Delay(int millisecondsDelay, DelayType delayType, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
|
||||
{
|
||||
var delayTimeSpan = TimeSpan.FromMilliseconds(millisecondsDelay);
|
||||
return Delay(delayTimeSpan, delayType, delayTiming, cancellationToken);
|
||||
return Delay(delayTimeSpan, delayType, delayTiming, cancellationToken, cancelImmediately);
|
||||
}
|
||||
|
||||
public static UniTask Delay(TimeSpan delayTimeSpan, DelayType delayType, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
|
||||
public static UniTask Delay(TimeSpan delayTimeSpan, DelayType delayType, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
|
||||
{
|
||||
if (delayTimeSpan < TimeSpan.Zero)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("Delay does not allow minus delayTimeSpan. delayTimeSpan:" + delayTimeSpan);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
// force use Realtime.
|
||||
if (PlayerLoopHelper.IsMainThread && !UnityEditor.EditorApplication.isPlaying)
|
||||
{
|
||||
delayType = DelayType.Realtime;
|
||||
}
|
||||
#endif
|
||||
|
||||
switch (delayType)
|
||||
{
|
||||
case DelayType.UnscaledDeltaTime:
|
||||
{
|
||||
return new UniTask(DelayIgnoreTimeScalePromise.Create(delayTimeSpan, delayTiming, cancellationToken, out var token), token);
|
||||
return new UniTask(DelayIgnoreTimeScalePromise.Create(delayTimeSpan, delayTiming, cancellationToken, cancelImmediately, out var token), token);
|
||||
}
|
||||
case DelayType.Realtime:
|
||||
{
|
||||
return new UniTask(DelayRealtimePromise.Create(delayTimeSpan, delayTiming, cancellationToken, out var token), token);
|
||||
return new UniTask(DelayRealtimePromise.Create(delayTimeSpan, delayTiming, cancellationToken, cancelImmediately, out var token), token);
|
||||
}
|
||||
case DelayType.DeltaTime:
|
||||
default:
|
||||
{
|
||||
return new UniTask(DelayPromise.Create(delayTimeSpan, delayTiming, cancellationToken, out var token), token);
|
||||
return new UniTask(DelayPromise.Create(delayTimeSpan, delayTiming, cancellationToken, cancelImmediately, out var token), token);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -127,7 +198,8 @@ namespace Cysharp.Threading.Tasks
|
||||
sealed class YieldPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<YieldPromise>
|
||||
{
|
||||
static TaskPool<YieldPromise> pool;
|
||||
public YieldPromise NextNode { get; set; }
|
||||
YieldPromise nextNode;
|
||||
public ref YieldPromise NextNode => ref nextNode;
|
||||
|
||||
static YieldPromise()
|
||||
{
|
||||
@@ -135,13 +207,15 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
|
||||
CancellationToken cancellationToken;
|
||||
CancellationTokenRegistration cancellationTokenRegistration;
|
||||
bool cancelImmediately;
|
||||
UniTaskCompletionSourceCore<object> core;
|
||||
|
||||
YieldPromise()
|
||||
{
|
||||
}
|
||||
|
||||
public static IUniTaskSource Create(PlayerLoopTiming timing, CancellationToken cancellationToken, out short token)
|
||||
public static IUniTaskSource Create(PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
@@ -153,8 +227,17 @@ namespace Cysharp.Threading.Tasks
|
||||
result = new YieldPromise();
|
||||
}
|
||||
|
||||
|
||||
result.cancellationToken = cancellationToken;
|
||||
result.cancelImmediately = cancelImmediately;
|
||||
|
||||
if (cancelImmediately && cancellationToken.CanBeCanceled)
|
||||
{
|
||||
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
|
||||
{
|
||||
var promise = (YieldPromise)state;
|
||||
promise.core.TrySetCanceled(promise.cancellationToken);
|
||||
}, result);
|
||||
}
|
||||
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
@@ -172,7 +255,14 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
finally
|
||||
{
|
||||
TryReturn();
|
||||
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
|
||||
{
|
||||
TryReturn();
|
||||
}
|
||||
else
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,6 +298,8 @@ namespace Cysharp.Threading.Tasks
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.Reset();
|
||||
cancellationToken = default;
|
||||
cancellationTokenRegistration.Dispose();
|
||||
cancelImmediately = default;
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
}
|
||||
@@ -215,7 +307,8 @@ namespace Cysharp.Threading.Tasks
|
||||
sealed class NextFramePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<NextFramePromise>
|
||||
{
|
||||
static TaskPool<NextFramePromise> pool;
|
||||
public NextFramePromise NextNode { get; set; }
|
||||
NextFramePromise nextNode;
|
||||
public ref NextFramePromise NextNode => ref nextNode;
|
||||
|
||||
static NextFramePromise()
|
||||
{
|
||||
@@ -223,14 +316,16 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
|
||||
int frameCount;
|
||||
CancellationToken cancellationToken;
|
||||
UniTaskCompletionSourceCore<AsyncUnit> core;
|
||||
CancellationToken cancellationToken;
|
||||
CancellationTokenRegistration cancellationTokenRegistration;
|
||||
bool cancelImmediately;
|
||||
|
||||
NextFramePromise()
|
||||
{
|
||||
}
|
||||
|
||||
public static IUniTaskSource Create(PlayerLoopTiming timing, CancellationToken cancellationToken, out short token)
|
||||
public static IUniTaskSource Create(PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
@@ -244,6 +339,16 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
result.frameCount = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1;
|
||||
result.cancellationToken = cancellationToken;
|
||||
result.cancelImmediately = cancelImmediately;
|
||||
|
||||
if (cancelImmediately && cancellationToken.CanBeCanceled)
|
||||
{
|
||||
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
|
||||
{
|
||||
var promise = (NextFramePromise)state;
|
||||
promise.core.TrySetCanceled(promise.cancellationToken);
|
||||
}, result);
|
||||
}
|
||||
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
@@ -261,7 +366,14 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
finally
|
||||
{
|
||||
TryReturn();
|
||||
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
|
||||
{
|
||||
TryReturn();
|
||||
}
|
||||
else
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,14 +414,143 @@ namespace Cysharp.Threading.Tasks
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.Reset();
|
||||
cancellationToken = default;
|
||||
cancellationTokenRegistration.Dispose();
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
UniTaskCompletionSourceCore<object> core;
|
||||
CancellationToken cancellationToken;
|
||||
CancellationTokenRegistration cancellationTokenRegistration;
|
||||
bool cancelImmediately;
|
||||
|
||||
WaitForEndOfFramePromise()
|
||||
{
|
||||
}
|
||||
|
||||
public static IUniTaskSource Create(MonoBehaviour coroutineRunner, CancellationToken cancellationToken, bool cancelImmediately, out short token)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
|
||||
}
|
||||
|
||||
if (!pool.TryPop(out var result))
|
||||
{
|
||||
result = new WaitForEndOfFramePromise();
|
||||
}
|
||||
|
||||
result.cancellationToken = cancellationToken;
|
||||
result.cancelImmediately = cancelImmediately;
|
||||
|
||||
if (cancelImmediately && cancellationToken.CanBeCanceled)
|
||||
{
|
||||
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
|
||||
{
|
||||
var promise = (WaitForEndOfFramePromise)state;
|
||||
promise.core.TrySetCanceled(promise.cancellationToken);
|
||||
}, result);
|
||||
}
|
||||
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
coroutineRunner.StartCoroutine(result);
|
||||
|
||||
token = result.core.Version;
|
||||
return result;
|
||||
}
|
||||
|
||||
public void GetResult(short token)
|
||||
{
|
||||
try
|
||||
{
|
||||
core.GetResult(token);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
|
||||
{
|
||||
TryReturn();
|
||||
}
|
||||
else
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public UniTaskStatus GetStatus(short token)
|
||||
{
|
||||
return core.GetStatus(token);
|
||||
}
|
||||
|
||||
public UniTaskStatus UnsafeGetStatus()
|
||||
{
|
||||
return core.UnsafeGetStatus();
|
||||
}
|
||||
|
||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||
{
|
||||
core.OnCompleted(continuation, state, token);
|
||||
}
|
||||
|
||||
bool TryReturn()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.Reset();
|
||||
Reset(); // Reset Enumerator
|
||||
cancellationToken = default;
|
||||
cancellationTokenRegistration.Dispose();
|
||||
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>
|
||||
{
|
||||
static TaskPool<DelayFramePromise> pool;
|
||||
public DelayFramePromise NextNode { get; set; }
|
||||
DelayFramePromise nextNode;
|
||||
public ref DelayFramePromise NextNode => ref nextNode;
|
||||
|
||||
static DelayFramePromise()
|
||||
{
|
||||
@@ -319,6 +560,8 @@ namespace Cysharp.Threading.Tasks
|
||||
int initialFrame;
|
||||
int delayFrameCount;
|
||||
CancellationToken cancellationToken;
|
||||
CancellationTokenRegistration cancellationTokenRegistration;
|
||||
bool cancelImmediately;
|
||||
|
||||
int currentFrameCount;
|
||||
UniTaskCompletionSourceCore<AsyncUnit> core;
|
||||
@@ -327,7 +570,7 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
}
|
||||
|
||||
public static IUniTaskSource Create(int delayFrameCount, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token)
|
||||
public static IUniTaskSource Create(int delayFrameCount, PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
@@ -342,6 +585,16 @@ namespace Cysharp.Threading.Tasks
|
||||
result.delayFrameCount = delayFrameCount;
|
||||
result.cancellationToken = cancellationToken;
|
||||
result.initialFrame = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1;
|
||||
result.cancelImmediately = cancelImmediately;
|
||||
|
||||
if (cancelImmediately && cancellationToken.CanBeCanceled)
|
||||
{
|
||||
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
|
||||
{
|
||||
var promise = (DelayFramePromise)state;
|
||||
promise.core.TrySetCanceled(promise.cancellationToken);
|
||||
}, result);
|
||||
}
|
||||
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
@@ -359,7 +612,14 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
finally
|
||||
{
|
||||
TryReturn();
|
||||
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
|
||||
{
|
||||
TryReturn();
|
||||
}
|
||||
else
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -397,7 +657,19 @@ namespace Cysharp.Threading.Tasks
|
||||
// skip in initial frame.
|
||||
if (initialFrame == Time.frameCount)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
// force use Realtime.
|
||||
if (PlayerLoopHelper.IsMainThread && !UnityEditor.EditorApplication.isPlaying)
|
||||
{
|
||||
//goto ++currentFrameCount
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -417,6 +689,8 @@ namespace Cysharp.Threading.Tasks
|
||||
currentFrameCount = default;
|
||||
delayFrameCount = default;
|
||||
cancellationToken = default;
|
||||
cancellationTokenRegistration.Dispose();
|
||||
cancelImmediately = default;
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
}
|
||||
@@ -424,7 +698,8 @@ namespace Cysharp.Threading.Tasks
|
||||
sealed class DelayPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<DelayPromise>
|
||||
{
|
||||
static TaskPool<DelayPromise> pool;
|
||||
public DelayPromise NextNode { get; set; }
|
||||
DelayPromise nextNode;
|
||||
public ref DelayPromise NextNode => ref nextNode;
|
||||
|
||||
static DelayPromise()
|
||||
{
|
||||
@@ -435,6 +710,8 @@ namespace Cysharp.Threading.Tasks
|
||||
float delayTimeSpan;
|
||||
float elapsed;
|
||||
CancellationToken cancellationToken;
|
||||
CancellationTokenRegistration cancellationTokenRegistration;
|
||||
bool cancelImmediately;
|
||||
|
||||
UniTaskCompletionSourceCore<object> core;
|
||||
|
||||
@@ -442,7 +719,7 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
}
|
||||
|
||||
public static IUniTaskSource Create(TimeSpan delayTimeSpan, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token)
|
||||
public static IUniTaskSource Create(TimeSpan delayTimeSpan, PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
@@ -458,6 +735,16 @@ namespace Cysharp.Threading.Tasks
|
||||
result.delayTimeSpan = (float)delayTimeSpan.TotalSeconds;
|
||||
result.cancellationToken = cancellationToken;
|
||||
result.initialFrame = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1;
|
||||
result.cancelImmediately = cancelImmediately;
|
||||
|
||||
if (cancelImmediately && cancellationToken.CanBeCanceled)
|
||||
{
|
||||
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
|
||||
{
|
||||
var promise = (DelayPromise)state;
|
||||
promise.core.TrySetCanceled(promise.cancellationToken);
|
||||
}, result);
|
||||
}
|
||||
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
@@ -475,7 +762,14 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
finally
|
||||
{
|
||||
TryReturn();
|
||||
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
|
||||
{
|
||||
TryReturn();
|
||||
}
|
||||
else
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -527,6 +821,8 @@ namespace Cysharp.Threading.Tasks
|
||||
delayTimeSpan = default;
|
||||
elapsed = default;
|
||||
cancellationToken = default;
|
||||
cancellationTokenRegistration.Dispose();
|
||||
cancelImmediately = default;
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
}
|
||||
@@ -534,7 +830,8 @@ namespace Cysharp.Threading.Tasks
|
||||
sealed class DelayIgnoreTimeScalePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<DelayIgnoreTimeScalePromise>
|
||||
{
|
||||
static TaskPool<DelayIgnoreTimeScalePromise> pool;
|
||||
public DelayIgnoreTimeScalePromise NextNode { get; set; }
|
||||
DelayIgnoreTimeScalePromise nextNode;
|
||||
public ref DelayIgnoreTimeScalePromise NextNode => ref nextNode;
|
||||
|
||||
static DelayIgnoreTimeScalePromise()
|
||||
{
|
||||
@@ -545,6 +842,8 @@ namespace Cysharp.Threading.Tasks
|
||||
float elapsed;
|
||||
int initialFrame;
|
||||
CancellationToken cancellationToken;
|
||||
CancellationTokenRegistration cancellationTokenRegistration;
|
||||
bool cancelImmediately;
|
||||
|
||||
UniTaskCompletionSourceCore<object> core;
|
||||
|
||||
@@ -552,7 +851,7 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
}
|
||||
|
||||
public static IUniTaskSource Create(TimeSpan delayFrameTimeSpan, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token)
|
||||
public static IUniTaskSource Create(TimeSpan delayFrameTimeSpan, PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
@@ -568,6 +867,16 @@ namespace Cysharp.Threading.Tasks
|
||||
result.delayFrameTimeSpan = (float)delayFrameTimeSpan.TotalSeconds;
|
||||
result.initialFrame = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1;
|
||||
result.cancellationToken = cancellationToken;
|
||||
result.cancelImmediately = cancelImmediately;
|
||||
|
||||
if (cancelImmediately && cancellationToken.CanBeCanceled)
|
||||
{
|
||||
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
|
||||
{
|
||||
var promise = (DelayIgnoreTimeScalePromise)state;
|
||||
promise.core.TrySetCanceled(promise.cancellationToken);
|
||||
}, result);
|
||||
}
|
||||
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
@@ -585,7 +894,14 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
finally
|
||||
{
|
||||
TryReturn();
|
||||
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
|
||||
{
|
||||
TryReturn();
|
||||
}
|
||||
else
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -637,6 +953,8 @@ namespace Cysharp.Threading.Tasks
|
||||
delayFrameTimeSpan = default;
|
||||
elapsed = default;
|
||||
cancellationToken = default;
|
||||
cancellationTokenRegistration.Dispose();
|
||||
cancelImmediately = default;
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
}
|
||||
@@ -644,7 +962,8 @@ namespace Cysharp.Threading.Tasks
|
||||
sealed class DelayRealtimePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<DelayRealtimePromise>
|
||||
{
|
||||
static TaskPool<DelayRealtimePromise> pool;
|
||||
public DelayRealtimePromise NextNode { get; set; }
|
||||
DelayRealtimePromise nextNode;
|
||||
public ref DelayRealtimePromise NextNode => ref nextNode;
|
||||
|
||||
static DelayRealtimePromise()
|
||||
{
|
||||
@@ -654,6 +973,8 @@ namespace Cysharp.Threading.Tasks
|
||||
long delayTimeSpanTicks;
|
||||
ValueStopwatch stopwatch;
|
||||
CancellationToken cancellationToken;
|
||||
CancellationTokenRegistration cancellationTokenRegistration;
|
||||
bool cancelImmediately;
|
||||
|
||||
UniTaskCompletionSourceCore<AsyncUnit> core;
|
||||
|
||||
@@ -661,7 +982,7 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
}
|
||||
|
||||
public static IUniTaskSource Create(TimeSpan delayTimeSpan, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token)
|
||||
public static IUniTaskSource Create(TimeSpan delayTimeSpan, PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
@@ -676,6 +997,16 @@ namespace Cysharp.Threading.Tasks
|
||||
result.stopwatch = ValueStopwatch.StartNew();
|
||||
result.delayTimeSpanTicks = delayTimeSpan.Ticks;
|
||||
result.cancellationToken = cancellationToken;
|
||||
result.cancelImmediately = cancelImmediately;
|
||||
|
||||
if (cancelImmediately && cancellationToken.CanBeCanceled)
|
||||
{
|
||||
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
|
||||
{
|
||||
var promise = (DelayRealtimePromise)state;
|
||||
promise.core.TrySetCanceled(promise.cancellationToken);
|
||||
}, result);
|
||||
}
|
||||
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
@@ -693,7 +1024,14 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
finally
|
||||
{
|
||||
TryReturn();
|
||||
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
|
||||
{
|
||||
TryReturn();
|
||||
}
|
||||
else
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -720,6 +1058,12 @@ namespace Cysharp.Threading.Tasks
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stopwatch.IsInvalid)
|
||||
{
|
||||
core.TrySetResult(AsyncUnit.Default);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stopwatch.ElapsedTicks >= delayTimeSpanTicks)
|
||||
{
|
||||
core.TrySetResult(AsyncUnit.Default);
|
||||
@@ -735,6 +1079,8 @@ namespace Cysharp.Threading.Tasks
|
||||
core.Reset();
|
||||
stopwatch = default;
|
||||
cancellationToken = default;
|
||||
cancellationTokenRegistration.Dispose();
|
||||
cancelImmediately = default;
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,6 +81,16 @@ namespace Cysharp.Threading.Tasks
|
||||
return factory();
|
||||
}
|
||||
|
||||
public static UniTask Create(Func<CancellationToken, UniTask> factory, CancellationToken cancellationToken)
|
||||
{
|
||||
return factory(cancellationToken);
|
||||
}
|
||||
|
||||
public static UniTask Create<T>(T state, Func<T, UniTask> factory)
|
||||
{
|
||||
return factory(state);
|
||||
}
|
||||
|
||||
public static UniTask<T> Create<T>(Func<UniTask<T>> factory)
|
||||
{
|
||||
return factory();
|
||||
@@ -137,11 +147,19 @@ namespace Cysharp.Threading.Tasks
|
||||
return () => asyncAction(cancellationToken).Forget();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// helper of create add UniTaskVoid to delegate.
|
||||
/// </summary>
|
||||
public static Action Action<T>(T state, Func<T, UniTaskVoid> asyncAction)
|
||||
{
|
||||
return () => asyncAction(state).Forget();
|
||||
}
|
||||
|
||||
#if UNITY_2018_3_OR_NEWER
|
||||
|
||||
/// <summary>
|
||||
/// Create async void(UniTaskVoid) UnityAction.
|
||||
/// For exampe: onClick.AddListener(UniTask.UnityAction(async () => { /* */ } ))
|
||||
/// For example: onClick.AddListener(UniTask.UnityAction(async () => { /* */ } ))
|
||||
/// </summary>
|
||||
public static UnityEngine.Events.UnityAction UnityAction(Func<UniTaskVoid> asyncAction)
|
||||
{
|
||||
@@ -150,13 +168,94 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
/// <summary>
|
||||
/// Create async void(UniTaskVoid) UnityAction.
|
||||
/// For exampe: onClick.AddListener(UniTask.UnityAction(FooAsync, this.GetCancellationTokenOnDestroy()))
|
||||
/// For example: onClick.AddListener(UniTask.UnityAction(FooAsync, this.GetCancellationTokenOnDestroy()))
|
||||
/// </summary>
|
||||
public static UnityEngine.Events.UnityAction UnityAction(Func<CancellationToken, UniTaskVoid> asyncAction, CancellationToken cancellationToken)
|
||||
{
|
||||
return () => asyncAction(cancellationToken).Forget();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create async void(UniTaskVoid) UnityAction.
|
||||
/// For example: onClick.AddListener(UniTask.UnityAction(FooAsync, Argument))
|
||||
/// </summary>
|
||||
public static UnityEngine.Events.UnityAction UnityAction<T>(T state, Func<T, UniTaskVoid> asyncAction)
|
||||
{
|
||||
return () => asyncAction(state).Forget();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create async void(UniTaskVoid) UnityAction.
|
||||
/// For example: onClick.AddListener(UniTask.UnityAction(async (T arg) => { /* */ } ))
|
||||
/// </summary>
|
||||
public static UnityEngine.Events.UnityAction<T> UnityAction<T>(Func<T, UniTaskVoid> asyncAction)
|
||||
{
|
||||
return (arg) => asyncAction(arg).Forget();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create async void(UniTaskVoid) UnityAction.
|
||||
/// For example: onClick.AddListener(UniTask.UnityAction(async (T0 arg0, T1 arg1) => { /* */ } ))
|
||||
/// </summary>
|
||||
public static UnityEngine.Events.UnityAction<T0, T1> UnityAction<T0, T1>(Func<T0, T1, UniTaskVoid> asyncAction)
|
||||
{
|
||||
return (arg0, arg1) => asyncAction(arg0, arg1).Forget();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create async void(UniTaskVoid) UnityAction.
|
||||
/// For example: onClick.AddListener(UniTask.UnityAction(async (T0 arg0, T1 arg1, T2 arg2) => { /* */ } ))
|
||||
/// </summary>
|
||||
public static UnityEngine.Events.UnityAction<T0, T1, T2> UnityAction<T0, T1, T2>(Func<T0, T1, T2, UniTaskVoid> asyncAction)
|
||||
{
|
||||
return (arg0, arg1, arg2) => asyncAction(arg0, arg1, arg2).Forget();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create async void(UniTaskVoid) UnityAction.
|
||||
/// For example: onClick.AddListener(UniTask.UnityAction(async (T0 arg0, T1 arg1, T2 arg2, T3 arg3) => { /* */ } ))
|
||||
/// </summary>
|
||||
public static UnityEngine.Events.UnityAction<T0, T1, T2, T3> UnityAction<T0, T1, T2, T3>(Func<T0, T1, T2, T3, UniTaskVoid> asyncAction)
|
||||
{
|
||||
return (arg0, arg1, arg2, arg3) => asyncAction(arg0, arg1, arg2, arg3).Forget();
|
||||
}
|
||||
|
||||
// <summary>
|
||||
/// Create async void(UniTaskVoid) UnityAction.
|
||||
/// For example: onClick.AddListener(UniTask.UnityAction(async (T arg, CancellationToken cancellationToken) => { /* */ } ))
|
||||
/// </summary>
|
||||
public static UnityEngine.Events.UnityAction<T> UnityAction<T>(Func<T, CancellationToken, UniTaskVoid> asyncAction, CancellationToken cancellationToken)
|
||||
{
|
||||
return (arg) => asyncAction(arg, cancellationToken).Forget();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create async void(UniTaskVoid) UnityAction.
|
||||
/// For example: onClick.AddListener(UniTask.UnityAction(async (T0 arg0, T1 arg1, CancellationToken cancellationToken) => { /* */ } ))
|
||||
/// </summary>
|
||||
public static UnityEngine.Events.UnityAction<T0, T1> UnityAction<T0, T1>(Func<T0, T1, CancellationToken, UniTaskVoid> asyncAction, CancellationToken cancellationToken)
|
||||
{
|
||||
return (arg0, arg1) => asyncAction(arg0, arg1, cancellationToken).Forget();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create async void(UniTaskVoid) UnityAction.
|
||||
/// For example: onClick.AddListener(UniTask.UnityAction(async (T0 arg0, T1 arg1, T2 arg2, CancellationToken cancellationToken) => { /* */ } ))
|
||||
/// </summary>
|
||||
public static UnityEngine.Events.UnityAction<T0, T1, T2> UnityAction<T0, T1, T2>(Func<T0, T1, T2, CancellationToken, UniTaskVoid> asyncAction, CancellationToken cancellationToken)
|
||||
{
|
||||
return (arg0, arg1, arg2) => asyncAction(arg0, arg1, arg2, cancellationToken).Forget();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create async void(UniTaskVoid) UnityAction.
|
||||
/// For example: onClick.AddListener(UniTask.UnityAction(async (T0 arg0, T1 arg1, T2 arg2, T3 arg3, CancellationToken cancellationToken) => { /* */ } ))
|
||||
/// </summary>
|
||||
public static UnityEngine.Events.UnityAction<T0, T1, T2, T3> UnityAction<T0, T1, T2, T3>(Func<T0, T1, T2, T3, CancellationToken, UniTaskVoid> asyncAction, CancellationToken cancellationToken)
|
||||
{
|
||||
return (arg0, arg1, arg2, arg3) => asyncAction(arg0, arg1, arg2, arg3, cancellationToken).Forget();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
@@ -175,6 +274,22 @@ namespace Cysharp.Threading.Tasks
|
||||
return new UniTask<T>(new DeferPromise<T>(factory), 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defer the task creation just before call await.
|
||||
/// </summary>
|
||||
public static UniTask Defer<TState>(TState state, Func<TState, UniTask> factory)
|
||||
{
|
||||
return new UniTask(new DeferPromiseWithState<TState>(state, factory), 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defer the task creation just before call await.
|
||||
/// </summary>
|
||||
public static UniTask<TResult> Defer<TState, TResult>(TState state, Func<TState, UniTask<TResult>> factory)
|
||||
{
|
||||
return new UniTask<TResult>(new DeferPromiseWithState<TState, TResult>(state, factory), 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Never complete.
|
||||
/// </summary>
|
||||
@@ -194,6 +309,7 @@ namespace Cysharp.Threading.Tasks
|
||||
sealed class ExceptionResultSource : IUniTaskSource
|
||||
{
|
||||
readonly ExceptionDispatchInfo exception;
|
||||
bool calledGet;
|
||||
|
||||
public ExceptionResultSource(Exception exception)
|
||||
{
|
||||
@@ -202,6 +318,11 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
public void GetResult(short token)
|
||||
{
|
||||
if (!calledGet)
|
||||
{
|
||||
calledGet = true;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
exception.Throw();
|
||||
}
|
||||
|
||||
@@ -219,11 +340,20 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
continuation(state);
|
||||
}
|
||||
|
||||
~ExceptionResultSource()
|
||||
{
|
||||
if (!calledGet)
|
||||
{
|
||||
UniTaskScheduler.PublishUnobservedTaskException(exception.SourceException);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class ExceptionResultSource<T> : IUniTaskSource<T>
|
||||
{
|
||||
readonly ExceptionDispatchInfo exception;
|
||||
bool calledGet;
|
||||
|
||||
public ExceptionResultSource(Exception exception)
|
||||
{
|
||||
@@ -232,12 +362,22 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
public T GetResult(short token)
|
||||
{
|
||||
if (!calledGet)
|
||||
{
|
||||
calledGet = true;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
exception.Throw();
|
||||
return default;
|
||||
}
|
||||
|
||||
void IUniTaskSource.GetResult(short token)
|
||||
{
|
||||
if (!calledGet)
|
||||
{
|
||||
calledGet = true;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
exception.Throw();
|
||||
}
|
||||
|
||||
@@ -255,6 +395,14 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
continuation(state);
|
||||
}
|
||||
|
||||
~ExceptionResultSource()
|
||||
{
|
||||
if (!calledGet)
|
||||
{
|
||||
UniTaskScheduler.PublishUnobservedTaskException(exception.SourceException);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class CanceledResultSource : IUniTaskSource
|
||||
@@ -341,10 +489,12 @@ namespace Cysharp.Threading.Tasks
|
||||
public UniTaskStatus GetStatus(short token)
|
||||
{
|
||||
var f = Interlocked.Exchange(ref factory, null);
|
||||
if (f == null) throw new InvalidOperationException("Can't call twice.");
|
||||
if (f != null)
|
||||
{
|
||||
task = f();
|
||||
awaiter = task.GetAwaiter();
|
||||
}
|
||||
|
||||
task = f();
|
||||
awaiter = task.GetAwaiter();
|
||||
return task.Status;
|
||||
}
|
||||
|
||||
@@ -383,10 +533,99 @@ namespace Cysharp.Threading.Tasks
|
||||
public UniTaskStatus GetStatus(short token)
|
||||
{
|
||||
var f = Interlocked.Exchange(ref factory, null);
|
||||
if (f == null) throw new InvalidOperationException("Can't call twice.");
|
||||
if (f != null)
|
||||
{
|
||||
task = f();
|
||||
awaiter = task.GetAwaiter();
|
||||
}
|
||||
|
||||
return task.Status;
|
||||
}
|
||||
|
||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||
{
|
||||
awaiter.SourceOnCompleted(continuation, state);
|
||||
}
|
||||
|
||||
public UniTaskStatus UnsafeGetStatus()
|
||||
{
|
||||
return task.Status;
|
||||
}
|
||||
}
|
||||
|
||||
sealed class DeferPromiseWithState<TState> : IUniTaskSource
|
||||
{
|
||||
Func<TState, UniTask> factory;
|
||||
TState argument;
|
||||
UniTask task;
|
||||
UniTask.Awaiter awaiter;
|
||||
|
||||
public DeferPromiseWithState(TState argument, Func<TState, UniTask> factory)
|
||||
{
|
||||
this.argument = argument;
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
public void GetResult(short token)
|
||||
{
|
||||
awaiter.GetResult();
|
||||
}
|
||||
|
||||
public UniTaskStatus GetStatus(short token)
|
||||
{
|
||||
var f = Interlocked.Exchange(ref factory, null);
|
||||
if (f != null)
|
||||
{
|
||||
task = f(argument);
|
||||
awaiter = task.GetAwaiter();
|
||||
}
|
||||
|
||||
return task.Status;
|
||||
}
|
||||
|
||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||
{
|
||||
awaiter.SourceOnCompleted(continuation, state);
|
||||
}
|
||||
|
||||
public UniTaskStatus UnsafeGetStatus()
|
||||
{
|
||||
return task.Status;
|
||||
}
|
||||
}
|
||||
|
||||
sealed class DeferPromiseWithState<TState, TResult> : IUniTaskSource<TResult>
|
||||
{
|
||||
Func<TState, UniTask<TResult>> factory;
|
||||
TState argument;
|
||||
UniTask<TResult> task;
|
||||
UniTask<TResult>.Awaiter awaiter;
|
||||
|
||||
public DeferPromiseWithState(TState argument, Func<TState, UniTask<TResult>> factory)
|
||||
{
|
||||
this.argument = argument;
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
public TResult GetResult(short token)
|
||||
{
|
||||
return awaiter.GetResult();
|
||||
}
|
||||
|
||||
void IUniTaskSource.GetResult(short token)
|
||||
{
|
||||
awaiter.GetResult();
|
||||
}
|
||||
|
||||
public UniTaskStatus GetStatus(short token)
|
||||
{
|
||||
var f = Interlocked.Exchange(ref factory, null);
|
||||
if (f != null)
|
||||
{
|
||||
task = f(argument);
|
||||
awaiter = task.GetAwaiter();
|
||||
}
|
||||
|
||||
task = f();
|
||||
awaiter = task.GetAwaiter();
|
||||
return task.Status;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,8 +7,60 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
public partial struct UniTask
|
||||
{
|
||||
#region OBSOLETE_RUN
|
||||
|
||||
[Obsolete("UniTask.Run is similar as Task.Run, it uses ThreadPool. For equivalent behaviour, use UniTask.RunOnThreadPool instead. If you don't want to use ThreadPool, you can use UniTask.Void(async void) or UniTask.Create(async UniTask) too.")]
|
||||
public static UniTask Run(Action action, bool configureAwait = true, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return RunOnThreadPool(action, configureAwait, cancellationToken);
|
||||
}
|
||||
|
||||
[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<object> action, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return RunOnThreadPool(action, state, configureAwait, cancellationToken);
|
||||
}
|
||||
|
||||
[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(Func<UniTask> action, bool configureAwait = true, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return RunOnThreadPool(action, configureAwait, cancellationToken);
|
||||
}
|
||||
|
||||
[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(Func<object, UniTask> action, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return RunOnThreadPool(action, state, configureAwait, cancellationToken);
|
||||
}
|
||||
|
||||
[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<T> Run<T>(Func<T> func, bool configureAwait = true, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return RunOnThreadPool(func, configureAwait, cancellationToken);
|
||||
}
|
||||
|
||||
[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<T> Run<T>(Func<UniTask<T>> func, bool configureAwait = true, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return RunOnThreadPool(func, configureAwait, cancellationToken);
|
||||
}
|
||||
|
||||
[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<T> Run<T>(Func<object, T> func, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return RunOnThreadPool(func, state, configureAwait, cancellationToken);
|
||||
}
|
||||
|
||||
[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<T> Run<T>(Func<object, UniTask<T>> func, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return RunOnThreadPool(func, state, configureAwait, cancellationToken);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
|
||||
public static async UniTask Run(Action action, bool configureAwait = true, CancellationToken cancellationToken = default)
|
||||
public static async UniTask RunOnThreadPool(Action action, bool configureAwait = true, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
@@ -36,7 +88,7 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
|
||||
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
|
||||
public static async UniTask Run(Action<object> action, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
|
||||
public static async UniTask RunOnThreadPool(Action<object> action, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
@@ -64,7 +116,7 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
|
||||
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
|
||||
public static async UniTask Run(Func<UniTask> action, bool configureAwait = true, CancellationToken cancellationToken = default)
|
||||
public static async UniTask RunOnThreadPool(Func<UniTask> action, bool configureAwait = true, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
@@ -92,7 +144,7 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
|
||||
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
|
||||
public static async UniTask Run(Func<object, UniTask> action, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
|
||||
public static async UniTask RunOnThreadPool(Func<object, UniTask> action, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
@@ -120,7 +172,7 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
|
||||
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
|
||||
public static async UniTask<T> Run<T>(Func<T> func, bool configureAwait = true, CancellationToken cancellationToken = default)
|
||||
public static async UniTask<T> RunOnThreadPool<T>(Func<T> func, bool configureAwait = true, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
@@ -147,7 +199,7 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
|
||||
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
|
||||
public static async UniTask<T> Run<T>(Func<UniTask<T>> func, bool configureAwait = true, CancellationToken cancellationToken = default)
|
||||
public static async UniTask<T> RunOnThreadPool<T>(Func<UniTask<T>> func, bool configureAwait = true, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
@@ -177,7 +229,7 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
|
||||
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
|
||||
public static async UniTask<T> Run<T>(Func<object, T> func, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
|
||||
public static async UniTask<T> RunOnThreadPool<T>(Func<object, T> func, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
@@ -204,7 +256,7 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
|
||||
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
|
||||
public static async UniTask<T> Run<T>(Func<object, UniTask<T>> func, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
|
||||
public static async UniTask<T> RunOnThreadPool<T>(Func<object, UniTask<T>> func, object state, bool configureAwait = true, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
|
||||
@@ -224,7 +224,8 @@ namespace Cysharp.Threading.Tasks
|
||||
sealed class ThreadPoolWorkItem : IThreadPoolWorkItem, ITaskPoolNode<ThreadPoolWorkItem>
|
||||
{
|
||||
static TaskPool<ThreadPoolWorkItem> pool;
|
||||
public ThreadPoolWorkItem NextNode { get; set; }
|
||||
ThreadPoolWorkItem nextNode;
|
||||
public ref ThreadPoolWorkItem NextNode => ref nextNode;
|
||||
|
||||
static ThreadPoolWorkItem()
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.Tracing;
|
||||
using System.Threading;
|
||||
using Cysharp.Threading.Tasks.Internal;
|
||||
|
||||
@@ -9,36 +10,47 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
public partial struct UniTask
|
||||
{
|
||||
public static UniTask WaitUntil(Func<bool> predicate, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
|
||||
public static UniTask WaitUntil(Func<bool> predicate, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
|
||||
{
|
||||
return new UniTask(WaitUntilPromise.Create(predicate, timing, cancellationToken, out var token), token);
|
||||
return new UniTask(WaitUntilPromise.Create(predicate, timing, cancellationToken, cancelImmediately, out var token), token);
|
||||
}
|
||||
|
||||
public static UniTask WaitWhile(Func<bool> predicate, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
|
||||
public static UniTask WaitUntil<T>(T state, Func<T, bool> predicate, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
|
||||
{
|
||||
return new UniTask(WaitWhilePromise.Create(predicate, timing, cancellationToken, out var token), token);
|
||||
return new UniTask(WaitUntilPromise<T>.Create(state, predicate, timing, cancellationToken, cancelImmediately, out var token), token);
|
||||
}
|
||||
|
||||
public static UniTask WaitUntilCanceled(CancellationToken cancellationToken, PlayerLoopTiming timing = PlayerLoopTiming.Update)
|
||||
public static UniTask WaitWhile(Func<bool> predicate, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
|
||||
{
|
||||
return new UniTask(WaitUntilCanceledPromise.Create(cancellationToken, timing, out var token), token);
|
||||
return new UniTask(WaitWhilePromise.Create(predicate, timing, cancellationToken, cancelImmediately, out var token), token);
|
||||
}
|
||||
|
||||
public static UniTask<U> WaitUntilValueChanged<T, U>(T target, Func<T, U> monitorFunction, PlayerLoopTiming monitorTiming = PlayerLoopTiming.Update, IEqualityComparer<U> equalityComparer = null, CancellationToken cancellationToken = default(CancellationToken))
|
||||
public static UniTask WaitWhile<T>(T state, Func<T, bool> predicate, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
|
||||
{
|
||||
return new UniTask(WaitWhilePromise<T>.Create(state, predicate, timing, cancellationToken, cancelImmediately, out var token), token);
|
||||
}
|
||||
|
||||
public static UniTask WaitUntilCanceled(CancellationToken cancellationToken, PlayerLoopTiming timing = PlayerLoopTiming.Update, bool completeImmediately = false)
|
||||
{
|
||||
return new UniTask(WaitUntilCanceledPromise.Create(cancellationToken, timing, completeImmediately, out var token), token);
|
||||
}
|
||||
|
||||
public static UniTask<U> WaitUntilValueChanged<T, U>(T target, Func<T, U> monitorFunction, PlayerLoopTiming monitorTiming = PlayerLoopTiming.Update, IEqualityComparer<U> equalityComparer = null, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
|
||||
where T : class
|
||||
{
|
||||
var unityObject = target as UnityEngine.Object;
|
||||
var isUnityObject = target is UnityEngine.Object; // don't use (unityObject == null)
|
||||
|
||||
return new UniTask<U>(isUnityObject
|
||||
? WaitUntilValueChangedUnityObjectPromise<T, U>.Create(target, monitorFunction, equalityComparer, monitorTiming, cancellationToken, out var token)
|
||||
: WaitUntilValueChangedStandardObjectPromise<T, U>.Create(target, monitorFunction, equalityComparer, monitorTiming, cancellationToken, out token), token);
|
||||
? WaitUntilValueChangedUnityObjectPromise<T, U>.Create(target, monitorFunction, equalityComparer, monitorTiming, cancellationToken, cancelImmediately, out var token)
|
||||
: WaitUntilValueChangedStandardObjectPromise<T, U>.Create(target, monitorFunction, equalityComparer, monitorTiming, cancellationToken, cancelImmediately, out token), token);
|
||||
}
|
||||
|
||||
sealed class WaitUntilPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<WaitUntilPromise>
|
||||
{
|
||||
static TaskPool<WaitUntilPromise> pool;
|
||||
public WaitUntilPromise NextNode { get; set; }
|
||||
WaitUntilPromise nextNode;
|
||||
public ref WaitUntilPromise NextNode => ref nextNode;
|
||||
|
||||
static WaitUntilPromise()
|
||||
{
|
||||
@@ -47,6 +59,8 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
Func<bool> predicate;
|
||||
CancellationToken cancellationToken;
|
||||
CancellationTokenRegistration cancellationTokenRegistration;
|
||||
bool cancelImmediately;
|
||||
|
||||
UniTaskCompletionSourceCore<object> core;
|
||||
|
||||
@@ -54,7 +68,7 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
}
|
||||
|
||||
public static IUniTaskSource Create(Func<bool> predicate, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token)
|
||||
public static IUniTaskSource Create(Func<bool> predicate, PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
@@ -68,6 +82,16 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
result.predicate = predicate;
|
||||
result.cancellationToken = cancellationToken;
|
||||
result.cancelImmediately = cancelImmediately;
|
||||
|
||||
if (cancelImmediately && cancellationToken.CanBeCanceled)
|
||||
{
|
||||
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
|
||||
{
|
||||
var promise = (WaitUntilPromise)state;
|
||||
promise.core.TrySetCanceled(promise.cancellationToken);
|
||||
}, result);
|
||||
}
|
||||
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
@@ -85,7 +109,14 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
finally
|
||||
{
|
||||
TryReturn();
|
||||
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
|
||||
{
|
||||
TryReturn();
|
||||
}
|
||||
else
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,30 +166,36 @@ namespace Cysharp.Threading.Tasks
|
||||
core.Reset();
|
||||
predicate = default;
|
||||
cancellationToken = default;
|
||||
cancellationTokenRegistration.Dispose();
|
||||
cancelImmediately = default;
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
}
|
||||
|
||||
sealed class WaitWhilePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<WaitWhilePromise>
|
||||
sealed class WaitUntilPromise<T> : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<WaitUntilPromise<T>>
|
||||
{
|
||||
static TaskPool<WaitWhilePromise> pool;
|
||||
public WaitWhilePromise NextNode { get; set; }
|
||||
static TaskPool<WaitUntilPromise<T>> pool;
|
||||
WaitUntilPromise<T> nextNode;
|
||||
public ref WaitUntilPromise<T> NextNode => ref nextNode;
|
||||
|
||||
static WaitWhilePromise()
|
||||
static WaitUntilPromise()
|
||||
{
|
||||
TaskPool.RegisterSizeGetter(typeof(WaitWhilePromise), () => pool.Size);
|
||||
TaskPool.RegisterSizeGetter(typeof(WaitUntilPromise<T>), () => pool.Size);
|
||||
}
|
||||
|
||||
Func<bool> predicate;
|
||||
Func<T, bool> predicate;
|
||||
T argument;
|
||||
CancellationToken cancellationToken;
|
||||
CancellationTokenRegistration cancellationTokenRegistration;
|
||||
bool cancelImmediately;
|
||||
|
||||
UniTaskCompletionSourceCore<object> core;
|
||||
|
||||
WaitWhilePromise()
|
||||
WaitUntilPromise()
|
||||
{
|
||||
}
|
||||
|
||||
public static IUniTaskSource Create(Func<bool> predicate, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token)
|
||||
public static IUniTaskSource Create(T argument, Func<T, bool> predicate, PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
@@ -167,11 +204,22 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
if (!pool.TryPop(out var result))
|
||||
{
|
||||
result = new WaitWhilePromise();
|
||||
result = new WaitUntilPromise<T>();
|
||||
}
|
||||
|
||||
result.predicate = predicate;
|
||||
result.argument = argument;
|
||||
result.cancellationToken = cancellationToken;
|
||||
result.cancelImmediately = cancelImmediately;
|
||||
|
||||
if (cancelImmediately && cancellationToken.CanBeCanceled)
|
||||
{
|
||||
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
|
||||
{
|
||||
var promise = (WaitUntilPromise<T>)state;
|
||||
promise.core.TrySetCanceled(promise.cancellationToken);
|
||||
}, result);
|
||||
}
|
||||
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
@@ -189,7 +237,141 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
finally
|
||||
{
|
||||
TryReturn();
|
||||
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
|
||||
{
|
||||
TryReturn();
|
||||
}
|
||||
else
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public UniTaskStatus GetStatus(short token)
|
||||
{
|
||||
return core.GetStatus(token);
|
||||
}
|
||||
|
||||
public UniTaskStatus UnsafeGetStatus()
|
||||
{
|
||||
return core.UnsafeGetStatus();
|
||||
}
|
||||
|
||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||
{
|
||||
core.OnCompleted(continuation, state, token);
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
core.TrySetCanceled(cancellationToken);
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (!predicate(argument))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
core.TrySetException(ex);
|
||||
return false;
|
||||
}
|
||||
|
||||
core.TrySetResult(null);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TryReturn()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.Reset();
|
||||
predicate = default;
|
||||
argument = default;
|
||||
cancellationToken = default;
|
||||
cancellationTokenRegistration.Dispose();
|
||||
cancelImmediately = default;
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
}
|
||||
|
||||
sealed class WaitWhilePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<WaitWhilePromise>
|
||||
{
|
||||
static TaskPool<WaitWhilePromise> pool;
|
||||
WaitWhilePromise nextNode;
|
||||
public ref WaitWhilePromise NextNode => ref nextNode;
|
||||
|
||||
static WaitWhilePromise()
|
||||
{
|
||||
TaskPool.RegisterSizeGetter(typeof(WaitWhilePromise), () => pool.Size);
|
||||
}
|
||||
|
||||
Func<bool> predicate;
|
||||
CancellationToken cancellationToken;
|
||||
CancellationTokenRegistration cancellationTokenRegistration;
|
||||
bool cancelImmediately;
|
||||
|
||||
UniTaskCompletionSourceCore<object> core;
|
||||
|
||||
WaitWhilePromise()
|
||||
{
|
||||
}
|
||||
|
||||
public static IUniTaskSource Create(Func<bool> predicate, PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
|
||||
}
|
||||
|
||||
if (!pool.TryPop(out var result))
|
||||
{
|
||||
result = new WaitWhilePromise();
|
||||
}
|
||||
|
||||
result.predicate = predicate;
|
||||
result.cancellationToken = cancellationToken;
|
||||
result.cancelImmediately = cancelImmediately;
|
||||
|
||||
if (cancelImmediately && cancellationToken.CanBeCanceled)
|
||||
{
|
||||
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
|
||||
{
|
||||
var promise = (WaitWhilePromise)state;
|
||||
promise.core.TrySetCanceled(promise.cancellationToken);
|
||||
}, result);
|
||||
}
|
||||
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
PlayerLoopHelper.AddAction(timing, result);
|
||||
|
||||
token = result.core.Version;
|
||||
return result;
|
||||
}
|
||||
|
||||
public void GetResult(short token)
|
||||
{
|
||||
try
|
||||
{
|
||||
core.GetResult(token);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
|
||||
{
|
||||
TryReturn();
|
||||
}
|
||||
else
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,29 +421,36 @@ namespace Cysharp.Threading.Tasks
|
||||
core.Reset();
|
||||
predicate = default;
|
||||
cancellationToken = default;
|
||||
cancellationTokenRegistration.Dispose();
|
||||
cancelImmediately = default;
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
}
|
||||
|
||||
sealed class WaitUntilCanceledPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<WaitUntilCanceledPromise>
|
||||
sealed class WaitWhilePromise<T> : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<WaitWhilePromise<T>>
|
||||
{
|
||||
static TaskPool<WaitUntilCanceledPromise> pool;
|
||||
public WaitUntilCanceledPromise NextNode { get; set; }
|
||||
static TaskPool<WaitWhilePromise<T>> pool;
|
||||
WaitWhilePromise<T> nextNode;
|
||||
public ref WaitWhilePromise<T> NextNode => ref nextNode;
|
||||
|
||||
static WaitUntilCanceledPromise()
|
||||
static WaitWhilePromise()
|
||||
{
|
||||
TaskPool.RegisterSizeGetter(typeof(WaitUntilCanceledPromise), () => pool.Size);
|
||||
TaskPool.RegisterSizeGetter(typeof(WaitWhilePromise<T>), () => pool.Size);
|
||||
}
|
||||
|
||||
Func<T, bool> predicate;
|
||||
T argument;
|
||||
CancellationToken cancellationToken;
|
||||
CancellationTokenRegistration cancellationTokenRegistration;
|
||||
bool cancelImmediately;
|
||||
|
||||
UniTaskCompletionSourceCore<object> core;
|
||||
|
||||
WaitUntilCanceledPromise()
|
||||
WaitWhilePromise()
|
||||
{
|
||||
}
|
||||
|
||||
public static IUniTaskSource Create(CancellationToken cancellationToken, PlayerLoopTiming timing, out short token)
|
||||
public static IUniTaskSource Create(T argument, Func<T, bool> predicate, PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
@@ -270,10 +459,22 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
if (!pool.TryPop(out var result))
|
||||
{
|
||||
result = new WaitUntilCanceledPromise();
|
||||
result = new WaitWhilePromise<T>();
|
||||
}
|
||||
|
||||
result.predicate = predicate;
|
||||
result.argument = argument;
|
||||
result.cancellationToken = cancellationToken;
|
||||
result.cancelImmediately = cancelImmediately;
|
||||
|
||||
if (cancelImmediately && cancellationToken.CanBeCanceled)
|
||||
{
|
||||
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
|
||||
{
|
||||
var promise = (WaitWhilePromise<T>)state;
|
||||
promise.core.TrySetCanceled(promise.cancellationToken);
|
||||
}, result);
|
||||
}
|
||||
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
@@ -291,7 +492,139 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
finally
|
||||
{
|
||||
TryReturn();
|
||||
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
|
||||
{
|
||||
TryReturn();
|
||||
}
|
||||
else
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public UniTaskStatus GetStatus(short token)
|
||||
{
|
||||
return core.GetStatus(token);
|
||||
}
|
||||
|
||||
public UniTaskStatus UnsafeGetStatus()
|
||||
{
|
||||
return core.UnsafeGetStatus();
|
||||
}
|
||||
|
||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||
{
|
||||
core.OnCompleted(continuation, state, token);
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
core.TrySetCanceled(cancellationToken);
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (predicate(argument))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
core.TrySetException(ex);
|
||||
return false;
|
||||
}
|
||||
|
||||
core.TrySetResult(null);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TryReturn()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.Reset();
|
||||
predicate = default;
|
||||
argument = default;
|
||||
cancellationToken = default;
|
||||
cancellationTokenRegistration.Dispose();
|
||||
cancelImmediately = default;
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
}
|
||||
|
||||
sealed class WaitUntilCanceledPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<WaitUntilCanceledPromise>
|
||||
{
|
||||
static TaskPool<WaitUntilCanceledPromise> pool;
|
||||
WaitUntilCanceledPromise nextNode;
|
||||
public ref WaitUntilCanceledPromise NextNode => ref nextNode;
|
||||
|
||||
static WaitUntilCanceledPromise()
|
||||
{
|
||||
TaskPool.RegisterSizeGetter(typeof(WaitUntilCanceledPromise), () => pool.Size);
|
||||
}
|
||||
|
||||
CancellationToken cancellationToken;
|
||||
CancellationTokenRegistration cancellationTokenRegistration;
|
||||
bool cancelImmediately;
|
||||
|
||||
UniTaskCompletionSourceCore<object> core;
|
||||
|
||||
WaitUntilCanceledPromise()
|
||||
{
|
||||
}
|
||||
|
||||
public static IUniTaskSource Create(CancellationToken cancellationToken, PlayerLoopTiming timing, bool cancelImmediately, out short token)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
|
||||
}
|
||||
|
||||
if (!pool.TryPop(out var result))
|
||||
{
|
||||
result = new WaitUntilCanceledPromise();
|
||||
}
|
||||
|
||||
result.cancellationToken = cancellationToken;
|
||||
result.cancelImmediately = cancelImmediately;
|
||||
|
||||
if (cancelImmediately && cancellationToken.CanBeCanceled)
|
||||
{
|
||||
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
|
||||
{
|
||||
var promise = (WaitUntilCanceledPromise)state;
|
||||
promise.core.TrySetResult(null);
|
||||
}, result);
|
||||
}
|
||||
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
PlayerLoopHelper.AddAction(timing, result);
|
||||
|
||||
token = result.core.Version;
|
||||
return result;
|
||||
}
|
||||
|
||||
public void GetResult(short token)
|
||||
{
|
||||
try
|
||||
{
|
||||
core.GetResult(token);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
|
||||
{
|
||||
TryReturn();
|
||||
}
|
||||
else
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -326,6 +659,8 @@ namespace Cysharp.Threading.Tasks
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.Reset();
|
||||
cancellationToken = default;
|
||||
cancellationTokenRegistration.Dispose();
|
||||
cancelImmediately = default;
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
}
|
||||
@@ -334,7 +669,8 @@ namespace Cysharp.Threading.Tasks
|
||||
sealed class WaitUntilValueChangedUnityObjectPromise<T, U> : IUniTaskSource<U>, IPlayerLoopItem, ITaskPoolNode<WaitUntilValueChangedUnityObjectPromise<T, U>>
|
||||
{
|
||||
static TaskPool<WaitUntilValueChangedUnityObjectPromise<T, U>> pool;
|
||||
public WaitUntilValueChangedUnityObjectPromise<T, U> NextNode { get; set; }
|
||||
WaitUntilValueChangedUnityObjectPromise<T, U> nextNode;
|
||||
public ref WaitUntilValueChangedUnityObjectPromise<T, U> NextNode => ref nextNode;
|
||||
|
||||
static WaitUntilValueChangedUnityObjectPromise()
|
||||
{
|
||||
@@ -347,6 +683,8 @@ namespace Cysharp.Threading.Tasks
|
||||
Func<T, U> monitorFunction;
|
||||
IEqualityComparer<U> equalityComparer;
|
||||
CancellationToken cancellationToken;
|
||||
CancellationTokenRegistration cancellationTokenRegistration;
|
||||
bool cancelImmediately;
|
||||
|
||||
UniTaskCompletionSourceCore<U> core;
|
||||
|
||||
@@ -354,7 +692,7 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
}
|
||||
|
||||
public static IUniTaskSource<U> Create(T target, Func<T, U> monitorFunction, IEqualityComparer<U> equalityComparer, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token)
|
||||
public static IUniTaskSource<U> Create(T target, Func<T, U> monitorFunction, IEqualityComparer<U> equalityComparer, PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
@@ -372,6 +710,16 @@ namespace Cysharp.Threading.Tasks
|
||||
result.currentValue = monitorFunction(target);
|
||||
result.equalityComparer = equalityComparer ?? UnityEqualityComparer.GetDefault<U>();
|
||||
result.cancellationToken = cancellationToken;
|
||||
result.cancelImmediately = cancelImmediately;
|
||||
|
||||
if (cancelImmediately && cancellationToken.CanBeCanceled)
|
||||
{
|
||||
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
|
||||
{
|
||||
var promise = (WaitUntilValueChangedUnityObjectPromise<T, U>)state;
|
||||
promise.core.TrySetCanceled(promise.cancellationToken);
|
||||
}, result);
|
||||
}
|
||||
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
@@ -389,7 +737,14 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
finally
|
||||
{
|
||||
TryReturn();
|
||||
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
|
||||
{
|
||||
TryReturn();
|
||||
}
|
||||
else
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -449,6 +804,8 @@ namespace Cysharp.Threading.Tasks
|
||||
monitorFunction = default;
|
||||
equalityComparer = default;
|
||||
cancellationToken = default;
|
||||
cancellationTokenRegistration.Dispose();
|
||||
cancelImmediately = default;
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
}
|
||||
@@ -457,7 +814,8 @@ namespace Cysharp.Threading.Tasks
|
||||
where T : class
|
||||
{
|
||||
static TaskPool<WaitUntilValueChangedStandardObjectPromise<T, U>> pool;
|
||||
public WaitUntilValueChangedStandardObjectPromise<T, U> NextNode { get; set; }
|
||||
WaitUntilValueChangedStandardObjectPromise<T, U> nextNode;
|
||||
public ref WaitUntilValueChangedStandardObjectPromise<T, U> NextNode => ref nextNode;
|
||||
|
||||
static WaitUntilValueChangedStandardObjectPromise()
|
||||
{
|
||||
@@ -469,6 +827,8 @@ namespace Cysharp.Threading.Tasks
|
||||
Func<T, U> monitorFunction;
|
||||
IEqualityComparer<U> equalityComparer;
|
||||
CancellationToken cancellationToken;
|
||||
CancellationTokenRegistration cancellationTokenRegistration;
|
||||
bool cancelImmediately;
|
||||
|
||||
UniTaskCompletionSourceCore<U> core;
|
||||
|
||||
@@ -476,7 +836,7 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
}
|
||||
|
||||
public static IUniTaskSource<U> Create(T target, Func<T, U> monitorFunction, IEqualityComparer<U> equalityComparer, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token)
|
||||
public static IUniTaskSource<U> Create(T target, Func<T, U> monitorFunction, IEqualityComparer<U> equalityComparer, PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
@@ -493,6 +853,16 @@ namespace Cysharp.Threading.Tasks
|
||||
result.currentValue = monitorFunction(target);
|
||||
result.equalityComparer = equalityComparer ?? UnityEqualityComparer.GetDefault<U>();
|
||||
result.cancellationToken = cancellationToken;
|
||||
result.cancelImmediately = cancelImmediately;
|
||||
|
||||
if (cancelImmediately && cancellationToken.CanBeCanceled)
|
||||
{
|
||||
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
|
||||
{
|
||||
var promise = (WaitUntilValueChangedStandardObjectPromise<T, U>)state;
|
||||
promise.core.TrySetCanceled(promise.cancellationToken);
|
||||
}, result);
|
||||
}
|
||||
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
@@ -510,7 +880,14 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
finally
|
||||
{
|
||||
TryReturn();
|
||||
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
|
||||
{
|
||||
TryReturn();
|
||||
}
|
||||
else
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -570,6 +947,8 @@ namespace Cysharp.Threading.Tasks
|
||||
monitorFunction = default;
|
||||
equalityComparer = default;
|
||||
cancellationToken = default;
|
||||
cancellationTokenRegistration.Dispose();
|
||||
cancelImmediately = default;
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
}
|
||||
|
||||
183
src/UniTask/Assets/Plugins/UniTask/Runtime/UniTask.WhenEach.cs
Normal file
183
src/UniTask/Assets/Plugins/UniTask/Runtime/UniTask.WhenEach.cs
Normal file
@@ -0,0 +1,183 @@
|
||||
using Cysharp.Threading.Tasks.Internal;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
public partial struct UniTask
|
||||
{
|
||||
public static IUniTaskAsyncEnumerable<WhenEachResult<T>> WhenEach<T>(IEnumerable<UniTask<T>> tasks)
|
||||
{
|
||||
return new WhenEachEnumerable<T>(tasks);
|
||||
}
|
||||
|
||||
public static IUniTaskAsyncEnumerable<WhenEachResult<T>> WhenEach<T>(params UniTask<T>[] tasks)
|
||||
{
|
||||
return new WhenEachEnumerable<T>(tasks);
|
||||
}
|
||||
}
|
||||
|
||||
public readonly struct WhenEachResult<T>
|
||||
{
|
||||
public T Result { get; }
|
||||
public Exception Exception { get; }
|
||||
|
||||
//[MemberNotNullWhen(false, nameof(Exception))]
|
||||
public bool IsCompletedSuccessfully => Exception == null;
|
||||
|
||||
//[MemberNotNullWhen(true, nameof(Exception))]
|
||||
public bool IsFaulted => Exception != null;
|
||||
|
||||
public WhenEachResult(T result)
|
||||
{
|
||||
this.Result = result;
|
||||
this.Exception = null;
|
||||
}
|
||||
|
||||
public WhenEachResult(Exception exception)
|
||||
{
|
||||
if (exception == null) throw new ArgumentNullException(nameof(exception));
|
||||
this.Result = default!;
|
||||
this.Exception = exception;
|
||||
}
|
||||
|
||||
public void TryThrow()
|
||||
{
|
||||
if (IsFaulted)
|
||||
{
|
||||
ExceptionDispatchInfo.Capture(Exception).Throw();
|
||||
}
|
||||
}
|
||||
|
||||
public T GetResult()
|
||||
{
|
||||
if (IsFaulted)
|
||||
{
|
||||
ExceptionDispatchInfo.Capture(Exception).Throw();
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (IsCompletedSuccessfully)
|
||||
{
|
||||
return Result?.ToString() ?? "";
|
||||
}
|
||||
else
|
||||
{
|
||||
return $"Exception{{{Exception.Message}}}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal enum WhenEachState : byte
|
||||
{
|
||||
NotRunning,
|
||||
Running,
|
||||
Completed
|
||||
}
|
||||
|
||||
internal sealed class WhenEachEnumerable<T> : IUniTaskAsyncEnumerable<WhenEachResult<T>>
|
||||
{
|
||||
IEnumerable<UniTask<T>> source;
|
||||
|
||||
public WhenEachEnumerable(IEnumerable<UniTask<T>> source)
|
||||
{
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public IUniTaskAsyncEnumerator<WhenEachResult<T>> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return new Enumerator(source, cancellationToken);
|
||||
}
|
||||
|
||||
sealed class Enumerator : IUniTaskAsyncEnumerator<WhenEachResult<T>>
|
||||
{
|
||||
readonly IEnumerable<UniTask<T>> source;
|
||||
CancellationToken cancellationToken;
|
||||
|
||||
Channel<WhenEachResult<T>> channel;
|
||||
IUniTaskAsyncEnumerator<WhenEachResult<T>> channelEnumerator;
|
||||
int completeCount;
|
||||
WhenEachState state;
|
||||
|
||||
public Enumerator(IEnumerable<UniTask<T>> source, CancellationToken cancellationToken)
|
||||
{
|
||||
this.source = source;
|
||||
this.cancellationToken = cancellationToken;
|
||||
}
|
||||
|
||||
public WhenEachResult<T> Current => channelEnumerator.Current;
|
||||
|
||||
public UniTask<bool> MoveNextAsync()
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
if (state == WhenEachState.NotRunning)
|
||||
{
|
||||
state = WhenEachState.Running;
|
||||
channel = Channel.CreateSingleConsumerUnbounded<WhenEachResult<T>>();
|
||||
channelEnumerator = channel.Reader.ReadAllAsync().GetAsyncEnumerator(cancellationToken);
|
||||
|
||||
if (source is UniTask<T>[] array)
|
||||
{
|
||||
ConsumeAll(this, array, array.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
using (var rentArray = ArrayPoolUtil.Materialize(source))
|
||||
{
|
||||
ConsumeAll(this, rentArray.Array, rentArray.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return channelEnumerator.MoveNextAsync();
|
||||
}
|
||||
|
||||
static void ConsumeAll(Enumerator self, UniTask<T>[] array, int length)
|
||||
{
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
RunWhenEachTask(self, array[i], length).Forget();
|
||||
}
|
||||
|
||||
static async UniTaskVoid RunWhenEachTask(Enumerator self, UniTask<T> task, int length)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await task;
|
||||
self.channel.Writer.TryWrite(new WhenEachResult<T>(result));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
self.channel.Writer.TryWrite(new WhenEachResult<T>(ex));
|
||||
}
|
||||
|
||||
if (Interlocked.Increment(ref self.completeCount) == length)
|
||||
{
|
||||
self.state = WhenEachState.Completed;
|
||||
self.channel.Writer.TryComplete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async UniTask DisposeAsync()
|
||||
{
|
||||
if (channelEnumerator != null)
|
||||
{
|
||||
await channelEnumerator.DisposeAsync();
|
||||
}
|
||||
|
||||
if (state != WhenEachState.Completed)
|
||||
{
|
||||
state = WhenEachState.Completed;
|
||||
channel.Writer.TryComplete(new OperationCanceledException());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7cac24fdda5112047a1cd3dd66b542c4
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "UniTask",
|
||||
"references": [
|
||||
],
|
||||
"rootNamespace": "",
|
||||
"references": [],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
@@ -14,6 +14,31 @@
|
||||
"name": "com.unity.modules.assetbundle",
|
||||
"expression": "",
|
||||
"define": "UNITASK_ASSETBUNDLE_SUPPORT"
|
||||
},
|
||||
{
|
||||
"name": "com.unity.modules.physics",
|
||||
"expression": "",
|
||||
"define": "UNITASK_PHYSICS_SUPPORT"
|
||||
},
|
||||
{
|
||||
"name": "com.unity.modules.physics2d",
|
||||
"expression": "",
|
||||
"define": "UNITASK_PHYSICS2D_SUPPORT"
|
||||
},
|
||||
{
|
||||
"name": "com.unity.modules.particlesystem",
|
||||
"expression": "",
|
||||
"define": "UNITASK_PARTICLESYSTEM_SUPPORT"
|
||||
},
|
||||
{
|
||||
"name": "com.unity.ugui",
|
||||
"expression": "",
|
||||
"define": "UNITASK_UGUI_SUPPORT"
|
||||
},
|
||||
{
|
||||
"name": "com.unity.modules.unitywebrequest",
|
||||
"expression": "",
|
||||
"define": "UNITASK_WEBREQUEST_SUPPORT"
|
||||
}
|
||||
],
|
||||
"noEngineReferences": false
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
#pragma warning disable CS0436
|
||||
|
||||
#if UNITASK_NETCORE || UNITY_2022_3_OR_NEWER
|
||||
#define SUPPORT_VALUETASK
|
||||
#endif
|
||||
|
||||
using Cysharp.Threading.Tasks.CompilerServices;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
@@ -69,7 +73,7 @@ namespace Cysharp.Threading.Tasks
|
||||
return new UniTask<bool>(new IsCanceledSource(source), token);
|
||||
}
|
||||
|
||||
#if !UNITY_2018_3_OR_NEWER
|
||||
#if SUPPORT_VALUETASK
|
||||
|
||||
public static implicit operator System.Threading.Tasks.ValueTask(in UniTask self)
|
||||
{
|
||||
@@ -78,7 +82,7 @@ namespace Cysharp.Threading.Tasks
|
||||
return default;
|
||||
}
|
||||
|
||||
#if NETSTANDARD2_0
|
||||
#if (UNITASK_NETCORE && NETSTANDARD2_0)
|
||||
return self.AsValueTask();
|
||||
#else
|
||||
return new System.Threading.Tasks.ValueTask(self.source, self.token);
|
||||
@@ -115,8 +119,13 @@ namespace Cysharp.Threading.Tasks
|
||||
var status = this.source.GetStatus(this.token);
|
||||
if (status.IsCompletedSuccessfully())
|
||||
{
|
||||
this.source.GetResult(this.token);
|
||||
return CompletedTasks.AsyncUnit;
|
||||
}
|
||||
else if (this.source is IUniTaskSource<AsyncUnit> asyncUnitSource)
|
||||
{
|
||||
return new UniTask<AsyncUnit>(asyncUnitSource, this.token);
|
||||
}
|
||||
|
||||
return new UniTask<AsyncUnit>(new AsyncUnitSource(this.source), this.token);
|
||||
}
|
||||
@@ -422,6 +431,7 @@ namespace Cysharp.Threading.Tasks
|
||||
var status = this.source.GetStatus(this.token);
|
||||
if (status.IsCompletedSuccessfully())
|
||||
{
|
||||
this.source.GetResult(this.token);
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
|
||||
@@ -434,7 +444,7 @@ namespace Cysharp.Threading.Tasks
|
||||
return self.AsUniTask();
|
||||
}
|
||||
|
||||
#if !UNITY_2018_3_OR_NEWER
|
||||
#if SUPPORT_VALUETASK
|
||||
|
||||
public static implicit operator System.Threading.Tasks.ValueTask<T>(in UniTask<T> self)
|
||||
{
|
||||
@@ -443,7 +453,7 @@ namespace Cysharp.Threading.Tasks
|
||||
return new System.Threading.Tasks.ValueTask<T>(self.result);
|
||||
}
|
||||
|
||||
#if NETSTANDARD2_0
|
||||
#if (UNITASK_NETCORE && NETSTANDARD2_0)
|
||||
return self.AsValueTask();
|
||||
#else
|
||||
return new System.Threading.Tasks.ValueTask<T>(self.source, self.token);
|
||||
|
||||
@@ -137,8 +137,8 @@ namespace Cysharp.Threading.Tasks
|
||||
if (continuation != null || Interlocked.CompareExchange(ref this.continuation, UniTaskCompletionSourceCoreShared.s_sentinel, null) != null)
|
||||
{
|
||||
continuation(continuationState);
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -165,8 +165,8 @@ namespace Cysharp.Threading.Tasks
|
||||
if (continuation != null || Interlocked.CompareExchange(ref this.continuation, UniTaskCompletionSourceCoreShared.s_sentinel, null) != null)
|
||||
{
|
||||
continuation(continuationState);
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -184,8 +184,8 @@ namespace Cysharp.Threading.Tasks
|
||||
if (continuation != null || Interlocked.CompareExchange(ref this.continuation, UniTaskCompletionSourceCoreShared.s_sentinel, null) != null)
|
||||
{
|
||||
continuation(continuationState);
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -319,7 +319,8 @@ namespace Cysharp.Threading.Tasks
|
||||
public class AutoResetUniTaskCompletionSource : IUniTaskSource, ITaskPoolNode<AutoResetUniTaskCompletionSource>, IPromise
|
||||
{
|
||||
static TaskPool<AutoResetUniTaskCompletionSource> pool;
|
||||
public AutoResetUniTaskCompletionSource NextNode { get; set; }
|
||||
AutoResetUniTaskCompletionSource nextNode;
|
||||
public ref AutoResetUniTaskCompletionSource NextNode => ref nextNode;
|
||||
|
||||
static AutoResetUniTaskCompletionSource()
|
||||
{
|
||||
@@ -327,6 +328,7 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
|
||||
UniTaskCompletionSourceCore<AsyncUnit> core;
|
||||
short version;
|
||||
|
||||
AutoResetUniTaskCompletionSource()
|
||||
{
|
||||
@@ -339,6 +341,7 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
result = new AutoResetUniTaskCompletionSource();
|
||||
}
|
||||
result.version = result.core.Version;
|
||||
TaskTracker.TrackActiveTask(result, 2);
|
||||
return result;
|
||||
}
|
||||
@@ -382,19 +385,19 @@ namespace Cysharp.Threading.Tasks
|
||||
[DebuggerHidden]
|
||||
public bool TrySetResult()
|
||||
{
|
||||
return core.TrySetResult(AsyncUnit.Default);
|
||||
return version == core.Version && core.TrySetResult(AsyncUnit.Default);
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
public bool TrySetCanceled(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return core.TrySetCanceled(cancellationToken);
|
||||
return version == core.Version && core.TrySetCanceled(cancellationToken);
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
public bool TrySetException(Exception exception)
|
||||
{
|
||||
return core.TrySetException(exception);
|
||||
return version == core.Version && core.TrySetException(exception);
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
@@ -408,7 +411,6 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
TryReturn();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
@@ -441,7 +443,8 @@ namespace Cysharp.Threading.Tasks
|
||||
public class AutoResetUniTaskCompletionSource<T> : IUniTaskSource<T>, ITaskPoolNode<AutoResetUniTaskCompletionSource<T>>, IPromise<T>
|
||||
{
|
||||
static TaskPool<AutoResetUniTaskCompletionSource<T>> pool;
|
||||
public AutoResetUniTaskCompletionSource<T> NextNode { get; set; }
|
||||
AutoResetUniTaskCompletionSource<T> nextNode;
|
||||
public ref AutoResetUniTaskCompletionSource<T> NextNode => ref nextNode;
|
||||
|
||||
static AutoResetUniTaskCompletionSource()
|
||||
{
|
||||
@@ -449,6 +452,7 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
|
||||
UniTaskCompletionSourceCore<T> core;
|
||||
short version;
|
||||
|
||||
AutoResetUniTaskCompletionSource()
|
||||
{
|
||||
@@ -461,6 +465,7 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
result = new AutoResetUniTaskCompletionSource<T>();
|
||||
}
|
||||
result.version = result.core.Version;
|
||||
TaskTracker.TrackActiveTask(result, 2);
|
||||
return result;
|
||||
}
|
||||
@@ -504,19 +509,19 @@ namespace Cysharp.Threading.Tasks
|
||||
[DebuggerHidden]
|
||||
public bool TrySetResult(T result)
|
||||
{
|
||||
return core.TrySetResult(result);
|
||||
return version == core.Version && core.TrySetResult(result);
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
public bool TrySetCanceled(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return core.TrySetCanceled(cancellationToken);
|
||||
return version == core.Version && core.TrySetCanceled(cancellationToken);
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
public bool TrySetException(Exception exception)
|
||||
{
|
||||
return core.TrySetException(exception);
|
||||
return version == core.Version && core.TrySetException(exception);
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
@@ -935,5 +940,5 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ namespace Cysharp.Threading.Tasks
|
||||
p.TrySetCanceled();
|
||||
break;
|
||||
case TaskStatus.Faulted:
|
||||
p.TrySetException(x.Exception);
|
||||
p.TrySetException(x.Exception.InnerException ?? x.Exception);
|
||||
break;
|
||||
case TaskStatus.RanToCompletion:
|
||||
p.TrySetResult(x.Result);
|
||||
@@ -58,7 +58,7 @@ namespace Cysharp.Threading.Tasks
|
||||
p.TrySetCanceled();
|
||||
break;
|
||||
case TaskStatus.Faulted:
|
||||
p.TrySetException(x.Exception);
|
||||
p.TrySetException(x.Exception.InnerException ?? x.Exception);
|
||||
break;
|
||||
case TaskStatus.RanToCompletion:
|
||||
p.TrySetResult();
|
||||
@@ -192,7 +192,7 @@ namespace Cysharp.Threading.Tasks
|
||||
/// <summary>
|
||||
/// Ignore task result when cancel raised first.
|
||||
/// </summary>
|
||||
public static UniTask WithCancellation(this UniTask task, CancellationToken cancellationToken)
|
||||
public static UniTask AttachExternalCancellation(this UniTask task, CancellationToken cancellationToken)
|
||||
{
|
||||
if (!cancellationToken.CanBeCanceled)
|
||||
{
|
||||
@@ -201,6 +201,7 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
task.Forget();
|
||||
return UniTask.FromCanceled(cancellationToken);
|
||||
}
|
||||
|
||||
@@ -209,13 +210,13 @@ namespace Cysharp.Threading.Tasks
|
||||
return task;
|
||||
}
|
||||
|
||||
return new UniTask(new WithCancellationSource(task, cancellationToken), 0);
|
||||
return new UniTask(new AttachExternalCancellationSource(task, cancellationToken), 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ignore task result when cancel raised first.
|
||||
/// </summary>
|
||||
public static UniTask<T> WithCancellation<T>(this UniTask<T> task, CancellationToken cancellationToken)
|
||||
public static UniTask<T> AttachExternalCancellation<T>(this UniTask<T> task, CancellationToken cancellationToken)
|
||||
{
|
||||
if (!cancellationToken.CanBeCanceled)
|
||||
{
|
||||
@@ -224,6 +225,7 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
task.Forget();
|
||||
return UniTask.FromCanceled<T>(cancellationToken);
|
||||
}
|
||||
|
||||
@@ -232,10 +234,10 @@ namespace Cysharp.Threading.Tasks
|
||||
return task;
|
||||
}
|
||||
|
||||
return new UniTask<T>(new WithCancellationSource<T>(task, cancellationToken), 0);
|
||||
return new UniTask<T>(new AttachExternalCancellationSource<T>(task, cancellationToken), 0);
|
||||
}
|
||||
|
||||
sealed class WithCancellationSource : IUniTaskSource
|
||||
sealed class AttachExternalCancellationSource : IUniTaskSource
|
||||
{
|
||||
static readonly Action<object> cancellationCallbackDelegate = CancellationCallback;
|
||||
|
||||
@@ -243,7 +245,7 @@ namespace Cysharp.Threading.Tasks
|
||||
CancellationTokenRegistration tokenRegistration;
|
||||
UniTaskCompletionSourceCore<AsyncUnit> core;
|
||||
|
||||
public WithCancellationSource(UniTask task, CancellationToken cancellationToken)
|
||||
public AttachExternalCancellationSource(UniTask task, CancellationToken cancellationToken)
|
||||
{
|
||||
this.cancellationToken = cancellationToken;
|
||||
this.tokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallbackDelegate, this);
|
||||
@@ -269,7 +271,7 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
static void CancellationCallback(object state)
|
||||
{
|
||||
var self = (WithCancellationSource)state;
|
||||
var self = (AttachExternalCancellationSource)state;
|
||||
self.core.TrySetCanceled(self.cancellationToken);
|
||||
}
|
||||
|
||||
@@ -294,7 +296,7 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
}
|
||||
|
||||
sealed class WithCancellationSource<T> : IUniTaskSource<T>
|
||||
sealed class AttachExternalCancellationSource<T> : IUniTaskSource<T>
|
||||
{
|
||||
static readonly Action<object> cancellationCallbackDelegate = CancellationCallback;
|
||||
|
||||
@@ -302,7 +304,7 @@ namespace Cysharp.Threading.Tasks
|
||||
CancellationTokenRegistration tokenRegistration;
|
||||
UniTaskCompletionSourceCore<T> core;
|
||||
|
||||
public WithCancellationSource(UniTask<T> task, CancellationToken cancellationToken)
|
||||
public AttachExternalCancellationSource(UniTask<T> task, CancellationToken cancellationToken)
|
||||
{
|
||||
this.cancellationToken = cancellationToken;
|
||||
this.tokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallbackDelegate, this);
|
||||
@@ -327,7 +329,7 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
static void CancellationCallback(object state)
|
||||
{
|
||||
var self = (WithCancellationSource<T>)state;
|
||||
var self = (AttachExternalCancellationSource<T>)state;
|
||||
self.core.TrySetCanceled(self.cancellationToken);
|
||||
}
|
||||
|
||||
@@ -454,7 +456,7 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Timeout with suppress OperationCanceledException. Returns (bool, IsCacneled).
|
||||
/// Timeout with suppress OperationCanceledException. Returns (bool, IsCanceled).
|
||||
/// </summary>
|
||||
public static async UniTask<bool> TimeoutWithoutException(this UniTask task, TimeSpan timeout, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null)
|
||||
{
|
||||
@@ -561,21 +563,23 @@ namespace Cysharp.Threading.Tasks
|
||||
UniTaskScheduler.PublishUnobservedTaskException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
awaiter.SourceOnCompleted(state =>
|
||||
else
|
||||
{
|
||||
using (var t = (StateTuple<UniTask.Awaiter>)state)
|
||||
awaiter.SourceOnCompleted(state =>
|
||||
{
|
||||
try
|
||||
using (var t = (StateTuple<UniTask.Awaiter>)state)
|
||||
{
|
||||
t.Item1.GetResult();
|
||||
try
|
||||
{
|
||||
t.Item1.GetResult();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
UniTaskScheduler.PublishUnobservedTaskException(ex);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
UniTaskScheduler.PublishUnobservedTaskException(ex);
|
||||
}
|
||||
}
|
||||
}, StateTuple.Create(awaiter));
|
||||
}, StateTuple.Create(awaiter));
|
||||
}
|
||||
}
|
||||
|
||||
public static void Forget(this UniTask task, Action<Exception> exceptionHandler, bool handleExceptionOnMainThread = true)
|
||||
@@ -629,21 +633,23 @@ namespace Cysharp.Threading.Tasks
|
||||
UniTaskScheduler.PublishUnobservedTaskException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
awaiter.SourceOnCompleted(state =>
|
||||
else
|
||||
{
|
||||
using (var t = (StateTuple<UniTask<T>.Awaiter>)state)
|
||||
awaiter.SourceOnCompleted(state =>
|
||||
{
|
||||
try
|
||||
using (var t = (StateTuple<UniTask<T>.Awaiter>)state)
|
||||
{
|
||||
t.Item1.GetResult();
|
||||
try
|
||||
{
|
||||
t.Item1.GetResult();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
UniTaskScheduler.PublishUnobservedTaskException(ex);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
UniTaskScheduler.PublishUnobservedTaskException(ex);
|
||||
}
|
||||
}
|
||||
}, StateTuple.Create(awaiter));
|
||||
}, StateTuple.Create(awaiter));
|
||||
}
|
||||
}
|
||||
|
||||
public static void Forget<T>(this UniTask<T> task, Action<Exception> exceptionHandler, bool handleExceptionOnMainThread = true)
|
||||
@@ -732,11 +738,51 @@ namespace Cysharp.Threading.Tasks
|
||||
return await await task;
|
||||
}
|
||||
|
||||
public static async UniTask Unwrap<T>(this UniTask<UniTask> task)
|
||||
public static async UniTask Unwrap(this UniTask<UniTask> task)
|
||||
{
|
||||
await await task;
|
||||
}
|
||||
|
||||
public static async UniTask<T> Unwrap<T>(this Task<UniTask<T>> task)
|
||||
{
|
||||
return await await task;
|
||||
}
|
||||
|
||||
public static async UniTask<T> Unwrap<T>(this Task<UniTask<T>> task, bool continueOnCapturedContext)
|
||||
{
|
||||
return await await task.ConfigureAwait(continueOnCapturedContext);
|
||||
}
|
||||
|
||||
public static async UniTask Unwrap(this Task<UniTask> task)
|
||||
{
|
||||
await await task;
|
||||
}
|
||||
|
||||
public static async UniTask Unwrap(this Task<UniTask> task, bool continueOnCapturedContext)
|
||||
{
|
||||
await await task.ConfigureAwait(continueOnCapturedContext);
|
||||
}
|
||||
|
||||
public static async UniTask<T> Unwrap<T>(this UniTask<Task<T>> task)
|
||||
{
|
||||
return await await task;
|
||||
}
|
||||
|
||||
public static async UniTask<T> Unwrap<T>(this UniTask<Task<T>> task, bool continueOnCapturedContext)
|
||||
{
|
||||
return await (await task).ConfigureAwait(continueOnCapturedContext);
|
||||
}
|
||||
|
||||
public static async UniTask Unwrap(this UniTask<Task> task)
|
||||
{
|
||||
await await task;
|
||||
}
|
||||
|
||||
public static async UniTask Unwrap(this UniTask<Task> task, bool continueOnCapturedContext)
|
||||
{
|
||||
await (await task).ConfigureAwait(continueOnCapturedContext);
|
||||
}
|
||||
|
||||
#if UNITY_2018_3_OR_NEWER
|
||||
|
||||
sealed class ToCoroutineEnumerator : IEnumerator
|
||||
|
||||
@@ -58,6 +58,7 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
try
|
||||
{
|
||||
task.GetAwaiter().GetResult();
|
||||
return new ReturnObservable<AsyncUnit>(AsyncUnit.Default);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -73,16 +74,19 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
static async UniTaskVoid Fire<T>(AsyncSubject<T> subject, UniTask<T> task)
|
||||
{
|
||||
T value;
|
||||
try
|
||||
{
|
||||
var value = await task;
|
||||
subject.OnNext(value);
|
||||
subject.OnCompleted();
|
||||
value = await task;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
subject.OnError(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
subject.OnNext(value);
|
||||
subject.OnCompleted();
|
||||
}
|
||||
|
||||
static async UniTaskVoid Fire(AsyncSubject<AsyncUnit> subject, UniTask task)
|
||||
@@ -90,13 +94,15 @@ namespace Cysharp.Threading.Tasks
|
||||
try
|
||||
{
|
||||
await task;
|
||||
subject.OnNext(AsyncUnit.Default);
|
||||
subject.OnCompleted();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
subject.OnError(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
subject.OnNext(AsyncUnit.Default);
|
||||
subject.OnCompleted();
|
||||
}
|
||||
|
||||
class ToUniTaskObserver<T> : IObserver<T>
|
||||
@@ -127,7 +133,7 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
var self = (ToUniTaskObserver<T>)state;
|
||||
self.disposable.Dispose();
|
||||
self.promise.TrySetCanceled();
|
||||
self.promise.TrySetCanceled(self.cancellationToken);
|
||||
}
|
||||
|
||||
public void OnNext(T value)
|
||||
@@ -197,7 +203,7 @@ namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
var self = (FirstValueToUniTaskObserver<T>)state;
|
||||
self.disposable.Dispose();
|
||||
self.promise.TrySetCanceled();
|
||||
self.promise.TrySetCanceled(self.cancellationToken);
|
||||
}
|
||||
|
||||
public void OnNext(T value)
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Cysharp.Threading.Tasks
|
||||
const int MaxArrayLength = 0X7FEFFFFF;
|
||||
const int InitialSize = 16;
|
||||
|
||||
static SpinLock gate = new SpinLock();
|
||||
static SpinLock gate = new SpinLock(false);
|
||||
static bool dequing = false;
|
||||
|
||||
static int actionListCount = 0;
|
||||
|
||||
@@ -21,18 +21,20 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
public static UniTask<UnityEngine.Object[]> AwaitForAllAssets(this AssetBundleRequest asyncOperation, CancellationToken cancellationToken)
|
||||
{
|
||||
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
|
||||
if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled<UnityEngine.Object[]>(cancellationToken);
|
||||
if (asyncOperation.isDone) return UniTask.FromResult<UnityEngine.Object[]>(asyncOperation.allAssets);
|
||||
return new UniTask<UnityEngine.Object[]>(AssetBundleRequestAllAssetsWithCancellationSource.Create(asyncOperation, cancellationToken, out var token), token);
|
||||
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, CancellationToken cancellationToken, bool cancelImmediately)
|
||||
{
|
||||
return AwaitForAllAssets(asyncOperation, progress: null, cancellationToken: cancellationToken, cancelImmediately: cancelImmediately);
|
||||
}
|
||||
|
||||
public static UniTask<UnityEngine.Object[]> AwaitForAllAssets(this AssetBundleRequest asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
|
||||
{
|
||||
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
|
||||
if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled<UnityEngine.Object[]>(cancellationToken);
|
||||
if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.allAssets);
|
||||
return new UniTask<UnityEngine.Object[]>(AssetBundleRequestAllAssetsConfiguredSource.Create(asyncOperation, timing, progress, cancellationToken, out var token), token);
|
||||
return new UniTask<UnityEngine.Object[]>(AssetBundleRequestAllAssetsConfiguredSource.Create(asyncOperation, timing, progress, cancellationToken, cancelImmediately, out var token), token);
|
||||
}
|
||||
|
||||
public struct AssetBundleRequestAllAssetsAwaiter : ICriticalNotifyCompletion
|
||||
@@ -46,6 +48,11 @@ namespace Cysharp.Threading.Tasks
|
||||
this.continuationAction = null;
|
||||
}
|
||||
|
||||
public AssetBundleRequestAllAssetsAwaiter GetAwaiter()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
public bool IsCompleted => asyncOperation.isDone;
|
||||
|
||||
public UnityEngine.Object[] GetResult()
|
||||
@@ -79,132 +86,11 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
}
|
||||
|
||||
sealed class AssetBundleRequestAllAssetsWithCancellationSource : IUniTaskSource<UnityEngine.Object[]>, IPlayerLoopItem, ITaskPoolNode<AssetBundleRequestAllAssetsWithCancellationSource>
|
||||
{
|
||||
static TaskPool<AssetBundleRequestAllAssetsWithCancellationSource> pool;
|
||||
public AssetBundleRequestAllAssetsWithCancellationSource NextNode { get; set; }
|
||||
|
||||
static AssetBundleRequestAllAssetsWithCancellationSource()
|
||||
{
|
||||
TaskPool.RegisterSizeGetter(typeof(AssetBundleRequestAllAssetsWithCancellationSource), () => pool.Size);
|
||||
}
|
||||
|
||||
readonly Action<AsyncOperation> continuationAction;
|
||||
AssetBundleRequest asyncOperation;
|
||||
CancellationToken cancellationToken;
|
||||
bool completed;
|
||||
|
||||
UniTaskCompletionSourceCore<UnityEngine.Object[]> core;
|
||||
|
||||
AssetBundleRequestAllAssetsWithCancellationSource()
|
||||
{
|
||||
continuationAction = Continuation;
|
||||
}
|
||||
|
||||
public static IUniTaskSource<UnityEngine.Object[]> Create(AssetBundleRequest asyncOperation, CancellationToken cancellationToken, out short token)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return AutoResetUniTaskCompletionSource<UnityEngine.Object[]>.CreateFromCanceled(cancellationToken, out token);
|
||||
}
|
||||
|
||||
if (!pool.TryPop(out var result))
|
||||
{
|
||||
result = new AssetBundleRequestAllAssetsWithCancellationSource();
|
||||
}
|
||||
|
||||
result.asyncOperation = asyncOperation;
|
||||
result.cancellationToken = cancellationToken;
|
||||
result.completed = false;
|
||||
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, result);
|
||||
|
||||
asyncOperation.completed += result.continuationAction;
|
||||
|
||||
token = result.core.Version;
|
||||
return result;
|
||||
}
|
||||
|
||||
void Continuation(AsyncOperation _)
|
||||
{
|
||||
asyncOperation.completed -= continuationAction;
|
||||
|
||||
if (completed)
|
||||
{
|
||||
TryReturn();
|
||||
}
|
||||
else
|
||||
{
|
||||
completed = true;
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
core.TrySetCanceled(cancellationToken);
|
||||
return;
|
||||
}
|
||||
|
||||
core.TrySetResult(asyncOperation.allAssets);
|
||||
}
|
||||
}
|
||||
|
||||
public UnityEngine.Object[] GetResult(short token)
|
||||
{
|
||||
return core.GetResult(token);
|
||||
}
|
||||
|
||||
void IUniTaskSource.GetResult(short token)
|
||||
{
|
||||
GetResult(token);
|
||||
}
|
||||
|
||||
public UniTaskStatus GetStatus(short token)
|
||||
{
|
||||
return core.GetStatus(token);
|
||||
}
|
||||
|
||||
public UniTaskStatus UnsafeGetStatus()
|
||||
{
|
||||
return core.UnsafeGetStatus();
|
||||
}
|
||||
|
||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||
{
|
||||
core.OnCompleted(continuation, state, token);
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (completed)
|
||||
{
|
||||
TryReturn();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
completed = true;
|
||||
core.TrySetCanceled(cancellationToken);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TryReturn()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.Reset();
|
||||
asyncOperation = default;
|
||||
cancellationToken = default;
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
}
|
||||
|
||||
sealed class AssetBundleRequestAllAssetsConfiguredSource : IUniTaskSource<UnityEngine.Object[]>, IPlayerLoopItem, ITaskPoolNode<AssetBundleRequestAllAssetsConfiguredSource>
|
||||
{
|
||||
static TaskPool<AssetBundleRequestAllAssetsConfiguredSource> pool;
|
||||
public AssetBundleRequestAllAssetsConfiguredSource NextNode { get; set; }
|
||||
AssetBundleRequestAllAssetsConfiguredSource nextNode;
|
||||
public ref AssetBundleRequestAllAssetsConfiguredSource NextNode => ref nextNode;
|
||||
|
||||
static AssetBundleRequestAllAssetsConfiguredSource()
|
||||
{
|
||||
@@ -214,15 +100,20 @@ namespace Cysharp.Threading.Tasks
|
||||
AssetBundleRequest asyncOperation;
|
||||
IProgress<float> progress;
|
||||
CancellationToken cancellationToken;
|
||||
CancellationTokenRegistration cancellationTokenRegistration;
|
||||
bool cancelImmediately;
|
||||
bool completed;
|
||||
|
||||
UniTaskCompletionSourceCore<UnityEngine.Object[]> core;
|
||||
|
||||
Action<AsyncOperation> continuationAction;
|
||||
|
||||
AssetBundleRequestAllAssetsConfiguredSource()
|
||||
{
|
||||
|
||||
continuationAction = Continuation;
|
||||
}
|
||||
|
||||
public static IUniTaskSource<UnityEngine.Object[]> Create(AssetBundleRequest asyncOperation, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, out short token)
|
||||
public static IUniTaskSource<UnityEngine.Object[]> Create(AssetBundleRequest asyncOperation, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, bool cancelImmediately, out short token)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
@@ -237,6 +128,19 @@ namespace Cysharp.Threading.Tasks
|
||||
result.asyncOperation = asyncOperation;
|
||||
result.progress = progress;
|
||||
result.cancellationToken = cancellationToken;
|
||||
result.cancelImmediately = cancelImmediately;
|
||||
result.completed = false;
|
||||
|
||||
asyncOperation.completed += result.continuationAction;
|
||||
|
||||
if (cancelImmediately && cancellationToken.CanBeCanceled)
|
||||
{
|
||||
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
|
||||
{
|
||||
var source = (AssetBundleRequestAllAssetsConfiguredSource)state;
|
||||
source.core.TrySetCanceled(source.cancellationToken);
|
||||
}, result);
|
||||
}
|
||||
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
@@ -254,7 +158,14 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
finally
|
||||
{
|
||||
TryReturn();
|
||||
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
|
||||
{
|
||||
TryReturn();
|
||||
}
|
||||
else
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,6 +191,12 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
// Already completed
|
||||
if (completed || asyncOperation == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
core.TrySetCanceled(cancellationToken);
|
||||
@@ -307,8 +224,28 @@ namespace Cysharp.Threading.Tasks
|
||||
asyncOperation = default;
|
||||
progress = default;
|
||||
cancellationToken = default;
|
||||
cancellationTokenRegistration.Dispose();
|
||||
cancelImmediately = default;
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
|
||||
void Continuation(AsyncOperation _)
|
||||
{
|
||||
if (completed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
completed = true;
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
core.TrySetCanceled(cancellationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
core.TrySetResult(asyncOperation.allAssets);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
@@ -20,16 +20,22 @@ namespace Cysharp.Threading.Tasks
|
||||
return ToUniTask(asyncOperation, cancellationToken: cancellationToken);
|
||||
}
|
||||
|
||||
public static UniTask<AsyncGPUReadbackRequest> ToUniTask(this AsyncGPUReadbackRequest asyncOperation, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
|
||||
public static UniTask<AsyncGPUReadbackRequest> WithCancellation(this AsyncGPUReadbackRequest asyncOperation, CancellationToken cancellationToken, bool cancelImmediately)
|
||||
{
|
||||
if (asyncOperation.done) return UniTask.FromResult(asyncOperation);
|
||||
return new UniTask<AsyncGPUReadbackRequest>(AsyncGPUReadbackRequestAwaiterConfiguredSource.Create(asyncOperation, timing, cancellationToken, out var token), token);
|
||||
return ToUniTask(asyncOperation, cancellationToken: cancellationToken, cancelImmediately: cancelImmediately);
|
||||
}
|
||||
|
||||
public static UniTask<AsyncGPUReadbackRequest> ToUniTask(this AsyncGPUReadbackRequest asyncOperation, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
|
||||
{
|
||||
if (asyncOperation.done) return UniTask.FromResult(asyncOperation);
|
||||
return new UniTask<AsyncGPUReadbackRequest>(AsyncGPUReadbackRequestAwaiterConfiguredSource.Create(asyncOperation, timing, cancellationToken, cancelImmediately, out var token), token);
|
||||
}
|
||||
|
||||
sealed class AsyncGPUReadbackRequestAwaiterConfiguredSource : IUniTaskSource<AsyncGPUReadbackRequest>, IPlayerLoopItem, ITaskPoolNode<AsyncGPUReadbackRequestAwaiterConfiguredSource>
|
||||
{
|
||||
static TaskPool<AsyncGPUReadbackRequestAwaiterConfiguredSource> pool;
|
||||
public AsyncGPUReadbackRequestAwaiterConfiguredSource NextNode { get; set; }
|
||||
AsyncGPUReadbackRequestAwaiterConfiguredSource nextNode;
|
||||
public ref AsyncGPUReadbackRequestAwaiterConfiguredSource NextNode => ref nextNode;
|
||||
|
||||
static AsyncGPUReadbackRequestAwaiterConfiguredSource()
|
||||
{
|
||||
@@ -38,15 +44,15 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
AsyncGPUReadbackRequest asyncOperation;
|
||||
CancellationToken cancellationToken;
|
||||
|
||||
CancellationTokenRegistration cancellationTokenRegistration;
|
||||
bool cancelImmediately;
|
||||
UniTaskCompletionSourceCore<AsyncGPUReadbackRequest> core;
|
||||
|
||||
AsyncGPUReadbackRequestAwaiterConfiguredSource()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public static IUniTaskSource<AsyncGPUReadbackRequest> Create(AsyncGPUReadbackRequest asyncOperation, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token)
|
||||
public static IUniTaskSource<AsyncGPUReadbackRequest> Create(AsyncGPUReadbackRequest asyncOperation, PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
@@ -60,6 +66,16 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
result.asyncOperation = asyncOperation;
|
||||
result.cancellationToken = cancellationToken;
|
||||
result.cancelImmediately = cancelImmediately;
|
||||
|
||||
if (cancelImmediately && cancellationToken.CanBeCanceled)
|
||||
{
|
||||
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
|
||||
{
|
||||
var promise = (AsyncGPUReadbackRequestAwaiterConfiguredSource)state;
|
||||
promise.core.TrySetCanceled(promise.cancellationToken);
|
||||
}, result);
|
||||
}
|
||||
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
@@ -77,7 +93,14 @@ namespace Cysharp.Threading.Tasks
|
||||
}
|
||||
finally
|
||||
{
|
||||
TryReturn();
|
||||
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
|
||||
{
|
||||
TryReturn();
|
||||
}
|
||||
else
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,6 +153,8 @@ namespace Cysharp.Threading.Tasks
|
||||
core.Reset();
|
||||
asyncOperation = default;
|
||||
cancellationToken = default;
|
||||
cancellationTokenRegistration.Dispose();
|
||||
cancelImmediately = default;
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Unity.Jobs;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cysharp.Threading.Tasks
|
||||
{
|
||||
@@ -85,7 +86,7 @@ namespace Cysharp.Threading.Tasks
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (jobHandle.IsCompleted)
|
||||
if (jobHandle.IsCompleted | PlayerLoopHelper.IsEditorApplicationQuitting)
|
||||
{
|
||||
jobHandle.Complete();
|
||||
core.TrySetResult(AsyncUnit.Default);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user