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.
Related
I have to use a buffer of size max_cache_req_len to read in the value received from mq_receive. Here is my code that is receiving a value from shared memory and then placing it on a queue:
size_t received_bytes = 0;
char buffer[MAX_CACHE_REQUEST_LEN];
received_bytes = 0;
memset(&buffer, 0, MAX_CACHE_REQUEST_LEN);
received_bytes = mq_receive(mq, buffer, MAX_CACHE_REQUEST_LEN, NULL);
if (received_bytes != -1)
{
item_type *item = malloc(sizeof(item_type));
item->path = buffer;
pthread_mutex_lock(&queue_lock);
steque_enqueue(&queue, item);
pthread_cond_signal(&queue_cond);
pthread_mutex_unlock(&queue_lock);
}
Here is my code that is taking the item off the queue, and placing it into a char* value. When I print the path, I get "".
void *worker(void *arg)
{
item_type *new_item;
char *path;
int fd;
while (1)
{
pthread_mutex_lock(&queue_lock);
while (steque_isempty(&queue) == 1)
pthread_cond_wait(&queue_cond, &queue_lock);
new_item = steque_pop(&queue);
path = new_item->path;
free(new_item);
new_item = NULL;
pthread_mutex_unlock(&queue_lock);
fd = simplecache_get(path);
sleep(cache_delay);
printf("%d\n", fd);
printf("%s\n", path);
// MAKE WORKER THREAD TAKE
if (fd == CACHE_FAILURE)
{
}
else
{
}
}
}
If I hardcode something like:
item->path = "buffer";
Then it prints buffer from within my worker function. This is a multithreaded application, I am just unsure what to do with my char[size] array to transform it into a char* and allow it to transfer.
Nutshell:
(char*)&char[size] queued -> queue turns it into a void* -> popped off queue, turned into a char* and value is lost
I am trying to write a code that is using threads in order to find files that contain a specific string in their name.
My code works most of the times. in some specific cases, threads are not able to acquire locks for some reason.
I tried debugging really hard (using prints, as you can see in the code) but I couldn't find the problem.
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <dirent.h>
#include <string.h>
void* threadFunction(void* searchTerm);
bool scanDirName(char * path, char * searchTerm);
int numOfThreads;
pthread_mutex_t qlock;
pthread_cond_t cond;
int count;
int matchingFiles;
struct Node {
char* data;
struct Node* next;
};
// Two global variables to store an address of front and rear nodes.
struct Node* front = NULL;
struct Node* rear = NULL;
// To Enqueue an integer
void Enqueue(char* x) {
/* printf("\nhere\n");*/
struct Node* temp = (struct Node*)malloc(sizeof(struct Node));
temp->data =x;
temp->next = NULL;
if(front == NULL && rear == NULL){
front = rear = temp;
pthread_cond_signal(&cond);
return;
}
rear->next = temp;
rear = temp;
}
// To Dequeue an integer.
char* Dequeue() {
struct Node* temp = front;
if(front == NULL) {
return NULL;
}
char* data;
data = front->data;
if(front == rear) {
front = rear = NULL;
}
else {
front = front->next;
}
// printf("\nfreeing %p, %s\n", temp, temp->data);
free(temp);
return data;
}
void* threadFunction(void* st) {
bool isFinished;
isFinished = false;
pthread_mutex_lock(&qlock);
while (true) {
char *filepath;
char *searchTerm;
searchTerm = (char *) st;
filepath = Dequeue();
pthread_mutex_unlock(&qlock);
if (filepath == NULL) {
printf("%ld waiting for lock \n",(long) pthread_self());
pthread_mutex_lock(&qlock);
count++;
if (isFinished) {
printf("%ld waking u up, we found %d items!\n",(long) pthread_self(), matchingFiles);
pthread_cond_broadcast(&cond);
if (count == numOfThreads) {
printf("Thread exited: %ld\n", (long) pthread_self());
pthread_mutex_unlock(&qlock);
pthread_exit((void*)0);
}
}
isFinished = false;
printf("%ld going to sleep\n",(long) pthread_self());
pthread_cond_wait(&cond, &qlock);
printf("%ld Woke up, try to compare %d == %d\n",(long) pthread_self(), count, numOfThreads);
if (count == numOfThreads) {
printf("Thread exited: %ld\n", (long) pthread_self());
pthread_mutex_unlock(&qlock);
pthread_exit((void*)1);
}
printf("%ld compare failed \n",(long) pthread_self());
count--;
}
else {
printf("%ld deq 1 item \n",(long) pthread_self());
isFinished = scanDirName(filepath, searchTerm);
}
}
}
bool scanDirName(char * path, char * searchTerm){
DIR * d = opendir(path); // open the path
char* str3;
if(d==NULL) return false; // if was not able return
;
struct dirent * dir; // for the directory entries
while ((dir = readdir(d)) != NULL) // if we were able to read somehting from the directory
{
printf("%ld STARTED A ROUND!\n",(long) pthread_self());
if(dir-> d_type == DT_DIR){ //
if (dir->d_type == DT_DIR && strcmp(dir->d_name, ".") != 0 & strcmp(dir->d_name, "..") != 0) // if it is a directory
{
str3 = malloc((1+strlen("/")+ strlen(path)+ strlen(dir->d_name))*sizeof(char));
if (!str3){
return false;
}
strcpy(str3, path);
strcat(str3, "/");
strcat(str3, dir->d_name);
// printf("\n---\n%s\n---\n",str3);
printf("%ld waiting for lock in func \n",(long) pthread_self());
pthread_mutex_lock(&qlock);
printf("%ld wake threads \n",(long) pthread_self());
Enqueue(str3);
pthread_cond_signal(&cond);
printf("%ld enq \n",(long) pthread_self());
pthread_mutex_unlock(&qlock);
printf("%ld locks gone \n",(long) pthread_self());
}
}
else if(dir-> d_type == DT_REG){ //
if(strstr(dir->d_name, searchTerm)){
matchingFiles++;
/*printf("%s/%s\n", path, dir->d_name);*/
}
}
printf("%ld finished A ROUND!\n",(long) pthread_self());
}
printf("%ld finished scanning!\n",(long) pthread_self());
fflush(stdout);
closedir(d); // finally close the directory
if (count == numOfThreads-1) {
return true;
}
return false;
}
int main(int argc, char* argv[]){
count = 0;
pthread_mutex_init(&qlock, NULL);
pthread_cond_init(&cond, NULL);
matchingFiles = 0;
if (argc != 4){
printf("ERROR\n");
exit(1);
}
char* rootSearchDir = argv[1];
char* searchTerm = argv[2];
int threadsNumber = atoi(argv[3]);
pthread_t threadsCollection[threadsNumber];
Enqueue(rootSearchDir);
numOfThreads = threadsNumber;
int i;
for (i=0; i<threadsNumber; i++){
if(pthread_create(&threadsCollection[i], NULL, threadFunction, (void*)searchTerm)) {
fprintf(stderr, "Error creating thread\n");
pthread_mutex_destroy(&qlock);
return 1;
}
}
int rc;
for (i=0; i<threadsNumber; i++){
rc = pthread_join((threadsCollection[i]), NULL);
if(rc) {
fprintf(stderr, "Error joining thread, %d\n", rc);
pthread_mutex_destroy(&qlock);
return 1;
}
}
}
I have attached all of the code (maybe it is important) but I suspect the problem is in scanDirName or threadFunction.
--- EDIT ---
I found the problem. I had a logical problem with the locks, fixed it.
Thanks all!
if filepath = Dequeue() returns a non-null; then your next iteration of this loop will call Dequeue() without holding &qlock. If you checked the return values, you would notice this.
scanDirName relies on count to set its return value; however it isn’t protected by qlock { see #1 }
If I read your code correctly, you are attempting to make a sort of dispatch mechanism, where names are emitted from scanDir() into a queue, and pulled from the queue, where a fresh scanDir() is spun up for each item. The handling of count is an attempt to detect whether the Dequeue failed because there is nothing left to do, or everyone is busy doing scanDirs.
There is a bit easier of a mechanism: use two queues. Scandir dumps entries into say “NameQ”, and thread function pulls names out of NameQ, dumping them into WorkQ. When a thread runs out of NameQ items, it looks into WorkQ for fresh ones to dispatch. Then you just have to keep track of how many threads are in scanDir(). If both queues are empty, and number of threads in scanDir is zero, there is nothing more to do. Your Basic logic ends up looking like this:
void* threadFunction(void* st) {
static int nreader = 0;
int ncount = 0, wcount = 0;
char *searchTerm;
searchTerm = (char *) st;
Menter(&qlock);
while (nreader || !(empty(&Names) && empty(&Work))) {
char *filepath;
filepath = Dequeue(&Names);
if (filepath) {
ncount++;
Enqueue(&Work, filepath);
} else if ((filepath = Dequeue(&Work)) != NULL) {
wcount++;
nreader++;
Mexit(&qlock);
scanDirName(filepath, searchTerm);
Menter(&qlock);
nreader--;
cv_wake(&cond);
} else {
cv_wait(&cond, &qlock);
}
}
Mexit(&qlock);
printf("%p: %d, %d items\n", pthread_self(), ncount, wcount);
return NULL;
}
Mexit, Menter are error checking covers for the posix funs, and a lot easier on the eyes than all that redundant typing...
static int menter(pthread_mutex_t *m, int line) {
if (pthread_mutex_lock(m) != 0) {
fprintf(stderr, "%p:%d Mutex lock failed!\n", pthread_self(),line);
abort();
}
DEBUG_LOCK("%d locked\n", line);
return 0;
}
#define Menter(x) menter((x), __LINE__)
static int mexit(pthread_mutex_t *m, int line) {
DEBUG_LOCK("%d unlocked\n", line);
if (pthread_mutex_unlock(m) != 0) {
fprintf(stderr, "%p:%d Mutex unlock failed!\n", pthread_self(),line);
abort();
}
return 0;
}
#define Mexit(x) mexit((x), __LINE__)
Why does this error occur when I run my code?
error: RUN FINISHED; Segmentation fault: 11; real time: 3s; user: 0ms; system: 0m
I'm creating 10 threads where each thread is a ticket seller. There is a 10by10 array that holds the seats of the tickets. Depending on the type of the ticket-seller a person will be sold that specific seat.
Is the issue with the pthreads?
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
/*
* File: ticketsellers.c
* Author: iantheflyinghawaiian
*
* Created on July 4, 2016, 11:27 AM
*/
#include <stdio.h>
#include <pthread.h>
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
// seller thread to serve one time slice (1 minute)
int theatre[10][10] ;
struct node
{
int info;
struct node *ptr;
}*front,*rear,*temp,*front1;
int count = 0;
/* Create an empty queue */
void create()
{
front = rear = NULL;
}
/* Returns queue size */
void queuesize()
{
printf("\n Queue size : %d", count);
}
/* Enqueing the queue */
void enq(int data)
{
if (rear == NULL)
{
rear = (struct node *)malloc(1*sizeof(struct node));
rear->ptr = NULL;
rear->info = data;
front = rear;
}
else
{
temp=(struct node *)malloc(1*sizeof(struct node));
rear->ptr = temp;
temp->info = data;
temp->ptr = NULL;
rear = temp;
}
count++;
}
/* Displaying the queue elements */
void display()
{
front1 = front;
if ((front1 == NULL) && (rear == NULL))
{
printf("Queue is empty");
return;
}
while (front1 != rear)
{
printf("%d ", front1->info);
front1 = front1->ptr;
}
if (front1 == rear)
printf("%d", front1->info);
}
/* Dequeing the queue */
void deq()
{
front1 = front;
if (front1 == NULL)
{
printf("\n Error: Trying to display elements from empty queue");
return;
}
else
if (front1->ptr != NULL)
{
front1 = front1->ptr;
printf("\n Dequed value : %d", front->info);
free(front);
front = front1;
}
else
{
printf("\n Dequed value : %d", front->info);
free(front);
front = NULL;
rear = NULL;
}
count--;
}
/* Returns the front element of queue */
int frontelement()
{
if ((front != NULL) && (rear != NULL))
return(front->info);
else
return 0;
}
/* Display if queue is empty or not */
void empty()
{
if ((front == NULL) && (rear == NULL))
printf("\n Queue empty");
else
printf("Queue not empty");
}
//Ticket Seller
void * sell(char *seller_type)
{
char seller_type1;
seller_type1 = *seller_type;
int i;
i = 0;
while (i == 0);
{
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond, &mutex);
pthread_mutex_unlock(&mutex);
// Serve any buyer available in this seller queue that is ready
// now to buy ticket till done with all relevant buyers in their queue
//………………
// Case statements for seller_types
switch(seller_type1)
{
case 'H' :
printf("Seller type: H\n");
i = 1;
break;
case 'M' :
printf("Seller type: M\n");
i = 1;
break;
case 'L' :
printf("Seller type: L\n");
i = 1;
break;
}
}
return NULL; // thread exits
}
void wakeup_all_seller_threads()
{
pthread_mutex_lock(&mutex);
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mutex);
}
int main()
{
int i, N;
pthread_t tids[10];
printf("Enter N value of Customers: ");
scanf("%d", &N);
printf("Number of Customers: %d", N);
char seller_type;
// Create necessary data structures for the simulator.
// Create buyers list for each seller ticket queue based on the
// N value within an hour and have them in the seller queue.
// Create 10 threads representing the 10 sellers.
seller_type = 'H';
pthread_create(&tids[i], NULL, sell, &seller_type);
seller_type = 'M';
for (i = 1; i < 4; i++)
pthread_create(&tids[i], NULL, sell, &seller_type);
seller_type = 'L';
for (i = 4; i < 10; i++)
pthread_create(&tids[i], NULL, sell, &seller_type);
// wakeup all seller threads
wakeup_all_seller_threads();
// wait for all seller threads to exit
for (i = 0 ; i < 10 ; i++)
pthread_join(&tids[i], NULL);
// Printout simulation results
//…………
exit(0);
}
As already assumed by #2501: the culprit causing the segfault was the uninitialized variable i in main.
I took the liberty to write a minimal example for your pthread-creating, with the addition of some printf`s.
It compiles without warnings with
gcc -W -Wall threadtest.c -o threadtest -pthread
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
struct seller_type {
char st;
int tid;
};
void *sell(void *arg)
{
struct seller_type *seller_type1 = arg;
int i;
i = 0;
printf("thread #%d: seller_type1->st = %c\n", seller_type1->tid,
seller_type1->st);
// no semicolon after while()
while (i == 0) {
switch (seller_type1->st) {
case 'H':
printf("Seller type: H\n");
i = 1;
break;
case 'M':
printf("Seller type: M\n");
i = 1;
break;
case 'L':
printf("Seller type: L\n");
i = 1;
break;
}
}
printf("thread #%d: Work done\n", seller_type1->tid);
return NULL;
}
int main()
{
int i = 0;
struct seller_type *seller_type1;
pthread_t tids[10];
seller_type1 = calloc(10, sizeof(struct seller_type));
// All error handling ommitted! Yes, ALL!
seller_type1[0].st = 'H';
seller_type1[0].tid = 0;
pthread_create(&tids[0], NULL, sell, &seller_type1[0]);
for (i = 1; i < 4; i++) {
seller_type1[i].st = 'M';
seller_type1[i].tid = i;
pthread_create(&tids[i], NULL, sell, &seller_type1[i]);
}
for (i = 4; i < 10; i++) {
seller_type1[i].st = 'L';
seller_type1[i].tid = i;
pthread_create(&tids[i], NULL, sell, &seller_type1[i]);
}
puts("All threads created");
// wait for all seller threads to exit
for (i = 0; i < 10; i++) {
pthread_join(tids[i], NULL);
printf("Thread %d joined\n", i);
}
puts("All threads joined");
exit(EXIT_SUCCESS);
}
I hope you can build upon that.
You have a data race when you pass seller_type to the newly created thread, while at the same time modify the variable in the main.
The type of the function passed to pthread_create must be: void*(*)(void*), not the type you're using.
Function pthread_join requires a type pthread_t as the first argument, not a pointer to that type, which is what you're passing to it.
The variable i in the main isn't initialized, and is used to index the array tids, in the first pthread_create call.
All of those four problems cause undefined behavior by themselves. I suspect the last one causes the crash.
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.
I'm trying to implement a program that uses a thread to read data from a file and write to a buffer of an arbitrary size, while two other threads read info from this buffer. Everything works fine, except for when I specify a buffer size of 1. When I do that, everything locks up. I'm more or less adapting the classic "consumer/producer" example from here Here's my code:
Struct I'm using:
struct prodcons {
char** buffer; pthread_mutex_t lock;
pthread_cond_t notempty; pthread_cond_t notfull;
int readpos, writepos, finished;
};
My "add to buffer" thread
static void buf_add(struct prodcons *b, char* data) {
/* Add the data after locking the buffer */
pthread_mutex_lock(&b-> lock);
printf("Reader adding %s\n", data);
int err;
/*Wait until buffer is not full*/
while ((b->writepos + 1) % numlines == b->readpos) {
err = pthread_cond_wait(&b->notfull, &b->lock);
if (err != 0) { fprintf(stderr, "cond wait");}
}
/* Needed to stop writes */
if (data != NULL) {
b->buffer[b->writepos] = strdup(data);
} else {
//fprintf(stderr, "End of file reached, adding NULL\n");
b->buffer[b->writepos] = NULL;
}
/* Increments the writing position */
(*b).writepos++;
if ((*b).writepos >= numlines) {
printf("Resetting write position\n");
(*b).writepos = 0;
}
/* Unlock */
pthread_cond_signal(&b->notempty);
pthread_mutex_unlock(&b->lock);
}
Here's what output looks like:
Reader adding 64.233.173.85
And then it just hangs. It's clear that it's never getting beyond that first while loop. It works with any other size, but not with 1. what would be the best way of implement a fix for this? If this helps, here is my "get from buffer" method.
static void *read_from_buffer(struct prodcons *b) {
pthread_mutex_lock(&b -> lock);
/* We have to wait for the buffer to have something in it */
while ((*b).writepos == (*b).readpos) {
pthread_cond_wait(&b->notempty, &b->lock);
}
/* Set the thread delay */
thread_delay.tv_sec = threaddelay / 100000;
thread_delay.tv_nsec = 1000*threaddelay%100000;
char *t = NULL;
/* Read the data and advance the reader pointer */
if ((*b).buffer[(*b).readpos] != NULL) {
t = (char*)malloc(strlen ((*b).buffer[(*b).readpos] ) + 1);
strcpy(t, (*b).buffer[(*b).readpos]);
printf("Consumer %u reading from buffer: got %s\n", (unsigned int)pthread_self(), t);
/*At this point, we should probably check is FQDN*/
if (strcmp(t, "-1") == 0) {
(*b).finished = 1;
} else {
nanosleep(&thread_delay, &thread_delay_rem);
check_cache(t, &cache);
}
}
/* We have to adjust the reading position */
(*b).readpos++;
if ( (*b).readpos >= numlines) {
(*b).readpos = 0;
}
/*Need to signal and unlock */
pthread_cond_signal (&b->notfull);
pthread_mutex_unlock(&b->lock);
return t;
}
I'm sure there is a fairly simple fix to handle this edge case, but I can't seem to figure it out. Any suggestions would be much appreciated!
EDIT: I also initialize my buffer like so:
static void init(struct prodcons *temp) {
(*temp).buffer = (char**)malloc(numlines * sizeof(char*));
Not having stepped through your code, but you're writing a terminating NUL '\0' byte, and that would take up an entire 1-byte buffer. The writer waits forever for space in the buffer.
while ((b->writepos + 1) % numlines == b->readpos) { /* always satisfied */