pthread_mutex_lock and pthread_cond_wait/signal causing deadlock - c

I`m using 2 threads in my program, 1 to print even numbers and the other to print odd numbers sequentially. When I run the below code, the programs blocks after printing 0 and 1. Seems like a deadlock.
But if I move rc=pthread_mutex_lock(&mutex) to above the while statements in both PrintEvenNos() and PrintOddNos(), the output is sequential and complete (as desired).
Could someone explain why it fails in the first case and what is causing the deadlock?
#include<stdio.h>
#include<pthread.h>
pthread_t tid[2];
unsigned int shared_data = 0;
pthread_mutex_t mutex;
pthread_cond_t even,odd;
unsigned int rc;
void* PrintEvenNos(void*);
void* PrintOddNos(void*);
void main(void)
{
pthread_create(&tid[0],0,&PrintEvenNos,0);
pthread_create(&tid[1],0,&PrintOddNos,0);
sleep(3);
pthread_join(tid[0],NULL);
pthread_join(tid[1],NULL);
}
void* PrintEvenNos(void *ptr)
{
//rc = pthread_mutex_lock(&mutex); /*works when I uncomment here and comment the next mutex_lock */
while (shared_data <= 5)
{rc = pthread_mutex_lock(&mutex);
if(shared_data%2 == 0)
{ printf("t1.....................................Even:%d\n",shared_data);
shared_data++;
pthread_cond_signal(&odd);
rc=pthread_mutex_unlock(&mutex);
}
else
{
pthread_cond_wait(&even, &mutex);
}
}
rc=pthread_mutex_unlock(&mutex);
}
void* PrintOddNos(void* ptr1)
{
// rc = pthread_mutex_lock(&mutex); /*works when I uncomment here and comment the next mutex_lock */
while (shared_data <= 5)
{
rc = pthread_mutex_lock(&mutex);
if(shared_data%2 != 0)
{
printf("t2.....................................odd:%d\n",shared_data);
shared_data++;
pthread_cond_signal(&even);
rc=pthread_mutex_unlock(&mutex);
}
else
{
pthread_cond_wait(&odd, &mutex);
}
}
rc=pthread_mutex_unlock(&mutex);
}

Your program has undefined behavior because your thread that was waiting re-acquired the lock when returning from pthread_cond_wait and then calls pthread_mutex_lock again on a mutex that it already owns.
Mutexes are meant to be used as you indicate by your comment. This is exactly what pthread_cond_wait is made for: it releases the lock on entry and re-acquires it on return.
Also, remove the pthread_mutex_unlock from the if branch, it is wrong. Generally you should only have one pair of lock/unlock calls that mark your critical section.

Move the lock and unlock as you mentioned in your comment. As placed, your program has a race condition and thus undefined behavior. You cannot access data that may be modified by another thread without synchronization to preclude concurrent access.

Related

sem_init and pthread_mutex_init

I was writing 2 similar codes for printing odd and even numbers from given number set using mutex lock and semaphore. Both of the codes works fine.
But, while using mutex lock, even if I wont declare the pthread_mutex_init function, still the program executes with no issues. But that's not the case with semaphore. For this case, I have to declare sem_init in main() else the program execution gets stuck in sem_wait() (found after debugging).
So, how in the case of mutex lock, even without declaring init(), the program executes?
For reference, I am attaching the semaphore code.
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
sem_t mutex;
pthread_t tid[2];
unsigned int shared_data[] = {23,45,67,44,56,78,91,102};
unsigned int rc;
int len=(sizeof(shared_data)/sizeof(shared_data[0]));
int i=0;
void *even(void *arg) {
rc = sem_wait(&mutex);
int temp = rc;
if(rc)
printf("Semaphore failed\n");
do{
if(shared_data[i] %2 == 0) {
printf("Even: %d\n",shared_data[i]);
i++;
}
else
rc = sem_post(&mutex);
}while(i<len);
}
void *odd(void *arg) {
rc = sem_wait(&mutex);
if(rc)
printf("Semaphore failed\n");
do {
if(shared_data[i] %2 != 0) {
printf("Odd: %d\n",shared_data[i]);
i++;
}
else
rc = sem_post(&mutex);
}while(i<len);
}
int main() {
sem_init(&mutex, 0,1);
pthread_create(&tid[0], 0, &even, 0);
pthread_create(&tid[1], 0, &odd, 0);
pthread_join(tid[0],NULL);
pthread_join(tid[1],NULL);
sem_destroy(&mutex);
return 0;
}
EDIT: Attaching the mutex lock code as well.
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
pthread_t tid[2];
unsigned int shared_data []= {23,45,67,44,56,78,91,102};
pthread_mutex_t mutex;
unsigned int rc;
int len=(sizeof(shared_data)/sizeof(shared_data[0]));
int i=0;
void* PrintEvenNos(void *ptr)
{
rc = pthread_mutex_lock(&mutex);
if(rc)
printf("Mutex lock has failed\n");
do
{
if(shared_data[i]%2 == 0)
{
printf("Even:%d\n",shared_data[i]);
i++;
} else {
rc=pthread_mutex_unlock(&mutex);
}
} while(i<len);
}
void* PrintOddNos(void* ptr1)
{
rc = pthread_mutex_lock(&mutex);
if(rc)
printf("Mutex lock has failed\n");
do
{
if(shared_data[i]%2 != 0)
{
printf("Odd:%d\n",shared_data[i]);
i++;
} else {
rc=pthread_mutex_unlock(&mutex);
}
} while(i<len);
}
void main(void)
{
pthread_create(&tid[0],0,PrintEvenNos,0);
pthread_create(&tid[1],0,PrintOddNos,0);
pthread_join(tid[0],NULL);
pthread_join(tid[1],NULL);
}
So, how in the case of mutex lock, even without declaring init(), the program executes?
This is undefined behavior, so there is no proper result. Per POSIX pthread_mutex_lock():
If mutex does not refer to an initialized mutex object, the behavior of pthread_mutex_lock(), pthread_mutex_trylock(), and pthread_mutex_unlock() is undefined.
"Appears to work" is one possible result of undefined behavior.
You have sem_init call for sem_t mutex;.
But pthread_mutex_init call is missing for pthread_mutex_t mutex;.
Both of the codes works fine.
No they don't; but first:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
Is how you should have initialized your mutex. On your system, this value might be zero, which would be equivalent to what you have. Either way, the problem is your program is broken.
One of your threads (even, odd) acquires a lock. In the case of even, when i is 0,1,2,5 or 6; you unlock it, which would permit odd() to proceed. In the case of odd, when i is 3,4,5 or 7, you unlock it, which would permit even() to proceed. So in your logic, the lock does nothing at all.
Also, semaphores are counters; so when you release it 5 times, you are permitting the next 5 sem_waits to proceed. Simple mutexes are gates, so only the first unlock has any effect, the subsequent 4 are errors. You don't check the error status of the unlock, which is typically the one that uncovers logic errors.
fwiw, on macos, the pthread_mutex_lock()'s both report an error.

Why some threads don't receive pthread_cond_broadcast?

I have a threadpool of workers. Each worker executes this routine:
void* worker(void* args){
...
pthread_mutex_lock(&mtx);
while (queue == NULL && stop == 0){
pthread_cond_wait(&cond, &mtx);
}
el = pop(queue);
pthread_mutex_unlock(&mtx);
...
}
main thread:
int main(){
...
while (stop == 0){
...
pthread_mutex_lock(&mtx);
insert(queue, el);
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mtx);
...
}
...
}
Then I have a signal handler that executes this code when it receives a signal:
void exit_handler(){
stop = 1;
pthread_mutex_lock(&mtx);
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mtx);
}
I have omitted declarations and initialization, but the original code has them.
After a signal is received most of the time it's all ok, but sometimes it seems that some worker threads stay in the wait loop because they don't see that the variable stop is changed and/or they are not waken up by the broadcast.
So the threads never end.
What I am missing?
EDIT: stop=1 moved inside the critical section in exit_handler. The issue remains.
EDIT2: I was executing the program on a VM with Ubuntu. Since the code appears to be totally right I tried to change VM and OS (XUbuntu) and now it seems to work correctly. Still don't know why, anyone has an idea?
Some guessing here, but it's too long for a comment, so if this is wrong, I will delete. I think you may have a misconception about how pthread_cond_broadcast works (at least something I've been burned with in the past). From the man page:
The pthread_cond_broadcast() function shall unblock all threads
currently blocked on the specified condition variable cond.
Ok, that make sense, _broadcast awakens all threads currently blocked on cond. However, only one of the awakened threads will then be able to lock the mutex after they're all awoken. Also from the man page:
The thread(s) that are unblocked shall contend for the mutex according
to the scheduling policy (if applicable), and as if each had called
pthread_mutex_lock().
So this means that if 3 threads are blocked on cond and _broadcast is called, all 3 threads will wake up, but only 1 can grab the mutex. The other 2 will still be stuck in pthread_cond_wait, waiting on a signal. Because of this, they don't see stop set to 1, and exit_handler (I'm assuming a Ctrl+c software signal?) is done signaling, so the remaining threads that lost the _broadcast competition are stuck in limbo, waiting on a signal that will never come, and unable to read that the stop flag has been set.
I think there are 2 options to work-around/fix this:
Use pthread_cond_timedwait. Even without being signaled, this will return from waiting at the specified time interval, see that stop == 1, and then exit.
Add pthread_cond_signal or pthread_cond_broadcast at the end of your worker function. This way, right before a thread exits, it will signal the cond variable allowing any other waiting threads to grab the mutex and finish processing. There is no harm in signaling a conditional variable if no threads are waiting on it, so this should be fine even for the last thread.
EDIT: Here is an MCVE that proves (as far as I can tell) that my answer above is wrong, heh. As soon as I press Ctrl+c, the program exits "immediately", which says to me all the threads are quickly acquiring the mutex after the broadcast, seeing that stop is false, and exiting. Then main joins on the threads and it's process over.
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <stdbool.h>
#include <signal.h>
#include <unistd.h>
#define NUM_THREADS 3
#define STACK_SIZE 10
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t c = PTHREAD_COND_INITIALIZER;
volatile bool stop = false;
int stack[STACK_SIZE] = { 0 };
int sp = 0; // stack pointer,, also doubles as the current stack size
void SigHandler(int sig)
{
if (sig == SIGINT)
{
stop = true;
}
else
{
printf("Received unexcepted signal %d\n", sig);
}
}
void* worker(void* param)
{
long tid = (long)(param);
while (stop == false)
{
// acquire the lock
pthread_mutex_lock(&m);
while (sp <= 0) // sp should never be < 0
{
// there is no data in the stack to consume, wait to get signaled
// this unlocks the mutex when it is called, and locks the
// mutex before it returns
pthread_cond_wait(&c, &m);
}
// when we get here we should be guaranteed sp >= 1
printf("thread %ld consuming stack[%d] = %d\n", tid, sp-1, stack[sp-1]);
sp--;
pthread_mutex_unlock(&m);
int sleepVal = rand() % 10;
printf("thread %ld sleeping for %d seconds...\n", tid, sleepVal);
sleep(sleepVal);
}
pthread_exit(NULL);
}
int main(void)
{
pthread_t threads[NUM_THREADS];
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
srand(time(NULL));
for (long i=0; i<NUM_THREADS; i++)
{
int rc = pthread_create(&threads[i], &attr, worker, (void*)i);
if (rc != 0)
{
fprintf(stderr, "Failed to create thread %ld\n", i);
}
}
while (stop == false)
{
// produce data in bursts
int numValsToInsert = rand() % (STACK_SIZE - sp);
printf("main producing %d values\n", numValsToInsert);
// acquire the lock
pthread_mutex_lock(&m);
for (int i=0; i<numValsToInsert; i++)
{
// produce values for the stack
int val = rand() % 10000;
// I think this should already be guaranteed..?
if (sp+1 < STACK_SIZE)
{
printf("main pushing stack[%d] = %d\n", sp, val);
stack[sp++] = val;
// signal the workers that data is ready
//printf("main signaling threads...\n");
//pthread_cond_signal(&c);
}
else
{
printf("stack full!\n");
}
}
pthread_mutex_unlock(&m);
// signal the workers that data is ready
printf("main signaling threads...\n");
pthread_cond_broadcast(&c);
int sleepVal = 1;//rand() % 5;
printf("main sleeping for %d seconds...\n", sleepVal);
sleep(sleepVal);
}
for (long i=0; i<NUM_THREADS; i++)
{
pthread_join(threads[i], NULL);
}
return 0;
}

How to make thread 2,3 and 4 wait for same signal from thread1?

I have a function to be executed by thread1,2,3,4.... Once thread1 gets access to the function, I use thread_cond for waiting for the other threads. Once thread1 does its work, I send thread_signal to the same cond. Thread2 is executing the function. But after it finishes execution, the other threads don't get access to the function.
Please help me
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
int limit = 0;
pthread_mutex_t mutex[100];
pthread_cond_t cond[100];
pthread_t tid[100];
void *enter()
{
if (limit == 1)
{
printf("waiting\n");
pthread_cond_wait(&cond[1],&mutex[1]);
}
gotofunction();
}
void gotofunction()
{
limit++;
/* Do work */
printf("Doing work\n");
sleep(1);
limit--;
printf("Going to give signal\n");
pthread_cond_signal(&cond[1]);
}
int main()
{
int n,i;
scanf("%d",&n);
for (i=0;i<100;i++)
{
pthread_mutex_init(&mutex[i], NULL);
pthread_cond_init(&cond[i], NULL);
}
for (i=0;i<n;i++)
{
pthread_create(&tid[i], NULL, enter, NULL);
sleep(0.5);
}
for (i=1;i<=n;i++)
{
pthread_join(tid[i], NULL);
}
}
Basically thread1 prints "doing work"
Thread2 prints "doing work"
Then nothing happens for the other threads
You must have the mutex that you passed to pthread_cond_wait() locked at the time you call it. That mutex must also be locked while you check and change the condition that the condition variable is paired with (in this case, limit == 1 is that condition).
You should also use the pattern while (condition) { pthread_cond_wait() } (rather than if (condition)), because the condition variable may wake up without the condition actually being true.
Changing your code to lock the mutex around the accesses to limit, and unlock it while simulating the work, looks like this:
void *enter()
{
pthread_mutex_lock(&mutex[1]);
while (limit == 1)
{
printf("waiting\n");
pthread_cond_wait(&cond[1],&mutex[1]);
}
gotofunction();
pthread_mutex_unlock(&mutex[1]);
return NULL;
}
void gotofunction()
{
limit++;
pthread_mutex_unlock(&mutex[1]);
/* Do work */
printf("Doing work\n");
sleep(1);
pthread_mutex_lock(&mutex[1]);
limit--;
printf("Going to give signal\n");
pthread_cond_signal(&cond[1]);
}
Of course when you check against a limit of 1, you could just use a plain mutex instead - but this scheme could be extended to allow N threads to execute work simultaneously, by changing the condition to while (limit >= N).

pthread_cond_wait and pthread_mutex_lock priority?

I have a multiple read threads and one write thread. If I lock mutex on one of the read threads and send broadcast from it, is it guaranteed that mutex will be locked by write thread waiting on pthread_cond_wait() or is there a possibility that another read thread that is wainting on pthread_mutex_lock() will lock mutex? Main question is does pthread_cond_wait() have priority over pthread_mutex_lock()?
If not, how can I achieve that the mutex will always be locked by write thread on pthread_cond_broadcast()?
Example
Read thread:
pthread_mutex_lock(mutex);
pthread_cond_broadcast(cond);
pthread_mutex_unlock(mutex);
Write thread:
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond, &mutex);
Let's assume both threads, read and write, reach the pthread_mutex_lock in the same moment. So, either write thread acquire the mutex on pthread_mutex_lock call, or read thread.
If it would be the write thread, the read one will wait on pthread_mutex_lock. The write, by calling pthread_cond_wait releases mutex and blocks on cond. It is done atomically. So, when read thread is grantex the mutex, we can be sure the the read one waits on cond. So, broadcast on cond reaches the write thread, it no more waits on cond but - still in scope of pthread_cond_wait - tries to get a lock on mutex (hold be read thread). After broadcasting cond the read thread releases the mutex and it goes to write thread. So write thread finally exits from pthread_cond_wait having the mutex locked. Remember to unlock it later.
If it would be the read thread, the write one will wait on pthread_mutex_lock, the read will broadcast a signal on cond then release the mutex. After then the write thread acquires the mutex on pthread_mutex_lock and immediately releases in it pthread_cond_wait waiting for cond (please note, that previous cond broadcast has no effect on current pthread_cond_wait). In the next iteration of read thread it acquires lock onmutex, send broadcast on cond and unlock mutex. It means the write thread moves forward on cond and acquires lock on mutex.
Does it answer your question about priority?
Update after comment.
Let's assume we have one thread (let's name it A for future reference) holding the lock on mutex and few other trying to acquire the same lock. As soon as the lock is released by first thread, there is no predictable which thread would acquire lock. Moreover, if the A thread has a loop and tries to reacquire lock on mutex, there is a chance it would be granted this lock and other threads would keep waiting. Adding pthread_cond_wait doesn't change anything in scope of granting a lock.
Let me quote fragments of POSIX specification (see https://stackoverflow.com/a/9625267/2989411 for reference):
These functions atomically release mutex and cause the calling thread to block on the condition variable cond; atomically here means "atomically with respect to access by another thread to the mutex and then the condition variable". That is, if another thread is able to acquire the mutex after the about-to-block thread has released it, then a subsequent call to pthread_cond_broadcast() or pthread_cond_signal() in that thread shall behave as if it were issued after the about-to-block thread has blocked.
And this is only guarantee given by standard regarding order of operations. Order of granting the lock to other threads is rather unpredictable and it changes depending on some very subtle fluctuation in timing.
For only mutex related code, please play a little with following code:
#define _GNU_SOURCE
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void *th(void *arg) {
int i;
char *s = arg;
for (i = 0; i < 10; ++i) {
pthread_mutex_lock(&mutex);
printf("%s %d\n", s, i);
//sleep(1);
pthread_mutex_unlock(&mutex);
#if 0
pthread_yield();
#endif
}
return NULL;
}
int main() {
int i;
for (i = 0; i < 10; ++i) {
pthread_t t1, t2, t3;
printf("================================\n");
pthread_create(&t1, NULL, th, "t1");
pthread_create(&t2, NULL, th, " t2");
pthread_create(&t3, NULL, th, " t3");
pthread_join(t1, NULL);
pthread_join(t2, NULL);
pthread_join(t3, NULL);
}
return 0;
}
On one machine (single CPU) it always shows whole loop from t3, then t2 and finally from t1. On another (2 cores) the order of threads is more random, but almost always it shows whole loop for each thread before granting the mutex to other thread. Rarely there is a situation like:
t1 8
t1 9
t3 0
t2 0
t2 1
[removed other t2 output]
t2 8
t2 9
t3 1
t3 2
Enable pthread_yield by replacing #if 0 with #if 1 and watch results and check output. For me it works in a way two threads display their output interlaced, then third thread finally has a chance to work. Add another or more thread. Play with sleep, etc. It confirms the random behaviour.
If you wish to experiment a little, compile and run following piece of code. It's an example of single producer - multiple consumers model. It can be run with two parameters: first is the number of consumer threads, second is the length of produced data series. If no parameters are given there is one consumer thread and 120 items to be processed. I also recommend with sleep/usleep in places marked /* play here */: change the value of arguments, remove the sleep at all, move it - when appropriate - to critical section or replace with pthread_yield and observe changes in behaviour.
#define _GNU_SOURCE
#include <assert.h>
#include <limits.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
struct data_t {
int seq;
int payload;
struct data_t *next;
};
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
struct data_t *first = NULL, *last = NULL;
int in_progress = 1;
int num_data = 120;
void push(int seq, int payload) {
struct data_t *e;
e = malloc(sizeof(struct data_t));
e->seq = seq;
e->payload = payload;
e->next = NULL;
if (last == NULL) {
assert(first == NULL);
first = last = e;
} else {
last->next = e;
last = e;
}
}
struct data_t pop() {
struct data_t res = {0};
if (first == NULL) {
res.seq = -1;
} else {
res.seq = first->seq;
res.payload = first->payload;
first = first->next;
if (first == NULL) {
last = NULL;
}
}
return res;
}
void *producer(void *arg __attribute__((unused))) {
int i;
printf("producer created\n");
for (i = 0; i < num_data; ++i) {
int val;
sleep(1); /* play here */
pthread_mutex_lock(&mutex);
val = rand() / (INT_MAX / 1000);
push(i, val);
pthread_mutex_unlock(&mutex);
pthread_cond_signal(&cond);
printf("prod %3d %3d signaled\n", i, val);
}
in_progress = 0;
printf("prod end\n");
pthread_cond_broadcast(&cond);
printf("prod end signaled\n");
return NULL;
}
void *consumer(void *arg) {
char c_id[1024];
int t_id = *(int *)arg;
sprintf(c_id, "%*s c %02d", t_id % 10, "", t_id);
printf("%s created\n", c_id);
while (1) {
struct data_t item;
pthread_mutex_lock(&mutex);
item = pop();
while (item.seq == -1 && in_progress) {
printf("%s waits for data\n", c_id);
pthread_cond_wait(&cond, &mutex);
printf("%s got signal\n", c_id);
item = pop();
}
if (!in_progress && item.seq == -1) {
printf("%s detected end of data.\n", c_id);
pthread_mutex_unlock(&mutex);
break;
}
pthread_mutex_unlock(&mutex);
printf("%s processing %3d %3d\n", c_id, item.seq, item.payload);
sleep(item.payload % 10); /* play here */
printf("%s processed %3d %3d\n", c_id, item.seq, item.payload);
}
printf("%s end\n", c_id);
return NULL;
}
int main(int argc, char *argv[]) {
int num_cons = 1;
pthread_t t_prod;
pthread_t *t_cons;
int i;
int *nums;
if (argc > 1) {
num_cons = atoi(argv[1]);
if (num_cons == 0) {
num_cons = 1;
}
if (num_cons > 99) {
num_cons = 99;
}
}
if (argc > 2) {
num_data = atoi(argv[2]);
if (num_data < 10) {
num_data = 10;
}
if (num_data > 600) {
num_data = 600;
}
}
printf("Spawning %d consumer%s for %d items.\n", num_cons, num_cons == 1 ? "" : "s", num_data);
t_cons = malloc(sizeof(pthread_t) * num_cons);
nums = malloc(sizeof(int) * num_cons);
if (!t_cons || !nums) {
printf("Out of memory!\n");
exit(1);
}
srand(time(NULL));
pthread_create(&t_prod, NULL, producer, NULL);
for (i = 0; i < num_cons; ++i) {
nums[i] = i + 1;
usleep(100000); /* play here */
pthread_create(t_cons + i, NULL, consumer, nums + i);
}
pthread_join(t_prod, NULL);
for (i = 0; i < num_cons; ++i) {
pthread_join(t_cons[i], NULL);
}
free(nums);
free(t_cons);
return 0;
}
I hope I have cleared your doubts and gave you some code to experiment and gain some confidence about pthread behaviour.

pthread_cond_wait for 2 threads

I'm trying to implement pthread_cond_wait for 2 threads. My test code is trying to use two threads to preform the following scenario:
Thread B waits for condition
Thread A prints "Hello" five times
Thread A signals thread B
Thread A waits
Thread B prints "Goodbye"
Thread B signals thread A
Loop to start (x5)
So far the code prints "Hello" five times and then gets stuck. From examples I've looked at it seems I'm on the right track, "Lock mutex, wait, get signaled by other thread, unlock mutex, do stuff, loop"
Test Code:
//Import
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
//global variables
pthread_cond_t condA = PTHREAD_COND_INITIALIZER;
pthread_cond_t condB = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void *threadA()
{
int i = 0, rValue, loopNum;
while(i<5)
{
//unlock mutex
rValue = pthread_mutex_unlock(&mutex);
//do stuff
for(loopNum = 1; loopNum <= 5; loopNum++)
printf("Hello %d\n", loopNum);
//signal condition of thread b
rValue = pthread_cond_signal(&condB);
//lock mutex
rValue = pthread_mutex_lock(&mutex);
//wait for turn
while( pthread_cond_wait(&condA, &mutex) != 0 )
i++;
}
}
void *threadB()
{
int n = 0, rValue;
while(n<5)
{
//lock mutex
rValue = pthread_mutex_lock(&mutex);
//wait for turn
while( pthread_cond_wait(&condB, &mutex) != 0 )
//unlock mutex
rValue = pthread_mutex_unlock(&mutex);
//do stuff
printf("Goodbye");
//signal condition a
rValue = pthread_cond_signal(&condA);
n++;
}
}
int main(int argc, char *argv[])
{
//create our threads
pthread_t a, b;
pthread_create(&a, NULL, threadA, NULL);
pthread_create(&b, NULL, threadB, NULL);
pthread_join(a, NULL);
pthread_join(b,NULL);
}
A pointer in the right direction would be greatly appreciated, thanks!
(Code compiled on Linux using "gcc timeTest.c -o timeTest -lpthread")
You have two problems. The first is that you aren't using while() loops correctly - for example, here:
//wait for turn
while( pthread_cond_wait(&condA, &mutex) != 0 )
i++;
The body of the while loop is the statement i++ - this will execute pthread_cond_wait() and i++ until the pthread_cond_wait() returns an error, so this is essentially an endless loop.
The second is that you can't use a pthreads condition variable on its own - it needs to be paired with some actual shared state (at its simplest, this shared state might just be a flag variable protected by a mutex). The pthread_cond_wait() function is used to wait for the shared state to reach a certain value, and the pthread_cond_signal() function is used when a thread has altered the shared state. Reworking your example to use such a variable:
//global variables
/* STATE_A = THREAD A runs next, STATE_B = THREAD B runs next */
enum { STATE_A, STATE_B } state = STATE_A;
pthread_cond_t condA = PTHREAD_COND_INITIALIZER;
pthread_cond_t condB = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void *threadA()
{
int i = 0, rValue, loopNum;
while(i<5)
{
/* Wait for state A */
pthread_mutex_lock(&mutex);
while (state != STATE_A)
pthread_cond_wait(&condA, &mutex);
pthread_mutex_unlock(&mutex);
//do stuff
for(loopNum = 1; loopNum <= 5; loopNum++)
printf("Hello %d\n", loopNum);
/* Set state to B and wake up thread B */
pthread_mutex_lock(&mutex);
state = STATE_B;
pthread_cond_signal(&condB);
pthread_mutex_unlock(&mutex);
i++;
}
return 0;
}
void *threadB()
{
int n = 0, rValue;
while(n<5)
{
/* Wait for state B */
pthread_mutex_lock(&mutex);
while (state != STATE_B)
pthread_cond_wait(&condB, &mutex);
pthread_mutex_unlock(&mutex);
//do stuff
printf("Goodbye\n");
/* Set state to A and wake up thread A */
pthread_mutex_lock(&mutex);
state = STATE_A;
pthread_cond_signal(&condA);
pthread_mutex_unlock(&mutex);
n++;
}
return 0;
}
Note that the use of two condition variables condA and condB is unnecessary here - the code would be just as correct if only one condition variable was used instead.
The code actually works almost fine on my machine when you add curly braces to the while loop.
Adding to what caf said, you'll enter an infinite loop when threadB is started after threadA has already sent the condB signal hence why you need to use a shared state in your while loop.
You can introduce artifical delay using usleep(1) on line 47 and see for yourself.

Resources