CXThread

From cxwiki

The CXThread class enables the creation of additional native preemptively-scheduled threads of execution. Each process starts with a singular Main Thread (which is NOT backed by a CXThread object) and may start additional threads by creating CXThread objects and starting them executing. The CXThread object must persist until after the thread in question has terminated; an attempt to destroy the CXThread object will block against completion of the thread. Each CXThread object controls a maximum of one native thread.
 
// Construct a CXThread object, providing a (non-unique) label for debugging purposes.
CXThread(const CXString& debugName);

// Start the thread executing.
virtual void Start(void);

// Request that the thread exit, and block until it does.
virtual void Stop(void);

// Request that the thread exit, but does not block.
void Stopping(void);

// Returns whether the thread was executing at the time of the call. Beware of race conditions.
bool IsRunning(void) const;

// Determines whether anybody has requested this thread to stop.
bool WantToExit(void) const;

 

Usage

Deriving from CXThread

A common usage is for an object to derive from CXThread in order to gain its own threaded functionality. The thread may be started at the end of the constructor, from some post-initialisation method, or even on demand in response to other methods being called.

// When creating a CXThread-derived object, override this function to implement the actual threaded functionality.
virtual void Proc(void) = 0;

In this scenario, it is important for the derived object's destructor to Stop() the thread before destroying any other state, to avoid having the thread function attempt to use destroyed state.

Calling a Lambda Function

Alternatively, for cases where a single function should be run in a thread and there is no need for additional state, a CXThread object can be created for a given lambda function. By default, this thread starts execution immediately, however this can be postponed or prevented entirely.

 

static CXThread* CallFunctionInNewThread(cx_function<void (void)> function, const CXString& debugName, bool bShouldStartThread = true);

In this scenario, the caller is responsible for managing the lifetime of the CXThread object, including delete it when done.

Restrictions

It is considered an error to delete or Stop() a CXThread object from within its thread.

All CXThread threads must have terminated before the Main Thread's main function returns. Actual CXThread objects may persist and be destroyed during global termination.

It is not safe to forcibly terminate a thread in C++. As a result, no such mechanism is provided by cxsource. The native platform APIs sometimes provide a function which appears to do this, however there are inevitably serious flaws with such a capability:

  1. If a thread is terminated at a "random" moment, then it may hold locks which are never released. This will result in deadlocks (normally) or resource starvation (cases where the lock is probed rather than blocked against). These locks may be in library code, including the standard library, and thus even if stack unwinding was to occur it could not fix the scenario. In addition to locks, the same problem occurs with other resources that may leak- pointers, file handles, sockets, etc. In the worst case, the console output or the memory allocators may be holding internal locks, causing deadlocks on any attempt to allocate memory or write logs.
  2. If a thread is terminated without c++ stack unwinding, which it typically is, then the above is also true of application code.
  3. If we assume that the thread will only be terminated when it is in a known state, then we can simply implement a custom shutdown signal which the thread checks when the it enters that known state. Thus, there is no need to forcibly terminate the thread and risk making a mistake.

Identification

Each currently-executing native thread, including the Main Thread and threads created outside of cxsource, has a native-assigned ThreadID which may be queried. This is unique at any given instant. CXThreads which are not currently executing do not have valid ThreadIDs.

// Returns the ThreadID of this CXThread.
ThreadID GetThreadID(void) const;

// Returns the ThreadID of the currently executing thread.
static ThreadID GetCurrentThreadID(void);

 

For threads created using CXThread, the CXThread* object may also be queried.

// Returns the currently executing thread.
static CXThread* __nullable GetCurrentThread(void);

 

All CXThread objects have debug names assigned at construction. Other types of thread may have pseudo-names or may have manually registered names.

static const char* GetThreadDebugName(CXThreadID threadID);

 

The Main Thread is considered special for a number of reasons. It can be identified.

static CXThreadID GetMainThreadID(void);
static bool IsCurrentThreadMainThread(void);