23

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.

Kay Zed
  • 1,304
  • 2
  • 21
  • 31
qble
  • 1,256
  • 2
  • 12
  • 29

4 Answers4

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.

Stephan Dollberg
  • 32,985
  • 16
  • 81
  • 107
  • That is exactly what I am looking for, I wasn't aware about it. Thanks! – qble Apr 08 '13 at 15:00
  • "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**? – qble Apr 08 '13 at 15:33
  • "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? – qble Apr 08 '13 at 15:35
  • @qble 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. – Stephan Dollberg Apr 08 '13 at 15:50
  • 3
    For the curious: Rationale for the design and implementation of `condition_variable_any`: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2406.html#gen_cond_var It was called `gen_cond_var` when this paper was written. – Howard Hinnant Apr 08 '13 at 15:54
  • "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? – qble Apr 08 '13 at 15:54
  • I have added question regarding libstdc++ implementation: http://stackoverflow.com/questions/15887306/do-i-need-to-synchronize-stdcondition-variable-condition-variable-anynotify – qble Apr 08 '13 at 19:58
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?

Community
  • 1
  • 1
Mark B
  • 95,107
  • 10
  • 109
  • 188
  • 2
    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. – qble Apr 08 '13 at 15:13
  • 1
    Interesting info, thanks. We've come full circle. `std::condition_variable_any` holds an internal `std::mutex` to protect itself. :-) – Howard Hinnant Apr 08 '13 at 15:14
  • That is what I understand. If **notify_one** would be not synchronized by ISO - then yes, we need external mutex for it. – qble Apr 08 '13 at 15:16
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.

Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271
  • Yes, I aware about this, and this is acceptable in that situation. – qble Apr 08 '13 at 15:18
  • 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. – Maxim Egorushkin Apr 08 '13 at 15:21
  • There is [non-critical race condition](http://en.wikipedia.org/wiki/Race_condition#Critical_and_non-critical_race_conditions). it is not UB. – qble Apr 08 '13 at 15:27
  • 2
    @qble - 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. – Pete Becker Apr 08 '13 at 15:53
  • 2
    @PeteBecker, you are talking about C++11 **DATA RACE**, yes it is UB, but is not same thing as [**RACE CONDITION**](http://en.wikipedia.org/wiki/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**. – qble Apr 08 '13 at 15:59
  • 1
    @qble - 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. – Pete Becker Apr 08 '13 at 16:24
  • @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! – qble Apr 08 '13 at 16:33
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.

Sherwood Wang
  • 685
  • 9
  • 19