CMSDK 2.0.1
Cross-platform C++ base library and SDK for the Psyclone AIOS platform
Loading...
Searching...
No Matches
cmlabs::ThreadManager Class Reference

Singleton registry that creates, controls and profiles all CMSDK threads. More...

#include <ThreadManager.h>

Inheritance diagram for cmlabs::ThreadManager:
[legend]
Collaboration diagram for cmlabs::ThreadManager:
[legend]

Public Member Functions

 ThreadManager ()
 Construct the manager and pre-allocate storage for 1024 thread slots. Prefer CreateThreadManager().
 ~ThreadManager ()
 Destructor: shuts down all threads, frees the storage block and clears the singleton pointer.
bool init ()
 Second-phase init: registers this instance as the singleton and starts the monitoring thread in slot 0.
bool shutdown ()
 Stop the monitoring loop and forcibly terminate all remaining threads.
int32 threadMonitoring ()
 Body of the monitoring thread (slot 0).
Public Member Functions inherited from cmlabs::Runnable
 Runnable ()
 Initialise flags: not running, allowed to continue.
virtual ~Runnable ()
 Destructor requests a stop (with the default timeout) before destruction proceeds.
virtual bool stop (uint32 timeout=200)
 Ask the worker loop to finish and wait for it to do so.

Static Public Member Functions

static bool CreateThreadManager ()
 Create and initialise the singleton (including its monitoring thread).
static bool CreateThread (THREAD_FUNCTION func, void *args, uint32 &newID, uint32 reqID=0)
 Create a new native thread and start it immediately.
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().
static bool InterruptThread (uint32 id)
 Kill the thread and restart it from its original entry function.
static bool TerminateThread (uint32 id)
 Forcibly terminate the thread and release its slot.
static bool IsThreadRunning (uint32 id)
 Check whether the thread is still alive at the OS level.
static bool JoinThread (uint32 id)
 Wait for a thread to have COMPLETELY finished, then release it - exactly once.
static bool GetThreadHandle (uint32 id, ThreadHandle &out)
 Copy out the OS-level handle of the thread in a manager slot.
static bool AddLocalThreadStats ()
 Sample and record CPU statistics for the calling thread itself.
static bool GetLocalThreadID (uint32 &id)
 Look up the manager slot ID of the calling thread.
static ThreadStats GetLocalThreadStats ()
 Get a copy of the statistics record for the calling thread.
static ThreadStats GetThreadStats (uint32 id)
 Get a copy of the statistics record for a specific thread.
static ThreadStatsGetAllThreadStats (uint32 &count)
 Snapshot the statistics of all live threads.
static bool Shutdown ()
 Terminate all managed threads, then destroy the singleton.
static bool UnitTest ()
 Self-contained unit test (create/run/join worker threads, verify stats).
static bool UnitTestSlotRecycling ()
 Churn MORE thread lifetimes than the registry has slots, in one process.

Static Public Attributes

static ThreadManagerSingleton = NULL
 The process-wide instance; NULL until CreateThreadManager() (or any lazy static call) runs.

Protected Member Functions

bool createThread (THREAD_FUNCTION func, void *args, uint32 &newID, uint32 reqID)
 Instance-side worker for CreateThread(); requires and manages the mutex internally.
bool pauseThread (uint32 id)
 Instance-side worker for PauseThread().
bool continueThread (uint32 id)
 Instance-side worker for ContinueThread().
bool interruptThread (uint32 id)
 Instance-side worker for InterruptThread().
bool terminateThread (uint32 id)
 Instance-side worker for TerminateThread().
bool isThreadRunning (uint32 id)
 Instance-side worker for IsThreadRunning().
bool joinThread (uint32 id)
 Instance-side worker for JoinThread().
void finaliseSlot (ThreadStats *stats, ThreadDataHeader *header)
 Mark a slot terminated and RELEASE it back to the allocation bitfield.
bool getThreadHandle (uint32 id, ThreadHandle &out)
 Instance-side worker for GetThreadHandle().
