Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why the constructor of std::mutex in C++ does not throw?

The pthread_mutex_init() function returns a non-zero value when it fails to initialize the mutex, while the std::mutex class in C++11 has a constructor of noexcept.

Say one chooses to implement a C++ mutex class on top of pthreads mutex. He wraps a pthread mutex inside the class and tries to initialize it by calling pthread_mutex_init() in constructor. If the function call returns a value other than zero, meaning error, the error can't be reported immediately since the constructor can not throw. One alternative is to throw an exception until the lock method is actually called on the mutex. But this approach just seems wrong.

Is there another way to do this, employing some clever tricks to guarantee that initializing a mutex always succeed?

Update: I am going to answer my own question on this one. According to language standard, in 30.4.1.3 pge 1163, it says ". If initialization of an object of a mutex type fails, an exception of type system_error shall be thrown. "

And a function of noexcept can throw inside the function body, it is just the caller can not catch the exception. If an exception is thrown inside a noexcept function, std::terminate will be called.

like image 950
John Z. Li Avatar asked Feb 09 '18 02:02

John Z. Li


People also ask

Is std :: mutex movable?

By design, std::mutex is not movable nor copyable. This means that a class A holding a mutex won't receive a default move constructor.

What is the role of std :: mutex?

std::mutex The mutex class is a synchronization primitive that can be used to protect shared data from being simultaneously accessed by multiple threads.

Can std :: mutex be copied?

std::mutex::mutexmutex objects cannot be copied/moved (both the copy constructor and assignment operator are deleted for this type).

What is constructor C++?

A constructor is a member function with the same name as its class. For example: class X { public: X(); // constructor for class X }; Constructors are used to create, and can initialize, objects of their class type.

Why the constructor of std::mutex in C++ does not throw?

The pthread_mutex_init () function returns a non-zero value when it fails to initialize the mutex, while the std::mutex class in C++11 has a constructor of noexcept. Say one chooses to implement a C++ mutex class on top of pthreads mutex.

Can mutex constructor throw system_error?

If initialization of an object of a mutex type fails, an exception of type system_error shall be thrown. " But mutex constructor can not throw. @JohnZ.Li: It's OK to answer your own question, and if nobody else comes up with a better answer, you can even accept it. Please However, please don't put the answer inside your question.

What is a mutex class in C++?

(since C++11) The mutex class is a synchronization primitive that can be used to protect shared data from being simultaneously accessed by multiple threads.

Is there a copy constructor for mutex in Windows?

In Windows, you have to create a named mutex and then open a new handle based on that name, for example, intended to be used in a new thread/process and std::mutex never creates named objects. So even if there was a copy constructor, it would be impossible to implement it.


3 Answers

The constructor of std::mutex needs to be constexpr (so that a global std::mutex can be statically initialized and used in constructors of other global objects), and therefore cannot call pthread_mutex_init (or similar functions) at all.

Instead, it needs to use PTHREAD_MUTEX_INITIALIZER or equivalent (e.g., SRWLOCK_INIT on Windows) to statically initialize the mutex.

like image 128
T.C. Avatar answered Oct 07 '22 02:10

T.C.


It seems to me that errors from pthread_mutex_init are simply ignored in libstdc++:

https://github.com/psp2sdk/libs/blob/master/include/c%2B%2B/bits/gthr-posix.h#L732

where __gthread_mutex_init_function is via macro __GTHREAD_MUTEX_INIT_FUNCTION invoked here

https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/bits/std_mutex.h#L75

that is in std::mutex constructor via its base class.


UPDATE

One can initialize Pthread mutex with PTHREAD_MUTEX_INITIALIZER and then

no error checks are performed

I guess error handling can be postponed to locking functions; quoting from documentation of pthread_mutex_lock and pthread_mutex_trylock ERRORS section:

EINVAL The value specified by mutex does not refer to an initialized mutex object.

This implies that errors in pthread_mutex_init can be safely ignored in std::mutex constructor.

like image 2
Daniel Langr Avatar answered Oct 07 '22 00:10

Daniel Langr


According to C++17 specification:

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

  1. The mutex types shall be DefaultConstructible and Destructible. If initialization of an object of a mutex type fails, an exception of type system_error shall be thrown. The mutex types shall not be copyable or movable.

So mutex type may throw an exception not std::mutex. std::mutex has noexcept, but std::recursive_mutex does not and they are both mutex types:

33.4.3.2.1 Class mutex [thread.mutex.class]

constexpr mutex() noexcept;

33.4.3.2.2 Class recursive_mutex [thread.mutex.recursive]

recursive_mutex();

Moreover:

20.5.5.12 Restrictions on exception handling [res.on.exception.handling]

  1. Any of the functions defined in the C++ standard library can report a failure by throwing an exception of a type described in its Throws: paragraph, or of a type derived from a type named in the Throws: paragraph that would be caught by an exception handler for the base type.

  2. Functions defined in the C++ standard library that do not have a Throws: paragraph but do have a potentially throwing exception specification may throw implementation-defined exceptions. Implementations should report errors by throwing exceptions of or derived from the standard exception classes (21.6.3.1, 21.8, 22.2).

There is no Throws paragraph and it does not have potentially throwing exception specification.

So std::mutex constructor shall never throw an exception or call std::terminate the same way as any other function from standard library with noexcept specification on conforming C++ implementation.

like image 2
Barricadenick Avatar answered Oct 07 '22 01:10

Barricadenick