33 Thread support library [thread]

33.4 Mutual exclusion [thread.mutex]

В этом разделе представлены механизмы взаимного исключения: мьютексы, блокировки и однократный вызов. Эти механизмы упрощают создание программ, свободных от расы ([intro.multithread]).

33.4.1 Header <mutex> synopsis [mutex.syn]

namespace std {
  class mutex;
  class recursive_mutex;
  class timed_mutex;
  class recursive_timed_mutex;

  struct defer_lock_t { explicit defer_lock_t() = default; };
  struct try_to_lock_t { explicit try_to_lock_t() = default; };
  struct adopt_lock_t { explicit adopt_lock_t() = default; };

  inline constexpr defer_lock_t  defer_lock { };
  inline constexpr try_to_lock_t try_to_lock { };
  inline constexpr adopt_lock_t  adopt_lock { };

  template <class Mutex> class lock_guard;
  template <class... MutexTypes> class scoped_lock;
  template <class Mutex> class unique_lock;

  template <class Mutex>
    void swap(unique_lock<Mutex>& x, unique_lock<Mutex>& y) noexcept;

  template <class L1, class L2, class... L3> int try_lock(L1&, L2&, L3&...);
  template <class L1, class L2, class... L3> void lock(L1&, L2&, L3&...);

  struct once_flag;

  template<class Callable, class... Args>
    void call_once(once_flag& flag, Callable&& func, Args&&... args);
}

33.4.2 Header <shared_­mutex> synopsis [shared_mutex.syn]

namespace std {
  class shared_mutex;
  class shared_timed_mutex;
  template <class Mutex> class shared_lock;
  template <class Mutex>
    void swap(shared_lock<Mutex>& x, shared_lock<Mutex>& y) noexcept;
}

33.4.3 Mutex requirements [thread.mutex.requirements]

33.4.3.1 In general [thread.mutex.requirements.general]

Объект мьютекса обеспечивает защиту от гонок данных и обеспечивает безопасную синхронизацию данных между ними execution agents. Агент выполнения owns - мьютекс с момента успешного вызова одной из функций блокировки до вызова разблокировки. Мьютексы могут быть рекурсивными или нерекурсивными и могут предоставлять одновременное владение одному или нескольким агентам выполнения. Поставляются как рекурсивные, так и нерекурсивные мьютексы.

33.4.3.2 Mutex types [thread.mutex.requirements.mutex]

mutex types Стандартные библиотеки типов mutex, recursive_­mutex, timed_­mutex, recursive_­timed_­mutex, shared_­mutex, и shared_­timed_­mutex. Они должны соответствовать требованиям, изложенным в этом разделе. В этом описании m обозначает объект типа мьютекса.

Типы мьютексов должны соответствовать Lockable требованиям.

Типы мьютексов должны быть DefaultConstructible и Destructible. Если инициализация объекта типа мьютекса не удалась,system_­error должно быть выброшено исключение типа . Типы мьютексов нельзя копировать или перемещать.

Условия ошибки для кодов ошибок, если таковые имеются, сообщаемые функциями-членами типов мьютексов, должны быть:

  • resource_­unavailable_­try_­again - если какой-либо управляемый тип дескриптора недоступен.

  • operation_­not_­permitted - если поток не имеет права выполнять операцию.

  • invalid_­argument - если какой-либо собственный тип дескриптора, манипулируемый как часть конструкции мьютекса, неверен.

Реализация должна обеспечивать операции блокировки и разблокировки, как описано ниже. В целях определения наличия гонки данных они ведут себя как атомарные операции ([intro.multithread]). Операции блокировки и разблокировки на одном мьютексе должны выполняться в едином общем порядке. [ Note: Это можно рассматривать как modification order мьютекс. ] [ Создание и уничтожение объекта типа мьютекса не обязательно должно быть потокобезопасным; следует использовать другую синхронизацию, чтобы гарантировать, что объекты мьютекса инициализированы и видны другим потокам. ] end noteNote: end note

Выражение m.lock() должно быть правильно сформированным и иметь следующую семантику:

Requires: Если m имеет тип mutex, timed_­mutex, shared_­mutexили shared_­timed_­mutex, вызывающий поток не является владельцем мьютекса.

Effects: Блокирует вызывающий поток до тех пор, пока для вызывающего потока не будет получено право владения мьютексом.

Postconditions: Вызывающий поток владеет мьютексом.

Return type: void.

Synchronization: Предыдущие unlock() операции на одном и том же объекте являются synchronize with этой операцией.

Throws: system_­error когда требуется исключение ([thread.req.exception]).

Error conditions:

  • operation_­not_­permitted - если поток не имеет права выполнять операцию.

  • resource_­deadlock_­would_­occur - если реализация обнаруживает, что возникнет тупик.

Выражение m.try_­lock() должно быть правильно сформированным и иметь следующую семантику:

Requires: Если m имеет тип mutex, timed_­mutex, shared_­mutexили shared_­timed_­mutex, вызывающий поток не является владельцем мьютекса.

Effects: Пытается получить право собственности на мьютекс для вызывающего потока без блокировки. Если право собственности не получено, нет никакого эффекта и try_­lock() немедленно возвращается. Реализация может не получить блокировку, даже если она не удерживается каким-либо другим потоком. [ Note: Этот ложный сбой обычно встречается редко, но допускает интересные реализации, основанные на простом сравнении и обмене (пункт [atomics]). ] Реализация должна гарантировать, что не будет последовательного возврата в отсутствие конкурирующих захватов мьютексов. end note try_­lock() false

Return type: bool.

Returns: true если право собственности на мьютекс было получено для вызывающего потока, в противном случае false.

Synchronization: Если try_­lock() возвращается true, предыдущие unlock() операции с тем же объектом synchronize with это операция. [ Note: Так lock() как не синхронизируется с неудачным последующим try_­lock(), правила видимости достаточно слабы, чтобы о состоянии после отказа было бы мало что известно, даже при отсутствии ложных отказов. ] end note

Throws: Ничего такого.

Выражение m.unlock() должно быть правильно сформированным и иметь следующую семантику:

Requires: Вызывающий поток должен владеть мьютексом.

Effects: Освобождает вызывающий поток от владения мьютексом.

Return type: void.

Synchronization: Эта операция является synchronizes with последующими операциями блокировки, которые получают право собственности на один и тот же объект.

Throws: Ничего такого.

