32 Thread support library [thread]

32.7 Semaphore [thread.sema]

Semaphores are lightweight synchronization primitives used to constrain concurrent access to a shared resource.
They are widely used to implement other synchronization primitives and, whenever both are applicable, can be more efficient than condition variables.
A counting semaphore is a semaphore object that models a non-negative resource count.
A binary semaphore is a semaphore object that has only two states.
A binary semaphore should be more efficient than the default implementation of a counting semaphore with a unit resource count.

32.7.1 Header <semaphore> synopsis [semaphore.syn]

namespace std {
  template<ptrdiff_t least_max_value = implementation-defined>
    class counting_semaphore;

  using binary_semaphore = counting_semaphore<1>;
}

32.7.2 Class template counting_­semaphore [thread.sema.cnt]

namespace std {
  template<ptrdiff_t least_max_value = implementation-defined>
  class counting_semaphore {
  public:
    static constexpr ptrdiff_t max() noexcept;

    constexpr explicit counting_semaphore(ptrdiff_t desired);
    ~counting_semaphore();

    counting_semaphore(const counting_semaphore&) = delete;
    counting_semaphore& operator=(const counting_semaphore&) = delete;

    void release(ptrdiff_t update = 1);
    void acquire();
    bool try_acquire() noexcept;
    template<class Rep, class Period>
      bool try_acquire_for(const chrono::duration<Rep, Period>& rel_time);
    template<class Clock, class Duration>
      bool try_acquire_until(const chrono::time_point<Clock, Duration>& abs_time);

  private:
    ptrdiff_t counter;          // exposition only
  };
}
Class template counting_­semaphore maintains an internal counter that is initialized when the semaphore is created.
The counter is decremented when a thread acquires the semaphore, and is incremented when a thread releases the semaphore.
If a thread tries to acquire the semaphore when the counter is zero, the thread will block until another thread increments the counter by releasing the semaphore.
least_­max_­value shall be non-negative; otherwise the program is ill-formed.
Concurrent invocations of the member functions of counting_­semaphore, other than its destructor, do not introduce data races.
static constexpr ptrdiff_t max() noexcept;
Returns: The maximum value of counter.
This value is greater than or equal to least_­max_­value.
constexpr explicit counting_semaphore(ptrdiff_t desired);
Preconditions: desired >= 0 is true, and desired <= max() is true.
Effects: Initializes counter with desired.
Throws: Nothing.
void release(ptrdiff_t update = 1);
Preconditions: update >= 0 is true, and update <= max() - counter is true.
Effects: Atomically execute counter += update.
Then, unblocks any threads that are waiting for counter to be greater than zero.
Synchronization: Strongly happens before invocations of try_­acquire that observe the result of the effects.
Throws: system_­error when an exception is required ([thread.req.exception]).
Error conditions: Any of the error conditions allowed for mutex types ([thread.mutex.requirements.mutex]).
bool try_acquire() noexcept;
Effects: Attempts to atomically decrement counter if it is positive, without blocking.
If counter is not decremented, there is no effect and try_­acquire immediately returns.
An implementation may fail to decrement counter even if it is positive.
[Note
:
This spurious failure is normally uncommon, but allows interesting implementations based on a simple compare and exchange ([atomics]).
— end note
]
An implementation should ensure that try_­acquire does not consistently return false in the absence of contending semaphore operations.
Returns: true if counter was decremented, otherwise false.
void acquire();
Effects: Repeatedly performs the following steps, in order:
  • Evaluates try_­acquire. If the result is true, returns.
  • Blocks on *this until counter is greater than zero.
Throws: system_­error when an exception is required ([thread.req.exception]).
Error conditions: Any of the error conditions allowed for mutex types ([thread.mutex.requirements.mutex]).
template<class Rep, class Period> bool try_acquire_for(const chrono::duration<Rep, Period>& rel_time); template<class Clock, class Duration> bool try_acquire_until(const chrono::time_point<Clock, Duration>& abs_time);
Effects: Repeatedly performs the following steps, in order:
  • Evaluates try_­acquire(). If the result is true, returns true.
  • Blocks on *this until counter is greater than zero or until the timeout expires. If it is unblocked by the timeout expiring, returns false.
The timeout expires ([thread.req.timing]) when the current time is after abs_­time (for try_­acquire_­until) or when at least rel_­time has passed from the start of the function (for try_­acquire_­for).
Throws: Timeout-related exceptions ([thread.req.timing]), or system_­error when a non-timeout-related exception is required ([thread.req.exception]).
Error conditions: Any of the error conditions allowed for mutex types ([thread.mutex.requirements.mutex]).