Skip to content

Commit 0410c3e

Browse files
authored
Implement APIs for some threading metrics (CoreCLR) (dotnet#24113)
Implement APIs for some threading metrics (CoreCLR) API review: https://github.com/dotnet/corefx/issues/35500
1 parent 924d4fc commit 0410c3e

File tree

14 files changed

+325
-34
lines changed

14 files changed

+325
-34
lines changed

src/System.Private.CoreLib/shared/System/Threading/ThreadPool.cs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,26 @@ public bool LocalFindAndPop(object obj)
381381
return null;
382382
}
383383
}
384+
385+
public int Count
386+
{
387+
get
388+
{
389+
bool lockTaken = false;
390+
try
391+
{
392+
m_foreignLock.Enter(ref lockTaken);
393+
return Math.Max(0, m_tailIndex - m_headIndex);
394+
}
395+
finally
396+
{
397+
if (lockTaken)
398+
{
399+
m_foreignLock.Exit(useMemoryBarrier: false);
400+
}
401+
}
402+
}
403+
}
384404
}
385405

386406
internal bool loggingEnabled;
@@ -512,6 +532,21 @@ internal bool LocalFindAndPop(object callback)
512532
return callback;
513533
}
514534

535+
public long LocalCount
536+
{
537+
get
538+
{
539+
long count = 0;
540+
foreach (WorkStealingQueue workStealingQueue in WorkStealingQueueList.Queues)
541+
{
542+
count += workStealingQueue.Count;
543+
}
544+
return count;
545+
}
546+
}
547+
548+
public long GlobalCount => workItems.Count;
549+
515550
/// <summary>
516551
/// Dispatches work items to this thread.
517552
/// </summary>
@@ -1241,5 +1276,23 @@ internal static object[] GetGloballyQueuedWorkItemsForDebugger() =>
12411276

12421277
internal static object[] GetLocallyQueuedWorkItemsForDebugger() =>
12431278
ToObjectArray(GetLocallyQueuedWorkItems());
1279+
1280+
/// <summary>
1281+
/// Gets the number of work items that are currently queued to be processed.
1282+
/// </summary>
1283+
/// <remarks>
1284+
/// For a thread pool implementation that may have different types of work items, the count includes all types that can
1285+
/// be tracked, which may only be the user work items including tasks. Some implementations may also include queued
1286+
/// timer and wait callbacks in the count. On Windows, the count is unlikely to include the number of pending IO
1287+
/// completions, as they get posted directly to an IO completion port.
1288+
/// </remarks>
1289+
public static long PendingWorkItemCount
1290+
{
1291+
get
1292+
{
1293+
ThreadPoolWorkQueue workQueue = ThreadPoolGlobals.workQueue;
1294+
return workQueue.LocalCount + workQueue.GlobalCount + PendingUnmanagedWorkItemCount;
1295+
}
1296+
}
12441297
}
12451298
}

src/System.Private.CoreLib/src/System/Threading/Monitor.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,5 +233,14 @@ public static void PulseAll(object obj)
233233

234234
ObjPulseAll(obj);
235235
}
236+
237+
/// <summary>
238+
/// Gets the number of times there was contention upon trying to take a <see cref="Monitor"/>'s lock so far.
239+
/// </summary>
240+
public static extern long LockContentionCount
241+
{
242+
[MethodImpl(MethodImplOptions.InternalCall)]
243+
get;
244+
}
236245
}
237246
}

src/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,36 @@ public static void GetAvailableThreads(out int workerThreads, out int completion
231231
GetAvailableThreadsNative(out workerThreads, out completionPortThreads);
232232
}
233233

234+
/// <summary>
235+
/// Gets the number of thread pool threads that currently exist.
236+
/// </summary>
237+
/// <remarks>
238+
/// For a thread pool implementation that may have different types of threads, the count includes all types.
239+
/// </remarks>
240+
public static extern int ThreadCount
241+
{
242+
[MethodImpl(MethodImplOptions.InternalCall)]
243+
get;
244+
}
245+
246+
/// <summary>
247+
/// Gets the number of work items that have been processed so far.
248+
/// </summary>
249+
/// <remarks>
250+
/// For a thread pool implementation that may have different types of work items, the count includes all types.
251+
/// </remarks>
252+
public static extern long CompletedWorkItemCount
253+
{
254+
[MethodImpl(MethodImplOptions.InternalCall)]
255+
get;
256+
}
257+
258+
private static extern long PendingUnmanagedWorkItemCount
259+
{
260+
[MethodImpl(MethodImplOptions.InternalCall)]
261+
get;
262+
}
263+
234264
private static RegisteredWaitHandle RegisterWaitForSingleObject( // throws RegisterWaitException
235265
WaitHandle waitObject,
236266
WaitOrTimerCallback callBack,

src/classlibnative/bcltype/objectnative.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,3 +355,9 @@ FCIMPL1(FC_BOOL_RET, ObjectNative::IsLockHeld, Object* pThisUNSAFE)
355355
}
356356
FCIMPLEND
357357

358+
FCIMPL0(INT64, ObjectNative::GetMonitorLockContentionCount)
359+
{
360+
FCALL_CONTRACT;
361+
return (INT64)Thread::GetTotalMonitorLockContentionCount();
362+
}
363+
FCIMPLEND

