Producer-consumer example. Sometimes the execution deadlocks - c

I am trying to learn how to use pthreads following Douglas Niehaus' classes.
Now I'm in the middle of the producer-consumer problem. This is the solution I've reached so far:
PRODUCER:
void *producer (void *parg) {
queue *fifo;
int item_produced;
pcdata *mydata;
int my_tid;
int *total_produced;
mydata = (pcdata *) parg;
fifo = mydata->q;
total_produced = mydata->count;
my_tid = mydata->tid;
/*
* Continue producing until the total produced reaches the
* configured maximum
*/
while (1) {
/*
* Do work to produce an item. Tthe get a slot in the queue for
* it. Finally, at the end of the loop, outside the critical
* section, announce that we produced it.
*/
do_work(PRODUCER_CPU, PRODUCER_BLOCK);
pthread_mutex_lock(fifo->mutex);
/*
* If the queue is full, we have no place to put anything we
* produce, so wait until it is not full.
*/
while(fifo->full && *total_produced != WORK_MAX) {
pthread_cond_wait(fifo->notFull,fifo->mutex);
}
if(*total_produced >= WORK_MAX) {
printf("PRODUCER %d is exitting because the production reached the MAX\n",my_tid);
pthread_cond_signal(fifo->notFull);
pthread_mutex_unlock(fifo->mutex);
break;
}
/*
* OK, so we produce an item. Increment the counter of total
* widgets produced, and add the new widget ID, its number, to the
* queue.
*/
item_produced = (*total_produced)++;
queueAdd (fifo, item_produced);
pthread_cond_signal(fifo->notEmpty);
pthread_mutex_unlock(fifo->mutex);
/*
* Announce the production outside the critical section
*/
printf("prod %d:\t %d.\n", my_tid, item_produced);
}
printf("prod %d:\texited\n", my_tid);
return (NULL);
}
CONSUMER:
void *consumer (void *carg)
{
queue *fifo;
int item_consumed;
pcdata *mydata;
int my_tid;
int *total_consumed;
mydata = (pcdata *) carg;
fifo = mydata->q;
total_consumed = mydata->count;
my_tid = mydata->tid;
/*
* Continue producing until the total consumed by all consumers
* reaches the configured maximum
*/
while (1) {
/*
* If the queue is empty, there is nothing to do, so wait until it
* si not empty.
*/
pthread_mutex_lock(fifo->mutex);
while(fifo->empty && *total_consumed != WORK_MAX) {
pthread_cond_wait(fifo->notEmpty,fifo->mutex);
}
if(*total_consumed >= WORK_MAX) {
printf("CONSUMER %d is exitting because the compsuption reached the MAX\n",my_tid);
pthread_cond_signal(fifo->notEmpty);
pthread_mutex_unlock(fifo->mutex);
break;
}
/*
* Remove the next item from the queue. Increment the count of the
* total consumed. Note that item_consumed is a local copy so this
* thread can retain a memory of which item it consumed even if
* others are busy consuming them.
*/
queueRemove (fifo, &item_consumed);
(*total_consumed)++;
pthread_cond_signal(fifo->notFull);
pthread_mutex_unlock(fifo->mutex);
/*
* Do work outside the critical region to consume the item
* obtained from the queue and then announce its consumption.
*/
do_work(CONSUMER_CPU,CONSUMER_CPU);
printf ("con %d:\t %d.\n", my_tid, item_consumed);
}
printf("con %d:\texited\n", my_tid);
return (NULL);
}
This seems to work fine with rather small number of producers and consumers, for example 100 consumers and 50 producers or viceversa. But if I try the code with more than 2000 producers and/or consumers, the execution hangs. I think I've reached a deadlock but I'm unable to find it.
Any help would be appreciated.
NOTE:
The signals inside the if-block:
if(*total_consumed >= WORK_MAX) { ... }
and the if-block:
if(*total_consumed >= WORK_MAX) {...}
are something that I added because the execution without them would hang up with more than 5 producers and/or consumers.
My reasoning is that if the work limit has been reached, the other producers should be told so they can exit too. And the same goes for the consumers.
NOTE 2:
The following code is already provided to the student:
typedef struct {
int buf[QUEUESIZE]; /* Array for Queue contents, managed as circular queue */
int head; /* Index of the queue head */
int tail; /* Index of the queue tail, the next empty slot */
int full; /* Flag set when queue is full */
int empty; /* Flag set when queue is empty */
pthread_mutex_t *mutex; /* Mutex protecting this Queue's data */
pthread_cond_t *notFull; /* Used by producers to await room to produce*/
pthread_cond_t *notEmpty; /* Used by consumers to await something to consume*/
} queue;
typedef struct {
queue *q;
int *count;
int tid;
} pcdata;
/******************************************************/
queue *queueInit (void) {
queue *q;
/*
* Allocate the structure that holds all queue information
*/
q = (queue *)malloc (sizeof (queue));
if (q == NULL) return (NULL);
/*
* Initialize the state variables. See the definition of the Queue
* structure for the definition of each.
*/
q->empty = 1;
q->full = 0;
q->head = 0;
q->tail = 0;
/*
* Allocate and initialize the queue mutex
*/
q->mutex = (pthread_mutex_t *) malloc (sizeof (pthread_mutex_t));
pthread_mutex_init (q->mutex, NULL);
/*
* Allocate and initialize the notFull and notEmpty condition
* variables
*/
q->notFull = (pthread_cond_t *) malloc (sizeof (pthread_cond_t));
pthread_cond_init (q->notFull, NULL);
q->notEmpty = (pthread_cond_t *) malloc (sizeof (pthread_cond_t));
pthread_cond_init (q->notEmpty, NULL);
return (q);
}
And in main:
int main (int argc, char *argv[]) {
// More initializations
pthread_t *con;
int cons;
int *concount;
queue *fifo;
int i;
pthread_t *pro;
int *procount;
int pros;
pcdata *thread_args;
fifo = queueInit ();
if (fifo == NULL) {
fprintf (stderr, "main: Queue Init failed.\n");
exit (1);
}
// More initializations and creation of the threads:
for (i=0; i<pros; i++){
/*
* Allocate memory for each producer's arguments
*/
thread_args = (pcdata *)malloc (sizeof (pcdata));
if (thread_args == NULL) {
fprintf (stderr, "main: Thread_Args Init failed.\n");
exit (1);
}
/*
* Fill them in and then create the producer thread
*/
thread_args->q = fifo;
thread_args->count = procount;
thread_args->tid = i;
pthread_create (&pro[i], NULL, producer, thread_args);
}
}
}