33.4.3.2.1 Class mutex [thread.mutex.class]

namespace std {
  class mutex {
  public:
    constexpr mutex() noexcept;
    ~mutex();

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

    void lock();
    bool try_lock();
    void unlock();

    using native_handle_type = implementation-defined; // See [thread.req.native]
    native_handle_type native_handle();                // See [thread.req.native]
  };
}

Класс mutex предоставляет нерекурсивный мьютекс с семантикой исключительного владения. Если один поток владеет объектом мьютекса, попытки другого потока получить право владения этим объектом будут неудачными (для try_­lock()) или блокируются (для lock()) до тех пор, пока поток-владелец не освободит владение с помощью вызова unlock().

[ Note: После того, как поток A вызвал unlock(), освободив мьютекс, другой поток B может заблокировать тот же мьютекс, заметить, что он больше не используется, разблокировать его и уничтожить до того, как поток, A кажется, вернется из своего вызова разблокировки. Для правильной обработки таких сценариев требуются реализации, если поток A не обращается к мьютексу после возврата вызова разблокировки. Эти случаи обычно возникают, когда объект со счетчиком ссылок содержит мьютекс, который используется для защиты счетчика ссылок. ]end note

Класс mutex должен удовлетворять всем требованиям mutex requirements. Это должен быть standard-layout class.

[ Note: Программа может заблокироваться, если поток, которому принадлежит mutex объект, вызывает lock() этот объект. Если реализация может обнаружить тупик, resource_­deadlock_­would_­occur может наблюдаться состояние ошибки. ] end note

Поведение программы не определено, если она уничтожает mutex объект, принадлежащий любому потоку, или поток завершается, пока владеет mutex объектом.

33.4.3.2.2 Class recursive_­mutex [thread.mutex.recursive]

namespace std {
  class recursive_mutex {
  public:
    recursive_mutex();
    ~recursive_mutex();

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

    void lock();
    bool try_lock() noexcept;
    void unlock();

    using native_handle_type = implementation-defined; // See [thread.req.native]
    native_handle_type native_handle();                // See [thread.req.native]
  };
}

Класс recursive_­mutex предоставляет рекурсивный мьютекс с семантикой исключительного владения. Если один поток владеет recursive_­mutex объектом, попытки другого потока получить право владения этим объектом завершатся ошибкой (для try_­lock()) или заблокируются (для lock()) до тех пор, пока первый поток полностью не освободит владение.

Класс recursive_­mutex должен удовлетворять всем требованиям mutex requirements. Это должен быть standard-layout class.

Поток, которому принадлежит recursive_­mutex объект, может получить дополнительные уровни владения путем вызова этого объекта lock() или try_­lock() для этого объекта. Не указано, сколько уровней владения может быть получено одним потоком. Если поток уже получил максимальный уровень владения recursive_­mutex объектом, дополнительные вызовы try_­lock() должны завершиться ошибкой, а дополнительные вызовы lock() вызовут исключение типа system_­error. Поток должен вызывать unlock() один раз для каждого уровня владения, полученного вызовами lock() и try_­lock(). Только когда все уровни владения высвобождены, право владения может быть приобретено другим потоком.

Поведение программы не определено, если:

  • он уничтожает recursive_­mutex объект, принадлежащий любому потоку, или

  • поток завершается при владении recursive_­mutex объектом.

33.4.3.3 Timed mutex types [thread.timedmutex.requirements]

timed mutex types Стандартные библиотеки типов timed_­mutex, recursive_­timed_­mutexи shared_­timed_­mutex. Они должны соответствовать требованиям, изложенным ниже. В этом описании m обозначает объект типа мьютекса, rel_­time обозначает объект создания экземпляра durationи abs_­time обозначает объект создания экземпляра time_­point.

Типы синхронизированных мьютексов должны соответствовать TimedLockable требованиям.

Выражение m.try_­lock_­for(rel_­time) должно быть правильно сформированным и иметь следующую семантику:

Requires: Если m имеет тип timed_­mutex или shared_­timed_­mutex, вызывающий поток не владеет мьютексом.

Effects: Функция пытается получить право собственности на мьютекс в течение относительного времени ожидания ([thread.req.timing]), указанного в rel_­time. Если время, указанное в, rel_­time меньше или равно rel_­time.zero(), функция пытается получить право владения без блокировки (как если бы путем вызова try_­lock()). Функция должна вернуться в течение тайм-аута, указанного в, rel_­time только если она получила право собственности на объект мьютекса. [ Note: Как и в случае try_­lock(), нет никакой гарантии, что право собственности будет получено, если блокировка доступна, но ожидается, что реализации приложат для этого серьезные усилия. ]end note

Return type: bool.

Returns: true если право собственности было получено, иначе false.

Synchronization: Если try_­lock_­for() возвращается true, предыдущие unlock() операции с тем же объектом synchronize with это операция.

Throws: Исключения, связанные с тайм-аутом ([thread.req.timing]).

Выражение m.try_­lock_­until(abs_­time) должно быть правильно сформированным и иметь следующую семантику:

Requires: Если m имеет тип timed_­mutex или shared_­timed_­mutex, вызывающий поток не владеет мьютексом.

Effects: Функция пытается получить право собственности на мьютекс. Если abs_­time он уже прошел, функция пытается получить право собственности без блокировки (как если бы путем вызова try_­lock()). Функция должна возвращаться до абсолютного тайм-аута ([thread.req.timing]), указанного в, abs_­time только если она получила право собственности на объект мьютекса. [ Note: Как и в случае try_­lock(), нет никакой гарантии, что право собственности будет получено, если блокировка доступна, но ожидается, что реализации приложат для этого серьезные усилия. ] end note

Return type: bool.

Returns: true если право собственности было получено, иначе false.

Synchronization: Если try_­lock_­until() возвращается true, предыдущие unlock() операции с тем же объектом synchronize with это операция.

Throws: Исключения, связанные с тайм-аутом ([thread.req.timing]).

33.4.3.3.1 Class timed_­mutex [thread.timedmutex.class]

namespace std {
  class timed_mutex {
  public:
    timed_mutex();
    ~timed_mutex();

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

    void lock();  // blocking
    bool try_lock();
    template <class Rep, class Period>
      bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
    template <class Clock, class Duration>
      bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);
    void unlock();

    using native_handle_type = implementation-defined; // See [thread.req.native]
    native_handle_type native_handle();                // See [thread.req.native]
  };
}

