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...
Attempting to unlock a mutex locked by a different thread results in undefined behavior. Attempting to unlock an unlocked mutex results in undefined behavior. This type of mutex provides error checking. A thread attempting to relock this mutex without first unlocking it will return with an error.
Deadlock! If a thread which had already locked a mutex, tries to lock the mutex again, it will enter into waiting list of that mutex which results in deadlock.
If the mutex is already locked by another thread, the thread waits for the mutex to become available. The thread that has locked a mutex becomes its current owner and remains the owner until the same thread has unlocked it.
The pthread_cond_wait() function atomically unlocks mutex and performs the wait for the condition. In this case, atomically means with respect to the mutex and the condition variable and another threads access to those objects through the pthread condition variable interfaces.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With