Related

Dont'tknow why this problem happening and Cant Figure this out solution.can anyone solve this?

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);
}

Is it possible to create the producer consumer problem with condition variables and signal instead of broadcast?

I am trying to run the producer-consumer problem while usingpthread_cond_signal() instead of pthread_cond_broadcast(), however, I attempted a lot of things and can't seem to do it (if I choose n producers and one consumer then the consumer finishes but not all producers finish, some are stuck on full queue so the problem never finishes executing. I got the initial code from someone else GitHub and I am trying to edit it to accomplish that (you can view the code on the attached link and I will paste it at the end of the post). The relevant parts here are the producer and consumer functions, currently I have the following:
Consumer:
void *consumer (void *carg)
{
queue *fifo;
int item_consumed;
pcdata *mydata;
int my_tid;
int *total_consumed;
mydata = (pcdata *) carg;
fifo = mydata->q;
total_consumed = mydata->count;
my_tid = mydata->tid;
while (1) {
pthread_mutex_lock(fifo->mutex); //start of the critical section
while (fifo->empty && *total_consumed != WORK_MAX) {
printf ("con %d: EMPTY.\n", my_tid);
pthread_cond_wait(fifo->notEmpty, fifo->mutex); //if queue is empty then wait for signal that it has something to start consuming
}
if (*total_consumed >= WORK_MAX) {
pthread_mutex_unlock(fifo->mutex); //if max work is reached then unlock the mutex and exit
break;
}
queueRemove (fifo, &item_consumed); //reaching this means that queue isn\t empty so just consume
(*total_consumed)++;
do_work(CONSUMER_CPU,CONSUMER_CPU);
printf ("con %d: %d.\n", my_tid, item_consumed);
pthread_cond_signal(fifo->notFull); //consumption is done so queue isn't full, signal to producer
pthread_mutex_unlock(fifo->mutex);
}
printf("con %d: exited\n", my_tid);
return (NULL);
}
Producer:
void *producer (void *parg)
{
queue *fifo;
int item_produced;
pcdata *mydata;
int my_tid;
int *total_produced;
mydata = (pcdata *) parg;
fifo = mydata->q;
total_produced = mydata->count;
my_tid = mydata->tid;
while (1) {
pthread_mutex_lock(fifo->mutex);
do_work(PRODUCER_CPU, PRODUCER_BLOCK);
while (fifo->full && *total_produced != WORK_MAX) {
printf ("prod %d: FULL.\n", my_tid);
pthread_cond_wait(fifo->notFull, fifo->mutex); //if queue is full then wait for signal that is not anyone to start producing
}
if (*total_produced >= WORK_MAX) {
pthread_mutex_unlock(fifo->mutex); //if total work reached then exit
break;
}
item_produced = (*total_produced)++;
queueAdd (fifo, item_produced);
printf("prod %d: %d.\n", my_tid, item_produced);
pthread_cond_signal(fifo->notEmpty); //reaching this means we produced something so signal that queue is not empty
pthread_mutex_unlock(fifo->mutex);
}
printf("prod %d: exited\n", my_tid);
return (NULL);
}
My problem is that not all threads are getting the signal to exit, for example running up to 13 producers and one consumer worked but everything above 13 producers with one consumer gets stuck, here is an execution sample of running 14 producers with one consumer:
con 0: EMPTY.
prod 3: 2.
prod 9: FULL.
prod 11: FULL.
prod 13: FULL.
prod 0: 0.
prod 8: 3.
prod 6: 1.
prod 12: FULL.
prod 7: 4.
prod 10: FULL.
prod 1: FULL.
prod 2: FULL.
prod 5: FULL.
prod 4: FULL.
prod 9: 5.
prod 3: FULL.
prod 0: FULL.
prod 6: FULL.
prod 8: FULL.
prod 7: FULL.
prod 9: FULL.
con 0: 0.
prod 11: 6.
prod 13: FULL.
prod 11: FULL.
con 0: 1.
prod 12: 7.
prod 10: FULL.
prod 12: FULL.
con 0: 2.
prod 1: 8.
prod 2: FULL.
prod 1: FULL.
con 0: 3.
prod 4: 9.
prod 5: FULL.
prod 4: FULL.
con 0: 4.
prod 3: 10.
prod 0: FULL.
prod 3: FULL.
con 0: 5.
prod 6: 11.
prod 8: FULL.
prod 6: FULL.
con 0: 6.
prod 7: 12.
prod 9: FULL.
prod 7: FULL.
con 0: 7.
prod 13: 13.
prod 11: FULL.
prod 13: FULL.
con 0: 8.
prod 12: 14.
prod 10: FULL.
prod 12: FULL.
con 0: 9.
prod 2: 15.
prod 1: FULL.
prod 2: FULL.
con 0: 10.
prod 5: 16.
prod 4: FULL.
prod 5: FULL.
con 0: 11.
prod 3: 17.
prod 0: FULL.
prod 3: FULL.
con 0: 12.
prod 8: 18.
prod 6: FULL.
prod 8: FULL.
con 0: 13.
prod 9: 19.
prod 7: FULL.
prod 9: FULL.
con 0: 14.
prod 11: 20.
prod 13: FULL.
prod 11: FULL.
con 0: 15.
prod 10: 21.
prod 12: FULL.
prod 10: FULL.
con 0: 16.
prod 2: 22.
prod 1: FULL.
prod 2: FULL.
con 0: 17.
prod 4: 23.
prod 5: FULL.
prod 4: FULL.
con 0: 18.
prod 0: 24.
prod 3: FULL.
prod 0: FULL.
con 0: 19.
prod 6: 25.
prod 8: FULL.
prod 6: FULL.
con 0: 20.
prod 7: 26.
prod 9: FULL.
prod 7: FULL.
con 0: 21.
prod 11: 27.
prod 13: FULL.
prod 11: FULL.
con 0: 22.
prod 12: 28.
prod 10: FULL.
prod 12: FULL.
con 0: 23.
prod 1: 29.
prod 2: exited
prod 1: exited
con 0: 24.
prod 4: exited
prod 5: exited
con 0: 25.
prod 3: exited
prod 0: exited
con 0: 26.
prod 8: exited
prod 6: exited
con 0: 27.
prod 9: exited
prod 7: exited
con 0: 28.
prod 13: exited
prod 11: exited
con 0: 29.
con 0: exited
prod 10: exited
Any help would be appreciated.
Full code if anyone is interested (it is long but I guess most relevant parts are already in the methods I have posted):
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
/*
* Define constants for how big the shared queue should be and how
* much total work the produceers and consumers should perform
*/
#define QUEUESIZE 5
#define WORK_MAX 30
/*
* These constants specify how much CPU bound work the producer and
* consumer do when processing an item. They also define how long each
* blocks when producing an item. Work and blocking are implemented
* int he do_work() routine that uses the msleep() routine to block
* for at least the specified number of milliseconds.
*/
#define PRODUCER_CPU 25
#define PRODUCER_BLOCK 10
#define CONSUMER_CPU 25
#define CONSUMER_BLOCK 10
/*****************************************************
* Shared Queue Related Structures and Routines *
*****************************************************/
typedef struct {
int buf[QUEUESIZE]; /* Array for Queue contents, managed as circular queue */
int head; /* Index of the queue head */
int tail; /* Index of the queue tail, the next empty slot */
int full; /* Flag set when queue is full */
int empty; /* Flag set when queue is empty */
pthread_mutex_t *mutex; /* Mutex protecting this Queue's data */
pthread_cond_t *notFull; /* Used by producers to await room to produce*/
pthread_cond_t *notEmpty; /* Used by consumers to await something to consume*/
} queue;
/*
* Create the queue shared among all producers and consumers
*/
queue *queueInit (void)
{
queue *q;
/*
* Allocate the structure that holds all queue information
*/
q = (queue *)malloc (sizeof (queue));
if (q == NULL) return (NULL);
/*
* Initialize the state variables. See the definition of the Queue
* structure for the definition of each.
*/
q->empty = 1;
q->full = 0;
q->head = 0;
q->tail = 0;
/*
* Allocate and initialize the queue mutex
*/
q->mutex = (pthread_mutex_t *) malloc (sizeof (pthread_mutex_t));
pthread_mutex_init (q->mutex, NULL);
/*
* Allocate and initialize the notFull and notEmpty condition
* variables
*/
q->notFull = (pthread_cond_t *) malloc (sizeof (pthread_cond_t));
pthread_cond_init (q->notFull, NULL);
q->notEmpty = (pthread_cond_t *) malloc (sizeof (pthread_cond_t));
pthread_cond_init (q->notEmpty, NULL);
return (q);
}
/*
* Delete the shared queue, deallocating dynamically allocated memory
*/
void queueDelete (queue *q)
{
/*
* Destroy the mutex and deallocate its memory
*/
pthread_mutex_destroy (q->mutex);
free (q->mutex);
/*
* Destroy and deallocate the condition variables
*/
pthread_cond_destroy (q->notFull);
free (q->notFull);
pthread_cond_destroy (q->notEmpty);
free (q->notEmpty);
/*
* Deallocate the queue structure
*/
free (q);
}
void queueAdd (queue *q, int in)
{
/*
* Put the input item into the free slot
*/
q->buf[q->tail] = in;
q->tail++;
/*
* wrap the value of tail around to zero if we reached the end of
* the array. This implements the circularity of the queue inthe
* array.
*/
if (q->tail == QUEUESIZE)
q->tail = 0;
/*
* If the tail pointer is equal to the head, then the enxt empty
* slot in the queue is occupied and the queue is FULL
*/
if (q->tail == q->head)
q->full = 1;
/*
* Since we just added an element to the queue, it is certainly not
* empty.
*/
q->empty = 0;
return;
}
void queueRemove (queue *q, int *out)
{
/*
* Copy the element at head into the output variable and increment
* the head pointer to move to the next element.
*/
*out = q->buf[q->head];
q->head++;
/*
* Wrap the index around to zero if it reached the size of the
* array. This implements the circualrity of the queue int he array.
*/
if (q->head == QUEUESIZE)
q->head = 0;
/*
* If head catches up to tail as we delete an item, then the queue
* is empty.
*/
if (q->head == q->tail)
q->empty = 1;
/*
* since we took an item out, the queue is certainly not full
*/
q->full = 0;
return;
}
/******************************************************
* Producer and Consumer Structures and Routines *
******************************************************/
/*
* Argument struct used to pass consumers and producers thier
* arguments.
*
* q - arg provides a pointer to the shared queue.
*
* count - arg is a pointer to a counter for this thread to track how
* much work it did.
*
* tid - arg provides the ID number of the producer or consumer,
* whichis also its index into the array of thread structures.
*
*/
typedef struct {
queue *q;
int *count;
int tid;
} pcdata;
int memory_access_area[100000];
/*
* Sleep for a specified number of milliseconds. We use this to
* simulate I/O, since it will block the process. Different lengths fo
* sleep simulate interaction with different devices.
*/
void msleep(unsigned int milli_seconds)
{
struct timespec req = {0}; /* init struct contents to zero */
time_t seconds;
/*
* Convert number of milliseconds input to seconds and residual
* milliseconds to handle the cse where input is more than one
* thousand milliseconds.
*/
seconds = (milli_seconds/1000);
milli_seconds = milli_seconds - (seconds * 1000);
/*
* Fill in the time_spec's seconds and nanoseconds fields. Note we
* multiply millisconds by 10^6 to convert to nanoseconds.
*/
req.tv_sec = seconds;
req.tv_nsec = milli_seconds * 1000000L;
/*
* Sleep for the required period. The first parameter specifies how
* long. In theory this thread can be awakened before the period is
* over, perhaps by a signal. If so the timespec specified by the
* second argument is filled in with how much time int he original
* request is left. We use the same one. If this happens, we just
* call nanosleep again to sleep for what remains of the origianl
* request.
*/
while(nanosleep(&req, &req)==-1) {
printf("restless\n");
continue;
}
}
/*
* Simulate doing work.
*/
void do_work(int cpu_iterations, int blocking_time)
{
int i;
int j;
int local_var;
local_var = 0;
for (j = 0; j < cpu_iterations; j++ ) {
for (i = 0; i < 1000; i++ ) {
local_var = memory_access_area[i];
}
}
if ( blocking_time > 0 ) {
msleep(blocking_time);
}
}
void *producer (void *parg)
{
queue *fifo;
int item_produced;
pcdata *mydata;
int my_tid;
int *total_produced;
mydata = (pcdata *) parg;
fifo = mydata->q;
total_produced = mydata->count;
my_tid = mydata->tid;
/*
* Continue producing until the total produced reaches the
* configured maximum
*/
while (1) {
pthread_mutex_lock(fifo->mutex);
do_work(PRODUCER_CPU, PRODUCER_BLOCK);
/*
* If the queue is full, we have no place to put anything we
* produce, so wait until it is not full.
*/
while (fifo->full && *total_produced != WORK_MAX) {
printf ("prod %d: FULL.\n", my_tid);
pthread_cond_wait(fifo->notFull, fifo->mutex);
}
/*
* Check to see if the total produced by all producers has reached
* the configured maximum, if so, we can quit.
*/
if (*total_produced >= WORK_MAX) {
pthread_mutex_unlock(fifo->mutex);
break;
}
/*
* OK, so we produce an item. Increment the counter of total
* widgets produced, and add the new widget ID, its number, to the
* queue.
*/
item_produced = (*total_produced)++;
queueAdd (fifo, item_produced);
/*
* Announce the production outside the critical section
*/
printf("prod %d: %d.\n", my_tid, item_produced);
pthread_cond_signal(fifo->notEmpty);
pthread_mutex_unlock(fifo->mutex);
}
printf("prod %d: exited\n", my_tid);
return (NULL);
}
void *consumer (void *carg)
{
queue *fifo;
int item_consumed;
pcdata *mydata;
int my_tid;
int *total_consumed;
mydata = (pcdata *) carg;
fifo = mydata->q;
total_consumed = mydata->count;
my_tid = mydata->tid;
/*
* Continue producing until the total consumed by all consumers
* reaches the configured maximum
*/
while (1) {
pthread_mutex_lock(fifo->mutex);
/*
* If the queue is empty, there is nothing to do, so wait until it
* si not empty.
*/
while (fifo->empty && *total_consumed != WORK_MAX) {
printf ("con %d: EMPTY.\n", my_tid);
pthread_cond_wait(fifo->notEmpty, fifo->mutex);
}
/*
* If total consumption has reached the configured limit, we can
* stop
*/
if (*total_consumed >= WORK_MAX) {
pthread_mutex_unlock(fifo->mutex);
break;
}
/*
* Remove the next item from the queue. Increment the count of the
* total consumed. Note that item_consumed is a local copy so this
* thread can retain a memory of which item it consumed even if
* others are busy consuming them.
*/
queueRemove (fifo, &item_consumed);
(*total_consumed)++;
do_work(CONSUMER_CPU,CONSUMER_CPU);
printf ("con %d: %d.\n", my_tid, item_consumed);
pthread_cond_signal(fifo->notFull);
pthread_mutex_unlock(fifo->mutex);
}
printf("con %d: exited\n", my_tid);
return (NULL);
}
/***************************************************
* Main allocates structures, creates threads, *
* waits to tear down. *
***************************************************/
int main (int argc, char *argv[])
{
pthread_t *con;
int cons;
int *concount;
queue *fifo;
int i;
pthread_t *pro;
int *procount;
int pros;
pcdata *thread_args;
/*
* Check the number of arguments and determine the numebr of
* producers and consumers
*/
if (argc != 3) {
printf("Usage: producer_consumer number_of_producers number_of_consumers\n");
exit(0);
}
pros = atoi(argv[1]);
cons = atoi(argv[2]);
/*
* Create the shared queue
*/
fifo = queueInit ();
if (fifo == NULL) {
fprintf (stderr, "main: Queue Init failed.\n");
exit (1);
}
/*
* Create a counter tracking how many items were produced, shared
* among all producers, and one to track how many items were
* consumed, shared among all consumers.
*/
procount = (int *) malloc (sizeof (int));
if (procount == NULL) {
fprintf(stderr, "procount allocation failed\n");
exit(1);
}
concount = (int *) malloc (sizeof (int));
if (concount == NULL) {
fprintf(stderr, "concount allocation failed\n");
exit(1);
}
/*
* Create arrays of thread structures, one for each producer and
* consumer
*/
pro = (pthread_t *) malloc (sizeof (pthread_t) * pros);
if (pro == NULL) {
fprintf(stderr, "pros\n");
exit(1);
}
con = (pthread_t *) malloc (sizeof (pthread_t) * cons);
if (con == NULL) {
fprintf(stderr, "cons\n");
exit(1);
}
/*
* Create the specified number of producers
*/
for (i=0; i<pros; i++){
/*
* Allocate memory for each producer's arguments
*/
thread_args = (pcdata *)malloc (sizeof (pcdata));
if (thread_args == NULL) {
fprintf (stderr, "main: Thread_Args Init failed.\n");
exit (1);
}
/*
* Fill them in and then create the producer thread
*/
thread_args->q = fifo;
thread_args->count = procount;
thread_args->tid = i;
pthread_create (&pro[i], NULL, producer, thread_args);
}
/*
* Create the specified number of consumers
*/
for (i=0; i<cons; i++){
/*
* Allocate space for next consumer's args
*/
thread_args = (pcdata *)malloc (sizeof (pcdata));
if (thread_args == NULL) {
fprintf (stderr, "main: Thread_Args Init failed.\n");
exit (1);
}
/*
* Fill them in and create the thread
*/
thread_args->q = fifo;
thread_args->count = concount;
thread_args->tid = i;
pthread_create (&con[i], NULL, consumer, thread_args);
}
/*
* Wait for the create producer and consumer threads to finish
* before this original thread will exit. We wait for all the
* producers before waiting for the consumers, but that is an
* unimportant detail.
*/
for (i=0; i<pros; i++)
pthread_join (pro[i], NULL);
for (i=0; i<cons; i++)
pthread_join (con[i], NULL);
/*
* Delete the shared fifo, now that we know there are no users of
* it. Since we are about to exit we could skip this step, but we
* put it here for neatness' sake.
*/
queueDelete (fifo);
return 0;
}
I am trying to run the producer-consumer problem while
using pthread_cond_signal() instead of pthread_cond_broadcast(),
however, I attempted a lot of things and can't seem to do it (if I
choose n producers and one consumer then the consumer finishes but not
all producers finish, some are stuck on full queue so the problem
never finishes executing.
Well that sounds eminently plausible. If you have multiple threads blocked on a CV, then one signal will wake one of them. The rest will remain blocked.
I am generally inclined to go the other way. If you use your CV correctly, then it is safe to always broadcast to it instead of signaling it, but doing the opposite exposes more area for possible bugs, especially when more than two threads are involved.
For the shutdown scenario in particular, I would recommend just using a broadcast. You need to wake potentially multiple threads, and that's exactly what pthread_cond_broadcast() is for. You could have the main thread do that instead of either consumer or producer if you wish. But if you insist on using only pthread_cond_signal() then you must be sure to call that function enough times to wake all threads that may be blocked on the CV. Again, some or all of those calls could be performed by the main thread.
Update
Notwithstanding my above recommendation to broadcast, a relatively good way to obtain clean shutdown with signaling only would be for each producer to signal the notFull CV before terminating. There are a couple of places you could put that, but I would probably do this, myself:
if (*total_produced >= WORK_MAX) {
pthread_mutex_unlock(fifo->mutex); //if total work reached then exit
pthread_cond_signal(fifo->notFull); // other producers should wake up and exit, too
break;
}
Note that the mutex does not need to be currently locked by the thread that sends a broadcast or signal.
Note also that if you go this route then the consumer wants analogous treatment.
Finally, note that for this particular code, the transformation you are trying to perform is a bad idea from a functional perspective, especially for cases where the numbers of producers and consumers differ. No matter how many of each you start with, it will tend to reduce to having the same number of active producers and consumers, and probably, over a longer time frame, towards having at most one of each at any given time. These consequences arise from situations where multiple consumers or multiple producers are blocked on the CV at the same time.

