How to cancel a thread from another thread (Glib threads)? - multithreading

I am developing an application library using GTK and the functions for threads in GLib. I have a thread (from now on will be called thread A) that is created when I hit an "ok" button in a certain graphical window. Thread A starts doing some heavy tasks. Another button named "cancel" is available to stop and finish thread A at any moment.
My aim is to code a function for the thread created when I hit the "cancel" button (thread B) that has the ability to end the thread A.
I create thread A with the function g_thread_create. However I cannot find any function similar to g_thread_cancel to stop thread A using thread B. Is this possible or cannot be done?
Thank you so much for any kind of information provided.

You might want to consider using GTask to run your task in a thread, rather than using a manually-created thread. If you use g_task_run_in_thread(), the operation will run in a separate thread automatically.
GTask is integrated with GCancellable, so to cancel the operation you would call g_cancellable_cancel() in the callback from your ‘Cancel’ button.
As OznOg says, you should treat the GCancellable as a way of gently (and thread-safely) telling your task that it should cancel. Depending on how your long-running task is written, you could either check g_cancellable_is_cancelled() once per loop iteration, or you could add the GSource from g_cancellable_source_new() to a poll loop in your task.
The advice about using threads with GLib is probably also relevant here.

I have developed a code that is able to cancel a thread from another, both of them created from a main one. The code works correctly according to my tests:
#include <pthread.h>
#include <stdio.h>
/* these variables are references to the first and second threads */
pthread_t inc_x_thread, inc_y_thread;
/* this function is run by the first thread */
void *inc_x(void *x_void_ptr)
{
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
/* increment x to 100 */
int *x_ptr = (int *)x_void_ptr;
while(++(*x_ptr) < 100000000);
printf("x increment finished\n");
/* the function must return something - NULL will do */
return NULL;
}
/* this function is run by the second thread */
void *inc_y(void *x_void_ptr)
{
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
/* increment y to 100 */
int *x_ptr = (int *)x_void_ptr;
pthread_cancel(inc_x_thread);
while(++(*x_ptr) < 100);
printf("y increment finished\n");
return NULL;
}
/* this is the main thread */
int main()
{
int x = 0, y = 0;
void *res;
/* show the initial values of x and y */
printf("x: %d, y: %d\n", x, y);
/* create a first thread */
if(pthread_create(&inc_x_thread, NULL, inc_x, &x)) {
fprintf(stderr, "Error creating thread\n");
return 1;
}
/* create a second thread */
if(pthread_create(&inc_y_thread, NULL, inc_y, &y)) {
fprintf(stderr, "Error creating thread\n");
return 1;
}
/* wait for the first thread to finish */
if(pthread_join(inc_x_thread, &res)) {
fprintf(stderr, "Error joining thread\n");
return 2;
}
if (res == PTHREAD_CANCELED)
printf(" thread was canceled\n");
else
printf(" thread wasn't canceled\n");
/* wait for the second thread to finish */
if(pthread_join(inc_y_thread, &res)) {
fprintf(stderr, "Error joining thread\n");
return 2;
}
if (res == PTHREAD_CANCELED)
printf(" thread was canceled\n");
else
printf(" thread wasn't canceled\n");
/* show the results*/
printf("x: %d, y: %d\n", x, y);
return 0;
}
You can compile the code by using: gcc example.c -lpthread
However, as OznOg and Philip Withnall have said, this is not the correct way of cancelling a thread. It is only a quick way of doing it that might not work in some specific situations. A better and safer way is to gently ask the thread to stop itself.

Related

Trigger multiple pthreads by pthread_cond_broadcast

