Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

Commit

Permalink
Queue ValueTaskAwaiter IAsyncStateMachineBox directly to ThreadPool (#…
Browse files Browse the repository at this point in the history
…21159)

* Queue ValueTaskAwaiter IAsyncStateMachineBox directly to ThreadPool

* Invert the dependency

* Move to UnsafeQueueUserWorkItem

* MRVTSC queue null or Deafult EC to UnsafeQUWI

* Revert MRVTSC change

* Add comment and validation

* Use s_invokeAsyncStateMachineBox for AsTask

* nits

* nits 2

* Rever ValueTask

* nits
  • Loading branch information
benaadams authored and stephentoub committed Nov 28, 2018
1 parent 204d2da commit e7ead79
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks.Sources;

Expand Down Expand Up @@ -111,7 +112,7 @@ void IStateMachineBoxAwareAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox b
}
else if (obj != null)
{
Unsafe.As<IValueTaskSource>(obj).OnCompleted(ValueTaskAwaiter.s_invokeAsyncStateMachineBox, box, _value._token,
Unsafe.As<IValueTaskSource>(obj).OnCompleted(ThreadPoolGlobals.s_invokeAsyncStateMachineBox, box, _value._token,
_value._continueOnCapturedContext ? ValueTaskSourceOnCompletedFlags.UseSchedulingContext : ValueTaskSourceOnCompletedFlags.None);
}
else
Expand Down Expand Up @@ -222,7 +223,7 @@ void IStateMachineBoxAwareAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox b
}
else if (obj != null)
{
Unsafe.As<IValueTaskSource<TResult>>(obj).OnCompleted(ValueTaskAwaiter.s_invokeAsyncStateMachineBox, box, _value._token,
Unsafe.As<IValueTaskSource<TResult>>(obj).OnCompleted(ThreadPoolGlobals.s_invokeAsyncStateMachineBox, box, _value._token,
_value._continueOnCapturedContext ? ValueTaskSourceOnCompletedFlags.UseSchedulingContext : ValueTaskSourceOnCompletedFlags.None);
}
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.

using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks.Sources;

Expand Down Expand Up @@ -101,25 +102,13 @@ void IStateMachineBoxAwareAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox b
}
else if (obj != null)
{
Unsafe.As<IValueTaskSource>(obj).OnCompleted(s_invokeAsyncStateMachineBox, box, _value._token, ValueTaskSourceOnCompletedFlags.UseSchedulingContext);
Unsafe.As<IValueTaskSource>(obj).OnCompleted(ThreadPoolGlobals.s_invokeAsyncStateMachineBox, box, _value._token, ValueTaskSourceOnCompletedFlags.UseSchedulingContext);
}
else
{
TaskAwaiter.UnsafeOnCompletedInternal(Task.CompletedTask, box, continueOnCapturedContext: true);
}
}

/// <summary>Shim used to invoke <see cref="ITaskCompletionAction.Invoke"/> of the supplied <see cref="IAsyncStateMachineBox"/>.</summary>
internal static readonly Action<object> s_invokeAsyncStateMachineBox = state =>
{
if (!(state is IAsyncStateMachineBox box))
{
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.state);
return;
}
box.MoveNext();
};
#endif
}

Expand Down Expand Up @@ -201,7 +190,7 @@ void IStateMachineBoxAwareAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox b
}
else if (obj != null)
{
Unsafe.As<IValueTaskSource<TResult>>(obj).OnCompleted(ValueTaskAwaiter.s_invokeAsyncStateMachineBox, box, _value._token, ValueTaskSourceOnCompletedFlags.UseSchedulingContext);
Unsafe.As<IValueTaskSource<TResult>>(obj).OnCompleted(ThreadPoolGlobals.s_invokeAsyncStateMachineBox, box, _value._token, ValueTaskSourceOnCompletedFlags.UseSchedulingContext);
}
else
{
Expand Down
30 changes: 30 additions & 0 deletions src/System.Private.CoreLib/src/System/Threading/ThreadPool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,18 @@ internal static class ThreadPoolGlobals
public static bool enableWorkerTracking;

public static readonly ThreadPoolWorkQueue workQueue = new ThreadPoolWorkQueue();

/// <summary>Shim used to invoke <see cref="IAsyncStateMachineBox.MoveNext"/> of the supplied <see cref="IAsyncStateMachineBox"/>.</summary>
internal static readonly Action<object> s_invokeAsyncStateMachineBox = state =>
{
if (!(state is IAsyncStateMachineBox box))
{
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.state);
return;
}
box.MoveNext();
};
}

[StructLayout(LayoutKind.Sequential)] // enforce layout so that padding reduces false sharing
Expand Down Expand Up @@ -1333,6 +1345,24 @@ public static bool UnsafeQueueUserWorkItem<TState>(Action<TState> callBack, TSta
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.callBack);
}

// If the callback is the runtime-provided invocation of an IAsyncStateMachineBox,
// then we can queue the Task state directly to the ThreadPool instead of
// wrapping it in a QueueUserWorkItemCallback.
//
// This occurs when user code queues its provided continuation to the ThreadPool;
// internally we call UnsafeQueueUserWorkItemInternal directly for Tasks.
if (ReferenceEquals(callBack, ThreadPoolGlobals.s_invokeAsyncStateMachineBox))
{
if (!(state is IAsyncStateMachineBox))
{
// The provided state must be the internal IAsyncStateMachineBox (Task) type
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.state);
}

UnsafeQueueUserWorkItemInternal((object)state, preferLocal);
return true;
}

EnsureVMInitialized();

ThreadPoolGlobals.workQueue.Enqueue(
Expand Down

0 comments on commit e7ead79

Please sign in to comment.