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

3

28

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;

}
Grandeur answered 2/12, 2013 at 9:46 Comment(10)
possible duplicate of Do pthread Mutexs work across threads if in shared memory?Charlsiecharlton
Also set the attribute for the conditon variables to be shared. Also please ee my answer.Charlsiecharlton
Thank you...I've made it-..maybe. I'll post the right code and sign your answer as THE ANSWER!Grandeur
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.Charlsiecharlton
As a final note: You should really add error checking to all of the relevant system calls your code issues.Charlsiecharlton
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 < 0Grandeur
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.Charlsiecharlton
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?Antiscorbutic
Nevermind. I found my answer. "pthread_cond_wait releases the mutex and it waits till condition cond_t is signaled".Antiscorbutic
You're right. I did not get it at once too that thing...Grandeur
C
36

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://mcmap.net/q/503835/-do-pthread-mutexes-work-across-threads-if-in-shared-memory

Charlsiecharlton answered 2/12, 2013 at 10:46 Comment(5)
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++11Meson
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.Colet
Another question, how to make sure that the mutex gets unlocked in case of process termination?Colet
@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/…Charlsiecharlton
@Charlsiecharlton I was replying to Trung Nguyen Thanks for the link, I'm looking at the robust attribute.Colet
B
10

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);
Brundisium answered 2/12, 2013 at 10:2 Comment(2)
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.Menadione
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.Vedic
J
4

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...

Jewelljewelle answered 2/12, 2013 at 10:1 Comment(2)
I've tried to remove the lock/unclock mutex before signal but it still doesn't work... :(Grandeur
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?Grandeur

© 2022 - 2024 — McMap. All rights reserved.