Threading and Synchronization Primitives module

Cnx provides a set of cross-platform multithreading and synchronization facilities to make working with concurrent code as simple and straightforward as possible. The API is similar to C++'s and that added by the C11 standard, but is supported on platforms and compilers where implementations of C11's threading may not be available (like Windows, for example).

Example:

    #include <Cnx/Thread.h>
    #include <Cnx/IO.h>
    #include <Cnx/Atomic.h>
    #include <Cnx/Assert.h>
   
    // A lambda that captures a `CnxBasicMutex*`, two `i32*`s and an `atomic_bool`*
    void LambdaFunction(example_print, void) {
        let bindings = LambdaBinding(CnxBasicMutex*, i32*, i32*, atomic_bool*);
   
        // run until the exit flag is set
        let exit_flag_ptr = bindings._4;
        while(!atomic_load(exit_flag_ptr)) {
            let_mut mutex_ptr = bindings._1;
            let locked = cnx_basic_mutex_lock(mutex_ptr);
   
            cnx_assert(cnx_result_is_ok(locked), "Failed to lock the mutex");
            if(cnx_result_is_ok(locked)) {
                let num1 = *(bindings._2);
                let num2 = *(bindings._3);
                println("Values: [{}, {}]", num1, num2);
            }
        }
    }
   
    // A lambda that captures a `CnxBasicMutex*`, two `i32*`s and an `atomic_bool`*
    void LambdaFunction(example_add, void) {
        let bindings = LambdaBinding(CnxBasicMutex*, i32*, i32*, atomic_bool*);
   
        // run until the exit flag is set
        let exit_flag_ptr = bindings._4;
        while(!atomic_load(exit_flag_ptr)) {
            let_mut mutex_ptr = bindings._1;
            let locked = cnx_basic_mutex_lock(mutex_ptr);
//
            cnx_assert(cnx_result_is_ok(locked), "Failed to lock the mutex");
            if(cnx_result_is_ok(locked)) {
                let_mut num1_ptr = bindings._2;
                let_mut num2_ptr = bindings._3;
                (*(num1_ptr))++;
                (*(num2_ptr))++;
            }
        }
    }
   
    // Spawns one thread that continuosly prints two `i32`s for 60 seconds.
    // Spawns a second thread that continuously increments said `i32`s.
    // After 60 seconds, joins both threads.
    void example(void) {
        i32 val1 = 0;
        i32 val2 = 0;
        atomic_bool exit_flag = false;
   
        let_mut maybe_mutex = cnx_basic_mutex_new();
   
        cnx_assert(cnx_result_is_ok(maybe_mutex), "Failed to create a new mutex");
        let_mut mutex = cnx_result_unwrap(maybe_mutex);
   
        // creates a new thread whose startup routine is the `LambdaFunction` `example_print`
        // the lambda will be automatically freed when the thread completes execution
        let_mut maybe_print_thread = cnx_thread_new(lambda(example_print, &mutex,
                                                                          &val1,
                                                                          &val2,
                                                                          &exit_flag));
        cnx_assert(cnx_result_is_ok(maybe_print_thread), "Failed to create print thread");
        let_mut print_thread = cnx_result_unwrap(maybe_print_thread);
   
        // creates a new thread whose startup routine is the `LambdaFunction` `example_add`
        // the lambda will be automatically freed when the thread completes execution
        let_mut maybe_add_thread = cnx_thread_new(lambda(example_add, &mutex,
                                                                      &val1,
                                                                      &val2,
                                                                      &exit_flag));
        cnx_assert(cnx_result_is_ok(maybe_add_thread), "Failed to create add thread");
        let_mut add_thread = cnx_result_unwrap(maybe_add_thread);
   
        cnx_this_thread_sleep_for(cnx_seconds(60));
   
        atomic_store(&exit_flag, true);
        let print_joined = cnx_thread_join(&print_thread);
        let add_joined = cnx_thread_join(&add_thread);
        cnx_assert(cnx_result_is_ok(print_joined), "Print thread failed to join");
        cnx_assert(cnx_result_is_ok(add_joined), "Adding thread failed to join");
   
        cnx_basic_mutex_free(&mutex);
   
        println("Finshed printing the numbers!");
    }

Classes

