Essentially, my program creates 3 threads. The "server" and 2 "workers." The workers are meant to sum the 3 digit positive integers in a 1000 line file (500 numbers per thread). After each worker has summed its part, the server prints each workers total. The only problem is my semaphores are not seeming to work.
Here is a version of my program:
// simple c program to simulate POSIX thread and semaphore
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <semaphore.h>
// define semaphores
sem_t s1;
FILE *file;
int sum1 = 0, sum2 = 0, num1 = 0, num2 = 0;
// file name
char fileName[10] = "data1.dat";
// server routine
void* server_routine()
{
printf("Server sent signal to worker thread 1\n");
printf("Server sent signal to worker thread 2\n");
sem_wait(&s1);
printf("Server recieved completion signal from worker thread 1\n");
sem_wait(&s1);
printf("Server recieved completion signal from worker thread 2\n\n");
// print the final results
printf("The sum of the first 500 numbers in the file is: %d\n", sum1);
printf("The sum of the last 500 numbers in the file is: %d\n\n", sum2);
pthread_exit(NULL);
}
// thread 1 reoutine
void* t1_routine()
{
printf("Thread 1 recieved signal from server\n");
file = fopen(fileName, "r");
for(int i = 0; i < 500; i++)
{
fscanf(file, "%d", &num1);
sum1 += num1;
}
printf("sum in thread 1: %d\n", sum1);
printf("Thread 1 sends completion signal to server\n");
sem_post(&s1);
pthread_exit(NULL);
}
// thread 2 routine
void* t2_routine()
{
printf("Thread 2 recieved signal from server\n");
file = fopen(fileName, "r");
fseek(file, 500 * 5, SEEK_SET);
for(int i = 0; i < 500; i++)
{
fscanf(file, "%d", &num2);
sum2 += num2;
}
printf("sum in thread 2: %d\n", sum2);
printf("Thread 2 sends completion signal to server\n");
sem_post(&s1);
pthread_exit(NULL);
}
// main function
int main(int argc, char *argv[])
{
// define threads
pthread_t server, t1, t2;
// initialize the semaphore
sem_init(&s1, 0, 0);
if(pthread_create(&server, NULL, &server_routine, NULL) != 0)
{
return 1;
}
if(pthread_create(&t1, NULL, &t1_routine, NULL) != 0)
{
return 2;
}
if(pthread_create(&t2, NULL, &t2_routine, NULL) != 0)
{
return 3;
}
if(pthread_join(server, NULL) != 0)
{
return 4;
}
if(pthread_join(t1, NULL) != 0)
{
return 5;
}
if(pthread_join(t2, NULL) != 0)
{
return 6;
}
// destroy semaphores
sem_close(&s1);
// exit thread
pthread_exit(NULL);
// end
return 0;
}
I've tested with less threads more semaphores as well, with non luck. I've tried different initial semaphore values. The only time I can get the correct output is when I manually wait with sleep(5); but that defeats the purpose of this project.
A few issues ...
Each client thread does its own/private fopen but FILE *file; is global so they overwrite each others values.
We need to make this variable function scoped so each thread has its own private pointer.
There are no fclose calls.
pthread_exit should not be done by the main thread. It is only for threads created with pthread_create.
Otherwise ...
Whichever thread does the fopen last will set the final value.
So, there is a race condition and the effect is the same as if the main thread (prior to pthread_create calls) had done a single fopen, defeating the purpose of each thread doing its own fopen.
Worse yet, a given thread may do the first fopen, then start with fscanf and have its file value changed when the second thread replaces the file value, so weird stuff happens to each thread because they are doing fseek/fscanf on the same FILE * instance.
Having the aforementioned fclose calls would have made the issue more evident.
Here is the refactored code. It is annotated:
// simple c program to simulate POSIX thread and semaphore
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <semaphore.h>
// define semaphores
sem_t s1;
// NOTE/BUG: each thread opens a different stream, so this must be function
// scoped
#if 0
FILE *file;
#endif
int sum1 = 0,
sum2 = 0,
num1 = 0,
num2 = 0;
// file name
char fileName[10] = "data1.dat";
// server routine
void *
server_routine()
{
printf("Server sent signal to worker thread 1\n");
printf("Server sent signal to worker thread 2\n");
sem_wait(&s1);
printf("Server recieved completion signal from worker thread 1\n");
sem_wait(&s1);
printf("Server recieved completion signal from worker thread 2\n\n");
// print the final results
printf("The sum of the first 500 numbers in the file is: %d\n", sum1);
printf("The sum of the last 500 numbers in the file is: %d\n\n", sum2);
pthread_exit(NULL);
}
// thread 1 reoutine
void *
t1_routine()
{
// NOTE/FIX: this must be function scoped (i.e. private to this thread)
#if 1
FILE *file;
#endif
printf("Thread 1 recieved signal from server\n");
file = fopen(fileName, "r");
for (int i = 0; i < 500; i++) {
fscanf(file, "%d", &num1);
sum1 += num1;
}
printf("sum in thread 1: %d\n", sum1);
printf("Thread 1 sends completion signal to server\n");
sem_post(&s1);
#if 1
fclose(file);
#endif
pthread_exit(NULL);
}
// thread 2 routine
void *
t2_routine()
{
// NOTE/FIX: this must be function scoped (i.e. private to this thread)
#if 1
FILE *file;
#endif
printf("Thread 2 recieved signal from server\n");
file = fopen(fileName, "r");
fseek(file, 500 * 5, SEEK_SET);
for (int i = 0; i < 500; i++) {
fscanf(file, "%d", &num2);
sum2 += num2;
}
printf("sum in thread 2: %d\n", sum2);
printf("Thread 2 sends completion signal to server\n");
sem_post(&s1);
#if 1
fclose(file);
#endif
pthread_exit(NULL);
}
// main function
int
main(int argc, char *argv[])
{
// define threads
pthread_t server, t1, t2;
// initialize the semaphore
sem_init(&s1, 0, 0);
if (pthread_create(&server, NULL, &server_routine, NULL) != 0) {
return 1;
}
if (pthread_create(&t1, NULL, &t1_routine, NULL) != 0) {
return 2;
}
if (pthread_create(&t2, NULL, &t2_routine, NULL) != 0) {
return 3;
}
if (pthread_join(server, NULL) != 0) {
return 4;
}
if (pthread_join(t1, NULL) != 0) {
return 5;
}
if (pthread_join(t2, NULL) != 0) {
return 6;
}
// destroy semaphores
sem_close(&s1);
// exit thread
// NOTE/BUG: only a subthread should do this
#if 0
pthread_exit(NULL);
#endif
// end
return 0;
}
In the code above, I've used cpp conditionals to denote old vs. new code:
#if 0
// old code
#else
// new code
#endif
#if 1
// new code
#endif
Note: this can be cleaned up by running the file through unifdef -k
UPDATE:
Thank you for the response Craig. I have implemented your suggestions to my own code but nothing seemed to change. I then decided to copy paste your updated code into a c file to test it out and I got the same result. It is as follows (in a separate comment since the output is too long): –
Max
It's hard to compare results because we're using different datasets. I created a perl script to create some data.
Most important is that the sum reported by the given worker matches what the server sees for that worker task.
Then, if we know what each per thread section of the file should sum to, that is another matter.
The per line termination is critical (e.g.): CRLF vs LF (see below)
The actual order of worker sem_post and termination doesn't really matter. It can vary system to system or, even, invocation to invocation. What matters is that the server thread waits for N threads (i.e.) N sem_wait calls before printing any sums.
I've produced an updated version below.
Server does not "signal" a worker. The worker "signals" the server by doing sem_post and the server "receives" it by doing sem_wait
I've create a task/thread struct to hold the sums, thread IDs, etc.
I've generalized the code to allow N threads.
Added check of \n placement (i.e. line width). That is, under linux/POSIX a four digit number would be followed by LF (newline) and length would be 5. But, under windows, it would be CRLF (carriage return/newline) and length would be 6.
Added check of file size to ensure it is exactly the desired/expected length.
Some additional diagnostics.
Here is the updated code:
// simple c program to simulate POSIX thread and semaphore
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <semaphore.h>
#include <sys/stat.h>
// number of bytes per line
// 5: 4 digits + LF
// 6: 4 digits + CRLF
#ifndef LINEWID
#define LINEWID (4 + 1)
#endif
// number of items / task
#ifndef COUNT
#define COUNT 500
#endif
// define semaphores
sem_t s1;
#if 0
int sum1 = 0,
sum2 = 0,
num1 = 0,
num2 = 0;
#endif
// file name
#if 0
char fileName[10] = "data1.dat";
#else
const char *fileName = "data1.dat";
#endif
// task control
typedef struct {
pthread_t tid; // thread ID
int tno; // thread index/offset
int sum; // sum
} tsk_t;
#define TSKMAX 50
int tskmax; // actual number of tasks
tsk_t tsklist[TSKMAX]; // list of tasks
// loop through all task blocks
#define TSKFORALL \
tsk_t *tsk = &tsklist[0]; tsk < &tsklist[tskmax]; ++tsk
// server routine
void *
server_routine(void *vp)
{
// NOTE/BUG: server does _not_ signal worker
#if 0
printf("Server sent signal to worker thread 1\n");
printf("Server sent signal to worker thread 2\n");
#endif
for (TSKFORALL) {
printf("Server waiting ...\n");
sem_wait(&s1);
printf("Server complete ...\n");
}
// print the final results
for (TSKFORALL)
printf("The sum of task %d is %d\n",tsk->tno,tsk->sum);
return (void *) 0;
}
// thread 1 reoutine
void *
worker_routine(void *vp)
{
FILE *file;
tsk_t *tsk = vp;
printf("Thread %d running ...\n",tsk->tno);
file = fopen(fileName, "r");
fseek(file,tsk->tno * COUNT * LINEWID,SEEK_SET);
tsk->sum = 0;
int num1;
int first = -1;
int last = -1;
for (int i = 0; i < COUNT; i++) {
if (fscanf(file, "%d", &num1) != 1) {
printf("Thread %d fscan error\n",tsk->tno);
break;
}
if (i == 0)
first = num1;
if (i == (COUNT - 1))
last = num1;
tsk->sum += num1;
}
printf("sum in thread %d: %d (first %d, last %d)\n",
tsk->tno, tsk->sum, first, last);
sem_post(&s1);
#if 1
fclose(file);
#endif
return (void *) 0;
}
// main function
int
main(int argc, char **argv)
{
--argc;
++argv;
setlinebuf(stdout);
setlinebuf(stderr);
if (argc != 1)
tskmax = 2;
else
tskmax = atoi(*argv);
if (tskmax > TSKMAX)
tskmax = TSKMAX;
// define threads
pthread_t server;
printf("main: %d tasks\n",tskmax);
printf("main: %d count\n",COUNT);
FILE *file = fopen(fileName,"r");
if (file == NULL) {
printf("main: fopen failure\n");
exit(96);
}
// check alignment
char chr;
fseek(file,LINEWID - 1,0);
fread(&chr,1,1,file);
if (chr != '\n') {
printf("main: newline mismatch -- chr=%2.2X\n",chr);
exit(97);
}
// get the file size
struct stat st;
if (fstat(fileno(file),&st) < 0) {
printf("main: fstat fault\n");
exit(97);
}
// ensure the file has the correct size
off_t size = tskmax * LINEWID * COUNT;
if (st.st_size != size)
printf("main: wrong file size -- st_size=%llu size=%llu\n",
(unsigned long long) st.st_size,
(unsigned long long) size);
fclose(file);
// initialize the semaphore
sem_init(&s1, 0, 0);
// set the offsets
int tno = 0;
for (TSKFORALL, ++tno)
tsk->tno = tno;
if (pthread_create(&server, NULL, &server_routine, NULL) != 0)
return 98;
for (TSKFORALL) {
if (pthread_create(&tsk->tid,NULL,worker_routine,tsk) != 0)
return 1 + tsk->tno;
}
if (pthread_join(server, NULL) != 0) {
return 99;
}
for (TSKFORALL) {
if (pthread_join(tsk->tid, NULL) != 0) {
return 5;
}
}
// destroy semaphores
sem_close(&s1);
// end
return 0;
}
Here is the perl script output that I used to generate the data:
number of tasks 2
element count per task 500
line separater 0A
section 0 sum 124750
section 1 sum 125250
Here is the program output:
main: 2 tasks
Server waiting ...
Thread 0 running ...
Thread 1 running ...
sum in thread 1: 125250 (first 1, last 500)
sum in thread 0: 124750 (first 0, last 499)
Server complete ...
Server waiting ...
Server complete ...
The sum of task 0 is 124750
The sum of task 1 is 125250
Here is the perl script:
#!/usr/bin/perl
# gendata -- generate data
#
# arguments:
# 1 - number of tasks (DEFAULT: 2)
# 2 - number of items / task (DEFAULT: 500)
# 3 - line separater (DEFAULT: \n)
master(#ARGV);
exit(0);
# master -- master control
sub master
{
my(#argv) = #_;
$tskmax = shift(#argv);
$tskmax //= 2;
printf(STDERR "number of tasks %d\n",$tskmax);
$count = shift(#argv);
$count //= 500;
printf(STDERR "element count per task %d\n",$count);
$sep = shift(#argv);
$sep //= "\n";
printf(STDERR "line separater");
foreach $chr (split(//,$sep)) {
$hex = ord($chr);
printf(STDERR " %2.2X",$hex);
}
printf(STDERR "\n");
for ($itsk = 0; $itsk < $tskmax; ++$itsk) {
$val = $itsk;
$sum = 0;
for ($lno = 1; $lno <= $count; ++$lno, ++$val) {
printf("%4d%s",$val,$sep);
$sum += $val;
}
printf(STDERR "section %d sum %d\n",$itsk,$sum);
}
}
Related
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().
I'm trying to build a lift simulator in which a thread adds lift requests to the buffer and three threads representing lifts take the requests from the buffer and stimulate them simultaneously. I'm using pthread.h and semaphore.h to help me with the multithreaded nature of the program. As the requests need to be served in the order they are produced, I thought it would be best to use a queue as the buffer.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <time.h>
#include <semaphore.h>
#define THREAD_NUM 4
sem_t semEmpty;
sem_t semFull;
pthread_mutex_t mutexBuffer;
pthread_cond_t cond;
pthread_cond_t condII;
// file pointer assignment statements.
FILE *input;
FILE *output;
int count = 0;
int totalRequests = 0;
int totalMovement = 0;
// A structure to store requests.
struct Request
{
int reqNo;
int from;
int to;
};
// A structure to store lift info.
struct Lift
{
int liftNo;
int c_pos;
int movement;
int tMovement;
int tRequests;
};
// Declaring and defining queue functions.
struct queue
{
int size;
int f;
int r;
struct Request *arr;
};
int isEmpty(struct queue *q)
{
if (q->r == q->f)
{
return 1;
}
return 0;
};
int isFull(struct queue *q)
{
if (q->r == q->size - 1)
{
return 1;
}
return 0;
};
void enqueue(struct queue *q, struct Request req)
{
if (isFull(q))
{
printf("This Queue is full\n");
}
else
{
q->r++;
q->arr[q->r] = req;
printf("Enqued element.\n");
}
};
struct Request dequeue(struct queue *q)
{
struct Request r;
if (isEmpty(q))
{
printf("This Queue is empty.\n");
}
else
{
q->f++;
r = q->arr[q->f];
printf("Dequed element.\n");
}
return r;
};
// Declaring Buffer.
struct queue Buffer;
void *request(void *args)
{
struct Request r1;
while (!feof(input))
{
fscanf(input, "%d %d %d", &r1.reqNo, &r1.from, &r1.to);
fprintf(output, "--------------------------------------------\n");
fprintf(output, "Request No: %d\n", r1.reqNo);
fprintf(output, "From Floor %d to Floor %d\n", r1.from, r1.to);
fprintf(output, "--------------------------------------------\n");
while (isFull(&Buffer) == 1)
{
pthread_cond_wait(&condII, NULL);
}
sem_wait(&semEmpty);
pthread_mutex_lock(&mutexBuffer);
enqueue(&Buffer, r1);
count++;
pthread_mutex_unlock(&mutexBuffer);
sem_post(&semFull);
}
}
void *lift(void *args)
{
pthread_mutex_lock(&mutexBuffer);
struct Lift currentLift = *(struct Lift *)args;
struct Request r1;
pthread_mutex_unlock(&mutexBuffer);
while (count > 0)
{
while(isEmpty(&Buffer) == 0){
sem_wait(&semFull);
pthread_mutex_lock(&mutexBuffer);
r1 = dequeue(&Buffer);
count--;
pthread_cond_broadcast(&condII);
currentLift.movement = 0; // Resetting movement.
currentLift.tRequests++; // Incrementing total requests for lift.
totalRequests++; // Incrementing total requests.
fprintf(output, "Lift %d Operations\nPrevious Position: %d Floor\nRequest: number %d from Floor %d to Floor %d\n", currentLift.liftNo, currentLift.c_pos, r1.reqNo, r1.from, r1.to);
fprintf(output, "Detail Operations:\n\tGo from Floor %d to Floor %d\n", currentLift.c_pos, r1.from);
// Stimulating lift movement.
sleep(1);
currentLift.movement += abs(currentLift.c_pos - r1.from);
currentLift.c_pos = r1.from;
fprintf(output, "\tGo from Floor %d to Floor %d\n", r1.from, r1.to);
sleep(1);
currentLift.movement += abs(r1.to - r1.from);
currentLift.c_pos = r1.to;
currentLift.tMovement += currentLift.movement; // Adding to total movement of lift.
fprintf(output, "\t#movement for this request: %d\n", currentLift.movement);
fprintf(output, "\t#Requests: %d\n", currentLift.tRequests);
fprintf(output, "\tTotal #movement: %d\n", currentLift.tMovement);
fprintf(output, "Current Position: %d Floor\n", r1.to);
pthread_mutex_unlock(&mutexBuffer);
sem_post(&semEmpty);
pthread_mutex_lock(&mutexBuffer);
totalMovement += currentLift.tMovement;
pthread_mutex_unlock(&mutexBuffer);
}
}
}
int main(int argc, char *argv[])
{
// Creating thread array, mutexes, semaphores, and lifts.
pthread_t threads[THREAD_NUM];
pthread_mutex_init(&mutexBuffer, NULL);
sem_init(&semEmpty, 0, 5);
sem_init(&semFull, 0, 0);
pthread_cond_init(&cond, NULL);
pthread_cond_init(&condII, NULL);
Buffer.size = 5;
Buffer.f = -1;
Buffer.r = -1;
Buffer.arr = malloc(sizeof(struct Request) * Buffer.size);
struct Lift l1, l2, l3;
// Setting values for lifts.
l1.liftNo = 1;
l2.liftNo = 2;
l3.liftNo = 3;
l1.c_pos = 1;
l2.c_pos = 1;
l3.c_pos = 1;
l1.tMovement = 0;
l2.tMovement = 0;
l3.tMovement = 0;
l1.tRequests = 0;
l2.tRequests = 0;
l3.tRequests = 0;
l1.movement = 0;
l2.movement = 0;
l3.movement = 0;
// Dynamically allocating lifts for use in threads.
struct Lift *lift1 = malloc(sizeof(struct Lift));
*lift1 = l1;
struct Lift *lift2 = malloc(sizeof(struct Lift));
*lift2 = l3;
struct Lift *lift3 = malloc(sizeof(struct Lift));
*lift3 = l2;
// assigning files to file pointers.
input = fopen("sim_input.txt", "r");
if (input == NULL)
{
printf("No file read.\n\n");
return -1;
}
else
{
fseek(input, 0, SEEK_SET);
printf("File read successfully.\n\n");
}
output = fopen("sim_out.txt", "w");
if (output == NULL)
{
printf("No file read.\n\n");
return -1;
}
else
{
printf("File read successfully.\n\n");
}
// Creating producer thread and joining.
if (pthread_create(&threads[0], NULL, &request, NULL) != 0)
{
perror("Failed to create thread");
}
if (pthread_join(threads[0], NULL) != 0)
{
perror("Failed to join thread");
}
// Creating threads and passing lift structures.
if (pthread_create(&threads[1], NULL, &lift, lift1) != 0)
{
perror("Failed to create thread");
}
if (pthread_create(&threads[2], NULL, &lift, lift2) != 0)
{
perror("Failed to create thread");
}
if (pthread_create(&threads[3], NULL, &lift, lift3) != 0)
{
perror("Failed to create thread");
}
// Joining all lifts using loop structure.
for (int i = 1; i < THREAD_NUM; i++)
{
if (pthread_join(threads[i], NULL) != 0)
{
perror("Failed to join thread");
}
}
// Printing total requests and total movements.
fprintf(output, "Total number of requests: %d\n", totalRequests);
fprintf(output, "Total number of movements: %d\n", totalMovement);
// Freeing memory.
if (count < 1)
{
sem_destroy(&semEmpty);
sem_destroy(&semFull);
pthread_cond_destroy(&cond);
pthread_cond_destroy(&condII);
pthread_mutex_destroy(&mutexBuffer);
fclose(input);
fclose(output);
free(lift1);
free(lift2);
free(lift3);
}
return 0;
}
Here's the complete code for the program. It requires sim_input.txt with the following contents:
1 1 5
2 7 2
3 3 8
4 4 11
5 12 15
6 2 9
7 11 7
8 8 15
9 12 19
10 20 7
I assume the program crashes at while(isEmpty(&Buffer) == 0) where I try to fetch the size of the Buffer(Queue) but there might be some other reason too, can you please help?
I assume the program crashes at
You shouldn't assume. Run your program under debugger and observe where it crashes.
Your question lacks MCVE, because your program requires input file sim_input.txt which you didn't provide.
There are obvious bugs though -- you are accessing Buffer from multiple threads, some but not all of these accesses are guarded by a mutex.
For a mutex to be effective, all accesses to variables shared between multiple threads must be guarded, or you will have a data race, which is undefined behavior.
Update:
The actual crash is happening here:
pthread_cond_wait(&condII, NULL);
This call makes absolutely no sense: you must provide a mutex to pthread_cond_wait, and it must be the same mutex as the one used in pthread_cond_signal or similar. Here it should be mutexBuffer, and the code should look something like:
pthread_mutex_lock(&mutexBuffer);
while (isFull(&Buffer)) {
pthread_cond_wait(&condII, &mutexBuffer);
}
// sem_wait() <<- this is bogus/unnecessary
enqueue(&Buffer, r1);
count++;
pthread_mutex_unlock(&mutexBuffer);
There are many other bugs as well, e.g. the code in lift() assumes that it will run after request has been enqueued, and will exit immediately if that hasn't happened.
In my runs, all 3 lift threads exit before a single request is processed.
Hello I am having trouble with strtol
(specifically, that implicit conversion loses integer precision)
as well as with sem_open incompatible pointer types passing sem_t
I have tried to use named semaphores instead of unnamed semaphores, use sem_open instead of sem_init, and use sem_close and sem_unlink instead of sem_destroy, but still getting errors.
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <ctype.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include <semaphore.h>
// Global mutex and semaphores
pthread_mutex_t lock;
sem_t full;
sem_t empty;
// Global item to be incremented and enqueued
int global_value = 1;
// Queue
int* buffer;
int in_index;
int out_index;
// Producer and consumer arrays for comparing
int* producer_arr;
int* consumer_arr;
int p_idx = 0;
int c_idx = 0;
// Global args
int num_buffers;
int num_producers;
int num_consumers;
int items_produced;
int items_consumed;
int over_consume;
int over_consume_amount;
int p_time;
int c_time;
/*
* Function to remove item.
* Item removed is returned
*/
int dequeue_item() {
int item = buffer[out_index];
buffer[out_index] = 0;
out_index = (out_index + 1) % num_buffers;
return item;
}
/*
* Function to add item.
* Item added is returned.
* It is up to you to determine
* how to use the ruturn value.
* If you decide to not use it, then ignore
* the return value, do not change the
* return type to void.
*/
int enqueue_item(int item) {
buffer[in_index] = item;
in_index = (in_index + 1) % num_buffers;
return item;
}
void* producer(void* arg) {
int tid = *((int*) arg);
int item;
for (int i = 0; i < items_produced; i++) {
// Increment global item
item = global_value++;
// Lock semaphore and mutex
sem_wait(&empty);
pthread_mutex_lock(&lock);
// Produce item onto queue
enqueue_item(item);
producer_arr[p_idx++] = item;
printf( "%5d was produced by producer->\t%5d\n" , item, tid);
// Unlock mutex and semaphore
pthread_mutex_unlock(&lock);
sem_post(&full);
// Sleep
sleep(p_time);
}
pthread_exit(0);
}
void* consumer(void* arg) {
int tid = *((int*) arg);
int item;
if (!over_consume) {
// Either over or not required
for (int i = 0; i < items_consumed; i++) {
// Lock semaphore and mutex
sem_wait(&full);
pthread_mutex_lock(&lock);
// Consume item from queue
item = dequeue_item();
consumer_arr[c_idx++] = item;
printf( "%5d was consumed by consumer->\t%5d\n" , item, tid);
// Unlock mutex and semaphore
pthread_mutex_unlock(&lock);
sem_post(&empty);
// Sleep
sleep(c_time);
}
} else {
// Over consume if needed at the beginning of the program
over_consume = 0;
for (int i = 0; i < items_consumed + over_consume_amount; i++) {
// Lock semaphore and mutex
sem_wait(&full);
pthread_mutex_lock(&lock);
// Consume item from queue
item = dequeue_item();
consumer_arr[c_idx++] = item;
printf( "%5d was consumed by consumer->\t%5d\n" , item, tid);
// Unlock mutex and semaphore
pthread_mutex_unlock(&lock);
sem_post(&empty);
// Sleep
sleep(c_time);
}
}
pthread_exit(0);
}
/*
* $ ./pandc <N> <P> <C> <X> <Ptime> <Ctime>
* N = number of buffers to maintain
* P = number of producer threads
* C = number of consumer threads
* X = number of items each producer thread will produce
* Ptime = how long each producer thread will sleep after producing an item in seconds
* Ctime = how long each consumer thread will sleep after consuming an item in seconds
*/
int main(int argc, char** argv) {
// argv[0] is the program itself ("./pandc")
if (argc != 7) {
printf("Enter 6 arguments: \"$ ./pandc <N> <P> <C> <X> <Ptime> <Ctime>\"\n");
exit(EXIT_FAILURE);
}
// Print current time
time_t start_time = time(0);
printf( "Current time: %s\n" , ctime(&start_time));
// Read command-line args
num_buffers = strtol(argv[1], NULL, 10);
num_producers = strtol(argv[2], NULL, 10);
num_consumers = strtol(argv[3], NULL, 10);
items_produced = strtol(argv[4], NULL, 10);
items_consumed = (num_producers * items_produced) / num_consumers;
over_consume = (num_producers * items_produced) % num_consumers > 0 ? 1 : 0;
over_consume_amount = (num_producers * items_produced) - (num_consumers * items_consumed);
p_time = strtol(argv[5], NULL, 10);
c_time = strtol(argv[6], NULL, 10);
// Print producer-consumer problem information
printf("\t Number of Buffers : %5d\n", num_buffers);
printf("\t Number of Producers : %5d\n", num_producers);
printf("\t Number of Consumers : %5d\n", num_consumers);
printf("\tNumber of items Produced by each producer : %5d\n", items_produced);
printf("\tNumber of items Consumed by each consumer : %5d\n", items_consumed);
printf("\t Over consume on? : %5s\n", (over_consume) ? "yes" : "no");
printf("\t Over consume amount : %5d\n", over_consume_amount);
printf("\t Time each Producer Sleeps (seconds) : %5d\n", p_time);
printf("\t Time each Consumer Sleeps (seconds) : %5d\n", c_time);
printf("\n");
// Initialize mutex, semaphore, buffer, arrays
pthread_mutex_init(&lock, NULL); // mutex lock = 1;
sem_open( &full, 0, 0); // semaphore full = 0;
sem_open( &empty, 0, num_buffers); // semaphore empty = N;
buffer = malloc(sizeof(int*) * num_buffers); // buffer[N];
producer_arr = malloc(sizeof(int*) * num_producers * items_produced);
consumer_arr = malloc(sizeof(int*) * num_producers * items_produced);
// consumer_arr = malloc(sizeof(int*) * num_consumers * items_consumed);
in_index = 0;
out_index = 0;
// New threads
pthread_t producer_ids[num_producers];
pthread_t consumer_ids[num_consumers];
// Create producer and consumer threads
for (int i = 0; i < num_producers; i++) {
int id = i + 1;
pthread_create(&producer_ids[i], NULL, &producer, (void*) &id);
}
for (int i = 0; i < num_consumers; i++) {
int id = i + 1;
pthread_create(&consumer_ids[i], NULL, &consumer, (void*) &id);
}
// Join producer and consumer threads
for (int i = 0; i < num_producers; i++) {
pthread_join(producer_ids[i], NULL);
printf( "Producer thread joined:%5d\n" , i);
}
for (int i = 0; i < num_consumers; i++) {
pthread_join(consumer_ids[i], NULL);
printf( "Consumer thread joined:%5d\n" , i);
}
time_t end_time = time(0);
printf( "Current time: %s\n" , ctime(&end_time));
// Compare and match producer and consumer arrays
int match = 1; // Start out as true
fprintf(stderr, "Producer Array\t| Consumer Array\n");
for (int i = 0; i < num_producers * items_produced; i++) {
fprintf(stderr, "%d\t\t\t\t| %d\n", producer_arr[i], consumer_arr[i]);
if (producer_arr[i] != consumer_arr[i]) {
match = 0; // False when mismatch detected
}
}
fprintf(stderr, "\nConsume and Produce Arrays %s!\n", (match) ? "Match" : "DO NOT Match");
fprintf(stderr, "\nTotal Runtime: %d secs\n", (int) (end_time - start_time));
// Cleanup
pthread_mutex_destroy(&lock);
sem_unlink(&full);
sem_unlink(&empty);
free(buffer);
return 0;
}
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.
I have the bounded buffer, producer consumer problem to deal with and can only modify the prod and cons functions. This code runs without issues with only one consumer and producer threads. But with multiple of each it always gives me the same problem, sooner or later:
p5p1: p5p2a.c:207: bb_remove: Assertion `bbp->cnt > 0' failed.
What I don't get is how can this error occur when I check the bbp->cnt variable before calling the bbp_remove() function.
EDIT: The problem has been solved. I was not checking the variable within the lock, in either of the functions.
#include <sys/times.h>
#include <unistd.h>
#include <assert.h>
#include <stdlib.h>
#include <pthread.h>
#include <time.h>
#include <errno.h>
#include <stdio.h>
#define DEBUG 0
#define BUF_SIZE 100000
#define ITER 10000000
#define PROD_THRD 3
#define CONS_THRD 3
#define USAGE_STRING "Usage: %s\n"
extern int errno;
/* This is the bounded buffer type */
typedef struct {
int cnt, in, out;
pthread_mutex_t lock; /* Mutex to avoid race conditions */
int buf[BUF_SIZE]; /* The data passed is the id of the
* producer */
} bbuf_t;
typedef struct {
bbuf_t *bbp;
int id;
} parg_t;
/*
* yield()
* Because there is no yield system call in Linux, what we
* do is to put the thread to sleep for 1 ms. Actually, it
* will sleep for at least 1/HZ, which is 10 ms in Linux/386.
*/
#define YIELD_s 0
#define YIELD_ns 1000000
void yield() {
struct timespec st = {YIELD_s, YIELD_ns};
if( (nanosleep(&st, NULL)) == -1 && (errno == EINVAL)) {
perror("nanosleep");
pthread_exit(NULL);
}
}
/* Initialize bounded buffer */
int bbuf_init(bbuf_t *bbp) {
if(bbp == NULL)
return 0;
bbp->in = 0;
bbp->out = 0;
bbp->cnt = 0;
pthread_mutex_init(&(bbp->lock), NULL); /* I do not understand, but the
* compiler complains when I use
* PTHREAD_MUTEX_INITIALIZER */
return 1;
}
/* Print the bounded buffer members that matter */
void print_bbuf(bbuf_t *bbp) {
printf("bbp->in = %d bbp->out = %d bbp_cnt = %d \n",
bbp->in, bbp->out, bbp->cnt);
}
/* To validate the value of the members in, out and cnt of bbuf_t */
int val(int n, int min, int max) {
return( (min <= n) && (n <= max));
}
/* Ensure that the values of the members in, out and cnt are consistent */
int consist(int cnt, int in, int out) {
return ( in == ((out + cnt) % BUF_SIZE) );
}
/* This is the code of the checker thread. It is used to ensure that
* the bounded buffer has not been corrupted.
* Every 100 ms it checks the values of: in, out and cnt.
* This thread exits either if it detects the buffer has been corrupted
* or if it does not detect any activity in 50 consecutive iterations,
* i.e. approximately 5s. */
/* These constants are used with nanosleep() and
* put a process to sleep for 100 ms */
#define SLEEP_s 0
#define SLEEP_ns 100000000
#define MAX_IDLE 50
void *check(void *arg) {
bbuf_t *bbp = arg;
int cnt[2], in[2], out[2]; /* values read */
int idle;
struct timespec st = {SLEEP_s, SLEEP_ns}; /* 100 ms */
while(1) {
pthread_mutex_lock( &(bbp->lock) );
in[1] = bbp->in;
out[1] = bbp->out;
cnt[1] = bbp->cnt;
pthread_mutex_unlock( &(bbp->lock) );
if(in[1] == in[0] && out[1] == out[0] && cnt[1] == cnt[0] ) {
idle++;
if( idle >= MAX_IDLE ) {
printf("Checking thread exiting:");
print_bbuf(bbp);
printf("\t no activity detected for some time.\n");
pthread_exit(NULL);
}
} else {
idle = 0;
}
if( !val(in[1], 0, BUF_SIZE - 1) ) {
printf("Invalid value in = %d \n", in[1]);
pthread_exit(NULL);
} else if ( !val(out[1], 0, BUF_SIZE - 1) ) {
printf("Invalid value out = %d \n", out[1]);
pthread_exit(NULL);
} else if ( !val(cnt[1], 0, BUF_SIZE) ) {
printf("Invalid value cnt = %d \n", cnt[1]);
pthread_exit(NULL);
} else if ( !consist(cnt[1], in[1], out[1]) ) {
printf("Inconsistent buffer: cnt = %d in = %d out = %d \n",
cnt[1], in[1], out[1]);
pthread_exit(NULL);
}
if( (nanosleep(&st, NULL) == -1) && (errno == EINVAL)) {
perror("nanosleep");
pthread_exit(NULL);
}
in[0] = in[1];
out[0] = out[1];
cnt[0] = cnt[1];
}
}
/* The producer threads may use this code to
* enter one item into the buffer */
void bb_enter(bbuf_t *bbp, int me) {
assert( bbp->cnt < BUF_SIZE);
(bbp->buf)[bbp->in] = me;
(bbp->in)++;
(bbp->in) %= BUF_SIZE;
(bbp->cnt)++;
//printf("%d\n",bbp->cnt);
}
/* This is the code for the producer threads.
*
* To avoid busy waiting (or at least too much busy waiting) the producers
* should yield, using the yield() defined above, if the buffer is
* full. In that case, they should print a debugging message as well.
*
* Each producer should produce ITER (10 M) items: an integer with
* the id it receives in prod()'s argument.
*/
void *prod(void *arg) {
parg_t *parg = (parg_t *)arg;
bbuf_t *bbp = parg->bbp;
int me = parg->id;
/* Add variables and code, if necessary */
printf("I am a producer and have started\n");
int gcnt = 0;
while( gcnt <= ITER ){
if(bbp->cnt < BUF_SIZE){
pthread_mutex_lock(&(bbp->lock));
bb_enter(bbp,me);
gcnt++;
pthread_mutex_unlock(&(bbp->lock));}
else if( bbp->cnt == (BUF_SIZE-1)) {printf("I shall produce yield()\n"); yield();}
}
printf("I am a producer and have ended\n");
return;
}
/* The consumer threads may use this function to
* remove an item */
int bb_remove(bbuf_t *bbp) {
int val;
assert(bbp->cnt > 0);
val = (bbp->buf)[bbp->out];
(bbp->out)++;
(bbp->out) %= BUF_SIZE;
(bbp->cnt)--;
return val;
}
/* This is the code for the consumer threads.
* To avoid busy waiting (or at least too much busy waiting) consumers
* should yield, using the yield() defined above, if the buffer is
* empty. In that case, they should print a debugging message as well.
*
* Each consumer should consume ITER (10 M) items, and keep track of the
* producers of the items it consumes: use the cnt[] below.
*/
void *cons(void *arg) {
bbuf_t *bbp = (bbuf_t *)arg;
int cnt[PROD_THRD];
/* Add variables and code, if necessary:
* do not forget to initialize cnt */
printf("I am a consumer and have started\n");
int i;
for(i = 0; i < PROD_THRD; i++){
cnt[i] = 0;}
int temp;
int gcnt = 0;
while( gcnt <= ITER ){
if(bbp->cnt > 0){
pthread_mutex_lock(&(bbp->lock));
temp = bb_remove(bbp);
gcnt++;
cnt[temp]++;
pthread_mutex_unlock(&(bbp->lock));}
else if( bbp->cnt == 0) {printf("I shall consume yield()\n"); yield();}
}
printf("I am a consumer and have ended\n");
return;
}
int main(int argc, char *argv[]) {
int i;
pthread_t *tid, ctid;
parg_t *parg;
bbuf_t *bbp;
/* This is to measure the time it takes to run the program */
struct tms t;
clock_t start, end;
long ticks = sysconf(_SC_CLK_TCK);
start = times(&t);
if( argc != 1 ) {
printf(USAGE_STRING, argv[0]);
exit(1);
}
/* Array for pthread_join() */
if((tid = (pthread_t *) malloc((PROD_THRD + CONS_THRD) * sizeof(pthread_t)))
== NULL ) {
printf("Out of memory.\n");
exit(2);
}
/* Allocate Bounded Buffer */
if((bbp = (bbuf_t *) malloc(sizeof(bbuf_t))) == NULL ) {
printf("Out of memory. \n");
exit(2);
}
/* Initialize Bounded Buffer */
if( bbuf_init(bbp) == 0 ) {
printf("Failed to initialize bounded buffer\n");
exit(3);
}
/* Arguments for producer threads */
if((parg = (parg_t *) malloc( PROD_THRD * sizeof (parg_t))) == NULL ) {
printf("Out of memory.\n");
exit(2);
}
/* Create checker thread */
if( pthread_create(&ctid, NULL, check, bbp) )
perror("pthread_create");
printf("Created checker thread %u\n", (unsigned)ctid);
/* Create consumer threads */
for( i = 0; i < CONS_THRD; i++ ) {
/* We pass the same data structure, the bounded buffer,
* to each consumer: they need to synchronize to access it */
if( pthread_create(tid+i, NULL, cons, bbp) )
perror("pthread_create");
printf("Created consumer thread %u\n", (unsigned)tid[i]);
}
/* Create producer threads */
for( i = 0; i < PROD_THRD; i++ ) {
/* Because we want each consumer to keep track of the
* producer of the items it consumes, we assign an
* id to each producer thread */
parg[i].bbp = bbp;
parg[i].id = i;
if( pthread_create(tid+(i+CONS_THRD), NULL, prod, parg+i) )
perror("pthread_create");
printf("Created producer thread %u (%d)\n", (unsigned)tid[i+CONS_THRD], i);
}
/* Join consumer and producer threads */
for( i = 0; i < CONS_THRD + PROD_THRD; i ++ ) {
if( pthread_join(tid[i], NULL) == 0 ) {
printf("Joined thread %u.\n", (unsigned)tid[i]);
} else {
printf("Failed to join thread %u\n", (unsigned)tid[i]);
}
}
/* Join checker thread */
if( pthread_join(ctid, NULL) == 0 ) {
printf("Joined checker thread %u.\n", (unsigned)ctid);
} else {
printf("Failed to join checker thread %u\n", (unsigned)ctid);
}
/* How long did it take to run this ? */
end = times(&t);
printf("Wall time %2.4f s \n", (float)(end - start) / ticks);
return 0;
}
You should enter the mutex lock before you check bbp->cnt. Since you are checking it before you enter the mutex, another thread can reduce the value before you acquire the mutex and try to reduce the value yourself.