bool addLocalThreadStats ()
 Instance-side worker for AddLocalThreadStats() (pthreads self-reporting).
bool getLocalThreadID (uint32 &id)
 Instance-side worker for GetLocalThreadID(): linear scan of slots for the calling thread's OS ID.
ThreadStats getThreadStats (uint32 id)
 Instance-side worker for GetThreadStats(): copies the slot under the mutex.
ThreadStatsgetAllThreadStats (uint32 &count)
 Instance-side worker for GetAllThreadStats(): allocates and fills a snapshot array (caller frees with delete[]).
bool resizeThreadStorage (uint32 newCount)
 Grow the storage block to hold newCount slots.

Protected Attributes

utils::Mutex mutex
 Single lock serialising all registry and statistics access.
unsigned char * data
 Contiguous storage block: ThreadDataHeader + bitfield + ThreadStats[].
Protected Attributes inherited from cmlabs::Runnable
uint32 threadID
 ThreadManager slot ID of the worker thread (0 until known).
bool shouldContinue
 Loop-continuation flag; cleared by stop().
bool isRunning
 Set by the worker while its loop is active.

Detailed Description

Singleton registry that creates, controls and profiles all CMSDK threads.

All public functionality is exposed as static methods that lazily create the singleton on first use, so callers never need to construct one explicitly. Internally every operation takes the single manager mutex, serialising thread creation/termination and statistics access.

The manager also runs its own monitoring thread (slot 0, see threadMonitoring()) which samples per-thread CPU usage about once per second and detects threads that have finished on their own.

Warning
ThreadManager hands out raw preemptive threads. Any state shared between them - including a one-shot crank function running concurrently for several trigger firings - must be protected by the caller (e.g. utils::Mutex); the manager only synchronises its own bookkeeping, never user data.
TerminateThread()/InterruptThread() kill threads asynchronously (Windows TerminateThread / pthread_cancel). A thread killed this way does not unwind: it can leak resources or leave locks held. Prefer cooperative shutdown (Runnable::stop()) whenever possible.

Definition at line 213 of file ThreadManager.h.

Constructor & Destructor Documentation

◆ ThreadManager()

cmlabs::ThreadManager::ThreadManager ( )

Construct the manager and pre-allocate storage for 1024 thread slots. Prefer CreateThreadManager().

Definition at line 207 of file ThreadManager.cpp.

References data, cmlabs::Runnable::isRunning, mutex, resizeThreadStorage(), and cmlabs::Runnable::shouldContinue.

Referenced by CreateThreadManager().

◆ ~ThreadManager()

cmlabs::ThreadManager::~ThreadManager ( )

Destructor: shuts down all threads, frees the storage block and clears the singleton pointer.

Definition at line 221 of file ThreadManager.cpp.

References data, mutex, shutdown(), and Singleton.

Member Function Documentation

◆ AddLocalThreadStats()

bool cmlabs::ThreadManager::AddLocalThreadStats ( )
static

Sample and record CPU statistics for the calling thread itself.

Returns
true if a sample was taken and stored.
Note
Needed on pthreads (THREAD_STATS_ADHOC policy), where one thread cannot read another thread's CPU usage; each thread must call this periodically to self-report. On Windows the central monitor samples all threads and this is unnecessary.

Definition at line 141 of file ThreadManager.cpp.

References CreateThreadManager(), and Singleton.

◆ addLocalThreadStats()

◆ ContinueThread()

bool cmlabs::ThreadManager::ContinueThread ( uint32 id)
static

Resume a thread previously suspended with PauseThread().

Parameters
idManager slot ID.
Returns
true if the OS resume succeeded (status back to THREAD_RUNNING).

Definition at line 83 of file ThreadManager.cpp.

References CreateThreadManager(), and Singleton.

◆ continueThread()

bool cmlabs::ThreadManager::continueThread ( uint32 id)
protected

◆ CreateThread()

bool cmlabs::ThreadManager::CreateThread ( THREAD_FUNCTION func,
void * args,
uint32 & newID,
uint32 reqID = 0 )
static

