2

I have a Read thread and multiple write threads as the following.

Read thread:

pthread_mutex_lock(mutex);
while(1){
   pthread_cond_wait(cond, mutex);
   switch(state){
   //do something
   }
}
pthread_mutex_unlock(mutex);

Write thread1:

pthread_mutex_lock(mutex);
state = work;
pthread_cond_signal(cond);
//do something
pthread_mutex_unlock(mutex);

Write thread2:

pthread_mutex_lock(mutex);
state = lunch;
pthread_cond_signal(cond);
//do something
pthread_mutex_unlock(mutex);

Assume state is the shared variable and Read thread goes into waiting before all the write threads.

Say the two write threads try to get mutex at the same time and write1 acquires mutex and sends cond. At this point of time both write2 and read threads are locked on the mutex, and so far to my understanding, no thread has a priority to the mutex so we can't be sure when write1 releases the mutex that read thread will acquire it, which will lead to a missed signal and an overwritten state.

I have two questions here:

  1. Is my understanding correct, that the above situation is possible?
  2. If so, how do I make sure that no signal is missed in my code ?
Rachid K.
  • 4,490
  • 3
  • 11
  • 30
  • What is the point of the variable `enabled`? Can we assume that it is always true? If so, please remove it from the question, as it is not necessary in a [mre]. – Andreas Wenzel Nov 13 '20 at 06:33
  • In one of the states, enabled is made 0 which would end the read thread, but as you mentioned since it is not relevant to the question, removed `enabled` – Vishnu Kavali Nov 13 '20 at 07:28
  • It is not safe to rely only on `pthread_cond_wait` without checking some kind of variable protected by the mutex (for example `state`), as spurious wakeups are always possible. The loop should probably look something like this: `while(state==empty) pthread_cond_wait(cond, mutex);` There should be nothing else inside the `while` loop, all further processing should occur after the loop. See this question for further information: [Why does `pthread_cond_wait` have spurious wakeups?](https://stackoverflow.com/q/8594591/12149471) – Andreas Wenzel Nov 13 '20 at 07:38
  • Thank you for mentioning. I didn't include all the details as it would make the question more complicated and I felt it was not relevant to the question. But as per your answer if I set `state` to a special value in the read thread then I can also `while(state==specialvalue) pthread_cond_wait(cond, mutex);`. – Vishnu Kavali Nov 13 '20 at 07:55
  • The missing a signal is not, in general, something that you can reliably prevent, nor should you want to. CVs provide a mechanism for suspending operation until a state change, but it is the thread's responsibility to avoid waiting in the first place if the present state does not require it to do, and to check upon waking that the relevant details of the program state are appropriate for it to proceed. Implemented correctly, that means that missing a signal does not produce misbehavior. – John Bollinger Nov 14 '20 at 18:08

2 Answers2

1
  1. Is my understanding correct, that the above situation is possible?

Yes, you are correct.

  1. If so, how do I make sure that no signal is missed in my code.

If you want to make sure that every change of the variable state is noticed by the "read thread", then you must make sure that no "write thread" changes the variable state until it has been acknowledged by the "read thread". For example, you could make the "read thread" read the value of state and then set that variable to a special value, to indicate to the "writer threads" that state may now be overwritten with a new value.

However, this means that not only does the "read thread" have to wait for the "writer threads", but the "writer threads" must also wait for the "read thread". This is the producer-consumer problem. A typical solution would be to have two condition variables instead of one:

  • One for the "writer threads" to indicate to the "reader thread" that new data is ready to be read.
  • One for the "reader thread" to indicate to the "writer threads" that new data can now be written.

In order to minimize threads waiting for each other, you may want to allow for more than one set of data to be stored in your program at once, for example by implementing a queue.

Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39
  • Thanks for the clear answer. About the side note, please check [if predictable scheduling behavior is required, then that mutex shall be locked by the thread calling pthread_cond_broadcast() or pthread_cond_signal().](https://linux.die.net/man/3/pthread_cond_signal) and the comments to the answer [Calling pthread_cond_signal without locking mutex](https://stackoverflow.com/a/4567919/12041305) – Vishnu Kavali Nov 13 '20 at 08:08
  • @VishnuKavali: Yes, if you are using thread priorities that must be enforced (i.e. you require "predictable behavior"), then it is probably better to call `pthread_cond_wait` while the mutex is still locked. That way, there is no risk of [priority inversion](https://en.wikipedia.org/wiki/Priority_inversion). However, when not using thread priorities, I believe it is better for the mutex to not be locked. Even if this increases the chance of a spurious wakeup slightly, I believe it is generally more efficient. However, this may depend on the implementation. – Andreas Wenzel Nov 13 '20 at 09:25
  • @VishnuKavali: Note that the accepted answer of the question that you linked to talks most of the time about whether a mutex is required at all in the signalling thread. In that respect, the accepted answer does not directly answer the question. Only in the last paragraph does it discuss the question of when a mutex is used, whether it should be unlocked first or not. – Andreas Wenzel Nov 13 '20 at 09:52
  • @VishnuKavali: I have removed the "side note" from my answer, because it seems to be unclear what is best. Many people recommend signalling before unlocking the mutex, some recommend the opposite. For example, the [sample code of the documentation for condition variables on the Microsoft Windows platform](https://learn.microsoft.com/en-us/windows/win32/sync/using-condition-variables) unlocks the mutex first before signalling. – Andreas Wenzel Nov 14 '20 at 11:10
1

As you don't want to miss any state change, the write threads should not overwrite the variable but write the states into a FIFO (that you can implement as a linked list or a pipe or a circular buffer with read/write pointers for examples). Then, the write threads will push the states (one new element per-state in the FIFO) and the read thread will pop them one by one until the FIFO is empty (as it may have multiple states in the FIFO upon each wake up).

So, you can keep the current signaling implementation but just change the way state is managed: a FIFO of states instead of a single variable.

Rachid K.
  • 4,490
  • 3
  • 11
  • 30