mirror of https://github.com/Cysharp/UniTask
add experimental analyzer
parent
4f6166102d
commit
a1dee8b54f
|
@ -1,13 +1,15 @@
|
||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio Version 16
|
# Visual Studio Version 17
|
||||||
VisualStudioVersion = 16.0.29613.14
|
VisualStudioVersion = 17.0.31606.5
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniTask.NetCoreTests", "src\UniTask.NetCoreTests\UniTask.NetCoreTests.csproj", "{B3E311A4-70D8-4131-9965-C073A99D201A}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniTask.NetCoreTests", "src\UniTask.NetCoreTests\UniTask.NetCoreTests.csproj", "{B3E311A4-70D8-4131-9965-C073A99D201A}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniTask.NetCore", "src\UniTask.NetCore\UniTask.NetCore.csproj", "{16EE20D0-7FB1-483A-8467-A5EEDBF1F5BF}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniTask.NetCore", "src\UniTask.NetCore\UniTask.NetCore.csproj", "{16EE20D0-7FB1-483A-8467-A5EEDBF1F5BF}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniTask.NetCoreSandbox", "src\UniTask.NetCoreSandbox\UniTask.NetCoreSandbox.csproj", "{3915E72E-33E0-4A14-A6D8-872702200E58}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniTask.NetCoreSandbox", "src\UniTask.NetCoreSandbox\UniTask.NetCoreSandbox.csproj", "{3915E72E-33E0-4A14-A6D8-872702200E58}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniTask.Analyzer", "src\UniTask.Analyzer\UniTask.Analyzer.csproj", "{0AC6F052-A255-4EE3-9E05-1C02D49AB1C2}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
@ -27,6 +29,10 @@ Global
|
||||||
{3915E72E-33E0-4A14-A6D8-872702200E58}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{3915E72E-33E0-4A14-A6D8-872702200E58}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{3915E72E-33E0-4A14-A6D8-872702200E58}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{3915E72E-33E0-4A14-A6D8-872702200E58}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{3915E72E-33E0-4A14-A6D8-872702200E58}.Release|Any CPU.Build.0 = Release|Any CPU
|
{3915E72E-33E0-4A14-A6D8-872702200E58}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{0AC6F052-A255-4EE3-9E05-1C02D49AB1C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{0AC6F052-A255-4EE3-9E05-1C02D49AB1C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{0AC6F052-A255-4EE3-9E05-1C02D49AB1C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{0AC6F052-A255-4EE3-9E05-1C02D49AB1C2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"profiles": {
|
||||||
|
"UniTask.Analyzer": {
|
||||||
|
"commandName": "DebugRoslynComponent",
|
||||||
|
"targetProject": "..\\UniTask.NetCoreSandbox\\UniTask.NetCoreSandbox.csproj"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>library</OutputType>
|
||||||
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<IsRoslynComponent>true</IsRoslynComponent>
|
||||||
|
<TargetsForTfmSpecificContentInPackage>$(TargetsForTfmSpecificContentInPackage);PackBuildOutputs</TargetsForTfmSpecificContentInPackage>
|
||||||
|
<IncludeBuildOutput>false</IncludeBuildOutput>
|
||||||
|
<IncludeSymbols>false</IncludeSymbols>
|
||||||
|
<SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
|
||||||
|
<DevelopmentDependency>true</DevelopmentDependency>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.2">
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.8.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<Target Name="PackBuildOutputs" DependsOnTargets="SatelliteDllsProjectOutputGroup;DebugSymbolsProjectOutputGroup">
|
||||||
|
<ItemGroup>
|
||||||
|
<TfmSpecificPackageFile Include="$(TargetDir)\*.dll" PackagePath="analyzers\dotnet\cs" />
|
||||||
|
<TfmSpecificPackageFile Include="@(SatelliteDllsProjectOutputGroupOutput->'%(FinalOutputPath)')" PackagePath="analyzers\dotnet\cs\%(SatelliteDllsProjectOutputGroupOutput.Culture)\" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Target>
|
||||||
|
</Project>
|
|
@ -0,0 +1,54 @@
|
||||||
|
#pragma warning disable RS2008
|
||||||
|
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis.Diagnostics;
|
||||||
|
using Microsoft.CodeAnalysis.Operations;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace UniTask.Analyzer
|
||||||
|
{
|
||||||
|
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||||
|
public class UniTaskAnalyzer : DiagnosticAnalyzer
|
||||||
|
{
|
||||||
|
private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(
|
||||||
|
id: "UNITASK001",
|
||||||
|
title: "UniTaskAnalyzer001: Must pass CancellationToken",
|
||||||
|
messageFormat: "Must pass CancellationToken",
|
||||||
|
category: "Usage",
|
||||||
|
defaultSeverity: DiagnosticSeverity.Error,
|
||||||
|
isEnabledByDefault: true,
|
||||||
|
description: "Pass CancellationToken or CancellationToken.None.");
|
||||||
|
|
||||||
|
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(Rule); } }
|
||||||
|
|
||||||
|
public override void Initialize(AnalysisContext context)
|
||||||
|
{
|
||||||
|
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
|
||||||
|
context.EnableConcurrentExecution();
|
||||||
|
|
||||||
|
context.RegisterOperationAction(AnalyzeSymbol, OperationKind.Invocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AnalyzeSymbol(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,258 +0,0 @@
|
||||||
using BenchmarkDotNet.Attributes;
|
|
||||||
using System.Linq;
|
|
||||||
using BenchmarkDotNet.Configs;
|
|
||||||
using BenchmarkDotNet.Diagnosers;
|
|
||||||
using BenchmarkDotNet.Exporters;
|
|
||||||
using BenchmarkDotNet.Jobs;
|
|
||||||
using BenchmarkDotNet.Running;
|
|
||||||
using Cysharp.Threading.Tasks;
|
|
||||||
using PooledAwait;
|
|
||||||
using System;
|
|
||||||
using System.Runtime.ExceptionServices;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using Cysharp.Threading.Tasks.CompilerServices;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
|
|
||||||
[Config(typeof(BenchmarkConfig))]
|
|
||||||
public class AllocationCheck
|
|
||||||
{
|
|
||||||
// note: all the benchmarks use Task/Task<T> for the public API, because BenchmarkDotNet
|
|
||||||
// doesn't work reliably with more exotic task-types (even just ValueTask fails); instead,
|
|
||||||
// we'll obscure the cost of the outer awaitable by doing a relatively large number of
|
|
||||||
// iterations, so that we're only really measuring the inner loop
|
|
||||||
private const int InnerOps = 1000;
|
|
||||||
|
|
||||||
[Benchmark(OperationsPerInvoke = InnerOps)]
|
|
||||||
public async Task ViaUniTask()
|
|
||||||
{
|
|
||||||
for (int i = 0; i < InnerOps; i++)
|
|
||||||
{
|
|
||||||
var a = Core();
|
|
||||||
var b = Core();
|
|
||||||
var c = Core();
|
|
||||||
await a;
|
|
||||||
await b;
|
|
||||||
await c;
|
|
||||||
}
|
|
||||||
|
|
||||||
static async UniTask Core()
|
|
||||||
{
|
|
||||||
await new TestAwaiter(false, UniTaskStatus.Succeeded);
|
|
||||||
await new TestAwaiter(false, UniTaskStatus.Succeeded);
|
|
||||||
await new TestAwaiter(false, UniTaskStatus.Succeeded);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark(OperationsPerInvoke = InnerOps)]
|
|
||||||
public async Task<int> ViaUniTaskT()
|
|
||||||
{
|
|
||||||
var sum = 0;
|
|
||||||
for (int i = 0; i < InnerOps; i++)
|
|
||||||
{
|
|
||||||
var a = Core();
|
|
||||||
var b = Core();
|
|
||||||
var c = Core();
|
|
||||||
sum += await a;
|
|
||||||
sum += await b;
|
|
||||||
sum += await c;
|
|
||||||
}
|
|
||||||
return sum;
|
|
||||||
|
|
||||||
static async UniTask<int> Core()
|
|
||||||
{
|
|
||||||
var a = await new TestAwaiter<int>(false, UniTaskStatus.Succeeded, 10);
|
|
||||||
var b = await new TestAwaiter<int>(false, UniTaskStatus.Succeeded, 10);
|
|
||||||
var c = await new TestAwaiter<int>(false, UniTaskStatus.Succeeded, 10);
|
|
||||||
return 10;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//[Benchmark(OperationsPerInvoke = InnerOps)]
|
|
||||||
//[Benchmark]
|
|
||||||
public void ViaUniTaskVoid()
|
|
||||||
{
|
|
||||||
for (int i = 0; i < InnerOps; i++)
|
|
||||||
{
|
|
||||||
Core().Forget();
|
|
||||||
Core().Forget();
|
|
||||||
Core().Forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
static async UniTaskVoid Core()
|
|
||||||
{
|
|
||||||
await new TestAwaiter(false, UniTaskStatus.Succeeded);
|
|
||||||
await new TestAwaiter(false, UniTaskStatus.Succeeded);
|
|
||||||
await new TestAwaiter(false, UniTaskStatus.Succeeded);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Foo : IAsyncStateMachine
|
|
||||||
{
|
|
||||||
public AsyncUniTaskVoidMethodBuilder builder;
|
|
||||||
public TestAwaiter awaiter;
|
|
||||||
public TestAwaiter awaiterawaiter;
|
|
||||||
|
|
||||||
public int state;
|
|
||||||
|
|
||||||
public void MoveNext()
|
|
||||||
{
|
|
||||||
switch (state)
|
|
||||||
{
|
|
||||||
case -1:
|
|
||||||
awaiterawaiter = awaiter.GetAwaiter();
|
|
||||||
if (awaiterawaiter.IsCompleted)
|
|
||||||
{
|
|
||||||
goto case 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
state = 0;
|
|
||||||
builder.AwaitUnsafeOnCompleted(ref awaiterawaiter, ref this);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 0:
|
|
||||||
default:
|
|
||||||
goto END;
|
|
||||||
}
|
|
||||||
|
|
||||||
END:
|
|
||||||
builder.SetResult();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetStateMachine(IAsyncStateMachine stateMachine)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class TaskTestException : Exception
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct TestAwaiter : ICriticalNotifyCompletion
|
|
||||||
{
|
|
||||||
readonly UniTaskStatus status;
|
|
||||||
readonly bool isCompleted;
|
|
||||||
|
|
||||||
public TestAwaiter(bool isCompleted, UniTaskStatus status)
|
|
||||||
{
|
|
||||||
this.isCompleted = isCompleted;
|
|
||||||
this.status = status;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TestAwaiter GetAwaiter() => this;
|
|
||||||
|
|
||||||
public bool IsCompleted => isCompleted;
|
|
||||||
|
|
||||||
public void GetResult()
|
|
||||||
{
|
|
||||||
switch (status)
|
|
||||||
{
|
|
||||||
case UniTaskStatus.Faulted:
|
|
||||||
throw new TaskTestException();
|
|
||||||
case UniTaskStatus.Canceled:
|
|
||||||
throw new OperationCanceledException();
|
|
||||||
case UniTaskStatus.Pending:
|
|
||||||
case UniTaskStatus.Succeeded:
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnCompleted(Action continuation)
|
|
||||||
{
|
|
||||||
ThreadPool.UnsafeQueueUserWorkItem(ThreadPoolWorkItem.Create(continuation), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UnsafeOnCompleted(Action continuation)
|
|
||||||
{
|
|
||||||
ThreadPool.UnsafeQueueUserWorkItem(ThreadPoolWorkItem.Create(continuation), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct TestAwaiter<T> : ICriticalNotifyCompletion
|
|
||||||
{
|
|
||||||
readonly UniTaskStatus status;
|
|
||||||
readonly bool isCompleted;
|
|
||||||
readonly T value;
|
|
||||||
|
|
||||||
public TestAwaiter(bool isCompleted, UniTaskStatus status, T value)
|
|
||||||
{
|
|
||||||
this.isCompleted = isCompleted;
|
|
||||||
this.status = status;
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TestAwaiter<T> GetAwaiter() => this;
|
|
||||||
|
|
||||||
public bool IsCompleted => isCompleted;
|
|
||||||
|
|
||||||
public T GetResult()
|
|
||||||
{
|
|
||||||
switch (status)
|
|
||||||
{
|
|
||||||
case UniTaskStatus.Faulted:
|
|
||||||
throw new TaskTestException();
|
|
||||||
case UniTaskStatus.Canceled:
|
|
||||||
throw new OperationCanceledException();
|
|
||||||
case UniTaskStatus.Pending:
|
|
||||||
case UniTaskStatus.Succeeded:
|
|
||||||
default:
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnCompleted(Action continuation)
|
|
||||||
{
|
|
||||||
ThreadPool.UnsafeQueueUserWorkItem(ThreadPoolWorkItem.Create(continuation), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UnsafeOnCompleted(Action continuation)
|
|
||||||
{
|
|
||||||
ThreadPool.UnsafeQueueUserWorkItem(ThreadPoolWorkItem.Create(continuation), false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class ThreadPoolWorkItem : IThreadPoolWorkItem
|
|
||||||
{
|
|
||||||
public static readonly ConcurrentQueue<ThreadPoolWorkItem> pool = new ConcurrentQueue<ThreadPoolWorkItem>();
|
|
||||||
|
|
||||||
public static void CreatePoolItems(int count)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
pool.Enqueue(new ThreadPoolWorkItem());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Action continuation;
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static ThreadPoolWorkItem Create(Action continuation)
|
|
||||||
{
|
|
||||||
if (!pool.TryDequeue(out var item))
|
|
||||||
{
|
|
||||||
item = new ThreadPoolWorkItem();
|
|
||||||
}
|
|
||||||
|
|
||||||
item.continuation = continuation;
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public void Execute()
|
|
||||||
{
|
|
||||||
var call = continuation;
|
|
||||||
continuation = null;
|
|
||||||
pool.Enqueue(this);
|
|
||||||
|
|
||||||
call.Invoke();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,283 +0,0 @@
|
||||||
using BenchmarkDotNet.Attributes;
|
|
||||||
using System.Linq;
|
|
||||||
using BenchmarkDotNet.Configs;
|
|
||||||
using BenchmarkDotNet.Diagnosers;
|
|
||||||
using BenchmarkDotNet.Exporters;
|
|
||||||
using BenchmarkDotNet.Jobs;
|
|
||||||
using BenchmarkDotNet.Running;
|
|
||||||
using Cysharp.Threading.Tasks;
|
|
||||||
using PooledAwait;
|
|
||||||
using System;
|
|
||||||
using System.Runtime.ExceptionServices;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using Cysharp.Threading.Tasks.CompilerServices;
|
|
||||||
|
|
||||||
//class Program
|
|
||||||
//{
|
|
||||||
// static void Main(string[] args)
|
|
||||||
// {
|
|
||||||
// var switcher = new BenchmarkSwitcher(new[]
|
|
||||||
// {
|
|
||||||
// typeof(StandardBenchmark)
|
|
||||||
// });
|
|
||||||
|
|
||||||
//#if DEBUG
|
|
||||||
// var b = new StandardBenchmark();
|
|
||||||
|
|
||||||
//#else
|
|
||||||
// switcher.Run(args);
|
|
||||||
//#endif
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
public class BenchmarkConfig : ManualConfig
|
|
||||||
{
|
|
||||||
public BenchmarkConfig()
|
|
||||||
{
|
|
||||||
AddDiagnoser(MemoryDiagnoser.Default);
|
|
||||||
AddJob(Job.ShortRun.WithLaunchCount(1).WithIterationCount(1).WithWarmupCount(1)/*.RunOncePerIteration()*/);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// borrowed from PooledAwait
|
|
||||||
|
|
||||||
[Config(typeof(BenchmarkConfig))]
|
|
||||||
[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)]
|
|
||||||
[CategoriesColumn]
|
|
||||||
public class ComparisonBenchmarks
|
|
||||||
{
|
|
||||||
// note: all the benchmarks use Task/Task<T> for the public API, because BenchmarkDotNet
|
|
||||||
// doesn't work reliably with more exotic task-types (even just ValueTask fails); instead,
|
|
||||||
// we'll obscure the cost of the outer awaitable by doing a relatively large number of
|
|
||||||
// iterations, so that we're only really measuring the inner loop
|
|
||||||
private const int InnerOps = 1000;
|
|
||||||
|
|
||||||
public bool ConfigureAwait { get; set; } = false;
|
|
||||||
|
|
||||||
[Benchmark(OperationsPerInvoke = InnerOps, Description = ".NET")]
|
|
||||||
[BenchmarkCategory("Task<T>")]
|
|
||||||
public async Task<int> ViaTaskT()
|
|
||||||
{
|
|
||||||
int sum = 0;
|
|
||||||
for (int i = 0; i < InnerOps; i++)
|
|
||||||
sum += await Inner(1, 2).ConfigureAwait(ConfigureAwait);
|
|
||||||
return sum;
|
|
||||||
|
|
||||||
static async Task<int> Inner(int x, int y)
|
|
||||||
{
|
|
||||||
int i = x;
|
|
||||||
await Task.Yield();
|
|
||||||
i *= y;
|
|
||||||
await Task.Yield();
|
|
||||||
return 5 * i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark(OperationsPerInvoke = InnerOps, Description = ".NET")]
|
|
||||||
[BenchmarkCategory("Task")]
|
|
||||||
public async Task ViaTask()
|
|
||||||
{
|
|
||||||
for (int i = 0; i < InnerOps; i++)
|
|
||||||
await Inner().ConfigureAwait(ConfigureAwait);
|
|
||||||
|
|
||||||
static async Task Inner()
|
|
||||||
{
|
|
||||||
await Task.Yield();
|
|
||||||
await Task.Yield();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark(OperationsPerInvoke = InnerOps, Description = ".NET")]
|
|
||||||
[BenchmarkCategory("ValueTask<T>")]
|
|
||||||
public async Task<int> ViaValueTaskT()
|
|
||||||
{
|
|
||||||
int sum = 0;
|
|
||||||
for (int i = 0; i < InnerOps; i++)
|
|
||||||
sum += await Inner(1, 2).ConfigureAwait(ConfigureAwait);
|
|
||||||
return sum;
|
|
||||||
|
|
||||||
static async ValueTask<int> Inner(int x, int y)
|
|
||||||
{
|
|
||||||
int i = x;
|
|
||||||
await Task.Yield();
|
|
||||||
i *= y;
|
|
||||||
await Task.Yield();
|
|
||||||
return 5 * i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark(OperationsPerInvoke = InnerOps, Description = ".NET")]
|
|
||||||
[BenchmarkCategory("ValueTask")]
|
|
||||||
public async Task ViaValueTask()
|
|
||||||
{
|
|
||||||
for (int i = 0; i < InnerOps; i++)
|
|
||||||
await Inner().ConfigureAwait(ConfigureAwait);
|
|
||||||
|
|
||||||
static async ValueTask Inner()
|
|
||||||
{
|
|
||||||
await Task.Yield();
|
|
||||||
await Task.Yield();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark(OperationsPerInvoke = InnerOps, Description = "Pooled")]
|
|
||||||
[BenchmarkCategory("ValueTask<T>")]
|
|
||||||
public async Task<int> ViaPooledValueTaskT()
|
|
||||||
{
|
|
||||||
int sum = 0;
|
|
||||||
for (int i = 0; i < InnerOps; i++)
|
|
||||||
sum += await Inner(1, 2).ConfigureAwait(ConfigureAwait);
|
|
||||||
return sum;
|
|
||||||
|
|
||||||
static async PooledValueTask<int> Inner(int x, int y)
|
|
||||||
{
|
|
||||||
int i = x;
|
|
||||||
await Task.Yield();
|
|
||||||
i *= y;
|
|
||||||
await Task.Yield();
|
|
||||||
return 5 * i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark(OperationsPerInvoke = InnerOps, Description = "Pooled")]
|
|
||||||
[BenchmarkCategory("ValueTask")]
|
|
||||||
public async Task ViaPooledValueTask()
|
|
||||||
{
|
|
||||||
for (int i = 0; i < InnerOps; i++)
|
|
||||||
await Inner().ConfigureAwait(ConfigureAwait);
|
|
||||||
|
|
||||||
static async PooledValueTask Inner()
|
|
||||||
{
|
|
||||||
await Task.Yield();
|
|
||||||
await Task.Yield();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark(OperationsPerInvoke = InnerOps, Description = "Pooled")]
|
|
||||||
[BenchmarkCategory("Task<T>")]
|
|
||||||
public async Task<int> ViaPooledTaskT()
|
|
||||||
{
|
|
||||||
int sum = 0;
|
|
||||||
for (int i = 0; i < InnerOps; i++)
|
|
||||||
sum += await Inner(1, 2).ConfigureAwait(ConfigureAwait);
|
|
||||||
return sum;
|
|
||||||
|
|
||||||
static async PooledTask<int> Inner(int x, int y)
|
|
||||||
{
|
|
||||||
int i = x;
|
|
||||||
await Task.Yield();
|
|
||||||
i *= y;
|
|
||||||
await Task.Yield();
|
|
||||||
return 5 * i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark(OperationsPerInvoke = InnerOps, Description = "Pooled")]
|
|
||||||
[BenchmarkCategory("Task")]
|
|
||||||
public async Task ViaPooledTask()
|
|
||||||
{
|
|
||||||
for (int i = 0; i < InnerOps; i++)
|
|
||||||
await Inner().ConfigureAwait(ConfigureAwait);
|
|
||||||
|
|
||||||
static async PooledTask Inner()
|
|
||||||
{
|
|
||||||
await Task.Yield();
|
|
||||||
await Task.Yield();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---
|
|
||||||
|
|
||||||
//[Benchmark(OperationsPerInvoke = InnerOps, Description = "UniTaskVoid")]
|
|
||||||
//[BenchmarkCategory("UniTask")]
|
|
||||||
//public async Task ViaUniTaskVoid()
|
|
||||||
//{
|
|
||||||
// for (int i = 0; i < InnerOps; i++)
|
|
||||||
// {
|
|
||||||
// await Inner();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// static async UniTaskVoid Inner()
|
|
||||||
// {
|
|
||||||
// await UniTask.Yield();
|
|
||||||
// await UniTask.Yield();
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
[Benchmark(OperationsPerInvoke = InnerOps, Description = "UniTask")]
|
|
||||||
[BenchmarkCategory("UniTask")]
|
|
||||||
public async Task ViaUniTask()
|
|
||||||
{
|
|
||||||
for (int i = 0; i < InnerOps; i++)
|
|
||||||
{
|
|
||||||
await Inner();
|
|
||||||
}
|
|
||||||
|
|
||||||
static async UniTask Inner()
|
|
||||||
{
|
|
||||||
await UniTask.Yield();
|
|
||||||
await UniTask.Yield();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark(OperationsPerInvoke = InnerOps, Description = "UniTaskT")]
|
|
||||||
[BenchmarkCategory("UniTask")]
|
|
||||||
public async Task<int> ViaUniTaskT()
|
|
||||||
{
|
|
||||||
var sum = 0;
|
|
||||||
for (int i = 0; i < InnerOps; i++)
|
|
||||||
{
|
|
||||||
sum += await Inner(1, 2);
|
|
||||||
}
|
|
||||||
return sum;
|
|
||||||
|
|
||||||
static async UniTask<int> Inner(int x, int y)
|
|
||||||
{
|
|
||||||
int i = x;
|
|
||||||
await UniTask.Yield();
|
|
||||||
i *= y;
|
|
||||||
await UniTask.Yield();
|
|
||||||
return 5 * i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct MyAwaiter : ICriticalNotifyCompletion
|
|
||||||
{
|
|
||||||
public MyAwaiter GetAwaiter() => this;
|
|
||||||
|
|
||||||
public bool IsCompleted => false;
|
|
||||||
|
|
||||||
public void GetResult()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnCompleted(Action continuation)
|
|
||||||
{
|
|
||||||
continuation();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UnsafeOnCompleted(Action continuation)
|
|
||||||
{
|
|
||||||
continuation();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct MyTestStateMachine : IAsyncStateMachine
|
|
||||||
{
|
|
||||||
public void MoveNext()
|
|
||||||
{
|
|
||||||
//throw new NotImplementedException();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetStateMachine(IAsyncStateMachine stateMachine)
|
|
||||||
{
|
|
||||||
//throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -17,499 +17,34 @@ using System.Reactive.Concurrency;
|
||||||
|
|
||||||
namespace NetCoreSandbox
|
namespace NetCoreSandbox
|
||||||
{
|
{
|
||||||
public class MySyncContext : SynchronizationContext
|
public class Program
|
||||||
{
|
{
|
||||||
public MySyncContext()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Post(SendOrPostCallback d, object state)
|
|
||||||
{
|
|
||||||
Console.WriteLine("Called SyncContext Post!");
|
|
||||||
base.Post(d, state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Text
|
|
||||||
{
|
|
||||||
|
|
||||||
public string text { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ZeroAllocAsyncAwaitInDotNetCore
|
|
||||||
{
|
|
||||||
public ValueTask<int> NanikaAsync(int x, int y)
|
|
||||||
{
|
|
||||||
return Core(this, x, y);
|
|
||||||
|
|
||||||
static async UniTask<int> Core(ZeroAllocAsyncAwaitInDotNetCore self, int x, int y)
|
|
||||||
{
|
|
||||||
// nanika suru...
|
|
||||||
await Task.Delay(TimeSpan.FromSeconds(x + y));
|
|
||||||
|
|
||||||
return 10;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public class TaskTestException : Exception
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class Foo
|
|
||||||
{
|
|
||||||
public async UniTask MethodFooAsync()
|
|
||||||
{
|
|
||||||
await MethodBarAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async UniTask MethodBarAsync()
|
|
||||||
|
|
||||||
{
|
|
||||||
Throw();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Throw()
|
|
||||||
{
|
|
||||||
throw new Exception();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct TestAwaiter : ICriticalNotifyCompletion
|
|
||||||
{
|
|
||||||
readonly UniTaskStatus status;
|
|
||||||
readonly bool isCompleted;
|
|
||||||
|
|
||||||
public TestAwaiter(bool isCompleted, UniTaskStatus status)
|
|
||||||
{
|
|
||||||
this.isCompleted = isCompleted;
|
|
||||||
this.status = status;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TestAwaiter GetAwaiter() => this;
|
|
||||||
|
|
||||||
public bool IsCompleted => isCompleted;
|
|
||||||
|
|
||||||
public void GetResult()
|
|
||||||
{
|
|
||||||
switch (status)
|
|
||||||
{
|
|
||||||
case UniTaskStatus.Faulted:
|
|
||||||
throw new TaskTestException();
|
|
||||||
case UniTaskStatus.Canceled:
|
|
||||||
throw new OperationCanceledException();
|
|
||||||
case UniTaskStatus.Pending:
|
|
||||||
case UniTaskStatus.Succeeded:
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnCompleted(Action continuation)
|
|
||||||
{
|
|
||||||
ThreadPool.QueueUserWorkItem(_ => continuation(), null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UnsafeOnCompleted(Action continuation)
|
|
||||||
{
|
|
||||||
ThreadPool.UnsafeQueueUserWorkItem(_ => continuation(), null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public struct TestAwaiter<T> : ICriticalNotifyCompletion
|
|
||||||
{
|
|
||||||
readonly UniTaskStatus status;
|
|
||||||
readonly bool isCompleted;
|
|
||||||
readonly T value;
|
|
||||||
|
|
||||||
public TestAwaiter(bool isCompleted, UniTaskStatus status, T value)
|
|
||||||
{
|
|
||||||
this.isCompleted = isCompleted;
|
|
||||||
this.status = status;
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TestAwaiter<T> GetAwaiter() => this;
|
|
||||||
|
|
||||||
public bool IsCompleted => isCompleted;
|
|
||||||
|
|
||||||
public T GetResult()
|
|
||||||
{
|
|
||||||
switch (status)
|
|
||||||
{
|
|
||||||
case UniTaskStatus.Faulted:
|
|
||||||
throw new TaskTestException();
|
|
||||||
case UniTaskStatus.Canceled:
|
|
||||||
throw new OperationCanceledException();
|
|
||||||
case UniTaskStatus.Pending:
|
|
||||||
case UniTaskStatus.Succeeded:
|
|
||||||
default:
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnCompleted(Action continuation)
|
|
||||||
{
|
|
||||||
ThreadPool.QueueUserWorkItem(_ => continuation(), null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UnsafeOnCompleted(Action continuation)
|
|
||||||
{
|
|
||||||
ThreadPool.UnsafeQueueUserWorkItem(_ => continuation(), null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static partial class UnityUIComponentExtensions
|
|
||||||
{
|
|
||||||
public static void BindTo(this IUniTaskAsyncEnumerable<string> source, Text text)
|
|
||||||
{
|
|
||||||
AAAACORECORE(source, text).Forget();
|
|
||||||
|
|
||||||
async UniTaskVoid AAAACORECORE(IUniTaskAsyncEnumerable<string> source2, Text text2)
|
|
||||||
{
|
|
||||||
var e = source2.GetAsyncEnumerator();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
while (await e.MoveNextAsync())
|
|
||||||
{
|
|
||||||
text2.text = e.Current;
|
|
||||||
// action(e.Current);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (e != null)
|
|
||||||
{
|
|
||||||
await e.DisposeAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//public static IDisposable SubscribeToText<T>(this IObservable<T> source, Text text)
|
|
||||||
//{
|
|
||||||
// return source.SubscribeWithState(text, (x, t) => t.text = x.ToString());
|
|
||||||
//}
|
|
||||||
|
|
||||||
//public static IDisposable SubscribeToText<T>(this IObservable<T> source, Text text, Func<T, string> selector)
|
|
||||||
//{
|
|
||||||
// return source.SubscribeWithState2(text, selector, (x, t, s) => t.text = s(x));
|
|
||||||
//}
|
|
||||||
|
|
||||||
//public static IDisposable SubscribeToInteractable(this IObservable<bool> source, Selectable selectable)
|
|
||||||
//{
|
|
||||||
// return source.SubscribeWithState(selectable, (x, s) => s.interactable = x);
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Program
|
|
||||||
{
|
|
||||||
static string FlattenGenArgs(Type type)
|
|
||||||
{
|
|
||||||
if (type.IsGenericType)
|
|
||||||
{
|
|
||||||
var t = string.Join(", ", type.GetGenericArguments().Select(x => FlattenGenArgs(x)));
|
|
||||||
return Regex.Replace(type.Name, "`.+", "") + "<" + t + ">";
|
|
||||||
}
|
|
||||||
//x.ReturnType.GetGenericArguments()
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return type.Name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static async IAsyncEnumerable<int> FooAsync([EnumeratorCancellation]CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
yield return 1;
|
|
||||||
await Task.Delay(10, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class MyDisposable : IDisposable
|
|
||||||
{
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Test()
|
|
||||||
{
|
|
||||||
var disp = new MyDisposable();
|
|
||||||
|
|
||||||
using var _ = new MyDisposable();
|
|
||||||
|
|
||||||
Console.WriteLine("tako");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static async UniTask FooBarAsync()
|
|
||||||
{
|
|
||||||
await using (UniTask.ReturnToCurrentSynchronizationContext())
|
|
||||||
{
|
|
||||||
await UniTask.SwitchToThreadPool();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static async UniTask Aaa()
|
|
||||||
{
|
|
||||||
await FooBarAsync();
|
|
||||||
|
|
||||||
Console.WriteLine("FooBarAsync End");
|
|
||||||
}
|
|
||||||
|
|
||||||
static async UniTask WhereSelect()
|
|
||||||
{
|
|
||||||
await foreach (var item in UniTaskAsyncEnumerable.Range(1, 10)
|
|
||||||
.SelectAwait(async x =>
|
|
||||||
{
|
|
||||||
await UniTask.Yield();
|
|
||||||
return x;
|
|
||||||
})
|
|
||||||
.Where(x => x % 2 == 0))
|
|
||||||
{
|
|
||||||
Console.WriteLine(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static async Task Main(string[] args)
|
static async Task Main(string[] args)
|
||||||
{
|
{
|
||||||
#if !DEBUG
|
var cts = new CancellationTokenSource();
|
||||||
|
|
||||||
|
|
||||||
|
// OK.
|
||||||
|
await FooAsync(10, cts.Token);
|
||||||
|
|
||||||
|
// NG(Compiler Error)
|
||||||
|
await FooAsync(10);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//await new AllocationCheck().ViaUniTaskVoid();
|
|
||||||
//Console.ReadLine();
|
|
||||||
BenchmarkDotNet.Running.BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);
|
|
||||||
|
|
||||||
//await new ComparisonBenchmarks().ViaUniTaskT();
|
|
||||||
return;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
var e = UniTaskAsyncEnumerable.Create<int>(async (writer, token) =>
|
|
||||||
{
|
|
||||||
for (int i = 0; i < 5; i++)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Start {i}");
|
|
||||||
await writer.YieldAsync(i);
|
|
||||||
Console.WriteLine($"End {i}");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var ee = e.GetAsyncEnumerator();
|
|
||||||
while (await ee.MoveNextAsync())
|
|
||||||
{
|
|
||||||
Console.WriteLine("ForEach " + ee.Current);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static async UniTask YieldCore()
|
static async UniTask FooAsync(int x, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
await UniTask.Yield();
|
await UniTask.Yield();
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma warning disable CS1998
|
|
||||||
|
|
||||||
|
|
||||||
static async UniTask<int> AsyncTest()
|
|
||||||
{
|
|
||||||
// empty
|
|
||||||
await new TestAwaiter(false, UniTaskStatus.Succeeded);
|
|
||||||
await new TestAwaiter(true, UniTaskStatus.Succeeded);
|
|
||||||
await new TestAwaiter(false, UniTaskStatus.Succeeded);
|
|
||||||
return 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#pragma warning restore CS1998
|
|
||||||
|
|
||||||
void Foo()
|
|
||||||
{
|
|
||||||
|
|
||||||
// AsyncEnumerable.Range(1,10).Do(
|
|
||||||
|
|
||||||
// AsyncEnumerable.t
|
|
||||||
|
|
||||||
var sb = new StringBuilder();
|
|
||||||
sb.AppendLine(@"using System;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace Cysharp.Threading.Tasks.Linq
|
|
||||||
{
|
|
||||||
");
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var chako = typeof(AsyncEnumerable).GetMethods()
|
|
||||||
.OrderBy(x => x.Name)
|
|
||||||
.Select(x =>
|
|
||||||
{
|
|
||||||
var ret = FlattenGenArgs(x.ReturnType);
|
|
||||||
|
|
||||||
|
|
||||||
var generics = string.Join(", ", x.GetGenericArguments().Select(x => x.Name));
|
|
||||||
|
|
||||||
if (x.GetParameters().Length == 0) return "";
|
|
||||||
|
|
||||||
var self = x.GetParameters().First();
|
|
||||||
if (x.GetCustomAttributes(typeof(ExtensionAttribute), true).Length == 0)
|
|
||||||
{
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
var arg1Type = FlattenGenArgs(x.GetParameters().First().ParameterType);
|
|
||||||
|
|
||||||
var others = string.Join(", ", x.GetParameters().Skip(1).Select(y => FlattenGenArgs(y.ParameterType) + " " + y.Name));
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(others))
|
|
||||||
{
|
|
||||||
others = ", " + others;
|
|
||||||
}
|
|
||||||
|
|
||||||
var template = $"public static {ret} {x.Name}<{generics}>(this {arg1Type} {self.Name}{others})";
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return template.Replace("ValueTask", "UniTask").Replace("IAsyncEnumerable", "IUniTaskAsyncEnumerable").Replace("<>", "");
|
|
||||||
})
|
|
||||||
.Where(x => x != "")
|
|
||||||
.Select(x => x + "\r\n{\r\n throw new NotImplementedException();\r\n}")
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
var huga = string.Join("\r\n\r\n", chako);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
foreach (var item in typeof(AsyncEnumerable).GetMethods().Select(x => x.Name).Distinct())
|
|
||||||
{
|
|
||||||
if (item.EndsWith("AwaitAsync") || item.EndsWith("AwaitWithCancellationAsync") || item.EndsWith("WithCancellation"))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var item2 = item.Replace("Async", "");
|
|
||||||
item2 = item2.Replace("Await", "");
|
|
||||||
|
|
||||||
var format = @"
|
|
||||||
internal sealed class {0}
|
|
||||||
{{
|
|
||||||
}}
|
|
||||||
";
|
|
||||||
|
|
||||||
sb.Append(string.Format(format, item2));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.Append("}");
|
|
||||||
|
|
||||||
|
|
||||||
Console.WriteLine(sb.ToString());
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static async IAsyncEnumerable<int> AsyncGen()
|
|
||||||
{
|
|
||||||
await UniTask.SwitchToThreadPool();
|
|
||||||
yield return 10;
|
|
||||||
await UniTask.SwitchToThreadPool();
|
|
||||||
yield return 100;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyEnumerable : IEnumerable<int>
|
|
||||||
{
|
|
||||||
public IEnumerator<int> GetEnumerator()
|
|
||||||
{
|
|
||||||
return new MyEnumerator();
|
|
||||||
}
|
|
||||||
|
|
||||||
IEnumerator IEnumerable.GetEnumerator()
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class MyEnumerator : IEnumerator<int>
|
|
||||||
{
|
|
||||||
public int Current => throw new NotImplementedException();
|
|
||||||
|
|
||||||
object IEnumerator.Current => throw new NotImplementedException();
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Console.WriteLine("Called Dispose");
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool MoveNext()
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Reset()
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public class MyClass<T>
|
|
||||||
{
|
|
||||||
public CustomAsyncEnumerator<T> GetAsyncEnumerator()
|
|
||||||
{
|
|
||||||
//IAsyncEnumerable
|
|
||||||
return new CustomAsyncEnumerator<T>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public struct CustomAsyncEnumerator<T>
|
|
||||||
{
|
|
||||||
int count;
|
|
||||||
|
|
||||||
public T Current
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public UniTask<bool> MoveNextAsync()
|
|
||||||
{
|
|
||||||
if (count++ == 3)
|
|
||||||
{
|
|
||||||
return UniTask.FromResult(false);
|
|
||||||
//return false;
|
|
||||||
}
|
|
||||||
return UniTask.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public UniTask DisposeAsync()
|
|
||||||
{
|
|
||||||
return default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,473 +0,0 @@
|
||||||
using BenchmarkDotNet.Attributes;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
[Config(typeof(BenchmarkConfig))]
|
|
||||||
public class QueueCheck
|
|
||||||
{
|
|
||||||
Node node1 = new Node();
|
|
||||||
Node node2 = new Node();
|
|
||||||
RefNode refNode1 = new RefNode();
|
|
||||||
RefNode refNode2 = new RefNode();
|
|
||||||
Queue<Node> q1 = new Queue<Node>();
|
|
||||||
Stack<Node> s1 = new Stack<Node>();
|
|
||||||
ConcurrentQueue<Node> cq = new ConcurrentQueue<Node>();
|
|
||||||
ConcurrentStack<Node> cs = new ConcurrentStack<Node>();
|
|
||||||
static TaskPool<Node> pool;
|
|
||||||
static TaskPoolRefNode<RefNode> poolRefNode;
|
|
||||||
static TaskPoolEqualNull<Node> poolEqualNull;
|
|
||||||
static TaskPoolClass<Node> poolClass = new TaskPoolClass<Node>();
|
|
||||||
static TaskPoolWithoutSize<Node> poolWithoutSize;
|
|
||||||
static TaskPoolWithoutLock<Node> poolWithoutLock;
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void Queue()
|
|
||||||
{
|
|
||||||
q1.Enqueue(node1);
|
|
||||||
q1.Enqueue(node1);
|
|
||||||
q1.TryDequeue(out _);
|
|
||||||
q1.TryDequeue(out _);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void QueueLock()
|
|
||||||
{
|
|
||||||
lock (q1) { q1.Enqueue(node1); }
|
|
||||||
lock (q1) { q1.Enqueue(node1); }
|
|
||||||
lock (q1) { q1.TryDequeue(out _); }
|
|
||||||
lock (q1) { q1.TryDequeue(out _); }
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void Stack()
|
|
||||||
{
|
|
||||||
s1.Push(node1);
|
|
||||||
s1.Push(node2);
|
|
||||||
s1.TryPop(out _);
|
|
||||||
s1.TryPop(out _);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void StackLock()
|
|
||||||
{
|
|
||||||
lock (s1) { s1.Push(node1); }
|
|
||||||
lock (s1) { s1.Push(node2); }
|
|
||||||
lock (s1) { s1.TryPop(out _); }
|
|
||||||
lock (s1) { s1.TryPop(out _); }
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void ConcurrentQueue()
|
|
||||||
{
|
|
||||||
cq.Enqueue(node1);
|
|
||||||
cq.Enqueue(node1);
|
|
||||||
cq.TryDequeue(out _);
|
|
||||||
cq.TryDequeue(out _);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void ConcurrentStack()
|
|
||||||
{
|
|
||||||
cs.Push(node1);
|
|
||||||
cs.Push(node2);
|
|
||||||
cs.TryPop(out _);
|
|
||||||
cs.TryPop(out _);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void TaskPool()
|
|
||||||
{
|
|
||||||
pool.TryPush(node1);
|
|
||||||
pool.TryPush(node2);
|
|
||||||
pool.TryPop(out _);
|
|
||||||
pool.TryPop(out _);
|
|
||||||
}
|
|
||||||
[Benchmark]
|
|
||||||
public void TaskPoolRefNode()
|
|
||||||
{
|
|
||||||
poolRefNode.TryPush(refNode1);
|
|
||||||
poolRefNode.TryPush(refNode2);
|
|
||||||
poolRefNode.TryPop(out _);
|
|
||||||
poolRefNode.TryPop(out _);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void TaskPoolEqualNull()
|
|
||||||
{
|
|
||||||
poolEqualNull.TryPush(node1);
|
|
||||||
poolEqualNull.TryPush(node2);
|
|
||||||
poolEqualNull.TryPop(out _);
|
|
||||||
poolEqualNull.TryPop(out _);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void TaskPoolClass()
|
|
||||||
{
|
|
||||||
poolClass.TryPush(node1);
|
|
||||||
poolClass.TryPush(node2);
|
|
||||||
poolClass.TryPop(out _);
|
|
||||||
poolClass.TryPop(out _);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void TaskPoolWithoutSize()
|
|
||||||
{
|
|
||||||
poolWithoutSize.TryPush(node1);
|
|
||||||
poolWithoutSize.TryPush(node2);
|
|
||||||
poolWithoutSize.TryPop(out _);
|
|
||||||
poolWithoutSize.TryPop(out _);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public void TaskPoolWithoutLock()
|
|
||||||
{
|
|
||||||
poolWithoutLock.TryPush(node1);
|
|
||||||
poolWithoutLock.TryPush(node2);
|
|
||||||
poolWithoutLock.TryPop(out _);
|
|
||||||
poolWithoutLock.TryPop(out _);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class Node : ITaskPoolNode<Node>
|
|
||||||
{
|
|
||||||
public Node NextNode { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface ITaskPoolNode<T>
|
|
||||||
{
|
|
||||||
T NextNode { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class RefNode :ITaskPoolRefNode<RefNode>
|
|
||||||
{
|
|
||||||
RefNode nextNode;
|
|
||||||
public ref RefNode NextNode => ref nextNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface ITaskPoolRefNode<T>
|
|
||||||
{
|
|
||||||
ref T NextNode { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// mutable struct, don't mark readonly.
|
|
||||||
[StructLayout(LayoutKind.Auto)]
|
|
||||||
public struct TaskPoolWithoutLock<T>
|
|
||||||
where T : class, ITaskPoolNode<T>
|
|
||||||
{
|
|
||||||
int size;
|
|
||||||
T root;
|
|
||||||
|
|
||||||
public int Size => size;
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public bool TryPop(out T result)
|
|
||||||
{
|
|
||||||
//if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
|
||||||
{
|
|
||||||
var v = root;
|
|
||||||
if (!(v is null))
|
|
||||||
{
|
|
||||||
root = v.NextNode;
|
|
||||||
v.NextNode = null;
|
|
||||||
size--;
|
|
||||||
result = v;
|
|
||||||
// Volatile.Write(ref gate, 0);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Volatile.Write(ref gate, 0);
|
|
||||||
}
|
|
||||||
result = default;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public bool TryPush(T item)
|
|
||||||
{
|
|
||||||
//if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
|
||||||
{
|
|
||||||
//if (size < TaskPool.MaxPoolSize)
|
|
||||||
{
|
|
||||||
item.NextNode = root;
|
|
||||||
root = item;
|
|
||||||
size++;
|
|
||||||
// Volatile.Write(ref gate, 0);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
//else
|
|
||||||
{
|
|
||||||
// Volatile.Write(ref gate, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Auto)]
|
|
||||||
public struct TaskPool<T>
|
|
||||||
where T : class, ITaskPoolNode<T>
|
|
||||||
{
|
|
||||||
int gate;
|
|
||||||
int size;
|
|
||||||
T root;
|
|
||||||
|
|
||||||
public int Size => size;
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public bool TryPop(out T result)
|
|
||||||
{
|
|
||||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
|
||||||
{
|
|
||||||
var v = root;
|
|
||||||
if (!(v is null))
|
|
||||||
{
|
|
||||||
root = v.NextNode;
|
|
||||||
v.NextNode = null;
|
|
||||||
size--;
|
|
||||||
result = v;
|
|
||||||
Volatile.Write(ref gate, 0);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Volatile.Write(ref gate, 0);
|
|
||||||
}
|
|
||||||
result = default;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public bool TryPush(T item)
|
|
||||||
{
|
|
||||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
|
||||||
{
|
|
||||||
//if (size < TaskPool.MaxPoolSize)
|
|
||||||
{
|
|
||||||
item.NextNode = root;
|
|
||||||
root = item;
|
|
||||||
size++;
|
|
||||||
Volatile.Write(ref gate, 0);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
//else
|
|
||||||
{
|
|
||||||
// Volatile.Write(ref gate, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
[StructLayout(LayoutKind.Auto)]
|
|
||||||
public struct TaskPoolRefNode<T>
|
|
||||||
where T : class, ITaskPoolRefNode<T>
|
|
||||||
{
|
|
||||||
int gate;
|
|
||||||
int size;
|
|
||||||
T root;
|
|
||||||
|
|
||||||
public int Size => size;
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public bool TryPop(out T result)
|
|
||||||
{
|
|
||||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
|
||||||
{
|
|
||||||
var v = root;
|
|
||||||
if (!(v is null))
|
|
||||||
{
|
|
||||||
ref var nextNode = ref v.NextNode;
|
|
||||||
root = nextNode;
|
|
||||||
nextNode = null;
|
|
||||||
size--;
|
|
||||||
result = v;
|
|
||||||
Volatile.Write(ref gate, 0);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Volatile.Write(ref gate, 0);
|
|
||||||
}
|
|
||||||
result = default;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public bool TryPush(T item)
|
|
||||||
{
|
|
||||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
|
||||||
{
|
|
||||||
//if (size < TaskPool.MaxPoolSize)
|
|
||||||
{
|
|
||||||
item.NextNode = root;
|
|
||||||
root = item;
|
|
||||||
size++;
|
|
||||||
Volatile.Write(ref gate, 0);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
//else
|
|
||||||
{
|
|
||||||
// Volatile.Write(ref gate, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Auto)]
|
|
||||||
public struct TaskPoolEqualNull<T>
|
|
||||||
where T : class, ITaskPoolNode<T>
|
|
||||||
{
|
|
||||||
int gate;
|
|
||||||
int size;
|
|
||||||
T root;
|
|
||||||
|
|
||||||
public int Size => size;
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public bool TryPop(out T result)
|
|
||||||
{
|
|
||||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
|
||||||
{
|
|
||||||
var v = root;
|
|
||||||
if (v != null)
|
|
||||||
{
|
|
||||||
root = v.NextNode;
|
|
||||||
v.NextNode = null;
|
|
||||||
size--;
|
|
||||||
result = v;
|
|
||||||
Volatile.Write(ref gate, 0);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Volatile.Write(ref gate, 0);
|
|
||||||
}
|
|
||||||
result = default;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public bool TryPush(T item)
|
|
||||||
{
|
|
||||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
|
||||||
{
|
|
||||||
//if (size < TaskPool.MaxPoolSize)
|
|
||||||
{
|
|
||||||
item.NextNode = root;
|
|
||||||
root = item;
|
|
||||||
size++;
|
|
||||||
Volatile.Write(ref gate, 0);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
//else
|
|
||||||
{
|
|
||||||
// Volatile.Write(ref gate, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class TaskPoolClass<T>
|
|
||||||
where T : class, ITaskPoolNode<T>
|
|
||||||
{
|
|
||||||
int gate;
|
|
||||||
int size;
|
|
||||||
T root;
|
|
||||||
|
|
||||||
public int Size => size;
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public bool TryPop(out T result)
|
|
||||||
{
|
|
||||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
|
||||||
{
|
|
||||||
var v = root;
|
|
||||||
if (!(v is null))
|
|
||||||
{
|
|
||||||
root = v.NextNode;
|
|
||||||
v.NextNode = null;
|
|
||||||
size--;
|
|
||||||
result = v;
|
|
||||||
Volatile.Write(ref gate, 0);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Volatile.Write(ref gate, 0);
|
|
||||||
}
|
|
||||||
result = default;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public bool TryPush(T item)
|
|
||||||
{
|
|
||||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
|
||||||
{
|
|
||||||
//if (size < TaskPool.MaxPoolSize)
|
|
||||||
{
|
|
||||||
item.NextNode = root;
|
|
||||||
root = item;
|
|
||||||
size++;
|
|
||||||
Volatile.Write(ref gate, 0);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
//else
|
|
||||||
{
|
|
||||||
// Volatile.Write(ref gate, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Auto)]
|
|
||||||
public struct TaskPoolWithoutSize<T>
|
|
||||||
where T : class, ITaskPoolNode<T>
|
|
||||||
{
|
|
||||||
int gate;
|
|
||||||
T root;
|
|
||||||
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public bool TryPop(out T result)
|
|
||||||
{
|
|
||||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
|
||||||
{
|
|
||||||
var v = root;
|
|
||||||
if (!(v is null))
|
|
||||||
{
|
|
||||||
root = v.NextNode;
|
|
||||||
v.NextNode = null;
|
|
||||||
result = v;
|
|
||||||
Volatile.Write(ref gate, 0);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Volatile.Write(ref gate, 0);
|
|
||||||
}
|
|
||||||
result = default;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public bool TryPush(T item)
|
|
||||||
{
|
|
||||||
if (Interlocked.CompareExchange(ref gate, 1, 0) == 0)
|
|
||||||
{
|
|
||||||
//if (size < TaskPool.MaxPoolSize)
|
|
||||||
{
|
|
||||||
item.NextNode = root;
|
|
||||||
root = item;
|
|
||||||
Volatile.Write(ref gate, 0);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
//else
|
|
||||||
{
|
|
||||||
// Volatile.Write(ref gate, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -15,6 +15,12 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\UniTask.NetCore\UniTask.NetCore.csproj" />
|
<ProjectReference Include="..\UniTask.NetCore\UniTask.NetCore.csproj" />
|
||||||
|
|
||||||
|
|
||||||
|
<ProjectReference Include="..\UniTask.Analyzer\UniTask.Analyzer.csproj">
|
||||||
|
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
|
||||||
|
<OutputItemType>Analyzer</OutputItemType>
|
||||||
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
Loading…
Reference in New Issue