◆ createThread()

◆ CreateThreadManager()

bool cmlabs::ThreadManager::CreateThreadManager ( )
static

Create and initialise the singleton (including its monitoring thread).

Returns
true on success; false if initialisation failed (instance is deleted again).
Note
Called automatically by every other static method; explicit use is optional.

Definition at line 53 of file ThreadManager.cpp.

References init(), and ThreadManager().

Referenced by AddLocalThreadStats(), ContinueThread(), CreateThread(), GetAllThreadStats(), GetLocalThreadID(), GetLocalThreadStats(), GetThreadHandle(), GetThreadStats(), InterruptThread(), IsThreadRunning(), JoinThread(), PauseThread(), TerminateThread(), UnitTest(), and UnitTestSlotRecycling().

◆ finaliseSlot()

void cmlabs::ThreadManager::finaliseSlot ( ThreadStats * stats,
ThreadDataHeader * header )
protected

Mark a slot terminated and RELEASE it back to the allocation bitfield.

Parameters
statsSlot to finalise.
headerStorage header (activeCount owner).
Note
Caller MUST hold ::mutex, and both pointers must have been re-fetched after any release of it (the storage block can move).
Idempotent: does nothing if the slot is already THREAD_TERMINATED, since another terminate/join/monitor pass may have finalised it already.
Shared by terminateThread() and joinThread() deliberately. These were two copies of the same block, and the slot-release fix was added to only one of them - leaving the 1024-slot leak alive on the joinThread() path, which is the one every network teardown uses (and the ONLY reaper on macOS, where the monitor loop never runs). One copy, no drift.
Warning
The bit is only freed once ThreadStats::hThread is zero. Releasing it while the OS thread is still unwinding would let a concurrent createThread() hand out the slot and overwrite ThreadStats underneath the dying thread - trading a capacity bug for a use-after-free. If the handle is still set we keep the slot occupied and leak it, which is the old, safe behaviour.

Definition at line 602 of file ThreadManager.cpp.

References cmlabs::ThreadDataHeader::activeCount, cmlabs::ThreadDataHeader::bitFieldSize, BITFREE, cmlabs::ThreadStats::created, data, cmlabs::ThreadStats::func, cmlabs::ThreadStats::hThread, cmlabs::ThreadStats::id, cmlabs::utils::SetBit(), cmlabs::ThreadStats::status, and THREAD_TERMINATED.

Referenced by joinThread(), and terminateThread().

◆ GetAllThreadStats()

ThreadStats * cmlabs::ThreadManager::GetAllThreadStats ( uint32 & count)
static

Snapshot the statistics of all live threads.

Parameters
countReceives the number of records returned.
Returns
Newly allocated array of count ThreadStats, or NULL on failure.
Note
Caller owns the array and must release it with delete[].

Definition at line 185 of file ThreadManager.cpp.

References CreateThreadManager(), and Singleton.

◆ getAllThreadStats()

ThreadStats * cmlabs::ThreadManager::getAllThreadStats ( uint32 & count)
protected

Instance-side worker for GetAllThreadStats(): allocates and fills a snapshot array (caller frees with delete[]).

Definition at line 645 of file ThreadManager.cpp.

References cmlabs::ThreadDataHeader::activeCount, cmlabs::ThreadDataHeader::count, data, GETTHREADSTATS, mutex, cmlabs::ThreadStats::status, and THREAD_TERMINATED.

◆ GetLocalThreadID()

bool cmlabs::ThreadManager::GetLocalThreadID ( uint32 & id)
static

Look up the manager slot ID of the calling thread.

Parameters
idReceives the slot ID on success.
Returns
true if the calling thread's OS ID was found in the registry (i.e. it was created through ThreadManager).

Definition at line 150 of file ThreadManager.cpp.

References CreateThreadManager(), and Singleton.

◆ getLocalThreadID()

bool cmlabs::ThreadManager::getLocalThreadID ( uint32 & id)
protected

