Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

pthread conditions and process termination

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

like image 227
Yevgeniy P Avatar asked Dec 07 '13 08:12

Yevgeniy P


Video Answer


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

like image 77
Gusev Petr Avatar answered Sep 29 '22 05:09

Gusev Petr


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.

like image 32
MichaelGoren Avatar answered Sep 29 '22 06:09

MichaelGoren