BDE 4.14.0 Production release
|
Macros | |
#define | BSLS_SPINLOCK_USES_AGGREGATE_INITIALIZATION |
#define | BSLS_SPINLOCK_UNLOCKED { {0} } |
Provide a spin lock.
This component provides a "busy wait" mutual exclusion lock primitive ("mutex"). A SpinLock
is small and statically-initializable, but because it "spins" in a tight loop rather than using system operations to block the thread of execution, it is unsuited for use cases involving high contention or long critical regions. Additionally, this component does not provide any guarantee of fairness when multiple threads are contending for the same lock. Use bsls::SpinLockGuard
for automatic locking-unlocking in a scope.
WARNING: A bsls::SpinLock
must be aggregate initialized to BSLS_SPINLOCK_UNLOCKED
. For example:
Note that SpinLock
is a struct requiring aggregate initialization to allow lock variables to be statically initialized when using a C++03 compiler (i.e., without using constexpr
).
In this section we show intended use of this component.
Suppose that we want to determine the maximum number of threads executing a block of code concurrently. Note that such a use case naturally calls for a statically initialized lock and the critical region involves a few integer operations; SpinLock may be suitable.
First, we define a type to manage the count within a scope:
Next, we declare static variables to track the call count and a SpinLock to guard them. SpinLock
may be statically initialized using the BSLS_SPINLOCK_UNLOCKED
constant:
Next, by creating a MaxConcurrencyCounter
object, each thread entering the block of code uses the SpinLock
to synchronize manipulation of the static count variables:
Finally, closing the block synchronizes on the SpinLock
again to decrement the thread count. Any intervening code can run in parallel.
Suppose that we have a large array of objects to be manipulated concurrently by multiple threads, but the size of the array itself does not change. (This might be because it represents an inherently fixed number of objects or because changes to the array size are infrequent and controlled by some other synchronization mechanism like a "reader-writer" lock). Thus one thread can manipulate a particular object in the array concurrently with a different thread manipulating another. If the manipulations are short and contention is likely to be low, SpinLock might be suitable due to its small size.
In particular, imagine we want a threadsafe "multi-queue". In this case, we would have an array of queues, each with a SpinLock member for fine-grained locking. First, we define the type to be held in the array.
Next, we implement the creators. Note that a different idiom is used to initialize member variables of SpinLock
type than is used for static variables:
Then we implement the manipulator functions using SpinLockGuard
to ensure thread safety. Note that we do memory allocation and deallocation outside the scope of the lock, as these may involve system calls that should be avoided in the scope of a SpinLock.
To illustrate fine-grained locking with this queue, we create a thread function that will manipulate queues out of a large array at random. Since each element in the array is locked independently, these threads will rarely contend for the same queue and can run largely in parallel.
Finally, we create the "multi-queue" and several of these threads to manipulate it. We assume the existence of a createThread() function that starts a new thread of execution with a parameter, and we omit details of "joining" these threads.
#define BSLS_SPINLOCK_UNLOCKED { {0} } |
Use this macro as the value for initializing an object of type SpinLock
. For example:
#define BSLS_SPINLOCK_USES_AGGREGATE_INITIALIZATION |