Do condition variables still need a mutex if you're changing the checked value atomically?
Asked Answered
B

1

16

Here is the typical way to use a condition variable:

// The reader(s)
lock(some_mutex);
if(protected_by_mutex_var != desired_value)
    some_condition.wait(some_mutex);
unlock(some_mutex);

// The writer
lock(some_mutex);
protected_by_mutex_var = desired_value;
unlock(some_mutex);
some_condition.notify_all();

But if protected_by_mutex_var is set atomically by say, a compare-and-swap instruction, does the mutex serve any purpose (other than that pthreads and other APIs require you to pass in a mutex)? Is it protecting state used to implement the condition? If not, is it safe then to do this?:

// The writer
atomic_set(protected_by_mutex_var, desired_value);
some_condition.notify_all();

With the writer never directly interacting with the reader's mutex? Note that the 'protected_by_mutex_var' name is no longer really appropriate (it's not mutex protected anymore). If so, is it even necessary that different readers use the same mutex?

Burstone answered 27/3, 2010 at 23:41 Comment(1)
The whole point of condition variables is that they make "check the predicate, unlock the mutex, and sleep" atomic.Debouchment
S
15

Imagine the following scenario:

| Thread 1                                            | Thread 2                                           |
| if(protected_by_mutex_var != desired_value) -> true |                                                    |
|                                                     | atomic_set(protected_by_mutex_var, desired_value); |
|                                                     | some_condition.notify_all();                       |
| some_condition.wait(some_mutex);                    |                                                    |

This situation sees Thread 1 waiting for a notify that may never come. Because the statements acting on the condition are not part of the variable read / atomic set, this presents a race condition.

Using the mutex effectively makes these actions inseparable (assuming all accesses to the variable behave properly and lock the mutex.)

Smallsword answered 27/3, 2010 at 23:55 Comment(4)
Of course :p Waiting on conditions doesn't behave like poll().Burstone
"Because the statements acting on the condition are not part of the variable read / atomic set, this presents a race condition." would you explain this statement again in detail please?Darmstadt
@Anisha Kaul: Let's just consider the reader code for now. In this case the statement acting on the condition is "some_condition.wait(some_mutex)", and the variable read is happening in "if (protected_by_mutex_var != desired_value)". All I'm trying to say is that nothing guarantees that another thread won't interrupt between these two operations - they are not "together" / "atomic"; or as I put it earlier the wait is not "part of" the variable read. Similarly for the writer thread, for the atomic_set call and the some_condition.notify_all() call.Smallsword
Revisiting this question a couple years later I'd add an exception: if the receiving thread can otherwise detect when the value has been set (e.g. if it's an atomically incremented counter and the receiving thread caches the last seen value for comparison), then you can work around the race condition by waiting with a timeout. If your timeout is significant enough you can capture most of the benefit of the condition variable (still spend most of your time sleeping).Burstone

© 2022 - 2024 — McMap. All rights reserved.