Instance-side worker for GetLocalThreadID(): linear scan of slots for the calling thread's OS ID.

Definition at line 755 of file ThreadManager.cpp.

References cmlabs::ThreadDataHeader::count, data, cmlabs::utils::GetCurrentThreadUniqueID(), GETTHREADSTATS, cmlabs::ThreadStats::id, mutex, and cmlabs::ThreadStats::osID.

Referenced by addLocalThreadStats(), and GetLocalThreadStats().

◆ GetLocalThreadStats()

ThreadStats cmlabs::ThreadManager::GetLocalThreadStats ( )
static

Get a copy of the statistics record for the calling thread.

Returns
ThreadStats snapshot; created == 0 signals "not found".

Definition at line 160 of file ThreadManager.cpp.

References cmlabs::ThreadStats::created, CreateThreadManager(), getLocalThreadID(), and Singleton.

◆ GetThreadHandle()

bool cmlabs::ThreadManager::GetThreadHandle ( uint32 id,
ThreadHandle & out )
static

Copy out the OS-level handle of the thread in a manager slot.

Parameters
idManager slot ID.
outReceives a by-value copy of the thread's ThreadHandle.
Returns
true if the slot holds a live handle; false for an unused or empty slot (out is left untouched in that case).
Note
Handles are copied out under the manager mutex; never a pointer into the registry, which may be reallocated as it grows.

Definition at line 131 of file ThreadManager.cpp.

References CreateThreadManager(), Singleton, and ThreadHandle.

Referenced by cmlabs::NetworkChannel::autoDetectConnection(), cmlabs::NetworkChannel::startConnection(), and cmlabs::NetworkChannel::startListener().

◆ getThreadHandle()

bool cmlabs::ThreadManager::getThreadHandle ( uint32 id,
ThreadHandle & out )
protected

Instance-side worker for GetThreadHandle().

See also
GetThreadHandle()

Definition at line 511 of file ThreadManager.cpp.

References data, GETTHREADSTATS, cmlabs::ThreadStats::hThread, mutex, and ThreadHandle.

◆ GetThreadStats()

ThreadStats cmlabs::ThreadManager::GetThreadStats ( uint32 id)
static

Get a copy of the statistics record for a specific thread.

Parameters
idManager slot ID.
Returns
ThreadStats snapshot; created == 0 signals "not found".

Definition at line 174 of file ThreadManager.cpp.

References cmlabs::ThreadStats::created, CreateThreadManager(), and Singleton.

Referenced by UnitTest().

◆ getThreadStats()

ThreadStats cmlabs::ThreadManager::getThreadStats ( uint32 id)
protected

Instance-side worker for GetThreadStats(): copies the slot under the mutex.

Definition at line 632 of file ThreadManager.cpp.

References cmlabs::ThreadStats::created, data, GETTHREADSTATS, and mutex.

◆ init()

bool cmlabs::ThreadManager::init ( )

Second-phase init: registers this instance as the singleton and starts the monitoring thread in slot 0.

Returns
true on success; on failure the caller should delete the instance.

Definition at line 234 of file ThreadManager.cpp.

References cmlabs::ThreadDataHeader::activeCount, createThread(), data, mutex, Singleton, and cmlabs::ThreadMonitoring().

Referenced by CreateThreadManager().

◆ InterruptThread()

bool cmlabs::ThreadManager::InterruptThread ( uint32 id)
static

Kill the thread and restart it from its original entry function.

Parameters
idManager slot ID.
Returns
Currently always false (see implementation); the restart itself is attempted regardless.
Warning
Forcible kill + restart: no stack unwinding, no cleanup in the old thread. Use only for genuinely stuck threads.

Definition at line 92 of file ThreadManager.cpp.

References CreateThreadManager(), and Singleton.

◆ interruptThread()

◆ IsThreadRunning()

bool cmlabs::ThreadManager::IsThreadRunning ( uint32 id)
static

Check whether the thread is still alive at the OS level.

Parameters
idManager slot ID.
Returns
true if the underlying OS thread is running; false if it exited, was terminated, or the slot has no handle.

