I have a few questions.
1) std::condition_variable everywhere considered as a means of synchronizing data access. Therefore, usage examples usually look something like this:

 void put(const T &val) { std::unique_lock<std::mutex> lock(m_Mtx); m_Value = val; m_CondVar.notify_one(); } T get() { std::unique_lock<std::mutex> lock(m_Mtx); m_CondVar.wait(lock); return m_Value; } 

It's clear. But what if I do not need to transfer data, but only need to wake the waiting thread (if there is one) - can I, in this case, NOT use a mutex?

 void put() { m_CondVar.notify_one(); } 

The documentation about this seems to be not written. The documentation for pthread_cond_signal is vague:

It is not possible to use it at that time or you need to go through the pthread_cond_road_bathcast (pthread_cond_broadcast () or pthread_cond_signal ()). however, if it’s predicted, then the mutex should be pthread_cond_broadcast () or pthread_cond_signal ().

that can not be interpreted unequivocally.
The goal is micro-optimization, less mutexes, less potential context switching.

2) what is the cost of m_CondVar.notify_one(); ? Will it be a regular syscall?

3) maybe you know an easier way to notify (without sending a message or transmitting, it does not matter) one thread from another?

    2 answers 2

    You do not have the option not to use the mutex while waiting on std::condition_variable , because there is no such API. But even if it were, the exit from wait does not always mean that a signal was received via notify_one . There is such a thing as “spurious wake up”, because of which a similar exit from wait is possible. Therefore it is correct to have a divided variable (flag) that will be set on the side of the notifier, and checked on the side of the notifier. This flag can be protected with a mutex, or an atomic object can be used. But since the API already forces us to use a mutex, its use is fully justified.

    As for the price and other things: check in practice, the standard is not regulated by such things. There is no better way to wake up a stream in standard C ++. And so you can use different primitives, but the essence of this does not change: there is some kind of object and waiting for it.

    • get () without a mutex will fail, but the question is - is it possible to do put () without a mutex? - o2gy pm
    • @ o2gy, API does not prohibit this. But I described why this is undesirable. - ixSci
    • That is, if “spurious wake up” is not considered a problem, then everything is OK - it is possible without a mutex, right? - o2gy 2:22 pm
    • @ o2gy, I don’t see any other problems for the “in vacuum” code. - ixSci pm

    In principle, the @ixSci answer is correct, but there are few details. I rummaged a bit in the question and would like to add.

    1) But what if I do not need to transfer data, but only need to wake the waiting thread (if there is one) - can I in this case NOT use the mutex?

    The direct answer to this question is yes . In the examples on the cppreference here and here - that's exactly what they do. There, in general, all notify_one()/notify_all() calls are NOT made under the mutex.
    As for the “spurious wake up” - the POSIX standard directly says:

    It is true if you need to use it. Spurious wakeups from the pthread_cond_timedwait () or pthread_cond_wait () functions may occur. Since the return from pthread_cond_timedwait () or pthread_cond_wait () has been re-evaluated.

    Thus, indeed, the only correct way to use condition_variable involves the presence of conditions that determine whether something has changed in the process memory or not, and which should be rechecked after waking up the flow from cv.wait() .

    To ensure the specified correctness, it is necessary and sufficient to observe the following rule: update data in the T1 stream and check-and-wait on the same data in the T2 stream - must be performed under the same critical section.

    notify() can be performed both in the same critical section and outside it.
    Moreover, the documentation for notify_one even states that holding a lock during a notify() call is pessimization in the sense that if the lock during this call were already released, then the thread waiting for wait() could capture a mutex and would not break the law. So in some cases one would even recommend removing the lock before calling notify() . However, I put on a benchmark and it turned out that on my virtual machine this trick somehow slows down the program for some reason. So you need to test.

    PS: it is not possible to implement check-and-wait using atomic types. yes, atomic.load() and cv.wait() are atomic, but their sequence is not, and in this case it is required that the predicate is checked for atomicity and cv.wait() called in the sequence.

    PPS: a separate interesting moment is connected with the very concept of “spurious wake-up”, what it is. "spurious wake-up" is a behavior explicitly defined by the POSIX standard, in linux it may be due to the fact that the blocking syscall must be interrupted if the process receives a signal. Here I found a rather interesting post on this topic.

    2) what is the cost of m_CondVar.notify_one() ; ? Will it be a regular syscall?

    On linux, this is a syscall to the futex subsystem, which is actually not cheap. In strace it looks like this:

     futex(0x6063ac, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x6063a8, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) 

    In the same benchmark, I tried to compare notify() with neighboring syskollah, I got that notify() costs about the same as the mutex lock.

    3) maybe you know an easier way to notify (without sending a message or transmitting, it does not matter) one thread from another?

    I decided to test and compare among themselves the following methods:

    The result is a benchmark here .
    Semaphores and a bunch of mutex + condvar work about equally fast (apparently inside they have one implementation through futex), fdevent is about 2 times slower, mq_queue is already much slower.
    So, it turns out, yes - the fastest way to notify between threads is mutex + condvar.