0

I'm trying to code the producer/consumer problem using semaphores. I have 3, 1 acting as a mutex, and another 2 for the buffer which the producers and consumers can add/remove from. When adding/removing from the buffer I use the binary semaphore to lock/unlock it so that the global variables aren't subject to any race conditions. The produce semaphore represents how many spots are available in the buffer (how many things can be put into the buffer) while the consumer semaphore represents how many things can be removed from the buffer. I think my logic is wrong cause I always reach a deadlock. Also when I removed the produce and consume semaphores just to test whether or not the program does what its supposed to do, I still get race conditions even though the binary semaphore should be blocking that. What am I doing wrong?

enter code here
#include <stdio.h>  
#include <sys/types.h>  
#include <unistd.h> 
#include <stdlib.h>     
#include <sys/wait.h>   
#include <pthread.h>
#include </usr/include/semaphore.h>

#define MAXITEMS 100    
#define PRODUCER_NO 5   
#define NUM_PRODUCED 5 

void *producer_function (void);
void *consumer_function (void);
void add_buffer (long i);
long get_buffer ();


long sum_value = 0; 
long finished_producers;
long buffer[MAXITEMS];  
int size = 0;       
int front, rear = 0;    

pthread_t producerThread[5];
pthread_t consumerThread;
sem_t mutex, produce, consume;

int main(void)
{
    int i = 0;
    srand (time(NULL));
    sem_init (&mutex, 0, 1);
    sem_init (&produce, 0, 100);
    sem_init (&consume, 0, 0);
    for (i = 0; i < 5; i++)
    {
        pthread_create (&producerThread[i], NULL, (void *) producer_function, NULL);
    }
    pthread_create (&consumerThread, NULL, (void *) consumer_function, NULL);
    for (i = 0; i < 5; i++)
    {
        pthread_join (producerThread[i], NULL);
    }
    pthread_join (consumerThread, NULL);
    return(0);
}

void *producer_function(void)
{   
    long counter = 0;
    long producer_sum = 0L;
    while (counter < NUM_PRODUCED) 
    {
        sem_wait (&mutex);
        sem_wait (&produce);
        long rndNum = rand() % 10; 
        producer_sum += rndNum;
        add_buffer (rndNum);
        sem_post (&consume);
        counter++;
        if (counter == NUM_PRODUCED)
        {
            finished_producers++;
        }
        sem_post (&mutex);
        usleep(1000);
    }

    printf("--+---+----+----------+---------+---+--+---+------+----\n");
    printf("The sum of produced items for this producer at the end is: %ld \n", producer_sum);
    printf("--+---+----+----------+---------+---+--+---+------+----\n");
    return(0);
}

void *consumer_function (void)
{   
    while (1) 
    {
        sem_wait (&mutex);
        sem_wait (&consume);
        long readnum = get_buffer();
        sem_post (&produce);
        sum_value += readnum;
        sem_post (&mutex);
        //printf ("%ld\n", sum_value);
        if ((finished_producers == PRODUCER_NO) && (size == 0))
        {
            printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
            printf("The sum of the all produced items at the end is: %ld \n", sum_value);
            printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
            break;
        }
    }
}

void add_buffer(long i){
    buffer[rear] = i;
    rear = (rear+1) % MAXITEMS;
    size++;
}

