using Microsoft.Win32.SafeHandles;
using System.Diagnostics;
+using System.Diagnostics.Tracing;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
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);
data._boundHandle = this;
+ if (NativeRuntimeEventSource.Log.IsEnabled())
+ NativeRuntimeEventSource.Log.ThreadPoolIOEnqueue(Win32ThreadPoolNativeOverlapped.ToNativeOverlapped(preAllocated._overlappedWindowsThreadPool));
+
Interop.Kernel32.StartThreadpoolIo(_threadPoolHandle!);
return Win32ThreadPoolNativeOverlapped.ToNativeOverlapped(preAllocated._overlappedWindowsThreadPool);
boundHandle.Release();
+ if (NativeRuntimeEventSource.Log.IsEnabled())
+ NativeRuntimeEventSource.Log.ThreadPoolIODequeue(Win32ThreadPoolNativeOverlapped.ToNativeOverlapped(overlapped));
+
Win32ThreadPoolNativeOverlapped.CompleteWithCallback(ioResult, (uint)numberOfBytesTransferred, overlapped);
ThreadPool.IncrementCompletedWorkItemCount();
using Microsoft.Win32.SafeHandles;
using System.Diagnostics;
+using System.Diagnostics.Tracing;
using System.Runtime;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
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)
// 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);
}
{
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();
}
}
}
{
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;
}
}
}