I used a thread pool, the main thread continuous add tasks into a queue, and throw a pthread_cond_signal that indicate the queue is not empty to the work threads. Then the work threads read the queue and throw a pthread_cond_signal indicate the queue is not full to the main thread. And when the program ran, I found the pthread_cond_wait is very slow, if I delete the pthread_cond_wait in function threadpool_add, it's much quicker, but I doubt it may has something wrong, How can I let the threadpool_add function faster? it's the performance bottleneck of my project, Here is the code:
/*
* #function int threadpool_add(threadpool_t *pool, void*(*function)(void *arg), void *arg, char* buf)
* #desc add tasks to the queue
* #param [thread_num] number of thread in pool,
* [queue_max_size] size of the queue for task
*/
int threadpool_add(threadpool_t *pool, void*(*function)(void *arg), void *arg)
{
ulogd_log(ULOGD_NOTICE, "in threadpool_add\n");
if(pool == NULL || function == NULL || arg == NULL)
{
return -1;
}
pthread_mutex_lock(&(pool->lock));
/*
* wait untill queue is not full
*/
while ((pool->queue_size == pool->queue_max_size) && (!pool->shutdown))
{
pthread_cond_wait(&(pool->queue_not_full), &(pool->lock)); // if delete this, it's quicker
}
if (pool->shutdown)
{
pthread_mutex_unlock(&(pool->lock));
return 0;
}
pool->task_queue[pool->queue_rear].function = function;
pool->task_queue[pool->queue_rear].arg = arg;
pool->queue_rear = (pool->queue_rear + 1)%pool->queue_max_size;
pool->queue_size++;
/*
* notify to the threads, there has a task
*/
pthread_cond_signal(&(pool->queue_not_empty));
pthread_mutex_unlock(&(pool->lock));
ulogd_log(ULOGD_NOTICE, "threadpool_add, tpool:0x%x, shutdown: %d, queue_size:%d, queue_not_empty:0x%x\n", pool, pool->shutdown, pool->queue_size, &(pool->queue_not_empty));
return 0;
}
void *threadpool_thread(void *threadpool)
{
threadpool_t *pool = (threadpool_t *)threadpool;
threadpool_task_t task;
while(1)
{
ulogd_log(ULOGD_NOTICE, "in thread 0x%x, tpool: 0x%x\n", pthread_self(), pool);
/* Lock must be taken to wait on conditional variable */
pthread_mutex_lock(&(pool->lock));
while ((pool->queue_size == 0) && (!pool->shutdown))
{
ulogd_log(ULOGD_NOTICE, "thread 0x%x is waiting, queue_size:%d, queue_not_empty:0x%x\n", pthread_self(), pool->queue_size, &(pool->queue_not_empty));
pthread_cond_wait(&(pool->queue_not_empty), &(pool->lock));
}
if (pool->shutdown)
{
pthread_mutex_unlock(&(pool->lock));
ulogd_log(ULOGD_NOTICE,"thread 0x%x is exiting\n", pthread_self());
pthread_exit(NULL);
}
//get a task from queue
task.function = pool->task_queue[pool->queue_front].function;
task.arg = pool->task_queue[pool->queue_front].arg;
pool->queue_front = (pool->queue_front + 1)%pool->queue_max_size;
pool->queue_size--;
//now queue must be not full
pthread_mutex_unlock(&(pool->lock));
pthread_cond_signal(&(pool->queue_not_full));
// Get to work
ulogd_log(ULOGD_NOTICE, "thread 0x%x start working\n", pthread_self());
(*(task.function))(task.arg);
// task run over
ulogd_log(ULOGD_NOTICE, "thread 0x%x end working\n", pthread_self());
}
pthread_exit(NULL);
return (NULL);
}
Related
while(1) {
char message_buffer[SIZE];
ssize_t message_length = mq_receive(mq_identifier, message_buffer, _mqueue_max_msg_size NULL);
if(message_len == -1) { /* error handling... */}
pthread_t pt1;
int ret = pthread_create(&pt1, NULL, handle_message, message_buffer);
if(ret) { /* error handling ... */}
}
void * handle_message (void * message) {
puts((char *) message);
return NULL;
}
The above example is not an MRE but it is extremely simple:
I've got a main thread with a loop that constantly consumes messages from a message queue. Once a new message is received, it is stored in the local message_buffer buffer. Then, a new thread is spawned to "take care" of said new message, and thus the message buffer's address is passed into handle_message, which the new thread subsequently executes.
The problem
Often, 2 threads will print the same message, even though I can verify with a 100% certainty that the messages in the queue were not the same.
I am not completely certain, but I think I understand why this is happening:
say that I push 2 different messages to the mqueue and only then I begin consuming them.
In the first iteration of the while loop, the message will get consumed from the queue and saved to message_buffer. A new thread will get spawned and the address of message_length passed to it. But that thread may not be fast enough to print the buffer's contents to the stream before the next message gets consumed (on the next iteration of the loop), and the contents of message_buffer subsequently overridden. Thus the first and second thread now print the same value.
My question is: what is the most efficient way to solve this? I'm pretty new to parallel programming and threading/pthreads and I'm pretty overwhelmed by the different synchronization primitives.
Mutex trouble
static pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
while(1) {
char message_buffer[SIZE];
pthread_mutex_lock(&m);
ssize_t message_length = mq_receive(mq_identifier, message_buffer, _mqueue_max_msg_size NULL);
pthred_mutex_unlock(&m);
if(message_len == -1) { /* error handling... */}
pthread_t pt1;
int ret = pthread_create(&pt1, NULL, handle_message, message_buffer);
if(ret) { /* error handling ... */}
}
void * handle_message (void * message) {
char own_buffer[SIZE];
pthread_mutex_lock(&m);
strncpy(own_buffer, (char *) message, SIZE);
pthread_mutex_unlock(&m);
puts(own_buffer);
return NULL;
}
I don't think my current mutex implementation is right as the threads are still receiving duplicate messages. The main thread can lock the mutex, consume a message into the buffer, unlock the mutex, spawn a thread, but that thread still may hang and the main one could just rewrite the buffer again (as the buffer mutex was never locked by the new thread), effectively making my current mutex implementation useless? How do I overcome this?
The problem is that you end the loop that contains message_buffer before guaranteeing that the thread has finished with that memory.
while (1) {
char message_buffer[SIZE];
ssize_t message_length = mq_receive(...);
if (message_len == -1) { /* error handling */ }
pthread_t pt1;
int ret = pthread_create(&pt1, NULL, handle_message, message_buffer);
if (ret) { /* error handling */ }
/****** Can't go beyond here until thread is done with message_buffer. ******/
}
void * handle_message (void * message) {
char own_buffer[SIZE];
strncpy(own_buffer, (char *) message, SIZE);
/******* Only now can the caller loop back. ******/
puts(own_buffer);
return NULL;
}
You could use a semaphore or similar.
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
static int copied = 0;
while (1) {
char message_buffer[SIZE];
ssize_t message_length = mq_receive(...);
if (message_len == -1) { /* error handling */ }
pthread_t pt1;
int ret = pthread_create(&pt1, NULL, handle_message, message_buffer);
if (ret) { /* error handling */ }
// Wait until threads is done with message_buffer.
pthread_mutex_lock(&mutex);
while (!copied) pthread_cond_wait(&cond, &mutex);
copied = 0;
pthread_mutex_unlock(&mutex);
}
void * handle_message (void * message) {
char own_buffer[SIZE];
strncpy(own_buffer, (char *) message, SIZE);
// Done with caller's buffer.
// Signal caller to continue.
pthread_mutex_lock(&mutex);
copied = 1;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
puts(own_buffer);
return NULL;
}
(The added chunks effectively perform semaphore operations. See the last snippet of this answer for a more generic implementation.)
But there's a simpler solution: Make the copy before creating the thread.
while (1) {
char message_buffer[SIZE];
ssize_t message_length = mq_receive(...);
if (message_len == -1) { /* error handling */ }
pthread_t pt1;
int ret = pthread_create(&pt1, NULL, handle_message, strdup(message_buffer));
if (ret) { /* error handling */ }
}
void * handle_message (void * message) {
char * own_buffer = message;
puts(own_buffer);
free(own_buffer);
return NULL;
}
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'm having a bit of trouble getting a basic two-thread arrangement working.
I am reading a chunk of bytes into memory from stdin in one "producer" thread, and processing those bytes in a second "consumer" thread, once those bytes are available. Once the bytes are consumed, the consumer thread goes back to being dormant and the producer thread gets running again.
I am using pthread_cond_wait() and pthread_cond_signal() to have the two threads communicate to each other that data are produced or consumed.
Here is the code for the two threads:
void * produce_bytes(void *t_data)
{
pthread_data_t *d = (pthread_data_t *)t_data;
do {
pthread_mutex_lock(&d->input_lock);
d->n_bytes = fread(d->in_buf, sizeof(unsigned char), BUF_LENGTH_VALUE, stdin);
if (d->n_bytes > 0) {
fprintf(stdout, "PRODUCER ...signaling consumer...\n");
pthread_cond_signal(&d->input_cond);
fprintf(stdout, "PRODUCER ...consumer signaled...\n");
}
pthread_mutex_unlock(&d->input_lock);
} while (d->n_bytes > 0);
return NULL;
}
void * consume_bytes(void *t_data)
{
pthread_data_t *d = (pthread_data_t *)t_data;
pthread_mutex_lock(&d->input_lock);
while (d->n_bytes == 0)
pthread_cond_wait(&d->input_cond, &d->input_lock);
fprintf(stdout, "CONSUMER ...consuming chunk...\n");
d->n_bytes = 0;
fprintf(stdout, "CONSUMER ...chunk consumed...\n");
pthread_mutex_unlock(&d->input_lock);
}
The pthread_data_t is a struct I use to keep track of state:
typedef struct {
pthread_mutex_t input_lock;
pthread_cond_t input_cond;
unsigned char in_buf[BUF_LENGTH_VALUE];
size_t n_bytes;
} pthread_data_t;
I configure variables in my main() function; here is the relevant excerpt:
pthread_t producer_thread = NULL;
pthread_t consumer_thread = NULL;
pthread_data_t *thread_data = NULL;
thread_data = malloc(sizeof(pthread_data_t));
thread_data->n_bytes = 0;
pthread_mutex_init(&(thread_data->input_lock), NULL);
pthread_cond_init(&(thread_data->input_cond), NULL);
pthread_create(&producer_thread, NULL, produce_bytes, (void *) thread_data);
pthread_create(&consumer_thread, NULL, consume_bytes, (void *) thread_data);
pthread_join(producer_thread, NULL);
pthread_join(consumer_thread, NULL);
When I run this, produce_bytes() signals consume_bytes() successfully on the first iteration, but on the second and subsequent iterations, a signal is sent to consume_bytes() and it never gets heard, so the consumer function never gets run again:
PRODUCER ...signaling consumer...
PRODUCER ...consumer signaled...
CONSUMER ...consuming chunk...
CONSUMER ...chunk consumed...
PRODUCER ...signaling consumer...
PRODUCER ...consumer signaled...
PRODUCER ...signaling consumer...
PRODUCER ...consumer signaled...
PRODUCER ...signaling consumer...
PRODUCER ...consumer signaled...
...
I am using the tutorial here as the basis for what I'm trying to do. What I am doing wrong?
There are a few issues with that code:
produce_bytes locks the mutex for the duration of the blocking call to fread. A general rule of thumb for responsive applications is to lock the mutex for as short periods as possible. You may like to read the input into a temporary buffer first, then lock the mutex and copy the data to the buffer shared between threads. Same applies to consume_bytes which holds the mutex while calling fprintf which can block.
produce_bytes in while(d->n_bytes > 0) does not hold the mutex, which is a race condition because consume_bytes assigns a new value to d->n_bytes. Assuming you would like to exit that loop when fread returns 0 (EOF), you need to copy the return value of fread into a local variable not shared between threads and use that as the condition in while(read_bytes > 0)
consume_bytes does not have any loop around it so that it returns after the first condition variable notification. You probably would like to wrap it into a while loop and exit only when EOF (0 bytes) have been read.
Here is a working example which addresses Maxim's point 2 and 3, but not 1 because that is necessary for responsiveness but not strictly for correctness.
Note that I have not implemented a means for the producer to signal EOF to the consumer, so the consumer will never exit.
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define BUF_LENGTH_VALUE 100
typedef struct {
pthread_mutex_t input_lock;
pthread_cond_t input_cond;
unsigned char in_buf[BUF_LENGTH_VALUE];
size_t n_bytes;
} pthread_data_t;
void * produce_bytes(void *t_data)
{
pthread_data_t *d = (pthread_data_t *)t_data;
size_t local_byte_count = 0;
do {
pthread_mutex_lock(&d->input_lock);
local_byte_count = fread(d->in_buf, sizeof(unsigned char),
BUF_LENGTH_VALUE, stdin);
d->n_bytes += local_byte_count;
if (d->n_bytes > 0) {
fprintf(stdout, "PRODUCER ...signaling consumer...\n");
pthread_cond_signal(&d->input_cond);
fprintf(stdout, "PRODUCER ...consumer signaled...\n");
}
pthread_mutex_unlock(&d->input_lock);
// This is added to slow down the producer so that we can observe
// multiple consumptions.
sleep(1);
} while (local_byte_count > 0);
return NULL;
}
void * consume_bytes(void *t_data)
{
pthread_data_t *d = (pthread_data_t *)t_data;
while (1) {
pthread_mutex_lock(&d->input_lock);
while (d->n_bytes == 0) {
fprintf(stdout, "CONSUMER entering wait \n");
pthread_cond_wait(&d->input_cond, &d->input_lock);
}
fprintf(stdout, "CONSUMER ...consuming chunk...\n");
d->n_bytes = 0;
fprintf(stdout, "CONSUMER ...chunk consumed...\n");
pthread_mutex_unlock(&d->input_lock);
fflush(stdout);
}
}
int main(){
pthread_t producer_thread = NULL;
pthread_t consumer_thread = NULL;
pthread_data_t *thread_data = NULL;
thread_data = malloc(sizeof(pthread_data_t));
thread_data->n_bytes = 0;
pthread_mutex_init(&(thread_data->input_lock), NULL);
pthread_cond_init(&(thread_data->input_cond), NULL);
pthread_create(&producer_thread, NULL, produce_bytes, (void *) thread_data);
pthread_create(&consumer_thread, NULL, consume_bytes, (void *) thread_data);
pthread_join(producer_thread, NULL);
pthread_join(consumer_thread, NULL);
}
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;
}
}
I wrote following code:
void *produce(void* arg)
{
buffer* buff = (buffer *) arg;
while (1)
{
pthread_mutex_lock(&mutex);
if (elements_produced == JOB_SIZE)
{
pthread_mutex_unlock(&mutex);
pthread_exit(NULL);
}
elements_produced++;
while (buff->in_buff == CAPACITY)
{
pthread_cond_wait(&cond_empty, &mutex);
}
// produce
buff->buffer[buff->tail] = rand();
sum_produced += buff->buffer[buff->tail];
printf(">produced %d\n", buff->buffer[buff->tail]);
buff->tail = (buff->tail + 1) % CAPACITY;
buff->in_buff++;
pthread_cond_signal(&cond_empty);
pthread_mutex_unlock(&mutex);
}
pthread_exit(NULL);
}
void *consume(void* arg)
{
int rc;
buffer* buff = (buffer *) arg;
while (1)
{
rc = pthread_mutex_lock(&mutex);
if (elements_consumed == JOB_SIZE)
{
pthread_mutex_unlock(&mutex);
pthread_exit(NULL);
return 0;
}
elements_consumed++;
while (buff->in_buff == 0)
{
rc = pthread_cond_wait(&cond_empty, &mutex);
}
// consume
printf("<consumed %d\n", buff->buffer[buff->head]);
sum_consumed += buff->buffer[buff->head];
buff->head = (buff->head + 1) % CAPACITY;
buff->in_buff--;
pthread_cond_signal(&cond_full);
pthread_mutex_unlock(&mutex);
}
pthread_exit(NULL);
return 0;
}
All variables are properly initialized. The task is to produce JOB_SIZE elements and to consume them. From time to time it gets stuck in the dead lock. I am quite new to the posix threads so I am probably missing something very obvious (did producers/consumers many times in java/C#/python but now I am really stuck). I know it is much easier to do it with semaphores but I need to do it in this way.
Any suggestions?
You used cond_empty in both sides for the wait. You signal (but never wait on) cond_full.