未验证 提交 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)
#if FEATURE_PERFTRACING
#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())
NativeRuntimeEventSource.Log.ThreadPoolIOPack(pNativeOverlapped);
#endif
#endif
#endif
NativeOverlapped* pRet = pNativeOverlapped;
......
......@@ -3,6 +3,7 @@
using Microsoft.Win32.SafeHandles;
using System.Diagnostics;
using System.Diagnostics.Tracing;
using System.Runtime;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
......@@ -48,6 +49,9 @@ public sealed partial class RegisteredWaitHandle : MarshalByRefObject
_gcHandle.Free();
throw new OutOfMemoryException();
}
if (NativeRuntimeEventSource.Log.IsEnabled())
NativeRuntimeEventSource.Log.ThreadPoolIOEnqueue(this);
}
#pragma warning disable IDE0060 // Remove unused parameter
......@@ -91,6 +95,9 @@ private void PerformCallbackWindowsThreadPool(bool timedOut)
}
}
if (NativeRuntimeEventSource.Log.IsEnabled())
NativeRuntimeEventSource.Log.ThreadPoolIODequeue(this);
_ThreadPoolWaitOrTimerCallback.PerformWaitOrTimerCallback(_callbackHelper!, timedOut);
}
......
......@@ -3,6 +3,7 @@
using Microsoft.Win32.SafeHandles;
using System.Diagnostics;
using System.Diagnostics.Tracing;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
......@@ -54,6 +55,9 @@ private static unsafe ThreadPoolBoundHandle BindHandleWindowsThreadPool(SafeHand
Win32ThreadPoolNativeOverlapped* overlapped = Win32ThreadPoolNativeOverlapped.Allocate(callback, state, pinData, preAllocated: null, flowExecutionContext);
overlapped->Data._boundHandle = this;
if (NativeRuntimeEventSource.Log.IsEnabled())
NativeRuntimeEventSource.Log.ThreadPoolIOEnqueue(Win32ThreadPoolNativeOverlapped.ToNativeOverlapped(overlapped));
Interop.Kernel32.StartThreadpoolIo(_threadPoolHandle!);
return Win32ThreadPoolNativeOverlapped.ToNativeOverlapped(overlapped);
......@@ -82,6 +86,9 @@ private static unsafe ThreadPoolBoundHandle BindHandleWindowsThreadPool(SafeHand
data._boundHandle = this;
if (NativeRuntimeEventSource.Log.IsEnabled())
NativeRuntimeEventSource.Log.ThreadPoolIOEnqueue(Win32ThreadPoolNativeOverlapped.ToNativeOverlapped(preAllocated._overlappedWindowsThreadPool));
Interop.Kernel32.StartThreadpoolIo(_threadPoolHandle!);
return Win32ThreadPoolNativeOverlapped.ToNativeOverlapped(preAllocated._overlappedWindowsThreadPool);
......@@ -154,6 +161,9 @@ private static unsafe void OnNativeIOCompleted(IntPtr instance, IntPtr context,
boundHandle.Release();
if (NativeRuntimeEventSource.Log.IsEnabled())
NativeRuntimeEventSource.Log.ThreadPoolIODequeue(Win32ThreadPoolNativeOverlapped.ToNativeOverlapped(overlapped));
Win32ThreadPoolNativeOverlapped.CompleteWithCallback(ioResult, (uint)numberOfBytesTransferred, overlapped);
ThreadPool.IncrementCompletedWorkItemCount();
......
......@@ -3,6 +3,7 @@
using Microsoft.Win32.SafeHandles;
using System.Diagnostics;
using System.Diagnostics.Tracing;
using System.Runtime;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
......@@ -187,8 +188,13 @@ internal static unsafe void RequestWorkerThread()
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);
}
[SupportedOSPlatform("windows")]
public static unsafe bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapped)
......@@ -200,6 +206,10 @@ public static unsafe bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapp
// OS doesn't signal handle, so do it here
overlapped->InternalLow = (IntPtr)0;
if (NativeRuntimeEventSource.Log.IsEnabled())
NativeRuntimeEventSource.Log.ThreadPoolIOEnqueue(overlapped);
// Both types of callbacks are executed on the same thread pool
return ThreadPool.UnsafeQueueUserWorkItem(NativeOverlappedCallback, (nint)overlapped, preferLocal: false);
}
......
......@@ -10,36 +10,52 @@ namespace Tracing.Tests
{
internal sealed class RuntimeEventListener : EventListener
{
public volatile int TPWorkerThreadStartCount = 0;
public volatile int TPWorkerThreadStopCount = 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)
{
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)
{
if (eventData.EventName.Equals("ThreadPoolWorkerThreadStart"))
if (eventData.EventName.Equals("ThreadPoolWorkerThreadWait"))
{
Interlocked.Increment(ref TPWorkerThreadStartCount);
TPWaitEvent.Set();
Interlocked.Increment(ref TPWorkerThreadWaitCount);
TPWaitWorkerThreadEvent.Set();
}
else if (eventData.EventName.Equals("ThreadPoolWorkerThreadStop"))
else if (eventData.EventName.Equals("ThreadPoolIOPack"))
{
Interlocked.Increment(ref TPWorkerThreadStopCount);
TPWaitEvent.Set();
Interlocked.Increment(ref TPIOPack);
if (TPIOPack == TPIOPackGoal)
TPWaitIOPackEvent.Set();
}
else if (eventData.EventName.Equals("ThreadPoolWorkerThreadWait"))
else if (eventData.EventName.Equals("ThreadPoolIOEnqueue"))
{
Interlocked.Increment(ref TPWorkerThreadWaitCount);
TPWaitEvent.Set();
Interlocked.Increment(ref TPIOEnqueue);
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()
{
using (RuntimeEventListener listener = new RuntimeEventListener())
{
// This should fire at least one ThreadPoolWorkerThreadWait
int someNumber = 0;
Task[] tasks = new Task[100];
for (int i = 0; i < tasks.Length; i++)
......@@ -57,23 +74,57 @@ static int Main()
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 ||
listener.TPWorkerThreadStopCount > 0 ||
listener.TPWorkerThreadWaitCount > 0)
WaitHandle.WaitAll(waitEvents, TimeSpan.FromMinutes(1));
if (!TestLibrary.Utilities.IsNativeAot)
{
Console.WriteLine("Test Passed.");
return 100;
listener.TPWaitWorkerThreadEvent.WaitOne(TimeSpan.FromMinutes(1));
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($"ThreadPoolWorkerThreadStartCount: {listener.TPWorkerThreadStartCount}");
Console.WriteLine($"ThreadPoolWorkerThreadStopCount: {listener.TPWorkerThreadStopCount}");
Console.WriteLine($"ThreadPoolWorkerThreadWaitCount: {listener.TPWorkerThreadWaitCount}");
Console.WriteLine("Test Failed: Did not see all of the expected events.");
Console.WriteLine($"ThreadPoolIOPack: {listener.TPIOPack}");
Console.WriteLine($"ThreadPoolIOEnqueue: {listener.TPIOEnqueue}");
Console.WriteLine($"ThreadPoolIODequeue: {listener.TPIODequeue}");
return -1;
}
Console.WriteLine("Test Passed.");
return 100;
}
}
}
......
......@@ -10,5 +10,6 @@
<ItemGroup>
<Compile Include="EventListenerThreadPool.cs" />
<ProjectReference Include="../common/common.csproj" />
<ProjectReference Include="$(TestSourceDir)Common\CoreCLRTestLibrary\CoreCLRTestLibrary.csproj" />
</ItemGroup>
</Project>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册