add experimental analyzer

pull/323/head
neuecc 2021-08-27 16:44:05 +09:00
parent 4f6166102d
commit a1dee8b54f
9 changed files with 120 additions and 1496 deletions

View File

@ -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

View File

@ -0,0 +1,8 @@
{
"profiles": {
"UniTask.Analyzer": {
"commandName": "DebugRoslynComponent",
"targetProject": "..\\UniTask.NetCoreSandbox\\UniTask.NetCoreSandbox.csproj"
}
}
}

View File

@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>library</OutputType>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<IsRoslynComponent>true</IsRoslynComponent>
<TargetsForTfmSpecificContentInPackage>$(TargetsForTfmSpecificContentInPackage);PackBuildOutputs</TargetsForTfmSpecificContentInPackage>
<IncludeBuildOutput>false</IncludeBuildOutput>
<IncludeSymbols>false</IncludeSymbols>
<SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
<DevelopmentDependency>true</DevelopmentDependency>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.8.0" />
</ItemGroup>
<Target Name="PackBuildOutputs" DependsOnTargets="SatelliteDllsProjectOutputGroup;DebugSymbolsProjectOutputGroup">
<ItemGroup>
<TfmSpecificPackageFile Include="$(TargetDir)\*.dll" PackagePath="analyzers\dotnet\cs" />
<TfmSpecificPackageFile Include="@(SatelliteDllsProjectOutputGroupOutput->'%(FinalOutputPath)')" PackagePath="analyzers\dotnet\cs\%(SatelliteDllsProjectOutputGroupOutput.Culture)\" />
</ItemGroup>
</Target>
</Project>

View File

@ -0,0 +1,54 @@
#pragma warning disable RS2008
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
using System.Collections.Immutable;
using System.Threading;
namespace UniTask.Analyzer
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class UniTaskAnalyzer : DiagnosticAnalyzer
{
private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(
id: "UNITASK001",
title: "UniTaskAnalyzer001: Must pass CancellationToken",
messageFormat: "Must pass CancellationToken",
category: "Usage",
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true,
description: "Pass CancellationToken or CancellationToken.None.");
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(Rule); } }
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
context.RegisterOperationAction(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);
}
}
}
}
}
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}
} }

View File

@ -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;
}
}

View File

@ -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>