Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable IOPack, IOEnqueue, and IODequeue on Windows #88894

Merged
merged 22 commits into from
Aug 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
a8ed540
Testing IOPack
eduardo-vp Jul 13, 2023
8153deb
Add IOEnqueue event
eduardo-vp Jul 14, 2023
ed000ad
Add IODequeue event in NativeOverlappedCallback for Windows Threadpool
eduardo-vp Jul 24, 2023
d3ed46e
Search for ThreadPoolIOPack event
eduardo-vp Jul 25, 2023
bd58b31
Add EventPipeEventThreadPoolIOPack initialization
eduardo-vp Aug 1, 2023
4ae8996
Use EventLevel.Verbose, test fixed
eduardo-vp Aug 1, 2023
9cdf4a5
Add test to NativeAot CI runs + Evaluate WorkerThread events only in …
eduardo-vp Aug 2, 2023
70032d9
Fixed typo in runtime.yml
eduardo-vp Aug 2, 2023
1aee4f9
Fix test
eduardo-vp Aug 3, 2023
0fda41d
Nit: Comment removed
eduardo-vp Aug 3, 2023
951be33
Merge branch 'main' into feature/add-threadpool-events
eduardo-vp Aug 3, 2023
4d13acc
Test IOPack, IOEnqueue and IODequeue
eduardo-vp Aug 3, 2023
37cdadf
Nit: Fixed variable name
eduardo-vp Aug 3, 2023
cb86c96
Used a separate ManualResetEvent
eduardo-vp Aug 3, 2023
432136a
Used different ManualResetEvent for each IOEvent
eduardo-vp Aug 3, 2023
4d86a5e
Update test, test UnsafeQueueNativeOverlapped in Windows only
eduardo-vp Aug 4, 2023
8fb1008
Added events in RegisterWaitHandle and real async IO for Windows thre…
eduardo-vp Aug 5, 2023
2e94127
Test fixed. Still missing why the first part doesn't work on NativeAot
eduardo-vp Aug 5, 2023
e12ce91
PR comments
eduardo-vp Aug 8, 2023
07dd9ee
Merge branch 'main' into feature/add-threadpool-events
eduardo-vp Aug 8, 2023
84de91a
Fire IODequeue before CompleteWithCallback in OnNativeIOCompleted
eduardo-vp Aug 8, 2023
995f189
Fixed comment
eduardo-vp Aug 8, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -48,6 +49,9 @@ internal unsafe RegisteredWaitHandle(SafeWaitHandle waitHandle, _ThreadPoolWaitO
_gcHandle.Free();
throw new OutOfMemoryException();
}

if (NativeRuntimeEventSource.Log.IsEnabled())
NativeRuntimeEventSource.Log.ThreadPoolIOEnqueue(this);
}

#pragma warning disable IDE0060 // Remove unused parameter
Expand Down Expand Up @@ -91,6 +95,9 @@ private void PerformCallbackWindowsThreadPool(bool timedOut)
}
}

if (NativeRuntimeEventSource.Log.IsEnabled())
NativeRuntimeEventSource.Log.ThreadPoolIODequeue(this);

_ThreadPoolWaitOrTimerCallback.PerformWaitOrTimerCallback(_callbackHelper!, timedOut);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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));
eduardo-vp marked this conversation as resolved.
Show resolved Hide resolved

Win32ThreadPoolNativeOverlapped.CompleteWithCallback(ioResult, (uint)numberOfBytesTransferred, overlapped);
ThreadPool.IncrementCompletedWorkItemCount();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -187,8 +188,13 @@ internal static RegisteredWaitHandle RegisterWaitForSingleObject(
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)
Expand All @@ -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);
eduardo-vp marked this conversation as resolved.
Show resolved Hide resolved

// Both types of callbacks are executed on the same thread pool
return ThreadPool.UnsafeQueueUserWorkItem(NativeOverlappedCallback, (nint)overlapped, preferLocal: false);
}
Expand Down
99 changes: 75 additions & 24 deletions src/tests/tracing/eventlistener/EventListenerThreadPool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}
}
Expand All @@ -50,30 +66,65 @@ 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++)
{
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;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@
<ItemGroup>
<Compile Include="EventListenerThreadPool.cs" />
<ProjectReference Include="../common/common.csproj" />
<ProjectReference Include="$(TestSourceDir)Common\CoreCLRTestLibrary\CoreCLRTestLibrary.csproj" />
</ItemGroup>
</Project>