Reputation: 177
I'm trying to write very simple multi threading program just to get the catch of it but I fail to understand what exactly is wrong in one of the cases. So:
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
char string[100];
pthread_t thr_id_rd;
pthread_t thr_id_wr;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond, cond1;
int read = 0;
void *thread_rd()
{
pthread_mutex_lock(&lock);
while (1) {
pthread_cond_wait(&cond, &lock);
printf("rd: entered: %s\n", string);
pthread_cond_signal(&cond1);
}
pthread_mutex_unlock(&lock);
}
void *thread_wr()
{
pthread_mutex_lock(&lock);
while (1) {
printf("wr: enter something: ");
scanf("%s", string);
pthread_cond_signal(&cond);
pthread_cond_wait(&cond1, &lock);
}
pthread_mutex_unlock(&lock);
}
int main(int argc, char *argv[])
{
pthread_create(&thr_id_rd, NULL, thread_rd, NULL);
pthread_create(&thr_id_wr, NULL, thread_wr, NULL);
pthread_join(thr_id_rd, NULL);
pthread_join(thr_id_wr, NULL);
return 0;
}
The above seems to work correctly. But when I edit the two threads like this:
void *thread_rd()
{
while (1) {
pthread_mutex_lock(&lock);
pthread_cond_wait(&cond, &lock);
printf("rd: entered: %s\n", string);
pthread_cond_signal(&cond1);
pthread_mutex_unlock(&lock);
}
}
void *thread_wr()
{
while (1) {
pthread_mutex_lock(&lock);
printf("wr: enter something: ");
scanf("%s", string);
pthread_cond_signal(&cond);
pthread_cond_wait(&cond1, &lock);
pthread_mutex_unlock(&lock);
}
}
I'm getting undefined behavior - sometimes it's OK sometimes the program is stuck. According the man pages pthread_cond_wait must be called with mutex locked but there is no such restriction for _cond_signal (Question: What is the best practice?). So I decided to call it with mutex locked...
Obviously I'm complete newbie so please excuse my stupid question :( I'll be very grateful if someone can explain this to me...
Upvotes: 5
Views: 15694
Reputation: 231163
pthread conditional variable signals are not persistent. If you signal when nothing is waiting for the signal, the signal will be lost. So you could see something like this:
WR: Lock
WR: Read input
WR: Signal cond
WR: Wait for cond1 (implicitly unlocks)
RD: Lock
RD: Wait for cond (implicitly unlocks)
At this point you have a deadlock - both threads are waiting for signals which will never come.
The usual pattern with condvars is to have a flag (or some other persistent condition) paired with the conditional variable. You can then do a loop like:
while (!flag) pthread_cond_wait(&cond, &lock);
To signal, you set the flag, and then signal the condvar to wake up any waiters.
In your case you can actually use string
as your flag - have string == NULL
be the wake-up condition paired with cond1
and string != NULL
paired with cond
. Now if your reader thread enters the lock late it'll see that string != NULL
and not wait on the condvar.
Upvotes: 5