Класс timed_­mutex предоставляет нерекурсивный мьютекс с семантикой исключительного владения. Если один поток владеет timed_­mutex объектом, попытки другого потока приобрести в собственность этого объекта не получится (для try_­lock()) или блока (для lock(), try_­lock_­for()и try_­lock_­until()) до тех пор , владеющего нить не выпустило собственность с помощью вызова unlock() или вызова try_­lock_­for() или try_­lock_­until() тайма - аута (имеющего не удалось получить право собственности).

Класс timed_­mutex должен удовлетворять всем требованиям timed mutex requirements. Это должен быть standard-layout class.

Поведение программы не определено, если:

  • он уничтожает timed_­mutex объект, принадлежащий любому потоку,

  • поток , который владеет timed_­mutex объект звонки lock(), try_­lock(), try_­lock_­for()или try_­lock_­until() на этом объекте, или

  • поток завершается при владении timed_­mutex объектом.

33.4.3.3.2 Class recursive_­timed_­mutex [thread.timedmutex.recursive]

namespace std {
  class recursive_timed_mutex {
  public:
    recursive_timed_mutex();
    ~recursive_timed_mutex();

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

    void lock();  // blocking
    bool try_lock() noexcept;
    template <class Rep, class Period>
      bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
    template <class Clock, class Duration>
      bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);
    void unlock();

    using native_handle_type = implementation-defined; // See [thread.req.native]
    native_handle_type native_handle();                // See [thread.req.native]
  };
}

Класс recursive_­timed_­mutex предоставляет рекурсивный мьютекс с семантикой исключительного владения. Если один поток владеет recursive_­timed_­mutex объектом, попытки другого потока приобрести в собственность этого объекта не получится (для try_­lock()) или блок (для lock(), try_­lock_­for()и try_­lock_­until()) до тех пор , владеющее поток не полностью освобождается право собственности или вызов try_­lock_­for() или try_­lock_­until() тайм - аут (не добившись право собственности).

Класс recursive_­timed_­mutex должен удовлетворять всем требованиям timed mutex requirements. Это должен быть класс стандартной компоновки (пункт [class]).

Поток , который владеет recursive_­timed_­mutex объектом может приобрести дополнительные уровни владения вызывающего lock(), try_­lock(), try_­lock_­for()или try_­lock_­until() на этом объект. Не указано, сколько уровней владения может быть получено одним потоком. Если поток уже приобрел максимальный уровень владения для recursive_­timed_­mutex объекта, дополнительные вызовов try_­lock(), try_­lock_­for()или try_­lock_­until() ослабеют, а также дополнительные призывов lock() бросит исключение типа system_­error. Поток наречет unlock() один раз для каждого уровня владения приобретаемого вызовов lock(), try_­lock(), try_­lock_­for(), и try_­lock_­until(). Только когда все уровни владения были освобождены, владение объектом может быть приобретено другим потоком.

Поведение программы не определено, если:

  • он уничтожает recursive_­timed_­mutex объект, принадлежащий любому потоку, или

  • поток завершается при владении recursive_­timed_­mutex объектом.

33.4.3.4 Shared mutex types [thread.sharedmutex.requirements]

Стандартные библиотеки типов shared_­mutex и shared_­timed_­mutex есть shared mutex types. Типы общих мьютексов должны соответствовать requirements of mutex typesи дополнительно должны соответствовать требованиям, изложенным ниже. В этом описании m обозначает объект общего типа мьютекса.

В дополнение к режиму монопольного владения блокировкой, указанному в [thread.mutex.requirements.mutex], общие типы мьютексов предоставляют shared lock режим владения. Несколько агентов выполнения могут одновременно владеть общей блокировкой общего типа мьютекса. Но ни один агент выполнения не должен удерживать разделяемую блокировку, в то время как другой агент выполнения удерживает монопольную блокировку того же общего типа мьютекса, и наоборот. Максимальное количество исполнительных агентов, которые могут совместно использовать общую блокировку для одного общего типа мьютекса, не указано, но должно быть не менее 10000. Если больше, чем максимальное количество исполнительных агентов пытается получить общую блокировку, избыточные исполнительные агенты должны блокировать до тех пор, пока количество разделяемых блокировок не уменьшится ниже максимального значения другими исполнительными агентами, снимающими свою общую блокировку.

Выражение m.lock_­shared() должно быть правильно сформированным и иметь следующую семантику:

Requires: Вызывающий поток не владеет мьютексом.

Effects: Блокирует вызывающий поток до тех пор, пока для вызывающего потока не будет получено совместное владение мьютексом. Если выбрасывается исключение, то для текущего потока не должна быть получена разделяемая блокировка.

Postconditions: Вызывающий поток имеет общую блокировку мьютекса.

Return type: void.

Synchronization: Предыдущие unlock() операции на одном и том же объекте являются synchronize with этой операцией.

Throws: system_­error когда требуется исключение ([thread.req.exception]).

Error conditions:

  • operation_­not_­permitted - если поток не имеет права выполнять операцию.

  • resource_­deadlock_­would_­occur - если реализация обнаруживает, что возникнет тупик.

Выражение m.unlock_­shared() должно быть правильно сформированным и иметь следующую семантику:

Requires: Вызывающий поток должен удерживать общую блокировку мьютекса.

Effects: Снимает общую блокировку мьютекса, удерживаемого вызывающим потоком.

Return type: void.

Synchronization: Эта операция является synchronizes with последующими lock() операциями, которые получают право собственности на один и тот же объект.

Throws: Ничего такого.

Выражение m.try_­lock_­shared() должно быть правильно сформированным и иметь следующую семантику:

Requires: Вызывающий поток не владеет мьютексом.

Effects: Пытается получить совместное владение мьютексом для вызывающего потока без блокировки. Если долевое владение не получено, нет эффекта и try_­lock_­shared() немедленно возвращается. Реализация может не получить блокировку, даже если она не удерживается каким-либо другим потоком.

Return type: bool.

Returns: true если была приобретена блокировка долевого владения, в false противном случае.

Synchronization: Если try_­lock_­shared() возвращается true, предыдущие unlock() операции с тем же объектом synchronize with это операция.

Throws: Ничего такого.

33.4.3.4.1 Class shared_mutex [thread.sharedmutex.class]

namespace std {
  class shared_mutex {
  public:
    shared_mutex();
    ~shared_mutex();

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

