Pthread Condition Variables Not Signalling Even Though Set to Pthread_Process_Shared

Pthread Condition Variables not Signalling even though set to PTHREAD_PROCESS_SHARED

You're truncating the shared memory for mutex and conditional variable unnecessarily in the son process. Since this part used to happen before fork() in the original code you were doing it only once for the couple. But in two separate processes you're initializing the shared memory with cross-process pthread mutex and conditional variable respectively in father and then destroying everything that pthreads put in there by using O_TRUNC in the son. The correct code is following:

father.c

#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_init(&mutexAttr);
pthread_mutexattr_setpshared(&mutexAttr, PTHREAD_PROCESS_SHARED);
pthread_mutex_init(mutex, &mutexAttr);

/* set condition shared between processes */
pthread_condattr_t condAttr;
pthread_condattr_init(&condAttr);
pthread_condattr_setpshared(&condAttr, PTHREAD_PROCESS_SHARED);
pthread_cond_init(condition, &condAttr);

/*************************************/


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);

exit(0);
}

son.c

#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, mode);

if (des_mutex < 0) {
perror("failure on shm_open on des_mutex");
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, mode);

if (des_cond < 0) {
perror("failure on shm_open on des_cond");
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_mutex_lock(mutex);
pthread_cond_signal(condition);
printf("son signaled\n");
pthread_mutex_unlock(mutex);
exit(0);
}

Testing:

1st terminal: $ ./father
father waits on condition

2nd terminal: $ ./son
son signaled

1st terminal: Signaled by son process, wake up!!!!!!!!
$

Unable to send signal using pthread_cond_signal to another process in C

Mutex shared among processes should be initialised by only one of them

The error is that you are initialising the mutex and the condition on both processes. However as they are shared, you should initialise them only on the consumer.

I also suggest not to mix the old System V shmget/shmat functions with the new POSIX shm_open.

I defined a new shared integer shint and which points to a shared memory initialised with shm_open and attached via mmap. shint is used as a flag to wait on the pthread condition.

In my view the producer, which starts after the consumer, doesn't need to truncate the memory segment and can open the shared memory with only O_RDWR. The producer takes the lock first and set shint to 1.

On the other hand the consumer, which starts first, has to create the shared memory segments and resize them with ftruncate. It also set the shared flag shint to 0 and wait on it. Also, before calling shm_open it is better to unlink (via shm_unlik) all used shared memory segments, to clean eventual errors on previous calls (for example if the code crash before unlinking).

I moved the initialisation of attributes for the mutex and condition at the beginning, because it seems more clear and correct.


Producer.c

int main()
{
int des_cond, des_msg, des_mutex;
int mode = S_IRWXU | S_IRWXG;

/* set mutex shared between processes */
pthread_mutexattr_t mutexAttr;
pthread_mutexattr_init(&mutexAttr);
pthread_mutexattr_setpshared(&mutexAttr, PTHREAD_PROCESS_SHARED);

/* set condition shared between processes */
pthread_condattr_t condAttr;
pthread_condattr_init(&condAttr);
pthread_condattr_setpshared(&condAttr, PTHREAD_PROCESS_SHARED);

des_mutex = shm_open(MUTEX, O_RDWR, mode);

if (des_mutex < 0) {
perror("failure on shm_open on des_mutex");
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_RDWR, mode);

if (des_cond < 0) {
perror("failure on shm_open on des_cond");
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);
}

int fd = shm_open(MESSAGE, O_RDWR, 0644);
if (fd < 0) {
perror("failure on shm_open on fd");
exit(1);
}

if(ftruncate(fd, sizeof(int)) == -1) {
perror("Error on ftruncate to sizeof ftruncate fd\n");
exit(-1);
}

int *shint;
shint = (int *) mmap(NULL, sizeof(int), PROT_WRITE|PROT_READ, MAP_SHARED, fd, 0);

if (shint == MAP_FAILED ) {
perror("Error on mmap on shint\n");
exit(1);
}


// set ot 0
*shint = 0;


int count=0;
while(count++<10)
{
pthread_mutex_lock(mutex);
*shint = 1;
pthread_mutex_unlock(mutex);

pthread_cond_signal(condition);
printf("signal sent to consumer, %d\n",count);

sleep(3);
}

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;
}

Consumer.c

int main()
{
int des_cond, des_msg, des_mutex;
int mode = S_IRWXU | S_IRWXG;

// Unlink first to clean
shm_unlink(MUTEX);
shm_unlink(OKTOWRITE);
shm_unlink(MESSAGE);

pthread_mutexattr_t mutexAttr;
pthread_mutexattr_init(&mutexAttr);
pthread_mutexattr_setpshared(&mutexAttr, PTHREAD_PROCESS_SHARED);

pthread_condattr_t condAttr;
pthread_condattr_init(&condAttr);
pthread_condattr_setpshared(&condAttr, PTHREAD_PROCESS_SHARED);


des_mutex = shm_open(MUTEX, O_CREAT | O_RDWR, 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, 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);
}

