Hi guys, little trouble with mutex locking. I have most of the logic down for this, but I think I've become tangled up in the process. The idea is to have a "producer" making pieces of data (there is a finite amount that can be made, and when this is reached, the producer ends) and a set of "consumers" eating the data. There can only be so much data present at any one time, so when that limit is reached, the producer pauses producing. When there is no data left, the consumers stop consuming.
My problem comes when I find that the code as it stands works some of the time, and others, the mutex locking causes the code to pause, each thread waiting for the lock to unlock "from what I can tell". I was hoping I could get a couple of pairs of fresh eyes on this, as I've been looking at it all week with limited success. I've also used a couple of defines at the start for my own benefit, to reduce the apparent complexity of the code.
Code:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
typedef enum {FALSE,TRUE} boolean;
#define NUMCONSUMERS 2
#define NUMPRODUCERS 1
#define PACKETS 10
#define tryMainlock pthread_mutex_trylock(&dataMutex)
#define takeMainlock pthread_mutex_lock(&dataMutex)
#define releaseMainlock pthread_mutex_unlock(&dataMutex)
#define waitMainlock pthread_cond_wait(&dataPresentCondition, &dataMutex);
#define signalMainlock pthread_cond_signal(&dataPresentCondition);
#define trydatalock pthread_mutex_trylock(&IsthereDataleft)
#define takedatalock pthread_mutex_lock(&IsthereDataleft)
#define releasedatalock pthread_mutex_unlock(&IsthereDataleft)
pthread_mutex_t dataMutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t dataPresentCondition = PTHREAD_COND_INITIALIZER;
pthread_mutex_t IsthereDataleft = PTHREAD_MUTEX_INITIALIZER;
int sharedData=0; //amount of data present
int BUFFER = 5;
int dataleft=PACKETS;
int finish = 1;
void *Producer(void *threadid){
int rc;
printf("Producer Thread %x: Created\n", (int)threadid);
while (1)
{
printf("Producer %x: Attempting to take lock...\n", (int)threadid);
rc = takeMainlock; //take hold of the lock
if (!dataleft) //If there's no data left to add to the stream, close the thread
{
printf("Producer Thread %x: Completed, exiting...\n", (int)threadid);
rc = releaseMainlock;
rc = signalMainlock;
rc = takedatalock;
finish = 0;
rc = releasedatalock;
return NULL;
}
while (sharedData == BUFFER)
{
printf("Buffer is full\n");
rc = releaseMainlock;
rc = pthread_cond_broadcast(&dataPresentCondition);
if (rc)
{
printf("........s ........ed up yo.\n");
}
printf("Producer Thread %x: Waiting for lock...\n", (int) threadid);
rc = waitMainlock;
if (rc)
{
printf("........s ........ed yo.\n");
}
}
printf("Producer %x: Lock Acquired-----------------------------------------\n", (int)threadid);
sharedData++;
dataleft--;
rc = releaseMainlock;
rc = signalMainlock;
if (rc)
{
printf("........s ........ed yo.\n");
}
printf("Producer %x: Releasing Lock++++++++++++++++++\n", (int)threadid);
}
}
void *Consumer(void *threadid){
int rc;
printf("Consumer Thread %x: Created\n", (int)threadid);
while (1)
{
printf("Consumer %x: Attempting to take Main lock...\n",(int) threadid);
rc = takeMainlock; //take hold of main lock
printf("Consumer %x: Lock Acquired**********************************************\n", (int)threadid);
while (sharedData == 0) //if the buffer is empty
{
printf("Consumer %x: Shared Data is 0+++++++++++++++++++++++++++++\n", (int) threadid);
rc = releaseMainlock;
rc = signalMainlock;
if (rc)
{
printf("........s ........ed yo.\n");
}
rc = takedatalock;
if (!finish)
{
rc = releaseMainlock;
rc = signalMainlock;
rc = releasedatalock;
printf("Consumer %x: Completed. Exiting...\n",(int) threadid);
return NULL;
}
rc = releasedatalock;
printf("Consumer %x: Data still left to process. Waiting for unlock...\n", (int) threadid);
rc = waitMainlock;
printf("Consumer %x: Waiting Period Finished\n", (int) threadid);
if (rc)
{
printf("........s ........ed yo.\n");
}
}
printf("Consumer %x: Lock Acquired**********************************************\n", (int)threadid);
sharedData--;
rc = releaseMainlock;
rc = pthread_cond_broadcast(&dataPresentCondition);
if (rc)
{
printf("........s ........ed yo.\n");
}
printf("Consumer %x: Releasing Lock+++++++++++++++++\n", (int)threadid);
}
}
int main(int argc, char **argv)
{
int rc; //error checking
int i;
pthread_t consumer[NUMCONSUMERS];
pthread_t producer[NUMPRODUCERS];
rc = takedatalock; //lock to determine whether there's any point waiting for data
for (i=0; i <NUMPRODUCERS; i++) { //Build up the producers
rc = pthread_create(&producer[i], NULL, Producer, (void *)i);
if (rc)
printf("Error building Producer Thread: %x\n", i);
}
for (i=0; i <NUMCONSUMERS; i++) { //Build up the consumers
rc = pthread_create(&consumer[i], NULL, Consumer, (void *)i);
if (rc)
printf("Error building Consumer Thread: %x\n", i);
}
printf("All Producers and Consumers created\n");
for (i=0; i <NUMPRODUCERS; i++) { //Join up the producers
rc = pthread_join(producer[i], NULL);
if (rc)
printf("Error: Producer %x: Failed to join\n", i);
}
rc = releasedatalock; //producers finished, no data left to make
printf("datalock closed, consumers finishing...\n");
rc = pthread_cond_broadcast(&dataPresentCondition);
for (i=0; i <NUMCONSUMERS; i++) { //Join up the consumers
rc = pthread_join(consumer[i], NULL);
printf("Consumer %x: Joined correctly!\n", i);
if (rc)
printf("Error: Consumer %x: Failed to join\n", i);
}
rc = pthread_mutex_destroy(&dataMutex);
rc = pthread_cond_destroy(&dataPresentCondition);
rc = pthread_mutex_destroy(&IsthereDataleft);
printf("All Threads finished. Exiting....\n");
return 0;
}
As I said, its probably something small, but I can't quite see it anymore for all the other code. I get the feeling that its something to do with the buffer, as there are times where the Consumers will simply never try to use the data, and when the producer reaches the buffer, it is also stuck.
There is also a chance there is a separate problem when it comes to the ending of the threads as well, as it can be the case that one consumer will exit having found there is nothing left to consume, and the rest will be trapped in limbo.
I've attached a downloadable version to this post as well. Cheers Lads.