Condition Variable
Categories:
5 minute read
Overview
The condition variable is a relatively low-level synchronization mechanism in operating systems that can simultaneously block multiple threads. When the condition is met, the condition variable can wake up one or all waiting threads.
Operating systems or language libraries provide condition variable functionality, for example:
- POSIX
pthread_cond_t - C++
std::condition_variable - Java
java.util.concurrent.locks.Condition - Python
threading.Condition - Rust
std::sync::condvar
A thread must hold a mutex before waiting on a condition variable. When the condition variable blocks the thread, it synchronously releases the mutex. When the condition is met and the thread is awakened, the condition variable automatically re-locks the mutex. When an error occurs while waiting on the condition variable, the condition variable also automatically re-locks the mutex before returning.
XWOS condition variable functionality is similar and mainly includes the following operations:
- Thread A waits for the condition variable’s condition to be met and blocks;
- Another thread B or interrupt context or other context makes the condition met via unicast or broadcast, and wakes up thread A blocked on the condition variable.
- Thread A waits on the condition variable while holding a lock, which is automatically released;
- Thread A waits on the condition variable while holding a lock, and when the condition is met, the lock is automatically re-acquired;
- Thread A waits on the condition variable while holding a lock, and when an error occurs and returns, the lock is not automatically re-acquired;
- Supports multiple lock types:
- Mutex
- Spinlock
- Seqlock
- Custom lock and unlock functions
- Supports operation without an associated lock.
Condition Variable Object and Object Descriptor
The condition variable object is a derived class of XWOS Object struct xwos_object.
Similarly, condition variable objects also use the condition variable object descriptor xwos_cond_d
to solve the problems of validity and identity legitimacy.
The condition variable object descriptor is composed of a pointer to the condition variable object and a tag:
typedef struct {
struct xwos_cond * cond; /**< Pointer to the condition variable object */
xwsq_t tik; /**< Tag */
} xwos_cond_d;
When referencing an object through the object descriptor, first check the value of obj->magic to see if it is 0x58574F53U, which confirms that the pointer obj points to a valid XWOS object.
Then compare the tags obj->tik and tik to see if they are equal, which confirms the object’s identity.
Since the object’s tik is globally unique, when the object is released, its tik will be destructed to 0 by the destructor.
When the memory address is reconstructed into a new object, its tik will definitely differ from the tik in the object descriptor.
Condition Variable Initialization, Destruction and Dynamic Creation, Deletion
Static Initialization and Destruction
- Static initialization:
xwos_cond_init()- Static means the user predefines thread structure objects, which are allocated memory by the compiler at compile time.
- Destroy statically initialized condition variable:
xwos_cond_fini()
Dynamic Creation and Deletion
- Dynamic creation:
xwos_cond_create()- Dynamic means the program allocates memory through memory allocation functions at runtime and constructs the object on the allocated memory.
- Delete dynamically created condition variable:
xwos_cond_delete()
Unicast
xwos_cond_unicast() can be used in any context to make the condition variable’s condition met, but only wakes up one thread.
Unicast does not produce a select signal.
Broadcast
xwos_cond_broadcast() can be used in any context to make the condition variable’s condition met, waking up all threads.
Broadcast also causes the condition variable to send a select signal to the bound signal selector.
Waiting on Condition Variable
xwos_cond_wait(): Wait on the condition variable, can only be used in thread contextxwos_cond_wait_to(): Timed wait on the condition variable, can only be used in thread contextxwos_cond_wait_unintr(): Uninterruptible wait on the condition variable, can only be used in thread context
When using lock functions with side effects, for example:
xwos_splk_lock_cpuirq()xwos_splk_lock_cpuirqsv()xwos_splk_lock_irqs()xwos_splk_lock_bh()xwos_sqlk_wr_lock_cpuirq()xwos_sqlk_wr_lock_cpuirqsv()xwos_sqlk_wr_lock_irqs()xwos_sqlk_wr_lock_bh()xwos_sqlk_rdex_lock_cpuirq()xwos_sqlk_rdex_lock_cpuirqsv()xwos_sqlk_rdex_lock_irqs()xwos_sqlk_rdex_lock_bh()
Waiting on the condition variable does not manage the scheduler switch, interrupt switch, or interrupt bottom half switch.
Whatever state existed before waiting, regardless of whether the return value is XWOK or an error code, the state remains the same after waiting.
XWOS condition variables differ from pthread_cond_t:
- For XWOS condition variables, the lock is only re-acquired when waiting on the condition variable returns
XWOK. If an error code is returned, whether the lock is re-acquired is indeterminate. pthread_cond_wait()always waits until the mutex is locked before returning.
Freeze and Thaw
Freeze
The condition variable can be frozen using xwos_cond_freeze().
A frozen condition variable cannot be unicast or broadcast, but does not affect wait operations.
Thaw
A frozen condition variable can be thawed via xwos_cond_thaw().
After the condition variable is thawed, unicast and broadcast can resume.
Binding and Unbinding Signal Selector
The condition variable can be bound to a signal selector via xwos_cond_bind().
When the condition variable is broadcast, it sends a select signal to the signal selector. The signal selector then wakes up the waiting thread. Note that unicast does not produce a select signal.
A bound condition variable can be unbound via xwos_cond_unbind().
Condition Variable Object Lifecycle Management
The base class of the condition variable object is XWOS Object struct xwos_object.
The condition variable object also has two sets of lifecycle management CAPI:
-
Access lifecycle management CAPI using object pointer: requires ensuring that the object is definitely valid when calling the CAPI, and there is no case of released-then-reallocated as another object.
xwos_cond_grab(): Increase reference count.xwos_cond_put(): Decrease reference count. When the reference count drops to 0, the garbage collection function is called to release the object.
-
Access lifecycle management CAPI using object descriptor: used when the user cannot guarantee that the object is definitely valid or cannot guarantee that the object has not become another object.
xwos_cond_acquire(): Determine that the object is valid and legitimate through the object descriptor, then increase the reference count.xwos_cond_release(): Determine that the object is valid and legitimate through the object descriptor, then decrease the reference count. When the reference count drops to 0, the garbage collection function is called to release the object.