int fd = shm_open(MESSAGE, O_CREAT|O_RDWR, 0644);
if (fd < 0) {
perror("failure on shm_open on fd");
exit(1);
}

if(ftruncate(fd, 16) == -1) {
perror("Error on ftruncate to sizeof ftruncate fd\n");
exit(-1);
}
int *shint;
shint = (int *) mmap(NULL, sizeof(int), PROT_WRITE|PROT_READ, MAP_SHARED, fd, 0);

if (shint == MAP_FAILED ) {
perror("Error on mmap on shint\n");
exit(1);
}


*shint = 0;

if (pthread_mutex_init(mutex, &mutexAttr) != 0) {printf("Error initi mutex"); exit(111);}
if (pthread_cond_init(condition, &condAttr) != 0) {printf("Error initi cond"); exit(111);}

while(1)
{
printf("Receiver waits on for signal from hello1.c \n");

pthread_mutex_lock(mutex);
while(shint == 0)
pthread_cond_wait(condition, mutex);
printf("Waiting"); sleep(1);
pthread_mutex_unlock(mutex);

printf("Signal received, wake up!!!!!!!!\n");

break;
//reset
pthread_mutex_lock(mutex);
shared_stuff->conditionSatisfied=0;
pthread_mutex_unlock(mutex);
}

return 0;
}

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

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

pthread condition variables on Linux, odd behaviour

Have you set the PTHREAD_PROCESS_SHARED attribute on both your condvar and mutex?

For Linux consult the following man pages:

  • pthread_mutexattr_init (with sample)
  • pthread_mutexattr_setpshared
  • pthread_condattr_init
  • pthread_condattr_setpshared

Methods, types, constants etc. are normally defined in /usr/include/pthread.h, /usr/include/nptl/pthread.h.

Process-shared condition variable : how to recover after one process dies?

Ok, quoting posix pthread_mutex_lock() reference:

If mutex is a robust mutex and the process containing the owning thread terminated while holding the mutex lock, a call to pthread_mutex_lock() shall return the error value [EOWNERDEAD]. [...] In these cases, the mutex is locked by the thread but the state it protects is marked as inconsistent. The application should ensure that the state is made consistent for reuse and when that is complete call pthread_mutex_consistent(). If the application is unable to recover the state, it should unlock the mutex without a prior call to pthread_mutex_consistent(), after which the mutex is marked permanently unusable.

So in addition to alk's comment to robustly handle processes dying with the mutex locked we need to watch for EOWNERDEAD when calling pthread_mutex_lock() and pthread_cond_timedwait(), and call pthread_mutex_consistent() on it.

Something like:

if ((errno = pthread_cond_timedwait(&ticket->cond, &ticket->mutex, &ts)) == 0)
continue;
if (errno == EOWNERDEAD) /* Recover mutex owned by dead process */
pthread_mutex_consistent(&ticket->mutex);
else if (errno != ETIMEDOUT)
fail("pthread_cond_timedwait");

pthread_cond_wait never returning with EOWNERDEAD

EOWNERDEAD is a defined return value for pthread_mutex_lock(), not for pthread_cond_wait(). This is perhaps because CVs do not have owners in the same sense that mutexes do. In any case, there is no reason to expect a wait on a CV ever to return EOWNERDEAD.

Moreover, a thread waiting for on CV specifically does not hold the associated mutex for the duration, and will not try to reacquire it until the wait is over. If the CV isn't signaled then there isn't any reason for the thread even to notice that the mutex owner is dead.*

The bottom line is that POSIX does not define robust condition variables in the same sense that it defines robust mutexes, and using a robust mutex with a CV does not impart robustness on the CV. So,

how can i detect the termination/crashing/exiting of the "owner" process in the blocking pthread_cond_wait call?

You can't. Condition variables do not have that capability.

If you're not satisfied with the resulting risk of failure of one process causing a hang in another then you have several options, among them:

  • use threads instead of processes;
  • use pthread_cond_timedwait() instead of pthread_cond_wait(), and abort or attempt recovery if the wait times out;
  • use a second thread in the waiting process to monitor the state of the other cooperating process(es), and to take corrective action when necessary.

*And that is potentially a more fundamental issue here. Is the process that dies while holding the mutex the (only) one that you expect to signal the CV? If so, then you're never going to see that signal in your failure case.

pthread conditions and process termination

It seems that the p2 process is waiting on the conditional variable eternally since the p1 process has no chance to send a notification being terminated by ctrl-c. As you and other people have already mentioned pthread conditional variable does not "know" about its original process termination.

If you cannot use another inter process communication features and still insist on shared mutex and conditional variable, I would think about trapping the signal.



Related Topics



Leave a reply



Submit