1 /* GLIB - Library of useful routines for C programming
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
4 * GAsyncQueue: thread pool implementation.
5 * Copyright (C) 2000 Sebastian Wilhelmi; University of Karlsruhe
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
29 typedef struct _GRealThreadPool GRealThreadPool;
31 struct _GRealThreadPool
42 /* The following is just an address to mark the stop order for a
43 * thread, it could be any address (as long, as it isn;t a valid
44 * GThreadPool address) */
45 static const gpointer stop_this_thread_marker = (gpointer) &g_thread_pool_new;
47 /* Here all unused threads are waiting, depending on their priority */
48 static GAsyncQueue *unused_thread_queue[G_THREAD_PRIORITY_URGENT + 1];
49 static gint unused_threads = 0;
50 static gint max_unused_threads = 0;
51 G_LOCK_DEFINE_STATIC (unused_threads);
53 static GMutex *inform_mutex = NULL;
54 static GCond *inform_cond = NULL;
56 static void g_thread_pool_free_internal (GRealThreadPool* pool);
57 static void g_thread_pool_thread_proxy (gpointer data);
58 static void g_thread_pool_start_thread (GRealThreadPool* pool, GError **error);
59 static void g_thread_pool_wakeup_and_stop_all (GRealThreadPool* pool);
61 #define g_thread_should_run(pool, len) \
62 ((pool)->running || (!(pool)->immediate && (len) > 0))
65 g_thread_pool_thread_proxy (gpointer data)
67 GRealThreadPool *pool = data;
69 g_async_queue_lock (pool->queue);
73 gboolean goto_global_pool = !pool->pool.exclusive;
74 gint len = g_async_queue_length_unlocked (pool->queue);
76 if (g_thread_should_run (pool, len))
78 task = g_async_queue_pop_unlocked (pool->queue);
80 if (pool->num_threads > pool->max_threads && pool->max_threads != -1)
81 /* We are in fact a superfluous threads, so we go to the
82 * global pool and just hand the data further to the next one
83 * waiting in the queue */
85 g_async_queue_push_unlocked (pool->queue, task);
86 goto_global_pool = TRUE;
88 else if (pool->running || !pool->immediate)
90 g_async_queue_unlock (pool->queue);
91 pool->pool.thread_func (task, pool->pool.user_data);
92 g_async_queue_lock (pool->queue);
95 len = g_async_queue_length_unlocked (pool->queue);
98 if (!g_thread_should_run (pool, len))
99 g_cond_broadcast (inform_cond);
101 if (!pool->running && (pool->immediate || len <= 0))
102 goto_global_pool = TRUE;
104 /* At this pool there is no thread waiting */
105 goto_global_pool = FALSE;
107 if (goto_global_pool)
109 GThreadPriority priority = pool->pool.priority;
112 if (!pool->running && !pool->waiting)
114 if (pool->num_threads == 0)
116 g_async_queue_unlock (pool->queue);
117 g_thread_pool_free_internal (pool);
119 else if (len == - pool->num_threads)
120 g_thread_pool_wakeup_and_stop_all (pool);
123 g_async_queue_unlock (pool->queue);
125 g_async_queue_lock (unused_thread_queue[priority]);
127 G_LOCK (unused_threads);
128 if (unused_threads >= max_unused_threads && max_unused_threads != -1)
130 G_UNLOCK (unused_threads);
131 g_async_queue_unlock (unused_thread_queue[priority]);
132 /* Stop this thread */
136 G_UNLOCK (unused_threads);
139 g_async_queue_pop_unlocked (unused_thread_queue[priority]);
141 G_LOCK (unused_threads);
143 G_UNLOCK (unused_threads);
145 g_async_queue_unlock (unused_thread_queue[priority]);
147 if (pool == stop_this_thread_marker)
148 /* Stop this thread */
151 g_async_queue_lock (pool->queue);
153 /* pool->num_threads++ is not done here, but in
154 * g_thread_pool_start_thread to make the new started thread
155 * known to the pool, before itself can do it. */
161 g_thread_pool_start_thread (GRealThreadPool *pool,
164 gboolean success = FALSE;
165 GThreadPriority priority = pool->pool.priority;
166 GAsyncQueue *queue = unused_thread_queue[priority];
168 if (pool->num_threads >= pool->max_threads && pool->max_threads != -1)
169 /* Enough threads are already running */
172 g_async_queue_lock (queue);
174 if (g_async_queue_length_unlocked (queue) < 0)
176 /* First we try a thread with the right priority */
177 g_async_queue_push_unlocked (queue, pool);
181 g_async_queue_unlock (queue);
183 /* We will not search for threads with other priorities, because changing
184 * priority is quite unportable */
188 GError *local_error = NULL;
189 /* No thread was found, we have to start one new */
190 g_thread_create (g_thread_pool_thread_proxy, pool,
191 pool->pool.stack_size, FALSE,
192 pool->pool.bound, priority, &local_error);
196 g_propagate_error (error, local_error);
201 /* See comment in g_thread_pool_thread_proxy as to why this is done
202 * here and not there */
207 g_thread_pool_new (GFunc thread_func,
211 GThreadPriority priority,
216 GRealThreadPool *retval;
218 g_return_val_if_fail (thread_func, NULL);
219 g_return_val_if_fail (!exclusive || max_threads != -1, NULL);
220 g_return_val_if_fail (max_threads >= -1, NULL);
221 g_return_val_if_fail (g_thread_supported (), NULL);
223 retval = g_new (GRealThreadPool, 1);
225 retval->pool.thread_func = thread_func;
226 retval->pool.stack_size = stack_size;
227 retval->pool.bound = bound;
228 retval->pool.priority = priority;
229 retval->pool.exclusive = exclusive;
230 retval->pool.user_data = user_data;
231 retval->queue = g_async_queue_new ();
232 retval->max_threads = max_threads;
233 retval->num_threads = 0;
234 retval->running = TRUE;
238 inform_mutex = g_mutex_new ();
239 inform_cond = g_cond_new ();
240 for (priority = G_THREAD_PRIORITY_LOW;
241 priority < G_THREAD_PRIORITY_URGENT + 1; priority++)
242 unused_thread_queue[priority] = g_async_queue_new ();
245 if (retval->pool.exclusive)
247 g_async_queue_lock (retval->queue);
249 while (retval->num_threads < retval->max_threads)
251 GError *local_error = NULL;
252 g_thread_pool_start_thread (retval, &local_error);
255 g_propagate_error (error, local_error);
260 g_async_queue_unlock (retval->queue);
263 return (GThreadPool*) retval;
267 g_thread_pool_push (GThreadPool *pool,
271 GRealThreadPool *real = (GRealThreadPool*) pool;
273 g_return_if_fail (real);
275 g_async_queue_lock (real->queue);
279 g_async_queue_unlock (real->queue);
280 g_return_if_fail (real->running);
283 if (!pool->exclusive && g_async_queue_length_unlocked (real->queue) >= 0)
284 /* No thread is waiting in the queue */
285 g_thread_pool_start_thread (real, error);
287 g_async_queue_push_unlocked (real->queue, data);
288 g_async_queue_unlock (real->queue);
292 g_thread_pool_set_max_threads (GThreadPool *pool,
296 GRealThreadPool *real = (GRealThreadPool*) pool;
299 g_return_if_fail (real);
300 g_return_if_fail (real->running);
301 g_return_if_fail (!real->pool.exclusive || max_threads != -1);
302 g_return_if_fail (max_threads >= -1);
304 g_async_queue_lock (real->queue);
306 real->max_threads = max_threads;
309 to_start = real->max_threads - real->num_threads;
311 to_start = g_async_queue_length_unlocked (real->queue);
313 for ( ; to_start > 0; to_start--)
315 GError *local_error = NULL;
316 g_thread_pool_start_thread (real, &local_error);
319 g_propagate_error (error, local_error);
324 g_async_queue_unlock (real->queue);
328 g_thread_pool_get_max_threads (GThreadPool *pool)
330 GRealThreadPool *real = (GRealThreadPool*) pool;
333 g_return_val_if_fail (real, 0);
334 g_return_val_if_fail (real->running, 0);
336 g_async_queue_lock (real->queue);
338 retval = real->max_threads;
340 g_async_queue_unlock (real->queue);
346 g_thread_pool_get_num_threads (GThreadPool *pool)
348 GRealThreadPool *real = (GRealThreadPool*) pool;
351 g_return_val_if_fail (real, 0);
352 g_return_val_if_fail (real->running, 0);
354 g_async_queue_lock (real->queue);
356 retval = real->num_threads;
358 g_async_queue_unlock (real->queue);
364 g_thread_pool_unprocessed (GThreadPool *pool)
366 GRealThreadPool *real = (GRealThreadPool*) pool;
369 g_return_val_if_fail (real, 0);
370 g_return_val_if_fail (real->running, 0);
372 unprocessed = g_async_queue_length (real->queue);
374 return MAX (unprocessed, 0);
378 g_thread_pool_free (GThreadPool *pool,
382 GRealThreadPool *real = (GRealThreadPool*) pool;
384 g_return_if_fail (real);
385 g_return_if_fail (real->running);
386 /* It there's no thread allowed here, there is not much sense in
387 * not stopping this pool immediatly, when it's not empty */
388 g_return_if_fail (immediate || real->max_threads != 0 ||
389 g_async_queue_length (real->queue) == 0);
391 g_async_queue_lock (real->queue);
393 real->running = FALSE;
394 real->immediate = immediate;
395 real->waiting = wait;
399 g_mutex_lock (inform_mutex);
400 while (g_async_queue_length_unlocked (real->queue) != -real->num_threads)
402 g_async_queue_unlock (real->queue);
403 g_cond_wait (inform_cond, inform_mutex);
404 g_async_queue_lock (real->queue);
406 g_mutex_unlock (inform_mutex);
409 if (g_async_queue_length_unlocked (real->queue) == -real->num_threads)
411 /* No thread is currently doing something (and nothing is left
412 * to process in the queue) */
413 if (real->num_threads == 0) /* No threads left, we clean up */
415 g_async_queue_unlock (real->queue);
416 g_thread_pool_free_internal (real);
420 g_thread_pool_wakeup_and_stop_all (real);
423 real->waiting = FALSE; /* The last thread should cleanup the pool */
424 g_async_queue_unlock (real->queue);
428 g_thread_pool_free_internal (GRealThreadPool* pool)
430 g_return_if_fail (pool);
431 g_return_if_fail (!pool->running);
432 g_return_if_fail (pool->num_threads == 0);
434 g_async_queue_unref (pool->queue);
440 g_thread_pool_wakeup_and_stop_all (GRealThreadPool* pool)
444 g_return_if_fail (pool);
445 g_return_if_fail (!pool->running);
446 g_return_if_fail (pool->num_threads != 0);
447 g_return_if_fail (g_async_queue_length_unlocked (pool->queue) ==
450 pool->immediate = TRUE;
451 for (i = 0; i < pool->num_threads; i++)
452 g_async_queue_push_unlocked (pool->queue, GUINT_TO_POINTER (1));
456 g_thread_pool_set_max_unused_threads (gint max_threads)
458 g_return_if_fail (max_threads >= -1);
460 G_LOCK (unused_threads);
462 max_unused_threads = max_threads;
464 if (max_unused_threads < unused_threads && max_unused_threads != -1)
466 guint close_down_num = unused_threads - max_unused_threads;
467 GThreadPriority priority;
469 while (close_down_num > 0)
471 guint old_close_down_num = close_down_num;
472 for (priority = G_THREAD_PRIORITY_LOW;
473 priority < G_THREAD_PRIORITY_URGENT + 1 && close_down_num > 0;
476 GAsyncQueue *queue = unused_thread_queue[priority];
477 g_async_queue_lock (queue);
479 if (g_async_queue_length_unlocked (queue) < 0)
481 g_async_queue_push_unlocked (queue,
482 stop_this_thread_marker);
486 g_async_queue_unlock (queue);
489 /* Just to make sure, there are no counting problems */
490 g_assert (old_close_down_num != close_down_num);
494 G_UNLOCK (unused_threads);
498 g_thread_pool_get_max_unused_threads (void)
502 G_LOCK (unused_threads);
503 retval = max_unused_threads;
504 G_UNLOCK (unused_threads);
509 guint g_thread_pool_get_num_unused_threads (void)
513 G_LOCK (unused_threads);
514 retval = unused_threads;
515 G_UNLOCK (unused_threads);
520 void g_thread_pool_stop_unused_threads (void)
522 guint oldval = g_thread_pool_get_max_unused_threads ();
523 g_thread_pool_set_max_unused_threads (0);
524 g_thread_pool_set_max_unused_threads (oldval);