Producer Consumer program using semaphores and pthreads - c

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.

Related

How to find the number of rounds for the producer to deliver characters to buffer?

I have a producer consumer program that reads a file char by char and puts the content inside a buffer.
I need help with outputting the number of rounds the producer function made to deliver characters to the buffer. A round would mean one or more continuous writes to the buffer without being interrupted by wait (due to a full queue).
#include <pthread.h>
#include <semaphore.h>
#include <stdlib.h>
#include <stdio.h>
/*
This program provides a possible solution for producer-consumer problem using mutex and semaphore.
I have used 5 producers and 5 consumers to demonstrate the solution. You can always play with these values.
*/
#define MaxItems 5 // Maximum items a producer can produce or a consumer can consume
#define BufferSize 5 // Size of the buffer
sem_t empty;
sem_t full;
int in = 0;
int out = 0;
int buffer[BufferSize];
pthread_mutex_t mutex;
void *producer(void *pno)
{
int item;
for(int i = 0; i < MaxItems; i++) {
item = rand(); // Produce an random item
sem_wait(&empty);
pthread_mutex_lock(&mutex);
buffer[in] = item;
printf("Producer %d: Insert Item %d at %d\n", *((int *)pno),buffer[in],in);
in = (in+1)%BufferSize;
pthread_mutex_unlock(&mutex);
sem_post(&full);
}
}
void *consumer(void *cno)
{
for(int i = 0; i < MaxItems; i++) {
sem_wait(&full);
pthread_mutex_lock(&mutex);
int item = buffer[out];
printf("Consumer %d: Remove Item %d from %d\n",*((int *)cno),item, out);
out = (out+1)%BufferSize;
pthread_mutex_unlock(&mutex);
sem_post(&empty);
}
}
int main()
{
pthread_t pro[5],con[5];
pthread_mutex_init(&mutex, NULL);
sem_init(&empty,0,BufferSize);
sem_init(&full,0,0);
FILE *fp = fopen("file.txt", "r");
if (fp != NULL) {
if (fseek(fp, 0L, SEEK_END) == 0) {
/* Get the size of the file. */
p1.BUFFER_SIZE = ftell(fp);
if (p1.BUFFER_SIZE == -1) { /* Error */ }
/* Allocate our buffer to that size. */
p1.item = malloc(sizeof(char) * (p1.BUFFER_SIZE + 1));
/* Go back to the start of the file. */
if (fseek(fp, 0L, SEEK_SET) != 0) { /* Error */ }
/* Read the entire file into memory. */
size_t newLen = fread(p1.item, sizeof(char), p1.BUFFER_SIZE, fp);
if ( ferror( fp ) != 0 ) {
fputs("Error reading file", stderr);
} else {
p1.item[newLen++] = '\0'; /* Just to be safe. */
}
}
int a[5] = {1,2,3,4,5}; //Just used for numbering the producer and consumer
for(int i = 0; i < 5; i++) {
pthread_create(&pro[i], NULL, (void *)producer, (void *)&a[i]);
}
for(int i = 0; i < 5; i++) {
pthread_create(&con[i], NULL, (void *)consumer, (void *)&a[i]);
}
for(int i = 0; i < 5; i++) {
pthread_join(pro[i], NULL);
}
for(int i = 0; i < 5; i++) {
pthread_join(con[i], NULL);
}
pthread_mutex_destroy(&mutex);
sem_destroy(&empty);
sem_destroy(&full);
return 0;
}
In general, there is no way to determine whether the time it takes for a call to sem_wait() to return is extended on account of the semaphore's value initially being zero. The sem_wait() function does not communicate that information.
One thing you could do, however, is to start out with a sem_trywait(), which will fail instead of blocking if it cannot immediately decrement the target semaphore. You could increment a counter in that case, and then proceed to perform a regular sem_wait(). Example:
int full_count = 0;
void *producer(void *pno) {
int my_num = *(int *)pno;
for(int i = 0; i < MaxItems; i++) {
int item = rand(); // Produce an random item
int result = sem_trywait(&empty);
if (result == -1) {
if (errno == EAGAIN) {
full_count += 1;
result = sem_wait(&empty);
// handle any error ...
} else {
// handle other error ...
}
}
pthread_mutex_lock(&mutex);
buffer[in] = item;
printf("Producer %d: Insert Item %d at %d\n", my_num, buffer[in], in);
in = (in + 1) % BufferSize;
pthread_mutex_unlock(&mutex);
sem_post(&full);
}
}
Do note that there is no guarantee there that in the event that sem_wait() is called, it will actually block, because the semaphore can be incremented between the trywait and the wait. But it does tell you that if a wait had been performed instead of the trywait, then that wait would have blocked. In that case the producer is indeed delayed on account of a full buffer, even if it doesn't spend any of that delay blocked in sem_wait().

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

What's wrong with this first readers-writers solution implementation in C using mutex? [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I am trying to implement the first readers writers problem (reader's preference) in C. I am using mutex locks and unlocks to make sure that no writer can access the thread if a reader has a lock and any reader can access the thread if the first reader has a lock. Here is my code. I am unable to get my code till the end i.e., it is not reaching the thread join part. I guess I am getting a deadlock somewhere or maybe I am placing my mutex locks and unlocks in wrong place.
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <fcntl.h>
#include <sys/types.h>
#define FALSE 0
#define TRUE 1
#define SLOWNESS 30000
#define INVALID_ACCNO -99999
#define SIZE 100
#define WRITE_ITR 100000
#define READ_ITR 100000
#define MAX_BALANCE 1000000
typedef struct {
int accno;
float balance;
} account;
// sleep function
void rest()
{
usleep(100);
}
//Global shared data structure
account account_list[SIZE]; /* this is the data structure that the readers and writers will be accessing concurrently.*/
pthread_mutex_t rw_lock = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t r_lock = PTHREAD_MUTEX_INITIALIZER;
/* Writer thread - will update the account_list data structure.
Takes as argument the seed for the srand() function.
*/
void * writer_thr(void * arg)
{
printf("Writer thread ID %ld\n", pthread_self());
srand(*((unsigned int *) arg)); /* set random number seed for this writer */
int i, j;
int r_idx;
unsigned char found; /* For every update_acc[j], set to TRUE if found in account_list, else set to FALSE */
account update_acc[WRITE_ITR];
/* first create a random data set of account updates */
for (i = 0; i < WRITE_ITR;i++)
{
r_idx = rand() % SIZE; /* a random number in the range [0, SIZE) */
update_acc[i].accno = account_list[r_idx].accno;
update_acc[i].balance = 1000.0 + (float) (rand() % MAX_BALANCE);
}//end for
/* open a writer thread log file */
char thr_fname[64];
snprintf(thr_fname, 64, "writer_%ld_thr.log", pthread_self());
FILE* fd = fopen(thr_fname, "w");
if (!fd)
{
fprintf(stderr,"Failed to open writer log file %s\n", thr_fname);
pthread_exit(&errno);
}//end if
/* The writer thread will now try to update the shared account_list data structure.
For each entry 'j' in the update_acc[] array, it will find the corresponding
account number in the account_list array and update the balance of that account
number with the value stored in update_acc[j].
*/
int temp_accno;
for (j = 0; j < WRITE_ITR;j++) {
found = FALSE;
for (i = 0; i < SIZE;i++) {
if (account_list[i].accno == update_acc[j].accno) {
found = 1;
temp_accno = account_list[i].accno;
pthread_mutex_lock(&rw_lock);
account_list[i].accno = INVALID_ACCNO;
account_list[i].balance = update_acc[j].balance;
account_list[i].accno = temp_accno;
rest(); /* makes the write long duration - SO AS TO INTRODUCE LATENCY IN WRITE before going for next 'j' */
pthread_mutex_unlock(&rw_lock);
fprintf(fd, "Account number = %d [%d]: old balance = %6.2f, new balance = %6.2f\n",
account_list[i].accno, update_acc[j].accno, account_list[i].balance, update_acc[j].balance);
}//end if
if (!found)
fprintf(fd, "Failed to find account number %d!\n", update_acc[j].accno);
} // end test-set for-loop
}
fclose(fd);
return NULL;
}
/* Reader thread - will read the account_list data structure.
Takes as argument the seed for the srand() function.
*/
void * reader_thr(void *arg)
{
printf("Reader thread ID %ld\n", pthread_self());
srand(*((unsigned int *) arg)); /* set random number seed for this reader */
int i, j;
int r_idx;
unsigned char found; /* For every read_acc[j], set to TRUE if found in account_list, else set to FALSE */
account read_acc[READ_ITR];
/* first create a random data set of account updates */
for (i = 0; i < READ_ITR;i++)
{
r_idx = rand() % SIZE; /* a random number in the range [0, SIZE) */
read_acc[i].accno = account_list[r_idx].accno;
read_acc[i].balance = 0.0; /* we are going to read in the value */
}//end for
/* open a reader thread log file */
char thr_fname[64];
snprintf(thr_fname, 64, "reader_%ld_thr.log", pthread_self());
FILE *fd = fopen(thr_fname, "w");
if (!fd)
{
fprintf(stderr,"Failed to reader log file %s\n", thr_fname);
pthread_exit(&errno);
}//end if
/* The reader thread will now try to read the shared account_list data structure.
For each entry 'j' in the read_acc[] array, the reader will fetch the
corresponding balance from the account_list[] array and store in
read_acc[j].balance. */
for (j = 0; j < READ_ITR;j++) {
/* Now read the shared data structure */
found = FALSE;
for (i = 0; i < SIZE;i++) {
rest();
if (account_list[i].accno == read_acc[j].accno) {
found = TRUE;
fprintf(fd, "Account number = %d [%d], balance read = %6.2f\n",
account_list[i].accno, read_acc[j].accno, read_acc[j].balance);
pthread_mutex_lock(&r_lock);
if(j == 1)
{
pthread_mutex_lock(&rw_lock);
}
pthread_mutex_unlock(&r_lock);
read_acc[j].balance = account_list[i].balance;
pthread_mutex_lock(&r_lock);
if(j == READ_ITR - 1)
{
pthread_mutex_unlock(&rw_lock);
}
pthread_mutex_unlock(&r_lock);
}
if (!found)
fprintf(fd, "Failed to find account number %d!\n", read_acc[j].accno);
} // end test-set for-loop
}
fclose(fd);
return NULL;
}
/* populate the shared account_list data structure */
void create_testset() {
time_t t;
srand(time(&t));
int i;
for (i = 0;i < SIZE;i++) {
account_list[i].accno = 1000 + rand() % RAND_MAX;
account_list[i].balance = 100 + rand() % MAX_BALANCE;
}
return;
}
void usage(char *str) {
printf("Usage: %s -r <NUM_READERS> -w <NUM_WRITERS>\n", str);
return;
}
int main(int argc, char *argv[])
{
time_t t;
unsigned int seed;
int i;
int READ_THREADS; /* number of readers to create */
int WRITE_THREADS; /* number of writers to create */
if(argc <= 3)
{
usage("./rw");
exit(EXIT_FAILURE);
}
int opt;
while((opt = getopt(argc, argv, "r:w:")) != -1)
{
switch(opt)
{
case 'r':
READ_THREADS = atoi(optarg);
break;
case 'w':
WRITE_THREADS = atoi(optarg);
break;
default:
usage("./rw");
exit(EXIT_FAILURE);
}
}
pthread_t* reader_idx = (pthread_t *) malloc(sizeof(pthread_t) * READ_THREADS); /* holds thread IDs of readers */
pthread_t* writer_idx = (pthread_t *) malloc(sizeof(pthread_t) * WRITE_THREADS); /* holds thread IDs of writers */
/* create readers */
for (i = 0;i < READ_THREADS;i++)
{
seed = (unsigned int) time(&t);
if((pthread_create(&reader_idx[i], NULL, reader_thr, &seed)) != 0)
{
perror("pthread reader create");
exit(-1);
}
}
printf("Done creating reader threads!\n");
/* create writers */
for (i = 0;i < WRITE_THREADS;i++)
{
seed = (unsigned int) time(&t);
/* YOUR CODE GOES HERE */
if((pthread_create(&writer_idx[i], NULL, writer_thr, &seed)) != 0)
{
perror("pthread writer create");
exit(-1);
}
}
printf("Done creating writer threads!\n");
/* Join all reader and writer threads.
*/
for(i = 0; i < READ_THREADS; i++)
{
pthread_join(reader_idx[i], NULL);
}
for(i = 0; i < WRITE_THREADS; i++)
{
pthread_join(writer_idx[i], NULL);
}
printf("Reader threads joined.\n");
printf("Writer threads joined.\n");
pthread_mutex_destroy(&r_lock);
pthread_mutex_destroy(&rw_lock);
return 0;
}
Your code is a mess. There are several things that are wrong with it and each one of them breaks the RW locking mechanism that you are trying to implement.
Both your reader threads and writer threads need to deal with reader exclusion and writer exclusion. Your current code completely ignores the reader exclusion in writer thread.
Your writer thread is reading from the shared structure (if (account_list[i].accno == update_acc[j].accno)) without excluding other writers.
I do not think this is implementable with just mutexes as you seem to be trying to do. E.g., last reader thread out of the critical section needs to be able to let waiting writers go. You probably need at least conditional variables or semaphores to do this.
My suggestion is to use the POSIX pthread_rwlock_init and friends instead.
If you insist on doing this yourself then please read at least this Concurrent Control with "Readers" and "Writers" paper for inspiration on how this can be implemented.

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.

Printing Buffer contents

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

Resources