Producer–Consumer & Condition Variables program gets stuck in a infinite loop - c

When I run my program( running in C ) with usleep it gets stuck in an infinite loop. Without usleep the program doesnt run concurrently though. Any help will be much appreciated.
The program is supposed to allow producers make food while consumers take the food at the same time. My program gets stuck after adding about 5 items and stops. I think it could be a thread not being unlocked but I cant figure it out.
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
// rc stands for return code
#define NUM_THREADS 4 // declare consumers
#define NUM_PRODUCERS 2 // declare producers
#define MAX_BUFFER 10 // declare max buffer
pthread_mutex_t bufferBox; // delcare buffer
struct foodItem{ // structure for food
int serialCode;
int producer;
struct foodItem * next;
};
struct buffer{ // Structure for buffer
int size;
struct foodItem * head;
};
struct buffer * myBuffer;
void addFood(int producer, struct buffer * buffer);
void removeItem(struct buffer * buffer);
int serial;
void addFood(int producer, struct buffer * buffer){ // ADD TO BUFFER FUNCTION
struct foodItem * newItem = malloc(sizeof(struct foodItem));
newItem -> producer = producer;
newItem -> serialCode = serial;
if(buffer->size==0){
buffer-> head = newItem;
buffer->size++;
printf("item added serial%d\n",serial);
serial++;
}
else{
struct foodItem * item = buffer ->head;
while(item->next != NULL ){
item = item-> next;
}
item ->next =newItem;
buffer->size++;
printf("item added serial%d\n",serial);
serial++;
}
}
void removeItem(struct buffer * buffer){ //REMOVE FROM BUFFER FUNCTION
if(buffer->size ==1){
free(buffer->head);
}
else{
struct foodItem * temp = buffer -> head;
buffer -> head = buffer ->head->next;
free(temp);
}
buffer->size--;
printf("item removed\n");
}
void *Producers(void *threadid){
int i =11;
while(i>0){
if(myBuffer->size < MAX_BUFFER){
pthread_mutex_lock(&bufferBox);
addFood((int)threadid, myBuffer);
addFood((int)threadid, myBuffer);
pthread_mutex_unlock(&bufferBox);
usleep(20000);
}
else{
printf("OverFlow\n");
}
i--;
}
pthread_exit(NULL);
}
void *Consumers(void *threadid) {
usleep(20000);
int i =6;
while( i >0){
if(myBuffer->size > 0){
pthread_mutex_lock(&bufferBox);
removeItem(myBuffer);
pthread_mutex_unlock(&bufferBox);
usleep(15000);
}
else{
printf("UnderFlow\n");
}
i--;
}
pthread_exit(NULL);
}
int main (int argc, const char * argv[]) {
pthread_t consumers[NUM_THREADS];
pthread_t producers[NUM_PRODUCERS];
long rc,t,i;
int size =0;
myBuffer = malloc(sizeof(struct buffer));
for (t=0;t<NUM_PRODUCERS;t++) {
printf("Creating Producers %ld\n",t);
rc = pthread_create(&producers[t],NULL,Producers,(void *)t); // initial producer
if (rc) {
printf("ERROR return code from pthread_create(): %ld\n",rc);
exit(-1);
}
}
//usleep(10000);
for (t=0;t<NUM_THREADS;t++) {
printf("Creating Consumers %ld\n",t);
rc = pthread_create(&consumers[t],NULL,Consumers,(void *)t); // initial consumers
if (rc) {
printf("ERROR return code from pthread_create(): %ld\n",rc);
exit(-1);
}
}
// wait for threads to exit
for(t=0;t<NUM_THREADS;t++) {
pthread_join(producers[t], NULL);
}
// wait for threads to exit
for(t=0;t<NUM_THREADS;t++) {
pthread_join(consumers[t], NULL);
}
return 0;
}