    // Exclusive ownership
    void lock(); // blocking
    bool try_lock();
    void unlock();

    // Shared ownership
    void lock_shared(); // blocking
    bool try_lock_shared();
    void unlock_shared();

    using native_handle_type = implementation-defined; // See [thread.req.native]
    native_handle_type native_handle();                // See [thread.req.native]
  };
}

Класс shared_­mutex предоставляет нерекурсивный мьютекс с семантикой совместного владения.

Класс shared_­mutex должен удовлетворять всем требованиям shared mutex requirements. Это должен быть класс стандартной компоновки (пункт [class]).

Поведение программы не определено, если:

  • он уничтожает shared_­mutex объект, принадлежащий любому потоку,

  • поток пытается рекурсивно получить право собственности на a shared_­mutex, или

  • поток завершается, когда ему принадлежит какое-либо право собственности на a shared_­mutex.

shared_­mutex может быть синонимом shared_­timed_­mutex.

33.4.3.5 Shared timed mutex types [thread.sharedtimedmutex.requirements]

Стандартный тип библиотеки shared_­timed_­mutex - это shared timed mutex type. Общие синхронизированных типы взаимных блокировок должны отвечать требованиям timed mutex types, shared mutex typesи , кроме того , должны отвечать требованиям , изложенным ниже. В этом описании m обозначает объект общего типа синхронизированного мьютекса, rel_­type обозначает объект создания экземпляра durationи abs_­time обозначает объект создания экземпляра time_­point.

Выражение m.try_­lock_­shared_­for(rel_­time) должно быть правильно сформированным и иметь следующую семантику:

Requires: Вызывающий поток не владеет мьютексом.

Effects: Пытается получить право владения разделяемой блокировкой для вызывающего потока в течение относительного времени ожидания ([thread.req.timing]), указанного в rel_­time. Если время, указанное в, rel_­time меньше или равно rel_­time.zero(), функция пытается получить право владения без блокировки (как если бы путем вызова try_­lock_­shared()). Функция должна вернуться в течение тайм-аута, указанного в, rel_­time только если она получила совместное владение объектом мьютекса. [ Note: Как и в случае try_­lock(), нет никакой гарантии, что право собственности будет получено, если блокировка доступна, но ожидается, что реализации приложат для этого серьезные усилия. ] Если выбрасывается исключение, то для текущего потока не должна быть получена разделяемая блокировка. end note

Return type: bool.

Returns: true если была получена разделяемая блокировка, false иначе.

Synchronization: Если try_­lock_­shared_­for() возвращается true, предыдущие unlock() операции с тем же объектом синхронизируются с ([intro.multithread]) этой операцией.

Throws: Исключения, связанные с тайм-аутом ([thread.req.timing]).

Выражение m.try_­lock_­shared_­until(abs_­time) должно быть правильно сформированным и иметь следующую семантику:

Requires: Вызывающий поток не владеет мьютексом.

Effects: Функция пытается получить совместное владение мьютексом. Если abs_­time он уже прошел, функция пытается получить совместное владение без блокировки (как если бы путем вызова try_­lock_­shared()). Функция должна возвращаться до истечения абсолютного тайм-аута ([thread.req.timing]), указанного в, abs_­time только если она получила совместное владение объектом мьютекса. [ Note: Как и в случае try_­lock(), нет никакой гарантии, что право собственности будет получено, если блокировка доступна, но ожидается, что реализации приложат для этого серьезные усилия. ] Если выбрасывается исключение, то для текущего потока не должна быть получена разделяемая блокировка. end note

Return type: bool.

Returns: true если была получена разделяемая блокировка, false иначе.

Synchronization: Если try_­lock_­shared_­until() возвращается true, предыдущие unlock() операции с тем же объектом синхронизируются с ([intro.multithread]) этой операцией.

Throws: Исключения, связанные с тайм-аутом ([thread.req.timing]).

33.4.3.5.1 Class shared_­timed_­mutex [thread.sharedtimedmutex.class]

namespace std {
  class shared_timed_mutex {
  public:
    shared_timed_mutex();
    ~shared_timed_mutex();

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

    // Exclusive ownership
    void lock();  // blocking
    bool try_lock();
    template <class Rep, class Period>
      bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
    template <class Clock, class Duration>
      bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);
    void unlock();

    // Shared ownership
    void lock_shared();  // blocking
    bool try_lock_shared();
    template <class Rep, class Period>
      bool
      try_lock_shared_for(const chrono::duration<Rep, Period>& rel_time);
    template <class Clock, class Duration>
      bool
      try_lock_shared_until(const chrono::time_point<Clock, Duration>& abs_time);
    void unlock_shared();
  };
}

Класс shared_­timed_­mutex предоставляет нерекурсивный мьютекс с семантикой совместного владения.

Класс shared_­timed_­mutex должен удовлетворять всем требованиям shared timed mutex requirements. Это должен быть класс стандартной компоновки (пункт [class]).

Поведение программы не определено, если:

  • он уничтожает shared_­timed_­mutex объект, принадлежащий любому потоку,

  • поток пытается рекурсивно получить право собственности на a shared_­timed_­mutex, или

  • поток завершается, когда ему принадлежит какое-либо право собственности на a shared_­timed_­mutex.

33.4.4 Locks [thread.lock]

A lock - это объект, который содержит ссылку на блокируемый объект и может разблокировать блокируемый объект во время разрушения блокировки (например, при выходе из области действия блока). Агент выполнения может использовать блокировку, чтобы помочь в управлении владением блокируемым объектом безопасным способом. Блокировка называется own блокируемым объектом, если он в настоящее время управляет владением этим блокируемым объектом для агента выполнения. Блокировка не управляет временем существования блокируемого объекта, на который она ссылается. [ Note: Замки предназначены для облегчения бремени разблокировки запираемого объекта как в нормальных, так и в исключительных обстоятельствах. ] end note

Некоторые конструкторы блокировок используют типы тегов, которые описывают, что следует делать с блокируемым объектом во время создания блокировки.

namespace std {
  struct defer_lock_t  { };     // do not acquire ownership of the mutex
  struct try_to_lock_t { };     // try to acquire ownership of the mutex
                                // without blocking
  struct adopt_lock_t  { };     // assume the calling thread has already
                                // obtained mutex ownership and manage it

