BDE 4.14.0 Production release
Loading...
Searching...
No Matches
bslmt_fastpostsemaphore

Detailed Description

Outline

Purpose

Provide a semaphore class optimizing post.

Classes

See also
bslmt_semaphore

Description

This component defines a semaphore, bslmt::FastPostSemaphore, with the post operation being optimized at the potential expense of other operations. In particular, bslmt::FastPostSemaphore is an efficient synchronization primitive that enables sharing of a counted number of resources. bslmt::FastPostSemaphore supports the methods timedWait, enable, and disable in addition to the standard semaphore methods.

Commonly, during periods of time when the protected resource is scarce (the semaphore count is frequently zero) and threads are frequently blocked on wait methods, pessimizing the performance of the threads that block will have little effect on overall performance. In this case, optimizing post may be a performance improvement. Note that when the resource is plentiful, there are no blocked threads and we expect the differences between semaphore implementations to be trivial.

Supported Clock-Types

bsls::SystemClockType supplies the enumeration indicating the system clock on which timeouts supplied to other methods should be based. If the clock type indicated at construction is bsls::SystemClockType::e_REALTIME, the absTime argument passed to the timedWait method should be expressed as an absolute offset since 00:00:00 UTC, January 1, 1970 (which matches the epoch used in bsls::SystemTime::now(bsls::SystemClockType::e_REALTIME). If the clock type indicated at construction is bsls::SystemClockType::e_MONOTONIC, the absTime argument passed to the timedWait method should be expressed as an absolute offset since the epoch of this clock (which matches the epoch used in bsls::SystemTime::now(bsls::SystemClockType::e_MONOTONIC).

Usage

This section illustrates intended use of this component.

Example 1: A Simple Queue

This example illustrates a very simple fixed-size queue where potential clients can push integers to a queue, and later retrieve the integer values from the queue in FIFO order. Also, waitUntilEmpty is implemented to depict the common usage of getDisabledState.

First, we define the IntQueue class:

/// FIFO queue of integer values.
class IntQueue {
// DATA
bsl::vector<int> d_data; // queue values
bslmt::FastPostSemaphore d_pushSem; // resource availability
// for push
bslmt::FastPostSemaphore d_popSem; // resource availability
// for pop
bsls::AtomicUint d_pushIdx; // index to push to
bsls::AtomicUint d_popIdx; // index to pop from
mutable bslmt::Mutex d_emptyMutex; // blocking point for
// 'waitUntilEmpty'
mutable bslmt::Condition d_emptyCondition; // condition variable for
// 'waitUntilEmpty'
// NOT IMPLEMENTED
IntQueue(const IntQueue&);
IntQueue& operator=(const IntQueue&);
public:
// PUBLIC CONSTANTS
enum ReturnValue {
e_SUCCESS = bslmt::FastPostSemaphore::e_SUCCESS, // indicates
// success
e_DISABLED = bslmt::FastPostSemaphore::e_DISABLED, // indicates
// queue is
// disabled
e_FAILED = bslmt::FastPostSemaphore::e_FAILED // indicates
// failure
};
// CREATORS
/// Create an `IntQueue` object with the specified `capacity`.
/// Optionally specify a `basicAllocator` used to supply memory. If
/// `basicAllocator` is 0, the currently installed default allocator
/// is used.
explicit
IntQueue(bsl::size_t capacity, bslma::Allocator *basicAllocator = 0);
/// Destroy this object.
//! ~IntQueue() = default;
// MANIPULATORS
/// Disable dequeueing from this queue. All subsequent invocations
/// of `popFront` and `waitUntilEmpty` will fail immediately. All
/// blocked invocations of `popFront` and `waitUntilEmpty` will fail
/// immediately. If the queue is already dequeue disabled, this
/// method has no effect.
void disablePopFront();
/// Enable dequeuing. If the queue is not dequeue disabled, this method
/// has no effect.
void enablePopFront();
/// Remove the element from the front of this queue and load that
/// element into the specified `value`. If the queue is empty,
/// block until it is not empty. Return 0 on success, and a nonzero
/// value if the queue is disabled.
int popFront(int *value);
/// Append the specified `value` to the back of this queue, blocking
/// until either space is available - if necessary - or the queue is
/// disabled. Return 0 on success, and a nonzero value if the queue
/// is disabled.
int pushBack(int value);
// ACCESSORS
/// Block until all the elements in this queue are removed. Return
/// 0 on success, and a non-zero value if the queue is not empty and
/// `isPopFrontDisabled()`. A blocked thread waiting for the queue
/// to empty will return a non-zero value if `disablePopFront` is
/// invoked.
int waitUntilEmpty() const;
};
Definition bslstl_vector.h:1025
Definition bslma_allocator.h:457
Definition bslmt_condition.h:220
Definition bslmt_fastpostsemaphore.h:327
@ e_FAILED
Definition bslmt_fastpostsemaphore.h:355
@ e_SUCCESS
Definition bslmt_fastpostsemaphore.h:345
@ e_DISABLED
Definition bslmt_fastpostsemaphore.h:347
Definition bslmt_mutex.h:315
Definition bsls_atomic.h:1043

Next, implement the queue:

// CREATORS
IntQueue::IntQueue(bsl::size_t capacity, bslma::Allocator *basicAllocator)
: d_data(capacity, basicAllocator)
, d_pushSem(static_cast<int>(capacity))
, d_popSem(0)
, d_pushIdx(0)
, d_popIdx(0)
, d_emptyMutex()
, d_emptyCondition()
{
}
// MANIPULATORS
void IntQueue::disablePopFront()
{
d_popSem.disable();
}
void IntQueue::enablePopFront()
{
d_popSem.enable();
}
int IntQueue::popFront(int *value)
{
// wait for available element
int rv = d_popSem.wait();
if (0 != rv) {
return rv; // RETURN
}
*value = d_data[d_popIdx++ % d_data.size()];
d_pushSem.post(); // signal additional empty element
if (0 == d_popSem.getValue()) {
{
bslmt::LockGuard<bslmt::Mutex> guard(&d_emptyMutex);
}
d_emptyCondition.broadcast();
}
return 0;
}
int IntQueue::pushBack(int value)
{
// wait for an empty element
int rv = d_pushSem.wait();
if (0 != rv) {
return rv; // RETURN
}
d_data[d_pushIdx++ % d_data.size()] = value;
d_popSem.post(); // signal additional available element
return 0;
}
// ACCESSORS
int IntQueue::waitUntilEmpty() const
{
bslmt::LockGuard<bslmt::Mutex> guard(&d_emptyMutex);
int state = d_popSem.getDisabledState();
while (d_popSem.getValue()) {
if (state != d_popSem.getDisabledState()) {
return e_DISABLED; // RETURN
}
d_emptyCondition.wait(&d_emptyMutex);
}
return e_SUCCESS;
}
Definition bslmt_lockguard.h:234

Then, declare an instance of IntQueue:

IntQueue queue(10);

Next, populate some values:

assert(0 == queue.pushBack(5));
assert(0 == queue.pushBack(7));
assert(0 == queue.pushBack(3));

Now, pop and verify the values:

int value;
assert(0 == queue.popFront(&value));
assert(5 == value);
assert(0 == queue.popFront(&value));
assert(7 == value);
assert(0 == queue.popFront(&value));
assert(3 == value);

Finally, use waitUntilEmpty to verify the queue is empty:

assert(IntQueue::e_SUCCESS == queue.waitUntilEmpty());