Files
2025-10-25 03:02:53 +03:00

9.7 KiB

[thread.sema]

32 Concurrency support library [thread]

32.8 Semaphore [thread.sema]

32.8.1 General [thread.sema.general]

1

#

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.

2

#

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.8.2 Header synopsis [semaphore.syn]

🔗

namespace std {// [thread.sema.cnt], class template counting_semaphoretemplate<ptrdiff_t least_max_value = implementation-defined>class counting_semaphore; using binary_semaphore = counting_semaphore<1>;}

32.8.3 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};}

1

#

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.

2

#

least_max_value shall be non-negative; otherwise the program is ill-formed.

3

#

Concurrent invocations of the member functions of counting_semaphore, other than its destructor, do not introduce data races.

🔗

static constexpr ptrdiff_t max() noexcept;

4

#

Returns: The maximum value of counter.

This value is greater than or equal to least_max_value.

🔗

constexpr explicit counting_semaphore(ptrdiff_t desired);

5

#

Preconditions: desired >= 0 is true, anddesired <= max() is true.

6

#

Effects: Initializes counter with desired.

7

#

Throws: Nothing.

🔗

void release(ptrdiff_t update = 1);

8

#

Preconditions: update >= 0 is true, andupdate <= max() - counter is true.

9

#

Effects: Atomically execute counter += update.

Then, unblocks any threads that are waiting for counter to be greater than zero.

10

#

Synchronization: Strongly happens before invocations of try_acquire that observe the result of the effects.

11

#

Throws: system_error when an exception is required ([thread.req.exception]).

12

#

Error conditions: Any of the error conditions allowed for mutex types ([thread.mutex.requirements.mutex]).

🔗

bool try_acquire() noexcept;

13

#

Effects: Attempts to atomically decrement counter if it is positive, without blocking.

If counter is not decremented, there is no effect andtry_acquire immediately returns.

An implementation may fail to decrement counter even if it is positive.

[Note 1:

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.

14

#

Returns: true if counter was decremented, otherwise false.

🔗

void acquire();

15

#

Effects: Repeatedly performs the following steps, in order:

  • (15.1)

    Evaluates try_acquire(). If the result is true, returns.

  • (15.2)

    Blocks on *this until counter is greater than zero.

16

#

Throws: system_error when an exception is required ([thread.req.exception]).

17

#

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);

18

#

Effects: Repeatedly performs the following steps, in order:

  • (18.1)

    Evaluates try_acquire(). If the result is true, returns true.

  • (18.2)

    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).

19

#

Throws: Timeout-related exceptions ([thread.req.timing]), or system_error when a non-timeout-related exception is required ([thread.req.exception]).

20

#

Error conditions: Any of the error conditions allowed for mutex types ([thread.mutex.requirements.mutex]).