I'm working on a linux app which uses producer and consumer threads. It's a fairly mature app and I don't want to change the architecture more than I have to.
The producer and consumer threads are linked via a waitable queue. This is a class implemented via std::queue together with a condition variable and mutex.
Now I want the consumer threads to be able to fork/exec a child process, and to wait till either the child process is finished, or the waitable queue is non empty, whichever happens first. If the waitable queue is non empty then the child process must be killed. Edit: The child processes are third party apps that can't be changed.
One possibility is to call pthread_cond_signal() on my condition variable when the child process terminates, but how to make that happen? I can't use a handler for SIGCHLD, at least not directly, because the man page says that pthread_cond_signal() can't be used from a signal handler.
One possible way would be spawn the child process, then start a thread to a blocking waitpid(), and finally pthread_cond_signal(). This seems a little clunky though: do I really need to spawn a thread just to keep an eye on a pid?
For mixing waitpid and select/poll/epoll there's the Self Pipe Trick. Is there any equivalent for mixing waitpid and condition variables?
Note 1: On some implementations, SIGCHLD interrupts the condition variable wait functions. This isn't portable and if possible I'd rather not rely on this behaviour.
Note 2: As the condition variable is encapsulated within the waitable queue class, I'd need to extend this class to allow the app to signal the mutex. This is just a trivial implementation detail which I've glossed over in my question.
Maybe this works, but I am not sure:
Create a semaphore, which will be registered in your waitable queue and locked/altered/unlocked whenever the waitable queue itself is going to change it's own lock in order to indicate a change of state. You should change it's own mutex while it holds the semaphore.
Implement a signal handler for SIGCHLD
, which then will perform semaphore locking/altering/unlocking when a 3rd party app will terminate and does nothing if that is not the case.
In the above cases, they will wait for 1 and increment (as one semaphore operation) on your semaphore, when they want to acquire the semaphore lock, then do their work and then change the semaphore to 0 (decrementing by 2) in order to unlock it for your waiting thread. This way you wont have two sucessful locks from any of the queues/3rd party apps continously.
In your actual thread, that is supposed to either wait for a 3rd party app to terminate or your waitable queue, you basically have it wait for a lock on the same semaphore while waiting for 0 (and decrement it, if there are other waiters for 0). If the lock is acquired, you check if the mutex on your waitable queue is released. If not, you know that your 3rd party app terminated. You perform your work and then change your semaphore to 1 by incrementing it and thus unlock the semaphore for your queue and 3rd party app again.
Since a semop(2)
-lock call can be interupted by signal handlers, you will have to check on EINTR
and loop any lock attempt you have.
This probably works if it is guaranteed that a signal handler will finish it's execution (of which I think that it is).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With