0

I am a complete newbie and I have been coding producer-consumer problem with fixed buffer size what I observed for buffer-size 1 is very different from what I expected. I print "x-->" when I produce x and "-->x" when x is consumed. The output I get is like this: \

0 --->
1 --->
2 --->
---> 0
---> 1
---> 2
3 --->
4 --->
5 --->
---> 3
---> 4
---> 5

I am confused about how 1,2,3 reproduced at once and then 1,2,3 consumed at once can someone explain, please.

Here is my code

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#define BUFFER_SIZE 1
#define OVER (-1)


int filled = 0;

struct prodcons {
    int buffer[BUFFER_SIZE];      /* the actual data */
    pthread_mutex_t lock;         /* mutex ensuring exclusive access to buffer */
    int readpos, writepos;        /* positions for reading and writing */
    pthread_cond_t notempty;      /* signaled when buffer is not empty */
    pthread_cond_t notfull;       /* signaled when buffer is not full */
};

void init(struct prodcons * b)
{
    pthread_mutex_init(&b->lock, NULL);
    pthread_cond_init(&b->notempty, NULL);
    pthread_cond_init(&b->notfull, NULL);
    b->readpos = 0;
    b->writepos = 0;
}

void put(struct prodcons * b, int data)
{
    pthread_mutex_lock(&b->lock);
    if(BUFFER_SIZE == 1) {
        if(filled) {
            pthread_cond_wait(&b->notfull, &b->lock);
        }
        b->buffer[0] = data;
        filled = 1;
    }
    else {
        /* Wait until buffer is not full */
        while ((b->writepos + 1) % BUFFER_SIZE == b->readpos) {
            pthread_cond_wait(&b->notfull, &b->lock);
            /* pthread_cond_wait reacquired b->lock before returning */
        }
        /* Write the data and advance write pointer */
        b->buffer[b->writepos] = data;
        b->writepos++;
        if (b->writepos >= BUFFER_SIZE) b->writepos = 0;
        /* Signal that the buffer is now not empty */
    }

    pthread_cond_signal(&b->notempty);
    pthread_mutex_unlock(&b->lock);
}

int get(struct prodcons * b)
{
    int data = 0;
    pthread_mutex_lock(&b->lock);
    if(BUFFER_SIZE == 1) {
        if(!filled) {
            pthread_cond_wait(&b->notempty, &b->lock);
        }
        data = b->buffer[0];
        filled = 0;
    }
    else {
        /* Wait until buffer is not empty */
        while (b->writepos == b->readpos) {
            pthread_cond_wait(&b->notempty, &b->lock);
        }
        /* Read the data and advance read pointer */
        data = b->buffer[b->readpos];
        b->readpos++;
        if (b->readpos >= BUFFER_SIZE) b->readpos = 0;
        /* Signal that the buffer is now not full */
    }
    pthread_cond_signal(&b->notfull);
    pthread_mutex_unlock(&b->lock);
    return data;
}

struct prodcons buffer;

void * producer(void * data)
{
    int n;
    for (n = 0; n < 10000; n++) {
        printf("%d --->\n", n);
        put(&buffer, n);
    }
    put(&buffer, OVER);
    return NULL;
}

void * consumer(void * data)
{
    int d;
    while (1) {
        d = get(&buffer);
        if (d == OVER) break;
        printf("---> %d\n", d);
    }
    return NULL;
}
int main(void)
{
    pthread_t th_a, th_b;
    void * retval;
    init(&buffer);
    /* Create the threads */
    pthread_create(&th_a, NULL, producer, 0);
    pthread_create(&th_b, NULL, consumer, 0);
    /* Wait until producer and consumer finish. */
    pthread_join(th_a, &retval);
    pthread_join(th_b, &retval);
    return 0;
}
Nikhil Mishra
  • 31
  • 1
  • 5
  • 1
    What do you mean by "at once"? Perhaps it would be clearer if you also described what you expect the output to be like. – kaylum Feb 18 '21 at 05:43
  • 1
    It may be simply buffering in the log output. To check, change `printf("...")` to `fprintf(stderr, "...")`. The standard error output stream is designed to avoid buffering, so error messages show up immediately. – Harun Feb 18 '21 at 05:46
  • 1
    You forgot to use [fflush(3)](https://man7.org/linux/man-pages/man3/fflush.3.html) in your code, and you could use [usleep(3)](https://man7.org/linux/man-pages/man3/usleep.3.html) to slow it down – Basile Starynkevitch Feb 18 '21 at 06:01
  • but "at once" I mean how can more than 1 items get produced consecutively without any item being consumed in between in a buffer with a buffer size of 1(this was just an illusion created by the printf statements an is not really it happening). Yeah but I realized that print() statements are out of critical section so they might not be synchronized. The expected answer was that of Answer of @Erdal Küçük – Nikhil Mishra Feb 18 '21 at 14:22

1 Answers1

0

The output is not guaranteed as you expect it. I implemented a timestamp, which shows, when the put and get methods are called (time is relative to when init was called):

[ 44491 ns] 0 --->
[115427 ns] 1 --->
[153189 ns] ---> 0
[178376 ns] 2 --->
[182891 ns] 3 --->
[183518 ns] ---> 1
[188542 ns] ---> 2
[198661 ns] 4 --->
[203461 ns] ---> 3
[206636 ns] ---> 4
[204180 ns] 5 --->
[212146 ns] 6 --->
[221019 ns] ---> 5
[224146 ns] ---> 6
[221877 ns] 7 --->
[230976 ns] 8 --->
[232130 ns] 9 --->
[232347 ns] ---> 7
[237920 ns] ---> 8
[239051 ns] ---> 9
[239312 ns] 10 --->
[244770 ns] 11 --->
[245918 ns] 12 --->
[246106 ns] ---> 10
...

You should know, that stdout is shared by the two threads, so how the locking is implemented there, who knows.

If you want synchronized output, you have to put printf into the synchronized sections (after accessing your buffer in the put and get methods).

Then you get the output:

[ 31900 ns] 0 --->
[ 77228 ns] ---> 0
[ 85722 ns] 1 --->
[ 90959 ns] ---> 1
[ 95490 ns] 2 --->
[ 99591 ns] ---> 2
[103447 ns] 3 --->
[107385 ns] ---> 3
[111325 ns] 4 --->
[115215 ns] ---> 4
[119143 ns] 5 --->
[123066 ns] ---> 5
[126962 ns] 6 --->
[130860 ns] ---> 6
[134561 ns] 7 --->
[138427 ns] ---> 7
[142188 ns] 8 --->
[146115 ns] ---> 8
[149797 ns] 9 --->
[153534 ns] ---> 9
[157856 ns] 10 --->
[161631 ns] ---> 10
...
Erdal Küçük
  • 4,810
  • 1
  • 6
  • 11