Share condition variable & mutex between processes: does mutex have to locked before?

40,557

Solution 1

To be shareable between processes a mutex needs to be initialised accordingly via a properly initialised attribute: http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutexattr_setpshared.html

#include <pthread.h>

...

pthread_mutex_t * pmutex = NULL;
pthread_mutexattr_t attrmutex;

/* Initialise attribute to mutex. */
pthread_mutexattr_init(&attrmutex);
pthread_mutexattr_setpshared(&attrmutex, PTHREAD_PROCESS_SHARED);

/* Allocate memory to pmutex here. */

/* Initialise mutex. */
pthread_mutex_init(pmutex, &attrmutex);

/* Use the mutex. */

/* Clean up. */
pthread_mutex_destroy(pmutex);
pthread_mutexattr_destroy(&attrmutex); 

(error checking left out for the sake of this example's readability)

The same applies to a condition variable which should be shared between processes: http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_condattr_setpshared.html

#include <pthread.h>

...

pthread_cond_t * pcond = NULL;
pthread_condattr_t attrcond;

/* Initialise attribute to condition. */
pthread_condattr_init(&attrcond);
pthread_condattr_setpshared(&attrcond, PTHREAD_PROCESS_SHARED);

/* Allocate memory to pcond here. */

/* Initialise condition. */
pthread_cond_init(pcond, &attrcond);

/* Use the condition. */

/* Clean up. */
pthread_cond_destroy(pcond);
pthread_condattr_destroy(&attrcond); 

(error checking left out for the sake of this example's readability)


Also see this answer: https://stackoverflow.com/a/2390670/694576

Solution 2

Waiting for a condition should be preceded by a while statement, like this:

pthread_mutex_lock(mutex);
while(!conditionSatisfied)
    pthread_cond_wait(condition, mutex);
pthread_mutex_unlock(mutex);

while signaling should be done in the following way:

pthread_mutex_lock(mutex);
conditionSatisfied = true;
pthread_cond_signal(condition);
pthread_mutex_unlock(mutex);

Solution 3

yes, mutex have to be locked before pthread_cond_wait, but doesn't have to for pthread_cond_signal. If you look back at your code, you see that the mutex will be unlocked twice, which is a sign of error... it is also possible for the child to call unlock on a mutex which has been destroyed by the parent...

Btw sleeping doesn't guarantee that the parent will execute first. To ensure this you will need ... a condition variable...

Share:
40,557

Related videos on Youtube

SagittariusA
Author by

SagittariusA

Updated on June 27, 2020

Comments

  • SagittariusA
    SagittariusA almost 4 years

    I need to some little help to understand how to use condition variables in C to resolve an exercise. Here is a little example:

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <pthread.h>
    #include <string.h>
    #include <errno.h>
    #include <sys/types.h>
    #include <sys/mman.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    
    #define OKTOWRITE "/oktowrite"
    #define MESSAGE "/message"
    #define MUTEX "/lock"
    
    int main(int argc, char** argv)
    {
      pthread_cond_t* condition;
      pthread_mutex_t *mutex;
      char* message;
      int des_cond, des_msg, des_mutex;
      int mode = S_IRWXU | S_IRWXG;
    
      des_mutex = shm_open(MUTEX, O_CREAT | O_RDWR | O_TRUNC, mode);
    
      if (des_mutex < 0)
      {
        perror("failure on shm_open on des_mutex");
        exit(1);
      }
    
      if (ftruncate(des_mutex, sizeof(pthread_mutex_t)) == -1)
      {
        perror("Error on ftruncate to sizeof pthread_cond_t\n");
        exit(-1);
      }
    
      mutex = (pthread_mutex_t*) mmap(NULL, sizeof(pthread_mutex_t),
          PROT_READ | PROT_WRITE, MAP_SHARED, des_mutex, 0);
    
      if (mutex == MAP_FAILED )
      {
        perror("Error on mmap on mutex\n");
        exit(1);
      }
    
      pthread_mutex_init(mutex, NULL );
    
      des_cond = shm_open(OKTOWRITE, O_CREAT | O_RDWR | O_TRUNC, mode);
    
      if (des_cond < 0)
      {
        perror("failure on shm_open on des_cond");
        exit(1);
      }
    
      if (ftruncate(des_cond, sizeof(pthread_cond_t)) == -1)
      {
        perror("Error on ftruncate to sizeof pthread_cond_t\n");
        exit(-1);
      }
    
      condition = (pthread_cond_t*) mmap(NULL, sizeof(pthread_cond_t),
          PROT_READ | PROT_WRITE, MAP_SHARED, des_cond, 0);
    
      if (condition == MAP_FAILED )
      {
        perror("Error on mmap on condition\n");
        exit(1);
      }
    
      pthread_cond_init(condition, NULL );
    
      if (!fork())
      {
        sleep(3);
        pthread_mutex_lock(mutex);
        pthread_cond_signal(condition);
        pthread_mutex_unlock(mutex);
        printf("son signaled\n");
        exit(0);
      }
      else
      {
        printf("wait on condition\n");
    
        pthread_mutex_lock(mutex);
        pthread_cond_wait(condition, mutex);
        pthread_mutex_unlock(mutex);
    
        printf("Signaled by son process, wake up\n");
    
        pthread_mutex_destroy(mutex);
        pthread_cond_destroy(condition);
    
        shm_unlink(OKTOWRITE);
        shm_unlink(MESSAGE);
        shm_unlink(MUTEX);
    
        return 0;
      }
    }
    

    The problem is that the father of the process keeps on being locked, even after son's signaling. Everything is in shared memory (using shm_open and mmap) so the condition should be the same for both the processes. Am I maybe making a mistake by locking the mutex before calling wait or signal?

    EDIT: Thanks to all who helped me. Here's the right code with the CRITICAL parts marked:

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <pthread.h>
    #include <string.h>
    #include <errno.h>
    #include <sys/types.h>
    #include <sys/mman.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    
    #define OKTOWRITE "/condwrite"
    #define MESSAGE "/msg"
    #define MUTEX "/mutex_lock"
    
    int main(int argc, char** argv) {
    
    pthread_cond_t* condition;
    pthread_mutex_t* mutex;
    char* message;
    int des_cond, des_msg, des_mutex;
    int mode = S_IRWXU | S_IRWXG;
    
    des_mutex = shm_open(MUTEX, O_CREAT | O_RDWR | O_TRUNC, mode);
    
    if (des_mutex < 0) {
        perror("failure on shm_open on des_mutex");
        exit(1);
    }
    
    if (ftruncate(des_mutex, sizeof(pthread_mutex_t)) == -1) {
        perror("Error on ftruncate to sizeof pthread_cond_t\n");
        exit(-1);
    }
    
    mutex = (pthread_mutex_t*) mmap(NULL, sizeof(pthread_mutex_t),
            PROT_READ | PROT_WRITE, MAP_SHARED, des_mutex, 0);
    
    if (mutex == MAP_FAILED ) {
        perror("Error on mmap on mutex\n");
        exit(1);
    }
    
    des_cond = shm_open(OKTOWRITE, O_CREAT | O_RDWR | O_TRUNC, mode);
    
    if (des_cond < 0) {
        perror("failure on shm_open on des_cond");
        exit(1);
    }
    
    if (ftruncate(des_cond, sizeof(pthread_cond_t)) == -1) {
        perror("Error on ftruncate to sizeof pthread_cond_t\n");
        exit(-1);
    }
    
    condition = (pthread_cond_t*) mmap(NULL, sizeof(pthread_cond_t),
            PROT_READ | PROT_WRITE, MAP_SHARED, des_cond, 0);
    
    if (condition == MAP_FAILED ) {
        perror("Error on mmap on condition\n");
        exit(1);
    }
    
    
             /* HERE WE GO */
    /**************************************/
    
        /* set mutex shared between processes */
    pthread_mutexattr_t mutexAttr;
    pthread_mutexattr_setpshared(&mutexAttr, PTHREAD_PROCESS_SHARED);
    pthread_mutex_init(mutex, &mutexAttr);
    
    /* set condition shared between processes */
    pthread_condattr_t condAttr;
    pthread_condattr_setpshared(&condAttr, PTHREAD_PROCESS_SHARED);
    pthread_cond_init(condition, &condAttr);
    
        /*************************************/
    
    if (!fork()) {
    
        sleep(10);
    
        pthread_mutex_lock(mutex);
        pthread_cond_signal(condition);
        printf("son signaled\n");
        pthread_mutex_unlock(mutex);
        exit(0);
    }
    
    else {
    
        printf("father waits on condition\n");
    
         pthread_mutex_lock(mutex);
         pthread_cond_wait(condition, mutex);
         pthread_mutex_unlock(mutex);
    
         printf("Signaled by son process, wake up!!!!!!!!\n");
    
        pthread_condattr_destroy(&condAttr);
        pthread_mutexattr_destroy(&mutexAttr);
        pthread_mutex_destroy(mutex);
        pthread_cond_destroy(condition);
    
        shm_unlink(OKTOWRITE);
        shm_unlink(MESSAGE);
        shm_unlink(MUTEX);
    
    }
    
    return 0;
    
    }
    
    • alk
      alk over 10 years
    • alk
      alk over 10 years
      Also set the attribute for the conditon variables to be shared. Also please ee my answer.
    • SagittariusA
      SagittariusA over 10 years
      Thank you...I've made it-..maybe. I'll post the right code and sign your answer as THE ANSWER!
    • alk
      alk over 10 years
      If adding new code to your question, do not delete the old code, as this would make the answers already posted to the old question useless or at least difficult to underatand (without the old question's context). Also please take care of properly formatting (mainly indenting) code to be posted here. Thank you.
    • alk
      alk over 10 years
      As a final note: You should really add error checking to all of the relevant system calls your code issues.
    • SagittariusA
      SagittariusA over 10 years
      Ehm I did not delete the old code...in fact I can see both the correct that the first I wrote. For error checking, do you mean the ones related to condition variable and mutex? Because shm_open, mmap etc are all checked, jf they return a value < 0
    • alk
      alk over 10 years
      I wrote my comment old/new code before you applied your update. For the error checking I referred to the pthread_* calls as well as fork(). Each call that is defined that it may fail will fail one day sooner or later. Out of these I'd consider at least those relevant to test which my leave over unusable data if they failed.
    • stdout
      stdout about 7 years
      I didn't get one point above. (Let's assume) We first grab the shared mutex lock in the parent process and start wait on a condition which is supposed to be signalled by the child process. However, child process has to have that mutex (before signalling the condition) which is already held by the parent process. So, I don't get how deadlock doesn't happen here. Can someone please explain to me?
    • stdout
      stdout about 7 years
      Nevermind. I found my answer. "pthread_cond_wait releases the mutex and it waits till condition cond_t is signaled".
    • SagittariusA
      SagittariusA about 7 years
      You're right. I did not get it at once too that thing...
  • WhozCraig
    WhozCraig over 10 years
    The signal doesn't need to be done with the mutex latched (assuming the waiter is properly managing spurious wake-up, which it always should). The rest of this I agree with. Obviously the latch is required for modifying the predicate, however. But the last two lines can be reversed.
  • SagittariusA
    SagittariusA over 10 years
    I've tried to remove the lock/unclock mutex before signal but it still doesn't work... :(
  • SagittariusA
    SagittariusA over 10 years
    Sorry guys...I don't really understand the problem. Maybe, isn't it possible for condition variables to be shared between processes but only between threads?
  • Trung Nguyen
    Trung Nguyen over 7 years
    Now, I am using C++11, So I have a question that can C++11 mutex and conditon do that? Because I cannot search anywhere writing about IPC feature of C++11
  • Victor Sergienko
    Victor Sergienko over 6 years
    The link is dead. This is "Advanced Linux Programming" by by Mark Mitchell, Alex Samuel, Jeffrey Oldham - right? It is googlable, but I don't see anything that can be made a permanent link. Looks a bit dated too. Covers /proc but not /sys.
  • Dení
    Dení over 5 years
    Unfortunately, standard C++ sync primitives don't handle this case. Even std::mutex::native_handle won't help here, since an attribute has to be initialized prior to the primitive construction.
  • Dení
    Dení over 5 years
    Another question, how to make sure that the mutex gets unlocked in case of process termination?
  • alk
    alk over 5 years
    @Denis: The question about how to handle the case that a process holding a mutex dies is worth to be posed separately. To start diving into this non trivial case you might like to read this: pubs.opengroup.orga/onlinepubs/9699919799/functions/…
  • Dení
    Dení over 5 years
    @alk I was replying to Trung Nguyen Thanks for the link, I'm looking at the robust attribute.