33 Thread support library [thread]

33.4 Mutual exclusion [thread.mutex]

33.4.3 Mutex requirements [thread.mutex.requirements]

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 notetry_­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 объектом.