Thread Synchronization Helpers
Overview
Conditional Directives
Type Definitions
Enumerations
Data Structures
Return Values and Error Codes
Known Limitations
Example Programs
Functions
Overview
The thread helpers are a set of data structures and standalone functions that give the same mutex, condition variable, and semaphore calls on every supported platform. Under WIN32 they call the Win32 API. Under UNIX they call POSIX threads.
These types and functions are not meant to be used directly. They are used by the gxMutex, gxCondition, and gxSemaphore classes. Applications should use those classes. The exception message functions are the main helpers that applications call directly, usually with the pointer returned by gxMutex::GetMutex().
Include file: thelpers.h. Source file: thelpers.cpp.
Conditional Directives
__WIN32__ - Use the Win32 API. Mutexes use critical sections (private) or mutex handles (shared). Condition variables are emulated with a manual-reset event.
__POSIX__ - Use POSIX threads (pthread_mutex_t and pthread_cond_t).
__HPUX10__ - With __POSIX__, use the HPUX 10.20 CMA (POSIX 1003.4 draft) interface. Shared mutexes and conditions are not supported.
__LINUX__ - With __POSIX__, mutexes are created as robust mutexes, and gxThreadMutexMakeConsistent() is enabled.
One of __WIN32__ or __POSIX__ must be defined or the header will not compile.
Type Definitions
gxMutexAttribute - Mutex attribute type. LPSECURITY_ATTRIBUTES under WIN32, cma_t_attr under HPUX 10, and pthread_mutexattr_t under POSIX.
gxConditionAttribute - Condition attribute type. LPSECURITY_ATTRIBUTES under WIN32, cma_t_attr under HPUX 10, and pthread_condattr_t under POSIX.
gxProcessType - Defined in thrtypes.h. gxPROCESS_PRIVATE (0) means the object is used only by threads of one process. gxPROCESS_SHARED (1) means the object can be shared by more than one process.
Enumerations
enum gxMutexError { // Mutex error codes
gxMUTEX_NO_ERROR = 0, // No errors reported
gxMUTEX_INVALID_CODE, // Invalid error code
gxMUTEX_ATTR_DESTROY_ERROR, // Error destroying attribute
gxMUTEX_ATTR_INIT_ERROR, // Error initializing the attribute
gxMUTEX_DESTROY_ERROR, // Error destroying mutex
gxMUTEX_INIT_ERROR, // Error initializing the mutex
gxMUTEX_LOCK_ERROR, // Error locking the mutex
gxMUTEX_SET_SHARE_ERROR, // Error setting shared attribute
gxMUTEX_TRY_LOCK_ERROR, // Error trying to lock the mutex
gxMUTEX_UNLOCK_ERROR, // Error unlocking the mutex
gxMUTEX_NOTRECOVERABLE_ERROR,
gxMUTEX_OWNERDEAD_ERROR,
gxMUTEX_BUSY_ERROR,
gxMUTEX_PERM_ERROR,
gxMUTEX_INVAL_ERROR
};
enum gxConditionError { // Condition variables error codes
gxCONDITION_NO_ERROR = 0, // No errors reported
gxCONDITION_INVALID_CODE, // Invalid error code
gxCONDITION_ATTR_DESTROY_ERROR, // Error destroying attribute
gxCONDITION_ATTR_INIT_ERROR, // Error initializing the attribute
gxCONDITION_BROADCAST_ERROR, // Error broadcasting
gxCONDITION_DESTROY_ERROR, // Error destroying condition
gxCONDITION_EXTERNAL_ERROR, // External mutex error
gxCONDITION_INIT_ERROR, // Error initializing condition
gxCONDITION_INTERNAL_ERROR, // Internal condition error
gxCONDITION_SET_SHARE_ERROR, // Error setting shared attribute
gxCONDITION_SIGNAL_ERROR, // Error signaling
gxCONDITION_TIMED_WAIT_ERROR, // Error during a timed waiting
gxCONDITION_WAIT_ERROR // Error waiting
};
enum gxSemaphoreError { // Semaphore error codes
gxSEMAPHORE_NO_ERROR = 0, // No errors reported
gxSEMAPHORE_INVALID_CODE, // Invalid error code
gxSEMAPHORE_DESTROY_ERROR, // Error destroying semaphore
gxSEMAPHORE_INIT_ERROR, // Error initializing the semaphore
gxSEMAPHORE_INTERNAL_ERROR, // Internal mutex error
gxSEMAPHORE_POST_ERROR, // Error posting
gxSEMAPHORE_WAIT_ERROR // Error waiting
};
Data Structures
All three structures have public members, a constructor that clears the error code and the is_initialized flag and sets the process type to private, and a private copy constructor and assignment operator. Each structure must be initialized with its init function before use and destroyed with its destroy function after use. A structure used by more than one process must be placed in memory that all the processes can reach.
// Data structure used to create shared and single process mutexs
struct gxMutex_t
{
public:
gxProcessType process_type; // Shared or private access
gxMutexError mutex_error; // Last error reported
gxMutexAttribute mutex_attribute; // Mutex attribute
int is_initialized; // True if initialized
int not_recoverable; // 10/24/2026: Set after ENOTRECOVERABLE
#if defined (__WIN32__)
// Mutex types that can be shared across multiple processes
HANDLE shared_mutex;
// Mutex type that can only be shared by threads of the same process
CRITICAL_SECTION mutex;
#elif defined (__POSIX__)
pthread_mutex_t mutex;
#endif
};
// Data structure used to create shared and single process condition variables
struct gxCondition_t
{
public:
gxProcessType process_type; // Shared or private access
gxConditionError condition_error; // Last error reported
gxConditionAttribute condition_attribute; // Condition attribute
int is_initialized; // True if initialized
#if defined (__WIN32__)
// Members needed to emulate condition variables under WIN32
int waiters_count; // Number of threads waiting
int release_count; // Number of threads to release
int wait_generation_count; // Current generation count
gxMutex_t waiters_count_lock; // Serialize access to generation count
HANDLE event; // Event used to block and release waiting threads
#elif defined (__POSIX__)
pthread_cond_t condition;
#endif
};
// Data structure used to create shared and single process semaphores
struct gxSemaphore_t
{
public:
gxProcessType process_type; // Shared or private access
gxSemaphoreError semaphore_error; // Last error reported
int is_initialized; // True if initialized
int value; // Value during operation
int value_after_operation; // Value after an operation
gxMutex_t mutex; // Lock for semaphore variables
gxCondition_t semaphore; // Condition used for wait operations
};
NOTE: 10/14/2026: Fixed gxThreadSemaphoreInit() so it sets gxSemaphore_t::process_type. The constructor now also sets value and value_after_operation to 1.
Return Values and Error Codes
All the int functions return 0 on success. On failure they return a non-zero value. Under POSIX this is usually the error code returned by the pthread call, such as EBUSY or ETIMEDOUT. Under WIN32, and for errors found by the helpers themselves, it is 1.
Each structure also has an error field (mutex_error, condition_error, or semaphore_error). A failing call stores an error code there. Use the matching exception message function to get a text message for it.
The error field holds the result of the last call. NOTE: 10/14/2026: Fixed the error fields so a successful call clears them. Before this fix the stored error was never cleared, and later condition, semaphore, and destroy calls returned 1 even when they worked. The return value of each call now reports only that call. The mutex error is cleared while the lock is held, and the semaphore error while the semaphore lock is held.
Known Limitations
These notes describe the current source code. Callers should know about them.
1. NOTE: 10/14/2026: Fixed gxThreadMutexInit() and gxThreadConditionInit() so they set is_initialized to true. The destroy functions now release the native objects, and a second init call is skipped.
2. NOTE: 10/14/2026: Fixed gxThreadConditionInit() so it creates shared (PTHREAD_PROCESS_SHARED) condition variables on Linux. Shared semaphores now work on Linux. The structure must be placed in memory that all the processes can reach.
3. NOTE: 10/14/2026: Fixed the WIN32 gxThreadConditionWait() and gxThreadConditionTimedWait() so they release the external mutex during the wait for private mutexes too. The WIN32 code was changed by reading the Win32 API rules. It was not compiled or tested on Windows.
4. NOTE: 10/14/2026: Fixed the WIN32 gxThreadMutexTryLock() so a private mutex calls TryEnterCriticalSection(). This needs a Windows SDK with _WIN32_WINNT 0x0400 or later.
5. Under POSIX, gxThreadMutexDestroy() calls pthread_mutex_unlock() before pthread_mutex_destroy(). This lets a thread destroy a mutex it still holds, which the gxThread sleep functions do. On Linux the mutex is robust, so an unlock by a thread that does not own it fails with EPERM. On other POSIX platforms, destroy a mutex only when it is unlocked or held by the calling thread.
6. In glibc (seen with 2.39), a pthread_mutex_trylock() call that returns ENOTRECOVERABLE on a robust mutex leaves the mutex in a bad state. A later pthread_mutex_unlock() call crashes inside glibc and a later pthread_mutex_lock() call never returns. NOTE: 10/24/2026: Worked around in version 6.0. When gxThreadMutexLock() or gxThreadMutexTryLock() gets ENOTRECOVERABLE, the not_recoverable member of the gxMutex_t is set. gxThreadMutexLock(), gxThreadMutexTryLock() and gxThreadMutexUnlock() then return ENOTRECOVERABLE and store gxMUTEX_NOTRECOVERABLE_ERROR without calling glibc, and gxThreadMutexDestroy() skips its unlock call. A mutex that is not recoverable cannot be used again. Destroy it and initialize it again. The first trylock call still reaches glibc, because the state cannot be known before it is made.
Example Programs
examples_threads/deadlock1 and examples_threads/deadlock2 call gxThreadMutexExceptionMessage() with the gxMutex_t pointer returned by gxMutex::GetMutex(). The other thread examples, such as examples_threads/mutex and examples_threads/condition, use these helpers through the gxMutex, gxCondition, and gxSemaphore classes.
Functions
gxThreadConditionAttributeDestroy()
gxThreadConditionAttributeInit()
gxThreadConditionBroadcast()
gxThreadConditionDestroy()
gxThreadConditionExceptionMessage()
gxThreadConditionInit()
gxThreadConditionSignal()
gxThreadConditionTimedWait()
gxThreadConditionWait()
gxThreadMutexAttributeDestroy()
gxThreadMutexAttributeInit()
gxThreadMutexDestroy()
gxThreadMutexExceptionMessage()
gxThreadMutexInit()
gxThreadMutexLock()
gxThreadMutexMakeConsistent()
gxThreadMutexTryLock()
gxThreadMutexUnlock()
gxThreadSemaphoreDecrement()
gxThreadSemaphoreDestroy()
gxThreadSemaphoreExceptionMessage()
gxThreadSemaphoreIncrement()
gxThreadSemaphoreInit()
gxThreadSemaphorePost()
gxThreadSemaphoreValue()
gxThreadSemaphoreWait()
gxMutex_t::gxMutex_t()
gxMutex_t::gxMutex_t(const gxMutex_t &ob)
gxMutex_t::operator=()
gxMutex_t::~gxMutex_t()
gxCondition_t::gxCondition_t()
gxCondition_t::gxCondition_t(const gxCondition_t &ob)
gxCondition_t::operator=()
gxCondition_t::~gxCondition_t()
gxSemaphore_t::gxSemaphore_t()
gxSemaphore_t::gxSemaphore_t(const gxSemaphore_t &ob)
gxSemaphore_t::operator=()
gxSemaphore_t::~gxSemaphore_t()
int gxThreadConditionAttributeDestroy(gxConditionAttribute *attr)Standalone function used to destroy a condition attribute. Under POSIX it calls pthread_condattr_destroy() (pthread_condattr_delete() under HPUX 10). Under WIN32 there is nothing to free and it returns 0. Returns a non-zero value if the attribute cannot be destroyed.
int gxThreadConditionAttributeInit(gxConditionAttribute *attr)Standalone function used to initialize a condition attribute with default settings. Under POSIX it calls pthread_condattr_init() (pthread_condattr_create() under HPUX 10). Under WIN32 it sets the security attribute to null, so the event handle cannot be inherited by child processes. Returns a non-zero value if the attribute cannot be initialized.
int gxThreadConditionBroadcast(gxCondition_t *c)Standalone function used to wake all the threads waiting on the condition. Under POSIX it calls pthread_cond_broadcast(). Under WIN32, if any threads are waiting, it sets the event, marks every current waiter for release, and starts a new wait generation. Returns a non-zero value if an error occurs. A successful call clears condition_error. NOTE: 10/14/2026: Fixed the WIN32 version so it unlocks the internal waiters_count_lock mutex when SetEvent() fails.
int gxThreadConditionDestroy(gxCondition_t *c)Standalone function used to destroy a condition. It returns 0 without doing anything if is_initialized is false. It first clears condition_error. Under POSIX it destroys the attribute (shared conditions only) and calls pthread_cond_destroy(). Under WIN32 it closes the event handle, resets the counters, and destroys the internal mutex. On success it resets process_type to private and is_initialized to false. Returns a non-zero value if an error occurs. NOTE: 10/14/2026: Fixed. This function used to return 0 without destroying anything, because the init function never set is_initialized.
const char *gxThreadConditionExceptionMessage(gxCondition_t *c)Standalone function that returns a null-terminated message for the error stored in c->condition_error. Codes past the end of the message table return the "Invalid exception code" message. The string is static. Do not free or change it.
int gxThreadConditionInit(gxCondition_t *c, gxProcessType type = gxPROCESS_PRIVATE)Standalone function used to initialize a condition variable. It returns 0 without doing anything if is_initialized is true. It clears condition_error and sets process_type from "type". On success it sets is_initialized to true. Under POSIX a private condition uses default attributes. A shared condition sets PTHREAD_PROCESS_SHARED with pthread_condattr_setpshared() on Linux and on platforms that define PTHREAD_PROCESS_SHARED. On HPUX 10 and platforms without PTHREAD_PROCESS_SHARED, a shared request falls back to a private condition, stores gxCONDITION_SET_SHARE_ERROR, and returns 1. Under WIN32 it creates an unnamed manual-reset event and an internal waiters_count_lock mutex of the same process type, and clears the counters. Returns 0 on success or a non-zero value if an error occurs. NOTE: 10/14/2026: Fixed shared conditions on Linux, which used to fall back to private ones, and fixed is_initialized, which was never set.
int gxThreadConditionSignal(gxCondition_t *c)Standalone function used to wake a thread waiting on the condition. Under POSIX it calls pthread_cond_signal(). Under WIN32, if there are more waiters than pending releases, it sets the event, adds one to the release count, and starts a new wait generation. If no thread is waiting the signal is lost. Returns a non-zero value if an error occurs. A successful call clears condition_error. NOTE: 10/14/2026: Fixed the WIN32 version so it unlocks the internal waiters_count_lock mutex when SetEvent() fails.
int gxThreadConditionTimedWait(gxCondition_t *c, gxMutex_t *external_mutex, unsigned long sec, unsigned long nsec)Standalone function used to block the calling thread until the condition is signaled or the time runs out. The caller must hold "external_mutex". The mutex is locked again when the function returns. The wait time is "sec" seconds plus "nsec" nanoseconds, measured from the time of the call. Returns 0 if the condition was signaled. On a timeout it stores gxCONDITION_TIMED_WAIT_ERROR and returns a non-zero value: ETIMEDOUT under POSIX and 1 under WIN32. A successful call clears condition_error. Under POSIX it calls pthread_cond_timedwait(). Under WIN32 it waits on the emulation event with WaitForSingleObject() for the time that is left. NOTE: 10/14/2026: Fixed the POSIX deadline, which was time(0) + "sec" with "nsec" used as the absolute nanosecond field. The wait could end up to one second early, and an "nsec" value of one second or more failed with EINVAL at once. The gxThread nSleep(), uSleep() and mSleep() functions use this call, so they now sleep for the full time. Fixed the WIN32 version: a timeout used to return 0, the timer restarted on each wake-up, a thread that timed out could take a release meant for another thread, and the external mutex was only released if it was shared. The WIN32 changes were not compiled or tested on Windows.
int gxThreadConditionWait(gxCondition_t *c, gxMutex_t *external_mutex)Standalone function used to block the calling thread until the condition is signaled. The caller must hold "external_mutex". The mutex is locked again when the function returns. Callers should test their own condition in a loop, because a thread can wake up without its condition being true. Under POSIX it calls pthread_cond_wait(). Under WIN32 the wait is emulated with a manual-reset event and a generation count. The last released thread resets the event. Returns a non-zero value if an error occurs. A successful call clears condition_error. NOTE: 10/14/2026: Fixed the WIN32 version: the external mutex is now released during the wait for private mutexes too, a failed WaitForSingleObject() now locks the external mutex again and lowers the waiter count before returning, and the last-waiter test is made while the internal lock is held. The WIN32 changes were not compiled or tested on Windows.
int gxThreadMutexAttributeDestroy(gxMutexAttribute *attr)Standalone function used to destroy a mutex attribute. Under POSIX it calls pthread_mutexattr_destroy() (pthread_mutexattr_delete() under HPUX 10). Under WIN32 there is nothing to free and it returns 0. Returns a non-zero value if the attribute cannot be destroyed.
int gxThreadMutexAttributeInit(gxMutexAttribute *attr)Standalone function used to initialize a mutex attribute with default settings. Under POSIX it calls pthread_mutexattr_init() (pthread_mutexattr_create() under HPUX 10). Under WIN32 it sets the security attribute to null, so the mutex handle cannot be inherited by child processes. Returns a non-zero value if the attribute cannot be initialized.
int gxThreadMutexDestroy(gxMutex_t *m)Standalone function used to destroy a mutex. It returns 0 without doing anything if is_initialized is false. It first clears mutex_error. Under POSIX it destroys the attribute (shared mutexes, and all mutexes on Linux), calls pthread_mutex_unlock() (skipped for a mutex that is not recoverable, as of 10/24/2026), and then calls pthread_mutex_destroy(). See Known Limitations items 5 and 6. Under WIN32 it closes the shared mutex handle or deletes the critical section. On success it resets process_type to private and is_initialized to false. Returns a non-zero value if an error occurs. NOTE: 10/14/2026: Fixed. This function used to return 0 without destroying anything, because the init function never set is_initialized. A stale error from an earlier call no longer makes it fail, and the Linux attribute of a private mutex is now destroyed.
const char *gxThreadMutexExceptionMessage(gxMutex_t *m)Standalone function that returns a null-terminated message for the error stored in m->mutex_error. Codes past the end of the message table return the "Invalid exception code" message. The string is static. Do not free or change it.
int gxThreadMutexInit(gxMutex_t *m, gxProcessType type = gxPROCESS_PRIVATE)Standalone function used to initialize a mutex. It returns 0 without doing anything if is_initialized is true. It clears mutex_error and sets process_type from "type". On success it sets is_initialized to true. Under Linux, both private and shared mutexes are created with an attribute set to PTHREAD_PROCESS_PRIVATE or PTHREAD_PROCESS_SHARED, and made robust with pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST). On 10/14/2026 this call was changed from the old pthread_mutexattr_setrobust_np() name to the standard pthread_mutexattr_setrobust() name. If the call fails, init fails with gxMUTEX_ATTR_INIT_ERROR. With a robust mutex, gxThreadMutexLock() returns EOWNERDEAD if the owner thread or process died while holding the lock. Under other POSIX platforms that define PTHREAD_PROCESS_SHARED, a shared mutex sets PTHREAD_PROCESS_SHARED and a private mutex uses default attributes. These mutexes are not robust. On HPUX 10 and platforms without PTHREAD_PROCESS_SHARED, a shared request falls back to a private mutex, stores gxMUTEX_SET_SHARE_ERROR, and returns 1 even though the mutex can be used. Under WIN32 a private mutex is a critical section. A shared mutex is an unnamed mutex created with CreateMutex(), not owned at creation. Returns 0 on success, or a non-zero value if an error occurs. NOTE: 10/14/2026: Fixed is_initialized, which was never set, and the pthread_mutexattr_setrobust() result, which was ignored.
int gxThreadMutexLock(gxMutex_t *m)Standalone function used to lock the mutex. The calling thread blocks until the mutex is free. A successful call clears mutex_error. Under POSIX it calls pthread_mutex_lock() and returns its error code. If the code is EOWNERDEAD, mutex_error is set to gxMUTEX_OWNERDEAD_ERROR. In this case the caller does own the lock. It should repair the protected data, call gxThreadMutexMakeConsistent(), and then unlock. ENOTRECOVERABLE stores gxMUTEX_NOTRECOVERABLE_ERROR. Other errors store gxMUTEX_LOCK_ERROR. Under WIN32 a shared mutex is locked with WaitForSingleObject() with no time-out. A WAIT_ABANDONED result counts as success. A private mutex calls EnterCriticalSection(). Returns 0 if the mutex was locked. NOTE: 10/14/2026: Fixed the ENOTRECOVERABLE test. The code compared the return value with the enum value gxMUTEX_NOTRECOVERABLE_ERROR, so ENOTRECOVERABLE was stored as gxMUTEX_LOCK_ERROR.
int gxThreadMutexMakeConsistent(gxMutex_t *m)Standalone function used to mark a robust mutex as consistent after a lock call returned EOWNERDEAD. The calling thread must hold the lock. This function works only under POSIX on Linux. It calls pthread_mutex_consistent(). On 10/14/2026 this call was changed from the old pthread_mutex_consistent_np() name to the standard pthread_mutex_consistent() name. On success it clears mutex_error. If the call returns EINVAL it stores gxMUTEX_INVAL_ERROR. Returns the pthread_mutex_consistent() result. On all other platforms it does nothing and returns 0. The gxMutex::MutexLock() and gxMutex::MutexTryLock() functions call this automatically unless auto fix has been turned off with gxMutex::DisableAutoFix().
int gxThreadMutexTryLock(gxMutex_t *m)Standalone function used to try to lock the mutex without blocking. A successful call clears mutex_error. Under POSIX it calls pthread_mutex_trylock() and returns its error code. EBUSY stores gxMUTEX_BUSY_ERROR. EOWNERDEAD stores gxMUTEX_OWNERDEAD_ERROR, and the caller then owns the lock, as with gxThreadMutexLock(). ENOTRECOVERABLE stores gxMUTEX_NOTRECOVERABLE_ERROR (see Known Limitations item 6). Under WIN32 a shared mutex calls WaitForSingleObject() with a zero time-out. WAIT_ABANDONED counts as success, because Windows gives the lock to the caller. WAIT_TIMEOUT and WAIT_FAILED store gxMUTEX_TRY_LOCK_ERROR and return 1. A private mutex calls TryEnterCriticalSection() and returns 1 with gxMUTEX_TRY_LOCK_ERROR if the lock is held by another thread. Returns 0 if the mutex was locked. NOTE: 10/14/2026: Fixed the ENOTRECOVERABLE test, and fixed the WIN32 version: a private mutex called LeaveCriticalSection() and never locked, WAIT_ABANDONED was reported as a failure, and WAIT_FAILED was reported as a success. The WIN32 changes were not compiled or tested on Windows.
int gxThreadMutexUnlock(gxMutex_t *m)Standalone function used to unlock the mutex. Any error stored by an earlier call is cleared before the unlock. Under POSIX it calls pthread_mutex_unlock(). EPERM (the caller does not own the mutex) stores gxMUTEX_PERM_ERROR. Other errors store gxMUTEX_UNLOCK_ERROR. Unlocking a mutex the thread does not own is undefined for non-robust POSIX mutexes. If the mutex is not recoverable it returns ENOTRECOVERABLE without calling glibc (10/24/2026, see Known Limitations item 6). Under WIN32 a shared mutex calls ReleaseMutex(), which fails if the caller does not own it. A private mutex calls LeaveCriticalSection(). Returns 0 on success or a non-zero value if an error occurs.
int gxThreadSemaphoreDecrement(gxSemaphore_t *s)Standalone function used to lower the semaphore value by one without blocking. The value may go below zero. This lets several threads move up on a semaphore before another thread can move down, for example during start up. Returns a non-zero value if an error occurs. A successful call clears semaphore_error.
int gxThreadSemaphoreDestroy(gxSemaphore_t *s)Standalone function used to destroy a semaphore. It returns 0 without doing anything if is_initialized is false. It first clears semaphore_error. It destroys the inner condition and then the inner mutex, sets is_initialized to false, sets the value back to 1, and sets process_type to private. Returns a non-zero value if an error occurs. NOTE: 10/14/2026: Fixed. The inner condition and mutex used to stay allocated because their init functions never set is_initialized.
const char *gxThreadSemaphoreExceptionMessage(gxSemaphore_t *s)Standalone function that returns a null-terminated message for the error stored in s->semaphore_error. Codes past the end of the message table return the "Invalid exception code" message. The string is static. Do not free or change it.
int gxThreadSemaphoreIncrement(gxSemaphore_t *s)Standalone function used to raise the semaphore value by one without blocking, and to wake one thread waiting on the semaphore. Returns a non-zero value if an error occurs. A successful call clears semaphore_error. NOTE: 10/14/2026: Fixed. Waiting threads were not signaled, so a thread blocked in gxThreadSemaphoreWait() stayed blocked after the value went up.
int gxThreadSemaphoreInit(gxSemaphore_t *s, gxProcessType type = gxPROCESS_PRIVATE)Standalone function used to initialize a semaphore. It returns 0 without doing anything if is_initialized is true. It clears semaphore_error, sets process_type from "type", and initializes the inner condition with "type". Under POSIX the inner mutex also uses "type". Under WIN32 the inner mutex is always shared. On success it sets is_initialized to true and both value and value_after_operation to 1. If the condition fails it stores gxSEMAPHORE_INIT_ERROR. If the mutex fails it stores gxSEMAPHORE_INTERNAL_ERROR and the semaphore is left partly initialized. Returns 0 on success or a non-zero value if an error occurs. NOTE: 10/14/2026: Fixed shared semaphores on Linux, which failed because shared conditions were not supported, and fixed value_after_operation and process_type, which were never set.
int gxThreadSemaphorePost(gxSemaphore_t *s)Standalone function used to raise the semaphore value by one and signal one waiting thread. The value is changed while the inner mutex is held. The signal is sent after the mutex is unlocked. Returns a non-zero value if an error occurs. A successful call clears semaphore_error.
int gxThreadSemaphoreValue(gxSemaphore_t *s)Standalone function that returns value_after_operation, the semaphore value recorded by init or by the last wait, post, increment, or decrement call. It is read without locking, and the value may change at any time after it is read. Use it only for status and debugging.
int gxThreadSemaphoreWait(gxSemaphore_t *s)Standalone function used to lower the semaphore value by one. If the value is zero or less, the calling thread blocks on the inner condition until the value is above zero. Returns a non-zero value if an error occurs. A successful call clears semaphore_error. NOTE: 10/14/2026: Fixed. If the condition wait failed, the function used to return with the inner mutex still locked. It now unlocks the mutex first.
gxMutex_t::gxMutex_t()Constructor. Sets is_initialized to false, process_type to gxPROCESS_PRIVATE, and mutex_error to gxMUTEX_NO_ERROR. It does not create the native mutex. Call gxThreadMutexInit().
gxMutex_t::gxMutex_t(const gxMutex_t &ob)Private copy constructor used to disallow copying.
void gxMutex_t::operator=(const gxMutex_t &ob)Private assignment operator used to disallow assignment.
gxMutex_t::~gxMutex_t()Destructor. It does nothing. It does not destroy the native mutex. Call gxThreadMutexDestroy().
gxCondition_t::gxCondition_t()Constructor. Sets is_initialized to false, process_type to gxPROCESS_PRIVATE, and condition_error to gxCONDITION_NO_ERROR. It does not create the native condition. Call gxThreadConditionInit().
gxCondition_t::gxCondition_t(const gxCondition_t &ob)Private copy constructor used to disallow copying.
void gxCondition_t::operator=(const gxCondition_t &ob)Private assignment operator used to disallow assignment.
gxCondition_t::~gxCondition_t()Destructor. It does nothing. It does not destroy the native condition. Call gxThreadConditionDestroy().
gxSemaphore_t::gxSemaphore_t()Constructor. Sets is_initialized to false, process_type to gxPROCESS_PRIVATE, semaphore_error to gxSEMAPHORE_NO_ERROR, and value and value_after_operation to 1. Call gxThreadSemaphoreInit() before use. NOTE: 10/14/2026: Fixed. The value fields were left uninitialized.
gxSemaphore_t::gxSemaphore_t(const gxSemaphore_t &ob)Private copy constructor used to disallow copying.
void gxSemaphore_t::operator=(const gxSemaphore_t &ob)Private assignment operator used to disallow assignment.
gxSemaphore_t::~gxSemaphore_t()Destructor. It does nothing. It does not destroy the inner mutex and condition. Call gxThreadSemaphoreDestroy().