  inline constexpr defer_lock_t   defer_lock { };
  inline constexpr try_to_lock_t  try_to_lock { };
  inline constexpr adopt_lock_t   adopt_lock { };
}

33.4.4.1 Class template lock_­guard [thread.lock.guard]

namespace std {
  template <class Mutex>
  class lock_guard {
  public:
    using mutex_type = Mutex;

    explicit lock_guard(mutex_type& m);
    lock_guard(mutex_type& m, adopt_lock_t);
    ~lock_guard();

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

  private:
    mutex_type& pm; // exposition only
  };

  template<class Mutex> lock_guard(lock_guard<Mutex>) -> lock_guard<Mutex>;
}

Объект типа lock_­guard контролирует владение блокируемым объектом в области. lock_­guard Объект сохраняет право собственности на блокируемый объект на протяжении всего lock_­guard объекта , lifetime. Поведение программы не определено, если блокируемый объект, на который указывает ссылка, pm не существует в течение всего времени существования lock_­guard объекта. Поставляемый Mutex тип должен соответствовать BasicLockable требованиям.

explicit lock_guard(mutex_type& m);

Requires: Если mutex_­type это не рекурсивный мьютекс, вызывающий поток не владеет мьютексом m.

Effects: Как будто мимо m.lock().

Postconditions: &pm == &m

lock_guard(mutex_type& m, adopt_lock_t);

Requires: Вызывающий поток владеет мьютексом m.

Postconditions: &pm == &m

Throws: Ничего такого.

~lock_guard();

Effects: Как будто мимо pm.unlock().

33.4.4.2 Class template scoped_­lock [thread.lock.scoped]

namespace std {
  template <class... MutexTypes>
  class scoped_lock {
  public:
    using mutex_type = Mutex;  // If MutexTypes... consists of the single type Mutex

    explicit scoped_lock(MutexTypes&... m);
    explicit scoped_lock(MutexTypes&... m, adopt_lock_t);
    ~scoped_lock();

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

  private:
    tuple<MutexTypes&...> pm; // exposition only
  };

  template<class... MutexTypes>
    scoped_lock(scoped_lock<MutexTypes...>) -> scoped_lock<MutexTypes...>;
}

Объект типа scoped_­lock контролирует владение блокируемыми объектами в области. scoped_­lock Объект сохраняет право собственности на запираемых объектов на всей территории scoped_­lock объекта , lifetime. Поведение программы не определено, если блокируемые объекты, на которые указывает ссылка pm , не существуют в течение всего времени существования scoped_­lock объекта. Когда sizeof...(MutexTypes) есть 1, поставляемый Mutex тип должен соответствовать BasicLockable требованиям. В противном случае каждый из типов мьютексов должен соответствовать Lockable требованиям.

explicit scoped_lock(MutexTypes&... m);

Requires: Если MutexTypes тип не является рекурсивным мьютексом, вызывающий поток не владеет соответствующим элементом мьютекса m.

Effects: Инициализируется pm с помощью tie(m...). Тогда если sizeof...(MutexTypes) есть 0, то никаких эффектов. В противном случае, если sizeof...(MutexTypes) есть 1, то m.lock(). В противном случае lock(m...).

explicit scoped_lock(MutexTypes&... m, adopt_lock_t);

Requires: Вызывающий поток владеет всеми мьютексами в m.

Effects: Инициализируется pm с помощью tie(m...).

Throws: Ничего такого.

~scoped_lock();

Effects: Для всех i ин [0, sizeof...(MutexTypes)), get<i>(pm).unlock().

33.4.4.3 Class template unique_­lock [thread.lock.unique]

namespace std {
  template <class Mutex>
  class unique_lock {
  public:
    using mutex_type = Mutex;

    // [thread.lock.unique.cons], construct/copy/destroy
    unique_lock() noexcept;
    explicit unique_lock(mutex_type& m);
    unique_lock(mutex_type& m, defer_lock_t) noexcept;
    unique_lock(mutex_type& m, try_to_lock_t);
    unique_lock(mutex_type& m, adopt_lock_t);
    template <class Clock, class Duration>
      unique_lock(mutex_type& m, const chrono::time_point<Clock, Duration>& abs_time);
    template <class Rep, class Period>
      unique_lock(mutex_type& m, const chrono::duration<Rep, Period>& rel_time);
    ~unique_lock();

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

    unique_lock(unique_lock&& u) noexcept;
    unique_lock& operator=(unique_lock&& u);

    // [thread.lock.unique.locking], locking
    void lock();
    bool try_lock();

    template <class Rep, class Period>
      bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
    template <class Clock, class Duration>
      bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);

    void unlock();

    // [thread.lock.unique.mod], modifiers
    void swap(unique_lock& u) noexcept;
    mutex_type* release() noexcept;

    // [thread.lock.unique.obs], observers
    bool owns_lock() const noexcept;
    explicit operator bool () const noexcept;
    mutex_type* mutex() const noexcept;

  private:
    mutex_type* pm; // exposition only
    bool owns;      // exposition only
  };

  template<class Mutex> unique_lock(unique_lock<Mutex>) -> unique_lock<Mutex>;

  template <class Mutex>
    void swap(unique_lock<Mutex>& x, unique_lock<Mutex>& y) noexcept;
}

Объект типа unique_­lock контролирует владение блокируемым объектом в области. Право собственности на запираемый объект может быть приобретено во время строительства или после строительства и может быть передано после приобретения другому unique_­lock объекту. Объекты типа unique_­lock нельзя копировать, но можно перемещать. Поведение программы не определено , если содержащаяся указатель pm не является нулевым , и блокируемый объект , на который указывает pm не существует для всего оставшегося lifetime от unique_­lock объекта. Поставляемый Mutex тип должен соответствовать BasicLockable требованиям.

[ Note: unique_­lock<Mutex> соответствует BasicLockable требованиям. Если Mutex соответствует Lockable требованиям, unique_­lock<Mutex> также соответствует Lockable требованиям; если Mutex соответствует TimedLockable требованиям, unique_­lock<Mutex> также соответствует TimedLockable требованиям. ] end note

33.4.4.3.1 unique_­lock constructors, destructor, and assignment [thread.lock.unique.cons]

unique_lock() noexcept;

Effects: Создает объект типа unique_­lock.

Postconditions: pm == 0 и owns == false.

explicit unique_lock(mutex_type& m);

Requires: Если mutex_­type это не рекурсивный мьютекс, вызывающий поток не владеет мьютексом.

