I am simulating F1 training. I am using fork() and shared memory between processes.
I am generating a random time for the cars that need to run for 5,400,000.
I fork() for each car.
the sons must simulate the generation of times.
the father takes care of the display, he also makes a system ("clear") with a sleep (1) to slow down the display.
but the program does not stop despite the 5,400,000 limit.
The program lasts more than 20min without stopping!
I would like it to generate the display a number of times and stop!
Thank you.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <stdbool.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <sys/wait.h>
#define NUMBER_OF_CARS 20
#define MIN 25000 // time generator
#define MAX 40000
int numeroVoiture[NUMBER_OF_CARS] = {44, 77, 11, 33, 3, 4, 5, 18, 14, 31, 16, 55, 10, 22, 7, 99, 9, 47, 6, 63};
typedef struct {
unsigned int id;
unsigned int s1;
unsigned int s2;
unsigned int s3;
unsigned int best_S1;
unsigned int best_S2;
unsigned int best_S3;
unsigned int tempsTotal;
unsigned int best_Circuit;
unsigned int lap;
unsigned int compteurStand;
unsigned int out;
} voiture;
voiture *shared_memory;
voiture copyTableau[NUMBER_OF_CARS];
int faireDesTours(int i);
unsigned int generateNumber(void);
void afficherTableau(void);
int compare (const void * a, const void * b);
void initVoiture(int i);
void sortLap(void);
int main(void)
{
/***************************************************
* Creating shared memory *
****************************************************/
int segment_id = shmget(IPC_PRIVATE, sizeof(voiture) * NUMBER_OF_CARS, 0666 | IPC_CREAT);
if (segment_id == -1) {
perror("shmget() failed !");
exit(EXIT_FAILURE);
}
shared_memory = shmat(segment_id, NULL, 0);
if (shared_memory == (void *) (-1)) {
perror("shmat() failed !");
exit(EXIT_FAILURE);
}
/**********************************************************
* Creation of child / cars *
**********************************************************/
for (int i = 0; i < NUMBER_OF_CARS; ++i)
{
/******** problem fork *********/
pid_t pid = fork();
if (pid == -1) {
perror("fork failed !");
exit(EXIT_FAILURE);
}
/******** child *********/
if(pid == 0) {
shared_memory[i].id = numeroVoiture[i]; //each car has a number
faireDesTours(i); //5400000
}
/******** father *********/
else {
wait(NULL);
system("clear");
// copy of array
memcpy( copyTableau, shared_memory, sizeof(copyTableau) );
//trier Tableau;
qsort( copyTableau, NUMBER_OF_CARS, sizeof(voiture), compare );
sortLap();
afficherTableau();
sleep(1);
}
}
/******** Detach memory segments *********/
shmdt(shared_memory);
/******** Delete shared memory *********/
shmctl(segment_id, IPC_RMID, NULL);
exit(EXIT_SUCCESS);
}
unsigned int tempsMaxCircuit = 5400000;
int faireDesTours( int i ) {
initVoiture(i);
unsigned int tour_complet;
while (shared_memory[i].tempsTotal <= tempsMaxCircuit) //no exceeded time
{
tour_complet = 0;
srand(time(NULL) + getpid());
/* **** S1 **** */
shared_memory[i].s1 = generateNumber();
if (shared_memory[i].s1 < shared_memory[i].best_S1) {
shared_memory[i].best_S1 = shared_memory[i].s1;
}
shared_memory[i].tempsTotal += shared_memory[i].s1;
tour_complet += shared_memory[i].s1;
/* *************************************** */
if (shared_memory[i].tempsTotal >= tempsMaxCircuit)
{
break;
}
/* **** S2 **** */
shared_memory[i].s2 = generateNumber();
if (shared_memory[i].s2 < shared_memory[i].best_S2) {
shared_memory[i].best_S2 = shared_memory[i].s2;
}
shared_memory[i].tempsTotal += shared_memory[i].s2;
tour_complet += shared_memory[i].s2;
/* *************************************** */
if (shared_memory[i].tempsTotal >= tempsMaxCircuit)
{
break;
}
/* **** S3 **** */
shared_memory[i].s3 = generateNumber();
if (shared_memory[i].s3 < shared_memory[i].best_S3) {
shared_memory[i].best_S3 = shared_memory[i].s3;
}
shared_memory[i].tempsTotal += shared_memory[i].s3;
tour_complet += shared_memory[i].s3;
/* *************************************** */
/* **** Best Time Circuit **** */
if (tour_complet < shared_memory[i].best_Circuit) {
shared_memory[i].best_Circuit = tour_complet;
shared_memory[i].best_Circuit = tour_complet;
}
/* *************************************** */
}
return 0;
}
unsigned int generateNumber(void)
{
return rand()%(MAX-MIN+1)+MIN;
}
void afficherTableau(void) { // Display
printf("\n\tBest times per complete lap\n");
printf(" ===================================================================================\n");
printf(" | ID | s1 | s2 | s3 | Tour | LAP |\n");
printf(" |==================================================================================\n");
for (int i = 0; i < NUMBER_OF_CARS; i++){
printf(" | %2d | %5d | %5d | %5d | %6d | %5d |\n", \
copyTableau[i].id, \
copyTableau[i].s1, copyTableau[i].s2, copyTableau[i].s3, \
copyTableau[i].best_Circuit,\
copyTableau[i].lap);
}
printf(" ===================================================================================\n\n");
}
// function sort
int compare(const void * a, const void * b)
{
voiture *voitureA = (voiture *)a;
voiture *voitureB = (voiture *)b;
return ( voitureA->best_Circuit - voitureB->best_Circuit );
}
// init each structure value of car
void initVoiture(int i) {
shared_memory[i].s1 = 0;
shared_memory[i].s2 = 0;
shared_memory[i].s3 = 0;
shared_memory[i].best_S1 = MAX;
shared_memory[i].best_S2 = MAX;
shared_memory[i].best_S3 = MAX;
shared_memory[i].best_Circuit = 3 * MAX;
shared_memory[i].tempsTotal = 0;
shared_memory[i].lap = 0;
shared_memory[i].compteurStand = 0;
shared_memory[i].out = false;
}
void sortLap(void) {
unsigned int difference;
for (int i = 1; i < NUMBER_OF_CARS; i++)
{
difference = ( copyTableau[i].best_Circuit - copyTableau[i - 1].best_Circuit );
copyTableau[i].lap = difference;
}
}
At the core, you have
for (int i = 0; i < NUMBER_OF_CARS; ++i)
{
/******** problem fork *********/
pid_t pid = fork();
if (pid == -1) {
perror("fork failed !");
exit(EXIT_FAILURE);
}
/******** child *********/
if(pid == 0) {
...
}
/******** father *********/
else {
wait(NULL);
...
}
}
You're missing a call to exit in the child. Without this, each child loops around and starts creating children of its own. It will eventually end, but not after creating a huge number of processes unintentionally.
By the way, it's kind of weird to create a child if the first thing the parent does is wait for the child to end.
I have a problem with a circular buffer I want to build where the reader only has read-only access. In order to achieve a smooth rollover, I have the writer to set an id in iterator+1 of the rollover data structure to 0 for which I check in with the reader. My algorith seems to work fine until the first roll over, then for some reason, the resder will read 0 from the id which the writer obviously has set.
i have some compileable example code to demonstrate the problem right here:
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#define NUM_ALM 5
#define ERROR -1
#define OK 0
//even IDs = alarm active
//odd IDs = alarm clear
enum alarmid {
BFD_ACT = 0x02,
BFD_CLR = 0x03,
LOS_ACT = 0x0C
};
typedef struct alarm_s {
long timestamp;
int alarmid;
int arg1;
int arg2;
}alarm_t;
int alarm_add(int id, int arg1, int arg2);
int next_alarm_read(alarm_t *res);
void *alarm_reader(void *arg);
static alarm_t *roller;
pthread_cond_t cv;
pthread_mutex_t mutex;
int main (void)
{
int i =0;
alarm_t dat;
pthread_t reader;
int ret;
roller = calloc(NUM_ALM,sizeof(alarm_t));
printf("allocated memory: %lukB\n",(sizeof(alarm_t)*NUM_ALM)/1024);
for (i = 1; i< NUM_ALM; i++){
alarm_add(LOS_ACT,i,0);
}
ret = pthread_create(&reader,NULL,alarm_reader,NULL);
if (ret){
printf("Error - pthread_create() return code: %d\n",ret);
return ERROR;
}
sleep(1);
alarm_add(BFD_ACT,8,0);
alarm_add(BFD_ACT,8,0);
alarm_add(BFD_ACT,8,0);
alarm_add(BFD_ACT,8,0);
alarm_add(BFD_CLR,8,0);
alarm_add(BFD_CLR,8,0);
alarm_add(BFD_CLR,8,0);
alarm_add(BFD_CLR,8,0);
alarm_add(BFD_ACT,8,0);
pthread_join(reader,NULL);
}
void *alarm_reader(void *arg)
{
static alarm_t dat={0};
int err = 0;
while(err <= 2)
{
if (next_alarm_read(&dat)== OK)
printf("read alarm id %d, arg1 %d,arg2 %d\n",dat.alarmid,dat.arg1,dat.arg2);
else{
printf("alarm_reader() next_alarm_read() returned ERROR, wait\n");
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cv, &mutex);
pthread_mutex_unlock(&mutex);
err++;
}
}
printf("alarm_reader exit!\n");
}
int alarm_add(int id, int arg1, int arg2)
{
static int i = 0;
alarm_t dat={0};
if (i<NUM_ALM){
dat.timestamp = time(NULL);
dat.alarmid = id;
dat.arg1 = arg1;
dat.arg2 = arg2;
if (&roller[i]){
memcpy(&roller[i],&dat,sizeof(alarm_t));
if (i+1<NUM_ALM)
roller[i+1].alarmid = 0;
else
roller[0].alarmid = 0;
pthread_cond_signal(&cv);
printf("added id %d, arg1 %d, arg2 %d #%d\n",roller[i].alarmid,roller[i].arg1,roller[i].arg2,i);
i++;
}
} else {
i = 0;
}
return 0;
}
int next_alarm_read(alarm_t *res)
{
static int i = 0;
static long prev_time = 0;
if (!res)
return ERROR;
if (i<NUM_ALM)
{
if (roller[i].alarmid!=0){
printf("next_alarm_read() reading #%d\n",i);
res->timestamp = roller[i].timestamp;
res->alarmid = roller[i].alarmid;
res->arg1 = roller[i].arg1;
res->arg2 = roller[i].arg2;
prev_time = roller[i].timestamp;
i++;
} else {
printf("next_alarm_read() #%d is %d,return ERROR\n",i,roller[i].alarmid);
return ERROR;
}
} else {
i = 0;
}
return OK;
}
Where the outpout looks like:
added id 12, arg1 1, arg2 0 #0
added id 12, arg1 2, arg2 0 #1
added id 12, arg1 3, arg2 0 #2
added id 12, arg1 4, arg2 0 #3
next_alarm_read() reading #0
read alarm id 12, arg1 1,arg2 0
next_alarm_read() reading #1
read alarm id 12, arg1 2,arg2 0
next_alarm_read() reading #2
read alarm id 12, arg1 3,arg2 0
next_alarm_read() reading #3
read alarm id 12, arg1 4,arg2 0
next_alarm_read() #4 is 0,return ERROR
alarm_reader() next_alarm_read() returned ERROR, wait
added id 2, arg1 8, arg2 0 #4
added id 2, arg1 8, arg2 0 #0
added id 2, arg1 8, arg2 0 #1
added id 3, arg1 8, arg2 0 #2
added id 3, arg1 8, arg2 0 #3
added id 3, arg1 8, arg2 0 #4
added id 2, arg1 8, arg2 0 #0
next_alarm_read() reading #4
read alarm id 3, arg1 8,arg2 0
read alarm id 3, arg1 8,arg2 0
next_alarm_read() reading #0
read alarm id 2, arg1 8,arg2 0
next_alarm_read() #1 is 0,return ERROR
alarm_reader() next_alarm_read() returned ERROR, wait
the bottom print for next_alarm_read() #1 is 0,return ERROR is wrong, the id should be 2. Why does this not work as intended I'nm wondering?
A few issues:
I'm not sure what if (&roller[i]) is supposed to do/mean.
The sleep in main isn't really needed and I suspect it's an attempt to ameliorate the other issues below.
alarm_add will drop an entry at the rollover point.
Also, it may overrun the reader and overwrite entries before the reader can see the entries (i.e. a race condition).
The reader and writer both need to see each others current queue indexes (i.e. they shouldn't be function scoped static) to prevent overrun/race
There should be two condition variables and not just one:
The writer detects the queue is full and needs to block until the reader has drained an entry
The reader detects an empty queue and needs to block until the writer adds a new entry
Here's a refactored version of your code that should address these issues. I've added some debug code. It may not be perfect [and may err on the side of conservatism], but it should get you a bit further [please pardon the gratuitous style cleanup]:
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#define NUM_ALM 5
#define ERROR -1
#define OK 0
double tvzero;
//even IDs = alarm active
//odd IDs = alarm clear
enum alarmid {
BFD_ACT = 0x02,
BFD_CLR = 0x03,
LOS_ACT = 0x0C
};
typedef struct alarm_s {
long timestamp;
int alarmid;
int arg1;
int arg2;
} alarm_t;
void alarm_add(int id, int arg1, int arg2);
int next_alarm_read(alarm_t * res);
void *alarm_reader(void *arg);
static alarm_t *roller;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
// reader variables
pthread_cond_t cv_notempty; // writer signals when queue not empty
volatile int need_notempty; // reader sets this before waiting
volatile int idxdeq; // reader's queue index
// writer variables
pthread_cond_t cv_notfull; // reader signals when queue not full
volatile int need_notfull; // writer sets this before waiting
volatile int idxenq; // writer's queue index
volatile int stopall;
double
tvgetf(void)
{
struct timespec ts;
double sec;
clock_gettime(CLOCK_REALTIME,&ts);
sec = ts.tv_nsec;
sec /= 1e9;
sec += ts.tv_sec;
sec -= tvzero;
return sec;
}
#define DBG(_reason) \
dbg(_reason)
void
dbg(const char *reason)
{
double tvnow;
tvnow = tvgetf();
printf("[%.9f] %s\n",tvnow,reason);
}
int
main(void)
{
int i = 0;
pthread_t reader;
int ret;
tvzero = tvgetf();
roller = calloc(NUM_ALM, sizeof(alarm_t));
printf("allocated memory: %lukB\n", (sizeof(alarm_t) * NUM_ALM) / 1024);
// NOTE: queuing more than a full queue here will cause writer to block
// forever because reader is not yet started
for (i = 1; i < NUM_ALM; i++) {
alarm_add(LOS_ACT, i, 0);
}
ret = pthread_create(&reader, NULL, alarm_reader, NULL);
if (ret) {
printf("Error - pthread_create() return code: %d\n", ret);
return ERROR;
}
#if 0
sleep(1);
#endif
alarm_add(BFD_ACT, 8, 0);
alarm_add(BFD_ACT, 8, 0);
alarm_add(BFD_ACT, 8, 0);
alarm_add(BFD_ACT, 8, 0);
alarm_add(BFD_CLR, 8, 0);
alarm_add(BFD_CLR, 8, 0);
alarm_add(BFD_CLR, 8, 0);
alarm_add(BFD_CLR, 8, 0);
alarm_add(BFD_ACT, 8, 0);
// tell reader that all items are queued and it should stop when it
// processes the final item
pthread_mutex_lock(&mutex);
stopall = 1;
if (need_notempty)
pthread_cond_signal(&cv_notempty);
pthread_mutex_unlock(&mutex);
pthread_join(reader, NULL);
return 0;
}
// RETURNS: queue index to process (-1=empty)
int
queue_notempty(void)
{
int curidx;
do {
curidx = idxdeq;
// queue is empty
if (curidx == idxenq) {
curidx = -1;
break;
}
// advance dequeue index
idxdeq += 1;
idxdeq %= NUM_ALM;
} while (0);
return curidx;
}
// RETURNS: queue index to use (-1=full)
int
queue_notfull(void)
{
int nxtidx;
int curidx;
do {
// get current index
curidx = idxenq;
// advance to next slot (wrapping if necessary)
nxtidx = curidx;
nxtidx += 1;
nxtidx %= NUM_ALM;
// queue is full
if (nxtidx == idxdeq) {
curidx = -1;
break;
}
// store back adjusted index
idxenq = nxtidx;
} while (0);
return curidx;
}
void *
alarm_reader(void *arg)
{
alarm_t dat = { 0 };
while (1) {
if (next_alarm_read(&dat))
break;
printf("read alarm id %d, arg1 %d,arg2 %d\n",
dat.alarmid, dat.arg1, dat.arg2);
}
printf("alarm_reader exit!\n");
return (void *) 0;
}
void
alarm_add(int id, int arg1, int arg2)
{
int curidx;
alarm_t *rol;
pthread_mutex_lock(&mutex);
while (1) {
curidx = queue_notfull();
// have an open slot -- store item into it
if (curidx >= 0) {
rol = &roller[curidx];
rol->timestamp = time(NULL);
rol->alarmid = id;
rol->arg1 = arg1;
rol->arg2 = arg2;
printf("added id %d, arg1 %d, arg2 %d #%d\n",
rol->alarmid, rol->arg1, rol->arg2, curidx);
// unblock reader if necessary
if (need_notempty) {
DBG("writer signal notempty");
need_notempty = 0;
pthread_cond_signal(&cv_notempty);
}
break;
}
// queue is full -- wait for reader to free up some space
DBG("writer need_notfull");
need_notfull = 1;
pthread_cond_wait(&cv_notfull,&mutex);
DBG("writer wakeup");
}
pthread_mutex_unlock(&mutex);
}
// RETURNS: 1=stop, 0=normal
int
next_alarm_read(alarm_t *res)
{
//static long prev_time = 0;
int curidx;
alarm_t *rol;
int stopflg = 0;
pthread_mutex_lock(&mutex);
while (1) {
curidx = queue_notempty();
// queue has an entry -- process it
if (curidx >= 0) {
rol = &roller[curidx];
printf("next_alarm_read() reading #%d\n", curidx);
*res = *rol;
//prev_time = rol->timestamp;
// if writer is waiting/blocking, wake it up because we just
// freed up a queue slot
if (need_notfull) {
DBG("reader signal notfull");
need_notfull = 0;
pthread_cond_signal(&cv_notfull);
}
break;
}
// stop when master has enqueued everything
stopflg = stopall;
if (stopflg)
break;
// queue is empty -- we must wait for writer to add something
DBG("reader need_notempty");
need_notempty = 1;
pthread_cond_wait(&cv_notempty,&mutex);
}
pthread_mutex_unlock(&mutex);
return stopflg;
}
UPDATE:
I don't understand the do while(0); "loops" in the two Q functions, can you elaboratea little, please?
The do while(0) is a technique that I use a lot to replace if/else ladder logic. I didn't invent it [it's discussed in some style guides, notably, "Code Complete"], but a lot of people that I've shown it to seem to like it. See my answer: About the exclusiveness of the cases of an if block for a better explanation.
And I guessx what my initrial post didn't include is: the master should be able to enqueue things on an ongoing basis, there's no stopall and the reader should start reading as soon as something is available.
Actually, I did realize that and the code I posted allows for that.
You may want to issue the pthread_create before enqueuing any messages to prevent the potential deadlock I mentioned in the code comments.
A fix for this would be to remove stopall, the pthread_cond-signal() (from main) is already done inside alarm_add() so this should work fine.
The stopall is not to synchronize against overflow/underflow. It is merely if the writer (main thread) wants the receiver/thread to finish up and stop cleanly. It's more like a way to send an "EOF" condition to the reader.
If your application is to run "forever", you can remove the stopall.
Or, a cleaner way to signal "EOF": the main thread could enqueue a special "stop" message (e.g. a message with a timestamp of -1) to tell the receiver that no more messages will be sent ever and we wish to terminate the program.
What I suggest is that you add a "diagnostic mode" to validate your program:
Have the main do the pthread_create and then do:
for (i = 1; i < 10000000; i++) {
alarm_add(LOS_ACT, i, 0);
}
The reader should examine the arg1 values that come in. They should increment as above. If they don't, there is a logic error or race condition.
Here is an updated version of my code with a -D option for a diagnostic/unit test mode. Note that all printing is disabled to allow it to run at extreme speed:
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#define NUM_ALM 5
#define ERROR -1
#define OK 0
int opt_diag;
double tvzero;
//even IDs = alarm active
//odd IDs = alarm clear
enum alarmid {
BFD_ACT = 0x02,
BFD_CLR = 0x03,
LOS_ACT = 0x0C
};
typedef struct alarm_s {
long timestamp;
int alarmid;
int arg1;
int arg2;
} alarm_t;
void alarm_add(int id, int arg1, int arg2);
int next_alarm_read(alarm_t * res);
void *alarm_reader(void *arg);
static alarm_t *roller;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
// reader variables
pthread_cond_t cv_notempty; // writer signals when queue not empty
volatile int need_notempty; // reader sets this before waiting
volatile int idxdeq; // reader's queue index
// writer variables
pthread_cond_t cv_notfull; // reader signals when queue not full
volatile int need_notfull; // writer sets this before waiting
volatile int idxenq; // writer's queue index
volatile int stopall;
double
tvgetf(void)
{
struct timespec ts;
double sec;
clock_gettime(CLOCK_REALTIME,&ts);
sec = ts.tv_nsec;
sec /= 1e9;
sec += ts.tv_sec;
sec -= tvzero;
return sec;
}
#define prtf(_fmt...) \
do { \
if (opt_diag) \
break; \
printf(_fmt); \
} while (0)
#define DBG(_reason) \
dbg(_reason)
void
dbg(const char *reason)
{
double tvnow;
if (! opt_diag) {
tvnow = tvgetf();
printf("[%.9f] %s\n",tvnow,reason);
}
}
int
main(int argc,char **argv)
{
int i = 0;
char *cp;
pthread_t reader;
int ret;
--argc;
++argv;
for (; argc > 0; --argc, ++argv) {
cp = *argv;
if (*cp != '-')
break;
switch (cp[1]) {
case 'D':
cp += 2;
opt_diag = (*cp != 0) ? atoi(cp) : 10000000;
break;
}
}
tvzero = tvgetf();
roller = calloc(NUM_ALM, sizeof(alarm_t));
printf("allocated memory: %lukB\n", (sizeof(alarm_t) * NUM_ALM) / 1024);
// NOTE: queuing more than a full queue here will cause writer to block
// forever because reader is not yet started
if (! opt_diag) {
for (i = 1; i < NUM_ALM; i++) {
alarm_add(LOS_ACT, i, 0);
}
}
ret = pthread_create(&reader, NULL, alarm_reader, NULL);
if (ret) {
printf("Error - pthread_create() return code: %d\n", ret);
return ERROR;
}
#if 0
sleep(1);
#endif
if (opt_diag) {
for (i = 1; i < opt_diag; i++) {
alarm_add(LOS_ACT, i, 0);
}
}
else {
alarm_add(BFD_ACT, 8, 0);
alarm_add(BFD_ACT, 8, 0);
alarm_add(BFD_ACT, 8, 0);
alarm_add(BFD_ACT, 8, 0);
alarm_add(BFD_CLR, 8, 0);
alarm_add(BFD_CLR, 8, 0);
alarm_add(BFD_CLR, 8, 0);
alarm_add(BFD_CLR, 8, 0);
alarm_add(BFD_ACT, 8, 0);
}
// tell reader that all items are queued and it should stop when it
// processes the final item
pthread_mutex_lock(&mutex);
stopall = 1;
if (need_notempty)
pthread_cond_signal(&cv_notempty);
pthread_mutex_unlock(&mutex);
pthread_join(reader, NULL);
return 0;
}
// RETURNS: queue index to process (-1=empty)
int
queue_notempty(void)
{
int curidx;
do {
curidx = idxdeq;
// queue is empty
if (curidx == idxenq) {
curidx = -1;
break;
}
// advance dequeue index
idxdeq += 1;
idxdeq %= NUM_ALM;
} while (0);
return curidx;
}
// RETURNS: queue index to use (-1=full)
int
queue_notfull(void)
{
int nxtidx;
int curidx;
do {
// get current index
curidx = idxenq;
// advance to next slot (wrapping if necessary)
nxtidx = curidx;
nxtidx += 1;
nxtidx %= NUM_ALM;
// queue is full
if (nxtidx == idxdeq) {
curidx = -1;
break;
}
// store back adjusted index
idxenq = nxtidx;
} while (0);
return curidx;
}
void *
alarm_reader(void *arg)
{
alarm_t dat = { 0 };
static int expval = 1;
while (1) {
if (next_alarm_read(&dat))
break;
if (opt_diag) {
if (dat.arg1 != expval) {
printf("expected: %d got %d\n",expval,dat.arg1);
exit(1);
}
++expval;
}
prtf("read alarm id %d, arg1 %d,arg2 %d\n",
dat.alarmid, dat.arg1, dat.arg2);
}
printf("alarm_reader exit!\n");
return (void *) 0;
}
void
alarm_add(int id, int arg1, int arg2)
{
int curidx;
alarm_t *rol;
pthread_mutex_lock(&mutex);
while (1) {
curidx = queue_notfull();
// have an open slot -- store item into it
if (curidx >= 0) {
rol = &roller[curidx];
rol->timestamp = time(NULL);
rol->alarmid = id;
rol->arg1 = arg1;
rol->arg2 = arg2;
prtf("added id %d, arg1 %d, arg2 %d #%d\n",
rol->alarmid, rol->arg1, rol->arg2, curidx);
// unblock reader if necessary
if (need_notempty) {
DBG("writer signal notempty");
need_notempty = 0;
pthread_cond_signal(&cv_notempty);
}
break;
}
// queue is full -- wait for reader to free up some space
DBG("writer need_notfull");
need_notfull = 1;
pthread_cond_wait(&cv_notfull,&mutex);
DBG("writer wakeup");
}
pthread_mutex_unlock(&mutex);
}
// RETURNS: 1=stop, 0=normal
int
next_alarm_read(alarm_t *res)
{
//static long prev_time = 0;
int curidx;
alarm_t *rol;
int stopflg = 0;
pthread_mutex_lock(&mutex);
while (1) {
curidx = queue_notempty();
// queue has an entry -- process it
if (curidx >= 0) {
rol = &roller[curidx];
prtf("next_alarm_read() reading #%d\n", curidx);
*res = *rol;
//prev_time = rol->timestamp;
// if writer is waiting/blocking, wake it up because we just
// freed up a queue slot
if (need_notfull) {
DBG("reader signal notfull");
need_notfull = 0;
pthread_cond_signal(&cv_notfull);
}
break;
}
// stop when master has enqueued everything
stopflg = stopall;
if (stopflg)
break;
// queue is empty -- we must wait for writer to add something
DBG("reader need_notempty");
need_notempty = 1;
pthread_cond_wait(&cv_notempty,&mutex);
}
pthread_mutex_unlock(&mutex);
return stopflg;
}
Here is a version of your original code with the diagnostic option added:
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
int opt_diag;
#define NUM_ALM 5
#define ERROR -1
#define OK 0
//even IDs = alarm active
//odd IDs = alarm clear
enum alarmid {
BFD_ACT = 0x02,
BFD_CLR = 0x03,
LOS_ACT = 0x0C
};
typedef struct alarm_s {
long timestamp;
int alarmid;
int arg1;
int arg2;
} alarm_t;
int alarm_add(int id, int arg1, int arg2);
int next_alarm_read(alarm_t * res);
void *alarm_reader(void *arg);
static alarm_t *roller;
pthread_cond_t cv;
pthread_mutex_t mutex;
#define prtf(_fmt...) \
do { \
if (opt_diag) \
break; \
printf(_fmt); \
} while (0)
int
main(int argc,char **argv)
{
int i = 0;
char *cp;
pthread_t reader;
int ret;
--argc;
++argv;
for (; argc > 0; --argc, ++argv) {
cp = *argv;
if (*cp != '-')
break;
switch (cp[1]) {
case 'D':
cp += 2;
opt_diag = (*cp != 0) ? atoi(cp) : 10000000;
break;
}
}
roller = calloc(NUM_ALM, sizeof(alarm_t));
printf("allocated memory: %lukB\n", (sizeof(alarm_t) * NUM_ALM) / 1024);
if (! opt_diag) {
for (i = 1; i < NUM_ALM; i++) {
alarm_add(LOS_ACT, i, 0);
}
}
ret = pthread_create(&reader, NULL, alarm_reader, NULL);
if (ret) {
printf("Error - pthread_create() return code: %d\n", ret);
return ERROR;
}
if (opt_diag) {
for (i = 1; i < opt_diag; i++) {
alarm_add(LOS_ACT, i, 0);
}
}
else {
sleep(1);
alarm_add(BFD_ACT, 8, 0);
alarm_add(BFD_ACT, 8, 0);
alarm_add(BFD_ACT, 8, 0);
alarm_add(BFD_ACT, 8, 0);
alarm_add(BFD_CLR, 8, 0);
alarm_add(BFD_CLR, 8, 0);
alarm_add(BFD_CLR, 8, 0);
alarm_add(BFD_CLR, 8, 0);
alarm_add(BFD_ACT, 8, 0);
}
pthread_join(reader, NULL);
}
void *
alarm_reader(void *arg)
{
static alarm_t dat = { 0 };
int expval = 1;
int err = 0;
while (err <= 2) {
if (next_alarm_read(&dat) == OK) {
prtf("read alarm id %d, arg1 %d,arg2 %d\n", dat.alarmid, dat.arg1, dat.arg2);
if (opt_diag) {
if (dat.arg1 != expval) {
printf("expected: %d got %d\n",expval,dat.arg1);
exit(1);
}
++expval;
}
}
else {
prtf("alarm_reader() next_alarm_read() returned ERROR, wait\n");
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cv, &mutex);
pthread_mutex_unlock(&mutex);
err++;
}
}
printf("alarm_reader exit!\n");
return (void *) 0;
}
int
alarm_add(int id, int arg1, int arg2)
{
static int i = 0;
alarm_t dat = { 0 };
if (i < NUM_ALM) {
dat.timestamp = time(NULL);
dat.alarmid = id;
dat.arg1 = arg1;
dat.arg2 = arg2;
if (&roller[i]) {
memcpy(&roller[i], &dat, sizeof(alarm_t));
if (i + 1 < NUM_ALM)
roller[i + 1].alarmid = 0;
else
roller[0].alarmid = 0;
pthread_cond_signal(&cv);
prtf("added id %d, arg1 %d, arg2 %d #%d\n", roller[i].alarmid, roller[i].arg1, roller[i].arg2, i);
i++;
}
}
else {
i = 0;
}
return 0;
}
int
next_alarm_read(alarm_t * res)
{
static int i = 0;
//static long prev_time = 0;
if (!res)
return ERROR;
if (i < NUM_ALM) {
if (roller[i].alarmid != 0) {
prtf("next_alarm_read() reading #%d\n", i);
res->timestamp = roller[i].timestamp;
res->alarmid = roller[i].alarmid;
res->arg1 = roller[i].arg1;
res->arg2 = roller[i].arg2;
//prev_time = roller[i].timestamp;
i++;
}
else {
prtf("next_alarm_read() #%d is %d,return ERROR\n", i, roller[i].alarmid);
return ERROR;
}
}
else {
i = 0;
}
return OK;
}
I'm working on the producer and consumer problem. The producer is generating a random variable and placing it in the buffer. After this is done I want to print out the contents of the buffer. I also want to print the contents of the buffer after the consumer consumes a variable from the buffer. So just as an example,
Producer Thread 34567834 adds 43 to the buffer, and the current buffer contains 7, 29, 43
I am not sure of a way to print the contents of a buffer in one printf() statement. Thanks for any help.
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
//Submit with screen shot of compiling and running code.
#define SIZE 20
#define NUMB_THREADS 10
#define PRODUCER_LOOPS 10
#define CONSUMER_LOOPS 2
#define TRUE 1
#define FALSE 0
typedef int buffer_t;
buffer_t buffer[SIZE];
int buffer_index;
pthread_mutex_t buffer_mutex;
/* initially buffer will be empty. full_sem
will be initialized to buffer SIZE, which means
SIZE number of producer threads can write to it.
And empty_sem will be initialized to 0, so no
consumer can read from buffer until a producer
thread posts to empty_sem */
sem_t full_sem; /* when 0, buffer is full */
sem_t empty_sem; /* when 0, buffer is empty. Kind of
like an index for the buffer */
/* sem_post algorithm:
mutex_lock sem_t->mutex
sem_t->value++
mutex_unlock sem_t->mutex
sem_wait algorithn:
mutex_lock sem_t->mutex
while (sem_t->value > 0) {
mutex_unlock sem_t->mutex
sleep... wake up
mutex_lock sem_t->mutex
}
sem_t->value--
mutex_unlock sem_t->mutex
*/
void insertbuffer(buffer_t value) {
if (buffer_index < SIZE) {
buffer[buffer_index++] = value;
} else {
printf("Buffer overflow\n");
}
}
buffer_t dequeuebuffer() {
if (buffer_index > 0) {
return buffer[--buffer_index]; // buffer_index-- would be error!
} else {
printf("Buffer underflow\n");
}
return 0;
}
int isempty() {
if (buffer_index == 0)
return TRUE;
return FALSE;
}
int isfull() {
if (buffer_index == SIZE)
return TRUE;
return FALSE;
}
void *producer2(void *thread_n) {
int thread_numb = *(int *)thread_n;
buffer_t value;
int i=0;
while (i++ < PRODUCER_LOOPS) {
sleep(rand() % 10);
value = rand() % 100;
pthread_mutex_lock(&buffer_mutex);
do {
// cond variables do the unlock/wait and wakeup/lock atomically,
// which avoids possible race conditions
pthread_mutex_unlock(&buffer_mutex);
// cannot go to slepp holding lock
sem_wait(&full_sem); // sem=0: wait. sem>0: go and decrement it
// there could still be race condition here. another
// thread could wake up and aqcuire lock and fill up
// buffer. that's why we need to check for spurious wakeups
pthread_mutex_lock(&buffer_mutex);
} while (isfull()); // check for spurios wake-ups
insertbuffer(value);
pthread_mutex_unlock(&buffer_mutex);
sem_post(&empty_sem); // post (increment) emptybuffer semaphore
//printf("Producer Thread %d adds %d added %d to buffer\n", pthread_self(), thread_numb, value);
printf("Producer Thread %d adds %d to the buffer, and the current buffer contains %d \n", pthread_self(), value, *buffer);
}
pthread_exit(0);
}
void *consumer2(void *thread_n) {
int thread_numb = *(int *)thread_n;
buffer_t value;
int i=0;
while (i++ < CONSUMER_LOOPS) {
pthread_mutex_lock(&buffer_mutex);
do {
pthread_mutex_unlock(&buffer_mutex);
sem_wait(&empty_sem);
pthread_mutex_lock(&buffer_mutex);
} while (isempty()); //check for spurios wakeups
value = dequeuebuffer(value);
pthread_mutex_unlock(&buffer_mutex);
sem_post(&full_sem); // post (increment) fullbuffer semaphore
printf("Consumer Thread %d dequeue %d from buffer, and the current buffer contains %d \n", pthread_self(), value, *buffer);
}
pthread_exit(0);
}
int main(int argc, int **argv) {
buffer_index = 0;
pthread_mutex_init(&buffer_mutex, NULL);
sem_init(&full_sem, // sem_t *sem
0, // int pshared. 0 = shared between threads of process, 1 = shared between processes
SIZE); // unsigned int value. Initial value
sem_init(&empty_sem,
0,
0);
/* full_sem is initialized to buffer size because SIZE number of
producers can add one element to buffer each. They will wait
semaphore each time, which will decrement semaphore value.
empty_sem is initialized to 0, because buffer starts empty and
consumer cannot take any element from it. They will have to wait
until producer posts to that semaphore (increments semaphore
value) */
pthread_t thread[NUMB_THREADS];
int thread_numb[NUMB_THREADS];
int i;
for (i = 0; i < NUMB_THREADS; ) {
thread_numb[i] = i;
if(i <= 2)
{
pthread_create(thread + i, // pthread_t *t
NULL, // const pthread_attr_t *attr
producer2, // void *(*start_routine) (void *)
thread_numb + i); // void *arg
}
thread_numb[i] = i;
// playing a bit with thread and thread_numb pointers...
pthread_create(&thread[i], // pthread_t *t
NULL, // const pthread_attr_t *attr
consumer2, // void *(*start_routine) (void *)
&thread_numb[i]); // void *arg
i++;
}
for (i = 0; i < NUMB_THREADS; i++)
pthread_join(thread[i], NULL);
pthread_mutex_destroy(&buffer_mutex);
sem_destroy(&full_sem);
sem_destroy(&empty_sem);
return 0;
}
You can't print an array of unknown length in one line, but you can modify your insertbuffer to show the details every time you insert a value. Or, implement the printing as a separate function. Then calling it is a one-liner, obviously.
void insertbuffer(int threadnum, buffer_t value) {
int i;
if (buffer_index < SIZE) {
buffer[buffer_index++] = value;
printf("Producer Thread %d adds %d to the buffer, and the current buffer contains",
threadnum, (int)value);
for(i=0; i<buffer_index; i++) {
if(i)
printf(",");
printf(" %d", (int)buffer[i]);
}
printf("\n");
} else {
printf("Buffer overflow\n");
}
}
I'm coding a multithreaded program for exercise. Given an array (100 positions) of random numbers, I have to divide it by 5 arrays and give them to 5 pthreads in order to find the maximum and return these values to the main function that find the maximum between them. These is my code so far:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#define NUM_THREADS 5
#define DIM_VETTORE 100
void *Calcola_max(void* args){
}
int main(){
int vettore[DIM_VETTORE];
int t;
int i;
srand(time(NULL));
/*riempio il vettore con numeri random*/
for (i=0; i<DIM_VETTORE; i++){
vettore[i]=rand() % 500 + 1;
printf("Numero in posizione %d: %d\n", i,vettore[i]);
}
/*indico le dimensioni di ogni array splittato*/
int dimensione_split=DIM_VETTORE/NUM_THREADS;
printf("Dimensione degli array splittati: %d\n", dimensione_split);
/*creo tutti i thread*/
pthread_t thread[NUM_THREADS];
for (t=0;t<NUM_THREADS; t++){
printf("Main: creazione thread %d\n", t);
int rc;
rc=pthread_create(&thread[t], NULL, Calcola_max, &vettore);
if (rc) {
printf("ERRORE: %d\n", rc);
exit(-1);
}
}
}
My question are: how can I split the array? And how can I pass each array to each pthread? Thanks in advance
So, I've edited my code but this time it gives me segmentation fault after the pthread creation. IMO I'm wrong to pass the argument of thread function in this way:
...
pthread_create(&thread[t], NULL, Calcola_max, (void *)&start[i]);
...
void *Calcola_max(void *a){
...
s = *(int *)a;
...
Here is my entire code:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#define NUM_THREADS 5
#define DIM_VETTORE 100
int vettore[DIM_VETTORE];
int start[100];
int max[100]; //vettore dove vanno tutti i minimi calcolati dai pthread
void *Calcola_max(void *a){
int array;
int n=DIM_VETTORE/NUM_THREADS;
int s, i;
int start, stop;
int massimo;
s = *(int *)a;
start = s * n;
if ( s != (NUM_THREADS-1) )
{
stop = start + n;
}
else
{
stop = DIM_VETTORE;
}
massimo=vettore[start];
for (i = start+1; i < stop; i++ )
{
if ( vettore[i] > massimo )
massimo = vettore[i];
}
max[s] = massimo;
//array = (int) a;
int k;
int max=0;
for (k=0; k<DIM_VETTORE; k++){ //qui devo mettere il range corrente del vettore, o mettere uno split di vettore
printf("Massimo corrente: %d\n",max);
if (vettore[k]>max) max=vettore[k];
}
//return(NULL); /* Thread exits (dies) */
pthread_exit;
}
int main(){
//int vettore[DIM_VETTORE];
int massimo; //vettore dei minimi finale in cui opero confronto e calcolo il minimo
int t;
int i, j;
srand(time(NULL));
/*riempio il vettore con numeri random*/
for (i=0; i<DIM_VETTORE; i++){
//int num; //contenitore numero random
vettore[i]=rand() % 500 + 1;
//printf("Numero in posizione %d: %d\n", i,vettore[i]);
}
/*indico le dimensioni di ogni array splittato*/
int dimensione_split=DIM_VETTORE/NUM_THREADS;
printf("Dimensione degli array splittati: %d\n", dimensione_split);
/*creo tutti i thread*/
pthread_t thread[NUM_THREADS];
for (t=0;t<NUM_THREADS; t++){
start[i] = i;
printf("Main: creazione thread %d\n", t);
int rc;
//int pos_vettore;
//for (pos_vettore=0; pos_vettore<100; pos_vettore+20){
rc=pthread_create(&thread[t], NULL, Calcola_max, (void *)&start[i]);
if (rc) {
printf("ERRORE: %d\n", rc);
exit(-1);
}
//}
}
/*joino i threads*/
for (i = 0; i < NUM_THREADS; i++)
pthread_join(thread[i], NULL);
massimo= max[0];
sleep(3);
for (i = 1; i < NUM_THREADS; i++)
if ( max[i] > massimo )
massimo = max[i];
printf("Il massimo è: %d\n", massimo);
}
Your pthreads can access the array in your main program easily. You won't need to split the array for that. Just make sure that the pthreads are modifiying different parts of the main array. Use a struct or typedef to pass the relevant information to the pthread functions.
As mentioned, you don't split or copy the array.
Threads share the same regions of memory as the process that creates them, so you could just pass around the array.
If the aim of the game is to find some perf gain from using threads, then you almost certainly don't want to use heap allocated memory.
It's got to be said that there are probably better ways, I would probably look to SIMD or some other SSE extension before threads, but whatever ...
The following example, which has had about 5 minutes thought, and requires better error checking, and verification of the logic (because it's 9am on Sunday), demonstrates how I think the most efficient way to thread the calculations might be.
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <pthread.h>
#define THREADS 5
#define DATA 100
typedef struct _pthread_arg_t {
pthread_t thread;
int *data;
unsigned int end;
int max;
} pthread_arg_t;
void* pthread_routine(void *arg) {
pthread_arg_t *info = (pthread_arg_t*) arg;
int *it = info->data,
*end = info->data + info->end;
while (it < end) {
if (*it > info->max) {
info->max = *it;
}
it++;
}
pthread_exit(NULL);
}
int main(int argc, char *argv[]) {
pthread_arg_t threads[THREADS];
int data[DATA],
thread = 0,
limit = 0,
result = 0;
memset(&threads, 0, sizeof(pthread_arg_t) * THREADS);
memset(&data, 0, sizeof(int) * DATA);
while (limit < DATA) {
/* you can replace this with randomm number */
data[limit] = limit;
limit++;
}
limit = DATA/THREADS;
while (thread < THREADS) {
threads[thread].data = &data[thread * limit];
threads[thread].end = limit;
if (pthread_create(&threads[thread].thread, NULL, pthread_routine, &threads[thread]) != 0) {
/* do something */
return 1;
}
thread++;
}
thread = 0;
while (thread < THREADS) {
if (pthread_join(threads[thread].thread, NULL) != 0) {
/* do something */
return 1;
}
thread++;
}
thread = 0;
result = threads[0].max;
printf("result:\n");
while (thread < THREADS) {
printf("\t%d - %d: %d\n",
thread * limit,
thread * limit + limit - 1,
threads[thread].max);
if (threads[thread].max > result) {
result = threads[thread].max;
}
thread++;
}
printf("max\t%d\n", result);
return 0;
}
Notice that this is lock and malloc free, you could probably reduce instructions further with more fiddling ...
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.