Search code examples
multithreadingpthreadscondition-variable

pthread_cond_signal doesn't unblock pthread_cond_wait thread immediately


I'm observing a behaviour which doesn't seem to be inline with how pthread_cond_signal and pthread_cond_wait should behave (accordingly to manpages). man 3 pthread_cond_signal stipulates that:

The pthread_cond_signal() function shall unblock at least one of the threads that are blocked on the specified condition variable cond (if any threads are blocked on cond).

This isn't precise enough and doesn't clarify if, at the same time, the thread calling pthread_cond_signal will yield its time back to the scheduler.

Here's an example program:

     1  #include <pthread.h>
     2  #include <iostream>
     3  #include <time.h>
     4  #include <unistd.h>
     5
     6  int mMsg = 0;
     7  pthread_mutex_t mMsgMutex;
     8  pthread_cond_t mMsgCond;
     9  pthread_t consumerThread;
    10  pthread_t producerThread;
    11
    12  void* producer(void* data) {
    13      (void) data;
    14      while(true) {
    15          pthread_mutex_lock(&mMsgMutex);
    16          std::cout << "1> locked" << std::endl;
    17          mMsg += 1;
    18          std::cout << "1> sending signal, mMsg = " << mMsg << "" << std::endl;
    19          pthread_cond_signal(&mMsgCond);
    20          pthread_mutex_unlock(&mMsgMutex);
    21      }
    22
    23      return nullptr;
    24  }
    25
    26  void* consumer(void* data) {
    27      (void) data;
    28      pthread_mutex_lock(&mMsgMutex);
    29
    30      while(true) {
    31          while (mMsg == 0) {
    32              pthread_cond_wait(&mMsgCond, &mMsgMutex);
    33          }
    34          std::cout << "2> wake up, msg: " << mMsg << std::endl;
    35          mMsg = 0;
    36      }
    37
    38      return nullptr;
    39  }
    40
    41  int main()
    42  {
    43      pthread_mutex_init(&mMsgMutex, nullptr);
    44      pthread_cond_init(&mMsgCond, nullptr);
    45
    46      pthread_create(&consumerThread, nullptr, consumer, nullptr);
    47
    48      std::cout << "starting producer..." << std::endl;
    49
    50      sleep(1);
    51      pthread_create(&producerThread, nullptr, producer, nullptr);
    52
    53      pthread_join(consumerThread, nullptr);
    54      pthread_join(producerThread, nullptr);
    55      return 0;
    56  }

Here's the output:

starting producer...
1> locked
1> sending signal, mMsg = 1
1> locked
1> sending signal, mMsg = 2
1> locked
1> sending signal, mMsg = 3
1> locked
1> sending signal, mMsg = 4
1> locked
1> sending signal, mMsg = 5
1> locked
1> sending signal, mMsg = 6
1> locked
1> sending signal, mMsg = 7
1> locked
1> sending signal, mMsg = 8
1> locked
1> sending signal, mMsg = 9
1> locked
1> sending signal, mMsg = 10
2> wake up, msg: 10
1> locked
1> sending signal, mMsg = 1
1> locked
1> sending signal, mMsg = 2
1> locked
1> sending signal, mMsg = 3
...

It seems like there's no guarantee that any pthread_cond_signal will indeed immediately unblock any waiting pthread_cond_wait thread. At the same time it seems that any amount of pthread_cond_signal can be lost after first one has been issued.

Is this really the intended behaviour or am I doing something wrong here?


Solution

  • This is the intended behavior. pthread_cond_signal does not yield it's remaining runtime, but will continue to run.

    And yes, pthread_cond_signal will immediately unblock (one or more) thread waiting on the corresponding condition variable. However, that doesn't guarantee that said waiting thread will immediately run. It just tells the OS that this thread is no longer blocked, and it's up to the OS thread scheduler to decide when to start running it. Since the signalling thread is already running, is hot in the cache etc., it will likely have plenty of time to do something before the now-unblocked thread starts doing anything.

    In your example above, if you don't want to skip messages, maybe what you're looking for is something like a producer-consumer queue, maybe backed by a ring buffer.