Effects: Создает объект типа unique_­lock и вызывает m.lock().

Postconditions: pm == addressof(m) и owns == true.

unique_lock(mutex_type& m, defer_lock_t) noexcept;

Effects: Создает объект типа unique_­lock.

Postconditions: pm == addressof(m) и owns == false.

unique_lock(mutex_type& m, try_to_lock_t);

Requires: Поставляемый Mutex тип должен соответствовать Lockable требованиям. Если mutex_­type это не рекурсивный мьютекс, вызывающий поток не владеет мьютексом.

Effects: Создает объект типа unique_­lock и вызывает m.try_­lock().

Postconditions: pm == addressof(m) и owns == res, где res - значение, возвращаемое вызовом m.try_­lock().

unique_lock(mutex_type& m, adopt_lock_t);

Requires: Вызывающий поток владеет мьютексом.

Effects: Создает объект типа unique_­lock.

Postconditions: pm == addressof(m) и owns == true.

Throws: Ничего такого.

template <class Clock, class Duration> unique_lock(mutex_type& m, const chrono::time_point<Clock, Duration>& abs_time);

Requires: Если mutex_­type это не рекурсивный мьютекс, вызывающий поток не владеет мьютексом. Поставляемый Mutex тип должен соответствовать TimedLockable требованиям.

Effects: Создает объект типа unique_­lock и вызывает m.try_­lock_­until(abs_­time).

Postconditions: pm == addressof(m) и owns == res, где res - значение, возвращаемое вызовом m.try_­lock_­until(abs_­time).

template <class Rep, class Period> unique_lock(mutex_type& m, const chrono::duration<Rep, Period>& rel_time);

Requires: Если mutex_­type это не рекурсивный мьютекс, вызывающий поток не владеет мьютексом. Поставляемый Mutex тип должен соответствовать TimedLockable требованиям.

Effects: Создает объект типа unique_­lock и вызывает m.try_­lock_­for(rel_­time).

Postconditions: pm == addressof(m) и owns == res, где res - значение, возвращаемое вызовом m.try_­lock_­for(rel_­time).

unique_lock(unique_lock&& u) noexcept;

Postconditions: pm == u_­p.pm и owns == u_­p.owns (где u_­p - состояние u непосредственно перед этой конструкцией), u.pm == 0 и u.owns == false.

unique_lock& operator=(unique_lock&& u);

Effects: Если owns звонит pm->unlock().

Postconditions: pm == u_­p.pm и owns == u_­p.owns (где u_­p - состояние u непосредственно перед этой конструкцией), u.pm == 0 и u.owns == false.

[ Note: С рекурсивным мьютексом оба *this и u могут владеть одним и тем же мьютексом до назначения. В этом случае *this после присвоения мьютекс будет принадлежать и u не будет. ] end note

Throws: Ничего такого.

~unique_lock();

Effects: Если owns звонит pm->unlock().

33.4.4.3.2 unique_­lock locking [thread.lock.unique.locking]

void lock();

Effects: Как будто мимо pm->lock().

Postconditions: owns == true.

Throws: Любое выброшенное исключение pm->lock(). system_­error когда требуется исключение ([thread.req.exception]).

Error conditions:

  • operation_­not_­permitted - если pm есть nullptr.

  • resource_­deadlock_­would_­occur - если на входе owns есть true.

bool try_lock();

Requires: Поставляемый Mutex должен соответствовать Lockable требованиям.

Effects: Как будто мимо pm->try_­lock().

Returns: Значение, возвращаемое вызовом try_­lock().

Postconditions: owns == res, где res - значение, возвращаемое вызовом try_­lock().

Throws: Любое выброшенное исключение pm->try_­lock(). system_­error когда требуется исключение ([thread.req.exception]).

Error conditions:

  • operation_­not_­permitted - если pm есть nullptr.

  • resource_­deadlock_­would_­occur - если на входе owns есть true.

template <class Clock, class Duration> bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);

Requires: Поставляемый Mutex тип должен соответствовать TimedLockable требованиям.

Effects: Как будто мимо pm->try_­lock_­until(abs_­time).

Returns: Значение, возвращаемое вызовом try_­lock_­until(abs_­time).

Postconditions: owns == res, где res - значение, возвращаемое вызовом try_­lock_­until(abs_­time).

Throws: Любое выброшенное исключение pm->try_­lock_­until(). system_­error когда требуется исключение ([thread.req.exception]).

Error conditions:

  • operation_­not_­permitted - если pm есть nullptr.

  • resource_­deadlock_­would_­occur - если на входе owns есть true.

template <class Rep, class Period> bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);

Requires: Поставляемый Mutex тип должен соответствовать TimedLockable требованиям.

Effects: Как будто мимо pm->try_­lock_­for(rel_­time).

Returns: Значение, возвращаемое вызовом try_­lock_­until(rel_­time).

Postconditions: owns == res, где res - значение, возвращаемое вызовом try_­lock_­for(rel_­time).

Throws: Любое выброшенное исключение pm->try_­lock_­for(). system_­error когда требуется исключение ([thread.req.exception]).

Error conditions:

  • operation_­not_­permitted - если pm есть nullptr.

  • resource_­deadlock_­would_­occur - если на входе owns есть true.

void unlock();

Effects: Как будто мимо pm->unlock().

Postconditions: owns == false.

Throws: system_­error когда требуется исключение ([thread.req.exception]).

Error conditions:

  • operation_­not_­permitted - если на входе owns есть false.

33.4.4.3.3 unique_­lock modifiers [thread.lock.unique.mod]

void swap(unique_lock& u) noexcept;

Effects: Меняет местами элементы данных *this и u.

mutex_type* release() noexcept;

Returns: Предыдущее значение pm.

Postconditions: pm == 0 и owns == false.

template <class Mutex> void swap(unique_lock<Mutex>& x, unique_lock<Mutex>& y) noexcept;

Effects: Как будто мимо x.swap(y).

33.4.4.3.4 unique_­lock observers [thread.lock.unique.obs]

bool owns_lock() const noexcept;

Returns: owns.

explicit operator bool() const noexcept;

Returns: owns.

mutex_type *mutex() const noexcept;

Returns: pm.

33.4.4.4 Class template shared_­lock [thread.lock.shared]

namespace std {
  template <class Mutex>
  class shared_lock {
  public:
    using mutex_type = Mutex;