src/classlibnative/bcltype/objectnative.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ class ObjectNative
3838
static FCDECL1(void, Pulse, Object* pThisUNSAFE);
3939
static FCDECL1(void, PulseAll, Object* pThisUNSAFE);
4040
static FCDECL1(FC_BOOL_RET, IsLockHeld, Object* pThisUNSAFE);
41+
static FCDECL0(INT64, GetMonitorLockContentionCount);
4142
};
4243

4344
#endif // _OBJECTNATIVE_H_

src/vm/comthreadpool.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,30 @@ FCIMPL2(VOID, ThreadPoolNative::CorGetAvailableThreads,DWORD* workerThreads, DWO
181181
}
182182
FCIMPLEND
183183

184+
/*****************************************************************************************************/
185+
FCIMPL0(INT32, ThreadPoolNative::GetThreadCount)
186+
{
187+
FCALL_CONTRACT;
188+
return ThreadpoolMgr::GetThreadCount();
189+
}
190+
FCIMPLEND
191+
192+
/*****************************************************************************************************/
193+
FCIMPL0(INT64, ThreadPoolNative::GetCompletedWorkItemCount)
194+
{
195+
FCALL_CONTRACT;
196+
return (INT64)Thread::GetTotalThreadPoolCompletionCount();
197+
}
198+
FCIMPLEND
199+
200+
/*****************************************************************************************************/
201+
FCIMPL0(INT64, ThreadPoolNative::GetPendingUnmanagedWorkItemCount)
202+
{
203+
FCALL_CONTRACT;
204+
return PerAppDomainTPCountList::GetUnmanagedTPCount()->GetNumRequests();
205+
}
206+
FCIMPLEND
207+
184208
/*****************************************************************************************************/
185209

186210
FCIMPL0(VOID, ThreadPoolNative::NotifyRequestProgress)

src/vm/comthreadpool.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ class ThreadPoolNative
2828
static FCDECL2(FC_BOOL_RET, CorSetMinThreads, DWORD workerThreads, DWORD completionPortThreads);
2929
static FCDECL2(VOID, CorGetMinThreads, DWORD* workerThreads, DWORD* completionPortThreads);
3030
static FCDECL2(VOID, CorGetAvailableThreads, DWORD* workerThreads, DWORD* completionPortThreads);
31+
static FCDECL0(INT32, GetThreadCount);
32+
static FCDECL0(INT64, GetCompletedWorkItemCount);
33+
static FCDECL0(INT64, GetPendingUnmanagedWorkItemCount);
3134

3235
static FCDECL0(VOID, NotifyRequestProgress);
3336
static FCDECL0(FC_BOOL_RET, NotifyRequestComplete);

src/vm/ecalllist.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,9 @@ FCFuncStart(gThreadPoolFuncs)
678678
FCFuncElement("GetAvailableThreadsNative", ThreadPoolNative::CorGetAvailableThreads)
679679
FCFuncElement("SetMinThreadsNative", ThreadPoolNative::CorSetMinThreads)
680680
FCFuncElement("GetMinThreadsNative", ThreadPoolNative::CorGetMinThreads)
681+
FCFuncElement("get_ThreadCount", ThreadPoolNative::GetThreadCount)
682+
FCFuncElement("get_CompletedWorkItemCount", ThreadPoolNative::GetCompletedWorkItemCount)
683+
FCFuncElement("get_PendingUnmanagedWorkItemCount", ThreadPoolNative::GetPendingUnmanagedWorkItemCount)
681684
FCFuncElement("RegisterWaitForSingleObjectNative", ThreadPoolNative::CorRegisterWaitForSingleObject)
682685
FCFuncElement("BindIOCompletionCallbackNative", ThreadPoolNative::CorBindIoCompletionCallback)
683686
FCFuncElement("SetMaxThreadsNative", ThreadPoolNative::CorSetMaxThreads)
@@ -915,6 +918,7 @@ FCFuncStart(gMonitorFuncs)
915918
FCFuncElement("ObjPulse", ObjectNative::Pulse)
916919
FCFuncElement("ObjPulseAll", ObjectNative::PulseAll)
917920
FCFuncElement("IsEnteredNative", ObjectNative::IsLockHeld)
921+
FCFuncElement("get_LockContentionCount", ObjectNative::GetMonitorLockContentionCount)
918922
FCFuncEnd()
919923

920924
FCFuncStart(gOverlappedFuncs)

src/vm/syncblk.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2575,6 +2575,7 @@ BOOL AwareLock::EnterEpilogHelper(Thread* pCurThread, INT32 timeOut)
25752575
FireEtwContentionStart_V1(ETW::ContentionLog::ContentionStructs::ManagedContention, GetClrInstanceId());
25762576

25772577
LogContention();
2578+
Thread::IncrementMonitorLockContentionCount(pCurThread);
25782579

25792580
OBJECTREF obj = GetOwningObject();
25802581

src/vm/threadpoolrequest.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,12 @@ class UnManagedPerAppDomainTPCount : public IPerAppDomainTPCount {
221221
_ASSERT(FALSE);
222222
}
223223

224+
inline ULONG GetNumRequests()
225+
{
226+
LIMITED_METHOD_CONTRACT;
227+
return VolatileLoad(&m_NumRequests);
228+
}
229+
224230
private:
225231
SpinLock m_lock;
226232
ULONG m_NumRequests;

0 commit comments

Comments
 (0)