struct CnxJThread
A CnxJThread is the handle type for OS-level threads which are automatically joined when their handle goes out of scope and use a dedicated stop token to signal to the thread when its execution should end.

Typedefs

using CnxBasicMutex = __cnx_basic_mutex
CnxBasicMutex is the basic mutual exclusion object type provided by Cnx's threading and synchronization API. Using a CnxBasicMutex to control access to shared resources will ensure that all access is synchronized and mutually exclusive between threads.
using CnxRecursiveBasicMutex = __cnx_recursive_basic_mutex
CnxRecursiveBasicMutex is the basic reentrant mutual exclusion object type provided by Cnx's threading and synchronization API. Using a CnxRecursiveBasicMutex to control access to shared resources will ensure that all access is synchronized and mutually exclusive between threads, and allows for rentrant ownership of the lock on that access.
using CnxBasicCondvar = __cnx_condvar
CnxBasicCondvar is the Condition Variable object type provided by Cnx's threading and synchronization API. Using a CnxBasicCondvar allows to block one or more threads until an event is triggered, without wasting CPU resources.
using CnxThreadID = __cnx_thread_id
A CnxThreadID uniquely identifies an indiviual thread.
using CnxThread = __cnx_thread
A CnxThread is the handle type for basic OS-level threads provided by Cnx's threading and synchronization API. CnxThread provides the facilities for creating multiple threads and ending their execution, enabling concurrent tasks and operations to be performed.
using CnxStopToken = atomic_bool
A CnxStopToken is associated with a CnxJThread and is used to signal to it when it should end its execution.
using CnxTLSKey = __cnx_tls_key
A CnxTLSKey is the key type for creating, accessing, and modifying a thread-local storage object.
using CnxOnceFlag = __cnx_exec_once_flag
A CnxOnceFlag is a synchronization flag type for use with cnx_execute_once to ensure a given function is executed exactly once, regardless of how many threads attempt to call it, as long as all threads do so through the same CnxOnceFlag and cnx_execute_once

Functions