Since the examples for pthreads with pthread_cond_broadcast wakeup are sparse i wrote one, but are unsure if this is correctly synchronized and the way to do it:
do all threads share the same c and mtx variable?
is it necessary upon pthread_cond_wait return to test if some condition is actually met? in my case every call to broadcast should wake up every thread exactly once, but no-one else should do so. (do i prevent spurious wakeups?)
the program currently does not exit despite async cancel type. also no success with deferred cancellation tried in example code despite pthread_cond_wait being a cancellation point so.
overall does it work like i expect it to.
#include <pthread.h>
#include <iostream>
#include <unistd.h>
struct p_args{
int who;
};
pthread_cond_t c; //share between compilation units
pthread_mutex_t mtx;
void *threadFunc(void *vargs){
//pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
struct p_args * args = (struct p_args *) vargs;
while(true){
//wait for trigger one loop
pthread_mutex_lock(&mtx);
pthread_cond_wait(&c, &mtx);
pthread_mutex_unlock(&mtx);
//should be entangled output showing concurrent execution
std::cout << "t " << args->who << std::endl;
/* expensive work */
}
delete args;
}
int main(int argc, char* argv[])
{
pthread_cond_init(&c, NULL);
pthread_mutex_init(&mtx, NULL);
pthread_t thread_id[2];
struct p_args *args0 = new p_args();
struct p_args *args1 = new p_args();
args0->who = 0;
args1->who = 1;
pthread_create(&thread_id[0], NULL, threadFunc, args0);
pthread_create(&thread_id[1], NULL, threadFunc, args1);
sleep(3);
pthread_mutex_lock(&mtx);
pthread_cond_broadcast(&c);
pthread_mutex_unlock(&mtx);
sleep(3);//test if thread waits
pthread_cancel(thread_id[0]);
pthread_cancel(thread_id[1]);
pthread_join (thread_id[0], NULL);
pthread_join (thread_id[1], NULL);
//could perform cleanup here
return 0;
}
Regarding exiting deferred:
thread_id[0] exits fine and i am stuck in line `pthread_join (thread_id[1], NULL);`, it says (Exiting) but seems stuck on a lock, with debugger:
<br>
[![enter image description here][2]][2]
<br>
EDIT final solution i came up with:
#include <pthread.h>
#include <iostream>
#include <unistd.h>
struct p_args{
int who;
};
pthread_cond_t c;
pthread_mutex_t mtx;
bool doSome[2];
bool exitFlag;
void *threadFunc(void *vargs){
struct p_args * args = (struct p_args *) vargs;
while(true){
//wait for trigger one loop
pthread_mutex_lock(&mtx);
do {
pthread_cond_wait(&c, &mtx);
if(exitFlag) {
std::cout << "return " << args->who << std::endl;
delete args;
pthread_mutex_unlock(&mtx);
return NULL;
}
} while(doSome == false);
doSome[args->who] = false;
pthread_mutex_unlock(&mtx);
std::cout << "t " << args->who << std::endl;
}
}
int main(int argc, char* argv[])
{
pthread_cond_init(&c, NULL);
pthread_mutex_init(&mtx, NULL);
pthread_t thread_id[2];
struct p_args *args0 = new p_args();
struct p_args *args1 = new p_args();
args0->who = 0;
args1->who = 1;
doSome[0] = doSome[1] = true;
exitFlag = false;
pthread_create(&thread_id[0], NULL, threadFunc, args0);
pthread_create(&thread_id[1], NULL, threadFunc, args1);
doSome[0] = doSome[1] = true;
pthread_cond_broadcast(&c);
sleep(3);
doSome[0] = doSome[1] = true;
pthread_cond_broadcast(&c);
sleep(3);
exitFlag = true;
pthread_cond_broadcast(&c);
pthread_join (thread_id[0], NULL);
pthread_join (thread_id[1], NULL);
return 0;
}
do all threads share the same c and mtx variable?
Yes, just like any other global variable. You could print their addresses from each thread to confirm it.
is it necessary upon pthread_cond_wait return to test if some condition is actually met?
Yes, all wait interfaces are subject to spurious wakeups, and you're always responsible for checking your own predicate. See the documentation or a good book.
the program currently does not exit ...
pthread_cancel is uniformly horrible and should never be used. It's really hard to get right. If you want to tell your thread to exit, write a notification mechanism - build it into the existing predicate loop - and signal/broadcast to make sure all threads wake up and realize it's time to die.
Regarding exiting deferred: thread_id[0] exits fine and i am stuck in line pthread_join (thread_id[1], NULL);, it says (Exiting) but seems stuck on a lock
One of the hard things about pthread_cancel is cleanup. If cancellation occurs while you're holding a lock, you need to have used pthread_cleanup_push to emulate cancel-compatible RAII semantics. Otherwise the first thread may (and in this case, did) die with the mutex still locked.
In this case the second thread is trying to exit from pthread_const_wait due to cancellation, but it needs to regain the lock and can't.
The usual form of a condition variable loop is this (and a good reference book should show something similar):
void *thread(void *data)
{
struct Args *args = (struct Args *)data;
/* this lock protects both the exit and work predicates.
* It should probably be part of your argument struct,
* globals are not recommended.
* Error handling omitted for brevity,
* but you should really check the return values.
*/
pthread_mutex_lock(&args->mutex);
while (!exit_predicate(args)) {
while (!work_predicate(args)) {
/* check the return value here too */
pthread_cond_wait(&args->condition, &args->mutex);
}
/* work_predicate() is true and we have the lock */
do_work(args);
}
/* unlock (explicitly) only once.
* If you need to cope with cancellation, you do need
* pthread_cleanup_push/pop instead.
*/
pthread_mutex_unlock(&args->mutex);
return data;
}
where your custom code can just go in bool exit_predicate(struct Args*), bool work_predicate(struct Args*) and void do_work(struct Args*). The loop structure itself rarely needs much alteration.

