Pthread Condition Variables on Linux, Odd Behaviour

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.

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!!!!!!!!
$

condition variables signaling

Because of the mutex locks, it doesn't matter if you do it before or after the signal, because the variable cannot be read until the mutex is unlocked.

Killing a pthread waiting on a condition variable

Do you have access to the queue, and control of the object schema for enqueued items? If so, define a queue object type that when de-queued, instructs the thread that is processing the item to exit gracefully.

Now, to shut down these threads, simply post a number of these "quit" objects to the HEAD of the queue that corresponds to the number of threads that are servicing the queue, and join on the threads.

This seems much cleaner than the "nuclear option" of pthread_cancel/kill.

What happens when pthread_cond_broadcast is called and multiple threads are awoken only to compete for the same mutex?

What happens when you call pthread_cond_broadcast() and multiple
threads wake up just to compete for the same mutex lock. One of the
threads takes the mutex lock but what happens to the other threads? Do
they go back to sleep? Or do they spin until the lock is available
again?

When you call pthread_cond_broadcast(), all threads then waiting on the specified condition variable stop doing so. All such threads will have passed (a pointer to) the same mutex to pthread_cond_wait(), else the behavior is undefined. Each thread that was unblocked will (re)acquire that mutex before returning successfully from pthread_cond_wait(). That may require some or even all of them to block, just as if they were all contending for the same mutex under any other circumstances. They do not spin, and they do not require any further interaction with the CV for them to resume, but each one will hold the mutex locked when it returns from pthread_cond_wait(), just as it did when it called that function.

Condition Variable in Shared Memory - is this code POSIX-conformant?

I can easily see how PTHREAD_PROCESS_SHARED can be tricky to implement on the OS-level (e.g. MacOS doesn't, except for rwlocks it seems). But just from reading the standard, you seem to have a case.

For completeness, you might want to assert on sysconf(_SC_THREAD_PROCESS_SHARED) and the return value of the *_setpshared() function calls— maybe there's another "surprise" waiting for you (but I can see from the comments that you already checked that SHARED is actually supported).

@JesperE: you might want to refer to the API docs at the OpenGroup instead of the HP docs.

Why does pthread_cond_signal not work?

I have answered a similar question here: pthread condition variables on Linux, odd behaviour.

The problem is that you wait before even testing the condition you want to wait for is true.
What happens is that thread 1 signals before thread 2 is waiting, therefore the signal is lost and thread 2 will be waiting forever.

In order to avoid this, first test what you want to wait for, then wait only if it's not here.

EDIT: Ok, here is a possible solution with only one mutex and one condtion (untested)

Thread 1:

pthread_mutex_lock(&mutex); 
while(thread_1_should_work == false){ // wait until the condition is satisfied
pthread_cond_wait(&cond, &mutex);
}

//at this point, we owe the mutex and we know thread_1_should_work is true;

// do work

thread_1_shoudl_work = false;
thread_2_should_work = true;

pthread_cond_broadcast(&cond); //wake up any waiting thread (if it's not their turn, they'll call wait again)
pthread_mutex_unlock(&mutex);

... and so on.



Related Topics



Leave a reply



Submit