Why is std::shared_mutex a timed mutex?

591 views
Skip to first unread message

Stephan Tolksdorf

unread,
May 13, 2013, 7:12:43 AM5/13/13
to std-dis...@isocpp.org
Hi,

I've just noticed that the shared_mutex type voted into C++1y at
Bristol[1] is a timed mutex type, i.e. has to support try_lock methods
with a timeout. I'm curious why the committee decided to include the
timed methods in the shared_mutex type although it didn't include them
in the basic mutex type for performance reasons.

Depending on the synchronization primitives provided by the OS, an
implementation that has to support waiting with a timeout may
necessarily be less efficient than an implementation without that
requirement. The standard shared mutex type in Windows (Vista and
later), the "Slim Reader/Writer (SRW) Lock"[2], does not support waiting
with a timeout. An implementation based e.g. on the Futex primitive
supported in Linux and FreeBSD may also be more complicated and slower
if it has to support waiting with a timeout.

[1] http://open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3659.html
[2] http://msdn.microsoft.com/en-us/library/windows/desktop/aa904937.aspx

Best regards,
Stephan


Howard Hinnant

unread,
May 13, 2013, 10:36:07 AM5/13/13
to std-dis...@isocpp.org
This was an engineering tradeoff / judgement made on my part. On the one hand we could partition up the mutex types as finely as possible: does / doesn't have timed operations, does / doesn't support shared locking, does / doesn't support upgrade locking (note shared_mutex was designed within the context of upgrade_mutex although upgrade_mutex was not accepted). This would make for 4 new mutex types. Recursion would be yet another way to partition up the functionality.

On the other hand we could just build in the way proposed (half-accepted): add two new mutex types: shared_mutex and upgrade_mutex.

I chose the latter with the rationale that:

1. The timed shared / unique locking are arguably desired functionality as indicated by the inclusion of these operations in the POSIX standard.

2. I felt that shared locking in general is functionality that needed to be in C++, and proposing two new mutexes (shared and upgrade) was already pushing the practical limits of getting this proposal accepted. Proposing 4 new mutexes would be far riskier.

3. I had demonstrated a very reasonable implementation that is 100% portable, built on top of std::mutex and std::condition_variable (and supports the timed operations with no extra expense). Although the implementation doesn't show it, I kept the mutex state in a single word so that one could optimize the operations (i.e. fast path) with atomics.

4. I've been asked previous questions along the lines of: Why doesn't the shared_mutex support feature X? One example of X is reader/writer priority. And I had one person make a very good argument to me why he needed it, complete with his implementation of it. I noted though that his acme::shared_mutex was exactly the right solution for his application. And it would interoperate seamlessly with the rest of the std-defined mutex/lock environment.

I.e. std::unique_lock<acme::shared_mutex> worked perfectly. As does std::shared_lock<acme::shared_mutex>. Custom mutexes, if they adhere to the mutex requirements, just work.

So additionally, modulo a minor wording defect that you have brought to my attention, the mutexes you mention could be supplied by a 3rd party, and can work with unique_lock / shared_lock at el. perfectly, as long as the client doesn't use the timed operations. Just as you can put a non-DefaultConstructible type into a std::vector, unless you use one of the members that requires DefaultConstructible, the lock types happily just work when their Mutex meets a subset of the requirements.

The note in [thread.lock.shared]:

[Note: shared_lock<Mutex> meets the TimedLockable requirements (30.2.5.4). — end note]

is an overstatement. Each member of the shared_lock states the requirements on Mutex in terms of its Effects clause. E.g:

void lock();

Effects: pm->lock_shared().

I.e. the Mutex must support lock_shared(). That's it.

Should these shared_mutexes-without-timed-operations become prevalent in the future, there is nothing stopping the committee from standardizing them too. And they will just work in the existing mutex/lock ecosystem.

Howard

Stephan Tolksdorf

unread,
May 13, 2013, 2:43:58 PM5/13/13
to std-dis...@isocpp.org
On 13.05.13 16:36, Howard Hinnant wrote:
> This was an engineering tradeoff / judgement made on my part.
[...]

Thank you for your detailed reply, and for all your work in the
standards committee!

> Should these shared_mutexes-without-timed-operations become prevalent in the future, there is nothing stopping the committee from standardizing them too.

Since the standard (process-internal) mutex types on Windows and OSX
both don't support try-lock operations with timeouts, it's probably safe
to assume that such timeout functionality has only rather special use
cases. Hence, I suspect that most potential users would prefer a
maximally efficient basic shared_mutex type, even if that meant having
no timed shared mutex operations in C++1y at all.

- Stephan



Reply all
Reply to author
Forward
0 new messages