Event notification without mutex
Asked Answered
F

4

24

C++11 has the std::condition_variable, its wait function is

template< class Predicate >
void wait( std::unique_lock<std::mutex>& lock, Predicate pred );

It requires a mutex.

As far as I understand - its notify_one can be called without synchronization (I know the idiomatic way is to use it with a mutex).

I have an object which is already internally synchronized - so I don't need a mutex to protect it. One thread should wait for some event associated with that object, and others would be notified.

How to do such notification without a mutex in C++11? I.e. it is easy to do with a condition_variable, but it needs a mutex. I thought about using a fake mutex type, but std::mutex is nailed in the wait interface.

An option is to poll a std::atomic_flag + sleep, but I don't like sleeping.

Fredricfredrick answered 8/4, 2013 at 14:47 Comment(0)
H
15

Use std::condition_variable_any you can use any class with it which implements the BasicLockable Concept.

Given a bad feeling about this I checked the implementation of std::condition_variable_any of libc++. It turns out that it uses a plain std::condition_variable together with a std::shared_ptr to a std::mutex, so there is definitely some overhead involved without digging any deeper. (There is some other post here on SO which covers this, though I first have to search that)
As a matter of that I would probably recommend to redesign your case so that synchronization is really only done by a mutex protecting a plain condition variable.

Horton answered 8/4, 2013 at 14:59 Comment(7)
That is exactly what I am looking for, I wasn't aware about it. Thanks!Fredricfredrick
"It turns out that it uses a plain std::condition_variable together with a std::shared_ptr to a std::mutex, so there is definitely some overhead involved without digging any deeper." - interesting, does it locks internal mutex within notify_one? What about plain std::condition_variable - does it locks any mutex with notify_one?Fredricfredrick
"As a matter of that I would probably recommend to redesign your case so that synchronization is really only done by a mutex protecting a plain condition variable." - I don't need to protect notify_one. Why I need mutex, which only be used within one thread?Fredricfredrick
@Fredricfredrick yeah, it locks the mutex and releases it instantly. Plain std::condition_variable just calls the underlying notify mechanism, nevertheless this is all implementation defined and as such relying on it wouldn't make me feel well and the reason why I would personally redesign it.Horton
For the curious: Rationale for the design and implementation of condition_variable_any: open-std.org/jtc1/sc22/wg21/docs/papers/2007/… It was called gen_cond_var when this paper was written.Loehr
"why I would personally redesign it" - interesting, how to do that? I don't like using mutex, because object is already internally synchronized. Are there any other primitives than condition_variable to send/get notifications in C++11?Fredricfredrick
I have added question regarding libstdc++ implementation: #15887806Fredricfredrick
M
7

In some threading models (although I doubt in modern ones) the mutex is needed to protect the condition variable itself (not the object you're synchronizing) from concurrent access. If the condition variable wasn't protected by a mutex you could encounter problems on the condition itself.

See Why do pthreads’ condition variable functions require a mutex?

Mcduffie answered 8/4, 2013 at 15:10 Comment(3)
I think std::condition_variable_any::notify_one does not need mutex for protection - that is by ISO. If some architectures would require protection of notify_one - library would do that by itself, otherwise it would be not standard-conforming.Fredricfredrick
Interesting info, thanks. We've come full circle. std::condition_variable_any holds an internal std::mutex to protect itself. :-)Loehr
That is what I understand. If notify_one would be not synchronized by ISO - then yes, we need external mutex for it.Fredricfredrick
P
1

I have some object, which already internally synchronized - I don't need mutex to protect it. One thread should wait for some event associated with that object, and others would notify.

If you don't hold the mutex the waiting thread is going to miss notifications, regardless whether you use condition_variable or condition_variable_any with the internal mutex.

You need to associate at least one bit of extra information with the condition variable, and this bit should be protected by a mutex.

Petropavlovsk answered 8/4, 2013 at 15:17 Comment(7)
Yes, I aware about this, and this is acceptable in that situation.Fredricfredrick
You missed the ending of my sentence. There is a race condition if the mutex is not held both when checking that state and then waiting on the condition variable.Petropavlovsk
There is non-critical race condition. it is not UB.Fredricfredrick
@Fredricfredrick - note that the definition of "race condition" in that linked page is not the same as the definition of "race condition" in the language definition. The language definition says that if a program both modifies and reads a variable and the two operations are not synchronized, there is a race condition. It also says that the behavior of a program with a race condition is undefined. So you can guess that on your system there may be such a thing as a "non-critical race condition", but that is not a conclusion you can draw without careful examination of the specifications for your system.Neopythagoreanism
@PeteBecker, you are talking about C++11 DATA RACE, yes it is UB, but is not same thing as RACE CONDITION: "A race condition or race hazard is the behavior of an electronic or software system where the output is dependent on the sequence or timing of other uncontrollable events". In short, even when you use mutex for mutual exclusion of two threads - you DO HAVE RACE CONDITION - because order is not predictable, but there is no UB. And in situation discussed here - we have race condition not data race.Fredricfredrick
@Fredricfredrick - yes, as I said, I was talking about the language definition. If you're using C++ synchronization objects you have to start with the language definition, not with something someone posted on the Internet. If you want to apply some other definition, by all means do so, but be sure that it's appropriate, As I said, that means looking at the guarantees for the synchronization primitives that you are using, including what guarantees your compiler makes for those primitives.Neopythagoreanism
@PeteBecker, in that particualar case (with lost notification of condition_variable) we really have race condtion using wiki's definition - which IS NOT CRITICAL by C++11 ISO, because condition_variable is internally synchronized by ISO . Definition of data race from ISO is not appropriate to that situation - we dont have any non-synchronized read-writes - everything is synchronized!Fredricfredrick
T
0

C++20 provides std::atomic::wait. https://en.cppreference.com/w/cpp/atomic/atomic/wait

It’s supposed to be faster. On Linux, it can be implemented with futex(2). But it’s also possible that a C++ library implements it with mutex.

Turnstone answered 1/3, 2023 at 2:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.