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 18 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
2 changes: 1 addition & 1 deletion eng/pipelines/runtime.yml
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ extends:
extraStepsTemplate: /eng/pipelines/coreclr/nativeaot-post-build-steps.yml
extraStepsParameters:
creator: dotnet-bot
testBuildArgs: 'nativeaot tree ";nativeaot;Loader;Interop;tracing/eventpipe/config;tracing/eventpipe/diagnosticport;tracing/eventpipe/reverse;tracing/eventpipe/processenvironment;tracing/eventpipe/simpleruntimeeventvalidation;tracing/eventpipe/processinfo2;" test tracing/eventcounter/runtimecounters.csproj /p:BuildNativeAotFrameworkObjects=true'
testBuildArgs: 'nativeaot tree ";nativeaot;Loader;Interop;tracing/eventpipe/config;tracing/eventpipe/diagnosticport;tracing/eventpipe/reverse;tracing/eventpipe/processenvironment;tracing/eventpipe/simpleruntimeeventvalidation;tracing/eventpipe/processinfo2;" test tracing/eventcounter/runtimecounters.csproj test tracing/eventlistener/EventListenerThreadPool.csproj /p:BuildNativeAotFrameworkObjects=true'
liveLibrariesBuildConfig: Release
testRunNamePrefixSuffix: NativeAOT_$(_BuildConfig)
extraVariablesTemplates:
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/nativeaot/Runtime/eventpipe/dotnetruntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3122,6 +3122,7 @@ void InitDotNETRuntime(void)
EventPipeEventThreadPoolWorkerThreadAdjustmentStats = EventPipeAdapter::AddEvent(EventPipeProviderDotNETRuntime,56,65536,0,EP_EVENT_LEVEL_VERBOSE,true);
EventPipeEventThreadPoolIOEnqueue = EventPipeAdapter::AddEvent(EventPipeProviderDotNETRuntime,63,2147549184,0,EP_EVENT_LEVEL_VERBOSE,true);
EventPipeEventThreadPoolIODequeue = EventPipeAdapter::AddEvent(EventPipeProviderDotNETRuntime,64,2147549184,0,EP_EVENT_LEVEL_VERBOSE,true);
EventPipeEventThreadPoolIOPack = EventPipeAdapter::AddEvent(EventPipeProviderDotNETRuntime,65,65536,0,EP_EVENT_LEVEL_VERBOSE,true);
EventPipeEventThreadPoolWorkingThreadCount = EventPipeAdapter::AddEvent(EventPipeProviderDotNETRuntime,60,65536,0,EP_EVENT_LEVEL_VERBOSE,true);
EventPipeEventGCAllocationTick_V4 = EventPipeAdapter::AddEvent(EventPipeProviderDotNETRuntime,10,1,4,EP_EVENT_LEVEL_VERBOSE,true);
EventPipeEventGCHeapStats_V2 = EventPipeAdapter::AddEvent(EventPipeProviderDotNETRuntime,4,1,2,EP_EVENT_LEVEL_INFORMATIONAL,false);
Expand Down
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 @@ -43,6 +44,9 @@ internal unsafe RegisteredWaitHandle(SafeWaitHandle waitHandle, _ThreadPoolWaitO

_tpWait = Interop.Kernel32.CreateThreadpoolWait(&RegisteredWaitCallback, (IntPtr)_gcHandle, IntPtr.Zero);

if (NativeRuntimeEventSource.Log.IsEnabled())
NativeRuntimeEventSource.Log.ThreadPoolIOEnqueue(this);
eduardo-vp marked this conversation as resolved.
Show resolved Hide resolved

if (_tpWait == IntPtr.Zero)
{
_gcHandle.Free();
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((NativeOverlapped*)overlapped);
eduardo-vp marked this conversation as resolved.
Show resolved Hide resolved
eduardo-vp marked this conversation as resolved.
Show resolved Hide resolved

Interop.Kernel32.StartThreadpoolIo(_threadPoolHandle!);

return Win32ThreadPoolNativeOverlapped.ToNativeOverlapped(overlapped);
Expand Down Expand Up @@ -157,6 +161,9 @@ private static unsafe void OnNativeIOCompleted(IntPtr instance, IntPtr context,
Win32ThreadPoolNativeOverlapped.CompleteWithCallback(ioResult, (uint)numberOfBytesTransferred, overlapped);
ThreadPool.IncrementCompletedWorkItemCount();

if (NativeRuntimeEventSource.Log.IsEnabled())
NativeRuntimeEventSource.Log.ThreadPoolIODequeue((NativeOverlapped*)overlappedPtr);
eduardo-vp marked this conversation as resolved.
Show resolved Hide resolved

wrapper.Exit();
}

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: 83 additions & 16 deletions src/tests/tracing/eventlistener/EventListenerThreadPool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,24 @@ 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);
}
}

Expand All @@ -29,17 +39,35 @@ protected override void OnEventWritten(EventWrittenEventArgs eventData)
if (eventData.EventName.Equals("ThreadPoolWorkerThreadStart"))
{
Interlocked.Increment(ref TPWorkerThreadStartCount);
TPWaitEvent.Set();
TPWaitWorkerThreadEvent.Set();
}
else if (eventData.EventName.Equals("ThreadPoolWorkerThreadStop"))
{
Interlocked.Increment(ref TPWorkerThreadStopCount);
TPWaitEvent.Set();
TPWaitWorkerThreadEvent.Set();
}
else if (eventData.EventName.Equals("ThreadPoolWorkerThreadWait"))
{
Interlocked.Increment(ref TPWorkerThreadWaitCount);
TPWaitEvent.Set();
TPWaitWorkerThreadEvent.Set();
}
else if (eventData.EventName.Equals("ThreadPoolIOPack"))
{
Interlocked.Increment(ref TPIOPack);
if (TPIOPack == TPIOPackGoal)
TPWaitIOPackEvent.Set();
}
else if (eventData.EventName.Equals("ThreadPoolIOEnqueue"))
{
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 +78,69 @@ static int Main()
{
using (RuntimeEventListener listener = new RuntimeEventListener())
{
// This should fire either of TPWorkerThreadStartCount, TPWorkerThreadStopCount or TPWorkerThreadWaitCount
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) // This test works in CoreCLR only, don't know why yet
eduardo-vp marked this conversation as resolved.
Show resolved Hide resolved
{
Console.WriteLine("Test Passed.");
return 100;
listener.TPWaitWorkerThreadEvent.WaitOne(TimeSpan.FromMinutes(1));
if (listener.TPWorkerThreadStartCount == 0 &&
listener.TPWorkerThreadStopCount == 0 &&
listener.TPWorkerThreadWaitCount == 0)
eduardo-vp marked this conversation as resolved.
Show resolved Hide resolved
{
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}");
return -1;
}
}
else

if (listener.TPIOPack != listener.TPIOPackGoal &&
listener.TPIOEnqueue != listener.TPIOEnqueueGoal &&
listener.TPIODequeue != listener.TPIODequeueGoal)
eduardo-vp marked this conversation as resolved.
Show resolved Hide resolved
{
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>