pthread conditions and process termination

Viewed 3881

I have a process-shared pthread condition (with associated mutex). What would happen if a process waiting on this condition (using pthread_cond_wait() or pthread_cond_timedwait()) gets terminated? Can this condition still be used by other processes?

In my scenario process #1 waits on the condition and gets terminated. Process #2 at some point sees that it's the only one now using the condition and calls pthread_cond_destroy().

What i m seeing is that pthread_cond_destroy() simply hangs. Has anyone encountered the same problem?

From the man page of pthread_cond_destroy() it says that destroying the condition on which some thread is still waiting results in undefined behavior. In my case nobody is waiting anymore when process #2 calls pthread_cond_destroy() because the waiting process #1 was terminated, but apparently the condition itself still thinks there is a waiting thread.

Is there any way around this problem?

Edit:

Per request, i post the sample programs (i reversed p1 & p2 here):

p1.cpp:

#include <sys/types.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>

struct MyCond {
    pthread_mutex_t m;
    pthread_cond_t c;
};

int main()
{
    pthread_mutexattr_t ma;
pthread_mutexattr_init(&ma);
pthread_mutexattr_setpshared(&ma, PTHREAD_PROCESS_SHARED);

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

int fd = shm_open("/test_cond_p", O_RDWR|O_CREAT, 0666);
ftruncate(fd, sizeof(MyCond));

MyCond *c = (MyCond *)mmap(NULL, sizeof(MyCond),
    PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
//close (fd);

pthread_mutex_init(&c->m, &ma);
pthread_cond_init(&c->c, &ca);
printf("Inited MyCond, %x\n", c);

puts("Press Enter to continue");
fgetc(stdin);

    int r = pthread_cond_signal(&c->c);
    printf("After pthread_cond_signal, r=%d\n", r);

puts("Before pthread_cond_destroy");
r = pthread_cond_destroy(&c->c);
printf("After pthread_cond_destroy, r=%d\n", r);
r = pthread_mutex_destroy(&c->m);
printf("After pthread_mutex_destroy, r=%d\n", r);

munmap(c, sizeof(MyCond));
shm_unlink("/test_cond_p");

return 0;
}

p2.cpp:

#include <sys/types.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/time.h>

struct MyCond {
pthread_mutex_t m;
pthread_cond_t c;
};

int main()
{
int fd = shm_open("/test_cond_p", O_RDWR, 0666);

MyCond *c = (MyCond *)mmap(NULL, sizeof(MyCond),
    PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
//close (fd);

pthread_mutex_lock(&c->m);
puts("Before pthread_cond_wait");
int r = pthread_cond_wait(&c->c, &c->m);
printf("After pthread_cond_wait, r=%d\n", r);

munmap(c, sizeof(MyCond));
return 0;
}

Run p1 first, then run p2, after it says "before pthread_cond_wait", Ctrl-C it. Then press Enter in p1's shell.

At first, i was not able to reproduce the hang, but i had both pthread_cond_destroy() and pthread_mutex_destroy() to return EBUSY.

But now the hang reproduces if we call pthread_cond_signal() before pthread_cond_destroy() (see the code above).

3 Answers

The source code of pthread_cond_destroy says the following:

Thus, we can assume that all waiters that are still accessing the condvar have been woken. We wait until they have confirmed to have woken up by decrementing __wrefs.

So we can simply reset __wrefs to zero before pthread_cond_destroy:

c->c.__data.__wrefs = 0;
r = pthread_cond_destroy(&c->c);

I ran you sample with this change and P1 completes with no hang.

Note that before this commit __wrefs was called __nwaiters.

One more option might be the explicitly call pthread_ond_broadcast() before calling pthread_cond_destroy().

Like this:

r = pthread_cond_broadcast(&c->c);
puts("Before pthread_cond_destroy");
r = pthread_cond_destroy(&c->c);
printf("After pthread_cond_destroy, r=%d\n", r);
r = pthread_mutex_destroy(&c->m);
printf("After pthread_mutex_destroy, r=%d\n", r);
Related