feature: add synchronization primitives api including mutexes, condition variables, and read-write locks
This commit is contained in:
parent
80462a42f1
commit
37c59b003c
1 changed files with 419 additions and 0 deletions
419
Source/DrangPlatform/Include/drang/sync.h
Normal file
419
Source/DrangPlatform/Include/drang/sync.h
Normal file
|
|
@ -0,0 +1,419 @@
|
|||
#pragma once
|
||||
#include "error.h"
|
||||
#include "platform.h"
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
DRANG_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* @brief Opaque structure representing a mutex (mutual exclusion lock).
|
||||
*
|
||||
* A mutex provides mutual exclusion synchronization, allowing only one thread
|
||||
* to access a shared resource at a time. This is an opaque structure whose
|
||||
* actual implementation depends on the target platform.
|
||||
*
|
||||
* @remarks Use drang_mutex_new() or drang_mutex_init() to create and initialize.
|
||||
* Always pair with drang_mutex_free() or drang_mutex_fini() respectively.
|
||||
*/
|
||||
struct drang_mutex;
|
||||
|
||||
/**
|
||||
* @brief Returns the size in bytes required for a mutex structure.
|
||||
*
|
||||
* This function returns the platform-specific size needed to allocate
|
||||
* a mutex structure. Useful for stack allocation or custom memory management.
|
||||
*
|
||||
* @return Size in bytes required for a drang_mutex structure.
|
||||
* @remarks The returned size is platform-specific and may vary between builds.
|
||||
*/
|
||||
DRANG_API size_t drang_mutex_size(void);
|
||||
|
||||
/**
|
||||
* @brief Creates and initializes a new mutex on the heap.
|
||||
*
|
||||
* Allocates memory for a new mutex and initializes it to a usable state.
|
||||
* The mutex is created in an unlocked state.
|
||||
*
|
||||
* @param[out] mutex Pointer to store the address of the newly created mutex.
|
||||
* @return 0 on success, negative error code on failure.
|
||||
* @retval 0 Success - mutex created and initialized.
|
||||
* @retval DRANG_ENOMEM Insufficient memory to allocate the mutex.
|
||||
* @retval DRANG_EINVAL Invalid parameter (mutex is NULL).
|
||||
* @remarks The created mutex must be freed with drang_mutex_free().
|
||||
* The mutex is created in an unlocked state and ready for use.
|
||||
*/
|
||||
DRANG_API int drang_mutex_new(struct drang_mutex **mutex);
|
||||
|
||||
/**
|
||||
* @brief Destroys and frees a mutex created with drang_mutex_new().
|
||||
*
|
||||
* Finalizes the mutex and releases its allocated memory. The mutex should
|
||||
* not be locked when this function is called.
|
||||
*
|
||||
* @param[in] mutex Pointer to the mutex to destroy and free, or NULL.
|
||||
* @remarks Safe to call with NULL pointer. The mutex should be unlocked
|
||||
* before calling this function. Undefined behavior if the mutex
|
||||
* is currently locked or if other threads are waiting on it.
|
||||
*/
|
||||
DRANG_API void drang_mutex_free(struct drang_mutex *mutex);
|
||||
|
||||
/**
|
||||
* @brief Initializes a pre-allocated mutex structure.
|
||||
*
|
||||
* Initializes a mutex structure that was allocated by the caller (e.g., on
|
||||
* the stack or as part of a larger structure). The mutex is initialized
|
||||
* to an unlocked state.
|
||||
*
|
||||
* @param[in] mutex Pointer to the mutex structure to initialize.
|
||||
* @return 0 on success, negative error code on failure.
|
||||
* @retval 0 Success - mutex initialized and ready for use.
|
||||
* @retval DRANG_EINVAL Invalid parameter (mutex is NULL).
|
||||
* @retval DRANG_EBUSY Mutex is already initialized.
|
||||
* @remarks The mutex must be finalized with drang_mutex_fini() when no longer needed.
|
||||
* Use this for stack-allocated mutexes or when embedding in other structures.
|
||||
*/
|
||||
DRANG_API int drang_mutex_init(struct drang_mutex *mutex);
|
||||
|
||||
/**
|
||||
* @brief Finalizes a mutex initialized with drang_mutex_init().
|
||||
*
|
||||
* Cleans up resources associated with the mutex. Does not free the mutex
|
||||
* structure itself. The mutex should not be locked when this function is called.
|
||||
*
|
||||
* @param[in] mutex Pointer to the mutex to finalize.
|
||||
* @remarks The mutex should be unlocked before calling this function.
|
||||
* Undefined behavior if the mutex is currently locked or if other
|
||||
* threads are waiting on it. Safe to call with NULL pointer.
|
||||
*/
|
||||
DRANG_API void drang_mutex_fini(struct drang_mutex *mutex);
|
||||
|
||||
/**
|
||||
* @brief Locks the mutex, blocking until acquisition is possible.
|
||||
*
|
||||
* Attempts to lock the mutex. If the mutex is already locked by another thread,
|
||||
* the calling thread will block until the mutex becomes available. If the same
|
||||
* thread attempts to lock a mutex it already owns, the behavior is undefined.
|
||||
*
|
||||
* @param[in] mutex Pointer to the mutex to lock.
|
||||
* @return 0 on success, negative error code on failure.
|
||||
* @retval 0 Success - mutex is now locked by the calling thread.
|
||||
* @retval DRANG_EINVAL Invalid parameter (mutex is NULL or not initialized).
|
||||
* @retval DRANG_EDEADLK Deadlock detected (implementation-specific).
|
||||
* @remarks The mutex must be unlocked with drang_mutex_unlock() by the same thread.
|
||||
* Recursive locking (same thread locking twice) results in undefined behavior.
|
||||
*/
|
||||
DRANG_API int drang_mutex_lock(struct drang_mutex *mutex);
|
||||
|
||||
/**
|
||||
* @brief Unlocks a previously locked mutex.
|
||||
*
|
||||
* Releases the mutex, allowing other threads to acquire it. The mutex must
|
||||
* have been previously locked by the calling thread.
|
||||
*
|
||||
* @param[in] mutex Pointer to the mutex to unlock.
|
||||
* @return 0 on success, negative error code on failure.
|
||||
* @retval 0 Success - mutex is now unlocked and available to other threads.
|
||||
* @retval DRANG_EINVAL Invalid parameter (mutex is NULL or not initialized).
|
||||
* @retval DRANG_EPERM The calling thread does not own the mutex.
|
||||
* @remarks Only the thread that locked the mutex should unlock it.
|
||||
* Unlocking a mutex that is not locked results in undefined behavior.
|
||||
*/
|
||||
DRANG_API int drang_mutex_unlock(struct drang_mutex *mutex);
|
||||
|
||||
/**
|
||||
* @brief Opaque structure representing a condition variable.
|
||||
*
|
||||
* A condition variable allows threads to wait for certain conditions to become true.
|
||||
* It works in conjunction with a mutex to provide efficient thread synchronization.
|
||||
* This is an opaque structure whose actual implementation depends on the target platform.
|
||||
*
|
||||
* @remarks Always use condition variables with an associated mutex.
|
||||
* Use drang_cond_new() or drang_cond_init() to create and initialize.
|
||||
* Always pair with drang_cond_free() or drang_cond_fini() respectively.
|
||||
*/
|
||||
struct drang_cond;
|
||||
|
||||
/**
|
||||
* @brief Returns the size in bytes required for a condition variable structure.
|
||||
*
|
||||
* This function returns the platform-specific size needed to allocate
|
||||
* a condition variable structure. Useful for stack allocation or custom memory management.
|
||||
*
|
||||
* @return Size in bytes required for a drang_cond structure.
|
||||
* @remarks The returned size is platform-specific and may vary between builds.
|
||||
*/
|
||||
DRANG_API size_t drang_cond_size(void);
|
||||
|
||||
/**
|
||||
* @brief Creates and initializes a new condition variable on the heap.
|
||||
*
|
||||
* Allocates memory for a new condition variable and initializes it to a usable state.
|
||||
* The condition variable is ready to be used with wait, signal, and broadcast operations.
|
||||
*
|
||||
* @param[out] cond Pointer to store the address of the newly created condition variable.
|
||||
* @return 0 on success, negative error code on failure.
|
||||
* @retval 0 Success - condition variable created and initialized.
|
||||
* @retval DRANG_ENOMEM Insufficient memory to allocate the condition variable.
|
||||
* @retval DRANG_EINVAL Invalid parameter (cond is NULL).
|
||||
* @remarks The created condition variable must be freed with drang_cond_free().
|
||||
* Ready for use immediately after successful creation.
|
||||
*/
|
||||
DRANG_API int drang_cond_new(struct drang_cond **cond);
|
||||
|
||||
/**
|
||||
* @brief Destroys and frees a condition variable created with drang_cond_new().
|
||||
*
|
||||
* Finalizes the condition variable and releases its allocated memory. No threads
|
||||
* should be waiting on the condition variable when this function is called.
|
||||
*
|
||||
* @param[in] cond Pointer to the condition variable to destroy and free, or NULL.
|
||||
* @remarks Safe to call with NULL pointer. Undefined behavior if threads are
|
||||
* currently waiting on this condition variable.
|
||||
*/
|
||||
DRANG_API void drang_cond_free(struct drang_cond *cond);
|
||||
|
||||
/**
|
||||
* @brief Initializes a pre-allocated condition variable structure.
|
||||
*
|
||||
* Initializes a condition variable structure that was allocated by the caller
|
||||
* (e.g., on the stack or as part of a larger structure). The condition variable
|
||||
* is initialized and ready for use.
|
||||
*
|
||||
* @param[in] cond Pointer to the condition variable structure to initialize.
|
||||
* @return 0 on success, negative error code on failure.
|
||||
* @retval 0 Success - condition variable initialized and ready for use.
|
||||
* @retval DRANG_EINVAL Invalid parameter (cond is NULL).
|
||||
* @retval DRANG_EBUSY Condition variable is already initialized.
|
||||
* @remarks The condition variable must be finalized with drang_cond_fini() when no longer needed.
|
||||
* Use this for stack-allocated condition variables or when embedding in other structures.
|
||||
*/
|
||||
DRANG_API int drang_cond_init(struct drang_cond *cond);
|
||||
|
||||
/**
|
||||
* @brief Finalizes a condition variable initialized with drang_cond_init().
|
||||
*
|
||||
* Cleans up resources associated with the condition variable. Does not free the
|
||||
* condition variable structure itself. No threads should be waiting on the
|
||||
* condition variable when this function is called.
|
||||
*
|
||||
* @param[in] cond Pointer to the condition variable to finalize.
|
||||
* @remarks Undefined behavior if threads are currently waiting on this condition variable.
|
||||
* Safe to call with NULL pointer.
|
||||
*/
|
||||
DRANG_API void drang_cond_fini(struct drang_cond *cond);
|
||||
|
||||
/**
|
||||
* @brief Signals one thread waiting on the condition variable.
|
||||
*
|
||||
* Wakes up at least one thread that is currently waiting on the condition variable.
|
||||
* If no threads are waiting, the signal is lost (no queuing). If multiple threads
|
||||
* are waiting, it is unspecified which thread will be awakened.
|
||||
*
|
||||
* @param[in] cond Pointer to the condition variable to signal.
|
||||
* @return 0 on success, negative error code on failure.
|
||||
* @retval 0 Success - signal sent (though no guarantee a thread was waiting).
|
||||
* @retval DRANG_EINVAL Invalid parameter (cond is NULL or not initialized).
|
||||
* @remarks Does not require holding the associated mutex, but it's recommended
|
||||
* for predictable behavior. If no threads are waiting, the signal is lost.
|
||||
*/
|
||||
DRANG_API int drang_cond_signal(struct drang_cond *cond);
|
||||
|
||||
/**
|
||||
* @brief Signals all threads waiting on the condition variable.
|
||||
*
|
||||
* Wakes up all threads that are currently waiting on the condition variable.
|
||||
* If no threads are waiting, the broadcast is lost (no queuing). All waiting
|
||||
* threads will compete to reacquire their associated mutex.
|
||||
*
|
||||
* @param[in] cond Pointer to the condition variable to broadcast.
|
||||
* @return 0 on success, negative error code on failure.
|
||||
* @retval 0 Success - broadcast sent (though no guarantee threads were waiting).
|
||||
* @retval DRANG_EINVAL Invalid parameter (cond is NULL or not initialized).
|
||||
* @remarks Does not require holding the associated mutex, but it's recommended
|
||||
* for predictable behavior. If no threads are waiting, the broadcast is lost.
|
||||
*/
|
||||
DRANG_API int drang_cond_broadcast(struct drang_cond *cond);
|
||||
|
||||
/**
|
||||
* @brief Waits indefinitely on a condition variable.
|
||||
*
|
||||
* Atomically unlocks the mutex and waits for the condition variable to be signaled.
|
||||
* When awakened (by signal or broadcast), the mutex is reacquired before returning.
|
||||
* The calling thread must own the mutex before calling this function.
|
||||
*
|
||||
* @param[in] cond Pointer to the condition variable to wait on.
|
||||
* @param[in] mutex Pointer to the mutex associated with the condition variable.
|
||||
* @return 0 on success, negative error code on failure.
|
||||
* @retval 0 Success - condition was signaled and mutex reacquired.
|
||||
* @retval DRANG_EINVAL Invalid parameter (cond or mutex is NULL or not initialized).
|
||||
* @retval DRANG_EPERM The calling thread does not own the mutex.
|
||||
* @remarks The mutex must be locked by the calling thread before this call.
|
||||
* Spurious wakeups are possible - always check your condition in a loop.
|
||||
* The mutex is guaranteed to be locked when this function returns successfully.
|
||||
*/
|
||||
DRANG_API int drang_cond_wait(struct drang_cond *cond, struct drang_mutex *mutex);
|
||||
|
||||
/**
|
||||
* @brief Waits on a condition variable with a timeout.
|
||||
*
|
||||
* Atomically unlocks the mutex and waits for the condition variable to be signaled
|
||||
* or for the timeout to expire. When awakened or timed out, the mutex is reacquired
|
||||
* before returning. The calling thread must own the mutex before calling this function.
|
||||
*
|
||||
* @param[in] cond Pointer to the condition variable to wait on.
|
||||
* @param[in] mutex Pointer to the mutex associated with the condition variable.
|
||||
* @param[in] timeout_ms Timeout in milliseconds (0 = no timeout, wait indefinitely).
|
||||
* @return 0 on success, negative error code on failure or timeout.
|
||||
* @retval 0 Success - condition was signaled and mutex reacquired.
|
||||
* @retval DRANG_ETIMEDOUT Timeout expired before condition was signaled.
|
||||
* @retval DRANG_EINVAL Invalid parameter (cond or mutex is NULL or not initialized).
|
||||
* @retval DRANG_EPERM The calling thread does not own the mutex.
|
||||
* @remarks The mutex must be locked by the calling thread before this call.
|
||||
* Spurious wakeups are possible - always check your condition in a loop.
|
||||
* The mutex is guaranteed to be locked when this function returns (success or timeout).
|
||||
* A timeout of 0 means wait indefinitely (equivalent to drang_cond_wait).
|
||||
*/
|
||||
DRANG_API int drang_cond_timedwait(struct drang_cond *cond, struct drang_mutex *mutex, uint64_t timeout_ms);
|
||||
|
||||
/**
|
||||
* @brief Opaque structure representing a read-write lock.
|
||||
*
|
||||
* A read-write lock allows multiple concurrent readers or a single writer.
|
||||
* This provides better performance than a mutex when reads are more frequent
|
||||
* than writes. This is an opaque structure whose actual implementation depends
|
||||
* on the target platform.
|
||||
*
|
||||
* @remarks Readers can acquire the lock concurrently, but writers have exclusive access.
|
||||
* Use drang_rwlock_new() or drang_rwlock_init() to create and initialize.
|
||||
* Always pair with drang_rwlock_free() or drang_rwlock_fini() respectively.
|
||||
*/
|
||||
struct drang_rwlock;
|
||||
|
||||
/**
|
||||
* @brief Returns the size in bytes required for a read-write lock structure.
|
||||
*
|
||||
* This function returns the platform-specific size needed to allocate
|
||||
* a read-write lock structure. Useful for stack allocation or custom memory management.
|
||||
*
|
||||
* @return Size in bytes required for a drang_rwlock structure.
|
||||
* @remarks The returned size is platform-specific and may vary between builds.
|
||||
*/
|
||||
DRANG_API size_t drang_rwlock_size(void);
|
||||
|
||||
/**
|
||||
* @brief Creates and initializes a new read-write lock on the heap.
|
||||
*
|
||||
* Allocates memory for a new read-write lock and initializes it to a usable state.
|
||||
* The lock is created in an unlocked state, ready for readers or writers.
|
||||
*
|
||||
* @param[out] rwlock Pointer to store the address of the newly created read-write lock.
|
||||
* @return 0 on success, negative error code on failure.
|
||||
* @retval 0 Success - read-write lock created and initialized.
|
||||
* @retval DRANG_ENOMEM Insufficient memory to allocate the read-write lock.
|
||||
* @retval DRANG_EINVAL Invalid parameter (rwlock is NULL).
|
||||
* @remarks The created read-write lock must be freed with drang_rwlock_free().
|
||||
* The lock is created in an unlocked state and ready for use.
|
||||
*/
|
||||
DRANG_API int drang_rwlock_new(struct drang_rwlock **rwlock);
|
||||
|
||||
/**
|
||||
* @brief Destroys and frees a read-write lock created with drang_rwlock_new().
|
||||
*
|
||||
* Finalizes the read-write lock and releases its allocated memory. The lock should
|
||||
* not have any readers or writers when this function is called.
|
||||
*
|
||||
* @param[in] rwlock Pointer to the read-write lock to destroy and free, or NULL.
|
||||
* @remarks Safe to call with NULL pointer. The lock should be unlocked
|
||||
* before calling this function. Undefined behavior if the lock
|
||||
* currently has readers or a writer.
|
||||
*/
|
||||
DRANG_API void drang_rwlock_free(struct drang_rwlock *rwlock);
|
||||
|
||||
/**
|
||||
* @brief Initializes a pre-allocated read-write lock structure.
|
||||
*
|
||||
* Initializes a read-write lock structure that was allocated by the caller
|
||||
* (e.g., on the stack or as part of a larger structure). The lock is
|
||||
* initialized to an unlocked state.
|
||||
*
|
||||
* @param[in] rwlock Pointer to the read-write lock structure to initialize.
|
||||
* @return 0 on success, negative error code on failure.
|
||||
* @retval 0 Success - read-write lock initialized and ready for use.
|
||||
* @retval DRANG_EINVAL Invalid parameter (rwlock is NULL).
|
||||
* @retval DRANG_EBUSY Read-write lock is already initialized.
|
||||
* @remarks The lock must be finalized with drang_rwlock_fini() when no longer needed.
|
||||
* Use this for stack-allocated locks or when embedding in other structures.
|
||||
*/
|
||||
DRANG_API int drang_rwlock_init(struct drang_rwlock *rwlock);
|
||||
|
||||
/**
|
||||
* @brief Finalizes a read-write lock initialized with drang_rwlock_init().
|
||||
*
|
||||
* Cleans up resources associated with the read-write lock. Does not free the
|
||||
* lock structure itself. The lock should not have any readers or writers
|
||||
* when this function is called.
|
||||
*
|
||||
* @param[in] rwlock Pointer to the read-write lock to finalize.
|
||||
* @remarks The lock should be unlocked before calling this function.
|
||||
* Undefined behavior if the lock currently has readers or a writer.
|
||||
* Safe to call with NULL pointer.
|
||||
*/
|
||||
DRANG_API void drang_rwlock_fini(struct drang_rwlock *rwlock);
|
||||
|
||||
/**
|
||||
* @brief Acquires a read lock on the read-write lock.
|
||||
*
|
||||
* Attempts to acquire a read lock. Multiple threads can hold read locks
|
||||
* simultaneously, but read locks are exclusive with write locks. If a writer
|
||||
* currently holds the lock, the calling thread will block until the write
|
||||
* lock is released.
|
||||
*
|
||||
* @param[in] rwlock Pointer to the read-write lock to acquire for reading.
|
||||
* @return 0 on success, negative error code on failure.
|
||||
* @retval 0 Success - read lock acquired.
|
||||
* @retval DRANG_EINVAL Invalid parameter (rwlock is NULL or not initialized).
|
||||
* @retval DRANG_EDEADLK Deadlock detected (implementation-specific).
|
||||
* @remarks The read lock must be released with drang_rwlock_unlock().
|
||||
* Multiple threads can hold read locks concurrently.
|
||||
* Read locks block writers but not other readers.
|
||||
*/
|
||||
DRANG_API int drang_rwlock_rdlock(struct drang_rwlock *rwlock);
|
||||
|
||||
/**
|
||||
* @brief Acquires a write lock on the read-write lock.
|
||||
*
|
||||
* Attempts to acquire a write lock. Write locks are exclusive - only one thread
|
||||
* can hold a write lock, and write locks are exclusive with read locks. If any
|
||||
* readers or another writer currently hold the lock, the calling thread will
|
||||
* block until all locks are released.
|
||||
*
|
||||
* @param[in] rwlock Pointer to the read-write lock to acquire for writing.
|
||||
* @return 0 on success, negative error code on failure.
|
||||
* @retval 0 Success - write lock acquired.
|
||||
* @retval DRANG_EINVAL Invalid parameter (rwlock is NULL or not initialized).
|
||||
* @retval DRANG_EDEADLK Deadlock detected (implementation-specific).
|
||||
* @remarks The write lock must be released with drang_rwlock_unlock().
|
||||
* Write locks are exclusive - only one writer at a time.
|
||||
* Write locks block both readers and other writers.
|
||||
*/
|
||||
DRANG_API int drang_rwlock_wrlock(struct drang_rwlock *rwlock);
|
||||
|
||||
/**
|
||||
* @brief Releases a previously acquired read or write lock.
|
||||
*
|
||||
* Releases either a read lock or write lock that was previously acquired by
|
||||
* the calling thread. The type of lock (read or write) is tracked internally.
|
||||
*
|
||||
* @param[in] rwlock Pointer to the read-write lock to unlock.
|
||||
* @return 0 on success, negative error code on failure.
|
||||
* @retval 0 Success - lock released.
|
||||
* @retval DRANG_EINVAL Invalid parameter (rwlock is NULL or not initialized).
|
||||
* @retval DRANG_EPERM The calling thread does not hold any lock on this rwlock.
|
||||
* @remarks Only the thread that acquired the lock should release it.
|
||||
* Works for both read and write locks.
|
||||
* Unlocking when no lock is held results in undefined behavior.
|
||||
*/
|
||||
DRANG_API int drang_rwlock_unlock(struct drang_rwlock *rwlock);
|
||||
|
||||
DRANG_END_DECLS
|
||||
Loading…
Add table
Add a link
Reference in a new issue