Which synchronization primitive should I employ here?

while(1) {
char message_buffer[SIZE];
ssize_t message_length = mq_receive(mq_identifier, message_buffer, _mqueue_max_msg_size NULL);
if(message_len == -1) { /* error handling... */}
pthread_t pt1;
int ret = pthread_create(&pt1, NULL, handle_message, message_buffer);
if(ret) { /* error handling ... */}
}
void * handle_message (void * message) {
puts((char *) message);
return NULL;
}
The above example is not an MRE but it is extremely simple:
I've got a main thread with a loop that constantly consumes messages from a message queue. Once a new message is received, it is stored in the local message_buffer buffer. Then, a new thread is spawned to "take care" of said new message, and thus the message buffer's address is passed into handle_message, which the new thread subsequently executes.
The problem
Often, 2 threads will print the same message, even though I can verify with a 100% certainty that the messages in the queue were not the same.
I am not completely certain, but I think I understand why this is happening:
say that I push 2 different messages to the mqueue and only then I begin consuming them.
In the first iteration of the while loop, the message will get consumed from the queue and saved to message_buffer. A new thread will get spawned and the address of message_length passed to it. But that thread may not be fast enough to print the buffer's contents to the stream before the next message gets consumed (on the next iteration of the loop), and the contents of message_buffer subsequently overridden. Thus the first and second thread now print the same value.
My question is: what is the most efficient way to solve this? I'm pretty new to parallel programming and threading/pthreads and I'm pretty overwhelmed by the different synchronization primitives.
Mutex trouble
static pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
while(1) {
char message_buffer[SIZE];
pthread_mutex_lock(&m);
ssize_t message_length = mq_receive(mq_identifier, message_buffer, _mqueue_max_msg_size NULL);
pthred_mutex_unlock(&m);
if(message_len == -1) { /* error handling... */}
pthread_t pt1;
int ret = pthread_create(&pt1, NULL, handle_message, message_buffer);
if(ret) { /* error handling ... */}
}
void * handle_message (void * message) {
char own_buffer[SIZE];
pthread_mutex_lock(&m);
strncpy(own_buffer, (char *) message, SIZE);
pthread_mutex_unlock(&m);
puts(own_buffer);
return NULL;
}
I don't think my current mutex implementation is right as the threads are still receiving duplicate messages. The main thread can lock the mutex, consume a message into the buffer, unlock the mutex, spawn a thread, but that thread still may hang and the main one could just rewrite the buffer again (as the buffer mutex was never locked by the new thread), effectively making my current mutex implementation useless? How do I overcome this?
The problem is that you end the loop that contains message_buffer before guaranteeing that the thread has finished with that memory.
while (1) {
char message_buffer[SIZE];
ssize_t message_length = mq_receive(...);
if (message_len == -1) { /* error handling */ }
pthread_t pt1;
int ret = pthread_create(&pt1, NULL, handle_message, message_buffer);
if (ret) { /* error handling */ }
/****** Can't go beyond here until thread is done with message_buffer. ******/
}
void * handle_message (void * message) {
char own_buffer[SIZE];
strncpy(own_buffer, (char *) message, SIZE);
/******* Only now can the caller loop back. ******/
puts(own_buffer);
return NULL;
}
You could use a semaphore or similar.
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
static int copied = 0;
while (1) {
char message_buffer[SIZE];
ssize_t message_length = mq_receive(...);
if (message_len == -1) { /* error handling */ }
pthread_t pt1;
int ret = pthread_create(&pt1, NULL, handle_message, message_buffer);
if (ret) { /* error handling */ }
// Wait until threads is done with message_buffer.
pthread_mutex_lock(&mutex);
while (!copied) pthread_cond_wait(&cond, &mutex);
copied = 0;
pthread_mutex_unlock(&mutex);
}
void * handle_message (void * message) {
char own_buffer[SIZE];
strncpy(own_buffer, (char *) message, SIZE);
// Done with caller's buffer.
// Signal caller to continue.
pthread_mutex_lock(&mutex);
copied = 1;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
puts(own_buffer);
return NULL;
}
(The added chunks effectively perform semaphore operations. See the last snippet of this answer for a more generic implementation.)
But there's a simpler solution: Make the copy before creating the thread.
while (1) {
char message_buffer[SIZE];
ssize_t message_length = mq_receive(...);
if (message_len == -1) { /* error handling */ }
pthread_t pt1;
int ret = pthread_create(&pt1, NULL, handle_message, strdup(message_buffer));
if (ret) { /* error handling */ }
}
void * handle_message (void * message) {
char * own_buffer = message;
puts(own_buffer);
free(own_buffer);
return NULL;
}

