mirror of https://github.com/Cysharp/UniTask
Compare commits
No commits in common. "master" and "2.0.15" have entirely different histories.
|
@ -1 +0,0 @@
|
||||||
github: [neuecc]
|
|
|
@ -1,12 +0,0 @@
|
||||||
# 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
|
|
||||||
ignore:
|
|
||||||
# I just want update action when major/minor version is updated. patch updates are too noisy.
|
|
||||||
- dependency-name: '*'
|
|
||||||
update-types:
|
|
||||||
- version-update:semver-patch
|
|
|
@ -3,86 +3,71 @@ name: Build-Debug
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- "master"
|
- "**"
|
||||||
|
tags:
|
||||||
|
- "!*" # not a tag push
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
types:
|
||||||
- "master"
|
- opened
|
||||||
|
- synchronize
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-dotnet:
|
build-dotnet:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 10
|
env:
|
||||||
|
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||||
|
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
|
||||||
|
NUGET_XMLDOC_MODE: skip
|
||||||
steps:
|
steps:
|
||||||
- uses: Cysharp/Actions/.github/actions/checkout@main
|
- uses: actions/checkout@v2
|
||||||
- uses: Cysharp/Actions/.github/actions/setup-dotnet@main
|
- uses: actions/setup-dotnet@v1
|
||||||
- run: dotnet build -c Debug
|
with:
|
||||||
- run: dotnet test -c Debug
|
dotnet-version: 3.1.101
|
||||||
|
- run: dotnet test -c Debug ./src/UniTask.NetCoreTests/UniTask.NetCoreTests.csproj
|
||||||
|
|
||||||
build-unity:
|
build-unity:
|
||||||
if: ${{ ((github.event_name == 'push' && github.repository_owner == 'Cysharp') || startsWith(github.event.pull_request.head.label, 'Cysharp:')) && github.triggering_actor != 'dependabot[bot]' }}
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
|
||||||
max-parallel: 2
|
|
||||||
matrix:
|
matrix:
|
||||||
unity: ["2022.3.39f1", "6000.0.12f1"] # Test with LTS
|
unity: ['2019.3.9f1', '2020.1.0b5']
|
||||||
|
include:
|
||||||
|
- unity: 2019.3.9f1
|
||||||
|
license: UNITY_2019_3
|
||||||
|
- unity: 2020.1.0b5
|
||||||
|
license: UNITY_2020_1
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 30 # Unity build takes more than 20min.
|
container:
|
||||||
|
# with linux-il2cpp. image from https://hub.docker.com/r/gableroux/unity3d/tags
|
||||||
|
image: gableroux/unity3d:${{ matrix.unity }}-linux-il2cpp
|
||||||
steps:
|
steps:
|
||||||
- name: Load secrets
|
- run: apt update && apt install git -y
|
||||||
id: op-load-secret
|
- uses: actions/checkout@v2
|
||||||
uses: 1password/load-secrets-action@581a835fb51b8e7ec56b71cf2ffddd7e68bb25e0 # v2.0.0
|
# create unity activation file and store to artifacts.
|
||||||
|
- run: /opt/Unity/Editor/Unity -quit -batchmode -nographics -logFile -createManualActivationFile || exit 0
|
||||||
|
- uses: actions/upload-artifact@v1
|
||||||
with:
|
with:
|
||||||
export-env: false
|
name: Unity_v${{ matrix.unity }}.alf
|
||||||
|
path: ./Unity_v${{ matrix.unity }}.alf
|
||||||
|
# activate Unity from manual license file(ulf)
|
||||||
|
- run: echo -n "$UNITY_LICENSE" >> .Unity.ulf
|
||||||
env:
|
env:
|
||||||
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN_PUBLIC }}
|
UNITY_LICENSE: ${{ secrets[matrix.license] }}
|
||||||
UNITY_EMAIL: "op://${{ vars.OP_VAULT_ACTIONS_PUBLIC }}/UNITY_LICENSE/username"
|
- 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.
|
||||||
UNITY_PASSWORD: "op://${{ vars.OP_VAULT_ACTIONS_PUBLIC }}/UNITY_LICENSE/credential"
|
run: /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -manualLicenseFile .Unity.ulf || exit 0
|
||||||
UNITY_SERIAL: "op://${{ vars.OP_VAULT_ACTIONS_PUBLIC }}/UNITY_LICENSE/serial"
|
|
||||||
|
|
||||||
- uses: Cysharp/Actions/.github/actions/checkout@main
|
# Execute scripts: RuntimeUnitTestToolkit
|
||||||
|
- name: Build UnitTest(Linux64, mono)
|
||||||
|
run: /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod UnitTestBuilder.BuildUnitTest /headless /ScriptBackend mono /BuildTarget StandaloneLinux64
|
||||||
|
working-directory: src/UniTask
|
||||||
|
- name: Execute UnitTest
|
||||||
|
run: ./src/UniTask/bin/UnitTest/StandaloneLinux64_Mono2x/test
|
||||||
|
|
||||||
# Execute scripts: Export Package
|
# Execute scripts: Export Package
|
||||||
# /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod PackageExporter.Export
|
- name: Export unitypackage
|
||||||
- name: Build Unity (.unitypacakge)
|
run: /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod PackageExporter.Export
|
||||||
if: ${{ startsWith(matrix.unity, '2022') }} # only execute once
|
working-directory: src/UniTask
|
||||||
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
|
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
- uses: Cysharp/Actions/.github/actions/check-metas@main # check meta files
|
|
||||||
with:
|
|
||||||
directory: src/UniTask
|
|
||||||
|
|
||||||
# Store artifacts.
|
# Store artifacts.
|
||||||
- uses: Cysharp/Actions/.github/actions/upload-artifact@main
|
- uses: actions/upload-artifact@v2
|
||||||
if: ${{ startsWith(matrix.unity, '2021') }} # only execute 2021
|
|
||||||
with:
|
with:
|
||||||
name: UniTask.unitypackage-${{ matrix.unity }}.zip
|
name: UniTask.unitypackage.zip
|
||||||
path: ./src/UniTask/*.unitypackage
|
path: ./src/UniTask/*.unitypackage
|
||||||
retention-days: 1
|
|
||||||
|
|
|
@ -8,24 +8,24 @@ on:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
run-docfx:
|
run-docfx:
|
||||||
|
if: "!(contains(github.event.head_commit.message, '[skip ci]') || contains(github.event.head_commit.message, '[ci skip]'))"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 10
|
|
||||||
steps:
|
steps:
|
||||||
- uses: Cysharp/Actions/.github/actions/checkout@main
|
- uses: actions/checkout@v2
|
||||||
- uses: Cysharp/Actions/.github/actions/checkout@main
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
repository: Cysharp/DocfxTemplate
|
repository: Cysharp/DocfxTemplate
|
||||||
path: docs/_DocfxTemplate
|
path: docs/_DocfxTemplate
|
||||||
- uses: Kirbyrawr/docfx-action@db9a22c8fe1e8693a2a21be54cb0b87dfaa72cc4
|
- uses: Kirbyrawr/docfx-action@master
|
||||||
name: Docfx metadata
|
name: Docfx metadata
|
||||||
with:
|
with:
|
||||||
args: metadata docs/docfx.json
|
args: metadata docs/docfx.json
|
||||||
- uses: Kirbyrawr/docfx-action@db9a22c8fe1e8693a2a21be54cb0b87dfaa72cc4
|
- uses: Kirbyrawr/docfx-action@master
|
||||||
name: Docfx build
|
name: Docfx build
|
||||||
with:
|
with:
|
||||||
args: build docs/docfx.json
|
args: build docs/docfx.json
|
||||||
- name: Publish to GitHub Pages
|
- name: Publish to GitHub Pages
|
||||||
uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4.0.0
|
uses: peaceiris/actions-gh-pages@v3
|
||||||
with:
|
with:
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
publish_dir: docs/_site
|
publish_dir: docs/_site
|
|
@ -1,110 +1,105 @@
|
||||||
name: build-release
|
name: Build-Release
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
push:
|
||||||
inputs:
|
tags:
|
||||||
tag:
|
- "[0-9]+.[0-9]+.[0-9]+*"
|
||||||
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:
|
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:
|
build-dotnet:
|
||||||
needs: [update-packagejson]
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 10
|
env:
|
||||||
|
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||||
|
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
|
||||||
|
NUGET_XMLDOC_MODE: skip
|
||||||
steps:
|
steps:
|
||||||
- run: echo ${{ needs.update-packagejson.outputs.sha }}
|
- uses: actions/checkout@v2
|
||||||
- uses: Cysharp/Actions/.github/actions/checkout@main
|
- uses: actions/setup-dotnet@v1
|
||||||
with:
|
with:
|
||||||
ref: ${{ needs.update-packagejson.outputs.sha }}
|
dotnet-version: 3.1.101
|
||||||
- uses: Cysharp/Actions/.github/actions/setup-dotnet@main
|
# set release tag(*.*.*) to env.GIT_TAG
|
||||||
|
- run: echo ::set-env name=GIT_TAG::${GITHUB_REF#refs/tags/}
|
||||||
|
|
||||||
# build and pack
|
# build and pack
|
||||||
- run: dotnet build -c Release -p:Version=${{ inputs.tag }}
|
- run: dotnet build -c Release -p:Version=${{ env.GIT_TAG }}
|
||||||
- run: dotnet test -c Release --no-build
|
- run: dotnet test -c Release --no-build
|
||||||
- run: dotnet pack ./src/UniTask.NetCore/UniTask.NetCore.csproj -c Release --no-build -p:Version=${{ inputs.tag }} -o ./publish
|
- run: dotnet pack ./src/UniTask.NetCore/UniTask.NetCore.csproj -c Release --no-build -p:Version=${{ env.GIT_TAG }}
|
||||||
|
|
||||||
# Store artifacts.
|
# Store artifacts.
|
||||||
- uses: Cysharp/Actions/.github/actions/upload-artifact@main
|
- uses: actions/upload-artifact@v1
|
||||||
with:
|
with:
|
||||||
name: nuget
|
name: nuget
|
||||||
path: ./publish/
|
path: ./src/UniTask.NetCore/bin/Release/UniTask.${{ env.GIT_TAG }}.nupkg
|
||||||
retention-days: 1
|
|
||||||
|
|
||||||
build-unity:
|
build-unity:
|
||||||
needs: [update-packagejson]
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
unity: ["2022.3.39f1"]
|
unity: ['2019.3.9f1']
|
||||||
|
include:
|
||||||
|
- unity: 2019.3.9f1
|
||||||
|
license: UNITY_2019_3
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 15
|
container:
|
||||||
|
# with linux-il2cpp. image from https://hub.docker.com/r/gableroux/unity3d/tags
|
||||||
|
image: gableroux/unity3d:${{ matrix.unity }}-linux-il2cpp
|
||||||
steps:
|
steps:
|
||||||
- name: Load secrets
|
- run: apt update && apt install git -y
|
||||||
id: op-load-secret
|
- uses: actions/checkout@v2
|
||||||
uses: 1password/load-secrets-action@581a835fb51b8e7ec56b71cf2ffddd7e68bb25e0 # v2.0.0
|
- run: echo -n "$UNITY_LICENSE" >> .Unity.ulf
|
||||||
with:
|
|
||||||
export-env: false
|
|
||||||
env:
|
env:
|
||||||
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN_PUBLIC }}
|
UNITY_LICENSE: ${{ secrets[matrix.license] }}
|
||||||
UNITY_EMAIL: "op://${{ vars.OP_VAULT_ACTIONS_PUBLIC }}/UNITY_LICENSE/username"
|
- run: /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -manualLicenseFile .Unity.ulf || exit 0
|
||||||
UNITY_PASSWORD: "op://${{ vars.OP_VAULT_ACTIONS_PUBLIC }}/UNITY_LICENSE/credential"
|
|
||||||
UNITY_SERIAL: "op://${{ vars.OP_VAULT_ACTIONS_PUBLIC }}/UNITY_LICENSE/serial"
|
# set release tag(*.*.*) to env.GIT_TAG
|
||||||
|
- run: echo ::set-env name=GIT_TAG::${GITHUB_REF#refs/tags/}
|
||||||
|
|
||||||
- run: echo ${{ needs.update-packagejson.outputs.sha }}
|
|
||||||
- uses: Cysharp/Actions/.github/actions/checkout@main
|
|
||||||
with:
|
|
||||||
ref: ${{ needs.update-packagejson.outputs.sha }}
|
|
||||||
# Execute scripts: Export Package
|
# Execute scripts: Export Package
|
||||||
# /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod PackageExporter.Export
|
- name: Export unitypackage
|
||||||
- name: Build Unity (.unitypacakge)
|
run: /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod PackageExporter.Export
|
||||||
uses: Cysharp/Actions/.github/actions/unity-builder@main
|
working-directory: src/UniTask
|
||||||
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.
|
# Store artifacts.
|
||||||
- uses: Cysharp/Actions/.github/actions/upload-artifact@main
|
- uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: UniTask.${{ inputs.tag }}.unitypackage
|
name: UniTask.${{ env.GIT_TAG }}.unitypackage
|
||||||
path: ./src/UniTask/UniTask.${{ inputs.tag }}.unitypackage
|
path: ./src/UniTask/UniTask.${{ env.GIT_TAG }}.unitypackage
|
||||||
retention-days: 1
|
|
||||||
|
|
||||||
# release
|
|
||||||
create-release:
|
create-release:
|
||||||
needs: [update-packagejson, build-dotnet, build-unity]
|
needs: [build-dotnet, build-unity]
|
||||||
uses: Cysharp/Actions/.github/workflows/create-release.yaml@main
|
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:
|
with:
|
||||||
commit-id: ${{ needs.update-packagejson.outputs.sha }}
|
dotnet-version: 3.1.101
|
||||||
dry-run: ${{ inputs.dry-run }}
|
# set release tag(*.*.*) to env.GIT_TAG
|
||||||
tag: ${{ inputs.tag }}
|
- run: echo ::set-env name=GIT_TAG::${GITHUB_REF#refs/tags/}
|
||||||
nuget-push: true
|
|
||||||
release-upload: true
|
|
||||||
release-asset-path: ./UniTask.${{ inputs.tag }}.unitypackage/UniTask.${{ inputs.tag }}.unitypackage
|
|
||||||
secrets: inherit
|
|
||||||
|
|
||||||
cleanup:
|
# Create Releases
|
||||||
if: ${{ needs.update-packagejson.outputs.is-branch-created == 'true' }}
|
- uses: actions/create-release@v1
|
||||||
needs: [update-packagejson, build-dotnet, build-unity]
|
id: create_release
|
||||||
uses: Cysharp/Actions/.github/workflows/clean-packagejson-branch.yaml@main
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
with:
|
||||||
branch: ${{ needs.update-packagejson.outputs.branch-name }}
|
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
|
|
@ -1,10 +0,0 @@
|
||||||
name: Prevent github change
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
paths:
|
|
||||||
- ".github/**/*.yaml"
|
|
||||||
- ".github/**/*.yml"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
detect:
|
|
||||||
uses: Cysharp/Actions/.github/workflows/prevent-github-change.yaml@main
|
|
|
@ -1,10 +0,0 @@
|
||||||
name: "Close stale issues"
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
schedule:
|
|
||||||
- cron: "0 0 * * *"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
stale:
|
|
||||||
uses: Cysharp/Actions/.github/workflows/stale-issue.yaml@main
|
|
|
@ -8,7 +8,8 @@ on:
|
||||||
jobs:
|
jobs:
|
||||||
generateTOC:
|
generateTOC:
|
||||||
name: TOC Generator
|
name: TOC Generator
|
||||||
uses: Cysharp/Actions/.github/workflows/toc-generator.yaml@main
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: technote-space/toc-generator@v2.4.0
|
||||||
with:
|
with:
|
||||||
TOC_TITLE: "## Table of Contents"
|
TOC_TITLE: "## Table of Contents"
|
||||||
secrets: inherit
|
|
||||||
|
|
|
@ -130,15 +130,78 @@ UpgradeLog*.XML
|
||||||
Assets/WSATestCertificate.pfx
|
Assets/WSATestCertificate.pfx
|
||||||
.vs/
|
.vs/
|
||||||
|
|
||||||
# Unity
|
Assembly-CSharp\.csproj
|
||||||
|
|
||||||
# Unity
|
UniRx\.Async\.csproj
|
||||||
.vsconfig
|
|
||||||
src/UniTask/Library/*
|
UniRx\.Async\.Editor\.csproj
|
||||||
src/UniTask/Temp/*
|
|
||||||
src/UniTask/Logs/*
|
UniRx\.Async\.Tests\.csproj
|
||||||
src/UniTask/[Uu]ser[Ss]ettings/
|
|
||||||
src/UniTask/*.sln
|
UniTask\.sln
|
||||||
src/UniTask/*.csproj
|
|
||||||
src/UniTask/*.unitypackage
|
RuntimeUnitTestToolkit\.csproj
|
||||||
!src/UniTask/Packages/
|
|
||||||
|
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
|
||||||
|
|
643
README.md
643
README.md
|
@ -1,22 +1,18 @@
|
||||||
UniTask
|
UniTask
|
||||||
===
|
===
|
||||||
[](https://github.com/Cysharp/UniTask/actions) [](https://github.com/Cysharp/UniTask/releases) [](https://github.com/Cysharp/UniTask/blob/master/README_CN.md)
|
[](https://github.com/Cysharp/UniTask/actions) [](https://github.com/Cysharp/UniTask/releases)
|
||||||
|
|
||||||
Provides an efficient allocation free async/await integration for Unity.
|
Provides an efficient async/await integration to Unity.
|
||||||
|
|
||||||
* Struct based `UniTask<T>` and custom AsyncMethodBuilder to achieve zero allocation
|
* Struct based `UniTask<T>` and custom AsyncMethodBuilder to achive zero allocation
|
||||||
* Makes all Unity AsyncOperations and Coroutines awaitable
|
* All Unity AsyncOperations and Coroutine to awaitable
|
||||||
* PlayerLoop based task(`UniTask.Yield`, `UniTask.Delay`, `UniTask.DelayFrame`, etc..) that enable replacing all coroutine operations
|
* PlayerLoop based task(`UniTask.Yield`, `UniTask.Delay`, `UniTask.DelayFrame`, etc..) that enable to replace all coroutine operation
|
||||||
* MonoBehaviour Message Events and uGUI Events as awaitable/async-enumerable
|
* MonoBehaviour Message Events and uGUI Events as awaitable/async-enumerable
|
||||||
* Runs completely on Unity's PlayerLoop so doesn't use threads and runs on WebGL, wasm, etc.
|
* Completely run on Unity's PlayerLoop so don't use thread and run on WebGL, wasm, etc.
|
||||||
* Asynchronous LINQ, with Channel and AsyncReactiveProperty
|
* Asynchronous LINQ, with Channel and AsyncReactiveProperty
|
||||||
* TaskTracker window to prevent memory leaks
|
* TaskTracker window to prevent memory leak
|
||||||
* Highly compatible behaviour with Task/ValueTask/IValueTaskSource
|
* Highly compatible behaviour with Task/ValueTask/IValueTaskSource
|
||||||
|
|
||||||
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 -->
|
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
||||||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
@ -24,7 +20,6 @@ For advanced tips, see blog post: [Extends UnityWebRequest via async decorator p
|
||||||
- [Getting started](#getting-started)
|
- [Getting started](#getting-started)
|
||||||
- [Basics of UniTask and AsyncOperation](#basics-of-unitask-and-asyncoperation)
|
- [Basics of UniTask and AsyncOperation](#basics-of-unitask-and-asyncoperation)
|
||||||
- [Cancellation and Exception handling](#cancellation-and-exception-handling)
|
- [Cancellation and Exception handling](#cancellation-and-exception-handling)
|
||||||
- [Timeout handling](#timeout-handling)
|
|
||||||
- [Progress](#progress)
|
- [Progress](#progress)
|
||||||
- [PlayerLoop](#playerloop)
|
- [PlayerLoop](#playerloop)
|
||||||
- [async void vs async UniTaskVoid](#async-void-vs-async-unitaskvoid)
|
- [async void vs async UniTaskVoid](#async-void-vs-async-unitaskvoid)
|
||||||
|
@ -33,18 +28,12 @@ For advanced tips, see blog post: [Extends UnityWebRequest via async decorator p
|
||||||
- [AsyncEnumerable and Async LINQ](#asyncenumerable-and-async-linq)
|
- [AsyncEnumerable and Async LINQ](#asyncenumerable-and-async-linq)
|
||||||
- [Awaitable Events](#awaitable-events)
|
- [Awaitable Events](#awaitable-events)
|
||||||
- [Channel](#channel)
|
- [Channel](#channel)
|
||||||
- [vs Awaitable](#vs-awaitable)
|
|
||||||
- [For Unit Testing](#for-unit-testing)
|
- [For Unit Testing](#for-unit-testing)
|
||||||
- [ThreadPool limitation](#threadpool-limitation)
|
|
||||||
- [IEnumerator.ToUniTask limitation](#ienumeratortounitask-limitation)
|
|
||||||
- [For UnityEditor](#for-unityeditor)
|
|
||||||
- [Compare with Standard Task API](#compare-with-standard-task-api)
|
|
||||||
- [Pooling Configuration](#pooling-configuration)
|
- [Pooling Configuration](#pooling-configuration)
|
||||||
- [Allocation on Profiler](#allocation-on-profiler)
|
|
||||||
- [UniTaskSynchronizationContext](#unitasksynchronizationcontext)
|
|
||||||
- [API References](#api-references)
|
- [API References](#api-references)
|
||||||
- [UPM Package](#upm-package)
|
- [UPM Package](#upm-package)
|
||||||
- [Install via git URL](#install-via-git-url)
|
- [Install via git URL](#install-via-git-url)
|
||||||
|
- [Install via OpenUPM](#install-via-openupm)
|
||||||
- [.NET Core](#net-core)
|
- [.NET Core](#net-core)
|
||||||
- [License](#license)
|
- [License](#license)
|
||||||
|
|
||||||
|
@ -52,7 +41,7 @@ For advanced tips, see blog post: [Extends UnityWebRequest via async decorator p
|
||||||
|
|
||||||
Getting started
|
Getting started
|
||||||
---
|
---
|
||||||
Install via [UPM package](#upm-package) with git reference or asset package(`UniTask.*.*.*.unitypackage`) available in [UniTask/releases](https://github.com/Cysharp/UniTask/releases).
|
Install via [UPM package](#upm-package) or asset package(`UniTask.*.*.*.unitypackage`) available in [UniTask/releases](https://github.com/Cysharp/UniTask/releases) page.
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
// extension awaiter/methods can be used by this namespace
|
// extension awaiter/methods can be used by this namespace
|
||||||
|
@ -68,13 +57,12 @@ async UniTask<string> DemoAsync()
|
||||||
await SceneManager.LoadSceneAsync("scene2");
|
await SceneManager.LoadSceneAsync("scene2");
|
||||||
|
|
||||||
// .WithCancellation enables Cancel, GetCancellationTokenOnDestroy synchornizes with lifetime of GameObject
|
// .WithCancellation enables Cancel, GetCancellationTokenOnDestroy synchornizes with lifetime of GameObject
|
||||||
// after Unity 2022.2, you can use `destroyCancellationToken` in MonoBehaviour
|
|
||||||
var asset2 = await Resources.LoadAsync<TextAsset>("bar").WithCancellation(this.GetCancellationTokenOnDestroy());
|
var asset2 = await Resources.LoadAsync<TextAsset>("bar").WithCancellation(this.GetCancellationTokenOnDestroy());
|
||||||
|
|
||||||
// .ToUniTask accepts progress callback(and all options), Progress.Create is a lightweight alternative of IProgress<T>
|
// .ToUniTask accepts progress callback(and all options), Progress.Create is a lightweight alternative of IProgress<T>
|
||||||
var asset3 = await Resources.LoadAsync<TextAsset>("baz").ToUniTask(Progress.Create<float>(x => Debug.Log(x)));
|
var asset3 = await Resources.LoadAsync<TextAsset>("baz").ToUniTask(Progress.Create<float>(x => Debug.Log(x)));
|
||||||
|
|
||||||
// await frame-based operation like a coroutine
|
// await frame-based operation like coroutine
|
||||||
await UniTask.DelayFrame(100);
|
await UniTask.DelayFrame(100);
|
||||||
|
|
||||||
// replacement of yield return new WaitForSeconds/WaitForSecondsRealtime
|
// replacement of yield return new WaitForSeconds/WaitForSecondsRealtime
|
||||||
|
@ -87,13 +75,8 @@ async UniTask<string> DemoAsync()
|
||||||
await UniTask.Yield();
|
await UniTask.Yield();
|
||||||
await UniTask.NextFrame();
|
await UniTask.NextFrame();
|
||||||
|
|
||||||
// replacement of WaitForEndOfFrame
|
// replacement of WaitForEndOfFrame(same as UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate))
|
||||||
#if UNITY_2023_1_OR_NEWER
|
|
||||||
await UniTask.WaitForEndOfFrame();
|
await UniTask.WaitForEndOfFrame();
|
||||||
#else
|
|
||||||
// requires MonoBehaviour(CoroutineRunner))
|
|
||||||
await UniTask.WaitForEndOfFrame(this); // this is MonoBehaviour
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// replacement of yield return new WaitForFixedUpdate(same as UniTask.Yield(PlayerLoopTiming.FixedUpdate))
|
// replacement of yield return new WaitForFixedUpdate(same as UniTask.Yield(PlayerLoopTiming.FixedUpdate))
|
||||||
await UniTask.WaitForFixedUpdate();
|
await UniTask.WaitForFixedUpdate();
|
||||||
|
@ -104,10 +87,10 @@ async UniTask<string> DemoAsync()
|
||||||
// special helper of WaitUntil
|
// special helper of WaitUntil
|
||||||
await UniTask.WaitUntilValueChanged(this, x => x.isActive);
|
await UniTask.WaitUntilValueChanged(this, x => x.isActive);
|
||||||
|
|
||||||
// You can await IEnumerator coroutines
|
// You can await IEnumerator coroutine
|
||||||
await FooCoroutineEnumerator();
|
await FooCoroutineEnumerator();
|
||||||
|
|
||||||
// You can await a standard task
|
// You can await standard task
|
||||||
await Task.Run(() => 100);
|
await Task.Run(() => 100);
|
||||||
|
|
||||||
// Multithreading, run on ThreadPool under this code
|
// Multithreading, run on ThreadPool under this code
|
||||||
|
@ -129,12 +112,15 @@ async UniTask<string> DemoAsync()
|
||||||
var task2 = GetTextAsync(UnityWebRequest.Get("http://bing.com"));
|
var task2 = GetTextAsync(UnityWebRequest.Get("http://bing.com"));
|
||||||
var task3 = GetTextAsync(UnityWebRequest.Get("http://yahoo.com"));
|
var task3 = GetTextAsync(UnityWebRequest.Get("http://yahoo.com"));
|
||||||
|
|
||||||
// concurrent async-wait and get results easily by tuple syntax
|
// concurrent async-wait and get result easily by tuple syntax
|
||||||
var (google, bing, yahoo) = await UniTask.WhenAll(task1, task2, task3);
|
var (google, bing, yahoo) = await UniTask.WhenAll(task1, task2, task3);
|
||||||
|
|
||||||
// shorthand of WhenAll, tuple can await directly
|
// shorthand of WhenAll, tuple can await directly
|
||||||
var (google2, bing2, yahoo2) = await (task1, task2, task3);
|
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 async-value.(or you can use `UniTask`(no result), `UniTaskVoid`(fire and forget)).
|
||||||
return (asset as TextAsset)?.text ?? throw new InvalidOperationException("Asset not found");
|
return (asset as TextAsset)?.text ?? throw new InvalidOperationException("Asset not found");
|
||||||
}
|
}
|
||||||
|
@ -142,11 +128,11 @@ async UniTask<string> DemoAsync()
|
||||||
|
|
||||||
Basics of UniTask and AsyncOperation
|
Basics of UniTask and AsyncOperation
|
||||||
---
|
---
|
||||||
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`.
|
UniTask feature rely on C# 7.0([task-like custom async method builder feature](https://github.com/dotnet/roslyn/blob/master/docs/features/task-types.md)) so required Unity version is after `Unity 2018.3`, officialy lower support version is `Unity 2018.4.13f1`.
|
||||||
|
|
||||||
Why 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.
|
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.
|
||||||
|
|
||||||
You can await `AsyncOperation`, `ResourceRequest`, `AssetBundleRequest`, `AssetBundleCreateRequest`, `UnityWebRequestAsyncOperation`, `AsyncGPUReadbackRequest`, `IEnumerator` and others when `using Cysharp.Threading.Tasks;`.
|
You can await `AsyncOperation`, `ResourceRequest`, `AssetBundleRequest`, `AssetBundleCreateRequest`, `UnityWebRequestAsyncOperation`, `IEnumerator` and others when `using Cysharp.Threading.Tasks;`.
|
||||||
|
|
||||||
UniTask provides three pattern of extension methods.
|
UniTask provides three pattern of extension methods.
|
||||||
|
|
||||||
|
@ -156,16 +142,16 @@ UniTask provides three pattern of extension methods.
|
||||||
* .ToUniTask(IProgress, PlayerLoopTiming, CancellationToken);
|
* .ToUniTask(IProgress, PlayerLoopTiming, CancellationToken);
|
||||||
```
|
```
|
||||||
|
|
||||||
`WithCancellation` is a simple version of `ToUniTask`, both return `UniTask`. For details of cancellation, see: [Cancellation and Exception handling](#cancellation-and-exception-handling) section.
|
`WithCancellation` is a simple version of `ToUniTask`, both returns `UniTask`. Details of cancellation, see: [Cancellation and Exception handling](#cancellation-and-exception-handling) 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: WithCancellation is returned from native timing of PlayerLoop but ToUniTask is returned from specified PlayerLoopTiming. Details of timing, see: [PlayerLoop](#playerloop) section.
|
||||||
|
|
||||||
> 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
|
```csharp
|
||||||
public async UniTaskVoid LoadManyAsync()
|
public class SceneAssets
|
||||||
|
{
|
||||||
|
public SceneAssets()
|
||||||
{
|
{
|
||||||
// parallel load.
|
// parallel load.
|
||||||
var (a, b, c) = await UniTask.WhenAll(
|
var (a, b, c) = await UniTask.WhenAll(
|
||||||
|
@ -179,9 +165,10 @@ async UniTask<Sprite> LoadAsSprite(string path)
|
||||||
var resource = await Resources.LoadAsync<Sprite>(path);
|
var resource = await Resources.LoadAsync<Sprite>(path);
|
||||||
return (resource as Sprite);
|
return (resource as Sprite);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
If you want to convert a callback to UniTask, you can use `UniTaskCompletionSource<T>` which is a lightweight edition of `TaskCompletionSource<T>`.
|
If you want to convert callback to UniTask, you can use `UniTaskCompletionSource<T>` that is the lightweight edition of `TaskCompletionSource<T>`.
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
public UniTask<int> WrapByUniTaskCompletionSource()
|
public UniTask<int> WrapByUniTaskCompletionSource()
|
||||||
|
@ -198,7 +185,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.
|
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 if you want to only allow using the coroutine system.
|
If you want to convert async to coroutine, you can use `.ToCoroutine()`, this is useful to use only allow coroutine system.
|
||||||
|
|
||||||
UniTask can not await twice. This is a similar constraint to the [ValueTask/IValueTaskSource](https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.valuetask-1?view=netcore-3.1) introduced in .NET Standard 2.1.
|
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.
|
||||||
|
|
||||||
|
@ -217,13 +204,11 @@ await task;
|
||||||
await task; // NG, throws Exception
|
await task; // NG, throws Exception
|
||||||
```
|
```
|
||||||
|
|
||||||
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.
|
Store to the class field, you can use `UniTask.Lazy` that gurantee call multipletimes. `.Prevent()` allows for multiple calls (internally cached results). This is useful when multiple calls in a function scope.
|
||||||
|
|
||||||
Also `UniTaskCompletionSource` can await multiple times and await from many callers.
|
|
||||||
|
|
||||||
Cancellation and Exception handling
|
Cancellation and Exception handling
|
||||||
---
|
---
|
||||||
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.
|
Some UniTask factory methods have `CancellationToken cancellationToken = default` parameter. Andalso some async operation for unity have `WithCancellation(CancellationToken)` and `ToUniTask(..., CancellationToken cancellation = default)` extension methods.
|
||||||
|
|
||||||
You can pass `CancellationToken` to parameter by standard [`CancellationTokenSource`](https://docs.microsoft.com/en-us/dotnet/api/system.threading.cancellationtokensource).
|
You can pass `CancellationToken` to parameter by standard [`CancellationTokenSource`](https://docs.microsoft.com/en-us/dotnet/api/system.threading.cancellationtokensource).
|
||||||
|
|
||||||
|
@ -240,68 +225,18 @@ await UnityWebRequest.Get("http://google.co.jp").SendWebRequest().WithCancellati
|
||||||
await UniTask.DelayFrame(1000, cancellationToken: cts.Token);
|
await UniTask.DelayFrame(1000, cancellationToken: cts.Token);
|
||||||
```
|
```
|
||||||
|
|
||||||
CancellationToken can be created by `CancellationTokenSource` or MonoBehaviour's extension method `GetCancellationTokenOnDestroy`.
|
CancellationToken can create by `CancellationTokenSource` or MonoBehaviour's extension method `GetCancellationTokenOnDestroy`.
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
// this CancellationToken lifecycle is same as GameObject.
|
// this CancellationToken lifecycle is same as GameObject.
|
||||||
await UniTask.DelayFrame(1000, cancellationToken: this.GetCancellationTokenOnDestroy());
|
await UniTask.DelayFrame(1000, cancellationToken: this.GetCancellationTokenOnDestroy());
|
||||||
```
|
```
|
||||||
|
|
||||||
For propagate Cancellation, all async method recommend to accept `CancellationToken cancellationToken` at last argument, and pass `CancellationToken` from root to end.
|
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`.
|
||||||
|
|
||||||
```csharp
|
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.`
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
After Unity 2022.2, Unity adds CancellationToken in [MonoBehaviour.destroyCancellationToken](https://docs.unity3d.com/ScriptReference/MonoBehaviour-destroyCancellationToken.html) and [Application.exitCancellationToken](https://docs.unity3d.com/ScriptReference/Application-exitCancellationToken.html).
|
|
||||||
|
|
||||||
When cancellation is detected, all methods throw `OperationCanceledException` and propagate upstream. When exception(not limited to `OperationCanceledException`) is not handled in async method, it is propagated finally to `UniTaskScheduler.UnobservedTaskException`. The default behaviour of received unhandled exception is to write log as exception. Log level can be changed using `UniTaskScheduler.UnobservedExceptionWriteLogType`. If you want to use custom behaviour, set an action to `UniTaskScheduler.UnobservedTaskException.`
|
|
||||||
|
|
||||||
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
|
```csharp
|
||||||
public async UniTask<int> FooAsync()
|
public async UniTask<int> FooAsync()
|
||||||
|
@ -311,7 +246,7 @@ public async UniTask<int> FooAsync()
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
If you handle an exception but want to ignore(propagate to global cancellation handling), use an exception filter.
|
If you handle exception but want to ignore(propagete to global cancellation handling), use exception filter.
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
public async UniTask<int> BarAsync()
|
public async UniTask<int> BarAsync()
|
||||||
|
@ -321,14 +256,14 @@ public async UniTask<int> BarAsync()
|
||||||
var x = await FooAsync();
|
var x = await FooAsync();
|
||||||
return x * 2;
|
return x * 2;
|
||||||
}
|
}
|
||||||
catch (Exception ex) when (!(ex is OperationCanceledException)) // when (ex is not OperationCanceledException) at C# 9.0
|
catch (Exception ex) when (!(ex is OperationCanceledException))
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
var (isCanceled, _) = await UniTask.DelayFrame(10, cancellationToken: cts.Token).SuppressCancellationThrow();
|
var (isCanceled, _) = await UniTask.DelayFrame(10, cancellationToken: cts.Token).SuppressCancellationThrow();
|
||||||
|
@ -338,117 +273,11 @@ if (isCanceled)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
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
|
Progress
|
||||||
---
|
---
|
||||||
Some async operations for unity have `ToUniTask(IProgress<float> progress = null, ...)` extension methods.
|
Some async operation for unity have `ToUniTask(IProgress<float> progress = null, ...)` extension methods.
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
var progress = Progress.Create<float>(x => Debug.Log(x));
|
var progress = Progress.Create<float>(x => Debug.Log(x));
|
||||||
|
@ -458,9 +287,9 @@ var request = await UnityWebRequest.Get("http://google.co.jp")
|
||||||
.ToUniTask(progress: progress);
|
.ToUniTask(progress: progress);
|
||||||
```
|
```
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
Implementing IProgress interface to caller is better as there is no lambda allocation.
|
Implements IProgress interface to caller is more better, there is no allocation of lambda.
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
public class Foo : MonoBehaviour, IProgress<float>
|
public class Foo : MonoBehaviour, IProgress<float>
|
||||||
|
@ -481,7 +310,7 @@ public class Foo : MonoBehaviour, IProgress<float>
|
||||||
|
|
||||||
PlayerLoop
|
PlayerLoop
|
||||||
---
|
---
|
||||||
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`.
|
UniTask is run on custom [PlayerLoop](https://docs.unity3d.com/ScriptReference/LowLevel.PlayerLoop.html). UniTask's playerloop based method(such as `Delay`, `DelayFrame`, `asyncOperation.ToUniTask`, etc...) accepts this `PlayerLoopTiming`.
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
public enum PlayerLoopTiming
|
public enum PlayerLoopTiming
|
||||||
|
@ -506,40 +335,28 @@ public enum PlayerLoopTiming
|
||||||
|
|
||||||
PostLateUpdate = 12,
|
PostLateUpdate = 12,
|
||||||
LastPostLateUpdate = 13
|
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.
|
It indicates when to run, you can check [PlayerLoopList.md](https://gist.github.com/neuecc/bc3a1cfd4d74501ad057e49efcd7bdae) to Unity's default playerloop and injected UniTask's custom loop.
|
||||||
|
|
||||||
`PlayerLoopTiming.Update` is similar to `yield return null` in a coroutine, but it is called before Update(Update and uGUI events(button.onClick, etc...) are called on `ScriptRunBehaviourUpdate`, yield return null is called on `ScriptRunDelayedDynamicFrameRate`). `PlayerLoopTiming.FixedUpdate` is similar to `WaitForFixedUpdate`.
|
`PlayerLoopTiming.Update` is similar 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.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()`.
|
`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`.
|
||||||
>
|
|
||||||
> Note: In Unity 2023.1 or newer, `await UniTask.WaitForEndOfFrame();` no longer requires MonoBehaviour. It uses `UnityEngine.Awaitable.EndOfFrameAsync`.
|
|
||||||
|
|
||||||
`yield return null` and `UniTask.Yield` are similar but different. `yield return null` always returns next frame but `UniTask.Yield` returns next called. That is, call `UniTask.Yield(PlayerLoopTiming.Update)` on `PreUpdate`, it returns same frame. `UniTask.NextFrame()` guarantees return next frame, you can expect this to behave exactly the same as `yield return null`.
|
> UniTask.Yield(without CancellationToken) is a special type, returns `YieldAwaitable` and run on YieldRunner. It is most lightweight and faster.
|
||||||
|
|
||||||
> UniTask.Yield(without CancellationToken) is a special type, returns `YieldAwaitable` and runs on YieldRunner. It is the most lightweight and fastest.
|
AsyncOperation is returned from native timing. For example, await `SceneManager.LoadSceneAsync` is returned from `EarlyUpdate.UpdatePreloading` and after called, loaded scene called from `EarlyUpdate.ScriptRunDelayedStartupFrame`. Also `await UnityWebRequest` is returned from `EarlyUpdate.ExecuteMainThreadJobs`.
|
||||||
|
|
||||||
`AsyncOperation` is returned from native timing. For example, await `SceneManager.LoadSceneAsync` is returned from `EarlyUpdate.UpdatePreloading` and after being called, the loaded scene's `Start` is called from `EarlyUpdate.ScriptRunDelayedStartupFrame`. Also `await UnityWebRequest` is returned from `EarlyUpdate.ExecuteMainThreadJobs`.
|
In UniTask, await directly and `WithCancellation` use native timing, `ToUniTask` use specified timing. This is usually not a particular problem, but with `LoadSceneAsync`, causes different order of Start and continuation after await. so recommend not to use `LoadSceneAsync.ToUniTask`.
|
||||||
|
|
||||||
In UniTask, await directly uses native timing, while `WithCancellation` and `ToUniTask` use specified timing. This is usually not a particular problem, but with `LoadSceneAsync`, it causes a different order of Start and continuation after await. So it is recommended not to use `LoadSceneAsync.ToUniTask`.
|
In stacktrace, you can check where is running in playerloop.
|
||||||
|
|
||||||
> 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.
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
By default, UniTask's PlayerLoop is initialized at `[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]`.
|
In default, UniTask's PlayerLoop is initialized at `[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]`.
|
||||||
|
|
||||||
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.
|
The order in which methods are called in BeforeSceneLoad is indeterminate, so if you want to use UniTask in other BeforeSceneLoad methods, you should try to initialize it before this.
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
// AfterAssembliesLoaded is called before BeforeSceneLoad
|
// AfterAssembliesLoaded is called before BeforeSceneLoad
|
||||||
|
@ -551,9 +368,9 @@ public static void InitUniTaskLoop()
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
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.
|
If you import Unity's `Entities` package, that reset custom player loop to default at `BeforeSceneLoad` and inject ECS's loop. When Unity call ECS's inject method after UniTask's initialize method, UniTask will no longer work.
|
||||||
|
|
||||||
To solve this issue, you can re-initialize the UniTask PlayerLoop after ECS is initialized.
|
To solve this issue, you can re-initialize UniTask PlayerLoop after ECS initialized.
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
// Get ECS Loop.
|
// Get ECS Loop.
|
||||||
|
@ -563,50 +380,9 @@ var playerLoop = ScriptBehaviourUpdateOrder.CurrentPlayerLoop;
|
||||||
PlayerLoopHelper.Initialize(ref playerLoop);
|
PlayerLoopHelper.Initialize(ref playerLoop);
|
||||||
```
|
```
|
||||||
|
|
||||||
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()
|
|
||||||
{
|
|
||||||
UnityEngine.Debug.Log("UniTaskPlayerLoop ready? " + PlayerLoopHelper.IsInjectedUniTaskPlayerLoop());
|
|
||||||
PlayerLoopHelper.DumpCurrentPlayerLoop();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
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 vs async UniTaskVoid
|
||||||
---
|
---
|
||||||
`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()`.
|
`async void` is a standard C# taks system so does not run on UniTask systems. It is better not to use. `async UniTaskVoid` is a lightweight version of `async UniTask` because it does not have awaitable completion and report error immediately to `UniTaskScheduler.UnobservedTaskException`. If you don't require to await it(fire and forget), use `UniTaskVoid` is better. Unfortunately to dismiss warning, require to using with `Forget()`.
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
public async UniTaskVoid FireAndForgetMethod()
|
public async UniTaskVoid FireAndForgetMethod()
|
||||||
|
@ -621,7 +397,7 @@ public void Caller()
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
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`。
|
Also UniTask have `Forget` method, it is similar with UniTaskVoid and same effects with it. However still UniTaskVoid is more efficient if completely do not use await。
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
public async UniTask DoAsync()
|
public async UniTask DoAsync()
|
||||||
|
@ -636,7 +412,7 @@ public void Caller()
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
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.
|
Using async lambda in register event, it is used `async void`. To avoid it, you can use `UniTask.Action` or `UniTask.UnityAction` that creates delegate via `async UniTaskVoid` lambda.
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
Action actEvent;
|
Action actEvent;
|
||||||
|
@ -651,21 +427,9 @@ actEvent += UniTask.Action(async () => { await UniTask.Yield(); });
|
||||||
unityEvent += UniTask.UnityAction(async () => { await UniTask.Yield(); });
|
unityEvent += UniTask.UnityAction(async () => { await UniTask.Yield(); });
|
||||||
```
|
```
|
||||||
|
|
||||||
`UniTaskVoid` can also be used in MonoBehaviour's `Start` method.
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
class Sample : MonoBehaviour
|
|
||||||
{
|
|
||||||
async UniTaskVoid Start()
|
|
||||||
{
|
|
||||||
// async init code.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
UniTaskTracker
|
UniTaskTracker
|
||||||
---
|
---
|
||||||
useful for checking (leaked) UniTasks. You can open tracker window in `Window -> UniTask Tracker`.
|
useful for check(leak) UniTasks. You can open tracker window in `Window -> UniTask Tracker`.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
@ -675,16 +439,13 @@ useful for checking (leaked) UniTasks. You can open tracker window in `Window ->
|
||||||
* Enable Tracking(Toggle) - Start to track async/await UniTask. Performance impact: low.
|
* Enable Tracking(Toggle) - Start to track async/await UniTask. Performance impact: low.
|
||||||
* Enable StackTrace(Toggle) - Capture StackTrace when task is started. Performance impact: high.
|
* Enable StackTrace(Toggle) - Capture StackTrace when task is started. Performance impact: high.
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
External Assets
|
External Assets
|
||||||
---
|
---
|
||||||
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).
|
In default, UniTask supports DOTween and Addressables(`AsyncOperationHandle` and `AsyncOpereationHandle<T>` as awaitable).
|
||||||
|
|
||||||
There are defined in separated asmdefs like `UniTask.TextMeshPro`, `UniTask.DOTween`, `UniTask.Addressables`.
|
For DOTween support, require to `com.demigiant.dotween` import from [OpenUPM](https://openupm.com/packages/com.demigiant.dotween/) or define `UNITASK_DOTWEEN_SUPPORT` to enable it.
|
||||||
|
|
||||||
TextMeshPro and Addressables support are automatically enabled when importing their packages from package manager.
|
|
||||||
However for DOTween support, after importing from the [DOTWeen assets](https://assetstore.unity.com/packages/tools/animation/dotween-hotween-v2-27676r) and define the scripting define symbol `UNITASK_DOTWEEN_SUPPORT` to enable it.
|
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
// sequential
|
// sequential
|
||||||
|
@ -699,15 +460,13 @@ await UniTask.WhenAll(
|
||||||
transform.DOScale(10, 3).WithCancellation(ct));
|
transform.DOScale(10, 3).WithCancellation(ct));
|
||||||
```
|
```
|
||||||
|
|
||||||
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
|
AsyncEnumerable and Async LINQ
|
||||||
---
|
---
|
||||||
Unity 2020.2 supports C# 8.0 so you can use `await foreach`. This is the new Update notation in the async era.
|
Unity 2020.2.0a12 supports C# 8.0 so you can use `await foreach`. This is the new Update notation in async era.
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
// Unity 2020.2, C# 8.0
|
// Unity 2020.2.0a12, C# 8.0
|
||||||
await foreach (var _ in UniTaskAsyncEnumerable.EveryUpdate().WithCancellation(token))
|
await foreach (var _ in UniTaskAsyncEnumerable.EveryUpdate(token))
|
||||||
{
|
{
|
||||||
Debug.Log("Update() " + Time.frameCount);
|
Debug.Log("Update() " + Time.frameCount);
|
||||||
}
|
}
|
||||||
|
@ -717,23 +476,10 @@ In a C# 7.3 environment, you can use the `ForEachAsync` method to work in almost
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
// C# 7.3(Unity 2018.3~)
|
// C# 7.3(Unity 2018.3~)
|
||||||
await UniTaskAsyncEnumerable.EveryUpdate().ForEachAsync(_ =>
|
await UniTaskAsyncEnumerable.EveryUpdate(token).ForEachAsync(_ =>
|
||||||
{
|
{
|
||||||
Debug.Log("Update() " + Time.frameCount);
|
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.
|
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.
|
||||||
|
@ -744,19 +490,11 @@ await okButton.OnClickAsAsyncEnumerable().Where((x, i) => i % 2 == 0).ForEachAsy
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
Fire and Forget style(for example, event handling), you can also use `Subscribe`.
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
okButton.OnClickAsAsyncEnumerable().Where((x, i) => i % 2 == 0).Subscribe(_ =>
|
|
||||||
{
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
Async LINQ is enabled when `using Cysharp.Threading.Tasks.Linq;`, and `UniTaskAsyncEnumerable` is defined in `UniTask.Linq` asmdef.
|
Async LINQ is enabled when `using Cysharp.Threading.Tasks.Linq;`, and `UniTaskAsyncEnumerable` is defined in `UniTask.Linq` asmdef.
|
||||||
|
|
||||||
It's closer to UniRx (Reactive Extensions), but UniTaskAsyncEnumerable is a pull-based asynchronous stream, whereas Rx was a push-based asynchronous stream. Note that although similar, the characteristics are different and the details behave differently along with them.
|
It's closer to UniRx (Reactive Extensions), but UniTaskAsyncEnumerable is a pull-based asynchronous stream, whereas Rx was a push-based asynchronous stream. Note that although similar, the characteristics are different and the details behave differently along with them.
|
||||||
|
|
||||||
`UniTaskAsyncEnumerable` is the entry point like `Enumerable`. In addition to the standard query operators, there are other generators for Unity such as `EveryUpdate`, `Timer`, `TimerFrame`, `Interval`, `IntervalFrame`, and `EveryValueChanged`. And also added additional UniTask original query operators like `Append`, `Prepend`, `DistinctUntilChanged`, `ToHashSet`, `Buffer`, `CombineLatest`,`Merge` `Do`, `Never`, `ForEachAsync`, `Pairwise`, `Publish`, `Queue`, `Return`, `SkipUntil`, `TakeUntil`, `SkipUntilCanceled`, `TakeUntilCanceled`, `TakeLast`, `Subscribe`.
|
`UniTaskAsyncEnumerable` is the entry point like `Enumerbale`. In addition to the standard query operators, there are other generators for Unity such as `EveryUpdate`, `Timer`, `TimerFrame`, `Interval`, `IntervalFrame`, and `EveryValueChanged`. And also added additional UniTask original query operators like `Append`, `Prepend`, `DistinctUntilChanged`, `ToHashSet`, `Buffer`, `CombineLatest`, `Do`, `Never`, `ForEachAsync`, `Pairwise`, `Publish`, `Queue`, `Return`, `SkipUntilCanceled`, `TakeUntilCanceled`, `TakeLast`.
|
||||||
|
|
||||||
The method with Func as an argument has three additional overloads, `***Await`, `***AwaitWithCancellation`.
|
The method with Func as an argument has three additional overloads, `***Await`, `***AwaitWithCancellation`.
|
||||||
|
|
||||||
|
@ -768,38 +506,6 @@ SelectAwaitWithCancellation(Func<T, CancellationToken, UniTask<TR>> selector)
|
||||||
|
|
||||||
If you want to use the `async` method inside the func, use the `***Await` or `***AwaitWithCancellation`.
|
If you want to use the `async` method inside the func, use the `***Await` or `***AwaitWithCancellation`.
|
||||||
|
|
||||||
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).
|
|
||||||
public async IAsyncEnumerable<int> MyEveryUpdate([EnumeratorCancellation]CancellationToken cancelationToken = default)
|
|
||||||
{
|
|
||||||
var frameCount = 0;
|
|
||||||
await UniTask.Yield();
|
|
||||||
while (!token.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
yield return frameCount++;
|
|
||||||
await UniTask.Yield();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// UniTaskAsyncEnumerable.Create and use `await writer.YieldAsync` instead of `yield return`.
|
|
||||||
public IUniTaskAsyncEnumerable<int> MyEveryUpdate()
|
|
||||||
{
|
|
||||||
// writer(IAsyncWriter<T>) has `YieldAsync(value)` method.
|
|
||||||
return UniTaskAsyncEnumerable.Create<int>(async (writer, token) =>
|
|
||||||
{
|
|
||||||
var frameCount = 0;
|
|
||||||
await UniTask.Yield();
|
|
||||||
while (!token.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
await writer.YieldAsync(frameCount++); // instead of `yield return`
|
|
||||||
await UniTask.Yield();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Awaitable Events
|
Awaitable Events
|
||||||
---
|
---
|
||||||
All uGUI component implements `***AsAsyncEnumerable` to convert asynchronous streams of events.
|
All uGUI component implements `***AsAsyncEnumerable` to convert asynchronous streams of events.
|
||||||
|
@ -844,13 +550,32 @@ async UniTask TripleClick(CancellationToken token)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
All MonoBehaviour message events can convert async-streams by `AsyncTriggers` that can be enabled by `using Cysharp.Threading.Tasks.Triggers;`. AsyncTrigger can be created using `GetAsync***Trigger` and triggers itself as UniTaskAsyncEnumerable.
|
All MonoBehaviour message events can convert async-streams by `AsyncTriggers` that can enable by `using Cysharp.Threading.Tasks.Triggers;`.
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
var trigger = this.GetOnCollisionEnterAsyncHandler();
|
using Cysharp.Threading.Tasks.Triggers;
|
||||||
await trigger.OnCollisionEnterAsync();
|
|
||||||
await trigger.OnCollisionEnterAsync();
|
async UniTaskVoid MonitorCollision()
|
||||||
await trigger.OnCollisionEnterAsync();
|
{
|
||||||
|
await gameObject.OnCollisionEnterAsync();
|
||||||
|
Debug.Log("Collision Enter");
|
||||||
|
/* do anything */
|
||||||
|
|
||||||
|
await gameObject.OnCollisionExitAsync();
|
||||||
|
Debug.Log("Collision Exit");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Similar as uGUI event, AsyncTrigger can get by `GetAsync***Trigger` and trigger it self is UniTaskAsyncEnumerable.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// use await multiple times, get AsyncTriggerHandler is more efficient.
|
||||||
|
using(var trigger = this.GetOnCollisionEnterAsyncHandler())
|
||||||
|
{
|
||||||
|
await OnCollisionEnterAsync();
|
||||||
|
await OnCollisionEnterAsync();
|
||||||
|
await OnCollisionEnterAsync();
|
||||||
|
}
|
||||||
|
|
||||||
// every moves.
|
// every moves.
|
||||||
await this.GetAsyncMoveTrigger().ForEachAsync(axisEventData =>
|
await this.GetAsyncMoveTrigger().ForEachAsync(axisEventData =>
|
||||||
|
@ -858,71 +583,41 @@ await this.GetAsyncMoveTrigger().ForEachAsync(axisEventData =>
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
`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).
|
`AsyncReactiveProperty`, `AsyncReadOnlyReactiveProperty` is UniTask version of UniTask's ReactiveProperty.
|
||||||
|
|
||||||
```csharp
|
`BindTo` extension method of `IUniTaskAsyncEnumerable<T>` for binding asynchronous stream values to Unity components(Text/Selectable/TMP/Text).
|
||||||
var rp = new AsyncReactiveProperty<int>(99);
|
|
||||||
|
|
||||||
// AsyncReactiveProperty itself is IUniTaskAsyncEnumerable, you can query by LINQ
|
|
||||||
rp.ForEachAsync(x =>
|
|
||||||
{
|
|
||||||
Debug.Log(x);
|
|
||||||
}, this.GetCancellationTokenOnDestroy()).Forget();
|
|
||||||
|
|
||||||
rp.Value = 10; // push 10 to all subscriber
|
|
||||||
rp.Value = 11; // push 11 to all subscriber
|
|
||||||
|
|
||||||
// WithoutCurrent ignore initial value
|
|
||||||
// BindTo bind stream value to unity components.
|
|
||||||
rp.WithoutCurrent().BindTo(this.textComponent);
|
|
||||||
|
|
||||||
await rp.WaitAsync(); // wait until next value set
|
|
||||||
|
|
||||||
// also exists ToReadOnlyAsyncReactiveProperty
|
|
||||||
var rp2 = new AsyncReactiveProperty<int>(99);
|
|
||||||
var rorp = rp.CombineLatest(rp2, (x, y) => (x, y)).ToReadOnlyAsyncReactiveProperty(CancellationToken.None);
|
|
||||||
```
|
|
||||||
|
|
||||||
A pull-type asynchronous stream does not get the next values until the asynchronous processing in the sequence is complete. This could spill data from push-type events such as buttons.
|
A pull-type asynchronous stream does not get the next values until the asynchronous processing in the sequence is complete. This could spill data from push-type events such as buttons.
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
// can not get click event during 3 seconds complete.
|
// can not get click event during 3 seconds complete.
|
||||||
await button.OnClickAsAsyncEnumerable().ForEachAwaitAsync(async x =>
|
await button.OnClickAsAsyncEnumerable().ForEachAwaitAsync()
|
||||||
{
|
{
|
||||||
await UniTask.Delay(TimeSpan.FromSeconds(3));
|
await UniTask.Delay(TimeSpan.FromSeconds(3));
|
||||||
});
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
It is useful (prevent double-click) but not useful sometimes.
|
It is useful(prevent double-click) but not useful in sometimes.
|
||||||
|
|
||||||
Using the `Queue()` method will also queue events during asynchronous processing.
|
Using `Queue()` method, which will also queue events during asynchronous processing.
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
// queued message in asynchronous processing
|
// queued message in asynchronous processing
|
||||||
await button.OnClickAsAsyncEnumerable().Queue().ForEachAwaitAsync(async x =>
|
await button.OnClickAsAsyncEnumerable().Queue().ForEachAwaitAsync()
|
||||||
{
|
{
|
||||||
await UniTask.Delay(TimeSpan.FromSeconds(3));
|
await UniTask.Delay(TimeSpan.FromSeconds(3));
|
||||||
});
|
}
|
||||||
```
|
|
||||||
|
|
||||||
Or use `Subscribe`, fire and forget style.
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
button.OnClickAsAsyncEnumerable().Subscribe(async x =>
|
|
||||||
{
|
|
||||||
await UniTask.Delay(TimeSpan.FromSeconds(3));
|
|
||||||
});
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Channel
|
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.
|
`Channel` is same as [System.Threading.Tasks.Channels](https://docs.microsoft.com/ja-jp/dotnet/api/system.threading.channels?view=netcore-3.1) that is similar as GoLang Channel.
|
||||||
|
|
||||||
Currently it only supports multiple-producer, single-consumer unbounded channels. It can create by `Channel.CreateSingleConsumerUnbounded<T>()`.
|
Currently only supports multiple-producer, single-consumer unbounded channel. It can create by `Channel.CreateSingleConsumerUnbounded<T>()`.
|
||||||
|
|
||||||
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.
|
For producer(`.Writer`), `TryWrite` to push value and `TryComplete` to complete channel. For consumer(`.Reader`), `TryRead`, `WaitToReadAsync`, `ReadAsync`, `Completion` and `ReadAllAsync` to read queued messages.
|
||||||
|
|
||||||
`ReadAllAsync` returns `IUniTaskAsyncEnumerable<T>` so query LINQ operators. Reader only allows single-consumer but uses `.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 use `.Publish()` query operator to enable multicast message. For example, make pub/sub utility.
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
public class AsyncMessageBroker<T> : IDisposable
|
public class AsyncMessageBroker<T> : IDisposable
|
||||||
|
@ -957,17 +652,9 @@ public class AsyncMessageBroker<T> : IDisposable
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
vs Awaitable
|
|
||||||
---
|
|
||||||
Unity 6 introduces the awaitable type, [Awaitable](https://docs.unity3d.com/6000.0/Documentation/ScriptReference/Awaitable.html). To put it simply, Awaitable can be considered a subset of UniTask, and in fact, Awaitable's design was influenced by UniTask. It should be able to handle PlayerLoop-based awaits, pooled Tasks, and support for cancellation with `CancellationToken` in a similar way. With its inclusion in the standard library, you may wonder whether to continue using UniTask or migrate to Awaitable. Here's a brief guide.
|
|
||||||
|
|
||||||
First, the functionality provided by Awaitable is equivalent to what coroutines offer. Instead of `yield return`, you use await; `await NextFrameAsync()` replaces `yield return null`; and there are equivalents for `WaitForSeconds` and `EndOfFrame`. However, that's the extent of it. Being coroutine-based in terms of functionality, it lacks Task-based features. In practical application development using async/await, operations like `WhenAll` are essential. Additionally, UniTask enables many frame-based operations (such as `DelayFrame`) and more flexible PlayerLoopTiming control, which are not available in Awaitable. Of course, there's no Tracker Window either.
|
|
||||||
|
|
||||||
Therefore, I recommend using UniTask for application development. UniTask is a superset of Awaitable and includes many essential features. For library development, where you want to avoid external dependencies, using Awaitable as a return type for methods would be appropriate. Awaitable can be converted to UniTask using `AsUniTask`, so there's no issue in handling Awaitable-based functionality within the UniTask library. Of course, if you don't need to worry about dependencies, using UniTask would be the best choice even for library development.
|
|
||||||
|
|
||||||
For Unit Testing
|
For Unit Testing
|
||||||
---
|
---
|
||||||
Unity's `[UnityTest]` attribute can test coroutine(IEnumerator) but can not test async. `UniTask.ToCoroutine` bridges async/await to coroutine so you can test async methods.
|
Unity's `[UnityTest]` attribute can test coroutine(IEnumerator) but can not test async. `UniTask.ToCoroutine` bridges async/await to coroutine so you can test async method.
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
[UnityTest]
|
[UnityTest]
|
||||||
|
@ -990,81 +677,11 @@ public IEnumerator DelayIgnore() => UniTask.ToCoroutine(async () =>
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
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 the alternative apis are.
|
|
||||||
|
|
||||||
Use standard type.
|
|
||||||
|
|
||||||
| .NET Type | UniTask Type |
|
|
||||||
| --- | --- |
|
|
||||||
| `IProgress<T>` | --- |
|
|
||||||
| `CancellationToken` | --- |
|
|
||||||
| `CancellationTokenSource` | --- |
|
|
||||||
|
|
||||||
Use UniTask type.
|
|
||||||
|
|
||||||
| .NET Type | UniTask Type |
|
|
||||||
| --- | --- |
|
|
||||||
| `Task`/`ValueTask` | `UniTask` |
|
|
||||||
| `Task<T>`/`ValueTask<T>` | `UniTask<T>` |
|
|
||||||
| `async void` | `async UniTaskVoid` |
|
|
||||||
| `+= async () => { }` | `UniTask.Void`, `UniTask.Action`, `UniTask.UnityAction` |
|
|
||||||
| --- | `UniTaskCompletionSource` |
|
|
||||||
| `TaskCompletionSource<T>` | `UniTaskCompletionSource<T>`/`AutoResetUniTaskCompletionSource<T>` |
|
|
||||||
| `ManualResetValueTaskSourceCore<T>` | `UniTaskCompletionSourceCore<T>` |
|
|
||||||
| `IValueTaskSource` | `IUniTaskSource` |
|
|
||||||
| `IValueTaskSource<T>` | `IUniTaskSource<T>` |
|
|
||||||
| `ValueTask.IsCompleted` | `UniTask.Status.IsCompleted()` |
|
|
||||||
| `ValueTask<T>.IsCompleted` | `UniTask<T>.Status.IsCompleted()` |
|
|
||||||
| `new Progress<T>` | `Progress.Create<T>` |
|
|
||||||
| `CancellationToken.Register(UnsafeRegister)` | `CancellationToken.RegisterWithoutCaptureExecutionContext` |
|
|
||||||
| `CancellationTokenSource.CancelAfter` | `CancellationTokenSource.CancelAfterSlim` |
|
|
||||||
| `Channel.CreateUnbounded<T>(false){ SingleReader = true }` | `Channel.CreateSingleConsumerUnbounded<T>` |
|
|
||||||
| `IAsyncEnumerable<T>` | `IUniTaskAsyncEnumerable<T>` |
|
|
||||||
| `IAsyncEnumerator<T>` | `IUniTaskAsyncEnumerator<T>` |
|
|
||||||
| `IAsyncDisposable` | `IUniTaskAsyncDisposable` |
|
|
||||||
| `Task.Delay` | `UniTask.Delay` |
|
|
||||||
| `Task.Yield` | `UniTask.Yield` |
|
|
||||||
| `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` |
|
|
||||||
| `Task.FromCanceled` | `UniTask.FromCanceled` |
|
|
||||||
| `Task.ContinueWith` | `UniTask.ContinueWith` |
|
|
||||||
| `TaskScheduler.UnobservedTaskException` | `UniTaskScheduler.UnobservedTaskException` |
|
|
||||||
|
|
||||||
Pooling Configuration
|
Pooling Configuration
|
||||||
---
|
---
|
||||||
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.
|
UniTask is aggressively caching async promise object to achive zero allocation. In default, cache all promises but you can configure `TaskPool.SetMaxPoolSize` to your value, the value indicates cache size per type. `TaskPool.GetCacheSizeInfo` returns current cached object in pool.
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
foreach (var (type, size) in TaskPool.GetCacheSizeInfo())
|
foreach (var (type, size) in TaskPool.GetCacheSizeInfo())
|
||||||
|
@ -1073,44 +690,19 @@ 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. And also currently due to IL2CPP limitation, in IL2CPP build, UniTask do boxing AsyncStateMachine when needed so sometimes exists `one` allocation.
|
||||||
---
|
|
||||||
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.
|
|
||||||
|
|
||||||
Unity supports Code Optimization option starting in 2020.1 (right, footer).
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
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 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
|
|
||||||
{
|
|
||||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
|
||||||
public static void Inject()
|
|
||||||
{
|
|
||||||
SynchronizationContext.SetSynchronizationContext(new UniTaskSynchronizationContext());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
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
|
API References
|
||||||
---
|
---
|
||||||
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).
|
UniTask's API References is hosted at [cysharp.github.io/UniTask](https://cysharp.github.io/UniTask/api/Cysharp.Threading.Tasks.html) by [DocFX](https://dotnet.github.io/docfx/) and [Cysharp/DocfXTemplate](https://github.com/Cysharp/DocfxTemplate).
|
||||||
|
|
||||||
For example, UniTask's factory methods can 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).
|
For example, UniTask's factory methods can see at [UniTask#methods](https://cysharp.github.io/UniTask/api/Cysharp.Threading.Tasks.UniTask.html#methods-1). UniTaskAsyncEnumerable's factory/extension methods can see at [UniTaskAsyncEnumerable#methods](https://cysharp.github.io/UniTask/api/Cysharp.Threading.Tasks.Linq.UniTaskAsyncEnumerable.html#methods-1).
|
||||||
|
|
||||||
UPM Package
|
UPM Package
|
||||||
---
|
---
|
||||||
### Install via git URL
|
### Install via git URL
|
||||||
|
|
||||||
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
|
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
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
@ -1118,8 +710,15 @@ Requires a version of unity that supports path query parameter for git packages
|
||||||
|
|
||||||
or add `"com.cysharp.unitask": "https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask"` to `Packages/manifest.json`.
|
or add `"com.cysharp.unitask": "https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask"` to `Packages/manifest.json`.
|
||||||
|
|
||||||
If you want to set a target version, UniTask uses the `*.*.*` release tag so you can specify a version like `#2.1.0`. For example `https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask#2.1.0`.
|
If you want to set a target version, UniTask is using `*.*.*` release tag so you can specify a version like `#2.0.13`. For example `https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask#2.0.13`.
|
||||||
|
|
||||||
|
### Install via OpenUPM
|
||||||
|
|
||||||
|
The package is available on the [openupm registry](https://openupm.com). It's recommended to install it via [openupm-cli](https://github.com/openupm/openupm-cli).
|
||||||
|
|
||||||
|
```
|
||||||
|
openupm add com.cysharp.unitask
|
||||||
|
```
|
||||||
|
|
||||||
.NET Core
|
.NET Core
|
||||||
---
|
---
|
||||||
|
@ -1127,11 +726,11 @@ For .NET Core, use NuGet.
|
||||||
|
|
||||||
> PM> Install-Package [UniTask](https://www.nuget.org/packages/UniTask)
|
> PM> Install-Package [UniTask](https://www.nuget.org/packages/UniTask)
|
||||||
|
|
||||||
UniTask of .NET Core version is a subset of Unity UniTask with PlayerLoop dependent methods removed.
|
UniTask of .NET Core version is a subset of Unity UniTask, removed PlayerLoop dependent methods.
|
||||||
|
|
||||||
It runs at higher performance than the standard Task/ValueTask, but you should be careful to ignore the ExecutionContext/SynchronizationContext when using it. `AsyncLocal` also does not work because it ignores ExecutionContext.
|
It runs at higher performance than the standard Task/ValueTask, but you should be careful to ignore the ExecutionContext/SynchronizationContext when using it. `AysncLocal` also does not work because it ignores ExecutionContext.
|
||||||
|
|
||||||
If you use UniTask internally, but provide ValueTask as an external API, you can write it like the following(Inspired by [PooledAwait](https://github.com/mgravell/PooledAwait)).
|
If you use UniTask internally, but provide ValueTask as an external API, you can write like the following(Inspired by [PooledAwait](https://github.com/mgravell/PooledAwait)).
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
public class ZeroAllocAsyncAwaitInDotNetCore
|
public class ZeroAllocAsyncAwaitInDotNetCore
|
||||||
|
@ -1164,7 +763,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.
|
.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 are equivalent to UniTask are provided as [Cysharp/ValueTaskSupplement](https://github.com/Cysharp/ValueTaskSupplement).
|
Utility methods such as WhenAll which is equivalent to UniTask are provided as [Cysharp/ValueTaskSupplement](https://github.com/Cysharp/ValueTaskSupplement).
|
||||||
|
|
||||||
License
|
License
|
||||||
---
|
---
|
||||||
|
|
1170
README_CN.md
1170
README_CN.md
File diff suppressed because it is too large
Load Diff
|
@ -1,15 +1,13 @@
|
||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio Version 17
|
# Visual Studio Version 16
|
||||||
VisualStudioVersion = 17.0.31606.5
|
VisualStudioVersion = 16.0.29613.14
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniTask.NetCoreTests", "src\UniTask.NetCoreTests\UniTask.NetCoreTests.csproj", "{B3E311A4-70D8-4131-9965-C073A99D201A}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniTask.NetCoreTests", "src\UniTask.NetCoreTests\UniTask.NetCoreTests.csproj", "{B3E311A4-70D8-4131-9965-C073A99D201A}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniTask.NetCore", "src\UniTask.NetCore\UniTask.NetCore.csproj", "{16EE20D0-7FB1-483A-8467-A5EEDBF1F5BF}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniTask.NetCore", "src\UniTask.NetCore\UniTask.NetCore.csproj", "{16EE20D0-7FB1-483A-8467-A5EEDBF1F5BF}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniTask.NetCoreSandbox", "src\UniTask.NetCoreSandbox\UniTask.NetCoreSandbox.csproj", "{3915E72E-33E0-4A14-A6D8-872702200E58}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniTask.NetCoreSandbox", "src\UniTask.NetCoreSandbox\UniTask.NetCoreSandbox.csproj", "{3915E72E-33E0-4A14-A6D8-872702200E58}"
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniTask.Analyzer", "src\UniTask.Analyzer\UniTask.Analyzer.csproj", "{0AC6F052-A255-4EE3-9E05-1C02D49AB1C2}"
|
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
@ -29,10 +27,6 @@ Global
|
||||||
{3915E72E-33E0-4A14-A6D8-872702200E58}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{3915E72E-33E0-4A14-A6D8-872702200E58}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{3915E72E-33E0-4A14-A6D8-872702200E58}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{3915E72E-33E0-4A14-A6D8-872702200E58}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{3915E72E-33E0-4A14-A6D8-872702200E58}.Release|Any CPU.Build.0 = Release|Any CPU
|
{3915E72E-33E0-4A14-A6D8-872702200E58}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{0AC6F052-A255-4EE3-9E05-1C02D49AB1C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{0AC6F052-A255-4EE3-9E05-1C02D49AB1C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{0AC6F052-A255-4EE3-9E05-1C02D49AB1C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{0AC6F052-A255-4EE3-9E05-1C02D49AB1C2}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
{
|
|
||||||
"profiles": {
|
|
||||||
"UniTask.Analyzer": {
|
|
||||||
"commandName": "DebugRoslynComponent",
|
|
||||||
"targetProject": "..\\UniTask.NetCoreSandbox\\UniTask.NetCoreSandbox.csproj"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
<PropertyGroup>
|
|
||||||
<OutputType>library</OutputType>
|
|
||||||
<TargetFramework>netstandard2.0</TargetFramework>
|
|
||||||
<LangVersion>latest</LangVersion>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<IsRoslynComponent>true</IsRoslynComponent>
|
|
||||||
<TargetsForTfmSpecificContentInPackage>$(TargetsForTfmSpecificContentInPackage);PackBuildOutputs</TargetsForTfmSpecificContentInPackage>
|
|
||||||
<IncludeBuildOutput>false</IncludeBuildOutput>
|
|
||||||
<IncludeSymbols>false</IncludeSymbols>
|
|
||||||
<SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
|
|
||||||
<DevelopmentDependency>true</DevelopmentDependency>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.2">
|
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
|
||||||
<PrivateAssets>all</PrivateAssets>
|
|
||||||
</PackageReference>
|
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.8.0" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<Target Name="PackBuildOutputs" DependsOnTargets="SatelliteDllsProjectOutputGroup;DebugSymbolsProjectOutputGroup">
|
|
||||||
<ItemGroup>
|
|
||||||
<TfmSpecificPackageFile Include="$(TargetDir)\*.dll" PackagePath="analyzers\dotnet\cs" />
|
|
||||||
<TfmSpecificPackageFile Include="@(SatelliteDllsProjectOutputGroupOutput->'%(FinalOutputPath)')" PackagePath="analyzers\dotnet\cs\%(SatelliteDllsProjectOutputGroupOutput.Culture)\" />
|
|
||||||
</ItemGroup>
|
|
||||||
</Target>
|
|
||||||
</Project>
|
|
|
@ -1,54 +0,0 @@
|
||||||
#pragma warning disable RS2008
|
|
||||||
|
|
||||||
using Microsoft.CodeAnalysis;
|
|
||||||
using Microsoft.CodeAnalysis.Diagnostics;
|
|
||||||
using Microsoft.CodeAnalysis.Operations;
|
|
||||||
using System.Collections.Immutable;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace UniTask.Analyzer
|
|
||||||
{
|
|
||||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
|
||||||
public class UniTaskAnalyzer : DiagnosticAnalyzer
|
|
||||||
{
|
|
||||||
private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(
|
|
||||||
id: "UNITASK001",
|
|
||||||
title: "UniTaskAnalyzer001: Must pass CancellationToken",
|
|
||||||
messageFormat: "Must pass CancellationToken",
|
|
||||||
category: "Usage",
|
|
||||||
defaultSeverity: DiagnosticSeverity.Error,
|
|
||||||
isEnabledByDefault: true,
|
|
||||||
description: "Pass CancellationToken or CancellationToken.None.");
|
|
||||||
|
|
||||||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(Rule); } }
|
|
||||||
|
|
||||||
public override void Initialize(AnalysisContext context)
|
|
||||||
{
|
|
||||||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
|
|
||||||
context.EnableConcurrentExecution();
|
|
||||||
|
|
||||||
context.RegisterOperationAction(AnalyzeOperation, OperationKind.Invocation);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void AnalyzeOperation(OperationAnalysisContext context)
|
|
||||||
{
|
|
||||||
var token = context.Compilation.GetTypeByMetadataName(typeof(CancellationToken).FullName);
|
|
||||||
if (token == null) return;
|
|
||||||
|
|
||||||
if (context.Operation is IInvocationOperation invocation)
|
|
||||||
{
|
|
||||||
foreach (var arg in invocation.Arguments)
|
|
||||||
{
|
|
||||||
if (arg.ArgumentKind == ArgumentKind.DefaultValue)
|
|
||||||
{
|
|
||||||
if (SymbolEqualityComparer.Default.Equals(arg.Parameter.Type, token))
|
|
||||||
{
|
|
||||||
var diagnostic = Diagnostic.Create(Rule, arg.Syntax.GetLocation());
|
|
||||||
context.ReportDiagnostic(diagnostic);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,101 +0,0 @@
|
||||||
#if !NETSTANDARD2_0
|
|
||||||
|
|
||||||
#pragma warning disable 0649
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Threading.Tasks.Sources;
|
|
||||||
|
|
||||||
namespace Cysharp.Threading.Tasks
|
|
||||||
{
|
|
||||||
public static class AsyncEnumerableExtensions
|
|
||||||
{
|
|
||||||
public static IUniTaskAsyncEnumerable<T> AsUniTaskAsyncEnumerable<T>(this IAsyncEnumerable<T> source)
|
|
||||||
{
|
|
||||||
return new AsyncEnumerableToUniTaskAsyncEnumerable<T>(source);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IAsyncEnumerable<T> AsAsyncEnumerable<T>(this IUniTaskAsyncEnumerable<T> source)
|
|
||||||
{
|
|
||||||
return new UniTaskAsyncEnumerableToAsyncEnumerable<T>(source);
|
|
||||||
}
|
|
||||||
|
|
||||||
sealed class AsyncEnumerableToUniTaskAsyncEnumerable<T> : IUniTaskAsyncEnumerable<T>
|
|
||||||
{
|
|
||||||
readonly IAsyncEnumerable<T> source;
|
|
||||||
|
|
||||||
public AsyncEnumerableToUniTaskAsyncEnumerable(IAsyncEnumerable<T> source)
|
|
||||||
{
|
|
||||||
this.source = source;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
return new Enumerator(source.GetAsyncEnumerator(cancellationToken));
|
|
||||||
}
|
|
||||||
|
|
||||||
sealed class Enumerator : IUniTaskAsyncEnumerator<T>
|
|
||||||
{
|
|
||||||
readonly IAsyncEnumerator<T> enumerator;
|
|
||||||
|
|
||||||
public Enumerator(IAsyncEnumerator<T> enumerator)
|
|
||||||
{
|
|
||||||
this.enumerator = enumerator;
|
|
||||||
}
|
|
||||||
|
|
||||||
public T Current => enumerator.Current;
|
|
||||||
|
|
||||||
public async UniTask DisposeAsync()
|
|
||||||
{
|
|
||||||
await enumerator.DisposeAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async UniTask<bool> MoveNextAsync()
|
|
||||||
{
|
|
||||||
return await enumerator.MoveNextAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sealed class UniTaskAsyncEnumerableToAsyncEnumerable<T> : IAsyncEnumerable<T>
|
|
||||||
{
|
|
||||||
readonly IUniTaskAsyncEnumerable<T> source;
|
|
||||||
|
|
||||||
public UniTaskAsyncEnumerableToAsyncEnumerable(IUniTaskAsyncEnumerable<T> source)
|
|
||||||
{
|
|
||||||
this.source = source;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
return new Enumerator(source.GetAsyncEnumerator(cancellationToken));
|
|
||||||
}
|
|
||||||
|
|
||||||
sealed class Enumerator : IAsyncEnumerator<T>
|
|
||||||
{
|
|
||||||
readonly IUniTaskAsyncEnumerator<T> enumerator;
|
|
||||||
|
|
||||||
public Enumerator(IUniTaskAsyncEnumerator<T> enumerator)
|
|
||||||
{
|
|
||||||
this.enumerator = enumerator;
|
|
||||||
}
|
|
||||||
|
|
||||||
public T Current => enumerator.Current;
|
|
||||||
|
|
||||||
public ValueTask DisposeAsync()
|
|
||||||
{
|
|
||||||
return enumerator.DisposeAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ValueTask<bool> MoveNextAsync()
|
|
||||||
{
|
|
||||||
return enumerator.MoveNextAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,11 +1,5 @@
|
||||||
#pragma warning disable 0649
|
#pragma warning disable 0649
|
||||||
|
|
||||||
#if UNITASK_NETCORE || UNITY_2022_3_OR_NEWER
|
|
||||||
#define SUPPORT_VALUETASK
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if SUPPORT_VALUETASK
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Tasks.Sources;
|
using System.Threading.Tasks.Sources;
|
||||||
|
@ -16,7 +10,7 @@ namespace Cysharp.Threading.Tasks
|
||||||
{
|
{
|
||||||
public static ValueTask AsValueTask(this in UniTask task)
|
public static ValueTask AsValueTask(this in UniTask task)
|
||||||
{
|
{
|
||||||
#if (UNITASK_NETCORE && NETSTANDARD2_0)
|
#if NETSTANDARD2_0
|
||||||
return new ValueTask(new UniTaskValueTaskSource(task), 0);
|
return new ValueTask(new UniTaskValueTaskSource(task), 0);
|
||||||
#else
|
#else
|
||||||
return task;
|
return task;
|
||||||
|
@ -25,24 +19,25 @@ namespace Cysharp.Threading.Tasks
|
||||||
|
|
||||||
public static ValueTask<T> AsValueTask<T>(this in UniTask<T> task)
|
public static ValueTask<T> AsValueTask<T>(this in UniTask<T> task)
|
||||||
{
|
{
|
||||||
#if (UNITASK_NETCORE && NETSTANDARD2_0)
|
#if NETSTANDARD2_0
|
||||||
return new ValueTask<T>(new UniTaskValueTaskSource<T>(task), 0);
|
return new ValueTask<T>(new UniTaskValueTaskSource<T>(task), 0);
|
||||||
#else
|
#else
|
||||||
return task;
|
return task;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async UniTask<T> AsUniTask<T>(this ValueTask<T> task)
|
public static UniTask<T> AsUniTask<T>(this ValueTask<T> task, bool useCurrentSynchronizationContext = true)
|
||||||
{
|
{
|
||||||
return await task;
|
// NOTE: get _obj and _token directly for low overhead conversion but not yet implemented.
|
||||||
|
return task.AsTask().AsUniTask(useCurrentSynchronizationContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async UniTask AsUniTask(this ValueTask task)
|
public static UniTask AsUniTask(this ValueTask task, bool useCurrentSynchronizationContext = true)
|
||||||
{
|
{
|
||||||
await task;
|
return task.AsTask().AsUniTask(useCurrentSynchronizationContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if (UNITASK_NETCORE && NETSTANDARD2_0)
|
#if NETSTANDARD2_0
|
||||||
|
|
||||||
class UniTaskValueTaskSource : IValueTaskSource
|
class UniTaskValueTaskSource : IValueTaskSource
|
||||||
{
|
{
|
||||||
|
@ -101,4 +96,3 @@ namespace Cysharp.Threading.Tasks
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
|
@ -62,8 +62,7 @@ namespace Cysharp.Threading.Tasks
|
||||||
sealed class ThreadPoolWorkItem : IThreadPoolWorkItem, ITaskPoolNode<ThreadPoolWorkItem>
|
sealed class ThreadPoolWorkItem : IThreadPoolWorkItem, ITaskPoolNode<ThreadPoolWorkItem>
|
||||||
{
|
{
|
||||||
static TaskPool<ThreadPoolWorkItem> pool;
|
static TaskPool<ThreadPoolWorkItem> pool;
|
||||||
ThreadPoolWorkItem nextNode;
|
public ThreadPoolWorkItem NextNode { get; set; }
|
||||||
public ref ThreadPoolWorkItem NextNode => ref nextNode;
|
|
||||||
|
|
||||||
static ThreadPoolWorkItem()
|
static ThreadPoolWorkItem()
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,13 +1,10 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net6.0;net7.0;netstandard2.1;netstandard2.0</TargetFrameworks>
|
<TargetFrameworks>netcoreapp3.1;netstandard2.1;netstandard2.0</TargetFrameworks>
|
||||||
<AssemblyName>UniTask</AssemblyName>
|
<AssemblyName>UniTask</AssemblyName>
|
||||||
<LangVersion>8.0</LangVersion>
|
<LangVersion>8.0</LangVersion>
|
||||||
<RootNamespace>Cysharp.Threading.Tasks</RootNamespace>
|
<RootNamespace>Cysharp.Threading.Tasks</RootNamespace>
|
||||||
<DefineConstants>UNITASK_NETCORE</DefineConstants>
|
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
|
||||||
<NoWarn>$(NoWarn);CS1591</NoWarn>
|
|
||||||
|
|
||||||
<!-- NuGet Packaging -->
|
<!-- NuGet Packaging -->
|
||||||
<Id>UniTask</Id>
|
<Id>UniTask</Id>
|
||||||
|
@ -22,9 +19,6 @@
|
||||||
<RepositoryType>git</RepositoryType>
|
<RepositoryType>git</RepositoryType>
|
||||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||||
<PackageIcon>Icon.png</PackageIcon>
|
<PackageIcon>Icon.png</PackageIcon>
|
||||||
<SignAssembly>true</SignAssembly>
|
|
||||||
<AssemblyOriginatorKeyFile>opensource.snk</AssemblyOriginatorKeyFile>
|
|
||||||
<IsPackable>true</IsPackable>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -32,8 +26,29 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<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 Include="..\UniTask\Assets\Plugins\UniTask\Runtime\**\*.cs"
|
||||||
<Compile Remove="..\UniTask\Assets\Plugins\UniTask\Runtime\_InternalVisibleTo.cs" />
|
Exclude="
|
||||||
|
..\UniTask\Assets\Plugins\UniTask\Editor\*.cs;
|
||||||
|
..\UniTask\Assets\Plugins\UniTask\Runtime\Triggers\*.cs;
|
||||||
|
..\UniTask\Assets\Plugins\UniTask\Runtime\Linq\UnityExtensions\*.cs;
|
||||||
|
|
||||||
|
..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\UnityEqualityComparer.cs;
|
||||||
|
..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\DiagnosticsExtensions.cs;
|
||||||
|
..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\PlayerLoopRunner.cs;
|
||||||
|
..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\ContinuationQueue.cs;
|
||||||
|
|
||||||
|
..\UniTask\Assets\Plugins\UniTask\Runtime\CancellationTokenSourceExtensions.cs;
|
||||||
|
..\UniTask\Assets\Plugins\UniTask\Runtime\EnumeratorAsyncExtensions.cs;
|
||||||
|
..\UniTask\Assets\Plugins\UniTask\Runtime\PlayerLoopHelper.cs;
|
||||||
|
..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.Delay.cs;
|
||||||
|
..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.Run.cs;
|
||||||
|
..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.Bridge.cs;
|
||||||
|
..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.WaitUntil.cs;
|
||||||
|
..\UniTask\Assets\Plugins\UniTask\Runtime\UnityAsyncExtensions.cs;
|
||||||
|
..\UniTask\Assets\Plugins\UniTask\Runtime\UnityAsyncExtensions.uGUI.cs;
|
||||||
|
..\UniTask\Assets\Plugins\UniTask\Runtime\UnityAsyncExtensions.MonoBehaviour.cs;
|
||||||
|
..\UniTask\Assets\Plugins\UniTask\Runtime\UnityBindingExtensions.cs;
|
||||||
|
" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
Binary file not shown.
|
@ -0,0 +1,258 @@
|
||||||
|
using BenchmarkDotNet.Attributes;
|
||||||
|
using System.Linq;
|
||||||
|
using BenchmarkDotNet.Configs;
|
||||||
|
using BenchmarkDotNet.Diagnosers;
|
||||||
|
using BenchmarkDotNet.Exporters;
|
||||||
|
using BenchmarkDotNet.Jobs;
|
||||||
|
using BenchmarkDotNet.Running;
|
||||||
|
using Cysharp.Threading.Tasks;
|
||||||
|
using PooledAwait;
|
||||||
|
using System;
|
||||||
|
using System.Runtime.ExceptionServices;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using Cysharp.Threading.Tasks.CompilerServices;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
|
[Config(typeof(BenchmarkConfig))]
|
||||||
|
public class AllocationCheck
|
||||||
|
{
|
||||||
|
// note: all the benchmarks use Task/Task<T> for the public API, because BenchmarkDotNet
|
||||||
|
// doesn't work reliably with more exotic task-types (even just ValueTask fails); instead,
|
||||||
|
// we'll obscure the cost of the outer awaitable by doing a relatively large number of
|
||||||
|
// iterations, so that we're only really measuring the inner loop
|
||||||
|
private const int InnerOps = 1000;
|
||||||
|
|
||||||
|
[Benchmark(OperationsPerInvoke = InnerOps)]
|
||||||
|
public async Task ViaUniTask()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < InnerOps; i++)
|
||||||
|
{
|
||||||
|
var a = Core();
|
||||||
|
var b = Core();
|
||||||
|
var c = Core();
|
||||||
|
await a;
|
||||||
|
await b;
|
||||||
|
await c;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async UniTask Core()
|
||||||
|
{
|
||||||
|
await new TestAwaiter(false, UniTaskStatus.Succeeded);
|
||||||
|
await new TestAwaiter(false, UniTaskStatus.Succeeded);
|
||||||
|
await new TestAwaiter(false, UniTaskStatus.Succeeded);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark(OperationsPerInvoke = InnerOps)]
|
||||||
|
public async Task<int> ViaUniTaskT()
|
||||||
|
{
|
||||||
|
var sum = 0;
|
||||||
|
for (int i = 0; i < InnerOps; i++)
|
||||||
|
{
|
||||||
|
var a = Core();
|
||||||
|
var b = Core();
|
||||||
|
var c = Core();
|
||||||
|
sum += await a;
|
||||||
|
sum += await b;
|
||||||
|
sum += await c;
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
|
||||||
|
static async UniTask<int> Core()
|
||||||
|
{
|
||||||
|
var a = await new TestAwaiter<int>(false, UniTaskStatus.Succeeded, 10);
|
||||||
|
var b = await new TestAwaiter<int>(false, UniTaskStatus.Succeeded, 10);
|
||||||
|
var c = await new TestAwaiter<int>(false, UniTaskStatus.Succeeded, 10);
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//[Benchmark(OperationsPerInvoke = InnerOps)]
|
||||||
|
//[Benchmark]
|
||||||
|
public void ViaUniTaskVoid()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < InnerOps; i++)
|
||||||
|
{
|
||||||
|
Core().Forget();
|
||||||
|
Core().Forget();
|
||||||
|
Core().Forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
static async UniTaskVoid Core()
|
||||||
|
{
|
||||||
|
await new TestAwaiter(false, UniTaskStatus.Succeeded);
|
||||||
|
await new TestAwaiter(false, UniTaskStatus.Succeeded);
|
||||||
|
await new TestAwaiter(false, UniTaskStatus.Succeeded);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Foo : IAsyncStateMachine
|
||||||
|
{
|
||||||
|
public AsyncUniTaskVoidMethodBuilder builder;
|
||||||
|
public TestAwaiter awaiter;
|
||||||
|
public TestAwaiter awaiterawaiter;
|
||||||
|
|
||||||
|
public int state;
|
||||||
|
|
||||||
|
public void MoveNext()
|
||||||
|
{
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case -1:
|
||||||
|
awaiterawaiter = awaiter.GetAwaiter();
|
||||||
|
if (awaiterawaiter.IsCompleted)
|
||||||
|
{
|
||||||
|
goto case 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
state = 0;
|
||||||
|
builder.AwaitUnsafeOnCompleted(ref awaiterawaiter, ref this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 0:
|
||||||
|
default:
|
||||||
|
goto END;
|
||||||
|
}
|
||||||
|
|
||||||
|
END:
|
||||||
|
builder.SetResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetStateMachine(IAsyncStateMachine stateMachine)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TaskTestException : Exception
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct TestAwaiter : ICriticalNotifyCompletion
|
||||||
|
{
|
||||||
|
readonly UniTaskStatus status;
|
||||||
|
readonly bool isCompleted;
|
||||||
|
|
||||||
|
public TestAwaiter(bool isCompleted, UniTaskStatus status)
|
||||||
|
{
|
||||||
|
this.isCompleted = isCompleted;
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TestAwaiter GetAwaiter() => this;
|
||||||
|
|
||||||
|
public bool IsCompleted => isCompleted;
|
||||||
|
|
||||||
|
public void GetResult()
|
||||||
|
{
|
||||||
|
switch (status)
|
||||||
|
{
|
||||||
|
case UniTaskStatus.Faulted:
|
||||||
|
throw new TaskTestException();
|
||||||
|
case UniTaskStatus.Canceled:
|
||||||
|
throw new OperationCanceledException();
|
||||||
|
case UniTaskStatus.Pending:
|
||||||
|
case UniTaskStatus.Succeeded:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnCompleted(Action continuation)
|
||||||
|
{
|
||||||
|
ThreadPool.UnsafeQueueUserWorkItem(ThreadPoolWorkItem.Create(continuation), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UnsafeOnCompleted(Action continuation)
|
||||||
|
{
|
||||||
|
ThreadPool.UnsafeQueueUserWorkItem(ThreadPoolWorkItem.Create(continuation), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct TestAwaiter<T> : ICriticalNotifyCompletion
|
||||||
|
{
|
||||||
|
readonly UniTaskStatus status;
|
||||||
|
readonly bool isCompleted;
|
||||||
|
readonly T value;
|
||||||
|
|
||||||
|
public TestAwaiter(bool isCompleted, UniTaskStatus status, T value)
|
||||||
|
{
|
||||||
|
this.isCompleted = isCompleted;
|
||||||
|
this.status = status;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TestAwaiter<T> GetAwaiter() => this;
|
||||||
|
|
||||||
|
public bool IsCompleted => isCompleted;
|
||||||
|
|
||||||
|
public T GetResult()
|
||||||
|
{
|
||||||
|
switch (status)
|
||||||
|
{
|
||||||
|
case UniTaskStatus.Faulted:
|
||||||
|
throw new TaskTestException();
|
||||||
|
case UniTaskStatus.Canceled:
|
||||||
|
throw new OperationCanceledException();
|
||||||
|
case UniTaskStatus.Pending:
|
||||||
|
case UniTaskStatus.Succeeded:
|
||||||
|
default:
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnCompleted(Action continuation)
|
||||||
|
{
|
||||||
|
ThreadPool.UnsafeQueueUserWorkItem(ThreadPoolWorkItem.Create(continuation), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UnsafeOnCompleted(Action continuation)
|
||||||
|
{
|
||||||
|
ThreadPool.UnsafeQueueUserWorkItem(ThreadPoolWorkItem.Create(continuation), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class ThreadPoolWorkItem : IThreadPoolWorkItem
|
||||||
|
{
|
||||||
|
public static readonly ConcurrentQueue<ThreadPoolWorkItem> pool = new ConcurrentQueue<ThreadPoolWorkItem>();
|
||||||
|
|
||||||
|
public static void CreatePoolItems(int count)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
pool.Enqueue(new ThreadPoolWorkItem());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Action continuation;
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static ThreadPoolWorkItem Create(Action continuation)
|
||||||
|
{
|
||||||
|
if (!pool.TryDequeue(out var item))
|
||||||
|
{
|
||||||
|
item = new ThreadPoolWorkItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
item.continuation = continuation;
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void Execute()
|
||||||
|
{
|
||||||
|
var call = continuation;
|
||||||
|
continuation = null;
|
||||||
|
pool.Enqueue(this);
|
||||||
|
|
||||||
|
call.Invoke();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,283 @@
|
||||||
|
using BenchmarkDotNet.Attributes;
|
||||||
|
using System.Linq;
|
||||||
|
using BenchmarkDotNet.Configs;
|
||||||
|
using BenchmarkDotNet.Diagnosers;
|
||||||
|
using BenchmarkDotNet.Exporters;
|
||||||
|
using BenchmarkDotNet.Jobs;
|
||||||
|
using BenchmarkDotNet.Running;
|
||||||
|
using Cysharp.Threading.Tasks;
|
||||||
|
using PooledAwait;
|
||||||
|
using System;
|
||||||
|
using System.Runtime.ExceptionServices;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using Cysharp.Threading.Tasks.CompilerServices;
|
||||||
|
|
||||||
|
//class Program
|
||||||
|
//{
|
||||||
|
// static void Main(string[] args)
|
||||||
|
// {
|
||||||
|
// var switcher = new BenchmarkSwitcher(new[]
|
||||||
|
// {
|
||||||
|
// typeof(StandardBenchmark)
|
||||||
|
// });
|
||||||
|
|
||||||
|
//#if DEBUG
|
||||||
|
// var b = new StandardBenchmark();
|
||||||
|
|
||||||
|
//#else
|
||||||
|
// switcher.Run(args);
|
||||||
|
//#endif
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
public class BenchmarkConfig : ManualConfig
|
||||||
|
{
|
||||||
|
public BenchmarkConfig()
|
||||||
|
{
|
||||||
|
AddDiagnoser(MemoryDiagnoser.Default);
|
||||||
|
AddJob(Job.ShortRun.WithLaunchCount(1).WithIterationCount(1).WithWarmupCount(1)/*.RunOncePerIteration()*/);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// borrowed from PooledAwait
|
||||||
|
|
||||||
|
[Config(typeof(BenchmarkConfig))]
|
||||||
|
[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)]
|
||||||
|
[CategoriesColumn]
|
||||||
|
public class ComparisonBenchmarks
|
||||||
|
{
|
||||||
|
// note: all the benchmarks use Task/Task<T> for the public API, because BenchmarkDotNet
|
||||||
|
// doesn't work reliably with more exotic task-types (even just ValueTask fails); instead,
|
||||||
|
// we'll obscure the cost of the outer awaitable by doing a relatively large number of
|
||||||
|
// iterations, so that we're only really measuring the inner loop
|
||||||
|
private const int InnerOps = 1000;
|
||||||
|
|
||||||
|
public bool ConfigureAwait { get; set; } = false;
|
||||||
|
|
||||||
|
[Benchmark(OperationsPerInvoke = InnerOps, Description = ".NET")]
|
||||||
|
[BenchmarkCategory("Task<T>")]
|
||||||
|
public async Task<int> ViaTaskT()
|
||||||
|
{
|
||||||
|
int sum = 0;
|
||||||
|
for (int i = 0; i < InnerOps; i++)
|
||||||
|
sum += await Inner(1, 2).ConfigureAwait(ConfigureAwait);
|
||||||
|
return sum;
|
||||||
|
|
||||||
|
static async Task<int> Inner(int x, int y)
|
||||||
|
{
|
||||||
|
int i = x;
|
||||||
|
await Task.Yield();
|
||||||
|
i *= y;
|
||||||
|
await Task.Yield();
|
||||||
|
return 5 * i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark(OperationsPerInvoke = InnerOps, Description = ".NET")]
|
||||||
|
[BenchmarkCategory("Task")]
|
||||||
|
public async Task ViaTask()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < InnerOps; i++)
|
||||||
|
await Inner().ConfigureAwait(ConfigureAwait);
|
||||||
|
|
||||||
|
static async Task Inner()
|
||||||
|
{
|
||||||
|
await Task.Yield();
|
||||||
|
await Task.Yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark(OperationsPerInvoke = InnerOps, Description = ".NET")]
|
||||||
|
[BenchmarkCategory("ValueTask<T>")]
|
||||||
|
public async Task<int> ViaValueTaskT()
|
||||||
|
{
|
||||||
|
int sum = 0;
|
||||||
|
for (int i = 0; i < InnerOps; i++)
|
||||||
|
sum += await Inner(1, 2).ConfigureAwait(ConfigureAwait);
|
||||||
|
return sum;
|
||||||
|
|
||||||
|
static async ValueTask<int> Inner(int x, int y)
|
||||||
|
{
|
||||||
|
int i = x;
|
||||||
|
await Task.Yield();
|
||||||
|
i *= y;
|
||||||
|
await Task.Yield();
|
||||||
|
return 5 * i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark(OperationsPerInvoke = InnerOps, Description = ".NET")]
|
||||||
|
[BenchmarkCategory("ValueTask")]
|
||||||
|
public async Task ViaValueTask()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < InnerOps; i++)
|
||||||
|
await Inner().ConfigureAwait(ConfigureAwait);
|
||||||
|
|
||||||
|
static async ValueTask Inner()
|
||||||
|
{
|
||||||
|
await Task.Yield();
|
||||||
|
await Task.Yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark(OperationsPerInvoke = InnerOps, Description = "Pooled")]
|
||||||
|
[BenchmarkCategory("ValueTask<T>")]
|
||||||
|
public async Task<int> ViaPooledValueTaskT()
|
||||||
|
{
|
||||||
|
int sum = 0;
|
||||||
|
for (int i = 0; i < InnerOps; i++)
|
||||||
|
sum += await Inner(1, 2).ConfigureAwait(ConfigureAwait);
|
||||||
|
return sum;
|
||||||
|
|
||||||
|
static async PooledValueTask<int> Inner(int x, int y)
|
||||||
|
{
|
||||||
|
int i = x;
|
||||||
|
await Task.Yield();
|
||||||
|
i *= y;
|
||||||
|
await Task.Yield();
|
||||||
|
return 5 * i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark(OperationsPerInvoke = InnerOps, Description = "Pooled")]
|
||||||
|
[BenchmarkCategory("ValueTask")]
|
||||||
|
public async Task ViaPooledValueTask()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < InnerOps; i++)
|
||||||
|
await Inner().ConfigureAwait(ConfigureAwait);
|
||||||
|
|
||||||
|
static async PooledValueTask Inner()
|
||||||
|
{
|
||||||
|
await Task.Yield();
|
||||||
|
await Task.Yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark(OperationsPerInvoke = InnerOps, Description = "Pooled")]
|
||||||
|
[BenchmarkCategory("Task<T>")]
|
||||||
|
public async Task<int> ViaPooledTaskT()
|
||||||
|
{
|
||||||
|
int sum = 0;
|
||||||
|
for (int i = 0; i < InnerOps; i++)
|
||||||
|
sum += await Inner(1, 2).ConfigureAwait(ConfigureAwait);
|
||||||
|
return sum;
|
||||||
|
|
||||||
|
static async PooledTask<int> Inner(int x, int y)
|
||||||
|
{
|
||||||
|
int i = x;
|
||||||
|
await Task.Yield();
|
||||||
|
i *= y;
|
||||||
|
await Task.Yield();
|
||||||
|
return 5 * i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark(OperationsPerInvoke = InnerOps, Description = "Pooled")]
|
||||||
|
[BenchmarkCategory("Task")]
|
||||||
|
public async Task ViaPooledTask()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < InnerOps; i++)
|
||||||
|
await Inner().ConfigureAwait(ConfigureAwait);
|
||||||
|
|
||||||
|
static async PooledTask Inner()
|
||||||
|
{
|
||||||
|
await Task.Yield();
|
||||||
|
await Task.Yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---
|
||||||
|
|
||||||
|
//[Benchmark(OperationsPerInvoke = InnerOps, Description = "UniTaskVoid")]
|
||||||
|
//[BenchmarkCategory("UniTask")]
|
||||||
|
//public async Task ViaUniTaskVoid()
|
||||||
|
//{
|
||||||
|
// for (int i = 0; i < InnerOps; i++)
|
||||||
|
// {
|
||||||
|
// await Inner();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// static async UniTaskVoid Inner()
|
||||||
|
// {
|
||||||
|
// await UniTask.Yield();
|
||||||
|
// await UniTask.Yield();
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
[Benchmark(OperationsPerInvoke = InnerOps, Description = "UniTask")]
|
||||||
|
[BenchmarkCategory("UniTask")]
|
||||||
|
public async Task ViaUniTask()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < InnerOps; i++)
|
||||||
|
{
|
||||||
|
await Inner();
|
||||||
|
}
|
||||||
|
|
||||||
|
static async UniTask Inner()
|
||||||
|
{
|
||||||
|
await UniTask.Yield();
|
||||||
|
await UniTask.Yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark(OperationsPerInvoke = InnerOps, Description = "UniTaskT")]
|
||||||
|
[BenchmarkCategory("UniTask")]
|
||||||
|
public async Task<int> ViaUniTaskT()
|
||||||
|
{
|
||||||
|
var sum = 0;
|
||||||
|
for (int i = 0; i < InnerOps; i++)
|
||||||
|
{
|
||||||
|
sum += await Inner(1, 2);
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
|
||||||
|
static async UniTask<int> Inner(int x, int y)
|
||||||
|
{
|
||||||
|
int i = x;
|
||||||
|
await UniTask.Yield();
|
||||||
|
i *= y;
|
||||||
|
await UniTask.Yield();
|
||||||
|
return 5 * i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct MyAwaiter : ICriticalNotifyCompletion
|
||||||
|
{
|
||||||
|
public MyAwaiter GetAwaiter() => this;
|
||||||
|
|
||||||
|
public bool IsCompleted => false;
|
||||||
|
|
||||||
|
public void GetResult()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnCompleted(Action continuation)
|
||||||
|
{
|
||||||
|
continuation();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UnsafeOnCompleted(Action continuation)
|
||||||
|
{
|
||||||
|
continuation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct MyTestStateMachine : IAsyncStateMachine
|
||||||
|
{
|
||||||
|
public void MoveNext()
|
||||||
|
{
|
||||||
|
//throw new NotImplementedException();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetStateMachine(IAsyncStateMachine stateMachine)
|
||||||
|
{
|
||||||
|
//throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,4 @@
|
||||||
#pragma warning disable CS1998
|
using Cysharp.Threading.Tasks;
|
||||||
|
|
||||||
using Cysharp.Threading.Tasks;
|
|
||||||
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System;
|
using System;
|
||||||
|
@ -17,34 +15,509 @@ using System.Reactive.Concurrency;
|
||||||
|
|
||||||
namespace NetCoreSandbox
|
namespace NetCoreSandbox
|
||||||
{
|
{
|
||||||
public class Program
|
public class MySyncContext : SynchronizationContext
|
||||||
{
|
{
|
||||||
static async Task Main(string[] args)
|
public MySyncContext()
|
||||||
{
|
{
|
||||||
var cts = new CancellationTokenSource();
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// OK.
|
public class TaskTestException : Exception
|
||||||
await FooAsync(10, cts.Token);
|
{
|
||||||
|
|
||||||
// NG(Compiler Error)
|
}
|
||||||
// await FooAsync(10);
|
|
||||||
|
|
||||||
|
|
||||||
|
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 FooAsync(int x, CancellationToken cancellationToken = default)
|
|
||||||
|
|
||||||
|
|
||||||
|
static async UniTask Aaa()
|
||||||
|
{
|
||||||
|
await FooBarAsync();
|
||||||
|
|
||||||
|
Console.WriteLine("FooBarAsync End");
|
||||||
|
}
|
||||||
|
|
||||||
|
static async UniTask WhereSelect()
|
||||||
|
{
|
||||||
|
await foreach (var item in UniTaskAsyncEnumerable.Range(1, 10)
|
||||||
|
.SelectAwait(async x =>
|
||||||
|
{
|
||||||
|
await UniTask.Yield();
|
||||||
|
return x;
|
||||||
|
})
|
||||||
|
.Where(x => x % 2 == 0))
|
||||||
|
{
|
||||||
|
Console.WriteLine(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static async Task Main(string[] args)
|
||||||
|
{
|
||||||
|
#if !DEBUG
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//await new AllocationCheck().ViaUniTaskVoid();
|
||||||
|
//Console.ReadLine();
|
||||||
|
BenchmarkDotNet.Running.BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);
|
||||||
|
|
||||||
|
//await new ComparisonBenchmarks().ViaUniTaskT();
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
// await new AllocationCheck().ViaUniTaskVoid();
|
||||||
|
|
||||||
|
// AsyncTest().Forge
|
||||||
|
|
||||||
|
Console.WriteLine("A?");
|
||||||
|
var a = await new ZeroAllocAsyncAwaitInDotNetCore().NanikaAsync(1, 2);
|
||||||
|
Console.WriteLine("RET:" + a);
|
||||||
|
await WhereSelect();
|
||||||
|
|
||||||
|
SynchronizationContext.SetSynchronizationContext(new MySyncContext());
|
||||||
|
|
||||||
|
await Aaa();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//AsyncTest().Forget();
|
||||||
|
|
||||||
|
// AsyncTest().Forget();
|
||||||
|
|
||||||
|
ThreadPool.SetMinThreads(100, 100);
|
||||||
|
|
||||||
|
//List<UniTask<int>> list = new List<UniTask<int>>();
|
||||||
|
for (int i = 0; i < short.MaxValue; i++)
|
||||||
|
{
|
||||||
|
//// list.Add(AsyncTest());
|
||||||
|
await YieldCore();
|
||||||
|
}
|
||||||
|
//await UniTask.WhenAll(list);
|
||||||
|
|
||||||
|
//Console.WriteLine("TOGO");
|
||||||
|
|
||||||
|
//var a = await AsyncTest();
|
||||||
|
//var b = AsyncTest();
|
||||||
|
//var c = AsyncTest();
|
||||||
|
await YieldCore();
|
||||||
|
|
||||||
|
//await b;
|
||||||
|
//await c;
|
||||||
|
|
||||||
|
|
||||||
|
//foreach (var item in Cysharp.Threading.Tasks.Internal.TaskPool.GetCacheSizeInfo())
|
||||||
|
//{
|
||||||
|
// Console.WriteLine(item);
|
||||||
|
//}
|
||||||
|
|
||||||
|
Console.ReadLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
static async UniTask YieldCore()
|
||||||
{
|
{
|
||||||
await UniTask.Yield();
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,396 @@
|
||||||
|
using BenchmarkDotNet.Attributes;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
[Config(typeof(BenchmarkConfig))]
|
||||||
|
public class QueueCheck
|
||||||
|
{
|
||||||
|
Node node1 = new Node();
|
||||||
|
Node node2 = new Node();
|
||||||
|
Queue<Node> q1 = new Queue<Node>();
|
||||||
|
Stack<Node> s1 = new Stack<Node>();
|
||||||
|
ConcurrentQueue<Node> cq = new ConcurrentQueue<Node>();
|
||||||
|
ConcurrentStack<Node> cs = new ConcurrentStack<Node>();
|
||||||
|
static TaskPool<Node> pool;
|
||||||
|
static TaskPoolEqualNull<Node> poolEqualNull;
|
||||||
|
static TaskPoolClass<Node> poolClass = new TaskPoolClass<Node>();
|
||||||
|
static TaskPoolWithoutSize<Node> poolWithoutSize;
|
||||||
|
static TaskPoolWithoutLock<Node> poolWithoutLock;
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public void Queue()
|
||||||
|
{
|
||||||
|
q1.Enqueue(node1);
|
||||||
|
q1.Enqueue(node1);
|
||||||
|
q1.TryDequeue(out _);
|
||||||
|
q1.TryDequeue(out _);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public void QueueLock()
|
||||||
|
{
|
||||||
|
lock (q1) { q1.Enqueue(node1); }
|
||||||
|
lock (q1) { q1.Enqueue(node1); }
|
||||||
|
lock (q1) { q1.TryDequeue(out _); }
|
||||||
|
lock (q1) { q1.TryDequeue(out _); }
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public void Stack()
|
||||||
|
{
|
||||||
|
s1.Push(node1);
|
||||||
|
s1.Push(node2);
|
||||||
|
s1.TryPop(out _);
|
||||||
|
s1.TryPop(out _);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public void StackLock()
|
||||||
|
{
|
||||||
|
lock (s1) { s1.Push(node1); }
|
||||||
|
lock (s1) { s1.Push(node2); }
|
||||||
|
lock (s1) { s1.TryPop(out _); }
|
||||||
|
lock (s1) { s1.TryPop(out _); }
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public void ConcurrentQueue()
|
||||||
|
{
|
||||||
|
cq.Enqueue(node1);
|
||||||
|
cq.Enqueue(node1);
|
||||||
|
cq.TryDequeue(out _);
|
||||||
|
cq.TryDequeue(out _);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public void ConcurrentStack()
|
||||||
|
{
|
||||||
|
cs.Push(node1);
|
||||||
|
cs.Push(node2);
|
||||||
|
cs.TryPop(out _);
|
||||||
|
cs.TryPop(out _);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public void TaskPool()
|
||||||
|
{
|
||||||
|
pool.TryPush(node1);
|
||||||
|
pool.TryPush(node2);
|
||||||
|
pool.TryPop(out _);
|
||||||
|
pool.TryPop(out _);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public void TaskPoolEqualNull()
|
||||||
|
{
|
||||||
|
poolEqualNull.TryPush(node1);
|
||||||
|
poolEqualNull.TryPush(node2);
|
||||||
|
poolEqualNull.TryPop(out _);
|
||||||
|
poolEqualNull.TryPop(out _);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public void TaskPoolClass()
|
||||||
|
{
|
||||||
|
poolClass.TryPush(node1);
|
||||||
|
poolClass.TryPush(node2);
|
||||||
|
poolClass.TryPop(out _);
|
||||||
|
poolClass.TryPop(out _);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public void TaskPoolWithoutSize()
|
||||||
|
{
|
||||||
|
poolWithoutSize.TryPush(node1);
|
||||||
|
poolWithoutSize.TryPush(node2);
|
||||||
|
poolWithoutSize.TryPop(out _);
|
||||||
|
poolWithoutSize.TryPop(out _);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public void TaskPoolWithoutLock()
|
||||||
|
{
|
||||||
|
poolWithoutLock.TryPush(node1);
|
||||||
|
poolWithoutLock.TryPush(node2);
|
||||||
|
poolWithoutLock.TryPop(out _);
|
||||||
|
poolWithoutLock.TryPop(out _);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class Node : ITaskPoolNode<Node>
|
||||||
|
{
|
||||||
|
public Node NextNode { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ITaskPoolNode<T>
|
||||||
|
{
|
||||||
|
T NextNode { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// mutable struct, don't mark readonly.
|
||||||
|
[StructLayout(LayoutKind.Auto)]
|
||||||
|
public struct TaskPoolWithoutLock<T>
|
||||||
|
where T : class, ITaskPoolNode<T>
|
||||||
|
{
|
||||||
|
int size;
|
||||||
|
T root;
|
||||||
|
|
||||||
|
public int Size => size;
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public bool TryPop(out T result)
|
||||||
|
{
|
||||||
|
//if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
||||||
|
{
|
||||||
|
var v = root;
|
||||||
|
if (!(v is null))
|
||||||
|
{
|
||||||
|
root = v.NextNode;
|
||||||
|
v.NextNode = null;
|
||||||
|
size--;
|
||||||
|
result = v;
|
||||||
|
// Volatile.Write(ref gate, 0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Volatile.Write(ref gate, 0);
|
||||||
|
}
|
||||||
|
result = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public bool TryPush(T item)
|
||||||
|
{
|
||||||
|
//if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
||||||
|
{
|
||||||
|
//if (size < TaskPool.MaxPoolSize)
|
||||||
|
{
|
||||||
|
item.NextNode = root;
|
||||||
|
root = item;
|
||||||
|
size++;
|
||||||
|
// Volatile.Write(ref gate, 0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//else
|
||||||
|
{
|
||||||
|
// Volatile.Write(ref gate, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Auto)]
|
||||||
|
public struct TaskPool<T>
|
||||||
|
where T : class, ITaskPoolNode<T>
|
||||||
|
{
|
||||||
|
int gate;
|
||||||
|
int size;
|
||||||
|
T root;
|
||||||
|
|
||||||
|
public int Size => size;
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public bool TryPop(out T result)
|
||||||
|
{
|
||||||
|
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
||||||
|
{
|
||||||
|
var v = root;
|
||||||
|
if (!(v is null))
|
||||||
|
{
|
||||||
|
root = v.NextNode;
|
||||||
|
v.NextNode = null;
|
||||||
|
size--;
|
||||||
|
result = v;
|
||||||
|
Volatile.Write(ref gate, 0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Volatile.Write(ref gate, 0);
|
||||||
|
}
|
||||||
|
result = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public bool TryPush(T item)
|
||||||
|
{
|
||||||
|
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
||||||
|
{
|
||||||
|
//if (size < TaskPool.MaxPoolSize)
|
||||||
|
{
|
||||||
|
item.NextNode = root;
|
||||||
|
root = item;
|
||||||
|
size++;
|
||||||
|
Volatile.Write(ref gate, 0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//else
|
||||||
|
{
|
||||||
|
// Volatile.Write(ref gate, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Auto)]
|
||||||
|
public struct TaskPoolEqualNull<T>
|
||||||
|
where T : class, ITaskPoolNode<T>
|
||||||
|
{
|
||||||
|
int gate;
|
||||||
|
int size;
|
||||||
|
T root;
|
||||||
|
|
||||||
|
public int Size => size;
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public bool TryPop(out T result)
|
||||||
|
{
|
||||||
|
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
||||||
|
{
|
||||||
|
var v = root;
|
||||||
|
if (v != null)
|
||||||
|
{
|
||||||
|
root = v.NextNode;
|
||||||
|
v.NextNode = null;
|
||||||
|
size--;
|
||||||
|
result = v;
|
||||||
|
Volatile.Write(ref gate, 0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Volatile.Write(ref gate, 0);
|
||||||
|
}
|
||||||
|
result = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public bool TryPush(T item)
|
||||||
|
{
|
||||||
|
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
||||||
|
{
|
||||||
|
//if (size < TaskPool.MaxPoolSize)
|
||||||
|
{
|
||||||
|
item.NextNode = root;
|
||||||
|
root = item;
|
||||||
|
size++;
|
||||||
|
Volatile.Write(ref gate, 0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//else
|
||||||
|
{
|
||||||
|
// Volatile.Write(ref gate, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TaskPoolClass<T>
|
||||||
|
where T : class, ITaskPoolNode<T>
|
||||||
|
{
|
||||||
|
int gate;
|
||||||
|
int size;
|
||||||
|
T root;
|
||||||
|
|
||||||
|
public int Size => size;
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public bool TryPop(out T result)
|
||||||
|
{
|
||||||
|
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
||||||
|
{
|
||||||
|
var v = root;
|
||||||
|
if (!(v is null))
|
||||||
|
{
|
||||||
|
root = v.NextNode;
|
||||||
|
v.NextNode = null;
|
||||||
|
size--;
|
||||||
|
result = v;
|
||||||
|
Volatile.Write(ref gate, 0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Volatile.Write(ref gate, 0);
|
||||||
|
}
|
||||||
|
result = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public bool TryPush(T item)
|
||||||
|
{
|
||||||
|
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
||||||
|
{
|
||||||
|
//if (size < TaskPool.MaxPoolSize)
|
||||||
|
{
|
||||||
|
item.NextNode = root;
|
||||||
|
root = item;
|
||||||
|
size++;
|
||||||
|
Volatile.Write(ref gate, 0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//else
|
||||||
|
{
|
||||||
|
// Volatile.Write(ref gate, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Auto)]
|
||||||
|
public struct TaskPoolWithoutSize<T>
|
||||||
|
where T : class, ITaskPoolNode<T>
|
||||||
|
{
|
||||||
|
int gate;
|
||||||
|
T root;
|
||||||
|
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public bool TryPop(out T result)
|
||||||
|
{
|
||||||
|
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
||||||
|
{
|
||||||
|
var v = root;
|
||||||
|
if (!(v is null))
|
||||||
|
{
|
||||||
|
root = v.NextNode;
|
||||||
|
v.NextNode = null;
|
||||||
|
result = v;
|
||||||
|
Volatile.Write(ref gate, 0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Volatile.Write(ref gate, 0);
|
||||||
|
}
|
||||||
|
result = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public bool TryPush(T item)
|
||||||
|
{
|
||||||
|
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
||||||
|
{
|
||||||
|
//if (size < TaskPool.MaxPoolSize)
|
||||||
|
{
|
||||||
|
item.NextNode = root;
|
||||||
|
root = item;
|
||||||
|
Volatile.Write(ref gate, 0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//else
|
||||||
|
{
|
||||||
|
// Volatile.Write(ref gate, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,9 +2,8 @@
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
<RootNamespace>NetCoreSandbox</RootNamespace>
|
<RootNamespace>NetCoreSandbox</RootNamespace>
|
||||||
<IsPackable>false</IsPackable>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -16,12 +15,6 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\UniTask.NetCore\UniTask.NetCore.csproj" />
|
<ProjectReference Include="..\UniTask.NetCore\UniTask.NetCore.csproj" />
|
||||||
|
|
||||||
|
|
||||||
<ProjectReference Include="..\UniTask.Analyzer\UniTask.Analyzer.csproj">
|
|
||||||
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
|
|
||||||
<OutputItemType>Analyzer</OutputItemType>
|
|
||||||
</ProjectReference>
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -1,167 +0,0 @@
|
||||||
using Cysharp.Threading.Tasks;
|
|
||||||
using FluentAssertions;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Channels;
|
|
||||||
using Cysharp.Threading.Tasks.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace NetCoreTests
|
|
||||||
{
|
|
||||||
public class AsyncLazyTest
|
|
||||||
{
|
|
||||||
[Fact]
|
|
||||||
public async Task LazyLazy()
|
|
||||||
{
|
|
||||||
{
|
|
||||||
var l = UniTask.Lazy(() => After());
|
|
||||||
var a = AwaitAwait(l.Task);
|
|
||||||
var b = AwaitAwait(l.Task);
|
|
||||||
var c = AwaitAwait(l.Task);
|
|
||||||
|
|
||||||
await a;
|
|
||||||
await b;
|
|
||||||
await c;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
var l = UniTask.Lazy(() => AfterException());
|
|
||||||
var a = AwaitAwait(l.Task);
|
|
||||||
var b = AwaitAwait(l.Task);
|
|
||||||
var c = AwaitAwait(l.Task);
|
|
||||||
|
|
||||||
await Assert.ThrowsAsync<TaskTestException>(async () => await a);
|
|
||||||
await Assert.ThrowsAsync<TaskTestException>(async () => await b);
|
|
||||||
await Assert.ThrowsAsync<TaskTestException>(async () => await c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task LazyImmediate()
|
|
||||||
{
|
|
||||||
{
|
|
||||||
var l = UniTask.Lazy(() => UniTask.FromResult(1).AsUniTask());
|
|
||||||
var a = AwaitAwait(l.Task);
|
|
||||||
var b = AwaitAwait(l.Task);
|
|
||||||
var c = AwaitAwait(l.Task);
|
|
||||||
|
|
||||||
await a;
|
|
||||||
await b;
|
|
||||||
await c;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
var l = UniTask.Lazy(() => UniTask.FromException(new TaskTestException()));
|
|
||||||
var a = AwaitAwait(l.Task);
|
|
||||||
var b = AwaitAwait(l.Task);
|
|
||||||
var c = AwaitAwait(l.Task);
|
|
||||||
|
|
||||||
await Assert.ThrowsAsync<TaskTestException>(async () => await a);
|
|
||||||
await Assert.ThrowsAsync<TaskTestException>(async () => await b);
|
|
||||||
await Assert.ThrowsAsync<TaskTestException>(async () => await c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static async UniTask AwaitAwait(UniTask t)
|
|
||||||
{
|
|
||||||
await t;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async UniTask After()
|
|
||||||
{
|
|
||||||
await UniTask.Yield();
|
|
||||||
Thread.Sleep(TimeSpan.FromSeconds(1));
|
|
||||||
await UniTask.Yield();
|
|
||||||
await UniTask.Yield();
|
|
||||||
}
|
|
||||||
|
|
||||||
async UniTask AfterException()
|
|
||||||
{
|
|
||||||
await UniTask.Yield();
|
|
||||||
Thread.Sleep(TimeSpan.FromSeconds(1));
|
|
||||||
await UniTask.Yield();
|
|
||||||
throw new TaskTestException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class AsyncLazyTest2
|
|
||||||
{
|
|
||||||
[Fact]
|
|
||||||
public async Task LazyLazy()
|
|
||||||
{
|
|
||||||
{
|
|
||||||
var l = UniTask.Lazy(() => After());
|
|
||||||
var a = AwaitAwait(l.Task);
|
|
||||||
var b = AwaitAwait(l.Task);
|
|
||||||
var c = AwaitAwait(l.Task);
|
|
||||||
|
|
||||||
var a2 = await a;
|
|
||||||
var b2 = await b;
|
|
||||||
var c2 = await c;
|
|
||||||
(a2, b2, c2).Should().Be((10, 10, 10));
|
|
||||||
}
|
|
||||||
{
|
|
||||||
var l = UniTask.Lazy(() => AfterException());
|
|
||||||
var a = AwaitAwait(l.Task);
|
|
||||||
var b = AwaitAwait(l.Task);
|
|
||||||
var c = AwaitAwait(l.Task);
|
|
||||||
|
|
||||||
await Assert.ThrowsAsync<TaskTestException>(async () => await a);
|
|
||||||
await Assert.ThrowsAsync<TaskTestException>(async () => await b);
|
|
||||||
await Assert.ThrowsAsync<TaskTestException>(async () => await c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task LazyImmediate()
|
|
||||||
{
|
|
||||||
{
|
|
||||||
var l = UniTask.Lazy(() => UniTask.FromResult(1));
|
|
||||||
var a = AwaitAwait(l.Task);
|
|
||||||
var b = AwaitAwait(l.Task);
|
|
||||||
var c = AwaitAwait(l.Task);
|
|
||||||
|
|
||||||
var a2 = await a;
|
|
||||||
var b2 = await b;
|
|
||||||
var c2 = await c;
|
|
||||||
(a2, b2, c2).Should().Be((1, 1, 1));
|
|
||||||
}
|
|
||||||
{
|
|
||||||
var l = UniTask.Lazy(() => UniTask.FromException<int>(new TaskTestException()));
|
|
||||||
var a = AwaitAwait(l.Task);
|
|
||||||
var b = AwaitAwait(l.Task);
|
|
||||||
var c = AwaitAwait(l.Task);
|
|
||||||
|
|
||||||
await Assert.ThrowsAsync<TaskTestException>(async () => await a);
|
|
||||||
await Assert.ThrowsAsync<TaskTestException>(async () => await b);
|
|
||||||
await Assert.ThrowsAsync<TaskTestException>(async () => await c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static async UniTask<int> AwaitAwait(UniTask<int> t)
|
|
||||||
{
|
|
||||||
return await t;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async UniTask<int> After()
|
|
||||||
{
|
|
||||||
await UniTask.Yield();
|
|
||||||
Thread.Sleep(TimeSpan.FromSeconds(1));
|
|
||||||
await UniTask.Yield();
|
|
||||||
await UniTask.Yield();
|
|
||||||
return 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
async UniTask<int> AfterException()
|
|
||||||
{
|
|
||||||
await UniTask.Yield();
|
|
||||||
Thread.Sleep(TimeSpan.FromSeconds(1));
|
|
||||||
await UniTask.Yield();
|
|
||||||
throw new TaskTestException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -31,7 +31,7 @@ namespace NetCoreTests
|
||||||
|
|
||||||
var ar = await array;
|
var ar = await array;
|
||||||
|
|
||||||
ar.Should().Equal(new[] { 99, 100, 100, 100, 131 });
|
ar.Should().BeEquivalentTo(new[] { 99, 100, 100, 100, 131 });
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -49,7 +49,7 @@ namespace NetCoreTests
|
||||||
|
|
||||||
var ar = await array;
|
var ar = await array;
|
||||||
|
|
||||||
ar.Should().Equal(new[] { 100, 100, 100, 131, 191 });
|
ar.Should().BeEquivalentTo(new[] { 100, 100, 100, 131, 191 });
|
||||||
}
|
}
|
||||||
|
|
||||||
//[Fact]
|
//[Fact]
|
||||||
|
@ -70,7 +70,7 @@ namespace NetCoreTests
|
||||||
|
|
||||||
// var ar = await array;
|
// var ar = await array;
|
||||||
|
|
||||||
// ar.Should().Equal(new[] { 99, 100, 100, 100, 131 });
|
// ar.Should().BeEquivalentTo(new[] { 99, 100, 100, 100, 131 });
|
||||||
//}
|
//}
|
||||||
|
|
||||||
//[Fact]
|
//[Fact]
|
||||||
|
@ -88,7 +88,7 @@ namespace NetCoreTests
|
||||||
|
|
||||||
// var ar = await array;
|
// var ar = await array;
|
||||||
|
|
||||||
// ar.Should().Equal(new[] { 100, 100, 100, 131, 191 });
|
// ar.Should().BeEquivalentTo(new[] { 100, 100, 100, 131, 191 });
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
@ -112,85 +112,6 @@ namespace NetCoreTests
|
||||||
state.Value.Should().Be(20);
|
state.Value.Should().Be(20);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task WaitAsyncTest()
|
|
||||||
{
|
|
||||||
var rp = new AsyncReactiveProperty<int>(128);
|
|
||||||
|
|
||||||
var f = await rp.FirstAsync();
|
|
||||||
f.Should().Be(128);
|
|
||||||
|
|
||||||
{
|
|
||||||
var t = rp.WaitAsync();
|
|
||||||
rp.Value = 99;
|
|
||||||
rp.Value = 100;
|
|
||||||
var v = await t;
|
|
||||||
|
|
||||||
v.Should().Be(99);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var t = rp.WaitAsync();
|
|
||||||
rp.Value = 99;
|
|
||||||
rp.Value = 100;
|
|
||||||
var v = await t;
|
|
||||||
|
|
||||||
v.Should().Be(99);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task WaitAsyncCancellationTest()
|
|
||||||
{
|
|
||||||
var cts = new CancellationTokenSource();
|
|
||||||
|
|
||||||
var rp = new AsyncReactiveProperty<int>(128);
|
|
||||||
|
|
||||||
var t = rp.WaitAsync(cts.Token);
|
|
||||||
|
|
||||||
cts.Cancel();
|
|
||||||
|
|
||||||
rp.Value = 99;
|
|
||||||
rp.Value = 100;
|
|
||||||
|
|
||||||
await Assert.ThrowsAsync<OperationCanceledException>(async () => { await t; });
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task ReadOnlyWaitAsyncTest()
|
|
||||||
{
|
|
||||||
var rp = new AsyncReactiveProperty<int>(128);
|
|
||||||
var rrp = rp.ToReadOnlyAsyncReactiveProperty(CancellationToken.None);
|
|
||||||
|
|
||||||
var t = rrp.WaitAsync();
|
|
||||||
rp.Value = 99;
|
|
||||||
rp.Value = 100;
|
|
||||||
var v = await t;
|
|
||||||
|
|
||||||
v.Should().Be(99);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task ReadOnlyWaitAsyncCancellationTest()
|
|
||||||
{
|
|
||||||
var cts = new CancellationTokenSource();
|
|
||||||
|
|
||||||
var rp = new AsyncReactiveProperty<int>(128);
|
|
||||||
var rrp = rp.ToReadOnlyAsyncReactiveProperty(CancellationToken.None);
|
|
||||||
|
|
||||||
var t = rrp.WaitAsync(cts.Token);
|
|
||||||
|
|
||||||
cts.Cancel();
|
|
||||||
|
|
||||||
rp.Value = 99;
|
|
||||||
rp.Value = 100;
|
|
||||||
|
|
||||||
await Assert.ThrowsAsync<OperationCanceledException>(async () => { await t; });
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -276,8 +276,8 @@ namespace NetCoreTests
|
||||||
reference.Writer.TryComplete();
|
reference.Writer.TryComplete();
|
||||||
channel.Writer.TryComplete();
|
channel.Writer.TryComplete();
|
||||||
|
|
||||||
(await ta1).Should().Equal(new[] { 10, 20, 30 });
|
(await ta1).Should().BeEquivalentTo(new[] { 10, 20, 30 });
|
||||||
(await ta2).Should().Equal(new[] { 10, 20, 30 });
|
(await ta2).Should().BeEquivalentTo(new[] { 10, 20, 30 });
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
|
@ -1,590 +0,0 @@
|
||||||
using Cysharp.Threading.Tasks;
|
|
||||||
using FluentAssertions;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Channels;
|
|
||||||
using Cysharp.Threading.Tasks.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace NetCoreTests
|
|
||||||
{
|
|
||||||
public class CompletionSourceTest
|
|
||||||
{
|
|
||||||
[Fact]
|
|
||||||
public async Task SetFirst()
|
|
||||||
{
|
|
||||||
{
|
|
||||||
var tcs = new UniTaskCompletionSource();
|
|
||||||
|
|
||||||
tcs.TrySetResult();
|
|
||||||
await tcs.Task; // ok.
|
|
||||||
await tcs.Task; // ok.
|
|
||||||
tcs.Task.Status.Should().Be(UniTaskStatus.Succeeded);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var tcs = new UniTaskCompletionSource();
|
|
||||||
|
|
||||||
tcs.TrySetException(new TestException());
|
|
||||||
|
|
||||||
await Assert.ThrowsAsync<TestException>(async () => await tcs.Task);
|
|
||||||
await Assert.ThrowsAsync<TestException>(async () => await tcs.Task);
|
|
||||||
|
|
||||||
tcs.Task.Status.Should().Be(UniTaskStatus.Faulted);
|
|
||||||
}
|
|
||||||
|
|
||||||
var cts = new CancellationTokenSource();
|
|
||||||
|
|
||||||
{
|
|
||||||
var tcs = new UniTaskCompletionSource();
|
|
||||||
|
|
||||||
tcs.TrySetException(new OperationCanceledException(cts.Token));
|
|
||||||
|
|
||||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
|
||||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
|
||||||
|
|
||||||
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var tcs = new UniTaskCompletionSource();
|
|
||||||
|
|
||||||
tcs.TrySetCanceled(cts.Token);
|
|
||||||
|
|
||||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
|
||||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
|
||||||
|
|
||||||
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task SingleOnFirst()
|
|
||||||
{
|
|
||||||
{
|
|
||||||
var tcs = new UniTaskCompletionSource();
|
|
||||||
|
|
||||||
async UniTask Await()
|
|
||||||
{
|
|
||||||
await tcs.Task;
|
|
||||||
}
|
|
||||||
|
|
||||||
var a = Await();
|
|
||||||
|
|
||||||
tcs.TrySetResult();
|
|
||||||
await a;
|
|
||||||
await tcs.Task; // ok.
|
|
||||||
tcs.Task.Status.Should().Be(UniTaskStatus.Succeeded);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var tcs = new UniTaskCompletionSource();
|
|
||||||
|
|
||||||
async UniTask Await()
|
|
||||||
{
|
|
||||||
await tcs.Task;
|
|
||||||
}
|
|
||||||
|
|
||||||
var a = Await();
|
|
||||||
|
|
||||||
tcs.TrySetException(new TestException());
|
|
||||||
await Assert.ThrowsAsync<TestException>(async () => await a);
|
|
||||||
await Assert.ThrowsAsync<TestException>(async () => await tcs.Task);
|
|
||||||
tcs.Task.Status.Should().Be(UniTaskStatus.Faulted);
|
|
||||||
}
|
|
||||||
|
|
||||||
var cts = new CancellationTokenSource();
|
|
||||||
|
|
||||||
{
|
|
||||||
var tcs = new UniTaskCompletionSource();
|
|
||||||
|
|
||||||
async UniTask Await()
|
|
||||||
{
|
|
||||||
await tcs.Task;
|
|
||||||
}
|
|
||||||
|
|
||||||
var a = Await();
|
|
||||||
|
|
||||||
tcs.TrySetException(new OperationCanceledException(cts.Token));
|
|
||||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
|
|
||||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
|
||||||
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
var tcs = new UniTaskCompletionSource();
|
|
||||||
|
|
||||||
async UniTask Await()
|
|
||||||
{
|
|
||||||
await tcs.Task;
|
|
||||||
}
|
|
||||||
|
|
||||||
var a = Await();
|
|
||||||
|
|
||||||
tcs.TrySetCanceled(cts.Token);
|
|
||||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
|
|
||||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
|
||||||
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task MultiOne()
|
|
||||||
{
|
|
||||||
{
|
|
||||||
var tcs = new UniTaskCompletionSource();
|
|
||||||
|
|
||||||
async UniTask Await()
|
|
||||||
{
|
|
||||||
await tcs.Task;
|
|
||||||
}
|
|
||||||
|
|
||||||
var a = Await();
|
|
||||||
var b = Await();
|
|
||||||
tcs.TrySetResult();
|
|
||||||
await a;
|
|
||||||
await b;
|
|
||||||
await tcs.Task; // ok.
|
|
||||||
tcs.Task.Status.Should().Be(UniTaskStatus.Succeeded);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var tcs = new UniTaskCompletionSource();
|
|
||||||
|
|
||||||
async UniTask Await()
|
|
||||||
{
|
|
||||||
await tcs.Task;
|
|
||||||
}
|
|
||||||
|
|
||||||
var a = Await();
|
|
||||||
var b = Await();
|
|
||||||
|
|
||||||
tcs.TrySetException(new TestException());
|
|
||||||
await Assert.ThrowsAsync<TestException>(async () => await a);
|
|
||||||
await Assert.ThrowsAsync<TestException>(async () => await b);
|
|
||||||
await Assert.ThrowsAsync<TestException>(async () => await tcs.Task);
|
|
||||||
tcs.Task.Status.Should().Be(UniTaskStatus.Faulted);
|
|
||||||
}
|
|
||||||
|
|
||||||
var cts = new CancellationTokenSource();
|
|
||||||
|
|
||||||
{
|
|
||||||
var tcs = new UniTaskCompletionSource();
|
|
||||||
|
|
||||||
async UniTask Await()
|
|
||||||
{
|
|
||||||
await tcs.Task;
|
|
||||||
}
|
|
||||||
|
|
||||||
var a = Await();
|
|
||||||
var b = Await();
|
|
||||||
|
|
||||||
tcs.TrySetException(new OperationCanceledException(cts.Token));
|
|
||||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
|
|
||||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await b)).CancellationToken.Should().Be(cts.Token);
|
|
||||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
|
||||||
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
var tcs = new UniTaskCompletionSource();
|
|
||||||
|
|
||||||
async UniTask Await()
|
|
||||||
{
|
|
||||||
await tcs.Task;
|
|
||||||
}
|
|
||||||
|
|
||||||
var a = Await();
|
|
||||||
var b = Await();
|
|
||||||
|
|
||||||
tcs.TrySetCanceled(cts.Token);
|
|
||||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
|
|
||||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await b)).CancellationToken.Should().Be(cts.Token);
|
|
||||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
|
||||||
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task MultiTwo()
|
|
||||||
{
|
|
||||||
{
|
|
||||||
var tcs = new UniTaskCompletionSource();
|
|
||||||
|
|
||||||
async UniTask Await()
|
|
||||||
{
|
|
||||||
await tcs.Task;
|
|
||||||
}
|
|
||||||
|
|
||||||
var a = Await();
|
|
||||||
var b = Await();
|
|
||||||
var c = Await();
|
|
||||||
tcs.TrySetResult();
|
|
||||||
await a;
|
|
||||||
await b;
|
|
||||||
await c;
|
|
||||||
await tcs.Task; // ok.
|
|
||||||
tcs.Task.Status.Should().Be(UniTaskStatus.Succeeded);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var tcs = new UniTaskCompletionSource();
|
|
||||||
|
|
||||||
async UniTask Await()
|
|
||||||
{
|
|
||||||
await tcs.Task;
|
|
||||||
}
|
|
||||||
|
|
||||||
var a = Await();
|
|
||||||
var b = Await();
|
|
||||||
var c = Await();
|
|
||||||
|
|
||||||
tcs.TrySetException(new TestException());
|
|
||||||
await Assert.ThrowsAsync<TestException>(async () => await a);
|
|
||||||
await Assert.ThrowsAsync<TestException>(async () => await b);
|
|
||||||
await Assert.ThrowsAsync<TestException>(async () => await c);
|
|
||||||
await Assert.ThrowsAsync<TestException>(async () => await tcs.Task);
|
|
||||||
tcs.Task.Status.Should().Be(UniTaskStatus.Faulted);
|
|
||||||
}
|
|
||||||
|
|
||||||
var cts = new CancellationTokenSource();
|
|
||||||
|
|
||||||
{
|
|
||||||
var tcs = new UniTaskCompletionSource();
|
|
||||||
|
|
||||||
async UniTask Await()
|
|
||||||
{
|
|
||||||
await tcs.Task;
|
|
||||||
}
|
|
||||||
|
|
||||||
var a = Await();
|
|
||||||
var b = Await();
|
|
||||||
var c = Await();
|
|
||||||
|
|
||||||
tcs.TrySetException(new OperationCanceledException(cts.Token));
|
|
||||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
|
|
||||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await b)).CancellationToken.Should().Be(cts.Token);
|
|
||||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await c)).CancellationToken.Should().Be(cts.Token);
|
|
||||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
|
||||||
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
var tcs = new UniTaskCompletionSource();
|
|
||||||
|
|
||||||
async UniTask Await()
|
|
||||||
{
|
|
||||||
await tcs.Task;
|
|
||||||
}
|
|
||||||
|
|
||||||
var a = Await();
|
|
||||||
var b = Await();
|
|
||||||
var c = Await();
|
|
||||||
|
|
||||||
tcs.TrySetCanceled(cts.Token);
|
|
||||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
|
|
||||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await b)).CancellationToken.Should().Be(cts.Token);
|
|
||||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await c)).CancellationToken.Should().Be(cts.Token);
|
|
||||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
|
||||||
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class TestException : Exception
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class CompletionSourceTest2
|
|
||||||
{
|
|
||||||
[Fact]
|
|
||||||
public async Task SetFirst()
|
|
||||||
{
|
|
||||||
{
|
|
||||||
var tcs = new UniTaskCompletionSource<int>();
|
|
||||||
|
|
||||||
tcs.TrySetResult(10);
|
|
||||||
var a = await tcs.Task; // ok.
|
|
||||||
var b = await tcs.Task; // ok.
|
|
||||||
a.Should().Be(10);
|
|
||||||
b.Should().Be(10);
|
|
||||||
tcs.Task.Status.Should().Be(UniTaskStatus.Succeeded);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var tcs = new UniTaskCompletionSource<int>();
|
|
||||||
|
|
||||||
tcs.TrySetException(new TestException());
|
|
||||||
|
|
||||||
await Assert.ThrowsAsync<TestException>(async () => await tcs.Task);
|
|
||||||
await Assert.ThrowsAsync<TestException>(async () => await tcs.Task);
|
|
||||||
|
|
||||||
tcs.Task.Status.Should().Be(UniTaskStatus.Faulted);
|
|
||||||
}
|
|
||||||
|
|
||||||
var cts = new CancellationTokenSource();
|
|
||||||
|
|
||||||
{
|
|
||||||
var tcs = new UniTaskCompletionSource<int>();
|
|
||||||
|
|
||||||
tcs.TrySetException(new OperationCanceledException(cts.Token));
|
|
||||||
|
|
||||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
|
||||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
|
||||||
|
|
||||||
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var tcs = new UniTaskCompletionSource<int>();
|
|
||||||
|
|
||||||
tcs.TrySetCanceled(cts.Token);
|
|
||||||
|
|
||||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
|
||||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
|
||||||
|
|
||||||
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task SingleOnFirst()
|
|
||||||
{
|
|
||||||
{
|
|
||||||
var tcs = new UniTaskCompletionSource<int>();
|
|
||||||
|
|
||||||
async UniTask<int> Await()
|
|
||||||
{
|
|
||||||
return await tcs.Task;
|
|
||||||
}
|
|
||||||
|
|
||||||
var a = Await();
|
|
||||||
|
|
||||||
tcs.TrySetResult(10);
|
|
||||||
var r1 = await a;
|
|
||||||
var r2 = await tcs.Task; // ok.
|
|
||||||
r1.Should().Be(10);
|
|
||||||
r2.Should().Be(10);
|
|
||||||
tcs.Task.Status.Should().Be(UniTaskStatus.Succeeded);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var tcs = new UniTaskCompletionSource<int>();
|
|
||||||
|
|
||||||
async UniTask<int> Await()
|
|
||||||
{
|
|
||||||
return await tcs.Task;
|
|
||||||
}
|
|
||||||
|
|
||||||
var a = Await();
|
|
||||||
|
|
||||||
tcs.TrySetException(new TestException());
|
|
||||||
await Assert.ThrowsAsync<TestException>(async () => await a);
|
|
||||||
await Assert.ThrowsAsync<TestException>(async () => await tcs.Task);
|
|
||||||
tcs.Task.Status.Should().Be(UniTaskStatus.Faulted);
|
|
||||||
}
|
|
||||||
|
|
||||||
var cts = new CancellationTokenSource();
|
|
||||||
|
|
||||||
{
|
|
||||||
var tcs = new UniTaskCompletionSource<int>();
|
|
||||||
|
|
||||||
async UniTask<int> Await()
|
|
||||||
{
|
|
||||||
return await tcs.Task;
|
|
||||||
}
|
|
||||||
|
|
||||||
var a = Await();
|
|
||||||
|
|
||||||
tcs.TrySetException(new OperationCanceledException(cts.Token));
|
|
||||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
|
|
||||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
|
||||||
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
var tcs = new UniTaskCompletionSource<int>();
|
|
||||||
|
|
||||||
async UniTask<int> Await()
|
|
||||||
{
|
|
||||||
return await tcs.Task;
|
|
||||||
}
|
|
||||||
|
|
||||||
var a = Await();
|
|
||||||
|
|
||||||
tcs.TrySetCanceled(cts.Token);
|
|
||||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
|
|
||||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
|
||||||
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task MultiOne()
|
|
||||||
{
|
|
||||||
{
|
|
||||||
var tcs = new UniTaskCompletionSource<int>();
|
|
||||||
|
|
||||||
async UniTask<int> Await()
|
|
||||||
{
|
|
||||||
return await tcs.Task;
|
|
||||||
}
|
|
||||||
|
|
||||||
var a = Await();
|
|
||||||
var b = Await();
|
|
||||||
tcs.TrySetResult(10);
|
|
||||||
var r1 = await a;
|
|
||||||
var r2 = await b;
|
|
||||||
var r3 = await tcs.Task; // ok.
|
|
||||||
(r1, r2, r3).Should().Be((10, 10, 10));
|
|
||||||
tcs.Task.Status.Should().Be(UniTaskStatus.Succeeded);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var tcs = new UniTaskCompletionSource<int>();
|
|
||||||
|
|
||||||
async UniTask<int> Await()
|
|
||||||
{
|
|
||||||
return await tcs.Task;
|
|
||||||
}
|
|
||||||
|
|
||||||
var a = Await();
|
|
||||||
var b = Await();
|
|
||||||
|
|
||||||
tcs.TrySetException(new TestException());
|
|
||||||
await Assert.ThrowsAsync<TestException>(async () => await a);
|
|
||||||
await Assert.ThrowsAsync<TestException>(async () => await b);
|
|
||||||
await Assert.ThrowsAsync<TestException>(async () => await tcs.Task);
|
|
||||||
tcs.Task.Status.Should().Be(UniTaskStatus.Faulted);
|
|
||||||
}
|
|
||||||
|
|
||||||
var cts = new CancellationTokenSource();
|
|
||||||
|
|
||||||
{
|
|
||||||
var tcs = new UniTaskCompletionSource<int>();
|
|
||||||
|
|
||||||
async UniTask<int> Await()
|
|
||||||
{
|
|
||||||
return await tcs.Task;
|
|
||||||
}
|
|
||||||
|
|
||||||
var a = Await();
|
|
||||||
var b = Await();
|
|
||||||
|
|
||||||
tcs.TrySetException(new OperationCanceledException(cts.Token));
|
|
||||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
|
|
||||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await b)).CancellationToken.Should().Be(cts.Token);
|
|
||||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
|
||||||
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
var tcs = new UniTaskCompletionSource<int>();
|
|
||||||
|
|
||||||
async UniTask<int> Await()
|
|
||||||
{
|
|
||||||
return await tcs.Task;
|
|
||||||
}
|
|
||||||
|
|
||||||
var a = Await();
|
|
||||||
var b = Await();
|
|
||||||
|
|
||||||
tcs.TrySetCanceled(cts.Token);
|
|
||||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
|
|
||||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await b)).CancellationToken.Should().Be(cts.Token);
|
|
||||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
|
||||||
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task MultiTwo()
|
|
||||||
{
|
|
||||||
{
|
|
||||||
var tcs = new UniTaskCompletionSource<int>();
|
|
||||||
|
|
||||||
async UniTask<int> Await()
|
|
||||||
{
|
|
||||||
return await tcs.Task;
|
|
||||||
}
|
|
||||||
|
|
||||||
var a = Await();
|
|
||||||
var b = Await();
|
|
||||||
var c = Await();
|
|
||||||
tcs.TrySetResult(10);
|
|
||||||
var r1 = await a;
|
|
||||||
var r2 = await b;
|
|
||||||
var r3 = await c;
|
|
||||||
var r4 = await tcs.Task; // ok.
|
|
||||||
(r1, r2, r3, r4).Should().Be((10, 10, 10, 10));
|
|
||||||
tcs.Task.Status.Should().Be(UniTaskStatus.Succeeded);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var tcs = new UniTaskCompletionSource<int>();
|
|
||||||
|
|
||||||
async UniTask<int> Await()
|
|
||||||
{
|
|
||||||
return await tcs.Task;
|
|
||||||
}
|
|
||||||
|
|
||||||
var a = Await();
|
|
||||||
var b = Await();
|
|
||||||
var c = Await();
|
|
||||||
|
|
||||||
tcs.TrySetException(new TestException());
|
|
||||||
await Assert.ThrowsAsync<TestException>(async () => await a);
|
|
||||||
await Assert.ThrowsAsync<TestException>(async () => await b);
|
|
||||||
await Assert.ThrowsAsync<TestException>(async () => await c);
|
|
||||||
await Assert.ThrowsAsync<TestException>(async () => await tcs.Task);
|
|
||||||
tcs.Task.Status.Should().Be(UniTaskStatus.Faulted);
|
|
||||||
}
|
|
||||||
|
|
||||||
var cts = new CancellationTokenSource();
|
|
||||||
|
|
||||||
{
|
|
||||||
var tcs = new UniTaskCompletionSource<int>();
|
|
||||||
|
|
||||||
async UniTask<int> Await()
|
|
||||||
{
|
|
||||||
return await tcs.Task;
|
|
||||||
}
|
|
||||||
|
|
||||||
var a = Await();
|
|
||||||
var b = Await();
|
|
||||||
var c = Await();
|
|
||||||
|
|
||||||
tcs.TrySetException(new OperationCanceledException(cts.Token));
|
|
||||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
|
|
||||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await b)).CancellationToken.Should().Be(cts.Token);
|
|
||||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await c)).CancellationToken.Should().Be(cts.Token);
|
|
||||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
|
||||||
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
var tcs = new UniTaskCompletionSource<int>();
|
|
||||||
|
|
||||||
async UniTask<int> Await()
|
|
||||||
{
|
|
||||||
return await tcs.Task;
|
|
||||||
}
|
|
||||||
|
|
||||||
var a = Await();
|
|
||||||
var b = Await();
|
|
||||||
var c = Await();
|
|
||||||
|
|
||||||
tcs.TrySetCanceled(cts.Token);
|
|
||||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
|
|
||||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await b)).CancellationToken.Should().Be(cts.Token);
|
|
||||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await c)).CancellationToken.Should().Be(cts.Token);
|
|
||||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
|
||||||
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class TestException : Exception
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -481,7 +481,7 @@ namespace NetCoreTests.Linq
|
||||||
list.Add(x);
|
list.Add(x);
|
||||||
});
|
});
|
||||||
|
|
||||||
list.Should().Equal(Enumerable.Range(1, 10));
|
list.Should().BeEquivalentTo(Enumerable.Range(1, 10));
|
||||||
|
|
||||||
var list2 = new List<(int, int)>();
|
var list2 = new List<(int, int)>();
|
||||||
await Enumerable.Range(5, 10).ToUniTaskAsyncEnumerable().ForEachAsync((index, x) =>
|
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();
|
var list3 = Enumerable.Range(5, 10).Select((index, x) => (index, x)).ToArray();
|
||||||
list2.Should().Equal(list3);
|
list2.Should().BeEquivalentTo(list3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ namespace NetCoreTests.Linq
|
||||||
var xs = await Enumerable.Range(start, count).ToUniTaskAsyncEnumerable().Append(99).ToArrayAsync();
|
var xs = await Enumerable.Range(start, count).ToUniTaskAsyncEnumerable().Append(99).ToArrayAsync();
|
||||||
var ys = Enumerable.Range(start, count).Append(99).ToArray();
|
var ys = Enumerable.Range(start, count).Append(99).ToArray();
|
||||||
|
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -50,7 +50,7 @@ namespace NetCoreTests.Linq
|
||||||
var xs = await Enumerable.Range(start, count).ToUniTaskAsyncEnumerable().Prepend(99).ToArrayAsync();
|
var xs = await Enumerable.Range(start, count).ToUniTaskAsyncEnumerable().Prepend(99).ToArrayAsync();
|
||||||
var ys = Enumerable.Range(start, count).Prepend(99).ToArray();
|
var ys = Enumerable.Range(start, count).Prepend(99).ToArray();
|
||||||
|
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -85,7 +85,7 @@ namespace NetCoreTests.Linq
|
||||||
|
|
||||||
var xs = await l.ToUniTaskAsyncEnumerable().Concat(r.ToUniTaskAsyncEnumerable()).ToArrayAsync();
|
var xs = await l.ToUniTaskAsyncEnumerable().Concat(r.ToUniTaskAsyncEnumerable()).ToArrayAsync();
|
||||||
var ys = l.Concat(r).ToArray();
|
var ys = l.Concat(r).ToArray();
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -119,17 +119,17 @@ namespace NetCoreTests.Linq
|
||||||
{
|
{
|
||||||
var xs = await Enumerable.Range(1, 0).ToUniTaskAsyncEnumerable().DefaultIfEmpty(99).ToArrayAsync();
|
var xs = await Enumerable.Range(1, 0).ToUniTaskAsyncEnumerable().DefaultIfEmpty(99).ToArrayAsync();
|
||||||
var ys = Enumerable.Range(1, 0).DefaultIfEmpty(99).ToArray();
|
var ys = Enumerable.Range(1, 0).DefaultIfEmpty(99).ToArray();
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var xs = await Enumerable.Range(1, 1).ToUniTaskAsyncEnumerable().DefaultIfEmpty(99).ToArrayAsync();
|
var xs = await Enumerable.Range(1, 1).ToUniTaskAsyncEnumerable().DefaultIfEmpty(99).ToArrayAsync();
|
||||||
var ys = Enumerable.Range(1, 1).DefaultIfEmpty(99).ToArray();
|
var ys = Enumerable.Range(1, 1).DefaultIfEmpty(99).ToArray();
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var xs = await Enumerable.Range(1, 10).ToUniTaskAsyncEnumerable().DefaultIfEmpty(99).ToArrayAsync();
|
var xs = await Enumerable.Range(1, 10).ToUniTaskAsyncEnumerable().DefaultIfEmpty(99).ToArrayAsync();
|
||||||
var ys = Enumerable.Range(1, 10).DefaultIfEmpty(99).ToArray();
|
var ys = Enumerable.Range(1, 10).DefaultIfEmpty(99).ToArray();
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
// Throw
|
// Throw
|
||||||
{
|
{
|
||||||
|
|
|
@ -34,11 +34,11 @@ namespace NetCoreTests.Linq
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
var xs = await UniTaskAsyncEnumerable.Range(1, 10).ToObservable().ToArray();
|
var xs = await UniTaskAsyncEnumerable.Range(1, 10).ToObservable().ToArray();
|
||||||
xs.Should().Equal(Enumerable.Range(1, 10));
|
xs.Should().BeEquivalentTo(Enumerable.Range(1, 10));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var xs = await UniTaskAsyncEnumerable.Range(1, 0).ToObservable().ToArray();
|
var xs = await UniTaskAsyncEnumerable.Range(1, 0).ToObservable().ToArray();
|
||||||
xs.Should().Equal(Enumerable.Range(1, 0));
|
xs.Should().BeEquivalentTo(Enumerable.Range(1, 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,21 +70,21 @@ namespace NetCoreTests.Linq
|
||||||
var xs = await Observable.Range(1, 100).ToUniTaskAsyncEnumerable().ToArrayAsync();
|
var xs = await Observable.Range(1, 100).ToUniTaskAsyncEnumerable().ToArrayAsync();
|
||||||
var ys = await Observable.Range(1, 100).ToArray();
|
var ys = await Observable.Range(1, 100).ToArray();
|
||||||
|
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var xs = await Observable.Range(1, 100, ThreadPoolScheduler.Instance).ToUniTaskAsyncEnumerable().ToArrayAsync();
|
var xs = await Observable.Range(1, 100, ThreadPoolScheduler.Instance).ToUniTaskAsyncEnumerable().ToArrayAsync();
|
||||||
var ys = await Observable.Range(1, 100, ThreadPoolScheduler.Instance).ToArray();
|
var ys = await Observable.Range(1, 100, ThreadPoolScheduler.Instance).ToArray();
|
||||||
|
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var xs = await Observable.Empty<int>(ThreadPoolScheduler.Instance).ToUniTaskAsyncEnumerable().ToArrayAsync();
|
var xs = await Observable.Empty<int>(ThreadPoolScheduler.Instance).ToUniTaskAsyncEnumerable().ToArrayAsync();
|
||||||
var ys = await Observable.Empty<int>(ThreadPoolScheduler.Instance).ToArray();
|
var ys = await Observable.Empty<int>(ThreadPoolScheduler.Instance).ToArray();
|
||||||
|
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,25 +95,25 @@ namespace NetCoreTests.Linq
|
||||||
var xs = await Enumerable.Range(1, 100).ToUniTaskAsyncEnumerable().ToDictionaryAsync(x => x);
|
var xs = await Enumerable.Range(1, 100).ToUniTaskAsyncEnumerable().ToDictionaryAsync(x => x);
|
||||||
var ys = Enumerable.Range(1, 100).ToDictionary(x => x);
|
var ys = Enumerable.Range(1, 100).ToDictionary(x => x);
|
||||||
|
|
||||||
xs.OrderBy(x => x.Key).Should().Equal(ys.OrderBy(x => x.Key));
|
xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var xs = await Enumerable.Range(1, 0).ToUniTaskAsyncEnumerable().ToDictionaryAsync(x => x);
|
var xs = await Enumerable.Range(1, 0).ToUniTaskAsyncEnumerable().ToDictionaryAsync(x => x);
|
||||||
var ys = Enumerable.Range(1, 0).ToDictionary(x => x);
|
var ys = Enumerable.Range(1, 0).ToDictionary(x => x);
|
||||||
|
|
||||||
xs.OrderBy(x => x.Key).Should().Equal(ys.OrderBy(x => x.Key));
|
xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var xs = await Enumerable.Range(1, 100).ToUniTaskAsyncEnumerable().ToDictionaryAsync(x => x, x => x * 2);
|
var 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);
|
var ys = Enumerable.Range(1, 100).ToDictionary(x => x, x => x * 2);
|
||||||
|
|
||||||
xs.OrderBy(x => x.Key).Should().Equal(ys.OrderBy(x => x.Key));
|
xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var xs = await Enumerable.Range(1, 0).ToUniTaskAsyncEnumerable().ToDictionaryAsync(x => x, x => x * 2);
|
var 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);
|
var ys = Enumerable.Range(1, 0).ToDictionary(x => x, x => x * 2);
|
||||||
|
|
||||||
xs.OrderBy(x => x.Key).Should().Equal(ys.OrderBy(x => x.Key));
|
xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,34 +126,34 @@ namespace NetCoreTests.Linq
|
||||||
var ys = arr.ToLookup(x => x);
|
var ys = arr.ToLookup(x => x);
|
||||||
|
|
||||||
xs.Count.Should().Be(ys.Count);
|
xs.Count.Should().Be(ys.Count);
|
||||||
xs.Should().BeEquivalentTo(ys);
|
xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key));
|
||||||
foreach (var key in xs.Select(x => x.Key))
|
foreach (var key in xs.Select(x => x.Key))
|
||||||
{
|
{
|
||||||
xs[key].Should().Equal(ys[key]);
|
xs[key].Should().BeEquivalentTo(ys[key]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var xs = await Enumerable.Range(1, 0).ToUniTaskAsyncEnumerable().ToLookupAsync(x => x);
|
var xs = await Enumerable.Range(1, 0).ToUniTaskAsyncEnumerable().ToLookupAsync(x => x);
|
||||||
var ys = Enumerable.Range(1, 0).ToLookup(x => x);
|
var ys = Enumerable.Range(1, 0).ToLookup(x => x);
|
||||||
|
|
||||||
xs.Should().BeEquivalentTo(ys);
|
xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var xs = await arr.ToUniTaskAsyncEnumerable().ToLookupAsync(x => x, x => x * 2);
|
var xs = await arr.ToUniTaskAsyncEnumerable().ToLookupAsync(x => x, x => x * 2);
|
||||||
var ys = arr.ToLookup(x => x, x => x * 2);
|
var ys = arr.ToLookup(x => x, x => x * 2);
|
||||||
|
|
||||||
xs.Count.Should().Be(ys.Count);
|
xs.Count.Should().Be(ys.Count);
|
||||||
xs.Should().BeEquivalentTo(ys);
|
xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key));
|
||||||
foreach (var key in xs.Select(x => x.Key))
|
foreach (var key in xs.Select(x => x.Key))
|
||||||
{
|
{
|
||||||
xs[key].Should().Equal(ys[key]);
|
xs[key].Should().BeEquivalentTo(ys[key]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var xs = await Enumerable.Range(1, 0).ToUniTaskAsyncEnumerable().ToLookupAsync(x => x, x => x * 2);
|
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);
|
var ys = Enumerable.Range(1, 0).ToLookup(x => x, x => x * 2);
|
||||||
|
|
||||||
xs.Should().BeEquivalentTo(ys);
|
xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,13 +164,13 @@ namespace NetCoreTests.Linq
|
||||||
var xs = await Enumerable.Range(1, 100).ToUniTaskAsyncEnumerable().ToListAsync();
|
var xs = await Enumerable.Range(1, 100).ToUniTaskAsyncEnumerable().ToListAsync();
|
||||||
var ys = Enumerable.Range(1, 100).ToList();
|
var ys = Enumerable.Range(1, 100).ToList();
|
||||||
|
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var xs = await Enumerable.Empty<int>().ToUniTaskAsyncEnumerable().ToListAsync();
|
var xs = await Enumerable.Empty<int>().ToUniTaskAsyncEnumerable().ToListAsync();
|
||||||
var ys = Enumerable.Empty<int>().ToList();
|
var ys = Enumerable.Empty<int>().ToList();
|
||||||
|
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,13 +181,13 @@ namespace NetCoreTests.Linq
|
||||||
var xs = await new[] { 1, 20, 4, 5, 20, 4, 6 }.ToUniTaskAsyncEnumerable().ToHashSetAsync();
|
var xs = await new[] { 1, 20, 4, 5, 20, 4, 6 }.ToUniTaskAsyncEnumerable().ToHashSetAsync();
|
||||||
var ys = new[] { 1, 20, 4, 5, 20, 4, 6 }.ToHashSet();
|
var ys = new[] { 1, 20, 4, 5, 20, 4, 6 }.ToHashSet();
|
||||||
|
|
||||||
xs.OrderBy(x => x).Should().Equal(ys.OrderBy(x => x));
|
xs.OrderBy(x => x).Should().BeEquivalentTo(ys.OrderBy(x => x));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var xs = await Enumerable.Empty<int>().ToUniTaskAsyncEnumerable().ToHashSetAsync();
|
var xs = await Enumerable.Empty<int>().ToUniTaskAsyncEnumerable().ToHashSetAsync();
|
||||||
var ys = Enumerable.Empty<int>().ToHashSet();
|
var ys = Enumerable.Empty<int>().ToHashSet();
|
||||||
|
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,194 +0,0 @@
|
||||||
#pragma warning disable CS1998
|
|
||||||
#pragma warning disable CS0162
|
|
||||||
|
|
||||||
using Cysharp.Threading.Tasks;
|
|
||||||
using Cysharp.Threading.Tasks.Linq;
|
|
||||||
using FluentAssertions;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace NetCoreTests.Linq
|
|
||||||
{
|
|
||||||
public class CreateTest
|
|
||||||
{
|
|
||||||
[Fact]
|
|
||||||
public async Task SyncCreation()
|
|
||||||
{
|
|
||||||
var from = 10;
|
|
||||||
var count = 100;
|
|
||||||
|
|
||||||
var xs = await UniTaskAsyncEnumerable.Create<int>(async (writer, token) =>
|
|
||||||
{
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
await writer.YieldAsync(from + i);
|
|
||||||
}
|
|
||||||
}).ToArrayAsync();
|
|
||||||
|
|
||||||
var ys = await Range(from, count).AsUniTaskAsyncEnumerable().ToArrayAsync();
|
|
||||||
|
|
||||||
xs.Should().Equal(ys);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task SyncManually()
|
|
||||||
{
|
|
||||||
var list = new List<int>();
|
|
||||||
var xs = UniTaskAsyncEnumerable.Create<int>(async (writer, token) =>
|
|
||||||
{
|
|
||||||
list.Add(100);
|
|
||||||
await writer.YieldAsync(10);
|
|
||||||
|
|
||||||
list.Add(200);
|
|
||||||
await writer.YieldAsync(20);
|
|
||||||
|
|
||||||
list.Add(300);
|
|
||||||
await writer.YieldAsync(30);
|
|
||||||
|
|
||||||
list.Add(400);
|
|
||||||
});
|
|
||||||
|
|
||||||
list.Should().BeEmpty();
|
|
||||||
var e = xs.GetAsyncEnumerator();
|
|
||||||
|
|
||||||
list.Should().BeEmpty();
|
|
||||||
|
|
||||||
await e.MoveNextAsync();
|
|
||||||
list.Should().Equal(100);
|
|
||||||
e.Current.Should().Be(10);
|
|
||||||
|
|
||||||
await e.MoveNextAsync();
|
|
||||||
list.Should().Equal(100, 200);
|
|
||||||
e.Current.Should().Be(20);
|
|
||||||
|
|
||||||
await e.MoveNextAsync();
|
|
||||||
list.Should().Equal(100, 200, 300);
|
|
||||||
e.Current.Should().Be(30);
|
|
||||||
|
|
||||||
(await e.MoveNextAsync()).Should().BeFalse();
|
|
||||||
list.Should().Equal(100, 200, 300, 400);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task SyncExceptionFirst()
|
|
||||||
{
|
|
||||||
var from = 10;
|
|
||||||
var count = 100;
|
|
||||||
|
|
||||||
var xs = UniTaskAsyncEnumerable.Create<int>(async (writer, token) =>
|
|
||||||
{
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
throw new UniTaskTestException();
|
|
||||||
await writer.YieldAsync(from + i);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs.ToArrayAsync());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task SyncException()
|
|
||||||
{
|
|
||||||
var from = 10;
|
|
||||||
var count = 100;
|
|
||||||
|
|
||||||
var xs = UniTaskAsyncEnumerable.Create<int>(async (writer, token) =>
|
|
||||||
{
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
await writer.YieldAsync(from + i);
|
|
||||||
|
|
||||||
if (i == 15)
|
|
||||||
{
|
|
||||||
throw new UniTaskTestException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs.ToArrayAsync());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task ASyncManually()
|
|
||||||
{
|
|
||||||
var list = new List<int>();
|
|
||||||
var xs = UniTaskAsyncEnumerable.Create<int>(async (writer, token) =>
|
|
||||||
{
|
|
||||||
await UniTask.Yield();
|
|
||||||
|
|
||||||
list.Add(100);
|
|
||||||
await writer.YieldAsync(10);
|
|
||||||
|
|
||||||
await UniTask.Yield();
|
|
||||||
|
|
||||||
list.Add(200);
|
|
||||||
await writer.YieldAsync(20);
|
|
||||||
|
|
||||||
await UniTask.Yield();
|
|
||||||
list.Add(300);
|
|
||||||
await UniTask.Yield();
|
|
||||||
await writer.YieldAsync(30);
|
|
||||||
|
|
||||||
await UniTask.Yield();
|
|
||||||
|
|
||||||
list.Add(400);
|
|
||||||
});
|
|
||||||
|
|
||||||
list.Should().BeEmpty();
|
|
||||||
var e = xs.GetAsyncEnumerator();
|
|
||||||
|
|
||||||
list.Should().BeEmpty();
|
|
||||||
|
|
||||||
await e.MoveNextAsync();
|
|
||||||
list.Should().Equal(100);
|
|
||||||
e.Current.Should().Be(10);
|
|
||||||
|
|
||||||
await e.MoveNextAsync();
|
|
||||||
list.Should().Equal(100, 200);
|
|
||||||
e.Current.Should().Be(20);
|
|
||||||
|
|
||||||
await e.MoveNextAsync();
|
|
||||||
list.Should().Equal(100, 200, 300);
|
|
||||||
e.Current.Should().Be(30);
|
|
||||||
|
|
||||||
(await e.MoveNextAsync()).Should().BeFalse();
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
yield return from + i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -22,7 +22,7 @@ namespace NetCoreTests.Linq
|
||||||
var xs = await UniTaskAsyncEnumerable.Range(start, count).ToArrayAsync();
|
var xs = await UniTaskAsyncEnumerable.Range(start, count).ToArrayAsync();
|
||||||
var ys = Enumerable.Range(start, count).ToArray();
|
var ys = Enumerable.Range(start, count).ToArray();
|
||||||
|
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
@ -36,7 +36,7 @@ namespace NetCoreTests.Linq
|
||||||
var xs = await UniTaskAsyncEnumerable.Repeat(value, count).ToArrayAsync();
|
var xs = await UniTaskAsyncEnumerable.Repeat(value, count).ToArrayAsync();
|
||||||
var ys = Enumerable.Repeat(value, count).ToArray();
|
var ys = Enumerable.Repeat(value, count).ToArray();
|
||||||
|
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -45,7 +45,7 @@ namespace NetCoreTests.Linq
|
||||||
var xs = await UniTaskAsyncEnumerable.Empty<int>().ToArrayAsync();
|
var xs = await UniTaskAsyncEnumerable.Empty<int>().ToArrayAsync();
|
||||||
var ys = Enumerable.Empty<int>().ToArray();
|
var ys = Enumerable.Empty<int>().ToArray();
|
||||||
|
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
|
|
@ -23,26 +23,26 @@ namespace NetCoreTests.Linq
|
||||||
{
|
{
|
||||||
var a = await src.Where(x => x % 2 == 0).ToArrayAsync();
|
var a = await src.Where(x => x % 2 == 0).ToArrayAsync();
|
||||||
var expected = range.Where(x => x % 2 == 0).ToArray();
|
var expected = range.Where(x => x % 2 == 0).ToArray();
|
||||||
a.Should().Equal(expected);
|
a.Should().BeEquivalentTo(expected);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var a = await src.Where((x, i) => (x + i) % 2 == 0).ToArrayAsync();
|
var a = await src.Where((x, i) => (x + i) % 2 == 0).ToArrayAsync();
|
||||||
var expected = range.Where((x, i) => (x + i) % 2 == 0).ToArray();
|
var expected = range.Where((x, i) => (x + i) % 2 == 0).ToArray();
|
||||||
a.Should().Equal(expected);
|
a.Should().BeEquivalentTo(expected);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var a = await src.WhereAwait(x => UniTask.Run(() => x % 2 == 0)).ToArrayAsync();
|
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 b = await src.WhereAwait(x => UniTask.FromResult(x % 2 == 0)).ToArrayAsync();
|
||||||
var expected = range.Where(x => x % 2 == 0).ToArray();
|
var expected = range.Where(x => x % 2 == 0).ToArray();
|
||||||
a.Should().Equal(expected);
|
a.Should().BeEquivalentTo(expected);
|
||||||
b.Should().Equal(expected);
|
b.Should().BeEquivalentTo(expected);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var a = await src.WhereAwait((x, i) => UniTask.Run(() => (x + i) % 2 == 0)).ToArrayAsync();
|
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 b = await src.WhereAwait((x, i) => UniTask.FromResult((x + i) % 2 == 0)).ToArrayAsync();
|
||||||
var expected = range.Where((x, i) => (x + i) % 2 == 0).ToArray();
|
var expected = range.Where((x, i) => (x + i) % 2 == 0).ToArray();
|
||||||
a.Should().Equal(expected);
|
a.Should().BeEquivalentTo(expected);
|
||||||
b.Should().Equal(expected);
|
b.Should().BeEquivalentTo(expected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ namespace NetCoreTests.Linq
|
||||||
var a = await data.ToUniTaskAsyncEnumerable().OfType<int>().ToArrayAsync();
|
var a = await data.ToUniTaskAsyncEnumerable().OfType<int>().ToArrayAsync();
|
||||||
var b = data.OfType<int>().ToArray();
|
var b = data.OfType<int>().ToArray();
|
||||||
|
|
||||||
a.Should().Equal(b);
|
a.Should().BeEquivalentTo(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ namespace NetCoreTests.Linq
|
||||||
var a = await data.ToUniTaskAsyncEnumerable().Cast<int>().ToArrayAsync();
|
var a = await data.ToUniTaskAsyncEnumerable().Cast<int>().ToArrayAsync();
|
||||||
var b = data.Cast<int>().ToArray();
|
var b = data.Cast<int>().ToArray();
|
||||||
|
|
||||||
a.Should().Equal(b);
|
a.Should().BeEquivalentTo(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 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();
|
var ys = outer.Join(inner, x => x, x => x, (x, y) => (x, y)).ToArray();
|
||||||
|
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(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 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();
|
var ys = outer.Join(inner, x => x, x => x, (x, y) => (x, y)).ToArray();
|
||||||
|
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(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 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();
|
var ys = outer.Join(inner, x => x, x => x, (x, y) => (x, y)).ToArray();
|
||||||
|
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -123,7 +123,7 @@ namespace NetCoreTests.Linq
|
||||||
var ys = arr.GroupBy(x => x).ToArray();
|
var ys = arr.GroupBy(x => x).ToArray();
|
||||||
|
|
||||||
xs.Length.Should().Be(ys.Length);
|
xs.Length.Should().Be(ys.Length);
|
||||||
xs.Should().BeEquivalentTo(ys);
|
xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -131,7 +131,7 @@ namespace NetCoreTests.Linq
|
||||||
var ys = arr.GroupBy(x => x, (key, xs) => (key, xs.ToArray())).ToArray();
|
var ys = arr.GroupBy(x => x, (key, xs) => (key, xs.ToArray())).ToArray();
|
||||||
|
|
||||||
xs.Length.Should().Be(ys.Length);
|
xs.Length.Should().Be(ys.Length);
|
||||||
xs.OrderBy(x => x.key).SelectMany(x => x.Item2).Should().Equal(ys.OrderBy(x => x.key).SelectMany(x => x.Item2));
|
xs.OrderBy(x => x.key).SelectMany(x => x.Item2).Should().BeEquivalentTo(ys.OrderBy(x => x.key).SelectMany(x => x.Item2));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -139,7 +139,7 @@ namespace NetCoreTests.Linq
|
||||||
var ys = arr.GroupBy(x => x).ToArray();
|
var ys = arr.GroupBy(x => x).ToArray();
|
||||||
|
|
||||||
xs.Length.Should().Be(ys.Length);
|
xs.Length.Should().Be(ys.Length);
|
||||||
xs.Should().BeEquivalentTo(ys);
|
xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -147,7 +147,7 @@ namespace NetCoreTests.Linq
|
||||||
var ys = arr.GroupBy(x => x, (key, xs) => (key, xs.ToArray())).ToArray();
|
var ys = arr.GroupBy(x => x, (key, xs) => (key, xs.ToArray())).ToArray();
|
||||||
|
|
||||||
xs.Length.Should().Be(ys.Length);
|
xs.Length.Should().Be(ys.Length);
|
||||||
xs.OrderBy(x => x.key).SelectMany(x => x.Item2).Should().Equal(ys.OrderBy(x => x.key).SelectMany(x => x.Item2));
|
xs.OrderBy(x => x.key).SelectMany(x => x.Item2).Should().BeEquivalentTo(ys.OrderBy(x => x.key).SelectMany(x => x.Item2));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -155,7 +155,7 @@ namespace NetCoreTests.Linq
|
||||||
var ys = arr.GroupBy(x => x).ToArray();
|
var ys = arr.GroupBy(x => x).ToArray();
|
||||||
|
|
||||||
xs.Length.Should().Be(ys.Length);
|
xs.Length.Should().Be(ys.Length);
|
||||||
xs.Should().BeEquivalentTo(ys);
|
xs.OrderBy(x => x.Key).Should().BeEquivalentTo(ys.OrderBy(x => x.Key));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -163,7 +163,7 @@ namespace NetCoreTests.Linq
|
||||||
var ys = arr.GroupBy(x => x, (key, xs) => (key, xs.ToArray())).ToArray();
|
var ys = arr.GroupBy(x => x, (key, xs) => (key, xs.ToArray())).ToArray();
|
||||||
|
|
||||||
xs.Length.Should().Be(ys.Length);
|
xs.Length.Should().Be(ys.Length);
|
||||||
xs.OrderBy(x => x.key).SelectMany(x => x.Item2).Should().Equal(ys.OrderBy(x => x.key).SelectMany(x => x.Item2));
|
xs.OrderBy(x => x.key).SelectMany(x => x.Item2).Should().BeEquivalentTo(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();
|
var ys = outer.GroupJoin(inner, x => x, x => x, (x, y) => (x, string.Join(", ", y))).ToArray();
|
||||||
|
|
||||||
xs.Length.Should().Be(ys.Length);
|
xs.Length.Should().Be(ys.Length);
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var xs = await outer.ToUniTaskAsyncEnumerable().GroupJoinAwait(inner.ToUniTaskAsyncEnumerable(), x => RandomRun(x), x => RandomRun(x), (x, y) => RandomRun((x, string.Join(", ", y)))).ToArrayAsync();
|
var 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();
|
var ys = outer.GroupJoin(inner, x => x, x => x, (x, y) => (x, string.Join(", ", y))).ToArray();
|
||||||
|
|
||||||
xs.Length.Should().Be(ys.Length);
|
xs.Length.Should().Be(ys.Length);
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var xs = await outer.ToUniTaskAsyncEnumerable().GroupJoinAwaitWithCancellation(inner.ToUniTaskAsyncEnumerable(), (x, _) => RandomRun(x), (x, _) => RandomRun(x), (x, y, _) => RandomRun((x, string.Join(", ", y)))).ToArrayAsync();
|
var 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();
|
var ys = outer.GroupJoin(inner, x => x, x => x, (x, y) => (x, string.Join(", ", y))).ToArray();
|
||||||
|
|
||||||
xs.Length.Should().Be(ys.Length);
|
xs.Length.Should().Be(ys.Length);
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,139 +0,0 @@
|
||||||
#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 xs = await UniTaskAsyncEnumerable.Range(1, collection).Skip(skipCount).ToArrayAsync();
|
||||||
var ys = Enumerable.Range(1, collection).Skip(skipCount).ToArray();
|
var ys = Enumerable.Range(1, collection).Skip(skipCount).ToArray();
|
||||||
|
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -52,7 +52,7 @@ namespace NetCoreTests.Linq
|
||||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipLast(skipCount).ToArrayAsync();
|
var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipLast(skipCount).ToArrayAsync();
|
||||||
var ys = Enumerable.Range(1, collection).SkipLast(skipCount).ToArray();
|
var ys = Enumerable.Range(1, collection).SkipLast(skipCount).ToArray();
|
||||||
|
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -77,7 +77,7 @@ namespace NetCoreTests.Linq
|
||||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeLast(takeCount).ToArrayAsync();
|
var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeLast(takeCount).ToArrayAsync();
|
||||||
var ys = Enumerable.Range(1, collection).TakeLast(takeCount).ToArray();
|
var ys = Enumerable.Range(1, collection).TakeLast(takeCount).ToArray();
|
||||||
|
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -103,7 +103,7 @@ namespace NetCoreTests.Linq
|
||||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).Take(takeCount).ToArrayAsync();
|
var xs = await UniTaskAsyncEnumerable.Range(1, collection).Take(takeCount).ToArrayAsync();
|
||||||
var ys = Enumerable.Range(1, collection).Take(takeCount).ToArray();
|
var ys = Enumerable.Range(1, collection).Take(takeCount).ToArray();
|
||||||
|
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -130,37 +130,37 @@ namespace NetCoreTests.Linq
|
||||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipWhile(x => x < skipCount).ToArrayAsync();
|
var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipWhile(x => x < skipCount).ToArrayAsync();
|
||||||
var ys = Enumerable.Range(1, collection).SkipWhile(x => x < skipCount).ToArray();
|
var ys = Enumerable.Range(1, collection).SkipWhile(x => x < skipCount).ToArray();
|
||||||
|
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipWhile((x, i) => x < (skipCount - i)).ToArrayAsync();
|
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();
|
var ys = Enumerable.Range(1, collection).SkipWhile((x, i) => x < (skipCount - i)).ToArray();
|
||||||
|
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipWhileAwait(x => UniTask.Run(() => x < skipCount)).ToArrayAsync();
|
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();
|
var ys = Enumerable.Range(1, collection).SkipWhile(x => x < skipCount).ToArray();
|
||||||
|
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipWhileAwait((x, i) => UniTask.Run(() => x < (skipCount - i))).ToArrayAsync();
|
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();
|
var ys = Enumerable.Range(1, collection).SkipWhile((x, i) => x < (skipCount - i)).ToArray();
|
||||||
|
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipWhileAwaitWithCancellation((x, _) => UniTask.Run(() => x < skipCount)).ToArrayAsync();
|
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();
|
var ys = Enumerable.Range(1, collection).SkipWhile(x => x < skipCount).ToArray();
|
||||||
|
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipWhileAwaitWithCancellation((x, i, _) => UniTask.Run(() => x < (skipCount - i))).ToArrayAsync();
|
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();
|
var ys = Enumerable.Range(1, collection).SkipWhile((x, i) => x < (skipCount - i)).ToArray();
|
||||||
|
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,37 +213,37 @@ namespace NetCoreTests.Linq
|
||||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeWhile(x => x < skipCount).ToArrayAsync();
|
var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeWhile(x => x < skipCount).ToArrayAsync();
|
||||||
var ys = Enumerable.Range(1, collection).TakeWhile(x => x < skipCount).ToArray();
|
var ys = Enumerable.Range(1, collection).TakeWhile(x => x < skipCount).ToArray();
|
||||||
|
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeWhile((x, i) => x < (skipCount - i)).ToArrayAsync();
|
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();
|
var ys = Enumerable.Range(1, collection).TakeWhile((x, i) => x < (skipCount - i)).ToArray();
|
||||||
|
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeWhileAwait(x => UniTask.Run(() => x < skipCount)).ToArrayAsync();
|
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();
|
var ys = Enumerable.Range(1, collection).TakeWhile(x => x < skipCount).ToArray();
|
||||||
|
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeWhileAwait((x, i) => UniTask.Run(() => x < (skipCount - i))).ToArrayAsync();
|
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();
|
var ys = Enumerable.Range(1, collection).TakeWhile((x, i) => x < (skipCount - i)).ToArray();
|
||||||
|
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeWhileAwaitWithCancellation((x, _) => UniTask.Run(() => x < skipCount)).ToArrayAsync();
|
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();
|
var ys = Enumerable.Range(1, collection).TakeWhile(x => x < skipCount).ToArray();
|
||||||
|
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeWhileAwaitWithCancellation((x, i, _) => UniTask.Run(() => x < (skipCount - i))).ToArrayAsync();
|
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();
|
var ys = Enumerable.Range(1, collection).TakeWhile((x, i) => x < (skipCount - i)).ToArray();
|
||||||
|
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ namespace NetCoreTests.Linq
|
||||||
var xs = await Enumerable.Range(start, count).ToUniTaskAsyncEnumerable().Reverse().ToArrayAsync();
|
var xs = await Enumerable.Range(start, count).ToUniTaskAsyncEnumerable().Reverse().ToArrayAsync();
|
||||||
var ys = Enumerable.Range(start, count).Reverse().ToArray();
|
var ys = Enumerable.Range(start, count).Reverse().ToArray();
|
||||||
|
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -46,18 +46,18 @@ namespace NetCoreTests.Linq
|
||||||
{
|
{
|
||||||
var xs = await UniTaskAsyncEnumerable.Range(1, count).Select(x => x * x).ToArrayAsync();
|
var xs = await UniTaskAsyncEnumerable.Range(1, count).Select(x => x * x).ToArrayAsync();
|
||||||
var ys = Enumerable.Range(1, count).Select(x => x * x).ToArray();
|
var ys = Enumerable.Range(1, count).Select(x => x * x).ToArray();
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
|
|
||||||
var zs = await UniTaskAsyncEnumerable.Range(1, count).SelectAwait((x) => UniTask.Run(() => x * x)).ToArrayAsync();
|
var zs = await UniTaskAsyncEnumerable.Range(1, count).SelectAwait((x) => UniTask.Run(() => x * x)).ToArrayAsync();
|
||||||
zs.Should().Equal(ys);
|
zs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var xs = await UniTaskAsyncEnumerable.Range(1, count).Select((x, i) => x * x * i).ToArrayAsync();
|
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();
|
var ys = Enumerable.Range(1, count).Select((x, i) => x * x * i).ToArray();
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
|
|
||||||
var zs = await UniTaskAsyncEnumerable.Range(1, count).SelectAwait((x, i) => UniTask.Run(() => x * x * i)).ToArrayAsync();
|
var zs = await UniTaskAsyncEnumerable.Range(1, count).SelectAwait((x, i) => UniTask.Run(() => x * x * i)).ToArrayAsync();
|
||||||
zs.Should().Equal(ys);
|
zs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,22 +98,22 @@ namespace NetCoreTests.Linq
|
||||||
{
|
{
|
||||||
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectMany(x => UniTaskAsyncEnumerable.Range(99, rightCount * x)).ToArrayAsync();
|
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();
|
var ys = Enumerable.Range(1, leftCount).SelectMany(x => Enumerable.Range(99, rightCount * x)).ToArray();
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectMany((i, x) => UniTaskAsyncEnumerable.Range(99 * i, rightCount * x)).ToArrayAsync();
|
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();
|
var ys = Enumerable.Range(1, leftCount).SelectMany((i, x) => Enumerable.Range(99 * i, rightCount * x)).ToArray();
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectMany(x => UniTaskAsyncEnumerable.Range(99, rightCount * x), (x, y) => x * y).ToArrayAsync();
|
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();
|
var ys = Enumerable.Range(1, leftCount).SelectMany(x => Enumerable.Range(99, rightCount * x), (x, y) => x * y).ToArray();
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectMany((i, x) => UniTaskAsyncEnumerable.Range(99 * i, rightCount * x), (x, y) => x * y).ToArrayAsync();
|
var 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();
|
var ys = Enumerable.Range(1, leftCount).SelectMany((i, x) => Enumerable.Range(99 * i, rightCount * x), (x, y) => x * y).ToArray();
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
|
|
||||||
// await
|
// 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 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();
|
var ys = Enumerable.Range(1, leftCount).SelectMany(x => Enumerable.Range(99, rightCount * x)).ToArray();
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwait((i, x) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99 * i, rightCount * x))).ToArrayAsync();
|
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();
|
var ys = Enumerable.Range(1, leftCount).SelectMany((i, x) => Enumerable.Range(99 * i, rightCount * x)).ToArray();
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwait(x => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99, rightCount * x)), (x, y) => UniTask.Run(() => x * y)).ToArrayAsync();
|
var 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();
|
var ys = Enumerable.Range(1, leftCount).SelectMany(x => Enumerable.Range(99, rightCount * x), (x, y) => x * y).ToArray();
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwait((i, x) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99 * i, rightCount * x)), (x, y) => UniTask.Run(() => x * y)).ToArrayAsync();
|
var 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();
|
var ys = Enumerable.Range(1, leftCount).SelectMany((i, x) => Enumerable.Range(99 * i, rightCount * x), (x, y) => x * y).ToArray();
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
|
|
||||||
// with cancel
|
// 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 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();
|
var ys = Enumerable.Range(1, leftCount).SelectMany(x => Enumerable.Range(99, rightCount * x)).ToArray();
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwaitWithCancellation((i, x, _) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99 * i, rightCount * x))).ToArrayAsync();
|
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();
|
var ys = Enumerable.Range(1, leftCount).SelectMany((i, x) => Enumerable.Range(99 * i, rightCount * x)).ToArray();
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwaitWithCancellation((x, _) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99, rightCount * x)), (x, y, _) => UniTask.Run(() => x * y)).ToArrayAsync();
|
var 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();
|
var ys = Enumerable.Range(1, leftCount).SelectMany(x => Enumerable.Range(99, rightCount * x), (x, y) => x * y).ToArray();
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwaitWithCancellation((i, x, _) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99 * i, rightCount * x)), (x, y, _) => UniTask.Run(() => x * y)).ToArrayAsync();
|
var 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();
|
var ys = Enumerable.Range(1, leftCount).SelectMany((i, x) => Enumerable.Range(99 * i, rightCount * x), (x, y) => x * y).ToArray();
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,17 +219,17 @@ namespace NetCoreTests.Linq
|
||||||
{
|
{
|
||||||
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).Zip(UniTaskAsyncEnumerable.Range(99, rightCount)).ToArrayAsync();
|
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();
|
var ys = Enumerable.Range(1, leftCount).Zip(Enumerable.Range(99, rightCount)).ToArray();
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).ZipAwait(UniTaskAsyncEnumerable.Range(99, rightCount), (x, y) => UniTask.Run(() => (x, y))).ToArrayAsync();
|
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();
|
var ys = Enumerable.Range(1, leftCount).Zip(Enumerable.Range(99, rightCount)).ToArray();
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).ZipAwaitWithCancellation(UniTaskAsyncEnumerable.Range(99, rightCount), (x, y, _) => UniTask.Run(() => (x, y))).ToArrayAsync();
|
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();
|
var ys = Enumerable.Range(1, leftCount).Zip(Enumerable.Range(99, rightCount)).ToArray();
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -288,7 +288,7 @@ namespace NetCoreTests.Linq
|
||||||
var xs = await UniTaskAsyncEnumerable.Range(0, rangeCount).Buffer(bufferCount).Select(x => string.Join(",", x)).ToArrayAsync();
|
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();
|
var ys = await AsyncEnumerable.Range(0, rangeCount).Buffer(bufferCount).Select(x => string.Join(",", x)).ToArrayAsync();
|
||||||
|
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[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 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();
|
var ys = await AsyncEnumerable.Range(0, rangeCount).Buffer(bufferCount, skipCount).Select(x => string.Join(",", x)).ToArrayAsync();
|
||||||
|
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -402,7 +402,7 @@ namespace NetCoreTests.Linq
|
||||||
public async Task PariwiseImmediate()
|
public async Task PariwiseImmediate()
|
||||||
{
|
{
|
||||||
var xs = await UniTaskAsyncEnumerable.Range(1, 5).Pairwise().ToArrayAsync();
|
var xs = await UniTaskAsyncEnumerable.Range(1, 5).Pairwise().ToArrayAsync();
|
||||||
xs.Should().Equal((1, 2), (2, 3), (3, 4), (4, 5));
|
xs.Should().BeEquivalentTo((1, 2), (2, 3), (3, 4), (4, 5));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -426,7 +426,7 @@ namespace NetCoreTests.Linq
|
||||||
|
|
||||||
await complete;
|
await complete;
|
||||||
|
|
||||||
list.Should().Equal((10, 20), (20, 30), (30, 40), (40, 50));
|
list.Should().BeEquivalentTo((10, 20), (20, 30), (30, 40), (40, 50));
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyException : Exception
|
class MyException : Exception
|
||||||
|
|
|
@ -26,7 +26,7 @@ namespace NetCoreTests.Linq
|
||||||
|
|
||||||
rp.Value = 2;
|
rp.Value = 2;
|
||||||
|
|
||||||
(await b).Should().Equal(1, 2);
|
(await b).Should().BeEquivalentTo(1, 2);
|
||||||
|
|
||||||
var c = multicast.ToArrayAsync();
|
var c = multicast.ToArrayAsync();
|
||||||
|
|
||||||
|
@ -36,8 +36,8 @@ namespace NetCoreTests.Linq
|
||||||
|
|
||||||
rp.Dispose();
|
rp.Dispose();
|
||||||
|
|
||||||
(await a).Should().Equal(1, 2, 3, 4, 5);
|
(await a).Should().BeEquivalentTo(1, 2, 3, 4, 5);
|
||||||
(await c).Should().Equal(3, 4, 5);
|
(await c).Should().BeEquivalentTo(3, 4, 5);
|
||||||
|
|
||||||
disp.Dispose();
|
disp.Dispose();
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ namespace NetCoreTests.Linq
|
||||||
|
|
||||||
rp.Value = 2;
|
rp.Value = 2;
|
||||||
|
|
||||||
(await b).Should().Equal(1, 2);
|
(await b).Should().BeEquivalentTo(1, 2);
|
||||||
|
|
||||||
var c = multicast.ToArrayAsync();
|
var c = multicast.ToArrayAsync();
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ namespace NetCoreTests.Linq
|
||||||
l.Add(x);
|
l.Add(x);
|
||||||
});
|
});
|
||||||
|
|
||||||
l.Should().Equal(100, 110, 120, 130, 140, 150, 160, 170, 180, 190);
|
l.Should().BeEquivalentTo(100, 110, 120, 130, 140, 150, 160, 170, 180, 190);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,10 +34,10 @@ namespace NetCoreTests.Linq
|
||||||
{
|
{
|
||||||
var ys = array.Distinct().ToArray();
|
var ys = array.Distinct().ToArray();
|
||||||
{
|
{
|
||||||
(await array.ToUniTaskAsyncEnumerable().Distinct().ToArrayAsync()).Should().Equal(ys);
|
(await array.ToUniTaskAsyncEnumerable().Distinct().ToArrayAsync()).Should().BeEquivalentTo(ys);
|
||||||
(await array.ToUniTaskAsyncEnumerable().Distinct(x => x).ToArrayAsync()).Should().Equal(ys);
|
(await array.ToUniTaskAsyncEnumerable().Distinct(x => x).ToArrayAsync()).Should().BeEquivalentTo(ys);
|
||||||
(await array.ToUniTaskAsyncEnumerable().DistinctAwait(x => UniTask.Run(() => x)).ToArrayAsync()).Should().Equal(ys);
|
(await array.ToUniTaskAsyncEnumerable().DistinctAwait(x => UniTask.Run(() => x)).ToArrayAsync()).Should().BeEquivalentTo(ys);
|
||||||
(await array.ToUniTaskAsyncEnumerable().DistinctAwaitWithCancellation((x, _) => UniTask.Run(() => x)).ToArrayAsync()).Should().Equal(ys);
|
(await array.ToUniTaskAsyncEnumerable().DistinctAwaitWithCancellation((x, _) => UniTask.Run(() => x)).ToArrayAsync()).Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,10 +71,10 @@ namespace NetCoreTests.Linq
|
||||||
{
|
{
|
||||||
var ys = await array.ToAsyncEnumerable().DistinctUntilChanged().ToArrayAsync();
|
var ys = await array.ToAsyncEnumerable().DistinctUntilChanged().ToArrayAsync();
|
||||||
{
|
{
|
||||||
(await array.ToUniTaskAsyncEnumerable().DistinctUntilChanged().ToArrayAsync()).Should().Equal(ys);
|
(await array.ToUniTaskAsyncEnumerable().DistinctUntilChanged().ToArrayAsync()).Should().BeEquivalentTo(ys);
|
||||||
(await array.ToUniTaskAsyncEnumerable().DistinctUntilChanged(x => x).ToArrayAsync()).Should().Equal(ys);
|
(await array.ToUniTaskAsyncEnumerable().DistinctUntilChanged(x => x).ToArrayAsync()).Should().BeEquivalentTo(ys);
|
||||||
(await array.ToUniTaskAsyncEnumerable().DistinctUntilChangedAwait(x => UniTask.Run(() => x)).ToArrayAsync()).Should().Equal(ys);
|
(await array.ToUniTaskAsyncEnumerable().DistinctUntilChangedAwait(x => UniTask.Run(() => x)).ToArrayAsync()).Should().BeEquivalentTo(ys);
|
||||||
(await array.ToUniTaskAsyncEnumerable().DistinctUntilChangedAwaitWithCancellation((x, _) => UniTask.Run(() => x)).ToArrayAsync()).Should().Equal(ys);
|
(await array.ToUniTaskAsyncEnumerable().DistinctUntilChangedAwaitWithCancellation((x, _) => UniTask.Run(() => x)).ToArrayAsync()).Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,7 +112,7 @@ namespace NetCoreTests.Linq
|
||||||
{
|
{
|
||||||
var xs = await a1.ToUniTaskAsyncEnumerable().Except(a2.ToUniTaskAsyncEnumerable()).ToArrayAsync();
|
var xs = await a1.ToUniTaskAsyncEnumerable().Except(a2.ToUniTaskAsyncEnumerable()).ToArrayAsync();
|
||||||
var ys = a1.Except(a2).ToArray();
|
var ys = a1.Except(a2).ToArray();
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -141,7 +141,7 @@ namespace NetCoreTests.Linq
|
||||||
{
|
{
|
||||||
var xs = await a1.ToUniTaskAsyncEnumerable().Intersect(a2.ToUniTaskAsyncEnumerable()).ToArrayAsync();
|
var xs = await a1.ToUniTaskAsyncEnumerable().Intersect(a2.ToUniTaskAsyncEnumerable()).ToArrayAsync();
|
||||||
var ys = a1.Intersect(a2).ToArray();
|
var ys = a1.Intersect(a2).ToArray();
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -170,7 +170,7 @@ namespace NetCoreTests.Linq
|
||||||
{
|
{
|
||||||
var xs = await a1.ToUniTaskAsyncEnumerable().Union(a2.ToUniTaskAsyncEnumerable()).ToArrayAsync();
|
var xs = await a1.ToUniTaskAsyncEnumerable().Union(a2.ToUniTaskAsyncEnumerable()).ToArrayAsync();
|
||||||
var ys = a1.Union(a2).ToArray();
|
var ys = a1.Union(a2).ToArray();
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,32 +59,32 @@ namespace NetCoreTests.Linq
|
||||||
{
|
{
|
||||||
var xs = await array.ToUniTaskAsyncEnumerable().OrderBy(x => x).ToArrayAsync();
|
var xs = await array.ToUniTaskAsyncEnumerable().OrderBy(x => x).ToArrayAsync();
|
||||||
var ys = array.OrderBy(x => x).ToArray();
|
var ys = array.OrderBy(x => x).ToArray();
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var xs = await array.ToUniTaskAsyncEnumerable().OrderByDescending(x => x).ToArrayAsync();
|
var xs = await array.ToUniTaskAsyncEnumerable().OrderByDescending(x => x).ToArrayAsync();
|
||||||
var ys = array.OrderByDescending(x => x).ToArray();
|
var ys = array.OrderByDescending(x => x).ToArray();
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var xs = await array.ToUniTaskAsyncEnumerable().OrderByAwait(RandomRun).ToArrayAsync();
|
var xs = await array.ToUniTaskAsyncEnumerable().OrderByAwait(RandomRun).ToArrayAsync();
|
||||||
var ys = array.OrderBy(x => x).ToArray();
|
var ys = array.OrderBy(x => x).ToArray();
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var xs = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwait(RandomRun).ToArrayAsync();
|
var xs = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwait(RandomRun).ToArrayAsync();
|
||||||
var ys = array.OrderByDescending(x => x).ToArray();
|
var ys = array.OrderByDescending(x => x).ToArray();
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var xs = await array.ToUniTaskAsyncEnumerable().OrderByAwaitWithCancellation(RandomRun).ToArrayAsync();
|
var xs = await array.ToUniTaskAsyncEnumerable().OrderByAwaitWithCancellation(RandomRun).ToArrayAsync();
|
||||||
var ys = array.OrderBy(x => x).ToArray();
|
var ys = array.OrderBy(x => x).ToArray();
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(ys);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var xs = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwaitWithCancellation(RandomRun).ToArrayAsync();
|
var xs = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwaitWithCancellation(RandomRun).ToArrayAsync();
|
||||||
var ys = array.OrderByDescending(x => x).ToArray();
|
var ys = array.OrderByDescending(x => x).ToArray();
|
||||||
xs.Should().Equal(ys);
|
xs.Should().BeEquivalentTo(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 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();
|
var h2 = await array.ToUniTaskAsyncEnumerable().OrderByDescending(x => x.Age).ThenByDescending(x => x.FirstName).ThenByDescending(x => x.LastName).ToArrayAsync();
|
||||||
|
|
||||||
a.Should().Equal(a2);
|
a.Should().BeEquivalentTo(a2);
|
||||||
b.Should().Equal(b2);
|
b.Should().BeEquivalentTo(b2);
|
||||||
c.Should().Equal(c2);
|
c.Should().BeEquivalentTo(c2);
|
||||||
d.Should().Equal(d2);
|
d.Should().BeEquivalentTo(d2);
|
||||||
e.Should().Equal(e2);
|
e.Should().BeEquivalentTo(e2);
|
||||||
f.Should().Equal(f2);
|
f.Should().BeEquivalentTo(f2);
|
||||||
g.Should().Equal(g2);
|
g.Should().BeEquivalentTo(g2);
|
||||||
h.Should().Equal(h2);
|
h.Should().BeEquivalentTo(h2);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var a2 = await array.ToUniTaskAsyncEnumerable().OrderByAwait(x => RandomRun(x.Age)).ThenByAwait(x => RandomRun(x.FirstName)).ThenByAwait(x => RandomRun(x.LastName)).ToArrayAsync();
|
var 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 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();
|
var h2 = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwait(x => RandomRun(x.Age)).ThenByDescendingAwait(x => RandomRun(x.FirstName)).ThenByDescendingAwait(x => RandomRun(x.LastName)).ToArrayAsync();
|
||||||
|
|
||||||
a.Should().Equal(a2);
|
a.Should().BeEquivalentTo(a2);
|
||||||
b.Should().Equal(b2);
|
b.Should().BeEquivalentTo(b2);
|
||||||
c.Should().Equal(c2);
|
c.Should().BeEquivalentTo(c2);
|
||||||
d.Should().Equal(d2);
|
d.Should().BeEquivalentTo(d2);
|
||||||
e.Should().Equal(e2);
|
e.Should().BeEquivalentTo(e2);
|
||||||
f.Should().Equal(f2);
|
f.Should().BeEquivalentTo(f2);
|
||||||
g.Should().Equal(g2);
|
g.Should().BeEquivalentTo(g2);
|
||||||
h.Should().Equal(h2);
|
h.Should().BeEquivalentTo(h2);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var a2 = await array.ToUniTaskAsyncEnumerable().OrderByAwaitWithCancellation((x, ct) => RandomRun(x.Age)).ThenByAwaitWithCancellation((x, ct) => RandomRun(x.FirstName)).ThenByAwaitWithCancellation((x, ct) => RandomRun(x.LastName)).ToArrayAsync();
|
var 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 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();
|
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().Equal(a2);
|
a.Should().BeEquivalentTo(a2);
|
||||||
b.Should().Equal(b2);
|
b.Should().BeEquivalentTo(b2);
|
||||||
c.Should().Equal(c2);
|
c.Should().BeEquivalentTo(c2);
|
||||||
d.Should().Equal(d2);
|
d.Should().BeEquivalentTo(d2);
|
||||||
e.Should().Equal(e2);
|
e.Should().BeEquivalentTo(e2);
|
||||||
f.Should().Equal(f2);
|
f.Should().BeEquivalentTo(f2);
|
||||||
g.Should().Equal(g2);
|
g.Should().BeEquivalentTo(g2);
|
||||||
h.Should().Equal(h2);
|
h.Should().BeEquivalentTo(h2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ namespace NetCoreTests.Linq
|
||||||
rp.Value = 4;
|
rp.Value = 4;
|
||||||
rp.Value = 5;
|
rp.Value = 5;
|
||||||
|
|
||||||
(await xs).Should().Equal(1, 2, 3, 4, 5);
|
(await xs).Should().BeEquivalentTo(1, 2, 3, 4, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -39,11 +39,11 @@ namespace NetCoreTests.Linq
|
||||||
rp.Value = 4;
|
rp.Value = 4;
|
||||||
rp.Value = 5;
|
rp.Value = 5;
|
||||||
|
|
||||||
(await xs).Should().Equal(1, 2, 3, 4);
|
(await xs).Should().BeEquivalentTo(1, 2, 3, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task TakeUntilCanceled()
|
public async Task TakeUntil()
|
||||||
{
|
{
|
||||||
var cts = new CancellationTokenSource();
|
var cts = new CancellationTokenSource();
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ namespace NetCoreTests.Linq
|
||||||
await c;
|
await c;
|
||||||
var foo = await xs;
|
var foo = await xs;
|
||||||
|
|
||||||
foo.Should().Equal(new[] { 1, 10, 20 });
|
foo.Should().BeEquivalentTo(new[] { 1, 10, 20 });
|
||||||
|
|
||||||
async Task CancelAsync()
|
async Task CancelAsync()
|
||||||
{
|
{
|
||||||
|
@ -72,7 +72,7 @@ namespace NetCoreTests.Linq
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task SkipUntilCanceled()
|
public async Task SkipUntil()
|
||||||
{
|
{
|
||||||
var cts = new CancellationTokenSource();
|
var cts = new CancellationTokenSource();
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ namespace NetCoreTests.Linq
|
||||||
await c;
|
await c;
|
||||||
var foo = await xs;
|
var foo = await xs;
|
||||||
|
|
||||||
foo.Should().Equal(new[] { 20, 30, 40 });
|
foo.Should().BeEquivalentTo(new[] { 30, 40 });
|
||||||
|
|
||||||
async Task CancelAsync()
|
async Task CancelAsync()
|
||||||
{
|
{
|
||||||
|
@ -102,64 +102,5 @@ namespace NetCoreTests.Linq
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task TakeUntil()
|
|
||||||
{
|
|
||||||
var cts = new AsyncReactiveProperty<int>(0);
|
|
||||||
|
|
||||||
var rp = new AsyncReactiveProperty<int>(1);
|
|
||||||
|
|
||||||
var xs = rp.TakeUntil(cts.WaitAsync()).ToArrayAsync();
|
|
||||||
|
|
||||||
var c = CancelAsync();
|
|
||||||
|
|
||||||
await c;
|
|
||||||
var foo = await xs;
|
|
||||||
|
|
||||||
foo.Should().Equal(new[] { 1, 10, 20 });
|
|
||||||
|
|
||||||
async Task CancelAsync()
|
|
||||||
{
|
|
||||||
rp.Value = 10;
|
|
||||||
await Task.Yield();
|
|
||||||
rp.Value = 20;
|
|
||||||
await Task.Yield();
|
|
||||||
cts.Value = 9999;
|
|
||||||
rp.Value = 30;
|
|
||||||
await Task.Yield();
|
|
||||||
rp.Value = 40;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task SkipUntil()
|
|
||||||
{
|
|
||||||
var cts = new AsyncReactiveProperty<int>(0);
|
|
||||||
|
|
||||||
var rp = new AsyncReactiveProperty<int>(1);
|
|
||||||
|
|
||||||
var xs = rp.SkipUntil(cts.WaitAsync()).ToArrayAsync();
|
|
||||||
|
|
||||||
var c = CancelAsync();
|
|
||||||
|
|
||||||
await c;
|
|
||||||
var foo = await xs;
|
|
||||||
|
|
||||||
foo.Should().Equal(new[] { 20, 30, 40 });
|
|
||||||
|
|
||||||
async Task CancelAsync()
|
|
||||||
{
|
|
||||||
rp.Value = 10;
|
|
||||||
await Task.Yield();
|
|
||||||
rp.Value = 20;
|
|
||||||
await Task.Yield();
|
|
||||||
cts.Value = 9999;
|
|
||||||
rp.Value = 30;
|
|
||||||
await Task.Yield();
|
|
||||||
rp.Value = 40;
|
|
||||||
|
|
||||||
rp.Dispose(); // complete.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
#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(20);
|
||||||
ev.SetResult(30);
|
ev.SetResult(30);
|
||||||
|
|
||||||
one.NextCalled.Should().Equal(10, 20, 30);
|
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||||
|
|
||||||
ev.SetCompleted();
|
ev.SetCompleted();
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ namespace NetCoreTests
|
||||||
ev.SetCompleted();
|
ev.SetCompleted();
|
||||||
ev.SetCanceled(default);
|
ev.SetCanceled(default);
|
||||||
|
|
||||||
one.NextCalled.Should().Equal(10, 20, 30);
|
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||||
one.CompletedCalled.Count.Should().Be(1);
|
one.CompletedCalled.Count.Should().Be(1);
|
||||||
}
|
}
|
||||||
// after removed, onemore
|
// after removed, onemore
|
||||||
|
@ -59,7 +59,7 @@ namespace NetCoreTests
|
||||||
ev.SetResult(20);
|
ev.SetResult(20);
|
||||||
ev.SetResult(30);
|
ev.SetResult(30);
|
||||||
|
|
||||||
one.NextCalled.Should().Equal(10, 20, 30);
|
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||||
|
|
||||||
ev.SetCompleted();
|
ev.SetCompleted();
|
||||||
|
|
||||||
|
@ -71,7 +71,7 @@ namespace NetCoreTests
|
||||||
ev.SetCompleted();
|
ev.SetCompleted();
|
||||||
ev.SetCanceled(default);
|
ev.SetCanceled(default);
|
||||||
|
|
||||||
one.NextCalled.Should().Equal(10, 20, 30);
|
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||||
one.CompletedCalled.Count.Should().Be(1);
|
one.CompletedCalled.Count.Should().Be(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,10 +102,10 @@ namespace NetCoreTests
|
||||||
ev.SetResult(20);
|
ev.SetResult(20);
|
||||||
ev.SetResult(30);
|
ev.SetResult(30);
|
||||||
|
|
||||||
one.NextCalled.Should().Equal(10, 20, 30);
|
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||||
two.NextCalled.Should().Equal(10, 20, 30);
|
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||||
three.NextCalled.Should().Equal(10, 20, 30);
|
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||||
four.NextCalled.Should().Equal(10, 20, 30);
|
four.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||||
|
|
||||||
ev.SetCompleted();
|
ev.SetCompleted();
|
||||||
|
|
||||||
|
@ -120,11 +120,11 @@ namespace NetCoreTests
|
||||||
ev.SetCompleted();
|
ev.SetCompleted();
|
||||||
ev.SetCanceled(default);
|
ev.SetCanceled(default);
|
||||||
|
|
||||||
one.NextCalled.Should().Equal(10, 20, 30);
|
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||||
one.CompletedCalled.Count.Should().Be(1);
|
one.CompletedCalled.Count.Should().Be(1);
|
||||||
two.NextCalled.Should().Equal(10, 20, 30);
|
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||||
three.CompletedCalled.Count.Should().Be(1);
|
three.CompletedCalled.Count.Should().Be(1);
|
||||||
two.NextCalled.Should().Equal(10, 20, 30);
|
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||||
three.CompletedCalled.Count.Should().Be(1);
|
three.CompletedCalled.Count.Should().Be(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,10 +145,10 @@ namespace NetCoreTests
|
||||||
ev.SetResult(30);
|
ev.SetResult(30);
|
||||||
ev.Add(four);
|
ev.Add(four);
|
||||||
|
|
||||||
one.NextCalled.Should().Equal(10, 20, 30);
|
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||||
two.NextCalled.Should().Equal(10, 20, 30);
|
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||||
three.NextCalled.Should().Equal(10, 20, 30);
|
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||||
four.NextCalled.Should().Equal(10, 20, 30);
|
four.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||||
|
|
||||||
ev.SetCompleted();
|
ev.SetCompleted();
|
||||||
|
|
||||||
|
@ -163,11 +163,11 @@ namespace NetCoreTests
|
||||||
ev.SetCompleted();
|
ev.SetCompleted();
|
||||||
ev.SetCanceled(default);
|
ev.SetCanceled(default);
|
||||||
|
|
||||||
one.NextCalled.Should().Equal(10, 20, 30);
|
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||||
one.CompletedCalled.Count.Should().Be(1);
|
one.CompletedCalled.Count.Should().Be(1);
|
||||||
two.NextCalled.Should().Equal(10, 20, 30);
|
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||||
three.CompletedCalled.Count.Should().Be(1);
|
three.CompletedCalled.Count.Should().Be(1);
|
||||||
two.NextCalled.Should().Equal(10, 20, 30);
|
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||||
three.CompletedCalled.Count.Should().Be(1);
|
three.CompletedCalled.Count.Should().Be(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -190,9 +190,9 @@ namespace NetCoreTests
|
||||||
ev.SetResult(20);
|
ev.SetResult(20);
|
||||||
ev.SetResult(30);
|
ev.SetResult(30);
|
||||||
|
|
||||||
one.NextCalled.Should().Equal(10, 20, 30);
|
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||||
two.NextCalled.Should().Equal(10, 20, 30);
|
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||||
three.NextCalled.Should().Equal(10, 20, 30);
|
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||||
|
|
||||||
ev.Remove(one);
|
ev.Remove(one);
|
||||||
|
|
||||||
|
@ -200,9 +200,9 @@ namespace NetCoreTests
|
||||||
ev.SetResult(50);
|
ev.SetResult(50);
|
||||||
ev.SetResult(60);
|
ev.SetResult(60);
|
||||||
|
|
||||||
one.NextCalled.Should().Equal(10, 20, 30);
|
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||||
two.NextCalled.Should().Equal(10, 20, 30, 40, 50, 60);
|
two.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60);
|
||||||
three.NextCalled.Should().Equal(10, 20, 30, 40, 50, 60);
|
three.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -222,9 +222,9 @@ namespace NetCoreTests
|
||||||
ev.SetResult(20);
|
ev.SetResult(20);
|
||||||
ev.SetResult(30);
|
ev.SetResult(30);
|
||||||
|
|
||||||
one.NextCalled.Should().Equal(10, 20, 30);
|
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||||
two.NextCalled.Should().Equal(10, 20, 30);
|
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||||
three.NextCalled.Should().Equal(10, 20, 30);
|
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||||
|
|
||||||
ev.Remove(two);
|
ev.Remove(two);
|
||||||
|
|
||||||
|
@ -232,9 +232,9 @@ namespace NetCoreTests
|
||||||
ev.SetResult(50);
|
ev.SetResult(50);
|
||||||
ev.SetResult(60);
|
ev.SetResult(60);
|
||||||
|
|
||||||
one.NextCalled.Should().Equal(10, 20, 30, 40, 50, 60);
|
one.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60);
|
||||||
two.NextCalled.Should().Equal(10, 20, 30);
|
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||||
three.NextCalled.Should().Equal(10, 20, 30, 40, 50, 60);
|
three.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -254,9 +254,9 @@ namespace NetCoreTests
|
||||||
ev.SetResult(20);
|
ev.SetResult(20);
|
||||||
ev.SetResult(30);
|
ev.SetResult(30);
|
||||||
|
|
||||||
one.NextCalled.Should().Equal(10, 20, 30);
|
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||||
two.NextCalled.Should().Equal(10, 20, 30);
|
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||||
three.NextCalled.Should().Equal(10, 20, 30);
|
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||||
|
|
||||||
ev.Remove(three);
|
ev.Remove(three);
|
||||||
|
|
||||||
|
@ -264,9 +264,9 @@ namespace NetCoreTests
|
||||||
ev.SetResult(50);
|
ev.SetResult(50);
|
||||||
ev.SetResult(60);
|
ev.SetResult(60);
|
||||||
|
|
||||||
one.NextCalled.Should().Equal(10, 20, 30, 40, 50, 60);
|
one.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60);
|
||||||
two.NextCalled.Should().Equal(10, 20, 30, 40, 50, 60);
|
two.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40, 50, 60);
|
||||||
three.NextCalled.Should().Equal(10, 20, 30);
|
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -321,9 +321,9 @@ namespace NetCoreTests
|
||||||
ev.SetResult(20);
|
ev.SetResult(20);
|
||||||
ev.SetResult(30);
|
ev.SetResult(30);
|
||||||
|
|
||||||
one.NextCalled.Should().Equal(10);
|
one.NextCalled.Should().BeEquivalentTo(10);
|
||||||
two.NextCalled.Should().Equal(10, 20, 30);
|
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||||
three.NextCalled.Should().Equal(10, 20, 30);
|
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Run2()
|
public void Run2()
|
||||||
|
@ -342,9 +342,9 @@ namespace NetCoreTests
|
||||||
ev.SetResult(20);
|
ev.SetResult(20);
|
||||||
ev.SetResult(30);
|
ev.SetResult(30);
|
||||||
|
|
||||||
one.NextCalled.Should().Equal(10);
|
one.NextCalled.Should().BeEquivalentTo(10);
|
||||||
two.NextCalled.Should().Equal(10, 20, 30);
|
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||||
three.NextCalled.Should().Equal(10, 20, 30);
|
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Run3()
|
public void Run3()
|
||||||
|
@ -363,9 +363,9 @@ namespace NetCoreTests
|
||||||
ev.SetResult(20);
|
ev.SetResult(20);
|
||||||
ev.SetResult(30);
|
ev.SetResult(30);
|
||||||
|
|
||||||
one.NextCalled.Should().Equal(10);
|
one.NextCalled.Should().BeEquivalentTo(10);
|
||||||
two.NextCalled.Should().Equal(10, 20, 30);
|
two.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||||
three.NextCalled.Should().Equal(10, 20, 30);
|
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -390,9 +390,9 @@ namespace NetCoreTests
|
||||||
ev.SetResult(20);
|
ev.SetResult(20);
|
||||||
ev.SetResult(30);
|
ev.SetResult(30);
|
||||||
|
|
||||||
one.NextCalled.Should().Equal(10, 20, 30);
|
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||||
two.NextCalled.Count.Should().Be(0);
|
two.NextCalled.Count.Should().Be(0);
|
||||||
three.NextCalled.Should().Equal(10, 20, 30);
|
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Run2()
|
public void Run2()
|
||||||
|
@ -412,9 +412,9 @@ namespace NetCoreTests
|
||||||
ev.SetResult(20);
|
ev.SetResult(20);
|
||||||
ev.SetResult(30);
|
ev.SetResult(30);
|
||||||
|
|
||||||
one.NextCalled.Should().Equal(10, 20, 30);
|
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||||
two.NextCalled.Should().Equal(10);
|
two.NextCalled.Should().BeEquivalentTo(10);
|
||||||
three.NextCalled.Should().Equal(10, 20, 30);
|
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Run3()
|
public void Run3()
|
||||||
|
@ -434,9 +434,9 @@ namespace NetCoreTests
|
||||||
ev.SetResult(20);
|
ev.SetResult(20);
|
||||||
ev.SetResult(30);
|
ev.SetResult(30);
|
||||||
|
|
||||||
one.NextCalled.Should().Equal(10, 20, 30);
|
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||||
two.NextCalled.Should().Equal(10);
|
two.NextCalled.Should().BeEquivalentTo(10);
|
||||||
three.NextCalled.Should().Equal(10, 20, 30);
|
three.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -464,10 +464,10 @@ namespace NetCoreTests
|
||||||
ev.SetResult(20);
|
ev.SetResult(20);
|
||||||
ev.SetResult(30);
|
ev.SetResult(30);
|
||||||
|
|
||||||
one.NextCalled.Should().Equal(10, 20, 30);
|
one.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||||
two.NextCalled.Count.Should().Be(0);
|
two.NextCalled.Count.Should().Be(0);
|
||||||
three.NextCalled.Count.Should().Be(0);
|
three.NextCalled.Count.Should().Be(0);
|
||||||
four.NextCalled.Should().Equal(10, 20, 30);
|
four.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Run2()
|
public void Run2()
|
||||||
|
@ -490,10 +490,10 @@ namespace NetCoreTests
|
||||||
ev.SetResult(20);
|
ev.SetResult(20);
|
||||||
ev.SetResult(30);
|
ev.SetResult(30);
|
||||||
|
|
||||||
one.NextCalled.Should().Equal(10);
|
one.NextCalled.Should().BeEquivalentTo(10);
|
||||||
two.NextCalled.Count.Should().Be(0);
|
two.NextCalled.Count.Should().Be(0);
|
||||||
three.NextCalled.Count.Should().Be(0);
|
three.NextCalled.Count.Should().Be(0);
|
||||||
four.NextCalled.Should().Equal(10, 20, 30);
|
four.NextCalled.Should().BeEquivalentTo(10, 20, 30);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -533,10 +533,10 @@ namespace NetCoreTests
|
||||||
ev.SetResult(30);
|
ev.SetResult(30);
|
||||||
ev.SetResult(40);
|
ev.SetResult(40);
|
||||||
|
|
||||||
one.NextCalled.Should().Equal(10, 20, 30, 40);
|
one.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40);
|
||||||
two.NextCalled.Should().Equal(20, 30, 40);
|
two.NextCalled.Should().BeEquivalentTo(20, 30, 40);
|
||||||
three.NextCalled.Should().Equal(30, 40);
|
three.NextCalled.Should().BeEquivalentTo(30, 40);
|
||||||
four.NextCalled.Should().Equal(40);
|
four.NextCalled.Should().BeEquivalentTo(40);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Run2()
|
public void Run2()
|
||||||
|
@ -573,10 +573,10 @@ namespace NetCoreTests
|
||||||
ev.SetResult(30);
|
ev.SetResult(30);
|
||||||
ev.SetResult(40);
|
ev.SetResult(40);
|
||||||
|
|
||||||
one.NextCalled.Should().Equal(10, 20, 30, 40);
|
one.NextCalled.Should().BeEquivalentTo(10, 20, 30, 40);
|
||||||
two.NextCalled.Should().Equal(20, 30, 40);
|
two.NextCalled.Should().BeEquivalentTo(20, 30, 40);
|
||||||
three.NextCalled.Should().Equal(30, 40);
|
three.NextCalled.Should().BeEquivalentTo(30, 40);
|
||||||
four.NextCalled.Should().Equal(40);
|
four.NextCalled.Should().BeEquivalentTo(40);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
|
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
|
|
||||||
|
|
|
@ -1,80 +0,0 @@
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Cysharp.Threading.Tasks;
|
|
||||||
using FluentAssertions;
|
|
||||||
using NetCoreTests.Linq;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace NetCoreTests
|
|
||||||
{
|
|
||||||
public class AutoResetUniTaskCompletionSourceTest
|
|
||||||
{
|
|
||||||
[Fact]
|
|
||||||
public async Task SetResultAfterReturn()
|
|
||||||
{
|
|
||||||
var source1 = AutoResetUniTaskCompletionSource.Create();
|
|
||||||
source1.TrySetResult();
|
|
||||||
await source1.Task;
|
|
||||||
|
|
||||||
source1.TrySetResult().Should().BeFalse();
|
|
||||||
|
|
||||||
var source2 = AutoResetUniTaskCompletionSource.Create();
|
|
||||||
source2.TrySetResult();
|
|
||||||
await source2.Task;
|
|
||||||
|
|
||||||
source2.TrySetResult().Should().BeFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task SetCancelAfterReturn()
|
|
||||||
{
|
|
||||||
var source = AutoResetUniTaskCompletionSource.Create();
|
|
||||||
source.TrySetResult();
|
|
||||||
await source.Task;
|
|
||||||
|
|
||||||
source.TrySetCanceled().Should().BeFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task SetExceptionAfterReturn()
|
|
||||||
{
|
|
||||||
var source = AutoResetUniTaskCompletionSource.Create();
|
|
||||||
source.TrySetResult();
|
|
||||||
await source.Task;
|
|
||||||
|
|
||||||
source.TrySetException(new UniTaskTestException()).Should().BeFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task SetResultWithValueAfterReturn()
|
|
||||||
{
|
|
||||||
var source1 = AutoResetUniTaskCompletionSource<int>.Create();
|
|
||||||
source1.TrySetResult(100);
|
|
||||||
(await source1.Task).Should().Be(100);
|
|
||||||
|
|
||||||
source1.TrySetResult(100).Should().BeFalse();
|
|
||||||
|
|
||||||
var source2 = AutoResetUniTaskCompletionSource.Create();
|
|
||||||
source2.TrySetResult();
|
|
||||||
await source2.Task;
|
|
||||||
source2.TrySetResult().Should().BeFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task SetCancelWithValueAfterReturn()
|
|
||||||
{
|
|
||||||
var source = AutoResetUniTaskCompletionSource<int>.Create();
|
|
||||||
source.TrySetResult(100);
|
|
||||||
(await source.Task).Should().Be(100);
|
|
||||||
source.TrySetCanceled().Should().BeFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task SetExceptionWithValueAfterReturn()
|
|
||||||
{
|
|
||||||
var source = AutoResetUniTaskCompletionSource<int>.Create();
|
|
||||||
source.TrySetResult(100);
|
|
||||||
(await source.Task).Should().Be(100);
|
|
||||||
source.TrySetException(new UniTaskTestException()).Should().BeFalse();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,69 +0,0 @@
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
using Cysharp.Threading.Tasks;
|
|
||||||
using FluentAssertions;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace NetCoreTests
|
|
||||||
{
|
|
||||||
public class WithCancellationTest
|
|
||||||
{
|
|
||||||
[Fact]
|
|
||||||
public async Task Standard()
|
|
||||||
{
|
|
||||||
CancellationTokenSource cts = new CancellationTokenSource();
|
|
||||||
|
|
||||||
var v = await UniTask.Run(() => 10).AttachExternalCancellation(cts.Token);
|
|
||||||
|
|
||||||
v.Should().Be(10);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task Cancel()
|
|
||||||
{
|
|
||||||
CancellationTokenSource cts = new CancellationTokenSource();
|
|
||||||
|
|
||||||
var t = UniTask.Create(async () =>
|
|
||||||
{
|
|
||||||
await Task.Delay(TimeSpan.FromSeconds(1));
|
|
||||||
return 10;
|
|
||||||
}).AttachExternalCancellation(cts.Token);
|
|
||||||
|
|
||||||
cts.Cancel();
|
|
||||||
|
|
||||||
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await t)).CancellationToken.Should().Be(cts.Token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -20,11 +20,8 @@ public static class EditorRunnerChecker
|
||||||
{
|
{
|
||||||
Debug.Log("Start");
|
Debug.Log("Start");
|
||||||
|
|
||||||
//var r = await UnityWebRequest.Get("https://bing.com/").SendWebRequest().ToUniTask();
|
var r = await UnityWebRequest.Get("https://bing.com/").SendWebRequest().ToUniTask();
|
||||||
//Debug.Log(r.downloadHandler.text.Substring(0, 100));
|
Debug.Log(r.downloadHandler.text.Substring(0, 100));
|
||||||
//await UniTask.Yield();
|
|
||||||
|
|
||||||
await UniTask.DelayFrame(30);
|
|
||||||
|
|
||||||
Debug.Log("End");
|
Debug.Log("End");
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
"allowUnsafeCode": false,
|
"allowUnsafeCode": false,
|
||||||
"overrideReferences": false,
|
"overrideReferences": false,
|
||||||
"precompiledReferences": [],
|
"precompiledReferences": [],
|
||||||
"autoReferenced": false,
|
"autoReferenced": true,
|
||||||
"defineConstraints": [],
|
"defineConstraints": [],
|
||||||
"versionDefines": [],
|
"versionDefines": [],
|
||||||
"noEngineReferences": false
|
"noEngineReferences": false
|
||||||
|
|
|
@ -7,239 +7,113 @@ namespace Cysharp.Threading.Tasks
|
||||||
{
|
{
|
||||||
public class AsyncLazy
|
public class AsyncLazy
|
||||||
{
|
{
|
||||||
static Action<object> continuation = SetCompletionSource;
|
Func<UniTask> valueFactory;
|
||||||
|
UniTask target;
|
||||||
Func<UniTask> taskFactory;
|
|
||||||
UniTaskCompletionSource completionSource;
|
|
||||||
UniTask.Awaiter awaiter;
|
|
||||||
|
|
||||||
object syncLock;
|
object syncLock;
|
||||||
bool initialized;
|
bool initialized;
|
||||||
|
|
||||||
public AsyncLazy(Func<UniTask> taskFactory)
|
public AsyncLazy(Func<UniTask> valueFactory)
|
||||||
{
|
{
|
||||||
this.taskFactory = taskFactory;
|
this.valueFactory = valueFactory;
|
||||||
this.completionSource = new UniTaskCompletionSource();
|
this.target = default;
|
||||||
this.syncLock = new object();
|
this.syncLock = new object();
|
||||||
this.initialized = false;
|
this.initialized = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal AsyncLazy(UniTask task)
|
internal AsyncLazy(UniTask value)
|
||||||
{
|
{
|
||||||
this.taskFactory = null;
|
this.valueFactory = null;
|
||||||
this.completionSource = new UniTaskCompletionSource();
|
this.target = value;
|
||||||
this.syncLock = null;
|
this.syncLock = null;
|
||||||
this.initialized = true;
|
this.initialized = true;
|
||||||
|
|
||||||
var awaiter = task.GetAwaiter();
|
|
||||||
if (awaiter.IsCompleted)
|
|
||||||
{
|
|
||||||
SetCompletionSource(awaiter);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.awaiter = awaiter;
|
|
||||||
awaiter.SourceOnCompleted(continuation, this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public UniTask Task
|
public UniTask Task => EnsureInitialized();
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
EnsureInitialized();
|
|
||||||
return completionSource.Task;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
public UniTask.Awaiter GetAwaiter() => EnsureInitialized().GetAwaiter();
|
||||||
|
|
||||||
public UniTask.Awaiter GetAwaiter() => Task.GetAwaiter();
|
UniTask EnsureInitialized()
|
||||||
|
|
||||||
void EnsureInitialized()
|
|
||||||
{
|
{
|
||||||
if (Volatile.Read(ref initialized))
|
if (Volatile.Read(ref initialized))
|
||||||
{
|
{
|
||||||
return;
|
return target;
|
||||||
}
|
}
|
||||||
|
|
||||||
EnsureInitializedCore();
|
return EnsureInitializedCore();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EnsureInitializedCore()
|
UniTask EnsureInitializedCore()
|
||||||
{
|
{
|
||||||
lock (syncLock)
|
lock (syncLock)
|
||||||
{
|
{
|
||||||
if (!Volatile.Read(ref initialized))
|
if (!Volatile.Read(ref initialized))
|
||||||
{
|
{
|
||||||
var f = Interlocked.Exchange(ref taskFactory, null);
|
var f = Interlocked.Exchange(ref valueFactory, null);
|
||||||
if (f != null)
|
if (f != null)
|
||||||
{
|
{
|
||||||
var task = f();
|
target = f().Preserve(); // with preserve(allow multiple await).
|
||||||
var awaiter = task.GetAwaiter();
|
|
||||||
if (awaiter.IsCompleted)
|
|
||||||
{
|
|
||||||
SetCompletionSource(awaiter);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.awaiter = awaiter;
|
|
||||||
awaiter.SourceOnCompleted(continuation, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
Volatile.Write(ref initialized, true);
|
Volatile.Write(ref initialized, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void SetCompletionSource(in UniTask.Awaiter awaiter)
|
return target;
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
awaiter.GetResult();
|
|
||||||
completionSource.TrySetResult();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
completionSource.TrySetException(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void SetCompletionSource(object state)
|
|
||||||
{
|
|
||||||
var self = (AsyncLazy)state;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
self.awaiter.GetResult();
|
|
||||||
self.completionSource.TrySetResult();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
self.completionSource.TrySetException(ex);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
self.awaiter = default;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AsyncLazy<T>
|
public class AsyncLazy<T>
|
||||||
{
|
{
|
||||||
static Action<object> continuation = SetCompletionSource;
|
Func<UniTask<T>> valueFactory;
|
||||||
|
UniTask<T> target;
|
||||||
Func<UniTask<T>> taskFactory;
|
|
||||||
UniTaskCompletionSource<T> completionSource;
|
|
||||||
UniTask<T>.Awaiter awaiter;
|
|
||||||
|
|
||||||
object syncLock;
|
object syncLock;
|
||||||
bool initialized;
|
bool initialized;
|
||||||
|
|
||||||
public AsyncLazy(Func<UniTask<T>> taskFactory)
|
public AsyncLazy(Func<UniTask<T>> valueFactory)
|
||||||
{
|
{
|
||||||
this.taskFactory = taskFactory;
|
this.valueFactory = valueFactory;
|
||||||
this.completionSource = new UniTaskCompletionSource<T>();
|
this.target = default;
|
||||||
this.syncLock = new object();
|
this.syncLock = new object();
|
||||||
this.initialized = false;
|
this.initialized = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal AsyncLazy(UniTask<T> task)
|
internal AsyncLazy(UniTask<T> value)
|
||||||
{
|
{
|
||||||
this.taskFactory = null;
|
this.valueFactory = null;
|
||||||
this.completionSource = new UniTaskCompletionSource<T>();
|
this.target = value;
|
||||||
this.syncLock = null;
|
this.syncLock = null;
|
||||||
this.initialized = true;
|
this.initialized = true;
|
||||||
|
|
||||||
var awaiter = task.GetAwaiter();
|
|
||||||
if (awaiter.IsCompleted)
|
|
||||||
{
|
|
||||||
SetCompletionSource(awaiter);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.awaiter = awaiter;
|
|
||||||
awaiter.SourceOnCompleted(continuation, this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public UniTask<T> Task
|
public UniTask<T> Task => EnsureInitialized();
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
EnsureInitialized();
|
|
||||||
return completionSource.Task;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
public UniTask<T>.Awaiter GetAwaiter() => EnsureInitialized().GetAwaiter();
|
||||||
|
|
||||||
public UniTask<T>.Awaiter GetAwaiter() => Task.GetAwaiter();
|
UniTask<T> EnsureInitialized()
|
||||||
|
|
||||||
void EnsureInitialized()
|
|
||||||
{
|
{
|
||||||
if (Volatile.Read(ref initialized))
|
if (Volatile.Read(ref initialized))
|
||||||
{
|
{
|
||||||
return;
|
return target;
|
||||||
}
|
}
|
||||||
|
|
||||||
EnsureInitializedCore();
|
return EnsureInitializedCore();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EnsureInitializedCore()
|
UniTask<T> EnsureInitializedCore()
|
||||||
{
|
{
|
||||||
lock (syncLock)
|
lock (syncLock)
|
||||||
{
|
{
|
||||||
if (!Volatile.Read(ref initialized))
|
if (!Volatile.Read(ref initialized))
|
||||||
{
|
{
|
||||||
var f = Interlocked.Exchange(ref taskFactory, null);
|
var f = Interlocked.Exchange(ref valueFactory, null);
|
||||||
if (f != null)
|
if (f != null)
|
||||||
{
|
{
|
||||||
var task = f();
|
target = f().Preserve(); // with preserve(allow multiple await).
|
||||||
var awaiter = task.GetAwaiter();
|
|
||||||
if (awaiter.IsCompleted)
|
|
||||||
{
|
|
||||||
SetCompletionSource(awaiter);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.awaiter = awaiter;
|
|
||||||
awaiter.SourceOnCompleted(continuation, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
Volatile.Write(ref initialized, true);
|
Volatile.Write(ref initialized, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void SetCompletionSource(in UniTask<T>.Awaiter awaiter)
|
return target;
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var result = awaiter.GetResult();
|
|
||||||
completionSource.TrySetResult(result);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
completionSource.TrySetException(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void SetCompletionSource(object state)
|
|
||||||
{
|
|
||||||
var self = (AsyncLazy<T>)state;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var result = self.awaiter.GetResult();
|
|
||||||
self.completionSource.TrySetResult(result);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
self.completionSource.TrySetException(ex);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
self.awaiter = default;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ namespace Cysharp.Threading.Tasks
|
||||||
{
|
{
|
||||||
T Value { get; }
|
T Value { get; }
|
||||||
IUniTaskAsyncEnumerable<T> WithoutCurrent();
|
IUniTaskAsyncEnumerable<T> WithoutCurrent();
|
||||||
UniTask<T> WaitAsync(CancellationToken cancellationToken = default);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface IAsyncReactiveProperty<T> : IReadOnlyAsyncReactiveProperty<T>
|
public interface IAsyncReactiveProperty<T> : IReadOnlyAsyncReactiveProperty<T>
|
||||||
|
@ -70,11 +69,6 @@ namespace Cysharp.Threading.Tasks
|
||||||
return latestValue?.ToString();
|
return latestValue?.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public UniTask<T> WaitAsync(CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
return new UniTask<T>(WaitAsyncSource.Create(this, cancellationToken, out var token), token);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool isValueType;
|
static bool isValueType;
|
||||||
|
|
||||||
static AsyncReactiveProperty()
|
static AsyncReactiveProperty()
|
||||||
|
@ -82,136 +76,7 @@ namespace Cysharp.Threading.Tasks
|
||||||
isValueType = typeof(T).IsValueType;
|
isValueType = typeof(T).IsValueType;
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class WaitAsyncSource : IUniTaskSource<T>, ITriggerHandler<T>, ITaskPoolNode<WaitAsyncSource>
|
class WithoutCurrentEnumerable : IUniTaskAsyncEnumerable<T>
|
||||||
{
|
|
||||||
static Action<object> cancellationCallback = CancellationCallback;
|
|
||||||
|
|
||||||
static TaskPool<WaitAsyncSource> pool;
|
|
||||||
WaitAsyncSource nextNode;
|
|
||||||
ref WaitAsyncSource ITaskPoolNode<WaitAsyncSource>.NextNode => ref nextNode;
|
|
||||||
|
|
||||||
static WaitAsyncSource()
|
|
||||||
{
|
|
||||||
TaskPool.RegisterSizeGetter(typeof(WaitAsyncSource), () => pool.Size);
|
|
||||||
}
|
|
||||||
|
|
||||||
AsyncReactiveProperty<T> parent;
|
|
||||||
CancellationToken cancellationToken;
|
|
||||||
CancellationTokenRegistration cancellationTokenRegistration;
|
|
||||||
UniTaskCompletionSourceCore<T> core;
|
|
||||||
|
|
||||||
WaitAsyncSource()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IUniTaskSource<T> Create(AsyncReactiveProperty<T> parent, CancellationToken cancellationToken, out short token)
|
|
||||||
{
|
|
||||||
if (cancellationToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
return AutoResetUniTaskCompletionSource<T>.CreateFromCanceled(cancellationToken, out token);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pool.TryPop(out var result))
|
|
||||||
{
|
|
||||||
result = new WaitAsyncSource();
|
|
||||||
}
|
|
||||||
|
|
||||||
result.parent = parent;
|
|
||||||
result.cancellationToken = cancellationToken;
|
|
||||||
|
|
||||||
if (cancellationToken.CanBeCanceled)
|
|
||||||
{
|
|
||||||
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
result.parent.triggerEvent.Add(result);
|
|
||||||
|
|
||||||
TaskTracker.TrackActiveTask(result, 3);
|
|
||||||
|
|
||||||
token = result.core.Version;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TryReturn()
|
|
||||||
{
|
|
||||||
TaskTracker.RemoveTracking(this);
|
|
||||||
core.Reset();
|
|
||||||
cancellationTokenRegistration.Dispose();
|
|
||||||
cancellationTokenRegistration = default;
|
|
||||||
parent.triggerEvent.Remove(this);
|
|
||||||
parent = null;
|
|
||||||
cancellationToken = default;
|
|
||||||
return pool.TryPush(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void CancellationCallback(object state)
|
|
||||||
{
|
|
||||||
var self = (WaitAsyncSource)state;
|
|
||||||
self.OnCanceled(self.cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
// IUniTaskSource
|
|
||||||
|
|
||||||
public T GetResult(short token)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return core.GetResult(token);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
TryReturn();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void IUniTaskSource.GetResult(short token)
|
|
||||||
{
|
|
||||||
GetResult(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
|
||||||
{
|
|
||||||
core.OnCompleted(continuation, state, token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public UniTaskStatus GetStatus(short token)
|
|
||||||
{
|
|
||||||
return core.GetStatus(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public UniTaskStatus UnsafeGetStatus()
|
|
||||||
{
|
|
||||||
return core.UnsafeGetStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ITriggerHandler
|
|
||||||
|
|
||||||
ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; }
|
|
||||||
ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; }
|
|
||||||
|
|
||||||
public void OnCanceled(CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
core.TrySetCanceled(cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnCompleted()
|
|
||||||
{
|
|
||||||
// Complete as Cancel.
|
|
||||||
core.TrySetCanceled(CancellationToken.None);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnError(Exception ex)
|
|
||||||
{
|
|
||||||
core.TrySetException(ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnNext(T value)
|
|
||||||
{
|
|
||||||
core.TrySetResult(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sealed class WithoutCurrentEnumerable : IUniTaskAsyncEnumerable<T>
|
|
||||||
{
|
{
|
||||||
readonly AsyncReactiveProperty<T> parent;
|
readonly AsyncReactiveProperty<T> parent;
|
||||||
|
|
||||||
|
@ -388,11 +253,6 @@ namespace Cysharp.Threading.Tasks
|
||||||
return latestValue?.ToString();
|
return latestValue?.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public UniTask<T> WaitAsync(CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
return new UniTask<T>(WaitAsyncSource.Create(this, cancellationToken, out var token), token);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool isValueType;
|
static bool isValueType;
|
||||||
|
|
||||||
static ReadOnlyAsyncReactiveProperty()
|
static ReadOnlyAsyncReactiveProperty()
|
||||||
|
@ -400,136 +260,7 @@ namespace Cysharp.Threading.Tasks
|
||||||
isValueType = typeof(T).IsValueType;
|
isValueType = typeof(T).IsValueType;
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class WaitAsyncSource : IUniTaskSource<T>, ITriggerHandler<T>, ITaskPoolNode<WaitAsyncSource>
|
class WithoutCurrentEnumerable : IUniTaskAsyncEnumerable<T>
|
||||||
{
|
|
||||||
static Action<object> cancellationCallback = CancellationCallback;
|
|
||||||
|
|
||||||
static TaskPool<WaitAsyncSource> pool;
|
|
||||||
WaitAsyncSource nextNode;
|
|
||||||
ref WaitAsyncSource ITaskPoolNode<WaitAsyncSource>.NextNode => ref nextNode;
|
|
||||||
|
|
||||||
static WaitAsyncSource()
|
|
||||||
{
|
|
||||||
TaskPool.RegisterSizeGetter(typeof(WaitAsyncSource), () => pool.Size);
|
|
||||||
}
|
|
||||||
|
|
||||||
ReadOnlyAsyncReactiveProperty<T> parent;
|
|
||||||
CancellationToken cancellationToken;
|
|
||||||
CancellationTokenRegistration cancellationTokenRegistration;
|
|
||||||
UniTaskCompletionSourceCore<T> core;
|
|
||||||
|
|
||||||
WaitAsyncSource()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IUniTaskSource<T> Create(ReadOnlyAsyncReactiveProperty<T> parent, CancellationToken cancellationToken, out short token)
|
|
||||||
{
|
|
||||||
if (cancellationToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
return AutoResetUniTaskCompletionSource<T>.CreateFromCanceled(cancellationToken, out token);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pool.TryPop(out var result))
|
|
||||||
{
|
|
||||||
result = new WaitAsyncSource();
|
|
||||||
}
|
|
||||||
|
|
||||||
result.parent = parent;
|
|
||||||
result.cancellationToken = cancellationToken;
|
|
||||||
|
|
||||||
if (cancellationToken.CanBeCanceled)
|
|
||||||
{
|
|
||||||
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
result.parent.triggerEvent.Add(result);
|
|
||||||
|
|
||||||
TaskTracker.TrackActiveTask(result, 3);
|
|
||||||
|
|
||||||
token = result.core.Version;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TryReturn()
|
|
||||||
{
|
|
||||||
TaskTracker.RemoveTracking(this);
|
|
||||||
core.Reset();
|
|
||||||
cancellationTokenRegistration.Dispose();
|
|
||||||
cancellationTokenRegistration = default;
|
|
||||||
parent.triggerEvent.Remove(this);
|
|
||||||
parent = null;
|
|
||||||
cancellationToken = default;
|
|
||||||
return pool.TryPush(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void CancellationCallback(object state)
|
|
||||||
{
|
|
||||||
var self = (WaitAsyncSource)state;
|
|
||||||
self.OnCanceled(self.cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
// IUniTaskSource
|
|
||||||
|
|
||||||
public T GetResult(short token)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return core.GetResult(token);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
TryReturn();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void IUniTaskSource.GetResult(short token)
|
|
||||||
{
|
|
||||||
GetResult(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
|
||||||
{
|
|
||||||
core.OnCompleted(continuation, state, token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public UniTaskStatus GetStatus(short token)
|
|
||||||
{
|
|
||||||
return core.GetStatus(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public UniTaskStatus UnsafeGetStatus()
|
|
||||||
{
|
|
||||||
return core.UnsafeGetStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ITriggerHandler
|
|
||||||
|
|
||||||
ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; }
|
|
||||||
ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; }
|
|
||||||
|
|
||||||
public void OnCanceled(CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
core.TrySetCanceled(cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnCompleted()
|
|
||||||
{
|
|
||||||
// Complete as Cancel.
|
|
||||||
core.TrySetCanceled(CancellationToken.None);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnError(Exception ex)
|
|
||||||
{
|
|
||||||
core.TrySetException(ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnNext(T value)
|
|
||||||
{
|
|
||||||
core.TrySetResult(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sealed class WithoutCurrentEnumerable : IUniTaskAsyncEnumerable<T>
|
|
||||||
{
|
{
|
||||||
readonly ReadOnlyAsyncReactiveProperty<T> parent;
|
readonly ReadOnlyAsyncReactiveProperty<T> parent;
|
||||||
|
|
||||||
|
|
|
@ -9,56 +9,6 @@ namespace Cysharp.Threading.Tasks
|
||||||
public static class CancellationTokenExtensions
|
public static class CancellationTokenExtensions
|
||||||
{
|
{
|
||||||
static readonly Action<object> cancellationTokenCallback = Callback;
|
static readonly Action<object> cancellationTokenCallback = Callback;
|
||||||
static readonly Action<object> disposeCallback = DisposeCallback;
|
|
||||||
|
|
||||||
public static CancellationToken ToCancellationToken(this UniTask task)
|
|
||||||
{
|
|
||||||
var cts = new CancellationTokenSource();
|
|
||||||
ToCancellationTokenCore(task, cts).Forget();
|
|
||||||
return cts.Token;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CancellationToken ToCancellationToken(this UniTask task, CancellationToken linkToken)
|
|
||||||
{
|
|
||||||
if (linkToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
return linkToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!linkToken.CanBeCanceled)
|
|
||||||
{
|
|
||||||
return ToCancellationToken(task);
|
|
||||||
}
|
|
||||||
|
|
||||||
var cts = CancellationTokenSource.CreateLinkedTokenSource(linkToken);
|
|
||||||
ToCancellationTokenCore(task, cts).Forget();
|
|
||||||
|
|
||||||
return cts.Token;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CancellationToken ToCancellationToken<T>(this UniTask<T> task)
|
|
||||||
{
|
|
||||||
return ToCancellationToken(task.AsUniTask());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CancellationToken ToCancellationToken<T>(this UniTask<T> task, CancellationToken linkToken)
|
|
||||||
{
|
|
||||||
return ToCancellationToken(task.AsUniTask(), linkToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
static async UniTaskVoid ToCancellationTokenCore(UniTask task, CancellationTokenSource cts)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await task;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
UniTaskScheduler.PublishUnobservedTaskException(ex);
|
|
||||||
}
|
|
||||||
cts.Cancel();
|
|
||||||
cts.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static (UniTask, CancellationTokenRegistration) ToUniTask(this CancellationToken cancellationToken)
|
public static (UniTask, CancellationTokenRegistration) ToUniTask(this CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
@ -125,17 +75,6 @@ namespace Cysharp.Threading.Tasks
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CancellationTokenRegistration AddTo(this IDisposable disposable, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
return cancellationToken.RegisterWithoutCaptureExecutionContext(disposeCallback, disposable);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void DisposeCallback(object state)
|
|
||||||
{
|
|
||||||
var d = (IDisposable)state;
|
|
||||||
d.Dispose();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct CancellationTokenAwaitable
|
public struct CancellationTokenAwaitable
|
||||||
|
|
|
@ -4,29 +4,31 @@ using System.Threading;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using Cysharp.Threading.Tasks.Triggers;
|
using Cysharp.Threading.Tasks.Triggers;
|
||||||
using System;
|
using System;
|
||||||
using Cysharp.Threading.Tasks.Internal;
|
|
||||||
|
|
||||||
namespace Cysharp.Threading.Tasks
|
namespace Cysharp.Threading.Tasks
|
||||||
{
|
{
|
||||||
|
public static class CancellationTokenSourceExtensions
|
||||||
public static partial class CancellationTokenSourceExtensions
|
|
||||||
{
|
{
|
||||||
readonly static Action<object> CancelCancellationTokenSourceStateDelegate = new Action<object>(CancelCancellationTokenSourceState);
|
public static void CancelAfterSlim(this CancellationTokenSource cts, int millisecondsDelay, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update)
|
||||||
|
{
|
||||||
static void CancelCancellationTokenSourceState(object state)
|
var delay = UniTask.Delay(millisecondsDelay, ignoreTimeScale, delayTiming, cts.Token);
|
||||||
|
CancelAfterCore(cts, delay).Forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void CancelAfterSlim(this CancellationTokenSource cts, TimeSpan delayTimeSpan, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update)
|
||||||
|
{
|
||||||
|
var delay = UniTask.Delay(delayTimeSpan, ignoreTimeScale, delayTiming, cts.Token);
|
||||||
|
CancelAfterCore(cts, delay).Forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
static async UniTaskVoid CancelAfterCore(CancellationTokenSource cts, UniTask delayTask)
|
||||||
|
{
|
||||||
|
var alreadyCanceled = await delayTask.SuppressCancellationThrow();
|
||||||
|
if (!alreadyCanceled)
|
||||||
{
|
{
|
||||||
var cts = (CancellationTokenSource)state;
|
|
||||||
cts.Cancel();
|
cts.Cancel();
|
||||||
|
cts.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IDisposable CancelAfterSlim(this CancellationTokenSource cts, int millisecondsDelay, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update)
|
|
||||||
{
|
|
||||||
return CancelAfterSlim(cts, TimeSpan.FromMilliseconds(millisecondsDelay), delayType, delayTiming);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IDisposable CancelAfterSlim(this CancellationTokenSource cts, TimeSpan delayTimeSpan, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update)
|
|
||||||
{
|
|
||||||
return PlayerLoopTimer.StartNew(delayTimeSpan, false, delayType, delayTiming, cts.Token, CancelCancellationTokenSourceStateDelegate, cts);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void RegisterRaiseCancelOnDestroy(this CancellationTokenSource cts, Component component)
|
public static void RegisterRaiseCancelOnDestroy(this CancellationTokenSource cts, Component component)
|
||||||
|
@ -37,7 +39,11 @@ namespace Cysharp.Threading.Tasks
|
||||||
public static void RegisterRaiseCancelOnDestroy(this CancellationTokenSource cts, GameObject gameObject)
|
public static void RegisterRaiseCancelOnDestroy(this CancellationTokenSource cts, GameObject gameObject)
|
||||||
{
|
{
|
||||||
var trigger = gameObject.GetAsyncDestroyTrigger();
|
var trigger = gameObject.GetAsyncDestroyTrigger();
|
||||||
trigger.CancellationToken.RegisterWithoutCaptureExecutionContext(CancelCancellationTokenSourceStateDelegate, cts);
|
trigger.CancellationToken.RegisterWithoutCaptureExecutionContext(state =>
|
||||||
|
{
|
||||||
|
var cts2 = (CancellationTokenSource)state;
|
||||||
|
cts2.Cancel();
|
||||||
|
}, cts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,12 +41,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||||
// runner is finished, return first.
|
// runner is finished, return first.
|
||||||
if (runner != null)
|
if (runner != null)
|
||||||
{
|
{
|
||||||
#if ENABLE_IL2CPP
|
|
||||||
// workaround for IL2CPP bug.
|
|
||||||
PlayerLoopHelper.AddContinuation(PlayerLoopTiming.LastPostLateUpdate, runner.ReturnAction);
|
|
||||||
#else
|
|
||||||
runner.Return();
|
runner.Return();
|
||||||
#endif
|
|
||||||
runner = null;
|
runner = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,12 +56,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||||
// runner is finished, return.
|
// runner is finished, return.
|
||||||
if (runner != null)
|
if (runner != null)
|
||||||
{
|
{
|
||||||
#if ENABLE_IL2CPP
|
|
||||||
// workaround for IL2CPP bug.
|
|
||||||
PlayerLoopHelper.AddContinuation(PlayerLoopTiming.LastPostLateUpdate, runner.ReturnAction);
|
|
||||||
#else
|
|
||||||
runner.Return();
|
runner.Return();
|
||||||
#endif
|
|
||||||
runner = null;
|
runner = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||||
|
|
||||||
using Cysharp.Threading.Tasks.Internal;
|
using Cysharp.Threading.Tasks.Internal;
|
||||||
using System;
|
using System;
|
||||||
|
@ -8,18 +8,10 @@ using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace Cysharp.Threading.Tasks.CompilerServices
|
namespace Cysharp.Threading.Tasks.CompilerServices
|
||||||
{
|
{
|
||||||
// #ENABLE_IL2CPP in this file is to avoid bug of IL2CPP VM.
|
|
||||||
// Issue is tracked on https://issuetracker.unity3d.com/issues/il2cpp-incorrect-results-when-calling-a-method-from-outside-class-in-a-struct
|
|
||||||
// but currently it is labeled `Won't Fix`.
|
|
||||||
|
|
||||||
internal interface IStateMachineRunner
|
internal interface IStateMachineRunner
|
||||||
{
|
{
|
||||||
Action MoveNext { get; }
|
Action MoveNext { get; }
|
||||||
void Return();
|
void Return();
|
||||||
|
|
||||||
#if ENABLE_IL2CPP
|
|
||||||
Action ReturnAction { get; }
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal interface IStateMachineRunnerPromise : IUniTaskSource
|
internal interface IStateMachineRunnerPromise : IUniTaskSource
|
||||||
|
@ -40,7 +32,6 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||||
|
|
||||||
internal static class StateMachineUtility
|
internal static class StateMachineUtility
|
||||||
{
|
{
|
||||||
// Get AsyncStateMachine internal state to check IL2CPP bug
|
|
||||||
public static int GetState(IAsyncStateMachine stateMachine)
|
public static int GetState(IAsyncStateMachine stateMachine)
|
||||||
{
|
{
|
||||||
var info = stateMachine.GetType().GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
|
var info = stateMachine.GetType().GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
|
||||||
|
@ -55,19 +46,16 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||||
static TaskPool<AsyncUniTaskVoid<TStateMachine>> pool;
|
static TaskPool<AsyncUniTaskVoid<TStateMachine>> pool;
|
||||||
|
|
||||||
#if ENABLE_IL2CPP
|
#if ENABLE_IL2CPP
|
||||||
public Action ReturnAction { get; }
|
IAsyncStateMachine stateMachine; // unfortunatelly boxed to fix IL2CPP issue.
|
||||||
#endif
|
#else
|
||||||
|
|
||||||
TStateMachine stateMachine;
|
TStateMachine stateMachine;
|
||||||
|
#endif
|
||||||
|
|
||||||
public Action MoveNext { get; }
|
public Action MoveNext { get; }
|
||||||
|
|
||||||
public AsyncUniTaskVoid()
|
public AsyncUniTaskVoid()
|
||||||
{
|
{
|
||||||
MoveNext = Run;
|
MoveNext = Run;
|
||||||
#if ENABLE_IL2CPP
|
|
||||||
ReturnAction = Return;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SetStateMachine(ref TStateMachine stateMachine, ref IStateMachineRunner runnerFieldRef)
|
public static void SetStateMachine(ref TStateMachine stateMachine, ref IStateMachineRunner runnerFieldRef)
|
||||||
|
@ -87,8 +75,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||||
TaskPool.RegisterSizeGetter(typeof(AsyncUniTaskVoid<TStateMachine>), () => pool.Size);
|
TaskPool.RegisterSizeGetter(typeof(AsyncUniTaskVoid<TStateMachine>), () => pool.Size);
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncUniTaskVoid<TStateMachine> nextNode;
|
public AsyncUniTaskVoid<TStateMachine> NextNode { get; set; }
|
||||||
public ref AsyncUniTaskVoid<TStateMachine> NextNode => ref nextNode;
|
|
||||||
|
|
||||||
public void Return()
|
public void Return()
|
||||||
{
|
{
|
||||||
|
@ -131,19 +118,18 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||||
static TaskPool<AsyncUniTask<TStateMachine>> pool;
|
static TaskPool<AsyncUniTask<TStateMachine>> pool;
|
||||||
|
|
||||||
#if ENABLE_IL2CPP
|
#if ENABLE_IL2CPP
|
||||||
readonly Action returnDelegate;
|
IAsyncStateMachine stateMachine; // unfortunatelly boxed to fix IL2CPP issue.
|
||||||
|
#else
|
||||||
|
TStateMachine stateMachine;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
public Action MoveNext { get; }
|
public Action MoveNext { get; }
|
||||||
|
|
||||||
TStateMachine stateMachine;
|
|
||||||
UniTaskCompletionSourceCore<AsyncUnit> core;
|
UniTaskCompletionSourceCore<AsyncUnit> core;
|
||||||
|
|
||||||
AsyncUniTask()
|
AsyncUniTask()
|
||||||
{
|
{
|
||||||
MoveNext = Run;
|
MoveNext = Run;
|
||||||
#if ENABLE_IL2CPP
|
|
||||||
returnDelegate = Return;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SetStateMachine(ref TStateMachine stateMachine, ref IStateMachineRunnerPromise runnerPromiseFieldRef)
|
public static void SetStateMachine(ref TStateMachine stateMachine, ref IStateMachineRunnerPromise runnerPromiseFieldRef)
|
||||||
|
@ -158,22 +144,13 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||||
result.stateMachine = stateMachine; // copy struct StateMachine(in release build).
|
result.stateMachine = stateMachine; // copy struct StateMachine(in release build).
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncUniTask<TStateMachine> nextNode;
|
public AsyncUniTask<TStateMachine> NextNode { get; set; }
|
||||||
public ref AsyncUniTask<TStateMachine> NextNode => ref nextNode;
|
|
||||||
|
|
||||||
static AsyncUniTask()
|
static AsyncUniTask()
|
||||||
{
|
{
|
||||||
TaskPool.RegisterSizeGetter(typeof(AsyncUniTask<TStateMachine>), () => pool.Size);
|
TaskPool.RegisterSizeGetter(typeof(AsyncUniTask<TStateMachine>), () => pool.Size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Return()
|
|
||||||
{
|
|
||||||
TaskTracker.RemoveTracking(this);
|
|
||||||
core.Reset();
|
|
||||||
stateMachine = default;
|
|
||||||
pool.TryPush(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TryReturn()
|
bool TryReturn()
|
||||||
{
|
{
|
||||||
TaskTracker.RemoveTracking(this);
|
TaskTracker.RemoveTracking(this);
|
||||||
|
@ -219,12 +196,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
#if ENABLE_IL2CPP
|
|
||||||
// workaround for IL2CPP bug.
|
|
||||||
PlayerLoopHelper.AddContinuation(PlayerLoopTiming.LastPostLateUpdate, returnDelegate);
|
|
||||||
#else
|
|
||||||
TryReturn();
|
TryReturn();
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,6 +217,14 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||||
{
|
{
|
||||||
core.OnCompleted(continuation, state, token);
|
core.OnCompleted(continuation, state, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~AsyncUniTask()
|
||||||
|
{
|
||||||
|
if (TryReturn())
|
||||||
|
{
|
||||||
|
GC.ReRegisterForFinalize(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal sealed class AsyncUniTask<TStateMachine, T> : IStateMachineRunnerPromise<T>, IUniTaskSource<T>, ITaskPoolNode<AsyncUniTask<TStateMachine, T>>
|
internal sealed class AsyncUniTask<TStateMachine, T> : IStateMachineRunnerPromise<T>, IUniTaskSource<T>, ITaskPoolNode<AsyncUniTask<TStateMachine, T>>
|
||||||
|
@ -253,20 +233,18 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||||
static TaskPool<AsyncUniTask<TStateMachine, T>> pool;
|
static TaskPool<AsyncUniTask<TStateMachine, T>> pool;
|
||||||
|
|
||||||
#if ENABLE_IL2CPP
|
#if ENABLE_IL2CPP
|
||||||
readonly Action returnDelegate;
|
IAsyncStateMachine stateMachine; // unfortunatelly boxed to fix IL2CPP issue.
|
||||||
|
#else
|
||||||
|
TStateMachine stateMachine;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
public Action MoveNext { get; }
|
public Action MoveNext { get; }
|
||||||
|
|
||||||
TStateMachine stateMachine;
|
|
||||||
UniTaskCompletionSourceCore<T> core;
|
UniTaskCompletionSourceCore<T> core;
|
||||||
|
|
||||||
AsyncUniTask()
|
AsyncUniTask()
|
||||||
{
|
{
|
||||||
MoveNext = Run;
|
MoveNext = Run;
|
||||||
#if ENABLE_IL2CPP
|
|
||||||
returnDelegate = Return;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SetStateMachine(ref TStateMachine stateMachine, ref IStateMachineRunnerPromise<T> runnerPromiseFieldRef)
|
public static void SetStateMachine(ref TStateMachine stateMachine, ref IStateMachineRunnerPromise<T> runnerPromiseFieldRef)
|
||||||
|
@ -279,24 +257,18 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||||
|
|
||||||
runnerPromiseFieldRef = result; // set runner before copied.
|
runnerPromiseFieldRef = result; // set runner before copied.
|
||||||
result.stateMachine = stateMachine; // copy struct StateMachine(in release build).
|
result.stateMachine = stateMachine; // copy struct StateMachine(in release build).
|
||||||
|
|
||||||
|
// UnityEngine.Debug.Log($"SetStateMachine State:" + StateMachineUtility.GetState(stateMachine));
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncUniTask<TStateMachine, T> nextNode;
|
|
||||||
public ref AsyncUniTask<TStateMachine, T> NextNode => ref nextNode;
|
public AsyncUniTask<TStateMachine, T> NextNode { get; set; }
|
||||||
|
|
||||||
static AsyncUniTask()
|
static AsyncUniTask()
|
||||||
{
|
{
|
||||||
TaskPool.RegisterSizeGetter(typeof(AsyncUniTask<TStateMachine, T>), () => pool.Size);
|
TaskPool.RegisterSizeGetter(typeof(AsyncUniTask<TStateMachine, T>), () => pool.Size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Return()
|
|
||||||
{
|
|
||||||
TaskTracker.RemoveTracking(this);
|
|
||||||
core.Reset();
|
|
||||||
stateMachine = default;
|
|
||||||
pool.TryPush(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TryReturn()
|
bool TryReturn()
|
||||||
{
|
{
|
||||||
TaskTracker.RemoveTracking(this);
|
TaskTracker.RemoveTracking(this);
|
||||||
|
@ -343,12 +315,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
#if ENABLE_IL2CPP
|
|
||||||
// workaround for IL2CPP bug.
|
|
||||||
PlayerLoopHelper.AddContinuation(PlayerLoopTiming.LastPostLateUpdate, returnDelegate);
|
|
||||||
#else
|
|
||||||
TryReturn();
|
TryReturn();
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -375,6 +342,14 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||||
{
|
{
|
||||||
core.OnCompleted(continuation, state, token);
|
core.OnCompleted(continuation, state, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~AsyncUniTask()
|
||||||
|
{
|
||||||
|
if (TryReturn())
|
||||||
|
{
|
||||||
|
GC.ReRegisterForFinalize(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,24 +32,10 @@ namespace Cysharp.Threading.Tasks
|
||||||
return new UniTask(EnumeratorPromise.Create(enumerator, timing, cancellationToken, out var token), token);
|
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>
|
sealed class EnumeratorPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<EnumeratorPromise>
|
||||||
{
|
{
|
||||||
static TaskPool<EnumeratorPromise> pool;
|
static TaskPool<EnumeratorPromise> pool;
|
||||||
EnumeratorPromise nextNode;
|
public EnumeratorPromise NextNode { get; set; }
|
||||||
public ref EnumeratorPromise NextNode => ref nextNode;
|
|
||||||
|
|
||||||
static EnumeratorPromise()
|
static EnumeratorPromise()
|
||||||
{
|
{
|
||||||
|
@ -58,9 +44,6 @@ namespace Cysharp.Threading.Tasks
|
||||||
|
|
||||||
IEnumerator innerEnumerator;
|
IEnumerator innerEnumerator;
|
||||||
CancellationToken cancellationToken;
|
CancellationToken cancellationToken;
|
||||||
int initialFrame;
|
|
||||||
bool loopRunning;
|
|
||||||
bool calledGetResult;
|
|
||||||
|
|
||||||
UniTaskCompletionSourceCore<object> core;
|
UniTaskCompletionSourceCore<object> core;
|
||||||
|
|
||||||
|
@ -83,18 +66,10 @@ namespace Cysharp.Threading.Tasks
|
||||||
|
|
||||||
result.innerEnumerator = ConsumeEnumerator(innerEnumerator);
|
result.innerEnumerator = ConsumeEnumerator(innerEnumerator);
|
||||||
result.cancellationToken = cancellationToken;
|
result.cancellationToken = cancellationToken;
|
||||||
result.loopRunning = true;
|
|
||||||
result.calledGetResult = false;
|
PlayerLoopHelper.AddAction(timing, result);
|
||||||
result.initialFrame = -1;
|
|
||||||
|
|
||||||
token = result.core.Version;
|
token = result.core.Version;
|
||||||
|
|
||||||
// run immediately.
|
|
||||||
if (result.MoveNext())
|
|
||||||
{
|
|
||||||
PlayerLoopHelper.AddAction(timing, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,17 +77,13 @@ namespace Cysharp.Threading.Tasks
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
calledGetResult = true;
|
|
||||||
core.GetResult(token);
|
core.GetResult(token);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
|
||||||
if (!loopRunning)
|
|
||||||
{
|
{
|
||||||
TryReturn();
|
TryReturn();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public UniTaskStatus GetStatus(short token)
|
public UniTaskStatus GetStatus(short token)
|
||||||
{
|
{
|
||||||
|
@ -131,38 +102,12 @@ namespace Cysharp.Threading.Tasks
|
||||||
|
|
||||||
public bool MoveNext()
|
public bool MoveNext()
|
||||||
{
|
{
|
||||||
if (calledGetResult)
|
|
||||||
{
|
|
||||||
loopRunning = false;
|
|
||||||
TryReturn();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (innerEnumerator == null) // invalid status, returned but loop running?
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cancellationToken.IsCancellationRequested)
|
if (cancellationToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
loopRunning = false;
|
|
||||||
core.TrySetCanceled(cancellationToken);
|
core.TrySetCanceled(cancellationToken);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (initialFrame == -1)
|
|
||||||
{
|
|
||||||
// Time can not touch in threadpool.
|
|
||||||
if (PlayerLoopHelper.IsMainThread)
|
|
||||||
{
|
|
||||||
initialFrame = Time.frameCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (initialFrame == Time.frameCount)
|
|
||||||
{
|
|
||||||
return true; // already executed in first frame, skip.
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (innerEnumerator.MoveNext())
|
if (innerEnumerator.MoveNext())
|
||||||
|
@ -172,12 +117,10 @@ namespace Cysharp.Threading.Tasks
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
loopRunning = false;
|
|
||||||
core.TrySetException(ex);
|
core.TrySetException(ex);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
loopRunning = false;
|
|
||||||
core.TrySetResult(null);
|
core.TrySetResult(null);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -188,10 +131,17 @@ namespace Cysharp.Threading.Tasks
|
||||||
core.Reset();
|
core.Reset();
|
||||||
innerEnumerator = default;
|
innerEnumerator = default;
|
||||||
cancellationToken = default;
|
cancellationToken = default;
|
||||||
|
|
||||||
return pool.TryPush(this);
|
return pool.TryPush(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~EnumeratorPromise()
|
||||||
|
{
|
||||||
|
if (TryReturn())
|
||||||
|
{
|
||||||
|
GC.ReRegisterForFinalize(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Unwrap YieldInstructions
|
// Unwrap YieldInstructions
|
||||||
|
|
||||||
static IEnumerator ConsumeEnumerator(IEnumerator enumerator)
|
static IEnumerator ConsumeEnumerator(IEnumerator enumerator)
|
||||||
|
@ -203,10 +153,11 @@ namespace Cysharp.Threading.Tasks
|
||||||
{
|
{
|
||||||
yield return null;
|
yield return null;
|
||||||
}
|
}
|
||||||
else if (current is CustomYieldInstruction cyi)
|
else if (current is CustomYieldInstruction)
|
||||||
{
|
{
|
||||||
// WWW, WaitForSecondsRealtime
|
// WWW, WaitForSecondsRealtime
|
||||||
while (cyi.keepWaiting)
|
var e2 = UnwrapWaitCustomYieldInstruction((CustomYieldInstruction)current);
|
||||||
|
while (e2.MoveNext())
|
||||||
{
|
{
|
||||||
yield return null;
|
yield return null;
|
||||||
}
|
}
|
||||||
|
@ -232,7 +183,7 @@ namespace Cysharp.Threading.Tasks
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
goto WARN;
|
yield return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (current is IEnumerator e3)
|
else if (current is IEnumerator e3)
|
||||||
|
@ -245,14 +196,17 @@ namespace Cysharp.Threading.Tasks
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
goto WARN;
|
// WaitForEndOfFrame, WaitForFixedUpdate, others.
|
||||||
|
yield return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
// WWW and others as CustomYieldInstruction.
|
||||||
|
static IEnumerator UnwrapWaitCustomYieldInstruction(CustomYieldInstruction yieldInstruction)
|
||||||
WARN:
|
{
|
||||||
// WaitForEndOfFrame, WaitForFixedUpdate, others.
|
while (yieldInstruction.keepWaiting)
|
||||||
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;
|
yield return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -262,12 +216,12 @@ namespace Cysharp.Threading.Tasks
|
||||||
static IEnumerator UnwrapWaitForSeconds(WaitForSeconds waitForSeconds)
|
static IEnumerator UnwrapWaitForSeconds(WaitForSeconds waitForSeconds)
|
||||||
{
|
{
|
||||||
var second = (float)waitForSeconds_Seconds.GetValue(waitForSeconds);
|
var second = (float)waitForSeconds_Seconds.GetValue(waitForSeconds);
|
||||||
var elapsed = 0.0f;
|
var startTime = DateTimeOffset.UtcNow;
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
yield return null;
|
yield return null;
|
||||||
|
|
||||||
elapsed += Time.deltaTime;
|
var elapsed = (DateTimeOffset.UtcNow - startTime).TotalSeconds;
|
||||||
if (elapsed >= second)
|
if (elapsed >= second)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
|
@ -285,3 +239,4 @@ namespace Cysharp.Threading.Tasks
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
654
src/UniTask/Assets/Plugins/UniTask/Runtime/External/AddressableAsyncExtensions.cs
vendored
Normal file
654
src/UniTask/Assets/Plugins/UniTask/Runtime/External/AddressableAsyncExtensions.cs
vendored
Normal file
|
@ -0,0 +1,654 @@
|
||||||
|
// asmdef Version Defines, enabled when com.unity.addressables is imported.
|
||||||
|
|
||||||
|
#if UNITASK_ADDRESSABLE_SUPPORT
|
||||||
|
|
||||||
|
using Cysharp.Threading.Tasks.Internal;
|
||||||
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.ExceptionServices;
|
||||||
|
using System.Threading;
|
||||||
|
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||||
|
|
||||||
|
namespace Cysharp.Threading.Tasks
|
||||||
|
{
|
||||||
|
public static class AddressableAsyncExtensions
|
||||||
|
{
|
||||||
|
#region AsyncOperationHandle
|
||||||
|
|
||||||
|
public static AsyncOperationHandleAwaiter GetAwaiter(this AsyncOperationHandle handle)
|
||||||
|
{
|
||||||
|
return new AsyncOperationHandleAwaiter(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UniTask WithCancellation(this AsyncOperationHandle handle, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (handle.IsDone) return UniTask.CompletedTask;
|
||||||
|
return new UniTask(AsyncOperationHandleWithCancellationSource.Create(handle, cancellationToken, out var token), token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UniTask ToUniTask(this AsyncOperationHandle handle, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
|
||||||
|
{
|
||||||
|
if (handle.IsDone) return UniTask.CompletedTask;
|
||||||
|
return new UniTask(AsyncOperationHandleConfiguredSource.Create(handle, timing, progress, cancellationToken, out var token), token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct AsyncOperationHandleAwaiter : ICriticalNotifyCompletion
|
||||||
|
{
|
||||||
|
AsyncOperationHandle handle;
|
||||||
|
Action<AsyncOperationHandle> continuationAction;
|
||||||
|
|
||||||
|
public AsyncOperationHandleAwaiter(AsyncOperationHandle handle)
|
||||||
|
{
|
||||||
|
this.handle = handle;
|
||||||
|
this.continuationAction = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsCompleted => handle.IsDone;
|
||||||
|
|
||||||
|
public void GetResult()
|
||||||
|
{
|
||||||
|
if (continuationAction != null)
|
||||||
|
{
|
||||||
|
handle.Completed -= continuationAction;
|
||||||
|
continuationAction = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handle.Status == AsyncOperationStatus.Failed)
|
||||||
|
{
|
||||||
|
var e = handle.OperationException;
|
||||||
|
handle = default;
|
||||||
|
ExceptionDispatchInfo.Capture(e).Throw();
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = handle.Result;
|
||||||
|
handle = default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnCompleted(Action continuation)
|
||||||
|
{
|
||||||
|
UnsafeOnCompleted(continuation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UnsafeOnCompleted(Action continuation)
|
||||||
|
{
|
||||||
|
Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction);
|
||||||
|
continuationAction = PooledDelegate<AsyncOperationHandle>.Create(continuation);
|
||||||
|
handle.Completed += continuationAction;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class AsyncOperationHandleWithCancellationSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleWithCancellationSource>
|
||||||
|
{
|
||||||
|
static TaskPool<AsyncOperationHandleWithCancellationSource> pool;
|
||||||
|
public AsyncOperationHandleWithCancellationSource NextNode { get; set; }
|
||||||
|
|
||||||
|
static AsyncOperationHandleWithCancellationSource()
|
||||||
|
{
|
||||||
|
TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleWithCancellationSource), () => pool.Size);
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly Action<AsyncOperationHandle> continuationAction;
|
||||||
|
AsyncOperationHandle handle;
|
||||||
|
CancellationToken cancellationToken;
|
||||||
|
bool completed;
|
||||||
|
|
||||||
|
UniTaskCompletionSourceCore<AsyncUnit> core;
|
||||||
|
|
||||||
|
AsyncOperationHandleWithCancellationSource()
|
||||||
|
{
|
||||||
|
continuationAction = Continuation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IUniTaskSource Create(AsyncOperationHandle handle, CancellationToken cancellationToken, out short token)
|
||||||
|
{
|
||||||
|
if (cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pool.TryPop(out var result))
|
||||||
|
{
|
||||||
|
result = new AsyncOperationHandleWithCancellationSource();
|
||||||
|
}
|
||||||
|
|
||||||
|
result.handle = handle;
|
||||||
|
result.cancellationToken = cancellationToken;
|
||||||
|
result.completed = false;
|
||||||
|
|
||||||
|
TaskTracker.TrackActiveTask(result, 3);
|
||||||
|
|
||||||
|
PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, result);
|
||||||
|
|
||||||
|
handle.Completed += result.continuationAction;
|
||||||
|
|
||||||
|
token = result.core.Version;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Continuation(AsyncOperationHandle _)
|
||||||
|
{
|
||||||
|
handle.Completed -= continuationAction;
|
||||||
|
|
||||||
|
if (completed)
|
||||||
|
{
|
||||||
|
TryReturn();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
completed = true;
|
||||||
|
if (handle.Status == AsyncOperationStatus.Failed)
|
||||||
|
{
|
||||||
|
core.TrySetException(handle.OperationException);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
core.TrySetResult(AsyncUnit.Default);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetResult(short token)
|
||||||
|
{
|
||||||
|
core.GetResult(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UniTaskStatus GetStatus(short token)
|
||||||
|
{
|
||||||
|
return core.GetStatus(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UniTaskStatus UnsafeGetStatus()
|
||||||
|
{
|
||||||
|
return core.UnsafeGetStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||||
|
{
|
||||||
|
core.OnCompleted(continuation, state, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool MoveNext()
|
||||||
|
{
|
||||||
|
if (completed)
|
||||||
|
{
|
||||||
|
TryReturn();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
completed = true;
|
||||||
|
core.TrySetCanceled(cancellationToken);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TryReturn()
|
||||||
|
{
|
||||||
|
TaskTracker.RemoveTracking(this);
|
||||||
|
core.Reset();
|
||||||
|
handle = default;
|
||||||
|
cancellationToken = default;
|
||||||
|
return pool.TryPush(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
~AsyncOperationHandleWithCancellationSource()
|
||||||
|
{
|
||||||
|
if (TryReturn())
|
||||||
|
{
|
||||||
|
GC.ReRegisterForFinalize(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class AsyncOperationHandleConfiguredSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleConfiguredSource>
|
||||||
|
{
|
||||||
|
static TaskPool<AsyncOperationHandleConfiguredSource> pool;
|
||||||
|
public AsyncOperationHandleConfiguredSource NextNode { get; set; }
|
||||||
|
|
||||||
|
static AsyncOperationHandleConfiguredSource()
|
||||||
|
{
|
||||||
|
TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleConfiguredSource), () => pool.Size);
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncOperationHandle handle;
|
||||||
|
IProgress<float> progress;
|
||||||
|
CancellationToken cancellationToken;
|
||||||
|
|
||||||
|
UniTaskCompletionSourceCore<AsyncUnit> core;
|
||||||
|
|
||||||
|
AsyncOperationHandleConfiguredSource()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IUniTaskSource Create(AsyncOperationHandle handle, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, out short token)
|
||||||
|
{
|
||||||
|
if (cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pool.TryPop(out var result))
|
||||||
|
{
|
||||||
|
result = new AsyncOperationHandleConfiguredSource();
|
||||||
|
}
|
||||||
|
|
||||||
|
result.handle = handle;
|
||||||
|
result.progress = progress;
|
||||||
|
result.cancellationToken = cancellationToken;
|
||||||
|
|
||||||
|
TaskTracker.TrackActiveTask(result, 3);
|
||||||
|
|
||||||
|
PlayerLoopHelper.AddAction(timing, result);
|
||||||
|
|
||||||
|
token = result.core.Version;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetResult(short token)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
core.GetResult(token);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
TryReturn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public UniTaskStatus GetStatus(short token)
|
||||||
|
{
|
||||||
|
return core.GetStatus(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UniTaskStatus UnsafeGetStatus()
|
||||||
|
{
|
||||||
|
return core.UnsafeGetStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||||
|
{
|
||||||
|
core.OnCompleted(continuation, state, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool MoveNext()
|
||||||
|
{
|
||||||
|
if (cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
core.TrySetCanceled(cancellationToken);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (progress != null)
|
||||||
|
{
|
||||||
|
progress.Report(handle.PercentComplete);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handle.IsDone)
|
||||||
|
{
|
||||||
|
if (handle.Status == AsyncOperationStatus.Failed)
|
||||||
|
{
|
||||||
|
core.TrySetException(handle.OperationException);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
core.TrySetResult(AsyncUnit.Default);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TryReturn()
|
||||||
|
{
|
||||||
|
core.Reset();
|
||||||
|
TaskTracker.RemoveTracking(this);
|
||||||
|
handle = default;
|
||||||
|
progress = default;
|
||||||
|
cancellationToken = default;
|
||||||
|
return pool.TryPush(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
~AsyncOperationHandleConfiguredSource()
|
||||||
|
{
|
||||||
|
if (TryReturn())
|
||||||
|
{
|
||||||
|
GC.ReRegisterForFinalize(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region AsyncOperationHandle_T
|
||||||
|
|
||||||
|
public static AsyncOperationHandleAwaiter<T> GetAwaiter<T>(this AsyncOperationHandle<T> handle)
|
||||||
|
{
|
||||||
|
return new AsyncOperationHandleAwaiter<T>(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UniTask<T> WithCancellation<T>(this AsyncOperationHandle<T> handle, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (handle.IsDone) return UniTask.FromResult(handle.Result);
|
||||||
|
return new UniTask<T>(AsyncOperationHandleWithCancellationSource<T>.Create(handle, cancellationToken, out var token), token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UniTask<T> ToUniTask<T>(this AsyncOperationHandle<T> handle, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
|
||||||
|
{
|
||||||
|
if (handle.IsDone) return UniTask.FromResult(handle.Result);
|
||||||
|
return new UniTask<T>(AsyncOperationHandleConfiguredSource<T>.Create(handle, timing, progress, cancellationToken, out var token), token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct AsyncOperationHandleAwaiter<T> : ICriticalNotifyCompletion
|
||||||
|
{
|
||||||
|
AsyncOperationHandle<T> handle;
|
||||||
|
Action<AsyncOperationHandle> continuationAction;
|
||||||
|
|
||||||
|
public AsyncOperationHandleAwaiter(AsyncOperationHandle<T> handle)
|
||||||
|
{
|
||||||
|
this.handle = handle;
|
||||||
|
this.continuationAction = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsCompleted => handle.IsDone;
|
||||||
|
|
||||||
|
public T GetResult()
|
||||||
|
{
|
||||||
|
if (continuationAction != null)
|
||||||
|
{
|
||||||
|
handle.CompletedTypeless -= continuationAction;
|
||||||
|
continuationAction = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handle.Status == AsyncOperationStatus.Failed)
|
||||||
|
{
|
||||||
|
var e = handle.OperationException;
|
||||||
|
handle = default;
|
||||||
|
ExceptionDispatchInfo.Capture(e).Throw();
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = handle.Result;
|
||||||
|
handle = default;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnCompleted(Action continuation)
|
||||||
|
{
|
||||||
|
UnsafeOnCompleted(continuation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UnsafeOnCompleted(Action continuation)
|
||||||
|
{
|
||||||
|
Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction);
|
||||||
|
continuationAction = PooledDelegate<AsyncOperationHandle>.Create(continuation);
|
||||||
|
handle.CompletedTypeless += continuationAction;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class AsyncOperationHandleWithCancellationSource<T> : IUniTaskSource<T>, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleWithCancellationSource<T>>
|
||||||
|
{
|
||||||
|
static TaskPool<AsyncOperationHandleWithCancellationSource<T>> pool;
|
||||||
|
public AsyncOperationHandleWithCancellationSource<T> NextNode { get; set; }
|
||||||
|
|
||||||
|
static AsyncOperationHandleWithCancellationSource()
|
||||||
|
{
|
||||||
|
TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleWithCancellationSource<T>), () => pool.Size);
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly Action<AsyncOperationHandle<T>> continuationAction;
|
||||||
|
AsyncOperationHandle<T> handle;
|
||||||
|
CancellationToken cancellationToken;
|
||||||
|
bool completed;
|
||||||
|
|
||||||
|
UniTaskCompletionSourceCore<T> core;
|
||||||
|
|
||||||
|
AsyncOperationHandleWithCancellationSource()
|
||||||
|
{
|
||||||
|
continuationAction = Continuation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IUniTaskSource<T> Create(AsyncOperationHandle<T> handle, CancellationToken cancellationToken, out short token)
|
||||||
|
{
|
||||||
|
if (cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
return AutoResetUniTaskCompletionSource<T>.CreateFromCanceled(cancellationToken, out token);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pool.TryPop(out var result))
|
||||||
|
{
|
||||||
|
result = new AsyncOperationHandleWithCancellationSource<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
result.handle = handle;
|
||||||
|
result.cancellationToken = cancellationToken;
|
||||||
|
result.completed = false;
|
||||||
|
|
||||||
|
TaskTracker.TrackActiveTask(result, 3);
|
||||||
|
|
||||||
|
PlayerLoopHelper.AddAction(PlayerLoopTiming.Update, result);
|
||||||
|
|
||||||
|
handle.Completed += result.continuationAction;
|
||||||
|
|
||||||
|
token = result.core.Version;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Continuation(AsyncOperationHandle<T> _)
|
||||||
|
{
|
||||||
|
handle.Completed -= continuationAction;
|
||||||
|
|
||||||
|
if (completed)
|
||||||
|
{
|
||||||
|
TryReturn();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
completed = true;
|
||||||
|
if (handle.Status == AsyncOperationStatus.Failed)
|
||||||
|
{
|
||||||
|
core.TrySetException(handle.OperationException);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
core.TrySetResult(handle.Result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public T GetResult(short token)
|
||||||
|
{
|
||||||
|
return core.GetResult(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IUniTaskSource.GetResult(short token)
|
||||||
|
{
|
||||||
|
GetResult(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UniTaskStatus GetStatus(short token)
|
||||||
|
{
|
||||||
|
return core.GetStatus(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UniTaskStatus UnsafeGetStatus()
|
||||||
|
{
|
||||||
|
return core.UnsafeGetStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||||
|
{
|
||||||
|
core.OnCompleted(continuation, state, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool MoveNext()
|
||||||
|
{
|
||||||
|
if (completed)
|
||||||
|
{
|
||||||
|
TryReturn();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
completed = true;
|
||||||
|
core.TrySetCanceled(cancellationToken);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TryReturn()
|
||||||
|
{
|
||||||
|
TaskTracker.RemoveTracking(this);
|
||||||
|
core.Reset();
|
||||||
|
handle = default;
|
||||||
|
cancellationToken = default;
|
||||||
|
return pool.TryPush(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
~AsyncOperationHandleWithCancellationSource()
|
||||||
|
{
|
||||||
|
if (TryReturn())
|
||||||
|
{
|
||||||
|
GC.ReRegisterForFinalize(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class AsyncOperationHandleConfiguredSource<T> : IUniTaskSource<T>, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleConfiguredSource<T>>
|
||||||
|
{
|
||||||
|
static TaskPool<AsyncOperationHandleConfiguredSource<T>> pool;
|
||||||
|
public AsyncOperationHandleConfiguredSource<T> NextNode { get; set; }
|
||||||
|
|
||||||
|
static AsyncOperationHandleConfiguredSource()
|
||||||
|
{
|
||||||
|
TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleConfiguredSource<T>), () => pool.Size);
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncOperationHandle<T> handle;
|
||||||
|
IProgress<float> progress;
|
||||||
|
CancellationToken cancellationToken;
|
||||||
|
|
||||||
|
UniTaskCompletionSourceCore<T> core;
|
||||||
|
|
||||||
|
AsyncOperationHandleConfiguredSource()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IUniTaskSource<T> Create(AsyncOperationHandle<T> handle, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, out short token)
|
||||||
|
{
|
||||||
|
if (cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
return AutoResetUniTaskCompletionSource<T>.CreateFromCanceled(cancellationToken, out token);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pool.TryPop(out var result))
|
||||||
|
{
|
||||||
|
result = new AsyncOperationHandleConfiguredSource<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
result.handle = handle;
|
||||||
|
result.progress = progress;
|
||||||
|
result.cancellationToken = cancellationToken;
|
||||||
|
|
||||||
|
TaskTracker.TrackActiveTask(result, 3);
|
||||||
|
|
||||||
|
PlayerLoopHelper.AddAction(timing, result);
|
||||||
|
|
||||||
|
token = result.core.Version;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T GetResult(short token)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return core.GetResult(token);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
TryReturn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IUniTaskSource.GetResult(short token)
|
||||||
|
{
|
||||||
|
GetResult(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UniTaskStatus GetStatus(short token)
|
||||||
|
{
|
||||||
|
return core.GetStatus(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UniTaskStatus UnsafeGetStatus()
|
||||||
|
{
|
||||||
|
return core.UnsafeGetStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||||
|
{
|
||||||
|
core.OnCompleted(continuation, state, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool MoveNext()
|
||||||
|
{
|
||||||
|
if (cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
core.TrySetCanceled(cancellationToken);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (progress != null)
|
||||||
|
{
|
||||||
|
progress.Report(handle.PercentComplete);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handle.IsDone)
|
||||||
|
{
|
||||||
|
if (handle.Status == AsyncOperationStatus.Failed)
|
||||||
|
{
|
||||||
|
core.TrySetException(handle.OperationException);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
core.TrySetResult(handle.Result);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TryReturn()
|
||||||
|
{
|
||||||
|
TaskTracker.RemoveTracking(this);
|
||||||
|
core.Reset();
|
||||||
|
handle = default;
|
||||||
|
progress = default;
|
||||||
|
cancellationToken = default;
|
||||||
|
return pool.TryPush(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
~AsyncOperationHandleConfiguredSource()
|
||||||
|
{
|
||||||
|
if (TryReturn())
|
||||||
|
{
|
||||||
|
GC.ReRegisterForFinalize(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,483 +0,0 @@
|
||||||
// asmdef Version Defines, enabled when com.unity.addressables is imported.
|
|
||||||
|
|
||||||
#if UNITASK_ADDRESSABLE_SUPPORT
|
|
||||||
|
|
||||||
using Cysharp.Threading.Tasks.Internal;
|
|
||||||
using System;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.ExceptionServices;
|
|
||||||
using System.Threading;
|
|
||||||
using UnityEngine.AddressableAssets;
|
|
||||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
|
||||||
|
|
||||||
namespace Cysharp.Threading.Tasks
|
|
||||||
{
|
|
||||||
public static class AddressablesAsyncExtensions
|
|
||||||
{
|
|
||||||
#region AsyncOperationHandle
|
|
||||||
|
|
||||||
public static UniTask.Awaiter GetAwaiter(this AsyncOperationHandle handle)
|
|
||||||
{
|
|
||||||
return ToUniTask(handle).GetAwaiter();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static UniTask WithCancellation(this AsyncOperationHandle handle, CancellationToken cancellationToken, bool cancelImmediately = false, bool autoReleaseWhenCanceled = false)
|
|
||||||
{
|
|
||||||
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), bool cancelImmediately = false, bool autoReleaseWhenCanceled = false)
|
|
||||||
{
|
|
||||||
if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled(cancellationToken);
|
|
||||||
|
|
||||||
if (!handle.IsValid())
|
|
||||||
{
|
|
||||||
// autoReleaseHandle:true handle is invalid(immediately internal handle == null) so return completed.
|
|
||||||
return UniTask.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (handle.IsDone)
|
|
||||||
{
|
|
||||||
if (handle.Status == AsyncOperationStatus.Failed)
|
|
||||||
{
|
|
||||||
return UniTask.FromException(handle.OperationException);
|
|
||||||
}
|
|
||||||
return UniTask.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new UniTask(AsyncOperationHandleConfiguredSource.Create(handle, timing, progress, cancellationToken, cancelImmediately, autoReleaseWhenCanceled, out var token), token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct AsyncOperationHandleAwaiter : ICriticalNotifyCompletion
|
|
||||||
{
|
|
||||||
AsyncOperationHandle handle;
|
|
||||||
Action<AsyncOperationHandle> continuationAction;
|
|
||||||
|
|
||||||
public AsyncOperationHandleAwaiter(AsyncOperationHandle handle)
|
|
||||||
{
|
|
||||||
this.handle = handle;
|
|
||||||
this.continuationAction = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsCompleted => handle.IsDone;
|
|
||||||
|
|
||||||
public void GetResult()
|
|
||||||
{
|
|
||||||
if (continuationAction != null)
|
|
||||||
{
|
|
||||||
handle.Completed -= continuationAction;
|
|
||||||
continuationAction = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (handle.Status == AsyncOperationStatus.Failed)
|
|
||||||
{
|
|
||||||
var e = handle.OperationException;
|
|
||||||
handle = default;
|
|
||||||
ExceptionDispatchInfo.Capture(e).Throw();
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = handle.Result;
|
|
||||||
handle = default;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnCompleted(Action continuation)
|
|
||||||
{
|
|
||||||
UnsafeOnCompleted(continuation);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UnsafeOnCompleted(Action continuation)
|
|
||||||
{
|
|
||||||
Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction);
|
|
||||||
continuationAction = PooledDelegate<AsyncOperationHandle>.Create(continuation);
|
|
||||||
handle.Completed += continuationAction;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sealed class AsyncOperationHandleConfiguredSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleConfiguredSource>
|
|
||||||
{
|
|
||||||
static TaskPool<AsyncOperationHandleConfiguredSource> pool;
|
|
||||||
AsyncOperationHandleConfiguredSource nextNode;
|
|
||||||
public ref AsyncOperationHandleConfiguredSource NextNode => ref nextNode;
|
|
||||||
|
|
||||||
static AsyncOperationHandleConfiguredSource()
|
|
||||||
{
|
|
||||||
TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleConfiguredSource), () => pool.Size);
|
|
||||||
}
|
|
||||||
|
|
||||||
readonly Action<AsyncOperationHandle> completedCallback;
|
|
||||||
AsyncOperationHandle handle;
|
|
||||||
CancellationToken cancellationToken;
|
|
||||||
CancellationTokenRegistration cancellationTokenRegistration;
|
|
||||||
IProgress<float> progress;
|
|
||||||
bool autoReleaseWhenCanceled;
|
|
||||||
bool cancelImmediately;
|
|
||||||
bool completed;
|
|
||||||
|
|
||||||
UniTaskCompletionSourceCore<AsyncUnit> core;
|
|
||||||
|
|
||||||
AsyncOperationHandleConfiguredSource()
|
|
||||||
{
|
|
||||||
completedCallback = HandleCompleted;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IUniTaskSource Create(AsyncOperationHandle handle, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, bool cancelImmediately, bool autoReleaseWhenCanceled, out short token)
|
|
||||||
{
|
|
||||||
if (cancellationToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pool.TryPop(out var result))
|
|
||||||
{
|
|
||||||
result = new AsyncOperationHandleConfiguredSource();
|
|
||||||
}
|
|
||||||
|
|
||||||
result.handle = handle;
|
|
||||||
result.progress = progress;
|
|
||||||
result.cancellationToken = cancellationToken;
|
|
||||||
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.completedCallback;
|
|
||||||
|
|
||||||
token = result.core.Version;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HandleCompleted(AsyncOperationHandle _)
|
|
||||||
{
|
|
||||||
if (handle.IsValid())
|
|
||||||
{
|
|
||||||
handle.Completed -= completedCallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (completed)
|
|
||||||
{
|
|
||||||
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
|
|
||||||
{
|
|
||||||
core.TrySetResult(AsyncUnit.Default);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool MoveNext()
|
|
||||||
{
|
|
||||||
if (completed)
|
|
||||||
{
|
|
||||||
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.GetDownloadStatus().Percent);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TryReturn()
|
|
||||||
{
|
|
||||||
TaskTracker.RemoveTracking(this);
|
|
||||||
core.Reset();
|
|
||||||
handle = default;
|
|
||||||
progress = default;
|
|
||||||
cancellationToken = default;
|
|
||||||
cancellationTokenRegistration.Dispose();
|
|
||||||
return pool.TryPush(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#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, bool cancelImmediately = false, bool autoReleaseWhenCanceled = false)
|
|
||||||
{
|
|
||||||
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), bool cancelImmediately = false, bool autoReleaseWhenCanceled = false)
|
|
||||||
{
|
|
||||||
if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled<T>(cancellationToken);
|
|
||||||
|
|
||||||
if (!handle.IsValid())
|
|
||||||
{
|
|
||||||
throw new Exception("Attempting to use an invalid operation handle");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (handle.IsDone)
|
|
||||||
{
|
|
||||||
if (handle.Status == AsyncOperationStatus.Failed)
|
|
||||||
{
|
|
||||||
return UniTask.FromException<T>(handle.OperationException);
|
|
||||||
}
|
|
||||||
return UniTask.FromResult(handle.Result);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
AsyncOperationHandleConfiguredSource<T> nextNode;
|
|
||||||
public ref AsyncOperationHandleConfiguredSource<T> NextNode => ref nextNode;
|
|
||||||
|
|
||||||
static AsyncOperationHandleConfiguredSource()
|
|
||||||
{
|
|
||||||
TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleConfiguredSource<T>), () => pool.Size);
|
|
||||||
}
|
|
||||||
|
|
||||||
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()
|
|
||||||
{
|
|
||||||
completedCallback = HandleCompleted;
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
return AutoResetUniTaskCompletionSource<T>.CreateFromCanceled(cancellationToken, out token);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pool.TryPop(out var result))
|
|
||||||
{
|
|
||||||
result = new AsyncOperationHandleConfiguredSource<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
result.handle = handle;
|
|
||||||
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.completedCallback;
|
|
||||||
|
|
||||||
token = result.core.Version;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HandleCompleted(AsyncOperationHandle<T> argHandle)
|
|
||||||
{
|
|
||||||
if (handle.IsValid())
|
|
||||||
{
|
|
||||||
handle.Completed -= completedCallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (completed)
|
|
||||||
{
|
|
||||||
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
|
|
||||||
{
|
|
||||||
core.TrySetResult(argHandle.Result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public T GetResult(short token)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return core.GetResult(token);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
|
|
||||||
{
|
|
||||||
TryReturn();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
TaskTracker.RemoveTracking(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void IUniTaskSource.GetResult(short token)
|
|
||||||
{
|
|
||||||
GetResult(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public UniTaskStatus GetStatus(short token)
|
|
||||||
{
|
|
||||||
return core.GetStatus(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public UniTaskStatus UnsafeGetStatus()
|
|
||||||
{
|
|
||||||
return core.UnsafeGetStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
|
||||||
{
|
|
||||||
core.OnCompleted(continuation, state, token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool MoveNext()
|
|
||||||
{
|
|
||||||
if (completed)
|
|
||||||
{
|
|
||||||
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.GetDownloadStatus().Percent);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TryReturn()
|
|
||||||
{
|
|
||||||
TaskTracker.RemoveTracking(this);
|
|
||||||
core.Reset();
|
|
||||||
handle = default;
|
|
||||||
progress = default;
|
|
||||||
cancellationToken = default;
|
|
||||||
cancellationTokenRegistration.Dispose();
|
|
||||||
return pool.TryPush(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,28 +0,0 @@
|
||||||
{
|
|
||||||
"name": "UniTask.Addressables",
|
|
||||||
"references": [
|
|
||||||
"UniTask",
|
|
||||||
"Unity.ResourceManager",
|
|
||||||
"Unity.Addressables"
|
|
||||||
],
|
|
||||||
"includePlatforms": [],
|
|
||||||
"excludePlatforms": [],
|
|
||||||
"allowUnsafeCode": false,
|
|
||||||
"overrideReferences": false,
|
|
||||||
"precompiledReferences": [],
|
|
||||||
"autoReferenced": true,
|
|
||||||
"defineConstraints": [],
|
|
||||||
"versionDefines": [
|
|
||||||
{
|
|
||||||
"name": "com.unity.addressables",
|
|
||||||
"expression": "",
|
|
||||||
"define": "UNITASK_ADDRESSABLE_SUPPORT"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "com.unity.addressables.cn",
|
|
||||||
"expression": "",
|
|
||||||
"define": "UNITASK_ADDRESSABLE_SUPPORT"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"noEngineReferences": false
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 593a5b492d29ac6448b1ebf7f035ef33
|
|
||||||
AssemblyDefinitionImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,8 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 25cb2f742bfeb1d48a4e65d3140b955d
|
|
||||||
folderAsset: yes
|
|
||||||
DefaultImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,436 +0,0 @@
|
||||||
// asmdef Version Defines, enabled when com.demigiant.dotween is imported.
|
|
||||||
|
|
||||||
#if UNITASK_DOTWEEN_SUPPORT
|
|
||||||
|
|
||||||
using Cysharp.Threading.Tasks.Internal;
|
|
||||||
using DG.Tweening;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace Cysharp.Threading.Tasks
|
|
||||||
{
|
|
||||||
public enum TweenCancelBehaviour
|
|
||||||
{
|
|
||||||
Kill,
|
|
||||||
KillWithCompleteCallback,
|
|
||||||
Complete,
|
|
||||||
CompleteWithSequenceCallback,
|
|
||||||
CancelAwait,
|
|
||||||
|
|
||||||
// AndCancelAwait
|
|
||||||
KillAndCancelAwait,
|
|
||||||
KillWithCompleteCallbackAndCancelAwait,
|
|
||||||
CompleteAndCancelAwait,
|
|
||||||
CompleteWithSequenceCallbackAndCancelAwait
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class DOTweenAsyncExtensions
|
|
||||||
{
|
|
||||||
enum CallbackType
|
|
||||||
{
|
|
||||||
Kill,
|
|
||||||
Complete,
|
|
||||||
Pause,
|
|
||||||
Play,
|
|
||||||
Rewind,
|
|
||||||
StepComplete
|
|
||||||
}
|
|
||||||
|
|
||||||
public static TweenAwaiter GetAwaiter(this Tween tween)
|
|
||||||
{
|
|
||||||
return new TweenAwaiter(tween);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static UniTask WithCancellation(this Tween tween, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
Error.ThrowArgumentNullException(tween, nameof(tween));
|
|
||||||
|
|
||||||
if (!tween.IsActive()) return UniTask.CompletedTask;
|
|
||||||
return new UniTask(TweenConfiguredSource.Create(tween, TweenCancelBehaviour.Kill, cancellationToken, CallbackType.Kill, out var token), token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static UniTask ToUniTask(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
Error.ThrowArgumentNullException(tween, nameof(tween));
|
|
||||||
|
|
||||||
if (!tween.IsActive()) return UniTask.CompletedTask;
|
|
||||||
return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, CallbackType.Kill, out var token), token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static UniTask AwaitForComplete(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
Error.ThrowArgumentNullException(tween, nameof(tween));
|
|
||||||
|
|
||||||
if (!tween.IsActive()) return UniTask.CompletedTask;
|
|
||||||
return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, CallbackType.Complete, out var token), token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static UniTask AwaitForPause(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
Error.ThrowArgumentNullException(tween, nameof(tween));
|
|
||||||
|
|
||||||
if (!tween.IsActive()) return UniTask.CompletedTask;
|
|
||||||
return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, CallbackType.Pause, out var token), token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static UniTask AwaitForPlay(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
Error.ThrowArgumentNullException(tween, nameof(tween));
|
|
||||||
|
|
||||||
if (!tween.IsActive()) return UniTask.CompletedTask;
|
|
||||||
return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, CallbackType.Play, out var token), token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static UniTask AwaitForRewind(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
Error.ThrowArgumentNullException(tween, nameof(tween));
|
|
||||||
|
|
||||||
if (!tween.IsActive()) return UniTask.CompletedTask;
|
|
||||||
return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, CallbackType.Rewind, out var token), token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static UniTask AwaitForStepComplete(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
Error.ThrowArgumentNullException(tween, nameof(tween));
|
|
||||||
|
|
||||||
if (!tween.IsActive()) return UniTask.CompletedTask;
|
|
||||||
return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, CallbackType.StepComplete, out var token), token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct TweenAwaiter : ICriticalNotifyCompletion
|
|
||||||
{
|
|
||||||
readonly Tween tween;
|
|
||||||
|
|
||||||
// killed(non active) as completed.
|
|
||||||
public bool IsCompleted => !tween.IsActive();
|
|
||||||
|
|
||||||
public TweenAwaiter(Tween tween)
|
|
||||||
{
|
|
||||||
this.tween = tween;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TweenAwaiter GetAwaiter()
|
|
||||||
{
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void GetResult()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnCompleted(System.Action continuation)
|
|
||||||
{
|
|
||||||
UnsafeOnCompleted(continuation);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UnsafeOnCompleted(System.Action continuation)
|
|
||||||
{
|
|
||||||
// onKill is called after OnCompleted, both Complete(false/true) and Kill(false/true).
|
|
||||||
tween.onKill = PooledTweenCallback.Create(continuation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sealed class TweenConfiguredSource : IUniTaskSource, ITaskPoolNode<TweenConfiguredSource>
|
|
||||||
{
|
|
||||||
static TaskPool<TweenConfiguredSource> pool;
|
|
||||||
TweenConfiguredSource nextNode;
|
|
||||||
public ref TweenConfiguredSource NextNode => ref nextNode;
|
|
||||||
|
|
||||||
static TweenConfiguredSource()
|
|
||||||
{
|
|
||||||
TaskPool.RegisterSizeGetter(typeof(TweenConfiguredSource), () => pool.Size);
|
|
||||||
}
|
|
||||||
|
|
||||||
readonly TweenCallback onCompleteCallbackDelegate;
|
|
||||||
|
|
||||||
Tween tween;
|
|
||||||
TweenCancelBehaviour cancelBehaviour;
|
|
||||||
CancellationToken cancellationToken;
|
|
||||||
CancellationTokenRegistration cancellationRegistration;
|
|
||||||
CallbackType callbackType;
|
|
||||||
bool canceled;
|
|
||||||
|
|
||||||
TweenCallback originalCompleteAction;
|
|
||||||
UniTaskCompletionSourceCore<AsyncUnit> core;
|
|
||||||
|
|
||||||
TweenConfiguredSource()
|
|
||||||
{
|
|
||||||
onCompleteCallbackDelegate = OnCompleteCallbackDelegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IUniTaskSource Create(Tween tween, TweenCancelBehaviour cancelBehaviour, CancellationToken cancellationToken, CallbackType callbackType, out short token)
|
|
||||||
{
|
|
||||||
if (cancellationToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
DoCancelBeforeCreate(tween, cancelBehaviour);
|
|
||||||
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pool.TryPop(out var result))
|
|
||||||
{
|
|
||||||
result = new TweenConfiguredSource();
|
|
||||||
}
|
|
||||||
|
|
||||||
result.tween = tween;
|
|
||||||
result.cancelBehaviour = cancelBehaviour;
|
|
||||||
result.cancellationToken = cancellationToken;
|
|
||||||
result.callbackType = callbackType;
|
|
||||||
result.canceled = false;
|
|
||||||
|
|
||||||
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;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void DoCancelBeforeCreate(Tween tween, TweenCancelBehaviour tweenCancelBehaviour)
|
|
||||||
{
|
|
||||||
|
|
||||||
switch (tweenCancelBehaviour)
|
|
||||||
{
|
|
||||||
case TweenCancelBehaviour.Kill:
|
|
||||||
default:
|
|
||||||
tween.Kill(false);
|
|
||||||
break;
|
|
||||||
case TweenCancelBehaviour.KillAndCancelAwait:
|
|
||||||
tween.Kill(false);
|
|
||||||
break;
|
|
||||||
case TweenCancelBehaviour.KillWithCompleteCallback:
|
|
||||||
tween.Kill(true);
|
|
||||||
break;
|
|
||||||
case TweenCancelBehaviour.KillWithCompleteCallbackAndCancelAwait:
|
|
||||||
tween.Kill(true);
|
|
||||||
break;
|
|
||||||
case TweenCancelBehaviour.Complete:
|
|
||||||
tween.Complete(false);
|
|
||||||
break;
|
|
||||||
case TweenCancelBehaviour.CompleteAndCancelAwait:
|
|
||||||
tween.Complete(false);
|
|
||||||
break;
|
|
||||||
case TweenCancelBehaviour.CompleteWithSequenceCallback:
|
|
||||||
tween.Complete(true);
|
|
||||||
break;
|
|
||||||
case TweenCancelBehaviour.CompleteWithSequenceCallbackAndCancelAwait:
|
|
||||||
tween.Complete(true);
|
|
||||||
break;
|
|
||||||
case TweenCancelBehaviour.CancelAwait:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void GetResult(short token)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
core.GetResult(token);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
TryReturn();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public UniTaskStatus GetStatus(short token)
|
|
||||||
{
|
|
||||||
return core.GetStatus(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public UniTaskStatus UnsafeGetStatus()
|
|
||||||
{
|
|
||||||
return core.UnsafeGetStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
|
||||||
{
|
|
||||||
core.OnCompleted(continuation, state, token);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TryReturn()
|
|
||||||
{
|
|
||||||
TaskTracker.RemoveTracking(this);
|
|
||||||
core.Reset();
|
|
||||||
cancellationRegistration.Dispose();
|
|
||||||
|
|
||||||
RestoreOriginalCallback();
|
|
||||||
|
|
||||||
tween = default;
|
|
||||||
cancellationToken = default;
|
|
||||||
originalCompleteAction = default;
|
|
||||||
return pool.TryPush(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RestoreOriginalCallback()
|
|
||||||
{
|
|
||||||
switch (callbackType)
|
|
||||||
{
|
|
||||||
case CallbackType.Kill:
|
|
||||||
tween.onKill = originalCompleteAction;
|
|
||||||
break;
|
|
||||||
case CallbackType.Complete:
|
|
||||||
tween.onComplete = originalCompleteAction;
|
|
||||||
break;
|
|
||||||
case CallbackType.Pause:
|
|
||||||
tween.onPause = originalCompleteAction;
|
|
||||||
break;
|
|
||||||
case CallbackType.Play:
|
|
||||||
tween.onPlay = originalCompleteAction;
|
|
||||||
break;
|
|
||||||
case CallbackType.Rewind:
|
|
||||||
tween.onRewind = originalCompleteAction;
|
|
||||||
break;
|
|
||||||
case CallbackType.StepComplete:
|
|
||||||
tween.onStepComplete = originalCompleteAction;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sealed class PooledTweenCallback
|
|
||||||
{
|
|
||||||
static readonly ConcurrentQueue<PooledTweenCallback> pool = new ConcurrentQueue<PooledTweenCallback>();
|
|
||||||
|
|
||||||
readonly TweenCallback runDelegate;
|
|
||||||
|
|
||||||
Action continuation;
|
|
||||||
|
|
||||||
|
|
||||||
PooledTweenCallback()
|
|
||||||
{
|
|
||||||
runDelegate = Run;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static TweenCallback Create(Action continuation)
|
|
||||||
{
|
|
||||||
if (!pool.TryDequeue(out var item))
|
|
||||||
{
|
|
||||||
item = new PooledTweenCallback();
|
|
||||||
}
|
|
||||||
|
|
||||||
item.continuation = continuation;
|
|
||||||
return item.runDelegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
void Run()
|
|
||||||
{
|
|
||||||
var call = continuation;
|
|
||||||
continuation = null;
|
|
||||||
if (call != null)
|
|
||||||
{
|
|
||||||
pool.Enqueue(this);
|
|
||||||
call.Invoke();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,22 +0,0 @@
|
||||||
{
|
|
||||||
"name": "UniTask.DOTween",
|
|
||||||
"references": [
|
|
||||||
"UniTask",
|
|
||||||
"DOTween.Modules"
|
|
||||||
],
|
|
||||||
"includePlatforms": [],
|
|
||||||
"excludePlatforms": [],
|
|
||||||
"allowUnsafeCode": false,
|
|
||||||
"overrideReferences": false,
|
|
||||||
"precompiledReferences": [],
|
|
||||||
"autoReferenced": true,
|
|
||||||
"defineConstraints": [],
|
|
||||||
"versionDefines": [
|
|
||||||
{
|
|
||||||
"name": "com.demigiant.dotween",
|
|
||||||
"expression": "",
|
|
||||||
"define": "UNITASK_DOTWEEN_SUPPORT"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"noEngineReferences": false
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 029c1c1b674aaae47a6841a0b89ad80e
|
|
||||||
AssemblyDefinitionImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
332
src/UniTask/Assets/Plugins/UniTask/Runtime/External/DoTweenAsyncExtensions.cs
vendored
Normal file
332
src/UniTask/Assets/Plugins/UniTask/Runtime/External/DoTweenAsyncExtensions.cs
vendored
Normal file
|
@ -0,0 +1,332 @@
|
||||||
|
// asmdef Version Defines, enabled when com.demigiant.dotween is imported.
|
||||||
|
|
||||||
|
#if UNITASK_DOTWEEN_SUPPORT
|
||||||
|
|
||||||
|
using Cysharp.Threading.Tasks.Internal;
|
||||||
|
using DG.Tweening;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace Cysharp.Threading.Tasks
|
||||||
|
{
|
||||||
|
// The idea of TweenCancelBehaviour is borrowed from https://www.shibuya24.info/entry/dotween_async_await
|
||||||
|
public enum TweenCancelBehaviour
|
||||||
|
{
|
||||||
|
Kill,
|
||||||
|
KillWithCompleteCallback,
|
||||||
|
Complete,
|
||||||
|
CompleteWithSeqeunceCallback,
|
||||||
|
CancelAwait,
|
||||||
|
|
||||||
|
// AndCancelAwait
|
||||||
|
KillAndCancelAwait,
|
||||||
|
KillWithCompleteCallbackAndCancelAwait,
|
||||||
|
CompleteAndCancelAwait,
|
||||||
|
CompleteWithSeqeunceCallbackAndCancelAwait
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class DOTweenAsyncExtensions
|
||||||
|
{
|
||||||
|
public static TweenAwaiter GetAwaiter(this Tween tween)
|
||||||
|
{
|
||||||
|
return new TweenAwaiter(tween);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UniTask WithCancellation(this Tween tween, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
Error.ThrowArgumentNullException(tween, nameof(tween));
|
||||||
|
|
||||||
|
if (!tween.IsActive()) return UniTask.CompletedTask;
|
||||||
|
return new UniTask(TweenConfiguredSource.Create(tween, TweenCancelBehaviour.Kill, cancellationToken, out var token), token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UniTask ToUniTask(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
Error.ThrowArgumentNullException(tween, nameof(tween));
|
||||||
|
|
||||||
|
if (!tween.IsActive()) return UniTask.CompletedTask;
|
||||||
|
return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, out var token), token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct TweenAwaiter : ICriticalNotifyCompletion
|
||||||
|
{
|
||||||
|
readonly Tween tween;
|
||||||
|
|
||||||
|
// killed(non active) as completed.
|
||||||
|
public bool IsCompleted => !tween.IsActive();
|
||||||
|
|
||||||
|
public TweenAwaiter(Tween tween)
|
||||||
|
{
|
||||||
|
this.tween = tween;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TweenAwaiter GetAwaiter()
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetResult()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnCompleted(System.Action continuation)
|
||||||
|
{
|
||||||
|
UnsafeOnCompleted(continuation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UnsafeOnCompleted(System.Action continuation)
|
||||||
|
{
|
||||||
|
// onKill is called after OnCompleted, both Complete(false/true) and Kill(false/true).
|
||||||
|
tween.onKill = PooledTweenCallback.Create(continuation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class TweenConfiguredSource : IUniTaskSource, ITaskPoolNode<TweenConfiguredSource>
|
||||||
|
{
|
||||||
|
static TaskPool<TweenConfiguredSource> pool;
|
||||||
|
public TweenConfiguredSource NextNode { get; set; }
|
||||||
|
|
||||||
|
static TweenConfiguredSource()
|
||||||
|
{
|
||||||
|
TaskPool.RegisterSizeGetter(typeof(TweenConfiguredSource), () => pool.Size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static readonly TweenCallback EmptyTweenCallback = () => { };
|
||||||
|
|
||||||
|
readonly TweenCallback onKillDelegate;
|
||||||
|
readonly TweenCallback onUpdateDelegate;
|
||||||
|
|
||||||
|
Tween tween;
|
||||||
|
TweenCancelBehaviour cancelBehaviour;
|
||||||
|
CancellationToken cancellationToken;
|
||||||
|
bool canceled;
|
||||||
|
|
||||||
|
TweenCallback originalUpdateAction;
|
||||||
|
UniTaskCompletionSourceCore<AsyncUnit> core;
|
||||||
|
|
||||||
|
TweenConfiguredSource()
|
||||||
|
{
|
||||||
|
onKillDelegate = OnKill;
|
||||||
|
onUpdateDelegate = OnUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IUniTaskSource Create(Tween tween, TweenCancelBehaviour cancelBehaviour, CancellationToken cancellationToken, out short token)
|
||||||
|
{
|
||||||
|
if (cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
DoCancelBeforeCreate(tween, cancelBehaviour);
|
||||||
|
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pool.TryPop(out var result))
|
||||||
|
{
|
||||||
|
result = new TweenConfiguredSource();
|
||||||
|
}
|
||||||
|
|
||||||
|
result.tween = tween;
|
||||||
|
result.cancelBehaviour = cancelBehaviour;
|
||||||
|
result.cancellationToken = cancellationToken;
|
||||||
|
|
||||||
|
result.originalUpdateAction = tween.onUpdate;
|
||||||
|
result.canceled = false;
|
||||||
|
|
||||||
|
if (result.originalUpdateAction == result.onUpdateDelegate)
|
||||||
|
{
|
||||||
|
result.originalUpdateAction = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
tween.onUpdate = result.onUpdateDelegate;
|
||||||
|
tween.onKill = result.onKillDelegate;
|
||||||
|
|
||||||
|
TaskTracker.TrackActiveTask(result, 3);
|
||||||
|
|
||||||
|
token = result.core.Version;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnKill()
|
||||||
|
{
|
||||||
|
if (canceled)
|
||||||
|
{
|
||||||
|
core.TrySetCanceled(cancellationToken);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
core.TrySetResult(AsyncUnit.Default);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnUpdate()
|
||||||
|
{
|
||||||
|
originalUpdateAction?.Invoke();
|
||||||
|
|
||||||
|
if (!cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (this.cancelBehaviour)
|
||||||
|
{
|
||||||
|
case TweenCancelBehaviour.Kill:
|
||||||
|
default:
|
||||||
|
this.tween.Kill(false);
|
||||||
|
break;
|
||||||
|
case TweenCancelBehaviour.KillAndCancelAwait:
|
||||||
|
this.canceled = true;
|
||||||
|
this.tween.Kill(false);
|
||||||
|
break;
|
||||||
|
case TweenCancelBehaviour.KillWithCompleteCallback:
|
||||||
|
this.tween.Kill(true);
|
||||||
|
break;
|
||||||
|
case TweenCancelBehaviour.KillWithCompleteCallbackAndCancelAwait:
|
||||||
|
this.canceled = true;
|
||||||
|
this.tween.Kill(true);
|
||||||
|
break;
|
||||||
|
case TweenCancelBehaviour.Complete:
|
||||||
|
this.tween.Complete(false);
|
||||||
|
break;
|
||||||
|
case TweenCancelBehaviour.CompleteAndCancelAwait:
|
||||||
|
this.canceled = true;
|
||||||
|
this.tween.Complete(false);
|
||||||
|
break;
|
||||||
|
case TweenCancelBehaviour.CompleteWithSeqeunceCallback:
|
||||||
|
this.tween.Complete(true);
|
||||||
|
break;
|
||||||
|
case TweenCancelBehaviour.CompleteWithSeqeunceCallbackAndCancelAwait:
|
||||||
|
this.canceled = true;
|
||||||
|
this.tween.Complete(true);
|
||||||
|
break;
|
||||||
|
case TweenCancelBehaviour.CancelAwait:
|
||||||
|
this.tween.onKill = EmptyTweenCallback; // replace to empty(avoid callback after Canceled(instance is returned to pool.)
|
||||||
|
this.core.TrySetCanceled(this.cancellationToken);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void DoCancelBeforeCreate(Tween tween, TweenCancelBehaviour tweenCancelBehaviour)
|
||||||
|
{
|
||||||
|
|
||||||
|
switch (tweenCancelBehaviour)
|
||||||
|
{
|
||||||
|
case TweenCancelBehaviour.Kill:
|
||||||
|
default:
|
||||||
|
tween.Kill(false);
|
||||||
|
break;
|
||||||
|
case TweenCancelBehaviour.KillAndCancelAwait:
|
||||||
|
tween.Kill(false);
|
||||||
|
break;
|
||||||
|
case TweenCancelBehaviour.KillWithCompleteCallback:
|
||||||
|
tween.Kill(true);
|
||||||
|
break;
|
||||||
|
case TweenCancelBehaviour.KillWithCompleteCallbackAndCancelAwait:
|
||||||
|
tween.Kill(true);
|
||||||
|
break;
|
||||||
|
case TweenCancelBehaviour.Complete:
|
||||||
|
tween.Complete(false);
|
||||||
|
break;
|
||||||
|
case TweenCancelBehaviour.CompleteAndCancelAwait:
|
||||||
|
tween.Complete(false);
|
||||||
|
break;
|
||||||
|
case TweenCancelBehaviour.CompleteWithSeqeunceCallback:
|
||||||
|
tween.Complete(true);
|
||||||
|
break;
|
||||||
|
case TweenCancelBehaviour.CompleteWithSeqeunceCallbackAndCancelAwait:
|
||||||
|
tween.Complete(true);
|
||||||
|
break;
|
||||||
|
case TweenCancelBehaviour.CancelAwait:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetResult(short token)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
core.GetResult(token);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
TryReturn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public UniTaskStatus GetStatus(short token)
|
||||||
|
{
|
||||||
|
return core.GetStatus(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UniTaskStatus UnsafeGetStatus()
|
||||||
|
{
|
||||||
|
return core.UnsafeGetStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||||
|
{
|
||||||
|
core.OnCompleted(continuation, state, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TryReturn()
|
||||||
|
{
|
||||||
|
TaskTracker.RemoveTracking(this);
|
||||||
|
core.Reset();
|
||||||
|
tween.onUpdate = originalUpdateAction;
|
||||||
|
tween.onKill = null;
|
||||||
|
tween = default;
|
||||||
|
cancellationToken = default;
|
||||||
|
originalUpdateAction = default;
|
||||||
|
return pool.TryPush(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
~TweenConfiguredSource()
|
||||||
|
{
|
||||||
|
if (TryReturn())
|
||||||
|
{
|
||||||
|
GC.ReRegisterForFinalize(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class PooledTweenCallback
|
||||||
|
{
|
||||||
|
static readonly ConcurrentQueue<PooledTweenCallback> pool = new ConcurrentQueue<PooledTweenCallback>();
|
||||||
|
|
||||||
|
readonly TweenCallback runDelegate;
|
||||||
|
|
||||||
|
Action continuation;
|
||||||
|
|
||||||
|
|
||||||
|
PooledTweenCallback()
|
||||||
|
{
|
||||||
|
runDelegate = Run;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static TweenCallback Create(Action continuation)
|
||||||
|
{
|
||||||
|
if (!pool.TryDequeue(out var item))
|
||||||
|
{
|
||||||
|
item = new PooledTweenCallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
item.continuation = continuation;
|
||||||
|
return item.runDelegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
void Run()
|
||||||
|
{
|
||||||
|
var call = continuation;
|
||||||
|
continuation = null;
|
||||||
|
if (call != null)
|
||||||
|
{
|
||||||
|
pool.Enqueue(this);
|
||||||
|
call.Invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,224 +0,0 @@
|
||||||
#if UNITASK_TEXTMESHPRO_SUPPORT
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
using TMPro;
|
|
||||||
|
|
||||||
namespace Cysharp.Threading.Tasks
|
|
||||||
{
|
|
||||||
public static partial class TextMeshProAsyncExtensions
|
|
||||||
{
|
|
||||||
public static IAsyncValueChangedEventHandler<string> GetAsyncValueChangedEventHandler(this TMP_InputField inputField)
|
|
||||||
{
|
|
||||||
return new AsyncUnityEventHandler<string>(inputField.onValueChanged, inputField.GetCancellationTokenOnDestroy(), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IAsyncValueChangedEventHandler<string> GetAsyncValueChangedEventHandler(this TMP_InputField inputField, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
return new AsyncUnityEventHandler<string>(inputField.onValueChanged, cancellationToken, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static UniTask<string> OnValueChangedAsync(this TMP_InputField inputField)
|
|
||||||
{
|
|
||||||
return new AsyncUnityEventHandler<string>(inputField.onValueChanged, inputField.GetCancellationTokenOnDestroy(), true).OnInvokeAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static UniTask<string> OnValueChangedAsync(this TMP_InputField inputField, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
return new AsyncUnityEventHandler<string>(inputField.onValueChanged, cancellationToken, true).OnInvokeAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IUniTaskAsyncEnumerable<string> OnValueChangedAsAsyncEnumerable(this TMP_InputField inputField)
|
|
||||||
{
|
|
||||||
return new UnityEventHandlerAsyncEnumerable<string>(inputField.onValueChanged, inputField.GetCancellationTokenOnDestroy());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IUniTaskAsyncEnumerable<string> OnValueChangedAsAsyncEnumerable(this TMP_InputField inputField, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
return new UnityEventHandlerAsyncEnumerable<string>(inputField.onValueChanged, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IAsyncEndEditEventHandler<string> GetAsyncEndEditEventHandler(this TMP_InputField inputField)
|
|
||||||
{
|
|
||||||
return new AsyncUnityEventHandler<string>(inputField.onEndEdit, inputField.GetCancellationTokenOnDestroy(), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IAsyncEndEditEventHandler<string> GetAsyncEndEditEventHandler(this TMP_InputField inputField, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
return new AsyncUnityEventHandler<string>(inputField.onEndEdit, cancellationToken, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static UniTask<string> OnEndEditAsync(this TMP_InputField inputField)
|
|
||||||
{
|
|
||||||
return new AsyncUnityEventHandler<string>(inputField.onEndEdit, inputField.GetCancellationTokenOnDestroy(), true).OnInvokeAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static UniTask<string> OnEndEditAsync(this TMP_InputField inputField, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
return new AsyncUnityEventHandler<string>(inputField.onEndEdit, cancellationToken, true).OnInvokeAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IUniTaskAsyncEnumerable<string> OnEndEditAsAsyncEnumerable(this TMP_InputField inputField)
|
|
||||||
{
|
|
||||||
return new UnityEventHandlerAsyncEnumerable<string>(inputField.onEndEdit, inputField.GetCancellationTokenOnDestroy());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IUniTaskAsyncEnumerable<string> OnEndEditAsAsyncEnumerable(this TMP_InputField inputField, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
return new UnityEventHandlerAsyncEnumerable<string>(inputField.onEndEdit, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IAsyncEndTextSelectionEventHandler<(string, int, int)> GetAsyncEndTextSelectionEventHandler(this TMP_InputField inputField)
|
|
||||||
{
|
|
||||||
return new AsyncUnityEventHandler<(string, int, int)>(new TextSelectionEventConverter(inputField.onEndTextSelection), inputField.GetCancellationTokenOnDestroy(), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IAsyncEndTextSelectionEventHandler<(string, int, int)> GetAsyncEndTextSelectionEventHandler(this TMP_InputField inputField, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
return new AsyncUnityEventHandler<(string, int, int)>(new TextSelectionEventConverter(inputField.onEndTextSelection), cancellationToken, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static UniTask<(string, int, int)> OnEndTextSelectionAsync(this TMP_InputField inputField)
|
|
||||||
{
|
|
||||||
return new AsyncUnityEventHandler<(string, int, int)>(new TextSelectionEventConverter(inputField.onEndTextSelection), inputField.GetCancellationTokenOnDestroy(), true).OnInvokeAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static UniTask<(string, int, int)> OnEndTextSelectionAsync(this TMP_InputField inputField, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
return new AsyncUnityEventHandler<(string, int, int)>(new TextSelectionEventConverter(inputField.onEndTextSelection), cancellationToken, true).OnInvokeAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IUniTaskAsyncEnumerable<(string, int, int)> OnEndTextSelectionAsAsyncEnumerable(this TMP_InputField inputField)
|
|
||||||
{
|
|
||||||
return new UnityEventHandlerAsyncEnumerable<(string, int, int)>(new TextSelectionEventConverter(inputField.onEndTextSelection), inputField.GetCancellationTokenOnDestroy());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IUniTaskAsyncEnumerable<(string, int, int)> OnEndTextSelectionAsAsyncEnumerable(this TMP_InputField inputField, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
return new UnityEventHandlerAsyncEnumerable<(string, int, int)>(new TextSelectionEventConverter(inputField.onEndTextSelection), cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IAsyncTextSelectionEventHandler<(string, int, int)> GetAsyncTextSelectionEventHandler(this TMP_InputField inputField)
|
|
||||||
{
|
|
||||||
return new AsyncUnityEventHandler<(string, int, int)>(new TextSelectionEventConverter(inputField.onTextSelection), inputField.GetCancellationTokenOnDestroy(), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IAsyncTextSelectionEventHandler<(string, int, int)> GetAsyncTextSelectionEventHandler(this TMP_InputField inputField, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
return new AsyncUnityEventHandler<(string, int, int)>(new TextSelectionEventConverter(inputField.onTextSelection), cancellationToken, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static UniTask<(string, int, int)> OnTextSelectionAsync(this TMP_InputField inputField)
|
|
||||||
{
|
|
||||||
return new AsyncUnityEventHandler<(string, int, int)>(new TextSelectionEventConverter(inputField.onTextSelection), inputField.GetCancellationTokenOnDestroy(), true).OnInvokeAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static UniTask<(string, int, int)> OnTextSelectionAsync(this TMP_InputField inputField, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
return new AsyncUnityEventHandler<(string, int, int)>(new TextSelectionEventConverter(inputField.onTextSelection), cancellationToken, true).OnInvokeAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IUniTaskAsyncEnumerable<(string, int, int)> OnTextSelectionAsAsyncEnumerable(this TMP_InputField inputField)
|
|
||||||
{
|
|
||||||
return new UnityEventHandlerAsyncEnumerable<(string, int, int)>(new TextSelectionEventConverter(inputField.onTextSelection), inputField.GetCancellationTokenOnDestroy());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IUniTaskAsyncEnumerable<(string, int, int)> OnTextSelectionAsAsyncEnumerable(this TMP_InputField inputField, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
return new UnityEventHandlerAsyncEnumerable<(string, int, int)>(new TextSelectionEventConverter(inputField.onTextSelection), cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IAsyncDeselectEventHandler<string> GetAsyncDeselectEventHandler(this TMP_InputField inputField)
|
|
||||||
{
|
|
||||||
return new AsyncUnityEventHandler<string>(inputField.onDeselect, inputField.GetCancellationTokenOnDestroy(), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IAsyncDeselectEventHandler<string> GetAsyncDeselectEventHandler(this TMP_InputField inputField, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
return new AsyncUnityEventHandler<string>(inputField.onDeselect, cancellationToken, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static UniTask<string> OnDeselectAsync(this TMP_InputField inputField)
|
|
||||||
{
|
|
||||||
return new AsyncUnityEventHandler<string>(inputField.onDeselect, inputField.GetCancellationTokenOnDestroy(), true).OnInvokeAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static UniTask<string> OnDeselectAsync(this TMP_InputField inputField, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
return new AsyncUnityEventHandler<string>(inputField.onDeselect, cancellationToken, true).OnInvokeAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IUniTaskAsyncEnumerable<string> OnDeselectAsAsyncEnumerable(this TMP_InputField inputField)
|
|
||||||
{
|
|
||||||
return new UnityEventHandlerAsyncEnumerable<string>(inputField.onDeselect, inputField.GetCancellationTokenOnDestroy());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IUniTaskAsyncEnumerable<string> OnDeselectAsAsyncEnumerable(this TMP_InputField inputField, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
return new UnityEventHandlerAsyncEnumerable<string>(inputField.onDeselect, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IAsyncSelectEventHandler<string> GetAsyncSelectEventHandler(this TMP_InputField inputField)
|
|
||||||
{
|
|
||||||
return new AsyncUnityEventHandler<string>(inputField.onSelect, inputField.GetCancellationTokenOnDestroy(), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IAsyncSelectEventHandler<string> GetAsyncSelectEventHandler(this TMP_InputField inputField, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
return new AsyncUnityEventHandler<string>(inputField.onSelect, cancellationToken, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static UniTask<string> OnSelectAsync(this TMP_InputField inputField)
|
|
||||||
{
|
|
||||||
return new AsyncUnityEventHandler<string>(inputField.onSelect, inputField.GetCancellationTokenOnDestroy(), true).OnInvokeAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static UniTask<string> OnSelectAsync(this TMP_InputField inputField, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
return new AsyncUnityEventHandler<string>(inputField.onSelect, cancellationToken, true).OnInvokeAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IUniTaskAsyncEnumerable<string> OnSelectAsAsyncEnumerable(this TMP_InputField inputField)
|
|
||||||
{
|
|
||||||
return new UnityEventHandlerAsyncEnumerable<string>(inputField.onSelect, inputField.GetCancellationTokenOnDestroy());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IUniTaskAsyncEnumerable<string> OnSelectAsAsyncEnumerable(this TMP_InputField inputField, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
return new UnityEventHandlerAsyncEnumerable<string>(inputField.onSelect, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IAsyncSubmitEventHandler<string> GetAsyncSubmitEventHandler(this TMP_InputField inputField)
|
|
||||||
{
|
|
||||||
return new AsyncUnityEventHandler<string>(inputField.onSubmit, inputField.GetCancellationTokenOnDestroy(), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IAsyncSubmitEventHandler<string> GetAsyncSubmitEventHandler(this TMP_InputField inputField, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
return new AsyncUnityEventHandler<string>(inputField.onSubmit, cancellationToken, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static UniTask<string> OnSubmitAsync(this TMP_InputField inputField)
|
|
||||||
{
|
|
||||||
return new AsyncUnityEventHandler<string>(inputField.onSubmit, inputField.GetCancellationTokenOnDestroy(), true).OnInvokeAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static UniTask<string> OnSubmitAsync(this TMP_InputField inputField, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
return new AsyncUnityEventHandler<string>(inputField.onSubmit, cancellationToken, true).OnInvokeAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IUniTaskAsyncEnumerable<string> OnSubmitAsAsyncEnumerable(this TMP_InputField inputField)
|
|
||||||
{
|
|
||||||
return new UnityEventHandlerAsyncEnumerable<string>(inputField.onSubmit, inputField.GetCancellationTokenOnDestroy());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IUniTaskAsyncEnumerable<string> OnSubmitAsAsyncEnumerable(this TMP_InputField inputField, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
return new UnityEventHandlerAsyncEnumerable<string>(inputField.onSubmit, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,66 +0,0 @@
|
||||||
<#@ template debug="false" hostspecific="false" language="C#" #>
|
|
||||||
<#@ assembly name="System.Core" #>
|
|
||||||
<#@ import namespace="System.Linq" #>
|
|
||||||
<#@ import namespace="System.Text" #>
|
|
||||||
<#@ import namespace="System.Collections.Generic" #>
|
|
||||||
<#@ output extension=".cs" #>
|
|
||||||
<#
|
|
||||||
var handlers = new (string name, string type)[] {
|
|
||||||
("ValueChanged", "string"),
|
|
||||||
("EndEdit", "string"),
|
|
||||||
("EndTextSelection", "(string, int, int)"),
|
|
||||||
("TextSelection", "(string, int, int)"),
|
|
||||||
("Deselect", "string"),
|
|
||||||
("Select", "string"),
|
|
||||||
("Submit", "string"),
|
|
||||||
};
|
|
||||||
|
|
||||||
Func<string, bool> shouldConvert = x => x.EndsWith("TextSelection");
|
|
||||||
Func<string, string> eventName = x => shouldConvert(x) ? $"new TextSelectionEventConverter(inputField.on{x})" : $"inputField.on{x}";
|
|
||||||
#>
|
|
||||||
#if UNITASK_TEXTMESHPRO_SUPPORT
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
using TMPro;
|
|
||||||
|
|
||||||
namespace Cysharp.Threading.Tasks
|
|
||||||
{
|
|
||||||
public static partial class TextMeshProAsyncExtensions
|
|
||||||
{
|
|
||||||
<# foreach(var (name, type) in handlers) { #>
|
|
||||||
public static IAsync<#= (name) #>EventHandler<<#= type #>> GetAsync<#= (name) #>EventHandler(this TMP_InputField inputField)
|
|
||||||
{
|
|
||||||
return new AsyncUnityEventHandler<<#= type #>>(<#= eventName(name) #>, inputField.GetCancellationTokenOnDestroy(), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IAsync<#= (name) #>EventHandler<<#= type #>> GetAsync<#= (name) #>EventHandler(this TMP_InputField inputField, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
return new AsyncUnityEventHandler<<#= type #>>(<#= eventName(name) #>, cancellationToken, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static UniTask<<#= type #>> On<#= (name) #>Async(this TMP_InputField inputField)
|
|
||||||
{
|
|
||||||
return new AsyncUnityEventHandler<<#= type #>>(<#= eventName(name) #>, inputField.GetCancellationTokenOnDestroy(), true).OnInvokeAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static UniTask<<#= type #>> On<#= (name) #>Async(this TMP_InputField inputField, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
return new AsyncUnityEventHandler<<#= type #>>(<#= eventName(name) #>, cancellationToken, true).OnInvokeAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IUniTaskAsyncEnumerable<<#= type #>> On<#= (name) #>AsAsyncEnumerable(this TMP_InputField inputField)
|
|
||||||
{
|
|
||||||
return new UnityEventHandlerAsyncEnumerable<<#= type #>>(<#= eventName(name) #>, inputField.GetCancellationTokenOnDestroy());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IUniTaskAsyncEnumerable<<#= type #>> On<#= (name) #>AsAsyncEnumerable(this TMP_InputField inputField, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
return new UnityEventHandlerAsyncEnumerable<<#= type #>>(<#= eventName(name) #>, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
<# } #>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,130 +0,0 @@
|
||||||
#if UNITASK_TEXTMESHPRO_SUPPORT
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
using TMPro;
|
|
||||||
using UnityEngine.Events;
|
|
||||||
|
|
||||||
namespace Cysharp.Threading.Tasks
|
|
||||||
{
|
|
||||||
public static partial class TextMeshProAsyncExtensions
|
|
||||||
{
|
|
||||||
// <string> -> Text
|
|
||||||
public static void BindTo(this IUniTaskAsyncEnumerable<string> source, TMP_Text text, bool rebindOnError = true)
|
|
||||||
{
|
|
||||||
BindToCore(source, text, text.GetCancellationTokenOnDestroy(), rebindOnError).Forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void BindTo(this IUniTaskAsyncEnumerable<string> source, TMP_Text text, CancellationToken cancellationToken, bool rebindOnError = true)
|
|
||||||
{
|
|
||||||
BindToCore(source, text, cancellationToken, rebindOnError).Forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
static async UniTaskVoid BindToCore(IUniTaskAsyncEnumerable<string> source, TMP_Text text, CancellationToken cancellationToken, bool rebindOnError)
|
|
||||||
{
|
|
||||||
var repeat = false;
|
|
||||||
BIND_AGAIN:
|
|
||||||
var e = source.GetAsyncEnumerator(cancellationToken);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
bool moveNext;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
moveNext = await e.MoveNextAsync();
|
|
||||||
repeat = false;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
if (ex is OperationCanceledException) return;
|
|
||||||
|
|
||||||
if (rebindOnError && !repeat)
|
|
||||||
{
|
|
||||||
repeat = true;
|
|
||||||
goto BIND_AGAIN;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!moveNext) return;
|
|
||||||
|
|
||||||
text.text = e.Current;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (e != null)
|
|
||||||
{
|
|
||||||
await e.DisposeAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// <T> -> Text
|
|
||||||
|
|
||||||
public static void BindTo<T>(this IUniTaskAsyncEnumerable<T> source, TMP_Text text, bool rebindOnError = true)
|
|
||||||
{
|
|
||||||
BindToCore(source, text, text.GetCancellationTokenOnDestroy(), rebindOnError).Forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void BindTo<T>(this IUniTaskAsyncEnumerable<T> source, TMP_Text text, CancellationToken cancellationToken, bool rebindOnError = true)
|
|
||||||
{
|
|
||||||
BindToCore(source, text, cancellationToken, rebindOnError).Forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void BindTo<T>(this AsyncReactiveProperty<T> source, TMP_Text text, bool rebindOnError = true)
|
|
||||||
{
|
|
||||||
BindToCore(source, text, text.GetCancellationTokenOnDestroy(), rebindOnError).Forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
static async UniTaskVoid BindToCore<T>(IUniTaskAsyncEnumerable<T> source, TMP_Text text, CancellationToken cancellationToken, bool rebindOnError)
|
|
||||||
{
|
|
||||||
var repeat = false;
|
|
||||||
BIND_AGAIN:
|
|
||||||
var e = source.GetAsyncEnumerator(cancellationToken);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
bool moveNext;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
moveNext = await e.MoveNextAsync();
|
|
||||||
repeat = false;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
if (ex is OperationCanceledException) return;
|
|
||||||
|
|
||||||
if (rebindOnError && !repeat)
|
|
||||||
{
|
|
||||||
repeat = true;
|
|
||||||
goto BIND_AGAIN;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!moveNext) return;
|
|
||||||
|
|
||||||
text.text = e.Current.ToString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (e != null)
|
|
||||||
{
|
|
||||||
await e.DisposeAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,27 +0,0 @@
|
||||||
{
|
|
||||||
"name": "UniTask.TextMeshPro",
|
|
||||||
"references": [
|
|
||||||
"UniTask",
|
|
||||||
"Unity.TextMeshPro"
|
|
||||||
],
|
|
||||||
"includePlatforms": [],
|
|
||||||
"excludePlatforms": [],
|
|
||||||
"allowUnsafeCode": false,
|
|
||||||
"overrideReferences": false,
|
|
||||||
"precompiledReferences": [],
|
|
||||||
"autoReferenced": true,
|
|
||||||
"defineConstraints": [],
|
|
||||||
"versionDefines": [
|
|
||||||
{
|
|
||||||
"name": "com.unity.textmeshpro",
|
|
||||||
"expression": "",
|
|
||||||
"define": "UNITASK_TEXTMESHPRO_SUPPORT"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "com.unity.ugui",
|
|
||||||
"expression": "2.0.0",
|
|
||||||
"define": "UNITASK_TEXTMESHPRO_SUPPORT"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"noEngineReferences": false
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: dc47925d1a5fa2946bdd37746b2b5d48
|
|
||||||
AssemblyDefinitionImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,9 +1,4 @@
|
||||||
#pragma warning disable CS1591
|
#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;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
@ -24,8 +19,9 @@ namespace Cysharp.Threading.Tasks
|
||||||
|
|
||||||
// similar as IValueTaskSource
|
// similar as IValueTaskSource
|
||||||
public interface IUniTaskSource
|
public interface IUniTaskSource
|
||||||
#if SUPPORT_VALUETASK
|
#if !UNITY_2018_3_OR_NEWER && !NETSTANDARD2_0
|
||||||
: System.Threading.Tasks.Sources.IValueTaskSource
|
: System.Threading.Tasks.Sources.IValueTaskSource
|
||||||
|
#pragma warning disable CS0108
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
UniTaskStatus GetStatus(short token);
|
UniTaskStatus GetStatus(short token);
|
||||||
|
@ -34,7 +30,8 @@ namespace Cysharp.Threading.Tasks
|
||||||
|
|
||||||
UniTaskStatus UnsafeGetStatus(); // only for debug use.
|
UniTaskStatus UnsafeGetStatus(); // only for debug use.
|
||||||
|
|
||||||
#if SUPPORT_VALUETASK
|
#if !UNITY_2018_3_OR_NEWER && !NETSTANDARD2_0
|
||||||
|
#pragma warning restore CS0108
|
||||||
|
|
||||||
System.Threading.Tasks.Sources.ValueTaskSourceStatus System.Threading.Tasks.Sources.IValueTaskSource.GetStatus(short token)
|
System.Threading.Tasks.Sources.ValueTaskSourceStatus System.Threading.Tasks.Sources.IValueTaskSource.GetStatus(short token)
|
||||||
{
|
{
|
||||||
|
@ -56,13 +53,13 @@ namespace Cysharp.Threading.Tasks
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface IUniTaskSource<out T> : IUniTaskSource
|
public interface IUniTaskSource<out T> : IUniTaskSource
|
||||||
#if SUPPORT_VALUETASK
|
#if !UNITY_2018_3_OR_NEWER && !NETSTANDARD2_0
|
||||||
, System.Threading.Tasks.Sources.IValueTaskSource<T>
|
, System.Threading.Tasks.Sources.IValueTaskSource<T>
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
new T GetResult(short token);
|
new T GetResult(short token);
|
||||||
|
|
||||||
#if SUPPORT_VALUETASK
|
#if !UNITY_2018_3_OR_NEWER && !NETSTANDARD2_0
|
||||||
|
|
||||||
new public UniTaskStatus GetStatus(short token)
|
new public UniTaskStatus GetStatus(short token)
|
||||||
{
|
{
|
||||||
|
|
|
@ -12,7 +12,7 @@ namespace Cysharp.Threading.Tasks.Internal
|
||||||
|
|
||||||
readonly PlayerLoopTiming timing;
|
readonly PlayerLoopTiming timing;
|
||||||
|
|
||||||
SpinLock gate = new SpinLock(false);
|
SpinLock gate = new SpinLock();
|
||||||
bool dequing = false;
|
bool dequing = false;
|
||||||
|
|
||||||
int actionListCount = 0;
|
int actionListCount = 0;
|
||||||
|
@ -70,17 +70,13 @@ namespace Cysharp.Threading.Tasks.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Clear()
|
public void Clear()
|
||||||
{
|
{
|
||||||
var rest = actionListCount + waitingListCount;
|
|
||||||
|
|
||||||
actionListCount = 0;
|
actionListCount = 0;
|
||||||
actionList = new Action[InitialSize];
|
actionList = new Action[InitialSize];
|
||||||
|
|
||||||
waitingListCount = 0;
|
waitingListCount = 0;
|
||||||
waitingList = new Action[InitialSize];
|
waitingList = new Action[InitialSize];
|
||||||
|
|
||||||
return rest;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// delegate entrypoint.
|
// delegate entrypoint.
|
||||||
|
@ -132,14 +128,6 @@ namespace Cysharp.Threading.Tasks.Internal
|
||||||
case PlayerLoopTiming.LastPostLateUpdate:
|
case PlayerLoopTiming.LastPostLateUpdate:
|
||||||
LastPostLateUpdate();
|
LastPostLateUpdate();
|
||||||
break;
|
break;
|
||||||
#if UNITY_2020_2_OR_NEWER
|
|
||||||
case PlayerLoopTiming.TimeUpdate:
|
|
||||||
TimeUpdate();
|
|
||||||
break;
|
|
||||||
case PlayerLoopTiming.LastTimeUpdate:
|
|
||||||
LastTimeUpdate();
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -162,10 +150,6 @@ namespace Cysharp.Threading.Tasks.Internal
|
||||||
void LastPreLateUpdate() => RunCore();
|
void LastPreLateUpdate() => RunCore();
|
||||||
void PostLateUpdate() => RunCore();
|
void PostLateUpdate() => RunCore();
|
||||||
void LastPostLateUpdate() => RunCore();
|
void LastPostLateUpdate() => RunCore();
|
||||||
#if UNITY_2020_2_OR_NEWER
|
|
||||||
void TimeUpdate() => RunCore();
|
|
||||||
void LastTimeUpdate() => RunCore();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
[System.Diagnostics.DebuggerHidden]
|
[System.Diagnostics.DebuggerHidden]
|
||||||
void RunCore()
|
void RunCore()
|
||||||
|
@ -186,18 +170,11 @@ namespace Cysharp.Threading.Tasks.Internal
|
||||||
|
|
||||||
for (int i = 0; i < actionListCount; i++)
|
for (int i = 0; i < actionListCount; i++)
|
||||||
{
|
{
|
||||||
|
|
||||||
var action = actionList[i];
|
var action = actionList[i];
|
||||||
actionList[i] = null;
|
actionList[i] = null;
|
||||||
try
|
|
||||||
{
|
|
||||||
action();
|
action();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
UnityEngine.Debug.LogException(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
bool lockTaken = false;
|
bool lockTaken = false;
|
||||||
|
|
|
@ -148,7 +148,7 @@ namespace Cysharp.Threading.Tasks.Internal
|
||||||
|
|
||||||
foreach (var candidateMethod in methods)
|
foreach (var candidateMethod in methods)
|
||||||
{
|
{
|
||||||
var attributes = candidateMethod.GetCustomAttributes<StateMachineAttribute>(false);
|
var attributes = candidateMethod.GetCustomAttributes<StateMachineAttribute>();
|
||||||
if (attributes == null)
|
if (attributes == null)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -39,7 +39,7 @@ namespace Cysharp.Threading.Tasks.Internal
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
public static void ThrowArgumentException(string message)
|
public static void ThrowArgumentException<T>(string message)
|
||||||
{
|
{
|
||||||
throw new ArgumentException(message);
|
throw new ArgumentException(message);
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,24 +48,14 @@ namespace Cysharp.Threading.Tasks.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Clear()
|
public void Clear()
|
||||||
{
|
{
|
||||||
lock (arrayLock)
|
lock (arrayLock)
|
||||||
{
|
{
|
||||||
var rest = 0;
|
|
||||||
|
|
||||||
for (var index = 0; index < loopItems.Length; index++)
|
for (var index = 0; index < loopItems.Length; index++)
|
||||||
{
|
{
|
||||||
if (loopItems[index] != null)
|
|
||||||
{
|
|
||||||
rest++;
|
|
||||||
}
|
|
||||||
|
|
||||||
loopItems[index] = null;
|
loopItems[index] = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
tail = 0;
|
|
||||||
return rest;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,14 +108,6 @@ namespace Cysharp.Threading.Tasks.Internal
|
||||||
case PlayerLoopTiming.LastPostLateUpdate:
|
case PlayerLoopTiming.LastPostLateUpdate:
|
||||||
LastPostLateUpdate();
|
LastPostLateUpdate();
|
||||||
break;
|
break;
|
||||||
#if UNITY_2020_2_OR_NEWER
|
|
||||||
case PlayerLoopTiming.TimeUpdate:
|
|
||||||
TimeUpdate();
|
|
||||||
break;
|
|
||||||
case PlayerLoopTiming.LastTimeUpdate:
|
|
||||||
LastTimeUpdate();
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -148,10 +130,6 @@ namespace Cysharp.Threading.Tasks.Internal
|
||||||
void LastPreLateUpdate() => RunCore();
|
void LastPreLateUpdate() => RunCore();
|
||||||
void PostLateUpdate() => RunCore();
|
void PostLateUpdate() => RunCore();
|
||||||
void LastPostLateUpdate() => RunCore();
|
void LastPostLateUpdate() => RunCore();
|
||||||
#if UNITY_2020_2_OR_NEWER
|
|
||||||
void TimeUpdate() => RunCore();
|
|
||||||
void LastTimeUpdate() => RunCore();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
[System.Diagnostics.DebuggerHidden]
|
[System.Diagnostics.DebuggerHidden]
|
||||||
void RunCore()
|
void RunCore()
|
||||||
|
@ -165,6 +143,7 @@ namespace Cysharp.Threading.Tasks.Internal
|
||||||
{
|
{
|
||||||
var j = tail - 1;
|
var j = tail - 1;
|
||||||
|
|
||||||
|
// eliminate array-bound check for i
|
||||||
for (int i = 0; i < loopItems.Length; i++)
|
for (int i = 0; i < loopItems.Length; i++)
|
||||||
{
|
{
|
||||||
var action = loopItems[i];
|
var action = loopItems[i];
|
||||||
|
|
|
@ -7,8 +7,7 @@ namespace Cysharp.Threading.Tasks.Internal
|
||||||
{
|
{
|
||||||
static TaskPool<PooledDelegate<T>> pool;
|
static TaskPool<PooledDelegate<T>> pool;
|
||||||
|
|
||||||
PooledDelegate<T> nextNode;
|
public PooledDelegate<T> NextNode { get; set; }
|
||||||
public ref PooledDelegate<T> NextNode => ref nextNode;
|
|
||||||
|
|
||||||
static PooledDelegate()
|
static PooledDelegate()
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
#if UNITY_2020_2_OR_NEWER
|
|
||||||
var result = unityWebRequest.result;
|
|
||||||
return (result == UnityWebRequest.Result.ConnectionError)
|
|
||||||
|| (result == UnityWebRequest.Result.DataProcessingError)
|
|
||||||
|| (result == UnityWebRequest.Result.ProtocolError);
|
|
||||||
#else
|
|
||||||
return unityWebRequest.isHttpError || unityWebRequest.isNetworkError;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Diagnostics;
|
|
||||||
|
|
||||||
namespace Cysharp.Threading.Tasks.Internal
|
|
||||||
{
|
|
||||||
internal readonly struct ValueStopwatch
|
|
||||||
{
|
|
||||||
static readonly double TimestampToTicks = TimeSpan.TicksPerSecond / (double)Stopwatch.Frequency;
|
|
||||||
|
|
||||||
readonly long startTimestamp;
|
|
||||||
|
|
||||||
public static ValueStopwatch StartNew() => new ValueStopwatch(Stopwatch.GetTimestamp());
|
|
||||||
|
|
||||||
ValueStopwatch(long startTimestamp)
|
|
||||||
{
|
|
||||||
this.startTimestamp = startTimestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TimeSpan Elapsed => TimeSpan.FromTicks(this.ElapsedTicks);
|
|
||||||
|
|
||||||
public bool IsInvalid => startTimestamp == 0;
|
|
||||||
|
|
||||||
public long ElapsedTicks
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (startTimestamp == 0)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("Detected invalid initialization(use 'default'), only to create from StartNew().");
|
|
||||||
}
|
|
||||||
|
|
||||||
var delta = Stopwatch.GetTimestamp() - startTimestamp;
|
|
||||||
return (long)(delta * TimestampToTicks);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -17,7 +17,7 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||||
{
|
{
|
||||||
Error.ThrowArgumentNullException(source, nameof(source));
|
Error.ThrowArgumentNullException(source, nameof(source));
|
||||||
|
|
||||||
return new AppendPrepend<TSource>(source, element, false);
|
return new AppendPrepend<TSource>(source, element, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||||
|
|
||||||
if (enumerator == null)
|
if (enumerator == null)
|
||||||
{
|
{
|
||||||
if (state == State.RequirePrepend)
|
if (state == State.RequireAppend)
|
||||||
{
|
{
|
||||||
Current = element;
|
Current = element;
|
||||||
state = State.None;
|
state = State.None;
|
||||||
|
|
|
@ -1,184 +0,0 @@
|
||||||
using Cysharp.Threading.Tasks.Internal;
|
|
||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace Cysharp.Threading.Tasks.Linq
|
|
||||||
{
|
|
||||||
public static partial class UniTaskAsyncEnumerable
|
|
||||||
{
|
|
||||||
public static IUniTaskAsyncEnumerable<T> Create<T>(Func<IAsyncWriter<T>, CancellationToken, UniTask> create)
|
|
||||||
{
|
|
||||||
Error.ThrowArgumentNullException(create, nameof(create));
|
|
||||||
return new Create<T>(create);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface IAsyncWriter<T>
|
|
||||||
{
|
|
||||||
UniTask YieldAsync(T value);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal sealed class Create<T> : IUniTaskAsyncEnumerable<T>
|
|
||||||
{
|
|
||||||
readonly Func<IAsyncWriter<T>, CancellationToken, UniTask> create;
|
|
||||||
|
|
||||||
public Create(Func<IAsyncWriter<T>, CancellationToken, UniTask> create)
|
|
||||||
{
|
|
||||||
this.create = create;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
return new _Create(create, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
sealed class _Create : MoveNextSource, IUniTaskAsyncEnumerator<T>
|
|
||||||
{
|
|
||||||
readonly Func<IAsyncWriter<T>, CancellationToken, UniTask> create;
|
|
||||||
readonly CancellationToken cancellationToken;
|
|
||||||
|
|
||||||
int state = -1;
|
|
||||||
AsyncWriter writer;
|
|
||||||
|
|
||||||
public _Create(Func<IAsyncWriter<T>, CancellationToken, UniTask> create, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
this.create = create;
|
|
||||||
this.cancellationToken = cancellationToken;
|
|
||||||
TaskTracker.TrackActiveTask(this, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
public T Current { get; private set; }
|
|
||||||
|
|
||||||
public UniTask DisposeAsync()
|
|
||||||
{
|
|
||||||
TaskTracker.RemoveTracking(this);
|
|
||||||
writer.Dispose();
|
|
||||||
return default;
|
|
||||||
}
|
|
||||||
|
|
||||||
public UniTask<bool> MoveNextAsync()
|
|
||||||
{
|
|
||||||
if (state == -2) return default;
|
|
||||||
|
|
||||||
completionSource.Reset();
|
|
||||||
MoveNext();
|
|
||||||
return new UniTask<bool>(this, completionSource.Version);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MoveNext()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
switch (state)
|
|
||||||
{
|
|
||||||
case -1: // init
|
|
||||||
{
|
|
||||||
writer = new AsyncWriter(this);
|
|
||||||
RunWriterTask(create(writer, cancellationToken)).Forget();
|
|
||||||
if (Volatile.Read(ref state) == -2)
|
|
||||||
{
|
|
||||||
return; // complete synchronously
|
|
||||||
}
|
|
||||||
state = 0; // wait YieldAsync, it set TrySetResult(true)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case 0:
|
|
||||||
writer.SignalWriter();
|
|
||||||
return;
|
|
||||||
default:
|
|
||||||
goto DONE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
state = -2;
|
|
||||||
completionSource.TrySetException(ex);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
DONE:
|
|
||||||
state = -2;
|
|
||||||
completionSource.TrySetResult(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
async UniTaskVoid RunWriterTask(UniTask task)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await task;
|
|
||||||
goto DONE;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Volatile.Write(ref state, -2);
|
|
||||||
completionSource.TrySetException(ex);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
DONE:
|
|
||||||
Volatile.Write(ref state, -2);
|
|
||||||
completionSource.TrySetResult(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetResult(T value)
|
|
||||||
{
|
|
||||||
Current = value;
|
|
||||||
completionSource.TrySetResult(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sealed class AsyncWriter : IUniTaskSource, IAsyncWriter<T>, IDisposable
|
|
||||||
{
|
|
||||||
readonly _Create enumerator;
|
|
||||||
|
|
||||||
UniTaskCompletionSourceCore<AsyncUnit> core;
|
|
||||||
|
|
||||||
public AsyncWriter(_Create enumerator)
|
|
||||||
{
|
|
||||||
this.enumerator = enumerator;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
var status = core.GetStatus(core.Version);
|
|
||||||
if (status == UniTaskStatus.Pending)
|
|
||||||
{
|
|
||||||
core.TrySetCanceled();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void GetResult(short token)
|
|
||||||
{
|
|
||||||
core.GetResult(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public UniTaskStatus GetStatus(short token)
|
|
||||||
{
|
|
||||||
return core.GetStatus(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public UniTaskStatus UnsafeGetStatus()
|
|
||||||
{
|
|
||||||
return core.UnsafeGetStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
|
||||||
{
|
|
||||||
core.OnCompleted(continuation, state, token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public UniTask YieldAsync(T value)
|
|
||||||
{
|
|
||||||
core.Reset();
|
|
||||||
enumerator.SetResult(value);
|
|
||||||
return new UniTask(this, core.Version);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SignalWriter()
|
|
||||||
{
|
|
||||||
core.TrySetResult(AsyncUnit.Default);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 0202f723469f93945afa063bfb440d15
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,234 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
|
||||||
using Cysharp.Threading.Tasks.Internal;
|
|
||||||
|
|
||||||
namespace Cysharp.Threading.Tasks.Linq
|
|
||||||
{
|
|
||||||
public static partial class UniTaskAsyncEnumerable
|
|
||||||
{
|
|
||||||
public static IUniTaskAsyncEnumerable<T> Merge<T>(this IUniTaskAsyncEnumerable<T> first, IUniTaskAsyncEnumerable<T> second)
|
|
||||||
{
|
|
||||||
Error.ThrowArgumentNullException(first, nameof(first));
|
|
||||||
Error.ThrowArgumentNullException(second, nameof(second));
|
|
||||||
|
|
||||||
return new Merge<T>(new [] { first, second });
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IUniTaskAsyncEnumerable<T> Merge<T>(this IUniTaskAsyncEnumerable<T> first, IUniTaskAsyncEnumerable<T> second, IUniTaskAsyncEnumerable<T> third)
|
|
||||||
{
|
|
||||||
Error.ThrowArgumentNullException(first, nameof(first));
|
|
||||||
Error.ThrowArgumentNullException(second, nameof(second));
|
|
||||||
Error.ThrowArgumentNullException(third, nameof(third));
|
|
||||||
|
|
||||||
return new Merge<T>(new[] { first, second, third });
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IUniTaskAsyncEnumerable<T> Merge<T>(this IEnumerable<IUniTaskAsyncEnumerable<T>> sources)
|
|
||||||
{
|
|
||||||
return 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: ca56812f160c45d0bacb4339819edf1a
|
|
||||||
timeCreated: 1694133666
|
|
|
@ -88,7 +88,6 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||||
this.selector = selector;
|
this.selector = selector;
|
||||||
this.cancellationToken = cancellationToken;
|
this.cancellationToken = cancellationToken;
|
||||||
this.moveNextAction = MoveNext;
|
this.moveNextAction = MoveNext;
|
||||||
TaskTracker.TrackActiveTask(this, 3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public TResult Current { get; private set; }
|
public TResult Current { get; private set; }
|
||||||
|
@ -157,7 +156,6 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||||
|
|
||||||
public UniTask DisposeAsync()
|
public UniTask DisposeAsync()
|
||||||
{
|
{
|
||||||
TaskTracker.RemoveTracking(this);
|
|
||||||
return enumerator.DisposeAsync();
|
return enumerator.DisposeAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -197,7 +195,6 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||||
this.selector = selector;
|
this.selector = selector;
|
||||||
this.cancellationToken = cancellationToken;
|
this.cancellationToken = cancellationToken;
|
||||||
this.moveNextAction = MoveNext;
|
this.moveNextAction = MoveNext;
|
||||||
TaskTracker.TrackActiveTask(this, 3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public TResult Current { get; private set; }
|
public TResult Current { get; private set; }
|
||||||
|
@ -266,7 +263,6 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||||
|
|
||||||
public UniTask DisposeAsync()
|
public UniTask DisposeAsync()
|
||||||
{
|
{
|
||||||
TaskTracker.RemoveTracking(this);
|
|
||||||
return enumerator.DisposeAsync();
|
return enumerator.DisposeAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -306,7 +302,6 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||||
this.selector = selector;
|
this.selector = selector;
|
||||||
this.cancellationToken = cancellationToken;
|
this.cancellationToken = cancellationToken;
|
||||||
this.moveNextAction = MoveNext;
|
this.moveNextAction = MoveNext;
|
||||||
TaskTracker.TrackActiveTask(this, 3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public TResult Current { get; private set; }
|
public TResult Current { get; private set; }
|
||||||
|
@ -387,7 +382,6 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||||
|
|
||||||
public UniTask DisposeAsync()
|
public UniTask DisposeAsync()
|
||||||
{
|
{
|
||||||
TaskTracker.RemoveTracking(this);
|
|
||||||
return enumerator.DisposeAsync();
|
return enumerator.DisposeAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -428,7 +422,6 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||||
this.selector = selector;
|
this.selector = selector;
|
||||||
this.cancellationToken = cancellationToken;
|
this.cancellationToken = cancellationToken;
|
||||||
this.moveNextAction = MoveNext;
|
this.moveNextAction = MoveNext;
|
||||||
TaskTracker.TrackActiveTask(this, 3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public TResult Current { get; private set; }
|
public TResult Current { get; private set; }
|
||||||
|
@ -509,7 +502,6 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||||
|
|
||||||
public UniTask DisposeAsync()
|
public UniTask DisposeAsync()
|
||||||
{
|
{
|
||||||
TaskTracker.RemoveTracking(this);
|
|
||||||
return enumerator.DisposeAsync();
|
return enumerator.DisposeAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -549,7 +541,6 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||||
this.selector = selector;
|
this.selector = selector;
|
||||||
this.cancellationToken = cancellationToken;
|
this.cancellationToken = cancellationToken;
|
||||||
this.moveNextAction = MoveNext;
|
this.moveNextAction = MoveNext;
|
||||||
TaskTracker.TrackActiveTask(this, 3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public TResult Current { get; private set; }
|
public TResult Current { get; private set; }
|
||||||
|
@ -630,7 +621,6 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||||
|
|
||||||
public UniTask DisposeAsync()
|
public UniTask DisposeAsync()
|
||||||
{
|
{
|
||||||
TaskTracker.RemoveTracking(this);
|
|
||||||
return enumerator.DisposeAsync();
|
return enumerator.DisposeAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -671,7 +661,6 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||||
this.selector = selector;
|
this.selector = selector;
|
||||||
this.cancellationToken = cancellationToken;
|
this.cancellationToken = cancellationToken;
|
||||||
this.moveNextAction = MoveNext;
|
this.moveNextAction = MoveNext;
|
||||||
TaskTracker.TrackActiveTask(this, 3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public TResult Current { get; private set; }
|
public TResult Current { get; private set; }
|
||||||
|
@ -752,7 +741,6 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||||
|
|
||||||
public UniTask DisposeAsync()
|
public UniTask DisposeAsync()
|
||||||
{
|
{
|
||||||
TaskTracker.RemoveTracking(this);
|
|
||||||
return enumerator.DisposeAsync();
|
return enumerator.DisposeAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,187 +0,0 @@
|
||||||
using Cysharp.Threading.Tasks.Internal;
|
|
||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace Cysharp.Threading.Tasks.Linq
|
|
||||||
{
|
|
||||||
public static partial class UniTaskAsyncEnumerable
|
|
||||||
{
|
|
||||||
public static IUniTaskAsyncEnumerable<TSource> SkipUntil<TSource>(this IUniTaskAsyncEnumerable<TSource> source, UniTask other)
|
|
||||||
{
|
|
||||||
Error.ThrowArgumentNullException(source, nameof(source));
|
|
||||||
|
|
||||||
return new SkipUntil<TSource>(source, other, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IUniTaskAsyncEnumerable<TSource> SkipUntil<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<CancellationToken, UniTask> other)
|
|
||||||
{
|
|
||||||
Error.ThrowArgumentNullException(source, nameof(source));
|
|
||||||
Error.ThrowArgumentNullException(source, nameof(other));
|
|
||||||
|
|
||||||
return new SkipUntil<TSource>(source, default, other);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal sealed class SkipUntil<TSource> : IUniTaskAsyncEnumerable<TSource>
|
|
||||||
{
|
|
||||||
readonly IUniTaskAsyncEnumerable<TSource> source;
|
|
||||||
readonly UniTask other;
|
|
||||||
readonly Func<CancellationToken, UniTask> other2;
|
|
||||||
|
|
||||||
public SkipUntil(IUniTaskAsyncEnumerable<TSource> source, UniTask other, Func<CancellationToken, UniTask> other2)
|
|
||||||
{
|
|
||||||
this.source = source;
|
|
||||||
this.other = other;
|
|
||||||
this.other2 = other2;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IUniTaskAsyncEnumerator<TSource> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
if (other2 != null)
|
|
||||||
{
|
|
||||||
return new _SkipUntil(source, this.other2(cancellationToken), cancellationToken);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return new _SkipUntil(source, this.other, cancellationToken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sealed class _SkipUntil : MoveNextSource, IUniTaskAsyncEnumerator<TSource>
|
|
||||||
{
|
|
||||||
static readonly Action<object> CancelDelegate1 = OnCanceled1;
|
|
||||||
static readonly Action<object> MoveNextCoreDelegate = MoveNextCore;
|
|
||||||
|
|
||||||
readonly IUniTaskAsyncEnumerable<TSource> source;
|
|
||||||
CancellationToken cancellationToken1;
|
|
||||||
|
|
||||||
bool completed;
|
|
||||||
CancellationTokenRegistration cancellationTokenRegistration1;
|
|
||||||
IUniTaskAsyncEnumerator<TSource> enumerator;
|
|
||||||
UniTask<bool>.Awaiter awaiter;
|
|
||||||
bool continueNext;
|
|
||||||
Exception exception;
|
|
||||||
|
|
||||||
public _SkipUntil(IUniTaskAsyncEnumerable<TSource> source, UniTask other, CancellationToken cancellationToken1)
|
|
||||||
{
|
|
||||||
this.source = source;
|
|
||||||
this.cancellationToken1 = cancellationToken1;
|
|
||||||
if (cancellationToken1.CanBeCanceled)
|
|
||||||
{
|
|
||||||
this.cancellationTokenRegistration1 = cancellationToken1.RegisterWithoutCaptureExecutionContext(CancelDelegate1, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
TaskTracker.TrackActiveTask(this, 3);
|
|
||||||
RunOther(other).Forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
public TSource Current { get; private set; }
|
|
||||||
|
|
||||||
public UniTask<bool> MoveNextAsync()
|
|
||||||
{
|
|
||||||
if (exception != null)
|
|
||||||
{
|
|
||||||
return UniTask.FromException<bool>(exception);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cancellationToken1.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
return UniTask.FromCanceled<bool>(cancellationToken1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (enumerator == null)
|
|
||||||
{
|
|
||||||
enumerator = source.GetAsyncEnumerator(cancellationToken1);
|
|
||||||
}
|
|
||||||
completionSource.Reset();
|
|
||||||
|
|
||||||
if (completed)
|
|
||||||
{
|
|
||||||
SourceMoveNext();
|
|
||||||
}
|
|
||||||
return new UniTask<bool>(this, completionSource.Version);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SourceMoveNext()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
LOOP:
|
|
||||||
awaiter = enumerator.MoveNextAsync().GetAwaiter();
|
|
||||||
if (awaiter.IsCompleted)
|
|
||||||
{
|
|
||||||
continueNext = true;
|
|
||||||
MoveNextCore(this);
|
|
||||||
if (continueNext)
|
|
||||||
{
|
|
||||||
continueNext = false;
|
|
||||||
goto LOOP;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
awaiter.SourceOnCompleted(MoveNextCoreDelegate, this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
completionSource.TrySetException(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void MoveNextCore(object state)
|
|
||||||
{
|
|
||||||
var self = (_SkipUntil)state;
|
|
||||||
|
|
||||||
if (self.TryGetResult(self.awaiter, out var result))
|
|
||||||
{
|
|
||||||
if (result)
|
|
||||||
{
|
|
||||||
self.Current = self.enumerator.Current;
|
|
||||||
self.completionSource.TrySetResult(true);
|
|
||||||
if (self.continueNext)
|
|
||||||
{
|
|
||||||
self.SourceMoveNext();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self.completionSource.TrySetResult(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async UniTaskVoid RunOther(UniTask other)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await other;
|
|
||||||
completed = true;
|
|
||||||
SourceMoveNext();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
exception = ex;
|
|
||||||
completionSource.TrySetException(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void OnCanceled1(object state)
|
|
||||||
{
|
|
||||||
var self = (_SkipUntil)state;
|
|
||||||
self.completionSource.TrySetCanceled(self.cancellationToken1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public UniTask DisposeAsync()
|
|
||||||
{
|
|
||||||
TaskTracker.RemoveTracking(this);
|
|
||||||
cancellationTokenRegistration1.Dispose();
|
|
||||||
if (enumerator != null)
|
|
||||||
{
|
|
||||||
return enumerator.DisposeAsync();
|
|
||||||
}
|
|
||||||
return default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: de932d79c8d9f3841a066d05ff29edc9
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -32,17 +32,13 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||||
|
|
||||||
sealed class _SkipUntilCanceled : MoveNextSource, IUniTaskAsyncEnumerator<TSource>
|
sealed class _SkipUntilCanceled : MoveNextSource, IUniTaskAsyncEnumerator<TSource>
|
||||||
{
|
{
|
||||||
static readonly Action<object> CancelDelegate1 = OnCanceled1;
|
|
||||||
static readonly Action<object> CancelDelegate2 = OnCanceled2;
|
|
||||||
static readonly Action<object> MoveNextCoreDelegate = MoveNextCore;
|
static readonly Action<object> MoveNextCoreDelegate = MoveNextCore;
|
||||||
|
|
||||||
readonly IUniTaskAsyncEnumerable<TSource> source;
|
readonly IUniTaskAsyncEnumerable<TSource> source;
|
||||||
CancellationToken cancellationToken1;
|
CancellationToken cancellationToken1;
|
||||||
CancellationToken cancellationToken2;
|
CancellationToken cancellationToken2;
|
||||||
CancellationTokenRegistration cancellationTokenRegistration1;
|
|
||||||
CancellationTokenRegistration cancellationTokenRegistration2;
|
|
||||||
|
|
||||||
int isCanceled;
|
bool isCanceled;
|
||||||
IUniTaskAsyncEnumerator<TSource> enumerator;
|
IUniTaskAsyncEnumerator<TSource> enumerator;
|
||||||
UniTask<bool>.Awaiter awaiter;
|
UniTask<bool>.Awaiter awaiter;
|
||||||
bool continueNext;
|
bool continueNext;
|
||||||
|
@ -52,14 +48,6 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||||
this.source = source;
|
this.source = source;
|
||||||
this.cancellationToken1 = cancellationToken1;
|
this.cancellationToken1 = cancellationToken1;
|
||||||
this.cancellationToken2 = cancellationToken2;
|
this.cancellationToken2 = cancellationToken2;
|
||||||
if (cancellationToken1.CanBeCanceled)
|
|
||||||
{
|
|
||||||
this.cancellationTokenRegistration1 = cancellationToken1.RegisterWithoutCaptureExecutionContext(CancelDelegate1, this);
|
|
||||||
}
|
|
||||||
if (cancellationToken1 != cancellationToken2 && cancellationToken2.CanBeCanceled)
|
|
||||||
{
|
|
||||||
this.cancellationTokenRegistration2 = cancellationToken2.RegisterWithoutCaptureExecutionContext(CancelDelegate2, this);
|
|
||||||
}
|
|
||||||
TaskTracker.TrackActiveTask(this, 3);
|
TaskTracker.TrackActiveTask(this, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,18 +55,15 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||||
|
|
||||||
public UniTask<bool> MoveNextAsync()
|
public UniTask<bool> MoveNextAsync()
|
||||||
{
|
{
|
||||||
|
if (cancellationToken1.IsCancellationRequested) isCanceled = true;
|
||||||
|
if (cancellationToken2.IsCancellationRequested) isCanceled = true;
|
||||||
|
|
||||||
if (enumerator == null)
|
if (enumerator == null)
|
||||||
{
|
{
|
||||||
if (cancellationToken1.IsCancellationRequested) isCanceled = 1;
|
|
||||||
if (cancellationToken2.IsCancellationRequested) isCanceled = 1;
|
|
||||||
enumerator = source.GetAsyncEnumerator(cancellationToken2); // use only AsyncEnumerator provided token.
|
enumerator = source.GetAsyncEnumerator(cancellationToken2); // use only AsyncEnumerator provided token.
|
||||||
}
|
}
|
||||||
completionSource.Reset();
|
completionSource.Reset();
|
||||||
|
|
||||||
if (isCanceled != 0)
|
|
||||||
{
|
|
||||||
SourceMoveNext();
|
SourceMoveNext();
|
||||||
}
|
|
||||||
return new UniTask<bool>(this, completionSource.Version);
|
return new UniTask<bool>(this, completionSource.Version);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,13 +102,27 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||||
{
|
{
|
||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
|
AGAIN:
|
||||||
|
|
||||||
|
if (self.isCanceled)
|
||||||
|
{
|
||||||
|
self.continueNext = false;
|
||||||
self.Current = self.enumerator.Current;
|
self.Current = self.enumerator.Current;
|
||||||
self.completionSource.TrySetResult(true);
|
self.completionSource.TrySetResult(true);
|
||||||
if (self.continueNext)
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (self.cancellationToken1.IsCancellationRequested) self.isCanceled = true;
|
||||||
|
if (self.cancellationToken2.IsCancellationRequested) self.isCanceled = true;
|
||||||
|
|
||||||
|
if (self.isCanceled) goto AGAIN;
|
||||||
|
|
||||||
|
if (!self.continueNext)
|
||||||
{
|
{
|
||||||
self.SourceMoveNext();
|
self.SourceMoveNext();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
self.completionSource.TrySetResult(false);
|
self.completionSource.TrySetResult(false);
|
||||||
|
@ -131,37 +130,9 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void OnCanceled1(object state)
|
|
||||||
{
|
|
||||||
var self = (_SkipUntilCanceled)state;
|
|
||||||
if (self.isCanceled == 0)
|
|
||||||
{
|
|
||||||
if (Interlocked.Increment(ref self.isCanceled) == 1)
|
|
||||||
{
|
|
||||||
self.cancellationTokenRegistration2.Dispose();
|
|
||||||
self.SourceMoveNext();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void OnCanceled2(object state)
|
|
||||||
{
|
|
||||||
var self = (_SkipUntilCanceled)state;
|
|
||||||
if (self.isCanceled == 0)
|
|
||||||
{
|
|
||||||
if (Interlocked.Increment(ref self.isCanceled) == 1)
|
|
||||||
{
|
|
||||||
self.cancellationTokenRegistration2.Dispose();
|
|
||||||
self.SourceMoveNext();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public UniTask DisposeAsync()
|
public UniTask DisposeAsync()
|
||||||
{
|
{
|
||||||
TaskTracker.RemoveTracking(this);
|
TaskTracker.RemoveTracking(this);
|
||||||
cancellationTokenRegistration1.Dispose();
|
|
||||||
cancellationTokenRegistration2.Dispose();
|
|
||||||
if (enumerator != null)
|
if (enumerator != null)
|
||||||
{
|
{
|
||||||
return enumerator.DisposeAsync();
|
return enumerator.DisposeAsync();
|
||||||
|
|
|
@ -1,536 +0,0 @@
|
||||||
using Cysharp.Threading.Tasks.Internal;
|
|
||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
using Subscribes = Cysharp.Threading.Tasks.Linq.Subscribe;
|
|
||||||
|
|
||||||
namespace Cysharp.Threading.Tasks.Linq
|
|
||||||
{
|
|
||||||
public static partial class UniTaskAsyncEnumerable
|
|
||||||
{
|
|
||||||
// OnNext
|
|
||||||
|
|
||||||
public static IDisposable Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Action<TSource> action)
|
|
||||||
{
|
|
||||||
Error.ThrowArgumentNullException(source, nameof(source));
|
|
||||||
Error.ThrowArgumentNullException(action, nameof(action));
|
|
||||||
|
|
||||||
var cts = new CancellationTokenDisposable();
|
|
||||||
Subscribes.SubscribeCore(source, action, Subscribes.NopError, Subscribes.NopCompleted, cts.Token).Forget();
|
|
||||||
return cts;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IDisposable Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTaskVoid> action)
|
|
||||||
{
|
|
||||||
Error.ThrowArgumentNullException(source, nameof(source));
|
|
||||||
Error.ThrowArgumentNullException(action, nameof(action));
|
|
||||||
|
|
||||||
var cts = new CancellationTokenDisposable();
|
|
||||||
Subscribes.SubscribeCore(source, action, Subscribes.NopError, Subscribes.NopCompleted, cts.Token).Forget();
|
|
||||||
return cts;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IDisposable Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTaskVoid> action)
|
|
||||||
{
|
|
||||||
Error.ThrowArgumentNullException(source, nameof(source));
|
|
||||||
Error.ThrowArgumentNullException(action, nameof(action));
|
|
||||||
|
|
||||||
var cts = new CancellationTokenDisposable();
|
|
||||||
Subscribes.SubscribeCore(source, action, Subscribes.NopError, Subscribes.NopCompleted, cts.Token).Forget();
|
|
||||||
return cts;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Action<TSource> action, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
Error.ThrowArgumentNullException(source, nameof(source));
|
|
||||||
Error.ThrowArgumentNullException(action, nameof(action));
|
|
||||||
|
|
||||||
Subscribes.SubscribeCore(source, action, Subscribes.NopError, Subscribes.NopCompleted, cancellationToken).Forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTaskVoid> action, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
Error.ThrowArgumentNullException(source, nameof(source));
|
|
||||||
Error.ThrowArgumentNullException(action, nameof(action));
|
|
||||||
|
|
||||||
Subscribes.SubscribeCore(source, action, Subscribes.NopError, Subscribes.NopCompleted, cancellationToken).Forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTaskVoid> action, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
Error.ThrowArgumentNullException(source, nameof(source));
|
|
||||||
Error.ThrowArgumentNullException(action, nameof(action));
|
|
||||||
|
|
||||||
Subscribes.SubscribeCore(source, action, Subscribes.NopError, Subscribes.NopCompleted, cancellationToken).Forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IDisposable SubscribeAwait<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask> onNext)
|
|
||||||
{
|
|
||||||
Error.ThrowArgumentNullException(source, nameof(source));
|
|
||||||
Error.ThrowArgumentNullException(onNext, nameof(onNext));
|
|
||||||
|
|
||||||
var cts = new CancellationTokenDisposable();
|
|
||||||
Subscribes.SubscribeAwaitCore(source, onNext, Subscribes.NopError, Subscribes.NopCompleted, cts.Token).Forget();
|
|
||||||
return cts;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SubscribeAwait<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask> onNext, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
Error.ThrowArgumentNullException(source, nameof(source));
|
|
||||||
Error.ThrowArgumentNullException(onNext, nameof(onNext));
|
|
||||||
|
|
||||||
Subscribes.SubscribeAwaitCore(source, onNext, Subscribes.NopError, Subscribes.NopCompleted, cancellationToken).Forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IDisposable SubscribeAwait<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask> onNext)
|
|
||||||
{
|
|
||||||
Error.ThrowArgumentNullException(source, nameof(source));
|
|
||||||
Error.ThrowArgumentNullException(onNext, nameof(onNext));
|
|
||||||
|
|
||||||
var cts = new CancellationTokenDisposable();
|
|
||||||
Subscribes.SubscribeAwaitCore(source, onNext, Subscribes.NopError, Subscribes.NopCompleted, cts.Token).Forget();
|
|
||||||
return cts;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SubscribeAwait<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask> onNext, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
Error.ThrowArgumentNullException(source, nameof(source));
|
|
||||||
Error.ThrowArgumentNullException(onNext, nameof(onNext));
|
|
||||||
|
|
||||||
Subscribes.SubscribeAwaitCore(source, onNext, Subscribes.NopError, Subscribes.NopCompleted, cancellationToken).Forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
// OnNext, OnError
|
|
||||||
|
|
||||||
public static IDisposable Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Action<TSource> onNext, Action<Exception> onError)
|
|
||||||
{
|
|
||||||
Error.ThrowArgumentNullException(source, nameof(source));
|
|
||||||
Error.ThrowArgumentNullException(onNext, nameof(onNext));
|
|
||||||
Error.ThrowArgumentNullException(onError, nameof(onError));
|
|
||||||
|
|
||||||
var cts = new CancellationTokenDisposable();
|
|
||||||
Subscribes.SubscribeCore(source, onNext, onError, Subscribes.NopCompleted, cts.Token).Forget();
|
|
||||||
return cts;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IDisposable Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTaskVoid> onNext, Action<Exception> onError)
|
|
||||||
{
|
|
||||||
Error.ThrowArgumentNullException(source, nameof(source));
|
|
||||||
Error.ThrowArgumentNullException(onNext, nameof(onNext));
|
|
||||||
Error.ThrowArgumentNullException(onError, nameof(onError));
|
|
||||||
|
|
||||||
var cts = new CancellationTokenDisposable();
|
|
||||||
Subscribes.SubscribeCore(source, onNext, onError, Subscribes.NopCompleted, cts.Token).Forget();
|
|
||||||
return cts;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Action<TSource> onNext, Action<Exception> onError, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
Error.ThrowArgumentNullException(source, nameof(source));
|
|
||||||
Error.ThrowArgumentNullException(onNext, nameof(onNext));
|
|
||||||
Error.ThrowArgumentNullException(onError, nameof(onError));
|
|
||||||
|
|
||||||
Subscribes.SubscribeCore(source, onNext, onError, Subscribes.NopCompleted, cancellationToken).Forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTaskVoid> onNext, Action<Exception> onError, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
Error.ThrowArgumentNullException(source, nameof(source));
|
|
||||||
Error.ThrowArgumentNullException(onNext, nameof(onNext));
|
|
||||||
Error.ThrowArgumentNullException(onError, nameof(onError));
|
|
||||||
|
|
||||||
Subscribes.SubscribeCore(source, onNext, onError, Subscribes.NopCompleted, cancellationToken).Forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IDisposable SubscribeAwait<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask> onNext, Action<Exception> onError)
|
|
||||||
{
|
|
||||||
Error.ThrowArgumentNullException(source, nameof(source));
|
|
||||||
Error.ThrowArgumentNullException(onNext, nameof(onNext));
|
|
||||||
Error.ThrowArgumentNullException(onError, nameof(onError));
|
|
||||||
|
|
||||||
var cts = new CancellationTokenDisposable();
|
|
||||||
Subscribes.SubscribeAwaitCore(source, onNext, onError, Subscribes.NopCompleted, cts.Token).Forget();
|
|
||||||
return cts;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SubscribeAwait<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask> onNext, Action<Exception> onError, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
Error.ThrowArgumentNullException(source, nameof(source));
|
|
||||||
Error.ThrowArgumentNullException(onNext, nameof(onNext));
|
|
||||||
Error.ThrowArgumentNullException(onError, nameof(onError));
|
|
||||||
|
|
||||||
Subscribes.SubscribeAwaitCore(source, onNext, onError, Subscribes.NopCompleted, cancellationToken).Forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IDisposable SubscribeAwait<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask> onNext, Action<Exception> onError)
|
|
||||||
{
|
|
||||||
Error.ThrowArgumentNullException(source, nameof(source));
|
|
||||||
Error.ThrowArgumentNullException(onNext, nameof(onNext));
|
|
||||||
Error.ThrowArgumentNullException(onError, nameof(onError));
|
|
||||||
|
|
||||||
var cts = new CancellationTokenDisposable();
|
|
||||||
Subscribes.SubscribeAwaitCore(source, onNext, onError, Subscribes.NopCompleted, cts.Token).Forget();
|
|
||||||
return cts;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SubscribeAwait<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask> onNext, Action<Exception> onError, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
Error.ThrowArgumentNullException(source, nameof(source));
|
|
||||||
Error.ThrowArgumentNullException(onNext, nameof(onNext));
|
|
||||||
Error.ThrowArgumentNullException(onError, nameof(onError));
|
|
||||||
|
|
||||||
Subscribes.SubscribeAwaitCore(source, onNext, onError, Subscribes.NopCompleted, cancellationToken).Forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
// OnNext, OnCompleted
|
|
||||||
|
|
||||||
public static IDisposable Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Action<TSource> onNext, Action onCompleted)
|
|
||||||
{
|
|
||||||
Error.ThrowArgumentNullException(source, nameof(source));
|
|
||||||
Error.ThrowArgumentNullException(onNext, nameof(onNext));
|
|
||||||
Error.ThrowArgumentNullException(onCompleted, nameof(onCompleted));
|
|
||||||
|
|
||||||
var cts = new CancellationTokenDisposable();
|
|
||||||
Subscribes.SubscribeCore(source, onNext, Subscribes.NopError, onCompleted, cts.Token).Forget();
|
|
||||||
return cts;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IDisposable Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTaskVoid> onNext, Action onCompleted)
|
|
||||||
{
|
|
||||||
Error.ThrowArgumentNullException(source, nameof(source));
|
|
||||||
Error.ThrowArgumentNullException(onNext, nameof(onNext));
|
|
||||||
Error.ThrowArgumentNullException(onCompleted, nameof(onCompleted));
|
|
||||||
|
|
||||||
var cts = new CancellationTokenDisposable();
|
|
||||||
Subscribes.SubscribeCore(source, onNext, Subscribes.NopError, onCompleted, cts.Token).Forget();
|
|
||||||
return cts;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Action<TSource> onNext, Action onCompleted, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
Error.ThrowArgumentNullException(source, nameof(source));
|
|
||||||
Error.ThrowArgumentNullException(onNext, nameof(onNext));
|
|
||||||
Error.ThrowArgumentNullException(onCompleted, nameof(onCompleted));
|
|
||||||
|
|
||||||
Subscribes.SubscribeCore(source, onNext, Subscribes.NopError, onCompleted, cancellationToken).Forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTaskVoid> onNext, Action onCompleted, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
Error.ThrowArgumentNullException(source, nameof(source));
|
|
||||||
Error.ThrowArgumentNullException(onNext, nameof(onNext));
|
|
||||||
Error.ThrowArgumentNullException(onCompleted, nameof(onCompleted));
|
|
||||||
|
|
||||||
Subscribes.SubscribeCore(source, onNext, Subscribes.NopError, onCompleted, cancellationToken).Forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IDisposable SubscribeAwait<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask> onNext, Action onCompleted)
|
|
||||||
{
|
|
||||||
Error.ThrowArgumentNullException(source, nameof(source));
|
|
||||||
Error.ThrowArgumentNullException(onNext, nameof(onNext));
|
|
||||||
Error.ThrowArgumentNullException(onCompleted, nameof(onCompleted));
|
|
||||||
|
|
||||||
var cts = new CancellationTokenDisposable();
|
|
||||||
Subscribes.SubscribeAwaitCore(source, onNext, Subscribes.NopError, onCompleted, cts.Token).Forget();
|
|
||||||
return cts;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SubscribeAwait<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask> onNext, Action onCompleted, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
Error.ThrowArgumentNullException(source, nameof(source));
|
|
||||||
Error.ThrowArgumentNullException(onNext, nameof(onNext));
|
|
||||||
Error.ThrowArgumentNullException(onCompleted, nameof(onCompleted));
|
|
||||||
|
|
||||||
Subscribes.SubscribeAwaitCore(source, onNext, Subscribes.NopError, onCompleted, cancellationToken).Forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IDisposable SubscribeAwait<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask> onNext, Action onCompleted)
|
|
||||||
{
|
|
||||||
Error.ThrowArgumentNullException(source, nameof(source));
|
|
||||||
Error.ThrowArgumentNullException(onNext, nameof(onNext));
|
|
||||||
Error.ThrowArgumentNullException(onCompleted, nameof(onCompleted));
|
|
||||||
|
|
||||||
var cts = new CancellationTokenDisposable();
|
|
||||||
Subscribes.SubscribeAwaitCore(source, onNext, Subscribes.NopError, onCompleted, cts.Token).Forget();
|
|
||||||
return cts;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SubscribeAwait<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask> onNext, Action onCompleted, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
Error.ThrowArgumentNullException(source, nameof(source));
|
|
||||||
Error.ThrowArgumentNullException(onNext, nameof(onNext));
|
|
||||||
Error.ThrowArgumentNullException(onCompleted, nameof(onCompleted));
|
|
||||||
|
|
||||||
Subscribes.SubscribeAwaitCore(source, onNext, Subscribes.NopError, onCompleted, cancellationToken).Forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
// IObserver
|
|
||||||
|
|
||||||
public static IDisposable Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, IObserver<TSource> observer)
|
|
||||||
{
|
|
||||||
Error.ThrowArgumentNullException(source, nameof(source));
|
|
||||||
Error.ThrowArgumentNullException(observer, nameof(observer));
|
|
||||||
|
|
||||||
var cts = new CancellationTokenDisposable();
|
|
||||||
Subscribes.SubscribeCore(source, observer, cts.Token).Forget();
|
|
||||||
return cts;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Subscribe<TSource>(this IUniTaskAsyncEnumerable<TSource> source, IObserver<TSource> observer, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
Error.ThrowArgumentNullException(source, nameof(source));
|
|
||||||
Error.ThrowArgumentNullException(observer, nameof(observer));
|
|
||||||
|
|
||||||
Subscribes.SubscribeCore(source, observer, cancellationToken).Forget();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal sealed class CancellationTokenDisposable : IDisposable
|
|
||||||
{
|
|
||||||
readonly CancellationTokenSource cts = new CancellationTokenSource();
|
|
||||||
|
|
||||||
public CancellationToken Token => cts.Token;
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (!cts.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
cts.Cancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static class Subscribe
|
|
||||||
{
|
|
||||||
public static readonly Action<Exception> NopError = _ => { };
|
|
||||||
public static readonly Action NopCompleted = () => { };
|
|
||||||
|
|
||||||
public static async UniTaskVoid SubscribeCore<TSource>(IUniTaskAsyncEnumerable<TSource> source, Action<TSource> onNext, Action<Exception> onError, Action onCompleted, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var e = source.GetAsyncEnumerator(cancellationToken);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
while (await e.MoveNextAsync())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
onNext(e.Current);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
UniTaskScheduler.PublishUnobservedTaskException(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onCompleted();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
if (onError == NopError)
|
|
||||||
{
|
|
||||||
UniTaskScheduler.PublishUnobservedTaskException(ex);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ex is OperationCanceledException) return;
|
|
||||||
|
|
||||||
onError(ex);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (e != null)
|
|
||||||
{
|
|
||||||
await e.DisposeAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async UniTaskVoid SubscribeCore<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTaskVoid> onNext, Action<Exception> onError, Action onCompleted, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var e = source.GetAsyncEnumerator(cancellationToken);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
while (await e.MoveNextAsync())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
onNext(e.Current).Forget();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
UniTaskScheduler.PublishUnobservedTaskException(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onCompleted();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
if (onError == NopError)
|
|
||||||
{
|
|
||||||
UniTaskScheduler.PublishUnobservedTaskException(ex);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ex is OperationCanceledException) return;
|
|
||||||
|
|
||||||
onError(ex);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (e != null)
|
|
||||||
{
|
|
||||||
await e.DisposeAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async UniTaskVoid SubscribeCore<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTaskVoid> onNext, Action<Exception> onError, Action onCompleted, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var e = source.GetAsyncEnumerator(cancellationToken);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
while (await e.MoveNextAsync())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
onNext(e.Current, cancellationToken).Forget();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
UniTaskScheduler.PublishUnobservedTaskException(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onCompleted();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
if (onError == NopError)
|
|
||||||
{
|
|
||||||
UniTaskScheduler.PublishUnobservedTaskException(ex);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ex is OperationCanceledException) return;
|
|
||||||
|
|
||||||
onError(ex);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (e != null)
|
|
||||||
{
|
|
||||||
await e.DisposeAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async UniTaskVoid SubscribeCore<TSource>(IUniTaskAsyncEnumerable<TSource> source, IObserver<TSource> observer, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var e = source.GetAsyncEnumerator(cancellationToken);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
while (await e.MoveNextAsync())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
observer.OnNext(e.Current);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
UniTaskScheduler.PublishUnobservedTaskException(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
observer.OnCompleted();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
if (ex is OperationCanceledException) return;
|
|
||||||
|
|
||||||
observer.OnError(ex);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (e != null)
|
|
||||||
{
|
|
||||||
await e.DisposeAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async UniTaskVoid SubscribeAwaitCore<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask> onNext, Action<Exception> onError, Action onCompleted, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var e = source.GetAsyncEnumerator(cancellationToken);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
while (await e.MoveNextAsync())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await onNext(e.Current);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
UniTaskScheduler.PublishUnobservedTaskException(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onCompleted();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
if (onError == NopError)
|
|
||||||
{
|
|
||||||
UniTaskScheduler.PublishUnobservedTaskException(ex);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ex is OperationCanceledException) return;
|
|
||||||
|
|
||||||
onError(ex);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (e != null)
|
|
||||||
{
|
|
||||||
await e.DisposeAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async UniTaskVoid SubscribeAwaitCore<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask> onNext, Action<Exception> onError, Action onCompleted, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var e = source.GetAsyncEnumerator(cancellationToken);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
while (await e.MoveNextAsync())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await onNext(e.Current, cancellationToken);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
UniTaskScheduler.PublishUnobservedTaskException(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onCompleted();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
if (onError == NopError)
|
|
||||||
{
|
|
||||||
UniTaskScheduler.PublishUnobservedTaskException(ex);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ex is OperationCanceledException) return;
|
|
||||||
|
|
||||||
onError(ex);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (e != null)
|
|
||||||
{
|
|
||||||
await e.DisposeAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 263479eb04c189741931fc0e2f615c2d
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -1,190 +0,0 @@
|
||||||
using Cysharp.Threading.Tasks.Internal;
|
|
||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace Cysharp.Threading.Tasks.Linq
|
|
||||||
{
|
|
||||||
public static partial class UniTaskAsyncEnumerable
|
|
||||||
{
|
|
||||||
public static IUniTaskAsyncEnumerable<TSource> TakeUntil<TSource>(this IUniTaskAsyncEnumerable<TSource> source, UniTask other)
|
|
||||||
{
|
|
||||||
Error.ThrowArgumentNullException(source, nameof(source));
|
|
||||||
|
|
||||||
return new TakeUntil<TSource>(source, other, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IUniTaskAsyncEnumerable<TSource> TakeUntil<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<CancellationToken, UniTask> other)
|
|
||||||
{
|
|
||||||
Error.ThrowArgumentNullException(source, nameof(source));
|
|
||||||
Error.ThrowArgumentNullException(source, nameof(other));
|
|
||||||
|
|
||||||
return new TakeUntil<TSource>(source, default, other);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal sealed class TakeUntil<TSource> : IUniTaskAsyncEnumerable<TSource>
|
|
||||||
{
|
|
||||||
readonly IUniTaskAsyncEnumerable<TSource> source;
|
|
||||||
readonly UniTask other;
|
|
||||||
readonly Func<CancellationToken, UniTask> other2;
|
|
||||||
|
|
||||||
public TakeUntil(IUniTaskAsyncEnumerable<TSource> source, UniTask other, Func<CancellationToken, UniTask> other2)
|
|
||||||
{
|
|
||||||
this.source = source;
|
|
||||||
this.other = other;
|
|
||||||
this.other2 = other2;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IUniTaskAsyncEnumerator<TSource> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
if (other2 != null)
|
|
||||||
{
|
|
||||||
return new _TakeUntil(source, this.other2(cancellationToken), cancellationToken);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return new _TakeUntil(source, this.other, cancellationToken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sealed class _TakeUntil : MoveNextSource, IUniTaskAsyncEnumerator<TSource>
|
|
||||||
{
|
|
||||||
static readonly Action<object> CancelDelegate1 = OnCanceled1;
|
|
||||||
static readonly Action<object> MoveNextCoreDelegate = MoveNextCore;
|
|
||||||
|
|
||||||
readonly IUniTaskAsyncEnumerable<TSource> source;
|
|
||||||
CancellationToken cancellationToken1;
|
|
||||||
CancellationTokenRegistration cancellationTokenRegistration1;
|
|
||||||
|
|
||||||
bool completed;
|
|
||||||
Exception exception;
|
|
||||||
IUniTaskAsyncEnumerator<TSource> enumerator;
|
|
||||||
UniTask<bool>.Awaiter awaiter;
|
|
||||||
|
|
||||||
public _TakeUntil(IUniTaskAsyncEnumerable<TSource> source, UniTask other, CancellationToken cancellationToken1)
|
|
||||||
{
|
|
||||||
this.source = source;
|
|
||||||
this.cancellationToken1 = cancellationToken1;
|
|
||||||
|
|
||||||
if (cancellationToken1.CanBeCanceled)
|
|
||||||
{
|
|
||||||
this.cancellationTokenRegistration1 = cancellationToken1.RegisterWithoutCaptureExecutionContext(CancelDelegate1, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
TaskTracker.TrackActiveTask(this, 3);
|
|
||||||
|
|
||||||
RunOther(other).Forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
public TSource Current { get; private set; }
|
|
||||||
|
|
||||||
public UniTask<bool> MoveNextAsync()
|
|
||||||
{
|
|
||||||
if (completed)
|
|
||||||
{
|
|
||||||
return CompletedTasks.False;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (exception != null)
|
|
||||||
{
|
|
||||||
return UniTask.FromException<bool>(exception);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cancellationToken1.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
return UniTask.FromCanceled<bool>(cancellationToken1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (enumerator == null)
|
|
||||||
{
|
|
||||||
enumerator = source.GetAsyncEnumerator(cancellationToken1);
|
|
||||||
}
|
|
||||||
|
|
||||||
completionSource.Reset();
|
|
||||||
SourceMoveNext();
|
|
||||||
return new UniTask<bool>(this, completionSource.Version);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SourceMoveNext()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
awaiter = enumerator.MoveNextAsync().GetAwaiter();
|
|
||||||
if (awaiter.IsCompleted)
|
|
||||||
{
|
|
||||||
MoveNextCore(this);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
awaiter.SourceOnCompleted(MoveNextCoreDelegate, this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
completionSource.TrySetException(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void MoveNextCore(object state)
|
|
||||||
{
|
|
||||||
var self = (_TakeUntil)state;
|
|
||||||
|
|
||||||
if (self.TryGetResult(self.awaiter, out var result))
|
|
||||||
{
|
|
||||||
if (result)
|
|
||||||
{
|
|
||||||
if (self.exception != null)
|
|
||||||
{
|
|
||||||
self.completionSource.TrySetException(self.exception);
|
|
||||||
}
|
|
||||||
else if (self.cancellationToken1.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
self.completionSource.TrySetCanceled(self.cancellationToken1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self.Current = self.enumerator.Current;
|
|
||||||
self.completionSource.TrySetResult(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self.completionSource.TrySetResult(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async UniTaskVoid RunOther(UniTask other)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await other;
|
|
||||||
completed = true;
|
|
||||||
completionSource.TrySetResult(false);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
exception = ex;
|
|
||||||
completionSource.TrySetException(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void OnCanceled1(object state)
|
|
||||||
{
|
|
||||||
var self = (_TakeUntil)state;
|
|
||||||
self.completionSource.TrySetCanceled(self.cancellationToken1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public UniTask DisposeAsync()
|
|
||||||
{
|
|
||||||
TaskTracker.RemoveTracking(this);
|
|
||||||
cancellationTokenRegistration1.Dispose();
|
|
||||||
if (enumerator != null)
|
|
||||||
{
|
|
||||||
return enumerator.DisposeAsync();
|
|
||||||
}
|
|
||||||
return default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 12bda324162f15349afefc2c152ac07f
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
|
@ -19,6 +19,8 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||||
{
|
{
|
||||||
internal static async UniTask<TSource[]> ToArrayAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, CancellationToken cancellationToken)
|
internal static async UniTask<TSource[]> ToArrayAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
// UnityEngine.Debug.Log("Called ToArray");
|
||||||
|
|
||||||
var pool = ArrayPool<TSource>.Shared;
|
var pool = ArrayPool<TSource>.Shared;
|
||||||
var array = pool.Rent(16);
|
var array = pool.Rent(16);
|
||||||
|
|
||||||
|
|
|
@ -248,8 +248,6 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||||
return current;
|
return current;
|
||||||
}
|
}
|
||||||
|
|
||||||
lock (queuedResult)
|
|
||||||
{
|
|
||||||
if (queuedResult.Count != 0)
|
if (queuedResult.Count != 0)
|
||||||
{
|
{
|
||||||
current = queuedResult.Dequeue();
|
current = queuedResult.Dequeue();
|
||||||
|
@ -262,7 +260,6 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public UniTask<bool> MoveNextAsync()
|
public UniTask<bool> MoveNextAsync()
|
||||||
{
|
{
|
||||||
|
|
|
@ -4,49 +4,36 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||||
{
|
{
|
||||||
public static partial class UniTaskAsyncEnumerable
|
public static partial class UniTaskAsyncEnumerable
|
||||||
{
|
{
|
||||||
public static IUniTaskAsyncEnumerable<AsyncUnit> EveryUpdate(PlayerLoopTiming updateTiming = PlayerLoopTiming.Update, bool cancelImmediately = false)
|
public static IUniTaskAsyncEnumerable<AsyncUnit> EveryUpdate(PlayerLoopTiming updateTiming = PlayerLoopTiming.Update)
|
||||||
{
|
{
|
||||||
return new EveryUpdate(updateTiming, cancelImmediately);
|
return new EveryUpdate(updateTiming);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class EveryUpdate : IUniTaskAsyncEnumerable<AsyncUnit>
|
internal class EveryUpdate : IUniTaskAsyncEnumerable<AsyncUnit>
|
||||||
{
|
{
|
||||||
readonly PlayerLoopTiming updateTiming;
|
readonly PlayerLoopTiming updateTiming;
|
||||||
readonly bool cancelImmediately;
|
|
||||||
|
|
||||||
public EveryUpdate(PlayerLoopTiming updateTiming, bool cancelImmediately)
|
public EveryUpdate(PlayerLoopTiming updateTiming)
|
||||||
{
|
{
|
||||||
this.updateTiming = updateTiming;
|
this.updateTiming = updateTiming;
|
||||||
this.cancelImmediately = cancelImmediately;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IUniTaskAsyncEnumerator<AsyncUnit> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
public IUniTaskAsyncEnumerator<AsyncUnit> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
return new _EveryUpdate(updateTiming, cancellationToken, cancelImmediately);
|
return new _EveryUpdate(updateTiming, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
class _EveryUpdate : MoveNextSource, IUniTaskAsyncEnumerator<AsyncUnit>, IPlayerLoopItem
|
class _EveryUpdate : MoveNextSource, IUniTaskAsyncEnumerator<AsyncUnit>, IPlayerLoopItem
|
||||||
{
|
{
|
||||||
readonly PlayerLoopTiming updateTiming;
|
readonly PlayerLoopTiming updateTiming;
|
||||||
readonly CancellationToken cancellationToken;
|
CancellationToken cancellationToken;
|
||||||
readonly CancellationTokenRegistration cancellationTokenRegistration;
|
|
||||||
|
|
||||||
bool disposed;
|
bool disposed;
|
||||||
|
|
||||||
public _EveryUpdate(PlayerLoopTiming updateTiming, CancellationToken cancellationToken, bool cancelImmediately)
|
public _EveryUpdate(PlayerLoopTiming updateTiming, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
this.updateTiming = updateTiming;
|
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);
|
TaskTracker.TrackActiveTask(this, 2);
|
||||||
PlayerLoopHelper.AddAction(updateTiming, this);
|
PlayerLoopHelper.AddAction(updateTiming, this);
|
||||||
|
@ -56,14 +43,10 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||||
|
|
||||||
public UniTask<bool> MoveNextAsync()
|
public UniTask<bool> MoveNextAsync()
|
||||||
{
|
{
|
||||||
if (disposed) return CompletedTasks.False;
|
// return false instead of throw
|
||||||
|
if (disposed || cancellationToken.IsCancellationRequested) return CompletedTasks.False;
|
||||||
|
|
||||||
completionSource.Reset();
|
completionSource.Reset();
|
||||||
|
|
||||||
if (cancellationToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
completionSource.TrySetCanceled(cancellationToken);
|
|
||||||
}
|
|
||||||
return new UniTask<bool>(this, completionSource.Version);
|
return new UniTask<bool>(this, completionSource.Version);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,7 +54,6 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||||
{
|
{
|
||||||
if (!disposed)
|
if (!disposed)
|
||||||
{
|
{
|
||||||
cancellationTokenRegistration.Dispose();
|
|
||||||
disposed = true;
|
disposed = true;
|
||||||
TaskTracker.RemoveTracking(this);
|
TaskTracker.RemoveTracking(this);
|
||||||
}
|
}
|
||||||
|
@ -80,13 +62,7 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||||
|
|
||||||
public bool MoveNext()
|
public bool MoveNext()
|
||||||
{
|
{
|
||||||
if (cancellationToken.IsCancellationRequested)
|
if (disposed || cancellationToken.IsCancellationRequested)
|
||||||
{
|
|
||||||
completionSource.TrySetCanceled(cancellationToken);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (disposed)
|
|
||||||
{
|
{
|
||||||
completionSource.TrySetResult(false);
|
completionSource.TrySetResult(false);
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -7,7 +7,7 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||||
{
|
{
|
||||||
public static partial class UniTaskAsyncEnumerable
|
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, bool cancelImmediately = false)
|
public static IUniTaskAsyncEnumerable<TProperty> EveryValueChanged<TTarget, TProperty>(TTarget target, Func<TTarget, TProperty> propertySelector, PlayerLoopTiming monitorTiming = PlayerLoopTiming.Update, IEqualityComparer<TProperty> equalityComparer = null)
|
||||||
where TTarget : class
|
where TTarget : class
|
||||||
{
|
{
|
||||||
var unityObject = target as UnityEngine.Object;
|
var unityObject = target as UnityEngine.Object;
|
||||||
|
@ -15,11 +15,11 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||||
|
|
||||||
if (isUnityObject)
|
if (isUnityObject)
|
||||||
{
|
{
|
||||||
return new EveryValueChangedUnityObject<TTarget, TProperty>(target, propertySelector, equalityComparer ?? UnityEqualityComparer.GetDefault<TProperty>(), monitorTiming, cancelImmediately);
|
return new EveryValueChangedUnityObject<TTarget, TProperty>(target, propertySelector, equalityComparer ?? UnityEqualityComparer.GetDefault<TProperty>(), monitorTiming);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return new EveryValueChangedStandardObject<TTarget, TProperty>(target, propertySelector, equalityComparer ?? UnityEqualityComparer.GetDefault<TProperty>(), monitorTiming, cancelImmediately);
|
return new EveryValueChangedStandardObject<TTarget, TProperty>(target, propertySelector, equalityComparer ?? UnityEqualityComparer.GetDefault<TProperty>(), monitorTiming);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,20 +30,18 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||||
readonly Func<TTarget, TProperty> propertySelector;
|
readonly Func<TTarget, TProperty> propertySelector;
|
||||||
readonly IEqualityComparer<TProperty> equalityComparer;
|
readonly IEqualityComparer<TProperty> equalityComparer;
|
||||||
readonly PlayerLoopTiming monitorTiming;
|
readonly PlayerLoopTiming monitorTiming;
|
||||||
readonly bool cancelImmediately;
|
|
||||||
|
|
||||||
public EveryValueChangedUnityObject(TTarget target, Func<TTarget, TProperty> propertySelector, IEqualityComparer<TProperty> equalityComparer, PlayerLoopTiming monitorTiming, bool cancelImmediately)
|
public EveryValueChangedUnityObject(TTarget target, Func<TTarget, TProperty> propertySelector, IEqualityComparer<TProperty> equalityComparer, PlayerLoopTiming monitorTiming)
|
||||||
{
|
{
|
||||||
this.target = target;
|
this.target = target;
|
||||||
this.propertySelector = propertySelector;
|
this.propertySelector = propertySelector;
|
||||||
this.equalityComparer = equalityComparer;
|
this.equalityComparer = equalityComparer;
|
||||||
this.monitorTiming = monitorTiming;
|
this.monitorTiming = monitorTiming;
|
||||||
this.cancelImmediately = cancelImmediately;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IUniTaskAsyncEnumerator<TProperty> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
public IUniTaskAsyncEnumerator<TProperty> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
return new _EveryValueChanged(target, propertySelector, equalityComparer, monitorTiming, cancellationToken, cancelImmediately);
|
return new _EveryValueChanged(target, propertySelector, equalityComparer, monitorTiming, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class _EveryValueChanged : MoveNextSource, IUniTaskAsyncEnumerator<TProperty>, IPlayerLoopItem
|
sealed class _EveryValueChanged : MoveNextSource, IUniTaskAsyncEnumerator<TProperty>, IPlayerLoopItem
|
||||||
|
@ -52,14 +50,13 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||||
readonly UnityEngine.Object targetAsUnityObject;
|
readonly UnityEngine.Object targetAsUnityObject;
|
||||||
readonly IEqualityComparer<TProperty> equalityComparer;
|
readonly IEqualityComparer<TProperty> equalityComparer;
|
||||||
readonly Func<TTarget, TProperty> propertySelector;
|
readonly Func<TTarget, TProperty> propertySelector;
|
||||||
readonly CancellationToken cancellationToken;
|
CancellationToken cancellationToken;
|
||||||
readonly CancellationTokenRegistration cancellationTokenRegistration;
|
|
||||||
|
|
||||||
bool first;
|
bool first;
|
||||||
TProperty currentValue;
|
TProperty currentValue;
|
||||||
bool disposed;
|
bool disposed;
|
||||||
|
|
||||||
public _EveryValueChanged(TTarget target, Func<TTarget, TProperty> propertySelector, IEqualityComparer<TProperty> equalityComparer, PlayerLoopTiming monitorTiming, CancellationToken cancellationToken, bool cancelImmediately)
|
public _EveryValueChanged(TTarget target, Func<TTarget, TProperty> propertySelector, IEqualityComparer<TProperty> equalityComparer, PlayerLoopTiming monitorTiming, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
this.target = target;
|
this.target = target;
|
||||||
this.targetAsUnityObject = target as UnityEngine.Object;
|
this.targetAsUnityObject = target as UnityEngine.Object;
|
||||||
|
@ -67,16 +64,6 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||||
this.equalityComparer = equalityComparer;
|
this.equalityComparer = equalityComparer;
|
||||||
this.cancellationToken = cancellationToken;
|
this.cancellationToken = cancellationToken;
|
||||||
this.first = true;
|
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);
|
TaskTracker.TrackActiveTask(this, 2);
|
||||||
PlayerLoopHelper.AddAction(monitorTiming, this);
|
PlayerLoopHelper.AddAction(monitorTiming, this);
|
||||||
}
|
}
|
||||||
|
@ -85,15 +72,8 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||||
|
|
||||||
public UniTask<bool> MoveNextAsync()
|
public UniTask<bool> MoveNextAsync()
|
||||||
{
|
{
|
||||||
if (disposed) return CompletedTasks.False;
|
// return false instead of throw
|
||||||
|
if (disposed || cancellationToken.IsCancellationRequested) return CompletedTasks.False;
|
||||||
completionSource.Reset();
|
|
||||||
|
|
||||||
if (cancellationToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
completionSource.TrySetCanceled(cancellationToken);
|
|
||||||
return new UniTask<bool>(this, completionSource.Version);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (first)
|
if (first)
|
||||||
{
|
{
|
||||||
|
@ -106,6 +86,7 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||||
return CompletedTasks.True;
|
return CompletedTasks.True;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
completionSource.Reset();
|
||||||
return new UniTask<bool>(this, completionSource.Version);
|
return new UniTask<bool>(this, completionSource.Version);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,7 +94,6 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||||
{
|
{
|
||||||
if (!disposed)
|
if (!disposed)
|
||||||
{
|
{
|
||||||
cancellationTokenRegistration.Dispose();
|
|
||||||
disposed = true;
|
disposed = true;
|
||||||
TaskTracker.RemoveTracking(this);
|
TaskTracker.RemoveTracking(this);
|
||||||
}
|
}
|
||||||
|
@ -122,18 +102,13 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||||
|
|
||||||
public bool MoveNext()
|
public bool MoveNext()
|
||||||
{
|
{
|
||||||
if (disposed || targetAsUnityObject == null)
|
if (disposed || cancellationToken.IsCancellationRequested || targetAsUnityObject == null) // destroyed = cancel.
|
||||||
{
|
{
|
||||||
completionSource.TrySetResult(false);
|
completionSource.TrySetResult(false);
|
||||||
DisposeAsync().Forget();
|
DisposeAsync().Forget();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cancellationToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
completionSource.TrySetCanceled(cancellationToken);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
TProperty nextValue = default(TProperty);
|
TProperty nextValue = default(TProperty);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -164,20 +139,18 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||||
readonly Func<TTarget, TProperty> propertySelector;
|
readonly Func<TTarget, TProperty> propertySelector;
|
||||||
readonly IEqualityComparer<TProperty> equalityComparer;
|
readonly IEqualityComparer<TProperty> equalityComparer;
|
||||||
readonly PlayerLoopTiming monitorTiming;
|
readonly PlayerLoopTiming monitorTiming;
|
||||||
readonly bool cancelImmediately;
|
|
||||||
|
|
||||||
public EveryValueChangedStandardObject(TTarget target, Func<TTarget, TProperty> propertySelector, IEqualityComparer<TProperty> equalityComparer, PlayerLoopTiming monitorTiming, bool cancelImmediately)
|
public EveryValueChangedStandardObject(TTarget target, Func<TTarget, TProperty> propertySelector, IEqualityComparer<TProperty> equalityComparer, PlayerLoopTiming monitorTiming)
|
||||||
{
|
{
|
||||||
this.target = new WeakReference<TTarget>(target, false);
|
this.target = new WeakReference<TTarget>(target, false);
|
||||||
this.propertySelector = propertySelector;
|
this.propertySelector = propertySelector;
|
||||||
this.equalityComparer = equalityComparer;
|
this.equalityComparer = equalityComparer;
|
||||||
this.monitorTiming = monitorTiming;
|
this.monitorTiming = monitorTiming;
|
||||||
this.cancelImmediately = cancelImmediately;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IUniTaskAsyncEnumerator<TProperty> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
public IUniTaskAsyncEnumerator<TProperty> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
return new _EveryValueChanged(target, propertySelector, equalityComparer, monitorTiming, cancellationToken, cancelImmediately);
|
return new _EveryValueChanged(target, propertySelector, equalityComparer, monitorTiming, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class _EveryValueChanged : MoveNextSource, IUniTaskAsyncEnumerator<TProperty>, IPlayerLoopItem
|
sealed class _EveryValueChanged : MoveNextSource, IUniTaskAsyncEnumerator<TProperty>, IPlayerLoopItem
|
||||||
|
@ -185,30 +158,19 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||||
readonly WeakReference<TTarget> target;
|
readonly WeakReference<TTarget> target;
|
||||||
readonly IEqualityComparer<TProperty> equalityComparer;
|
readonly IEqualityComparer<TProperty> equalityComparer;
|
||||||
readonly Func<TTarget, TProperty> propertySelector;
|
readonly Func<TTarget, TProperty> propertySelector;
|
||||||
readonly CancellationToken cancellationToken;
|
CancellationToken cancellationToken;
|
||||||
readonly CancellationTokenRegistration cancellationTokenRegistration;
|
|
||||||
|
|
||||||
bool first;
|
bool first;
|
||||||
TProperty currentValue;
|
TProperty currentValue;
|
||||||
bool disposed;
|
bool disposed;
|
||||||
|
|
||||||
public _EveryValueChanged(WeakReference<TTarget> target, Func<TTarget, TProperty> propertySelector, IEqualityComparer<TProperty> equalityComparer, PlayerLoopTiming monitorTiming, CancellationToken cancellationToken, bool cancelImmediately)
|
public _EveryValueChanged(WeakReference<TTarget> target, Func<TTarget, TProperty> propertySelector, IEqualityComparer<TProperty> equalityComparer, PlayerLoopTiming monitorTiming, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
this.target = target;
|
this.target = target;
|
||||||
this.propertySelector = propertySelector;
|
this.propertySelector = propertySelector;
|
||||||
this.equalityComparer = equalityComparer;
|
this.equalityComparer = equalityComparer;
|
||||||
this.cancellationToken = cancellationToken;
|
this.cancellationToken = cancellationToken;
|
||||||
this.first = true;
|
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);
|
TaskTracker.TrackActiveTask(this, 2);
|
||||||
PlayerLoopHelper.AddAction(monitorTiming, this);
|
PlayerLoopHelper.AddAction(monitorTiming, this);
|
||||||
}
|
}
|
||||||
|
@ -217,15 +179,7 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||||
|
|
||||||
public UniTask<bool> MoveNextAsync()
|
public UniTask<bool> MoveNextAsync()
|
||||||
{
|
{
|
||||||
if (disposed) return CompletedTasks.False;
|
if (disposed || cancellationToken.IsCancellationRequested) return CompletedTasks.False;
|
||||||
|
|
||||||
completionSource.Reset();
|
|
||||||
|
|
||||||
if (cancellationToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
completionSource.TrySetCanceled(cancellationToken);
|
|
||||||
return new UniTask<bool>(this, completionSource.Version);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (first)
|
if (first)
|
||||||
{
|
{
|
||||||
|
@ -238,6 +192,7 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||||
return CompletedTasks.True;
|
return CompletedTasks.True;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
completionSource.Reset();
|
||||||
return new UniTask<bool>(this, completionSource.Version);
|
return new UniTask<bool>(this, completionSource.Version);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,7 +200,6 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||||
{
|
{
|
||||||
if (!disposed)
|
if (!disposed)
|
||||||
{
|
{
|
||||||
cancellationTokenRegistration.Dispose();
|
|
||||||
disposed = true;
|
disposed = true;
|
||||||
TaskTracker.RemoveTracking(this);
|
TaskTracker.RemoveTracking(this);
|
||||||
}
|
}
|
||||||
|
@ -254,19 +208,14 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||||
|
|
||||||
public bool MoveNext()
|
public bool MoveNext()
|
||||||
{
|
{
|
||||||
if (disposed || !target.TryGetTarget(out var t))
|
UnityEngine.Debug.Log("TRY_RESULT:" + target.TryGetTarget(out var _));
|
||||||
|
if (disposed || cancellationToken.IsCancellationRequested || !target.TryGetTarget(out var t))
|
||||||
{
|
{
|
||||||
completionSource.TrySetResult(false);
|
completionSource.TrySetResult(false);
|
||||||
DisposeAsync().Forget();
|
DisposeAsync().Forget();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cancellationToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
completionSource.TrySetCanceled(cancellationToken);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
TProperty nextValue = default(TProperty);
|
TProperty nextValue = default(TProperty);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue