26 if (!threadManager->
init()) {
28 delete(threadManager);
215 bool anyRunning =
true;
219 for (uint32 n=0; n<header->
count; n++, stats++) {
232 for (uint32 n=0; n<header->
count; n++) {
420 if (!
mutex.enter()) {
438 for (uint32 n=0; n<header->
count; n++) {
440 allStats[p++] = *stats;
442 if (p >= count)
break;
453 unsigned char *newData = (
unsigned char*) malloc(size);
454 memset(newData, 0, size);
457 header->
count = newCount;
466 memset(newData+statsOffset, 0, 10*2*(
sizeof(uint64)));
471 memset(firstStat, 0, newCount*
sizeof(
ThreadStats));
477 memcpy(newData+statsOffset,
data+statsOffset, 10*2*(
sizeof(uint64)));
502 if ( (stats == NULL) || (stats->
created == 0) ) {
507 uint32 timeOffset =
sizeof(stats->
id) +
sizeof(stats->
created) +
sizeof(stats->
status)
509 uint32 cpuUsageOffset = timeOffset + 10 *
sizeof(uint64);
517 memcpy(stats+timeOffset, stats+timeOffset+
sizeof(uint64), 9*
sizeof(uint64));
519 memcpy(stats+cpuUsageOffset, stats+cpuUsageOffset+
sizeof(uint64), 9*
sizeof(uint64));
525 header->time[0] = stats->
time[0];
552 for (uint32 n=0; n<header->
count; n++) {
553 if (stats->
osID == osID) {
577 sigemptyset(&cancel);
578 sigaddset(&cancel, SIGQUIT);
579 pthread_sigmask(SIG_UNBLOCK, &cancel, NULL);
594 uint32 timeOffset =
sizeof(stats->
id) +
sizeof(stats->
created) +
sizeof(stats->
status)
596 uint32 cpuUsageOffset = timeOffset + 10 *
sizeof(uint64);
604 uint32 headerTimeOffset =
sizeof(header->size)+
sizeof(header->count)+
sizeof(header->activeCount)+
sizeof(header->statColPolicy);
605 uint32 headerCPUUsageOffset = headerTimeOffset + 10 *
sizeof(uint64);
608 uint32 interval = 1000000, n;
610 uint64 lastCalc = 0, t;
612 if ( (t =
GetTimeNow()) - lastCalc > interval ) {
615 if (!
mutex.enter()) {
627 for (n=0; n<header->count; n++) {
639 memcpy(header+headerTimeOffset, header+headerTimeOffset+
sizeof(uint64), 9*
sizeof(uint64));
641 memcpy(header+headerCPUUsageOffset, header+headerCPUUsageOffset+
sizeof(uint64), 9*
sizeof(uint64));
643 header->time[0] = header->currentCPUTicks[0] = 0;
649 for (n=0; n<header->count; n++) {
652 memcpy(stats+timeOffset, stats+timeOffset+
sizeof(uint64), 9*
sizeof(uint64));
654 memcpy(stats+cpuUsageOffset, stats+cpuUsageOffset+
sizeof(uint64), 9*
sizeof(uint64));
673 memcpy(header+headerTimeOffset, header+headerTimeOffset+
sizeof(uint64), 9*
sizeof(uint64));
675 memcpy(header+headerCPUUsageOffset, header+headerCPUUsageOffset+
sizeof(uint64), 9*
sizeof(uint64));
678 header->currentCPUTicks[0] = sumUsage;
702 uint32 finishedCount;
703 TMTestState() : total(0), finishedCount(0) {}
708static const uint64 TM_ITERATIONS_PER_THREAD = 200000ULL;
711 TMTestState* state = (TMTestState*)arg;
713 for (uint64 i = 0; i < TM_ITERATIONS_PER_THREAD; i++)
715 if (state->mutex.enter()) {
716 state->total += localSum;
717 state->finishedCount++;
718 state->mutex.leave();
730 unittest::fail(
"ThreadManager test: CreateThreadManager() failed");
734 const uint32 NUMTHREADS = 8;
736 uint32 ids[NUMTHREADS];
737 for (uint32 n = 0; n < NUMTHREADS; n++)
744 for (uint32 n = 0; n < NUMTHREADS; n++) {
746 unittest::fail(
"ThreadManager test: CreateThread() failed for worker %u", n);
753 if (started != NUMTHREADS) {
754 unittest::fail(
"ThreadManager test: started %u of %u threads", started, NUMTHREADS);
763 const uint32 TIMEOUTMS = 5000;
765 bool allFinished =
false;
766 while (waitedMs < TIMEOUTMS) {
768 if (state.mutex.
enter()) {
769 done = state.finishedCount;
772 if (done >= NUMTHREADS) {
781 unittest::fail(
"ThreadManager test: only %u of %u workers finished within %ums",
782 state.finishedCount, NUMTHREADS, TIMEOUTMS);
789 uint64 expected = (uint64)NUMTHREADS * TM_ITERATIONS_PER_THREAD;
790 if (state.total != expected) {
791 unittest::fail(
"ThreadManager test: total work %llu != expected %llu",
792 state.total, expected);
796 if (state.finishedCount != NUMTHREADS) {
798 state.finishedCount, NUMTHREADS);
806 unittest::fail(
"ThreadManager test: GetThreadStats returned no creation time for id %u", ids[0]);
813 unittest::metric(
"threads_per_sec", (
double)NUMTHREADS / us * 1e6,
"threads/s",
true);
830 "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)
int32 GetTimeAgeMS(uint64 t)
Age of a timestamp relative to now, in milliseconds.
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 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.
bool TryReapThread(ThreadHandle &hThread)
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.