You need to be careful to initialize any data before using it, for example your addFood(...) routine at the top add a line like this
newItem -> next = NULL;
Similarly in your removeItem(...) function;
if(buffer->size ==1){
free(buffer->head);
buffer->head = NULL;
}
Also as #EOF says in his comment above use the mutex to protect access to buffer->size in your Producers(...) and Consumers(...) routines. For example;
pthread_mutex_lock(&bufferBox);
if(myBuffer->size < MAX_BUFFER) {
....
pthread_mutex_unlock(&bufferBox);
After fixing all of these problems your producers seem to exit last leaving the queue completely full. Not sure what behavior you expect.

Related

How to realloc across multiple threads in C?

I have created a list of an undefined size using malloc which keeps track of threads and their data. However, if I realloc while the threads are still running, they are no longer able to save their data as the struct's memory location has changed. My project is constantly adding/subtracting threads so I need realloc.
Code:
#include <windows.h>
#include <stdio.h>
typedef struct option_data {
char* contract[3];
} option_data;
typedef struct thread_data {
char* thread;
} thread_data;
DWORD WINAPI optionSymbol(void* dat) {
option_data* data = (option_data*)dat;
data->contract[0] = 6;
data->contract[1] = 7;
data->contract[2] = 5;
return 0;
}
int create_thread(void* data, void* dat2) {
HANDLE thread = CreateThread(NULL, 0, optionSymbol, data, 0, NULL);
thread_data* t_data = (thread_data*)dat2;
t_data->thread = thread;
return 0;
}
void reallocation(lista, sizeOfList)
{
char** list = lista;
char* listThingy = realloc(*list, sizeOfList * sizeof * list);
if (listThingy == NULL)
free(listThingy);
else
*list = listThingy;
}
int getChains(void)
{
option_data* optionDataList = malloc(sizeof(option_data));
thread_data* threadDataList = malloc(sizeof(thread_data));
create_thread(&optionDataList[0], &threadDataList[0]);
//reallocation(&optionDataList, 2)
//reallocation(*threadDataList, 2) <-- The code returns unpredictably when un-noting these two lines.
create_thread(&optionDataList[0], &threadDataList[0]);
WaitForSingleObject(threadDataList[0].thread, INFINITE);
CloseHandle(threadDataList[0].thread);
printf("%i", optionDataList[0].contract[0]);
return 0;
}
int main()
{
getChains();
return 0;
}
How would I realloc without changing the original memory location of the structs or send the new memory location to the threads?

malloc() is returning the same address multiple times, even when I haven't used free()

EDIT: I did use free(), ignore the title.
The gist is that every time malloc() is called, the address 0x8403620
is returned, which I found out using Gdb.
tellers[i] = create_teller(0, i, NULL);
I first use malloc() on line 72 to create 3 teller structures. The first addressed returned, visible through Gdb, is 0x84003620. The second is
0x84033a0, the third 0x84034e0. Everything seems fine.
clients[i] = create_client(0, i, -1, -1);
Then I use malloc() on line 77 with the create_client() function to
create 100 clients. The first address, assigned to client[0], is ...
0x8403620. The same as tellers[0]. It gets worse. The next address
returned from malloc() is 0x8403620 again for when i = 1, and so
on for i = 3, 4, ..., 99.
It isn't inherently the create_client() or the create_teller() functions, but
instead the malloc() function itself.
This is simply a very odd situation.
Now, I'd like to ask: Am I using malloc() wrong? Or is my version of malloc() bugged and should I somehow reinstall whatever it is? It's most likely my code since it works for creating the tellers, just not for the clients.
Here is the full code:
#include <pthread.h>
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>
#include <assert.h>
typedef struct teller teller_t;
typedef struct client client_t;
teller_t * create_teller (pthread_t thread_id, int id, client_t *assigned_client);
client_t * create_client (pthread_t thread_id, int id, int operation, int amount);
void * run_teller (void *arg);
void * run_client (void *arg);
/* types of operations */
#define DEPOSIT 0
#define WITHDRAW 1
#define NUM_TELLERS 3
#define NUM_CLIENTS 100
struct client {
pthread_t thread_id;
int id;
int operation;
int amount;
};
struct teller {
pthread_t thread_id;
int id;
bool available;
client_t *assigned_client;
};
client_t *clients[100];
teller_t *tellers[3];
/* only 2 tellers at a time can access */
sem_t safe;
/* only 1 teller at a time can access */
sem_t manager;
/* amount of tellers available, at most 3 */
sem_t line; /* rename to available? */
/* each teller waiting for a client to be assigned to them */
sem_t wait_for_client[3];
int
main (int argc, char **argv) {
(void) argc;
(void) argv;
srand(time(NULL));
/* This also tells us how many clients have been served */
int client_index = 0;
sem_init(&safe, 0, 2);
sem_init(&manager, 0, 1);
sem_init(&line, 0, 0);
for (int i = 0; i < 3; i++)
sem_init(&wait_for_client[i], 0, 0);
for (int i = 0; i < NUM_TELLERS; i++) {
tellers[i] = create_teller(0, i, NULL);
pthread_create(&tellers[i]->thread_id, NULL, run_teller, (void *) tellers[i]);
}
for (int i = 0; i < NUM_CLIENTS; i++) {
clients[i] = create_client(0, i, -1, -1);
pthread_create(&clients[i]->thread_id, NULL, run_client, (void *) clients[i]);
}
/* DEBUG
for (int i = 0; i < NUM_CLIENTS; i++) {
printf("client %d has id %d\n", i, clients[i]->id);
}
*/
// No threads should get past this point!!!
// ==------------------------------------==
// Should all of this below be handled by the clients instead of main?
while (1) {
if (client_index >= NUM_CLIENTS) {
// TODO:
// tell tellers that there are no more clients
// so they should close, then then close the bank.
break;
}
sem_wait(&line);
for (int i = 0; i < 3; i++) {
if (tellers[i]->available) {
int client_id = clients[client_index]->id;
//printf("client_index = %d\n", client_index); // DEBUG
tellers[i]->assigned_client = clients[client_index++];
tellers[i]->available = false;
printf(
"Client %d goes to Teller %d\n",
client_id,
tellers[i]->id
);
sem_post(&wait_for_client[i]);
break;
}
}
//sem_post(&line); // Is this needed?
}
return EXIT_SUCCESS;
}
teller_t *
create_teller (pthread_t thread_id, int id, client_t *assigned_client) {
teller_t *t = (teller_t *) malloc(sizeof(teller_t));
if (t == NULL) {
printf("ERROR: Unable to allocate teller_t.\n");
exit(EXIT_FAILURE);
}
t->thread_id = thread_id;
t->id = id;
t->available = true;
t->assigned_client = assigned_client;
return t;
}
/* TODO: Malloc returns the same address everytime, fix this */
client_t *
create_client (pthread_t thread_id, int id, int operation, int amount) {
client_t *c = malloc(sizeof(client_t));
if (c == NULL) {
printf("ERROR: Unable to allocate client_t.\n");
exit(EXIT_FAILURE);
}
c->thread_id = thread_id;
c->id = id;
c->operation = operation;
c->amount = amount;
return c;
}
void *
run_teller (void *arg) {
teller_t *t = (teller_t *) arg;
printf("Teller %d is available\n", t->id);
while (1) {
/* tell the line that a teller is available */
sem_post(&line);
/* pass when the line assignes a client to this teller */
sem_wait(&wait_for_client[t->id]);
assert(t->assigned_client != NULL);
if (t->assigned_client->operation == WITHDRAW) {
}
else {
}
}
free(arg);
pthread_cancel(t->thread_id);
return NULL;
}
void *
run_client (void *arg) {
client_t *c = (client_t *) arg;
c->operation = rand() & 1;
printf(
"Client %d waits in line to make a %s\n",
c->id,
((c->operation == DEPOSIT) ? "Deposit" : "Withdraw")
);
free(arg);
pthread_cancel(c->thread_id);
return NULL;
}
Then I use malloc() on line 77 with the create_client() function to create 100 clients.
Not exactly, you create one object, then you spawn a thread that manages that object, run_client() and then repeat. But run_client() basically does nothing except free() your client object! So malloc is totally right returning the same address again, as it is now free memory.
It just happens that your client threads are faster than your main one. Your problem here is that you are freeing the objects from secondary threads while leaving the dangling pointers in the global pointer array. If you use that array for debugging purposes, then nothing is actually wrong here, but if you want to use the client objects somewhen in the future, then you should not free your clients in the first place.

Producer Consumer stuck in deadlock with consumer

I'm trying to implement the producer consumer with conditional variables so I can learn about synchronization. I'm using the github to guide me and solved some seg faults but now it seems that my consumer never gets executed or is stuck in deadlock. I'm not sure what could be the error. I have included printf statements in the producer to execute everytime it runs and it completes producing any string in messages.txt less than 5. The consumer though does not and is stuck in a deadlock.
#define max 5
int par = 0;
// Data structure for queue
struct queue
{
char *items; // array to store queue elements
int maxsize; // maximum capacity of the queue
int front; // front points to front element in the queue (if any)
int rear; // rear points to last element in the queue
int size; // current capacity of the queue
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
};
// Utility function to initialize queue
struct queue* newQueue(int size)
{
struct queue *pt = NULL;
pt = (struct queue*)malloc(sizeof(struct queue));
pt->items = (char*)malloc(size * sizeof(char));
pt->maxsize = size;
pt->front = 0;
pt->rear = -1;
pt->size = 0;
pthread_mutex_init(&pt->mutex, NULL);
pthread_cond_init(&pt->can_produce, NULL);
pthread_cond_init(&pt->can_consume, NULL);
return pt;
}
// Utility function to return the size of the queue
int size(struct queue *pt)
{
return pt->size;
}
// Utility function to check if the queue is empty or not
int isEmpty(struct queue *pt)
{
return !size(pt);
}
// Utility function to return front element in queue
char front(struct queue *pt)
{
if (isEmpty(pt))
{
//printf("UnderFlow\nProgram Terminated\n");
}
return pt->items[pt->front];
}
// Utility function to add an element x in the queue
void enqueue(struct queue *pt, char x)
{
if (size(pt) == pt->maxsize)
{
//printf("OverFlow\nProgram Terminated\n");
}
//printf("Inserting %c\t", x);
pt->rear = (pt->rear + 1) % pt->maxsize; // circular queue
pt->items[pt->rear] = x;
pt->size++;
//printf("front = %c, rear = %c\n", pt->front, pt->rear);
}
// Utility function to remove element from the queue
void dequeue(struct queue *pt)
{
if (isEmpty(pt)) // front == rear
{
//printf("UnderFlow\nProgram Terminated\n");
}
//printf("Removing %c\t", front(pt));
pt->front = (pt->front + 1) % pt->maxsize; // circular queue
pt->size--;
//printf("front = %d, rear = %c\n", pt->front, pt->rear);
}
void consumer_f(void *arg)
{
struct queue *pt = (struct queue*)arg;
while (par==0 && !isEmpty(pt))
{
pthread_mutex_lock(&pt->mutex);
if (pt->size == 0)
{ // empty
// wait for new items to be appended to the buffer
pthread_cond_wait(&pt->can_consume, &pt->mutex);
}
printf("%c", pt->front);
dequeue(pt);
pthread_cond_signal(&pt->can_produce);
pthread_mutex_unlock(&pt->mutex);
}
}
void producer_f(void *arg)
{
struct queue *pt = (struct queue*)arg;
char tmp;
FILE *fp;
fp = fopen("messages.txt", "r");
if (fp == NULL)
{
//fprintf(stderr, "error opening messages.txt");
return -1;
}
while ((tmp = fgetc(fp)) != EOF)
{
pthread_mutex_lock(&pt->mutex);
if (pt->size == max)
pthread_cond_wait(&pt->can_produce, &pt->mutex);
enqueue(pt, tmp);
printf("sent");
pthread_cond_signal(&pt->can_consume);
pthread_mutex_unlock(&pt->mutex);
}
par = 1; //denotes EOF for consumer */
}
int main()
{
printf("nop");
struct queue *pt = newQueue(5);
pthread_t producer;
pthread_t consumer;
printf("got here");
if (pthread_create(&producer, NULL, &producer_f, pt))
{
//fprintf(stderr, "Error creating producer thread\n");
return -1;
}
if (pthread_create(&consumer, NULL, &consumer_f, pt))
{
//fprintf(stderr, "Error creating consumer thread\n");
return -1;
}
if (pthread_join(producer_f, NULL))
{
//fprintf(stderr, "Error joining proucer thread\n");
return -1;
}
if (pthread_join(consumer_f, NULL))
{
//fprintf(stderr, "Error joinging consumer thread\n");
return -1;
}
return 0;
}
The consumer thread is not going to deadlock state but it is exiting without consuming as EOS is reached before consumer started consuming.
As you know, threads can get scheduled in a random way by OS (I mean, at least you can assume that they get scheduled in a random way). With this assumption, the producer might have started and read all bytes and enabled eos flag i.e. par=1. If consumer thread starts after par=1, it is not consuming at all.
To handle this case, you need to update the consumer_f() function.
while (1) //Run the loop always
{
pthread_mutex_lock(&pt->mutex);
if ((pt->size == 0) && (par == 0)) //Make sure that EOS is not already reached.
{ // empty
// wait for new items to be appended to the buffer
pthread_cond_wait(&pt->can_consume, &pt->mutex);
}
if(par && isEmpty(pt))
{
//If Array is empty and eos is reached, unlock and exit loop
pthread_mutex_lock(&pt->mutex);
break;
}
//Other code
}
Similarly, you need to enable eos flag inside mutex in producer_f().
while ((tmp = fgetc(fp)) != EOF)
{
//Your code
}
pthread_mutex_lock(&pt->mutex);
par = 1;
pthread_cond_signal(&pt->can_consume); //To make sure that consumer thread wakesup
pthread_mutex_unlock(&pt->mutex);
PS: pt->size == 0 can be replaced with isEmpty(pt) for more readability.

Where is my message queue producing a segmentation fault?

The message queue simply stops working when dealing with many many threads. It only seems to work okay with 10 threads, for exmaple. GDB tells me
Program received signal SIGSEGV, Segmentation fault.
__GI_____strtol_l_internal (nptr=0x0, endptr=endptr#entry=0x0, base=base#entry=10, group=group#entry=0, loc=0x7ffff78b0060 <_nl_global_locale>)
at ../stdlib/strtol_l.c:298
298 ../stdlib/strtol_l.c: No such file or directory.
But I have no idea what this means. The same code on Windows works fine but on linux it doesn't, which confuses me more.
You can see below how this queue works. It is a singly linked list with locking while receiving messages. Please help me find where I messed up.
typedef struct Message {
unsigned type;
unsigned code;
void *data;
} Message;
typedef struct MessageQueueElement {
Message message;
struct MessageQueueElement *next;
} MessageQueueElement;
typedef struct MessageQueue {
MessageQueueElement *first;
MessageQueueElement *last;
} MessageQueue;
MessageQueue mq;
pthread_mutex_t emptyLock, sendLock;
pthread_cond_t emptyCond;
void init() {
mq.first = malloc(sizeof(MessageQueueElement));
mq.last = mq.first;
pthread_mutex_init(&emptyLock, NULL);
pthread_mutex_init(&sendLock, NULL);
pthread_cond_init(&emptyCond, NULL);
}
void clean() {
free(mq.first);
pthread_mutex_destroy(&emptyLock);
pthread_mutex_destroy(&sendLock);
pthread_cond_destroy(&emptyCond);
}
void sendMessage(MessageQueue *this, Message *message) {
pthread_mutex_lock(&sendLock);
if (this->first == this->last) {
pthread_mutex_lock(&emptyLock);
this->last->message = *message;
this->last = this->last->next = malloc(sizeof(MessageQueueElement));
pthread_cond_signal(&emptyCond);
pthread_mutex_unlock(&emptyLock);
} else {
this->last->message = *message;
this->last = this->last->next = malloc(sizeof(MessageQueueElement));
}
pthread_mutex_unlock(&sendLock);
}
int waitMessage(MessageQueue *this, int (*readMessage)(unsigned, unsigned, void *)) {
pthread_mutex_lock(&emptyLock);
if (this->first == this->last) {
pthread_cond_wait(&emptyCond, &emptyLock);
}
pthread_mutex_unlock(&emptyLock);
int n = readMessage(this->first->message.type, this->first->message.code, this->first->message.data);
MessageQueueElement *temp = this->first;
this->first = this->first->next;
free(temp);
return n;
}
some test code:
#define EXIT_MESSAGE 0
#define THREAD_MESSAGE 1
#define JUST_A_MESSAGE 2
#define EXIT 0
#define CONTINUE 1
int readMessage(unsigned type, unsigned code, void *data) {
if (type == THREAD_MESSAGE) {
printf("message from thread %d: %s\n", code, (char *)data);
free(data);
} else if (type == JUST_A_MESSAGE) {
puts((char *)data);
free(data);
} else if (type == EXIT_MESSAGE) {
puts("ending the program");
return EXIT;
}
return CONTINUE;
}
int nThreads;
int counter = 0;
void *worker(void *p) {
double pi = 0.0;
for (int i = 0; i < 1000000; i += 1) {
pi += (4.0 / (8.0 * i + 1.0) - 2.0 / (8.0 * i + 4.0) - 1.0 / (8.0 * i + 5.0) - 1.0 / (8.0 * i + 6.0)) / pow(16.0, i);
}
char *s = malloc(100);
sprintf(s, "pi equals %.8f", pi);
sendMessage(&mq, &(Message){.type = THREAD_MESSAGE, .code = (int)(intptr_t)p, .data = s});
counter += 1;
char *s2 = malloc(100);
sprintf(s2, "received %d message%s", counter, counter == 1 ? "" : "s");
sendMessage(&mq, &(Message){.type = JUST_A_MESSAGE, .data = s2});
if (counter == nThreads) {
sendMessage(&mq, &(Message){.type = EXIT_MESSAGE});
}
}
int main(int argc, char **argv) {
clock_t timer = clock();
init();
nThreads = atoi(argv[1]);
pthread_t threads[nThreads];
for (int i = 0; i < nThreads; i += 1) {
pthread_create(&threads[i], NULL, worker, (void *)(intptr_t)i);
}
while (waitMessage(&mq, readMessage));
for (int i = 0; i < nThreads; i += 1) {
pthread_join(threads[i], NULL);
}
clean();
timer = clock() - timer;
printf("%.2f\n", (double)timer / CLOCKS_PER_SEC);
return 0;
}
--- EDIT ---
Okay I managed to fix the problem by changing the program a bit using semaphores. The waitMessage function doesn't have to be locked since it is accessed by only one thread and the values that it modifies does not clash with sendMessage.
MessageQueue mq;
pthread_mutex_t mutex;
sem_t sem;
void init() {
mq.first = malloc(sizeof(MessageQueueElement));
mq.last = mq.first;
pthread_mutex_init(&mutex, NULL);
sem_init(&sem, 0, 0);
}
void clean() {
free(mq.first);
pthread_mutex_destroy(&mutex);
sem_destroy(&sem);
}
void sendMessage(MessageQueue *this, Message *message) {
pthread_mutex_lock(&mutex);
this->last->message = *message;
this->last = this->last->next = malloc(sizeof(MessageQueueElement));
pthread_mutex_unlock(&mutex);
sem_post(&sem);
}
int waitMessage(MessageQueue *this, int (*readMessage)(unsigned, unsigned, void *)) {
sem_wait(&sem);
int n = readMessage(this->first->message.type, this->first->message.code, this->first->message.data);
MessageQueueElement *temp = this->first;
this->first = this->first->next;
free(temp);
return n;
}
Your waitMessage function is modifying this->first outside of any locking. This is a bad thing.
It's often not worth recreating things that are already provided for you by an OS. You're effectively trying to set up a pipe of Message structures. You could simply use an anonymous pipe instead (see here for Linux, or here for Windows) and write/read Message structures to/from it. There's also POSIX message queues which are probably a bit more efficient.
In your case with multiple worker threads you'd have to have a supplementary mutex semaphore to control which worker is trying to read from the pipe or message queue.

Producer and consumer with semaphores, multiple pthreads, 2 buffers

OK. So I have 2 buffers, implemented as heap. It has an init, insert, and delete function. The priority in this case does not matter though.
For each buffer, there are 3 producer threads producing an item (letter) and inserting it into a buffer. Three producers for a buffer holding uppercase letters, and three for a buffer holding lowercase letters. Obviously only 1 producer may append at a time to a buffer.
There are 10 consumer threads. These threads can access either buffer, but only up to 3 at a time per buffer, and not when a producer is producing. I am trying to instruct the threads to go first to the buffer with more items in it, then the other.
My problem is with deadlocks I believe. I am getting segmentation faults, so I assume that my semaphores are not working correctly (I have not implemented them correctly).
I first worked on this problem using a single buffer, with multiple consumers/producers and was able to get it to work. I have basically segmented the 1 buffer solution into multiple parts.
I'm using 3 semaphores for each buffer. A mutex, so only 1 item can take or append something per buffer. These are named mutex and mutex2. emptycount and fillcount are the counting sems used that start at the max_buffer_size and 0 respectively.
The consumers must take a letter from each buffer before consuming. The order in which they do this is not important, so I'm trying to tell each one to take something from the buffer with more in it first, then the other buffer. I've put a semaphore starting at 3 outside of the consumer code for each buffer. These are sems upper and lower, and are used to make sure only 3 consumers can enter any one buffer at a time.
Variables countUpper and countLower are supposed to count how many items are in each buffer at the time.
I'm sure my sems are placed incorrectly but i cannot find out where, I'm very new to threading. The queues are abstracted and in another file, but they work correctly. Also, I'm only producing lowercase letters atm, but that shouldn't matter.
Any help is appreciated.
#include "pq.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <semaphore.h>
#include <pthread.h>
#define MAX_CPRODUCER 3
#define MAX_LPRODUCER 3
#define MAX_CONSUMER 10
#define MAX_INT 2147483647
// Is the structure for the items produced by the producer thread
typedef struct product product_t;
struct product {
char item;
int PrdPID;
unsigned int sequence_num;
};
typedef struct pq_struct pq_struct_t;
struct pq_struct
{
pq_t lower;
pq_t upper;
};
int countUpper, countLower;
sem_t mutex, fillCount, emptyCount;
sem_t mutex2, fillCount2, emptyCount2;
sem_t lower, upper;
//Produce a letter to put in
product_t *produce(int pid, unsigned int count)
{
product_t *aProduct = malloc( sizeof(*aProduct) );
aProduct->item = (char)(rand() % 26 + 97); //To get ascii lowercase
aProduct->PrdPID = pid;
aProduct->sequence_num = count;
return aProduct;
}
//Append the letter to the buffer
void append(void *pq, product_t *aProduct)
{
//Insert letter into queue
//Key is irrelevant and always 1 for this program
pq_insert((pq_t*)pq, 1, (void*)aProduct);
//printf("Appended: ID = %d Count = %d Letter = %c\n", aProduct->PrdPID, aProduct->sequence_num, aProduct->item);
}
//Take a letter from buffer
product_t *take(void *pq)
{
//Delete from buffer and place in our consumer product
product_t *aProduct = (product_t*)pq_delete((pq_t*)pq);
return aProduct;
}
//Consume the letter
void consume(product_t *aProduct, product_t *theProduct, int id)
{
printf("Removing: ID = %d Count = %d Letter = %c\n", aProduct->PrdPID, aProduct->sequence_num, aProduct->item);
free(aProduct); //Takes place of consume
free(theProduct);
}
//Producer function for uppercase buffer
void *ProducerCapital (void *pq)
{
pq_t *upperPq = pq;
product_t *cProduct = NULL;
unsigned int count = 0;
for (;;)
{
cProduct = produce(1, ++count);
sem_wait(&emptyCount);
sem_wait(&mutex);
//Make new item
append(upperPq, cProduct);
++countUpper;
//
sem_post(&mutex);
sem_post(&fillCount);
}
pthread_exit (NULL);
}
//Producer thread for lowercase buffer
void *ProducerLower (void *pq)
{
pq_t *lowerPq = pq;
product_t *lProduct = NULL;
unsigned int count = 0;
for (;;)
{
lProduct = produce(1, ++count);
sem_wait(&emptyCount2);
sem_wait(&mutex2);
//Make new item
append(lowerPq, lProduct);
++countLower;
//
sem_post(&mutex2);
sem_post(&fillCount2);
}
pthread_exit (NULL);
}
void ConsumeUpper(pq_t *pqUpper, product_t *capProd)
{
sem_wait(&fillCount);
sem_wait(&mutex);
//Take Item
capProd = (product_t*)take((void*)pqUpper);
//
sem_post(&mutex);
sem_post(&emptyCount);
}
void ConsumeLower(pq_t *pqLower, product_t *lowProd)
{
sem_wait(&fillCount2);
sem_wait(&mutex2);
//Take Item
lowProd = (product_t*)take((void*)pqLower);
//
sem_post(&mutex2);
sem_post(&emptyCount2);
}
void *Consumer (void *pq)
{
pq_struct_t *sharePqs = (pq_struct_t*)pq;
product_t *capitalProduct = NULL;
product_t *lowerProduct = NULL;
for (;;)
{
if(countUpper < countLower)
{
sem_wait(&lower);
--countLower;
ConsumeLower(&sharePqs->lower, capitalProduct);
sem_post(&lower);
sem_wait(&upper);
--countUpper;
ConsumeUpper(&sharePqs->upper, capitalProduct);
sem_post(&upper);
}
else
{
sem_wait(&upper);
--countUpper;
ConsumeUpper(&sharePqs->upper, capitalProduct);
sem_post(&upper);
sem_wait(&lower);
--countLower;
ConsumeLower(&sharePqs->lower, capitalProduct);
sem_post(&lower);
}
consume(capitalProduct, lowerProduct , 2);
}
pthread_exit (NULL);
}
int main ()
{
//Create queue
pq_struct_t my_pqs;
//Initialize the queue
pq_init(&my_pqs.upper);
pq_init(&my_pqs.lower);
pthread_t cProducers[MAX_CPRODUCER];
pthread_t lProducers[MAX_LPRODUCER];
pthread_t consumers[MAX_CONSUMER];
srand(time(NULL)); // randomize random function call
int i, code;
countUpper = 0;
countLower = 0;
sem_init(&mutex, 0, 1);
sem_init(&fillCount, 0, 0);
sem_init(&emptyCount, 0, MAX_INT);
sem_init(&mutex2, 0, 1);
sem_init(&fillCount2, 0, 0);
sem_init(&emptyCount2, 0, MAX_INT);
sem_init(&lower, 0, 3);
sem_init(&upper, 0, 3);
for (i=0; i<MAX_CONSUMER; ++i)
{
printf ("In main: creating consumer thread %d\n", i);
code = pthread_create(&consumers[i], NULL, Consumer, (void *)&my_pqs);
if (code)
{
printf ("Error: pthread_create: %d\n", code);
exit (-1);
}
}
for (i=0; i<MAX_CPRODUCER; ++i)
{
printf ("In main: creating producer thread %d\n", i);
code = pthread_create(&cProducers[i], NULL, ProducerCapital, (void *)&my_pqs.upper);
if (code)
{
printf ("Error: pthread_create: %d\n", code);
exit (-1);
}
}
for (i=0; i<MAX_LPRODUCER; ++i)
{
printf ("In main: creating producer thread %d\n", i);
code = pthread_create(&lProducers[i], NULL, ProducerLower, (void *)&my_pqs.lower);
if (code)
{
printf ("Error: pthread_create: %d\n", code);
exit (-1);
}
}
pthread_exit (NULL);
}

Resources