long get_buffer(){
    long v;
    v = buffer[front];
    front = (front+1) % MAXITEMS;
    size--;
    return v;
}
user2929779
  • 359
  • 5
  • 13
  • 3
    You may misunderstand what [semaphores](http://en.wikipedia.org/wiki/Semaphore_(programming)) are for: to protect access to data, not to implement data or to tie consumers or producers to it. In your case, only one semaphore is necessary to control access to your buffer (which is the shared resource). All consumers and producers then will work with that one semaphore and queue up for exclusive access to your buffer, while only one of the threads can manipulate the buffer in whichever way necessary. (Also, in your semaphore.h include, remove the absolute path, that's *bad* practice.) – Jens Oct 28 '13 at 21:36
  • I get what semaphores are for and I know only 1 is needed but regardless I don't see what's wrong with my code. – user2929779 Oct 28 '13 at 22:01
  • While you're in the process of fixing things, fix your thread procedures. they are required to be `void* func(void*)`. In both your cases, the param list is wrong, and the consumer thread isn't even returning anything at all. And the casts are both incorrect, and unnecessary if done right. – WhozCraig Oct 28 '13 at 22:08
  • Yeah I've never been great with pointers. And I'm pretty sure the consumer thread isn't returning anything cause the program hits deadlock and size never reaches zero. – user2929779 Oct 28 '13 at 22:14
  • Part of that is the finished_producers handling. You're essentially trying to protected both the buffer and that counter with the same mutex, and the way this is organized, you can't. Is it mandatory you use only semaphore for this? (its not a deal breaker, just a pain, is why I ask). – WhozCraig Oct 28 '13 at 22:22
  • Passing functions to `pthread_create` that have a wrong signature is a serious programming error. Depending on the calling convention of the architecture this may pollute a register, a position on the stack which the called function regards as safe. Same for the return value. Not providing a return value where the system expects one will cause the system to interpret some random register content as a pointer. – Jens Gustedt Oct 28 '13 at 22:39
  • Yeah I have to use semaphores. The way I see it is, 5 threads are gonna go through the producer function. Because finished_producers is a global variable it should be incremented to 5 by the time each thread completes. But it's not. That's where I'm stuck. – user2929779 Oct 28 '13 at 22:42
  • @JensGustedt I don't see how this is an error? I'm not using the return value for anything. – user2929779 Oct 28 '13 at 22:46
  • @user2929779, your system might use the return value, you can't know. – Jens Gustedt Oct 28 '13 at 22:59
  • @JensGustedt How would I correct that? This is the only way I've been taught. – user2929779 Oct 28 '13 at 23:02
  • And then, never use system functions like `sem_wait` without checking the return value for errors. These functions may wakeup when your thread receives an interrupt. The man page lists all possible error paths. Also `usleep` is deprecated since 2001, use nanosleep instead. – Jens Gustedt Oct 28 '13 at 23:03
  • http://stackoverflow.com/questions/19642938/how-to-provide-a-sequence-of-interleaving-threads-to-show-that-a-code-breaks-and – Sebastien Oct 29 '13 at 01:26

1 Answers1

0

user2929779,

I think its essential to not have the mutex locked, when waiting for a consume notification in the consumer, or vice versa a produce notification in the producer. Imagine you're getting blocked because of waiting for a consume notification, and no producer was able to publish such a notification then your consumer keeps the mutex locked and no producer ever gets the chance to produce a new item...

So the order is important here: 1.) First wait for notification from remote side 2.) lock mutex 3.) modify global data 4.) release mutex 5.) notify remote side

Try this instead:

void *producer_function(void)
{   
    long counter = 0;
    long producer_sum = 0L;
    while (counter < NUM_PRODUCED) 
    {

        sem_wait (&produce);
        sem_wait (&mutex);
        long rndNum = rand() % 10;
        producer_sum += rndNum;
        add_buffer (rndNum);
        counter++;
        if (counter == NUM_PRODUCED)
        {
            finished_producers++;
        }
        sem_post (&mutex);      
        sem_post (&consume);
        usleep(1000);
    }

    printf("--+---+----+----------+---------+---+--+---+------+----\n");
    printf("The sum of produced items for this producer at the end is: %ld \n", producer_sum);
    printf("--+---+----+----------+---------+---+--+---+------+----\n");
    return(0);
}

void *consumer_function (void)
{   
    while (1) 
    {

        sem_wait (&consume);
        sem_wait (&mutex);
        long readnum = get_buffer();
        sum_value += readnum;
        if ((finished_producers == PRODUCER_NO) && (size == 0))
        {
            printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
            printf("The sum of the all produced items at the end is: %ld \n", sum_value);
            printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
            break;
        }
        sem_post (&mutex);
        sem_post (&produce);

    //printf ("%ld\n", sum_value);
    }
    return(0);
}

P.S. For now ignoring return values of system calls just to show example implementation...

P.S.S. See also pseudo code on wiki http://en.wikipedia.org/wiki/Producer%E2%80%93consumer_problem#Using_semaphores ...