on 15/05/2008 22:51 Brent Casavant said the following:
On Thu, 15 May 2008, Andriy Gapon wrote:
With current libthr behavior the GUI thread would never have a chance to get
the mutex as worker thread would always be a winner (as my small program
shows).
The example you gave indicates an incorrect mechanism being used for the
GUI to communicate with this worker thread. For the behavior you desire,
you need a common condition that lets both the GUI and the work item
generator indicate that there is something for the worker to do, *and*
you need seperate mechanisms for the GUI and work item generator to add
to their respective queues.
Brent,
that was just an example. Probably a quite bad example.
I should only limit myself to the program that I sent and I should
repeat that the result that it produces is not what I would call
reasonably expected. And I will repeat that I understand that the
behavior is not prohibited by standards (well, never letting other
threads to run is probably not prohibited either).
Something like this (could be made even better with a little effor):
struct worker_queues_s {
pthread_mutex_t work_mutex;
struct work_queue_s work_queue;
pthread_mutex_t gui_mutex;
struct gui_queue_s gui_queue;
pthread_mutex_t stuff_mutex;
int stuff_todo;
pthread_cond_t stuff_cond;
};
struct worker_queue_s wq;
int
main(int argc, char *argv[]) {
// blah blah
init_worker_queue(&wq);
// blah blah
}
void
gui_callback(...) {
// blah blah
// Set up GUI message
pthread_mutex_lock(&wq.gui_mutex);
// Add GUI message to queue
pthread_mutex_unlock(&wq.gui_mutex);
pthread_mutex_lock(&wq.stuff_mutex);
wq.stuff_todo++;
pthread_cond_signal(&wq.stuff_cond);
pthread_mutex_unlock(&wq.stuff_mutex);
// blah blah
}
void*
work_generator_thread(void*) {
// blah blah
while (1) {
// Set up work to do
pthread_mutex_lock(&wq.work_mutex);
// Add work item to queue
pthread_mutex_unlock(&wq.work_mutex);
pthread_mutex_lock(&wq.stuff_mutex);
wq.stuff_todo++;
pthread_cond_signal(&wq.stuff_cond);
pthread_mutex_unlock(&wq.stuff_mutex);
}
// blah blah
}
void*
worker_thread(void* arg) {
// blah blah
while (1) {
// Wait for there to be something to do
pthread_mutex_lock(&wq.stuff_mutex);
while (wq.stuff_todo < 1) {
pthread_cond_wait(&wq.stuff_cond,
&wq.stuff_mutex);
}
pthread_mutex_unlock(&wq.stuff_mutex);
// Handle GUI messages
pthread_mutex_lock(&wq.gui_mutex);
while (!gui_queue_empty(&wq.gui_queue) {
// dequeue and process GUI messages
pthread_mutex_lock(&wq.stuff_mutex);
wq.stuff_todo--;
pthread_mutex_unlock(&wq.stuff_mutex);
}
pthread_mutex_unlock(&wq.gui_mutex);
// Handle work items
pthread_mutex_lock(&wq.work_mutex);
while (!work_queue_empty(&wq.work_queue)) {
// dequeue and process work item
pthread_mutex_lock(&wq.stuff_mutex);
wq.stuff_todo--;
pthread_mutex_unlock(&wq.stuff_mutex);
}
pthread_mutex_unlock(&wq.work_mutex);
}
// blah blah
}
This should accomplish what you desire. Caution that I haven't
compiled, run, or tested it, but I'm pretty sure it's a solid
solution.
The key here is unifying the two input sources (the GUI and work queues)
without blocking on either one of them individually. The value of
(wq.stuff_todo < 1) becomes a proxy for the value of
(gui_queue_empty(...) && work_queue_empty(...)).
I hope that helps,
Brent
--
Andriy Gapon
_______________________________________________
freebsd-stable@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-stable
To unsubscribe, send any mail to "[EMAIL PROTECTED]"