Catching SIGUSR1 with sigtimedwait()

I'm not new to programming, but pretty new to Linux. I'm trying to use signals to asynchronously catch a push on a button, like this:
Run a worker thread which raises SIGUSR1 when the button is pushed.
Run a loop (main thread) around sigtimedwait() that will rotate info every two seconds (as long as the button is not pushed) or break (when the button is pushed).
According to the notes on sigtimedwait(), one should block the signals you want to wait for, then call sigtimedwait(). But I never see sigtimedwait() catching the blocked signals. I have run the code below in a few ways to see what happens with different scenarios:
Call to pthread_sigmask() disabled, call to signal() disabled,
result: programs exits with message "User defined signal 1".
Call to pthread_sigmask() disabled, call to signal() enabled, result:
message "Button 1 pressed sync1 hit" appears, sigtimedwait() always
returns EAGAIN.
Call to pthread_sigmask() enabled, call to signal() disabled, result:
message "Button 1 pressed" appears, sigtimedwait() always returns
EAGAIN.
Call to pthread_sigmask() enabled, call to signal() enabled, result
of course same as previous because the handler will not be called.
All as expected, except for the fact that sigtimedwait() doesn't seem to catch the signal when it's pending.
I've looked into similar code, e.g. this. But I don't understand how that particular code could work: SIGUSR1 isn't blocked, so raising that should immediately terminate the program (the default action for SIGUSR1).
It looks like I'm missing something here. What am I doing wrong? Or is the whole idea of using raise() in a worker thread wrong? I'm running this on a Raspberry Pi 3 with Raspbian Stretch (Debian 9.1), could there be a problem in that?
[I know printf() shouldn't be used in a signal handler, but for this purpose it works]
Any help appreciated, thx!
#include <stdio.h>
#include <stdlib.h>
#include <bcm2835.h>
#include <signal.h>
#include <pthread.h>
#include <errno.h>
#define PIN_BUTTON1 RPI_V2_GPIO_P1_22 // GPIO #24
// Thread function
void* check_button1(void* param)
{
while (true)
{
if (bcm2835_gpio_lev(PIN_BUTTON1) == HIGH)
{
printf("Button 1 pressed ");
raise(SIGUSR1);
}
delay(250);
}
}
// Signal handler, if applied
volatile sig_atomic_t usr_interrupt = 0;
void sync1(int sig)
{
printf("sync1 hit ... ");
usr_interrupt = 1;
}
int main(int argc, char** argv)
{
if (!bcm2835_init())
{
printf("Failed to initialize BCM2835 GPIO library.");
return 1;
}
bcm2835_gpio_fsel(PIN_BUTTON1, BCM2835_GPIO_FSEL_INPT);
sigset_t sigusr;
sigemptyset(&sigusr);
sigaddset(&sigusr, SIGUSR1);
pthread_sigmask(SIG_BLOCK, &sigusr, NULL);
signal(SIGUSR1, sync1);
// Start the threads to read the button pin state
pthread_t th1;
pthread_create(&th1, NULL, check_button1, NULL);
// Create a two second loop
struct timespec timeout = { 0 };
timeout.tv_sec = 2;
usr_interrupt = 0;
int nLoopCount = 0;
while (true)
{
printf("Loop %d, waiting %d seconds ... ", ++nLoopCount, timeout.tv_sec);
int nResult = sigtimedwait(&sigusr, NULL, &timeout);
if (nResult < 0)
{
switch (errno)
{
case EAGAIN: printf("EAGAIN "); break; // Time out, no signal raised, next loop
case EINTR: printf("EINTR "); break; // Interrupted by a signal other than SIGCHLD.
case EINVAL: printf("EINVAL "); exit(1); // Invalid timeout
default: printf("Result=%d Error=%d ", nResult, errno); break;
}
printf("\n");
continue;
}
printf("Signal %d caught\n", nResult);
}
return 0;
}
ADDENDUM: In the meantime, I got this working by replacing raise(SIGUSR1) by kill(getpid(), SIGUSR1). Strange, because according to the manual raise(x) is equivalent to kill(getpid, x) in single-threaded programs and to pthread_kill(pthread_self(), x) in multi-threaded ones. Replacing raise(SIGUSR1) by pthread_kill(pthread_self, SIGUSR1) has no effect. If anyone could explain this to me ...

Linux multi-thread, pausing one thread while continue running the other threads within the same process

I cannot find a proper solution to my problem.
If i have more than one thread in one process. And I want to make only one thread to sleep while running the other threads within the same process, is there any predefined syntax for it or do i have to do my own implementation (sleep) ?
Ideally i want to send a indication from a thread to another thread when it is time for sleep.
Edited (2015-08-24)
I have two main threads, one for sending data over a network, the other receives the data from the network. Beside jitter, the receiving thread does validation and verification and some file management which in time could lead that it will drag behind. What i like to do is to add something like a micro sleep to the sender so that the receiver could catch up. sched_yield() will not help in this case because the HW has a multi core CPU with more than 40 cores.
From your description in the comments, it looks like you're trying to synchronize 2 threads so that one of them doesn't fall behind too far from the other.
If that's the case, you're going about this the wrong way. It is seldom a good idea to do synchronization by sleeping, because the scheduler may incur unpredictable and long delays that cause the other (slow) thread to remain stopped in the run queue without being scheduled. Even if it works most of the time, it's still a race condition, and it's an ugly hack.
Given your use case and constraints, I think you'd be better off using barriers (see pthread_barrier_init(3)). Pthread barriers allow you to create a rendezvous point in the code where threads can catch up.
You call pthread_barrier_init(3) as part of the initialization code, specifying the number of threads that will be synchronized using that barrier. In this case, it's 2.
Then, threads synchronize with others by calling pthread_barrier_wait(3). The call blocks until the number of threads specified in pthread_barrier_init(3) call pthread_barrier_wait(3), at which point every thread that was blocked in pthread_barrier_wait(3) becomes runnable and the cycle begins again. Essentially, barriers create a synchronization point where no one can move forward until everyone arrives. I think this is exactly what you're looking for.
Here's an example that simulates a fast sender thread and a slow receiver thread. They both synchronize with barriers to ensure that the sender does not do any work while the receiver is still processing other requests. The threads synchronize at the end of their work unit, but of course, you can choose where each thread calls pthread_barrier_wait(3), thereby controlling exactly when (and where) threads synchronize.
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
pthread_barrier_t barrier;
void *sender_thr(void *arg) {
printf("Entered sender thread\n");
int i;
for (i = 0; i < 10; i++) {
/* Simulate some work (500 ms) */
if (usleep(500000) < 0) {
perror("usleep(3) error");
}
printf("Sender thread synchronizing.\n");
/* Wait for receiver to catch up */
int barrier_res = pthread_barrier_wait(&barrier);
if (barrier_res == PTHREAD_BARRIER_SERIAL_THREAD)
printf("Sender thread was last.\n");
else if (barrier_res == 0)
printf("Sender thread was first.\n");
else
fprintf(stderr, "pthread_barrier_wait(3) error on sender: %s\n", strerror(barrier_res));
}
return NULL;
}
void *receiver_thr(void *arg) {
printf("Entered receiver thread\n");
int i;
for (i = 0; i < 10; i++) {
/* Simulate a lot of work */
if (usleep(2000000) < 0) {
perror("usleep(3) error");
}
printf("Receiver thread synchronizing.\n");
/* Catch up with sender */
int barrier_res = pthread_barrier_wait(&barrier);
if (barrier_res == PTHREAD_BARRIER_SERIAL_THREAD)
printf("Receiver thread was last.\n");
else if (barrier_res == 0)
printf("Receiver thread was first.\n");
else
fprintf(stderr, "pthread_barrier_wait(3) error on receiver: %s\n", strerror(barrier_res));
}
return NULL;
}
int main(void) {
int barrier_res;
if ((barrier_res = pthread_barrier_init(&barrier, NULL, 2)) != 0) {
fprintf(stderr, "pthread_barrier_init(3) error: %s\n", strerror(barrier_res));
exit(EXIT_FAILURE);
}
pthread_t threads[2];
int thread_res;
if ((thread_res = pthread_create(&threads[0], NULL, sender_thr, NULL)) != 0) {
fprintf(stderr, "pthread_create(3) error on sender thread: %s\n", strerror(thread_res));
exit(EXIT_FAILURE);
}
if ((thread_res = pthread_create(&threads[1], NULL, receiver_thr, NULL)) != 0) {
fprintf(stderr, "pthread_create(3) error on receiver thread: %s\n", strerror(thread_res));
exit(EXIT_FAILURE);
}
/* Do some work... */
if ((thread_res = pthread_join(threads[0], NULL)) != 0) {
fprintf(stderr, "pthread_join(3) error on sender thread: %s\n", strerror(thread_res));
exit(EXIT_FAILURE);
}
if ((thread_res = pthread_join(threads[1], NULL)) != 0) {
fprintf(stderr, "pthread_join(3) error on receiver thread: %s\n", strerror(thread_res));
exit(EXIT_FAILURE);
}
if ((barrier_res = pthread_barrier_destroy(&barrier)) != 0) {
fprintf(stderr, "pthread_barrier_destroy(3) error: %s\n", strerror(barrier_res));
exit(EXIT_FAILURE);
}
return 0;
}
Note that, as specified in the manpage for pthread_barrier_wait(3), once the desired number of threads call pthread_barrier_wait(3), the barrier state is reset to the original state that was in use after the last call to pthread_barrier_init(3), which means that the barrier atomically unlocks and resets state, so it is always ready for the next synchronization point, which is wonderful.
Once you're done with the barrier, don't forget to free the associated resources with pthread_barrier_destroy(3).

pthread_cond_broadcast problem

Using pthreads in linux 2.6.30 I am trying to send a single signal which will cause multiple threads to begin execution. The broadcast seems to only be received by one thread. I have tried both pthread_cond_signal and pthread cond_broadcast and both seem to have the same behavior. For the mutex in pthread_cond_wait, I have tried both common mutexes and separate (local) mutexes with no apparent difference.
worker_thread(void *p)
{
// setup stuff here
printf("Thread %d ready for action \n", p->thread_no);
pthread_cond_wait(p->cond_var, p->mutex);
printf("Thread %d off to work \n", p->thread_no);
// work stuff
}
dispatch_thread(void *p)
{
// setup stuff
printf("Wakeup, everyone ");
pthread_cond_broadcast(p->cond_var);
printf("everyone should be working \n");
// more stuff
}
main()
{
pthread_cond_init(cond_var);
for (i=0; i!=num_cores; i++) {
pthread_create(worker_thread...);
}
pthread_create(dispatch_thread...);
}
Output:
Thread 0 ready for action
Thread 1 ready for action
Thread 2 ready for action
Thread 3 ready for action
Wakeup, everyone
everyone should be working
Thread 0 off to work
What's a good way to send signals to all the threads?
First off, you should have the mutex locked at the point where you call pthread_cond_wait(). It's generally a good idea to hold the mutex when you call pthread_cond_broadcast(), as well.
Second off, you should loop calling pthread_cond_wait() while the wait condition is true. Spurious wakeups can happen, and you must be able to handle them.
Finally, your actual problem: you are signaling all threads, but some of them aren't waiting yet when the signal is sent. Your main thread and dispatch thread are racing your worker threads: if the main thread can launch the dispatch thread, and the dispatch thread can grab the mutex and broadcast on it before the worker threads can, then those worker threads will never wake up.
You need a synchronization point prior to signaling where you wait to signal till all threads are known to be waiting for the signal. That, or you can keep signaling till you know all threads have been woken up.
In this case, you could use the mutex to protect a count of sleeping threads. Each thread grabs the mutex and increments the count. If the count matches the count of worker threads, then it's the last thread to increment the count and so signals on another condition variable sharing the same mutex to the sleeping dispatch thread that all threads are ready. The thread then waits on the original condition, which causes it release the mutex.
If the dispatch thread wasn't sleeping yet when the last worker thread signals on that condition, it will find that the count already matches the desired count and not bother waiting, but immediately broadcast on the shared condition to wake workers, who are now guaranteed to all be sleeping.
Anyway, here's some working source code that fleshes out your sample code and includes my solution:
#include <stdio.h>
#include <pthread.h>
#include <err.h>
static const int num_cores = 8;
struct sync {
pthread_mutex_t *mutex;
pthread_cond_t *cond_var;
int thread_no;
};
static int sleeping_count = 0;
static pthread_cond_t all_sleeping_cond = PTHREAD_COND_INITIALIZER;
void *
worker_thread(void *p_)
{
struct sync *p = p_;
// setup stuff here
pthread_mutex_lock(p->mutex);
printf("Thread %d ready for action \n", p->thread_no);
sleeping_count += 1;
if (sleeping_count >= num_cores) {
/* Last worker to go to sleep. */
pthread_cond_signal(&all_sleeping_cond);
}
int err = pthread_cond_wait(p->cond_var, p->mutex);
if (err) warnc(err, "pthread_cond_wait");
printf("Thread %d off to work \n", p->thread_no);
pthread_mutex_unlock(p->mutex);
// work stuff
return NULL;
}
void *
dispatch_thread(void *p_)
{
struct sync *p = p_;
// setup stuff
pthread_mutex_lock(p->mutex);
while (sleeping_count < num_cores) {
pthread_cond_wait(&all_sleeping_cond, p->mutex);
}
printf("Wakeup, everyone ");
int err = pthread_cond_broadcast(p->cond_var);
if (err) warnc(err, "pthread_cond_broadcast");
printf("everyone should be working \n");
pthread_mutex_unlock(p->mutex);
// more stuff
return NULL;
}
int
main(void)
{
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond_var = PTHREAD_COND_INITIALIZER;
pthread_t worker[num_cores];
struct sync info[num_cores];
for (int i = 0; i < num_cores; i++) {
struct sync *p = &info[i];
p->mutex = &mutex;
p->cond_var = &cond_var;
p->thread_no = i;
pthread_create(&worker[i], NULL, worker_thread, p);
}
pthread_t dispatcher;
struct sync p = {&mutex, &cond_var, num_cores};
pthread_create(&dispatcher, NULL, dispatch_thread, &p);
pthread_exit(NULL);
/* not reached */
return 0;
}

In pthread, how to reliably pass signal to another thread?

I'm trying to write a simple thread pool program in pthread. However, it seems that pthread_cond_signal doesn't block, which creates a problem. For example, let's say I have a "producer-consumer" program:
pthread_cond_t my_cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t my_cond_m = PTHREAD_MUTEX_INITIALIZER;
void * liberator(void * arg)
{
// XXX make sure he is ready to be freed
sleep(1);
pthread_mutex_lock(&my_cond_m);
pthread_cond_signal(&my_cond);
pthread_mutex_unlock(&my_cond_m);
return NULL;
}
int main()
{
pthread_t t1;
pthread_create(&t1, NULL, liberator, NULL);
// XXX Don't take too long to get ready. Otherwise I'll miss
// the wake up call forever
//sleep(3);
pthread_mutex_lock(&my_cond_m);
pthread_cond_wait(&my_cond, &my_cond_m);
pthread_mutex_unlock(&my_cond_m);
pthread_join(t1, NULL);
return 0;
}
As described in the two XXX marks, if I take away the sleep calls, then main() may stall because it has missed the wake up call from liberator(). Of course, sleep isn't a very robust way to ensure that either.
In real life situation, this would be a worker thread telling the manager thread that it is ready for work, or the manager thread announcing that new work is available.
How would you do this reliably in pthread?
Elaboration
#Borealid's answer kind of works, but his explanation of the problem could be better. I suggest anyone looking at this question to read the discussion in the comments to understand what's going on.
In particular, I myself would amend his answer and code example like this, to make this clearer. (Since Borealid's original answer, while compiled and worked, confused me a lot)
// In main
pthread_mutex_lock(&my_cond_m);
// If the flag is not set, it means liberator has not
// been run yet. I'll wait for him through pthread's signaling
// mechanism
// If it _is_ set, it means liberator has been run. I'll simply
// skip waiting since I've already synchronized. I don't need to
// use pthread's signaling mechanism
if(!flag) pthread_cond_wait(&my_cond, &my_cond_m);
pthread_mutex_unlock(&my_cond_m);
// In liberator thread
pthread_mutex_lock(&my_cond_m);
// Signal anyone who's sleeping. If no one is sleeping yet,
// they should check this flag which indicates I have already
// sent the signal. This is needed because pthread's signals
// is not like a message queue -- a sent signal is lost if
// nobody's waiting for a condition when it's sent.
// You can think of this flag as a "persistent" signal
flag = 1;
pthread_cond_signal(&my_cond);
pthread_mutex_unlock(&my_cond_m);
Use a synchronization variable.
In main:
pthread_mutex_lock(&my_cond_m);
while (!flag) {
pthread_cond_wait(&my_cond, &my_cond_m);
}
pthread_mutex_unlock(&my_cond_m);
In the thread:
pthread_mutex_lock(&my_cond_m);
flag = 1;
pthread_cond_broadcast(&my_cond);
pthread_mutex_unlock(&my_cond_m);
For a producer-consumer problem, this would be the consumer sleeping when the buffer is empty, and the producer sleeping when it is full. Remember to acquire the lock before accessing the global variable.
I found out the solution here. For me, the tricky bit to understand the problem is that:
Producers and consumers must be able to communicate both ways. Either way is not enough.
This two-way communication can be packed into one pthread condition.
To illustrate, the blog post mentioned above demonstrated that this is actually meaningful and desirable behavior:
pthread_mutex_lock(&cond_mutex);
pthread_cond_broadcast(&cond):
pthread_cond_wait(&cond, &cond_mutex);
pthread_mutex_unlock(&cond_mutex);
The idea is that if both the producers and consumers employ this logic, it will be safe for either of them to be sleeping first, since the each will be able to wake the other role up. Put it in another way, in a typical producer-consumer sceanrio -- if a consumer needs to sleep, it's because a producer needs to wake up, and vice versa. Packing this logic in a single pthread condition makes sense.
Of course, the above code has the unintended behavior that a worker thread will also wake up another sleeping worker thread when it actually just wants to wake the producer. This can be solved by a simple variable check as #Borealid suggested:
while(!work_available) pthread_cond_wait(&cond, &cond_mutex);
Upon a worker broadcast, all worker threads will be awaken, but one-by-one (because of the implicit mutex locking in pthread_cond_wait). Since one of the worker threads will consume the work (setting work_available back to false), when other worker threads awake and actually get to work, the work will be unavailable so the worker will sleep again.
Here's some commented code I tested, for anyone interested:
// gcc -Wall -pthread threads.c -lpthread
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <assert.h>
pthread_cond_t my_cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t my_cond_m = PTHREAD_MUTEX_INITIALIZER;
int * next_work = NULL;
int all_work_done = 0;
void * worker(void * arg)
{
int * my_work = NULL;
while(!all_work_done)
{
pthread_mutex_lock(&my_cond_m);
if(next_work == NULL)
{
// Signal producer to give work
pthread_cond_broadcast(&my_cond);
// Wait for work to arrive
// It is wrapped in a while loop because the condition
// might be triggered by another worker thread intended
// to wake up the producer
while(!next_work && !all_work_done)
pthread_cond_wait(&my_cond, &my_cond_m);
}
// Work has arrived, cache it locally so producer can
// put in next work ASAP
my_work = next_work;
next_work = NULL;
pthread_mutex_unlock(&my_cond_m);
if(my_work)
{
printf("Worker %d consuming work: %d\n", (int)(pthread_self() % 100), *my_work);
free(my_work);
}
}
return NULL;
}
int * create_work()
{
int * ret = (int *)malloc(sizeof(int));
assert(ret);
*ret = rand() % 100;
return ret;
}
void * producer(void * arg)
{
int i;
for(i = 0; i < 10; i++)
{
pthread_mutex_lock(&my_cond_m);
while(next_work != NULL)
{
// There's still work, signal a worker to pick it up
pthread_cond_broadcast(&my_cond);
// Wait for work to be picked up
pthread_cond_wait(&my_cond, &my_cond_m);
}
// No work is available now, let's put work on the queue
next_work = create_work();
printf("Producer: Created work %d\n", *next_work);
pthread_mutex_unlock(&my_cond_m);
}
// Some workers might still be waiting, release them
pthread_cond_broadcast(&my_cond);
all_work_done = 1;
return NULL;
}
int main()
{
pthread_t t1, t2, t3, t4;
pthread_create(&t1, NULL, worker, NULL);
pthread_create(&t2, NULL, worker, NULL);
pthread_create(&t3, NULL, worker, NULL);
pthread_create(&t4, NULL, worker, NULL);
producer(NULL);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
pthread_join(t3, NULL);
pthread_join(t4, NULL);
return 0;
}

Resources