diff --git a/src/UniTask.NetCoreSandbox/Program.cs b/src/UniTask.NetCoreSandbox/Program.cs index 706e6e0..ed6e222 100644 --- a/src/UniTask.NetCoreSandbox/Program.cs +++ b/src/UniTask.NetCoreSandbox/Program.cs @@ -15,6 +15,19 @@ using System.Reactive.Concurrency; namespace NetCoreSandbox { + public class MySyncContext : SynchronizationContext + { + public MySyncContext() + { + } + + public override void Post(SendOrPostCallback d, object state) + { + Console.WriteLine("Called SyncContext Post!"); + base.Post(d, state); + } + } + public class Text { @@ -193,12 +206,54 @@ namespace NetCoreSandbox 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 Task Main(string[] args) { #if !DEBUG + + + //await new AllocationCheck().ViaUniTaskVoid(); //Console.ReadLine(); BenchmarkDotNet.Running.BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args); @@ -210,6 +265,12 @@ namespace NetCoreSandbox // AsyncTest().Forget(); + SynchronizationContext.SetSynchronizationContext(new MySyncContext()); + + await Aaa(); + + + //AsyncTest().Forget(); diff --git a/src/UniTask.NetCoreSandbox/QueueCheck.cs b/src/UniTask.NetCoreSandbox/QueueCheck.cs new file mode 100644 index 0000000..db55939 --- /dev/null +++ b/src/UniTask.NetCoreSandbox/QueueCheck.cs @@ -0,0 +1,396 @@ +using BenchmarkDotNet.Attributes; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; + +[Config(typeof(BenchmarkConfig))] +public class QueueCheck +{ + Node node1 = new Node(); + Node node2 = new Node(); + Queue q1 = new Queue(); + Stack s1 = new Stack(); + ConcurrentQueue cq = new ConcurrentQueue(); + ConcurrentStack cs = new ConcurrentStack(); + static TaskPool pool; + 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 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; } +} + +// 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 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/Assets/Plugins/UniTask/Runtime/CompilerServices/AsyncUniTaskMethodBuilder.cs b/src/UniTask/Assets/Plugins/UniTask/Runtime/CompilerServices/AsyncUniTaskMethodBuilder.cs index 7aaafee..da800f7 100644 --- a/src/UniTask/Assets/Plugins/UniTask/Runtime/CompilerServices/AsyncUniTaskMethodBuilder.cs +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/CompilerServices/AsyncUniTaskMethodBuilder.cs @@ -47,6 +47,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices // 3. SetException [DebuggerHidden] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void SetException(Exception exception) { if (runnerPromise == null) @@ -61,6 +62,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices // 4. SetResult [DebuggerHidden] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void SetResult() { if (runnerPromise != null) @@ -71,6 +73,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices // 5. AwaitOnCompleted [DebuggerHidden] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine @@ -86,6 +89,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices // 6. AwaitUnsafeOnCompleted [DebuggerHidden] [SecuritySafeCritical] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : ICriticalNotifyCompletion where TStateMachine : IAsyncStateMachine @@ -100,6 +104,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices // 7. Start [DebuggerHidden] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Start(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine { @@ -146,9 +151,10 @@ namespace Cysharp.Threading.Tasks.CompilerServices } // 2. TaskLike Task property. - [DebuggerHidden] public UniTask Task { + [DebuggerHidden] + [MethodImpl(MethodImplOptions.AggressiveInlining)] get { if (runnerPromise != null) @@ -168,6 +174,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices // 3. SetException [DebuggerHidden] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void SetException(Exception exception) { if (runnerPromise == null) @@ -182,6 +189,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices // 4. SetResult [DebuggerHidden] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void SetResult(T result) { if (runnerPromise == null) @@ -196,6 +204,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices // 5. AwaitOnCompleted [DebuggerHidden] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine @@ -211,6 +220,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices // 6. AwaitUnsafeOnCompleted [DebuggerHidden] [SecuritySafeCritical] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : ICriticalNotifyCompletion where TStateMachine : IAsyncStateMachine @@ -225,6 +235,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices // 7. Start [DebuggerHidden] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Start(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine { diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/CompilerServices/AsyncUniTaskVoidMethodBuilder.cs b/src/UniTask/Assets/Plugins/UniTask/Runtime/CompilerServices/AsyncUniTaskVoidMethodBuilder.cs index 3736e78..312e599 100644 --- a/src/UniTask/Assets/Plugins/UniTask/Runtime/CompilerServices/AsyncUniTaskVoidMethodBuilder.cs +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/CompilerServices/AsyncUniTaskVoidMethodBuilder.cs @@ -35,6 +35,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices // 3. SetException [DebuggerHidden] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void SetException(Exception exception) { // runner is finished, return first. @@ -49,6 +50,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices // 4. SetResult [DebuggerHidden] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void SetResult() { // runner is finished, return. @@ -61,6 +63,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices // 5. AwaitOnCompleted [DebuggerHidden] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine @@ -76,6 +79,7 @@ namespace Cysharp.Threading.Tasks.CompilerServices // 6. AwaitUnsafeOnCompleted [DebuggerHidden] [SecuritySafeCritical] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : ICriticalNotifyCompletion where TStateMachine : IAsyncStateMachine