Related
I am new to MultiThreading in C. I am trying to write a code where we use multithreading. The code is as follows:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include <windows.h>
#include <time.h>
#include <math.h>
int finish = 0;
int mess_size=15;
double exponGenerator(double myLamda)
{
double pv = 0.0;
while (1)
{
pv = (double)rand() / (double)RAND_MAX;
if (pv != 1)
break;
}
pv = (-1.0 / myLamda) * log(1 - pv);
return pv;
}
struct packet{
int val;
time_t start_time;
double service_time;
};
struct queue{
struct packet arr_pac[10000];
int start;
int end;
int size;
};
double lambda = 5;
double servicerate = 5;
struct queue q1 ;
struct queue q2;
struct queue initialiseQueue(){
struct queue q ;
q.start = 0;
q.end = 0;
q.size=0;
return q;
}
struct process1{
int method;
double lambda;
struct queue Q1;
struct queue Q2;
};
struct process2{
struct queue q;
double u;
};
struct process1 queueenv(int method)
{
struct process1 temp ;
temp.method = method;
temp.lambda = lambda;
temp.Q1 = q1;
temp.Q2 = q2;
return temp;
}
struct process2 serverenv(double u, struct queue q)
{
struct process2 temp;
temp.u = u;
temp.q = q;
return temp;
}
int enque(struct queue q){
if (q.size < 10)
{
struct packet temp ;
temp.start_time = time(NULL);
temp.val = 1;
q.arr_pac[q.end] = temp;
q.end = q.end +1;
q.size = q.end - q.start;
//printf(" %d",q.arr_pac[0].end);
return q.size ;
}
else{
return -1;
}
}
struct packet deque(struct queue q) {
struct packet temp ;
printf(" %d ",q.end);
if(q.size >0){
printf("entered");
temp=q.arr_pac[q.start];
temp.service_time = difftime(time(NULL),temp.start_time);
q.start=q.start +1;
q.size = q.end - q.start;
return temp;
}
else{
printf("entered 2");
temp.service_time=0 ;
return temp;
}
}
int randomSelection(){
if(rand()%2 ==0){
return enque(q1);
}
else{
return enque(q2);
}
}
int minQueue(){
if(q1.size > q2.size){
return enque(q2);
}
else{
return enque(q1);
}
}
void queueprocess(struct process1 params){
double blockCounter = 0.0;
double blockPro = 0.0;
int queLenCounter = 0;
int averageQueueLen = 0;
int i = 0;
if (params.method == 0)
{
printf(" this is the %d =0",params.method);
for (i = 0; i < mess_size ; i++)
{
double interval = exponGenerator(params.lambda);
sleep(interval);
int res = randomSelection();
if (res == -1)
{
blockCounter++;
queLenCounter = queLenCounter +10;
}
else{
queLenCounter = queLenCounter + res;
//printf(" %d ",queLenCounter);
}
}
}
else if (params.method == 1)
{
printf(" this is the %d =1 ",params.method);
for (i = 0; i < mess_size ; i++)
{
double interval = exponGenerator(params.lambda);
sleep(interval);
int res = minQueue();
printf(" %d mn",q1.end);
if (res == -1)
{
blockCounter++;
queLenCounter = queLenCounter +10;
}
else{
queLenCounter = queLenCounter +res;
}
}
}
blockPro = blockCounter/mess_size;
averageQueueLen = queLenCounter/mess_size;
printf("Blocking Probability is : %f",&blockPro);
printf("Average Que Length is : %d",averageQueueLen);
finish = 1;
return;
}
void serverprocess(struct process2 serverparams)
{
struct packet processed_arr[10000];
int i=0,j;
while(1)
{
if (finish == 1 && serverparams.q.size == 0)
{
break;
}
else
{
double interval = exponGenerator(serverparams.u);
sleep(interval);
struct packet k = deque(serverparams.q);
if(!k.service_time){
processed_arr[i]=k;
i++;
}
}
}
float sourjanCounter=0;
float sourjan;
for(j=0;j<i;j++){
printf(" %f",processed_arr[j].service_time);
sourjanCounter = sourjanCounter+ processed_arr[j].service_time;
}
sourjan = sourjanCounter/(i-1);
printf("Average Sourjan Time is : %f ", &sourjan);
}
DWORD WINAPI threadone(){
printf(" thread one ");
struct process1 queueparams = queueenv(1);
queueprocess(queueparams);
}
DWORD WINAPI threadtwo(){
struct process2 server1params = serverenv(servicerate, q1);
serverprocess(server1params);
}
DWORD WINAPI threadthree(){
struct process2 server2params = serverenv(servicerate, q2);
serverprocess(server2params);
}
int main(int argc, char * argv[])
{
HANDLE hThrds[3];
int i = 0;
/*if (argc != 3)
{
printf("Incorrect parameters!\n");
return 0;
}
double lambda = atof(argv[1]);
double servicerate = atof(argv[2]);*/
for (i = 0; i < 1; i++)
{
DWORD thread1,thread2,thread3;
hThrds[2]=CreateThread(NULL,0, threadone, NULL,0,&thread1);
hThrds[0]=CreateThread(NULL,0, threadtwo, NULL,0,&thread2);
hThrds[1]=CreateThread(NULL,0, threadthree, NULL,0,&thread3);
WaitForMultipleObjects(3, hThrds,TRUE, INFINITE);
}
}
In my code I have a function called enque, which is called from a function called queueprocess. q1 is a structure and is defined globally, It is accessed by multiple functions at the same time. I am trying to change the value of q1 inside function enque and when I print its value inside the function enque it's value is correct, but if I try to print its value inside function queueprocess its value is 0. As a result of this my code is not moving forward. I have read about mutex, but I don't know what is mutex equivalent to win32. I don't know why mutex should be used for my program as well, since I am just accessing q1 from another function but I am not trying to change its value.
Can anyone please explain me why I am facing the problem mentioned above and what would be the possible solution of it?
From Microsoft documentation:
You can use a mutex object to protect a shared resource from
simultaneous access by multiple threads or processes. Each thread must
wait for ownership of the mutex before it can execute the code that
accesses the shared resource. For example, if several threads share
access to a database, the threads can use a mutex object to permit
only one thread at a time to write to the database.
The following example uses the CreateMutex function to create a mutex
object and the CreateThread function to create worker threads.
When a thread of this process writes to the database, it first
requests ownership of the mutex using the WaitForSingleObject
function. If the thread obtains ownership of the mutex, it writes to
the database and then releases its ownership of the mutex using the
ReleaseMutex function.
This example uses structured exception handling to ensure that the
thread properly releases the mutex object. The __finally block of code
is executed no matter how the __try block terminates (unless the __try
block includes a call to the TerminateThread function). This prevents
the mutex object from being abandoned inadvertently.
If a mutex is abandoned, the thread that owned the mutex did not
properly release it before terminating. In this case, the status of
the shared resource is indeterminate, and continuing to use the mutex
can obscure a potentially serious error. Some applications might
attempt to restore the resource to a consistent state; this example
simply returns an error and stops using the mutex. For more
information, see Mutex Objects.
#include <windows.h>
#include <stdio.h>
#define THREADCOUNT 2
HANDLE ghMutex;
DWORD WINAPI WriteToDatabase( LPVOID );
int main( void )
{
HANDLE aThread[THREADCOUNT];
DWORD ThreadID;
int i;
// Create a mutex with no initial owner
ghMutex = CreateMutex(
NULL, // default security attributes
FALSE, // initially not owned
NULL); // unnamed mutex
if (ghMutex == NULL)
{
printf("CreateMutex error: %d\n", GetLastError());
return 1;
}
// Create worker threads
for( i=0; i < THREADCOUNT; i++ )
{
aThread[i] = CreateThread(
NULL, // default security attributes
0, // default stack size
(LPTHREAD_START_ROUTINE) WriteToDatabase,
NULL, // no thread function arguments
0, // default creation flags
&ThreadID); // receive thread identifier
if( aThread[i] == NULL )
{
printf("CreateThread error: %d\n", GetLastError());
return 1;
}
}
// Wait for all threads to terminate
WaitForMultipleObjects(THREADCOUNT, aThread, TRUE, INFINITE);
// Close thread and mutex handles
for( i=0; i < THREADCOUNT; i++ )
CloseHandle(aThread[i]);
CloseHandle(ghMutex);
return 0;
}
DWORD WINAPI WriteToDatabase( LPVOID lpParam )
{
// lpParam not used in this example
UNREFERENCED_PARAMETER(lpParam);
DWORD dwCount=0, dwWaitResult;
// Request ownership of mutex.
while( dwCount < 20 )
{
dwWaitResult = WaitForSingleObject(
ghMutex, // handle to mutex
INFINITE); // no time-out interval
switch (dwWaitResult)
{
// The thread got ownership of the mutex
case WAIT_OBJECT_0:
__try {
// TODO: Write to the database
printf("Thread %d writing to database...\n",
GetCurrentThreadId());
dwCount++;
}
__finally {
// Release ownership of the mutex object
if (! ReleaseMutex(ghMutex))
{
// Handle error.
}
}
break;
// The thread got ownership of an abandoned mutex
// The database is in an indeterminate state
case WAIT_ABANDONED:
return FALSE;
}
}
return TRUE;
}
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;
}
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;
}
My problem is that I can't initialize the circular_buffer in main() and the basic problem is that when the program calls pthread_cond_wait() in the produce_numbers() function, I take a segmentation fault.
My code is below.
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int seed;
typedef struct circular_buffer {
void *buffer; // data buffer
void *buffer_end; // end of data buffer
size_t capacity; // maximum number of items in the buffer
size_t count; // number of items in the buffer
size_t sz; // size of each item in the buffer
void *head; // pointer to head
void *tail; // pointer to tail
pthread_mutex_t mutex; // needed to add/remove data from the buffer
pthread_cond_t can_produce; // signaled when items are removed
pthread_cond_t can_consume; // signaled when items are added
} circular_buffer;
struct thread_data {
int thread_id;
int thread_id2;
int seed;
int numbersofprod;
int talenum;
};
struct thread_data thread_data_array[40];
void cb_init(circular_buffer *cb, size_t capacity, size_t sz)
{
cb->buffer = malloc(capacity * sz);
if (cb->buffer == NULL) {
printf("Could not allocate memory..Exiting! \n");
exit(1);
}
// handle error
cb->buffer_end = (char *)cb->buffer + capacity * sz;
cb->capacity = capacity;
cb->count = 0;
cb->sz = sz;
cb->head = cb->buffer;
cb->tail = cb->buffer;
}
// remove first item from circular item void
cb_pop_front(circular_buffer *cb, void *item)
{
printf("pop_front");
if (cb->count == 0) {
printf("Access violation. Buffer is empty\n");
exit(1);
}
memcpy(item, cb->tail, cb->sz);
cb->tail = (char *)cb->tail + cb->sz;
if (cb->tail == cb->buffer_end)
cb->tail = cb->buffer;
cb->count--;
}
// add item to circular buffer
void cb_push_back(circular_buffer *cb, const void *item)
{
if (cb->count == cb->capacity) {
printf("Access violation. Buffer is full\n");
exit(1);
}
memcpy(cb->head, item, cb->sz);
cb->head = (char *)cb->head + cb->sz;
if (cb->head == cb->buffer_end)
cb->head = cb->buffer;
cb->count++;
}
// destroy circular buffer
void cb_free(circular_buffer *cb)
{
free(cb->buffer);
// clear out other fields too, just to be safe
}
void *consume_numbers(void *arg)
{
struct thread_data *my_data;
int threadId = my_data->thread_id2;
int rc;
circular_buffer *cb =
(circular_buffer *)arg; // δήλωση δείκτη σε δομή circular_buffer
printf("to capacity einai %d!\n", cb->capacity);
while (1) {
pthread_mutex_lock(&cb->mutex);
// elegxos an to buffer einai adeio
if (cb->count == 0) { // empty
// wait for new items to be appended to the buffer
printf("eeeeeee");
pthread_cond_wait(&cb->can_consume, &cb->mutex);
} // if
int *tmp_read = (int *)malloc(4);
cb_pop_front(cb, (void *)tmp_read);
free(tmp_read);
printf("Thread: consuming item %d!\n", cb->count);
// signal the fact that new items may be produced
rc = pthread_cond_signal(&cb->can_produce);
if (rc != 0) {
printf("ERROR: return code from pthread_cond_signal() is %d\n", rc);
pthread_exit(&rc);
} // if
rc = pthread_mutex_unlock(&cb->mutex);
if (rc != 0) {
printf("ERROR: return code from pthread_mutex_unlock() is %d\n", rc);
pthread_exit(&rc);
} // if
} // while
// de ftanei pote edw
return NULL;
} // consume_numbers
void *produce_numbers(void *threadarg)
{
struct thread_data *my_data;
my_data = (struct thread_data *)threadarg;
int seeed = my_data->seed;
int numberofprod = my_data->numbersofprod;
int threadId = my_data->thread_id;
int talenumber = my_data->talenum;
int j, r;
int *tid;
int rc;
circular_buffer *cb;
// open file
FILE *f = fopen("producers_in.txt", "w");
if (f == NULL) {
printf("Error opening file!\n");
exit(1);
} // telos anoigmatos arxeiou
srand(seeed); // paragwgi sporou
for (j = 0; j < numberofprod; j++) {
pthread_mutex_lock(&cb->mutex);
// elegxos an o buffer einai full
if (cb->count == cb->capacity) {
// wait until some elements are consumed
printf("mpika");
// pthread_cond_wait(&cb->can_produce, &cb->mutex);
if (rc != 0) {
printf("ERROR: return code from pthread_cond_wait() is %d\n",
rc);
pthread_exit(&rc);
} // if
} // while
else {
r = rand();
printf("tyxaios arithmos %d\n", r);
cb_push_back(
cb, (void *)&r); // grapse sto arxeio txt ton tyxaio arithmo r
const char *text = "the random number of producer with id";
fprintf(f, "%s ", text);
tid = (int *)threadarg;
fprintf(f, "%d\n", tid);
const char *text2 = "is";
fprintf(f, " %s ", text2);
fprintf(f, "%d\n", r);
printf("to count einai %d\n", cb->count);
// signal the fact that new items may be consumed
rc = pthread_cond_signal(&cb->can_consume);
if (rc != 0) {
printf("ERROR: return code from pthread_cond_signal() is %d\n",
rc);
pthread_exit(&rc);
} // if
rc = pthread_mutex_unlock(&cb->mutex);
if (rc != 0) {
printf("ERROR: return code from pthread_mutex_unlock() is %d\n",
rc);
pthread_exit(&rc);
} // if
} // else
} // for
fclose(f);
// NEVER REACHED
tid = (int *)threadarg;
pthread_exit(tid);
} // produce_numbers
// MAIN
int main(int argc, char *argv[])
{
if (argc != 6) {
printf("ERROR: the program should take 5 argument!\n");
exit(-1);
}
int producers = atoi(argv[1]);
int consumers = atoi(argv[2]);
int numbersofprod = atoi(argv[3]);
int talenum = atoi(argv[4]);
int seed = atoi(argv[5]);
/*elegxoume oti ta dedomena pou dothikan einai swsta.*/
if (producers < 1) {
printf("ERROR: the number of producers to run should be a positive number.Current number given %d.\n",
producers);
exit(-1);
}
if (consumers < 1) {
printf(
"ERROR: the number of consumers to run should be a positive number.Current number given %d.\n", consumers);
exit(-1);
}
if (numbersofprod <
1) {
printf("ERROR: the number of numbersofprod to run should be a positive number. Current number given %d.\n", numbersofprod);
exit(-1);
}
if (talenum < 1) {
printf("ERROR: the number of tale to run should be a positive number. Current number given %d.\n", talenum);
exit(-1);
}
if (seed < 1) {
printf("ERROR: the number of the seed to run should be a positive number. Current number given %d.\n", seed);
exit(-1);
}
printf("Main: We will create %d producer threads", producers);
printf(" and %d consumers threads.\n", consumers);
pthread_t *prod;
pthread_t *cons;
prod = malloc(producers * sizeof(pthread_t));
if (prod == NULL) {
printf("NOT ENOUGH MEMORY!\n");
return -1;
}
cons = malloc(consumers * sizeof(pthread_t));
if (cons == NULL) {
printf("NOT ENOUGH MEMORY!\n");
return -1;
}
int rc;
int rc2;
int threadCount;
int threadCount2;
int countArray[producers];
int countArray2[consumers];
//INITIALIZE MUTEX AND CONDITION VARIABLES
circular_buffer cb = {
.count=0,
.mutex = PTHREAD_MUTEX_INITIALIZER,
.can_produce = PTHREAD_COND_INITIALIZER,
.can_consume = PTHREAD_COND_INITIALIZER
};
cb_init(cb, talenum, 1); //end_of initializing
for (threadCount = 0; threadCount < producers; threadCount++) {
printf("Main: creating thread %d for producers\n ",
threadCount);
countArray[threadCount] = threadCount + 1;
thread_data_array[threadCount].thread_id = threadCount;
thread_data_array[threadCount].talenum = talenum;
thread_data_array[threadCount].seed = seed;
thread_data_array[threadCount].numbersofprod =
numbersofprod; /*dimiourgia tou thread*/
rc = pthread_create(&prod[threadCount], NULL, produce_numbers,
(void *)&thread_data_array[threadCount]);
/*elegxos oti to thread dimiourgithike swsta.*/
if (rc != 0) {
printf("ERROR: return code from pthread_create() is %d\n",
rc);
exit(-1);
}
for (threadCount2 = 0; threadCount2 < consumers;
threadCount2++) {
countArray2[threadCount2] = threadCount2 + 1;
thread_data_array[threadCount2].thread_id2 = threadCount2;
printf("Main: creating thread %d for consumers\n",
threadCount);
/*dimiourgia tou thread*/
rc2 = pthread_create(cons, NULL, consume_numbers,
(void *)&cb);
/*elegxos oti to thread dimiourgithike swsta.*/
if (rc2 != 0) {
printf(
"ERROR: return code from pthread_create() is %d\n",
rc2);
exit(-1);
}
}
}
void *status;
for (threadCount = 0; threadCount < producers;
threadCount++) {
rc = pthread_join(prod[threadCount], &status);
if (rc != 0) {
printf("ERROR: return code from pthread_join() is %d\n",
rc);
exit(-1);
}
printf("Main: Thread %d returned %d as status code.\n",
countArray[threadCount], (*(int *)status));
}
free(prod);
`` return 1;
void *status2;
for (threadCount2 = 0; threadCount2 < consumers; threadCount2++) {
rc2 = pthread_join(cons[threadCount2], &status2);
if (rc2 != 0) {
printf("ERROR: return code from pthread_join() is %d\n",
rc);
exit(-1);
}
printf("Main: Thread % d returned % d as status code.\n ", countArray2[threadCount2],
(*(int *)status2));
}
free(cons);
return 1;
}
Your problem is that you never call pthread_cond_init() on the cb.can_produce and cb.can_consume condition variables that are members of the struct circular_buffer.
You need to change the cb_init() function so that it initialises the new circular_buffer's mutex, can_produce and can_consume members with pthread_mutex_init() and pthread_cond_init().
You are also using the pointer variable cb uninitialised in the produce_numbers() function - it's declared as struct circular_buffer *cb; but never assigned to. You need to make cb a member of struct thread_arg instead, and set it in main() so that produce_numbers() has a reference to the same struct circular_buffer object as the other threads.
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.