26 if (!threadManager->
init()) {
28 delete(threadManager);
202 for (uint32 n=0; n<header->
count; n++) {
377 if (!
mutex.enter()) {
395 for (uint32 n=0; n<header->
count; n++) {
397 allStats[p++] = *stats;
399 if (p >= count)
break;
410 unsigned char *newData = (
unsigned char*) malloc(size);
411 memset(newData, 0, size);
414 header->
count = newCount;
423 memset(newData+statsOffset, 0, 10*2*(
sizeof(uint64)));
428 memset(firstStat, 0, newCount*
sizeof(
ThreadStats));
434 memcpy(newData+statsOffset,
data+statsOffset, 10*2*(
sizeof(uint64)));
459 if ( (stats == NULL) || (stats->
created == 0) ) {
464 uint32 timeOffset =
sizeof(stats->
id) +
sizeof(stats->
created) +
sizeof(stats->
status)
466 uint32 cpuUsageOffset = timeOffset + 10 *
sizeof(uint64);
474 memcpy(stats+timeOffset, stats+timeOffset+
sizeof(uint64), 9*
sizeof(uint64));
476 memcpy(stats+cpuUsageOffset, stats+cpuUsageOffset+
sizeof(uint64), 9*
sizeof(uint64));
482 header->time[0] = stats->
time[0];
509 for (uint32 n=0; n<header->
count; n++) {
510 if (stats->
osID == osID) {
534 sigemptyset(&cancel);
535 sigaddset(&cancel, SIGQUIT);
536 pthread_sigmask(SIG_UNBLOCK, &cancel, NULL);
551 uint32 timeOffset =
sizeof(stats->
id) +
sizeof(stats->
created) +
sizeof(stats->
status)
553 uint32 cpuUsageOffset = timeOffset + 10 *
sizeof(uint64);
561 uint32 headerTimeOffset =
sizeof(header->size)+
sizeof(header->count)+
sizeof(header->activeCount)+
sizeof(header->statColPolicy);
562 uint32 headerCPUUsageOffset = headerTimeOffset + 10 *
sizeof(uint64);
565 uint32 interval = 1000000, n;
567 uint64 lastCalc = 0, t;
569 if ( (t =
GetTimeNow()) - lastCalc > interval ) {
572 if (!
mutex.enter()) {
584 for (n=0; n<header->count; n++) {
596 memcpy(header+headerTimeOffset, header+headerTimeOffset+
sizeof(uint64), 9*
sizeof(uint64));
598 memcpy(header+headerCPUUsageOffset, header+headerCPUUsageOffset+
sizeof(uint64), 9*
sizeof(uint64));
600 header->time[0] = header->currentCPUTicks[0] = 0;
606 for (n=0; n<header->count; n++) {
609 memcpy(stats+timeOffset, stats+timeOffset+
sizeof(uint64), 9*
sizeof(uint64));
611 memcpy(stats+cpuUsageOffset, stats+cpuUsageOffset+
sizeof(uint64), 9*
sizeof(uint64));
630 memcpy(header+headerTimeOffset, header+headerTimeOffset+
sizeof(uint64), 9*
sizeof(uint64));
632 memcpy(header+headerCPUUsageOffset, header+headerCPUUsageOffset+
sizeof(uint64), 9*
sizeof(uint64));
635 header->currentCPUTicks[0] = sumUsage;
659 uint32 finishedCount;
660 TMTestState() : total(0), finishedCount(0) {}
665static const uint64 TM_ITERATIONS_PER_THREAD = 200000ULL;
668 TMTestState* state = (TMTestState*)arg;
670 for (uint64 i = 0; i < TM_ITERATIONS_PER_THREAD; i++)
672 if (state->mutex.enter()) {
673 state->total += localSum;
674 state->finishedCount++;
675 state->mutex.leave();
687 unittest::fail(
"ThreadManager test: CreateThreadManager() failed");
691 const uint32 NUMTHREADS = 8;
693 uint32 ids[NUMTHREADS];
694 for (uint32 n = 0; n < NUMTHREADS; n++)
701 for (uint32 n = 0; n < NUMTHREADS; n++) {
703 unittest::fail(
"ThreadManager test: CreateThread() failed for worker %u", n);
710 if (started != NUMTHREADS) {
711 unittest::fail(
"ThreadManager test: started %u of %u threads", started, NUMTHREADS);
720 const uint32 TIMEOUTMS = 5000;
722 bool allFinished =
false;
723 while (waitedMs < TIMEOUTMS) {
725 if (state.mutex.
enter()) {
726 done = state.finishedCount;
729 if (done >= NUMTHREADS) {
738 unittest::fail(
"ThreadManager test: only %u of %u workers finished within %ums",
739 state.finishedCount, NUMTHREADS, TIMEOUTMS);
746 uint64 expected = (uint64)NUMTHREADS * TM_ITERATIONS_PER_THREAD;
747 if (state.total != expected) {
748 unittest::fail(
"ThreadManager test: total work %llu != expected %llu",
749 state.total, expected);
753 if (state.finishedCount != NUMTHREADS) {
755 state.finishedCount, NUMTHREADS);
763 unittest::fail(
"ThreadManager test: GetThreadStats returned no creation time for id %u", ids[0]);
770 unittest::metric(
"threads_per_sec", (
double)NUMTHREADS / us * 1e6,
"threads/s",
true);
787 "Thread manager create/run/join of multiple worker threads",
"core");
CMSDK time: µs-resolution 64-bit timestamps and the Time Mapping Constant (TMC).
Process-wide thread registry and lifecycle manager: the concurrency core of CMSDK.
Small, dependency-free unit test harness used by all CMSDK object tests.
Cross-platform utility toolbox for CMSDK: threading, synchronization, shared memory,...
#define thread_ret_val(ret)
#define THREAD_STATS_ADHOC
#define THREAD_TERMINATED
#define THREAD_FUNCTION_CALL
THREAD_RET(* THREAD_FUNCTION)(void *)
#define THREAD_INTERRUPTED
bool isRunning
Set by the worker while its loop is active.
virtual bool stop(uint32 timeout=200)
Ask the worker loop to finish and wait for it to do so.
bool shouldContinue
Loop-continuation flag; cleared by stop().
Singleton registry that creates, controls and profiles all CMSDK threads.
bool createThread(THREAD_FUNCTION func, void *args, uint32 &newID, uint32 reqID)
Instance-side worker for CreateThread(); requires and manages the mutex internally.
bool resizeThreadStorage(uint32 newCount)
Grow the storage block to hold newCount slots.
bool addLocalThreadStats()
Instance-side worker for AddLocalThreadStats() (pthreads self-reporting).
static bool CreateThreadManager()
Create and initialise the singleton (including its monitoring thread).
bool pauseThread(uint32 id)
Instance-side worker for PauseThread().
bool getLocalThreadID(uint32 &id)
Instance-side worker for GetLocalThreadID(): linear scan of slots for the calling thread's OS ID.
static bool GetLocalThreadID(uint32 &id)
Look up the manager slot ID of the calling thread.
bool continueThread(uint32 id)
Instance-side worker for ContinueThread().
ThreadManager()
Construct the manager and pre-allocate storage for 1024 thread slots. Prefer CreateThreadManager().
bool interruptThread(uint32 id)
Instance-side worker for InterruptThread().
ThreadStats * getAllThreadStats(uint32 &count)
Instance-side worker for GetAllThreadStats(): allocates and fills a snapshot array (caller frees with...
static bool InterruptThread(uint32 id)
Kill the thread and restart it from its original entry function.
~ThreadManager()
Destructor: shuts down all threads, frees the storage block and clears the singleton pointer.
bool shutdown()
Stop the monitoring loop and forcibly terminate all remaining threads.
utils::Mutex mutex
Single lock serialising all registry and statistics access.
static ThreadStats GetLocalThreadStats()
Get a copy of the statistics record for the calling thread.
bool terminateThread(uint32 id)
Instance-side worker for TerminateThread().
static ThreadStats GetThreadStats(uint32 id)
Get a copy of the statistics record for a specific thread.
static bool CreateThread(THREAD_FUNCTION func, void *args, uint32 &newID, uint32 reqID=0)
Create a new native thread and start it immediately.
int32 threadMonitoring()
Body of the monitoring thread (slot 0).
unsigned char * data
Contiguous storage block: ThreadDataHeader + bitfield + ThreadStats[].
ThreadStats getThreadStats(uint32 id)
Instance-side worker for GetThreadStats(): copies the slot under the mutex.
static ThreadManager * Singleton
The process-wide instance; NULL until CreateThreadManager() (or any lazy static call) runs.
static bool UnitTest()
Self-contained unit test (create/run/join worker threads, verify stats).
static bool IsThreadRunning(uint32 id)
Check whether the thread is still alive at the OS level.
bool init()
Second-phase init: registers this instance as the singleton and starts the monitoring thread in slot ...
static bool TerminateThread(uint32 id)
Forcibly terminate the thread and release its slot.
static bool AddLocalThreadStats()
Sample and record CPU statistics for the calling thread itself.
static bool Shutdown()
Terminate all managed threads, then destroy the singleton.
static ThreadStats * GetAllThreadStats(uint32 &count)
Snapshot the statistics of all live threads.
static bool PauseThread(uint32 id)
Suspend the thread at whatever point it is currently executing.
static bool ContinueThread(uint32 id)
Resume a thread previously suspended with PauseThread().
bool isThreadRunning(uint32 id)
Instance-side worker for IsThreadRunning().
static UnitTestRunner & instance()
Access the singleton (created on first use).
void registerTest(const char *name, UnitTestFunc func, const char *description="", const char *category="", bool inDefaultRun=true)
Register a test with the runner.
Recursive mutual-exclusion lock, optionally named for cross-process use.
bool leave()
Release the mutex.
bool enter()
Block until the mutex is acquired.
uint64 GetTimeNow()
Return the current absolute time (µs since year 0) according to the TMC.
std::string PrintTimeDifString(uint64 t, bool us=true, bool ms=true)
int64 GetTimeAge(uint64 t)
Age of a timestamp relative to now.
static THREAD_RET THREAD_FUNCTION_CALL ThreadMonitoring(void *arg)
Entry function of the monitoring thread; delegates to ThreadManager::threadMonitoring().
#define GETTHREADSTATS(data, id)
Compute the address of thread slot id inside storage block data.
uint32 GetThreadStatColAbility()
Report this platform's capability for per-thread CPU statistics collection.
bool Sleep(uint32 ms)
Suspend the calling thread.
bool PauseThread(ThreadHandle hThread)
Suspend a thread's execution.
bool IsThreadRunning(ThreadHandle hThread)
Check whether a thread is still alive.
bool CreateThread(THREAD_FUNCTION func, void *args, ThreadHandle &thread, uint32 &osID)
Start a new OS thread.
bool TerminateThread(ThreadHandle hThread)
Forcibly kill a thread.
bool GetCurrentThreadUniqueID(uint32 &tid)
Get a process-unique id for the calling thread.
bool CheckForThreadFinished(ThreadHandle hThread)
Non-blocking check whether a thread has terminated.
bool ContinueThread(ThreadHandle hThread)
Resume a thread paused with PauseThread().
bool GetFirstFreeBitLoc(const char *bitfield, uint32 bytesize, uint32 &loc)
Find the first 0 (free) bit.
bool GetCPUTicks(ThreadHandle hThread, uint64 &ticks)
Get accumulated CPU time of a specific thread.
bool SetBit(uint32 loc, bit value, char *bitfield, uint32 bytesize)
Set bit loc to value.
uint32 Calc32BitFieldSize(uint32 bitsize)
Compute the byte size needed for a bitfield of bitsize bits, rounded up to a 32-bit boundary.
void fail(const char *fmt,...)
Set an explanatory reason shown on the FAIL line.
void metric(const char *name, double value, const char *unit="", bool higherIsBetter=true)
Record a performance metric.
void detail(const char *fmt,...)
Verbose-only indented diagnostic line (shown only when verbose=1).
void progress(int percent, const char *action)
Report progress with a short description of the current action.
void Register_ThreadManager_Tests()
Bookkeeping record for a single managed thread.
uint32 status
Lifecycle state (THREAD_NONE/INIT/RUNNING/PAUSED/INTERRUPTED/TERMINATED).
ThreadHandle hThread
Native handle (HANDLE on Windows, pthread_t wrapper on POSIX).
void * args
Argument passed to func, retained for restart.
uint64 currentCPUTicks[10]
Rolling CPU-tick readings matching time (newest first).
uint64 time[10]
Rolling timestamps of the last 10 statistics samples (newest first).
uint32 id
ThreadManager-assigned slot ID (index into the stats array).
uint32 osID
Operating-system thread ID (as reported by the OS, not the slot ID).
THREAD_FUNCTION func
Entry function, retained so InterruptThread() can restart the thread.
uint64 created
Creation time (microseconds, GetTimeNow()); 0 = slot unused/invalid.