Re[6]: std::condition_variable счетчик notify
От: uzhas Ниоткуда  
Дата: 17.06.15 11:36
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Изменение условия может быть долгим,

лечение рандомных багов может быть еще более долгим. я отношу cv к самым неочевидным и сложным примитивам (даже по форуму рсдн видно, что далеко не все вкуривают, как оно работает), поэтому рекомендую использовать стандартный паттерн с мьютексом

EP>например если это какое-нибудь STM.

тут я не понял переход к STM. не проще ли было привести vector::push_back как пример изменения условия? или ты это как-то увязал с инкрементом счетчика?

EP>В этом случае можно под мьютексом делать только notify_one — тогда пропущенных уведомлений не будет.


поясни какую разницу в семантике ты видишь при вызове notify_one под мьютексом и вне мьютекса? у тебя есть некие ожидания как cv откликнется на notify_one? есть гарантии со стороны C++ стандарта и\или POSIX?
Re[7]: std::condition_variable счетчик notify
От: Evgeny.Panasyuk Россия  
Дата: 17.06.15 12:01
Оценка: 26 (2)
Здравствуйте, uzhas, Вы писали:

EP>>например если это какое-нибудь STM.

U>тут я не понял переход к STM. не проще ли было привести vector::push_back как пример изменения условия? или ты это как-то увязал с инкрементом счетчика?

Я имею в виду случай когда есть атомарные операции, которые не нужно защищать муьютексом (например push для lock-free queue), но эффект от которых ждут по CV.

EP>>В этом случае можно под мьютексом делать только notify_one — тогда пропущенных уведомлений не будет.

U>поясни какую разницу в семантике ты видишь при вызове notify_one под мьютексом и вне мьютекса?

Это разница появляется в случае когда само состояние меняется не под мьютексом (например потому что оно атомарное).
То есть если какой-то поток сделал ++atomic_counter не под мьютексом, потом под мьютексом сделал notify_one — то поток находящийся на wait не пропустит это уведомление.
Если же и ++atomic_counter и notify_one находятся не под мьютексом, то возможна потеря уведомления:
condition_variable cv;
mutex m;
atomic<unsigned> counter = 0;
// ...
unique_lock<mutex> ul(m);
while(!counter)
{
    // <--- meanwhile in other thread: ++counter; cv.notify_one();
    // as the result notification is lost
    cv.wait(ul);
}
Если бы хотя бы notify_one было под мьютексом — то уведомление не потерялось бы.

U>у тебя есть некие ожидания как cv откликнется на notify_one? есть гарантии со стороны C++ стандарта и\или POSIX?


Если один поток сидит на wait — то notify_one должен его разбудить (точнее по ISO — unblock).
Отредактировано 17.06.2015 12:02 Evgeny.Panasyuk . Предыдущая версия .
Re[8]: std::condition_variable счетчик notify
От: uzhas Ниоткуда  
Дата: 17.06.15 16:15
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Это разница появляется в случае когда само состояние меняется не под мьютексом (например потому что оно атомарное).

EP>То есть если какой-то поток сделал ++atomic_counter не под мьютексом, потом под мьютексом сделал notify_one — то поток находящийся на wait не пропустит это уведомление.
аа, теперь понял о чем ты
сразу натолкнуло на вопрос: что происходит, если cv получил сигнал (кто-то позвал notify*()), но не может сделать reacquire lock, потому что мьютекс залочен в другом потоке?
то же самое про wait_for с таймаутом
читаем доку

30.5.1 Class condition_variable [thread.condition.condvar]
.....
template <class Rep, class Period>
cv_status wait_for(unique_lock<mutex>& lock, const chrono::duration<Rep, Period>& rel_time);

24 Requires: lock is locked by the calling thread, and either
— no other thread is waiting on this condition_variable object or
— lock.mutex() returns the same value for each of the lock arguments supplied by all concurrently waiting (via wait, wait_for, or wait_until) threads.
25 Effects:
— Atomically calls lock.unlock() and blocks on *this.
— When unblocked, calls lock.lock() (possibly blocking on the lock), then returns.
— The function will unblock when signaled by a call to notify_one() or a call to notify_all(),
by the elapsed time rel_time passing (30.2.4), or spuriously.
— If the function exits via an exception, lock.lock() shall be called prior to exiting the function scope.
26 Returns: cv_status::timeout if the function unblocked because rel_time elapsed, otherwise cv_status::no_timeout.


из чего мы делаем вывод, в wait_for мы можем повиснуть на бесконечно долго, даже если задали период времени в 10 секунд
далее мы можем уже позвать wait в другом потоке либо с локом того же мьютекса, либо со вторым мьютексом..тут мы закипаем

EP>Если один поток сидит на wait — то notify_one должен его разбудить

я телепатировал в неверном направлении, сорри
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.