15

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?

Joseph Garvin
  • 20,727
  • 18
  • 94
  • 165

1 Answers1

14

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.)

Mike Tunnicliffe
  • 10,674
  • 3
  • 31
  • 46
  • Of course :p Waiting on conditions doesn't behave like poll(). – Joseph Garvin Mar 28 '10 at 07:57
  • *"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? – Aquarius_Girl May 22 '12 at 04:54
  • @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. – Mike Tunnicliffe May 22 '12 at 17:01
  • 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). – Joseph Garvin May 23 '12 at 13:14