Почему конструктор std::mutex в C++ не бросает?
pthread_mutex_init()
функция возвращает ненулевое значение, когда не удается инициализировать мьютекс, в то время как std::mutex
класс в C++11 имеет конструктор noexcept
,
Скажем, кто-то решил реализовать класс мьютекса C++ поверх мьютекса pthreads. Он оборачивает мьютекс pthread внутри класса и пытается инициализировать его, вызывая pthread_mutex_init() в конструкторе. Если вызов функции возвращает значение, отличное от нуля, что означает ошибку, об ошибке нельзя сразу сообщить, так как конструктор не может выдать. Одна альтернатива - генерировать исключение до тех пор, пока метод блокировки не будет вызван в мьютексе. Но этот подход кажется неправильным.
Есть ли другой способ сделать это, используя некоторые хитрые уловки, чтобы гарантировать, что инициализация мьютекса всегда будет успешной?
Обновление: я собираюсь ответить на свой вопрос по этому вопросу. В соответствии с языковым стандартом в 30.4.1.3 pge 1163 написано: "Если инициализация объекта типа мьютекса не удалась, должно быть сгенерировано исключение типа system_error".
И функция noexcept может бросить внутрь тела функции, просто вызывающая сторона не может поймать исключение. Если исключение выдается внутри функции noexcept, будет вызван std::terminate.
3 ответа
Конструктор std::mutex
должно быть constexpr
(так что глобальный std::mutex
может быть статически инициализирован и использован в конструкторах других глобальных объектов), и поэтому не может вызывать pthread_mutex_init
(или аналогичные функции) на всех.
Вместо этого он должен использовать PTHREAD_MUTEX_INITIALIZER
или эквивалент (например, SRWLOCK_INIT
в Windows) для статической инициализации мьютекса.
Согласно спецификации C++17:
33.4.3.2 Типы мьютексов [thread.mutex.requirements.mutex]
- Типы мьютексов должны быть DefaultConstructible и Destructible. Если инициализация объекта типа мьютекса не удалась, должно быть выброшено исключение типа system_error. Типы мьютексов нельзя копировать или перемещать.
Так mutex type
может вызвать исключение, а не std::mutex
. std::mutex
имеет noexcept
, но std::recursive_mutex
нет, и они оба mutex types
:
33.4.3.2.1 Мьютекс класса [thread.mutex.class]
constexpr mutex() noexcept;
33.4.3.2.2 Класс recursive_mutex [thread.mutex.recursive]
recursive_mutex();
Кроме того:
20.5.5.12 Ограничения на обработку исключений [res.on.exception.handling]
Любая из функций, определенных в стандартной библиотеке C++, может сообщать о сбое, генерируя исключение типа, описанного в его параграфе Throws:, или типа, производного от типа, указанного в параграфе Throws:, которое будет перехвачено обработчиком исключений. для базового типа.
Функции, определенные в стандартной библиотеке C++, которые не имеют абзаца Throws:, но имеют спецификацию потенциально вызывающего исключения, могут вызывать исключения, определяемые реализацией. Реализации должны сообщать об ошибках, генерируя исключения или производные от стандартных классов исключений (21.6.3.1, 21.8, 22.2).
Здесь нет параграфа Throws и нет спецификации потенциально генерирующего исключения.
Так std::mutex
конструктор никогда не должен вызывать исключение или вызывать std::terminate
так же, как и любую другую функцию из стандартной библиотеки с noexcept
спецификация соответствующей реализации C++.
Мне кажется, что ошибки от pthread_mutex_init
просто игнорируются в libstdC++:
https://github.com/psp2sdk/libs/blob/master/include/c%2B%2B/bits/gthr-posix.h#L732
где __gthread_mutex_init_function
через макрос __GTHREAD_MUTEX_INIT_FUNCTION
вызывается здесь
https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/bits/std_mutex.h#L75
это в std::mutex
конструктор через его базовый класс.
ОБНОВИТЬ
Можно инициализировать мьютекс Pthread с помощью PTHREAD_MUTEX_INITIALIZER
а потом
проверка ошибок не выполняется
Я предполагаю, что обработка ошибок может быть отложена до блокировки функций; цитирование из документации pthread_mutex_lock
а также pthread_mutex_trylock
Раздел ОШИБКИ:
EINVAL
Значение, указанное в mutex, не относится к инициализированному объекту mutex.
Это подразумевает, что ошибки в pthread_mutex_init
может быть безопасно проигнорировано в std::mutex
конструктор.
Если вы подытожите все это, это приведет к тому, что pthread_mutex_init
не может быть вызвано в std::mutex
конструктор. Для построения / инициализации не требуется однозначного отображения. Напротив!