From a1dee8b54f4d0497fb9a16c1301ad513ee22d86f Mon Sep 17 00:00:00 2001 From: neuecc Date: Fri, 27 Aug 2021 16:44:05 +0900 Subject: [PATCH] add experimental analyzer --- UniTask.NetCore.sln | 14 +- .../Properties/launchSettings.json | 8 + src/UniTask.Analyzer/UniTask.Analyzer.csproj | 29 ++ src/UniTask.Analyzer/UniTaskAnalyzer.cs | 54 ++ src/UniTask.NetCoreSandbox/AllocationCheck.cs | 258 --------- src/UniTask.NetCoreSandbox/Benchmark.cs | 283 ---------- src/UniTask.NetCoreSandbox/Program.cs | 491 +----------------- src/UniTask.NetCoreSandbox/QueueCheck.cs | 473 ----------------- .../UniTask.NetCoreSandbox.csproj | 6 + 9 files changed, 120 insertions(+), 1496 deletions(-) create mode 100644 src/UniTask.Analyzer/Properties/launchSettings.json create mode 100644 src/UniTask.Analyzer/UniTask.Analyzer.csproj create mode 100644 src/UniTask.Analyzer/UniTaskAnalyzer.cs delete mode 100644 src/UniTask.NetCoreSandbox/AllocationCheck.cs delete mode 100644 src/UniTask.NetCoreSandbox/Benchmark.cs delete mode 100644 src/UniTask.NetCoreSandbox/QueueCheck.cs diff --git a/UniTask.NetCore.sln b/UniTask.NetCore.sln index 46cbf3e..aa5abe9 100644 --- a/UniTask.NetCore.sln +++ b/UniTask.NetCore.sln @@ -1,13 +1,15 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29613.14 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31606.5 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniTask.NetCoreTests", "src\UniTask.NetCoreTests\UniTask.NetCoreTests.csproj", "{B3E311A4-70D8-4131-9965-C073A99D201A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniTask.NetCoreTests", "src\UniTask.NetCoreTests\UniTask.NetCoreTests.csproj", "{B3E311A4-70D8-4131-9965-C073A99D201A}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniTask.NetCore", "src\UniTask.NetCore\UniTask.NetCore.csproj", "{16EE20D0-7FB1-483A-8467-A5EEDBF1F5BF}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniTask.NetCoreSandbox", "src\UniTask.NetCoreSandbox\UniTask.NetCoreSandbox.csproj", "{3915E72E-33E0-4A14-A6D8-872702200E58}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniTask.NetCoreSandbox", "src\UniTask.NetCoreSandbox\UniTask.NetCoreSandbox.csproj", "{3915E72E-33E0-4A14-A6D8-872702200E58}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniTask.Analyzer", "src\UniTask.Analyzer\UniTask.Analyzer.csproj", "{0AC6F052-A255-4EE3-9E05-1C02D49AB1C2}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -27,6 +29,10 @@ Global {3915E72E-33E0-4A14-A6D8-872702200E58}.Debug|Any CPU.Build.0 = Debug|Any CPU {3915E72E-33E0-4A14-A6D8-872702200E58}.Release|Any CPU.ActiveCfg = Release|Any CPU {3915E72E-33E0-4A14-A6D8-872702200E58}.Release|Any CPU.Build.0 = Release|Any CPU + {0AC6F052-A255-4EE3-9E05-1C02D49AB1C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0AC6F052-A255-4EE3-9E05-1C02D49AB1C2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0AC6F052-A255-4EE3-9E05-1C02D49AB1C2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0AC6F052-A255-4EE3-9E05-1C02D49AB1C2}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/UniTask.Analyzer/Properties/launchSettings.json b/src/UniTask.Analyzer/Properties/launchSettings.json new file mode 100644 index 0000000..9742dc6 --- /dev/null +++ b/src/UniTask.Analyzer/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "UniTask.Analyzer": { + "commandName": "DebugRoslynComponent", + "targetProject": "..\\UniTask.NetCoreSandbox\\UniTask.NetCoreSandbox.csproj" + } + } +} \ No newline at end of file diff --git a/src/UniTask.Analyzer/UniTask.Analyzer.csproj b/src/UniTask.Analyzer/UniTask.Analyzer.csproj new file mode 100644 index 0000000..0c1b6d3 --- /dev/null +++ b/src/UniTask.Analyzer/UniTask.Analyzer.csproj @@ -0,0 +1,29 @@ + + + library + netstandard2.0 + latest + enable + true + $(TargetsForTfmSpecificContentInPackage);PackBuildOutputs + false + false + true + true + + + + + runtime; build; native; contentfiles; analyzers + all + + + + + + + + + + + \ No newline at end of file diff --git a/src/UniTask.Analyzer/UniTaskAnalyzer.cs b/src/UniTask.Analyzer/UniTaskAnalyzer.cs new file mode 100644 index 0000000..3d28035 --- /dev/null +++ b/src/UniTask.Analyzer/UniTaskAnalyzer.cs @@ -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 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); + } + } + } + } + } + } +} \ No newline at end of file diff --git a/src/UniTask.NetCoreSandbox/AllocationCheck.cs b/src/UniTask.NetCoreSandbox/AllocationCheck.cs deleted file mode 100644 index 1efecb3..0000000 --- a/src/UniTask.NetCoreSandbox/AllocationCheck.cs +++ /dev/null @@ -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 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 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 Core() - { - var a = await new TestAwaiter(false, UniTaskStatus.Succeeded, 10); - var b = await new TestAwaiter(false, UniTaskStatus.Succeeded, 10); - var c = await new TestAwaiter(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 : 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 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 pool = new ConcurrentQueue(); - - 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(); - } -} \ No newline at end of file diff --git a/src/UniTask.NetCoreSandbox/Benchmark.cs b/src/UniTask.NetCoreSandbox/Benchmark.cs deleted file mode 100644 index 94a2ca7..0000000 --- a/src/UniTask.NetCoreSandbox/Benchmark.cs +++ /dev/null @@ -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 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")] - public async Task ViaTaskT() - { - int sum = 0; - for (int i = 0; i < InnerOps; i++) - sum += await Inner(1, 2).ConfigureAwait(ConfigureAwait); - return sum; - - static async Task 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")] - public async Task ViaValueTaskT() - { - int sum = 0; - for (int i = 0; i < InnerOps; i++) - sum += await Inner(1, 2).ConfigureAwait(ConfigureAwait); - return sum; - - static async ValueTask 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")] - public async Task ViaPooledValueTaskT() - { - int sum = 0; - for (int i = 0; i < InnerOps; i++) - sum += await Inner(1, 2).ConfigureAwait(ConfigureAwait); - return sum; - - static async PooledValueTask 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")] - public async Task ViaPooledTaskT() - { - int sum = 0; - for (int i = 0; i < InnerOps; i++) - sum += await Inner(1, 2).ConfigureAwait(ConfigureAwait); - return sum; - - static async PooledTask 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 ViaUniTaskT() - { - var sum = 0; - for (int i = 0; i < InnerOps; i++) - { - sum += await Inner(1, 2); - } - return sum; - - static async UniTask 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(); - } -} \ No newline at end of file diff --git a/src/UniTask.NetCoreSandbox/Program.cs b/src/UniTask.NetCoreSandbox/Program.cs index ad07bdf..8067ab7 100644 --- a/src/UniTask.NetCoreSandbox/Program.cs +++ b/src/UniTask.NetCoreSandbox/Program.cs @@ -17,499 +17,34 @@ using System.Reactive.Concurrency; namespace NetCoreSandbox { - public class MySyncContext : SynchronizationContext + public class Program { - public MySyncContext() - { - } - - public override void Post(SendOrPostCallback d, object state) - { - Console.WriteLine("Called SyncContext Post!"); - base.Post(d, state); - } - } - - public class Text - { - - public string text { get; set; } - } - - public class ZeroAllocAsyncAwaitInDotNetCore - { - public ValueTask NanikaAsync(int x, int y) - { - return Core(this, x, y); - - static async UniTask 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 : 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 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 source, Text text) - { - AAAACORECORE(source, text).Forget(); - - async UniTaskVoid AAAACORECORE(IUniTaskAsyncEnumerable 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(this IObservable source, Text text) - //{ - // return source.SubscribeWithState(text, (x, t) => t.text = x.ToString()); - //} - - //public static IDisposable SubscribeToText(this IObservable source, Text text, Func selector) - //{ - // return source.SubscribeWithState2(text, selector, (x, t, s) => t.text = s(x)); - //} - - //public static IDisposable SubscribeToInteractable(this IObservable 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 FooAsync([EnumeratorCancellation]CancellationToken cancellationToken = default) - { - yield return 1; - await Task.Delay(10, cancellationToken); - } - - public class MyDisposable : IDisposable - { - public void Dispose() - { - - } - } - - static void Test() - { - var disp = new MyDisposable(); - - using var _ = new MyDisposable(); - - Console.WriteLine("tako"); - } - - - static async UniTask FooBarAsync() - { - await using (UniTask.ReturnToCurrentSynchronizationContext()) - { - await UniTask.SwitchToThreadPool(); - - - - - } - } - - - - - static async UniTask Aaa() - { - await FooBarAsync(); - - Console.WriteLine("FooBarAsync End"); - } - - static async UniTask WhereSelect() - { - await foreach (var item in UniTaskAsyncEnumerable.Range(1, 10) - .SelectAwait(async x => - { - await UniTask.Yield(); - return x; - }) - .Where(x => x % 2 == 0)) - { - Console.WriteLine(item); - } - } - - static async Task Main(string[] args) { -#if !DEBUG - + var cts = new CancellationTokenSource(); + + + // OK. + await FooAsync(10, cts.Token); + + // NG(Compiler Error) + await FooAsync(10); + + + - //await new AllocationCheck().ViaUniTaskVoid(); - //Console.ReadLine(); - BenchmarkDotNet.Running.BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args); - //await new ComparisonBenchmarks().ViaUniTaskT(); - return; -#endif - - var e = UniTaskAsyncEnumerable.Create(async (writer, token) => - { - for (int i = 0; i < 5; i++) - { - Console.WriteLine($"Start {i}"); - await writer.YieldAsync(i); - Console.WriteLine($"End {i}"); - } - }); - - var ee = e.GetAsyncEnumerator(); - while (await ee.MoveNextAsync()) - { - Console.WriteLine("ForEach " + ee.Current); - } } - static async UniTask YieldCore() + static async UniTask FooAsync(int x, CancellationToken cancellationToken = default) { await UniTask.Yield(); } - -#pragma warning disable CS1998 - - - static async UniTask 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 AsyncGen() - { - await UniTask.SwitchToThreadPool(); - yield return 10; - await UniTask.SwitchToThreadPool(); - yield return 100; - } } - class MyEnumerable : IEnumerable - { - public IEnumerator GetEnumerator() - { - return new MyEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - throw new NotImplementedException(); - } - } - - class MyEnumerator : IEnumerator - { - 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 - { - public CustomAsyncEnumerator GetAsyncEnumerator() - { - //IAsyncEnumerable - return new CustomAsyncEnumerator(); - } - } - - - public struct CustomAsyncEnumerator - { - int count; - - public T Current - { - get - { - return default; - } - } - - public UniTask MoveNextAsync() - { - if (count++ == 3) - { - return UniTask.FromResult(false); - //return false; - } - return UniTask.FromResult(true); - } - - public UniTask DisposeAsync() - { - return default; - } - } - - - } diff --git a/src/UniTask.NetCoreSandbox/QueueCheck.cs b/src/UniTask.NetCoreSandbox/QueueCheck.cs deleted file mode 100644 index 3e0937c..0000000 --- a/src/UniTask.NetCoreSandbox/QueueCheck.cs +++ /dev/null @@ -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 q1 = new Queue(); - Stack s1 = new Stack(); - ConcurrentQueue cq = new ConcurrentQueue(); - ConcurrentStack cs = new ConcurrentStack(); - static TaskPool pool; - static TaskPoolRefNode poolRefNode; - static TaskPoolEqualNull poolEqualNull; - static TaskPoolClass poolClass = new TaskPoolClass(); - static TaskPoolWithoutSize poolWithoutSize; - static TaskPoolWithoutLock 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 -{ - public Node NextNode { get; set; } -} - -public interface ITaskPoolNode -{ - T NextNode { get; set; } -} - -public sealed class RefNode :ITaskPoolRefNode -{ - RefNode nextNode; - public ref RefNode NextNode => ref nextNode; -} - -public interface ITaskPoolRefNode -{ - ref T NextNode { get; } -} - - -// mutable struct, don't mark readonly. -[StructLayout(LayoutKind.Auto)] -public struct TaskPoolWithoutLock - where T : class, ITaskPoolNode -{ - 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 - where T : class, ITaskPoolNode -{ - 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 - where T : class, ITaskPoolRefNode -{ - 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 - where T : class, ITaskPoolNode -{ - 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 - where T : class, ITaskPoolNode -{ - 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 - where T : class, ITaskPoolNode -{ - 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; - } -} \ No newline at end of file diff --git a/src/UniTask.NetCoreSandbox/UniTask.NetCoreSandbox.csproj b/src/UniTask.NetCoreSandbox/UniTask.NetCoreSandbox.csproj index 6d531d0..cc33684 100644 --- a/src/UniTask.NetCoreSandbox/UniTask.NetCoreSandbox.csproj +++ b/src/UniTask.NetCoreSandbox/UniTask.NetCoreSandbox.csproj @@ -15,6 +15,12 @@ + + + + false + Analyzer +