未验证 提交 41f58f65 编写于 作者: E Eduardo Velarde 提交者: GitHub

Enable IOPack, IOEnqueue, and IODequeue on Windows (#88894)

Enable IOPack, IOEnqueue and IODequeue for the Windows Threadpool (the one NativeAOT uses by default). 
上级 a3dde4ab
...@@ -185,11 +185,9 @@ public static void Free(NativeOverlapped* nativeOverlappedPtr) ...@@ -185,11 +185,9 @@ public static void Free(NativeOverlapped* nativeOverlappedPtr)
#if FEATURE_PERFTRACING #if FEATURE_PERFTRACING
#if !((TARGET_BROWSER || TARGET_WASI) && !FEATURE_WASM_THREADS) #if !((TARGET_BROWSER || TARGET_WASI) && !FEATURE_WASM_THREADS)
#if !NATIVEAOT // TODO shipping criteria: no EVENTPIPE-NATIVEAOT-TODO left in the codebase
if (NativeRuntimeEventSource.Log.IsEnabled()) if (NativeRuntimeEventSource.Log.IsEnabled())
NativeRuntimeEventSource.Log.ThreadPoolIOPack(pNativeOverlapped); NativeRuntimeEventSource.Log.ThreadPoolIOPack(pNativeOverlapped);
#endif #endif
#endif
#endif #endif
NativeOverlapped* pRet = pNativeOverlapped; NativeOverlapped* pRet = pNativeOverlapped;
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
using Microsoft.Win32.SafeHandles; using Microsoft.Win32.SafeHandles;
using System.Diagnostics; using System.Diagnostics;
using System.Diagnostics.Tracing;
using System.Runtime; using System.Runtime;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
...@@ -48,6 +49,9 @@ public sealed partial class RegisteredWaitHandle : MarshalByRefObject ...@@ -48,6 +49,9 @@ public sealed partial class RegisteredWaitHandle : MarshalByRefObject
_gcHandle.Free(); _gcHandle.Free();
throw new OutOfMemoryException(); throw new OutOfMemoryException();
} }
if (NativeRuntimeEventSource.Log.IsEnabled())
NativeRuntimeEventSource.Log.ThreadPoolIOEnqueue(this);
} }
#pragma warning disable IDE0060 // Remove unused parameter #pragma warning disable IDE0060 // Remove unused parameter
...@@ -91,6 +95,9 @@ private void PerformCallbackWindowsThreadPool(bool timedOut) ...@@ -91,6 +95,9 @@ private void PerformCallbackWindowsThreadPool(bool timedOut)
} }
} }
if (NativeRuntimeEventSource.Log.IsEnabled())
NativeRuntimeEventSource.Log.ThreadPoolIODequeue(this);
_ThreadPoolWaitOrTimerCallback.PerformWaitOrTimerCallback(_callbackHelper!, timedOut); _ThreadPoolWaitOrTimerCallback.PerformWaitOrTimerCallback(_callbackHelper!, timedOut);
} }
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
using Microsoft.Win32.SafeHandles; using Microsoft.Win32.SafeHandles;
using System.Diagnostics; using System.Diagnostics;
using System.Diagnostics.Tracing;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
...@@ -54,6 +55,9 @@ private static unsafe ThreadPoolBoundHandle BindHandleWindowsThreadPool(SafeHand ...@@ -54,6 +55,9 @@ private static unsafe ThreadPoolBoundHandle BindHandleWindowsThreadPool(SafeHand
Win32ThreadPoolNativeOverlapped* overlapped = Win32ThreadPoolNativeOverlapped.Allocate(callback, state, pinData, preAllocated: null, flowExecutionContext); Win32ThreadPoolNativeOverlapped* overlapped = Win32ThreadPoolNativeOverlapped.Allocate(callback, state, pinData, preAllocated: null, flowExecutionContext);
overlapped->Data._boundHandle = this; overlapped->Data._boundHandle = this;
if (NativeRuntimeEventSource.Log.IsEnabled())
NativeRuntimeEventSource.Log.ThreadPoolIOEnqueue(Win32ThreadPoolNativeOverlapped.ToNativeOverlapped(overlapped));
Interop.Kernel32.StartThreadpoolIo(_threadPoolHandle!); Interop.Kernel32.StartThreadpoolIo(_threadPoolHandle!);
return Win32ThreadPoolNativeOverlapped.ToNativeOverlapped(overlapped); return Win32ThreadPoolNativeOverlapped.ToNativeOverlapped(overlapped);
...@@ -82,6 +86,9 @@ private static unsafe ThreadPoolBoundHandle BindHandleWindowsThreadPool(SafeHand ...@@ -82,6 +86,9 @@ private static unsafe ThreadPoolBoundHandle BindHandleWindowsThreadPool(SafeHand
data._boundHandle = this; data._boundHandle = this;
if (NativeRuntimeEventSource.Log.IsEnabled())
NativeRuntimeEventSource.Log.ThreadPoolIOEnqueue(Win32ThreadPoolNativeOverlapped.ToNativeOverlapped(preAllocated._overlappedWindowsThreadPool));
Interop.Kernel32.StartThreadpoolIo(_threadPoolHandle!); Interop.Kernel32.StartThreadpoolIo(_threadPoolHandle!);
return Win32ThreadPoolNativeOverlapped.ToNativeOverlapped(preAllocated._overlappedWindowsThreadPool); return Win32ThreadPoolNativeOverlapped.ToNativeOverlapped(preAllocated._overlappedWindowsThreadPool);
...@@ -154,6 +161,9 @@ private static unsafe void OnNativeIOCompleted(IntPtr instance, IntPtr context, ...@@ -154,6 +161,9 @@ private static unsafe void OnNativeIOCompleted(IntPtr instance, IntPtr context,
boundHandle.Release(); boundHandle.Release();
if (NativeRuntimeEventSource.Log.IsEnabled())
NativeRuntimeEventSource.Log.ThreadPoolIODequeue(Win32ThreadPoolNativeOverlapped.ToNativeOverlapped(overlapped));
Win32ThreadPoolNativeOverlapped.CompleteWithCallback(ioResult, (uint)numberOfBytesTransferred, overlapped); Win32ThreadPoolNativeOverlapped.CompleteWithCallback(ioResult, (uint)numberOfBytesTransferred, overlapped);
ThreadPool.IncrementCompletedWorkItemCount(); ThreadPool.IncrementCompletedWorkItemCount();
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
using Microsoft.Win32.SafeHandles; using Microsoft.Win32.SafeHandles;
using System.Diagnostics; using System.Diagnostics;
using System.Diagnostics.Tracing;
using System.Runtime; using System.Runtime;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
...@@ -187,8 +188,13 @@ internal static unsafe void RequestWorkerThread() ...@@ -187,8 +188,13 @@ internal static unsafe void RequestWorkerThread()
return registeredWaitHandle; return registeredWaitHandle;
} }
private static unsafe void NativeOverlappedCallback(nint overlappedPtr) => private static unsafe void NativeOverlappedCallback(nint overlappedPtr)
{
if (NativeRuntimeEventSource.Log.IsEnabled())
NativeRuntimeEventSource.Log.ThreadPoolIODequeue((NativeOverlapped*)overlappedPtr);
IOCompletionCallbackHelper.PerformSingleIOCompletionCallback(0, 0, (NativeOverlapped*)overlappedPtr); IOCompletionCallbackHelper.PerformSingleIOCompletionCallback(0, 0, (NativeOverlapped*)overlappedPtr);
}
[SupportedOSPlatform("windows")] [SupportedOSPlatform("windows")]
public static unsafe bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapped) public static unsafe bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapped)
...@@ -200,6 +206,10 @@ public static unsafe bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapp ...@@ -200,6 +206,10 @@ public static unsafe bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapp
// OS doesn't signal handle, so do it here // OS doesn't signal handle, so do it here
overlapped->InternalLow = (IntPtr)0; overlapped->InternalLow = (IntPtr)0;
if (NativeRuntimeEventSource.Log.IsEnabled())
NativeRuntimeEventSource.Log.ThreadPoolIOEnqueue(overlapped);
// Both types of callbacks are executed on the same thread pool // Both types of callbacks are executed on the same thread pool
return ThreadPool.UnsafeQueueUserWorkItem(NativeOverlappedCallback, (nint)overlapped, preferLocal: false); return ThreadPool.UnsafeQueueUserWorkItem(NativeOverlappedCallback, (nint)overlapped, preferLocal: false);
} }
......
...@@ -10,36 +10,52 @@ namespace Tracing.Tests ...@@ -10,36 +10,52 @@ namespace Tracing.Tests
{ {
internal sealed class RuntimeEventListener : EventListener internal sealed class RuntimeEventListener : EventListener
{ {
public volatile int TPWorkerThreadStartCount = 0;
public volatile int TPWorkerThreadStopCount = 0;
public volatile int TPWorkerThreadWaitCount = 0; public volatile int TPWorkerThreadWaitCount = 0;
public volatile int TPIOPack = 0;
public volatile int TPIOEnqueue = 0;
public volatile int TPIODequeue = 0;
public ManualResetEvent TPWaitEvent = new ManualResetEvent(false); public int TPIOPackGoal = 0;
public int TPIOEnqueueGoal = 1;
public int TPIODequeueGoal = 1;
public ManualResetEvent TPWaitWorkerThreadEvent = new ManualResetEvent(false);
public ManualResetEvent TPWaitIOPackEvent = new ManualResetEvent(false);
public ManualResetEvent TPWaitIOEnqueueEvent = new ManualResetEvent(false);
public ManualResetEvent TPWaitIODequeueEvent = new ManualResetEvent(false);
protected override void OnEventSourceCreated(EventSource source) protected override void OnEventSourceCreated(EventSource source)
{ {
if (source.Name.Equals("Microsoft-Windows-DotNETRuntime")) if (source.Name.Equals("Microsoft-Windows-DotNETRuntime"))
{ {
EnableEvents(source, EventLevel.Informational, (EventKeywords)0x10000); EnableEvents(source, EventLevel.Verbose, (EventKeywords)0x10000);
} }
} }
protected override void OnEventWritten(EventWrittenEventArgs eventData) protected override void OnEventWritten(EventWrittenEventArgs eventData)
{ {
if (eventData.EventName.Equals("ThreadPoolWorkerThreadStart")) if (eventData.EventName.Equals("ThreadPoolWorkerThreadWait"))
{ {
Interlocked.Increment(ref TPWorkerThreadStartCount); Interlocked.Increment(ref TPWorkerThreadWaitCount);
TPWaitEvent.Set(); TPWaitWorkerThreadEvent.Set();
} }
else if (eventData.EventName.Equals("ThreadPoolWorkerThreadStop")) else if (eventData.EventName.Equals("ThreadPoolIOPack"))
{ {
Interlocked.Increment(ref TPWorkerThreadStopCount); Interlocked.Increment(ref TPIOPack);
TPWaitEvent.Set(); if (TPIOPack == TPIOPackGoal)
TPWaitIOPackEvent.Set();
} }
else if (eventData.EventName.Equals("ThreadPoolWorkerThreadWait")) else if (eventData.EventName.Equals("ThreadPoolIOEnqueue"))
{ {
Interlocked.Increment(ref TPWorkerThreadWaitCount); Interlocked.Increment(ref TPIOEnqueue);
TPWaitEvent.Set(); if (TPIOEnqueue == TPIOEnqueueGoal)
TPWaitIOEnqueueEvent.Set();
}
else if (eventData.EventName.Equals("ThreadPoolIODequeue"))
{
Interlocked.Increment(ref TPIODequeue);
if (TPIODequeue == TPIODequeueGoal)
TPWaitIODequeueEvent.Set();
} }
} }
} }
...@@ -50,6 +66,7 @@ static int Main() ...@@ -50,6 +66,7 @@ static int Main()
{ {
using (RuntimeEventListener listener = new RuntimeEventListener()) using (RuntimeEventListener listener = new RuntimeEventListener())
{ {
// This should fire at least one ThreadPoolWorkerThreadWait
int someNumber = 0; int someNumber = 0;
Task[] tasks = new Task[100]; Task[] tasks = new Task[100];
for (int i = 0; i < tasks.Length; i++) for (int i = 0; i < tasks.Length; i++)
...@@ -57,23 +74,57 @@ static int Main() ...@@ -57,23 +74,57 @@ static int Main()
tasks[i] = Task.Run(() => { someNumber += 1; }); tasks[i] = Task.Run(() => { someNumber += 1; });
} }
listener.TPWaitEvent.WaitOne(TimeSpan.FromMinutes(3)); if (TestLibrary.Utilities.IsWindows)
{
// This part is Windows-specific, it should fire an IOPack, IOEnqueue and IODequeue event
listener.TPIOPackGoal += 1;
listener.TPIOEnqueueGoal += 1;
listener.TPIODequeueGoal += 1;
Overlapped overlapped = new Overlapped();
unsafe
{
NativeOverlapped* nativeOverlapped = overlapped.Pack(null);
ThreadPool.UnsafeQueueNativeOverlapped(nativeOverlapped);
}
}
// RegisterWaitForSingleObject should fire an IOEnqueue and IODequeue event
ManualResetEvent manualResetEvent = new ManualResetEvent(false);
WaitOrTimerCallback work = (x, timedOut) => { int y = (int)x; };
ThreadPool.RegisterWaitForSingleObject(manualResetEvent, work, 1, 100, true);
manualResetEvent.Set();
ManualResetEvent[] waitEvents = new ManualResetEvent[] {listener.TPWaitIOPackEvent,
listener.TPWaitIOEnqueueEvent,
listener.TPWaitIODequeueEvent};
if (listener.TPWorkerThreadStartCount > 0 || WaitHandle.WaitAll(waitEvents, TimeSpan.FromMinutes(1));
listener.TPWorkerThreadStopCount > 0 ||
listener.TPWorkerThreadWaitCount > 0) if (!TestLibrary.Utilities.IsNativeAot)
{ {
Console.WriteLine("Test Passed."); listener.TPWaitWorkerThreadEvent.WaitOne(TimeSpan.FromMinutes(1));
return 100; if (listener.TPWorkerThreadWaitCount == 0)
{
Console.WriteLine("Test Failed: Did not see the expected event.");
Console.WriteLine($"ThreadPoolWorkerThreadWaitCount: {listener.TPWorkerThreadWaitCount}");
return -1;
}
} }
else
if (!(listener.TPIOPack >= listener.TPIOPackGoal &&
listener.TPIOEnqueue >= listener.TPIOEnqueueGoal &&
listener.TPIODequeue >= listener.TPIODequeueGoal))
{ {
Console.WriteLine("Test Failed: Did not see any of the expected events."); Console.WriteLine("Test Failed: Did not see all of the expected events.");
Console.WriteLine($"ThreadPoolWorkerThreadStartCount: {listener.TPWorkerThreadStartCount}"); Console.WriteLine($"ThreadPoolIOPack: {listener.TPIOPack}");
Console.WriteLine($"ThreadPoolWorkerThreadStopCount: {listener.TPWorkerThreadStopCount}"); Console.WriteLine($"ThreadPoolIOEnqueue: {listener.TPIOEnqueue}");
Console.WriteLine($"ThreadPoolWorkerThreadWaitCount: {listener.TPWorkerThreadWaitCount}"); Console.WriteLine($"ThreadPoolIODequeue: {listener.TPIODequeue}");
return -1; return -1;
} }
Console.WriteLine("Test Passed.");
return 100;
} }
} }
} }
......
...@@ -10,5 +10,6 @@ ...@@ -10,5 +10,6 @@
<ItemGroup> <ItemGroup>
<Compile Include="EventListenerThreadPool.cs" /> <Compile Include="EventListenerThreadPool.cs" />
<ProjectReference Include="../common/common.csproj" /> <ProjectReference Include="../common/common.csproj" />
<ProjectReference Include="$(TestSourceDir)Common\CoreCLRTestLibrary\CoreCLRTestLibrary.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册