252 lines
9.7 KiB
Markdown
252 lines
9.7 KiB
Markdown
[thread.sema]
|
||
|
||
# 32 Concurrency support library [[thread]](./#thread)
|
||
|
||
## 32.8 Semaphore [thread.sema]
|
||
|
||
### [32.8.1](#general) General [[thread.sema.general]](thread.sema.general)
|
||
|
||
[1](#general-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L10435)
|
||
|
||
Semaphores are lightweight synchronization primitives
|
||
used to constrain concurrent access to a shared resource[.](#general-1.sentence-1)
|
||
|
||
They are widely used to implement other synchronization primitives and,
|
||
whenever both are applicable, can be more efficient than condition variables[.](#general-1.sentence-2)
|
||
|
||
[2](#general-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L10441)
|
||
|
||
A counting semaphore is a semaphore object
|
||
that models a non-negative resource count[.](#general-2.sentence-1)
|
||
|
||
A binary semaphore is a semaphore object that has only two states[.](#general-2.sentence-2)
|
||
|
||
A binary semaphore should be more efficient than
|
||
the default implementation of a counting semaphore with a unit resource count[.](#general-2.sentence-3)
|
||
|
||
### [32.8.2](#semaphore.syn) Header <semaphore> synopsis [[semaphore.syn]](semaphore.syn)
|
||
|
||
[ð](#header:%3csemaphore%3e)
|
||
|
||
namespace std {// [[thread.sema.cnt]](#cnt "32.8.3 Class template counting_semaphore"), class template counting_semaphoretemplate<ptrdiff_t least_max_value = *implementation-defined*>class counting_semaphore; using [binary_semaphore](#lib:binary_semaphore "32.8.2 Header <semaphore> synopsis [semaphore.syn]") = counting_semaphore<1>;}
|
||
|
||
### [32.8.3](#cnt) Class template counting_semaphore [[thread.sema.cnt]](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](#cnt-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L10490)
|
||
|
||
Class template counting_semaphore maintains an internal counter
|
||
that is initialized when the semaphore is created[.](#cnt-1.sentence-1)
|
||
|
||
The counter is decremented when a thread acquires the semaphore, and
|
||
is incremented when a thread releases the semaphore[.](#cnt-1.sentence-2)
|
||
|
||
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[.](#cnt-1.sentence-3)
|
||
|
||
[2](#cnt-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L10500)
|
||
|
||
least_max_value shall be non-negative; otherwise the program is ill-formed[.](#cnt-2.sentence-1)
|
||
|
||
[3](#cnt-3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L10503)
|
||
|
||
Concurrent invocations of the member functions of counting_semaphore,
|
||
other than its destructor, do not introduce data races[.](#cnt-3.sentence-1)
|
||
|
||
[ð](#lib:max,counting_semaphore)
|
||
|
||
`static constexpr ptrdiff_t max() noexcept;
|
||
`
|
||
|
||
[4](#cnt-4)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L10513)
|
||
|
||
*Returns*: The maximum value of counter[.](#cnt-4.sentence-1)
|
||
|
||
This value is greater than or equal to least_max_value[.](#cnt-4.sentence-2)
|
||
|
||
[ð](#lib:counting_semaphore,constructor)
|
||
|
||
`constexpr explicit counting_semaphore(ptrdiff_t desired);
|
||
`
|
||
|
||
[5](#cnt-5)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L10525)
|
||
|
||
*Preconditions*: desired >= 0 is true, anddesired <= max() is true[.](#cnt-5.sentence-1)
|
||
|
||
[6](#cnt-6)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L10530)
|
||
|
||
*Effects*: Initializes counter with desired[.](#cnt-6.sentence-1)
|
||
|
||
[7](#cnt-7)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L10534)
|
||
|
||
*Throws*: Nothing[.](#cnt-7.sentence-1)
|
||
|
||
[ð](#lib:release,counting_semaphore)
|
||
|
||
`void release(ptrdiff_t update = 1);
|
||
`
|
||
|
||
[8](#cnt-8)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L10545)
|
||
|
||
*Preconditions*: update >= 0 is true, andupdate <= max() - counter is true[.](#cnt-8.sentence-1)
|
||
|
||
[9](#cnt-9)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L10550)
|
||
|
||
*Effects*: Atomically execute counter += update[.](#cnt-9.sentence-1)
|
||
|
||
Then, unblocks any threads
|
||
that are waiting for counter to be greater than zero[.](#cnt-9.sentence-2)
|
||
|
||
[10](#cnt-10)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L10556)
|
||
|
||
*Synchronization*: Strongly happens before invocations of try_acquire that observe the result of the effects[.](#cnt-10.sentence-1)
|
||
|
||
[11](#cnt-11)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L10561)
|
||
|
||
*Throws*: system_error when an exception is required ([[thread.req.exception]](thread.req.exception "32.2.2 Exceptions"))[.](#cnt-11.sentence-1)
|
||
|
||
[12](#cnt-12)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L10565)
|
||
|
||
*Error conditions*: Any of the error conditions
|
||
allowed for mutex types ([[thread.mutex.requirements.mutex]](thread.mutex.requirements.mutex "32.6.4.2 Mutex types"))[.](#cnt-12.sentence-1)
|
||
|
||
[ð](#lib:try_acquire,counting_semaphore)
|
||
|
||
`bool try_acquire() noexcept;
|
||
`
|
||
|
||
[13](#cnt-13)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L10577)
|
||
|
||
*Effects*: Attempts to atomically decrement counter if it is positive,
|
||
without blocking[.](#cnt-13.sentence-1)
|
||
|
||
If counter is not decremented, there is no effect andtry_acquire immediately returns[.](#cnt-13.sentence-2)
|
||
|
||
An implementation may fail to decrement counter even if it is positive[.](#cnt-13.sentence-3)
|
||
|
||
[*Note [1](#cnt-note-1)*:
|
||
|
||
This spurious failure is normally uncommon, but
|
||
allows interesting implementations
|
||
based on a simple compare and exchange ([[atomics]](atomics "32.5 Atomic operations"))[.](#cnt-13.sentence-4)
|
||
|
||
â *end note*]
|
||
|
||
An implementation should ensure that try_acquire does not consistently return false in the absence of contending semaphore operations[.](#cnt-13.sentence-5)
|
||
|
||
[14](#cnt-14)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L10594)
|
||
|
||
*Returns*: true if counter was decremented, otherwise false[.](#cnt-14.sentence-1)
|
||
|
||
[ð](#lib:acquire,counting_semaphore)
|
||
|
||
`void acquire();
|
||
`
|
||
|
||
[15](#cnt-15)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L10605)
|
||
|
||
*Effects*: Repeatedly performs the following steps, in order:
|
||
|
||
- [(15.1)](#cnt-15.1)
|
||
|
||
Evaluates try_acquire()[.](#cnt-15.1.sentence-1)
|
||
If the result is true, returns[.](#cnt-15.1.sentence-2)
|
||
|
||
- [(15.2)](#cnt-15.2)
|
||
|
||
Blocks on *this until counter is greater than zero[.](#cnt-15.2.sentence-1)
|
||
|
||
[16](#cnt-16)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L10615)
|
||
|
||
*Throws*: system_error when an exception is required ([[thread.req.exception]](thread.req.exception "32.2.2 Exceptions"))[.](#cnt-16.sentence-1)
|
||
|
||
[17](#cnt-17)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L10619)
|
||
|
||
*Error conditions*: Any of the error conditions
|
||
allowed for mutex types ([[thread.mutex.requirements.mutex]](thread.mutex.requirements.mutex "32.6.4.2 Mutex types"))[.](#cnt-17.sentence-1)
|
||
|
||
[ð](#lib:try_acquire_for,counting_semaphore)
|
||
|
||
`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](#cnt-18)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L10635)
|
||
|
||
*Effects*: Repeatedly performs the following steps, in order:
|
||
|
||
- [(18.1)](#cnt-18.1)
|
||
|
||
Evaluates try_acquire()[.](#cnt-18.1.sentence-1)
|
||
If the result is true, returns true[.](#cnt-18.1.sentence-2)
|
||
|
||
- [(18.2)](#cnt-18.2)
|
||
|
||
Blocks on *this until counter is greater than zero or until the timeout expires[.](#cnt-18.2.sentence-1)
|
||
If it is unblocked by the timeout expiring, returns false[.](#cnt-18.2.sentence-2)
|
||
|
||
The timeout expires ([[thread.req.timing]](thread.req.timing "32.2.4 Timing specifications"))
|
||
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)[.](#cnt-18.sentence-2)
|
||
|
||
[19](#cnt-19)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L10653)
|
||
|
||
*Throws*: Timeout-related exceptions ([[thread.req.timing]](thread.req.timing "32.2.4 Timing specifications")), or system_error when a non-timeout-related exception is required ([[thread.req.exception]](thread.req.exception "32.2.2 Exceptions"))[.](#cnt-19.sentence-1)
|
||
|
||
[20](#cnt-20)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L10658)
|
||
|
||
*Error conditions*: Any of the error conditions
|
||
allowed for mutex types ([[thread.mutex.requirements.mutex]](thread.mutex.requirements.mutex "32.6.4.2 Mutex types"))[.](#cnt-20.sentence-1)
|