Why do we need a separate <shared_mutex> header file?

250 Views Asked by At

Can we just put shared_mutex under the same header file, rather than a separate <shared_mutex> header file?

When I use shared_mutex, I thought I only need to #include <mutex>. It turns out that I need to #include <shared_mutex> as well.

Is it more convenient to put all related mutexes into one header file <mutex>?

2

There are 2 best solutions below

1
On BEST ANSWER

Pointing out the premise of this question would be helpful to future academia.

If we take a closer look at the standard library, we see a certain pattern; a simple example is a look at the memory header which contains a few standard classes for easy dynamic memory management along with many functions and helper classes. This is also the case with the algorithm header.

The OP asks:

Can we just put [std::]shared_mutex under same header file [mutex], rather than separate shared_mutex header file?

When Howard Hinnant proposed Shared locking in C++, in his Proposed Wording, he writes it exactly like this:

Header <shared_mutex> synopsis

From the very beginning, he meant it to be in a separate header and made it so. Somehow this got into the standard exactly like this.

Can we correct this [if we imply there is a mistake to begin with]? Theoretically yes. Practically, it is complicated. C++ has always promised to be a stable language. Stroustrup has always reiterated on this throughout his over 50 years of work on this language. Trying to change this subtle diversion in the standard library is unnecessary and arguably harmless. Some users of the standard library will even see this as an advantage rather than an inconvenience.

Edit: Howard has given a clarification on this subject. I will quote this verbatim:

My rationale for proposing the separate header was that it added a lot of code for a use case that is (or should be) much less common than that for mutex. So compile-time takes a hit for a bunch of users who don't benefit from it. And soon that won't matter with the std module. This is a restatement of comments made above. Additionally my original proposal put even more code in shared_mutex: upgrade_mutex and upgrade_lock. The anticipated uses of these were even rarer. But a judgement was made that they did not warrant a separate header.

8
On

This is because engineers think in quite opposite way:

When I use a mutex, I don't have to know anything about anything else, like shared_mutex.

And here is the overall great advice as a rule of thumb:

"Perfection is achieved not when there is nothing more to add, but when there is nothing left to take away" (Antoine de Saint-Exupéry)

On the question about the counterexample - recursive_mutex.

The shared_mutex implementation is about 300+ lines of code with some API to learn. Compilation of this code is expensive and excessive in case you don't need it. The recursive_mutex is 10 lines of code while recursive_mutex is still a _Mutex_base and a "sibling" of mutex. They are "mutexes".

So it is quite reasonable to have them together. Otherwise, it goes to such huge fragmentation that you will payback for putting this together too much.

At the same time shared_mutex doesn't not implement _Mutex_base and doesn't inherit from it, so this is a "separate" thing.

Of course, this is a design decision to have the balance. Note that high level rules always provide a range instead of a specific precise answer so that one can apply it for wide variety of current circumstances and foresight. I explained the reason providing generic answer, which is the base for YAGNI principle (as Swift - Friday Pie noted), which is the base for Howard Hinnant's particular decision. It is your decision which generalization level of the answer to accept, all works.

So far it is the engineer's work to strive for balance; in case you decouple too much, you will have to payback for putting things together.