My C program creates a producer thread, saving data as fast as possible. The main thread consumes and prints these.
After days of bug finding, I noticed that if the mutex was initialized, then the program stops within 30 seconds (deadlock?).
However if the mutex is left uninitialized it works perfectly.
Can anyone explain this?? To avoid undefined behavior, I would prefer to initialize them if possible.
Further Info: Specifically it's locking up if "pthread_mutex_t signalM" (the signaling mutex) is initialized
Initialized
#include <stdlib.h> // exit_failure, exit_success
#include <stdio.h> // stdin, stdout, printf
#include <pthread.h> // threads
#include <string.h> // string
#include <unistd.h> // sleep
#include <stdbool.h> // bool
#include <fcntl.h> // open
struct event {
pthread_mutex_t critical;
pthread_mutex_t signalM;
pthread_cond_t signalC;
int eventCount;
};
struct allVars {
struct event inEvents;
struct event outEvents;
int bufferSize;
char buffer[10][128];
};
/**
* Advance the EventCount
*/
void advance(struct event *event) {
// increment the event counter
pthread_mutex_lock(&event->critical);
event->eventCount++;
pthread_mutex_unlock(&event->critical);
// signal await to continue
pthread_mutex_lock(&event->signalM);
pthread_cond_signal(&event->signalC);
pthread_mutex_unlock(&event->signalM);
}
/**
* Wait for ticket and buffer availability
*/
void await(struct event *event, int ticket) {
int eventCount;
// get the counter
pthread_mutex_lock(&event->critical);
eventCount = event->eventCount;
pthread_mutex_unlock(&event->critical);
// lock signaling mutex
pthread_mutex_lock(&event->signalM);
// loop until the ticket machine shows your number
while (ticket > eventCount) {
// wait until a ticket is called
pthread_cond_wait(&event->signalC, &event->signalM);
// get the counter
pthread_mutex_lock(&event->critical);
eventCount = event->eventCount;
pthread_mutex_unlock(&event->critical);
}
// unlock signaling mutex
pthread_mutex_unlock(&event->signalM);
}
/**
* Add to buffer
*/
void putBuffer(struct allVars *allVars, char data[]) {
// get the current write position
pthread_mutex_lock(&allVars->inEvents.critical);
int in = allVars->inEvents.eventCount;
pthread_mutex_unlock(&allVars->inEvents.critical);
// wait until theres a space free in the buffer
await(&allVars->outEvents, in - allVars->bufferSize + 1); // set to 2 to keep 1 index distance
// add data to buffer
strcpy(allVars->buffer[in % allVars->bufferSize], data);
// increment the eventCounter and signal
advance(&allVars->inEvents);
}
/**
* Get from buffer
*/
char *getBuffer(struct allVars *allVars) {
// get the current read position
pthread_mutex_lock(&allVars->outEvents.critical);
int out = allVars->outEvents.eventCount;
pthread_mutex_unlock(&allVars->outEvents.critical);
// wait until theres something in the buffer
await(&allVars->inEvents, out + 1);
// allocate memory for returned string
char *str = malloc(128);
// get the buffer data
strcpy(str, allVars->buffer[out % allVars->bufferSize]);
// increment the eventCounter and signal
advance(&allVars->outEvents);
return str;
}
/** child thread (producer) */
void *childThread(void *allVars) {
char str[10];
int count = 0;
while (true) {
sprintf(str, "%d", count++);
putBuffer(allVars, str);
}
pthread_exit(EXIT_SUCCESS);
}
int main(void) {
// init structs
struct event inEvents = {
PTHREAD_MUTEX_INITIALIZER,
PTHREAD_MUTEX_INITIALIZER,
PTHREAD_COND_INITIALIZER,
0
};
struct event outEvents = {
PTHREAD_MUTEX_INITIALIZER,
PTHREAD_MUTEX_INITIALIZER,
PTHREAD_COND_INITIALIZER,
0
};
struct allVars allVars = {
inEvents, // events
outEvents,
10, // buffersize
{"", {""}} // buffer[][]
};
// create child thread (producer)
pthread_t thread;
if (pthread_create(&thread, NULL, childThread, &allVars)) {
fprintf(stderr, "failed to create child thread");
exit(EXIT_FAILURE);
}
// (consumer)
while (true) {
char *out = getBuffer(&allVars);
printf("buf: %s\n", out);
free(out);
}
return (EXIT_SUCCESS);
}
Uninitialized
#include <stdlib.h> // exit_failure, exit_success
#include <stdio.h> // stdin, stdout, printf
#include <pthread.h> // threads
#include <string.h> // string
#include <unistd.h> // sleep
#include <stdbool.h> // bool
#include <fcntl.h> // open
struct event {
pthread_mutex_t critical;
pthread_mutex_t signalM;
pthread_cond_t signalC;
int eventCount;
};
struct allVars {
struct event inEvents;
struct event outEvents;
int bufferSize;
char buffer[10][128];
};
/**
* Advance the EventCount
*/
void advance(struct event *event) {
// increment the event counter
pthread_mutex_lock(&event->critical);
event->eventCount++;
pthread_mutex_unlock(&event->critical);
// signal await to continue
pthread_mutex_lock(&event->signalM);
pthread_cond_signal(&event->signalC);
pthread_mutex_unlock(&event->signalM);
}
/**
* Wait for ticket and buffer availability
*/
void await(struct event *event, int ticket) {
int eventCount;
// get the counter
pthread_mutex_lock(&event->critical);
eventCount = event->eventCount;
pthread_mutex_unlock(&event->critical);
// lock signaling mutex
pthread_mutex_lock(&event->signalM);
// loop until the ticket machine shows your number
while (ticket > eventCount) {
// wait until a ticket is called
pthread_cond_wait(&event->signalC, &event->signalM);
// get the counter
pthread_mutex_lock(&event->critical);
eventCount = event->eventCount;
pthread_mutex_unlock(&event->critical);
}
// unlock signaling mutex
pthread_mutex_unlock(&event->signalM);
}
/**
* Add to buffer
*/
void putBuffer(struct allVars *allVars, char data[]) {
// get the current write position
pthread_mutex_lock(&allVars->inEvents.critical);
int in = allVars->inEvents.eventCount;
pthread_mutex_unlock(&allVars->inEvents.critical);
// wait until theres a space free in the buffer
await(&allVars->outEvents, in - allVars->bufferSize + 1); // set to 2 to keep 1 index distance
// add data to buffer
strcpy(allVars->buffer[in % allVars->bufferSize], data);
// increment the eventCounter and signal
advance(&allVars->inEvents);
}
/**
* Get from buffer
*/
char *getBuffer(struct allVars *allVars) {
// get the current read position
pthread_mutex_lock(&allVars->outEvents.critical);
int out = allVars->outEvents.eventCount;
pthread_mutex_unlock(&allVars->outEvents.critical);
// wait until theres something in the buffer
await(&allVars->inEvents, out + 1);
// allocate memory for returned string
char *str = malloc(128);
// get the buffer data
strcpy(str, allVars->buffer[out % allVars->bufferSize]);
// increment the eventCounter and signal
advance(&allVars->outEvents);
return str;
}
/** child thread (producer) */
void *childThread(void *allVars) {
char str[10];
int count = 0;
while (true) {
sprintf(str, "%d", count++);
putBuffer(allVars, str);
}
pthread_exit(EXIT_SUCCESS);
}
int main(void) {
// init structs
struct event inEvents; /* = {
PTHREAD_MUTEX_INITIALIZER,
PTHREAD_MUTEX_INITIALIZER,
PTHREAD_COND_INITIALIZER,
0
}; */
struct event outEvents; /* = {
PTHREAD_MUTEX_INITIALIZER,
PTHREAD_MUTEX_INITIALIZER,
PTHREAD_COND_INITIALIZER,
0
}; */
struct allVars allVars = {
inEvents, // events
outEvents,
10, // buffersize
{"", {""}} // buffer[][]
};
// create child thread (producer)
pthread_t thread;
if (pthread_create(&thread, NULL, childThread, &allVars)) {
fprintf(stderr, "failed to create child thread");
exit(EXIT_FAILURE);
}
// (consumer)
while (true) {
char *out = getBuffer(&allVars);
printf("buf: %s\n", out);
free(out);
}
return (EXIT_SUCCESS);
}
Jonathan explained why the code that didn't initialize mutexes didn't deadlock (essentially because trying to use an uninitialized mutex would never block, it would just immediately return an error).
The problem causing the infinite wait in the version of the program that does properly initialize mutexes is that you aren't using your condition variables properly. The check of the predicate expression and the wait on the condition variable must be done atomically with respect to whatever other thread might be modifying the predicate. You code is checking a predicate that is a local variable that the other thread doesn't even have access to. Your code reads the actual predicate into a local variable within a critical section, but then the mutex for reading the predicate is released and a different mutex is acquired to read the 'false' predicate (which cannot be modified by the other thread anyway) atomically with the condition variable wait.
So you have a situation where the actual predicate, event->eventCount, can be modified and the signal for that modification be issued in between when the waiting thread reads the predicate and blocks on the condition variable.
I think the following will fix your deadlock, but I haven't had a chance to perform much testing. The change is essentially to remove the signalM mutex from struct event and replace any use of it with the critical mutex:
#include <stdlib.h> // exit_failure, exit_success
#include <stdio.h> // stdin, stdout, printf
#include <pthread.h> // threads
#include <string.h> // string
#include <unistd.h> // sleep
#include <stdbool.h> // bool
#include <fcntl.h> // open
struct event {
pthread_mutex_t critical;
pthread_cond_t signalC;
int eventCount;
};
struct allVars {
struct event inEvents;
struct event outEvents;
int bufferSize;
char buffer[10][128];
};
/**
* Advance the EventCount
*/
void advance(struct event *event) {
// increment the event counter
pthread_mutex_lock(&event->critical);
event->eventCount++;
pthread_mutex_unlock(&event->critical);
// signal await to continue
pthread_cond_signal(&event->signalC);
}
/**
* Wait for ticket and buffer availability
*/
void await(struct event *event, int ticket) {
// get the counter
pthread_mutex_lock(&event->critical);
// loop until the ticket machine shows your number
while (ticket > event->eventCount) {
// wait until a ticket is called
pthread_cond_wait(&event->signalC, &event->critical);
}
// unlock signaling mutex
pthread_mutex_unlock(&event->critical);
}
/**
* Add to buffer
*/
void putBuffer(struct allVars *allVars, char data[]) {
// get the current write position
pthread_mutex_lock(&allVars->inEvents.critical);
int in = allVars->inEvents.eventCount;
pthread_mutex_unlock(&allVars->inEvents.critical);
// wait until theres a space free in the buffer
await(&allVars->outEvents, in - allVars->bufferSize + 1); // set to 2 to keep 1 index distance
// add data to buffer
strcpy(allVars->buffer[in % allVars->bufferSize], data);
// increment the eventCounter and signal
advance(&allVars->inEvents);
}
/**
* Get from buffer
*/
char *getBuffer(struct allVars *allVars) {
// get the current read position
pthread_mutex_lock(&allVars->outEvents.critical);
int out = allVars->outEvents.eventCount;
pthread_mutex_unlock(&allVars->outEvents.critical);
// wait until theres something in the buffer
await(&allVars->inEvents, out + 1);
// allocate memory for returned string
char *str = malloc(128);
// get the buffer data
strcpy(str, allVars->buffer[out % allVars->bufferSize]);
// increment the eventCounter and signal
advance(&allVars->outEvents);
return str;
}
/** child thread (producer) */
void *childThread(void *allVars) {
char str[10];
int count = 0;
while (true) {
sprintf(str, "%d", count++);
putBuffer(allVars, str);
}
pthread_exit(EXIT_SUCCESS);
}
int main(void) {
// init structs
struct event inEvents = {
PTHREAD_MUTEX_INITIALIZER,
PTHREAD_COND_INITIALIZER,
0
};
struct event outEvents = {
PTHREAD_MUTEX_INITIALIZER,
PTHREAD_COND_INITIALIZER,
0
};
struct allVars allVars = {
inEvents, // events
outEvents,
10, // buffersize
{"", {""}} // buffer[][]
};
// create child thread (producer)
pthread_t thread;
if (pthread_create(&thread, NULL, childThread, &allVars)) {
fprintf(stderr, "failed to create child thread");
exit(EXIT_FAILURE);
}
// (consumer)
while (true) {
char *out = getBuffer(&allVars);
printf("buf: %s\n", out);
free(out);
}
return (EXIT_SUCCESS);
}
I modified the getBuffer() and putBuffer() routines as shown (in both the initialized and uninitialized versions of the code):
static
void putBuffer(struct allVars *allVars, char data[])
{
int lock_ok = pthread_mutex_lock(&allVars->inEvents.critical);
if (lock_ok != 0)
printf("%s(): lock error %d (%s)\n", __func__, lock_ok, strerror(lock_ok));
int in = allVars->inEvents.eventCount;
int unlock_ok = pthread_mutex_unlock(&allVars->inEvents.critical);
if (unlock_ok != 0)
printf("%s(): unlock error %d (%s)\n", __func__, unlock_ok, strerror(unlock_ok));
await(&allVars->outEvents, in - allVars->bufferSize + 1);
strcpy(allVars->buffer[in % allVars->bufferSize], data);
advance(&allVars->inEvents);
}
static
char *getBuffer(struct allVars *allVars)
{
int lock_ok = pthread_mutex_lock(&allVars->outEvents.critical);
if (lock_ok != 0)
printf("%s(): lock error %d (%s)\n", __func__, lock_ok, strerror(lock_ok));
int out = allVars->outEvents.eventCount;
int unlock_ok = pthread_mutex_unlock(&allVars->outEvents.critical);
if (unlock_ok != 0)
printf("%s(): unlock error %d (%s)\n", __func__, unlock_ok, strerror(unlock_ok));
await(&allVars->inEvents, out + 1);
char *str = malloc(128);
strcpy(str, allVars->buffer[out % allVars->bufferSize]);
advance(&allVars->outEvents);
return str;
}
Then running the uninitialized code yields a lot of messages like:
buf: 46566
putBuffer(): lock error 22 (Invalid argument)
getBuffer(): lock error 22 (Invalid argument)
putBuffer(): unlock error 22 (Invalid argument)
getBuffer(): unlock error 22 (Invalid argument)
Basically, it appears to me that your locking and unlocking is being ignored. There are other places in your code that you should check too.
Fundamentally, if you ignore the errors reported, you don't notice that the locking and unlocking is not working at all, and there's no reason for the code to stop running.
Always check the return values from system calls that can fail.
I don't have an immediate explanation for why the initialized code locks up. It does for me, running on Mac OS X 10.10.3 with GCC 5.1.0, after anywhere from about 100,000 to 800,000 iterations.
Related
This is an example code of produce consumer problems.
In Line 58,86,116,166,194 In function ‘void* Producer1(void*)’error: cast from ‘void*’ to ‘int’ loses precision [-fpermissive].
still don't know why this error happening and can't figure out its solution.
I Use Ubuntu as an operating system and g++ -pthread library function to solve this.
code is here:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include<semaphore.h>
//#include </usr/include/semaphore.h>
// for sleep
#include <unistd.h>
#define BUFF_SIZE 5 /* total number of slots */
typedef struct //Producing queue
{
char buf1[BUFF_SIZE]; /* shared var */
int in1; /* buf[in%BUFF_SIZE] is the first empty slot */
int out1; /* buf[out%BUFF_SIZE] is the first full slot */
sem_t full; /* keep track of the number of full spots */
sem_t empty; /* keep track of the number of empty spots */
// use correct type here
pthread_mutex_t mutex; /* enforce mutual exclusion to shared data */
} sbuf_t1;
typedef struct //Chokolate Cake queue
{
char buf2[BUFF_SIZE]; /* shared var */
int in2; /* buf[in%BUFF_SIZE] is the first empty slot */
int out2; /* buf[out%BUFF_SIZE] is the first full slot */
sem_t full; /* keep track of the number of full spots */
sem_t empty; /* keep track of the number of empty spots */
// use correct type here
pthread_mutex_t mutex; /* enforce mutual exclusion to shared data */
} sbuf_t2;
typedef struct //Vanilla Cake queue
{
char buf3[BUFF_SIZE]; /* shared var */
int in3; /* buf[in%BUFF_SIZE] is the first empty slot */
int out3; /* buf[out%BUFF_SIZE] is the first full slot */
sem_t full; /* keep track of the number of full spots */
sem_t empty; /* keep track of the number of empty spots */
// use correct type here
pthread_mutex_t mutex; /* enforce mutual exclusion to shared data */
} sbuf_t3;
sbuf_t1 shared1;
sbuf_t2 shared2;
sbuf_t3 shared3;
void *Producer1(void *arg)
{
int i, index;
char item;
index = (int)arg;
for(i=0; i<10; ++i)
{
sem_wait(&shared1.empty); //down called in this line
pthread_mutex_lock(&shared1.mutex); //acquired mutex lock now it can produced no contex will be switched
sleep(1);
shared1.buf1[shared1.in1] = 'C'; //item pusch in buffer like item[in] = item;
shared1.in1 = (shared1.in1+1)%BUFF_SIZE; //queue operation q = (in + 1) % buff_size
printf("[P%d] Producing Chokolate Cake...\n", index);
fflush(stdout);
/* Release the buffer */
pthread_mutex_unlock(&shared1.mutex); // unclock the mutex
/* Increment the number of full slots */
sem_post(&shared1.full); //buffer is full
//if (i % 2 == 1) sleep(1);
}
return NULL;
}
void *Producer2(void *arg)
{
int i, index;
char item;
index = (int)arg;
for(i=0; i<10; ++i)
{
item = i;
sem_wait(&shared1.empty);
pthread_mutex_lock(&shared1.mutex);
sleep(1);
shared1.buf1[shared1.in1] = 'V';
shared1.in1 = (shared1.in1+1)%BUFF_SIZE;
printf("[P%d] Producing Vanilla Cake...\n", index);
fflush(stdout);
/* Release the buffer */
pthread_mutex_unlock(&shared1.mutex);
/* Increment the number of full slots */
sem_post(&shared1.full);
//if (i % 2 == 1) sleep(1);
}
return NULL;
}
void *Chef_Z(void *arg)
{
int i, index;
char item;
index = (int)arg;
for (i=10; i > 0; i--) {
sem_wait(&shared1.full);
pthread_mutex_lock(&shared1.mutex);
sleep(1);
item=shared1.buf1[shared1.out1];
if(item == 'C') // Chokolate Cake queue
{
sem_wait(&shared2.full);
pthread_mutex_lock(&shared2.mutex);
shared2.buf2[shared2.in2]=item;
shared2.in2 = (shared2.in2+1)%BUFF_SIZE;
printf("[C_Z] Consuming Chokolate Cake and stored it in Chokolate queue ...\n");
pthread_mutex_unlock(&shared2.mutex);
/* Increment the number of full slots */
sem_post(&shared2.empty);
}
else if(item == 'V') // Vanilla Cake queue
{
sem_wait(&shared3.full);
pthread_mutex_lock(&shared3.mutex);
shared3.buf3[shared3.in3]=item;
shared3.in3 = (shared3.in3+1)%BUFF_SIZE;
printf("[C_Z] Consuming Vanilla Cake and stored it in Vanilla queue ...\n");
pthread_mutex_unlock(&shared3.mutex);
/* Increment the number of full slots */
sem_post(&shared3.empty);
}
shared1.out1 = (shared1.out1+1)%BUFF_SIZE;
fflush(stdout);
/* Release the buffer */
pthread_mutex_unlock(&shared1.mutex);
/* Increment the number of full slots */
sem_post(&shared1.empty);
/* Interleave producer and consumer execution */
//if (i % 2 == 1) sleep(1);
}
return NULL;
}
void *Waiter1(void *arg) //Chokolate cake waiter
{
int i, index;
char item;
index = (int)arg;
for (i=10; i > 0; i--) {
sem_wait(&shared2.full);
pthread_mutex_lock(&shared2.mutex);
sleep(1);
item=shared2.buf2[shared2.out2];
shared2.out2 = (shared2.out2+1)%BUFF_SIZE;
printf("[W%d] Consuming Chokolate Cake ...\n", index);
fflush(stdout);
/* Release the buffer */
pthread_mutex_unlock(&shared2.mutex);
/* Increment the number of full slots */
sem_post(&shared2.empty);
/* Interleave producer and consumer execution */
//if (i % 2 == 1) sleep(1);
}
return NULL;
}
void *Waiter2(void *arg) // Vanilla cake waiter
{
int i, index;
char item;
index = (int)arg;
for (i=10; i > 0; i--) {
sem_wait(&shared3.full);
pthread_mutex_lock(&shared3.mutex);
sleep(1);
item=shared3.buf3[shared3.out3];
shared3.out3 = (shared3.out3+1)%BUFF_SIZE;
printf("[W%d] Consuming Vanilla Cake ...\n", index);
fflush(stdout);
/* Release the buffer */
pthread_mutex_unlock(&shared3.mutex);
/* Increment the number of full slots */
sem_post(&shared3.empty);
/* Interleave producer and consumer execution */
//if (i % 2 == 1) sleep(1);
}
return NULL;
}
int main()
{
//pthread_t idP, idC;
pthread_t thread1,thread2,thread3,thread4,thread5;
int index;
void *producer1End;
void *producer2End;
void *chef_zEnd;
void *waiter1End;
void *waiter2End;
sem_init(&shared1.full, 0, 0);
sem_init(&shared1.empty, 0, BUFF_SIZE);
pthread_mutex_init(&shared1.mutex, NULL);
sem_init(&shared2.full, 0, 0);
sem_init(&shared2.empty, 0, BUFF_SIZE);
pthread_mutex_init(&shared2.mutex, NULL);
sem_init(&shared3.full, 0, 0);
sem_init(&shared3.empty, 0, BUFF_SIZE);
pthread_mutex_init(&shared3.mutex, NULL);
pthread_create(&thread1, NULL, Producer1, (void*)1 );
pthread_create(&thread2, NULL, Producer2, (void*)2 );
pthread_create(&thread3, NULL, Chef_Z, (void*)1 );
pthread_create(&thread4, NULL, Waiter1, (void*)1);
pthread_create(&thread5, NULL, Waiter2, (void*)2);
pthread_join(thread1,&producer1End);
pthread_join(thread2,&producer2End);
pthread_join(thread3,&chef_zEnd);
pthread_join(thread4,&waiter1End);
pthread_join(thread5,&waiter2End);
pthread_exit(NULL);
}
I'm working on the producer and consumer problem. The producer is generating a random variable and placing it in the buffer. After this is done I want to print out the contents of the buffer. I also want to print the contents of the buffer after the consumer consumes a variable from the buffer. So just as an example,
Producer Thread 34567834 adds 43 to the buffer, and the current buffer contains 7, 29, 43
I am not sure of a way to print the contents of a buffer in one printf() statement. Thanks for any help.
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
//Submit with screen shot of compiling and running code.
#define SIZE 20
#define NUMB_THREADS 10
#define PRODUCER_LOOPS 10
#define CONSUMER_LOOPS 2
#define TRUE 1
#define FALSE 0
typedef int buffer_t;
buffer_t buffer[SIZE];
int buffer_index;
pthread_mutex_t buffer_mutex;
/* initially buffer will be empty. full_sem
will be initialized to buffer SIZE, which means
SIZE number of producer threads can write to it.
And empty_sem will be initialized to 0, so no
consumer can read from buffer until a producer
thread posts to empty_sem */
sem_t full_sem; /* when 0, buffer is full */
sem_t empty_sem; /* when 0, buffer is empty. Kind of
like an index for the buffer */
/* sem_post algorithm:
mutex_lock sem_t->mutex
sem_t->value++
mutex_unlock sem_t->mutex
sem_wait algorithn:
mutex_lock sem_t->mutex
while (sem_t->value > 0) {
mutex_unlock sem_t->mutex
sleep... wake up
mutex_lock sem_t->mutex
}
sem_t->value--
mutex_unlock sem_t->mutex
*/
void insertbuffer(buffer_t value) {
if (buffer_index < SIZE) {
buffer[buffer_index++] = value;
} else {
printf("Buffer overflow\n");
}
}
buffer_t dequeuebuffer() {
if (buffer_index > 0) {
return buffer[--buffer_index]; // buffer_index-- would be error!
} else {
printf("Buffer underflow\n");
}
return 0;
}
int isempty() {
if (buffer_index == 0)
return TRUE;
return FALSE;
}
int isfull() {
if (buffer_index == SIZE)
return TRUE;
return FALSE;
}
void *producer2(void *thread_n) {
int thread_numb = *(int *)thread_n;
buffer_t value;
int i=0;
while (i++ < PRODUCER_LOOPS) {
sleep(rand() % 10);
value = rand() % 100;
pthread_mutex_lock(&buffer_mutex);
do {
// cond variables do the unlock/wait and wakeup/lock atomically,
// which avoids possible race conditions
pthread_mutex_unlock(&buffer_mutex);
// cannot go to slepp holding lock
sem_wait(&full_sem); // sem=0: wait. sem>0: go and decrement it
// there could still be race condition here. another
// thread could wake up and aqcuire lock and fill up
// buffer. that's why we need to check for spurious wakeups
pthread_mutex_lock(&buffer_mutex);
} while (isfull()); // check for spurios wake-ups
insertbuffer(value);
pthread_mutex_unlock(&buffer_mutex);
sem_post(&empty_sem); // post (increment) emptybuffer semaphore
//printf("Producer Thread %d adds %d added %d to buffer\n", pthread_self(), thread_numb, value);
printf("Producer Thread %d adds %d to the buffer, and the current buffer contains %d \n", pthread_self(), value, *buffer);
}
pthread_exit(0);
}
void *consumer2(void *thread_n) {
int thread_numb = *(int *)thread_n;
buffer_t value;
int i=0;
while (i++ < CONSUMER_LOOPS) {
pthread_mutex_lock(&buffer_mutex);
do {
pthread_mutex_unlock(&buffer_mutex);
sem_wait(&empty_sem);
pthread_mutex_lock(&buffer_mutex);
} while (isempty()); //check for spurios wakeups
value = dequeuebuffer(value);
pthread_mutex_unlock(&buffer_mutex);
sem_post(&full_sem); // post (increment) fullbuffer semaphore
printf("Consumer Thread %d dequeue %d from buffer, and the current buffer contains %d \n", pthread_self(), value, *buffer);
}
pthread_exit(0);
}
int main(int argc, int **argv) {
buffer_index = 0;
pthread_mutex_init(&buffer_mutex, NULL);
sem_init(&full_sem, // sem_t *sem
0, // int pshared. 0 = shared between threads of process, 1 = shared between processes
SIZE); // unsigned int value. Initial value
sem_init(&empty_sem,
0,
0);
/* full_sem is initialized to buffer size because SIZE number of
producers can add one element to buffer each. They will wait
semaphore each time, which will decrement semaphore value.
empty_sem is initialized to 0, because buffer starts empty and
consumer cannot take any element from it. They will have to wait
until producer posts to that semaphore (increments semaphore
value) */
pthread_t thread[NUMB_THREADS];
int thread_numb[NUMB_THREADS];
int i;
for (i = 0; i < NUMB_THREADS; ) {
thread_numb[i] = i;
if(i <= 2)
{
pthread_create(thread + i, // pthread_t *t
NULL, // const pthread_attr_t *attr
producer2, // void *(*start_routine) (void *)
thread_numb + i); // void *arg
}
thread_numb[i] = i;
// playing a bit with thread and thread_numb pointers...
pthread_create(&thread[i], // pthread_t *t
NULL, // const pthread_attr_t *attr
consumer2, // void *(*start_routine) (void *)
&thread_numb[i]); // void *arg
i++;
}
for (i = 0; i < NUMB_THREADS; i++)
pthread_join(thread[i], NULL);
pthread_mutex_destroy(&buffer_mutex);
sem_destroy(&full_sem);
sem_destroy(&empty_sem);
return 0;
}
You can't print an array of unknown length in one line, but you can modify your insertbuffer to show the details every time you insert a value. Or, implement the printing as a separate function. Then calling it is a one-liner, obviously.
void insertbuffer(int threadnum, buffer_t value) {
int i;
if (buffer_index < SIZE) {
buffer[buffer_index++] = value;
printf("Producer Thread %d adds %d to the buffer, and the current buffer contains",
threadnum, (int)value);
for(i=0; i<buffer_index; i++) {
if(i)
printf(",");
printf(" %d", (int)buffer[i]);
}
printf("\n");
} else {
printf("Buffer overflow\n");
}
}
I am trying to create two threads resembling TaskA and TaskB. Both TaskA and TaskB do some kind of computation that it is not very interesting for this post. TaskA and TaskB have to be executed 10 times in order to cover the whole array. TaskA has an input AA and an output BB. BB is also the input of TaskB. CC is the output of TaskB. Because BB is written by taskA and read by taskB we need mutexes.
The behavior I would like to achieve is that when TaskA operates on i, TaskB operates on i-1 in parallel, where i is the number of arrays that are processed.
I want to avoid TaskB to wait for TaskA to finish for every i.
The problem here is that I have a deadlock. ThreadA and ThreadB represent TaskA and TaskB. To make it easier I removed all the computations and I left only synchronization instructions. The deadlock is caused because ThreadA signals the conditional variable CV[0] before threadB is in the state that waits for CV[0].
Do you know any way to remove the deadlock but without TaskA waiting for TaskB to finish and vice versa. Ideally when TaskA operates on array i TaskB should operate on array i-1.
/* Includes */
#include <unistd.h> /* Symbolic Constants */
#include <sys/types.h> /* Primitive System Data Types */
#include <errno.h> /* Errors */
#include <stdio.h> /* Input/Output */
#include <stdlib.h> /* General Utilities */
#include <pthread.h> /* POSIX Threads */
#include <string.h> /* String handling */
#include <semaphore.h> /* Semaphore */
#include <stdint.h>
#define ARRAY_SIZE 2048*2400
#define DEBUG
//#define CHECK_RESULTS
pthread_mutex_t mutex[10];
pthread_cond_t cv[10];
/* prototype for thread routine */
void threadA ( void *ptr );
void threadB ( void *ptr );
struct thread_arg
{
uint32_t *in;
uint32_t *out;
uint32_t ID;
};
int main()
{
pthread_t pthA;
pthread_t pthB;
//Memory allocation
uint32_t *AA = malloc(10*ARRAY_SIZE*sizeof(uint32_t));
uint32_t *BB = malloc(10*ARRAY_SIZE*sizeof(uint32_t));
uint32_t *CC = malloc(10*ARRAY_SIZE*sizeof(uint32_t));
unsigned int j,i;
// THread Arguments
struct thread_arg arguments[2];
arguments[0].in = AA;
arguments[0].out = BB;
arguments[0].ID = 1;
arguments[1].in = BB;
arguments[1].out = CC;
arguments[1].ID = 2;
//Init arguments data
for (j=0;j<10;j++)
{
for (i=0;i<ARRAY_SIZE;i++)
{
AA[j*ARRAY_SIZE+i] = i;
BB[j*ARRAY_SIZE+i] = 0;
CC[j*ARRAY_SIZE+i] = 99 ;
}
}
//Semaphore and conditional variables init
for (i=0;i<10;i++){
pthread_mutex_init(&mutex[i], NULL);
pthread_cond_init (&cv[i], NULL);
}
pthread_create (&pthA, NULL, (void *) &threadA, (void *) &arguments[0]);
pthread_create (&pthB, NULL, (void *) &threadB, (void *) &arguments[1]);
pthread_join(pthA, NULL);
pthread_join(pthB, NULL);
// Destroy Semaphores and CVs
for (i=0;i<10;i++)
{
pthread_mutex_destroy(&mutex[i]);
pthread_cond_destroy(&cv[i]);
}
// Checking results
exit(0);
} /* main() */
void threadA ( void *ptr )
{
int i;
struct thread_arg *arg = (struct thread_arg *) ptr;
for (i=0;i<10;i++)
{
pthread_mutex_lock(&mutex[i]);
printf("TA: LOCK_M%d \n",i);
pthread_cond_signal(&cv[i]);
printf("TA: SIG_CV%d\n",i);
pthread_mutex_unlock(&mutex[i]);
printf("TA: UNL_M%d\n",i);
}
pthread_exit(0); /* exit thread */
}
void threadB ( void *ptr )
{
int i;
struct thread_arg *arg = (struct thread_arg *) ptr;
for (i=0;i<10;i++)
{
pthread_mutex_lock(&mutex[i]);
printf("TB: WAIT_CV%d\n",i,i);
pthread_cond_wait(&cv[i], &mutex[i]);
printf("TB CV%d_PASSED\n",i);
pthread_mutex_unlock(&mutex[i]);
printf("TB UNL_M%d \n",i);
}
pthread_exit(NULL);
}
As WhozCraig commented, a condition variable needs to be paired with a condition over some shared state, known as a predicate. The mutex is used to protect the shared state.
In this example, your shared state could be an integer that contains the highest index of BB[] that ThreadA has produced. ThreadB then waits for this number to reach the index that it is up to reading. In this design, you only need one mutex and one condition variable. The globals would then be:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
int BB_ready = -1; /* Protected by 'mutex' */
(Using the static PTHREAD_*_INITIALIZER initialisers means that you don't need to bother with pthread_*_init() and pthread_*_destroy()).
The loop in ThreadA would then be:
for (i=0;i<10;i++)
{
/* Process AA[i] into BB[i] here */
/* Now mark BB[i] as ready */
pthread_mutex_lock(&mutex);
printf("TA: LOCK_M%d \n",i);
BB_ready = i;
pthread_cond_signal(&cv);
printf("TA: SIG_CV%d\n",i);
pthread_mutex_unlock(&mutex);
printf("TA: UNL_M%d\n",i);
}
..and in ThreadB:
for (i=0;i<10;i++)
{
/* Wait for BB[i] to be ready */
pthread_mutex_lock(&mutex);
printf("TB: WAIT_CV%d\n",i);
while (BB_ready < i)
pthread_cond_wait(&cv, &mutex);
printf("TB CV%d_PASSED\n",i);
pthread_mutex_unlock(&mutex);
printf("TB UNL_M%d \n",i);
/* Now process BB[i] into CC[i] here */
}
Notice that pthread_cond_signal() is called whenever the shared state has changed, which allows the other thread to wake up and re-check the state, if it's waiting.
The waiting thread always loops around, checking the state and then waiting on the condition variable if the state isn't ready yet.
I am attempting to implement producer/consumer communication through a bounded buffer using semaphores and locks I've already implemented in C. I need to have the producer place "hello world" onto a 5-byte buffer, one character at a time. The producer needs to block if the buffer is full. I am struggling to figure out how to place the string on the buffer. Here is what I have so far:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define BUF_SIZE 5
// the buffer works like a stack for
// the sake of simplicity, if needed
// we may implement a queue
typedef struct {
int buf[BUF_SIZE]; // the buffer
size_t len; // number of items in the buffer
pthread_mutex_t mutex; // needed to add/remove data from the buffer
pthread_cond_t can_produce; // signaled when items are removed
pthread_cond_t can_consume; // signaled when items are added
} buffer_t;
// produce random numbers
void* producer(void *arg) {
buffer_t *buffer = (buffer_t*)arg;
while(1) {
#ifdef UNDERFLOW
// used to show that if the producer is somewhat "slow"
// the consumer will not fail (i.e. it'll just wait
// for new items to consume)
sleep(rand() % 3);
#endif
pthread_mutex_lock(&buffer->mutex);
if(buffer->len == BUF_SIZE) { // full
// wait until some elements are consumed
pthread_cond_wait(&buffer->can_produce, &buffer->mutex);
}
// in real life it may be some data fetched from
// sensors, the web, or just some I/O
int t = rand();
printf("Produced: %d\n", t);
// append data to the buffer
buffer->buf[buffer->len] = t;
++buffer->len;
// signal the fact that new items may be consumed
pthread_cond_signal(&buffer->can_consume);
pthread_mutex_unlock(&buffer->mutex);
}
// never reached
return NULL;
}
// consume random numbers
void* consumer(void *arg) {
buffer_t *buffer = (buffer_t*)arg;
while(1) {
#ifdef OVERFLOW
// show that the buffer won't overflow if the consumer
// is slow (i.e. the producer will wait)
sleep(rand() % 3);
#endif
pthread_mutex_lock(&buffer->mutex);
if(buffer->len == 0) { // empty
// wait for new items to be appended to the buffer
pthread_cond_wait(&buffer->can_consume, &buffer->mutex);
}
// grab data
--buffer->len;
printf("Consumed: %d\n", buffer->buf[buffer->len]);
// signal the fact that new items may be produced
pthread_cond_signal(&buffer->can_produce);
pthread_mutex_unlock(&buffer->mutex);
}
// never reached
return NULL;
}
int main(int argc, char *argv[]) {
buffer_t buffer = {
.len = 0,
.mutex = PTHREAD_MUTEX_INITIALIZER,
.can_produce = PTHREAD_COND_INITIALIZER,
.can_consume = PTHREAD_COND_INITIALIZER
};
pthread_t prod, cons;
pthread_create(&prod, NULL, producer, (void*)&buffer);
pthread_create(&cons, NULL, consumer, (void*)&buffer);
pthread_join(prod, NULL); // will wait forever
pthread_join(cons, NULL);
return 0;
}
a stack is a Last in, First out. Not what you want.
I would strongly implementing a circular buffer, with a head and tail pointer.
Then use the mutex (no need for a semaphore) to lock the critical code.
(the critical code for the producer is where it is adding/trying to add a char)
(the critical code for the consumer is where it is removing/trying to remove a char)
the mutex is so the two critical variables 'head' and 'tail' are stable
while any one process is accessing the circular queue.
I have written a code for producer-consumer problem.But I am not getting the output.There is no compilation error,but warning in my program.I am confused.Trying very hard.But can't get it.Please tell me what is wrong in my program.What will be the correct program.I am getting frustrated.Please help guys.
Here is the code-
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include </usr/include/semaphore.h>
#define BUFF_SIZE 5 /* total number of slots */
#define NP 3 /* total number of producers */
#define NC 3 /* total number of consumers */
#define NITERS 4 /* number of items produced/consumed */
typedef struct {
int buf[BUFF_SIZE]; /* shared var */
int in; /* buf[in%BUFF_SIZE] is the first empty slot */
int out; /* buf[out%BUFF_SIZE] is the first full slot */
sem_t full; /* keep track of the number of full spots */
sem_t empty; /* keep track of the number of empty spots */
sem_t mutex; /* enforce mutual exclusion to shared data */
} sbuf_t;
sbuf_t shared;
void *Producer(void *arg) {
int i, item, index;
index = (int) arg;
for (i = 0; i < NITERS; i++) {
/* Produce item */
item = i;
/* Prepare to write item to buf */
/* If there are no empty slots, wait */
sem_wait(&shared.empty);
/* If another thread uses the buffer, wait */
sem_wait(&shared.mutex);
shared.buf[shared.in] = item;
shared.in = (shared.in+1)%BUFF_SIZE;
printf("[P%d] Producing %d ...\n", index, item); fflush(stdout);
/* Release the buffer */
sem_post(&shared.mutex);
/* Increment the number of full slots */
sem_post(&shared.full);
/* Interleave producer and consumer execution */
if (i % 2 == 1) sleep(1);
}
return NULL;
}
void *Consumer(void *arg) {
int i, item, index;
index = (int) arg;
for (i = NITERS; i > 0; i--) {
sem_wait(&shared.full);
sem_wait(&shared.mutex);
item = i;
item = shared.buf[shared.out];
shared.out = (shared.out + 1) % BUFF_SIZE;
printf("[C%d] Consuming %d ...\n", index, item); fflush(stdout);
/* Release the buffer */
sem_post(&shared.mutex);
/* Increment the number of full slots */
sem_post(&shared.empty);
/* Interleave producer and consumer execution */
if (i % 2 == 1) sleep(1);
}
return NULL;
}
int main() {
pthread_t idP, idC;
int index;
sem_init(&shared.full, 0, 0);
sem_init(&shared.empty, 0, BUFF_SIZE);
pthread_mutex_init(&shared.mutex, NULL);
for (index = 0; index < NP; index++) {
/* Create a new producer */
pthread_create(&idP, NULL, Producer, (void*)index);
}
/*create a new Consumer*/
for (index = 0;index < NC;index++) {
pthread_create(&idC, NULL, Consumer, (void*)index);
}
pthread_exit(NULL);
}
Maybe you should take the Compiler warnings more serious.
Incorrect types and undefined functions are usually shown
as warning...
I haven't checked the Logic of your program, but the principle should work:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include </usr/include/semaphore.h>
// for sleep
#include <unistd.h>
#define BUFF_SIZE 5 /* total number of slots */
#define NP 3 /* total number of producers */
#define NC 3 /* total number of consumers */
#define NITERS 4 /* number of items produced/consumed */
typedef struct
{
int buf[BUFF_SIZE]; /* shared var */
int in; /* buf[in%BUFF_SIZE] is the first empty slot */
int out; /* buf[out%BUFF_SIZE] is the first full slot */
sem_t full; /* keep track of the number of full spots */
sem_t empty; /* keep track of the number of empty spots */
// use correct type here
pthread_mutex_t mutex; /* enforce mutual exclusion to shared data */
} sbuf_t;
sbuf_t shared;
void *Producer(void *arg)
{
int i, item, index;
index = (int)arg;
for (i=0; i < NITERS; i++)
{
/* Produce item */
item = i;
/* Prepare to write item to buf */
/* If there are no empty slots, wait */
sem_wait(&shared.empty);
/* If another thread uses the buffer, wait */
pthread_mutex_lock(&shared.mutex);
shared.buf[shared.in] = item;
shared.in = (shared.in+1)%BUFF_SIZE;
printf("[P%d] Producing %d ...\n", index, item);
fflush(stdout);
/* Release the buffer */
pthread_mutex_unlock(&shared.mutex);
/* Increment the number of full slots */
sem_post(&shared.full);
/* Interleave producer and consumer execution */
if (i % 2 == 1) sleep(1);
}
return NULL;
}
void *Consumer(void *arg)
{
int i, item, index;
index = (int)arg;
for (i=NITERS; i > 0; i--) {
sem_wait(&shared.full);
pthread_mutex_lock(&shared.mutex);
item=i;
item=shared.buf[shared.out];
shared.out = (shared.out+1)%BUFF_SIZE;
printf("[C%d] Consuming %d ...\n", index, item);
fflush(stdout);
/* Release the buffer */
pthread_mutex_unlock(&shared.mutex);
/* Increment the number of full slots */
sem_post(&shared.empty);
/* Interleave producer and consumer execution */
if (i % 2 == 1) sleep(1);
}
return NULL;
}
int main()
{
pthread_t idP, idC;
int index;
sem_init(&shared.full, 0, 0);
sem_init(&shared.empty, 0, BUFF_SIZE);
pthread_mutex_init(&shared.mutex, NULL);
for (index = 0; index < NP; index++)
{
/* Create a new producer */
pthread_create(&idP, NULL, Producer, (void*)index);
}
/*create a new Consumer*/
for(index=0; index<NC; index++)
{
pthread_create(&idC, NULL, Consumer, (void*)index);
}
pthread_exit(NULL);
}
I hope this helps.
There are still compiler warnings. The correct way to get the integer value from a void pointer is:
index = *(int*)arg;
And, also to pass an integer pointer, is below:
pthread_create(&idC,NULL,Consumer,(void*)&index);
I still have some doubts as to how the Consumer or the Producer thread will receive the passed integer value since that is an address of the index variable in the for loop in the main thread. As soon as index gets incremented, the Consumer or Producer thread is affected by this increment.