33 Thread support library [thread]

33.2 Requirements [thread.req]

33.2.1 Template parameter names [thread.req.paramname]

Throughout this Clause, the names of template parameters are used to express type requirements. If a template parameter is named Predicate, operator() applied to the template argument shall return a value that is convertible to bool.

33.2.2 Exceptions [thread.req.exception]

Some functions described in this Clause are specified to throw exceptions of type system_­error. Such exceptions shall be thrown if any of the function's error conditions is detected or a call to an operating system or other underlying API results in an error that prevents the library function from meeting its specifications. Failure to allocate storage shall be reported as described in [res.on.exception.handling].

[Example: Consider a function in this clause that is specified to throw exceptions of type system_­error and specifies error conditions that include operation_­not_­permitted for a thread that does not have the privilege to perform the operation. Assume that, during the execution of this function, an errno of EPERM is reported by a POSIX API call used by the implementation. Since POSIX specifies an errno of EPERM when “the caller does not have the privilege to perform the operation”, the implementation maps EPERM to an error_­condition of operation_­not_­permitted ([syserr]) and an exception of type system_­error is thrown. end example]

The error_­code reported by such an exception's code() member function shall compare equal to one of the conditions specified in the function's error condition element.

33.2.3 Native handles [thread.req.native]

Several classes described in this Clause have members native_­handle_­type and native_­handle. The presence of these members and their semantics is implementation-defined. [Note: These members allow implementations to provide access to implementation details. Their names are specified to facilitate portable compile-time detection. Actual use of these members is inherently non-portable. end note]

33.2.4 Timing specifications [thread.req.timing]

Several functions described in this Clause take an argument to specify a timeout. These timeouts are specified as either a duration or a time_­point type as specified in [time].

Implementations necessarily have some delay in returning from a timeout. Any overhead in interrupt response, function return, and scheduling induces a “quality of implementation” delay, expressed as duration Di. Ideally, this delay would be zero. Further, any contention for processor and memory resources induces a “quality of management” delay, expressed as duration Dm. The delay durations may vary from timeout to timeout, but in all cases shorter is better.

The member functions whose names end in _­for take an argument that specifies a duration. These functions produce relative timeouts. Implementations should use a steady clock to measure time for these functions.330 Given a duration argument Dt, the real-time duration of the timeout is Dt+Di+Dm.

The member functions whose names end in _­until take an argument that specifies a time point. These functions produce absolute timeouts. Implementations should use the clock specified in the time point to measure time for these functions. Given a clock time point argument Ct, the clock time point of the return from timeout should be Ct+Di+Dm when the clock is not adjusted during the timeout. If the clock is adjusted to the time Ca during the timeout, the behavior should be as follows:

  • if Ca>Ct, the waiting function should wake as soon as possible, i.e. Ca+Di+Dm, since the timeout is already satisfied. [Note: This specification may result in the total duration of the wait decreasing when measured against a steady clock. end note]

  • if Ca<=Ct, the waiting function should not time out until Clock​::​now() returns a time Cn>=Ct, i.e. waking at Ct+Di+Dm. [Note: When the clock is adjusted backwards, this specification may result in the total duration of the wait increasing when measured against a steady clock. When the clock is adjusted forwards, this specification may result in the total duration of the wait decreasing when measured against a steady clock. end note]

An implementation shall return from such a timeout at any point from the time specified above to the time it would return from a steady-clock relative timeout on the difference between Ct and the time point of the call to the _­until function. [Note: Implementations should decrease the duration of the wait when the clock is adjusted forwards. end note]

[Note: If the clock is not synchronized with a steady clock, e.g., a CPU time clock, these timeouts might not provide useful functionality. end note]

The resolution of timing provided by an implementation depends on both operating system and hardware. The finest resolution provided by an implementation is called the native resolution.

Implementation-provided clocks that are used for these functions shall meet the TrivialClock requirements.

