I have some random issues sometimes to join pthread. I can just say that the thread is not stuck in a deadlock with a mutex when the join is failing. Most of the time the thread is idle (sleep syscall) when the timeout occurred on join.
My need is basic. A way to start/stop a thread from the main thread. So I don't need to put mutex in start/stop manager on pthread state variable. The thread is working as an infinite loop most of the time. All my thread are designed with the same skeleton. A start and stop function. The thread function definition. I have a global variable g_event_ctx to store the current status of the thread. running to know I need to cancel it. is_joinable to know if I need to join the thread. Moreover I have sleep/read/write syscall on all my thread function (cancel point !)
typedef struct pthread_context
{
pthread_t id; /*!< pthread_t to be able to stop the thread later */
int running; /*!< allow to know if the thread is currently running */
int is_joinable; /*!< allow to know if the thread is joinable */
} str_pthread_context;
The code of the skeleton :
int start_x_manager (void)
{
pthread_t t_x;
if (g_event_ctx.x_thread.is_joinable) return 0;
PRINT_INFO ("Start x manager");
// start push x thread
if (pthread_create (&t_x, NULL, x_loop_thread, NULL))
PRINT_ERR_GOTO ("error on pthread_create for x thread");
pthread_setname_np(t_x, "x");
g_event_ctx.x_thread.id = t_x;
g_event_ctx.x_thread.is_joinable = 1;
g_event_ctx.x_thread.running = 1;
return 0;
error:
g_event_ctx.x_thread.running = 0;
g_event_ctx.x_thread.is_joinable = 0;
return 1;
}
int stop_x_manager (void)
{
struct timespec ts;
if (!g_event_ctx.x_thread.is_joinable) return 0;
PRINT_INFO ("Stop x manager");
if (g_event_ctx.x_thread.running)
{
CHECK_ERR_GOTO (pthread_cancel(g_event_ctx.x_thread.id) != 0, "Cannot cancel x thread");
g_event_ctx.x_thread.running = 0;
}
CHECK_ERR_GOTO (clock_gettime(CLOCK_REALTIME, &ts) == -1, "Cannot get clock time");
ts.tv_sec += 5;
CHECK_ERR_GOTO (pthread_timedjoin_np (g_event_ctx.x_thread.id, NULL, &ts) != 0, "Cannot join x_thread");
g_event_ctx.x_thread.is_joinable = 0;
return 0;
error:
g_event_ctx.x_thread.running = 0;
g_event_ctx.x_thread.is_joinable = 0;
return 1;
}
The skeleton of the thread function :
void *x_loop_thread (void *arg __attribute__((__unused__)))
{
CHECK_ERR_GOTO (pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0, "Cannot set cancel state");
CHECK_ERR_GOTO (pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0, "Cannot set cancel state");
PRINT_INFO ("Start x manager loop thread ...");
pthread_cleanup_push(x_manager_cleanup, some_stuff);
while (1)
{
// Do some stuff here
}
g_event_ctx.x_thread.running = 0;
pthread_exit (NULL);
error:
g_event_ctx.x_thread.running = 0;
pthread_cleanup_pop(1);
pthread_exit (NULL);
}
CHECK_ERR_GOTO is a macro which check a condition to know if I need to jump to label error.
What is the reason which can explain a timeout on the pthread_timedjoin_np ? Another piece of code which corrupted my thread id ? Is there a problem of design in my skeleton ?
You can sidestep the problem by putting a variable in the context structure indicating you want the background thread to stop, setting that variable in your main thread before calling join, and checking that variable periodically in the background thread, exiting the while(1) loop if it's true.
If you have any blocking calls that sleep forever, you can either have them time out and loop them with while(!want_to_stop) or, for select loops, add a file descriptor you can activate from the main thread when you want to stop (an eventfd or pipe).
Related
I am writing a concurrent C program where I want to wait for all threads to finish in the main().
Based on this solution, I wrote the following code in main():
// Create threads
pthread_t cid[num_mappers];
int t_iter;
for (t_iter = 0; t_iter < num_mappers; t_iter++){
pthread_create(&(cid[t_iter]), NULL, &map_consumer, NULL);
}
// Wait for all threads to finish
for (t_iter = 0; t_iter < num_mappers; t_iter++){
printf("Joining %d\n", t_iter);
int result = pthread_join(cid[t_iter], NULL);
}
printf("Done mapping.\n");
The function passed into threads is defined as:
// Consumer function for mapping phase
void *map_consumer(void *arg){
while (1){
pthread_mutex_lock(&g_lock);
if (g_cur >= g_numfull){
// No works to do, just quit
return NULL;
}
// Get the file name
char *filename = g_job_queue[g_cur];
g_cur++;
pthread_mutex_unlock(&g_lock);
// Do the mapping
printf("%s\n", filename);
g_map(filename);
}
}
The threads are all successfully created and executed, but the join loop will never finish if num_mappers >= 2.
You return without unlocking the mutex:
pthread_mutex_lock(&g_lock);
if (g_cur >= g_numfull){
// No works to do, just quit
return NULL; <-- mutex is still locked here
}
// Get the file name
char *filename = g_job_queue[g_cur];
g_cur++;
pthread_mutex_unlock(&g_lock);
So only one thread ever returns and ends - the first one, but since it never unlocks the mutex, the other threads remain blocked.
You need something more like
pthread_mutex_lock(&g_lock);
if (g_cur >= g_numfull){
// No works to do, just quit
pthread_mutex_unlock(&g_lock);
return NULL;
}
// Get the file name
char *filename = g_job_queue[g_cur];
g_cur++;
pthread_mutex_unlock(&g_lock);
I want to start a timer and have a function called when it expires.
Googling finds lots of examples, including the example in the manual, all of which use sigaction() to set a signal handler.
However, #Patryk says in this question that we can just
void cbf(union sigval);
struct sigevent sev;
timer_t timer;
sev.sigev_notify = SIGEV_THREAD;
sev.sigev_notify_function = cbf; //this function will be called when timer expires
sev.sigev_value.sival_ptr = (void*) arg;//this argument will be passed to cbf
timer_create(CLOCK_MONOTONIC, &sev, &timer);
which is shorter, simpler, cleaner, more maintainable ...
What gives? Is this correct? Is it just a wrapper for sigaction()? Why do the examples explicitly set a signal handler?
Also, if I start a timer either by this method, or by timer_settime and a signal handler, will cancelling the timer casue the system to remove the association between that timer and the callback, or do I have to do that explicitly?
[Update] You can choose either signals or the method I show in my answer below (or both, but that seems silly). It is a matter of taste. Singals might offer a little more fucntionality, at the cost of complciation.
If all you want to do is start a timer and be notified when it expires, the method in my answer is simplest.
Michael Kerrisk has a detailed example in his "The Linux Programming Interface" book:
/* ptmr_sigev_thread.c
This program demonstrates the use of threads as the notification mechanism
for expirations of a POSIX timer. Each of the program's command-line
arguments specifies the initial value and interval for a POSIX timer. The
format of these arguments is defined by the function itimerspecFromStr().
The program creates and arms one timer for each command-line argument.
The timer notification method is specified as SIGEV_THREAD, causing the
timer notifications to be delivered via a thread that invokes threadFunc()
as its start function. The threadFunc() function displays information
about the timer expiration, increments a global counter of timer expirations,
and signals a condition variable to indicate that the counter has changed.
In the main thread, a loop waits on the condition variable, and each time
the condition variable is signaled, the main thread prints the value of the
global variable that counts timer expirations.
Kernel support for Linux timers is provided since Linux 2.6. On older
systems, an incomplete user-space implementation of POSIX timers
was provided in glibc.
*/
#include <signal.h>
#include <time.h>
#include <pthread.h>
#include "curr_time.h" /* Declares currTime() */
#include "tlpi_hdr.h"
#include "itimerspec_from_str.h" /* Declares itimerspecFromStr() */
static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
static int expireCnt = 0; /* Number of expirations of all timers */
static void /* Thread notification function */
threadFunc(union sigval sv)
{
timer_t *tidptr;
int s;
tidptr = sv.sival_ptr;
printf("[%s] Thread notify\n", currTime("%T"));
printf(" timer ID=%ld\n", (long) *tidptr);
printf(" timer_getoverrun()=%d\n", timer_getoverrun(*tidptr));
/* Increment counter variable shared with main thread and signal
condition variable to notify main thread of the change. */
s = pthread_mutex_lock(&mtx);
if (s != 0)
errExitEN(s, "pthread_mutex_lock");
expireCnt += 1 + timer_getoverrun(*tidptr);
s = pthread_mutex_unlock(&mtx);
if (s != 0)
errExitEN(s, "pthread_mutex_unlock");
s = pthread_cond_signal(&cond);
if (s != 0)
errExitEN(s, "pthread_cond_signal");
}
int
main(int argc, char *argv[])
{
struct sigevent sev;
struct itimerspec ts;
timer_t *tidlist;
int s, j;
if (argc < 2)
usageErr("%s secs[/nsecs][:int-secs[/int-nsecs]]...\n", argv[0]);
tidlist = calloc(argc - 1, sizeof(timer_t));
if (tidlist == NULL)
errExit("malloc");
sev.sigev_notify = SIGEV_THREAD; /* Notify via thread */
sev.sigev_notify_function = threadFunc; /* Thread start function */
sev.sigev_notify_attributes = NULL;
/* Could be pointer to pthread_attr_t structure */
/* Create and start one timer for each command-line argument */
for (j = 0; j < argc - 1; j++) {
itimerspecFromStr(argv[j + 1], &ts);
sev.sigev_value.sival_ptr = &tidlist[j];
/* Passed as argument to threadFunc() */
if (timer_create(CLOCK_REALTIME, &sev, &tidlist[j]) == -1)
errExit("timer_create");
printf("Timer ID: %ld (%s)\n", (long) tidlist[j], argv[j + 1]);
if (timer_settime(tidlist[j], 0, &ts, NULL) == -1)
errExit("timer_settime");
}
/* The main thread waits on a condition variable that is signaled
on each invocation of the thread notification function. We
print a message so that the user can see that this occurred. */
s = pthread_mutex_lock(&mtx);
if (s != 0)
errExitEN(s, "pthread_mutex_lock");
for (;;) {
s = pthread_cond_wait(&cond, &mtx);
if (s != 0)
errExitEN(s, "pthread_cond_wait");
printf("main(): expireCnt = %d\n", expireCnt);
}
}
Taken from online source code.
Also read the Chapter 23 of the book, this code is explained in great detail there.
To test the code above, one would enter
$ ./ptmr_sigev_thread 5:5 10:10
This will set two timers: one with initial expiry of 5 seconds and an interval with 5 seconds, and the other with 10 respectively.
The definitions for helper functions can be found by following the link on the book's source code above.
It seems that I do not have to use a signal handler and can make the code much simpler, as shown here:
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>
static unsigned int pass_value_by_pointer = 42;
void Timer_has_expired(union sigval timer_data)
{
printf("Timer expiration handler function; %d\n", *(int *) timer_data.sival_ptr);
}
int main(void)
{
struct sigevent timer_signal_event;
timer_t timer;
struct itimerspec timer_period;
printf("Create timer\n");
timer_signal_event.sigev_notify = SIGEV_THREAD;
timer_signal_event.sigev_notify_function = Timer_has_expired; // This function will be called when timer expires
// Note that the following is a union. Assign one or the other (preferably by pointer)
//timer_signal_event.sigev_value.sival_int = 38; // This argument will be passed to the function
timer_signal_event.sigev_value.sival_ptr = (void *) &pass_value_by_pointer; // as will this (both in a structure)
timer_signal_event.sigev_notify_attributes = NULL;
timer_create(CLOCK_MONOTONIC, &timer_signal_event, &timer);
printf("Start timer\n");
timer_period.it_value.tv_sec = 1; // 1 second timer
timer_period.it_value.tv_nsec = 0; // no nano-seconds
timer_period.it_interval.tv_sec = 0; // non-repeating timer
timer_period.it_interval.tv_nsec = 0;
timer_settime(timer, 0, &timer_period, NULL);
sleep(2);
printf("----------------------------\n");
printf("Start timer a second time\n");
timer_settime(timer, 0, &timer_period, NULL);
sleep(2);
printf("----------------------------\n");
printf("Start timer a third time\n");
timer_settime(timer, 0, &timer_period, NULL);
printf("Cancel timer\n");
timer_delete(timer);
sleep(2);
printf("The timer expiration handler function should not have been called\n");
return EXIT_SUCCESS;
}
when run, it gives this output:
Create timer
Start timer
Timer expiration handler function; 42
----------------------------
Start timer a second time
Timer expiration handler function; 42
----------------------------
Start timer a third time
Cancel timer
The timer expiration handler function should not have been called
Linux has timerfd. https://lwn.net/Articles/251413/ . This will allows a waitable time to be used together with select/poll/epoll. Alternatively you can use the timeout on select/poll/epoll.
I Work with couple of threads. all running as long as an exit_flag is set to false.
I Have specific thread that doesn't recognize the change in the flag, and therefor not ending and freeing up its resources, and i'm trying to understand why.
UPDATE: After debugging a bit with gdb, i can see that given 'enough time' the problematic thread does detects the flag change.
My conclusion from this is that not enough time passes for the thread to detect the change in normal run.
How can i 'delay' my main thread, long enough for all threads to detect the flag change, without having to JOIN them? (the use of exit_flag was in an intention NOT to join the threads, as i don't want to manage all threads id's for that - i'm just detaching each one of them, except the thread that handles input).
I've tried using sleep(5) in close_server() method, after the flag changing, with no luck
Notes:
Other threads that loop on the same flag does terminate succesfully
exit_flag declaration is: static volatile bool exit_flag
All threads are reading the flag, flag value is changed only in close_server() method i have (which does only that)
Data race that may occur when a thread reads the flag just before its changed, doesn't matter to me, as long as in the next iteration of the while loop it will read the correct value.
No error occurs in the thread itself (according to strerr & stdout which are 'clean' from error messages (for the errors i handle in the thread)
Ths situation also occurs even when commenting out the entire while((!exit_flag) && (remain_data > 0)) code block - so this is not a sendfile hanging issure
station_info_t struct:
typedef struct station_info {
int socket_fd;
int station_num;
} station_info_t;
Problematic thread code:
void * station_handler(void * arg_p)
{
status_type_t rs = SUCCESS;
station_info_t * info = (station_info_t *)arg_p;
int remain_data = 0;
int sent_bytes = 0;
int song_fd = 0;
off_t offset = 0;
FILE * fp = NULL;
struct stat file_stat;
/* validate station number for this handler */
if(info->station_num < 0) {
fprintf(stderr, "station_handler() station_num = %d, something's very wrong! exiting\n", info->station_num);
exit(EXIT_FAILURE);
}
/* Open the file to send, and get his stats */
fp = fopen(srv_params.songs_names[info->station_num], "r");
if(NULL == fp) {
close(info->socket_fd);
free(info);
error_and_exit("fopen() failed! errno = ", errno);
}
song_fd = fileno(fp);
if( fstat(song_fd, &file_stat) ) {
close(info->socket_fd);
fclose(fp);
free(info);
error_and_exit("fstat() failed! errno = ", errno);
}
/** Run as long as no exit procedure was initiated */
while( !exit_flag ) {
offset = 0;
remain_data = file_stat.st_size;
while( (!exit_flag) && (remain_data > 0) ) {
sent_bytes = sendfile(info->socket_fd, song_fd, &offset, SEND_BUF);
if(sent_bytes < 0 ) {
error_and_exit("sendfile() failed! errno = ", errno);
}
remain_data = remain_data - sent_bytes;
usleep(USLEEP_TIME);
}
}
printf("Station %d handle exited\n", info->station_num);
/* Free \ close all resources */
close(info->socket_fd);
fclose(fp);
free(info);
return NULL;
}
I'll be glad to get some help.
Thanks guys
Well, as stated by user362924 the main issue is that i don't join the threads in my main thread, therefore not allowing them enough time to exit.
A workaround to the matter, if for some reason one wouldn't want to join all threads and dynamically manage thread id's, is to use sleep command in the end of the main thread, for a couple of seconds.
of course this workaround is not good practice and not recommended (to anyone who gets here by google)
my code is only using in one producer-one consumer situation.
here is my test code:
static void *afunc(void * arg) {
Queue* q = arg;
for(int i= 0; i< 100000; i++) {
*queue_pull(q) = i; //get one element space
queue_push(q); //increase the write pointer
}
return NULL;
}
static void *bfunc(void * arg) {
Queue* q = arg;
for(;;) {
int *i = queue_fetch(q); //get the first element in queue
printf("%d\n", *i);
queue_pop(q); //increase the read pointer
}
}
int main() {
Queue queue;
pthread_t a, b;
queue_init(&queue);
pthread_create(&a, NULL, afunc, &queue);
pthread_create(&b, NULL, bfunc, &queue);
sleep(100000);
return 0;
}
and here is the implementation of the circular queue
#define MAX_QUEUE_SIZE 3
typedef struct Queue{
int data[MAX_QUEUE_SIZE] ;
int read,write;
pthread_mutex_t mutex, mutex2;
pthread_cond_t not_empty, not_full;
}Queue;
int queue_init(Queue *queue) {
memset(queue, 0, sizeof(Queue));
pthread_mutex_init(&queue->mutex, NULL);
pthread_cond_init(&queue->not_empty, NULL);
pthread_mutex_init(&queue->mutex2, NULL);
pthread_cond_init(&queue->not_full, NULL);
return 0;
}
int* queue_fetch(Queue *queue) {
int* ret;
if (queue->read == queue->write) {
pthread_mutex_lock(&queue->mutex);
pthread_cond_wait(&queue->not_empty, &queue->mutex);
pthread_mutex_unlock(&queue->mutex);
}
ret = &(queue->data[queue->read]);
return ret;
}
void queue_pop(Queue *queue) {
nx_atomic_set(queue->read, (queue->read+1)%MAX_QUEUE_SIZE);
pthread_cond_signal(&queue->not_full);
}
int* queue_pull(Queue *queue) {
int* ret;
if ((queue->write+1)%MAX_QUEUE_SIZE == queue->read) {
pthread_mutex_lock(&queue->mutex2);
pthread_cond_wait(&queue->not_full, &queue->mutex2);
pthread_mutex_unlock(&queue->mutex2);
}
ret = &(queue->data[queue->write]);
return ret;
}
void queue_push(Queue *queue) {
nx_atomic_set(queue->write, (queue->write+1)%MAX_QUEUE_SIZE);
pthread_cond_signal(&queue->not_empty);
}
after a few moments, it seems the two child threads will turn into deadlock..
EDIT: i use two semaphore, but it also has some problem.. it's pretty
weird, if if just execute ./main, it seems fine, but if i redirect into a file, like ./main > a.txt, then wc -l a.txt, the result is not equal the enqueue number..
int queue_init(Queue *queue) {
memset(queue, 0, sizeof(Queue));
pthread_mutex_init(&queue->mutex, NULL);
sem_unlink("/not_empty");
queue->not_empty = sem_open("/not_empty", O_CREAT, 644, 0);
sem_unlink("/not_full");
queue->not_full = sem_open("/not_full", O_CREAT, 644, MAX_QUEUE_SIZE);
return 0;
}
int* queue_fetch(Queue *queue) {
sem_wait(queue->not_empty);
return &(queue->data[queue->read]);
}
void queue_pop(Queue *queue) {
nx_atomic_set(queue->read, (queue->read+1)%MAX_QUEUE_SIZE);
sem_post(queue->not_full);
}
int* queue_pull(Queue *queue) {
sem_wait(queue->not_full);
return &(queue->data[queue->write]);
}
void queue_push(Queue *queue) {
nx_atomic_set(queue->write, (queue->write+1)%MAX_QUEUE_SIZE);
sem_post(queue->not_empty);
}
You are manipulating the state of the queue outside the mutex, this is inherently racey.
I would suggest using a single mutex, but take it whenever you change or test the read & write indicies. This also means that you don't need the atomic sets.
Quite possibly one of your threads is waiting for a condition to be signalled after the signalling has occurred, causing both threads to wait for each other indefinitely.
Pthreads condition variables don't remain signalled -- the signalling is a momentary action. The condition variable isn't used determine whether to wait -- it's just used to wake up a thread that's already waiting; you need a different means for determining whether or not to wait, such as checking a flag or some sort of test condition.
Normally, you signal as follows:
Lock the mutex
Do your updates, generally leaving your test condition 'true' (eg. setting your flag)
Call pthread_cond_signal() or pthread_cond_broadcast()
Unlock the mutex
...and wait as follows:
Lock the mutex
Loop until your test expression is 'true' (eg. until your flag is set), calling pthread_cond_wait() only if the test is false (inside the loop).
After the loop, when your test has succeeded, do your work.
Unlock the mutex
For example, signalling might go something like this:
pthread_mutex_lock(&mtx); /* 1: lock mutex */
do_something_important(); /* 2: do your work... */
ready_flag = 1; /* ...and set the flag */
pthread_cond_signal(&cond); /* 3: signal the condition (before unlocking) */
pthread_mutex_unlock(&mtx); /* 4: unlock mutex */
and waiting might be something like this:
pthread_mutex_lock(&mtx); /* 1: lock mutex */
while (ready_flag == 0) /* 2: Loop until flag is set... */
pthread_cond_wait(&cond, &mtx); /* ...waiting when it isn't */
do_something_else(); /* 3: Do your work... */
ready_flag = 0; /* ...and clear the flag if it's all done */
pthread_mutex_unlock(&mtx); /* 4: unlock mutex */
The waiter won't miss the condition this way, because the mutex ensures that the waiter's test-and-wait and the signaller's set-and-signal cannot occur simultaneously.
This section of your queue_fetch() function:
if (queue->read == queue->write) {
pthread_mutex_lock(&queue->mutex);
pthread_cond_wait(&queue->not_empty, &queue->mutex);
pthread_mutex_unlock(&queue->mutex);
}
ret = &(queue->data[queue->read]);
..might be rewritten as follows:
pthread_mutex_lock(&queue->mutex);
while (queue->read == queue->write)
pthread_cond_wait(&queue->not_empty, &queue->mutex);
ret = &(queue->data[queue->read]);
pthread_mutex_unlock(&queue->mutex);
...where:
The lock/unlock of the mutex are moved around the if, so the mutex is held while the test expression is evaluated, and still held until the condition wait starts
The if is changed to a while in case the condition wait is prematurely interrupted
Access to queue->read and queue->write is done with the mutex held
Similar changes would be made to queue_pull().
As for the signalling code, the following section of queue_pop():
nx_atomic_set(queue->read, (queue->read+1)%MAX_QUEUE_SIZE);
pthread_cond_signal(&queue->not_full);
..might be changed to:
pthread_mutex_lock(&queue->mutex);
queue->read = (queue->read + 1) % MAX_QUEUE_SIZE;
pthread_cond_signal(&queue->not_full);
pthread_mutex_unlock(&queue->mutex);
..where:
The mutex is held while signalling the condition (this ensures the condition can't be signalled between the waiter deciding whether to wait and actually starting to wait, since the waiter would hold the mutex during that interval)
The mutex is held while changing queue->read as well rather than using nx_atomic_set() since the mutex is needed when signalling the condition anyway
Similar changes would be made to queue_push().
Additionally, you should just use a single mutex (so that the same mutex is always held when accessing read and write), and once the while loops are added to the condition waits there's little compelling reason to use more than one condition variable. If switching to a single condition variable, just signal the condition again after completing a wait:
pthread_mutex_lock(&queue->mutex);
while (queue->read == queue->write) {
pthread_cond_wait(&queue->cond, &queue->mutex);
pthread_cond_signal(&queue->cond); /* <-- signal next waiter, if any */
}
ret = &(queue->data[queue->read]);
pthread_mutex_unlock(&queue->mutex);
I would like to wake up a pthread from another pthread - but after some time. I know signal or pthread_signal with pthread_cond_wait can be used to wake another thread, but I can't see a way to schedule this. The situation would be something like:
THREAD 1:
========
while(1)
recv(low priority msg);
dump msg to buffer
THREAD 2:
========
while(1)
recv(high priority msg);
..do a little bit of processing with msg ..
dump msg to buffer
wake(THREAD3, 5-seconds-later); <-- **HOW TO DO THIS? **
//let some msgs collect for at least a 5 sec window.
//i.e.,Don't wake thread3 immediately for every msg rcvd.
THREAD 3:
=========
while(1)
do some stuff ..
Process all msgs in buffer
sleep(60 seconds).
Any simple way to schedule a wakeup (short of creating a 4th thread that wakes up every second and decides if there is a scheduled entry for thread-3 to wakeup). I really don't want to wakeup thread-3 frequently if there are only low priority msgs in queue. Also, since the messages come in bursts (say 1000 high priority messages in a single burst), I don't want to wake up thread-3 for every single message. It really slows things down (as there is a bunch of other processing stuff it does every time it wakes up).
I am using an ubuntu pc.
How about the use of the pthread_cond_t object available through the pthread API ?
You could share such an object within your threads and let them act on it appropriately.
The resulting code should look like this :
/*
* I lazily chose to make it global.
* You could dynamically allocate the memory for it
* And share the pointer between your threads in
* A data structure through the argument pointer
*/
pthread_cond_t cond_var;
pthread_mutex_t cond_mutex;
int wake_up = 0;
/* To call before creating your threads: */
int err;
if (0 != (err = pthread_cond_init(&cond_var, NULL))) {
/* An error occurred, handle it nicely */
}
if (0 != (err = pthread_mutex_init(&cond_mutex, NULL))) {
/* Error ! */
}
/*****************************************/
/* Within your threads */
void *thread_one(void *arg)
{
int err = 0;
/* Remember you can embed the cond_var
* and the cond_mutex in
* Whatever you get from arg pointer */
/* Some work */
/* Argh ! I want to wake up thread 3 */
pthread_mutex_lock(&cond_mutex);
wake_up = 1; // Tell thread 3 a wake_up rq has been done
pthread_mutex_unlock(&cond_mutex);
if (0 != (err = pthread_cond_broadcast(&cond_var))) {
/* Oops ... Error :S */
} else {
/* Thread 3 should be alright now ! */
}
/* Some work */
pthread_exit(NULL);
return NULL;
}
void *thread_three(void *arg)
{
int err;
/* Some work */
/* Oh, I need to sleep for a while ...
* I'll wait for thread_one to wake me up. */
pthread_mutex_lock(&cond_mutex);
while (!wake_up) {
err = pthread_cond_wait(&cond_var, &cond_mutex);
pthread_mutex_unlock(&cond_mutex);
if (!err || ETIMEDOUT == err) {
/* Woken up or time out */
} else {
/* Oops : error */
/* We might have to break the loop */
}
/* We lock the mutex again before the test */
pthread_mutex_lock(&cond_mutex);
}
/* Since we have acknowledged the wake_up rq
* We set "wake_up" to 0. */
wake_up = 0;
pthread_mutex_unlock(&cond_mutex);
/* Some work */
pthread_exit(NULL);
return NULL;
}
If you want your thread 3 to exit the blocking call to pthread_cond_wait() after a timeout, consider using pthread_cond_timedwait() instead (read the man carefully, the timeout value you supply is the ABSOLUTE time, not the amount of time you don't want to exceed).
If the timeout expires, pthread_cond_timedwait() will return an ETIMEDOUT error.
EDIT : I skipped error checking in the lock / unlock calls, don't forget to handle this potential issue !
EDIT² : I reviewed the code a little bit
You can have the woken thread do the wait itself. In the waking thread:
pthread_mutex_lock(&lock);
if (!wakeup_scheduled) {
wakeup_scheduled = 1;
wakeup_time = time() + 5;
pthread_cond_signal(&cond);
}
pthread_mutex_unlock(&lock);
In the waiting thread:
pthread_mutex_lock(&lock);
while (!wakeup_scheduled)
pthread_cond_wait(&cond, &lock);
pthread_mutex_unlock(&lock);
sleep_until(wakeup_time);
pthread_mutex_lock(&lock);
wakeup_scheduled = 0;
pthread_mutex_unlock(&lock);
Why not just compare the current time to one save earlier?
time_t last_uncond_wakeup = time(NULL);
time_t last_recv = 0;
while (1)
{
if (recv())
{
// Do things
last_recv = time(NULL);
}
// Possible other things
time_t now = time(NULL);
if ((last_recv != 0 && now - last_recv > 5) ||
(now - last_uncond_wakeup > 60))
{
wake(thread3);
last_uncond_wakeup = now;
last_recv = 0;
}
}