    // [thread.lock.shared.cons], construct/copy/destroy
    shared_lock() noexcept;
    explicit shared_lock(mutex_type& m);  // blocking
    shared_lock(mutex_type& m, defer_lock_t) noexcept;
    shared_lock(mutex_type& m, try_to_lock_t);
    shared_lock(mutex_type& m, adopt_lock_t);
    template <class Clock, class Duration>
      shared_lock(mutex_type& m,
                  const chrono::time_point<Clock, Duration>& abs_time);
    template <class Rep, class Period>
      shared_lock(mutex_type& m,
                const chrono::duration<Rep, Period>& rel_time);
    ~shared_lock();

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

    shared_lock(shared_lock&& u) noexcept;
    shared_lock& operator=(shared_lock&& u) noexcept;

    // [thread.lock.shared.locking], locking
    void lock();  // blocking
    bool try_lock();
    template <class Rep, class Period>
      bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
    template <class Clock, class Duration>
      bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);
    void unlock();

    // [thread.lock.shared.mod], modifiers
    void swap(shared_lock& u) noexcept;
    mutex_type* release() noexcept;

    // [thread.lock.shared.obs], observers
    bool owns_lock() const noexcept;
    explicit operator bool () const noexcept;
    mutex_type* mutex() const noexcept;

  private:
    mutex_type* pm; // exposition only
    bool owns;      // exposition only
  };

  template<class Mutex> shared_lock(shared_lock<Mutex>) -> shared_lock<Mutex>;

  template <class Mutex>
    void swap(shared_lock<Mutex>& x, shared_lock<Mutex>& y) noexcept;
}

Объект типа shared_­lock контролирует совместное владение блокируемым объектом в области. Совместное владение запираемым объектом может быть приобретено во время строительства или после строительства и может быть передано после приобретения другому shared_­lock объекту. Объекты типа shared_­lock нельзя копировать, но можно перемещать. Поведение программы не определено , если содержащаяся указатель pm не является нулевым , и блокируемый объект , на который указывает pm не существует для всего оставшегося lifetime от shared_­lock объекта. Поставляемый Mutex тип должен соответствовать требованиям shared mutex requirements.

[ Note: shared_­lock<Mutex> соответствует TimedLockable требованиям ([thread.req.lockable.timed]). ] end note

33.4.4.4.1 shared_­lock constructors, destructor, and assignment [thread.lock.shared.cons]

shared_lock() noexcept;

Effects: Создает объект типа shared_­lock.

Postconditions: pm == nullptr и owns == false.

explicit shared_lock(mutex_type& m);

Requires: Вызывающий поток не владеет мьютексом ни в каком режиме владения.

Effects: Создает объект типа shared_­lock и вызывает m.lock_­shared().

Postconditions: pm == addressof(m) и owns == true.

shared_lock(mutex_type& m, defer_lock_t) noexcept;

Effects: Создает объект типа shared_­lock.

Postconditions: pm == addressof(m) и owns == false.

shared_lock(mutex_type& m, try_to_lock_t);

Requires: Вызывающий поток не владеет мьютексом ни в каком режиме владения.

Effects: Создает объект типа shared_­lock и вызывает m.try_­lock_­shared().

Postconditions: pm == addressof(m) и owns == res где res значение, возвращаемое вызовом m.try_­lock_­shared().

shared_lock(mutex_type& m, adopt_lock_t);

Requires: Вызывающий поток имеет совместное владение мьютексом.

Effects: Создает объект типа shared_­lock.

Postconditions: pm == addressof(m) и owns == true.

template <class Clock, class Duration> shared_lock(mutex_type& m, const chrono::time_point<Clock, Duration>& abs_time);

Requires: Вызывающий поток не владеет мьютексом ни в каком режиме владения.

Effects: Создает объект типа shared_­lock и вызывает m.try_­lock_­shared_­until(abs_­time).

Postconditions: pm == addressof(m) и owns == res где res значение, возвращаемое вызовом m.try_­lock_­shared_­until(abs_­time).

template <class Rep, class Period> shared_lock(mutex_type& m, const chrono::duration<Rep, Period>& rel_time);

Requires: Вызывающий поток не владеет мьютексом ни в каком режиме владения.

Effects: Создает объект типа shared_­lock и вызывает m.try_­lock_­shared_­for(rel_­time).

Postconditions: pm == addressof(m) и owns == res где res значение, возвращаемое вызовом m.try_­lock_­shared_­for(rel_­time).

~shared_lock();

Effects: Если owns звонит pm->unlock_­shared().

shared_lock(shared_lock&& sl) noexcept;

Postconditions: pm == sl_­p.pm и owns == sl_­p.owns (где sl_­p - состояние sl непосредственно перед этой конструкцией), sl.pm == nullptr и sl.owns == false.

shared_lock& operator=(shared_lock&& sl) noexcept;

Effects: Если owns звонит pm->unlock_­shared().

Postconditions: pm == sl_­p.pm и owns == sl_­p.owns (где sl_­p - состояние sl непосредственно перед этим назначением), sl.pm == nullptr и sl.owns == false.

33.4.4.4.2 shared_­lock locking [thread.lock.shared.locking]

void lock();

Effects: Как будто мимо pm->lock_­shared().

Postconditions: owns == true.

Throws: Любое выброшенное исключение pm->lock_­shared(). system_­error когда требуется исключение ([thread.req.exception]).

Error conditions:

  • operation_­not_­permitted - если pm есть nullptr.

  • resource_­deadlock_­would_­occur - если на входе owns есть true.

bool try_lock();

Effects: Как будто мимо pm->try_­lock_­shared().

Returns: Значение, возвращаемое вызовом pm->try_­lock_­shared().

Postconditions: owns == res, где res - значение, возвращаемое вызовом pm->try_­lock_­shared().

Throws: Любое выброшенное исключение pm->try_­lock_­shared(). system_­error когда требуется исключение ([thread.req.exception]).

Error conditions:

  • operation_­not_­permitted - если pm есть nullptr.

  • resource_­deadlock_­would_­occur - если на входе owns есть true.

template <class Clock, class Duration> bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);

Effects: Как будто мимо pm->try_­lock_­shared_­until(abs_­time).

Returns: Значение, возвращаемое вызовом pm->try_­lock_­shared_­until(abs_­time).

Postconditions: owns == res, где res - значение, возвращаемое вызовом pm->try_­lock_­shared_­until(abs_­time).

