0

I recently learned Semaphore and Consumer Producer problem in class and encountered this question: I used two semaphores to indicate the empty and the full in a buffer plus a mutex.

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>


pthread_mutex_t mutex;


sem_t empty;
sem_t full;

int fill =0 ;

void *producer(void *param);
void *consumer(void *param);

void* producer(void *param)
{
    while(1) {
        sem_wait(&empty);
        pthread_mutex_lock(&mutex);
        fill++;
        pthread_mutex_unlock(&mutex);
        sem_post(&full);
    }
}


void *consumer(void *param)
{
    while(1){
        sem_wait(&full);
        pthread_mutex_lock(&mutex);
        fill--;
        pthread_mutex_unlock(&mutex);
        sem_post(&empty);
    }
}
void* dynamic_output(void* ptr){
    while(1){

    printf("fill = :%d\n",fill);

    sleep(1);
}}


int main()
{
  pthread_mutex_init(&mutex, NULL);
  sem_init(&empty, 0, 5);
  sem_init(&full, 0, 0);
  pthread_t gid1,oid1;
  pthread_t dy;

  pthread_create(&dy,NULL,dynamic_output,NULL);

  pthread_create(&gid1,NULL,&producer,NULL);
  pthread_create(&oid1,NULL,&consumer,NULL);

  pthread_join(gid1,NULL);
  pthread_join(oid1,NULL);

    return 0;
}

here I simply put a variable named 'fill' inside the producer and the consumer. Since I initiated the empty semaphore to 5, I expected 'fill' will be floating between 0 - 5 but when I printed them out I found the weird result:

fill = :0
fill = :19
fill = :446
fill = :112
fill = :156
fill = :-564
fill = :-345
fill = :-744
fill = :-1293

I really don't understand how fill could've become a negative number or number bigger than 5. I thought implementing the semaphores could ensure that the bounded buffer( here I didn't really have a buffer).

Thanks in advance.

Joji
  • 4,703
  • 7
  • 41
  • 86
  • Undefined behavior for unsynchronized, non readonly, non atomic access to an object from multiple threads. – EOF Oct 24 '17 at 23:07
  • there is only one shared variable, which is fill and I used a mutex. why do you say non atomic access to an object from multiple threads? – Joji Oct 25 '17 at 00:55
  • No, `fill` is **not** adequately protected by the mutex. Hint: If you wish to protect an object with a mutex, you must make **all** accesses to the object be done while holding the mutex. – EOF Oct 25 '17 at 07:57
  • @EOF so could you specify in this case what's missing in terms of accesses to the object? Because I really can't understand the problem here – Joji Oct 28 '17 at 04:32
  • You access `fill` in `dynamic_output` without taking the mutex, so the access to `fill` in `dynamic_output` is unsynchronized with concurrent, non-readonly access to `fill` in `consumer` and `producer`. Since `fill` is not atomic, this is undefined behavior. – EOF Oct 28 '17 at 06:08
  • @EOF so do you mean I should put `pthread_mutex_lock(&mutex);` and `pthread_mutex_unlock(&mutex); ` in `dynamic_output ` ? I thought there I didn't modify the variable so there is no race condition in `dynamic_output`. but anyway Anyway I figured out the problem myself. It's simply because sem_init' is deprecated on Mac OS X, so I compiled the exact same code on Linux and it worked out perfectly. As expected 'fill' is floating from 0 to 5. – Joji Oct 28 '17 at 06:27
  • It doesn't matter that `dynamic_output` doesn't modify `fill`. Unsynchronized access is undefined behavior if *any* of the threads modify the value and the object is not atomic. You wouldn't need the `pthread_mutex_lock()` if *all* of the threads only read from `fill`. Also, just because the code *appears to* work does not in any way mean it is correct. – EOF Oct 28 '17 at 07:30
  • sorry I still don't understand. as you said " Unsynchronized access is undefined behavior if any of the threads modify the value and the object is not atomic" but here all of the threads modifying the value has atomic operations for the shared variable `fill`. So I think it would be nice you could really explicitly point out which piece of code is wrong and how to correct it. – Joji Oct 28 '17 at 17:51
  • Accessing an object while holding a mutex is not atomic. When I write "atomic", I mean the C11 `_Atomic` specifier and operations from `stdatomic.h`. – EOF Oct 28 '17 at 18:15

0 Answers0