CnxResult(CnxBasicMutex)
Creates a new mutex.
CnxResult cnx_basic_mutex_init(CnxBasicMutex*restrict mutex)
Initializes the mutex pointed to by mutex
CnxResult cnx_basic_mutex_lock(CnxBasicMutex*restrict mutex)
Unconditionally locks the mutex pointed to by mutex
bool cnx_basic_mutex_try_lock(CnxBasicMutex*restrict mutex)
Attempts to lock the mutex pointed to by mutex
CnxResult cnx_basic_mutex_unlock(CnxBasicMutex*restrict mutex)
Unlocks the mutex pointed to by mutex
CnxResult cnx_basic_mutex_free(CnxBasicMutex*restrict mutex)
Destroys the mutex pointed to by mutex
CnxResult(CnxRecursiveBasicMutex)
Creates a new recursive mutex.
CnxResult cnx_recursive_basic_mutex_init(CnxRecursiveBasicMutex*restrict mutex)
Initializes the recursive mutex pointed to by mutex
CnxResult cnx_recursive_basic_mutex_lock(CnxRecursiveBasicMutex*restrict mutex)
Unconditionally locks the recursive mutex pointed to by mutex
bool cnx_recursive_basic_mutex_try_lock(CnxRecursiveBasicMutex*restrict mutex)
Attempts to lock the recursive mutex pointed to by mutex
CnxResult cnx_recursive_basic_mutex_unlock(CnxRecursiveBasicMutex*restrict mutex)
Unlocks the recursive mutex pointed to by mutex
CnxResult cnx_recursive_basic_mutex_free(CnxRecursiveBasicMutex*restrict mutex)
Destroys the recursive mutex pointed to by mutex
CnxResult(CnxBasicCondvar)
Creates a new condition variable, returning the result in a CnxResult
CnxResult cnx_basic_condvar_init(CnxBasicCondvar*restrict condvar)
Initializes the condition variable pointed to by condvar
CnxResult cnx_basic_condvar_signal(CnxBasicCondvar*restrict condvar)
Signals to the first thread waiting on the condition variable pointed to by condvar to wake and continue execution.
CnxResult cnx_basic_condvar_broadcast(CnxBasicCondvar*restrict condvar)
Signals to every thread waiting on the condition variable pointed to by condvar to wake and continue execution.
CnxResult cnx_basic_condvar_wait(CnxBasicCondvar*restrict condvar, CnxBasicMutex*restrict mutex)
Blocks on the condition variable pointed to by condvar until the thread is signalled by it.
CnxResult cnx_basic_condvar_wait_for(CnxBasicCondvar*restrict condvar, CnxBasicMutex*restrict mutex, CnxDuration to_wait) cnx_disable_if(!mutex
Blocks on the condition variable pointed to by condvar until the thread is signalled by it, or to_wait time has elapsed.
CnxResult cnx_basic_condvar_wait_until(CnxBasicCondvar*restrict condvar, CnxBasicMutex*restrict mutex, CnxTimePoint stop_point) cnx_disable_if(!mutex
Blocks on the condition variable pointed to by condvar until the thread is signalled by it, or the point in time indicated by stop_point time has been reached.
CnxResult cnx_basic_condvar_free(CnxBasicCondvar*restrict condvar)
Destroys the condition variable pointed to by condvar
CnxResult cnx_execute_once(CnxOnceFlag*restrict flag, void(*)(void) function) cnx_disable_if(!flag
Executes the given function exactly once.
CnxCompare cnx_thread_id_compare(CnxThreadID lhs, CnxThreadID rhs)
Compares the two CnxThreadIDs.
bool cnx_thread_id_equal(CnxThreadID lhs, CnxThreadID rhs)
Returns whether the two CnxThreadIDs are equal.
bool cnx_thread_id_less_than(CnxThreadID lhs, CnxThreadID rhs)
Returns whether the first CnxThreadID is less than the second.
bool cnx_thread_id_less_than_or_equal(CnxThreadID lhs, CnxThreadID rhs)
Returns whether the first CnxThreadID is less than or equal to the second.
bool cnx_thread_id_greater_than(CnxThreadID lhs, CnxThreadID rhs)
Returns whether the first CnxThreadID is greater than the second.
bool cnx_thread_id_greater_than_or_equal(CnxThreadID lhs, CnxThreadID rhs)
Returns whether the first CnxThreadID is greater than or equal to the second.
typedef Lambda(void) CnxThreadLambda
When spawning a new thread, a Lambda(void) is used as its startup routine. This typedef allows for receiving that startup routine as a function parameter.
CnxResult(CnxThread)
Spawns a new thread, with the given CnxThreadLambda as its startup routine.
CnxResult cnx_thread_init(CnxThread*restrict thread, CnxThreadLambda lambda)
Spawns a new thread, with the given CnxThreadLambda as its startup routine.
bool cnx_thread_is_null(const CnxThread*restrict thread)
Checks if the given thread handle is null (if it has been initialized)
CnxThreadID cnx_thread_get_id(const CnxThread*restrict thread)
Gets the ID of the current thread.
CnxResult cnx_thread_join(CnxThread*restrict thread)
Joins the given thread, blocking until its execution has completed.
CnxResult cnx_thread_detach(CnxThread*restrict thread)
Separates execution of the thread associated with the given thread handle from the handle.
void cnx_thread_free(void* thread)
Frees the given thread, blocking until it's joined or joining fails.
void cnx_this_thread_yield(void)
Yields execution of the current thread, allowing the operating system to execute other threads until it decides to return execution to this one.
void cnx_this_thread_sleep_for(CnxDuration duration)
Yields execution of the current thread until at least the given duration of time has passed.
CnxThreadID cnx_this_thread_get_id(void)
Returns the ID of the current thread.
void cnx_stop_token_request_stop(CnxStopToken*restrict token)
Requests the thread associated with the given stop token to end execution.
bool cnx_stop_token_stop_requested(const CnxStopToken*restrict token)
Returns whether the thread associated with the given stop token has been requested to end execution.
typedef Lambda(void, const CnxStopToken*) CnxJThreadLambda
When spawning a new CnxJThread, a Lambda(void, const CnxStopToken*) is used as its startup routine. This typedef allows for receiving that startup routine as a function parameter.
CnxResult(CnxJThread)
Spawns a new CnxJThread, with the given CnxJThreadLambda as its startup routine.
CnxResult cnx_jthread_init(CnxJThread*restrict thread, CnxJThreadLambda lambda)
Spawns a new CnxJThread, with the given CnxJThreadLambda as its startup routine.
CnxResult cnx_jthread_join(CnxJThread*restrict thread)
Joins the given CnxJThread, blocking until its execution has completed.
void cnx_jthread_free(void* thread)
Frees the given CnxJThread, blocking until it's joined or joining fails.
CnxResult(CnxTLSKey) cnx_tls_new(void *data
Creates a new thread-local storage.
CnxResult cnx_tls_init(CnxTLSKey*restrict key, void* data, void(__CNX_TLS_DESTRUCTOR_TAG*destructor)(void*))
Initializes a thread-local storage and associates it with the key pointed to by key
void* cnx_tls_get(CnxTLSKey key)
Retrieves the current value of the thread-local storage associated with key
CnxResult cnx_tls_set(CnxTLSKey key, void* data)
Sets the value of the thread-local storage associated with key to data

Defines

#define thread_local
A variable declared as thread_local is local to the current thread and guaranteed to not be accessible by other threads.
#define cnx_jthread_is_null(thread)
Checks if the given thread handle is null (if it has been initialized)
#define cnx_jthread_get_id(thread)
Gets the ID of the current thread.
#define cnx_jthread_detach(thread)
Separates execution of the thread associated with the CnxJThread thread handle from the handle.

Typedef documentation

typedef __cnx_basic_mutex CnxBasicMutex

CnxBasicMutex is the basic mutual exclusion object type provided by Cnx's threading and synchronization API. Using a CnxBasicMutex to control access to shared resources will ensure that all access is synchronized and mutually exclusive between threads.

typedef __cnx_recursive_basic_mutex CnxRecursiveBasicMutex

CnxRecursiveBasicMutex is the basic reentrant mutual exclusion object type provided by Cnx's threading and synchronization API. Using a CnxRecursiveBasicMutex to control access to shared resources will ensure that all access is synchronized and mutually exclusive between threads, and allows for rentrant ownership of the lock on that access.

typedef __cnx_condvar CnxBasicCondvar

CnxBasicCondvar is the Condition Variable object type provided by Cnx's threading and synchronization API. Using a CnxBasicCondvar allows to block one or more threads until an event is triggered, without wasting CPU resources.

typedef __cnx_thread_id CnxThreadID

A CnxThreadID uniquely identifies an indiviual thread.

typedef __cnx_thread CnxThread

A CnxThread is the handle type for basic OS-level threads provided by Cnx's threading and synchronization API. CnxThread provides the facilities for creating multiple threads and ending their execution, enabling concurrent tasks and operations to be performed.

typedef atomic_bool CnxStopToken

A CnxStopToken is associated with a CnxJThread and is used to signal to it when it should end its execution.

typedef __cnx_tls_key CnxTLSKey

A CnxTLSKey is the key type for creating, accessing, and modifying a thread-local storage object.

typedef __cnx_exec_once_flag CnxOnceFlag

A CnxOnceFlag is a synchronization flag type for use with cnx_execute_once to ensure a given function is executed exactly once, regardless of how many threads attempt to call it, as long as all threads do so through the same CnxOnceFlag and cnx_execute_once

Function documentation

CnxResult(CnxBasicMutex)

Creates a new mutex.

Returns A CnxBasicMutex if successful

Creating a mutex can fail, depending on memory and operating system level constraints. If creating the mutex is successful, returns a CnxBasicMutex, otherwise returns an error.

CnxResult cnx_basic_mutex_init(CnxBasicMutex*restrict mutex)

Initializes the mutex pointed to by mutex

Parameters
mutex - The mutex to initalize
Returns Ok if successful

Initializing a mutex can fail, depending on memory and operating system level constraints. If initializing the mutex is successful, returns Ok, otherwise returns an error.

CnxResult cnx_basic_mutex_lock(CnxBasicMutex*restrict mutex)

Unconditionally locks the mutex pointed to by mutex

Parameters
mutex - The mutex to lock
Returns Ok if successful

Locking a mutex can fail (for example, if it's already locked on the calling thread). If locking the mutex is successful, returns Ok, otherwise returns an error.

bool cnx_basic_mutex_try_lock(CnxBasicMutex*restrict mutex)

Attempts to lock the mutex pointed to by mutex

Parameters
mutex - The mutex to lock
Returns true if successful

If locking the mutex is successful, return true.

CnxResult cnx_basic_mutex_unlock(CnxBasicMutex*restrict mutex)

Unlocks the mutex pointed to by mutex

Parameters
mutex - The mutex to unlock
Returns Ok if successful

Unlocking a mutex can fail (for example, if it's already unlocked). If unlocking the mutex is successful, returns Ok, otherwise returns an error.

CnxResult cnx_basic_mutex_free(CnxBasicMutex*restrict mutex)

Destroys the mutex pointed to by mutex

Parameters
mutex - The mutex to free
Returns Ok if successful

Destroying a mutex can fail (for example, it it's still in use by other threads). If destroying the mutex is successful, returns Ok, otherwise returns an error.

CnxResult(CnxRecursiveBasicMutex)

Creates a new recursive mutex.

Returns A CnxRecursiveBasicMutex if successful

Creating a mutex can fail, depending on memory and operating system level constraints. If creating the recursive mutex is successful, returns a CnxRecursiveBasicMutex, otherwise returns an error.

CnxResult cnx_recursive_basic_mutex_init(CnxRecursiveBasicMutex*restrict mutex)

Initializes the recursive mutex pointed to by mutex

Parameters
mutex - The mutex to initialize
Returns Ok if successful

Initializing a mutex can fail, depending on memory and operating system level constraints. If initializing the mutex is successful, returns Ok, otherwise returns an error.

CnxResult cnx_recursive_basic_mutex_lock(CnxRecursiveBasicMutex*restrict mutex)

Unconditionally locks the recursive mutex pointed to by mutex

Parameters
mutex - The mutex to lock
Returns Ok if successful

Locking a mutex can fail. If locking the recursive mutex is successful, returns Ok, otherwise returns an error.

bool cnx_recursive_basic_mutex_try_lock(CnxRecursiveBasicMutex*restrict mutex)

Attempts to lock the recursive mutex pointed to by mutex

Parameters
mutex - The mutex to lock
Returns true if successful

If locking the recursive mutex is successful, return true.

CnxResult cnx_recursive_basic_mutex_unlock(CnxRecursiveBasicMutex*restrict mutex)

Unlocks the recursive mutex pointed to by mutex

Parameters
mutex - The mutex to unlock
Returns Ok if successful

Unlocking a mutex can fail (for example, if it's already unlocked). If unlocking the recursive mutex is successful, returns Ok, otherwise returns an error.

CnxResult cnx_recursive_basic_mutex_free(CnxRecursiveBasicMutex*restrict mutex)

Destroys the recursive mutex pointed to by mutex

Parameters
mutex - The mutex to free
Returns Ok if successful

Destroying a mutex can fail (for example, it it's still in use by other threads). If destroying the recursive mutex is successful, returns Ok, otherwise returns an error.

CnxResult(CnxBasicCondvar)

Creates a new condition variable, returning the result in a CnxResult

Returns A CnxRecursiveBasicMutex if successful

Creating a condition variable can fail, depending on memory and operating system level constraints. If creating the condition variable is successful, returns a CnxBasicCondvar, otherwise returns an error

CnxResult cnx_basic_condvar_init(CnxBasicCondvar*restrict condvar)

Initializes the condition variable pointed to by condvar

Parameters
condvar - The condvar to initialize
Returns Ok if successful

Initializing a condition variable can fail, depending on memory and operating system level constraints. If initializing the condition variable is successful, returns Ok, otherwise returns an error

CnxResult cnx_basic_condvar_signal(CnxBasicCondvar*restrict condvar)

Signals to the first thread waiting on the condition variable pointed to by condvar to wake and continue execution.

Parameters
condvar - The condvar to signal on
Returns Ok if successful

Signalling with a condition variable can fail. If signalling with the condition variable is successful, returns Ok, otherwise returns an error.

CnxResult cnx_basic_condvar_broadcast(CnxBasicCondvar*restrict condvar)

Signals to every thread waiting on the condition variable pointed to by condvar to wake and continue execution.

Parameters
condvar - The condvar to broadcast on
Returns Ok if successful

Signalling with a condition variable can fail. If signalling with the condition variable is successful, returns Ok, otherwise returns an error.

CnxResult cnx_basic_condvar_wait(CnxBasicCondvar*restrict condvar, CnxBasicMutex*restrict mutex)

Blocks on the condition variable pointed to by condvar until the thread is signalled by it.

Parameters
condvar - The condvar to wait on
mutex - The mutex associated with the condvar
Returns Ok if successful

Unlocks the mutex pointed to by mutex and blocks on the condition variable pointed to by condvar until the thread is signalled by it. The mutex is re-locked again before the function returns. The mutex must be locked by the calling thread prior to calling this.

Waiting on a condition variable can fail. If waiting on the condition variable is successful, returns Ok, otherwise returns an error.

CnxResult cnx_basic_condvar_wait_for(CnxBasicCondvar*restrict condvar, CnxBasicMutex*restrict mutex, CnxDuration to_wait) cnx_disable_if(!mutex

Blocks on the condition variable pointed to by condvar until the thread is signalled by it, or to_wait time has elapsed.

Parameters
condvar - The condvar to wait on
mutex - The mutex associated with the condvar
to_wait - The amount of time to wait on the condvar
Returns Ok if successful

Unlocks the mutex pointed to by mutex and blocks on the condition variable pointed to by condvar until the thread is signalled by it, or to_wait time has elapsed. The mutex is re-locked again before the function returns. The mutex must be locked by the calling thread prior to calling this.

Waiting on a condition variable can fail. Returns Ok if a signal is received from the condition variable before to_wait has elapsed, otherwise returns an error.

CnxResult cnx_basic_condvar_wait_until(CnxBasicCondvar*restrict condvar, CnxBasicMutex*restrict mutex, CnxTimePoint stop_point) cnx_disable_if(!mutex

Blocks on the condition variable pointed to by condvar until the thread is signalled by it, or the point in time indicated by stop_point time has been reached.

Parameters
condvar - The condvar to wait on
mutex - The mutex associated with the condvar
stop_point - The point in time to stop waiting on the condvar
Returns Ok if successful

Unlocks the mutex pointed to by mutex and blocks on the condition variable pointed to by condvar until the thread is signalled by it, or the time indicated by stop_point has been reached. The mutex is re-locked again before the function returns. The mutex must be locked by the calling thread prior to calling this.

Waiting on a condition variable can fail. Returns Ok if a signal is received from the condition variable before to_wait has elapsed, otherwise returns an error.

CnxResult cnx_basic_condvar_free(CnxBasicCondvar*restrict condvar)

Destroys the condition variable pointed to by condvar

Parameters
condvar - The condvar to free
Returns Ok if successful

Destroying a condition variable can fail (for example, if its still in use by another thread). If destroying the condition variable is successful, returns Ok, otherwise, returns an error.

CnxResult cnx_execute_once(CnxOnceFlag*restrict flag, void(*)(void) function) cnx_disable_if(!flag

Executes the given function exactly once.

Parameters
flag - The execute once flag associated with function's execution
function - The function to execute exactly once
Returns Ok if successful

Executes the given function exactly once, regardless how many threads attempt to execute it, or if they attempt to execute it multiple times, as long as all attempts to call it go through cnx_execute_once using the same CnxOnceFlag

Executing the function can fail (for example, on Windows). If execution is successful, return Ok, otherwise returns an error.

CnxCompare cnx_thread_id_compare(CnxThreadID lhs, CnxThreadID rhs)

Compares the two CnxThreadIDs.

Parameters
lhs - The id to compare
rhs - The id to compare lhs to
Returns A CnxCompare indicating how lhs compares to rhs

bool cnx_thread_id_equal(CnxThreadID lhs, CnxThreadID rhs)

Returns whether the two CnxThreadIDs are equal.

Parameters
lhs - The id to compare
rhs - The id to compare lhs to
Returns whether the two IDs are equal

bool cnx_thread_id_less_than(CnxThreadID lhs, CnxThreadID rhs)

Returns whether the first CnxThreadID is less than the second.

Parameters
lhs - The id to compare
rhs - The id to compare lhs to
Returns whether lhs is less than rhs

bool cnx_thread_id_less_than_or_equal(CnxThreadID lhs, CnxThreadID rhs)

Returns whether the first CnxThreadID is less than or equal to the second.

Parameters
lhs - The id to compare
rhs - The id to compare lhs to
Returns whether lhs is less than or equal to rhs

bool cnx_thread_id_greater_than(CnxThreadID lhs, CnxThreadID rhs)

Returns whether the first CnxThreadID is greater than the second.

Parameters
lhs - The id to compare
rhs - The id to compare lhs to
Returns whether lhs is greater than rhs

bool cnx_thread_id_greater_than_or_equal(CnxThreadID lhs, CnxThreadID rhs)

Returns whether the first CnxThreadID is greater than or equal to the second.

Parameters
lhs - The id to compare
rhs - The id to compare lhs to
Returns whether lhs is greater than or equal to rhs

typedef Lambda(void) CnxThreadLambda

When spawning a new thread, a Lambda(void) is used as its startup routine. This typedef allows for receiving that startup routine as a function parameter.

CnxResult(CnxThread)

Spawns a new thread, with the given CnxThreadLambda as its startup routine.

Returns A CnxThread on success

Spawns a new thread, with the given CnxThreadLambda as its startup routine. The given lambda will be automatically freed after the thread's execution completes.

Spawning a thread can fail due to memory or operating system level constraints. If spawning the thread is successful, returns a handle to the spawned thread. Otherwise, returns an error.

CnxResult cnx_thread_init(CnxThread*restrict thread, CnxThreadLambda lambda)

Spawns a new thread, with the given CnxThreadLambda as its startup routine.

Parameters
thread - The pointer to thread handle to set as the handle to the spawned thread
lambda - The lambda to invoke as the spawned thread's startup routine
Returns Ok on success

Spawns a new thread, with the given CnxThreadLambda as its startup routine. The given lambda will be automatically freed after the thread's execution completes.

Spawning a thread can fail due to memory or operating system level constraints. If spawning the thread is successful, the CnxThread pointed to by thread will be initialized with the handle to the spawned thread, and Ok will be returned. Otherwise, returns an error.

bool cnx_thread_is_null(const CnxThread*restrict thread)

Checks if the given thread handle is null (if it has been initialized)

Parameters
thread - The thread to check if is null
Returns whether the thread is null

CnxThreadID cnx_thread_get_id(const CnxThread*restrict thread)

Gets the ID of the current thread.

Parameters
thread - The thread to get the ID of
Returns The ID of the given thread

CnxResult cnx_thread_join(CnxThread*restrict thread)

Joins the given thread, blocking until its execution has completed.

Parameters
thread - The thread to join
Returns Ok if successful

Joining a thread can fail. Returns Ok if joining the thread is successful. Otherwise, returns an error.

CnxResult cnx_thread_detach(CnxThread*restrict thread)

Separates execution of the thread associated with the given thread handle from the handle.

Parameters
thread - The thread to detach
Returns Ok if successful

Separates execution of the thread associated with the given handle from that handle. When successfully detached, the associated thread will continue execution independently and will no longer be associated with any handle.

Detaching a thread can fail. Returns Ok if detaching the thread is successful. Otherwise, returns an error.

void cnx_thread_free(void* thread)

Frees the given thread, blocking until it's joined or joining fails.

Parameters
thread - The thread to free

void cnx_this_thread_yield(void)

Yields execution of the current thread, allowing the operating system to execute other threads until it decides to return execution to this one.

void cnx_this_thread_sleep_for(CnxDuration duration)

Yields execution of the current thread until at least the given duration of time has passed.

Parameters
duration - The minimum amount of time to yield execution for

CnxThreadID cnx_this_thread_get_id(void)

Returns the ID of the current thread.

Returns The ID of the current thread

void cnx_stop_token_request_stop(CnxStopToken*restrict token)

Requests the thread associated with the given stop token to end execution.

Parameters
token - The token associated with the thread to end

bool cnx_stop_token_stop_requested(const CnxStopToken*restrict token)

Returns whether the thread associated with the given stop token has been requested to end execution.

Parameters
token - The token associated with the thread to end
Returns whether the thread has been requested to end

typedef Lambda(void, const CnxStopToken*) CnxJThreadLambda

When spawning a new CnxJThread, a Lambda(void, const CnxStopToken*) is used as its startup routine. This typedef allows for receiving that startup routine as a function parameter.

CnxResult(CnxJThread)

Spawns a new CnxJThread, with the given CnxJThreadLambda as its startup routine.

Returns A CnxJThread on success

Spawns a new thread, with the given CnxJThreadLambda as its startup routine. The given lambda will be automatically freed after the thread's execution completes.

Spawning a thread can fail due to memory or operating system level constraints. If spawning the thread is successful, returns a handle to the spawned thread. Otherwise, returns an error.

CnxResult cnx_jthread_init(CnxJThread*restrict thread, CnxJThreadLambda lambda)

Spawns a new CnxJThread, with the given CnxJThreadLambda as its startup routine.

Parameters
thread - The pointer to thread handle to set as the handle to the spawned thread
lambda - The lambda to invoke as the spawned thread's startup routine
Returns Ok on success

Spawns a new thread, with the given CnxJThreadLambda as its startup routine. The given lambda will be automatically freed after the thread's execution completes.

Spawning a thread can fail due to memory or operating system level constraints. If spawning the thread is successful, the CnxThread pointed to by thread will be initialized with the handle to the spawned thread, and Ok will be returned. Otherwise, returns an error.

CnxResult cnx_jthread_join(CnxJThread*restrict thread)

Joins the given CnxJThread, blocking until its execution has completed.

Parameters
thread - The thread to join
Returns Ok if successful

Joining a thread can fail. Returns Ok if joining the thread is successful. Otherwise, returns an error.

void cnx_jthread_free(void* thread)

Frees the given CnxJThread, blocking until it's joined or joining fails.

Parameters
thread - The thread to free

CnxResult(CnxTLSKey) cnx_tls_new(void *data

Creates a new thread-local storage.

Returns A CnxTLSKey on success

Creates a new thread-local storage that will be destroyed by the given destructor function at thread exit.

Creating a thread-local storage can fail. If creation is successful, the storage will be set to data and the CnxTLSKey associated with the storage will be returned. Otherwise, an error will be returned.

CnxResult cnx_tls_init(CnxTLSKey*restrict key, void* data, void(__CNX_TLS_DESTRUCTOR_TAG*destructor)(void*))

Initializes a thread-local storage and associates it with the key pointed to by key

Parameters
key
data - The value to initialize the TLS to
Returns A CnxTLSKey on success

Initializes a new thread-local storage that will be destroyed by the given destructor function at thread exit, and associates the thread-local storage with the key pointed to by key.

Creating a thread-local storage can fail. If creation is successful, the storage will be set to data and the CnxTLSKey associated with the storage will be returned. Otherwise, an error will be returned.

void* cnx_tls_get(CnxTLSKey key)

Retrieves the current value of the thread-local storage associated with key

Parameters
key - The thread-local storage key to get the currently associated value of
Returns The current value of the TLS, or nullptr

If key is a key to a valid TLS, returns the current value of the TLS, otherwise returns a nullptr

CnxResult cnx_tls_set(CnxTLSKey key, void* data)

Sets the value of the thread-local storage associated with key to data

Parameters
key - The thread-local storage key to set the associated value of
data - The value to set the TLS to
Returns Ok on successful

Setting the value of a thread-local storage can fail. If setting the thread-local storage is successful, returns Ok. Otherwise, an error is returned

Define documentation

#define thread_local

A variable declared as thread_local is local to the current thread and guaranteed to not be accessible by other threads.

#define cnx_jthread_is_null(thread)

Checks if the given thread handle is null (if it has been initialized)

Parameters
thread - The thread to check if is null
Returns whether the thread is null

#define cnx_jthread_get_id(thread)

Gets the ID of the current thread.

Parameters
thread - The thread to get the ID of
Returns The ID of the given thread

#define cnx_jthread_detach(thread)

Separates execution of the thread associated with the CnxJThread thread handle from the handle.

Parameters
thread - The thread to detach
Returns Ok if successful

Separates execution of the thread associated with the given handle from that handle. When successfully detached, the associated thread will continue execution independently and will no longer be associated with any handle.

Detaching a thread can fail. Returns Ok if detaching the thread is successful. Otherwise, returns an error.