From 7041397b5ef33a5414d4577633a7dd15d3ebc542 Mon Sep 17 00:00:00 2001 From: Koundinya Veluri Date: Tue, 19 Feb 2019 13:17:59 -0800 Subject: [PATCH 1/7] Implement APIs for some threading metrics (CoreCLR) API review: https://github.com/dotnet/corefx/issues/35500 --- .../shared/System/Threading/ThreadPool.cs | 44 ++++++++++ .../src/System/Threading/Monitor.cs | 9 ++ .../System/Threading/ThreadPool.CoreCLR.cs | 30 +++++++ src/classlibnative/bcltype/objectnative.cpp | 6 ++ src/classlibnative/bcltype/objectnative.h | 1 + src/vm/comthreadpool.cpp | 24 +++++ src/vm/comthreadpool.h | 3 + src/vm/ecalllist.h | 4 + src/vm/syncblk.cpp | 3 + src/vm/syncblk.h | 17 ++++ src/vm/threadpoolrequest.h | 6 ++ src/vm/threads.cpp | 87 ++++++++++++++++--- src/vm/threads.h | 82 +++++++++++++++-- src/vm/win32threadpool.cpp | 23 ++++- src/vm/win32threadpool.h | 4 +- 15 files changed, 322 insertions(+), 21 deletions(-) diff --git a/src/System.Private.CoreLib/shared/System/Threading/ThreadPool.cs b/src/System.Private.CoreLib/shared/System/Threading/ThreadPool.cs index f0f0d9ca8dc4..50db98c077a4 100644 --- a/src/System.Private.CoreLib/shared/System/Threading/ThreadPool.cs +++ b/src/System.Private.CoreLib/shared/System/Threading/ThreadPool.cs @@ -381,6 +381,26 @@ public object TrySteal(ref bool missedSteal) return null; } } + + public int Count + { + get + { + bool lockTaken = false; + try + { + m_foreignLock.Enter(ref lockTaken); + return Math.Max(0, m_tailIndex - m_headIndex); + } + finally + { + if (lockTaken) + { + m_foreignLock.Exit(useMemoryBarrier: false); + } + } + } + } } internal bool loggingEnabled; @@ -512,6 +532,19 @@ public object Dequeue(ThreadPoolWorkQueueThreadLocals tl, ref bool missedSteal) return callback; } + public long Count + { + get + { + long count = workItems.Count; + foreach (WorkStealingQueue workStealingQueue in WorkStealingQueueList.Queues) + { + count += workStealingQueue.Count; + } + return count; + } + } + /// /// Dispatches work items to this thread. /// @@ -1248,5 +1281,16 @@ internal static object[] GetGloballyQueuedWorkItemsForDebugger() => internal static object[] GetLocallyQueuedWorkItemsForDebugger() => ToObjectArray(GetLocallyQueuedWorkItems()); + + /// + /// Gets the number of work items that are currently queued to be processed. + /// + /// + /// For a thread pool implementation that may have different types of work items, the count includes all types that can + /// be tracked, which may only be the user work items including tasks. Some implementations may also include queued + /// timer and wait callbacks in the count. On Windows, the count is unlikely to include the number of pending IO + /// completions, as they get posted directly to an IO completion port. + /// + public static long PendingWorkItemCount => ThreadPoolGlobals.workQueue.Count + PendingUnmanagedWorkItemCount; } } diff --git a/src/System.Private.CoreLib/src/System/Threading/Monitor.cs b/src/System.Private.CoreLib/src/System/Threading/Monitor.cs index 2837d908c6c6..08b0e112a205 100644 --- a/src/System.Private.CoreLib/src/System/Threading/Monitor.cs +++ b/src/System.Private.CoreLib/src/System/Threading/Monitor.cs @@ -234,5 +234,14 @@ public static void PulseAll(object obj) ObjPulseAll(obj); } + + /// + /// Gets the number of times there was contention upon trying to take a 's lock so far. + /// + public static extern long LockContentionCount + { + [MethodImpl(MethodImplOptions.InternalCall)] + get; + } } } diff --git a/src/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs b/src/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs index 02dedbf6e1e9..22be96f41a9c 100644 --- a/src/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs +++ b/src/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs @@ -228,6 +228,36 @@ public static void GetAvailableThreads(out int workerThreads, out int completion GetAvailableThreadsNative(out workerThreads, out completionPortThreads); } + /// + /// Gets the number of thread pool threads that currently exist. + /// + /// + /// For a thread pool implementation that may have different types of threads, the count includes all types. + /// + public static extern int ThreadCount + { + [MethodImpl(MethodImplOptions.InternalCall)] + get; + } + + /// + /// Gets the number of work items that have been processed so far. + /// + /// + /// For a thread pool implementation that may have different types of work items, the count includes all types. + /// + public static extern long CompletedWorkItemCount + { + [MethodImpl(MethodImplOptions.InternalCall)] + get; + } + + private static extern long PendingUnmanagedWorkItemCount + { + [MethodImpl(MethodImplOptions.InternalCall)] + get; + } + private static RegisteredWaitHandle RegisterWaitForSingleObject( // throws RegisterWaitException WaitHandle waitObject, WaitOrTimerCallback callBack, diff --git a/src/classlibnative/bcltype/objectnative.cpp b/src/classlibnative/bcltype/objectnative.cpp index 5304e0110e7d..4af411fc2cad 100644 --- a/src/classlibnative/bcltype/objectnative.cpp +++ b/src/classlibnative/bcltype/objectnative.cpp @@ -355,3 +355,9 @@ FCIMPL1(FC_BOOL_RET, ObjectNative::IsLockHeld, Object* pThisUNSAFE) } FCIMPLEND +FCIMPL0(INT64, ObjectNative::GetMonitorLockContentionCount) +{ + FCALL_CONTRACT; + return (INT64)AwareLock::GetLockContentionCount(); +} +FCIMPLEND diff --git a/src/classlibnative/bcltype/objectnative.h b/src/classlibnative/bcltype/objectnative.h index 573b04ea0a3a..3d008d95a907 100644 --- a/src/classlibnative/bcltype/objectnative.h +++ b/src/classlibnative/bcltype/objectnative.h @@ -38,6 +38,7 @@ class ObjectNative static FCDECL1(void, Pulse, Object* pThisUNSAFE); static FCDECL1(void, PulseAll, Object* pThisUNSAFE); static FCDECL1(FC_BOOL_RET, IsLockHeld, Object* pThisUNSAFE); + static FCDECL0(INT64, GetMonitorLockContentionCount); }; #endif // _OBJECTNATIVE_H_ diff --git a/src/vm/comthreadpool.cpp b/src/vm/comthreadpool.cpp index 18db9951cb0b..f5dc4233e029 100644 --- a/src/vm/comthreadpool.cpp +++ b/src/vm/comthreadpool.cpp @@ -181,6 +181,30 @@ FCIMPL2(VOID, ThreadPoolNative::CorGetAvailableThreads,DWORD* workerThreads, DWO } FCIMPLEND +/*****************************************************************************************************/ +FCIMPL0(INT32, ThreadPoolNative::GetThreadCount) +{ + FCALL_CONTRACT; + return ThreadpoolMgr::GetThreadCount(); +} +FCIMPLEND + +/*****************************************************************************************************/ +FCIMPL0(INT64, ThreadPoolNative::GetCompletedWorkItemCount) +{ + FCALL_CONTRACT; + return (INT64)Thread::GetTotalThreadPoolCompletionCount(); +} +FCIMPLEND + +/*****************************************************************************************************/ +FCIMPL0(INT64, ThreadPoolNative::GetPendingUnmanagedWorkItemCount) +{ + FCALL_CONTRACT; + return PerAppDomainTPCountList::GetUnmanagedTPCount()->GetNumRequests(); +} +FCIMPLEND + /*****************************************************************************************************/ FCIMPL0(VOID, ThreadPoolNative::NotifyRequestProgress) diff --git a/src/vm/comthreadpool.h b/src/vm/comthreadpool.h index 6b0f4ec969ca..d830c9e50513 100644 --- a/src/vm/comthreadpool.h +++ b/src/vm/comthreadpool.h @@ -28,6 +28,9 @@ class ThreadPoolNative 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, GetCompletedWorkItemCount); + static FCDECL0(INT64, GetPendingUnmanagedWorkItemCount); static FCDECL0(VOID, NotifyRequestProgress); static FCDECL0(FC_BOOL_RET, NotifyRequestComplete); diff --git a/src/vm/ecalllist.h b/src/vm/ecalllist.h index ae98cff19633..c91f7ee28865 100644 --- a/src/vm/ecalllist.h +++ b/src/vm/ecalllist.h @@ -676,6 +676,9 @@ FCFuncStart(gThreadPoolFuncs) FCFuncElement("GetAvailableThreadsNative", ThreadPoolNative::CorGetAvailableThreads) FCFuncElement("SetMinThreadsNative", ThreadPoolNative::CorSetMinThreads) FCFuncElement("GetMinThreadsNative", ThreadPoolNative::CorGetMinThreads) + FCFuncElement("get_ThreadCount", ThreadPoolNative::GetThreadCount) + FCFuncElement("get_CompletedWorkItemCount", ThreadPoolNative::GetCompletedWorkItemCount) + FCFuncElement("get_PendingUnmanagedWorkItemCount", ThreadPoolNative::GetPendingUnmanagedWorkItemCount) FCFuncElement("RegisterWaitForSingleObjectNative", ThreadPoolNative::CorRegisterWaitForSingleObject) FCFuncElement("BindIOCompletionCallbackNative", ThreadPoolNative::CorBindIoCompletionCallback) FCFuncElement("SetMaxThreadsNative", ThreadPoolNative::CorSetMaxThreads) @@ -913,6 +916,7 @@ FCFuncStart(gMonitorFuncs) FCFuncElement("ObjPulse", ObjectNative::Pulse) FCFuncElement("ObjPulseAll", ObjectNative::PulseAll) FCFuncElement("IsEnteredNative", ObjectNative::IsLockHeld) + FCFuncElement("get_LockContentionCount", ObjectNative::GetMonitorLockContentionCount) FCFuncEnd() FCFuncStart(gOverlappedFuncs) diff --git a/src/vm/syncblk.cpp b/src/vm/syncblk.cpp index ce31ac90b38b..690ac7c94bfe 100644 --- a/src/vm/syncblk.cpp +++ b/src/vm/syncblk.cpp @@ -2554,6 +2554,8 @@ inline void LogContention() #define LogContention() #endif +UINT64 AwareLock::s_lockContentionCount = 0; + BOOL AwareLock::EnterEpilogHelper(Thread* pCurThread, INT32 timeOut) { STATIC_CONTRACT_THROWS; @@ -2575,6 +2577,7 @@ BOOL AwareLock::EnterEpilogHelper(Thread* pCurThread, INT32 timeOut) FireEtwContentionStart_V1(ETW::ContentionLog::ContentionStructs::ManagedContention, GetClrInstanceId()); LogContention(); + InterlockedIncrement64((LONGLONG *)&s_lockContentionCount); OBJECTREF obj = GetOwningObject(); diff --git a/src/vm/syncblk.h b/src/vm/syncblk.h index 1ae14489c311..54f25e319a60 100644 --- a/src/vm/syncblk.h +++ b/src/vm/syncblk.h @@ -456,6 +456,10 @@ class AwareLock static const DWORD WaiterStarvationDurationMsBeforeStoppingPreemptingWaiters = 100; +#ifndef DACCESS_COMPILE + static UINT64 s_lockContentionCount; +#endif + // Only SyncBlocks can create AwareLocks. Hence this private constructor. AwareLock(DWORD indx) : m_Recursion(0), @@ -525,6 +529,19 @@ class AwareLock return m_HoldingThread; } +#ifndef DACCESS_COMPILE + static UINT64 GetLockContentionCount() + { + WRAPPER_NO_CONTRACT; + + if (sizeof(void *) >= sizeof(s_lockContentionCount)) + { + return VolatileLoad(&s_lockContentionCount); + } + return InterlockedCompareExchange64((LONGLONG *)&s_lockContentionCount, 0, 0); // prevent tearing + } +#endif + private: void ResetWaiterStarvationStartTime(); void RecordWaiterStarvationStartTime(); diff --git a/src/vm/threadpoolrequest.h b/src/vm/threadpoolrequest.h index eaba82389cee..3b2da28b5663 100644 --- a/src/vm/threadpoolrequest.h +++ b/src/vm/threadpoolrequest.h @@ -221,6 +221,12 @@ class UnManagedPerAppDomainTPCount : public IPerAppDomainTPCount { _ASSERT(FALSE); } + inline ULONG GetNumRequests() + { + LIMITED_METHOD_CONTRACT; + return VolatileLoad(&m_NumRequests); + } + private: SpinLock m_lock; ULONG m_NumRequests; diff --git a/src/vm/threads.cpp b/src/vm/threads.cpp index 6890290caaf8..a94ef7ed3993 100644 --- a/src/vm/threads.cpp +++ b/src/vm/threads.cpp @@ -83,7 +83,8 @@ PTR_ThreadLocalModule ThreadLocalBlock::GetTLMIfExists(MethodTable* pMT) BOOL Thread::s_fCleanFinalizedThread = FALSE; -Volatile Thread::s_threadPoolCompletionCountOverflow = 0; +UINT64 Thread::s_workerThreadPoolCompletionCountOverflow = 0; +UINT64 Thread::s_ioThreadPoolCompletionCountOverflow = 0; CrstStatic g_DeadlockAwareCrst; @@ -1526,7 +1527,8 @@ Thread::Thread() m_sfEstablisherOfActualHandlerFrame.Clear(); #endif // WIN64EXCEPTIONS - m_threadPoolCompletionCount = 0; + m_workerThreadPoolCompletionCount = 0; + m_ioThreadPoolCompletionCount = 0; Thread *pThread = GetThread(); InitContext(); @@ -5352,9 +5354,12 @@ BOOL ThreadStore::RemoveThread(Thread *target) if (target->IsBackground()) s_pThreadStore->m_BackgroundThreadCount--; - FastInterlockExchangeAdd( - &Thread::s_threadPoolCompletionCountOverflow, - target->m_threadPoolCompletionCount); + FastInterlockExchangeAddLong( + (LONGLONG *)&Thread::s_workerThreadPoolCompletionCountOverflow, + target->m_workerThreadPoolCompletionCount); + FastInterlockExchangeAddLong( + (LONGLONG *)&Thread::s_ioThreadPoolCompletionCountOverflow, + target->m_ioThreadPoolCompletionCount); _ASSERTE(s_pThreadStore->m_ThreadCount >= 0); _ASSERTE(s_pThreadStore->m_BackgroundThreadCount >= 0); @@ -8004,7 +8009,35 @@ BOOL ThreadStore::HoldingThreadStore(Thread *pThread) } } -LONG Thread::GetTotalThreadPoolCompletionCount() +NOINLINE void Thread::OnWorkerThreadPoolCompletionCountIncrementOverflow() +{ + WRAPPER_NO_CONTRACT; + + // Increment overflow, accumulate the count for this increment into the overflow count and reset the thread-local count + + // The thread store lock, in coordination with other places that read these values, ensures that both changes + // below become visible together + ThreadStoreLockHolder tsl; + + m_workerThreadPoolCompletionCount = 0; + InterlockedExchangeAdd64((LONGLONG *)&s_workerThreadPoolCompletionCountOverflow, (LONGLONG)UINT32_MAX + 1); +} + +NOINLINE void Thread::OnIOThreadPoolCompletionCountIncrementOverflow() +{ + WRAPPER_NO_CONTRACT; + + // Increment overflow, accumulate the count for this increment into the overflow count and reset the thread-local count + + // The thread store lock, in coordination with other places that read these values, ensures that both changes + // below become visible together + ThreadStoreLockHolder tsl; + + m_ioThreadPoolCompletionCount = 0; + InterlockedExchangeAdd64((LONGLONG *)&s_ioThreadPoolCompletionCountOverflow, (LONGLONG)UINT32_MAX + 1); +} + +UINT64 Thread::GetTotalWorkerThreadPoolCompletionCount() { CONTRACTL { @@ -8013,7 +8046,7 @@ LONG Thread::GetTotalThreadPoolCompletionCount() } CONTRACTL_END; - LONG total; + UINT64 total; if (g_fEEStarted) //make sure we actually have a thread store { // make sure up-to-date thread-local counts are visible to us @@ -8022,22 +8055,56 @@ LONG Thread::GetTotalThreadPoolCompletionCount() // enumerate all threads, summing their local counts. ThreadStoreLockHolder tsl; - total = s_threadPoolCompletionCountOverflow.Load(); + total = GetWorkerThreadPoolCompletionCountOverflow(); Thread *pThread = NULL; while ((pThread = ThreadStore::GetAllThreadList(pThread, 0, 0)) != NULL) { - total += pThread->m_threadPoolCompletionCount; + total += pThread->m_workerThreadPoolCompletionCount; } } else { - total = s_threadPoolCompletionCountOverflow.Load(); + total = GetWorkerThreadPoolCompletionCountOverflow(); } return total; } +UINT64 Thread::GetTotalThreadPoolCompletionCount() +{ + CONTRACTL + { + NOTHROW; + MODE_ANY; + } + CONTRACTL_END; + + UINT64 total; + if (g_fEEStarted) //make sure we actually have a thread store + { + // make sure up-to-date thread-local counts are visible to us + ::FlushProcessWriteBuffers(); + + // enumerate all threads, summing their local counts. + ThreadStoreLockHolder tsl; + + total = GetWorkerThreadPoolCompletionCountOverflow() + GetIOThreadPoolCompletionCountOverflow(); + + Thread *pThread = NULL; + while ((pThread = ThreadStore::GetAllThreadList(pThread, 0, 0)) != NULL) + { + total += pThread->m_workerThreadPoolCompletionCount; + total += pThread->m_ioThreadPoolCompletionCount; + } + } + else + { + total = GetWorkerThreadPoolCompletionCountOverflow() + GetIOThreadPoolCompletionCountOverflow(); + } + + return total; +} INT32 Thread::ResetManagedThreadObject(INT32 nPriority) { diff --git a/src/vm/threads.h b/src/vm/threads.h index 94ce27560405..161ab49985c5 100644 --- a/src/vm/threads.h +++ b/src/vm/threads.h @@ -3871,21 +3871,85 @@ class Thread: public IUnknown #endif // defined(FEATURE_PROFAPI_ATTACH_DETACH) || defined(DATA_PROFAPI_ATTACH_DETACH) private: - Volatile m_threadPoolCompletionCount; - static Volatile s_threadPoolCompletionCountOverflow; //counts completions for threads that have been destroyed. + UINT32 m_workerThreadPoolCompletionCount; + UINT32 m_ioThreadPoolCompletionCount; + static UINT64 s_workerThreadPoolCompletionCountOverflow; + static UINT64 s_ioThreadPoolCompletionCountOverflow; +#ifndef DACCESS_COMPILE public: - static void IncrementThreadPoolCompletionCount() + static void IncrementWorkerThreadPoolCompletionCount(Thread *pThread) { - LIMITED_METHOD_CONTRACT; - Thread* pThread = GetThread(); - if (pThread) - pThread->m_threadPoolCompletionCount++; + WRAPPER_NO_CONTRACT; + + if (pThread != nullptr) + { + UINT32 newCount = pThread->m_workerThreadPoolCompletionCount + 1; + if (newCount != 0) + { + pThread->m_workerThreadPoolCompletionCount = newCount; + } + else + { + pThread->OnWorkerThreadPoolCompletionCountIncrementOverflow(); + } + } else - FastInterlockIncrement(&s_threadPoolCompletionCountOverflow); + { + InterlockedIncrement64((LONGLONG *)&s_workerThreadPoolCompletionCountOverflow); + } } - static LONG GetTotalThreadPoolCompletionCount(); + static void IncrementIOThreadPoolCompletionCount(Thread *pThread) + { + WRAPPER_NO_CONTRACT; + + if (pThread != nullptr) + { + UINT32 newCount = pThread->m_ioThreadPoolCompletionCount + 1; + if (newCount != 0) + { + pThread->m_ioThreadPoolCompletionCount = newCount; + } + else + { + pThread->OnIOThreadPoolCompletionCountIncrementOverflow(); + } + } + else + { + InterlockedIncrement64((LONGLONG *)&s_ioThreadPoolCompletionCountOverflow); + } + } + + void OnWorkerThreadPoolCompletionCountIncrementOverflow(); + void OnIOThreadPoolCompletionCountIncrementOverflow(); + + static UINT64 GetWorkerThreadPoolCompletionCountOverflow() + { + WRAPPER_NO_CONTRACT; + + if (sizeof(void *) >= sizeof(s_workerThreadPoolCompletionCountOverflow)) + { + return VolatileLoad(&s_workerThreadPoolCompletionCountOverflow); + } + return InterlockedCompareExchange64((LONGLONG *)&s_workerThreadPoolCompletionCountOverflow, 0, 0); // prevent tearing + } + + static UINT64 GetIOThreadPoolCompletionCountOverflow() + { + WRAPPER_NO_CONTRACT; + + if (sizeof(void *) >= sizeof(s_ioThreadPoolCompletionCountOverflow)) + { + return VolatileLoad(&s_ioThreadPoolCompletionCountOverflow); + } + return InterlockedCompareExchange64((LONGLONG *)&s_ioThreadPoolCompletionCountOverflow, 0, 0); // prevent tearing + } + + static UINT64 GetTotalWorkerThreadPoolCompletionCount(); + static UINT64 GetTotalThreadPoolCompletionCount(); +#endif // !DACCESS_COMPILE private: diff --git a/src/vm/win32threadpool.cpp b/src/vm/win32threadpool.cpp index 29c1d21c99c0..db81651e07de 100644 --- a/src/vm/win32threadpool.cpp +++ b/src/vm/win32threadpool.cpp @@ -694,6 +694,14 @@ BOOL ThreadpoolMgr::GetAvailableThreads(DWORD* AvailableWorkerThreads, return TRUE; } +INT32 ThreadpoolMgr::GetThreadCount() +{ + WRAPPER_NO_CONTRACT; + + EnsureInitialized(); + return WorkerCounter.DangerousGetDirtyCounts().NumActive + CPThreadCounter.DangerousGetDirtyCounts().NumActive; +} + void QueueUserWorkItemHelp(LPTHREAD_START_ROUTINE Function, PVOID Context) { STATIC_CONTRACT_THROWS; @@ -912,7 +920,7 @@ void ThreadpoolMgr::AdjustMaxWorkersActive() _ASSERTE(ThreadAdjustmentLock.IsHeld()); DWORD currentTicks = GetTickCount(); - LONG totalNumCompletions = Thread::GetTotalThreadPoolCompletionCount(); + LONG totalNumCompletions = (LONG)Thread::GetTotalWorkerThreadPoolCompletionCount(); LONG numCompletions = totalNumCompletions - VolatileLoad(&PriorCompletedWorkRequests); LARGE_INTEGER startTime = CurrentSampleStartTime; @@ -2865,6 +2873,12 @@ DWORD WINAPI ThreadpoolMgr::AsyncCallbackCompletion(PVOID pArgs) ((WAITORTIMERCALLBACKFUNC) waitInfo->Callback) ( waitInfo->Context, asyncCallback->waitTimedOut != FALSE); + +#ifdef FEATURE_PAL + Thread::IncrementWorkerThreadPoolCompletionCount(pThread); +#else + Thread::IncrementIOThreadPoolCompletionCount(pThread); +#endif } return ERROR_SUCCESS; @@ -3610,6 +3624,12 @@ DWORD WINAPI ThreadpoolMgr::CompletionPortThreadStart(LPVOID lpArgs) ((LPOVERLAPPED_COMPLETION_ROUTINE) key)(errorCode, numBytes, pOverlapped); } + if ((void *)key != CallbackForInitiateDrainageOfCompletionPortQueue && + (void *)key != CallbackForContinueDrainageOfCompletionPortQueue) + { + Thread::IncrementIOThreadPoolCompletionCount(pThread); + } + if (pThread == NULL) { pThread = GetThread(); } @@ -4772,6 +4792,7 @@ DWORD WINAPI ThreadpoolMgr::AsyncTimerCallbackCompletion(PVOID pArgs) { TimerInfo* timerInfo = (TimerInfo*) pArgs; ((WAITORTIMERCALLBACKFUNC) timerInfo->Function) (timerInfo->Context, TRUE) ; + Thread::IncrementWorkerThreadPoolCompletionCount(pThread); if (InterlockedDecrement(&timerInfo->refCount) == 0) { diff --git a/src/vm/win32threadpool.h b/src/vm/win32threadpool.h index bb6ebc06130a..a215f301e405 100644 --- a/src/vm/win32threadpool.h +++ b/src/vm/win32threadpool.h @@ -244,6 +244,8 @@ class ThreadpoolMgr static BOOL GetAvailableThreads(DWORD* AvailableWorkerThreads, DWORD* AvailableIOCompletionThreads); + static INT32 GetThreadCount(); + static BOOL QueueUserWorkItem(LPTHREAD_START_ROUTINE Function, PVOID Context, ULONG Flags, @@ -831,7 +833,7 @@ class ThreadpoolMgr static void NotifyWorkItemCompleted() { WRAPPER_NO_CONTRACT; - Thread::IncrementThreadPoolCompletionCount(); + Thread::IncrementWorkerThreadPoolCompletionCount(GetThread()); UpdateLastDequeueTime(); } From 06852732967a6c017980dcbce7f5cfac69d32193 Mon Sep 17 00:00:00 2001 From: Koundinya Veluri Date: Tue, 26 Feb 2019 12:07:00 -0800 Subject: [PATCH 2/7] Use thread-local for lock contention counting, expose local and global pending work item counts --- .../shared/System/Threading/ThreadPool.cs | 28 ++++- src/classlibnative/bcltype/objectnative.cpp | 2 +- src/vm/syncblk.cpp | 4 +- src/vm/syncblk.h | 17 --- src/vm/threads.cpp | 35 +++--- src/vm/threads.h | 100 +++++++++++------- src/vm/win32threadpool.cpp | 5 +- 7 files changed, 107 insertions(+), 84 deletions(-) diff --git a/src/System.Private.CoreLib/shared/System/Threading/ThreadPool.cs b/src/System.Private.CoreLib/shared/System/Threading/ThreadPool.cs index 50db98c077a4..54edefe9a1a5 100644 --- a/src/System.Private.CoreLib/shared/System/Threading/ThreadPool.cs +++ b/src/System.Private.CoreLib/shared/System/Threading/ThreadPool.cs @@ -532,11 +532,11 @@ public object Dequeue(ThreadPoolWorkQueueThreadLocals tl, ref bool missedSteal) return callback; } - public long Count + public long LocalCount { get { - long count = workItems.Count; + long count = 0; foreach (WorkStealingQueue workStealingQueue in WorkStealingQueueList.Queues) { count += workStealingQueue.Count; @@ -545,6 +545,8 @@ public long Count } } + public long GlobalCount => workItems.Count; + /// /// Dispatches work items to this thread. /// @@ -1282,6 +1284,26 @@ internal static object[] GetGloballyQueuedWorkItemsForDebugger() => internal static object[] GetLocallyQueuedWorkItemsForDebugger() => ToObjectArray(GetLocallyQueuedWorkItems()); + /// + /// Gets the number of local work items that are currently queued to be processed. + /// + /// + /// Local work items are work items queued in some fashion that has association with a particular thread or its + /// execution environment, and are typically processed in last-in-first-out order. They may include tasks and work items + /// queued with with + /// preferLocal: true. + /// + public static long PendingLocalWorkItemCount => ThreadPoolGlobals.workQueue.LocalCount; + + /// + /// Gets the number of global work items that are currently queued to be processed. + /// + /// + /// Global work items are shared by all thread pool worker threads and are typically processed in first-in-first-out + /// order. See for other relevant remarks. + /// + public static long PendingGlobalWorkItemCount => ThreadPoolGlobals.workQueue.GlobalCount + PendingUnmanagedWorkItemCount; + /// /// Gets the number of work items that are currently queued to be processed. /// @@ -1291,6 +1313,6 @@ internal static object[] GetLocallyQueuedWorkItemsForDebugger() => /// timer and wait callbacks in the count. On Windows, the count is unlikely to include the number of pending IO /// completions, as they get posted directly to an IO completion port. /// - public static long PendingWorkItemCount => ThreadPoolGlobals.workQueue.Count + PendingUnmanagedWorkItemCount; + public static long PendingWorkItemCount => PendingLocalWorkItemCount + PendingGlobalWorkItemCount; } } diff --git a/src/classlibnative/bcltype/objectnative.cpp b/src/classlibnative/bcltype/objectnative.cpp index 4af411fc2cad..a5619e107f79 100644 --- a/src/classlibnative/bcltype/objectnative.cpp +++ b/src/classlibnative/bcltype/objectnative.cpp @@ -358,6 +358,6 @@ FCIMPLEND FCIMPL0(INT64, ObjectNative::GetMonitorLockContentionCount) { FCALL_CONTRACT; - return (INT64)AwareLock::GetLockContentionCount(); + return (INT64)Thread::GetTotalMonitorLockContentionCount(); } FCIMPLEND diff --git a/src/vm/syncblk.cpp b/src/vm/syncblk.cpp index 690ac7c94bfe..9aea18f0c6c0 100644 --- a/src/vm/syncblk.cpp +++ b/src/vm/syncblk.cpp @@ -2554,8 +2554,6 @@ inline void LogContention() #define LogContention() #endif -UINT64 AwareLock::s_lockContentionCount = 0; - BOOL AwareLock::EnterEpilogHelper(Thread* pCurThread, INT32 timeOut) { STATIC_CONTRACT_THROWS; @@ -2577,7 +2575,7 @@ BOOL AwareLock::EnterEpilogHelper(Thread* pCurThread, INT32 timeOut) FireEtwContentionStart_V1(ETW::ContentionLog::ContentionStructs::ManagedContention, GetClrInstanceId()); LogContention(); - InterlockedIncrement64((LONGLONG *)&s_lockContentionCount); + Thread::IncrementMonitorLockContentionCount(pCurThread); OBJECTREF obj = GetOwningObject(); diff --git a/src/vm/syncblk.h b/src/vm/syncblk.h index 54f25e319a60..1ae14489c311 100644 --- a/src/vm/syncblk.h +++ b/src/vm/syncblk.h @@ -456,10 +456,6 @@ class AwareLock static const DWORD WaiterStarvationDurationMsBeforeStoppingPreemptingWaiters = 100; -#ifndef DACCESS_COMPILE - static UINT64 s_lockContentionCount; -#endif - // Only SyncBlocks can create AwareLocks. Hence this private constructor. AwareLock(DWORD indx) : m_Recursion(0), @@ -529,19 +525,6 @@ class AwareLock return m_HoldingThread; } -#ifndef DACCESS_COMPILE - static UINT64 GetLockContentionCount() - { - WRAPPER_NO_CONTRACT; - - if (sizeof(void *) >= sizeof(s_lockContentionCount)) - { - return VolatileLoad(&s_lockContentionCount); - } - return InterlockedCompareExchange64((LONGLONG *)&s_lockContentionCount, 0, 0); // prevent tearing - } -#endif - private: void ResetWaiterStarvationStartTime(); void RecordWaiterStarvationStartTime(); diff --git a/src/vm/threads.cpp b/src/vm/threads.cpp index a94ef7ed3993..3ae3b931e360 100644 --- a/src/vm/threads.cpp +++ b/src/vm/threads.cpp @@ -85,6 +85,7 @@ BOOL Thread::s_fCleanFinalizedThread = FALSE; UINT64 Thread::s_workerThreadPoolCompletionCountOverflow = 0; UINT64 Thread::s_ioThreadPoolCompletionCountOverflow = 0; +UINT64 Thread::s_monitorLockContentionCountOverflow = 0; CrstStatic g_DeadlockAwareCrst; @@ -1529,6 +1530,7 @@ Thread::Thread() m_workerThreadPoolCompletionCount = 0; m_ioThreadPoolCompletionCount = 0; + m_monitorLockContentionCount = 0; Thread *pThread = GetThread(); InitContext(); @@ -5360,6 +5362,9 @@ BOOL ThreadStore::RemoveThread(Thread *target) FastInterlockExchangeAddLong( (LONGLONG *)&Thread::s_ioThreadPoolCompletionCountOverflow, target->m_ioThreadPoolCompletionCount); + FastInterlockExchangeAddLong( + (LONGLONG *)&Thread::s_monitorLockContentionCountOverflow, + target->m_monitorLockContentionCount); _ASSERTE(s_pThreadStore->m_ThreadCount >= 0); _ASSERTE(s_pThreadStore->m_BackgroundThreadCount >= 0); @@ -8009,9 +8014,11 @@ BOOL ThreadStore::HoldingThreadStore(Thread *pThread) } } -NOINLINE void Thread::OnWorkerThreadPoolCompletionCountIncrementOverflow() +NOINLINE void Thread::OnIncrementCountOverflow(UINT32 *threadLocalCount, UINT64 *overflowCount) { WRAPPER_NO_CONTRACT; + _ASSERTE(threadLocalCount != nullptr); + _ASSERTE(overflowCount != nullptr); // Increment overflow, accumulate the count for this increment into the overflow count and reset the thread-local count @@ -8019,25 +8026,11 @@ NOINLINE void Thread::OnWorkerThreadPoolCompletionCountIncrementOverflow() // below become visible together ThreadStoreLockHolder tsl; - m_workerThreadPoolCompletionCount = 0; - InterlockedExchangeAdd64((LONGLONG *)&s_workerThreadPoolCompletionCountOverflow, (LONGLONG)UINT32_MAX + 1); + *threadLocalCount = 0; + InterlockedExchangeAdd64((LONGLONG *)overflowCount, (LONGLONG)UINT32_MAX + 1); } -NOINLINE void Thread::OnIOThreadPoolCompletionCountIncrementOverflow() -{ - WRAPPER_NO_CONTRACT; - - // Increment overflow, accumulate the count for this increment into the overflow count and reset the thread-local count - - // The thread store lock, in coordination with other places that read these values, ensures that both changes - // below become visible together - ThreadStoreLockHolder tsl; - - m_ioThreadPoolCompletionCount = 0; - InterlockedExchangeAdd64((LONGLONG *)&s_ioThreadPoolCompletionCountOverflow, (LONGLONG)UINT32_MAX + 1); -} - -UINT64 Thread::GetTotalWorkerThreadPoolCompletionCount() +UINT64 Thread::GetTotalCount(SIZE_T threadLocalCountOffset, UINT64 *overflowCount) { CONTRACTL { @@ -8055,12 +8048,12 @@ UINT64 Thread::GetTotalWorkerThreadPoolCompletionCount() // enumerate all threads, summing their local counts. ThreadStoreLockHolder tsl; - total = GetWorkerThreadPoolCompletionCountOverflow(); + total = GetOverflowCount(overflowCount); Thread *pThread = NULL; while ((pThread = ThreadStore::GetAllThreadList(pThread, 0, 0)) != NULL) { - total += pThread->m_workerThreadPoolCompletionCount; + total += *GetThreadLocalCountRef(pThread, threadLocalCountOffset); } } else @@ -8080,6 +8073,8 @@ UINT64 Thread::GetTotalThreadPoolCompletionCount() } CONTRACTL_END; + _ASSERTE(overflowCount != nullptr); + UINT64 total; if (g_fEEStarted) //make sure we actually have a thread store { diff --git a/src/vm/threads.h b/src/vm/threads.h index 161ab49985c5..2eceed95f4fc 100644 --- a/src/vm/threads.h +++ b/src/vm/threads.h @@ -3872,83 +3872,111 @@ class Thread: public IUnknown private: UINT32 m_workerThreadPoolCompletionCount; - UINT32 m_ioThreadPoolCompletionCount; static UINT64 s_workerThreadPoolCompletionCountOverflow; + UINT32 m_ioThreadPoolCompletionCount; static UINT64 s_ioThreadPoolCompletionCountOverflow; + UINT32 m_monitorLockContentionCount; + static UINT64 s_monitorLockContentionCountOverflow; #ifndef DACCESS_COMPILE -public: - static void IncrementWorkerThreadPoolCompletionCount(Thread *pThread) +private: + static UINT32 *GetThreadLocalCountRef(Thread *pThread, SIZE_T threadLocalCountOffset) + { + WRAPPER_NO_CONTRACT; + _ASSERTE(threadLocalCountOffset <= sizeof(Thread) - sizeof(UINT32)); + + return (UINT32 *)((SIZE_T)pThread + threadLocalCountOffset); + } + + static void IncrementCount(Thread *pThread, SIZE_T threadLocalCountOffset, UINT64 *overflowCount) { WRAPPER_NO_CONTRACT; + _ASSERTE(overflowCount != nullptr); if (pThread != nullptr) { - UINT32 newCount = pThread->m_workerThreadPoolCompletionCount + 1; + UINT32 *threadLocalCount = GetThreadLocalCountRef(pThread, threadLocalCountOffset); + UINT32 newCount = *threadLocalCount + 1; if (newCount != 0) { - pThread->m_workerThreadPoolCompletionCount = newCount; + *threadLocalCount = newCount; } else { - pThread->OnWorkerThreadPoolCompletionCountIncrementOverflow(); + OnIncrementCountOverflow(threadLocalCount, overflowCount); } } else { - InterlockedIncrement64((LONGLONG *)&s_workerThreadPoolCompletionCountOverflow); + InterlockedIncrement64((LONGLONG *)overflowCount); } } - static void IncrementIOThreadPoolCompletionCount(Thread *pThread) + static void OnIncrementCountOverflow(UINT32 *threadLocalCount, UINT64 *overflowCount); + + static UINT64 GetOverflowCount(UINT64 *overflowCount) { WRAPPER_NO_CONTRACT; - if (pThread != nullptr) + if (sizeof(void *) >= sizeof(*overflowCount)) { - UINT32 newCount = pThread->m_ioThreadPoolCompletionCount + 1; - if (newCount != 0) - { - pThread->m_ioThreadPoolCompletionCount = newCount; - } - else - { - pThread->OnIOThreadPoolCompletionCountIncrementOverflow(); - } - } - else - { - InterlockedIncrement64((LONGLONG *)&s_ioThreadPoolCompletionCountOverflow); + return VolatileLoad(overflowCount); } + return InterlockedCompareExchange64((LONGLONG *)overflowCount, 0, 0); // prevent tearing } - void OnWorkerThreadPoolCompletionCountIncrementOverflow(); - void OnIOThreadPoolCompletionCountIncrementOverflow(); + 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); + } - if (sizeof(void *) >= sizeof(s_workerThreadPoolCompletionCountOverflow)) - { - return VolatileLoad(&s_workerThreadPoolCompletionCountOverflow); - } - return InterlockedCompareExchange64((LONGLONG *)&s_workerThreadPoolCompletionCountOverflow, 0, 0); // prevent tearing + static UINT64 GetTotalWorkerThreadPoolCompletionCount() + { + WRAPPER_NO_CONTRACT; + return GetTotalCount(offsetof(Thread, m_workerThreadPoolCompletionCount), &s_workerThreadPoolCompletionCountOverflow); } - static UINT64 GetIOThreadPoolCompletionCountOverflow() + static void IncrementIOThreadPoolCompletionCount(Thread *pThread) { WRAPPER_NO_CONTRACT; + IncrementCount(pThread, offsetof(Thread, m_ioThreadPoolCompletionCount), &s_ioThreadPoolCompletionCountOverflow); + } - if (sizeof(void *) >= sizeof(s_ioThreadPoolCompletionCountOverflow)) - { - return VolatileLoad(&s_ioThreadPoolCompletionCountOverflow); - } - return InterlockedCompareExchange64((LONGLONG *)&s_ioThreadPoolCompletionCountOverflow, 0, 0); // prevent tearing + static UINT64 GetIOThreadPoolCompletionCountOverflow() + { + WRAPPER_NO_CONTRACT; + return GetOverflowCount(&s_ioThreadPoolCompletionCountOverflow); } - static UINT64 GetTotalWorkerThreadPoolCompletionCount(); static UINT64 GetTotalThreadPoolCompletionCount(); + + static void IncrementMonitorLockContentionCount(Thread *pThread) + { + WRAPPER_NO_CONTRACT; + IncrementCount(pThread, offsetof(Thread, m_monitorLockContentionCount), &s_monitorLockContentionCountOverflow); + } + + static UINT64 GetMonitorLockContentionCountOverflow() + { + WRAPPER_NO_CONTRACT; + return GetOverflowCount(&s_monitorLockContentionCountOverflow); + } + + static UINT64 GetTotalMonitorLockContentionCount() + { + WRAPPER_NO_CONTRACT; + return GetTotalCount(offsetof(Thread, m_monitorLockContentionCount), &s_monitorLockContentionCountOverflow); + } #endif // !DACCESS_COMPILE private: diff --git a/src/vm/win32threadpool.cpp b/src/vm/win32threadpool.cpp index db81651e07de..725100891ec4 100644 --- a/src/vm/win32threadpool.cpp +++ b/src/vm/win32threadpool.cpp @@ -2874,9 +2874,7 @@ DWORD WINAPI ThreadpoolMgr::AsyncCallbackCompletion(PVOID pArgs) ((WAITORTIMERCALLBACKFUNC) waitInfo->Callback) ( waitInfo->Context, asyncCallback->waitTimedOut != FALSE); -#ifdef FEATURE_PAL - Thread::IncrementWorkerThreadPoolCompletionCount(pThread); -#else +#ifndef FEATURE_PAL Thread::IncrementIOThreadPoolCompletionCount(pThread); #endif } @@ -4792,7 +4790,6 @@ DWORD WINAPI ThreadpoolMgr::AsyncTimerCallbackCompletion(PVOID pArgs) { TimerInfo* timerInfo = (TimerInfo*) pArgs; ((WAITORTIMERCALLBACKFUNC) timerInfo->Function) (timerInfo->Context, TRUE) ; - Thread::IncrementWorkerThreadPoolCompletionCount(pThread); if (InterlockedDecrement(&timerInfo->refCount) == 0) { From c6768c547f3650a8dad55ccd5396a1f8002dffa5 Mon Sep 17 00:00:00 2001 From: Koundinya Veluri Date: Wed, 6 Mar 2019 15:48:34 -0800 Subject: [PATCH 3/7] Small fixes --- src/vm/threads.cpp | 2 -- src/vm/win32threadpool.cpp | 6 +++++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/vm/threads.cpp b/src/vm/threads.cpp index 3ae3b931e360..335d0d696bf4 100644 --- a/src/vm/threads.cpp +++ b/src/vm/threads.cpp @@ -8073,8 +8073,6 @@ UINT64 Thread::GetTotalThreadPoolCompletionCount() } CONTRACTL_END; - _ASSERTE(overflowCount != nullptr); - UINT64 total; if (g_fEEStarted) //make sure we actually have a thread store { diff --git a/src/vm/win32threadpool.cpp b/src/vm/win32threadpool.cpp index 725100891ec4..13a1e7b3901a 100644 --- a/src/vm/win32threadpool.cpp +++ b/src/vm/win32threadpool.cpp @@ -698,7 +698,11 @@ INT32 ThreadpoolMgr::GetThreadCount() { WRAPPER_NO_CONTRACT; - EnsureInitialized(); + if (!IsInitialized()) + { + return 0; + } + return WorkerCounter.DangerousGetDirtyCounts().NumActive + CPThreadCounter.DangerousGetDirtyCounts().NumActive; } From b89e39ab008f6c13c8c4780d72e5fbc415a697a2 Mon Sep 17 00:00:00 2001 From: Koundinya Veluri Date: Tue, 2 Apr 2019 14:36:10 -0700 Subject: [PATCH 4/7] Remove local/global variants of PendingWorkItemCount --- .../shared/System/Threading/ThreadPool.cs | 29 +++++-------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/src/System.Private.CoreLib/shared/System/Threading/ThreadPool.cs b/src/System.Private.CoreLib/shared/System/Threading/ThreadPool.cs index 54edefe9a1a5..fa7c17ef8e0b 100644 --- a/src/System.Private.CoreLib/shared/System/Threading/ThreadPool.cs +++ b/src/System.Private.CoreLib/shared/System/Threading/ThreadPool.cs @@ -1284,26 +1284,6 @@ internal static object[] GetGloballyQueuedWorkItemsForDebugger() => internal static object[] GetLocallyQueuedWorkItemsForDebugger() => ToObjectArray(GetLocallyQueuedWorkItems()); - /// - /// Gets the number of local work items that are currently queued to be processed. - /// - /// - /// Local work items are work items queued in some fashion that has association with a particular thread or its - /// execution environment, and are typically processed in last-in-first-out order. They may include tasks and work items - /// queued with with - /// preferLocal: true. - /// - public static long PendingLocalWorkItemCount => ThreadPoolGlobals.workQueue.LocalCount; - - /// - /// Gets the number of global work items that are currently queued to be processed. - /// - /// - /// Global work items are shared by all thread pool worker threads and are typically processed in first-in-first-out - /// order. See for other relevant remarks. - /// - public static long PendingGlobalWorkItemCount => ThreadPoolGlobals.workQueue.GlobalCount + PendingUnmanagedWorkItemCount; - /// /// Gets the number of work items that are currently queued to be processed. /// @@ -1313,6 +1293,13 @@ internal static object[] GetLocallyQueuedWorkItemsForDebugger() => /// timer and wait callbacks in the count. On Windows, the count is unlikely to include the number of pending IO /// completions, as they get posted directly to an IO completion port. /// - public static long PendingWorkItemCount => PendingLocalWorkItemCount + PendingGlobalWorkItemCount; + public static long PendingWorkItemCount + { + get + { + ThreadPoolWorkQueue workQueue = ThreadPoolGlobals.workQueue; + return workQueue.LocalCount + workQueue.GlobalCount + PendingUnmanagedWorkItemCount; + } + } } } From 99d4ad9da3faf9ad01451d2fe18d2a6b72a419c5 Mon Sep 17 00:00:00 2001 From: Koundinya Veluri Date: Tue, 2 Apr 2019 15:43:01 -0700 Subject: [PATCH 5/7] Remove FlushProcessWriteBuffers, add a missing volatile --- src/vm/threads.cpp | 6 ------ src/vm/threads.h | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/vm/threads.cpp b/src/vm/threads.cpp index 335d0d696bf4..a1484b5496cc 100644 --- a/src/vm/threads.cpp +++ b/src/vm/threads.cpp @@ -8042,9 +8042,6 @@ UINT64 Thread::GetTotalCount(SIZE_T threadLocalCountOffset, UINT64 *overflowCoun UINT64 total; if (g_fEEStarted) //make sure we actually have a thread store { - // make sure up-to-date thread-local counts are visible to us - ::FlushProcessWriteBuffers(); - // enumerate all threads, summing their local counts. ThreadStoreLockHolder tsl; @@ -8076,9 +8073,6 @@ UINT64 Thread::GetTotalThreadPoolCompletionCount() UINT64 total; if (g_fEEStarted) //make sure we actually have a thread store { - // make sure up-to-date thread-local counts are visible to us - ::FlushProcessWriteBuffers(); - // enumerate all threads, summing their local counts. ThreadStoreLockHolder tsl; diff --git a/src/vm/threads.h b/src/vm/threads.h index 2eceed95f4fc..5d2e7b309b32 100644 --- a/src/vm/threads.h +++ b/src/vm/threads.h @@ -3899,7 +3899,7 @@ class Thread: public IUnknown UINT32 newCount = *threadLocalCount + 1; if (newCount != 0) { - *threadLocalCount = newCount; + VolatileStoreWithoutBarrier(threadLocalCount, newCount); } else { From 50a1668244568214eb527c22a2aa4e939cab99a0 Mon Sep 17 00:00:00 2001 From: Koundinya Veluri Date: Tue, 2 Apr 2019 15:53:42 -0700 Subject: [PATCH 6/7] Preserve flush for hill climbing --- src/vm/threads.cpp | 13 +++++++++++++ src/vm/threads.h | 7 +++++++ src/vm/win32threadpool.cpp | 2 +- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/vm/threads.cpp b/src/vm/threads.cpp index a1484b5496cc..27b81e677941 100644 --- a/src/vm/threads.cpp +++ b/src/vm/threads.cpp @@ -8061,6 +8061,19 @@ UINT64 Thread::GetTotalCount(SIZE_T threadLocalCountOffset, UINT64 *overflowCoun return total; } +UINT64 Thread::GetRecentTotalCount(SIZE_T threadLocalCountOffset, UINT64 *overflowCount) +{ + WRAPPER_NO_CONTRACT; + + if (g_fEEStarted) + { + // make sure up-to-date thread-local counts are visible to us + ::FlushProcessWriteBuffers(); + } + + return GetTotalCount(threadLocalCountOffset, overflowCount); +} + UINT64 Thread::GetTotalThreadPoolCompletionCount() { CONTRACTL diff --git a/src/vm/threads.h b/src/vm/threads.h index 5d2e7b309b32..3e2331febffa 100644 --- a/src/vm/threads.h +++ b/src/vm/threads.h @@ -3926,6 +3926,7 @@ class Thread: public IUnknown } static UINT64 GetTotalCount(SIZE_T threadLocalCountOffset, UINT64 *overflowCount); + static UINT64 GetRecentTotalCount(SIZE_T threadLocalCountOffset, UINT64 *overflowCount); public: static void IncrementWorkerThreadPoolCompletionCount(Thread *pThread) @@ -3946,6 +3947,12 @@ class Thread: public IUnknown return GetTotalCount(offsetof(Thread, m_workerThreadPoolCompletionCount), &s_workerThreadPoolCompletionCountOverflow); } + static UINT64 GetRecentTotalWorkerThreadPoolCompletionCount() + { + WRAPPER_NO_CONTRACT; + return GetRecentTotalCount(offsetof(Thread, m_workerThreadPoolCompletionCount), &s_workerThreadPoolCompletionCountOverflow); + } + static void IncrementIOThreadPoolCompletionCount(Thread *pThread) { WRAPPER_NO_CONTRACT; diff --git a/src/vm/win32threadpool.cpp b/src/vm/win32threadpool.cpp index 13a1e7b3901a..511435302e78 100644 --- a/src/vm/win32threadpool.cpp +++ b/src/vm/win32threadpool.cpp @@ -924,7 +924,7 @@ void ThreadpoolMgr::AdjustMaxWorkersActive() _ASSERTE(ThreadAdjustmentLock.IsHeld()); DWORD currentTicks = GetTickCount(); - LONG totalNumCompletions = (LONG)Thread::GetTotalWorkerThreadPoolCompletionCount(); + LONG totalNumCompletions = (LONG)Thread::GetRecentTotalWorkerThreadPoolCompletionCount(); LONG numCompletions = totalNumCompletions - VolatileLoad(&PriorCompletedWorkRequests); LARGE_INTEGER startTime = CurrentSampleStartTime; From 23c427edcf8d3ab614e594c8ba329fe6d83f1c00 Mon Sep 17 00:00:00 2001 From: Koundinya Veluri Date: Tue, 2 Apr 2019 16:40:23 -0700 Subject: [PATCH 7/7] Remove flushes and a check for EE started --- src/vm/threads.cpp | 55 +++++++++----------------------------- src/vm/threads.h | 7 ----- src/vm/win32threadpool.cpp | 2 +- 3 files changed, 14 insertions(+), 50 deletions(-) diff --git a/src/vm/threads.cpp b/src/vm/threads.cpp index 27b81e677941..8a89d84ec677 100644 --- a/src/vm/threads.cpp +++ b/src/vm/threads.cpp @@ -8039,41 +8039,20 @@ UINT64 Thread::GetTotalCount(SIZE_T threadLocalCountOffset, UINT64 *overflowCoun } CONTRACTL_END; - UINT64 total; - if (g_fEEStarted) //make sure we actually have a thread store - { - // enumerate all threads, summing their local counts. - ThreadStoreLockHolder tsl; + // enumerate all threads, summing their local counts. + ThreadStoreLockHolder tsl; - total = GetOverflowCount(overflowCount); + UINT64 total = GetOverflowCount(overflowCount); - Thread *pThread = NULL; - while ((pThread = ThreadStore::GetAllThreadList(pThread, 0, 0)) != NULL) - { - total += *GetThreadLocalCountRef(pThread, threadLocalCountOffset); - } - } - else + Thread *pThread = NULL; + while ((pThread = ThreadStore::GetAllThreadList(pThread, 0, 0)) != NULL) { - total = GetWorkerThreadPoolCompletionCountOverflow(); + total += *GetThreadLocalCountRef(pThread, threadLocalCountOffset); } return total; } -UINT64 Thread::GetRecentTotalCount(SIZE_T threadLocalCountOffset, UINT64 *overflowCount) -{ - WRAPPER_NO_CONTRACT; - - if (g_fEEStarted) - { - // make sure up-to-date thread-local counts are visible to us - ::FlushProcessWriteBuffers(); - } - - return GetTotalCount(threadLocalCountOffset, overflowCount); -} - UINT64 Thread::GetTotalThreadPoolCompletionCount() { CONTRACTL @@ -8083,24 +8062,16 @@ UINT64 Thread::GetTotalThreadPoolCompletionCount() } CONTRACTL_END; - UINT64 total; - if (g_fEEStarted) //make sure we actually have a thread store - { - // enumerate all threads, summing their local counts. - ThreadStoreLockHolder tsl; + // enumerate all threads, summing their local counts. + ThreadStoreLockHolder tsl; - total = GetWorkerThreadPoolCompletionCountOverflow() + GetIOThreadPoolCompletionCountOverflow(); + UINT64 total = GetWorkerThreadPoolCompletionCountOverflow() + GetIOThreadPoolCompletionCountOverflow(); - Thread *pThread = NULL; - while ((pThread = ThreadStore::GetAllThreadList(pThread, 0, 0)) != NULL) - { - total += pThread->m_workerThreadPoolCompletionCount; - total += pThread->m_ioThreadPoolCompletionCount; - } - } - else + Thread *pThread = NULL; + while ((pThread = ThreadStore::GetAllThreadList(pThread, 0, 0)) != NULL) { - total = GetWorkerThreadPoolCompletionCountOverflow() + GetIOThreadPoolCompletionCountOverflow(); + total += pThread->m_workerThreadPoolCompletionCount; + total += pThread->m_ioThreadPoolCompletionCount; } return total; diff --git a/src/vm/threads.h b/src/vm/threads.h index 3e2331febffa..5d2e7b309b32 100644 --- a/src/vm/threads.h +++ b/src/vm/threads.h @@ -3926,7 +3926,6 @@ class Thread: public IUnknown } static UINT64 GetTotalCount(SIZE_T threadLocalCountOffset, UINT64 *overflowCount); - static UINT64 GetRecentTotalCount(SIZE_T threadLocalCountOffset, UINT64 *overflowCount); public: static void IncrementWorkerThreadPoolCompletionCount(Thread *pThread) @@ -3947,12 +3946,6 @@ class Thread: public IUnknown return GetTotalCount(offsetof(Thread, m_workerThreadPoolCompletionCount), &s_workerThreadPoolCompletionCountOverflow); } - static UINT64 GetRecentTotalWorkerThreadPoolCompletionCount() - { - WRAPPER_NO_CONTRACT; - return GetRecentTotalCount(offsetof(Thread, m_workerThreadPoolCompletionCount), &s_workerThreadPoolCompletionCountOverflow); - } - static void IncrementIOThreadPoolCompletionCount(Thread *pThread) { WRAPPER_NO_CONTRACT; diff --git a/src/vm/win32threadpool.cpp b/src/vm/win32threadpool.cpp index 511435302e78..13a1e7b3901a 100644 --- a/src/vm/win32threadpool.cpp +++ b/src/vm/win32threadpool.cpp @@ -924,7 +924,7 @@ void ThreadpoolMgr::AdjustMaxWorkersActive() _ASSERTE(ThreadAdjustmentLock.IsHeld()); DWORD currentTicks = GetTickCount(); - LONG totalNumCompletions = (LONG)Thread::GetRecentTotalWorkerThreadPoolCompletionCount(); + LONG totalNumCompletions = (LONG)Thread::GetTotalWorkerThreadPoolCompletionCount(); LONG numCompletions = totalNumCompletions - VolatileLoad(&PriorCompletedWorkRequests); LARGE_INTEGER startTime = CurrentSampleStartTime;