Definition at line 111 of file ThreadManager.cpp.

References CreateThreadManager(), and Singleton.

Referenced by cmlabs::ConcRunThreaded(), cmlabs::StopProcessTestThread(), cmlabs::NetworkManager::TestHTTP(), and cmlabs::RequestGateway::~RequestGateway().

◆ isThreadRunning()

bool cmlabs::ThreadManager::isThreadRunning ( uint32 id)
protected

Instance-side worker for IsThreadRunning().

See also
IsThreadRunning()

Definition at line 495 of file ThreadManager.cpp.

References data, GETTHREADSTATS, cmlabs::ThreadStats::hThread, cmlabs::utils::IsThreadRunning(), and mutex.

◆ JoinThread()

bool cmlabs::ThreadManager::JoinThread ( uint32 id)
static

Wait for a thread to have COMPLETELY finished, then release it - exactly once.

Parameters
idManager slot ID.
Returns
true once the thread is gone (or the slot was already empty, or it is us).
Note
Takes exclusive ownership of the slot's handle (copies it out, zeroes the slot) BEFORE joining, so no other joiner - including the monitoring loop's TryReapThread() - can join the same pthread_t twice. Double-join is undefined behaviour and corrupts the heap.
The join runs with the registry mutex RELEASED: the target may be blocked in Mutex::enter() on that very lock, so joining under it deadlocks.
Unbounded by design. A bounded join that expires AFTER the slot handle has been zeroed would leave the thread joinable by nobody - a silent loss of the guarantee. Bound the COOPERATIVE phase before calling this instead.
Warning
Never joins the calling thread (a thread cannot observe its own termination); returns true immediately in that case.

Definition at line 121 of file ThreadManager.cpp.

References CreateThreadManager(), and Singleton.

Referenced by cmlabs::NetworkChannel::endConnection(), cmlabs::NetworkChannel::shutdown(), cmlabs::NetworkChannel::stopListener(), UnitTestSlotRecycling(), and cmlabs::TestRequestExecutor::~TestRequestExecutor().

◆ joinThread()

bool cmlabs::ThreadManager::joinThread ( uint32 id)
protected

◆ PauseThread()

bool cmlabs::ThreadManager::PauseThread ( uint32 id)
static

Suspend the thread at whatever point it is currently executing.

Parameters
idManager slot ID.
Returns
true if the OS suspend succeeded (status becomes THREAD_PAUSED).
Warning
Suspending a thread that holds a lock can deadlock the process; pause only threads whose code you control. Not supported by all pthreads platforms.

Definition at line 74 of file ThreadManager.cpp.

References CreateThreadManager(), and Singleton.

◆ pauseThread()

bool cmlabs::ThreadManager::pauseThread ( uint32 id)
protected

◆ resizeThreadStorage()

bool cmlabs::ThreadManager::resizeThreadStorage ( uint32 newCount)
protected

Grow the storage block to hold newCount slots.

Parameters
newCountNew slot capacity (createThread grows by 4x when full).
Returns
true on success.
Note
Allocates a fresh block, copies header/bitfield/stats across and frees the old one, so all raw ThreadStats pointers are invalidated; callers must hold the mutex.

Definition at line 664 of file ThreadManager.cpp.

References cmlabs::ThreadDataHeader::activeCount, cmlabs::ThreadDataHeader::bitFieldSize, cmlabs::utils::Calc32BitFieldSize(), cmlabs::ThreadDataHeader::count, data, cmlabs::utils::GetThreadStatColAbility(), GETTHREADSTATS, cmlabs::ThreadDataHeader::size, and cmlabs::ThreadDataHeader::statColPolicy.

Referenced by createThread(), and ThreadManager().

◆ Shutdown()

bool cmlabs::ThreadManager::Shutdown ( )
static

Terminate all managed threads, then destroy the singleton.

Returns
true (also when no manager existed).
Warning
Uses forcible termination for any thread still running; make sure workers have been asked to stop first.

Definition at line 194 of file ThreadManager.cpp.