Slow pthread consumer

I've implemented a solution the producer / consumer problem in C using pthreads and semaphores.
My main thread is the producer and I launch N consumer threads.
My code is:
typedef struct
{
int buf[BUFSIZE]; /* shared var */
int in; /* buf[in%BUFSIZE] is the first empty slot */
int out; /* buf[out%BUFSIZE] 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 */
pthread_mutex_t mutex; /* enforce mutual exclusion to shared data */
} CONSUMER_STRUCT;
CONSUMER_STRUCT shared;
This is the code for each of my consumer threads:
void *Consumer(void *arg)
{
int fd, workerID, i, hit=0;
workerID = *(int *)arg;
for (;;) {
sem_wait(&shared.full);
pthread_mutex_lock(&shared.mutex);
fd = shared.buf[shared.out];
printf("\n[C%d] Consumed. I got %d ...Valor do buffer: %d na posição %d\n\n\n", workerID, fd, shared.buf[shared.out], shared.out);
ftp(fd, hit);
shared.buf[shared.out] = 0;
shared.out = (shared.out+1)%BUFSIZE;
fflush(stdout);
printf("\n\n\n\nEstado do buffer:\n\n\n\n");
for (i = 0; i < BUFSIZE; i++) {
//printf("%d ", shared.buf[i]);
}
/* Release the buffer */
pthread_mutex_unlock(&shared.mutex);
/* Increment the number of full slots */
sem_post(&shared.empty);
hit++;
}
return NULL;
}
And this is the code for my producer thread:
item = socketfd;
sem_wait(&shared.empty);
pthread_mutex_lock(&shared.mutex);
shared.buf[shared.in] = item;
shared.in = (shared.in + 1) % BUFSIZE;
fflush(stdout);
pthread_mutex_unlock(&shared.mutex);
sem_post(&shared.full);
Everything is working properly but serving 22 files takes around 20 seconds whilst creating one thread per request takes around 2 seconds! This seems to be executing a thread at a time and I want to execute all of them "at the same time".
Am I doing something wrong in my implementation approach?
For those who might come here with a similar problem, here's the fix.
Thanks to #Martin James and #EOF.
void *Consumer(void *arg)
{
int fd, workerID, i, hit=0;
workerID = *(int *)arg;
for (;;) {
sem_wait(&shared.full);
pthread_mutex_lock(&shared.mutex);
fd = shared.buf[shared.out];
shared.buf[shared.out] = 0;
shared.out = (shared.out+1)%BUFSIZE;
pthread_mutex_unlock(&shared.mutex);
printf("\n[C%d] Consumed. I got %d ...Valor do buffer: %d na posição %d\n\n\n", workerID, fd, shared.buf[shared.out], shared.out);
ftp(fd, hit);
fflush(stdout);
printf("\n\n\n\nEstado do buffer:\n\n\n\n");
for (i = 0; i < BUFSIZE; i++) {
//printf("%d ", shared.buf[i]);
}
/* Release the buffer */
/* Increment the number of full slots */
sem_post(&shared.empty);
hit++;
}
return NULL;
}
The problem is that I was locking the mutex, executing a function and then unlocking the mutex. This is what was causing so much delay on the execution.

Producer Consumer program using semaphores and pthreads

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.

Resources