Quick Links:

bal | bbl | bdl | bsl

Namespaces

Component bslmt_fastpostsemaphore
[Package bslmt]

Provide a semaphore class optimizing post. More...

Namespaces

namespace  bslmt

Detailed Description

Outline
Purpose:
Provide a semaphore class optimizing post.
Classes:
bslmt::FastPostSemaphore semaphore class optimizing post
See also:
Component 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:
  class IntQueue {
      // FIFO queue of integer values.

      // 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
      explicit
      IntQueue(bsl::size_t capacity, bslma::Allocator *basicAllocator = 0);
          // 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.

          // Destroy this object.

      // MANIPULATORS
      void disablePopFront();
          // 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 enablePopFront();
          // Enable dequeuing.  If the queue is not dequeue disabled, this
          // method has no effect.

      int popFront(int *value);
          // 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 pushBack(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.

      // ACCESSORS
      int waitUntilEmpty() const;
          // 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.
  };
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;
  }
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());