diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Overlapped.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Overlapped.cs
index 2f7fd6721e67e7..141aa3a7290791 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Overlapped.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Overlapped.cs
@@ -25,41 +25,6 @@
namespace System.Threading
{
- #region class _IOCompletionCallback
-
- internal sealed unsafe partial class _IOCompletionCallback
- {
- // call back helper
- internal static void PerformIOCompletionCallback(uint errorCode, uint numBytes, NativeOverlapped* pNativeOverlapped)
- {
- do
- {
- OverlappedData overlapped = OverlappedData.GetOverlappedFromNative(pNativeOverlapped);
-
- if (overlapped._callback is IOCompletionCallback iocb)
- {
- // We got here because of UnsafePack (or) Pack with EC flow suppressed
- iocb(errorCode, numBytes, pNativeOverlapped);
- }
- else
- {
- // We got here because of Pack
- var helper = (_IOCompletionCallback?)overlapped._callback;
- Debug.Assert(helper != null, "Should only be receiving a completion callback if a delegate was provided.");
- helper._errorCode = errorCode;
- helper._numBytes = numBytes;
- helper._pNativeOverlapped = pNativeOverlapped;
- ExecutionContext.RunInternal(helper._executionContext, IOCompletionCallback_Context_Delegate, helper);
- }
-
- // Quickly check the VM again, to see if a packet has arrived.
- OverlappedData.CheckVMForIOPacket(out pNativeOverlapped, out errorCode, out numBytes);
- } while (pNativeOverlapped != null);
- }
- }
-
- #endregion class _IOCompletionCallback
-
#region class OverlappedData
internal sealed unsafe class OverlappedData
@@ -120,9 +85,6 @@ internal sealed unsafe class OverlappedData
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern OverlappedData GetOverlappedFromNative(NativeOverlapped* nativeOverlappedPtr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void CheckVMForIOPacket(out NativeOverlapped* pNativeOverlapped, out uint errorCode, out uint numBytes);
}
#endregion class OverlappedData
diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs
index fda212fdf08227..e44eb3b3d74e4c 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs
@@ -356,13 +356,6 @@ internal void ResetThreadPoolThread()
Debug.Assert(this == CurrentThread);
Debug.Assert(IsThreadPoolThread);
- if (!ThreadPool.UsePortableThreadPool)
- {
- // Currently implemented in unmanaged method Thread::InternalReset and
- // called internally from the ThreadPool in NotifyWorkItemComplete.
- return;
- }
-
if (_mayNeedResetForThreadPool)
{
ResetThreadPoolThreadSlow();
diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Windows.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Windows.cs
index 66b91d05c38d36..279abdf1baa204 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Windows.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Windows.cs
@@ -18,32 +18,19 @@ public static unsafe bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapp
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.overlapped);
}
- if (UsePortableThreadPoolForIO)
- {
- // OS doesn't signal handle, so do it here
- overlapped->InternalLow = IntPtr.Zero;
-
- PortableThreadPool.ThreadPoolInstance.QueueNativeOverlapped(overlapped);
- return true;
- }
+ // OS doesn't signal handle, so do it here
+ overlapped->InternalLow = IntPtr.Zero;
- return PostQueuedCompletionStatus(overlapped);
+ PortableThreadPool.ThreadPoolInstance.QueueNativeOverlapped(overlapped);
+ return true;
}
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern unsafe bool PostQueuedCompletionStatus(NativeOverlapped* overlapped);
-
[Obsolete("ThreadPool.BindHandle(IntPtr) has been deprecated. Use ThreadPool.BindHandle(SafeHandle) instead.")]
[SupportedOSPlatform("windows")]
public static bool BindHandle(IntPtr osHandle)
{
- if (UsePortableThreadPoolForIO)
- {
- PortableThreadPool.ThreadPoolInstance.RegisterForIOCompletionNotifications(osHandle);
- return true;
- }
-
- return BindIOCompletionCallbackNative(osHandle);
+ PortableThreadPool.ThreadPoolInstance.RegisterForIOCompletionNotifications(osHandle);
+ return true;
}
[SupportedOSPlatform("windows")]
@@ -56,13 +43,8 @@ public static bool BindHandle(SafeHandle osHandle)
{
osHandle.DangerousAddRef(ref mustReleaseSafeHandle);
- if (UsePortableThreadPoolForIO)
- {
- PortableThreadPool.ThreadPoolInstance.RegisterForIOCompletionNotifications(osHandle.DangerousGetHandle());
- return true;
- }
-
- return BindIOCompletionCallbackNative(osHandle.DangerousGetHandle());
+ PortableThreadPool.ThreadPoolInstance.RegisterForIOCompletionNotifications(osHandle.DangerousGetHandle());
+ return true;
}
finally
{
@@ -70,8 +52,5 @@ public static bool BindHandle(SafeHandle osHandle)
osHandle.DangerousRelease();
}
}
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern bool BindIOCompletionCallbackNative(IntPtr fileHandle);
}
}
diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs
index 0906b97c6f5f35..031ff0cda2c800 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs
@@ -18,122 +18,6 @@
namespace System.Threading
{
- //
- // This type is necessary because VS 2010's debugger looks for a method named _ThreadPoolWaitCallbacck.PerformWaitCallback
- // on the stack to determine if a thread is a ThreadPool thread or not. We have a better way to do this for .NET 4.5, but
- // still need to maintain compatibility with VS 2010. When compat with VS 2010 is no longer an issue, this type may be
- // removed.
- //
- internal static class _ThreadPoolWaitCallback
- {
- internal static bool PerformWaitCallback() => ThreadPoolWorkQueue.Dispatch();
- }
-
- public sealed partial class RegisteredWaitHandle : MarshalByRefObject
- {
- private IntPtr _nativeRegisteredWaitHandle = InvalidHandleValue;
- private bool _releaseHandle;
-
- private static bool IsValidHandle(IntPtr handle) => handle != InvalidHandleValue && handle != IntPtr.Zero;
-
- internal void SetNativeRegisteredWaitHandle(IntPtr nativeRegisteredWaitHandle)
- {
- Debug.Assert(!ThreadPool.UsePortableThreadPool);
- Debug.Assert(IsValidHandle(nativeRegisteredWaitHandle));
- Debug.Assert(!IsValidHandle(_nativeRegisteredWaitHandle));
-
- _nativeRegisteredWaitHandle = nativeRegisteredWaitHandle;
- }
-
- internal void OnBeforeRegister()
- {
- if (ThreadPool.UsePortableThreadPool)
- {
- GC.SuppressFinalize(this);
- return;
- }
-
- Handle.DangerousAddRef(ref _releaseHandle);
- }
-
- ///
- /// Unregisters this wait handle registration from the wait threads.
- ///
- /// The event to signal when the handle is unregistered.
- /// If the handle was successfully marked to be removed and the provided wait handle was set as the user provided event.
- ///
- /// This method will only return true on the first call.
- /// Passing in a wait handle with a value of -1 will result in a blocking wait, where Unregister will not return until the full unregistration is completed.
- ///
- public bool Unregister(WaitHandle waitObject)
- {
- if (ThreadPool.UsePortableThreadPool)
- {
- return UnregisterPortable(waitObject);
- }
-
- s_callbackLock.Acquire();
- try
- {
- if (!IsValidHandle(_nativeRegisteredWaitHandle) ||
- !UnregisterWaitNative(_nativeRegisteredWaitHandle, waitObject?.SafeWaitHandle))
- {
- return false;
- }
- _nativeRegisteredWaitHandle = InvalidHandleValue;
-
- if (_releaseHandle)
- {
- Handle.DangerousRelease();
- _releaseHandle = false;
- }
- }
- finally
- {
- s_callbackLock.Release();
- }
-
- GC.SuppressFinalize(this);
- return true;
- }
-
- ~RegisteredWaitHandle()
- {
- if (ThreadPool.UsePortableThreadPool)
- {
- return;
- }
-
- s_callbackLock.Acquire();
- try
- {
- if (!IsValidHandle(_nativeRegisteredWaitHandle))
- {
- return;
- }
-
- WaitHandleCleanupNative(_nativeRegisteredWaitHandle);
- _nativeRegisteredWaitHandle = InvalidHandleValue;
-
- if (_releaseHandle)
- {
- Handle.DangerousRelease();
- _releaseHandle = false;
- }
- }
- finally
- {
- s_callbackLock.Release();
- }
- }
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void WaitHandleCleanupNative(IntPtr handle);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern bool UnregisterWaitNative(IntPtr handle, SafeHandle? waitObject);
- }
-
internal sealed partial class CompleteWaitThreadPoolWorkItem : IThreadPoolWorkItem
{
void IThreadPoolWorkItem.Execute() => CompleteWait();
@@ -141,7 +25,6 @@ internal sealed partial class CompleteWaitThreadPoolWorkItem : IThreadPoolWorkIt
// Entry point from unmanaged code
private void CompleteWait()
{
- Debug.Assert(ThreadPool.UsePortableThreadPool);
PortableThreadPool.CompleteWait(_registeredWaitHandle, _timedOut);
}
}
@@ -162,24 +45,22 @@ public UnmanagedThreadPoolWorkItem(IntPtr callback, IntPtr state)
public static partial class ThreadPool
{
- private static readonly byte UsePortableThreadPoolConfigValues = InitializeConfigAndDetermineUsePortableThreadPool();
+ internal static bool EnsureConfigInitialized()
+ {
+ return s_initialized;
+ }
- // SOS's ThreadPool command depends on the following names
- internal static readonly bool UsePortableThreadPool = UsePortableThreadPoolConfigValues != 0;
- internal static readonly bool UsePortableThreadPoolForIO = UsePortableThreadPoolConfigValues > 1;
+ private static readonly bool s_initialized = InitializeConfig();
// Indicates whether the thread pool should yield the thread from the dispatch loop to the runtime periodically so that
// the runtime may use the thread for processing other work
- internal static bool YieldFromDispatchLoop => !UsePortableThreadPool;
+ internal static bool YieldFromDispatchLoop => false;
- // This needs to be initialized after UsePortableThreadPool above, as it may depend on UsePortableThreadPool and the
- // config initialization
private static readonly bool IsWorkerTrackingEnabledInConfig = GetEnableWorkerTracking();
- private static unsafe byte InitializeConfigAndDetermineUsePortableThreadPool()
+ private static unsafe bool InitializeConfig()
{
- byte usePortableThreadPoolConfigValues = 0;
- int configVariableIndex = 0;
+ int configVariableIndex = 1;
while (true)
{
int nextConfigVariableIndex =
@@ -196,14 +77,7 @@ private static unsafe byte InitializeConfigAndDetermineUsePortableThreadPool()
Debug.Assert(nextConfigVariableIndex > configVariableIndex);
configVariableIndex = nextConfigVariableIndex;
- if (appContextConfigNameUnsafe == null)
- {
- // Special case for UsePortableThreadPool and similar, which don't go into the AppContext
- Debug.Assert(configValue != 0);
- Debug.Assert(!isBoolean);
- usePortableThreadPoolConfigValues = (byte)configValue;
- continue;
- }
+ Debug.Assert(appContextConfigNameUnsafe != null);
var appContextConfigName = new string(appContextConfigNameUnsafe);
if (isBoolean)
@@ -216,7 +90,7 @@ private static unsafe byte InitializeConfigAndDetermineUsePortableThreadPool()
}
}
- return usePortableThreadPoolConfigValues;
+ return true;
}
[MethodImpl(MethodImplOptions.InternalCall)]
@@ -227,111 +101,31 @@ private static extern unsafe int GetNextConfigUInt32Value(
out char* appContextConfigName);
private static bool GetEnableWorkerTracking() =>
- UsePortableThreadPool
- ? AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.EnableWorkerTracking", false)
- : GetEnableWorkerTrackingNative();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool CanSetMinIOCompletionThreads(int ioCompletionThreads);
-
- internal static void SetMinIOCompletionThreads(int ioCompletionThreads)
- {
- Debug.Assert(UsePortableThreadPool);
- Debug.Assert(!UsePortableThreadPoolForIO);
- Debug.Assert(ioCompletionThreads >= 0);
-
- bool success = SetMinThreadsNative(1, ioCompletionThreads); // worker thread count is ignored
- Debug.Assert(success);
- }
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool CanSetMaxIOCompletionThreads(int ioCompletionThreads);
-
- internal static void SetMaxIOCompletionThreads(int ioCompletionThreads)
- {
- Debug.Assert(UsePortableThreadPool);
- Debug.Assert(!UsePortableThreadPoolForIO);
- Debug.Assert(ioCompletionThreads > 0);
-
- bool success = SetMaxThreadsNative(1, ioCompletionThreads); // worker thread count is ignored
- Debug.Assert(success);
- }
+ AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.EnableWorkerTracking", false);
public static bool SetMaxThreads(int workerThreads, int completionPortThreads)
{
- if (UsePortableThreadPool)
- {
- return PortableThreadPool.ThreadPoolInstance.SetMaxThreads(workerThreads, completionPortThreads);
- }
-
- return
- workerThreads >= 0 &&
- completionPortThreads >= 0 &&
- SetMaxThreadsNative(workerThreads, completionPortThreads);
+ return PortableThreadPool.ThreadPoolInstance.SetMaxThreads(workerThreads, completionPortThreads);
}
public static void GetMaxThreads(out int workerThreads, out int completionPortThreads)
{
- if (UsePortableThreadPoolForIO)
- {
- PortableThreadPool.ThreadPoolInstance.GetMaxThreads(out workerThreads, out completionPortThreads);
- }
- else if (UsePortableThreadPool)
- {
- PortableThreadPool.ThreadPoolInstance.GetMaxThreads(out workerThreads, out _);
- GetMaxThreadsNative(out _, out completionPortThreads);
- }
- else
- {
- GetMaxThreadsNative(out workerThreads, out completionPortThreads);
- }
+ PortableThreadPool.ThreadPoolInstance.GetMaxThreads(out workerThreads, out completionPortThreads);
}
public static bool SetMinThreads(int workerThreads, int completionPortThreads)
{
- if (UsePortableThreadPool)
- {
- return PortableThreadPool.ThreadPoolInstance.SetMinThreads(workerThreads, completionPortThreads);
- }
-
- return
- workerThreads >= 0 &&
- completionPortThreads >= 0 &&
- SetMinThreadsNative(workerThreads, completionPortThreads);
+ return PortableThreadPool.ThreadPoolInstance.SetMinThreads(workerThreads, completionPortThreads);
}
public static void GetMinThreads(out int workerThreads, out int completionPortThreads)
{
- if (UsePortableThreadPoolForIO)
- {
- PortableThreadPool.ThreadPoolInstance.GetMinThreads(out workerThreads, out completionPortThreads);
- }
- else if (UsePortableThreadPool)
- {
- PortableThreadPool.ThreadPoolInstance.GetMinThreads(out workerThreads, out _);
- GetMinThreadsNative(out _, out completionPortThreads);
- }
- else
- {
- GetMinThreadsNative(out workerThreads, out completionPortThreads);
- }
+ PortableThreadPool.ThreadPoolInstance.GetMinThreads(out workerThreads, out completionPortThreads);
}
public static void GetAvailableThreads(out int workerThreads, out int completionPortThreads)
{
- if (UsePortableThreadPoolForIO)
- {
- PortableThreadPool.ThreadPoolInstance.GetAvailableThreads(out workerThreads, out completionPortThreads);
- }
- else if (UsePortableThreadPool)
- {
- PortableThreadPool.ThreadPoolInstance.GetAvailableThreads(out workerThreads, out _);
- GetAvailableThreadsNative(out _, out completionPortThreads);
- }
- else
- {
- GetAvailableThreadsNative(out workerThreads, out completionPortThreads);
- }
+ PortableThreadPool.ThreadPoolInstance.GetAvailableThreads(out workerThreads, out completionPortThreads);
}
///
@@ -344,22 +138,10 @@ public static int ThreadCount
{
get
{
- int count = 0;
- if (UsePortableThreadPool)
- {
- count += PortableThreadPool.ThreadPoolInstance.ThreadCount;
- }
- if (!UsePortableThreadPoolForIO)
- {
- count += GetThreadCount();
- }
- return count;
+ return PortableThreadPool.ThreadPoolInstance.ThreadCount;
}
}
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern int GetThreadCount();
-
///
/// Gets the number of work items that have been processed so far.
///
@@ -370,27 +152,10 @@ public static long CompletedWorkItemCount
{
get
{
- long count = 0;
- if (UsePortableThreadPool)
- {
- count += PortableThreadPool.ThreadPoolInstance.CompletedWorkItemCount;
- }
- if (!UsePortableThreadPoolForIO)
- {
- count += GetCompletedWorkItemCount();
- }
- return count;
+ return PortableThreadPool.ThreadPoolInstance.CompletedWorkItemCount;
}
}
- [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadPool_GetCompletedWorkItemCount")]
- private static partial long GetCompletedWorkItemCount();
-
- private static long PendingUnmanagedWorkItemCount => UsePortableThreadPool ? 0 : GetPendingUnmanagedWorkItemCount();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern long GetPendingUnmanagedWorkItemCount();
-
private static RegisteredWaitHandle RegisterWaitForSingleObject(
WaitHandle waitObject,
WaitOrTimerCallback callBack,
@@ -408,161 +173,43 @@ private static RegisteredWaitHandle RegisterWaitForSingleObject(
(int)millisecondsTimeOutInterval,
!executeOnlyOnce);
- registeredWaitHandle.OnBeforeRegister();
-
- if (UsePortableThreadPool)
- {
- PortableThreadPool.ThreadPoolInstance.RegisterWaitHandle(registeredWaitHandle);
- }
- else
- {
- IntPtr nativeRegisteredWaitHandle =
- RegisterWaitForSingleObjectNative(
- waitObject,
- registeredWaitHandle.Callback,
- (uint)registeredWaitHandle.TimeoutDurationMs,
- !registeredWaitHandle.Repeating,
- registeredWaitHandle);
- registeredWaitHandle.SetNativeRegisteredWaitHandle(nativeRegisteredWaitHandle);
- }
+ PortableThreadPool.ThreadPoolInstance.RegisterWaitHandle(registeredWaitHandle);
return registeredWaitHandle;
}
internal static void RequestWorkerThread()
{
- if (UsePortableThreadPool)
- {
- PortableThreadPool.ThreadPoolInstance.RequestWorker();
- return;
- }
-
- RequestWorkerThreadNative();
- }
-
- [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadPool_RequestWorkerThread")]
- private static partial Interop.BOOL RequestWorkerThreadNative();
-
- // Entry point from unmanaged code
- private static void EnsureGateThreadRunning()
- {
- Debug.Assert(UsePortableThreadPool);
- Debug.Assert(!UsePortableThreadPoolForIO);
-
- PortableThreadPool.EnsureGateThreadRunning();
+ PortableThreadPool.ThreadPoolInstance.RequestWorker();
}
- ///
- /// Called from the gate thread periodically to perform runtime-specific gate activities
- ///
- /// CPU utilization as a percentage since the last call
- /// True if the runtime still needs to perform gate activities, false otherwise
- internal static bool PerformRuntimeSpecificGateActivities(int cpuUtilization)
- {
- Debug.Assert(UsePortableThreadPool);
-
- if (UsePortableThreadPoolForIO)
- {
- return false;
- }
-
- return PerformRuntimeSpecificGateActivitiesNative(cpuUtilization) != Interop.BOOL.FALSE;
- }
-
- [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadPool_PerformGateActivities")]
- private static partial Interop.BOOL PerformRuntimeSpecificGateActivitiesNative(int cpuUtilization);
-
- // Entry point from unmanaged code
- private static void UnsafeQueueUnmanagedWorkItem(IntPtr callback, IntPtr state)
- {
- Debug.Assert(UsePortableThreadPool);
- UnsafeQueueHighPriorityWorkItemInternal(new UnmanagedThreadPoolWorkItem(callback, state));
- }
-
- // Native methods:
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern bool SetMinThreadsNative(int workerThreads, int completionPortThreads);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern bool SetMaxThreadsNative(int workerThreads, int completionPortThreads);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void GetMinThreadsNative(out int workerThreads, out int completionPortThreads);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void GetMaxThreadsNative(out int workerThreads, out int completionPortThreads);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void GetAvailableThreadsNative(out int workerThreads, out int completionPortThreads);
-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static bool NotifyWorkItemComplete(object? threadLocalCompletionCountObject, int currentTimeMs)
{
- if (UsePortableThreadPool)
- {
- return
- PortableThreadPool.ThreadPoolInstance.NotifyWorkItemComplete(
- threadLocalCompletionCountObject,
- currentTimeMs);
- }
-
- return NotifyWorkItemCompleteNative();
+ return
+ PortableThreadPool.ThreadPoolInstance.NotifyWorkItemComplete(
+ threadLocalCompletionCountObject,
+ currentTimeMs);
}
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern bool NotifyWorkItemCompleteNative();
-
internal static void ReportThreadStatus(bool isWorking)
{
- if (UsePortableThreadPool)
- {
- PortableThreadPool.ThreadPoolInstance.ReportThreadStatus(isWorking);
- return;
- }
-
- ReportThreadStatusNative(isWorking);
+ PortableThreadPool.ThreadPoolInstance.ReportThreadStatus(isWorking);
}
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void ReportThreadStatusNative(bool isWorking);
-
internal static void NotifyWorkItemProgress()
{
- if (UsePortableThreadPool)
- {
- PortableThreadPool.ThreadPoolInstance.NotifyWorkItemProgress();
- return;
- }
-
- NotifyWorkItemProgressNative();
+ PortableThreadPool.ThreadPoolInstance.NotifyWorkItemProgress();
}
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void NotifyWorkItemProgressNative();
-
- internal static bool NotifyThreadBlocked() =>
- UsePortableThreadPool && PortableThreadPool.ThreadPoolInstance.NotifyThreadBlocked();
+ internal static bool NotifyThreadBlocked() => PortableThreadPool.ThreadPoolInstance.NotifyThreadBlocked();
internal static void NotifyThreadUnblocked()
{
- Debug.Assert(UsePortableThreadPool);
PortableThreadPool.ThreadPoolInstance.NotifyThreadUnblocked();
}
internal static object? GetOrCreateThreadLocalCompletionCountObject() =>
- UsePortableThreadPool ? PortableThreadPool.ThreadPoolInstance.GetOrCreateThreadLocalCompletionCountObject() : null;
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern bool GetEnableWorkerTrackingNative();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern IntPtr RegisterWaitForSingleObjectNative(
- WaitHandle waitHandle,
- object state,
- uint timeOutInterval,
- bool executeOnlyOnce,
- RegisteredWaitHandle registeredWaitHandle
- );
+ PortableThreadPool.ThreadPoolInstance.GetOrCreateThreadLocalCompletionCountObject();
}
}
diff --git a/src/coreclr/debug/daccess/enummem.cpp b/src/coreclr/debug/daccess/enummem.cpp
index 7db97915d132d1..2503e68718d044 100644
--- a/src/coreclr/debug/daccess/enummem.cpp
+++ b/src/coreclr/debug/daccess/enummem.cpp
@@ -18,7 +18,6 @@
#include "typestring.h"
#include "daccess.h"
#include "binder.h"
-#include "win32threadpool.h"
#include "runtimeinfo.h"
#ifdef FEATURE_COMWRAPPERS
diff --git a/src/coreclr/debug/daccess/request.cpp b/src/coreclr/debug/daccess/request.cpp
index 7e2906cfcae443..c71829f9f6e483 100644
--- a/src/coreclr/debug/daccess/request.cpp
+++ b/src/coreclr/debug/daccess/request.cpp
@@ -10,7 +10,6 @@
//*****************************************************************************
#include "stdafx.h"
-#include
#include "typestring.h"
#include
@@ -268,91 +267,21 @@ VOID GetJITMethodInfo (EECodeInfo * pCodeInfo, JITTypes *pJITType, CLRDATA_ADDRE
}
HRESULT
-ClrDataAccess::GetWorkRequestData(CLRDATA_ADDRESS addr, struct DacpWorkRequestData *workRequestData)
+ClrDataAccess::GetHillClimbingLogEntry(CLRDATA_ADDRESS addr, struct DacpHillClimbingLogEntry* entry)
{
- if (addr == 0 || workRequestData == NULL)
- return E_INVALIDARG;
-
- SOSDacEnter();
-
- WorkRequest *pRequest = PTR_WorkRequest(TO_TADDR(addr));
- workRequestData->Function = (TADDR)(pRequest->Function);
- workRequestData->Context = (TADDR)(pRequest->Context);
- workRequestData->NextWorkRequest = (TADDR)(pRequest->next);
-
- SOSDacLeave();
- return hr;
+ return E_NOTIMPL;
}
HRESULT
-ClrDataAccess::GetHillClimbingLogEntry(CLRDATA_ADDRESS addr, struct DacpHillClimbingLogEntry *entry)
+ClrDataAccess::GetWorkRequestData(CLRDATA_ADDRESS addr, struct DacpWorkRequestData *workRequestData)
{
- if (addr == 0 || entry == NULL)
- return E_INVALIDARG;
-
- SOSDacEnter();
-
- HillClimbingLogEntry *pLogEntry = PTR_HillClimbingLogEntry(TO_TADDR(addr));
- entry->TickCount = pLogEntry->TickCount;
- entry->NewControlSetting = pLogEntry->NewControlSetting;
- entry->LastHistoryCount = pLogEntry->LastHistoryCount;
- entry->LastHistoryMean = pLogEntry->LastHistoryMean;
- entry->Transition = pLogEntry->Transition;
-
- SOSDacLeave();
- return hr;
+ return E_NOTIMPL;
}
HRESULT
ClrDataAccess::GetThreadpoolData(struct DacpThreadpoolData *threadpoolData)
{
- if (threadpoolData == NULL)
- return E_INVALIDARG;
-
- SOSDacEnter();
-
- threadpoolData->cpuUtilization = ThreadpoolMgr::cpuUtilization;
- threadpoolData->MinLimitTotalWorkerThreads = ThreadpoolMgr::MinLimitTotalWorkerThreads;
- threadpoolData->MaxLimitTotalWorkerThreads = ThreadpoolMgr::MaxLimitTotalWorkerThreads;
-
- //
- // Read ThreadpoolMgr::WorkerCounter
- //
- TADDR pCounter = DacGetTargetAddrForHostAddr(&ThreadpoolMgr::WorkerCounter,true);
- ThreadpoolMgr::ThreadCounter counter;
- DacReadAll(pCounter,&counter,sizeof(ThreadpoolMgr::ThreadCounter),true);
- ThreadpoolMgr::ThreadCounter::Counts counts = counter.counts;
-
- threadpoolData->NumWorkingWorkerThreads = counts.NumWorking;
- threadpoolData->NumIdleWorkerThreads = counts.NumActive - counts.NumWorking;
- threadpoolData->NumRetiredWorkerThreads = counts.NumRetired;
-
- threadpoolData->FirstUnmanagedWorkRequest = HOST_CDADDR(ThreadpoolMgr::WorkRequestHead);
-
- threadpoolData->HillClimbingLog = dac_cast(&HillClimbingLog);
- threadpoolData->HillClimbingLogFirstIndex = HillClimbingLogFirstIndex;
- threadpoolData->HillClimbingLogSize = HillClimbingLogSize;
-
-
- //
- // Read ThreadpoolMgr::CPThreadCounter
- //
- pCounter = DacGetTargetAddrForHostAddr(&ThreadpoolMgr::CPThreadCounter,true);
- DacReadAll(pCounter,&counter,sizeof(ThreadpoolMgr::ThreadCounter),true);
- counts = counter.counts;
-
- threadpoolData->NumCPThreads = (LONG)(counts.NumActive + counts.NumRetired);
- threadpoolData->NumFreeCPThreads = (LONG)(counts.NumActive - counts.NumWorking);
- threadpoolData->MaxFreeCPThreads = ThreadpoolMgr::MaxFreeCPThreads;
- threadpoolData->NumRetiredCPThreads = (LONG)(counts.NumRetired);
- threadpoolData->MaxLimitTotalCPThreads = ThreadpoolMgr::MaxLimitTotalCPThreads;
- threadpoolData->CurrentLimitTotalCPThreads = (LONG)(counts.NumActive); //legacy: currently has no meaning
- threadpoolData->MinLimitTotalCPThreads = ThreadpoolMgr::MinLimitTotalCPThreads;
- threadpoolData->NumTimers = 0;
- threadpoolData->AsyncTimerCallbackCompletionFPtr = NULL;
-
- SOSDacLeave();
- return hr;
+ return E_NOTIMPL;
}
HRESULT ClrDataAccess::GetThreadStoreData(struct DacpThreadStoreData *threadStoreData)
diff --git a/src/coreclr/debug/daccess/request_svr.cpp b/src/coreclr/debug/daccess/request_svr.cpp
index d913d0f5938ebb..36d21efb8df121 100644
--- a/src/coreclr/debug/daccess/request_svr.cpp
+++ b/src/coreclr/debug/daccess/request_svr.cpp
@@ -16,7 +16,6 @@
#if defined(FEATURE_SVR_GC)
#include
-#include
#include "request_common.h"
int GCHeapCount()
diff --git a/src/coreclr/debug/ee/dactable.cpp b/src/coreclr/debug/ee/dactable.cpp
index e888d4eda47e9b..6dac100ffb39c4 100644
--- a/src/coreclr/debug/ee/dactable.cpp
+++ b/src/coreclr/debug/ee/dactable.cpp
@@ -13,8 +13,6 @@
#include
#include "../../vm/virtualcallstub.h"
-#include "../../vm/win32threadpool.h"
-#include "../../vm/hillclimbing.h"
#include "../../vm/codeman.h"
#include "../../vm/eedbginterfaceimpl.h"
#include "../../vm/common.h"
diff --git a/src/coreclr/inc/CrstTypes.def b/src/coreclr/inc/CrstTypes.def
index 3d2cdcf8203b18..5ab977844a4deb 100644
--- a/src/coreclr/inc/CrstTypes.def
+++ b/src/coreclr/inc/CrstTypes.def
@@ -292,7 +292,7 @@ End
Crst JumpStubCache
AcquiredBefore ExecuteManRangeLock LoaderHeap SingleUseLock
AcquiredAfter AppDomainCache
- ILStubGen ThreadpoolTimerQueue ThreadpoolWaitThreads
+ ILStubGen
TypeIDMap BaseDomain AssemblyLoader
End
@@ -476,18 +476,6 @@ End
Crst ThreadIdDispenser
End
-Crst ThreadpoolTimerQueue
- AcquiredBefore UniqueStack
-End
-
-Crst ThreadpoolWaitThreads
- AcquiredBefore UniqueStack
-End
-
-Crst ThreadpoolWorker
- AcquiredBefore ThreadIdDispenser ThreadStore
-End
-
Crst ThreadStore
AcquiredBefore AvailableParamTypes DeadlockDetection DebuggerController
DebuggerHeapLock DebuggerJitInfo DynamicIL ExecuteManRangeLock HandleTable IbcProfile
@@ -561,7 +549,7 @@ End
Crst TieredCompilation
AcquiredAfter CodeVersioning
- AcquiredBefore ThreadpoolTimerQueue
+ AcquiredBefore FuncPtrStubs
End
Crst COMCallWrapper
diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h
index ce7d1836a363e9..9d8413f4ec0141 100644
--- a/src/coreclr/inc/clrconfigvalues.h
+++ b/src/coreclr/inc/clrconfigvalues.h
@@ -529,8 +529,6 @@ RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_ProcessorCount, W("PROCESSOR_COUNT"), 0, "S
///
/// Threadpool
///
-RETAIL_CONFIG_DWORD_INFO(INTERNAL_ThreadPool_UsePortableThreadPool, W("ThreadPool_UsePortableThreadPool"), 1, "Uses the managed portable thread pool implementation instead of the unmanaged one.")
-RETAIL_CONFIG_DWORD_INFO(INTERNAL_ThreadPool_UsePortableThreadPoolForIO, W("ThreadPool_UsePortableThreadPoolForIO"), 1, "Uses the managed portable thread pool implementation instead of the unmanaged one for async IO.")
RETAIL_CONFIG_DWORD_INFO(INTERNAL_ThreadPool_ForceMinWorkerThreads, W("ThreadPool_ForceMinWorkerThreads"), 0, "Overrides the MinThreads setting for the ThreadPool worker pool")
RETAIL_CONFIG_DWORD_INFO(INTERNAL_ThreadPool_ForceMaxWorkerThreads, W("ThreadPool_ForceMaxWorkerThreads"), 0, "Overrides the MaxThreads setting for the ThreadPool worker pool")
RETAIL_CONFIG_DWORD_INFO(INTERNAL_ThreadPool_DisableStarvationDetection, W("ThreadPool_DisableStarvationDetection"), 0, "Disables the ThreadPool feature that forces new threads to be added when workitems run for too long")
diff --git a/src/coreclr/inc/crsttypes.h b/src/coreclr/inc/crsttypes.h
index b5e52aef0a4afb..c41a84b50073f6 100644
--- a/src/coreclr/inc/crsttypes.h
+++ b/src/coreclr/inc/crsttypes.h
@@ -120,21 +120,18 @@ enum CrstType
CrstSystemDomain = 102,
CrstSystemDomainDelayedUnloadList = 103,
CrstThreadIdDispenser = 104,
- CrstThreadpoolTimerQueue = 105,
- CrstThreadpoolWaitThreads = 106,
- CrstThreadpoolWorker = 107,
- CrstThreadStore = 108,
- CrstTieredCompilation = 109,
- CrstTypeEquivalenceMap = 110,
- CrstTypeIDMap = 111,
- CrstUMEntryThunkCache = 112,
- CrstUMEntryThunkFreeListLock = 113,
- CrstUniqueStack = 114,
- CrstUnresolvedClassLock = 115,
- CrstUnwindInfoTableLock = 116,
- CrstVSDIndirectionCellLock = 117,
- CrstWrapperTemplate = 118,
- kNumberOfCrstTypes = 119
+ CrstThreadStore = 105,
+ CrstTieredCompilation = 106,
+ CrstTypeEquivalenceMap = 107,
+ CrstTypeIDMap = 108,
+ CrstUMEntryThunkCache = 109,
+ CrstUMEntryThunkFreeListLock = 110,
+ CrstUniqueStack = 111,
+ CrstUnresolvedClassLock = 112,
+ CrstUnwindInfoTableLock = 113,
+ CrstVSDIndirectionCellLock = 114,
+ CrstWrapperTemplate = 115,
+ kNumberOfCrstTypes = 116
};
#endif // __CRST_TYPES_INCLUDED
@@ -250,9 +247,6 @@ int g_rgCrstLevelMap[] =
13, // CrstSystemDomain
0, // CrstSystemDomainDelayedUnloadList
0, // CrstThreadIdDispenser
- 7, // CrstThreadpoolTimerQueue
- 7, // CrstThreadpoolWaitThreads
- 13, // CrstThreadpoolWorker
12, // CrstThreadStore
8, // CrstTieredCompilation
4, // CrstTypeEquivalenceMap
@@ -374,9 +368,6 @@ LPCSTR g_rgCrstNameMap[] =
"CrstSystemDomain",
"CrstSystemDomainDelayedUnloadList",
"CrstThreadIdDispenser",
- "CrstThreadpoolTimerQueue",
- "CrstThreadpoolWaitThreads",
- "CrstThreadpoolWorker",
"CrstThreadStore",
"CrstTieredCompilation",
"CrstTypeEquivalenceMap",
diff --git a/src/coreclr/inc/dacvars.h b/src/coreclr/inc/dacvars.h
index 4e91e8fa189f37..e7a1e8b5e82457 100644
--- a/src/coreclr/inc/dacvars.h
+++ b/src/coreclr/inc/dacvars.h
@@ -100,20 +100,6 @@ DEFINE_DACVAR(PTR_CallCountingStubManager, CallCountingStubManager__g_pManager,
DEFINE_DACVAR(PTR_ThreadStore, ThreadStore__s_pThreadStore, ThreadStore::s_pThreadStore)
-DEFINE_DACVAR(int, ThreadpoolMgr__cpuUtilization, ThreadpoolMgr::cpuUtilization)
-DEFINE_DACVAR(ThreadpoolMgr::ThreadCounter, ThreadpoolMgr__WorkerCounter, ThreadpoolMgr::WorkerCounter)
-DEFINE_DACVAR(int, ThreadpoolMgr__MinLimitTotalWorkerThreads, ThreadpoolMgr::MinLimitTotalWorkerThreads)
-DEFINE_DACVAR(DWORD, ThreadpoolMgr__MaxLimitTotalWorkerThreads, ThreadpoolMgr::MaxLimitTotalWorkerThreads)
-DEFINE_DACVAR(UNKNOWN_POINTER_TYPE /*PTR_WorkRequest*/, ThreadpoolMgr__WorkRequestHead, ThreadpoolMgr::WorkRequestHead) // PTR_WorkRequest is not defined. So use a pointer type
-DEFINE_DACVAR(UNKNOWN_POINTER_TYPE /*PTR_WorkRequest*/, ThreadpoolMgr__WorkRequestTail, ThreadpoolMgr::WorkRequestTail) //
-DEFINE_DACVAR(ThreadpoolMgr::ThreadCounter, ThreadpoolMgr__CPThreadCounter, ThreadpoolMgr::CPThreadCounter)
-DEFINE_DACVAR(LONG, ThreadpoolMgr__MaxFreeCPThreads, ThreadpoolMgr::MaxFreeCPThreads)
-DEFINE_DACVAR(LONG, ThreadpoolMgr__MaxLimitTotalCPThreads, ThreadpoolMgr::MaxLimitTotalCPThreads)
-DEFINE_DACVAR(LONG, ThreadpoolMgr__MinLimitTotalCPThreads, ThreadpoolMgr::MinLimitTotalCPThreads)
-DEFINE_DACVAR_NO_DUMP(SIZE_T, dac__HillClimbingLog, ::HillClimbingLog)
-DEFINE_DACVAR(int, dac__HillClimbingLogFirstIndex, ::HillClimbingLogFirstIndex)
-DEFINE_DACVAR(int, dac__HillClimbingLogSize, ::HillClimbingLogSize)
-
DEFINE_DACVAR(PTR_Thread, dac__g_pFinalizerThread, ::g_pFinalizerThread)
DEFINE_DACVAR(PTR_Thread, dac__g_pSuspensionThread, ::g_pSuspensionThread)
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.NativeAot.cs
index 1811bfad804062..67b66bd0e18484 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.NativeAot.cs
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.NativeAot.cs
@@ -23,7 +23,5 @@ public static partial class ThreadPool
internal static void ReportThreadStatus(bool isWorking)
{
}
-
- private static long PendingUnmanagedWorkItemCount => 0;
}
}
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs
index ba38ba52860115..13bb51c78bd3c6 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs
@@ -426,7 +426,7 @@ public static unsafe bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapp
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.overlapped);
}
- // OS doesn't signal handle, so do it here (CoreCLR does this assignment in ThreadPoolNative::CorPostQueuedCompletionStatus)
+ // OS doesn't signal handle, so do it here
overlapped->InternalLow = (IntPtr)0;
// Both types of callbacks are executed on the same thread pool
return UnsafeQueueUserWorkItem(NativeOverlappedCallback, (nint)overlapped, preferLocal: false);
diff --git a/src/coreclr/pal/inc/pal.h b/src/coreclr/pal/inc/pal.h
index cfb764368b576a..a48d0aab98f2a0 100644
--- a/src/coreclr/pal/inc/pal.h
+++ b/src/coreclr/pal/inc/pal.h
@@ -1270,16 +1270,6 @@ GetExitCodeProcess(
IN HANDLE hProcess,
IN LPDWORD lpExitCode);
-PALIMPORT
-BOOL
-PALAPI
-GetProcessTimes(
- IN HANDLE hProcess,
- OUT LPFILETIME lpCreationTime,
- OUT LPFILETIME lpExitTime,
- OUT LPFILETIME lpKernelTime,
- OUT LPFILETIME lpUserTime);
-
#define MAXIMUM_WAIT_OBJECTS 64
#define WAIT_OBJECT_0 0
#define WAIT_ABANDONED 0x00000080
@@ -4549,23 +4539,6 @@ PALIMPORT DLLEXPORT int __cdecl _putenv(const char *);
PALIMPORT WCHAR __cdecl PAL_ToUpperInvariant(WCHAR);
PALIMPORT WCHAR __cdecl PAL_ToLowerInvariant(WCHAR);
-/******************* PAL-specific I/O completion port *****************/
-
-typedef struct _PAL_IOCP_CPU_INFORMATION {
- union {
- FILETIME ftLastRecordedIdleTime;
- FILETIME ftLastRecordedCurrentTime;
- } LastRecordedTime;
- FILETIME ftLastRecordedKernelTime;
- FILETIME ftLastRecordedUserTime;
-} PAL_IOCP_CPU_INFORMATION;
-
-PALIMPORT
-INT
-PALAPI
-PAL_GetCPUBusyTime(
- IN OUT PAL_IOCP_CPU_INFORMATION *lpPrevCPUInfo);
-
/****************PAL Perf functions for PInvoke*********************/
#if PAL_PERF
PALIMPORT
diff --git a/src/coreclr/pal/src/thread/process.cpp b/src/coreclr/pal/src/thread/process.cpp
index 69c7fe04326a4e..18209de5070071 100644
--- a/src/coreclr/pal/src/thread/process.cpp
+++ b/src/coreclr/pal/src/thread/process.cpp
@@ -1819,249 +1819,6 @@ PAL_GetTransportPipeName(
suffix);
}
-/*++
-Function:
- GetProcessTimes
-
-See MSDN doc.
---*/
-BOOL
-PALAPI
-GetProcessTimes(
- IN HANDLE hProcess,
- OUT LPFILETIME lpCreationTime,
- OUT LPFILETIME lpExitTime,
- OUT LPFILETIME lpKernelTime,
- OUT LPFILETIME lpUserTime)
-{
- BOOL retval = FALSE;
- struct rusage resUsage;
- UINT64 calcTime;
- const UINT64 SECS_TO_100NS = 10000000ULL; // 10^7
- const UINT64 USECS_TO_100NS = 10ULL; // 10
- const UINT64 EPOCH_DIFF = 11644473600ULL; // number of seconds from 1 Jan. 1601 00:00 to 1 Jan 1970 00:00 UTC
-
- PERF_ENTRY(GetProcessTimes);
- ENTRY("GetProcessTimes(hProcess=%p, lpExitTime=%p, lpKernelTime=%p,"
- "lpUserTime=%p)\n",
- hProcess, lpCreationTime, lpExitTime, lpKernelTime, lpUserTime );
-
- /* Make sure hProcess is the current process, this is the only supported
- case */
- if(PROCGetProcessIDFromHandle(hProcess)!=GetCurrentProcessId())
- {
- ASSERT("GetProcessTimes() does not work on a process other than the "
- "current process.\n");
- SetLastError(ERROR_INVALID_HANDLE);
- goto GetProcessTimesExit;
- }
-
- /* First, we need to actually retrieve the relevant statistics from the
- OS */
- if (getrusage (RUSAGE_SELF, &resUsage) == -1)
- {
- ASSERT("Unable to get resource usage information for the current "
- "process\n");
- SetLastError(ERROR_INTERNAL_ERROR);
- goto GetProcessTimesExit;
- }
-
- TRACE ("getrusage User: %ld sec,%ld microsec. Kernel: %ld sec,%ld"
- " microsec\n",
- resUsage.ru_utime.tv_sec, resUsage.ru_utime.tv_usec,
- resUsage.ru_stime.tv_sec, resUsage.ru_stime.tv_usec);
-
- if (lpCreationTime)
- {
- // The IBC profile data uses this, instead of the actual
- // process creation time we just return the current time
-
- struct timeval tv;
- if (gettimeofday(&tv, NULL) == -1)
- {
- ASSERT("gettimeofday() failed; errno is %d (%s)\n", errno, strerror(errno));
-
- // Assign zero to lpCreationTime
- lpCreationTime->dwLowDateTime = 0;
- lpCreationTime->dwHighDateTime = 0;
- }
- else
- {
- calcTime = EPOCH_DIFF;
- calcTime += (UINT64)tv.tv_sec;
- calcTime *= SECS_TO_100NS;
- calcTime += ((UINT64)tv.tv_usec * USECS_TO_100NS);
-
- // Assign the time into lpCreationTime
- lpCreationTime->dwLowDateTime = (DWORD)calcTime;
- lpCreationTime->dwHighDateTime = (DWORD)(calcTime >> 32);
- }
- }
-
- if (lpExitTime)
- {
- // Assign zero to lpExitTime
- lpExitTime->dwLowDateTime = 0;
- lpExitTime->dwHighDateTime = 0;
- }
-
- if (lpUserTime)
- {
- /* Get the time of user mode execution, in 100s of nanoseconds */
- calcTime = (UINT64)resUsage.ru_utime.tv_sec * SECS_TO_100NS;
- calcTime += (UINT64)resUsage.ru_utime.tv_usec * USECS_TO_100NS;
-
- /* Assign the time into lpUserTime */
- lpUserTime->dwLowDateTime = (DWORD)calcTime;
- lpUserTime->dwHighDateTime = (DWORD)(calcTime >> 32);
- }
-
- if (lpKernelTime)
- {
- /* Get the time of kernel mode execution, in 100s of nanoseconds */
- calcTime = (UINT64)resUsage.ru_stime.tv_sec * SECS_TO_100NS;
- calcTime += (UINT64)resUsage.ru_stime.tv_usec * USECS_TO_100NS;
-
- /* Assign the time into lpUserTime */
- lpKernelTime->dwLowDateTime = (DWORD)calcTime;
- lpKernelTime->dwHighDateTime = (DWORD)(calcTime >> 32);
- }
-
- retval = TRUE;
-
-
-GetProcessTimesExit:
- LOGEXIT("GetProcessTimes returns BOOL %d\n", retval);
- PERF_EXIT(GetProcessTimes);
- return (retval);
-}
-
-#define FILETIME_TO_ULONGLONG(f) \
- (((ULONGLONG)(f).dwHighDateTime << 32) | ((ULONGLONG)(f).dwLowDateTime))
-
-/*++
-Function:
- PAL_GetCPUBusyTime
-
-The main purpose of this function is to compute the overall CPU utilization
-for the CLR thread pool to regulate the number of I/O completion port
-worker threads.
-Since there is no consistent API on Unix to get the CPU utilization
-from a user process, getrusage and gettimeofday are used to
-compute the current process's CPU utilization instead.
-This function emulates the ThreadpoolMgr::GetCPUBusyTime_NT function in
-win32threadpool.cpp of the CLR.
-
-See MSDN doc for GetSystemTimes.
---*/
-INT
-PALAPI
-PAL_GetCPUBusyTime(
- IN OUT PAL_IOCP_CPU_INFORMATION *lpPrevCPUInfo)
-{
- ULONGLONG nLastRecordedCurrentTime = 0;
- ULONGLONG nLastRecordedUserTime = 0;
- ULONGLONG nLastRecordedKernelTime = 0;
- ULONGLONG nKernelTime = 0;
- ULONGLONG nUserTime = 0;
- ULONGLONG nCurrentTime = 0;
- ULONGLONG nCpuBusyTime = 0;
- ULONGLONG nCpuTotalTime = 0;
- DWORD nReading = 0;
- struct rusage resUsage;
- struct timeval tv;
- static DWORD dwNumberOfProcessors = 0;
-
- if (dwNumberOfProcessors <= 0)
- {
- SYSTEM_INFO SystemInfo;
- GetSystemInfo(&SystemInfo);
- dwNumberOfProcessors = SystemInfo.dwNumberOfProcessors;
- if (dwNumberOfProcessors <= 0)
- {
- return 0;
- }
-
- UINT cpuLimit;
- if (PAL_GetCpuLimit(&cpuLimit) && cpuLimit < dwNumberOfProcessors)
- {
- dwNumberOfProcessors = cpuLimit;
- }
- }
-
- if (getrusage(RUSAGE_SELF, &resUsage) == -1)
- {
- ASSERT("getrusage() failed; errno is %d (%s)\n", errno, strerror(errno));
- return 0;
- }
- else
- {
- nKernelTime = (ULONGLONG)resUsage.ru_stime.tv_sec*tccSecondsTo100NanoSeconds +
- resUsage.ru_stime.tv_usec*tccMicroSecondsTo100NanoSeconds;
- nUserTime = (ULONGLONG)resUsage.ru_utime.tv_sec*tccSecondsTo100NanoSeconds +
- resUsage.ru_utime.tv_usec*tccMicroSecondsTo100NanoSeconds;
- }
-
- if (gettimeofday(&tv, NULL) == -1)
- {
- ASSERT("gettimeofday() failed; errno is %d (%s)\n", errno, strerror(errno));
- return 0;
- }
- else
- {
- nCurrentTime = (ULONGLONG)tv.tv_sec*tccSecondsTo100NanoSeconds +
- tv.tv_usec*tccMicroSecondsTo100NanoSeconds;
- }
-
- nLastRecordedCurrentTime = FILETIME_TO_ULONGLONG(lpPrevCPUInfo->LastRecordedTime.ftLastRecordedCurrentTime);
- nLastRecordedUserTime = FILETIME_TO_ULONGLONG(lpPrevCPUInfo->ftLastRecordedUserTime);
- nLastRecordedKernelTime = FILETIME_TO_ULONGLONG(lpPrevCPUInfo->ftLastRecordedKernelTime);
-
- if (nCurrentTime > nLastRecordedCurrentTime)
- {
- nCpuTotalTime = (nCurrentTime - nLastRecordedCurrentTime);
-#if HAVE_THREAD_SELF || HAVE__LWP_SELF || HAVE_VM_READ
- // For systems that run multiple threads of a process on multiple processors,
- // the accumulated userTime and kernelTime of this process may exceed
- // the elapsed time. In this case, the cpuTotalTime needs to be adjusted
- // according to number of processors so that the cpu utilization
- // will not be greater than 100.
- nCpuTotalTime *= dwNumberOfProcessors;
-#endif // HAVE_THREAD_SELF || HAVE__LWP_SELF || HAVE_VM_READ
- }
-
- if (nUserTime >= nLastRecordedUserTime &&
- nKernelTime >= nLastRecordedKernelTime)
- {
- nCpuBusyTime =
- (nUserTime - nLastRecordedUserTime)+
- (nKernelTime - nLastRecordedKernelTime);
- }
-
- if (nCpuTotalTime > 0 && nCpuBusyTime > 0)
- {
- nReading = (DWORD)((nCpuBusyTime*100)/nCpuTotalTime);
- TRACE("PAL_GetCPUBusyTime: nCurrentTime=%lld, nKernelTime=%lld, nUserTime=%lld, nReading=%d\n",
- nCurrentTime, nKernelTime, nUserTime, nReading);
- }
-
- if (nReading > 100)
- {
- ERROR("cpu utilization(%d) > 100\n", nReading);
- }
-
- lpPrevCPUInfo->LastRecordedTime.ftLastRecordedCurrentTime.dwLowDateTime = (DWORD)nCurrentTime;
- lpPrevCPUInfo->LastRecordedTime.ftLastRecordedCurrentTime.dwHighDateTime = (DWORD)(nCurrentTime >> 32);
-
- lpPrevCPUInfo->ftLastRecordedUserTime.dwLowDateTime = (DWORD)nUserTime;
- lpPrevCPUInfo->ftLastRecordedUserTime.dwHighDateTime = (DWORD)(nUserTime >> 32);
-
- lpPrevCPUInfo->ftLastRecordedKernelTime.dwLowDateTime = (DWORD)nKernelTime;
- lpPrevCPUInfo->ftLastRecordedKernelTime.dwHighDateTime = (DWORD)(nKernelTime >> 32);
-
- return (DWORD)nReading;
-}
-
/*++
Function:
GetCommandLineW
diff --git a/src/coreclr/pal/tests/palsuite/CMakeLists.txt b/src/coreclr/pal/tests/palsuite/CMakeLists.txt
index 4cef1073a89cbe..c88f4effb42ee4 100644
--- a/src/coreclr/pal/tests/palsuite/CMakeLists.txt
+++ b/src/coreclr/pal/tests/palsuite/CMakeLists.txt
@@ -848,7 +848,6 @@ add_executable_clr(paltests
threading/GetCurrentThreadId/test1/threadId.cpp
threading/GetExitCodeProcess/test1/childProcess.cpp
threading/GetExitCodeProcess/test1/test1.cpp
- threading/GetProcessTimes/test2/test2.cpp
threading/GetThreadTimes/test1/test1.cpp
threading/NamedMutex/test1/namedmutex.cpp
threading/NamedMutex/test1/nopal.cpp
diff --git a/src/coreclr/pal/tests/palsuite/compilableTests.txt b/src/coreclr/pal/tests/palsuite/compilableTests.txt
index 7f2d2804c41331..1cd7c5b964395b 100644
--- a/src/coreclr/pal/tests/palsuite/compilableTests.txt
+++ b/src/coreclr/pal/tests/palsuite/compilableTests.txt
@@ -738,7 +738,6 @@ threading/GetCurrentThread/test1/paltest_getcurrentthread_test1
threading/GetCurrentThread/test2/paltest_getcurrentthread_test2
threading/GetCurrentThreadId/test1/paltest_getcurrentthreadid_test1
threading/GetExitCodeProcess/test1/paltest_getexitcodeprocess_test1
-threading/GetProcessTimes/test2/paltest_getprocesstimes_test2
threading/GetThreadTimes/test1/paltest_getthreadtimes_test1
threading/NamedMutex/test1/paltest_namedmutex_test1
threading/OpenEventW/test1/paltest_openeventw_test1
diff --git a/src/coreclr/pal/tests/palsuite/paltestlist.txt b/src/coreclr/pal/tests/palsuite/paltestlist.txt
index 95cf097c591930..607f8c7d686417 100644
--- a/src/coreclr/pal/tests/palsuite/paltestlist.txt
+++ b/src/coreclr/pal/tests/palsuite/paltestlist.txt
@@ -649,7 +649,6 @@ threading/ExitThread/test1/paltest_exitthread_test1
threading/GetCurrentProcessId/test1/paltest_getcurrentprocessid_test1
threading/GetCurrentThread/test1/paltest_getcurrentthread_test1
threading/GetCurrentThread/test2/paltest_getcurrentthread_test2
-threading/GetProcessTimes/test2/paltest_getprocesstimes_test2
threading/GetThreadTimes/test1/paltest_getthreadtimes_test1
threading/NamedMutex/test1/paltest_namedmutex_test1
threading/QueryThreadCycleTime/test1/paltest_querythreadcycletime_test1
diff --git a/src/coreclr/pal/tests/palsuite/threading/GetProcessTimes/test2/test2.cpp b/src/coreclr/pal/tests/palsuite/threading/GetProcessTimes/test2/test2.cpp
deleted file mode 100644
index 7c1c7a10f65691..00000000000000
--- a/src/coreclr/pal/tests/palsuite/threading/GetProcessTimes/test2/test2.cpp
+++ /dev/null
@@ -1,121 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-/*=============================================================================
-**
-** Source: test2.c
-**
-** Purpose: Test to ensure GetProcessTimes works properly.
-**
-** Dependencies: PAL_Initialize
-** PAL_Terminate
-** Fail
-** ZeroMemory
-** CompareFileTime
-** GetLastError
-**
-
-**
-**===========================================================================*/
-#include
-
-
-PALTEST(threading_GetProcessTimes_test2_paltest_getprocesstimes_test2, "threading/GetProcessTimes/test2/paltest_getprocesstimes_test2")
-
-{
- int i, j, k;
- int *total;
-
- HANDLE hProcess;
- FILETIME createTime;
- FILETIME exitTime;
- FILETIME kernelTime1;
- FILETIME userTime1;
- FILETIME kernelTime2;
- FILETIME userTime2;
-
- DWORD dwError;
-
- /* initialize the PAL */
- if( PAL_Initialize(argc, argv) != 0 )
- {
- return( FAIL );
- }
-
- /* get our own process handle */
- hProcess = GetCurrentProcess();
- if( hProcess == NULL )
- {
- Fail( "GetCurrentProcess() returned a NULL handle.\n" );
- }
-
- /* zero our time structures */
- ZeroMemory( &createTime, sizeof(createTime) );
- ZeroMemory( &exitTime, sizeof(exitTime) );
- ZeroMemory( &kernelTime1, sizeof(kernelTime1) );
- ZeroMemory( &userTime1, sizeof(userTime1) );
- ZeroMemory( &kernelTime2, sizeof(kernelTime2) );
- ZeroMemory( &userTime2, sizeof(userTime2) );
-
- /* check the process times for the child process */
- if( ! GetProcessTimes( hProcess,
- &createTime,
- &exitTime,
- &kernelTime1,
- &userTime1 ) )
- {
- dwError = GetLastError();
- Fail( "GetProcessTimes() call failed with error code %d\n",
- dwError );
- }
-
-
- /* simulate some activity */
- for( i=0; i<1000; i++ )
- {
- for( j=0; j<1000; j++ )
- {
- /* do kernel work to increase system usage counters */
- total = (int*)malloc(1024 * 1024);
-
- *total = j * i;
- for( k=0; k<1000; k++ )
- {
- *total += k + i;
- }
-
- free(total);
- }
- }
-
- /* check the process times for the child process */
- if( ! GetProcessTimes( hProcess,
- &createTime,
- &exitTime,
- &kernelTime2,
- &userTime2 ) )
- {
- dwError = GetLastError();
- Fail( "GetProcessTimes() call failed with error code %d\n",
- dwError );
- }
-
-
- /* very simple logical checking of the results */
- if( CompareFileTime( &kernelTime1, &kernelTime2 ) > 0 )
- {
- Fail( "Unexpected kernel time value reported.\n" );
- }
-
- if( CompareFileTime( &userTime1, &userTime2 ) > 0 )
- {
- Fail( "Unexpected user time value reported.\n" );
- }
-
-
- /* terminate the PAL */
- PAL_Terminate();
-
- /* return success */
- return PASS;
-}
diff --git a/src/coreclr/vm/CMakeLists.txt b/src/coreclr/vm/CMakeLists.txt
index b5b9afb48b40e5..c59090a44f4405 100644
--- a/src/coreclr/vm/CMakeLists.txt
+++ b/src/coreclr/vm/CMakeLists.txt
@@ -82,7 +82,6 @@ set(VM_SOURCES_DAC_AND_WKS_COMMON
genericdict.cpp
generics.cpp
hash.cpp
- hillclimbing.cpp
ilinstrumentation.cpp
ilstubcache.cpp
ilstubresolver.cpp
@@ -116,7 +115,6 @@ set(VM_SOURCES_DAC_AND_WKS_COMMON
stublink.cpp
stubmgr.cpp
syncblk.cpp
- threadpoolrequest.cpp
threads.cpp
threadstatics.cpp
tieredcompilation.cpp
@@ -130,7 +128,6 @@ set(VM_SOURCES_DAC_AND_WKS_COMMON
vars.cpp
versionresilienthashcode.cpp
virtualcallstub.cpp
- win32threadpool.cpp
zapsig.cpp
)
@@ -180,7 +177,6 @@ set(VM_HEADERS_DAC_AND_WKS_COMMON
gcheaputilities.h
generics.h
hash.h
- hillclimbing.h
ilinstrumentation.h
ilstubcache.h
ilstubresolver.h
@@ -222,7 +218,6 @@ set(VM_HEADERS_DAC_AND_WKS_COMMON
stubmgr.h
syncblk.h
syncblk.inl
- threadpoolrequest.h
threads.h
threads.inl
threadstatics.h
@@ -239,7 +234,6 @@ set(VM_HEADERS_DAC_AND_WKS_COMMON
vars.hpp
versionresilienthashcode.h
virtualcallstub.h
- win32threadpool.h
zapsig.h
)
diff --git a/src/coreclr/vm/appdomain.cpp b/src/coreclr/vm/appdomain.cpp
index 39a02a3ffdd639..a08e5e0494e1fe 100644
--- a/src/coreclr/vm/appdomain.cpp
+++ b/src/coreclr/vm/appdomain.cpp
@@ -51,7 +51,6 @@
#include "appdomain.inl"
#include "typeparse.h"
-#include "threadpoolrequest.h"
#include "nativeoverlapped.h"
@@ -962,8 +961,6 @@ void SystemDomain::Attach()
CallCountingStubManager::Init();
#endif
- PerAppDomainTPCountList::InitAppDomainIndexList();
-
m_SystemDomainCrst.Init(CrstSystemDomain, (CrstFlags)(CRST_REENTRANCY | CRST_TAKEN_DURING_SHUTDOWN));
m_DelayedUnloadCrst.Init(CrstSystemDomainDelayedUnloadList, CRST_UNSAFE_COOPGC);
@@ -1913,14 +1910,7 @@ AppDomain::~AppDomain()
}
CONTRACTL_END;
-
- // release the TPIndex. note that since TPIndex values are recycled the TPIndex
- // can only be released once all threads in the AppDomain have exited.
- if (GetTPIndex().m_dwIndex != 0)
- PerAppDomainTPCountList::ResetAppDomainIndex(GetTPIndex());
-
m_AssemblyCache.Clear();
-
}
//*****************************************************************************
@@ -1938,11 +1928,6 @@ void AppDomain::Init()
SetStage( STAGE_CREATING);
- //Allocate the threadpool entry before the appdomain id list. Otherwise,
- //the thread pool list will be out of sync if insertion of id in
- //the appdomain fails.
- m_tpIndex = PerAppDomainTPCountList::AddNewTPIndex();
-
BaseDomain::Init();
// Set up the binding caches
diff --git a/src/coreclr/vm/appdomain.hpp b/src/coreclr/vm/appdomain.hpp
index 5798301810d2d0..d82018c67506ba 100644
--- a/src/coreclr/vm/appdomain.hpp
+++ b/src/coreclr/vm/appdomain.hpp
@@ -1956,12 +1956,6 @@ class AppDomain : public BaseDomain
RCWRefCache *GetRCWRefCache();
#endif // FEATURE_COMWRAPPERS
- TPIndex GetTPIndex()
- {
- LIMITED_METHOD_CONTRACT;
- return m_tpIndex;
- }
-
DefaultAssemblyBinder *CreateDefaultBinder();
void SetIgnoreUnhandledExceptions()
@@ -2210,9 +2204,6 @@ class AppDomain : public BaseDomain
RCWRefCache *m_pRCWRefCache;
#endif // FEATURE_COMWRAPPERS
- // The thread-pool index of this app domain among existing app domains (starting from 1)
- TPIndex m_tpIndex;
-
Volatile m_Stage;
ArrayList m_failedAssemblies;
diff --git a/src/coreclr/vm/ceemain.cpp b/src/coreclr/vm/ceemain.cpp
index 4094b4ff28db83..b3a161260114ac 100644
--- a/src/coreclr/vm/ceemain.cpp
+++ b/src/coreclr/vm/ceemain.cpp
@@ -175,8 +175,6 @@
#include "stacksampler.h"
#endif
-#include "win32threadpool.h"
-
#include
#ifdef FEATURE_COMINTEROP
@@ -644,7 +642,6 @@ void EEStartupHelper()
IfFailGo(ExecutableAllocator::StaticInitialize(FatalErrorHandler));
Thread::StaticInitialize();
- ThreadpoolMgr::StaticInitialize();
JITInlineTrackingMap::StaticInitialize();
MethodDescBackpatchInfoTracker::StaticInitialize();
diff --git a/src/coreclr/vm/comcache.cpp b/src/coreclr/vm/comcache.cpp
index c9afbbed923dd9..573ead1a96e355 100644
--- a/src/coreclr/vm/comcache.cpp
+++ b/src/coreclr/vm/comcache.cpp
@@ -9,7 +9,6 @@
#include "comcache.h"
#include "runtimecallablewrapper.h"
#include
-#include "win32threadpool.h"
#ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT
#include "olecontexthelpers.h"
diff --git a/src/coreclr/vm/comthreadpool.cpp b/src/coreclr/vm/comthreadpool.cpp
index 88094ff5323555..2d941b130991de 100644
--- a/src/coreclr/vm/comthreadpool.cpp
+++ b/src/coreclr/vm/comthreadpool.cpp
@@ -1,126 +1,10 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-
-/*============================================================
-**
-** Header: COMThreadPool.cpp
-**
-** Purpose: Native methods on System.ThreadPool
-** and its inner classes
-**
-**
-===========================================================*/
-
-/********************************************************************************************************************/
#include "common.h"
-#include "comdelegate.h"
+
#include "comthreadpool.h"
-#include "threadpoolrequest.h"
-#include "win32threadpool.h"
-#include "class.h"
-#include "object.h"
-#include "field.h"
-#include "excep.h"
#include "eeconfig.h"
-#include "corhost.h"
-#include "nativeoverlapped.h"
-#include "comsynchronizable.h"
-#include "callhelpers.h"
-#include "appdomain.inl"
-/*****************************************************************************************************/
-#ifdef _DEBUG
-void LogCall(MethodDesc* pMD, LPCUTF8 api)
-{
- LIMITED_METHOD_CONTRACT;
-
- LPCUTF8 cls = pMD->GetMethodTable()->GetDebugClassName();
- LPCUTF8 name = pMD->GetName();
-
- LOG((LF_THREADPOOL,LL_INFO1000,"%s: ", api));
- LOG((LF_THREADPOOL, LL_INFO1000,
- " calling %s.%s\n", cls, name));
-}
-#else
-#define LogCall(pMd,api)
-#endif
-
-VOID
-AcquireDelegateInfo(DelegateInfo *pDelInfo)
-{
- LIMITED_METHOD_CONTRACT;
-}
-
-VOID
-ReleaseDelegateInfo(DelegateInfo *pDelInfo)
-{
- CONTRACTL
- {
- NOTHROW;
- GC_TRIGGERS;
- MODE_ANY;
- } CONTRACTL_END;
-
- // The release methods of holders can be called with preemptive GC enabled. Ensure we're in cooperative mode
- // before calling pDelInfo->Release(), since that requires coop mode.
- GCX_COOP();
-
- pDelInfo->Release();
- ThreadpoolMgr::RecycleMemory( pDelInfo, ThreadpoolMgr::MEMTYPE_DelegateInfo );
-}
-
-//typedef Holder DelegateInfoHolder;
-
-typedef Wrapper DelegateInfoHolder;
-
-/*****************************************************************************************************/
-// Caller has to GC protect Objectrefs being passed in
-DelegateInfo *DelegateInfo::MakeDelegateInfo(OBJECTREF *state,
- OBJECTREF *waitEvent,
- OBJECTREF *registeredWaitHandle)
-{
- CONTRACTL
- {
- THROWS;
- GC_TRIGGERS;
- if (state != NULL || waitEvent != NULL || registeredWaitHandle != NULL)
- {
- MODE_COOPERATIVE;
- }
- else
- {
- MODE_ANY;
- }
- PRECONDITION(state == NULL || IsProtectedByGCFrame(state));
- PRECONDITION(waitEvent == NULL || IsProtectedByGCFrame(waitEvent));
- PRECONDITION(registeredWaitHandle == NULL || IsProtectedByGCFrame(registeredWaitHandle));
- INJECT_FAULT(COMPlusThrowOM());
- }
- CONTRACTL_END;
-
- DelegateInfoHolder delegateInfo = (DelegateInfo*) ThreadpoolMgr::GetRecycledMemory(ThreadpoolMgr::MEMTYPE_DelegateInfo);
-
- AppDomain* pAppDomain = ::GetAppDomain();
-
- if (state != NULL)
- delegateInfo->m_stateHandle = pAppDomain->CreateHandle(*state);
- else
- delegateInfo->m_stateHandle = NULL;
-
- if (waitEvent != NULL)
- delegateInfo->m_eventHandle = pAppDomain->CreateHandle(*waitEvent);
- else
- delegateInfo->m_eventHandle = NULL;
-
- if (registeredWaitHandle != NULL)
- delegateInfo->m_registeredWaitHandle = pAppDomain->CreateHandle(*registeredWaitHandle);
- else
- delegateInfo->m_registeredWaitHandle = NULL;
-
- delegateInfo.SuppressRelease();
-
- return delegateInfo;
-}
/*****************************************************************************************************/
// Enumerates some runtime config variables that are used by CoreLib for initialization. The config variable index should start
@@ -138,14 +22,6 @@ FCIMPL4(INT32, ThreadPoolNative::GetNextConfigUInt32Value,
_ASSERTE(isBooleanRef != NULL);
_ASSERTE(appContextConfigNameRef != NULL);
- if (!ThreadpoolMgr::UsePortableThreadPool())
- {
- *configValueRef = 0;
- *isBooleanRef = false;
- *appContextConfigNameRef = NULL;
- return -1;
- }
-
auto TryGetConfig =
[=](const CLRConfig::ConfigDWORDInfo &configInfo, bool isBoolean, const WCHAR *appContextConfigName) -> bool
{
@@ -163,13 +39,6 @@ FCIMPL4(INT32, ThreadPoolNative::GetNextConfigUInt32Value,
switch (configVariableIndex)
{
- case 0:
- // Special case for UsePortableThreadPool and similar, which don't go into the AppContext
- *configValueRef = ThreadpoolMgr::UsePortableThreadPoolForIO() ? 2 : 1;
- *isBooleanRef = false;
- *appContextConfigNameRef = NULL;
- return 1;
-
case 1: if (TryGetConfig(CLRConfig::INTERNAL_ThreadPool_ForceMinWorkerThreads, false, W("System.Threading.ThreadPool.MinThreads"))) { return 2; } FALLTHROUGH;
case 2: if (TryGetConfig(CLRConfig::INTERNAL_ThreadPool_ForceMaxWorkerThreads, false, W("System.Threading.ThreadPool.MaxThreads"))) { return 3; } FALLTHROUGH;
case 3: if (TryGetConfig(CLRConfig::INTERNAL_ThreadPool_DisableStarvationDetection, true, W("System.Threading.ThreadPool.DisableStarvationDetection"))) { return 4; } FALLTHROUGH;
@@ -200,655 +69,3 @@ FCIMPL4(INT32, ThreadPoolNative::GetNextConfigUInt32Value,
}
}
FCIMPLEND
-
-/*****************************************************************************************************/
-FCIMPL1(FC_BOOL_RET, ThreadPoolNative::CorCanSetMinIOCompletionThreads, DWORD ioCompletionThreads)
-{
- FCALL_CONTRACT;
- _ASSERTE_ALL_BUILDS(ThreadpoolMgr::UsePortableThreadPool());
-
- BOOL result = ThreadpoolMgr::CanSetMinIOCompletionThreads(ioCompletionThreads);
- FC_RETURN_BOOL(result);
-}
-FCIMPLEND
-
-/*****************************************************************************************************/
-FCIMPL1(FC_BOOL_RET, ThreadPoolNative::CorCanSetMaxIOCompletionThreads, DWORD ioCompletionThreads)
-{
- FCALL_CONTRACT;
- _ASSERTE_ALL_BUILDS(ThreadpoolMgr::UsePortableThreadPool());
-
- BOOL result = ThreadpoolMgr::CanSetMaxIOCompletionThreads(ioCompletionThreads);
- FC_RETURN_BOOL(result);
-}
-FCIMPLEND
-
-/*****************************************************************************************************/
-FCIMPL2(FC_BOOL_RET, ThreadPoolNative::CorSetMaxThreads,DWORD workerThreads, DWORD completionPortThreads)
-{
- FCALL_CONTRACT;
- _ASSERTE_ALL_BUILDS(!ThreadpoolMgr::UsePortableThreadPoolForIO());
-
- BOOL bRet = FALSE;
- HELPER_METHOD_FRAME_BEGIN_RET_0();
-
- bRet = ThreadpoolMgr::SetMaxThreads(workerThreads,completionPortThreads);
- HELPER_METHOD_FRAME_END();
- FC_RETURN_BOOL(bRet);
-}
-FCIMPLEND
-
-/*****************************************************************************************************/
-FCIMPL2(VOID, ThreadPoolNative::CorGetMaxThreads,DWORD* workerThreads, DWORD* completionPortThreads)
-{
- FCALL_CONTRACT;
- _ASSERTE_ALL_BUILDS(!ThreadpoolMgr::UsePortableThreadPoolForIO());
-
- ThreadpoolMgr::GetMaxThreads(workerThreads,completionPortThreads);
- return;
-}
-FCIMPLEND
-
-/*****************************************************************************************************/
-FCIMPL2(FC_BOOL_RET, ThreadPoolNative::CorSetMinThreads,DWORD workerThreads, DWORD completionPortThreads)
-{
- FCALL_CONTRACT;
- _ASSERTE_ALL_BUILDS(!ThreadpoolMgr::UsePortableThreadPoolForIO());
-
- BOOL bRet = FALSE;
- HELPER_METHOD_FRAME_BEGIN_RET_0();
-
- bRet = ThreadpoolMgr::SetMinThreads(workerThreads,completionPortThreads);
- HELPER_METHOD_FRAME_END();
- FC_RETURN_BOOL(bRet);
-}
-FCIMPLEND
-
-/*****************************************************************************************************/
-FCIMPL2(VOID, ThreadPoolNative::CorGetMinThreads,DWORD* workerThreads, DWORD* completionPortThreads)
-{
- FCALL_CONTRACT;
- _ASSERTE_ALL_BUILDS(!ThreadpoolMgr::UsePortableThreadPoolForIO());
-
- ThreadpoolMgr::GetMinThreads(workerThreads,completionPortThreads);
- return;
-}
-FCIMPLEND
-
-/*****************************************************************************************************/
-FCIMPL2(VOID, ThreadPoolNative::CorGetAvailableThreads,DWORD* workerThreads, DWORD* completionPortThreads)
-{
- FCALL_CONTRACT;
- _ASSERTE_ALL_BUILDS(!ThreadpoolMgr::UsePortableThreadPoolForIO());
-
- ThreadpoolMgr::GetAvailableThreads(workerThreads,completionPortThreads);
- return;
-}
-FCIMPLEND
-
-/*****************************************************************************************************/
-FCIMPL0(INT32, ThreadPoolNative::GetThreadCount)
-{
- FCALL_CONTRACT;
- _ASSERTE_ALL_BUILDS(!ThreadpoolMgr::UsePortableThreadPoolForIO());
-
- return ThreadpoolMgr::GetThreadCount();
-}
-FCIMPLEND
-
-/*****************************************************************************************************/
-extern "C" INT64 QCALLTYPE ThreadPool_GetCompletedWorkItemCount()
-{
- QCALL_CONTRACT;
- _ASSERTE_ALL_BUILDS(!ThreadpoolMgr::UsePortableThreadPoolForIO());
-
- INT64 result = 0;
-
- BEGIN_QCALL;
-
- result = (INT64)Thread::GetTotalThreadPoolCompletionCount();
-
- END_QCALL;
- return result;
-}
-
-/*****************************************************************************************************/
-FCIMPL0(INT64, ThreadPoolNative::GetPendingUnmanagedWorkItemCount)
-{
- FCALL_CONTRACT;
- _ASSERTE_ALL_BUILDS(!ThreadpoolMgr::UsePortableThreadPool());
-
- return PerAppDomainTPCountList::GetUnmanagedTPCount()->GetNumRequests();
-}
-FCIMPLEND
-
-/*****************************************************************************************************/
-
-FCIMPL0(VOID, ThreadPoolNative::NotifyRequestProgress)
-{
- FCALL_CONTRACT;
- _ASSERTE_ALL_BUILDS(!ThreadpoolMgr::UsePortableThreadPool());
- _ASSERTE(ThreadpoolMgr::IsInitialized()); // can't be here without requesting a thread first
-
- ThreadpoolMgr::NotifyWorkItemCompleted();
-
- if (ThreadpoolMgr::ShouldAdjustMaxWorkersActive())
- {
- DangerousNonHostedSpinLockTryHolder tal(&ThreadpoolMgr::ThreadAdjustmentLock);
- if (tal.Acquired())
- {
- HELPER_METHOD_FRAME_BEGIN_0();
- ThreadpoolMgr::AdjustMaxWorkersActive();
- HELPER_METHOD_FRAME_END();
- }
- else
- {
- // the lock is held by someone else, so they will take care of this for us.
- }
- }
-}
-FCIMPLEND
-
-FCIMPL1(VOID, ThreadPoolNative::ReportThreadStatus, CLR_BOOL isWorking)
-{
- FCALL_CONTRACT;
- _ASSERTE_ALL_BUILDS(!ThreadpoolMgr::UsePortableThreadPool());
-
- ThreadpoolMgr::ReportThreadStatus(isWorking);
-}
-FCIMPLEND
-
-FCIMPL0(FC_BOOL_RET, ThreadPoolNative::NotifyRequestComplete)
-{
- FCALL_CONTRACT;
- _ASSERTE_ALL_BUILDS(!ThreadpoolMgr::UsePortableThreadPool());
- _ASSERTE(ThreadpoolMgr::IsInitialized()); // can't be here without requesting a thread first
-
- ThreadpoolMgr::NotifyWorkItemCompleted();
-
- //
- // Now we need to possibly do one or both of: reset the thread's state, and/or perform a
- // "worker thread adjustment" (i.e., invoke Hill Climbing). We try to avoid these at all costs,
- // because they require an expensive helper method frame. So we first try a minimal thread reset,
- // then check if it covered everything that was needed, and we ask ThreadpoolMgr whether
- // we need a thread adjustment, before setting up the frame.
- //
- Thread *pThread = GetThread();
-
- INT32 priority = pThread->ResetManagedThreadObjectInCoopMode(ThreadNative::PRIORITY_NORMAL);
-
- bool needReset =
- priority != ThreadNative::PRIORITY_NORMAL ||
- !pThread->IsBackground();
-
- bool shouldAdjustWorkers = ThreadpoolMgr::ShouldAdjustMaxWorkersActive();
-
- //
- // If it's time for a thread adjustment, try to get the lock. This is just a "try," it won't block,
- // so it's ok to do this in cooperative mode. If we can't get the lock, then some other thread is
- // already doing the thread adjustment, so we needn't bother.
- //
- DangerousNonHostedSpinLockTryHolder tal(&ThreadpoolMgr::ThreadAdjustmentLock, shouldAdjustWorkers);
- if (!tal.Acquired())
- shouldAdjustWorkers = false;
-
- if (needReset || shouldAdjustWorkers)
- {
- HELPER_METHOD_FRAME_BEGIN_RET_0();
-
- if (shouldAdjustWorkers)
- {
- ThreadpoolMgr::AdjustMaxWorkersActive();
- tal.Release();
- }
-
- if (needReset)
- pThread->InternalReset(TRUE, TRUE, FALSE);
-
- HELPER_METHOD_FRAME_END();
- }
-
- //
- // Finally, ask ThreadpoolMgr whether it's ok to keep running work on this thread. Maybe Hill Climbing
- // wants this thread back.
- //
- BOOL result = ThreadpoolMgr::ShouldWorkerKeepRunning() ? TRUE : FALSE;
- FC_RETURN_BOOL(result);
-}
-FCIMPLEND
-
-
-/*****************************************************************************************************/
-
-FCIMPL0(FC_BOOL_RET, ThreadPoolNative::GetEnableWorkerTracking)
-{
- FCALL_CONTRACT;
- _ASSERTE_ALL_BUILDS(!ThreadpoolMgr::UsePortableThreadPool());
-
- BOOL result = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_ThreadPool_EnableWorkerTracking) ? TRUE : FALSE;
- FC_RETURN_BOOL(result);
-}
-FCIMPLEND
-
-/*****************************************************************************************************/
-
-struct RegisterWaitForSingleObjectCallback_Args
-{
- DelegateInfo *delegateInfo;
- BOOLEAN TimerOrWaitFired;
-};
-
-static VOID
-RegisterWaitForSingleObjectCallback_Worker(LPVOID ptr)
-{
- CONTRACTL
- {
- GC_TRIGGERS;
- THROWS;
- MODE_COOPERATIVE;
- }
- CONTRACTL_END;
-
- OBJECTREF orState = NULL;
-
- GCPROTECT_BEGIN( orState );
-
- RegisterWaitForSingleObjectCallback_Args *args = (RegisterWaitForSingleObjectCallback_Args *) ptr;
- orState = ObjectFromHandle(((DelegateInfo*) args->delegateInfo)->m_stateHandle);
-
-#ifdef _DEBUG
- MethodDesc *pMeth = CoreLibBinder::GetMethod(METHOD__TPWAITORTIMER_HELPER__PERFORM_WAITORTIMER_CALLBACK);
- LogCall(pMeth,"RWSOCallback");
-#endif
-
- // Caution: the args are not protected, we have to garantee there's no GC from here till
- // the managed call happens.
- PREPARE_NONVIRTUAL_CALLSITE(METHOD__TPWAITORTIMER_HELPER__PERFORM_WAITORTIMER_CALLBACK);
- DECLARE_ARGHOLDER_ARRAY(arg, 2);
- arg[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(orState);
- arg[ARGNUM_1] = DWORD_TO_ARGHOLDER(args->TimerOrWaitFired);
-
- // Call the method...
- CALL_MANAGED_METHOD_NORET(arg);
-
- GCPROTECT_END();
-}
-
-VOID NTAPI RegisterWaitForSingleObjectCallback(PVOID delegateInfo, BOOLEAN TimerOrWaitFired)
-{
- Thread* pThread = GetThreadNULLOk();
- if (pThread == NULL)
- {
- ClrFlsSetThreadType(ThreadType_Threadpool_Worker);
- pThread = SetupThreadNoThrow();
- if (pThread == NULL) {
- return;
- }
- }
-
- CONTRACTL
- {
- MODE_PREEMPTIVE; // Worker thread will be in preempt mode. We switch to coop below.
- THROWS;
- GC_TRIGGERS;
-
- PRECONDITION(CheckPointer(delegateInfo));
- }
- CONTRACTL_END;
-
- GCX_COOP();
-
- RegisterWaitForSingleObjectCallback_Args args = { ((DelegateInfo*) delegateInfo), TimerOrWaitFired };
-
- ManagedThreadBase::ThreadPool(RegisterWaitForSingleObjectCallback_Worker, &args);
-}
-
-FCIMPL5(LPVOID, ThreadPoolNative::CorRegisterWaitForSingleObject,
- Object* waitObjectUNSAFE,
- Object* stateUNSAFE,
- UINT32 timeout,
- CLR_BOOL executeOnlyOnce,
- Object* registeredWaitObjectUNSAFE)
-{
- FCALL_CONTRACT;
- _ASSERTE_ALL_BUILDS(!ThreadpoolMgr::UsePortableThreadPool());
-
- HANDLE handle = 0;
- struct _gc
- {
- WAITHANDLEREF waitObject;
- OBJECTREF state;
- OBJECTREF registeredWaitObject;
- } gc;
- gc.waitObject = (WAITHANDLEREF) ObjectToOBJECTREF(waitObjectUNSAFE);
- gc.state = (OBJECTREF) stateUNSAFE;
- gc.registeredWaitObject = (OBJECTREF) registeredWaitObjectUNSAFE;
- HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
-
- if(gc.waitObject == NULL)
- COMPlusThrow(kArgumentNullException);
-
- _ASSERTE(gc.registeredWaitObject != NULL);
-
- ULONG flag = executeOnlyOnce ? WAIT_SINGLE_EXECUTION | WAIT_FREE_CONTEXT : WAIT_FREE_CONTEXT;
-
- HANDLE hWaitHandle = gc.waitObject->GetWaitHandle();
- _ASSERTE(hWaitHandle);
-
- Thread* pCurThread = GetThread();
-
- DelegateInfoHolder delegateInfo = DelegateInfo::MakeDelegateInfo(
- &gc.state,
- (OBJECTREF *)&gc.waitObject,
- &gc.registeredWaitObject);
-
- if (!(ThreadpoolMgr::RegisterWaitForSingleObject(&handle,
- hWaitHandle,
- RegisterWaitForSingleObjectCallback,
- (PVOID) delegateInfo,
- (ULONG) timeout,
- flag)))
-
- {
- _ASSERTE(GetLastError() != ERROR_CALL_NOT_IMPLEMENTED);
-
- COMPlusThrowWin32();
- }
-
- delegateInfo.SuppressRelease();
- HELPER_METHOD_FRAME_END();
- return (LPVOID) handle;
-}
-FCIMPLEND
-
-VOID QueueUserWorkItemManagedCallback(PVOID pArg)
-{
- CONTRACTL
- {
- GC_TRIGGERS;
- THROWS;
- MODE_COOPERATIVE;
- } CONTRACTL_END;
-
- _ASSERTE(NULL != pArg);
-
- bool* wasNotRecalled = (bool*)pArg;
-
- MethodDescCallSite dispatch(METHOD__TP_WAIT_CALLBACK__PERFORM_WAIT_CALLBACK);
- *wasNotRecalled = dispatch.Call_RetBool(NULL);
-}
-
-extern "C" BOOL QCALLTYPE ThreadPool_RequestWorkerThread()
-{
- QCALL_CONTRACT;
-
- BOOL res = FALSE;
-
- BEGIN_QCALL;
-
- _ASSERTE_ALL_BUILDS(!ThreadpoolMgr::UsePortableThreadPool());
-
- ThreadpoolMgr::EnsureInitialized();
- ThreadpoolMgr::SetAppDomainRequestsActive();
-
- res = ThreadpoolMgr::QueueUserWorkItem(NULL,
- NULL,
- 0,
- FALSE);
- if (!res)
- {
- if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
- COMPlusThrow(kNotSupportedException);
- else
- COMPlusThrowWin32();
- }
-
- END_QCALL;
- return res;
-}
-
-extern "C" BOOL QCALLTYPE ThreadPool_PerformGateActivities(INT32 cpuUtilization)
-{
- QCALL_CONTRACT;
-
- bool needGateThread = false;
-
- BEGIN_QCALL;
-
- _ASSERTE_ALL_BUILDS(ThreadpoolMgr::UsePortableThreadPool() && !ThreadpoolMgr::UsePortableThreadPoolForIO());
-
- ThreadpoolMgr::PerformGateActivities(cpuUtilization);
- needGateThread = ThreadpoolMgr::NeedGateThreadForIOCompletions();
-
- END_QCALL;
-
- return needGateThread;
-}
-
-/********************************************************************************************************************/
-
-FCIMPL2(FC_BOOL_RET, ThreadPoolNative::CorUnregisterWait, LPVOID WaitHandle, Object* objectToNotify)
-{
- FCALL_CONTRACT;
- _ASSERTE_ALL_BUILDS(!ThreadpoolMgr::UsePortableThreadPool());
-
- BOOL retVal = false;
- SAFEHANDLEREF refSH = (SAFEHANDLEREF) ObjectToOBJECTREF(objectToNotify);
- HELPER_METHOD_FRAME_BEGIN_RET_1(refSH);
-
- HANDLE hWait = (HANDLE) WaitHandle;
- HANDLE hObjectToNotify = NULL;
-
- ThreadpoolMgr::WaitInfo *pWaitInfo = (ThreadpoolMgr::WaitInfo *)hWait;
- _ASSERTE(pWaitInfo != NULL);
-
- ThreadpoolMgr::WaitInfoHolder wiHolder(NULL);
-
- if (refSH != NULL)
- {
- // Create a GCHandle in the WaitInfo, so that it can hold on to the safe handle
- pWaitInfo->ExternalEventSafeHandle = GetAppDomain()->CreateHandle(NULL);
-
- // Holder will now release objecthandle in face of exceptions
- wiHolder.Assign(pWaitInfo);
-
- // Store SafeHandle in object handle. Holder will now release both safehandle and objecthandle
- // in case of exceptions
- StoreObjectInHandle(pWaitInfo->ExternalEventSafeHandle, refSH);
-
- // Acquire safe handle to examine its handle, then release.
- SafeHandleHolder shHolder(&refSH);
-
- if (refSH->GetHandle() == INVALID_HANDLE_VALUE)
- {
- hObjectToNotify = INVALID_HANDLE_VALUE;
- // We do not need the ObjectHandle, refcount on the safehandle etc
- wiHolder.Release();
- _ASSERTE(pWaitInfo->ExternalEventSafeHandle == NULL);
- }
- }
-
- _ASSERTE(hObjectToNotify == NULL || hObjectToNotify == INVALID_HANDLE_VALUE);
-
- // When hObjectToNotify is NULL ExternalEventSafeHandle contains event to notify (if it is non NULL).
- // When hObjectToNotify is INVALID_HANDLE_VALUE, UnregisterWaitEx blocks until dispose is complete.
- retVal = ThreadpoolMgr::UnregisterWaitEx(hWait, hObjectToNotify);
-
- if (retVal)
- wiHolder.SuppressRelease();
-
- HELPER_METHOD_FRAME_END();
- FC_RETURN_BOOL(retVal);
-}
-FCIMPLEND
-
-/********************************************************************************************************************/
-FCIMPL1(void, ThreadPoolNative::CorWaitHandleCleanupNative, LPVOID WaitHandle)
-{
- FCALL_CONTRACT;
- _ASSERTE_ALL_BUILDS(!ThreadpoolMgr::UsePortableThreadPool());
-
- HELPER_METHOD_FRAME_BEGIN_0();
-
- HANDLE hWait = (HANDLE)WaitHandle;
- ThreadpoolMgr::WaitHandleCleanup(hWait);
-
- HELPER_METHOD_FRAME_END();
-}
-FCIMPLEND
-
-/********************************************************************************************************************/
-
-struct BindIoCompletion_Args
-{
- DWORD ErrorCode;
- DWORD numBytesTransferred;
- LPOVERLAPPED lpOverlapped;
-};
-
-VOID BindIoCompletionCallBack_Worker(LPVOID args)
-{
- STATIC_CONTRACT_THROWS;
- STATIC_CONTRACT_GC_TRIGGERS;
- STATIC_CONTRACT_MODE_ANY;
-
- DWORD ErrorCode = ((BindIoCompletion_Args *)args)->ErrorCode;
- DWORD numBytesTransferred = ((BindIoCompletion_Args *)args)->numBytesTransferred;
- LPOVERLAPPED lpOverlapped = ((BindIoCompletion_Args *)args)->lpOverlapped;
-
- OVERLAPPEDDATAREF overlapped = ObjectToOVERLAPPEDDATAREF(OverlappedDataObject::GetOverlapped(lpOverlapped));
-
- GCPROTECT_BEGIN(overlapped);
- // we set processed to TRUE, now it's our responsibility to guarantee proper cleanup
-
-#ifdef _DEBUG
- MethodDesc *pMeth = CoreLibBinder::GetMethod(METHOD__IOCB_HELPER__PERFORM_IOCOMPLETION_CALLBACK);
- LogCall(pMeth,"IOCallback");
-#endif
-
- if (overlapped->m_callback != NULL)
- {
- // Caution: the args are not protected, we have to garantee there's no GC from here till
- PREPARE_NONVIRTUAL_CALLSITE(METHOD__IOCB_HELPER__PERFORM_IOCOMPLETION_CALLBACK);
- DECLARE_ARGHOLDER_ARRAY(arg, 3);
- arg[ARGNUM_0] = DWORD_TO_ARGHOLDER(ErrorCode);
- arg[ARGNUM_1] = DWORD_TO_ARGHOLDER(numBytesTransferred);
- arg[ARGNUM_2] = PTR_TO_ARGHOLDER(lpOverlapped);
-
- // Call the method...
- CALL_MANAGED_METHOD_NORET(arg);
- }
- else
- {
- // no user delegate to callback
- _ASSERTE((overlapped->m_callback == NULL) || !"This is benign, but should be optimized");
- }
- GCPROTECT_END();
-}
-
-void __stdcall BindIoCompletionCallbackStubEx(DWORD ErrorCode,
- DWORD numBytesTransferred,
- LPOVERLAPPED lpOverlapped,
- BOOL setStack)
-{
- Thread* pThread = GetThreadNULLOk();
- if (pThread == NULL)
- {
- // TODO: how do we notify user of OOM here?
- ClrFlsSetThreadType(ThreadType_Threadpool_Worker);
- pThread = SetupThreadNoThrow();
- if (pThread == NULL) {
- return;
- }
- }
-
- CONTRACTL
- {
- THROWS;
- MODE_ANY;
- GC_TRIGGERS;
- }
- CONTRACTL_END;
-
- LOG((LF_INTEROP, LL_INFO10000, "In IO_CallBackStub thread 0x%x retCode 0x%x, overlap 0x%x\n", pThread, ErrorCode, lpOverlapped));
-
- GCX_COOP();
-
- BindIoCompletion_Args args = {ErrorCode, numBytesTransferred, lpOverlapped};
- ManagedThreadBase::ThreadPool(BindIoCompletionCallBack_Worker, &args);
-
- LOG((LF_INTEROP, LL_INFO10000, "Leaving IO_CallBackStub thread 0x%x retCode 0x%x, overlap 0x%x\n", pThread, ErrorCode, lpOverlapped));
-}
-
-void WINAPI BindIoCompletionCallbackStub(DWORD ErrorCode,
- DWORD numBytesTransferred,
- LPOVERLAPPED lpOverlapped)
-{
- WRAPPER_NO_CONTRACT;
- BindIoCompletionCallbackStubEx(ErrorCode, numBytesTransferred, lpOverlapped, TRUE);
-}
-
-FCIMPL1(FC_BOOL_RET, ThreadPoolNative::CorBindIoCompletionCallback, HANDLE fileHandle)
-{
- FCALL_CONTRACT;
- _ASSERTE_ALL_BUILDS(!ThreadpoolMgr::UsePortableThreadPoolForIO());
-
- BOOL retVal = FALSE;
-
- HELPER_METHOD_FRAME_BEGIN_RET_0();
-
- HANDLE hFile = (HANDLE) fileHandle;
- DWORD errCode = 0;
-
- retVal = ThreadpoolMgr::BindIoCompletionCallback(hFile,
- BindIoCompletionCallbackStub,
- 0, // reserved, must be 0
- OUT errCode);
- if (!retVal)
- {
- if (errCode == ERROR_CALL_NOT_IMPLEMENTED)
- COMPlusThrow(kPlatformNotSupportedException);
- else
- {
- SetLastError(errCode);
- COMPlusThrowWin32();
- }
- }
-
- HELPER_METHOD_FRAME_END();
- FC_RETURN_BOOL(retVal);
-}
-FCIMPLEND
-
-FCIMPL1(FC_BOOL_RET, ThreadPoolNative::CorPostQueuedCompletionStatus, LPOVERLAPPED lpOverlapped)
-{
- FCALL_CONTRACT;
- _ASSERTE_ALL_BUILDS(!ThreadpoolMgr::UsePortableThreadPoolForIO());
-
- OVERLAPPEDDATAREF overlapped = ObjectToOVERLAPPEDDATAREF(OverlappedDataObject::GetOverlapped(lpOverlapped));
-
- BOOL res = FALSE;
-
- HELPER_METHOD_FRAME_BEGIN_RET_1(overlapped);
-
- // OS doesn't signal handle, so do it here
- lpOverlapped->Internal = 0;
-
- if (ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context, ThreadPoolIOEnqueue))
- FireEtwThreadPoolIOEnqueue(lpOverlapped, OBJECTREFToObject(overlapped), false, GetClrInstanceId());
-
- res = ThreadpoolMgr::PostQueuedCompletionStatus(lpOverlapped,
- BindIoCompletionCallbackStub);
-
- if (!res)
- {
- if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
- COMPlusThrow(kPlatformNotSupportedException);
- else
- COMPlusThrowWin32();
- }
-
- HELPER_METHOD_FRAME_END();
- FC_RETURN_BOOL(res);
-}
-FCIMPLEND
diff --git a/src/coreclr/vm/comthreadpool.h b/src/coreclr/vm/comthreadpool.h
index 46d9fda34d857b..e3ad28c01c0c17 100644
--- a/src/coreclr/vm/comthreadpool.h
+++ b/src/coreclr/vm/comthreadpool.h
@@ -1,73 +1,17 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-
-/*============================================================
-**
-** Header: COMThreadPool.h
-**
-** Purpose: Native methods on System.ThreadPool
-** and its inner classes
-**
-**
-===========================================================*/
-
#ifndef _COMTHREADPOOL_H
#define _COMTHREADPOOL_H
-#include "delegateinfo.h"
-#include "nativeoverlapped.h"
-
class ThreadPoolNative
{
-
public:
static FCDECL4(INT32, GetNextConfigUInt32Value,
INT32 configVariableIndex,
UINT32 *configValueRef,
BOOL *isBooleanRef,
LPCWSTR *appContextConfigNameRef);
- static FCDECL1(FC_BOOL_RET, CorCanSetMinIOCompletionThreads, DWORD ioCompletionThreads);
- static FCDECL1(FC_BOOL_RET, CorCanSetMaxIOCompletionThreads, DWORD ioCompletionThreads);
- static FCDECL2(FC_BOOL_RET, CorSetMaxThreads, DWORD workerThreads, DWORD completionPortThreads);
- static FCDECL2(VOID, CorGetMaxThreads, DWORD* workerThreads, DWORD* completionPortThreads);
- static FCDECL2(FC_BOOL_RET, CorSetMinThreads, DWORD workerThreads, DWORD completionPortThreads);
- static FCDECL2(VOID, CorGetMinThreads, DWORD* workerThreads, DWORD* completionPortThreads);
- static FCDECL2(VOID, CorGetAvailableThreads, DWORD* workerThreads, DWORD* completionPortThreads);
- static FCDECL0(INT32, GetThreadCount);
- static FCDECL0(INT64, GetPendingUnmanagedWorkItemCount);
-
- static FCDECL0(VOID, NotifyRequestProgress);
- static FCDECL0(FC_BOOL_RET, NotifyRequestComplete);
-
- static FCDECL0(FC_BOOL_RET, GetEnableWorkerTracking);
-
- static FCDECL1(void, ReportThreadStatus, CLR_BOOL isWorking);
-
- static FCDECL5(LPVOID, CorRegisterWaitForSingleObject,
- Object* waitObjectUNSAFE,
- Object* stateUNSAFE,
- UINT32 timeout,
- CLR_BOOL executeOnlyOnce,
- Object* registeredWaitObjectUNSAFE);
-
- static FCDECL1(FC_BOOL_RET, CorPostQueuedCompletionStatus, LPOVERLAPPED lpOverlapped);
- static FCDECL2(FC_BOOL_RET, CorUnregisterWait, LPVOID WaitHandle, Object * objectToNotify);
- static FCDECL1(void, CorWaitHandleCleanupNative, LPVOID WaitHandle);
- static FCDECL1(FC_BOOL_RET, CorBindIoCompletionCallback, HANDLE fileHandle);
};
-extern "C" INT64 QCALLTYPE ThreadPool_GetCompletedWorkItemCount();
-extern "C" BOOL QCALLTYPE ThreadPool_RequestWorkerThread();
-extern "C" BOOL QCALLTYPE ThreadPool_PerformGateActivities(INT32 cpuUtilization);
-
-VOID QueueUserWorkItemManagedCallback(PVOID pArg);
-void WINAPI BindIoCompletionCallbackStub(DWORD ErrorCode,
- DWORD numBytesTransferred,
- LPOVERLAPPED lpOverlapped);
-void SetAsyncResultProperties(
- OVERLAPPEDDATAREF overlapped,
- DWORD dwErrorCode,
- DWORD dwNumBytes);
-
#endif
diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h
index 3537c57d8cf05b..62cb4d8a634c12 100644
--- a/src/coreclr/vm/corelib.h
+++ b/src/coreclr/vm/corelib.h
@@ -900,19 +900,6 @@ DEFINE_METHOD(AUTORELEASEPOOL, CREATEAUTORELEASEPOOL, CreateAutoreleasePoo
DEFINE_METHOD(AUTORELEASEPOOL, DRAINAUTORELEASEPOOL, DrainAutoreleasePool, SM_RetVoid)
#endif // FEATURE_OBJCMARSHAL
-DEFINE_CLASS(IOCB_HELPER, Threading, _IOCompletionCallback)
-DEFINE_METHOD(IOCB_HELPER, PERFORM_IOCOMPLETION_CALLBACK, PerformIOCompletionCallback, SM_UInt_UInt_PtrNativeOverlapped_RetVoid)
-
-DEFINE_CLASS(TPWAITORTIMER_HELPER, Threading, _ThreadPoolWaitOrTimerCallback)
-DEFINE_METHOD(TPWAITORTIMER_HELPER, PERFORM_WAITORTIMER_CALLBACK, PerformWaitOrTimerCallback, SM__ThreadPoolWaitOrTimerCallback_Bool_RetVoid)
-
-DEFINE_CLASS(TP_WAIT_CALLBACK, Threading, _ThreadPoolWaitCallback)
-DEFINE_METHOD(TP_WAIT_CALLBACK, PERFORM_WAIT_CALLBACK, PerformWaitCallback, SM_RetBool)
-
-DEFINE_CLASS(THREAD_POOL, Threading, ThreadPool)
-DEFINE_METHOD(THREAD_POOL, ENSURE_GATE_THREAD_RUNNING, EnsureGateThreadRunning, SM_RetVoid)
-DEFINE_METHOD(THREAD_POOL, UNSAFE_QUEUE_UNMANAGED_WORK_ITEM, UnsafeQueueUnmanagedWorkItem, SM_IntPtr_IntPtr_RetVoid)
-
DEFINE_CLASS(TIMESPAN, System, TimeSpan)
diff --git a/src/coreclr/vm/corhost.cpp b/src/coreclr/vm/corhost.cpp
index e1babc7f6228e4..db99db62b10c56 100644
--- a/src/coreclr/vm/corhost.cpp
+++ b/src/coreclr/vm/corhost.cpp
@@ -28,7 +28,6 @@
#include "dllimportcallback.h"
#include "eventtrace.h"
-#include "win32threadpool.h"
#include "eventtrace.h"
#include "finalizerthread.h"
#include "threadsuspend.h"
diff --git a/src/coreclr/vm/delegateinfo.h b/src/coreclr/vm/delegateinfo.h
deleted file mode 100644
index 2f9f2d71860248..00000000000000
--- a/src/coreclr/vm/delegateinfo.h
+++ /dev/null
@@ -1,62 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-/*============================================================
-**
-** Header: DelegateInfo.h
-**
-**
-** Purpose: Native methods on System.ThreadPool
-** and its inner classes
-**
-
-**
-===========================================================*/
-#ifndef DELEGATE_INFO
-#define DELEGATE_INFO
-
-struct DelegateInfo;
-typedef DelegateInfo* DelegateInfoPtr;
-
-struct DelegateInfo
-{
- OBJECTHANDLE m_stateHandle;
- OBJECTHANDLE m_eventHandle;
- OBJECTHANDLE m_registeredWaitHandle;
-
-#ifndef DACCESS_COMPILE
- void Release()
- {
- CONTRACTL {
- // m_compressedStack->Release() can actually throw today because it has got a call
- // to new down the stack. However that is recent and the semantic of that api is such
- // it should not throw. I am expecting clenup of that function to take care of that
- // so I am adding this comment to make sure the issue is document.
- // Remove this comment once that work is done
- NOTHROW;
- GC_TRIGGERS;
- MODE_COOPERATIVE;
- FORBID_FAULT;
- }
- CONTRACTL_END;
-
-
- if (m_stateHandle)
- DestroyHandle(m_stateHandle);
- if (m_eventHandle)
- DestroyHandle(m_eventHandle);
- if (m_registeredWaitHandle)
- DestroyHandle(m_registeredWaitHandle);
- }
-#endif
-
- static DelegateInfo *MakeDelegateInfo(OBJECTREF *state,
- OBJECTREF *waitEvent,
- OBJECTREF *registeredWaitObject);
-};
-
-
-
-
-
-#endif // DELEGATE_INFO
diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h
index 6c06aacbf4fc76..bf3c849f318642 100644
--- a/src/coreclr/vm/ecalllist.h
+++ b/src/coreclr/vm/ecalllist.h
@@ -394,27 +394,6 @@ FCFuncEnd()
FCFuncStart(gThreadPoolFuncs)
FCFuncElement("GetNextConfigUInt32Value", ThreadPoolNative::GetNextConfigUInt32Value)
- FCFuncElement("PostQueuedCompletionStatus", ThreadPoolNative::CorPostQueuedCompletionStatus)
- FCFuncElement("GetAvailableThreadsNative", ThreadPoolNative::CorGetAvailableThreads)
- FCFuncElement("CanSetMinIOCompletionThreads", ThreadPoolNative::CorCanSetMinIOCompletionThreads)
- FCFuncElement("CanSetMaxIOCompletionThreads", ThreadPoolNative::CorCanSetMaxIOCompletionThreads)
- FCFuncElement("SetMinThreadsNative", ThreadPoolNative::CorSetMinThreads)
- FCFuncElement("GetMinThreadsNative", ThreadPoolNative::CorGetMinThreads)
- FCFuncElement("GetThreadCount", ThreadPoolNative::GetThreadCount)
- FCFuncElement("GetPendingUnmanagedWorkItemCount", ThreadPoolNative::GetPendingUnmanagedWorkItemCount)
- FCFuncElement("RegisterWaitForSingleObjectNative", ThreadPoolNative::CorRegisterWaitForSingleObject)
- FCFuncElement("BindIOCompletionCallbackNative", ThreadPoolNative::CorBindIoCompletionCallback)
- FCFuncElement("SetMaxThreadsNative", ThreadPoolNative::CorSetMaxThreads)
- FCFuncElement("GetMaxThreadsNative", ThreadPoolNative::CorGetMaxThreads)
- FCFuncElement("NotifyWorkItemCompleteNative", ThreadPoolNative::NotifyRequestComplete)
- FCFuncElement("NotifyWorkItemProgressNative", ThreadPoolNative::NotifyRequestProgress)
- FCFuncElement("GetEnableWorkerTrackingNative", ThreadPoolNative::GetEnableWorkerTracking)
- FCFuncElement("ReportThreadStatusNative", ThreadPoolNative::ReportThreadStatus)
-FCFuncEnd()
-
-FCFuncStart(gRegisteredWaitHandleFuncs)
- FCFuncElement("UnregisterWaitNative", ThreadPoolNative::CorUnregisterWait)
- FCFuncElement("WaitHandleCleanupNative", ThreadPoolNative::CorWaitHandleCleanupNative)
FCFuncEnd()
FCFuncStart(gWaitHandleFuncs)
@@ -573,7 +552,6 @@ FCFuncEnd()
FCFuncStart(gOverlappedFuncs)
FCFuncElement("AllocateNativeOverlapped", AllocateNativeOverlapped)
FCFuncElement("FreeNativeOverlapped", FreeNativeOverlapped)
- FCFuncElement("CheckVMForIOPacket", CheckVMForIOPacket)
FCFuncElement("GetOverlappedFromNative", GetOverlappedFromNative)
FCFuncEnd()
@@ -794,8 +772,6 @@ FCClassElement("ObjectMarshaler", "System.StubHelpers", gObjectMarshalerFuncs)
#endif
FCClassElement("OverlappedData", "System.Threading", gOverlappedFuncs)
-FCClassElement("RegisteredWaitHandle", "System.Threading", gRegisteredWaitHandleFuncs)
-
FCClassElement("RuntimeAssembly", "System.Reflection", gRuntimeAssemblyFuncs)
FCClassElement("RuntimeFieldHandle", "System", gCOMFieldHandleNewFuncs)
FCClassElement("RuntimeHelpers", "System.Runtime.CompilerServices", gRuntimeHelpers)
diff --git a/src/coreclr/vm/eventing/eventpipe/ep-rt-coreclr.h b/src/coreclr/vm/eventing/eventpipe/ep-rt-coreclr.h
index 620fe62f30268e..a494b4d156607e 100644
--- a/src/coreclr/vm/eventing/eventpipe/ep-rt-coreclr.h
+++ b/src/coreclr/vm/eventing/eventpipe/ep-rt-coreclr.h
@@ -11,7 +11,6 @@
#include
#include "fstream.h"
#include "typestring.h"
-#include "win32threadpool.h"
#include "clrversion.h"
#undef EP_INFINITE_WAIT
@@ -1661,15 +1660,6 @@ ep_rt_config_value_get_output_streaming (void)
return CLRConfig::GetConfigValue (CLRConfig::INTERNAL_EventPipeOutputStreaming) != 0;
}
-static
-inline
-bool
-ep_rt_config_value_get_use_portable_thread_pool (void)
-{
- STATIC_CONTRACT_NOTHROW;
- return ThreadpoolMgr::UsePortableThreadPool ();
-}
-
/*
* EventPipeSampleProfiler.
*/
diff --git a/src/coreclr/vm/hillclimbing.cpp b/src/coreclr/vm/hillclimbing.cpp
deleted file mode 100644
index 315ceb2bba8ea0..00000000000000
--- a/src/coreclr/vm/hillclimbing.cpp
+++ /dev/null
@@ -1,443 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-//=========================================================================
-
-//
-// HillClimbing.cpp
-//
-// Defines classes for the ThreadPool's HillClimbing concurrency-optimization
-// algorithm.
-//
-
-//=========================================================================
-
-//
-// TODO: write an essay about how/why this works. Maybe put it in BotR?
-//
-
-#include "common.h"
-#include "hillclimbing.h"
-#include "win32threadpool.h"
-
-//
-// Default compilation mode is /fp:precise, which disables fp intrinsics. This causes us to pull in FP stuff (sin,cos,etc.) from
-// The CRT, and increases our download size by ~5k. We don't need the extra precision this gets us, so let's switch to
-// the intrinsic versions.
-//
-#ifdef _MSC_VER
-#pragma float_control(precise, off)
-#endif
-
-
-
-const double pi = 3.141592653589793;
-
-void HillClimbing::Initialize()
-{
- CONTRACTL
- {
- THROWS;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- _ASSERTE(!ThreadpoolMgr::UsePortableThreadPool());
-
- m_wavePeriod = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_HillClimbing_WavePeriod);
- m_maxThreadWaveMagnitude = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_HillClimbing_MaxWaveMagnitude);
- m_threadMagnitudeMultiplier = (double)CLRConfig::GetConfigValue(CLRConfig::INTERNAL_HillClimbing_WaveMagnitudeMultiplier) / 100.0;
- m_samplesToMeasure = m_wavePeriod * (int)CLRConfig::GetConfigValue(CLRConfig::INTERNAL_HillClimbing_WaveHistorySize);
- m_targetThroughputRatio = (double)CLRConfig::GetConfigValue(CLRConfig::INTERNAL_HillClimbing_Bias) / 100.0;
- m_targetSignalToNoiseRatio = (double)CLRConfig::GetConfigValue(CLRConfig::INTERNAL_HillClimbing_TargetSignalToNoiseRatio) / 100.0;
- m_maxChangePerSecond = (double)CLRConfig::GetConfigValue(CLRConfig::INTERNAL_HillClimbing_MaxChangePerSecond);
- m_maxChangePerSample = (double)CLRConfig::GetConfigValue(CLRConfig::INTERNAL_HillClimbing_MaxChangePerSample);
- m_sampleIntervalLow = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_HillClimbing_SampleIntervalLow);
- m_sampleIntervalHigh = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_HillClimbing_SampleIntervalHigh);
- m_throughputErrorSmoothingFactor = (double)CLRConfig::GetConfigValue(CLRConfig::INTERNAL_HillClimbing_ErrorSmoothingFactor) / 100.0;
- m_gainExponent = (double)CLRConfig::GetConfigValue(CLRConfig::INTERNAL_HillClimbing_GainExponent) / 100.0;
- m_maxSampleError = (double)CLRConfig::GetConfigValue(CLRConfig::INTERNAL_HillClimbing_MaxSampleErrorPercent) / 100.0;
- m_currentControlSetting = 0;
- m_totalSamples = 0;
- m_lastThreadCount = 0;
- m_averageThroughputNoise = 0;
- m_elapsedSinceLastChange = 0;
- m_completionsSinceLastChange = 0;
- m_accumulatedCompletionCount = 0;
- m_accumulatedSampleDuration = 0;
-
- m_samples = new double[m_samplesToMeasure];
- m_threadCounts = new double[m_samplesToMeasure];
-
- // seed our random number generator with the CLR instance ID and the process ID, to avoid correlations with other CLR ThreadPool instances.
-#ifndef DACCESS_COMPILE
- m_randomIntervalGenerator.Init(((int)GetClrInstanceId() << 16) ^ (int)GetCurrentProcessId());
-#endif
- m_currentSampleInterval = m_randomIntervalGenerator.Next(m_sampleIntervalLow, m_sampleIntervalHigh+1);
-}
-
-int HillClimbing::Update(int currentThreadCount, double sampleDuration, int numCompletions, int* pNewSampleInterval)
-{
- LIMITED_METHOD_CONTRACT;
- _ASSERTE(!ThreadpoolMgr::UsePortableThreadPool());
-
-#ifdef DACCESS_COMPILE
- return 1;
-#else
-
- //
- // If someone changed the thread count without telling us, update our records accordingly.
- //
- if (currentThreadCount != m_lastThreadCount)
- ForceChange(currentThreadCount, Initializing);
-
- //
- // Update the cumulative stats for this thread count
- //
- m_elapsedSinceLastChange += sampleDuration;
- m_completionsSinceLastChange += numCompletions;
-
- //
- // Add in any data we've already collected about this sample
- //
- sampleDuration += m_accumulatedSampleDuration;
- numCompletions += m_accumulatedCompletionCount;
-
- //
- // We need to make sure we're collecting reasonably accurate data. Since we're just counting the end
- // of each work item, we are goinng to be missing some data about what really happened during the
- // sample interval. The count produced by each thread includes an initial work item that may have
- // started well before the start of the interval, and each thread may have been running some new
- // work item for some time before the end of the interval, which did not yet get counted. So
- // our count is going to be off by +/- threadCount workitems.
- //
- // The exception is that the thread that reported to us last time definitely wasn't running any work
- // at that time, and the thread that's reporting now definitely isn't running a work item now. So
- // we really only need to consider threadCount-1 threads.
- //
- // Thus the percent error in our count is +/- (threadCount-1)/numCompletions.
- //
- // We cannot rely on the frequency-domain analysis we'll be doing later to filter out this error, because
- // of the way it accumulates over time. If this sample is off by, say, 33% in the negative direction,
- // then the next one likely will be too. The one after that will include the sum of the completions
- // we missed in the previous samples, and so will be 33% positive. So every three samples we'll have
- // two "low" samples and one "high" sample. This will appear as periodic variation right in the frequency
- // range we're targeting, which will not be filtered by the frequency-domain translation.
- //
- if (m_totalSamples > 0 && ((currentThreadCount-1.0) / numCompletions) >= m_maxSampleError)
- {
- // not accurate enough yet. Let's accumulate the data so far, and tell the ThreadPool
- // to collect a little more.
- m_accumulatedSampleDuration = sampleDuration;
- m_accumulatedCompletionCount = numCompletions;
- *pNewSampleInterval = 10;
- return currentThreadCount;
- }
-
- //
- // We've got enouugh data for our sample; reset our accumulators for next time.
- //
- m_accumulatedSampleDuration = 0;
- m_accumulatedCompletionCount = 0;
-
- //
- // Add the current thread count and throughput sample to our history
- //
- double throughput = (double)numCompletions / sampleDuration;
- FireEtwThreadPoolWorkerThreadAdjustmentSample(throughput, GetClrInstanceId());
-
- int sampleIndex = m_totalSamples % m_samplesToMeasure;
- m_samples[sampleIndex] = throughput;
- m_threadCounts[sampleIndex] = currentThreadCount;
- m_totalSamples++;
-
- //
- // Set up defaults for our metrics
- //
- Complex threadWaveComponent = 0;
- Complex throughputWaveComponent = 0;
- double throughputErrorEstimate = 0;
- Complex ratio = 0;
- double confidence = 0;
-
- HillClimbingStateTransition transition = Warmup;
-
- //
- // How many samples will we use? It must be at least the three wave periods we're looking for, and it must also be a whole
- // multiple of the primary wave's period; otherwise the frequency we're looking for will fall between two frequency bands
- // in the Fourier analysis, and we won't be able to measure it accurately.
- //
- int sampleCount = ((int)min(m_totalSamples-1, m_samplesToMeasure) / m_wavePeriod) * m_wavePeriod;
-
- if (sampleCount > m_wavePeriod)
- {
- //
- // Average the throughput and thread count samples, so we can scale the wave magnitudes later.
- //
- double sampleSum = 0;
- double threadSum = 0;
- for (int i = 0; i < sampleCount; i++)
- {
- sampleSum += m_samples[(m_totalSamples - sampleCount + i) % m_samplesToMeasure];
- threadSum += m_threadCounts[(m_totalSamples - sampleCount + i) % m_samplesToMeasure];
- }
- double averageThroughput = sampleSum / sampleCount;
- double averageThreadCount = threadSum / sampleCount;
-
- if (averageThroughput > 0 && averageThreadCount > 0)
- {
- //
- // Calculate the periods of the adjacent frequency bands we'll be using to measure noise levels.
- // We want the two adjacent Fourier frequency bands.
- //
- double adjacentPeriod1 = sampleCount / (((double)sampleCount / (double)m_wavePeriod) + 1);
- double adjacentPeriod2 = sampleCount / (((double)sampleCount / (double)m_wavePeriod) - 1);
-
- //
- // Get the three different frequency components of the throughput (scaled by average
- // throughput). Our "error" estimate (the amount of noise that might be present in the
- // frequency band we're really interested in) is the average of the adjacent bands.
- //
- throughputWaveComponent = GetWaveComponent(m_samples, sampleCount, m_wavePeriod) / averageThroughput;
- throughputErrorEstimate = abs(GetWaveComponent(m_samples, sampleCount, adjacentPeriod1) / averageThroughput);
- if (adjacentPeriod2 <= sampleCount)
- throughputErrorEstimate = max(throughputErrorEstimate, abs(GetWaveComponent(m_samples, sampleCount, adjacentPeriod2) / averageThroughput));
-
- //
- // Do the same for the thread counts, so we have something to compare to. We don't measure thread count
- // noise, because there is none; these are exact measurements.
- //
- threadWaveComponent = GetWaveComponent(m_threadCounts, sampleCount, m_wavePeriod) / averageThreadCount;
-
- //
- // Update our moving average of the throughput noise. We'll use this later as feedback to
- // determine the new size of the thread wave.
- //
- if (m_averageThroughputNoise == 0)
- m_averageThroughputNoise = throughputErrorEstimate;
- else
- m_averageThroughputNoise = (m_throughputErrorSmoothingFactor * throughputErrorEstimate) + ((1.0-m_throughputErrorSmoothingFactor) * m_averageThroughputNoise);
-
- if (abs(threadWaveComponent) > 0)
- {
- //
- // Adjust the throughput wave so it's centered around the target wave, and then calculate the adjusted throughput/thread ratio.
- //
- ratio = (throughputWaveComponent - (m_targetThroughputRatio * threadWaveComponent)) / threadWaveComponent;
- transition = ClimbingMove;
- }
- else
- {
- ratio = 0;
- transition = Stabilizing;
- }
-
- //
- // Calculate how confident we are in the ratio. More noise == less confident. This has
- // the effect of slowing down movements that might be affected by random noise.
- //
- double noiseForConfidence = max(m_averageThroughputNoise, throughputErrorEstimate);
- if (noiseForConfidence > 0)
- confidence = (abs(threadWaveComponent) / noiseForConfidence) / m_targetSignalToNoiseRatio;
- else
- confidence = 1.0; //there is no noise!
-
- }
- }
-
- //
- // We use just the real part of the complex ratio we just calculated. If the throughput signal
- // is exactly in phase with the thread signal, this will be the same as taking the magnitude of
- // the complex move and moving that far up. If they're 180 degrees out of phase, we'll move
- // backward (because this indicates that our changes are having the opposite of the intended effect).
- // If they're 90 degrees out of phase, we won't move at all, because we can't tell whether we're
- // having a negative or positive effect on throughput.
- //
- double move = min(1.0, max(-1.0, ratio.r));
-
- //
- // Apply our confidence multiplier.
- //
- move *= min(1.0, max(0.0, confidence));
-
- //
- // Now apply non-linear gain, such that values around zero are attenuated, while higher values
- // are enhanced. This allows us to move quickly if we're far away from the target, but more slowly
- // if we're getting close, giving us rapid ramp-up without wild oscillations around the target.
- //
- double gain = m_maxChangePerSecond * sampleDuration;
- move = pow(fabs(move), m_gainExponent) * (move >= 0.0 ? 1 : -1) * gain;
- move = min(move, m_maxChangePerSample);
-
- //
- // If the result was positive, and CPU is > 95%, refuse the move.
- //
- if (move > 0.0 && ThreadpoolMgr::cpuUtilization > CpuUtilizationHigh)
- move = 0.0;
-
- //
- // Apply the move to our control setting
- //
- m_currentControlSetting += move;
-
- //
- // Calculate the new thread wave magnitude, which is based on the moving average we've been keeping of
- // the throughput error. This average starts at zero, so we'll start with a nice safe little wave at first.
- //
- int newThreadWaveMagnitude = (int)(0.5 + (m_currentControlSetting * m_averageThroughputNoise * m_targetSignalToNoiseRatio * m_threadMagnitudeMultiplier * 2.0));
- newThreadWaveMagnitude = min(newThreadWaveMagnitude, m_maxThreadWaveMagnitude);
- newThreadWaveMagnitude = max(newThreadWaveMagnitude, 1);
-
- //
- // Make sure our control setting is within the ThreadPool's limits
- //
- m_currentControlSetting = min(ThreadpoolMgr::MaxLimitTotalWorkerThreads-newThreadWaveMagnitude, m_currentControlSetting);
- m_currentControlSetting = max(ThreadpoolMgr::MinLimitTotalWorkerThreads, m_currentControlSetting);
-
- //
- // Calculate the new thread count (control setting + square wave)
- //
- int newThreadCount = (int)(m_currentControlSetting + newThreadWaveMagnitude * ((m_totalSamples / (m_wavePeriod/2)) % 2));
-
- //
- // Make sure the new thread count doesn't exceed the ThreadPool's limits
- //
- newThreadCount = min(ThreadpoolMgr::MaxLimitTotalWorkerThreads, newThreadCount);
- newThreadCount = max(ThreadpoolMgr::MinLimitTotalWorkerThreads, newThreadCount);
-
- //
- // Record these numbers for posterity
- //
- FireEtwThreadPoolWorkerThreadAdjustmentStats(
- sampleDuration,
- throughput,
- threadWaveComponent.r,
- throughputWaveComponent.r,
- throughputErrorEstimate,
- m_averageThroughputNoise,
- ratio.r,
- confidence,
- m_currentControlSetting,
- (unsigned short)newThreadWaveMagnitude,
- GetClrInstanceId());
-
- //
- // If all of this caused an actual change in thread count, log that as well.
- //
- if (newThreadCount != currentThreadCount)
- ChangeThreadCount(newThreadCount, transition);
-
- //
- // Return the new thread count and sample interval. This is randomized to prevent correlations with other periodic
- // changes in throughput. Among other things, this prevents us from getting confused by Hill Climbing instances
- // running in other processes.
- //
- // If we're at minThreads, and we seem to be hurting performance by going higher, we can't go any lower to fix this. So
- // we'll simply stay at minThreads much longer, and only occasionally try a higher value.
- //
- if (ratio.r < 0.0 && newThreadCount == ThreadpoolMgr::MinLimitTotalWorkerThreads)
- *pNewSampleInterval = (int)(0.5 + m_currentSampleInterval * (10.0 * min(-ratio.r, 1.0)));
- else
- *pNewSampleInterval = m_currentSampleInterval;
-
- return newThreadCount;
-
-#endif //DACCESS_COMPILE
-}
-
-
-void HillClimbing::ForceChange(int newThreadCount, HillClimbingStateTransition transition)
-{
- LIMITED_METHOD_CONTRACT;
- _ASSERTE(!ThreadpoolMgr::UsePortableThreadPool());
-
- if (newThreadCount != m_lastThreadCount)
- {
- m_currentControlSetting += (newThreadCount - m_lastThreadCount);
- ChangeThreadCount(newThreadCount, transition);
- }
-}
-
-
-void HillClimbing::ChangeThreadCount(int newThreadCount, HillClimbingStateTransition transition)
-{
- LIMITED_METHOD_CONTRACT;
-
- m_lastThreadCount = newThreadCount;
- m_currentSampleInterval = m_randomIntervalGenerator.Next(m_sampleIntervalLow, m_sampleIntervalHigh+1);
- double throughput = (m_elapsedSinceLastChange > 0) ? (m_completionsSinceLastChange / m_elapsedSinceLastChange) : 0;
- LogTransition(newThreadCount, throughput, transition);
- m_elapsedSinceLastChange = 0;
- m_completionsSinceLastChange = 0;
-}
-
-
-GARY_IMPL(HillClimbingLogEntry, HillClimbingLog, HillClimbingLogCapacity);
-GVAL_IMPL(int, HillClimbingLogFirstIndex);
-GVAL_IMPL(int, HillClimbingLogSize);
-
-
-void HillClimbing::LogTransition(int threadCount, double throughput, HillClimbingStateTransition transition)
-{
- LIMITED_METHOD_CONTRACT;
-
-#ifndef DACCESS_COMPILE
- int index = (HillClimbingLogFirstIndex + HillClimbingLogSize) % HillClimbingLogCapacity;
-
- if (HillClimbingLogSize == HillClimbingLogCapacity)
- {
- HillClimbingLogFirstIndex = (HillClimbingLogFirstIndex + 1) % HillClimbingLogCapacity;
- HillClimbingLogSize--; //hide this slot while we update it
- }
-
- HillClimbingLogEntry* entry = &HillClimbingLog[index];
-
- entry->TickCount = GetTickCount();
- entry->Transition = transition;
- entry->NewControlSetting = threadCount;
-
- entry->LastHistoryCount = (int)(min(m_totalSamples, m_samplesToMeasure) / m_wavePeriod) * m_wavePeriod;
- entry->LastHistoryMean = (float) throughput;
-
- HillClimbingLogSize++;
-
- FireEtwThreadPoolWorkerThreadAdjustmentAdjustment(
- throughput,
- threadCount,
- transition,
- GetClrInstanceId());
-
-#endif //DACCESS_COMPILE
-}
-
-Complex HillClimbing::GetWaveComponent(double* samples, int sampleCount, double period)
-{
- LIMITED_METHOD_CONTRACT;
- _ASSERTE(!ThreadpoolMgr::UsePortableThreadPool());
-
- _ASSERTE(sampleCount >= period); //can't measure a wave that doesn't fit
- _ASSERTE(period >= 2); //can't measure above the Nyquist frequency
-
- //
- // Calculate the sinusoid with the given period.
- // We're using the Goertzel algorithm for this. See http://en.wikipedia.org/wiki/Goertzel_algorithm.
- //
- double w = 2.0 * pi / period;
- double cosine = cos(w);
- double sine = sin(w);
- double coeff = 2.0 * cosine;
- double q0 = 0, q1 = 0, q2 = 0;
-
- for (int i = 0; i < sampleCount; i++)
- {
- double sample = samples[(m_totalSamples - sampleCount + i) % m_samplesToMeasure];
-
- q0 = coeff * q1 - q2 + sample;
- q2 = q1;
- q1 = q0;
- }
-
- return Complex(q1 - q2 * cosine, q2 * sine) / (double)sampleCount;
-}
-
diff --git a/src/coreclr/vm/hillclimbing.h b/src/coreclr/vm/hillclimbing.h
deleted file mode 100644
index 49bd023af113df..00000000000000
--- a/src/coreclr/vm/hillclimbing.h
+++ /dev/null
@@ -1,96 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-//=========================================================================
-
-//
-// HillClimbing.h
-//
-// Defines classes for the ThreadPool's HillClimbing concurrency-optimization
-// algorithm.
-//
-
-//=========================================================================
-
-#ifndef _HILLCLIMBING_H
-#define _HILLCLIMBING_H
-
-#include "complex.h"
-#include "random.h"
-
-enum HillClimbingStateTransition
-{
- Warmup,
- Initializing,
- RandomMove,
- ClimbingMove,
- ChangePoint,
- Stabilizing,
- Starvation, //used by ThreadpoolMgr
- ThreadTimedOut, //used by ThreadpoolMgr
- Undefined,
-};
-
-
-class HillClimbing
-{
-private:
- int m_wavePeriod;
- int m_samplesToMeasure;
- double m_targetThroughputRatio;
- double m_targetSignalToNoiseRatio;
- double m_maxChangePerSecond;
- double m_maxChangePerSample;
- int m_maxThreadWaveMagnitude;
- DWORD m_sampleIntervalLow;
- double m_threadMagnitudeMultiplier;
- DWORD m_sampleIntervalHigh;
- double m_throughputErrorSmoothingFactor;
- double m_gainExponent;
- double m_maxSampleError;
-
- double m_currentControlSetting;
- LONGLONG m_totalSamples;
- int m_lastThreadCount;
- double m_elapsedSinceLastChange; //elapsed seconds since last thread count change
- double m_completionsSinceLastChange; //number of completions since last thread count change
-
- double m_averageThroughputNoise;
-
- double* m_samples; //Circular buffer of the last m_samplesToMeasure samples
- double* m_threadCounts; //Thread counts effective at each of m_samples
-
- unsigned int m_currentSampleInterval;
- CLRRandom m_randomIntervalGenerator;
-
- int m_accumulatedCompletionCount;
- double m_accumulatedSampleDuration;
-
- void ChangeThreadCount(int newThreadCount, HillClimbingStateTransition transition);
- void LogTransition(int threadCount, double throughput, HillClimbingStateTransition transition);
-
- Complex GetWaveComponent(double* samples, int sampleCount, double period);
-
-public:
- void Initialize();
- int Update(int currentThreadCount, double sampleDuration, int numCompletions, int* pNewSampleInterval);
- void ForceChange(int newThreadCount, HillClimbingStateTransition transition);
-};
-
-#define HillClimbingLogCapacity 200
-
-struct HillClimbingLogEntry
-{
- DWORD TickCount;
- HillClimbingStateTransition Transition;
- int NewControlSetting;
- int LastHistoryCount;
- float LastHistoryMean;
-};
-
-GARY_DECL(HillClimbingLogEntry, HillClimbingLog, HillClimbingLogCapacity);
-GVAL_DECL(int, HillClimbingLogFirstIndex);
-GVAL_DECL(int, HillClimbingLogSize);
-typedef DPTR(HillClimbingLogEntry) PTR_HillClimbingLogEntry;
-
-#endif
diff --git a/src/coreclr/vm/metasig.h b/src/coreclr/vm/metasig.h
index f567d34be2f7da..dc710afa99b349 100644
--- a/src/coreclr/vm/metasig.h
+++ b/src/coreclr/vm/metasig.h
@@ -374,8 +374,6 @@ DEFINE_METASIG(SM(ArrByte_RetObj, a(b), j))
DEFINE_METASIG(SM(ArrByte_Bool_RetObj, a(b) F, j))
DEFINE_METASIG(SM(ArrByte_ArrByte_RefObj_RetObj, a(b) a(b) r(j), j))
-DEFINE_METASIG_T(SM(UInt_UInt_PtrNativeOverlapped_RetVoid, K K P(g(NATIVEOVERLAPPED)), v))
-
DEFINE_METASIG(IM(Long_RetVoid, l, v))
DEFINE_METASIG(IM(IntPtr_Int_RetVoid, I i, v))
DEFINE_METASIG(IM(IntInt_RetArrByte, i i, a(b)))
@@ -557,9 +555,6 @@ DEFINE_METASIG_T(SM(IntPtr_AssemblyName_RetAssemblyBase, I C(ASSEMBLY_NAME), C(A
DEFINE_METASIG_T(SM(Str_AssemblyBase_IntPtr_RetIntPtr, s C(ASSEMBLYBASE) I, I))
DEFINE_METASIG_T(SM(Str_AssemblyBase_Bool_UInt_RetIntPtr, s C(ASSEMBLYBASE) F K, I))
-// ThreadPool
-DEFINE_METASIG_T(SM(_ThreadPoolWaitOrTimerCallback_Bool_RetVoid, C(TPWAITORTIMER_HELPER) F, v))
-
// For FailFast
DEFINE_METASIG(SM(Str_RetVoid, s, v))
DEFINE_METASIG_T(SM(Str_Exception_RetVoid, s C(EXCEPTION), v))
diff --git a/src/coreclr/vm/nativeoverlapped.cpp b/src/coreclr/vm/nativeoverlapped.cpp
index c3b55ae130d429..4a9edb5ff780be 100644
--- a/src/coreclr/vm/nativeoverlapped.cpp
+++ b/src/coreclr/vm/nativeoverlapped.cpp
@@ -15,79 +15,10 @@
#include "fcall.h"
#include "nativeoverlapped.h"
#include "corhost.h"
-#include "win32threadpool.h"
#include "comsynchronizable.h"
#include "comthreadpool.h"
#include "marshalnative.h"
-//
-//The function is called from managed code to quicly check if a packet is available.
-//This is a perf-critical function. Even helper method frames are not created. We fall
-//back to the VM to do heavy weight operations like creating a new CP thread.
-//
-FCIMPL3(void, CheckVMForIOPacket, LPOVERLAPPED* lpOverlapped, DWORD* errorCode, DWORD* numBytes)
-{
- FCALL_CONTRACT;
- _ASSERTE_ALL_BUILDS(!ThreadpoolMgr::UsePortableThreadPoolForIO());
-
-#ifndef TARGET_UNIX
- Thread *pThread = GetThread();
- size_t key=0;
-
- //Poll and wait if GC is in progress, to avoid blocking GC for too long.
- FC_GC_POLL();
-
- *lpOverlapped = ThreadpoolMgr::CompletionPortDispatchWorkWithinAppDomain(pThread, errorCode, numBytes, &key);
- if(*lpOverlapped == NULL)
- {
- return;
- }
-
- OVERLAPPEDDATAREF overlapped = ObjectToOVERLAPPEDDATAREF(OverlappedDataObject::GetOverlapped(*lpOverlapped));
-
- if (overlapped->m_callback == NULL)
- {
- //We're not initialized yet, go back to the Vm, and process the packet there.
- ThreadpoolMgr::StoreOverlappedInfoInThread(pThread, *errorCode, *numBytes, key, *lpOverlapped);
-
- *lpOverlapped = NULL;
- return;
- }
- else
- {
- if(!pThread->IsRealThreadPoolResetNeeded())
- {
- pThread->ResetManagedThreadObjectInCoopMode(ThreadNative::PRIORITY_NORMAL);
- pThread->InternalReset(TRUE, FALSE, FALSE);
- if(ThreadpoolMgr::ShouldGrowCompletionPortThreadpool(ThreadpoolMgr::CPThreadCounter.DangerousGetDirtyCounts()))
- {
- //We may have to create a CP thread, go back to the Vm, and process the packet there.
- ThreadpoolMgr::StoreOverlappedInfoInThread(pThread, *errorCode, *numBytes, key, *lpOverlapped);
- *lpOverlapped = NULL;
- }
- }
- else
- {
- //A more complete reset is needed (due to change in priority etc), go back to the VM,
- //and process the packet there.
-
- ThreadpoolMgr::StoreOverlappedInfoInThread(pThread, *errorCode, *numBytes, key, *lpOverlapped);
- *lpOverlapped = NULL;
- }
- }
-
- // if this will be "dispatched" to the managed callback fire the IODequeue event:
- if (*lpOverlapped != NULL && ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context, ThreadPoolIODequeue))
- FireEtwThreadPoolIODequeue(*lpOverlapped, OverlappedDataObject::GetOverlapped(*lpOverlapped), GetClrInstanceId());
-
-#else // !TARGET_UNIX
- *lpOverlapped = NULL;
-#endif // !TARGET_UNIX
-
- return;
-}
-FCIMPLEND
-
FCIMPL1(LPOVERLAPPED, AllocateNativeOverlapped, OverlappedDataObject* overlappedUNSAFE)
{
FCALL_CONTRACT;
diff --git a/src/coreclr/vm/nativeoverlapped.h b/src/coreclr/vm/nativeoverlapped.h
index ff041cbbf0a86e..771f9fbf483270 100644
--- a/src/coreclr/vm/nativeoverlapped.h
+++ b/src/coreclr/vm/nativeoverlapped.h
@@ -73,7 +73,6 @@ typedef OverlappedDataObject* OVERLAPPEDDATAREF;
#endif
-FCDECL3(void, CheckVMForIOPacket, LPOVERLAPPED* lpOverlapped, DWORD* errorCode, DWORD* numBytes);
FCDECL1(LPOVERLAPPED, AllocateNativeOverlapped, OverlappedDataObject* overlapped);
FCDECL1(void, FreeNativeOverlapped, LPOVERLAPPED lpOverlapped);
FCDECL1(OverlappedDataObject*, GetOverlappedFromNative, LPOVERLAPPED lpOverlapped);
diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp
index ddaa9a3e98444e..a7cfdbc3dca33a 100644
--- a/src/coreclr/vm/qcallentrypoints.cpp
+++ b/src/coreclr/vm/qcallentrypoints.cpp
@@ -211,9 +211,6 @@ static const Entry s_QCall[] =
DllImportEntry(ThreadNative_GetCurrentOSThreadId)
DllImportEntry(ThreadNative_Abort)
DllImportEntry(ThreadNative_ResetAbort)
- DllImportEntry(ThreadPool_GetCompletedWorkItemCount)
- DllImportEntry(ThreadPool_RequestWorkerThread)
- DllImportEntry(ThreadPool_PerformGateActivities)
#ifdef TARGET_UNIX
DllImportEntry(WaitHandle_CorWaitOnePrioritizedNative)
#endif
diff --git a/src/coreclr/vm/threadpoolrequest.cpp b/src/coreclr/vm/threadpoolrequest.cpp
deleted file mode 100644
index fb31321f31a9fe..00000000000000
--- a/src/coreclr/vm/threadpoolrequest.cpp
+++ /dev/null
@@ -1,690 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-//=========================================================================
-
-//
-// ThreadPoolRequest.cpp
-//
-
-//
-//
-//=========================================================================
-
-#include "common.h"
-#include "comdelegate.h"
-#include "comthreadpool.h"
-#include "threadpoolrequest.h"
-#include "win32threadpool.h"
-#include "class.h"
-#include "object.h"
-#include "field.h"
-#include "excep.h"
-#include "eeconfig.h"
-#include "corhost.h"
-#include "nativeoverlapped.h"
-#include "appdomain.inl"
-
-BYTE PerAppDomainTPCountList::s_padding[MAX_CACHE_LINE_SIZE - sizeof(LONG)];
-// Make this point to unmanaged TP in case, no appdomains have initialized yet.
-// Cacheline aligned, hot variable
-DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) LONG PerAppDomainTPCountList::s_ADHint = -1;
-
-// Move out of from preceding variables' cache line
-DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) UnManagedPerAppDomainTPCount PerAppDomainTPCountList::s_unmanagedTPCount;
-//The list of all per-appdomain work-request counts.
-ArrayListStatic PerAppDomainTPCountList::s_appDomainIndexList;
-
-void PerAppDomainTPCountList::InitAppDomainIndexList()
-{
- LIMITED_METHOD_CONTRACT;
-
- if (!ThreadpoolMgr::UsePortableThreadPool())
- {
- s_appDomainIndexList.Init();
- }
-}
-
-
-//---------------------------------------------------------------------------
-//AddNewTPIndex adds and returns a per-appdomain TP entry whenever a new appdomain
-//is created. Our list count should be equal to the max number of appdomains created
-//in the system.
-//
-//Assumptions:
-//This function needs to be called under the SystemDomain lock.
-//The ArrayListStatic data dtructure allows traversing of the counts without a
-//lock, but addition to the list requires synchronization.
-//
-TPIndex PerAppDomainTPCountList::AddNewTPIndex()
-{
- STANDARD_VM_CONTRACT;
-
- if (ThreadpoolMgr::UsePortableThreadPool())
- {
- return TPIndex();
- }
-
- DWORD count = s_appDomainIndexList.GetCount();
- DWORD i = FindFirstFreeTpEntry();
-
- if (i == UNUSED_THREADPOOL_INDEX)
- i = count;
-
- TPIndex index(i+1);
- if(count > i)
- {
-
- IPerAppDomainTPCount * pAdCount = dac_cast(s_appDomainIndexList.Get(i));
- pAdCount->SetTPIndex(index);
- return index;
- }
-
-#ifdef _MSC_VER
- // Disable this warning - we intentionally want __declspec(align()) to insert trailing padding for us
-#pragma warning(disable:4316) // Object allocated on the heap may not be aligned for this type.
-#endif
- ManagedPerAppDomainTPCount * pAdCount = new ManagedPerAppDomainTPCount(index);
-#ifdef _MSC_VER
-#pragma warning(default:4316) // Object allocated on the heap may not be aligned for this type.
-#endif
- pAdCount->ResetState();
-
- IfFailThrow(s_appDomainIndexList.Append(pAdCount));
-
- return index;
-}
-
-DWORD PerAppDomainTPCountList::FindFirstFreeTpEntry()
-{
- CONTRACTL
- {
- NOTHROW;
- MODE_ANY;
- GC_NOTRIGGER;
- }
- CONTRACTL_END;
-
- _ASSERTE(!ThreadpoolMgr::UsePortableThreadPool());
-
- DWORD DwnumADs = s_appDomainIndexList.GetCount();
- DWORD Dwi;
- IPerAppDomainTPCount * pAdCount;
- DWORD DwfreeIndex = UNUSED_THREADPOOL_INDEX;
-
- for (Dwi=0;Dwi < DwnumADs;Dwi++)
- {
- pAdCount = dac_cast(s_appDomainIndexList.Get(Dwi));
- _ASSERTE(pAdCount);
-
- if(pAdCount->IsTPIndexUnused())
- {
- DwfreeIndex = Dwi;
- STRESS_LOG1(LF_THREADPOOL, LL_INFO1000, "FindFirstFreeTpEntry: reusing index %d\n", DwfreeIndex + 1);
- break;
- }
- }
-
- return DwfreeIndex;
-}
-
-//---------------------------------------------------------------------------
-//ResetAppDomainIndex: Resets the AppDomain ID and the per-appdomain
-// thread pool counts
-//
-//Arguments:
-//index - The index into the s_appDomainIndexList for the AppDomain we're
-// trying to clear (the AD being unloaded)
-//
-//Assumptions:
-//This function needs to be called from the AD unload thread after all domain
-//bound objects have been finalized when it's safe to recycle the TPIndex.
-//ClearAppDomainRequestsActive can be called from this function because no
-// managed code is running (If managed code is running, this function needs
-//to be called under a managed per-appdomain lock).
-//
-void PerAppDomainTPCountList::ResetAppDomainIndex(TPIndex index)
-{
- CONTRACTL
- {
- NOTHROW;
- MODE_ANY;
- GC_TRIGGERS;
- }
- CONTRACTL_END;
-
- if (ThreadpoolMgr::UsePortableThreadPool())
- {
- _ASSERTE(index.m_dwIndex == TPIndex().m_dwIndex);
- return;
- }
-
- IPerAppDomainTPCount * pAdCount = dac_cast(s_appDomainIndexList.Get(index.m_dwIndex-1));
- _ASSERTE(pAdCount);
-
- STRESS_LOG2(LF_THREADPOOL, LL_INFO1000, "ResetAppDomainIndex: index %d pAdCount %p\n", index.m_dwIndex, pAdCount);
-
- pAdCount->ResetState();
- pAdCount->SetTPIndexUnused();
-}
-
-//---------------------------------------------------------------------------
-//AreRequestsPendingInAnyAppDomains checks to see if there any requests pending
-//in other appdomains. It also checks for pending unmanaged work requests.
-//This function is called at end of thread quantum to see if the thread needs to
-//transition into a different appdomain. This function may also be called by
-//the scheduler to check for any unscheduled work.
-//
-bool PerAppDomainTPCountList::AreRequestsPendingInAnyAppDomains()
-{
- CONTRACTL
- {
- NOTHROW;
- MODE_ANY;
- GC_NOTRIGGER;
- }
- CONTRACTL_END;
-
- _ASSERTE(!ThreadpoolMgr::UsePortableThreadPool());
-
- DWORD DwnumADs = s_appDomainIndexList.GetCount();
- DWORD Dwi;
- IPerAppDomainTPCount * pAdCount;
- bool fRequestsPending = false;
-
- for (Dwi=0;Dwi < DwnumADs;Dwi++)
- {
-
- pAdCount = dac_cast(s_appDomainIndexList.Get(Dwi));
- _ASSERTE(pAdCount);
-
- if(pAdCount->IsRequestPending())
- {
- fRequestsPending = true;
- break;
- }
- }
-
- if(s_unmanagedTPCount.IsRequestPending())
- {
- fRequestsPending = true;
- }
-
- return fRequestsPending;
-}
-
-
-//---------------------------------------------------------------------------
-//GetAppDomainIndexForThreadpoolDispatch is essentailly the
-//"AppDomain Scheduler". This function makes fairness/policy decisions as to
-//which appdomain the thread needs to enter to. This function needs to guarantee
-//that all appdomain work requests are processed fairly. At this time all
-//appdomain requests and the unmanaged work requests are treated with the same
-//priority.
-//
-//Return Value:
-//The appdomain ID in which to dispatch the worker thread,nmanaged work items
-//need to be processed.
-//
-LONG PerAppDomainTPCountList::GetAppDomainIndexForThreadpoolDispatch()
-{
- CONTRACTL
- {
- NOTHROW;
- MODE_ANY;
- GC_NOTRIGGER;
- }
- CONTRACTL_END;
-
- _ASSERTE(!ThreadpoolMgr::UsePortableThreadPool());
-
- LONG hint = s_ADHint;
- DWORD count = s_appDomainIndexList.GetCount();
- IPerAppDomainTPCount * pAdCount;
- DWORD Dwi;
-
-
- if (hint != -1)
- {
- pAdCount = dac_cast(s_appDomainIndexList.Get(hint));
- }
- else
- {
- pAdCount = &s_unmanagedTPCount;
- }
-
- //temphint ensures that the check for appdomains proceeds in a pure round robin fashion.
- LONG temphint = hint;
-
- _ASSERTE( pAdCount);
-
- if (pAdCount->TakeActiveRequest())
- goto HintDone;
-
- //If there is no work in any appdomains, check the unmanaged queue,
- hint = -1;
-
- for (Dwi=0;Dwi(s_appDomainIndexList.Get(temphint));
- if (pAdCount->TakeActiveRequest())
- {
- hint = temphint;
- goto HintDone;
- }
-
- temphint++;
-
- _ASSERTE( temphint <= (LONG)count);
-
- if(temphint == (LONG)count)
- {
- temphint = 0;
- }
- }
-
- if (hint == -1 && !s_unmanagedTPCount.TakeActiveRequest())
- {
- //no work!
- return 0;
- }
-
-HintDone:
-
- if((hint+1) < (LONG)count)
- {
- s_ADHint = hint+1;
- }
- else
- {
- s_ADHint = -1;
- }
-
- if (hint == -1)
- {
- return hint;
- }
- else
- {
- return (hint+1);
- }
-}
-
-
-void UnManagedPerAppDomainTPCount::SetAppDomainRequestsActive()
-{
- WRAPPER_NO_CONTRACT;
- _ASSERTE(!ThreadpoolMgr::UsePortableThreadPool());
-
-#ifndef DACCESS_COMPILE
- LONG count = VolatileLoad(&m_outstandingThreadRequestCount);
- while (count < (LONG)ThreadpoolMgr::NumberOfProcessors)
- {
- LONG prevCount = InterlockedCompareExchange(&m_outstandingThreadRequestCount, count+1, count);
- if (prevCount == count)
- {
- ThreadpoolMgr::MaybeAddWorkingWorker();
- ThreadpoolMgr::EnsureGateThreadRunning();
- break;
- }
- count = prevCount;
- }
-#endif
-}
-
-bool FORCEINLINE UnManagedPerAppDomainTPCount::TakeActiveRequest()
-{
- LIMITED_METHOD_CONTRACT;
- _ASSERTE(!ThreadpoolMgr::UsePortableThreadPool());
-
- LONG count = VolatileLoad(&m_outstandingThreadRequestCount);
-
- while (count > 0)
- {
- LONG prevCount = InterlockedCompareExchange(&m_outstandingThreadRequestCount, count-1, count);
- if (prevCount == count)
- return true;
- count = prevCount;
- }
-
- return false;
-}
-
-
-FORCEINLINE void ReleaseWorkRequest(WorkRequest *workRequest) { ThreadpoolMgr::RecycleMemory( workRequest, ThreadpoolMgr::MEMTYPE_WorkRequest ); }
-typedef Wrapper< WorkRequest *, DoNothing, ReleaseWorkRequest > WorkRequestHolder;
-
-void UnManagedPerAppDomainTPCount::QueueUnmanagedWorkRequest(LPTHREAD_START_ROUTINE function, PVOID context)
-{
- CONTRACTL
- {
- THROWS;
- GC_TRIGGERS;
- MODE_ANY;
- }
- CONTRACTL_END;;
-
- _ASSERTE(!ThreadpoolMgr::UsePortableThreadPool());
-
-#ifndef DACCESS_COMPILE
- WorkRequestHolder pWorkRequest;
-
- //Note, ideally we would want to use our own queues instead of those in
- //the thread pool class. However, the queus in thread pool class have
- //caching support, that shares memory with other commonly used structures
- //in the VM thread pool implementation. So, we decided to leverage those.
-
- pWorkRequest = ThreadpoolMgr::MakeWorkRequest(function, context);
-
- //MakeWorkRequest should throw if unable to allocate memory
- _ASSERTE(pWorkRequest != NULL);
- PREFIX_ASSUME(pWorkRequest != NULL);
-
- if (ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context, ThreadPoolEnqueue))
- FireEtwThreadPoolEnqueue(pWorkRequest, GetClrInstanceId());
-
- m_lock.Init(LOCK_TYPE_DEFAULT);
-
- {
- SpinLock::Holder slh(&m_lock);
-
- ThreadpoolMgr::EnqueueWorkRequest(pWorkRequest);
- pWorkRequest.SuppressRelease();
- m_NumRequests++;
- }
-
- SetAppDomainRequestsActive();
-#endif //DACCESS_COMPILE
-}
-
-PVOID UnManagedPerAppDomainTPCount::DeQueueUnManagedWorkRequest(bool* lastOne)
-{
- CONTRACTL
- {
- NOTHROW;
- GC_TRIGGERS;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- _ASSERTE(!ThreadpoolMgr::UsePortableThreadPool());
-
- *lastOne = true;
-
- WorkRequest * pWorkRequest = ThreadpoolMgr::DequeueWorkRequest();
-
- if (pWorkRequest)
- {
- m_NumRequests--;
-
- if(m_NumRequests > 0)
- *lastOne = false;
- }
-
- return (PVOID) pWorkRequest;
-}
-
-//---------------------------------------------------------------------------
-//DispatchWorkItem manages dispatching of unmanaged work requests. It keeps
-//processing unmanaged requests for the "Quanta". Essentially this function is
-//a tight loop of dequeueing unmanaged work requests and dispatching them.
-//
-void UnManagedPerAppDomainTPCount::DispatchWorkItem(bool* foundWork, bool* wasNotRecalled)
-{
- _ASSERTE(!ThreadpoolMgr::UsePortableThreadPool());
-
-#ifndef DACCESS_COMPILE
- *foundWork = false;
- *wasNotRecalled = true;
-
- bool enableWorkerTracking = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_ThreadPool_EnableWorkerTracking) ? true : false;
-
- DWORD startTime;
- DWORD endTime;
-
- startTime = GetTickCount();
-
- //For all practical puposes, the unmanaged part of thread pool is treated
- //as a special appdomain for thread pool purposes. The same logic as the
- //one in managed code for dispatching thread pool requests is repeated here.
- //Namely we continue to process requests until eithere there are none, or
- //the "Quanta has expired". See threadpool.cs for the managed counterpart.
-
- WorkRequest * pWorkRequest=NULL;
- LPTHREAD_START_ROUTINE wrFunction;
- LPVOID wrContext;
-
- bool firstIteration = true;
- bool lastOne = false;
-
- while (*wasNotRecalled)
- {
- m_lock.Init(LOCK_TYPE_DEFAULT);
- {
- SpinLock::Holder slh(&m_lock);
- pWorkRequest = (WorkRequest*) DeQueueUnManagedWorkRequest(&lastOne);
- }
-
- if (NULL == pWorkRequest)
- break;
-
- if (firstIteration && !lastOne)
- SetAppDomainRequestsActive();
-
- firstIteration = false;
- *foundWork = true;
-
- if (GCHeapUtilities::IsGCInProgress(TRUE))
- {
- // GC is imminent, so wait until GC is complete before executing next request.
- // this reduces in-flight objects allocated right before GC, easing the GC's work
- GCHeapUtilities::WaitForGCCompletion(TRUE);
- }
-
- PREFIX_ASSUME(pWorkRequest != NULL);
- _ASSERTE(pWorkRequest);
-
- wrFunction = pWorkRequest->Function;
- wrContext = pWorkRequest->Context;
-
- if (ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context, ThreadPoolDequeue))
- FireEtwThreadPoolDequeue(pWorkRequest, GetClrInstanceId());
-
- ThreadpoolMgr::FreeWorkRequest(pWorkRequest);
-
- if (enableWorkerTracking)
- {
- ThreadpoolMgr::ReportThreadStatus(true);
- (wrFunction) (wrContext);
- ThreadpoolMgr::ReportThreadStatus(false);
- }
- else
- {
- (wrFunction) (wrContext);
- }
-
- ThreadpoolMgr::NotifyWorkItemCompleted();
- if (ThreadpoolMgr::ShouldAdjustMaxWorkersActive())
- {
- DangerousNonHostedSpinLockTryHolder tal(&ThreadpoolMgr::ThreadAdjustmentLock);
- if (tal.Acquired())
- {
- ThreadpoolMgr::AdjustMaxWorkersActive();
- }
- else
- {
- // the lock is held by someone else, so they will take care of this for us.
- }
- }
- *wasNotRecalled = ThreadpoolMgr::ShouldWorkerKeepRunning();
-
- Thread *pThread = GetThreadNULLOk();
- if (pThread)
- {
- _ASSERTE(!pThread->IsAbortRequested());
- pThread->InternalReset();
- }
-
- endTime = GetTickCount();
-
- if ((endTime - startTime) >= TP_QUANTUM)
- {
- break;
- }
- }
-
- // if we're exiting for any reason other than the queue being empty, then we need to make sure another thread
- // will visit us later.
- if (NULL != pWorkRequest)
- {
- SetAppDomainRequestsActive();
- }
-
-#endif //DACCESS_COMPILE
-}
-
-
-void ManagedPerAppDomainTPCount::SetAppDomainRequestsActive()
-{
- //This function should either be called by managed code or during AD unload, but before
- //the TpIndex is set to unused.
- //
- // Note that there is a separate count in managed code that stays in sync with this one over time.
- // The manage count is incremented before this one, and this one is decremented before the managed
- // one.
- //
-
- _ASSERTE(!ThreadpoolMgr::UsePortableThreadPool());
- _ASSERTE(m_index.m_dwIndex != UNUSED_THREADPOOL_INDEX);
-
-#ifndef DACCESS_COMPILE
- LONG count = VolatileLoad(&m_numRequestsPending);
- while (true)
- {
- LONG prev = InterlockedCompareExchange(&m_numRequestsPending, count+1, count);
- if (prev == count)
- {
- ThreadpoolMgr::MaybeAddWorkingWorker();
- ThreadpoolMgr::EnsureGateThreadRunning();
- break;
- }
- count = prev;
- }
-#endif
-}
-
-void ManagedPerAppDomainTPCount::ClearAppDomainRequestsActive()
-{
- LIMITED_METHOD_CONTRACT;
- _ASSERTE(!ThreadpoolMgr::UsePortableThreadPool());
-
- //This function should either be called by managed code or during AD unload, but before
- //the TpIndex is set to unused.
-
- _ASSERTE(m_index.m_dwIndex != UNUSED_THREADPOOL_INDEX);
-
- LONG count = VolatileLoad(&m_numRequestsPending);
- while (count > 0)
- {
- LONG prev = InterlockedCompareExchange(&m_numRequestsPending, 0, count);
- if (prev == count)
- break;
- count = prev;
- }
-}
-
-bool ManagedPerAppDomainTPCount::TakeActiveRequest()
-{
- LIMITED_METHOD_CONTRACT;
- _ASSERTE(!ThreadpoolMgr::UsePortableThreadPool());
-
- LONG count = VolatileLoad(&m_numRequestsPending);
- while (count > 0)
- {
- LONG prev = InterlockedCompareExchange(&m_numRequestsPending, count-1, count);
- if (prev == count)
- return true;
- count = prev;
- }
- return false;
-}
-
-#ifndef DACCESS_COMPILE
-
-//---------------------------------------------------------------------------
-//DispatchWorkItem makes sure the right exception handling frames are setup,
-//the thread is transitioned into the correct appdomain, and the right managed
-//callback is called.
-//
-void ManagedPerAppDomainTPCount::DispatchWorkItem(bool* foundWork, bool* wasNotRecalled)
-{
- _ASSERTE(!ThreadpoolMgr::UsePortableThreadPool());
-
- *foundWork = false;
- *wasNotRecalled = true;
-
- HRESULT hr;
- Thread * pThread = GetThreadNULLOk();
- if (pThread == NULL)
- {
- ClrFlsSetThreadType(ThreadType_Threadpool_Worker);
- pThread = SetupThreadNoThrow(&hr);
- if (pThread == NULL)
- {
- return;
- }
- }
-
- {
- CONTRACTL
- {
- MODE_PREEMPTIVE;
- THROWS;
- GC_TRIGGERS;
- }
- CONTRACTL_END;
-
- GCX_COOP();
-
- //
- // NOTE: there is a potential race between the time we retrieve the app
- // domain pointer, and the time which this thread enters the domain.
- //
- // To solve the race, we rely on the fact that there is a thread sync (via
- // GC) between releasing an app domain's handle, and destroying the
- // app domain. Thus it is important that we not go into preemptive gc mode
- // in that window.
- //
-
- {
- // This TPIndex may have been recycled since we chose it for workitem dispatch.
- // If so, the new AppDomain will necessarily have zero requests
- // pending (because the destruction of the previous AD that used this TPIndex
- // will have reset this object). We don't want to call into such an AppDomain.
- // TODO: fix this another way!
- // if (IsRequestPending())
- {
- ManagedThreadBase::ThreadPool(QueueUserWorkItemManagedCallback, wasNotRecalled);
- }
-
- if (pThread->IsAbortRequested())
- {
- // thread was aborted, and may not have had a chance to tell us it has work.
- ThreadpoolMgr::SetAppDomainRequestsActive();
- ThreadpoolMgr::QueueUserWorkItem(NULL,
- NULL,
- 0,
- FALSE);
- }
- }
-
- *foundWork = true;
- }
-}
-
-#endif // !DACCESS_COMPILE
diff --git a/src/coreclr/vm/threadpoolrequest.h b/src/coreclr/vm/threadpoolrequest.h
deleted file mode 100644
index f3cd4c49ccac60..00000000000000
--- a/src/coreclr/vm/threadpoolrequest.h
+++ /dev/null
@@ -1,269 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-//=========================================================================
-
-//
-// ThreadPoolRequest.h
-//
-
-//
-// This file contains definitions of classes needed to mainain per-appdomain
-// thread pool work requests. This is needed as unmanaged and managed work
-// requests are allocted, managed and dispatched in drastically different ways.
-// However, the scheduler need be aware of these differences, and it should
-// simply talk to a common interface for managing work request counts.
-//
-//=========================================================================
-
-#ifndef _THREADPOOL_REQUEST_H
-#define _THREADPOOL_REQUEST_H
-
-#include "util.hpp"
-
-#define TP_QUANTUM 2
-#define UNUSED_THREADPOOL_INDEX (DWORD)-1
-
-//--------------------------------------------------------------------------
-//IPerAppDomainTPCount is an interface for implementing per-appdomain thread
-//pool state. It's implementation should include logic to maintain work-counts,
-//notify thread pool class when work arrives or no work is left. Finally
-//there is logic to dipatch work items correctly in the right domain.
-//
-//Notes:
-//This class was designed to support both the managed and unmanaged uses
-//of thread pool. The unmananged part may directly be used through com
-//interfaces. The differences between the actual management of counts and
-//dispatching of work is quite different between the two. This interface
-//hides these differences to the thread scheduler implemented by the thread pool
-//class.
-//
-
-class IPerAppDomainTPCount{
-public:
- virtual void ResetState() = 0;
- virtual BOOL IsRequestPending() = 0;
-
- //This functions marks the beginning of requests queued for the domain.
- //It needs to notify the scheduler of work-arrival among other things.
- virtual void SetAppDomainRequestsActive() = 0;
-
- //This functions marks the end of requests queued for this domain.
- virtual void ClearAppDomainRequestsActive() = 0;
-
- //Clears the "active" flag if it was set, and returns whether it was set.
- virtual bool TakeActiveRequest() = 0;
-
- //Takes care of dispatching requests in the right domain.
- virtual void DispatchWorkItem(bool* foundWork, bool* wasNotRecalled) = 0;
- virtual void SetTPIndexUnused() = 0;
- virtual BOOL IsTPIndexUnused() = 0;
- virtual void SetTPIndex(TPIndex index) = 0;
-};
-
-typedef DPTR(IPerAppDomainTPCount) PTR_IPerAppDomainTPCount;
-
-#ifdef _MSC_VER
-// Disable this warning - we intentionally want __declspec(align()) to insert padding for us
-#pragma warning(disable: 4324) // structure was padded due to __declspec(align())
-#endif
-
-//--------------------------------------------------------------------------
-//ManagedPerAppDomainTPCount maintains per-appdomain thread pool state.
-//This class maintains the count of per-appdomain work-items queued by
-//ThreadPool.QueueUserWorkItem. It also dispatches threads in the appdomain
-//correctly by setting up the right exception handling frames etc.
-//
-//Note: The counts are not accurate, and neither do they need to be. The
-//actual work queue is in managed (implemented in threadpool.cs). This class
-//just provides heuristics to the thread pool scheduler, along with
-//synchronization to indicate start/end of requests to the scheduler.
-class ManagedPerAppDomainTPCount : public IPerAppDomainTPCount {
-public:
-
- ManagedPerAppDomainTPCount(TPIndex index) {ResetState(); m_index = index;}
-
- inline void ResetState()
- {
- LIMITED_METHOD_CONTRACT;
- VolatileStore(&m_numRequestsPending, (LONG)0);
- }
-
- inline BOOL IsRequestPending()
- {
- LIMITED_METHOD_CONTRACT;
-
- LONG count = VolatileLoad(&m_numRequestsPending);
- return count > 0;
- }
-
- void SetAppDomainRequestsActive();
- void ClearAppDomainRequestsActive();
- bool TakeActiveRequest();
-
- inline void SetTPIndex(TPIndex index)
- {
- LIMITED_METHOD_CONTRACT;
- //This function should be called during appdomain creation when no managed code
- //has started running yet. That implies, no requests should be pending
- //or dispatched to this structure yet.
-
- _ASSERTE(m_index.m_dwIndex == UNUSED_THREADPOOL_INDEX);
-
- m_index = index;
- }
-
- inline BOOL IsTPIndexUnused()
- {
- LIMITED_METHOD_CONTRACT;
- if (m_index.m_dwIndex == UNUSED_THREADPOOL_INDEX)
- {
- return TRUE;
- }
-
- return FALSE;
- }
-
- inline void SetTPIndexUnused()
- {
- WRAPPER_NO_CONTRACT;
- m_index.m_dwIndex = UNUSED_THREADPOOL_INDEX;
- }
-
- void DispatchWorkItem(bool* foundWork, bool* wasNotRecalled);
-
-private:
- TPIndex m_index;
- struct DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) {
- BYTE m_padding1[MAX_CACHE_LINE_SIZE - sizeof(LONG)];
- // Only use with VolatileLoad+VolatileStore+InterlockedCompareExchange
- LONG m_numRequestsPending;
- BYTE m_padding2[MAX_CACHE_LINE_SIZE];
- };
-};
-
-//--------------------------------------------------------------------------
-//UnManagedPerAppDomainTPCount maintains the thread pool state/counts for
-//unmanaged work requests. From thread pool point of view we treat unmanaged
-//requests as a special "appdomain". This helps in scheduling policies, and
-//follow same fairness policies as requests in other appdomains.
-class UnManagedPerAppDomainTPCount : public IPerAppDomainTPCount {
-public:
-
- UnManagedPerAppDomainTPCount()
- {
- LIMITED_METHOD_CONTRACT;
- ResetState();
- }
-
- inline void ResetState()
- {
- LIMITED_METHOD_CONTRACT;
- m_NumRequests = 0;
- VolatileStore(&m_outstandingThreadRequestCount, (LONG)0);
- }
-
- inline BOOL IsRequestPending()
- {
- LIMITED_METHOD_CONTRACT;
- return VolatileLoad(&m_outstandingThreadRequestCount) != (LONG)0 ? TRUE : FALSE;
- }
-
- void SetAppDomainRequestsActive();
-
- inline void ClearAppDomainRequestsActive()
- {
- LIMITED_METHOD_CONTRACT;
- VolatileStore(&m_outstandingThreadRequestCount, (LONG)0);
- }
-
- bool TakeActiveRequest();
-
- void QueueUnmanagedWorkRequest(LPTHREAD_START_ROUTINE function, PVOID context);
- PVOID DeQueueUnManagedWorkRequest(bool* lastOne);
-
- void DispatchWorkItem(bool* foundWork, bool* wasNotRecalled);
-
- inline void SetTPIndexUnused()
- {
- WRAPPER_NO_CONTRACT;
- _ASSERT(FALSE);
- }
-
- inline BOOL IsTPIndexUnused()
- {
- WRAPPER_NO_CONTRACT;
- _ASSERT(FALSE);
- return FALSE;
- }
-
- inline void SetTPIndex(TPIndex index)
- {
- WRAPPER_NO_CONTRACT;
- _ASSERT(FALSE);
- }
-
- inline ULONG GetNumRequests()
- {
- LIMITED_METHOD_CONTRACT;
- return VolatileLoad(&m_NumRequests);
- }
-
-private:
- SpinLock m_lock;
- ULONG m_NumRequests;
- struct DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) {
- BYTE m_padding1[MAX_CACHE_LINE_SIZE - sizeof(LONG)];
- // Only use with VolatileLoad+VolatileStore+InterlockedCompareExchange
- LONG m_outstandingThreadRequestCount;
- BYTE m_padding2[MAX_CACHE_LINE_SIZE];
- };
-};
-
-#ifdef _MSC_VER
-#pragma warning(default: 4324) // structure was padded due to __declspec(align())
-#endif
-
-//--------------------------------------------------------------------------
-//PerAppDomainTPCountList maintains the collection of per-appdomain thread
-//pool states. Per appdomain counts are added to the list during appdomain
-//creation inside the sdomain lock. The counts are reset during appdomain
-//unload after all the threads have
-//This class maintains the count of per-appdomain work-items queued by
-//ThreadPool.QueueUserWorkItem. It also dispatches threads in the appdomain
-//correctly by setting up the right exception handling frames etc.
-//
-//Note: The counts are not accurate, and neither do they need to be. The
-//actual work queue is in managed (implemented in threadpool.cs). This class
-//just provides heuristics to the thread pool scheduler, along with
-//synchronization to indicate start/end of requests to the scheduler.
-class PerAppDomainTPCountList{
-public:
- static void InitAppDomainIndexList();
- static void ResetAppDomainIndex(TPIndex index);
- static bool AreRequestsPendingInAnyAppDomains();
- static LONG GetAppDomainIndexForThreadpoolDispatch();
- static TPIndex AddNewTPIndex();
-
- inline static IPerAppDomainTPCount* GetPerAppdomainCount(TPIndex index)
- {
- return dac_cast(s_appDomainIndexList.Get(index.m_dwIndex-1));
- }
-
- inline static UnManagedPerAppDomainTPCount* GetUnmanagedTPCount()
- {
- return &s_unmanagedTPCount;
- }
-
-private:
- static DWORD FindFirstFreeTpEntry();
-
- static BYTE s_padding[MAX_CACHE_LINE_SIZE - sizeof(LONG)];
- DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) static LONG s_ADHint;
- DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) static UnManagedPerAppDomainTPCount s_unmanagedTPCount;
-
- //The list of all per-appdomain work-request counts.
- static ArrayListStatic s_appDomainIndexList;
-};
-
-#endif //_THREADPOOL_REQUEST_H
diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp
index 91b2f55d9ea33a..e1bc975673db28 100644
--- a/src/coreclr/vm/threads.cpp
+++ b/src/coreclr/vm/threads.cpp
@@ -20,7 +20,6 @@
#include "eeprofinterfaces.h"
#include "eeconfig.h"
#include "corhost.h"
-#include "win32threadpool.h"
#include "jitinterface.h"
#include "eventtrace.h"
#include "comutilnative.h"
@@ -34,7 +33,6 @@
#include "appdomain.inl"
#include "vmholder.h"
#include "exceptmacros.h"
-#include "win32threadpool.h"
#ifdef FEATURE_COMINTEROP
#include "runtimecallablewrapper.h"
@@ -136,8 +134,6 @@ PTR_ThreadLocalModule ThreadLocalBlock::GetTLMIfExists(MethodTable* pMT)
BOOL Thread::s_fCleanFinalizedThread = FALSE;
-UINT64 Thread::s_workerThreadPoolCompletionCountOverflow = 0;
-UINT64 Thread::s_ioThreadPoolCompletionCountOverflow = 0;
UINT64 Thread::s_monitorLockContentionCountOverflow = 0;
CrstStatic g_DeadlockAwareCrst;
@@ -1541,8 +1537,6 @@ Thread::Thread()
m_AbortRequestLock = 0;
m_fRudeAbortInitiated = FALSE;
- m_pIOCompletionContext = NULL;
-
#ifdef _DEBUG
m_fRudeAborted = FALSE;
m_dwAbortPoint = 0;
@@ -1600,8 +1594,6 @@ Thread::Thread()
m_sfEstablisherOfActualHandlerFrame.Clear();
#endif // FEATURE_EH_FUNCLETS
- m_workerThreadPoolCompletionCount = 0;
- m_ioThreadPoolCompletionCount = 0;
m_monitorLockContentionCount = 0;
m_pDomain = SystemDomain::System()->DefaultDomain();
@@ -1776,12 +1768,6 @@ void Thread::InitThread()
ThrowOutOfMemory();
}
}
-
- ret = Thread::AllocateIOCompletionContext();
- if (!ret)
- {
- ThrowOutOfMemory();
- }
}
// Allocate all the handles. When we are kicking of a new thread, we can call
@@ -1976,33 +1962,6 @@ BOOL Thread::HasStarted()
return FALSE;
}
-BOOL Thread::AllocateIOCompletionContext()
-{
- WRAPPER_NO_CONTRACT;
- PIOCompletionContext pIOC = new (nothrow) IOCompletionContext;
-
- if(pIOC != NULL)
- {
- pIOC->lpOverlapped = NULL;
- m_pIOCompletionContext = pIOC;
- return TRUE;
- }
- else
- {
- return FALSE;
- }
-}
-
-VOID Thread::FreeIOCompletionContext()
-{
- WRAPPER_NO_CONTRACT;
- if (m_pIOCompletionContext != NULL)
- {
- PIOCompletionContext pIOC = (PIOCompletionContext) m_pIOCompletionContext;
- delete pIOC;
- m_pIOCompletionContext = NULL;
- }
-}
void Thread::HandleThreadStartupFailure()
{
@@ -2676,8 +2635,6 @@ Thread::~Thread()
m_EventWait.CloseEvent();
}
- FreeIOCompletionContext();
-
if (m_OSContext)
delete m_OSContext;
@@ -5384,12 +5341,6 @@ BOOL ThreadStore::RemoveThread(Thread *target)
if (target->IsBackground())
s_pThreadStore->m_BackgroundThreadCount--;
- InterlockedExchangeAdd64(
- (LONGLONG *)&Thread::s_workerThreadPoolCompletionCountOverflow,
- target->m_workerThreadPoolCompletionCount);
- InterlockedExchangeAdd64(
- (LONGLONG *)&Thread::s_ioThreadPoolCompletionCountOverflow,
- target->m_ioThreadPoolCompletionCount);
InterlockedExchangeAdd64(
(LONGLONG *)&Thread::s_monitorLockContentionCountOverflow,
target->m_monitorLockContentionCount);
@@ -7581,13 +7532,6 @@ void ManagedThreadBase::KickOff(ADCallBackFcnType pTarget, LPVOID args)
ManagedThreadBase_FullTransition(pTarget, args, ManagedThread);
}
-// The IOCompletion, QueueUserWorkItem, RegisterWaitForSingleObject cases in the ThreadPool
-void ManagedThreadBase::ThreadPool(ADCallBackFcnType pTarget, LPVOID args)
-{
- WRAPPER_NO_CONTRACT;
- ManagedThreadBase_FullTransition(pTarget, args, ThreadPoolThread);
-}
-
// The Finalizer thread establishes exception handling at its base, but defers all the AppDomain
// transitions.
void ManagedThreadBase::FinalizerBase(ADCallBackFcnType pTarget)
@@ -7904,40 +7848,6 @@ UINT64 Thread::GetTotalCount(SIZE_T threadLocalCountOffset, UINT64 *overflowCoun
return total;
}
-UINT64 Thread::GetTotalThreadPoolCompletionCount()
-{
- CONTRACTL {
- NOTHROW;
- GC_TRIGGERS;
- }
- CONTRACTL_END;
-
- _ASSERTE(!ThreadpoolMgr::UsePortableThreadPoolForIO());
-
- bool usePortableThreadPool = ThreadpoolMgr::UsePortableThreadPool();
-
- // enumerate all threads, summing their local counts.
- ThreadStoreLockHolder tsl;
-
- UINT64 total = GetIOThreadPoolCompletionCountOverflow();
- if (!usePortableThreadPool)
- {
- total += GetWorkerThreadPoolCompletionCountOverflow();
- }
-
- Thread *pThread = NULL;
- while ((pThread = ThreadStore::GetAllThreadList(pThread, 0, 0)) != NULL)
- {
- if (!usePortableThreadPool)
- {
- total += pThread->m_workerThreadPoolCompletionCount;
- }
- total += pThread->m_ioThreadPoolCompletionCount;
- }
-
- return total;
-}
-
INT32 Thread::ResetManagedThreadObject(INT32 nPriority)
{
CONTRACTL {
diff --git a/src/coreclr/vm/threads.h b/src/coreclr/vm/threads.h
index 9b5d497734cc2f..b51804a471705d 100644
--- a/src/coreclr/vm/threads.h
+++ b/src/coreclr/vm/threads.h
@@ -3432,10 +3432,6 @@ class Thread
#endif // defined(PROFILING_SUPPORTED) || defined(PROFILING_SUPPORTED_DATA)
private:
- UINT32 m_workerThreadPoolCompletionCount;
- static UINT64 s_workerThreadPoolCompletionCountOverflow;
- UINT32 m_ioThreadPoolCompletionCount;
- static UINT64 s_ioThreadPoolCompletionCountOverflow;
UINT32 m_monitorLockContentionCount;
static UINT64 s_monitorLockContentionCountOverflow;
@@ -3489,38 +3485,6 @@ class Thread
static UINT64 GetTotalCount(SIZE_T threadLocalCountOffset, UINT64 *overflowCount);
public:
- static void IncrementWorkerThreadPoolCompletionCount(Thread *pThread)
- {
- WRAPPER_NO_CONTRACT;
- IncrementCount(pThread, offsetof(Thread, m_workerThreadPoolCompletionCount), &s_workerThreadPoolCompletionCountOverflow);
- }
-
- static UINT64 GetWorkerThreadPoolCompletionCountOverflow()
- {
- WRAPPER_NO_CONTRACT;
- return GetOverflowCount(&s_workerThreadPoolCompletionCountOverflow);
- }
-
- static UINT64 GetTotalWorkerThreadPoolCompletionCount()
- {
- WRAPPER_NO_CONTRACT;
- return GetTotalCount(offsetof(Thread, m_workerThreadPoolCompletionCount), &s_workerThreadPoolCompletionCountOverflow);
- }
-
- static void IncrementIOThreadPoolCompletionCount(Thread *pThread)
- {
- WRAPPER_NO_CONTRACT;
- IncrementCount(pThread, offsetof(Thread, m_ioThreadPoolCompletionCount), &s_ioThreadPoolCompletionCountOverflow);
- }
-
- static UINT64 GetIOThreadPoolCompletionCountOverflow()
- {
- WRAPPER_NO_CONTRACT;
- return GetOverflowCount(&s_ioThreadPoolCompletionCountOverflow);
- }
-
- static UINT64 GetTotalThreadPoolCompletionCount();
-
static void IncrementMonitorLockContentionCount(Thread *pThread)
{
WRAPPER_NO_CONTRACT;
@@ -4187,20 +4151,6 @@ class Thread
m_fAllowProfilerCallbacks = fValue;
}
-private:
- //
- //This context is used for optimizations on I/O thread pool thread. In case the
- //overlapped structure is from a different appdomain, it is stored in this structure
- //to be processed later correctly by entering the right domain.
- PVOID m_pIOCompletionContext;
- BOOL AllocateIOCompletionContext();
- VOID FreeIOCompletionContext();
-public:
- inline PVOID GetIOCompletionContext()
- {
- return m_pIOCompletionContext;
- }
-
private:
// Inside a host, we don't own a thread handle, and we avoid DuplicateHandle call.
// If a thread is dying after we obtain the thread handle, our SuspendThread may fail
@@ -6005,10 +5955,6 @@ struct ManagedThreadBase
static void KickOff(ADCallBackFcnType pTarget,
LPVOID args);
- // The IOCompletion, QueueUserWorkItem, RegisterWaitForSingleObject cases in
- // the ThreadPool
- static void ThreadPool(ADCallBackFcnType pTarget, LPVOID args);
-
// The Finalizer thread uses this path
static void FinalizerBase(ADCallBackFcnType pTarget);
};
diff --git a/src/coreclr/vm/tieredcompilation.cpp b/src/coreclr/vm/tieredcompilation.cpp
index 4e4da913e86641..3b15100a34fb66 100644
--- a/src/coreclr/vm/tieredcompilation.cpp
+++ b/src/coreclr/vm/tieredcompilation.cpp
@@ -10,7 +10,6 @@
#include "common.h"
#include "excep.h"
#include "log.h"
-#include "win32threadpool.h"
#include "threadsuspend.h"
#include "tieredcompilation.h"
diff --git a/src/coreclr/vm/vars.hpp b/src/coreclr/vm/vars.hpp
index 628cb97deb49a8..4698d840a84c12 100644
--- a/src/coreclr/vm/vars.hpp
+++ b/src/coreclr/vm/vars.hpp
@@ -621,25 +621,6 @@ GVAL_DECL(SIZE_T, g_runtimeVirtualSize);
#define MAXULONGLONG UI64(0xffffffffffffffff)
#endif
-struct TPIndex
-{
- DWORD m_dwIndex;
- TPIndex ()
- : m_dwIndex(0)
- {}
- explicit TPIndex (DWORD id)
- : m_dwIndex(id)
- {}
- BOOL operator==(const TPIndex& tpindex) const
- {
- return m_dwIndex == tpindex.m_dwIndex;
- }
- BOOL operator!=(const TPIndex& tpindex) const
- {
- return m_dwIndex != tpindex.m_dwIndex;
- }
-};
-
// Every Module is assigned a ModuleIndex, regardless of whether the Module is domain
// neutral or domain specific. When a domain specific Module is unloaded, its ModuleIndex
// can be reused.
diff --git a/src/coreclr/vm/win32threadpool.cpp b/src/coreclr/vm/win32threadpool.cpp
deleted file mode 100644
index 80e5e5db5d7855..00000000000000
--- a/src/coreclr/vm/win32threadpool.cpp
+++ /dev/null
@@ -1,4276 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-
-/*++
-
-Module Name:
-
- Win32ThreadPool.cpp
-
-Abstract:
-
- This module implements Threadpool support using Win32 APIs
-
-
-Revision History:
- December 1999 - Created
-
---*/
-
-#include "common.h"
-#include "log.h"
-#include "threadpoolrequest.h"
-#include "win32threadpool.h"
-#include "delegateinfo.h"
-#include "eeconfig.h"
-#include "dbginterface.h"
-#include "corhost.h"
-#include "eventtrace.h"
-#include "threads.h"
-#include "appdomain.inl"
-#include "nativeoverlapped.h"
-#include "hillclimbing.h"
-#include "configuration.h"
-
-
-#ifndef TARGET_UNIX
-#ifndef DACCESS_COMPILE
-
-// APIs that must be accessed through dynamic linking.
-typedef int (WINAPI *NtQueryInformationThreadProc) (
- HANDLE ThreadHandle,
- THREADINFOCLASS ThreadInformationClass,
- PVOID ThreadInformation,
- ULONG ThreadInformationLength,
- PULONG ReturnLength);
-NtQueryInformationThreadProc g_pufnNtQueryInformationThread = NULL;
-
-typedef int (WINAPI *NtQuerySystemInformationProc) (
- SYSTEM_INFORMATION_CLASS SystemInformationClass,
- PVOID SystemInformation,
- ULONG SystemInformationLength,
- PULONG ReturnLength OPTIONAL);
-NtQuerySystemInformationProc g_pufnNtQuerySystemInformation = NULL;
-
-typedef HANDLE (WINAPI * CreateWaitableTimerExProc) (
- LPSECURITY_ATTRIBUTES lpTimerAttributes,
- LPCTSTR lpTimerName,
- DWORD dwFlags,
- DWORD dwDesiredAccess);
-CreateWaitableTimerExProc g_pufnCreateWaitableTimerEx = NULL;
-
-typedef BOOL (WINAPI * SetWaitableTimerExProc) (
- HANDLE hTimer,
- const LARGE_INTEGER *lpDueTime,
- LONG lPeriod,
- PTIMERAPCROUTINE pfnCompletionRoutine,
- LPVOID lpArgToCompletionRoutine,
- void* WakeContext, //should be PREASON_CONTEXT, but it's not defined for us (and we don't use it)
- ULONG TolerableDelay);
-SetWaitableTimerExProc g_pufnSetWaitableTimerEx = NULL;
-
-#endif // !DACCESS_COMPILE
-#endif // !TARGET_UNIX
-
-BOOL ThreadpoolMgr::InitCompletionPortThreadpool = FALSE;
-HANDLE ThreadpoolMgr::GlobalCompletionPort; // used for binding io completions on file handles
-
-SVAL_IMPL(ThreadpoolMgr::ThreadCounter,ThreadpoolMgr,CPThreadCounter);
-
-SVAL_IMPL_INIT(LONG,ThreadpoolMgr,MaxLimitTotalCPThreads,1000); // = MaxLimitCPThreadsPerCPU * number of CPUS
-SVAL_IMPL(LONG,ThreadpoolMgr,MinLimitTotalCPThreads);
-SVAL_IMPL(LONG,ThreadpoolMgr,MaxFreeCPThreads); // = MaxFreeCPThreadsPerCPU * Number of CPUS
-
-// Cacheline aligned, hot variable
-DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) SVAL_IMPL(ThreadpoolMgr::ThreadCounter, ThreadpoolMgr, WorkerCounter);
-
-SVAL_IMPL(LONG,ThreadpoolMgr,MinLimitTotalWorkerThreads); // = MaxLimitCPThreadsPerCPU * number of CPUS
-SVAL_IMPL(LONG,ThreadpoolMgr,MaxLimitTotalWorkerThreads); // = MaxLimitCPThreadsPerCPU * number of CPUS
-
-SVAL_IMPL(LONG,ThreadpoolMgr,cpuUtilization);
-
-HillClimbing ThreadpoolMgr::HillClimbingInstance;
-
-// Cacheline aligned, 3 hot variables updated in a group
-DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) LONG ThreadpoolMgr::PriorCompletedWorkRequests = 0;
-DWORD ThreadpoolMgr::PriorCompletedWorkRequestsTime;
-DWORD ThreadpoolMgr::NextCompletedWorkRequestsTime;
-
-LARGE_INTEGER ThreadpoolMgr::CurrentSampleStartTime;
-
-unsigned int ThreadpoolMgr::WorkerThreadSpinLimit;
-bool ThreadpoolMgr::IsHillClimbingDisabled;
-int ThreadpoolMgr::ThreadAdjustmentInterval;
-
-#define INVALID_HANDLE ((HANDLE) -1)
-#define NEW_THREAD_THRESHOLD 7 // Number of requests outstanding before we start a new thread
-#define CP_THREAD_PENDINGIO_WAIT 5000 // polling interval when thread is retired but has a pending io
-#define GATE_THREAD_DELAY 500 /*milliseconds*/
-#define GATE_THREAD_DELAY_TOLERANCE 50 /*milliseconds*/
-#define DELAY_BETWEEN_SUSPENDS (5000 + GATE_THREAD_DELAY) // time to delay between suspensions
-
-Volatile ThreadpoolMgr::Initialization = 0; // indicator of whether the threadpool is initialized.
-
-bool ThreadpoolMgr::s_usePortableThreadPool = false;
-bool ThreadpoolMgr::s_usePortableThreadPoolForIO = false;
-
-// Cacheline aligned, hot variable
-DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) unsigned int ThreadpoolMgr::LastDequeueTime; // used to determine if work items are getting thread starved
-
-SPTR_IMPL(WorkRequest,ThreadpoolMgr,WorkRequestHead); // Head of work request queue
-SPTR_IMPL(WorkRequest,ThreadpoolMgr,WorkRequestTail); // Head of work request queue
-
-//unsigned int ThreadpoolMgr::LastCpuSamplingTime=0; // last time cpu utilization was sampled by gate thread
-unsigned int ThreadpoolMgr::LastCPThreadCreation=0; // last time a completion port thread was created
-unsigned int ThreadpoolMgr::NumberOfProcessors; // = NumberOfWorkerThreads - no. of blocked threads
-
-
-CrstStatic ThreadpoolMgr::WorkerCriticalSection;
-CLREvent * ThreadpoolMgr::RetiredCPWakeupEvent; // wakeup event for completion port threads
-CrstStatic ThreadpoolMgr::WaitThreadsCriticalSection;
-ThreadpoolMgr::LIST_ENTRY ThreadpoolMgr::WaitThreadsHead;
-
-CLRLifoSemaphore* ThreadpoolMgr::WorkerSemaphore;
-CLRLifoSemaphore* ThreadpoolMgr::RetiredWorkerSemaphore;
-
-// Cacheline aligned, hot variable
-DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) LONG ThreadpoolMgr::GateThreadStatus=GATE_THREAD_STATUS_NOT_RUNNING;
-
-// Move out of from preceding variables' cache line
-DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) ThreadpoolMgr::RecycledListsWrapper ThreadpoolMgr::RecycledLists;
-
-BOOL ThreadpoolMgr::IsApcPendingOnWaitThread = FALSE;
-
-#ifndef DACCESS_COMPILE
-
-// Macros for inserting/deleting from doubly linked list
-
-#define InitializeListHead(ListHead) (\
- (ListHead)->Flink = (ListHead)->Blink = (ListHead))
-
-//
-// these are named the same as slightly different macros in the NT headers
-//
-#undef RemoveHeadList
-#undef RemoveEntryList
-#undef InsertTailList
-#undef InsertHeadList
-
-#define RemoveHeadList(ListHead,FirstEntry) \
- {\
- FirstEntry = (LIST_ENTRY*) (ListHead)->Flink;\
- ((LIST_ENTRY*)FirstEntry->Flink)->Blink = (ListHead);\
- (ListHead)->Flink = FirstEntry->Flink;\
- }
-
-#define RemoveEntryList(Entry) {\
- LIST_ENTRY* _EX_Entry;\
- _EX_Entry = (Entry);\
- ((LIST_ENTRY*) _EX_Entry->Blink)->Flink = _EX_Entry->Flink;\
- ((LIST_ENTRY*) _EX_Entry->Flink)->Blink = _EX_Entry->Blink;\
- }
-
-#define InsertTailList(ListHead,Entry) \
- (Entry)->Flink = (ListHead);\
- (Entry)->Blink = (ListHead)->Blink;\
- ((LIST_ENTRY*)(ListHead)->Blink)->Flink = (Entry);\
- (ListHead)->Blink = (Entry);
-
-#define InsertHeadList(ListHead,Entry) {\
- LIST_ENTRY* _EX_Flink;\
- LIST_ENTRY* _EX_ListHead;\
- _EX_ListHead = (LIST_ENTRY*)(ListHead);\
- _EX_Flink = (LIST_ENTRY*) _EX_ListHead->Flink;\
- (Entry)->Flink = _EX_Flink;\
- (Entry)->Blink = _EX_ListHead;\
- _EX_Flink->Blink = (Entry);\
- _EX_ListHead->Flink = (Entry);\
- }
-
-#define IsListEmpty(ListHead) \
- ((ListHead)->Flink == (ListHead))
-
-#define SetLastHRError(hr) \
- if (HRESULT_FACILITY(hr) == FACILITY_WIN32)\
- SetLastError(HRESULT_CODE(hr));\
- else \
- SetLastError(ERROR_INVALID_DATA);\
-
-/************************************************************************/
-
-void ThreadpoolMgr::RecycledListsWrapper::Initialize( unsigned int numProcs )
-{
- CONTRACTL
- {
- THROWS;
- MODE_ANY;
- GC_NOTRIGGER;
- }
- CONTRACTL_END;
-
- pRecycledListPerProcessor = new RecycledListInfo[numProcs][MEMTYPE_COUNT];
-}
-
-//--//
-
-void ThreadpoolMgr::EnsureInitialized()
-{
- CONTRACTL
- {
- THROWS; // EnsureInitializedSlow can throw
- MODE_ANY;
- GC_NOTRIGGER;
- }
- CONTRACTL_END;
-
- if (IsInitialized())
- return;
-
- EnsureInitializedSlow();
-}
-
-NOINLINE void ThreadpoolMgr::EnsureInitializedSlow()
-{
- CONTRACTL
- {
- THROWS; // Initialize can throw
- MODE_ANY;
- GC_NOTRIGGER;
- }
- CONTRACTL_END;
-
- DWORD dwSwitchCount = 0;
-
-retry:
- if (InterlockedCompareExchange(&Initialization, 1, 0) == 0)
- {
- if (Initialize())
- Initialization = -1;
- else
- {
- Initialization = 0;
- COMPlusThrowOM();
- }
- }
- else // someone has already begun initializing.
- {
- // wait until it finishes
- while (Initialization != -1)
- {
- __SwitchToThread(0, ++dwSwitchCount);
- goto retry;
- }
- }
-}
-
-DWORD GetDefaultMaxLimitWorkerThreads(DWORD minLimit)
-{
- CONTRACTL
- {
- MODE_ANY;
- GC_NOTRIGGER;
- NOTHROW;
- }
- CONTRACTL_END;
-
- _ASSERTE(!ThreadpoolMgr::UsePortableThreadPool());
-
- //
- // We determine the max limit for worker threads as follows:
- //
- // 1) It must be at least MinLimitTotalWorkerThreads
- // 2) It must be no greater than (half the virtual address space)/(thread stack size)
- // 3) It must be <= MaxPossibleWorkerThreads
- //
- // TODO: what about CP threads? Can they follow a similar plan? How do we allocate
- // thread counts between the two kinds of threads?
- //
- SIZE_T stackReserveSize = 0;
- Thread::GetProcessDefaultStackSize(&stackReserveSize, NULL);
-
- ULONGLONG halfVirtualAddressSpace;
-
- MEMORYSTATUSEX memStats;
- memStats.dwLength = sizeof(memStats);
- if (GlobalMemoryStatusEx(&memStats))
- {
- halfVirtualAddressSpace = memStats.ullTotalVirtual / 2;
- }
- else
- {
- //assume the normal Win32 32-bit virtual address space
- halfVirtualAddressSpace = 0x000000007FFE0000ull / 2;
- }
-
- ULONGLONG limit = halfVirtualAddressSpace / stackReserveSize;
- limit = max(limit, (ULONGLONG)minLimit);
- limit = min(limit, (ULONGLONG)ThreadpoolMgr::ThreadCounter::MaxPossibleCount);
-
- _ASSERTE(FitsIn(limit));
- return (DWORD)limit;
-}
-
-DWORD GetForceMinWorkerThreadsValue()
-{
- WRAPPER_NO_CONTRACT;
- _ASSERTE(!ThreadpoolMgr::UsePortableThreadPool());
-
- return Configuration::GetKnobDWORDValue(W("System.Threading.ThreadPool.MinThreads"), CLRConfig::INTERNAL_ThreadPool_ForceMinWorkerThreads);
-}
-
-DWORD GetForceMaxWorkerThreadsValue()
-{
- WRAPPER_NO_CONTRACT;
- _ASSERTE(!ThreadpoolMgr::UsePortableThreadPool());
-
- return Configuration::GetKnobDWORDValue(W("System.Threading.ThreadPool.MaxThreads"), CLRConfig::INTERNAL_ThreadPool_ForceMaxWorkerThreads);
-}
-
-BOOL ThreadpoolMgr::Initialize()
-{
- CONTRACTL
- {
- THROWS;
- MODE_ANY;
- GC_NOTRIGGER;
- INJECT_FAULT(COMPlusThrowOM());
- }
- CONTRACTL_END;
-
- BOOL bRet = FALSE;
- BOOL bExceptionCaught = FALSE;
-
- NumberOfProcessors = GetCurrentProcessCpuCount();
- InitPlatformVariables();
-
- EX_TRY
- {
- if (!UsePortableThreadPool())
- {
- WorkerThreadSpinLimit = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_ThreadPool_UnfairSemaphoreSpinLimit);
- IsHillClimbingDisabled = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_HillClimbing_Disable) != 0;
- ThreadAdjustmentInterval = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_HillClimbing_SampleIntervalLow);
-
- WaitThreadsCriticalSection.Init(CrstThreadpoolWaitThreads);
- }
- if (!UsePortableThreadPoolForIO())
- {
- WorkerCriticalSection.Init(CrstThreadpoolWorker);
- }
-
- if (!UsePortableThreadPool())
- {
- // initialize WaitThreadsHead
- InitializeListHead(&WaitThreadsHead);
- }
-
- if (!UsePortableThreadPoolForIO())
- {
- RetiredCPWakeupEvent = new CLREvent();
- RetiredCPWakeupEvent->CreateAutoEvent(FALSE);
- _ASSERTE(RetiredCPWakeupEvent->IsValid());
- }
-
- if (!UsePortableThreadPool())
- {
- WorkerSemaphore = new CLRLifoSemaphore();
- WorkerSemaphore->Create(0, ThreadCounter::MaxPossibleCount);
-
- RetiredWorkerSemaphore = new CLRLifoSemaphore();
- RetiredWorkerSemaphore->Create(0, ThreadCounter::MaxPossibleCount);
- }
-
-#ifndef TARGET_UNIX
- //ThreadPool_CPUGroup
- if (CPUGroupInfo::CanEnableThreadUseAllCpuGroups())
- RecycledLists.Initialize( CPUGroupInfo::GetNumActiveProcessors() );
- else
- RecycledLists.Initialize( g_SystemInfo.dwNumberOfProcessors );
-#else // !TARGET_UNIX
- RecycledLists.Initialize( PAL_GetTotalCpuCount() );
-#endif // !TARGET_UNIX
- }
- EX_CATCH
- {
- if (!UsePortableThreadPoolForIO() && RetiredCPWakeupEvent)
- {
- delete RetiredCPWakeupEvent;
- RetiredCPWakeupEvent = NULL;
- }
-
- // Note: It is fine to call Destroy on uninitialized critical sections
- if (!UsePortableThreadPool())
- {
- WaitThreadsCriticalSection.Destroy();
- }
- if (!UsePortableThreadPoolForIO())
- {
- WorkerCriticalSection.Destroy();
- }
-
- bExceptionCaught = TRUE;
- }
- EX_END_CATCH(SwallowAllExceptions);
-
- if (bExceptionCaught)
- {
- goto end;
- }
-
- if (!UsePortableThreadPool())
- {
- // initialize Worker thread settings
- DWORD forceMin;
- forceMin = GetForceMinWorkerThreadsValue();
- MinLimitTotalWorkerThreads = forceMin > 0 ? (LONG)forceMin : (LONG)NumberOfProcessors;
-
- DWORD forceMax;
- forceMax = GetForceMaxWorkerThreadsValue();
- MaxLimitTotalWorkerThreads = forceMax > 0 ? (LONG)forceMax : (LONG)GetDefaultMaxLimitWorkerThreads(MinLimitTotalWorkerThreads);
-
- ThreadCounter::Counts counts;
- counts.NumActive = 0;
- counts.NumWorking = 0;
- counts.NumRetired = 0;
- counts.MaxWorking = MinLimitTotalWorkerThreads;
- WorkerCounter.counts.AsLongLong = counts.AsLongLong;
- }
-
- if (!UsePortableThreadPoolForIO())
- {
- // initialize CP thread settings
- MinLimitTotalCPThreads = NumberOfProcessors;
-
- // Use volatile store to guarantee make the value visible to the DAC (the store can be optimized out otherwise)
- VolatileStoreWithoutBarrier(&MaxFreeCPThreads, NumberOfProcessors*MaxFreeCPThreadsPerCPU);
-
- ThreadCounter::Counts counts;
- counts.NumActive = 0;
- counts.NumWorking = 0;
- counts.NumRetired = 0;
- counts.MaxWorking = MinLimitTotalCPThreads;
- CPThreadCounter.counts.AsLongLong = counts.AsLongLong;
-
-#ifndef TARGET_UNIX
- {
- GlobalCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
- NULL,
- 0, /*ignored for invalid handle value*/
- NumberOfProcessors);
- }
-#endif // !TARGET_UNIX
- }
-
- if (!UsePortableThreadPool())
- {
- HillClimbingInstance.Initialize();
- }
-
- bRet = TRUE;
-end:
- return bRet;
-}
-
-void ThreadpoolMgr::InitPlatformVariables()
-{
- CONTRACTL
- {
- NOTHROW;
- MODE_ANY;
- GC_NOTRIGGER;
- }
- CONTRACTL_END;
-
-#ifndef TARGET_UNIX
- HINSTANCE hNtDll;
- HINSTANCE hCoreSynch = nullptr;
- {
- CONTRACT_VIOLATION(GCViolation|FaultViolation);
- hNtDll = CLRLoadLibrary(W("ntdll.dll"));
- _ASSERTE(hNtDll);
- if (!UsePortableThreadPool())
- {
- hCoreSynch = CLRLoadLibrary(W("api-ms-win-core-synch-l1-1-0.dll"));
- _ASSERTE(hCoreSynch);
- }
- }
-
- // These APIs must be accessed via dynamic binding since they may be removed in future
- // OS versions.
- g_pufnNtQueryInformationThread = (NtQueryInformationThreadProc)GetProcAddress(hNtDll,"NtQueryInformationThread");
- g_pufnNtQuerySystemInformation = (NtQuerySystemInformationProc)GetProcAddress(hNtDll,"NtQuerySystemInformation");
-
- if (!UsePortableThreadPool())
- {
- // These APIs are only supported on newer Windows versions
- g_pufnCreateWaitableTimerEx = (CreateWaitableTimerExProc)GetProcAddress(hCoreSynch, "CreateWaitableTimerExW");
- g_pufnSetWaitableTimerEx = (SetWaitableTimerExProc)GetProcAddress(hCoreSynch, "SetWaitableTimerEx");
- }
-#endif
-}
-
-bool ThreadpoolMgr::CanSetMinIOCompletionThreads(DWORD ioCompletionThreads)
-{
- WRAPPER_NO_CONTRACT;
- _ASSERTE(UsePortableThreadPool());
- _ASSERTE(!UsePortableThreadPoolForIO());
-
- EnsureInitialized();
-
- // The lock used by SetMinThreads() and SetMaxThreads() is not taken here, the caller is expected to synchronize between
- // them. The conditions here should be the same as in the corresponding Set function.
- return ioCompletionThreads <= (DWORD)MaxLimitTotalCPThreads;
-}
-
-bool ThreadpoolMgr::CanSetMaxIOCompletionThreads(DWORD ioCompletionThreads)
-{
- WRAPPER_NO_CONTRACT;
- _ASSERTE(UsePortableThreadPool());
- _ASSERTE(!UsePortableThreadPoolForIO());
- _ASSERTE(ioCompletionThreads != 0);
-
- EnsureInitialized();
-
- // The lock used by SetMinThreads() and SetMaxThreads() is not taken here, the caller is expected to synchronize between
- // them. The conditions here should be the same as in the corresponding Set function.
- return ioCompletionThreads >= (DWORD)MinLimitTotalCPThreads;
-}
-
-BOOL ThreadpoolMgr::SetMaxThreadsHelper(DWORD MaxWorkerThreads,
- DWORD MaxIOCompletionThreads)
-{
- CONTRACTL
- {
- THROWS; // Crst can throw and toggle GC mode
- MODE_ANY;
- GC_TRIGGERS;
- }
- CONTRACTL_END;
-
- _ASSERTE(!UsePortableThreadPoolForIO());
-
- BOOL result = FALSE;
-
- // doesn't need to be WorkerCS, but using it to avoid race condition between setting min and max, and didn't want to create a new CS.
- CrstHolder csh(&WorkerCriticalSection);
-
- bool usePortableThreadPool = UsePortableThreadPool();
- if ((
- usePortableThreadPool ||
- (
- MaxWorkerThreads >= (DWORD)MinLimitTotalWorkerThreads &&
- MaxWorkerThreads != 0
- )
- ) &&
- MaxIOCompletionThreads >= (DWORD)MinLimitTotalCPThreads &&
- MaxIOCompletionThreads != 0)
- {
- if (!usePortableThreadPool && GetForceMaxWorkerThreadsValue() == 0)
- {
- MaxLimitTotalWorkerThreads = min(MaxWorkerThreads, (DWORD)ThreadCounter::MaxPossibleCount);
-
- ThreadCounter::Counts counts = WorkerCounter.GetCleanCounts();
- while (counts.MaxWorking > MaxLimitTotalWorkerThreads)
- {
- ThreadCounter::Counts newCounts = counts;
- newCounts.MaxWorking = MaxLimitTotalWorkerThreads;
-
- ThreadCounter::Counts oldCounts = WorkerCounter.CompareExchangeCounts(newCounts, counts);
- if (oldCounts == counts)
- counts = newCounts;
- else
- counts = oldCounts;
- }
- }
-
- MaxLimitTotalCPThreads = min(MaxIOCompletionThreads, (DWORD)ThreadCounter::MaxPossibleCount);
-
- result = TRUE;
- }
-
- return result;
- }
-
-/************************************************************************/
-BOOL ThreadpoolMgr::SetMaxThreads(DWORD MaxWorkerThreads,
- DWORD MaxIOCompletionThreads)
-{
- CONTRACTL
- {
- THROWS; // SetMaxThreadsHelper can throw and toggle GC mode
- MODE_ANY;
- GC_TRIGGERS;
- }
- CONTRACTL_END;
-
- _ASSERTE(!UsePortableThreadPoolForIO());
-
- EnsureInitialized();
-
- return SetMaxThreadsHelper(MaxWorkerThreads, MaxIOCompletionThreads);
-}
-
-BOOL ThreadpoolMgr::GetMaxThreads(DWORD* MaxWorkerThreads,
- DWORD* MaxIOCompletionThreads)
-{
- LIMITED_METHOD_CONTRACT;
- _ASSERTE(!UsePortableThreadPoolForIO());
-
- if (!MaxWorkerThreads || !MaxIOCompletionThreads)
- {
- SetLastHRError(ERROR_INVALID_DATA);
- return FALSE;
- }
-
- EnsureInitialized();
-
- *MaxWorkerThreads = UsePortableThreadPool() ? 1 : (DWORD)MaxLimitTotalWorkerThreads;
- *MaxIOCompletionThreads = MaxLimitTotalCPThreads;
- return TRUE;
-}
-
-BOOL ThreadpoolMgr::SetMinThreads(DWORD MinWorkerThreads,
- DWORD MinIOCompletionThreads)
-{
- CONTRACTL
- {
- THROWS; // Crst can throw and toggle GC mode
- MODE_ANY;
- GC_TRIGGERS;
- }
- CONTRACTL_END;
-
- _ASSERTE(!UsePortableThreadPoolForIO());
-
- EnsureInitialized();
-
- // doesn't need to be WorkerCS, but using it to avoid race condition between setting min and max, and didn't want to create a new CS.
- CrstHolder csh(&WorkerCriticalSection);
-
- BOOL init_result = FALSE;
-
- bool usePortableThreadPool = UsePortableThreadPool();
- if ((
- usePortableThreadPool ||
- (
- MinWorkerThreads >= 0 &&
- MinWorkerThreads <= (DWORD) MaxLimitTotalWorkerThreads
- )
- ) &&
- MinIOCompletionThreads >= 0 &&
- MinIOCompletionThreads <= (DWORD) MaxLimitTotalCPThreads)
- {
- if (!usePortableThreadPool && GetForceMinWorkerThreadsValue() == 0)
- {
- MinLimitTotalWorkerThreads = max(1, min(MinWorkerThreads, (DWORD)ThreadCounter::MaxPossibleCount));
-
- ThreadCounter::Counts counts = WorkerCounter.GetCleanCounts();
- while (counts.MaxWorking < MinLimitTotalWorkerThreads)
- {
- ThreadCounter::Counts newCounts = counts;
- newCounts.MaxWorking = MinLimitTotalWorkerThreads;
-
- ThreadCounter::Counts oldCounts = WorkerCounter.CompareExchangeCounts(newCounts, counts);
- if (oldCounts == counts)
- {
- counts = newCounts;
-
- // if we increased the limit, and there are pending workitems, we need
- // to dispatch a thread to process the work.
- if (newCounts.MaxWorking > oldCounts.MaxWorking &&
- PerAppDomainTPCountList::AreRequestsPendingInAnyAppDomains())
- {
- MaybeAddWorkingWorker();
- }
- }
- else
- {
- counts = oldCounts;
- }
- }
- }
-
- MinLimitTotalCPThreads = max(1, min(MinIOCompletionThreads, (DWORD)ThreadCounter::MaxPossibleCount));
-
- init_result = TRUE;
- }
-
- return init_result;
-}
-
-BOOL ThreadpoolMgr::GetMinThreads(DWORD* MinWorkerThreads,
- DWORD* MinIOCompletionThreads)
-{
- LIMITED_METHOD_CONTRACT;
- _ASSERTE(!UsePortableThreadPoolForIO());
-
- if (!MinWorkerThreads || !MinIOCompletionThreads)
- {
- SetLastHRError(ERROR_INVALID_DATA);
- return FALSE;
- }
-
- EnsureInitialized();
-
- *MinWorkerThreads = UsePortableThreadPool() ? 1 : (DWORD)MinLimitTotalWorkerThreads;
- *MinIOCompletionThreads = MinLimitTotalCPThreads;
- return TRUE;
-}
-
-BOOL ThreadpoolMgr::GetAvailableThreads(DWORD* AvailableWorkerThreads,
- DWORD* AvailableIOCompletionThreads)
-{
- LIMITED_METHOD_CONTRACT;
- _ASSERTE(!UsePortableThreadPoolForIO());
-
- if (!AvailableWorkerThreads || !AvailableIOCompletionThreads)
- {
- SetLastHRError(ERROR_INVALID_DATA);
- return FALSE;
- }
-
- EnsureInitialized();
-
- if (UsePortableThreadPool())
- {
- *AvailableWorkerThreads = 0;
- }
- else
- {
- ThreadCounter::Counts counts = WorkerCounter.GetCleanCounts();
-
- if (MaxLimitTotalWorkerThreads < counts.NumActive)
- *AvailableWorkerThreads = 0;
- else
- *AvailableWorkerThreads = MaxLimitTotalWorkerThreads - counts.NumWorking;
- }
-
- ThreadCounter::Counts counts = CPThreadCounter.GetCleanCounts();
- if (MaxLimitTotalCPThreads < counts.NumActive)
- *AvailableIOCompletionThreads = counts.NumActive - counts.NumWorking;
- else
- *AvailableIOCompletionThreads = MaxLimitTotalCPThreads - counts.NumWorking;
- return TRUE;
-}
-
-INT32 ThreadpoolMgr::GetThreadCount()
-{
- WRAPPER_NO_CONTRACT;
- _ASSERTE(!UsePortableThreadPoolForIO());
-
- if (!IsInitialized())
- {
- return 0;
- }
-
- INT32 workerThreadCount = UsePortableThreadPool() ? 0 : WorkerCounter.DangerousGetDirtyCounts().NumActive;
- return workerThreadCount + CPThreadCounter.DangerousGetDirtyCounts().NumActive;
-}
-
-void QueueUserWorkItemHelp(LPTHREAD_START_ROUTINE Function, PVOID Context)
-{
- STATIC_CONTRACT_THROWS;
- STATIC_CONTRACT_GC_TRIGGERS;
- STATIC_CONTRACT_MODE_ANY;
- /* Cannot use contract here because of SEH
- CONTRACTL
- {
- THROWS;
- GC_TRIGGERS;
- MODE_ANY;
- }
- CONTRACTL_END;*/
-
- _ASSERTE(!ThreadpoolMgr::UsePortableThreadPool());
-
- Function(Context);
-
- Thread *pThread = GetThreadNULLOk();
- if (pThread)
- {
- _ASSERTE(!pThread->IsAbortRequested());
- pThread->InternalReset();
- }
-}
-
-//
-// WorkingThreadCounts tracks the number of worker threads currently doing user work, and the maximum number of such threads
-// since the last time TakeMaxWorkingThreadCount was called. This information is for diagnostic purposes only,
-// and is tracked only if the CLR config value INTERNAL_ThreadPool_EnableWorkerTracking is non-zero (this feature is off
-// by default).
-//
-union WorkingThreadCounts
-{
- struct
- {
- int currentWorking : 16;
- int maxWorking : 16;
- };
-
- LONG asLong;
-};
-
-WorkingThreadCounts g_workingThreadCounts;
-
-//
-// If worker tracking is enabled (see above) then this is called immediately before and after a worker thread executes
-// each work item.
-//
-void ThreadpoolMgr::ReportThreadStatus(bool isWorking)
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- _ASSERTE(IsInitialized()); // can't be here without requesting a thread first
- _ASSERTE(CLRConfig::GetConfigValue(CLRConfig::INTERNAL_ThreadPool_EnableWorkerTracking));
-
- while (true)
- {
- WorkingThreadCounts currentCounts, newCounts;
- currentCounts.asLong = VolatileLoad(&g_workingThreadCounts.asLong);
-
- newCounts = currentCounts;
-
- if (isWorking)
- newCounts.currentWorking++;
-
- if (newCounts.currentWorking > newCounts.maxWorking)
- newCounts.maxWorking = newCounts.currentWorking;
-
- if (!isWorking)
- newCounts.currentWorking--;
-
- if (currentCounts.asLong == InterlockedCompareExchange(&g_workingThreadCounts.asLong, newCounts.asLong, currentCounts.asLong))
- break;
- }
-}
-
-//
-// Returns the max working count since the previous call to TakeMaxWorkingThreadCount, and resets WorkingThreadCounts.maxWorking.
-//
-int TakeMaxWorkingThreadCount()
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END;
- _ASSERTE(CLRConfig::GetConfigValue(CLRConfig::INTERNAL_ThreadPool_EnableWorkerTracking));
- while (true)
- {
- WorkingThreadCounts currentCounts, newCounts;
- currentCounts.asLong = VolatileLoad(&g_workingThreadCounts.asLong);
-
- newCounts = currentCounts;
- newCounts.maxWorking = 0;
-
- if (currentCounts.asLong == InterlockedCompareExchange(&g_workingThreadCounts.asLong, newCounts.asLong, currentCounts.asLong))
- {
- // If we haven't updated the counts since the last call to TakeMaxWorkingThreadCount, then we never updated maxWorking.
- // In that case, the number of working threads for the whole period since the last TakeMaxWorkingThreadCount is the
- // current number of working threads.
- return currentCounts.maxWorking == 0 ? currentCounts.currentWorking : currentCounts.maxWorking;
- }
- }
-}
-
-
-/************************************************************************/
-
-BOOL ThreadpoolMgr::QueueUserWorkItem(LPTHREAD_START_ROUTINE Function,
- PVOID Context,
- DWORD Flags,
- BOOL UnmanagedTPRequest)
-{
- CONTRACTL
- {
- THROWS; // EnsureInitialized, EnqueueWorkRequest can throw OOM
- GC_TRIGGERS;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- _ASSERTE_ALL_BUILDS(!UsePortableThreadPool());
-
- EnsureInitialized();
-
-
- if (Flags == CALL_OR_QUEUE)
- {
- // we've been asked to call this directly if the thread pressure is not too high
-
- int MinimumAvailableCPThreads = (NumberOfProcessors < 3) ? 3 : NumberOfProcessors;
-
- ThreadCounter::Counts counts = CPThreadCounter.GetCleanCounts();
- if ((MaxLimitTotalCPThreads - counts.NumActive) >= MinimumAvailableCPThreads )
- {
- QueueUserWorkItemHelp(Function, Context);
- return TRUE;
- }
-
- }
-
- if (UnmanagedTPRequest)
- {
- UnManagedPerAppDomainTPCount* pADTPCount;
- pADTPCount = PerAppDomainTPCountList::GetUnmanagedTPCount();
- pADTPCount->QueueUnmanagedWorkRequest(Function, Context);
- }
- else
- {
- // caller has already registered its TPCount; this call is just to adjust the thread count
- }
-
- return TRUE;
-}
-
-
-bool ThreadpoolMgr::ShouldWorkerKeepRunning()
-{
- WRAPPER_NO_CONTRACT;
- _ASSERTE(!UsePortableThreadPool());
-
- //
- // Maybe this thread should retire now. Let's see.
- //
- bool shouldThisThreadKeepRunning = true;
-
- // Dirty read is OK here; the worst that can happen is that we won't retire this time. In the
- // case where we might retire, we have to succeed a CompareExchange, which will have the effect
- // of validating this read.
- ThreadCounter::Counts counts = WorkerCounter.DangerousGetDirtyCounts();
- while (true)
- {
- if (counts.NumActive <= counts.MaxWorking)
- {
- shouldThisThreadKeepRunning = true;
- break;
- }
-
- ThreadCounter::Counts newCounts = counts;
- newCounts.NumWorking--;
- newCounts.NumActive--;
- newCounts.NumRetired++;
-
- ThreadCounter::Counts oldCounts = WorkerCounter.CompareExchangeCounts(newCounts, counts);
-
- if (oldCounts == counts)
- {
- shouldThisThreadKeepRunning = false;
- break;
- }
-
- counts = oldCounts;
- }
-
- return shouldThisThreadKeepRunning;
-}
-
-DangerousNonHostedSpinLock ThreadpoolMgr::ThreadAdjustmentLock;
-
-
-//
-// This method must only be called if ShouldAdjustMaxWorkersActive has returned true, *and*
-// ThreadAdjustmentLock is held.
-//
-void ThreadpoolMgr::AdjustMaxWorkersActive()
-{
- CONTRACTL
- {
- NOTHROW;
- if (GetThreadNULLOk()) { GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);}
- MODE_ANY;
- }
- CONTRACTL_END;
-
- _ASSERTE(!UsePortableThreadPool());
- _ASSERTE(ThreadAdjustmentLock.IsHeld());
-
- LARGE_INTEGER startTime = CurrentSampleStartTime;
- LARGE_INTEGER endTime;
- QueryPerformanceCounter(&endTime);
-
- static LARGE_INTEGER freq;
- if (freq.QuadPart == 0)
- QueryPerformanceFrequency(&freq);
-
- double elapsed = (double)(endTime.QuadPart - startTime.QuadPart) / freq.QuadPart;
-
- //
- // It's possible for the current sample to be reset while we're holding
- // ThreadAdjustmentLock. This will result in a very short sample, possibly
- // with completely bogus counts. We'll try to detect this by checking the sample
- // interval; if it's very short, then we try again later.
- //
- if (elapsed*1000.0 >= (ThreadAdjustmentInterval/2))
- {
- DWORD currentTicks = GetTickCount();
- LONG totalNumCompletions = (LONG)Thread::GetTotalWorkerThreadPoolCompletionCount();
- LONG numCompletions = totalNumCompletions - VolatileLoad(&PriorCompletedWorkRequests);
- ThreadCounter::Counts currentCounts = WorkerCounter.GetCleanCounts();
-
- int newMax = HillClimbingInstance.Update(
- currentCounts.MaxWorking,
- elapsed,
- numCompletions,
- &ThreadAdjustmentInterval);
-
- while (newMax != currentCounts.MaxWorking)
- {
- ThreadCounter::Counts newCounts = currentCounts;
- newCounts.MaxWorking = newMax;
-
- ThreadCounter::Counts oldCounts = WorkerCounter.CompareExchangeCounts(newCounts, currentCounts);
- if (oldCounts == currentCounts)
- {
- //
- // If we're increasing the max, inject a thread. If that thread finds work, it will inject
- // another thread, etc., until nobody finds work or we reach the new maximum.
- //
- // If we're reducing the max, whichever threads notice this first will retire themselves.
- //
- if (newMax > oldCounts.MaxWorking)
- MaybeAddWorkingWorker();
-
- break;
- }
- else
- {
- // we failed - maybe try again
- if (oldCounts.MaxWorking > currentCounts.MaxWorking &&
- oldCounts.MaxWorking >= newMax)
- {
- // someone (probably the gate thread) increased the thread count more than
- // we are about to do. Don't interfere.
- break;
- }
-
- currentCounts = oldCounts;
- }
- }
-
- PriorCompletedWorkRequests = totalNumCompletions;
- NextCompletedWorkRequestsTime = currentTicks + ThreadAdjustmentInterval;
- MemoryBarrier(); // flush previous writes (especially NextCompletedWorkRequestsTime)
- PriorCompletedWorkRequestsTime = currentTicks;
- CurrentSampleStartTime = endTime;;
- }
-}
-
-
-void ThreadpoolMgr::MaybeAddWorkingWorker()
-{
- CONTRACTL
- {
- NOTHROW;
- if (GetThreadNULLOk()) { GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);}
- MODE_ANY;
- }
- CONTRACTL_END;
-
- _ASSERTE(!UsePortableThreadPool());
-
- // counts volatile read paired with CompareExchangeCounts loop set
- ThreadCounter::Counts counts = WorkerCounter.DangerousGetDirtyCounts();
- ThreadCounter::Counts newCounts;
- while (true)
- {
- newCounts = counts;
- newCounts.NumWorking = max(counts.NumWorking, min(counts.NumWorking + 1, counts.MaxWorking));
- newCounts.NumActive = max(counts.NumActive, newCounts.NumWorking);
- newCounts.NumRetired = max(0, counts.NumRetired - (newCounts.NumActive - counts.NumActive));
-
- if (newCounts == counts)
- return;
-
- ThreadCounter::Counts oldCounts = WorkerCounter.CompareExchangeCounts(newCounts, counts);
-
- if (oldCounts == counts)
- break;
-
- counts = oldCounts;
- }
-
- int toUnretire = counts.NumRetired - newCounts.NumRetired;
- int toCreate = (newCounts.NumActive - counts.NumActive) - toUnretire;
- int toRelease = (newCounts.NumWorking - counts.NumWorking) - (toUnretire + toCreate);
-
- _ASSERTE(toUnretire >= 0);
- _ASSERTE(toCreate >= 0);
- _ASSERTE(toRelease >= 0);
- _ASSERTE(toUnretire + toCreate + toRelease <= 1);
-
- if (toUnretire > 0)
- {
- RetiredWorkerSemaphore->Release(toUnretire);
- }
-
- if (toRelease > 0)
- WorkerSemaphore->Release(toRelease);
-
- while (toCreate > 0)
- {
- if (CreateWorkerThread())
- {
- toCreate--;
- }
- else
- {
- //
- // Uh-oh, we promised to create a new thread, but the creation failed. We have to renege on our
- // promise. This may possibly result in no work getting done for a while, but the gate thread will
- // eventually notice that no completions are happening and force the creation of a new thread.
- // Of course, there's no guarantee *that* will work - but hopefully enough time will have passed
- // to allow whoever's using all the memory right now to release some.
- //
-
- // counts volatile read paired with CompareExchangeCounts loop set
- counts = WorkerCounter.DangerousGetDirtyCounts();
- while (true)
- {
- //
- // If we said we would create a thread, we also said it would be working. So we need to
- // decrement both NumWorking and NumActive by the number of threads we will no longer be creating.
- //
- newCounts = counts;
- newCounts.NumWorking -= toCreate;
- newCounts.NumActive -= toCreate;
-
- ThreadCounter::Counts oldCounts = WorkerCounter.CompareExchangeCounts(newCounts, counts);
-
- if (oldCounts == counts)
- break;
-
- counts = oldCounts;
- }
-
- toCreate = 0;
- }
- }
-}
-
-BOOL ThreadpoolMgr::PostQueuedCompletionStatus(LPOVERLAPPED lpOverlapped,
- LPOVERLAPPED_COMPLETION_ROUTINE Function)
-{
- CONTRACTL
- {
- THROWS; // EnsureInitialized can throw OOM
- GC_TRIGGERS;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- _ASSERTE(!UsePortableThreadPoolForIO());
-
-#ifndef TARGET_UNIX
- EnsureInitialized();
-
- _ASSERTE(GlobalCompletionPort != NULL);
-
- if (!InitCompletionPortThreadpool)
- InitCompletionPortThreadpool = TRUE;
-
- GrowCompletionPortThreadpoolIfNeeded();
-
- // In order to allow external ETW listeners to correlate activities that use our IO completion port
- // as a dispatch mechanism, we have to ensure the runtime's calls to ::PostQueuedCompletionStatus
- // and ::GetQueuedCompletionStatus are "annotated" with ETW events representing to operations
- // performed.
- // There are currently 2 codepaths that post to the GlobalCompletionPort:
- // 1. the managed API ThreadPool.UnsafeQueueNativeOverlapped(), calling CorPostQueuedCompletionStatus()
- // which already fires the ETW event as needed
- // 2. the managed API ThreadPool.RegisterWaitForSingleObject which needs to fire the ETW event
- // at the time the managed API is called (on the orignial user thread), and not when the ::PQCS
- // is called (from the dedicated wait thread).
- // If additional codepaths appear they need to either fire the ETW event before calling this or ensure
- // we do not fire an unmatched "dequeue" event in ThreadpoolMgr::CompletionPortThreadStart
- // The current possible values for Function:
- // - BindIoCompletionCallbackStub for ThreadPool.UnsafeQueueNativeOverlapped
- // - WaitIOCompletionCallback for ThreadPool.RegisterWaitForSingleObject
-
- return ::PostQueuedCompletionStatus(GlobalCompletionPort,
- 0,
- (ULONG_PTR) Function,
- lpOverlapped);
-#else
- SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
- return FALSE;
-#endif // !TARGET_UNIX
-}
-
-
-void ThreadpoolMgr::WaitIOCompletionCallback(
- DWORD dwErrorCode,
- DWORD numBytesTransferred,
- LPOVERLAPPED lpOverlapped)
-{
- CONTRACTL
- {
- THROWS;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- if (dwErrorCode == ERROR_SUCCESS)
- DWORD ret = AsyncCallbackCompletion((PVOID)lpOverlapped);
-}
-
-extern void WINAPI BindIoCompletionCallbackStub(DWORD ErrorCode,
- DWORD numBytesTransferred,
- LPOVERLAPPED lpOverlapped);
-
-
-// This is either made by a worker thread or a CP thread
-// indicated by threadTypeStatus
-void ThreadpoolMgr::EnsureGateThreadRunning()
-{
- LIMITED_METHOD_CONTRACT;
- _ASSERTE(!UsePortableThreadPoolForIO());
-
- if (UsePortableThreadPool())
- {
- GCX_COOP();
- MethodDescCallSite(METHOD__THREAD_POOL__ENSURE_GATE_THREAD_RUNNING).Call(NULL);
- return;
- }
-
- while (true)
- {
- switch (GateThreadStatus)
- {
- case GATE_THREAD_STATUS_REQUESTED:
- //
- // No action needed; the gate thread is running, and someone else has already registered a request
- // for it to stay.
- //
- return;
-
- case GATE_THREAD_STATUS_WAITING_FOR_REQUEST:
- //
- // Prevent the gate thread from exiting, if it hasn't already done so. If it has, we'll create it on the next iteration of
- // this loop.
- //
- InterlockedCompareExchange(&GateThreadStatus, GATE_THREAD_STATUS_REQUESTED, GATE_THREAD_STATUS_WAITING_FOR_REQUEST);
- break;
-
- case GATE_THREAD_STATUS_NOT_RUNNING:
- //
- // We need to create a new gate thread
- //
- if (InterlockedCompareExchange(&GateThreadStatus, GATE_THREAD_STATUS_REQUESTED, GATE_THREAD_STATUS_NOT_RUNNING) == GATE_THREAD_STATUS_NOT_RUNNING)
- {
- if (!CreateGateThread())
- {
- //
- // If we failed to create the gate thread, someone else will need to try again later.
- //
- GateThreadStatus = GATE_THREAD_STATUS_NOT_RUNNING;
- }
- return;
- }
- break;
-
- default:
- _ASSERTE(!"Invalid value of ThreadpoolMgr::GateThreadStatus");
- }
- }
-}
-
-bool ThreadpoolMgr::NeedGateThreadForIOCompletions()
-{
- LIMITED_METHOD_CONTRACT;
- _ASSERTE(!UsePortableThreadPoolForIO());
-
- if (!InitCompletionPortThreadpool)
- {
- return false;
- }
-
- ThreadCounter::Counts counts = CPThreadCounter.GetCleanCounts();
- return counts.NumActive <= counts.NumWorking;
-}
-
-bool ThreadpoolMgr::ShouldGateThreadKeepRunning()
-{
- LIMITED_METHOD_CONTRACT;
- _ASSERTE(!UsePortableThreadPool());
- _ASSERTE(GateThreadStatus == GATE_THREAD_STATUS_WAITING_FOR_REQUEST ||
- GateThreadStatus == GATE_THREAD_STATUS_REQUESTED);
-
- //
- // Switch to WAITING_FOR_REQUEST, and see if we had a request since the last check.
- //
- LONG previousStatus = InterlockedExchange(&GateThreadStatus, GATE_THREAD_STATUS_WAITING_FOR_REQUEST);
-
- if (previousStatus == GATE_THREAD_STATUS_WAITING_FOR_REQUEST)
- {
- //
- // No recent requests for the gate thread. Check to see if we're still needed.
- //
-
- //
- // Are there any free threads in the I/O completion pool? If there are, we don't need a gate thread.
- // This implies that whenever we decrement NumFreeCPThreads to 0, we need to call EnsureGateThreadRunning().
- //
- bool needGateThreadForCompletionPort = NeedGateThreadForIOCompletions();
-
- //
- // Are there any work requests in any worker queue? If so, we need a gate thread.
- // This imples that whenever a work queue goes from empty to non-empty, we need to call EnsureGateThreadRunning().
- //
- bool needGateThreadForWorkerThreads = PerAppDomainTPCountList::AreRequestsPendingInAnyAppDomains();
-
- //
- // If worker tracking is enabled, we need to fire periodic ETW events with active worker counts. This is
- // done by the gate thread.
- // We don't have to do anything special with EnsureGateThreadRunning() here, because this is only needed
- // once work has been added to the queue for the first time (which is covered above).
- //
- bool needGateThreadForWorkerTracking =
- 0 != CLRConfig::GetConfigValue(CLRConfig::INTERNAL_ThreadPool_EnableWorkerTracking);
-
- if (!(needGateThreadForCompletionPort ||
- needGateThreadForWorkerThreads ||
- needGateThreadForWorkerTracking))
- {
- //
- // It looks like we shouldn't be running. But another thread may now tell us to run. If so, they will set GateThreadStatus
- // back to GATE_THREAD_STATUS_REQUESTED.
- //
- previousStatus = InterlockedCompareExchange(&GateThreadStatus, GATE_THREAD_STATUS_NOT_RUNNING, GATE_THREAD_STATUS_WAITING_FOR_REQUEST);
- if (previousStatus == GATE_THREAD_STATUS_WAITING_FOR_REQUEST)
- return false;
- }
- }
-
-
- _ASSERTE(GateThreadStatus == GATE_THREAD_STATUS_WAITING_FOR_REQUEST ||
- GateThreadStatus == GATE_THREAD_STATUS_REQUESTED);
- return true;
-}
-
-
-
-//************************************************************************
-void ThreadpoolMgr::EnqueueWorkRequest(WorkRequest* workRequest)
-{
- CONTRACTL
- {
- NOTHROW;
- MODE_ANY;
- GC_NOTRIGGER;
- }
- CONTRACTL_END;
-
- _ASSERTE(!UsePortableThreadPool());
-
- AppendWorkRequest(workRequest);
-}
-
-WorkRequest* ThreadpoolMgr::DequeueWorkRequest()
-{
- WorkRequest* entry = NULL;
- CONTRACT(WorkRequest*)
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_PREEMPTIVE;
-
- POSTCONDITION(CheckPointer(entry, NULL_OK));
- } CONTRACT_END;
-
- _ASSERTE(!UsePortableThreadPool());
-
- entry = RemoveWorkRequest();
-
- RETURN entry;
-}
-
-void ThreadpoolMgr::ExecuteWorkRequest(bool* foundWork, bool* wasNotRecalled)
-{
- CONTRACTL
- {
- THROWS; // QueueUserWorkItem can throw
- GC_TRIGGERS;
- MODE_PREEMPTIVE;
- }
- CONTRACTL_END;
-
- _ASSERTE(!UsePortableThreadPool());
-
- IPerAppDomainTPCount* pAdCount;
-
- LONG index = PerAppDomainTPCountList::GetAppDomainIndexForThreadpoolDispatch();
-
- if (index == 0)
- {
- *foundWork = false;
- *wasNotRecalled = true;
- return;
- }
-
- if (index == -1)
- {
- pAdCount = PerAppDomainTPCountList::GetUnmanagedTPCount();
- }
- else
- {
-
- pAdCount = PerAppDomainTPCountList::GetPerAppdomainCount(TPIndex((DWORD)index));
- _ASSERTE(pAdCount);
- }
-
- pAdCount->DispatchWorkItem(foundWork, wasNotRecalled);
-}
-
-//--------------------------------------------------------------------------
-//This function informs the thread scheduler that the first requests has been
-//queued on an appdomain, or it's the first unmanaged TP request.
-//Arguments:
-// UnmanagedTP: Indicates that the request arises from the unmanaged
-//part of Thread Pool.
-//Assumptions:
-// This function must be called under a per-appdomain lock or the
-//correct lock under unmanaged TP queue.
-//
-BOOL ThreadpoolMgr::SetAppDomainRequestsActive(BOOL UnmanagedTP)
-{
- CONTRACTL
- {
- NOTHROW;
- MODE_ANY;
- GC_TRIGGERS;
- }
- CONTRACTL_END;
-
- _ASSERTE(!UsePortableThreadPool());
-
- BOOL fShouldSignalEvent = FALSE;
-
- IPerAppDomainTPCount* pAdCount;
-
- if(UnmanagedTP)
- {
- pAdCount = PerAppDomainTPCountList::GetUnmanagedTPCount();
- _ASSERTE(pAdCount);
- }
- else
- {
- Thread* pCurThread = GetThread();
- AppDomain* pAppDomain = pCurThread->GetDomain();
- _ASSERTE(pAppDomain);
-
- TPIndex tpindex = pAppDomain->GetTPIndex();
- pAdCount = PerAppDomainTPCountList::GetPerAppdomainCount(tpindex);
-
- _ASSERTE(pAdCount);
- }
-
- pAdCount->SetAppDomainRequestsActive();
-
- return fShouldSignalEvent;
-}
-
-void ThreadpoolMgr::ClearAppDomainRequestsActive(BOOL UnmanagedTP, LONG id)
-//--------------------------------------------------------------------------
-//This function informs the thread scheduler that the kast request has been
-//dequeued on an appdomain, or it's the last unmanaged TP request.
-//Arguments:
-// UnmanagedTP: Indicates that the request arises from the unmanaged
-//part of Thread Pool.
-// id: Indicates the id of the appdomain. The id is needed as this
-//function can be called (indirectly) from the appdomain unload thread from
-//unmanaged code to clear per-appdomain state during rude unload.
-//Assumptions:
-// This function must be called under a per-appdomain lock or the
-//correct lock under unmanaged TP queue.
-//
-{
- CONTRACTL
- {
- NOTHROW;
- MODE_ANY;
- GC_TRIGGERS;
- }
- CONTRACTL_END;
-
- _ASSERTE(!UsePortableThreadPool());
-
- IPerAppDomainTPCount* pAdCount;
-
- if(UnmanagedTP)
- {
- pAdCount = PerAppDomainTPCountList::GetUnmanagedTPCount();
- _ASSERTE(pAdCount);
- }
- else
- {
- Thread* pCurThread = GetThread();
- AppDomain* pAppDomain = pCurThread->GetDomain();
- _ASSERTE(pAppDomain);
-
- TPIndex tpindex = pAppDomain->GetTPIndex();
-
- pAdCount = PerAppDomainTPCountList::GetPerAppdomainCount(tpindex);
-
- _ASSERTE(pAdCount);
- }
-
- pAdCount->ClearAppDomainRequestsActive();
-}
-
-
-// Remove a block from the appropriate recycleList and return.
-// If recycleList is empty, fall back to new.
-LPVOID ThreadpoolMgr::GetRecycledMemory(enum MemType memType)
-{
- LPVOID result = NULL;
- CONTRACT(LPVOID)
- {
- THROWS;
- GC_NOTRIGGER;
- MODE_ANY;
- INJECT_FAULT(COMPlusThrowOM());
- POSTCONDITION(CheckPointer(result));
- } CONTRACT_END;
-
- if(RecycledLists.IsInitialized())
- {
- RecycledListInfo& list = RecycledLists.GetRecycleMemoryInfo( memType );
-
- result = list.Remove();
- }
-
- if(result == NULL)
- {
- switch (memType)
- {
- case MEMTYPE_DelegateInfo:
- result = new DelegateInfo;
- break;
- case MEMTYPE_AsyncCallback:
- result = new AsyncCallback;
- break;
- case MEMTYPE_WorkRequest:
- result = new WorkRequest;
- break;
- default:
- _ASSERTE(!"Unknown Memtype");
- result = NULL;
- break;
- }
- }
-
- RETURN result;
-}
-
-// Insert freed block in recycle list. If list is full, return to system heap
-void ThreadpoolMgr::RecycleMemory(LPVOID mem, enum MemType memType)
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- if(RecycledLists.IsInitialized())
- {
- RecycledListInfo& list = RecycledLists.GetRecycleMemoryInfo( memType );
-
- if(list.CanInsert())
- {
- list.Insert( mem );
- return;
- }
- }
-
- switch (memType)
- {
- case MEMTYPE_DelegateInfo:
- delete (DelegateInfo*) mem;
- break;
- case MEMTYPE_AsyncCallback:
- delete (AsyncCallback*) mem;
- break;
- case MEMTYPE_WorkRequest:
- delete (WorkRequest*) mem;
- break;
- default:
- _ASSERTE(!"Unknown Memtype");
-
- }
-}
-
-Thread* ThreadpoolMgr::CreateUnimpersonatedThread(LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpArgs, BOOL *pIsCLRThread)
-{
- STATIC_CONTRACT_NOTHROW;
- if (GetThreadNULLOk()) { STATIC_CONTRACT_GC_TRIGGERS;} else {DISABLED(STATIC_CONTRACT_GC_NOTRIGGER);}
- STATIC_CONTRACT_MODE_ANY;
- /* cannot use contract because of SEH
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END;*/
-
- Thread* pThread = NULL;
-
- if (g_fEEStarted) {
- *pIsCLRThread = TRUE;
- }
- else
- *pIsCLRThread = FALSE;
- if (*pIsCLRThread) {
- EX_TRY
- {
- pThread = SetupUnstartedThread();
- }
- EX_CATCH
- {
- pThread = NULL;
- }
- EX_END_CATCH(SwallowAllExceptions);
- if (pThread == NULL) {
- return NULL;
- }
- }
- DWORD threadId;
- BOOL bOK = FALSE;
- HANDLE threadHandle = NULL;
-
- if (*pIsCLRThread) {
- // CreateNewThread takes care of reverting any impersonation - so dont do anything here.
- bOK = pThread->CreateNewThread(0, // default stack size
- lpStartAddress,
- lpArgs, //arguments
- W(".NET ThreadPool Worker"));
- }
- else {
-#ifndef TARGET_UNIX
- HandleHolder token;
- BOOL bReverted = FALSE;
- bOK = RevertIfImpersonated(&bReverted, &token);
- if (bOK != TRUE)
- return NULL;
-#endif // !TARGET_UNIX
- threadHandle = CreateThread(NULL, // security descriptor
- 0, // default stack size
- lpStartAddress,
- lpArgs,
- CREATE_SUSPENDED,
- &threadId);
-
- SetThreadName(threadHandle, W(".NET ThreadPool Worker"));
-#ifndef TARGET_UNIX
- UndoRevert(bReverted, token);
-#endif // !TARGET_UNIX
- }
-
- if (*pIsCLRThread && !bOK)
- {
- pThread->DecExternalCount(FALSE);
- pThread = NULL;
- }
-
- if (*pIsCLRThread) {
- return pThread;
- }
- else
- return (Thread*)threadHandle;
-}
-
-
-BOOL ThreadpoolMgr::CreateWorkerThread()
-{
- CONTRACTL
- {
- if (GetThreadNULLOk()) { GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);}
- NOTHROW;
- MODE_ANY; // We may try to add a worker thread while queuing a work item thru an fcall
- }
- CONTRACTL_END;
-
- _ASSERTE(!UsePortableThreadPool());
-
- Thread *pThread;
- BOOL fIsCLRThread;
- if ((pThread = CreateUnimpersonatedThread(WorkerThreadStart, NULL, &fIsCLRThread)) != NULL)
- {
- if (fIsCLRThread) {
- pThread->ChooseThreadCPUGroupAffinity();
- pThread->StartThread();
- }
- else {
- DWORD status;
- status = ResumeThread((HANDLE)pThread);
- _ASSERTE(status != (DWORD) (-1));
- CloseHandle((HANDLE)pThread); // we don't need this anymore
- }
-
- return TRUE;
- }
-
- return FALSE;
-}
-
-
-DWORD WINAPI ThreadpoolMgr::WorkerThreadStart(LPVOID lpArgs)
-{
- ClrFlsSetThreadType (ThreadType_Threadpool_Worker);
-
- CONTRACTL
- {
- THROWS;
- GC_TRIGGERS;
- MODE_PREEMPTIVE;
- }
- CONTRACTL_END;
-
- _ASSERTE_ALL_BUILDS(!UsePortableThreadPool());
-
- Thread *pThread = NULL;
- DWORD dwSwitchCount = 0;
- BOOL fThreadInit = FALSE;
-
- ThreadCounter::Counts counts, oldCounts, newCounts;
- bool foundWork = true, wasNotRecalled = true;
-
- counts = WorkerCounter.GetCleanCounts();
- if (ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context, ThreadPoolWorkerThreadStart))
- FireEtwThreadPoolWorkerThreadStart(counts.NumActive, counts.NumRetired, GetClrInstanceId());
-
-#ifdef FEATURE_COMINTEROP
- BOOL fCoInited = FALSE;
- // Threadpool threads should be initialized as MTA. If we are unable to do so,
- // return failure.
- {
- fCoInited = SUCCEEDED(::CoInitializeEx(NULL, COINIT_MULTITHREADED));
- if (!fCoInited)
- {
- goto Exit;
- }
- }
-#endif // FEATURE_COMINTEROP
-Work:
-
- if (!fThreadInit) {
- if (g_fEEStarted) {
- pThread = SetupThreadNoThrow();
- if (pThread == NULL) {
- __SwitchToThread(0, ++dwSwitchCount);
- goto Work;
- }
-
- // converted to CLRThread and added to ThreadStore, pick an group affinity for this thread
- pThread->ChooseThreadCPUGroupAffinity();
-
- #ifdef FEATURE_COMINTEROP
- if (pThread->SetApartment(Thread::AS_InMTA) != Thread::AS_InMTA)
- {
- // counts volatile read paired with CompareExchangeCounts loop set
- counts = WorkerCounter.DangerousGetDirtyCounts();
- while (true)
- {
- newCounts = counts;
- newCounts.NumActive--;
- newCounts.NumWorking--;
- oldCounts = WorkerCounter.CompareExchangeCounts(newCounts, counts);
- if (oldCounts == counts)
- break;
- counts = oldCounts;
- }
- goto Exit;
- }
- #endif // FEATURE_COMINTEROP
-
- pThread->SetBackground(TRUE);
- fThreadInit = TRUE;
- }
- }
-
- GCX_PREEMP_NO_DTOR();
- _ASSERTE(pThread == NULL || !pThread->PreemptiveGCDisabled());
-
- // make sure there's really work. If not, go back to sleep
-
- // counts volatile read paired with CompareExchangeCounts loop set
- counts = WorkerCounter.DangerousGetDirtyCounts();
- while (true)
- {
- _ASSERTE(counts.NumActive > 0);
- _ASSERTE(counts.NumWorking > 0);
-
- newCounts = counts;
-
- bool retired;
-
- if (counts.NumActive > counts.MaxWorking)
- {
- newCounts.NumActive--;
- newCounts.NumRetired++;
- retired = true;
- }
- else
- {
- retired = false;
-
- if (foundWork)
- break;
- }
-
- newCounts.NumWorking--;
-
- oldCounts = WorkerCounter.CompareExchangeCounts(newCounts, counts);
-
- if (oldCounts == counts)
- {
- if (retired)
- goto Retire;
- else
- goto WaitForWork;
- }
-
- counts = oldCounts;
- }
-
- if (GCHeapUtilities::IsGCInProgress(TRUE))
- {
- // GC is imminent, so wait until GC is complete before executing next request.
- // this reduces in-flight objects allocated right before GC, easing the GC's work
- GCHeapUtilities::WaitForGCCompletion(TRUE);
- }
-
- {
- ThreadpoolMgr::UpdateLastDequeueTime();
- ThreadpoolMgr::ExecuteWorkRequest(&foundWork, &wasNotRecalled);
- }
-
- if (foundWork)
- {
- // Reset TLS etc. for next WorkRequest.
- if (pThread == NULL)
- pThread = GetThreadNULLOk();
-
- if (pThread)
- {
- _ASSERTE(!pThread->IsAbortRequested());
- pThread->InternalReset();
- }
- }
-
- if (wasNotRecalled)
- goto Work;
-
-Retire:
-
- counts = WorkerCounter.GetCleanCounts();
- FireEtwThreadPoolWorkerThreadRetirementStart(counts.NumActive, counts.NumRetired, GetClrInstanceId());
-
- // It's possible that some work came in just before we decremented the active thread count, in which
- // case whoever queued that work may be expecting us to pick it up - so they would not have signalled
- // the worker semaphore. If there are other threads waiting, they will never be woken up, because
- // whoever queued the work expects that it's already been picked up. The solution is to signal the semaphore
- // if there's any work available.
- if (PerAppDomainTPCountList::AreRequestsPendingInAnyAppDomains())
- MaybeAddWorkingWorker();
-
- while (true)
- {
-RetryRetire:
- if (RetiredWorkerSemaphore->Wait(WorkerTimeout))
- {
- foundWork = true;
-
- counts = WorkerCounter.GetCleanCounts();
- FireEtwThreadPoolWorkerThreadRetirementStop(counts.NumActive, counts.NumRetired, GetClrInstanceId());
- goto Work;
- }
-
- if (!IsIoPending())
- {
- //
- // We're going to exit. There's a nasty race here. We're about to decrement NumRetired,
- // since we're going to exit. Once we've done that, nobody will expect this thread
- // to be waiting for RetiredWorkerSemaphore. But between now and then, other threads still
- // think we're waiting on the semaphore, and they will happily do the following to try to
- // wake us up:
- //
- // 1) Decrement NumRetired
- // 2) Increment NumActive
- // 3) Increment NumWorking
- // 4) Signal RetiredWorkerSemaphore
- //
- // We will not receive that signal. If we don't do something special here,
- // we will decrement NumRetired an extra time, and leave the world thinking there
- // are fewer retired threads, and more working threads than reality.
- //
- // What can we do about this? First, we *need* to decrement NumRetired. If someone did it before us,
- // it might go negative. This is the easiest way to tell that we've encountered this race. In that case,
- // we will simply not commit the decrement, swallow the signal that was sent, and proceed as if we
- // got WAIT_OBJECT_0 in the wait above.
- //
- // If we don't hit zero while decrementing NumRetired, we still may have encountered this race. But
- // if we don't hit zero, then there's another retired thread that will pick up this signal. So it's ok
- // to exit.
- //
-
- // counts volatile read paired with CompareExchangeCounts loop set
- counts = WorkerCounter.DangerousGetDirtyCounts();
- while (true)
- {
- if (counts.NumRetired == 0)
- goto RetryRetire;
-
- newCounts = counts;
- newCounts.NumRetired--;
-
- oldCounts = WorkerCounter.CompareExchangeCounts(newCounts, counts);
- if (oldCounts == counts)
- {
- counts = newCounts;
- break;
- }
- counts = oldCounts;
- }
-
- FireEtwThreadPoolWorkerThreadRetirementStop(counts.NumActive, counts.NumRetired, GetClrInstanceId());
- goto Exit;
- }
- }
-
-WaitForWork:
-
- // It's possible that we decided we had no work just before some work came in,
- // but reduced the worker count *after* the work came in. In this case, we might
- // miss the notification of available work. So we make a sweep through the ADs here,
- // and wake up a thread (maybe this one!) if there is work to do.
- if (PerAppDomainTPCountList::AreRequestsPendingInAnyAppDomains())
- {
- foundWork = true;
- MaybeAddWorkingWorker();
- }
-
- if (ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context, ThreadPoolWorkerThreadWait))
- FireEtwThreadPoolWorkerThreadWait(counts.NumActive, counts.NumRetired, GetClrInstanceId());
-
-RetryWaitForWork:
- if (WorkerSemaphore->Wait(WorkerTimeout, WorkerThreadSpinLimit, NumberOfProcessors))
- {
- foundWork = true;
- goto Work;
- }
-
- if (!IsIoPending())
- {
- //
- // We timed out, and are about to exit. This puts us in a very similar situation to the
- // retirement case above - someone may think we're still waiting, and go ahead and:
- //
- // 1) Increment NumWorking
- // 2) Signal WorkerSemaphore
- //
- // The solution is much like retirement; when we're decrementing NumActive, we need to make
- // sure it doesn't drop below NumWorking. If it would, then we need to go back and wait
- // again.
- //
-
- DangerousNonHostedSpinLockHolder tal(&ThreadAdjustmentLock);
-
- // counts volatile read paired with CompareExchangeCounts loop set
- counts = WorkerCounter.DangerousGetDirtyCounts();
- while (true)
- {
- if (counts.NumActive == counts.NumWorking)
- {
- goto RetryWaitForWork;
- }
-
- newCounts = counts;
- newCounts.NumActive--;
-
- // if we timed out while active, then Hill Climbing needs to be told that we need fewer threads
- newCounts.MaxWorking = max(MinLimitTotalWorkerThreads, min(newCounts.NumActive, newCounts.MaxWorking));
-
- oldCounts = WorkerCounter.CompareExchangeCounts(newCounts, counts);
-
- if (oldCounts == counts)
- {
- HillClimbingInstance.ForceChange(newCounts.MaxWorking, ThreadTimedOut);
- goto Exit;
- }
-
- counts = oldCounts;
- }
- }
- else
- {
- goto RetryWaitForWork;
- }
-
-Exit:
-
-#ifdef FEATURE_COMINTEROP
- if (pThread) {
- pThread->SetApartment(Thread::AS_Unknown);
- pThread->CoUninitialize();
- }
-
- // Couninit the worker thread
- if (fCoInited)
- {
- CoUninitialize();
- }
-#endif
-
- if (pThread) {
- pThread->ClearThreadCPUGroupAffinity();
-
- DestroyThread(pThread);
- }
-
- _ASSERTE(!IsIoPending());
-
- counts = WorkerCounter.GetCleanCounts();
- if (ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context, ThreadPoolWorkerThreadStop))
- FireEtwThreadPoolWorkerThreadStop(counts.NumActive, counts.NumRetired, GetClrInstanceId());
-
- return ERROR_SUCCESS;
-}
-
-// this should only be called by unmanaged thread (i.e. there should be no mgd
-// caller on the stack) since we are swallowing terminal exceptions
-DWORD ThreadpoolMgr::SafeWait(CLREvent * ev, DWORD sleepTime, BOOL alertable)
-{
- STATIC_CONTRACT_NOTHROW;
- STATIC_CONTRACT_GC_NOTRIGGER;
- STATIC_CONTRACT_MODE_PREEMPTIVE;
- /* cannot use contract because of SEH
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_PREEMPTIVE;
- }
- CONTRACTL_END;*/
-
- DWORD status = WAIT_TIMEOUT;
- EX_TRY
- {
- status = ev->Wait(sleepTime,FALSE);
- }
- EX_CATCH
- {
- }
- EX_END_CATCH(SwallowAllExceptions)
- return status;
-}
-
-/************************************************************************/
-
-BOOL ThreadpoolMgr::RegisterWaitForSingleObject(PHANDLE phNewWaitObject,
- HANDLE hWaitObject,
- WAITORTIMERCALLBACK Callback,
- PVOID Context,
- ULONG timeout,
- DWORD dwFlag )
-{
- CONTRACTL
- {
- THROWS;
- MODE_ANY;
- if (GetThreadNULLOk()) { GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);}
- }
- CONTRACTL_END;
-
- _ASSERTE(!UsePortableThreadPool());
-
- EnsureInitialized();
-
- ThreadCB* threadCB;
- {
- CrstHolder csh(&WaitThreadsCriticalSection);
-
- threadCB = FindWaitThread();
- }
-
- *phNewWaitObject = NULL;
-
- if (threadCB)
- {
- WaitInfo* waitInfo = new (nothrow) WaitInfo;
-
- if (waitInfo == NULL)
- return FALSE;
-
- waitInfo->waitHandle = hWaitObject;
- waitInfo->Callback = Callback;
- waitInfo->Context = Context;
- waitInfo->timeout = timeout;
- waitInfo->flag = dwFlag;
- waitInfo->threadCB = threadCB;
- waitInfo->state = 0;
- waitInfo->refCount = 1; // safe to do this since no wait has yet been queued, so no other thread could be modifying this
- waitInfo->ExternalCompletionEvent = INVALID_HANDLE;
- waitInfo->ExternalEventSafeHandle = NULL;
-
- waitInfo->timer.startTime = GetTickCount();
- waitInfo->timer.remainingTime = timeout;
-
- *phNewWaitObject = waitInfo;
-
- // We fire the "enqueue" ETW event here, to "mark" the thread that had called the API, rather than the
- // thread that will PostQueuedCompletionStatus (the dedicated WaitThread).
- // This event correlates with ThreadPoolIODequeue in ThreadpoolMgr::AsyncCallbackCompletion
- if (ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context, ThreadPoolIOEnqueue))
- FireEtwThreadPoolIOEnqueue((LPOVERLAPPED)waitInfo, reinterpret_cast(Callback), (dwFlag & WAIT_SINGLE_EXECUTION) == 0, GetClrInstanceId());
-
- BOOL status = QueueUserAPC((PAPCFUNC)InsertNewWaitForSelf, threadCB->threadHandle, (size_t) waitInfo);
-
- if (status == FALSE)
- {
- *phNewWaitObject = NULL;
- delete waitInfo;
- }
-
- return status;
- }
-
- return FALSE;
-}
-
-
-// Returns a wait thread that can accommodate another wait request. The
-// caller is responsible for synchronizing access to the WaitThreadsHead
-ThreadpoolMgr::ThreadCB* ThreadpoolMgr::FindWaitThread()
-{
- CONTRACTL
- {
- THROWS; // CreateWaitThread can throw
- MODE_ANY;
- GC_TRIGGERS;
- }
- CONTRACTL_END;
-
- _ASSERTE(!UsePortableThreadPool());
-
- do
- {
- for (LIST_ENTRY* Node = (LIST_ENTRY*) WaitThreadsHead.Flink ;
- Node != &WaitThreadsHead ;
- Node = (LIST_ENTRY*)Node->Flink)
- {
- _ASSERTE(offsetof(WaitThreadInfo,link) == 0);
-
- ThreadCB* threadCB = ((WaitThreadInfo*) Node)->threadCB;
-
- if (threadCB->NumWaitHandles < MAX_WAITHANDLES) // this test and following ...
-
- {
- InterlockedIncrement(&threadCB->NumWaitHandles); // ... increment are protected by WaitThreadsCriticalSection.
- // but there might be a concurrent decrement in DeactivateWait
- // or InsertNewWaitForSelf, hence the interlock
- return threadCB;
- }
- }
-
- // if reached here, there are no wait threads available, so need to create a new one
- if (!CreateWaitThread())
- return NULL;
-
-
- // Now loop back
- } while (TRUE);
-
-}
-
-BOOL ThreadpoolMgr::CreateWaitThread()
-{
- CONTRACTL
- {
- THROWS; // CLREvent::CreateAutoEvent can throw OOM
- GC_TRIGGERS;
- MODE_ANY;
- INJECT_FAULT(COMPlusThrowOM());
- }
- CONTRACTL_END;
-
- _ASSERTE(!UsePortableThreadPool());
-
- DWORD threadId;
-
- if (g_fEEShutDown & ShutDown_Finalize2){
- // The process is shutting down. Shutdown thread has ThreadStore lock,
- // wait thread is blocked on the lock.
- return FALSE;
- }
-
- NewHolder waitThreadInfo(new (nothrow) WaitThreadInfo);
- if (waitThreadInfo == NULL)
- return FALSE;
-
- NewHolder threadCB(new (nothrow) ThreadCB);
-
- if (threadCB == NULL)
- {
- return FALSE;
- }
-
- threadCB->startEvent.CreateAutoEvent(FALSE);
- HANDLE threadHandle = Thread::CreateUtilityThread(Thread::StackSize_Small, WaitThreadStart, (LPVOID)threadCB, W(".NET ThreadPool Wait"), CREATE_SUSPENDED, &threadId);
-
- if (threadHandle == NULL)
- {
- threadCB->startEvent.CloseEvent();
- return FALSE;
- }
-
- waitThreadInfo.SuppressRelease();
- threadCB.SuppressRelease();
- threadCB->threadHandle = threadHandle;
- threadCB->threadId = threadId; // may be useful for debugging otherwise not used
- threadCB->NumWaitHandles = 0;
- threadCB->NumActiveWaits = 0;
- for (int i=0; i< MAX_WAITHANDLES; i++)
- {
- InitializeListHead(&(threadCB->waitPointer[i]));
- }
-
- waitThreadInfo->threadCB = threadCB;
-
- DWORD status = ResumeThread(threadHandle);
-
- {
- // We will QueueUserAPC on the newly created thread.
- // Let us wait until the thread starts running.
- GCX_PREEMP();
- DWORD timeout=500;
- while (TRUE) {
- if (g_fEEShutDown & ShutDown_Finalize2){
- // The process is shutting down. Shutdown thread has ThreadStore lock,
- // wait thread is blocked on the lock.
- return FALSE;
- }
- DWORD wait_status = threadCB->startEvent.Wait(timeout, FALSE);
- if (wait_status == WAIT_OBJECT_0) {
- break;
- }
- }
- }
- threadCB->startEvent.CloseEvent();
-
- // check to see if setup succeeded
- if (threadCB->threadHandle == NULL)
- return FALSE;
-
- InsertHeadList(&WaitThreadsHead,&waitThreadInfo->link);
-
- _ASSERTE(status != (DWORD) (-1));
-
- return (status != (DWORD) (-1));
-
-}
-
-// Executed as an APC on a WaitThread. Add the wait specified in pArg to the list of objects it is waiting on
-void ThreadpoolMgr::InsertNewWaitForSelf(WaitInfo* pArgs)
-{
- WRAPPER_NO_CONTRACT;
- _ASSERTE(!UsePortableThreadPool());
-
- WaitInfo* waitInfo = pArgs;
-
- // the following is safe since only this thread is allowed to change the state
- if (!(waitInfo->state & WAIT_DELETE))
- {
- waitInfo->state = (WAIT_REGISTERED | WAIT_ACTIVE);
- }
- else
- {
- // some thread unregistered the wait
- DeleteWait(waitInfo);
- return;
- }
-
-
- ThreadCB* threadCB = waitInfo->threadCB;
-
- _ASSERTE(threadCB->NumActiveWaits <= threadCB->NumWaitHandles);
-
- int index = FindWaitIndex(threadCB, waitInfo->waitHandle);
- _ASSERTE(index >= 0 && index <= threadCB->NumActiveWaits);
-
- if (index == threadCB->NumActiveWaits)
- {
- threadCB->waitHandle[threadCB->NumActiveWaits] = waitInfo->waitHandle;
- threadCB->NumActiveWaits++;
- }
- else
- {
- // this is a duplicate waithandle, so the increment in FindWaitThread
- // wasn't strictly necessary. This will avoid unnecessary thread creation.
- InterlockedDecrement(&threadCB->NumWaitHandles);
- }
-
- _ASSERTE(offsetof(WaitInfo, link) == 0);
- InsertTailList(&(threadCB->waitPointer[index]), (&waitInfo->link));
-
- return;
-}
-
-// returns the index of the entry that matches waitHandle or next free entry if not found
-int ThreadpoolMgr::FindWaitIndex(const ThreadCB* threadCB, const HANDLE waitHandle)
-{
- LIMITED_METHOD_CONTRACT;
- _ASSERTE(!UsePortableThreadPool());
-
- for (int i=0;iNumActiveWaits; i++)
- if (threadCB->waitHandle[i] == waitHandle)
- return i;
-
- // else not found
- return threadCB->NumActiveWaits;
-}
-
-
-// if no wraparound that the timer is expired if duetime is less than current time
-// if wraparound occurred, then the timer expired if dueTime was greater than last time or dueTime is less equal to current time
-#define TimeExpired(last,now,duetime) ((last) <= (now) ? \
- ((duetime) <= (now) && (duetime) >= (last)): \
- ((duetime) >= (last) || (duetime) <= (now)))
-
-#define TimeInterval(end,start) ((end) > (start) ? ((end) - (start)) : ((0xffffffff - (start)) + (end) + 1))
-
-// Returns the minimum of the remaining time to reach a timeout among all the waits
-DWORD ThreadpoolMgr::MinimumRemainingWait(LIST_ENTRY* waitInfo, unsigned int numWaits)
-{
- LIMITED_METHOD_CONTRACT;
- _ASSERTE(!UsePortableThreadPool());
-
- unsigned int min = (unsigned int) -1;
- DWORD currentTime = GetTickCount();
-
- for (unsigned i=0; i < numWaits ; i++)
- {
- WaitInfo* waitInfoPtr = (WaitInfo*) (waitInfo[i].Flink);
- PVOID waitInfoHead = &(waitInfo[i]);
- do
- {
- if (waitInfoPtr->timeout != INFINITE)
- {
- // compute remaining time
- DWORD elapsedTime = TimeInterval(currentTime,waitInfoPtr->timer.startTime );
-
- __int64 remainingTime = (__int64) (waitInfoPtr->timeout) - (__int64) elapsedTime;
-
- // update remaining time
- waitInfoPtr->timer.remainingTime = remainingTime > 0 ? (int) remainingTime : 0;
-
- // ... and min
- if (waitInfoPtr->timer.remainingTime < min)
- min = waitInfoPtr->timer.remainingTime;
- }
-
- waitInfoPtr = (WaitInfo*) (waitInfoPtr->link.Flink);
-
- } while ((PVOID) waitInfoPtr != waitInfoHead);
-
- }
- return min;
-}
-
-#ifdef _MSC_VER
-#ifdef HOST_64BIT
-#pragma warning (disable : 4716)
-#else
-#pragma warning (disable : 4715)
-#endif
-#endif
-#ifdef _PREFAST_
-#pragma warning(push)
-#pragma warning(disable:22008) // "Prefast integer overflow check on (0 + lval) is bogus. Tried local disable without luck, doing whole method."
-#endif
-
-DWORD WINAPI ThreadpoolMgr::WaitThreadStart(LPVOID lpArgs)
-{
- CONTRACTL
- {
- THROWS;
- GC_TRIGGERS;
- MODE_PREEMPTIVE;
- }
- CONTRACTL_END;
-
- ClrFlsSetThreadType (ThreadType_Wait);
-
- _ASSERTE_ALL_BUILDS(!UsePortableThreadPool());
-
- ThreadCB* threadCB = (ThreadCB*) lpArgs;
- Thread* pThread = SetupThreadNoThrow();
-
- if (pThread == NULL)
- {
- _ASSERTE(threadCB->threadHandle != NULL);
- threadCB->threadHandle = NULL;
- }
-
- threadCB->startEvent.Set();
-
- if (pThread == NULL)
- {
- return 0;
- }
-
- {
- // wait threads never die. (Why?)
- for (;;)
- {
- DWORD status;
- DWORD timeout = 0;
-
- if (threadCB->NumActiveWaits == 0)
- {
- status = ClrSleepEx(INFINITE,TRUE);
- _ASSERTE(status == WAIT_IO_COMPLETION);
- }
- else if (IsWaitThreadAPCPending())
- {
- //Do a sleep if an APC is pending, This was done to solve the corner case where the wait is signaled,
- //and APC to deregiter the wait never fires. That scenario leads to an infinite loop. This check would
- //allow the thread to enter alertable wait and thus cause the APC to fire.
-
- ResetWaitThreadAPCPending();
- status = ClrSleepEx(0,TRUE);
- continue;
- }
- else
- {
- // compute minimum timeout. this call also updates the remainingTime field for each wait
- timeout = MinimumRemainingWait(threadCB->waitPointer,threadCB->NumActiveWaits);
-
- status = WaitForMultipleObjectsEx( threadCB->NumActiveWaits,
- threadCB->waitHandle,
- FALSE, // waitall
- timeout,
- TRUE ); // alertable
-
- _ASSERTE( (status == WAIT_TIMEOUT) ||
- (status == WAIT_IO_COMPLETION) ||
- //It could be that there are no waiters at this point,
- //as the APC to deregister the wait may have run.
- (status == WAIT_OBJECT_0) ||
- (status >= WAIT_OBJECT_0 && status < (DWORD)(WAIT_OBJECT_0 + threadCB->NumActiveWaits)) ||
- (status == WAIT_FAILED));
-
- //It could be that the last waiter also got deregistered.
- if (threadCB->NumActiveWaits == 0)
- {
- continue;
- }
- }
-
- if (status == WAIT_IO_COMPLETION)
- continue;
-
- if (status == WAIT_TIMEOUT)
- {
- for (int i=0; i< threadCB->NumActiveWaits; i++)
- {
- WaitInfo* waitInfo = (WaitInfo*) (threadCB->waitPointer[i]).Flink;
- PVOID waitInfoHead = &(threadCB->waitPointer[i]);
-
- do
- {
- _ASSERTE(waitInfo->timer.remainingTime >= timeout);
-
- WaitInfo* wTemp = (WaitInfo*) waitInfo->link.Flink;
-
- if (waitInfo->timer.remainingTime == timeout)
- {
- ProcessWaitCompletion(waitInfo,i,TRUE);
- }
-
- waitInfo = wTemp;
-
- } while ((PVOID) waitInfo != waitInfoHead);
- }
- }
- else if (status >= WAIT_OBJECT_0 && status < (DWORD)(WAIT_OBJECT_0 + threadCB->NumActiveWaits))
- {
- unsigned index = status - WAIT_OBJECT_0;
- WaitInfo* waitInfo = (WaitInfo*) (threadCB->waitPointer[index]).Flink;
- PVOID waitInfoHead = &(threadCB->waitPointer[index]);
- BOOL isAutoReset;
-
- // Setting to unconditional TRUE is inefficient since we will re-enter the wait and release
- // the next waiter, but short of using undocumented NT apis is the only solution.
- // Querying the state with a WaitForSingleObject is not an option as it will reset an
- // auto reset event if it has been signalled since the previous wait.
- isAutoReset = TRUE;
-
- do
- {
- WaitInfo* wTemp = (WaitInfo*) waitInfo->link.Flink;
- ProcessWaitCompletion(waitInfo,index,FALSE);
-
- waitInfo = wTemp;
-
- } while (((PVOID) waitInfo != waitInfoHead) && !isAutoReset);
-
- // If an app registers a recurring wait for an event that is always signalled (!),
- // then no apc's will be executed since the thread never enters the alertable state.
- // This can be fixed by doing the following:
- // SleepEx(0,TRUE);
- // However, it causes an unnecessary context switch. It is not worth penalizing well
- // behaved apps to protect poorly written apps.
-
-
- }
- else
- {
- _ASSERTE(status == WAIT_FAILED);
- // wait failed: application error
- // find out which wait handle caused the wait to fail
- for (int i = 0; i < threadCB->NumActiveWaits; i++)
- {
- DWORD subRet = WaitForSingleObject(threadCB->waitHandle[i], 0);
-
- if (subRet != WAIT_FAILED)
- continue;
-
- // remove all waits associated with this wait handle
-
- WaitInfo* waitInfo = (WaitInfo*) (threadCB->waitPointer[i]).Flink;
- PVOID waitInfoHead = &(threadCB->waitPointer[i]);
-
- do
- {
- WaitInfo* temp = (WaitInfo*) waitInfo->link.Flink;
-
- DeactivateNthWait(waitInfo,i);
-
-
- // Note, we cannot cleanup here since there is no way to suppress finalization
- // we will just leak, and rely on the finalizer to clean up the memory
- //if (InterlockedDecrement(&waitInfo->refCount) == 0)
- // DeleteWait(waitInfo);
-
-
- waitInfo = temp;
-
- } while ((PVOID) waitInfo != waitInfoHead);
-
- break;
- }
- }
- }
- }
-
- //This is unreachable...so no return required.
-}
-#ifdef _PREFAST_
-#pragma warning(pop)
-#endif
-
-#ifdef _MSC_VER
-#ifdef HOST_64BIT
-#pragma warning (default : 4716)
-#else
-#pragma warning (default : 4715)
-#endif
-#endif
-
-void ThreadpoolMgr::ProcessWaitCompletion(WaitInfo* waitInfo,
- unsigned index,
- BOOL waitTimedOut
- )
-{
- STATIC_CONTRACT_THROWS;
- STATIC_CONTRACT_GC_TRIGGERS;
- STATIC_CONTRACT_MODE_PREEMPTIVE;
- /* cannot use contract because of SEH
- CONTRACTL
- {
- THROWS;
- GC_TRIGGERS;
- MODE_PREEMPTIVE;
- }
- CONTRACTL_END;*/
-
- AsyncCallback* asyncCallback = NULL;
- EX_TRY{
- if ( waitInfo->flag & WAIT_SINGLE_EXECUTION)
- {
- DeactivateNthWait (waitInfo,index) ;
- }
- else
- { // reactivate wait by resetting timer
- waitInfo->timer.startTime = GetTickCount();
- }
-
- asyncCallback = MakeAsyncCallback();
- if (asyncCallback)
- {
- asyncCallback->wait = waitInfo;
- asyncCallback->waitTimedOut = waitTimedOut;
-
- InterlockedIncrement(&waitInfo->refCount);
-
-#ifndef TARGET_UNIX
- if (FALSE == PostQueuedCompletionStatus((LPOVERLAPPED)asyncCallback, (LPOVERLAPPED_COMPLETION_ROUTINE)WaitIOCompletionCallback))
-#else // TARGET_UNIX
- if (FALSE == QueueUserWorkItem(AsyncCallbackCompletion, asyncCallback, QUEUE_ONLY))
-#endif // !TARGET_UNIX
- ReleaseAsyncCallback(asyncCallback);
- }
- }
- EX_CATCH {
- if (asyncCallback)
- ReleaseAsyncCallback(asyncCallback);
-
- EX_RETHROW;
- }
- EX_END_CATCH(SwallowAllExceptions);
-}
-
-
-DWORD WINAPI ThreadpoolMgr::AsyncCallbackCompletion(PVOID pArgs)
-{
- CONTRACTL
- {
- THROWS;
- MODE_PREEMPTIVE;
- GC_TRIGGERS;
- }
- CONTRACTL_END;
-
- Thread * pThread = GetThreadNULLOk();
- if (pThread == NULL)
- {
- HRESULT hr = ERROR_SUCCESS;
-
- ClrFlsSetThreadType(ThreadType_Threadpool_Worker);
- pThread = SetupThreadNoThrow(&hr);
-
- if (pThread == NULL)
- {
- return hr;
- }
- }
-
- {
- AsyncCallback * asyncCallback = (AsyncCallback*) pArgs;
-
- WaitInfo * waitInfo = asyncCallback->wait;
-
- AsyncCallbackHolder asyncCBHolder;
- asyncCBHolder.Assign(asyncCallback);
-
- // We fire the "dequeue" ETW event here, before executing the user code, to enable correlation with
- // the ThreadPoolIOEnqueue fired in ThreadpoolMgr::RegisterWaitForSingleObject
- if (ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context, ThreadPoolIODequeue))
- FireEtwThreadPoolIODequeue(waitInfo, reinterpret_cast(waitInfo->Callback), GetClrInstanceId());
-
- // the user callback can throw, the host must be prepared to handle it.
- // SQL is ok, since they have a top-level SEH handler. However, there's
- // no easy way to verify it
-
- ((WAITORTIMERCALLBACKFUNC) waitInfo->Callback)
- ( waitInfo->Context, asyncCallback->waitTimedOut != FALSE);
-
-#ifndef TARGET_UNIX
- Thread::IncrementIOThreadPoolCompletionCount(pThread);
-#endif
- }
-
- return ERROR_SUCCESS;
-}
-
-void ThreadpoolMgr::DeactivateWait(WaitInfo* waitInfo)
-{
- LIMITED_METHOD_CONTRACT;
-
- ThreadCB* threadCB = waitInfo->threadCB;
- DWORD endIndex = threadCB->NumActiveWaits-1;
- DWORD index;
-
- for (index = 0; index <= endIndex; index++)
- {
- LIST_ENTRY* head = &(threadCB->waitPointer[index]);
- LIST_ENTRY* current = head;
- do {
- if (current->Flink == (PVOID) waitInfo)
- goto FOUND;
-
- current = (LIST_ENTRY*) current->Flink;
-
- } while (current != head);
- }
-
-FOUND:
- _ASSERTE(index <= endIndex);
-
- DeactivateNthWait(waitInfo, index);
-}
-
-
-void ThreadpoolMgr::DeactivateNthWait(WaitInfo* waitInfo, DWORD index)
-{
- LIMITED_METHOD_CONTRACT;
- _ASSERTE(!UsePortableThreadPool());
-
- ThreadCB* threadCB = waitInfo->threadCB;
-
- if (waitInfo->link.Flink != waitInfo->link.Blink)
- {
- RemoveEntryList(&(waitInfo->link));
- }
- else
- {
-
- ULONG EndIndex = threadCB->NumActiveWaits -1;
-
- // Move the remaining ActiveWaitArray left.
-
- ShiftWaitArray( threadCB, index+1, index,EndIndex - index ) ;
-
- // repair the blink and flink of the first and last elements in the list
- for (unsigned int i = 0; i< EndIndex-index; i++)
- {
- WaitInfo* firstWaitInfo = (WaitInfo*) threadCB->waitPointer[index+i].Flink;
- WaitInfo* lastWaitInfo = (WaitInfo*) threadCB->waitPointer[index+i].Blink;
- firstWaitInfo->link.Blink = &(threadCB->waitPointer[index+i]);
- lastWaitInfo->link.Flink = &(threadCB->waitPointer[index+i]);
- }
- // initialize the entry just freed
- InitializeListHead(&(threadCB->waitPointer[EndIndex]));
-
- threadCB->NumActiveWaits-- ;
- InterlockedDecrement(&threadCB->NumWaitHandles);
- }
-
- waitInfo->state &= ~WAIT_ACTIVE ;
-
-}
-
-void ThreadpoolMgr::DeleteWait(WaitInfo* waitInfo)
-{
- CONTRACTL
- {
- if (waitInfo->ExternalEventSafeHandle != NULL) { THROWS;} else { NOTHROW; }
- MODE_ANY;
- if (GetThreadNULLOk()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);}
- }
- CONTRACTL_END;
-
- if(waitInfo->Context && (waitInfo->flag & WAIT_FREE_CONTEXT)) {
- DelegateInfo* pDelegate = (DelegateInfo*) waitInfo->Context;
-
- // Since the delegate release destroys a handle, we need to be in
- // co-operative mode
- {
- GCX_COOP();
- pDelegate->Release();
- }
-
- RecycleMemory( pDelegate, MEMTYPE_DelegateInfo );
- }
-
- if (waitInfo->flag & WAIT_INTERNAL_COMPLETION)
- {
- waitInfo->InternalCompletionEvent.Set();
- return; // waitInfo will be deleted by the thread that's waiting on this event
- }
- else if (waitInfo->ExternalCompletionEvent != INVALID_HANDLE)
- {
- SetEvent(waitInfo->ExternalCompletionEvent);
- }
- else if (waitInfo->ExternalEventSafeHandle != NULL)
- {
- // Release the safe handle and the GC handle holding it
- ReleaseWaitInfo(waitInfo);
- }
-
- delete waitInfo;
-
-
-}
-
-
-
-/************************************************************************/
-BOOL ThreadpoolMgr::UnregisterWaitEx(HANDLE hWaitObject,HANDLE Event)
-{
- CONTRACTL
- {
- THROWS; //NOTHROW;
- if (GetThreadNULLOk()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);}
- MODE_ANY;
- }
- CONTRACTL_END;
-
- _ASSERTE(!UsePortableThreadPool());
- _ASSERTE(IsInitialized()); // cannot call unregister before first registering
-
- const BOOL Blocking = (Event == (HANDLE) -1);
- WaitInfo* waitInfo = (WaitInfo*) hWaitObject;
-
- if (!hWaitObject)
- {
- return FALSE;
- }
-
- // we do not allow callbacks to run in the wait thread, hence the assert
- _ASSERTE(GetCurrentThreadId() != waitInfo->threadCB->threadId);
-
-
- if (Blocking)
- {
- waitInfo->InternalCompletionEvent.CreateAutoEvent(FALSE);
- waitInfo->flag |= WAIT_INTERNAL_COMPLETION;
-
- }
- else
- {
- waitInfo->ExternalCompletionEvent = (Event ? Event : INVALID_HANDLE);
- _ASSERTE((waitInfo->flag & WAIT_INTERNAL_COMPLETION) == 0);
- // we still want to block until the wait has been deactivated
- waitInfo->PartialCompletionEvent.CreateAutoEvent(FALSE);
- }
-
- BOOL status = QueueDeregisterWait(waitInfo->threadCB->threadHandle, waitInfo);
-
-
- if (status == 0)
- {
- STRESS_LOG1(LF_THREADPOOL, LL_ERROR, "Queue APC failed in UnregisterWaitEx %x", status);
-
- if (Blocking)
- waitInfo->InternalCompletionEvent.CloseEvent();
- else
- waitInfo->PartialCompletionEvent.CloseEvent();
- return FALSE;
- }
-
- if (!Blocking)
- {
- waitInfo->PartialCompletionEvent.Wait(INFINITE,TRUE);
- waitInfo->PartialCompletionEvent.CloseEvent();
- // we cannot do DeleteWait in DeregisterWait, since the DeleteWait could happen before
- // we close the event. So, the code has been moved here.
- if (InterlockedDecrement(&waitInfo->refCount) == 0)
- {
- DeleteWait(waitInfo);
- }
- }
-
- else // i.e. blocking
- {
- _ASSERTE(waitInfo->flag & WAIT_INTERNAL_COMPLETION);
- _ASSERTE(waitInfo->ExternalEventSafeHandle == NULL);
-
- waitInfo->InternalCompletionEvent.Wait(INFINITE,TRUE);
- waitInfo->InternalCompletionEvent.CloseEvent();
- delete waitInfo; // if WAIT_INTERNAL_COMPLETION is not set, waitInfo will be deleted in DeleteWait
- }
- return TRUE;
-}
-
-
-void ThreadpoolMgr::DeregisterWait(WaitInfo* pArgs)
-{
- WRAPPER_NO_CONTRACT;
-
- WaitInfo* waitInfo = pArgs;
-
- if ( ! (waitInfo->state & WAIT_REGISTERED) )
- {
- // set state to deleted, so that it does not get registered
- waitInfo->state |= WAIT_DELETE ;
-
- // since the wait has not even been registered, we dont need an interlock to decrease the RefCount
- waitInfo->refCount--;
-
- if (waitInfo->PartialCompletionEvent.IsValid())
- {
- waitInfo->PartialCompletionEvent.Set();
- }
- return;
- }
-
- if (waitInfo->state & WAIT_ACTIVE)
- {
- DeactivateWait(waitInfo);
- }
-
- if ( waitInfo->PartialCompletionEvent.IsValid())
- {
- waitInfo->PartialCompletionEvent.Set();
- return; // we cannot delete the wait here since the PartialCompletionEvent
- // may not have been closed yet. so, we return and rely on the waiter of PartialCompletionEvent
- // to do the close
- }
-
- if (InterlockedDecrement(&waitInfo->refCount) == 0)
- {
- DeleteWait(waitInfo);
- }
- return;
-}
-
-
-/* This gets called in a finalizer thread ONLY IF an app does not deregister the
- the wait. Note that just because the registeredWaitHandle is collected by GC
- does not mean it is safe to delete the wait. The refcount tells us when it is
- safe.
-*/
-void ThreadpoolMgr::WaitHandleCleanup(HANDLE hWaitObject)
-{
- LIMITED_METHOD_CONTRACT;
- _ASSERTE(!UsePortableThreadPool());
- _ASSERTE(IsInitialized()); // cannot call cleanup before first registering
-
- WaitInfo* waitInfo = (WaitInfo*) hWaitObject;
- _ASSERTE(waitInfo->refCount > 0);
-
- DWORD result = QueueDeregisterWait(waitInfo->threadCB->threadHandle, waitInfo);
-
- if (result == 0)
- STRESS_LOG1(LF_THREADPOOL, LL_ERROR, "Queue APC failed in WaitHandleCleanup %x", result);
-
-}
-
-BOOL ThreadpoolMgr::CreateGateThread()
-{
- LIMITED_METHOD_CONTRACT;
- _ASSERTE(!UsePortableThreadPool());
-
- HANDLE threadHandle = Thread::CreateUtilityThread(Thread::StackSize_Small, GateThreadStart, NULL, W(".NET ThreadPool Gate"));
-
- if (threadHandle)
- {
- CloseHandle(threadHandle); //we don't need this anymore
- return TRUE;
- }
-
- return FALSE;
-}
-
-
-
-/************************************************************************/
-
-BOOL ThreadpoolMgr::BindIoCompletionCallback(HANDLE FileHandle,
- LPOVERLAPPED_COMPLETION_ROUTINE Function,
- ULONG Flags,
- DWORD& errCode)
-{
- CONTRACTL
- {
- THROWS; // EnsureInitialized can throw
- if (GetThreadNULLOk()) { GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);}
- MODE_ANY;
- }
- CONTRACTL_END;
-
- _ASSERTE(!UsePortableThreadPoolForIO());
-
-#ifndef TARGET_UNIX
-
- errCode = S_OK;
-
- EnsureInitialized();
-
-
- _ASSERTE(GlobalCompletionPort != NULL);
-
- if (!InitCompletionPortThreadpool)
- InitCompletionPortThreadpool = TRUE;
-
- GrowCompletionPortThreadpoolIfNeeded();
-
- HANDLE h = CreateIoCompletionPort(FileHandle,
- GlobalCompletionPort,
- (ULONG_PTR) Function,
- NumberOfProcessors);
- if (h == NULL)
- {
- errCode = GetLastError();
- return FALSE;
- }
-
- _ASSERTE(h == GlobalCompletionPort);
-
- return TRUE;
-#else // TARGET_UNIX
- SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
- return FALSE;
-#endif // !TARGET_UNIX
-}
-
-#ifndef TARGET_UNIX
-BOOL ThreadpoolMgr::CreateCompletionPortThread(LPVOID lpArgs)
-{
- CONTRACTL
- {
- NOTHROW;
- if (GetThreadNULLOk()) { GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);}
- MODE_ANY;
- }
- CONTRACTL_END;
-
- _ASSERTE(!UsePortableThreadPoolForIO());
-
- Thread *pThread;
- BOOL fIsCLRThread;
- if ((pThread = CreateUnimpersonatedThread(CompletionPortThreadStart, lpArgs, &fIsCLRThread)) != NULL)
- {
- LastCPThreadCreation = GetTickCount(); // record this for use by logic to spawn additional threads
-
- if (fIsCLRThread) {
- pThread->ChooseThreadCPUGroupAffinity();
- pThread->StartThread();
- }
- else {
- DWORD status;
- status = ResumeThread((HANDLE)pThread);
- _ASSERTE(status != (DWORD) (-1));
- CloseHandle((HANDLE)pThread); // we don't need this anymore
- }
-
- ThreadCounter::Counts counts = CPThreadCounter.GetCleanCounts();
- FireEtwIOThreadCreate_V1(counts.NumActive + counts.NumRetired, counts.NumRetired, GetClrInstanceId());
-
- return TRUE;
- }
-
-
- return FALSE;
-}
-
-DWORD WINAPI ThreadpoolMgr::CompletionPortThreadStart(LPVOID lpArgs)
-{
- ClrFlsSetThreadType (ThreadType_Threadpool_IOCompletion);
-
- CONTRACTL
- {
- THROWS;
- if (GetThreadNULLOk()) { MODE_PREEMPTIVE;} else { DISABLED(MODE_ANY);}
- if (GetThreadNULLOk()) { GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);}
- }
- CONTRACTL_END;
-
- _ASSERTE_ALL_BUILDS(!UsePortableThreadPoolForIO());
-
- DWORD numBytes=0;
- size_t key=0;
-
- LPOVERLAPPED pOverlapped = NULL;
- DWORD errorCode;
- PIOCompletionContext context;
- BOOL fIsCompletionContext;
-
- const DWORD CP_THREAD_WAIT = 15000; /* milliseconds */
-
- _ASSERTE(GlobalCompletionPort != NULL);
-
- BOOL fThreadInit = FALSE;
- Thread *pThread = NULL;
-
- DWORD cpThreadWait = 0;
-
- if (g_fEEStarted) {
- pThread = SetupThreadNoThrow();
- if (pThread == NULL) {
- return 0;
- }
-
- // converted to CLRThread and added to ThreadStore, pick an group affinity for this thread
- pThread->ChooseThreadCPUGroupAffinity();
-
- fThreadInit = TRUE;
- }
-
-#ifdef FEATURE_COMINTEROP
- // Threadpool threads should be initialized as MTA. If we are unable to do so,
- // return failure.
- BOOL fCoInited = FALSE;
- {
- fCoInited = SUCCEEDED(::CoInitializeEx(NULL, COINIT_MULTITHREADED));
- if (!fCoInited)
- {
- goto Exit;
- }
- }
-
- if (pThread && pThread->SetApartment(Thread::AS_InMTA) != Thread::AS_InMTA)
- {
- // @todo: should we log the failure
- goto Exit;
- }
-#endif // FEATURE_COMINTEROP
-
- ThreadCounter::Counts oldCounts;
- ThreadCounter::Counts newCounts;
-
- cpThreadWait = CP_THREAD_WAIT;
- for (;; )
- {
-Top:
- if (!fThreadInit) {
- if (g_fEEStarted) {
- pThread = SetupThreadNoThrow();
- if (pThread == NULL) {
- break;
- }
-
- // converted to CLRThread and added to ThreadStore, pick an group affinity for this thread
- pThread->ChooseThreadCPUGroupAffinity();
-
-#ifdef FEATURE_COMINTEROP
- if (pThread->SetApartment(Thread::AS_InMTA) != Thread::AS_InMTA)
- {
- // @todo: should we log the failure
- goto Exit;
- }
-#endif // FEATURE_COMINTEROP
-
- fThreadInit = TRUE;
- }
- }
-
- GCX_PREEMP_NO_DTOR();
-
- //
- // We're about to wait on the IOCP; mark ourselves as no longer "working."
- //
- while (true)
- {
- ThreadCounter::Counts oldCounts = CPThreadCounter.DangerousGetDirtyCounts();
- ThreadCounter::Counts newCounts = oldCounts;
- newCounts.NumWorking--;
-
- //
- // If we've only got one thread left, it won't be allowed to exit, because we need to keep
- // one thread listening for completions. So there's no point in having a timeout; it will
- // only use power unnecessarily.
- //
- cpThreadWait = (newCounts.NumActive == 1) ? INFINITE : CP_THREAD_WAIT;
-
- if (oldCounts == CPThreadCounter.CompareExchangeCounts(newCounts, oldCounts))
- break;
- }
-
- errorCode = S_OK;
-
- if (lpArgs == NULL)
- {
- CONTRACT_VIOLATION(ThrowsViolation);
-
- context = NULL;
- fIsCompletionContext = FALSE;
-
- if (pThread == NULL)
- {
- pThread = GetThreadNULLOk();
- }
-
- if (pThread)
- {
- context = (PIOCompletionContext) pThread->GetIOCompletionContext();
-
- if (context->lpOverlapped != NULL)
- {
- errorCode = context->ErrorCode;
- numBytes = context->numBytesTransferred;
- pOverlapped = context->lpOverlapped;
- key = context->key;
-
- context->lpOverlapped = NULL;
- fIsCompletionContext = TRUE;
- }
- }
-
- if((context == NULL) || (!fIsCompletionContext))
- {
- _ASSERTE (context == NULL || context->lpOverlapped == NULL);
-
- BOOL status = GetQueuedCompletionStatus(
- GlobalCompletionPort,
- &numBytes,
- (PULONG_PTR)&key,
- &pOverlapped,
- cpThreadWait
- );
-
- if (status == 0)
- errorCode = GetLastError();
- }
- }
- else
- {
- QueuedStatus *CompletionStatus = (QueuedStatus*)lpArgs;
- numBytes = CompletionStatus->numBytes;
- key = (size_t)CompletionStatus->key;
- pOverlapped = CompletionStatus->pOverlapped;
- errorCode = CompletionStatus->errorCode;
- delete CompletionStatus;
- lpArgs = NULL; // one-time deal for initial CP packet
- }
-
- // We fire IODequeue events whether the IO completion was retrieved in the above call to
- // GetQueuedCompletionStatus or during an earlier call (e.g. in GateThreadStart, and passed here in lpArgs,
- // or in CompletionPortDispatchWorkWithinAppDomain, and passed here through StoreOverlappedInfoInThread)
-
- // For the purposes of activity correlation we only fire ETW events here, if needed OR if not fired at a higher
- // abstraction level (e.g. ThreadpoolMgr::RegisterWaitForSingleObject)
- // Note: we still fire the event for managed async IO, despite the fact we don't have a paired IOEnqueue event
- // for this case. We do this to "mark" the end of the previous workitem. When we provide full support at the higher
- // abstraction level for managed IO we can remove the IODequeues fired here
- if (ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context, ThreadPoolIODequeue)
- && !AreEtwIOQueueEventsSpeciallyHandled((LPOVERLAPPED_COMPLETION_ROUTINE)key) && pOverlapped != NULL)
- {
- FireEtwThreadPoolIODequeue(pOverlapped, OverlappedDataObject::GetOverlappedForTracing(pOverlapped), GetClrInstanceId());
- }
-
- bool enterRetirement;
-
- while (true)
- {
- //
- // When we reach this point, this thread is "active" but not "working." Depending on the result of the call to GetQueuedCompletionStatus,
- // and the state of the rest of the IOCP threads, we need to figure out whether to de-activate (exit) this thread, retire this thread,
- // or transition to "working."
- //
-
- // counts volatile read paired with CompareExchangeCounts loop set
- oldCounts = CPThreadCounter.DangerousGetDirtyCounts();
- newCounts = oldCounts;
- enterRetirement = false;
-
- if (errorCode == WAIT_TIMEOUT)
- {
- //
- // We timed out, and are going to try to exit or retire.
- //
- newCounts.NumActive--;
-
- //
- // We need at least one free thread, or we have no way of knowing if completions are being queued.
- //
- if (newCounts.NumWorking == newCounts.NumActive)
- {
- newCounts = oldCounts;
- newCounts.NumWorking++; //not really working, but we'll decremented it at the top
- if (oldCounts == CPThreadCounter.CompareExchangeCounts(newCounts, oldCounts))
- goto Top;
- else
- continue;
- }
-
- //
- // We can't exit a thread that has pending I/O - we'll "retire" it instead.
- //
- if (IsIoPending())
- {
- enterRetirement = true;
- newCounts.NumRetired++;
- }
- }
- else
- {
- //
- // We have work to do
- //
- newCounts.NumWorking++;
- }
-
- if (oldCounts == CPThreadCounter.CompareExchangeCounts(newCounts, oldCounts))
- break;
- }
-
- if (errorCode == WAIT_TIMEOUT)
- {
- if (!enterRetirement)
- {
- goto Exit;
- }
- else
- {
- // now in "retired mode" waiting for pending io to complete
- FireEtwIOThreadRetire_V1(newCounts.NumActive + newCounts.NumRetired, newCounts.NumRetired, GetClrInstanceId());
-
- for (;;)
- {
- DWORD status = SafeWait(RetiredCPWakeupEvent,CP_THREAD_PENDINGIO_WAIT,FALSE);
- _ASSERTE(status == WAIT_TIMEOUT || status == WAIT_OBJECT_0);
-
- if (status == WAIT_TIMEOUT)
- {
- if (IsIoPending())
- {
- continue;
- }
- else
- {
- // We can now exit; decrement the retired count.
- while (true)
- {
- // counts volatile read paired with CompareExchangeCounts loop set
- oldCounts = CPThreadCounter.DangerousGetDirtyCounts();
- newCounts = oldCounts;
- newCounts.NumRetired--;
- if (oldCounts == CPThreadCounter.CompareExchangeCounts(newCounts, oldCounts))
- break;
- }
- goto Exit;
- }
- }
- else
- {
- // put back into rotation -- we need a thread
- while (true)
- {
- // counts volatile read paired with CompareExchangeCounts loop set
- oldCounts = CPThreadCounter.DangerousGetDirtyCounts();
- newCounts = oldCounts;
- newCounts.NumRetired--;
- newCounts.NumActive++;
- newCounts.NumWorking++; //we're not really working, but we'll decrement this before waiting for work.
- if (oldCounts == CPThreadCounter.CompareExchangeCounts(newCounts, oldCounts))
- break;
- }
- FireEtwIOThreadUnretire_V1(newCounts.NumActive + newCounts.NumRetired, newCounts.NumRetired, GetClrInstanceId());
- goto Top;
- }
- }
- }
- }
-
- // we should not reach this point unless we have work to do
- _ASSERTE(errorCode != WAIT_TIMEOUT && !enterRetirement);
-
- // if we have no more free threads, start the gate thread
- if (newCounts.NumWorking >= newCounts.NumActive)
- EnsureGateThreadRunning();
-
-
- // We can not assert here. If stdin/stdout/stderr of child process are redirected based on
- // async io, GetQueuedCompletionStatus returns when child process operates on its stdin/stdout/stderr.
- // Parent process does not issue any ReadFile/WriteFile, and hence pOverlapped is going to be NULL.
- //_ASSERTE(pOverlapped != NULL);
-
- if (pOverlapped != NULL)
- {
- _ASSERTE(key != 0); // should be a valid function address
-
- if (key != 0)
- {
- if (GCHeapUtilities::IsGCInProgress(TRUE))
- {
- //Indicate that this thread is free, and waiting on GC, not doing any user work.
- //This helps in threads not getting injected when some threads have woken up from the
- //GC event, and some have not.
- while (true)
- {
- // counts volatile read paired with CompareExchangeCounts loop set
- oldCounts = CPThreadCounter.DangerousGetDirtyCounts();
- newCounts = oldCounts;
- newCounts.NumWorking--;
- if (oldCounts == CPThreadCounter.CompareExchangeCounts(newCounts, oldCounts))
- break;
- }
-
- // GC is imminent, so wait until GC is complete before executing next request.
- // this reduces in-flight objects allocated right before GC, easing the GC's work
- GCHeapUtilities::WaitForGCCompletion(TRUE);
-
- while (true)
- {
- // counts volatile read paired with CompareExchangeCounts loop set
- oldCounts = CPThreadCounter.DangerousGetDirtyCounts();
- newCounts = oldCounts;
- newCounts.NumWorking++;
- if (oldCounts == CPThreadCounter.CompareExchangeCounts(newCounts, oldCounts))
- break;
- }
-
- if (newCounts.NumWorking >= newCounts.NumActive)
- EnsureGateThreadRunning();
- }
- else
- {
- GrowCompletionPortThreadpoolIfNeeded();
- }
-
- {
- CONTRACT_VIOLATION(ThrowsViolation);
-
- ((LPOVERLAPPED_COMPLETION_ROUTINE) key)(errorCode, numBytes, pOverlapped);
- }
-
- Thread::IncrementIOThreadPoolCompletionCount(pThread);
-
- if (pThread == NULL)
- {
- pThread = GetThreadNULLOk();
- }
-
- if (pThread)
- {
- _ASSERTE(!pThread->IsAbortRequested());
- pThread->InternalReset();
- }
- }
- else
- {
- // Application bug - can't do much, just ignore it
- }
-
- }
-
- } // for (;;)
-
-Exit:
-
- oldCounts = CPThreadCounter.GetCleanCounts();
-
- // we should never destroy or retire all IOCP threads, because then we won't have any threads to notice incoming completions.
- _ASSERTE(oldCounts.NumActive > 0);
-
- FireEtwIOThreadTerminate_V1(oldCounts.NumActive + oldCounts.NumRetired, oldCounts.NumRetired, GetClrInstanceId());
-
-#ifdef FEATURE_COMINTEROP
- if (pThread) {
- pThread->SetApartment(Thread::AS_Unknown);
- pThread->CoUninitialize();
- }
- // Couninit the worker thread
- if (fCoInited)
- {
- CoUninitialize();
- }
-#endif
-
- if (pThread) {
- pThread->ClearThreadCPUGroupAffinity();
-
- DestroyThread(pThread);
- }
-
- return 0;
-}
-
-LPOVERLAPPED ThreadpoolMgr::CompletionPortDispatchWorkWithinAppDomain(
- Thread* pThread,
- DWORD* pErrorCode,
- DWORD* pNumBytes,
- size_t* pKey)
-//
-//This function is called just after dispatching the previous BindIO callback
-//to Managed code. This is a perf optimization to do a quick call to
-//GetQueuedCompletionStatus with a timeout of 0 ms. If there is work in the
-//same appdomain, dispatch it back immediately. If not stick it in a well known
-//place, and reenter the target domain. The timeout of zero is chosen so as to
-//not delay appdomain unloads.
-//
-{
- STATIC_CONTRACT_THROWS;
- STATIC_CONTRACT_GC_NOTRIGGER;
- STATIC_CONTRACT_MODE_ANY;
-
- LPOVERLAPPED lpOverlapped=NULL;
-
- BOOL status=FALSE;
- OVERLAPPEDDATAREF overlapped=NULL;
- BOOL ManagedCallback=FALSE;
-
- *pErrorCode = S_OK;
-
-
- //Very Very Important!
- //Do not change the timeout for GetQueuedCompletionStatus to a non-zero value.
- //Selecting a non-zero value can cause the thread to block, and lead to expensive context switches.
- //In real life scenarios, we have noticed a packet to be not available immediately, but very shortly
- //(after few 100's of instructions), and falling back to the VM is good in that case as compared to
- //taking a context switch. Changing the timeout to non-zero can lead to perf degrades, that are very
- //hard to diagnose.
-
- status = ::GetQueuedCompletionStatus(
- GlobalCompletionPort,
- pNumBytes,
- (PULONG_PTR)pKey,
- &lpOverlapped,
- 0);
-
- DWORD lastError = GetLastError();
-
- if (status == 0)
- {
- if (lpOverlapped != NULL)
- {
- *pErrorCode = lastError;
- }
- else
- {
- return NULL;
- }
- }
-
- if (((LPOVERLAPPED_COMPLETION_ROUTINE) *pKey) != BindIoCompletionCallbackStub)
- {
- //_ASSERTE(FALSE);
- }
- else
- {
- ManagedCallback = TRUE;
- overlapped = ObjectToOVERLAPPEDDATAREF(OverlappedDataObject::GetOverlapped(lpOverlapped));
- }
-
- if (ManagedCallback)
- {
- _ASSERTE(*pKey != 0); // should be a valid function address
-
- if (*pKey ==0)
- {
- //Application Bug.
- return NULL;
- }
- }
- else
- {
- //Just retruned back from managed code, a Thread structure should exist.
- _ASSERTE (pThread);
-
- //Oops, this is an overlapped fom a different appdomain. STick it in
- //the thread. We will process it later.
-
- StoreOverlappedInfoInThread(pThread, *pErrorCode, *pNumBytes, *pKey, lpOverlapped);
-
- lpOverlapped = NULL;
- }
-
-#ifndef DACCESS_COMPILE
- return lpOverlapped;
-#endif
-}
-
-void ThreadpoolMgr::StoreOverlappedInfoInThread(Thread* pThread, DWORD dwErrorCode, DWORD dwNumBytes, size_t key, LPOVERLAPPED lpOverlapped)
-{
- STATIC_CONTRACT_NOTHROW;
- STATIC_CONTRACT_GC_NOTRIGGER;
- STATIC_CONTRACT_MODE_ANY;
-
- _ASSERTE(pThread);
-
- PIOCompletionContext context;
-
- context = (PIOCompletionContext) pThread->GetIOCompletionContext();
-
- _ASSERTE(context);
-
- context->ErrorCode = dwErrorCode;
- context->numBytesTransferred = dwNumBytes;
- context->lpOverlapped = lpOverlapped;
- context->key = key;
-}
-
-BOOL ThreadpoolMgr::ShouldGrowCompletionPortThreadpool(ThreadCounter::Counts counts)
-{
- CONTRACTL
- {
- GC_NOTRIGGER;
- NOTHROW;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- _ASSERTE(!UsePortableThreadPoolForIO());
-
- if (counts.NumWorking >= counts.NumActive
- && (counts.NumActive == 0 || !GCHeapUtilities::IsGCInProgress(TRUE))
- )
- {
- // adjust limit if needed
- if (counts.NumRetired == 0)
- {
- if (counts.NumActive + counts.NumRetired < MaxLimitTotalCPThreads &&
- (counts.NumActive < MinLimitTotalCPThreads || cpuUtilization < CpuUtilizationLow))
- {
- // add one more check to make sure that we haven't fired off a new
- // thread since the last time time we checked the cpu utilization.
- // However, don't bother if we haven't reached the MinLimit (2*number of cpus)
- if ((counts.NumActive < MinLimitTotalCPThreads) ||
- SufficientDelaySinceLastSample(LastCPThreadCreation,counts.NumActive))
- {
- return TRUE;
- }
- }
- }
-
- if (counts.NumRetired > 0)
- return TRUE;
- }
- return FALSE;
-}
-
-void ThreadpoolMgr::GrowCompletionPortThreadpoolIfNeeded()
-{
- CONTRACTL
- {
- if (GetThreadNULLOk()) { GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);}
- NOTHROW;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- _ASSERTE(!UsePortableThreadPoolForIO());
-
- ThreadCounter::Counts oldCounts, newCounts;
- while (true)
- {
- oldCounts = CPThreadCounter.GetCleanCounts();
- newCounts = oldCounts;
-
- if(!ShouldGrowCompletionPortThreadpool(oldCounts))
- {
- break;
- }
- else
- {
- if (oldCounts.NumRetired > 0)
- {
- // wakeup retired thread instead
- RetiredCPWakeupEvent->Set();
- return;
- }
- else
- {
- // create a new thread. New IOCP threads start as "active" and "working"
- newCounts.NumActive++;
- newCounts.NumWorking++;
- if (oldCounts == CPThreadCounter.CompareExchangeCounts(newCounts, oldCounts))
- {
- if (!CreateCompletionPortThread(NULL))
- {
- // if thread creation failed, we have to adjust the counts back down.
- while (true)
- {
- // counts volatile read paired with CompareExchangeCounts loop set
- oldCounts = CPThreadCounter.DangerousGetDirtyCounts();
- newCounts = oldCounts;
- newCounts.NumActive--;
- newCounts.NumWorking--;
- if (oldCounts == CPThreadCounter.CompareExchangeCounts(newCounts, oldCounts))
- break;
- }
- }
- return;
- }
- }
- }
- }
-}
-#endif // !TARGET_UNIX
-
-// Returns true if there is pending io on the thread.
-BOOL ThreadpoolMgr::IsIoPending()
-{
- CONTRACTL
- {
- NOTHROW;
- MODE_ANY;
- GC_NOTRIGGER;
- }
- CONTRACTL_END;
-
-#ifndef TARGET_UNIX
- int Status;
- ULONG IsIoPending;
-
- if (g_pufnNtQueryInformationThread)
- {
- Status =(int) (*g_pufnNtQueryInformationThread)(GetCurrentThread(),
- ThreadIsIoPending,
- &IsIoPending,
- sizeof(IsIoPending),
- NULL);
-
-
- if ((Status < 0) || IsIoPending)
- return TRUE;
- else
- return FALSE;
- }
- return TRUE;
-#else
- return FALSE;
-#endif // !TARGET_UNIX
-}
-
-#ifndef TARGET_UNIX
-
-#ifdef HOST_64BIT
-#pragma warning (disable : 4716)
-#else
-#pragma warning (disable : 4715)
-#endif
-
-int ThreadpoolMgr::GetCPUBusyTime_NT(PROCESS_CPU_INFORMATION* pOldInfo)
-{
- LIMITED_METHOD_CONTRACT;
-
- PROCESS_CPU_INFORMATION newUsage;
- newUsage.idleTime.QuadPart = 0;
- newUsage.kernelTime.QuadPart = 0;
- newUsage.userTime.QuadPart = 0;
-
- if (CPUGroupInfo::CanEnableThreadUseAllCpuGroups())
- {
-#if !defined(FEATURE_NATIVEAOT) && !defined(TARGET_UNIX)
- FILETIME newIdleTime, newKernelTime, newUserTime;
-
- CPUGroupInfo::GetSystemTimes(&newIdleTime, &newKernelTime, &newUserTime);
- newUsage.idleTime.u.LowPart = newIdleTime.dwLowDateTime;
- newUsage.idleTime.u.HighPart = newIdleTime.dwHighDateTime;
- newUsage.kernelTime.u.LowPart = newKernelTime.dwLowDateTime;
- newUsage.kernelTime.u.HighPart = newKernelTime.dwHighDateTime;
- newUsage.userTime.u.LowPart = newUserTime.dwLowDateTime;
- newUsage.userTime.u.HighPart = newUserTime.dwHighDateTime;
-#endif
- }
- else
- {
- (*g_pufnNtQuerySystemInformation)(SystemProcessorPerformanceInformation,
- pOldInfo->usageBuffer,
- pOldInfo->usageBufferSize,
- NULL);
-
- SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION* pInfoArray = pOldInfo->usageBuffer;
- DWORD_PTR pmask = pOldInfo->affinityMask;
-
- int proc_no = 0;
- while (pmask)
- {
- if (pmask & 1)
- { //should be good: 1CPU 28823 years, 256CPUs 100+years
- newUsage.idleTime.QuadPart += pInfoArray[proc_no].IdleTime.QuadPart;
- newUsage.kernelTime.QuadPart += pInfoArray[proc_no].KernelTime.QuadPart;
- newUsage.userTime.QuadPart += pInfoArray[proc_no].UserTime.QuadPart;
- }
-
- pmask >>=1;
- proc_no++;
- }
- }
-
- __int64 cpuTotalTime, cpuBusyTime;
-
- cpuTotalTime = (newUsage.userTime.QuadPart - pOldInfo->userTime.QuadPart) +
- (newUsage.kernelTime.QuadPart - pOldInfo->kernelTime.QuadPart);
- cpuBusyTime = cpuTotalTime -
- (newUsage.idleTime.QuadPart - pOldInfo->idleTime.QuadPart);
-
- // Preserve reading
- pOldInfo->idleTime = newUsage.idleTime;
- pOldInfo->kernelTime = newUsage.kernelTime;
- pOldInfo->userTime = newUsage.userTime;
-
- __int64 reading = 0;
-
- if (cpuTotalTime > 0)
- reading = ((cpuBusyTime * 100) / cpuTotalTime);
-
- _ASSERTE(FitsIn(reading));
- return (int)reading;
-}
-
-#else // !TARGET_UNIX
-
-int ThreadpoolMgr::GetCPUBusyTime_NT(PAL_IOCP_CPU_INFORMATION* pOldInfo)
-{
- return PAL_GetCPUBusyTime(pOldInfo);
-}
-
-#endif // !TARGET_UNIX
-
-//
-// A timer that ticks every GATE_THREAD_DELAY milliseconds.
-// On platforms that support it, we use a coalescable waitable timer object.
-// For other platforms, we use Sleep, via __SwitchToThread.
-//
-class GateThreadTimer
-{
-#ifndef TARGET_UNIX
- HANDLE m_hTimer;
-
-public:
- GateThreadTimer()
- : m_hTimer(NULL)
- {
- CONTRACTL
- {
- NOTHROW;
- MODE_PREEMPTIVE;
- }
- CONTRACTL_END;
-
- if (g_pufnCreateWaitableTimerEx && g_pufnSetWaitableTimerEx)
- {
- m_hTimer = g_pufnCreateWaitableTimerEx(NULL, NULL, 0, TIMER_ALL_ACCESS);
- if (m_hTimer)
- {
- //
- // Set the timer to fire GATE_THREAD_DELAY milliseconds from now, then every GATE_THREAD_DELAY milliseconds thereafter.
- // We also set the tolerance to GET_THREAD_DELAY_TOLERANCE, allowing the OS to coalesce this timer.
- //
- LARGE_INTEGER dueTime;
- dueTime.QuadPart = MILLI_TO_100NANO(-(LONGLONG)GATE_THREAD_DELAY); //negative value indicates relative time
- if (!g_pufnSetWaitableTimerEx(m_hTimer, &dueTime, GATE_THREAD_DELAY, NULL, NULL, NULL, GATE_THREAD_DELAY_TOLERANCE))
- {
- CloseHandle(m_hTimer);
- m_hTimer = NULL;
- }
- }
- }
- }
-
- ~GateThreadTimer()
- {
- CONTRACTL
- {
- NOTHROW;
- MODE_PREEMPTIVE;
- }
- CONTRACTL_END;
-
- if (m_hTimer)
- {
- CloseHandle(m_hTimer);
- m_hTimer = NULL;
- }
- }
-
-#endif // !TARGET_UNIX
-
-public:
- void Wait()
- {
- CONTRACTL
- {
- NOTHROW;
- MODE_PREEMPTIVE;
- }
- CONTRACTL_END;
-
-#ifndef TARGET_UNIX
- if (m_hTimer)
- WaitForSingleObject(m_hTimer, INFINITE);
- else
-#endif // !TARGET_UNIX
- __SwitchToThread(GATE_THREAD_DELAY, CALLER_LIMITS_SPINNING);
- }
-};
-
-DWORD WINAPI ThreadpoolMgr::GateThreadStart(LPVOID lpArgs)
-{
- ClrFlsSetThreadType (ThreadType_Gate);
-
- CONTRACTL
- {
- NOTHROW;
- GC_TRIGGERS;
- MODE_PREEMPTIVE;
- }
- CONTRACTL_END;
-
- _ASSERTE(!UsePortableThreadPool());
- _ASSERTE(GateThreadStatus == GATE_THREAD_STATUS_REQUESTED);
-
- GateThreadTimer timer;
-
- // TODO: do we need to do this?
- timer.Wait(); // delay getting initial CPU reading
-
-#ifndef TARGET_UNIX
- PROCESS_CPU_INFORMATION prevCPUInfo;
-
- if (!g_pufnNtQuerySystemInformation)
- {
- _ASSERT(!"NtQuerySystemInformation API not available!");
- return 0;
- }
-
-#ifndef TARGET_UNIX
- //GateThread can start before EESetup, so ensure CPU group information is initialized;
- CPUGroupInfo::EnsureInitialized();
-#endif // !TARGET_UNIX
- // initialize CPU usage information structure;
- prevCPUInfo.idleTime.QuadPart = 0;
- prevCPUInfo.kernelTime.QuadPart = 0;
- prevCPUInfo.userTime.QuadPart = 0;
-
- PREFIX_ASSUME(NumberOfProcessors < 65536);
- prevCPUInfo.numberOfProcessors = NumberOfProcessors;
-
- /* In following cases, affinity mask can be zero
- * 1. hosted, the hosted process already uses multiple cpu groups.
- * thus, during CLR initialization, GetCurrentProcessCpuCount() returns 64, and GC threads
- * are created to fill up the initial CPU group. ==> use g_SystemInfo.dwNumberOfProcessors
- * 2. GCCpuGroups=1, CLR creates GC threads for all processors in all CPU groups
- * thus, the threadpool thread would use a whole CPU group (if Thread_UseAllCpuGroups is not set).
- * ==> use g_SystemInfo.dwNumberOfProcessors.
- * 3. !defined(TARGET_UNIX), GetCurrentProcessCpuCount()
- * returns g_SystemInfo.dwNumberOfProcessors ==> use g_SystemInfo.dwNumberOfProcessors;
- * Other cases:
- * 1. Normal case: the mask is all or a subset of all processors in a CPU group;
- * 2. GCCpuGroups=1 && Thread_UseAllCpuGroups = 1, the mask is not used
- */
- prevCPUInfo.affinityMask = GetCurrentProcessCpuMask();
- if (prevCPUInfo.affinityMask == 0)
- { // create a mask that has g_SystemInfo.dwNumberOfProcessors;
- DWORD_PTR mask = 0, maskpos = 1;
- for (unsigned int i=0; i < g_SystemInfo.dwNumberOfProcessors; i++)
- {
- mask |= maskpos;
- maskpos <<= 1;
- }
- prevCPUInfo.affinityMask = mask;
- }
-
- // in some cases GetCurrentProcessCpuCount() returns a number larger than
- // g_SystemInfo.dwNumberOfProcessor when there are CPU groups, use the larger
- // one to create buffer. This buffer must be cleared with 0's to get correct
- // CPU usage statistics
- int elementsNeeded = NumberOfProcessors > g_SystemInfo.dwNumberOfProcessors ?
- NumberOfProcessors : g_SystemInfo.dwNumberOfProcessors;
-
- //
- // When CLR threads are not using all groups, GetCPUBusyTime_NT will read element X from
- // the "prevCPUInfo.usageBuffer" array if and only if "prevCPUInfo.affinityMask" contains a
- // set bit in bit position X. This implies that GetCPUBusyTime_NT would read past the end
- // of the "usageBuffer" array if the index of the highest set bit in "affinityMask" was
- // ever larger than the index of the last array element.
- //
- // If necessary, expand "elementsNeeded" to ensure that the index of the last array element
- // is always at least as large as the index of the highest set bit in the "affinityMask".
- //
- // This expansion is necessary in any case where the mask returned by GetCurrentProcessCpuMask
- // (which is just a wrapper around the Win32 GetProcessAffinityMask API) contains set bits
- // at indices greater than or equal to the larger of the basline CPU count values (i.e.,
- // ThreadpoolMgr::NumberOfProcessors and g_SystemInfo.dwNumberOfProcessors) that were
- // captured earlier on (during ThreadpoolMgr::Initialize and during EEStartupHelper,
- // respectively). Note that in the relevant scenario (i.e., when CLR threads are not using
- // all groups) the mask and CPU counts are all collected via "group-unaware" APIs and are
- // all "group-local" values as a result.
- //
- // Expansion is necessary in at least the following cases:
- //
- // - If the baseline CPU counts were captured while running in groups that contain fewer
- // CPUs (in a multi-group system with heterogenous CPU counts across groups), but this code
- // is now running in a group that contains a larger number of CPUs.
- //
- // - If CPUs were hot-added to the system and then added to the current process affinity
- // mask at some point after the baseline CPU counts were captured.
- //
- if (!CPUGroupInfo::CanEnableThreadUseAllCpuGroups())
- {
- int elementsNeededToCoverMask = 0;
- DWORD_PTR remainingMask = prevCPUInfo.affinityMask;
- while (remainingMask != 0)
- {
- elementsNeededToCoverMask++;
- remainingMask >>= 1;
- }
-
- if (elementsNeeded < elementsNeededToCoverMask)
- {
- elementsNeeded = elementsNeededToCoverMask;
- }
- }
-
- if (!ClrSafeInt::multiply(elementsNeeded, sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION),
- prevCPUInfo.usageBufferSize))
- return 0;
-
- prevCPUInfo.usageBuffer = (SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *)alloca(prevCPUInfo.usageBufferSize);
- if (prevCPUInfo.usageBuffer == NULL)
- return 0;
-
- memset((void *)prevCPUInfo.usageBuffer, 0, prevCPUInfo.usageBufferSize); //must clear it with 0s
-
- GetCPUBusyTime_NT(&prevCPUInfo);
-#else // !TARGET_UNIX
- PAL_IOCP_CPU_INFORMATION prevCPUInfo;
- memset(&prevCPUInfo, 0, sizeof(prevCPUInfo));
-
- GetCPUBusyTime_NT(&prevCPUInfo); // ignore return value the first time
-#endif // !TARGET_UNIX
-
- BOOL IgnoreNextSample = FALSE;
-
- do
- {
- timer.Wait();
-
- if(CLRConfig::GetConfigValue(CLRConfig::INTERNAL_ThreadPool_EnableWorkerTracking))
- FireEtwThreadPoolWorkingThreadCount(TakeMaxWorkingThreadCount(), GetClrInstanceId());
-
-#ifdef DEBUGGING_SUPPORTED
- // if we are stopped at a debug breakpoint, go back to sleep
- if (CORDebuggerAttached() && g_pDebugInterface->IsStopped())
- continue;
-#endif // DEBUGGING_SUPPORTED
-
- if (!GCHeapUtilities::IsGCInProgress(FALSE) )
- {
- if (IgnoreNextSample)
- {
- IgnoreNextSample = FALSE;
- int cpuUtilizationTemp = GetCPUBusyTime_NT(&prevCPUInfo); // updates prevCPUInfo as side effect
- // don't artificially drive down average if cpu is high
- if (cpuUtilizationTemp <= CpuUtilizationLow)
- cpuUtilization = CpuUtilizationLow + 1;
- else
- cpuUtilization = cpuUtilizationTemp;
- }
- else
- {
- cpuUtilization = GetCPUBusyTime_NT(&prevCPUInfo); // updates prevCPUInfo as side effect
- }
- }
- else
- {
- int cpuUtilizationTemp = GetCPUBusyTime_NT(&prevCPUInfo); // updates prevCPUInfo as side effect
- // don't artificially drive down average if cpu is high
- if (cpuUtilizationTemp <= CpuUtilizationLow)
- cpuUtilization = CpuUtilizationLow + 1;
- else
- cpuUtilization = cpuUtilizationTemp;
- IgnoreNextSample = TRUE;
- }
-
- PerformGateActivities(cpuUtilization);
- }
- while (ShouldGateThreadKeepRunning());
-
- return 0;
-}
-
-void ThreadpoolMgr::PerformGateActivities(int cpuUtilization)
-{
- CONTRACTL
- {
- NOTHROW;
- GC_TRIGGERS;
- MODE_PREEMPTIVE;
- }
- CONTRACTL_END;
-
- _ASSERTE(!UsePortableThreadPoolForIO());
-
- ThreadpoolMgr::cpuUtilization = cpuUtilization;
-
-#ifndef TARGET_UNIX
- // don't mess with CP thread pool settings if not initialized yet
- if (InitCompletionPortThreadpool)
- {
- ThreadCounter::Counts oldCounts, newCounts;
- oldCounts = CPThreadCounter.GetCleanCounts();
-
- if (oldCounts.NumActive == oldCounts.NumWorking &&
- oldCounts.NumRetired == 0 &&
- oldCounts.NumActive < MaxLimitTotalCPThreads &&
- !GCHeapUtilities::IsGCInProgress(TRUE))
-
- {
- BOOL status;
- DWORD numBytes;
- size_t key;
- LPOVERLAPPED pOverlapped;
- DWORD errorCode;
-
- errorCode = S_OK;
-
- status = GetQueuedCompletionStatus(
- GlobalCompletionPort,
- &numBytes,
- (PULONG_PTR)&key,
- &pOverlapped,
- 0 // immediate return
- );
-
- if (status == 0)
- {
- errorCode = GetLastError();
- }
-
- if (errorCode != WAIT_TIMEOUT)
- {
- QueuedStatus *CompletionStatus = NULL;
-
- // loop, retrying until memory is allocated. Under such conditions the gate
- // thread is not useful anyway, so I feel comfortable with this behavior
- do
- {
- // make sure to free mem later in thread
- CompletionStatus = new (nothrow) QueuedStatus;
- if (CompletionStatus == NULL)
- {
- __SwitchToThread(GATE_THREAD_DELAY, CALLER_LIMITS_SPINNING);
- }
- }
- while (CompletionStatus == NULL);
-
- CompletionStatus->numBytes = numBytes;
- CompletionStatus->key = (PULONG_PTR)key;
- CompletionStatus->pOverlapped = pOverlapped;
- CompletionStatus->errorCode = errorCode;
-
- // IOCP threads are created as "active" and "working"
- while (true)
- {
- // counts volatile read paired with CompareExchangeCounts loop set
- oldCounts = CPThreadCounter.DangerousGetDirtyCounts();
- newCounts = oldCounts;
- newCounts.NumActive++;
- newCounts.NumWorking++;
- if (oldCounts == CPThreadCounter.CompareExchangeCounts(newCounts, oldCounts))
- break;
- }
-
- // loop, retrying until thread is created.
- while (!CreateCompletionPortThread((LPVOID)CompletionStatus))
- {
- __SwitchToThread(GATE_THREAD_DELAY, CALLER_LIMITS_SPINNING);
- }
- }
- }
- else if (cpuUtilization < CpuUtilizationLow)
- {
- // this could be an indication that threads might be getting blocked or there is no work
- if (oldCounts.NumWorking == oldCounts.NumActive && // don't bump the limit if there are already free threads
- oldCounts.NumRetired > 0)
- {
- RetiredCPWakeupEvent->Set();
- }
- }
- }
-#endif // !TARGET_UNIX
-
- if (!UsePortableThreadPool() &&
- 0 == CLRConfig::GetConfigValue(CLRConfig::INTERNAL_ThreadPool_DisableStarvationDetection))
- {
- if (PerAppDomainTPCountList::AreRequestsPendingInAnyAppDomains() && SufficientDelaySinceLastDequeue())
- {
- DangerousNonHostedSpinLockHolder tal(&ThreadAdjustmentLock);
-
- ThreadCounter::Counts counts = WorkerCounter.GetCleanCounts();
- while (counts.NumActive < MaxLimitTotalWorkerThreads && //don't add a thread if we're at the max
- counts.NumActive >= counts.MaxWorking) //don't add a thread if we're already in the process of adding threads
- {
- bool breakIntoDebugger = (0 != CLRConfig::GetConfigValue(CLRConfig::INTERNAL_ThreadPool_DebugBreakOnWorkerStarvation));
- if (breakIntoDebugger)
- {
- OutputDebugStringW(W("The CLR ThreadPool detected work queue starvation!"));
- DebugBreak();
- }
-
- ThreadCounter::Counts newCounts = counts;
- newCounts.MaxWorking = newCounts.NumActive + 1;
-
- ThreadCounter::Counts oldCounts = WorkerCounter.CompareExchangeCounts(newCounts, counts);
- if (oldCounts == counts)
- {
- HillClimbingInstance.ForceChange(newCounts.MaxWorking, Starvation);
- MaybeAddWorkingWorker();
- break;
- }
- else
- {
- counts = oldCounts;
- }
- }
- }
- }
-}
-
-// called by logic to spawn a new completion port thread.
-// return false if not enough time has elapsed since the last
-// time we sampled the cpu utilization.
-BOOL ThreadpoolMgr::SufficientDelaySinceLastSample(unsigned int LastThreadCreationTime,
- unsigned NumThreads, // total number of threads of that type (worker or CP)
- double throttleRate // the delay is increased by this percentage for each extra thread
- )
-{
- LIMITED_METHOD_CONTRACT;
-
- unsigned dwCurrentTickCount = GetTickCount();
-
- unsigned delaySinceLastThreadCreation = dwCurrentTickCount - LastThreadCreationTime;
-
- unsigned minWaitBetweenThreadCreation = GATE_THREAD_DELAY;
-
- if (throttleRate > 0.0)
- {
- _ASSERTE(throttleRate <= 1.0);
-
- unsigned adjustedThreadCount = NumThreads > NumberOfProcessors ? (NumThreads - NumberOfProcessors) : 0;
-
- minWaitBetweenThreadCreation = (unsigned) (GATE_THREAD_DELAY * pow((1.0 + throttleRate),(double)adjustedThreadCount));
- }
- // the amount of time to wait should grow up as the number of threads is increased
-
- return (delaySinceLastThreadCreation > minWaitBetweenThreadCreation);
-
-}
-
-
-// called by logic to spawn new worker threads, return true if it's been too long
-// since the last dequeue operation - takes number of worker threads into account
-// in deciding "too long"
-BOOL ThreadpoolMgr::SufficientDelaySinceLastDequeue()
-{
- LIMITED_METHOD_CONTRACT;
- _ASSERTE(!UsePortableThreadPool());
-
- #define DEQUEUE_DELAY_THRESHOLD (GATE_THREAD_DELAY * 2)
-
- unsigned delay = GetTickCount() - VolatileLoad(&LastDequeueTime);
- unsigned tooLong;
-
- if(cpuUtilization < CpuUtilizationLow)
- {
- tooLong = GATE_THREAD_DELAY;
- }
- else
- {
- ThreadCounter::Counts counts = WorkerCounter.GetCleanCounts();
- unsigned numThreads = counts.MaxWorking;
- tooLong = numThreads * DEQUEUE_DELAY_THRESHOLD;
- }
-
- return (delay > tooLong);
-
-}
-
-
-#ifdef _MSC_VER
-#ifdef HOST_64BIT
-#pragma warning (default : 4716)
-#else
-#pragma warning (default : 4715)
-#endif
-#endif
-
-#endif // !DACCESS_COMPILE
diff --git a/src/coreclr/vm/win32threadpool.h b/src/coreclr/vm/win32threadpool.h
deleted file mode 100644
index fe215efb7ced85..00000000000000
--- a/src/coreclr/vm/win32threadpool.h
+++ /dev/null
@@ -1,1030 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-
-/*++
-
-Module Name:
-
- Win32ThreadPool.h
-
-Abstract:
-
- This module is the header file for thread pools using Win32 APIs.
-
-Revision History:
-
-
---*/
-
-#ifndef _WIN32THREADPOOL_H
-#define _WIN32THREADPOOL_H
-
-#include "delegateinfo.h"
-#include "util.hpp"
-#include "nativeoverlapped.h"
-#include "hillclimbing.h"
-
-#define MAX_WAITHANDLES 64
-
-#define MAX_CACHED_EVENTS 40 // upper limit on number of wait events cached
-
-#define WAIT_REGISTERED 0x01
-#define WAIT_ACTIVE 0x02
-#define WAIT_DELETE 0x04
-
-#define WAIT_SINGLE_EXECUTION 0x00000001
-#define WAIT_FREE_CONTEXT 0x00000002
-#define WAIT_INTERNAL_COMPLETION 0x00000004
-
-#define QUEUE_ONLY 0x00000000 // do not attempt to call on the thread
-#define CALL_OR_QUEUE 0x00000001 // call on the same thread if not too busy, else queue
-
-const int MaxLimitThreadsPerCPU=250; // upper limit on number of cp threads per CPU
-const int MaxFreeCPThreadsPerCPU=2; // upper limit on number of free cp threads per CPU
-
-const int CpuUtilizationHigh=95; // remove threads when above this
-const int CpuUtilizationLow =80; // inject more threads if below this
-
-#ifndef TARGET_UNIX
-extern HANDLE (WINAPI *g_pufnCreateIoCompletionPort)(HANDLE FileHandle,
- HANDLE ExistingCompletionPort,
- ULONG_PTR CompletionKey,
- DWORD NumberOfConcurrentThreads);
-
-extern int (WINAPI *g_pufnNtQueryInformationThread) (HANDLE ThreadHandle,
- THREADINFOCLASS ThreadInformationClass,
- PVOID ThreadInformation,
- ULONG ThreadInformationLength,
- PULONG ReturnLength);
-
-extern int (WINAPI * g_pufnNtQuerySystemInformation) (SYSTEM_INFORMATION_CLASS SystemInformationClass,
- PVOID SystemInformation,
- ULONG SystemInformationLength,
- PULONG ReturnLength OPTIONAL);
-#endif // !TARGET_UNIX
-
-#define FILETIME_TO_INT64(t) (*(__int64*)&(t))
-#define MILLI_TO_100NANO(x) ((x) * 10000) // convert from milliseond to 100 nanosecond unit
-
-/**
- * This type is supposed to be private to ThreadpoolMgr.
- * It's at global scope because Strike needs to be able to access its
- * definition.
- */
-struct WorkRequest {
- WorkRequest* next;
- LPTHREAD_START_ROUTINE Function;
- PVOID Context;
-
-};
-
-typedef struct _IOCompletionContext
-{
- DWORD ErrorCode;
- DWORD numBytesTransferred;
- LPOVERLAPPED lpOverlapped;
- size_t key;
-} IOCompletionContext, *PIOCompletionContext;
-
-typedef DPTR(WorkRequest) PTR_WorkRequest;
-class ThreadpoolMgr
-{
- friend class ClrDataAccess;
- friend struct DelegateInfo;
- friend class ThreadPoolNative;
- friend class UnManagedPerAppDomainTPCount;
- friend class ManagedPerAppDomainTPCount;
- friend class PerAppDomainTPCountList;
- friend class HillClimbing;
- friend struct _DacGlobals;
-
-public:
- struct ThreadCounter
- {
- static const int MaxPossibleCount = 0x7fff;
-
- // padding to ensure we get our own cache line
- BYTE padding1[MAX_CACHE_LINE_SIZE];
-
- union Counts
- {
- struct
- {
- //
- // Note: these are signed rather than unsigned to allow us to detect under/overflow.
- //
- int MaxWorking : 16; //Determined by HillClimbing; adjusted elsewhere for timeouts, etc.
- int NumActive : 16; //Active means working or waiting on WorkerSemaphore. These are "warm/hot" threads.
- int NumWorking : 16; //Trying to get work from various queues. Not waiting on either semaphore.
- int NumRetired : 16; //Not trying to get work; waiting on RetiredWorkerSemaphore. These are "cold" threads.
-
- // Note: the only reason we need "retired" threads at all is that it allows some threads to eventually time out
- // even if other threads are getting work. If we ever make WorkerSemaphore a true LIFO semaphore, we will no longer
- // need the concept of "retirement" - instead, the very "coldest" threads will naturally be the first to time out.
- };
-
- LONGLONG AsLongLong;
-
- bool operator==(Counts other) {LIMITED_METHOD_CONTRACT; return AsLongLong == other.AsLongLong;}
- } counts;
-
- // padding to ensure we get our own cache line
- BYTE padding2[MAX_CACHE_LINE_SIZE];
-
- Counts GetCleanCounts()
- {
- LIMITED_METHOD_CONTRACT;
-#ifdef HOST_64BIT
- // VolatileLoad x64 bit read is atomic
- return DangerousGetDirtyCounts();
-#else // !HOST_64BIT
- // VolatileLoad may result in torn read
- Counts result;
-#ifndef DACCESS_COMPILE
- result.AsLongLong = InterlockedCompareExchange64(&counts.AsLongLong, 0, 0);
- ValidateCounts(result);
-#else
- result.AsLongLong = 0; //prevents prefast warning for DAC builds
-#endif
- return result;
-#endif // !HOST_64BIT
- }
-
- //
- // This does a non-atomic read of the counts. The returned value is suitable only
- // for use inside of a read-compare-exchange loop, where the compare-exhcange must succeed
- // before any action is taken. Use GetCleanWorkerCounts for other needs, but keep in mind
- // it's much slower.
- //
- Counts DangerousGetDirtyCounts()
- {
- LIMITED_METHOD_CONTRACT;
- Counts result;
-#ifndef DACCESS_COMPILE
- result.AsLongLong = VolatileLoad(&counts.AsLongLong);
-#else
- result.AsLongLong = 0; //prevents prefast warning for DAC builds
-#endif
- return result;
- }
-
-
- Counts CompareExchangeCounts(Counts newCounts, Counts oldCounts)
- {
- LIMITED_METHOD_CONTRACT;
- Counts result;
-#ifndef DACCESS_COMPILE
- result.AsLongLong = InterlockedCompareExchange64(&counts.AsLongLong, newCounts.AsLongLong, oldCounts.AsLongLong);
- if (result == oldCounts)
- {
- // can only do validation on success; if we failed, it may have been due to a previous
- // dirty read, which may contain invalid values.
- ValidateCounts(result);
- ValidateCounts(newCounts);
- }
-#else
- result.AsLongLong = 0; //prevents prefast warning for DAC builds
-#endif
- return result;
- }
-
- private:
- static void ValidateCounts(Counts counts)
- {
- LIMITED_METHOD_CONTRACT;
- _ASSERTE(counts.MaxWorking > 0);
- _ASSERTE(counts.NumActive >= 0);
- _ASSERTE(counts.NumWorking >= 0);
- _ASSERTE(counts.NumRetired >= 0);
- _ASSERTE(counts.NumWorking <= counts.NumActive);
- }
- };
-
-public:
-
- static void ReportThreadStatus(bool isWorking);
-
- // enumeration of different kinds of memory blocks that are recycled
- enum MemType
- {
- MEMTYPE_AsyncCallback = 0,
- MEMTYPE_DelegateInfo = 1,
- MEMTYPE_WorkRequest = 2,
- MEMTYPE_COUNT = 3,
- };
-
-#ifndef DACCESS_COMPILE
- static void StaticInitialize()
- {
- WRAPPER_NO_CONTRACT;
-
- s_usePortableThreadPool = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_ThreadPool_UsePortableThreadPool) != 0;
-#ifdef TARGET_WINDOWS
- s_usePortableThreadPoolForIO =
- s_usePortableThreadPool &&
- CLRConfig::GetConfigValue(CLRConfig::INTERNAL_ThreadPool_UsePortableThreadPoolForIO) != 0;
-#else // !TARGET_WINDOWS
- s_usePortableThreadPoolForIO = s_usePortableThreadPool;
-#endif // TARGET_WINDOWS
- }
-#endif // !DACCESS_COMPILE
-
- static bool UsePortableThreadPool()
- {
- LIMITED_METHOD_CONTRACT;
- return s_usePortableThreadPool;
- }
-
- static bool UsePortableThreadPoolForIO()
- {
- LIMITED_METHOD_CONTRACT;
- return s_usePortableThreadPoolForIO;
- }
-
- static BOOL Initialize();
-
- static BOOL SetMaxThreadsHelper(DWORD MaxWorkerThreads,
- DWORD MaxIOCompletionThreads);
-
- static bool CanSetMinIOCompletionThreads(DWORD ioCompletionThreads);
- static bool CanSetMaxIOCompletionThreads(DWORD ioCompletionThreads);
-
- static BOOL SetMaxThreads(DWORD MaxWorkerThreads,
- DWORD MaxIOCompletionThreads);
-
- static BOOL GetMaxThreads(DWORD* MaxWorkerThreads,
- DWORD* MaxIOCompletionThreads);
-
- static BOOL SetMinThreads(DWORD MinWorkerThreads,
- DWORD MinIOCompletionThreads);
-
- static BOOL GetMinThreads(DWORD* MinWorkerThreads,
- DWORD* MinIOCompletionThreads);
-
- static BOOL GetAvailableThreads(DWORD* AvailableWorkerThreads,
- DWORD* AvailableIOCompletionThreads);
-
- static INT32 GetThreadCount();
-
- static BOOL QueueUserWorkItem(LPTHREAD_START_ROUTINE Function,
- PVOID Context,
- ULONG Flags,
- BOOL UnmanagedTPRequest=TRUE);
-
- static BOOL PostQueuedCompletionStatus(LPOVERLAPPED lpOverlapped,
- LPOVERLAPPED_COMPLETION_ROUTINE Function);
-
- inline static BOOL IsCompletionPortInitialized()
- {
- LIMITED_METHOD_CONTRACT;
- return GlobalCompletionPort != NULL;
- }
-
- static BOOL RegisterWaitForSingleObject(PHANDLE phNewWaitObject,
- HANDLE hWaitObject,
- WAITORTIMERCALLBACK Callback,
- PVOID Context,
- ULONG timeout,
- DWORD dwFlag);
-
- static BOOL UnregisterWaitEx(HANDLE hWaitObject,HANDLE CompletionEvent);
- static void WaitHandleCleanup(HANDLE hWaitObject);
-
- static BOOL WINAPI BindIoCompletionCallback(HANDLE FileHandle,
- LPOVERLAPPED_COMPLETION_ROUTINE Function,
- ULONG Flags,
- DWORD& errorCode);
-
- static void WINAPI WaitIOCompletionCallback(DWORD dwErrorCode,
- DWORD numBytesTransferred,
- LPOVERLAPPED lpOverlapped);
-
- static BOOL SetAppDomainRequestsActive(BOOL UnmanagedTP = FALSE);
- static void ClearAppDomainRequestsActive(BOOL UnmanagedTP = FALSE, LONG index = -1);
-
- static inline void UpdateLastDequeueTime()
- {
- LIMITED_METHOD_CONTRACT;
- VolatileStore(&LastDequeueTime, (unsigned int)GetTickCount());
- }
-
- static void RecycleMemory(LPVOID mem, enum MemType memType);
-
-#ifndef TARGET_UNIX
- static LPOVERLAPPED CompletionPortDispatchWorkWithinAppDomain(Thread* pThread, DWORD* pErrorCode, DWORD* pNumBytes, size_t* pKey);
- static void StoreOverlappedInfoInThread(Thread* pThread, DWORD dwErrorCode, DWORD dwNumBytes, size_t key, LPOVERLAPPED lpOverlapped);
-#endif // !TARGET_UNIX
-
- // Enable filtering of correlation ETW events for cases handled at a higher abstraction level
-
-#ifndef DACCESS_COMPILE
- static FORCEINLINE BOOL AreEtwIOQueueEventsSpeciallyHandled(LPOVERLAPPED_COMPLETION_ROUTINE Function)
- {
- // We handle registered waits at a higher abstraction level
- return Function == ThreadpoolMgr::WaitIOCompletionCallback;
- }
-#endif
-
-private:
-
-#ifndef DACCESS_COMPILE
-
- inline static void FreeWorkRequest(WorkRequest* workRequest)
- {
- RecycleMemory( workRequest, MEMTYPE_WorkRequest ); //delete workRequest;
- }
-
- inline static WorkRequest* MakeWorkRequest(LPTHREAD_START_ROUTINE function, PVOID context)
- {
- CONTRACTL
- {
- THROWS;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END;;
-
- WorkRequest* wr = (WorkRequest*) GetRecycledMemory(MEMTYPE_WorkRequest);
- _ASSERTE(wr);
- if (NULL == wr)
- return NULL;
- wr->Function = function;
- wr->Context = context;
- wr->next = NULL;
- return wr;
- }
-
-#endif // #ifndef DACCESS_COMPILE
-
- typedef struct {
- DWORD numBytes;
- ULONG_PTR *key;
- LPOVERLAPPED pOverlapped;
- DWORD errorCode;
- } QueuedStatus;
-
- typedef DPTR(struct _LIST_ENTRY) PTR_LIST_ENTRY;
- typedef struct _LIST_ENTRY {
- struct _LIST_ENTRY *Flink;
- struct _LIST_ENTRY *Blink;
- } LIST_ENTRY, *PLIST_ENTRY;
-
- struct WaitInfo;
-
- typedef struct {
- HANDLE threadHandle;
- DWORD threadId;
- CLREvent startEvent;
- LONG NumWaitHandles; // number of wait objects registered to the thread <=64
- LONG NumActiveWaits; // number of objects, thread is actually waiting on (this may be less than
- // NumWaitHandles since the thread may not have activated some waits
- HANDLE waitHandle[MAX_WAITHANDLES]; // array of wait handles (copied from waitInfo since
- // we need them to be contiguous)
- LIST_ENTRY waitPointer[MAX_WAITHANDLES]; // array of doubly linked list of corresponding waitinfo
- } ThreadCB;
-
-
- typedef struct {
- ULONG startTime; // time at which wait was started
- // endTime = startTime+timeout
- ULONG remainingTime; // endTime - currentTime
- } WaitTimerInfo;
-
- struct WaitInfo {
- LIST_ENTRY link; // Win9x does not allow duplicate waithandles, so we need to
- // group all waits on a single waithandle using this linked list
- HANDLE waitHandle;
- WAITORTIMERCALLBACK Callback;
- PVOID Context;
- ULONG timeout;
- WaitTimerInfo timer;
- DWORD flag;
- DWORD state;
- ThreadCB* threadCB;
- LONG refCount; // when this reaches 0, the waitInfo can be safely deleted
- CLREvent PartialCompletionEvent; // used to synchronize deactivation of a wait
- CLREvent InternalCompletionEvent; // only one of InternalCompletion or ExternalCompletion is used
- // but I cant make a union since CLREvent has a non-default constructor
- HANDLE ExternalCompletionEvent; // they are signalled when all callbacks have completed (refCount=0)
- OBJECTHANDLE ExternalEventSafeHandle;
-
- } ;
-
- // structure used to maintain global information about wait threads. Protected by WaitThreadsCriticalSection
- typedef struct WaitThreadTag {
- LIST_ENTRY link;
- ThreadCB* threadCB;
- } WaitThreadInfo;
-
-
- struct AsyncCallback{
- WaitInfo* wait;
- BOOL waitTimedOut;
- } ;
-
-#ifndef DACCESS_COMPILE
-
- static VOID
- AcquireAsyncCallback(AsyncCallback *pAsyncCB)
- {
- LIMITED_METHOD_CONTRACT;
- }
-
- static VOID
- ReleaseAsyncCallback(AsyncCallback *pAsyncCB)
- {
- CONTRACTL
- {
- THROWS;
- GC_TRIGGERS;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- WaitInfo *waitInfo = pAsyncCB->wait;
- ThreadpoolMgr::RecycleMemory((LPVOID*)pAsyncCB, ThreadpoolMgr::MEMTYPE_AsyncCallback);
-
- // if this was a single execution, we now need to stop rooting registeredWaitHandle
- // in a GC handle. This will cause the finalizer to pick it up and call the cleanup
- // routine.
- if ( (waitInfo->flag & WAIT_SINGLE_EXECUTION) && (waitInfo->flag & WAIT_FREE_CONTEXT))
- {
-
- DelegateInfo* pDelegate = (DelegateInfo*) waitInfo->Context;
-
- _ASSERTE(pDelegate->m_registeredWaitHandle);
-
- {
- GCX_COOP();
- StoreObjectInHandle(pDelegate->m_registeredWaitHandle, NULL);
- }
- }
-
- if (InterlockedDecrement(&waitInfo->refCount) == 0)
- ThreadpoolMgr::DeleteWait(waitInfo);
-
- }
-
- typedef Holder AsyncCallbackHolder;
- inline static AsyncCallback* MakeAsyncCallback()
- {
- WRAPPER_NO_CONTRACT;
- return (AsyncCallback*) GetRecycledMemory(MEMTYPE_AsyncCallback);
- }
-
- static VOID ReleaseInfo(OBJECTHANDLE& hndSafeHandle,
- HANDLE hndNativeHandle)
- {
- CONTRACTL
- {
- NOTHROW;
- MODE_ANY;
- GC_TRIGGERS;
- }
- CONTRACTL_END
-
-// Use of EX_TRY, GCPROTECT etc in the same function is causing prefast to complain about local variables with
-// same name masking each other (#246). The error could not be suppressed with "#pragma PREFAST_SUPPRESS"
-#ifndef _PREFAST_
-
- if (hndSafeHandle != NULL)
- {
-
- SAFEHANDLEREF refSH = NULL;
-
- GCX_COOP();
- GCPROTECT_BEGIN(refSH);
-
- {
- EX_TRY
- {
- // Read the GC handle
- refSH = (SAFEHANDLEREF) ObjectToOBJECTREF(ObjectFromHandle(hndSafeHandle));
-
- // Destroy the GC handle
- DestroyHandle(hndSafeHandle);
-
- if (refSH != NULL)
- {
- SafeHandleHolder h(&refSH);
-
- HANDLE hEvent = refSH->GetHandle();
- if (hEvent != INVALID_HANDLE_VALUE)
- {
- SetEvent(hEvent);
- }
- }
- }
- EX_CATCH
- {
- }
- EX_END_CATCH(SwallowAllExceptions);
- }
-
- GCPROTECT_END();
-
- hndSafeHandle = NULL;
- }
-#endif
- }
-
-#endif // #ifndef DACCESS_COMPILE
-
- typedef struct {
- LIST_ENTRY link;
- HANDLE Handle;
- } WaitEvent ;
-
- static VOID AcquireWaitInfo(WaitInfo *pInfo)
- {
- }
- static VOID ReleaseWaitInfo(WaitInfo *pInfo)
- {
- WRAPPER_NO_CONTRACT;
-#ifndef DACCESS_COMPILE
- ReleaseInfo(pInfo->ExternalEventSafeHandle,
- pInfo->ExternalCompletionEvent);
-#endif
- }
-
- typedef Holder WaitInfoHolder;
-
- // Definitions and data structures to support recycling of high-frequency
- // memory blocks. We use a spin-lock to access the list
-
- class RecycledListInfo
- {
- static const unsigned int MaxCachedEntries = 40;
-
- struct Entry
- {
- Entry* next;
- };
-
- Volatile lock; // this is the spin lock
- DWORD count; // count of number of elements in the list
- Entry* root; // ptr to first element of recycled list
-#ifndef HOST_64BIT
- DWORD filler; // Pad the structure to a multiple of the 16.
-#endif
-
- //--//
-
-public:
- RecycledListInfo()
- {
- LIMITED_METHOD_CONTRACT;
-
- lock = 0;
- root = NULL;
- count = 0;
- }
-
- FORCEINLINE bool CanInsert()
- {
- LIMITED_METHOD_CONTRACT;
-
- return count < MaxCachedEntries;
- }
-
- FORCEINLINE LPVOID Remove()
- {
- LIMITED_METHOD_CONTRACT;
-
- if(root == NULL) return NULL; // No need for acquiring the lock, there's nothing to remove.
-
- AcquireLock();
-
- Entry* ret = (Entry*)root;
-
- if(ret)
- {
- root = ret->next;
- count -= 1;
- }
-
- ReleaseLock();
-
- return ret;
- }
-
- FORCEINLINE void Insert( LPVOID mem )
- {
- LIMITED_METHOD_CONTRACT;
-
- AcquireLock();
-
- Entry* entry = (Entry*)mem;
-
- entry->next = root;
-
- root = entry;
- count += 1;
-
- ReleaseLock();
- }
-
- private:
- FORCEINLINE void AcquireLock()
- {
- LIMITED_METHOD_CONTRACT;
-
- unsigned int rounds = 0;
-
- DWORD dwSwitchCount = 0;
-
- while(lock != 0 || InterlockedExchange( &lock, 1 ) != 0)
- {
- YieldProcessorNormalized(); // indicate to the processor that we are spinning
-
- rounds++;
-
- if((rounds % 32) == 0)
- {
- __SwitchToThread( 0, ++dwSwitchCount );
- }
- }
- }
-
- FORCEINLINE void ReleaseLock()
- {
- LIMITED_METHOD_CONTRACT;
-
- lock = 0;
- }
- };
-
- //
- // It's critical that we ensure these pointers are allocated by the linker away from
- // variables that are modified a lot at runtime.
- //
- // The use of the CacheGuard is a temporary solution,
- // the thread pool has to be refactor away from static variable and
- // toward a single global structure, where we can control the locality of variables.
- //
- class RecycledListsWrapper
- {
- DWORD CacheGuardPre[MAX_CACHE_LINE_SIZE/sizeof(DWORD)];
-
- RecycledListInfo (*pRecycledListPerProcessor)[MEMTYPE_COUNT]; // RecycledListInfo [numProc][MEMTYPE_COUNT]
-
- DWORD CacheGuardPost[MAX_CACHE_LINE_SIZE/sizeof(DWORD)];
-
- public:
- void Initialize( unsigned int numProcs );
-
- FORCEINLINE bool IsInitialized()
- {
- LIMITED_METHOD_CONTRACT;
-
- return pRecycledListPerProcessor != NULL;
- }
-
-#ifndef DACCESS_COMPILE
- FORCEINLINE RecycledListInfo& GetRecycleMemoryInfo( enum MemType memType )
- {
- LIMITED_METHOD_CONTRACT;
-
- DWORD processorNumber = 0;
-
-#ifndef TARGET_UNIX
- if (CPUGroupInfo::CanEnableThreadUseAllCpuGroups())
- processorNumber = CPUGroupInfo::CalculateCurrentProcessorNumber();
- else
- // Turns out GetCurrentProcessorNumber can return a value greater than the number of processors reported by
- // GetSystemInfo, if we're running in WOW64 on a machine with >32 processors.
- processorNumber = GetCurrentProcessorNumber()%NumberOfProcessors;
-#else // !TARGET_UNIX
- if (PAL_HasGetCurrentProcessorNumber())
- {
- // On linux, GetCurrentProcessorNumber which uses sched_getcpu() can return a value greater than the number
- // of processors reported by sysconf(_SC_NPROCESSORS_ONLN) when using OpenVZ kernel.
- processorNumber = GetCurrentProcessorNumber()%NumberOfProcessors;
- }
-#endif // !TARGET_UNIX
- return pRecycledListPerProcessor[processorNumber][memType];
- }
-#endif // DACCESS_COMPILE
- };
-
-#define GATE_THREAD_STATUS_NOT_RUNNING 0 // There is no gate thread
-#define GATE_THREAD_STATUS_REQUESTED 1 // There is a gate thread, and someone has asked it to stick around recently
-#define GATE_THREAD_STATUS_WAITING_FOR_REQUEST 2 // There is a gate thread, but nobody has asked it to stay. It may die soon
-
- // Private methods
-
- static Thread* CreateUnimpersonatedThread(LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpArgs, BOOL *pIsCLRThread);
-
- static BOOL CreateWorkerThread();
-
- static void EnqueueWorkRequest(WorkRequest* wr);
-
- static WorkRequest* DequeueWorkRequest();
-
- static void ExecuteWorkRequest(bool* foundWork, bool* wasNotRecalled);
-
-#ifndef DACCESS_COMPILE
-
- inline static void AppendWorkRequest(WorkRequest* entry)
- {
- CONTRACTL
- {
- NOTHROW;
- MODE_ANY;
- GC_NOTRIGGER;
- }
- CONTRACTL_END;
-
- _ASSERTE(!UsePortableThreadPool());
-
- if (WorkRequestTail)
- {
- _ASSERTE(WorkRequestHead != NULL);
- WorkRequestTail->next = entry;
- }
- else
- {
- _ASSERTE(WorkRequestHead == NULL);
- WorkRequestHead = entry;
- }
-
- WorkRequestTail = entry;
- _ASSERTE(WorkRequestTail->next == NULL);
- }
-
- inline static WorkRequest* RemoveWorkRequest()
- {
- CONTRACTL
- {
- NOTHROW;
- MODE_ANY;
- GC_NOTRIGGER;
- }
- CONTRACTL_END;
-
- _ASSERTE(!UsePortableThreadPool());
-
- WorkRequest* entry = NULL;
- if (WorkRequestHead)
- {
- entry = WorkRequestHead;
- WorkRequestHead = entry->next;
- if (WorkRequestHead == NULL)
- WorkRequestTail = NULL;
- }
- return entry;
- }
-
-public:
- static void EnsureInitialized();
-private:
- static void EnsureInitializedSlow();
-
-public:
- static void InitPlatformVariables();
-
- inline static BOOL IsInitialized()
- {
- LIMITED_METHOD_CONTRACT;
- return Initialization == -1;
- }
-
- static void MaybeAddWorkingWorker();
-
- static void NotifyWorkItemCompleted()
- {
- WRAPPER_NO_CONTRACT;
- _ASSERTE(!UsePortableThreadPool());
-
- Thread::IncrementWorkerThreadPoolCompletionCount(GetThreadNULLOk());
- UpdateLastDequeueTime();
- }
-
- static bool ShouldAdjustMaxWorkersActive()
- {
- WRAPPER_NO_CONTRACT;
- _ASSERTE(!UsePortableThreadPool());
-
- DWORD priorTime = PriorCompletedWorkRequestsTime;
- MemoryBarrier(); // read fresh value for NextCompletedWorkRequestsTime below
- DWORD requiredInterval = NextCompletedWorkRequestsTime - priorTime;
- DWORD elapsedInterval = GetTickCount() - priorTime;
- if (elapsedInterval >= requiredInterval)
- {
- ThreadCounter::Counts counts = WorkerCounter.GetCleanCounts();
- if (counts.NumActive <= counts.MaxWorking)
- return !IsHillClimbingDisabled;
- }
-
- return false;
- }
-
- static void AdjustMaxWorkersActive();
- static bool ShouldWorkerKeepRunning();
-
- static DWORD SafeWait(CLREvent * ev, DWORD sleepTime, BOOL alertable);
-
- static DWORD WINAPI WorkerThreadStart(LPVOID lpArgs);
-
- static BOOL AddWaitRequest(HANDLE waitHandle, WaitInfo* waitInfo);
-
-
- static ThreadCB* FindWaitThread(); // returns a wait thread that can accommodate another wait request
-
- static BOOL CreateWaitThread();
-
- static void WINAPI InsertNewWaitForSelf(WaitInfo* pArg);
-
- static int FindWaitIndex(const ThreadCB* threadCB, const HANDLE waitHandle);
-
- static DWORD MinimumRemainingWait(LIST_ENTRY* waitInfo, unsigned int numWaits);
-
- static void ProcessWaitCompletion( WaitInfo* waitInfo,
- unsigned index, // array index
- BOOL waitTimedOut);
-
- static DWORD WINAPI WaitThreadStart(LPVOID lpArgs);
-
- static DWORD WINAPI AsyncCallbackCompletion(PVOID pArgs);
-
- static void DeactivateWait(WaitInfo* waitInfo);
- static void DeactivateNthWait(WaitInfo* waitInfo, DWORD index);
-
- static void DeleteWait(WaitInfo* waitInfo);
-
-
- inline static void ShiftWaitArray( ThreadCB* threadCB,
- ULONG SrcIndex,
- ULONG DestIndex,
- ULONG count)
- {
- LIMITED_METHOD_CONTRACT;
- memmove(&threadCB->waitHandle[DestIndex],
- &threadCB->waitHandle[SrcIndex],
- count * sizeof(HANDLE));
- memmove(&threadCB->waitPointer[DestIndex],
- &threadCB->waitPointer[SrcIndex],
- count * sizeof(LIST_ENTRY));
- }
-
- static void WINAPI DeregisterWait(WaitInfo* pArgs);
-
-#ifndef TARGET_UNIX
- // holds the aggregate of system cpu usage of all processors
- typedef struct _PROCESS_CPU_INFORMATION
- {
- LARGE_INTEGER idleTime;
- LARGE_INTEGER kernelTime;
- LARGE_INTEGER userTime;
- DWORD_PTR affinityMask;
- int numberOfProcessors;
- SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION* usageBuffer;
- int usageBufferSize;
- } PROCESS_CPU_INFORMATION;
-
- static int GetCPUBusyTime_NT(PROCESS_CPU_INFORMATION* pOldInfo);
- static BOOL CreateCompletionPortThread(LPVOID lpArgs);
- static DWORD WINAPI CompletionPortThreadStart(LPVOID lpArgs);
-public:
- inline static bool HaveNativeWork()
- {
- LIMITED_METHOD_CONTRACT;
- return WorkRequestHead != NULL;
- }
-
- static void GrowCompletionPortThreadpoolIfNeeded();
- static BOOL ShouldGrowCompletionPortThreadpool(ThreadCounter::Counts counts);
-#else
- static int GetCPUBusyTime_NT(PAL_IOCP_CPU_INFORMATION* pOldInfo);
-
-#endif // !TARGET_UNIX
-
- static void PerformGateActivities(int cpuUtilization);
- static bool NeedGateThreadForIOCompletions();
-
-private:
- static BOOL IsIoPending();
-
- static BOOL CreateGateThread();
- static void EnsureGateThreadRunning();
- static bool ShouldGateThreadKeepRunning();
- static DWORD WINAPI GateThreadStart(LPVOID lpArgs);
- static BOOL SufficientDelaySinceLastSample(unsigned int LastThreadCreationTime,
- unsigned NumThreads, // total number of threads of that type (worker or CP)
- double throttleRate=0.0 // the delay is increased by this percentage for each extra thread
- );
- static BOOL SufficientDelaySinceLastDequeue();
-
- static LPVOID GetRecycledMemory(enum MemType memType);
-
- inline static DWORD QueueDeregisterWait(HANDLE waitThread, WaitInfo* waitInfo)
- {
- CONTRACTL
- {
- NOTHROW;
- MODE_ANY;
- GC_NOTRIGGER;
- }
- CONTRACTL_END;
-
- _ASSERTE(!UsePortableThreadPool());
-
- DWORD result = QueueUserAPC(reinterpret_cast(DeregisterWait), waitThread, reinterpret_cast(waitInfo));
- SetWaitThreadAPCPending();
- return result;
- }
-
-
- inline static void SetWaitThreadAPCPending() {IsApcPendingOnWaitThread = TRUE;}
- inline static void ResetWaitThreadAPCPending() {IsApcPendingOnWaitThread = FALSE;}
- inline static BOOL IsWaitThreadAPCPending() {return IsApcPendingOnWaitThread;}
-
-#endif // #ifndef DACCESS_COMPILE
- // Private variables
-
- static Volatile Initialization; // indicator of whether the threadpool is initialized.
-
- static bool s_usePortableThreadPool;
- static bool s_usePortableThreadPoolForIO;
-
- SVAL_DECL(LONG,MinLimitTotalWorkerThreads); // same as MinLimitTotalCPThreads
- SVAL_DECL(LONG,MaxLimitTotalWorkerThreads); // same as MaxLimitTotalCPThreads
-
- DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) static unsigned int LastDequeueTime; // used to determine if work items are getting thread starved
-
- static HillClimbing HillClimbingInstance;
-
- DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) static LONG PriorCompletedWorkRequests;
- static DWORD PriorCompletedWorkRequestsTime;
- static DWORD NextCompletedWorkRequestsTime;
-
- static LARGE_INTEGER CurrentSampleStartTime;
-
- static unsigned int WorkerThreadSpinLimit;
- static bool IsHillClimbingDisabled;
- static int ThreadAdjustmentInterval;
-
- SPTR_DECL(WorkRequest,WorkRequestHead); // Head of work request queue
- SPTR_DECL(WorkRequest,WorkRequestTail); // Head of work request queue
-
- static unsigned int LastCPThreadCreation; // last time a completion port thread was created
- static unsigned int NumberOfProcessors; // = NumberOfWorkerThreads - no. of blocked threads
-
- static BOOL IsApcPendingOnWaitThread; // Indicates if an APC is pending on the wait thread
-
- // This needs to be non-hosted, because worker threads can run prior to EE startup.
- static DangerousNonHostedSpinLock ThreadAdjustmentLock;
-
-public:
- static CrstStatic WorkerCriticalSection;
-
-private:
- static const DWORD WorkerTimeout = 20 * 1000;
-
- DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) SVAL_DECL(ThreadCounter,WorkerCounter);
-
- //
- // WorkerSemaphore is an UnfairSemaphore because:
- // 1) Threads enter and exit this semaphore very frequently, and thus benefit greatly from the spinning done by UnfairSemaphore
- // 2) There is no functional reason why any particular thread should be preferred when waking workers. This only impacts performance,
- // and un-fairness helps performance in this case.
- //
- static CLRLifoSemaphore* WorkerSemaphore;
-
- //
- // RetiredWorkerSemaphore is a regular CLRSemaphore, not an UnfairSemaphore, because if a thread waits on this semaphore is it almost certainly
- // NOT going to be released soon, so the spinning done in UnfairSemaphore only burns valuable CPU time. However, if UnfairSemaphore is ever
- // implemented in terms of a Win32 IO Completion Port, we should reconsider this. The IOCP's LIFO unblocking behavior could help keep working set
- // down, by constantly re-using the same small set of retired workers rather than round-robining between all of them as CLRSemaphore will do.
- // If we go that route, we should add a "no-spin" option to UnfairSemaphore.Wait to avoid wasting CPU.
- //
- static CLRLifoSemaphore* RetiredWorkerSemaphore;
-
- static CLREvent * RetiredCPWakeupEvent;
-
- static CrstStatic WaitThreadsCriticalSection;
- static LIST_ENTRY WaitThreadsHead; // queue of wait threads, each thread can handle upto 64 waits
-
- static BOOL InitCompletionPortThreadpool; // flag indicating whether completion port threadpool has been initialized
- static HANDLE GlobalCompletionPort; // used for binding io completions on file handles
-
-public:
- SVAL_DECL(ThreadCounter,CPThreadCounter);
-
-private:
- SVAL_DECL(LONG,MaxLimitTotalCPThreads); // = MaxLimitCPThreadsPerCPU * number of CPUS
- SVAL_DECL(LONG,MinLimitTotalCPThreads);
- SVAL_DECL(LONG,MaxFreeCPThreads); // = MaxFreeCPThreadsPerCPU * Number of CPUS
-
- DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) static LONG GateThreadStatus; // See GateThreadStatus enumeration
-
- SVAL_DECL(LONG,cpuUtilization);
-
- DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) static RecycledListsWrapper RecycledLists;
-};
-
-
-
-
-#endif // _WIN32THREADPOOL_H
diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.GateThread.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.GateThread.cs
index 26512a4e81c636..4564af6f9ca89d 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.GateThread.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.GateThread.cs
@@ -105,8 +105,6 @@ private static void GateThreadStart()
int cpuUtilization = (int)cpuUtilizationReader.CurrentUtilization;
threadPoolInstance._cpuUtilization = cpuUtilization;
- bool needGateThreadForRuntime = ThreadPool.PerformRuntimeSpecificGateActivities(cpuUtilization);
-
if (!disableStarvationDetection &&
threadPoolInstance._pendingBlockingAdjustment == PendingBlockingAdjustment.None &&
threadPoolInstance._separated.numRequestedWorkers > 0 &&
@@ -164,8 +162,7 @@ private static void GateThreadStart()
}
}
- if (!needGateThreadForRuntime &&
- threadPoolInstance._separated.numRequestedWorkers <= 0 &&
+ if (threadPoolInstance._separated.numRequestedWorkers <= 0 &&
threadPoolInstance._pendingBlockingAdjustment == PendingBlockingAdjustment.None &&
Interlocked.Decrement(ref threadPoolInstance._separated.gateThreadRunningState) <= GetRunningStateForNumRuns(0))
{
@@ -334,7 +331,5 @@ public bool HasBlockingAdjustmentDelayElapsed(int currentTimeMs, bool wasSignale
}
}
}
-
- internal static void EnsureGateThreadRunning() => GateThread.EnsureRunning(ThreadPoolInstance);
}
}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.cs
index 57f25de034c249..7adfe5a0b9ca42 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.cs
@@ -29,6 +29,12 @@ internal sealed partial class PortableThreadPool
private const int CpuUtilizationHigh = 95;
private const int CpuUtilizationLow = 80;
+#if CORECLR
+#pragma warning disable CA1823
+ private static readonly bool s_initialized = ThreadPool.EnsureConfigInitialized();
+#pragma warning restore CA1823
+#endif
+
private static readonly short ForcedMinWorkerThreads =
AppContextConfigHelper.GetInt16Config("System.Threading.ThreadPool.MinThreads", 0, false);
private static readonly short ForcedMaxWorkerThreads =
@@ -151,9 +157,7 @@ public bool SetMinThreads(int workerThreads, int ioCompletionThreads)
return false;
}
- if (ThreadPool.UsePortableThreadPoolForIO
- ? ioCompletionThreads > _legacy_maxIOCompletionThreads
- : !ThreadPool.CanSetMinIOCompletionThreads(ioCompletionThreads))
+ if (ioCompletionThreads > _legacy_maxIOCompletionThreads)
{
return false;
}
@@ -163,14 +167,7 @@ public bool SetMinThreads(int workerThreads, int ioCompletionThreads)
return false;
}
- if (ThreadPool.UsePortableThreadPoolForIO)
- {
- _legacy_minIOCompletionThreads = (short)Math.Max(1, ioCompletionThreads);
- }
- else
- {
- ThreadPool.SetMinIOCompletionThreads(ioCompletionThreads);
- }
+ _legacy_minIOCompletionThreads = (short)Math.Max(1, ioCompletionThreads);
short newMinThreads = (short)Math.Max(1, workerThreads);
if (newMinThreads == _minThreads)
@@ -243,9 +240,7 @@ public bool SetMaxThreads(int workerThreads, int ioCompletionThreads)
return false;
}
- if (ThreadPool.UsePortableThreadPoolForIO
- ? ioCompletionThreads < _legacy_minIOCompletionThreads
- : !ThreadPool.CanSetMaxIOCompletionThreads(ioCompletionThreads))
+ if (ioCompletionThreads < _legacy_minIOCompletionThreads)
{
return false;
}
@@ -255,14 +250,7 @@ public bool SetMaxThreads(int workerThreads, int ioCompletionThreads)
return false;
}
- if (ThreadPool.UsePortableThreadPoolForIO)
- {
- _legacy_maxIOCompletionThreads = (short)Math.Min(ioCompletionThreads, MaxPossibleThreadCount);
- }
- else
- {
- ThreadPool.SetMaxIOCompletionThreads(ioCompletionThreads);
- }
+ _legacy_maxIOCompletionThreads = (short)Math.Min(ioCompletionThreads, MaxPossibleThreadCount);
short newMaxThreads = (short)Math.Min(workerThreads, MaxPossibleThreadCount);
if (newMaxThreads == _maxThreads)
diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.cs
index 05de84a64cbdb2..e216cbb78d55c2 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.cs
@@ -109,11 +109,7 @@ internal void RestartTimeout()
///
internal PortableThreadPool.WaitThread? WaitThread { get; set; }
-#if CORECLR
- private bool UnregisterPortable(WaitHandle waitObject)
-#else
public bool Unregister(WaitHandle waitObject)
-#endif
{
// The registered wait handle must have been registered by this time, otherwise the instance is not handed out to
// the caller of the public variants of RegisterWaitForSingleObject
diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.Windows.cs
index da8e83c5b009e9..024110ea93b925 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.Windows.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.Windows.cs
@@ -14,8 +14,6 @@ public static partial class ThreadPool
[SupportedOSPlatform("windows")]
public static unsafe bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapped)
{
- Debug.Assert(UsePortableThreadPoolForIO);
-
if (overlapped == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.overlapped);
diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.cs
index c831d39e28466f..39e1d6453263e7 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.cs
@@ -17,8 +17,6 @@ internal sealed partial class CompleteWaitThreadPoolWorkItem : IThreadPoolWorkIt
public static partial class ThreadPool
{
- internal static bool UsePortableThreadPoolForIO => true;
-
// Indicates whether the thread pool should yield the thread from the dispatch loop to the runtime periodically so that
// the runtime may use the thread for processing other work
internal static bool YieldFromDispatchLoop => false;
@@ -75,15 +73,6 @@ public static void GetAvailableThreads(out int workerThreads, out int completion
///
internal static void RequestWorkerThread() => PortableThreadPool.ThreadPoolInstance.RequestWorker();
- ///
- /// Called from the gate thread periodically to perform runtime-specific gate activities
- ///
- /// CPU utilization as a percentage since the last call
- /// True if the runtime still needs to perform gate activities, false otherwise
-#pragma warning disable IDE0060
- internal static bool PerformRuntimeSpecificGateActivities(int cpuUtilization) => false;
-#pragma warning restore IDE0060
-
internal static void NotifyWorkItemProgress() => PortableThreadPool.ThreadPoolInstance.NotifyWorkItemProgress();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Windows.cs
index 097277e894d9db..f80107fcf1a66a 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Windows.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Windows.cs
@@ -17,8 +17,7 @@ private static ThreadPoolBoundHandle BindHandleCore(SafeHandle handle)
try
{
Debug.Assert(OperatingSystem.IsWindows());
- // ThreadPool.BindHandle will always return true, otherwise, it throws. See the underlying FCall
- // implementation in ThreadPoolNative::CorBindIoCompletionCallback to see the implementation.
+ // ThreadPool.BindHandle will always return true, otherwise, it throws.
bool succeeded = ThreadPool.BindHandle(handle);
Debug.Assert(succeeded);
}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs
index 55e6fcae7bf176..21a1e6bf0acb90 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs
@@ -1682,7 +1682,7 @@ public static long PendingWorkItemCount
get
{
ThreadPoolWorkQueue workQueue = s_workQueue;
- return ThreadPoolWorkQueue.LocalCount + workQueue.GlobalCount + PendingUnmanagedWorkItemCount;
+ return ThreadPoolWorkQueue.LocalCount + workQueue.GlobalCount;
}
}
}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Portable.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Portable.cs
index 2816f0ab6a858e..0e7892d36f3d92 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Portable.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Portable.cs
@@ -7,7 +7,7 @@
namespace System.Threading
{
//
- // Unix-specific implementation of Timer
+ // Portable implementation of Timer
//
internal sealed partial class TimerQueue : IThreadPoolWorkItem
{
diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Mono.cs
index c8e04b4dcd9566..29e2eb99d1c6e5 100644
--- a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Mono.cs
+++ b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Mono.cs
@@ -131,7 +131,7 @@ public static unsafe bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapp
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.overlapped);
}
- // OS doesn't signal handle, so do it here (CoreCLR does this assignment in ThreadPoolNative::CorPostQueuedCompletionStatus)
+ // OS doesn't signal handle, so do it here
overlapped->InternalLow = (IntPtr)0;
// Both types of callbacks are executed on the same thread pool
return UnsafeQueueUserWorkItem(NativeOverlappedCallback, (nint)overlapped, preferLocal: false);
diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Mono.cs
index fb700981fb26e3..6a6dd5b82e0060 100644
--- a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Mono.cs
+++ b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Mono.cs
@@ -13,7 +13,5 @@ public static partial class ThreadPool
internal static void ReportThreadStatus(bool isWorking)
{
}
-
- private static long PendingUnmanagedWorkItemCount => 0;
}
}
diff --git a/src/mono/mono/eventpipe/ep-rt-mono.h b/src/mono/mono/eventpipe/ep-rt-mono.h
index dc07922690cce5..348c7cbd3526a2 100644
--- a/src/mono/mono/eventpipe/ep-rt-mono.h
+++ b/src/mono/mono/eventpipe/ep-rt-mono.h
@@ -1002,15 +1002,6 @@ ep_rt_config_value_get_output_streaming (void)
return enable;
}
-static
-inline
-bool
-ep_rt_config_value_get_use_portable_thread_pool (void)
-{
- // Only supports portable thread pool.
- return true;
-}
-
static
inline
uint32_t
diff --git a/src/native/eventpipe/ep-rt.h b/src/native/eventpipe/ep-rt.h
index 96ab9b94663809..086975834d820f 100644
--- a/src/native/eventpipe/ep-rt.h
+++ b/src/native/eventpipe/ep-rt.h
@@ -402,10 +402,6 @@ inline
bool
ep_rt_config_value_get_output_streaming (void);
-static
-bool
-ep_rt_config_value_get_use_portable_thread_pool (void);
-
/*
* EventPipeSampleProfiler.
*/