Throws: Любое выброшенное исключение pm->try_­lock_­shared_­until(abs_­time). system_­error когда требуется исключение ([thread.req.exception]).

Error conditions:

  • operation_­not_­permitted - если pm есть nullptr.

  • resource_­deadlock_­would_­occur - если на входе owns есть true.

template <class Rep, class Period> bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);

Effects: Как будто мимо pm->try_­lock_­shared_­for(rel_­time).

Returns: Значение, возвращаемое вызовом pm->try_­lock_­shared_­for(rel_­time).

Postconditions: owns == res, где res - значение, возвращаемое вызовом pm->try_­lock_­shared_­for(rel_­time).

Throws: Любое выброшенное исключение pm->try_­lock_­shared_­for(rel_­time). system_­error когда требуется исключение ([thread.req.exception]).

Error conditions:

  • operation_­not_­permitted - если pm есть nullptr.

  • resource_­deadlock_­would_­occur - если на входе owns есть true.

void unlock();

Effects: Как будто мимо pm->unlock_­shared().

Postconditions: owns == false.

Throws: system_­error когда требуется исключение ([thread.req.exception]).

Error conditions:

  • operation_­not_­permitted - если на входе owns есть false.

33.4.4.4.3 shared_­lock modifiers [thread.lock.shared.mod]

void swap(shared_lock& sl) noexcept;

Effects: Меняет местами элементы данных *this и sl.

mutex_type* release() noexcept;

Returns: Предыдущее значение pm.

Postconditions: pm == nullptr и owns == false.

template <class Mutex> void swap(shared_lock<Mutex>& x, shared_lock<Mutex>& y) noexcept;

Effects: Как будто мимо x.swap(y).

33.4.4.4.4 shared_­lock observers [thread.lock.shared.obs]

bool owns_lock() const noexcept;

Returns: owns.

explicit operator bool() const noexcept;

Returns: owns.

mutex_type* mutex() const noexcept;

Returns: pm.

33.4.5 Generic locking algorithms [thread.lock.algorithm]

template <class L1, class L2, class... L3> int try_lock(L1&, L2&, L3&...);

Requires: Каждый тип параметра шаблона должен соответствовать Lockable требованиям. [ Шаблон класса отвечает этим требованиям при соответствующем инстанцирован. ]Note: unique_­lock end note

Effects: Вызывает try_­lock() каждый аргумент по порядку, начиная с первого, до тех пор, пока все аргументы не будут обработаны или вызов try_­lock() не завершится ошибкой, либо путем возврата, false либо путем выброса исключения. Если вызов для try_­lock() завершается неудачно, unlock() должны вызываться все предыдущие аргументы, и не должно быть никаких дальнейших вызовов try_­lock().

Returns: -1 если все вызовы try_­lock() возвращены true, в противном случае отсчитываемое от нуля значение индекса, указывающее аргумент, для которого был try_­lock() возвращен false.

template <class L1, class L2, class... L3> void lock(L1&, L2&, L3&...);

Requires: Каждый тип параметра шаблона должен отвечать Lockable требованиям, [ шаблонный класс отвечает этим требованиям при соответствующем инстанцирован. ]Note: unique_­lock end note

Effects: Все аргументы заблокированы через последовательность звонков lock(), try_­lock()или unlock() по каждому аргументу. Последовательность вызовов не должна приводить к тупиковой ситуации, но иначе не определена. [ Note: Должен использоваться алгоритм предотвращения взаимоблокировок, такой как попытка и откат, но конкретный алгоритм не указан, чтобы избежать чрезмерных ограничений реализаций. ] Если вызов или вызывает исключение, должен вызываться для любого аргумента, который был заблокирован вызовом или . end notelock() try_­lock() unlock() lock() try_­lock()

33.4.6 Call once [thread.once]

33.4.6.1 Struct once_­flag [thread.once.onceflag]

namespace std {
  struct once_flag {
    constexpr once_flag() noexcept;

    once_flag(const once_flag&) = delete;
    once_flag& operator=(const once_flag&) = delete;
  };
}

Класс once_­flag представляет собой непрозрачную структуру данных, которая call_­once используется для инициализации данных, не вызывая гонки данных или взаимоблокировки.

constexpr once_flag() noexcept;

Effects: Создает объект типа once_­flag.

Synchronization: Строительство once_­flag объекта не синхронизировано.

Postconditions: Внутреннее состояние объекта устанавливается таким образом, чтобы указать при вызове call_­once объекта в качестве начального аргумента, что функция не была вызвана.

33.4.6.2 Function call_­once [thread.once.callonce]

template<class Callable, class... Args> void call_once(once_flag& flag, Callable&& func, Args&&... args);

Requires:

INVOKE(std::forward<Callable>(func), std::forward<Args>(args)...)

(см. [func.require]) должно быть допустимым выражением.

Effects: Выполнение того, call_­once что не вызывает его, func есть passive казнь. Выполнение call_­once этого вызова func - это active исполнение. Активное исполнение вызовет INVOKE(​std​::​forward<Callable>(func), std​::​forward<Args>(args)...). Если такой вызов func вызывает исключение, выполняется exceptional, в противном случае - это так returning. Исключительное выполнение должно передать исключение вызывающему call_­once. Среди всех выполнений call_­once для любого данного once_­flag: не более одного должно быть повторным выполнением; если есть возвращающееся исполнение, это будет последнее активное выполнение; и есть пассивные казни, только если есть возвращающееся исполнение. [ Note: Пассивное выполнение позволяет другим потокам надежно наблюдать за результатами, полученными ранее выполненным возвратом. ] end note

Synchronization: Для любого данного once_­flag: все активные исполнения происходят в общем порядке; завершение активного выполнения synchronizes with начало следующего в этом общем порядке; и возвращающееся выполнение синхронизируется с возвратом из всех пассивных исполнений.

Throws: system_­error когда требуется исключение ([thread.req.exception]) или любое исключение, созданное func.

[Example:

// global flag, regular function
void init();
std::once_flag flag;

void f() {
  std::call_once(flag, init);
}

// function static flag, function object
struct initializer {
  void operator()();
};

void g() {
  static std::once_flag flag2;
  std::call_once(flag2, initializer());
}

// object flag, member function
class information {
  std::once_flag verified;
  void verifier();
public:
  void verify() { std::call_once(verified, &information::verifier, *this); }
};

end example]