A function that takes an argument which specifies a timeout will throw if, during its execution, a clock, time point, or time duration throws an exception. Such exceptions are referred to as timeout-related exceptions. [Note: Instantiations of clock, time point and duration types supplied by the implementation as specified in [time.clock] do not throw exceptions. end note]

All implementations for which standard time units are meaningful must necessarily have a steady clock within their hardware implementation.

33.2.5 Requirements for Lockable types [thread.req.lockable]

33.2.5.1 In general [thread.req.lockable.general]

An execution agent is an entity such as a thread that may perform work in parallel with other execution agents. [Note: Implementations or users may introduce other kinds of agents such as processes or thread-pool tasks. end note] The calling agent is determined by context, e.g. the calling thread that contains the call, and so on.

[Note: Some lockable objects are “agent oblivious” in that they work for any execution agent model because they do not determine or store the agent's ID (e.g., an ordinary spin lock). end note]

The standard library templates unique_­lock ([thread.lock.unique]), shared_­lock ([thread.lock.shared]), scoped_­lock ([thread.lock.scoped]), lock_­guard ([thread.lock.guard]), lock, try_­lock ([thread.lock.algorithm]), and condition_­variable_­any ([thread.condition.condvarany]) all operate on user-supplied lockable objects. The BasicLockable requirements, the Lockable requirements, and the TimedLockable requirements list the requirements imposed by these library types in order to acquire or release ownership of a lock by a given execution agent. [Note: The nature of any lock ownership and any synchronization it may entail are not part of these requirements. end note]

33.2.5.2 BasicLockable requirements [thread.req.lockable.basic]

A type L meets the BasicLockable requirements if the following expressions are well-formed and have the specified semantics (m denotes a value of type L).

m.lock()

Effects: Blocks until a lock can be acquired for the current execution agent. If an exception is thrown then a lock shall not have been acquired for the current execution agent.

m.unlock()

Requires: The current execution agent shall hold a lock on m.

Effects: Releases a lock on m held by the current execution agent.

Throws: Nothing.

33.2.5.3 Lockable requirements [thread.req.lockable.req]

A type L meets the Lockable requirements if it meets the BasicLockable requirements and the following expressions are well-formed and have the specified semantics (m denotes a value of type L).

m.try_lock()

Effects: Attempts to acquire a lock for the current execution agent without blocking. If an exception is thrown then a lock shall not have been acquired for the current execution agent.

Return type: bool.

Returns: true if the lock was acquired, false otherwise.

33.2.5.4 TimedLockable requirements [thread.req.lockable.timed]

A type L meets the TimedLockable requirements if it meets the Lockable requirements and the following expressions are well-formed and have the specified semantics (m denotes a value of type L, rel_­time denotes a value of an instantiation of duration, and abs_­time denotes a value of an instantiation of time_­point).

m.try_lock_for(rel_time)

Effects: Attempts to acquire a lock for the current execution agent within the relative timeout ([thread.req.timing]) specified by rel_­time. The function shall not return within the timeout specified by rel_­time unless it has obtained a lock on m for the current execution agent. If an exception is thrown then a lock shall not have been acquired for the current execution agent.

Return type: bool.

Returns: true if the lock was acquired, false otherwise.

m.try_lock_until(abs_time)

Effects: Attempts to acquire a lock for the current execution agent before the absolute timeout ([thread.req.timing]) specified by abs_­time. The function shall not return before the timeout specified by abs_­time unless it has obtained a lock on m for the current execution agent. If an exception is thrown then a lock shall not have been acquired for the current execution agent.

Return type: bool.

Returns: true if the lock was acquired, false otherwise.

33.2.6 decay_­copy [thread.decaycopy]

In several places in this Clause the operation DECAY_­COPY(x) is used. All such uses mean call the function decay_­copy(x) and use the result, where decay_­copy is defined as follows:

template <class T> decay_t<T> decay_copy(T&& v)
  { return std::forward<T>(v); }