Antoninus Twink said:
The simplest arrangement would just be to have a single mutex protecting
the queue.
To add a message to be logged, the first thread acquires the mutex,
enqueues the string, increments number_of_things_in_queue, and then
releases the mutex.
Meanwhile, the logger thread periodically tries to acquire the mutex.
When it's holding the mutex, it checks to see whether
number_of_things_in_queue is > 0. If so, it dequeues a string and
decrements number_of_things_in_queue. Then it releases the mutex.
[...]
FWIW, you don't actually "need" any kind of counter in there unless you are
enforcing a limit on the number of items that can be in the queue.
Something simple like this will suffice:
<pseudo-code typed in news reader>
_____________________________________________________________
struct node
{
struct node* next;
};
struct queue
{
struct node* head; /* = NULL */
struct node* tail;
};
void
queue_push(struct queue* const self,
struct node* node)
{
node->next = NULL;
if (! self->head)
{
self->head = node;
}
else
{
self->tail->node;
}
self->tail = node;
}
struct node*
queue_pop(struct queue* const self)
{
struct node* node = self->head;
if (node)
{
self->head = node->next;
}
return node;
}
/* thread-safe queue */
struct tsqueue
{
struct queue queue;
pthread_mutex_t mutex;
pthread_cond_t cond;
};
void
tsqueue_push(struct tsqueue* const self,
struct node* node)
{
pthread_mutex_lock(&self->mutex);
queue_push(&self->queue, node);
pthread_mutex_unlock(&self->mutex);
pthread_cond_signal(&self->cond);
}
struct node*
tsqueue_try_pop(struct tsqueue* const self)
{
struct node* node;
pthread_mutex_lock(&self->mutex);
node = queue_pop(&self->queue);
pthread_mutex_unlock(&self->mutex);
return node;
}
struct node*
tsqueue_wait_pop(struct tsqueue* const self)
{
struct node* node;
pthread_mutex_lock(&self->mutex);
while (! (node = queue_pop(&self->queue)))
{
pthread_cond_wait(&self->cond, &self->mutex);
}
pthread_mutex_unlock(&self->mutex);
return node;
}
_____________________________________________________________
Of course you don't need any mutexs if you create a distributed design. One
that I personally like for logging is each application thread has a
single-producer/consumer queue, and the logger thread
periodically/episodically polls those queues. Another way is to use a
multi-producer/single-consumer queue, which does not need locks.