References shutdown(), and Singleton.

Referenced by cmlabs::NetworkManager::UnitTest(), UnitTest(), cmlabs::NetworkManager::UnitTestDelayedConnect(), cmlabs::NetworkManager::UnitTestHTTP(), UnitTestSlotRecycling(), cmlabs::NetworkManager::WebsocketTest(), cmlabs::CleanShutdown::~CleanShutdown(), and cmlabs::MemoryManager::~MemoryManager().

◆ shutdown()

◆ TerminateThread()

bool cmlabs::ThreadManager::TerminateThread ( uint32 id)
static

Forcibly terminate the thread and release its slot.

Parameters
idManager slot ID.
Returns
true once the slot is marked THREAD_TERMINATED.
Warning
Asynchronous kill; the victim gets no chance to clean up.

Definition at line 101 of file ThreadManager.cpp.

References CreateThreadManager(), and Singleton.

Referenced by cmlabs::NetworkChannel::endConnection(), cmlabs::NetworkChannel::shutdown(), cmlabs::PsySpace::shutdown(), and cmlabs::NetworkChannel::stopListener().

◆ terminateThread()

bool cmlabs::ThreadManager::terminateThread ( uint32 id)
protected

◆ threadMonitoring()

int32 cmlabs::ThreadManager::threadMonitoring ( )

Body of the monitoring thread (slot 0).

Roughly once per second it either (a) under THREAD_STATS_ADHOC, rotates the process-wide summary buffers and reaps threads that finished, or (b) under central collection, samples every live thread's CPU ticks, sums them into the header and rotates all rolling windows. Runs until Runnable::stop() clears shouldContinue.

Returns
0 on clean exit (or when stats are disabled), -1 if the mutex could not be acquired.

Definition at line 804 of file ThreadManager.cpp.

References cmlabs::ThreadStats::args, cmlabs::ThreadStats::created, cmlabs::ThreadStats::currentCPUTicks, data, cmlabs::ThreadStats::func, cmlabs::utils::GetCPUTicks(), GETTHREADSTATS, cmlabs::GetTimeAge(), cmlabs::GetTimeNow(), cmlabs::ThreadStats::hThread, cmlabs::ThreadStats::id, cmlabs::Runnable::isRunning, LOG_PROCESS, LogPrint, mutex, cmlabs::ThreadStats::osID, cmlabs::PrintTimeDifString(), cmlabs::Runnable::shouldContinue, cmlabs::utils::Sleep(), cmlabs::ThreadStats::status, THREAD_STATS_ADHOC, THREAD_STATS_OFF, THREAD_TERMINATED, cmlabs::ThreadStats::time, and cmlabs::utils::TryReapThread().

◆ UnitTest()

bool cmlabs::ThreadManager::UnitTest ( )
static

◆ UnitTestSlotRecycling()

bool cmlabs::ThreadManager::UnitTestSlotRecycling ( )
static

Churn MORE thread lifetimes than the registry has slots, in one process.

Returns
true if creation kept working and slot ids were reused. Registered with the UnitTestRunner as "threadslots".
Note
Guards the registry slot leak: createThread() marks a slot occupied, and if nothing releases it, thread creation fails permanently after count lifetimes while activeCount still reports spare capacity. Every other suite test forks per run and creates a handful of threads, which is precisely why that went unnoticed.
Reaps via JoinThread() on purpose - the release was once implemented in terminateThread() only, so a terminateThread-based test would pass against the leak. JoinThread is also the path all NetworkChannel teardown uses, and the only reaper on macOS.

Definition at line 1066 of file ThreadManager.cpp.

References CreateThread(), CreateThreadManager(), cmlabs::unittest::detail(), cmlabs::unittest::fail(), JoinThread(), cmlabs::unittest::metric(), cmlabs::unittest::progress(), and Shutdown().

Referenced by cmlabs::Register_ThreadManager_Tests().

Member Data Documentation

◆ data

◆ mutex

◆ Singleton


The documentation for this class was generated from the following files: