/src/irssi/subprojects/glib-2.74.7/glib/gthreadpool.c
Line | Count | Source |
1 | | /* GLIB - Library of useful routines for C programming |
2 | | * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald |
3 | | * |
4 | | * GThreadPool: thread pool implementation. |
5 | | * Copyright (C) 2000 Sebastian Wilhelmi; University of Karlsruhe |
6 | | * |
7 | | * SPDX-License-Identifier: LGPL-2.1-or-later |
8 | | * |
9 | | * This library is free software; you can redistribute it and/or |
10 | | * modify it under the terms of the GNU Lesser General Public |
11 | | * License as published by the Free Software Foundation; either |
12 | | * version 2.1 of the License, or (at your option) any later version. |
13 | | * |
14 | | * This library is distributed in the hope that it will be useful, |
15 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
17 | | * Lesser General Public License for more details. |
18 | | * |
19 | | * You should have received a copy of the GNU Lesser General Public |
20 | | * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
21 | | */ |
22 | | |
23 | | /* |
24 | | * MT safe |
25 | | */ |
26 | | |
27 | | #include "config.h" |
28 | | |
29 | | #include "gthreadpool.h" |
30 | | |
31 | | #include "gasyncqueue.h" |
32 | | #include "gasyncqueueprivate.h" |
33 | | #include "gmain.h" |
34 | | #include "gtestutils.h" |
35 | | #include "gthreadprivate.h" |
36 | | #include "gtimer.h" |
37 | | #include "gutils.h" |
38 | | |
39 | | /** |
40 | | * SECTION:thread_pools |
41 | | * @title: Thread Pools |
42 | | * @short_description: pools of threads to execute work concurrently |
43 | | * @see_also: #GThread |
44 | | * |
45 | | * Sometimes you wish to asynchronously fork out the execution of work |
46 | | * and continue working in your own thread. If that will happen often, |
47 | | * the overhead of starting and destroying a thread each time might be |
48 | | * too high. In such cases reusing already started threads seems like a |
49 | | * good idea. And it indeed is, but implementing this can be tedious |
50 | | * and error-prone. |
51 | | * |
52 | | * Therefore GLib provides thread pools for your convenience. An added |
53 | | * advantage is, that the threads can be shared between the different |
54 | | * subsystems of your program, when they are using GLib. |
55 | | * |
56 | | * To create a new thread pool, you use g_thread_pool_new(). |
57 | | * It is destroyed by g_thread_pool_free(). |
58 | | * |
59 | | * If you want to execute a certain task within a thread pool, |
60 | | * you call g_thread_pool_push(). |
61 | | * |
62 | | * To get the current number of running threads you call |
63 | | * g_thread_pool_get_num_threads(). To get the number of still |
64 | | * unprocessed tasks you call g_thread_pool_unprocessed(). To control |
65 | | * the maximal number of threads for a thread pool, you use |
66 | | * g_thread_pool_get_max_threads() and g_thread_pool_set_max_threads(). |
67 | | * |
68 | | * Finally you can control the number of unused threads, that are kept |
69 | | * alive by GLib for future use. The current number can be fetched with |
70 | | * g_thread_pool_get_num_unused_threads(). The maximal number can be |
71 | | * controlled by g_thread_pool_get_max_unused_threads() and |
72 | | * g_thread_pool_set_max_unused_threads(). All currently unused threads |
73 | | * can be stopped by calling g_thread_pool_stop_unused_threads(). |
74 | | */ |
75 | | |
76 | | #define DEBUG_MSG(x) |
77 | | /* #define DEBUG_MSG(args) g_printerr args ; g_printerr ("\n"); */ |
78 | | |
79 | | typedef struct _GRealThreadPool GRealThreadPool; |
80 | | |
81 | | /** |
82 | | * GThreadPool: |
83 | | * @func: the function to execute in the threads of this pool |
84 | | * @user_data: the user data for the threads of this pool |
85 | | * @exclusive: are all threads exclusive to this pool |
86 | | * |
87 | | * The #GThreadPool struct represents a thread pool. It has three |
88 | | * public read-only members, but the underlying struct is bigger, |
89 | | * so you must not copy this struct. |
90 | | */ |
91 | | struct _GRealThreadPool |
92 | | { |
93 | | GThreadPool pool; |
94 | | GAsyncQueue *queue; |
95 | | GCond cond; |
96 | | gint max_threads; |
97 | | guint num_threads; |
98 | | gboolean running; |
99 | | gboolean immediate; |
100 | | gboolean waiting; |
101 | | GCompareDataFunc sort_func; |
102 | | gpointer sort_user_data; |
103 | | }; |
104 | | |
105 | | /* The following is just an address to mark the wakeup order for a |
106 | | * thread, it could be any address (as long, as it isn't a valid |
107 | | * GThreadPool address) |
108 | | */ |
109 | | static const gpointer wakeup_thread_marker = (gpointer) &g_thread_pool_new; |
110 | | static gint wakeup_thread_serial = 0; |
111 | | |
112 | | /* Here all unused threads are waiting */ |
113 | | static GAsyncQueue *unused_thread_queue = NULL; |
114 | | static gint unused_threads = 0; |
115 | | static gint max_unused_threads = 2; |
116 | | static gint kill_unused_threads = 0; |
117 | | static guint max_idle_time = 15 * 1000; |
118 | | |
119 | | static GThreadSchedulerSettings shared_thread_scheduler_settings; |
120 | | static gboolean have_shared_thread_scheduler_settings = FALSE; |
121 | | |
122 | | typedef struct |
123 | | { |
124 | | /* Either thread or error are set in the end. Both transfer-full. */ |
125 | | GThreadPool *pool; |
126 | | GThread *thread; |
127 | | GError *error; |
128 | | } SpawnThreadData; |
129 | | |
130 | | static GCond spawn_thread_cond; |
131 | | static GAsyncQueue *spawn_thread_queue; |
132 | | |
133 | | static void g_thread_pool_queue_push_unlocked (GRealThreadPool *pool, |
134 | | gpointer data); |
135 | | static void g_thread_pool_free_internal (GRealThreadPool *pool); |
136 | | static gpointer g_thread_pool_thread_proxy (gpointer data); |
137 | | static gboolean g_thread_pool_start_thread (GRealThreadPool *pool, |
138 | | GError **error); |
139 | | static void g_thread_pool_wakeup_and_stop_all (GRealThreadPool *pool); |
140 | | static GRealThreadPool* g_thread_pool_wait_for_new_pool (void); |
141 | | static gpointer g_thread_pool_wait_for_new_task (GRealThreadPool *pool); |
142 | | |
143 | | static void |
144 | | g_thread_pool_queue_push_unlocked (GRealThreadPool *pool, |
145 | | gpointer data) |
146 | 0 | { |
147 | 0 | if (pool->sort_func) |
148 | 0 | g_async_queue_push_sorted_unlocked (pool->queue, |
149 | 0 | data, |
150 | 0 | pool->sort_func, |
151 | 0 | pool->sort_user_data); |
152 | 0 | else |
153 | 0 | g_async_queue_push_unlocked (pool->queue, data); |
154 | 0 | } |
155 | | |
156 | | static GRealThreadPool* |
157 | | g_thread_pool_wait_for_new_pool (void) |
158 | 0 | { |
159 | 0 | GRealThreadPool *pool; |
160 | 0 | gint local_wakeup_thread_serial; |
161 | 0 | guint local_max_unused_threads; |
162 | 0 | gint local_max_idle_time; |
163 | 0 | gint last_wakeup_thread_serial; |
164 | 0 | gboolean have_relayed_thread_marker = FALSE; |
165 | |
|
166 | 0 | local_max_unused_threads = (guint) g_atomic_int_get (&max_unused_threads); |
167 | 0 | local_max_idle_time = g_atomic_int_get (&max_idle_time); |
168 | 0 | last_wakeup_thread_serial = g_atomic_int_get (&wakeup_thread_serial); |
169 | |
|
170 | 0 | do |
171 | 0 | { |
172 | 0 | if ((guint) g_atomic_int_get (&unused_threads) >= local_max_unused_threads) |
173 | 0 | { |
174 | | /* If this is a superfluous thread, stop it. */ |
175 | 0 | pool = NULL; |
176 | 0 | } |
177 | 0 | else if (local_max_idle_time > 0) |
178 | 0 | { |
179 | | /* If a maximal idle time is given, wait for the given time. */ |
180 | 0 | DEBUG_MSG (("thread %p waiting in global pool for %f seconds.", |
181 | 0 | g_thread_self (), local_max_idle_time / 1000.0)); |
182 | |
|
183 | 0 | pool = g_async_queue_timeout_pop (unused_thread_queue, |
184 | 0 | local_max_idle_time * 1000); |
185 | 0 | } |
186 | 0 | else |
187 | 0 | { |
188 | | /* If no maximal idle time is given, wait indefinitely. */ |
189 | 0 | DEBUG_MSG (("thread %p waiting in global pool.", g_thread_self ())); |
190 | 0 | pool = g_async_queue_pop (unused_thread_queue); |
191 | 0 | } |
192 | |
|
193 | 0 | if (pool == wakeup_thread_marker) |
194 | 0 | { |
195 | 0 | local_wakeup_thread_serial = g_atomic_int_get (&wakeup_thread_serial); |
196 | 0 | if (last_wakeup_thread_serial == local_wakeup_thread_serial) |
197 | 0 | { |
198 | 0 | if (!have_relayed_thread_marker) |
199 | 0 | { |
200 | | /* If this wakeup marker has been received for |
201 | | * the second time, relay it. |
202 | | */ |
203 | 0 | DEBUG_MSG (("thread %p relaying wakeup message to " |
204 | 0 | "waiting thread with lower serial.", |
205 | 0 | g_thread_self ())); |
206 | |
|
207 | 0 | g_async_queue_push (unused_thread_queue, wakeup_thread_marker); |
208 | 0 | have_relayed_thread_marker = TRUE; |
209 | | |
210 | | /* If a wakeup marker has been relayed, this thread |
211 | | * will get out of the way for 100 microseconds to |
212 | | * avoid receiving this marker again. |
213 | | */ |
214 | 0 | g_usleep (100); |
215 | 0 | } |
216 | 0 | } |
217 | 0 | else |
218 | 0 | { |
219 | 0 | if (g_atomic_int_add (&kill_unused_threads, -1) > 0) |
220 | 0 | { |
221 | 0 | pool = NULL; |
222 | 0 | break; |
223 | 0 | } |
224 | | |
225 | 0 | DEBUG_MSG (("thread %p updating to new limits.", |
226 | 0 | g_thread_self ())); |
227 | |
|
228 | 0 | local_max_unused_threads = (guint) g_atomic_int_get (&max_unused_threads); |
229 | 0 | local_max_idle_time = g_atomic_int_get (&max_idle_time); |
230 | 0 | last_wakeup_thread_serial = local_wakeup_thread_serial; |
231 | |
|
232 | 0 | have_relayed_thread_marker = FALSE; |
233 | 0 | } |
234 | 0 | } |
235 | 0 | } |
236 | 0 | while (pool == wakeup_thread_marker); |
237 | |
|
238 | 0 | return pool; |
239 | 0 | } |
240 | | |
241 | | static gpointer |
242 | | g_thread_pool_wait_for_new_task (GRealThreadPool *pool) |
243 | 0 | { |
244 | 0 | gpointer task = NULL; |
245 | |
|
246 | 0 | if (pool->running || (!pool->immediate && |
247 | 0 | g_async_queue_length_unlocked (pool->queue) > 0)) |
248 | 0 | { |
249 | | /* This thread pool is still active. */ |
250 | 0 | if (pool->max_threads != -1 && pool->num_threads > (guint) pool->max_threads) |
251 | 0 | { |
252 | | /* This is a superfluous thread, so it goes to the global pool. */ |
253 | 0 | DEBUG_MSG (("superfluous thread %p in pool %p.", |
254 | 0 | g_thread_self (), pool)); |
255 | 0 | } |
256 | 0 | else if (pool->pool.exclusive) |
257 | 0 | { |
258 | | /* Exclusive threads stay attached to the pool. */ |
259 | 0 | task = g_async_queue_pop_unlocked (pool->queue); |
260 | |
|
261 | 0 | DEBUG_MSG (("thread %p in exclusive pool %p waits for task " |
262 | 0 | "(%d running, %d unprocessed).", |
263 | 0 | g_thread_self (), pool, pool->num_threads, |
264 | 0 | g_async_queue_length_unlocked (pool->queue))); |
265 | 0 | } |
266 | 0 | else |
267 | 0 | { |
268 | | /* A thread will wait for new tasks for at most 1/2 |
269 | | * second before going to the global pool. |
270 | | */ |
271 | 0 | DEBUG_MSG (("thread %p in pool %p waits for up to a 1/2 second for task " |
272 | 0 | "(%d running, %d unprocessed).", |
273 | 0 | g_thread_self (), pool, pool->num_threads, |
274 | 0 | g_async_queue_length_unlocked (pool->queue))); |
275 | |
|
276 | 0 | task = g_async_queue_timeout_pop_unlocked (pool->queue, |
277 | 0 | G_USEC_PER_SEC / 2); |
278 | 0 | } |
279 | 0 | } |
280 | 0 | else |
281 | 0 | { |
282 | | /* This thread pool is inactive, it will no longer process tasks. */ |
283 | 0 | DEBUG_MSG (("pool %p not active, thread %p will go to global pool " |
284 | 0 | "(running: %s, immediate: %s, len: %d).", |
285 | 0 | pool, g_thread_self (), |
286 | 0 | pool->running ? "true" : "false", |
287 | 0 | pool->immediate ? "true" : "false", |
288 | 0 | g_async_queue_length_unlocked (pool->queue))); |
289 | 0 | } |
290 | |
|
291 | 0 | return task; |
292 | 0 | } |
293 | | |
294 | | static gpointer |
295 | | g_thread_pool_spawn_thread (gpointer data) |
296 | 0 | { |
297 | 0 | while (TRUE) |
298 | 0 | { |
299 | 0 | SpawnThreadData *spawn_thread_data; |
300 | 0 | GThread *thread = NULL; |
301 | 0 | GError *error = NULL; |
302 | 0 | const gchar *prgname = g_get_prgname (); |
303 | 0 | gchar name[16] = "pool"; |
304 | |
|
305 | 0 | if (prgname) |
306 | 0 | g_snprintf (name, sizeof (name), "pool-%s", prgname); |
307 | |
|
308 | 0 | g_async_queue_lock (spawn_thread_queue); |
309 | | /* Spawn a new thread for the given pool and wake the requesting thread |
310 | | * up again with the result. This new thread will have the scheduler |
311 | | * settings inherited from this thread and in extension of the thread |
312 | | * that created the first non-exclusive thread-pool. */ |
313 | 0 | spawn_thread_data = g_async_queue_pop_unlocked (spawn_thread_queue); |
314 | 0 | thread = g_thread_try_new (name, g_thread_pool_thread_proxy, spawn_thread_data->pool, &error); |
315 | |
|
316 | 0 | spawn_thread_data->thread = g_steal_pointer (&thread); |
317 | 0 | spawn_thread_data->error = g_steal_pointer (&error); |
318 | |
|
319 | 0 | g_cond_broadcast (&spawn_thread_cond); |
320 | 0 | g_async_queue_unlock (spawn_thread_queue); |
321 | 0 | } |
322 | |
|
323 | 0 | return NULL; |
324 | 0 | } |
325 | | |
326 | | static gpointer |
327 | | g_thread_pool_thread_proxy (gpointer data) |
328 | 0 | { |
329 | 0 | GRealThreadPool *pool; |
330 | |
|
331 | 0 | pool = data; |
332 | |
|
333 | 0 | DEBUG_MSG (("thread %p started for pool %p.", g_thread_self (), pool)); |
334 | |
|
335 | 0 | g_async_queue_lock (pool->queue); |
336 | |
|
337 | 0 | while (TRUE) |
338 | 0 | { |
339 | 0 | gpointer task; |
340 | |
|
341 | 0 | task = g_thread_pool_wait_for_new_task (pool); |
342 | 0 | if (task) |
343 | 0 | { |
344 | 0 | if (pool->running || !pool->immediate) |
345 | 0 | { |
346 | | /* A task was received and the thread pool is active, |
347 | | * so execute the function. |
348 | | */ |
349 | 0 | g_async_queue_unlock (pool->queue); |
350 | 0 | DEBUG_MSG (("thread %p in pool %p calling func.", |
351 | 0 | g_thread_self (), pool)); |
352 | 0 | pool->pool.func (task, pool->pool.user_data); |
353 | 0 | g_async_queue_lock (pool->queue); |
354 | 0 | } |
355 | 0 | } |
356 | 0 | else |
357 | 0 | { |
358 | | /* No task was received, so this thread goes to the global pool. */ |
359 | 0 | gboolean free_pool = FALSE; |
360 | |
|
361 | 0 | DEBUG_MSG (("thread %p leaving pool %p for global pool.", |
362 | 0 | g_thread_self (), pool)); |
363 | 0 | pool->num_threads--; |
364 | |
|
365 | 0 | if (!pool->running) |
366 | 0 | { |
367 | 0 | if (!pool->waiting) |
368 | 0 | { |
369 | 0 | if (pool->num_threads == 0) |
370 | 0 | { |
371 | | /* If the pool is not running and no other |
372 | | * thread is waiting for this thread pool to |
373 | | * finish and this is the last thread of this |
374 | | * pool, free the pool. |
375 | | */ |
376 | 0 | free_pool = TRUE; |
377 | 0 | } |
378 | 0 | else |
379 | 0 | { |
380 | | /* If the pool is not running and no other |
381 | | * thread is waiting for this thread pool to |
382 | | * finish and this is not the last thread of |
383 | | * this pool and there are no tasks left in the |
384 | | * queue, wakeup the remaining threads. |
385 | | */ |
386 | 0 | if (g_async_queue_length_unlocked (pool->queue) == |
387 | 0 | (gint) -pool->num_threads) |
388 | 0 | g_thread_pool_wakeup_and_stop_all (pool); |
389 | 0 | } |
390 | 0 | } |
391 | 0 | else if (pool->immediate || |
392 | 0 | g_async_queue_length_unlocked (pool->queue) <= 0) |
393 | 0 | { |
394 | | /* If the pool is not running and another thread is |
395 | | * waiting for this thread pool to finish and there |
396 | | * are either no tasks left or the pool shall stop |
397 | | * immediately, inform the waiting thread of a change |
398 | | * of the thread pool state. |
399 | | */ |
400 | 0 | g_cond_broadcast (&pool->cond); |
401 | 0 | } |
402 | 0 | } |
403 | |
|
404 | 0 | g_atomic_int_inc (&unused_threads); |
405 | 0 | g_async_queue_unlock (pool->queue); |
406 | |
|
407 | 0 | if (free_pool) |
408 | 0 | g_thread_pool_free_internal (pool); |
409 | |
|
410 | 0 | pool = g_thread_pool_wait_for_new_pool (); |
411 | 0 | g_atomic_int_add (&unused_threads, -1); |
412 | |
|
413 | 0 | if (pool == NULL) |
414 | 0 | break; |
415 | | |
416 | 0 | g_async_queue_lock (pool->queue); |
417 | |
|
418 | 0 | DEBUG_MSG (("thread %p entering pool %p from global pool.", |
419 | 0 | g_thread_self (), pool)); |
420 | | |
421 | | /* pool->num_threads++ is not done here, but in |
422 | | * g_thread_pool_start_thread to make the new started |
423 | | * thread known to the pool before itself can do it. |
424 | | */ |
425 | 0 | } |
426 | 0 | } |
427 | |
|
428 | 0 | return NULL; |
429 | 0 | } |
430 | | |
431 | | static gboolean |
432 | | g_thread_pool_start_thread (GRealThreadPool *pool, |
433 | | GError **error) |
434 | 0 | { |
435 | 0 | gboolean success = FALSE; |
436 | |
|
437 | 0 | if (pool->max_threads != -1 && pool->num_threads >= (guint) pool->max_threads) |
438 | | /* Enough threads are already running */ |
439 | 0 | return TRUE; |
440 | | |
441 | 0 | g_async_queue_lock (unused_thread_queue); |
442 | |
|
443 | 0 | if (g_async_queue_length_unlocked (unused_thread_queue) < 0) |
444 | 0 | { |
445 | 0 | g_async_queue_push_unlocked (unused_thread_queue, pool); |
446 | 0 | success = TRUE; |
447 | 0 | } |
448 | |
|
449 | 0 | g_async_queue_unlock (unused_thread_queue); |
450 | |
|
451 | 0 | if (!success) |
452 | 0 | { |
453 | 0 | const gchar *prgname = g_get_prgname (); |
454 | 0 | gchar name[16] = "pool"; |
455 | 0 | GThread *thread; |
456 | |
|
457 | 0 | if (prgname) |
458 | 0 | g_snprintf (name, sizeof (name), "pool-%s", prgname); |
459 | | |
460 | | /* No thread was found, we have to start a new one */ |
461 | 0 | if (pool->pool.exclusive) |
462 | 0 | { |
463 | | /* For exclusive thread-pools this is directly called from new() and |
464 | | * we simply start new threads that inherit the scheduler settings |
465 | | * from the current thread. |
466 | | */ |
467 | 0 | thread = g_thread_try_new (name, g_thread_pool_thread_proxy, pool, error); |
468 | 0 | } |
469 | 0 | else |
470 | 0 | { |
471 | | /* For non-exclusive thread-pools this can be called at any time |
472 | | * when a new thread is needed. We make sure to create a new thread |
473 | | * here with the correct scheduler settings: either by directly |
474 | | * providing them if supported by the GThread implementation or by |
475 | | * going via our helper thread. |
476 | | */ |
477 | 0 | if (have_shared_thread_scheduler_settings) |
478 | 0 | { |
479 | 0 | thread = g_thread_new_internal (name, g_thread_proxy, g_thread_pool_thread_proxy, pool, 0, &shared_thread_scheduler_settings, error); |
480 | 0 | } |
481 | 0 | else |
482 | 0 | { |
483 | 0 | SpawnThreadData spawn_thread_data = { (GThreadPool *) pool, NULL, NULL }; |
484 | |
|
485 | 0 | g_async_queue_lock (spawn_thread_queue); |
486 | |
|
487 | 0 | g_async_queue_push_unlocked (spawn_thread_queue, &spawn_thread_data); |
488 | |
|
489 | 0 | while (!spawn_thread_data.thread && !spawn_thread_data.error) |
490 | 0 | g_cond_wait (&spawn_thread_cond, _g_async_queue_get_mutex (spawn_thread_queue)); |
491 | |
|
492 | 0 | thread = spawn_thread_data.thread; |
493 | 0 | if (!thread) |
494 | 0 | g_propagate_error (error, g_steal_pointer (&spawn_thread_data.error)); |
495 | 0 | g_async_queue_unlock (spawn_thread_queue); |
496 | 0 | } |
497 | 0 | } |
498 | |
|
499 | 0 | if (thread == NULL) |
500 | 0 | return FALSE; |
501 | | |
502 | 0 | g_thread_unref (thread); |
503 | 0 | } |
504 | | |
505 | | /* See comment in g_thread_pool_thread_proxy as to why this is done |
506 | | * here and not there |
507 | | */ |
508 | 0 | pool->num_threads++; |
509 | |
|
510 | 0 | return TRUE; |
511 | 0 | } |
512 | | |
513 | | /** |
514 | | * g_thread_pool_new: |
515 | | * @func: a function to execute in the threads of the new thread pool |
516 | | * @user_data: user data that is handed over to @func every time it |
517 | | * is called |
518 | | * @max_threads: the maximal number of threads to execute concurrently |
519 | | * in the new thread pool, -1 means no limit |
520 | | * @exclusive: should this thread pool be exclusive? |
521 | | * @error: return location for error, or %NULL |
522 | | * |
523 | | * This function creates a new thread pool. |
524 | | * |
525 | | * Whenever you call g_thread_pool_push(), either a new thread is |
526 | | * created or an unused one is reused. At most @max_threads threads |
527 | | * are running concurrently for this thread pool. @max_threads = -1 |
528 | | * allows unlimited threads to be created for this thread pool. The |
529 | | * newly created or reused thread now executes the function @func |
530 | | * with the two arguments. The first one is the parameter to |
531 | | * g_thread_pool_push() and the second one is @user_data. |
532 | | * |
533 | | * Pass g_get_num_processors() to @max_threads to create as many threads as |
534 | | * there are logical processors on the system. This will not pin each thread to |
535 | | * a specific processor. |
536 | | * |
537 | | * The parameter @exclusive determines whether the thread pool owns |
538 | | * all threads exclusive or shares them with other thread pools. |
539 | | * If @exclusive is %TRUE, @max_threads threads are started |
540 | | * immediately and they will run exclusively for this thread pool |
541 | | * until it is destroyed by g_thread_pool_free(). If @exclusive is |
542 | | * %FALSE, threads are created when needed and shared between all |
543 | | * non-exclusive thread pools. This implies that @max_threads may |
544 | | * not be -1 for exclusive thread pools. Besides, exclusive thread |
545 | | * pools are not affected by g_thread_pool_set_max_idle_time() |
546 | | * since their threads are never considered idle and returned to the |
547 | | * global pool. |
548 | | * |
549 | | * @error can be %NULL to ignore errors, or non-%NULL to report |
550 | | * errors. An error can only occur when @exclusive is set to %TRUE |
551 | | * and not all @max_threads threads could be created. |
552 | | * See #GThreadError for possible errors that may occur. |
553 | | * Note, even in case of error a valid #GThreadPool is returned. |
554 | | * |
555 | | * Returns: the new #GThreadPool |
556 | | */ |
557 | | GThreadPool * |
558 | | g_thread_pool_new (GFunc func, |
559 | | gpointer user_data, |
560 | | gint max_threads, |
561 | | gboolean exclusive, |
562 | | GError **error) |
563 | 0 | { |
564 | 0 | return g_thread_pool_new_full (func, user_data, NULL, max_threads, exclusive, error); |
565 | 0 | } |
566 | | |
567 | | /** |
568 | | * g_thread_pool_new_full: |
569 | | * @func: a function to execute in the threads of the new thread pool |
570 | | * @user_data: user data that is handed over to @func every time it |
571 | | * is called |
572 | | * @item_free_func: (nullable): used to pass as a free function to |
573 | | * g_async_queue_new_full() |
574 | | * @max_threads: the maximal number of threads to execute concurrently |
575 | | * in the new thread pool, `-1` means no limit |
576 | | * @exclusive: should this thread pool be exclusive? |
577 | | * @error: return location for error, or %NULL |
578 | | * |
579 | | * This function creates a new thread pool similar to g_thread_pool_new() |
580 | | * but allowing @item_free_func to be specified to free the data passed |
581 | | * to g_thread_pool_push() in the case that the #GThreadPool is stopped |
582 | | * and freed before all tasks have been executed. |
583 | | * |
584 | | * Returns: (transfer full): the new #GThreadPool |
585 | | * |
586 | | * Since: 2.70 |
587 | | */ |
588 | | GThreadPool * |
589 | | g_thread_pool_new_full (GFunc func, |
590 | | gpointer user_data, |
591 | | GDestroyNotify item_free_func, |
592 | | gint max_threads, |
593 | | gboolean exclusive, |
594 | | GError **error) |
595 | 0 | { |
596 | 0 | GRealThreadPool *retval; |
597 | 0 | G_LOCK_DEFINE_STATIC (init); |
598 | |
|
599 | 0 | g_return_val_if_fail (func, NULL); |
600 | 0 | g_return_val_if_fail (!exclusive || max_threads != -1, NULL); |
601 | 0 | g_return_val_if_fail (max_threads >= -1, NULL); |
602 | | |
603 | 0 | retval = g_new (GRealThreadPool, 1); |
604 | |
|
605 | 0 | retval->pool.func = func; |
606 | 0 | retval->pool.user_data = user_data; |
607 | 0 | retval->pool.exclusive = exclusive; |
608 | 0 | retval->queue = g_async_queue_new_full (item_free_func); |
609 | 0 | g_cond_init (&retval->cond); |
610 | 0 | retval->max_threads = max_threads; |
611 | 0 | retval->num_threads = 0; |
612 | 0 | retval->running = TRUE; |
613 | 0 | retval->immediate = FALSE; |
614 | 0 | retval->waiting = FALSE; |
615 | 0 | retval->sort_func = NULL; |
616 | 0 | retval->sort_user_data = NULL; |
617 | |
|
618 | 0 | G_LOCK (init); |
619 | 0 | if (!unused_thread_queue) |
620 | 0 | unused_thread_queue = g_async_queue_new (); |
621 | | |
622 | | /* For the very first non-exclusive thread-pool we remember the thread |
623 | | * scheduler settings of the thread creating the pool, if supported by |
624 | | * the GThread implementation. This is then used for making sure that |
625 | | * all threads created on the non-exclusive thread-pool have the same |
626 | | * scheduler settings, and more importantly don't just inherit them |
627 | | * from the thread that just happened to push a new task and caused |
628 | | * a new thread to be created. |
629 | | * |
630 | | * Not doing so could cause real-time priority threads or otherwise |
631 | | * threads with problematic scheduler settings to be part of the |
632 | | * non-exclusive thread-pools. |
633 | | * |
634 | | * If this is not supported by the GThread implementation then we here |
635 | | * start a thread that will inherit the scheduler settings from this |
636 | | * very thread and whose only purpose is to spawn new threads with the |
637 | | * same settings for use by the non-exclusive thread-pools. |
638 | | * |
639 | | * |
640 | | * For non-exclusive thread-pools this is not required as all threads |
641 | | * are created immediately below and are running forever, so they will |
642 | | * automatically inherit the scheduler settings from this very thread. |
643 | | */ |
644 | 0 | if (!exclusive && !have_shared_thread_scheduler_settings && !spawn_thread_queue) |
645 | 0 | { |
646 | 0 | if (g_thread_get_scheduler_settings (&shared_thread_scheduler_settings)) |
647 | 0 | { |
648 | 0 | have_shared_thread_scheduler_settings = TRUE; |
649 | 0 | } |
650 | 0 | else |
651 | 0 | { |
652 | 0 | spawn_thread_queue = g_async_queue_new (); |
653 | 0 | g_cond_init (&spawn_thread_cond); |
654 | 0 | g_thread_new ("pool-spawner", g_thread_pool_spawn_thread, NULL); |
655 | 0 | } |
656 | 0 | } |
657 | 0 | G_UNLOCK (init); |
658 | |
|
659 | 0 | if (retval->pool.exclusive) |
660 | 0 | { |
661 | 0 | g_async_queue_lock (retval->queue); |
662 | |
|
663 | 0 | while (retval->num_threads < (guint) retval->max_threads) |
664 | 0 | { |
665 | 0 | GError *local_error = NULL; |
666 | |
|
667 | 0 | if (!g_thread_pool_start_thread (retval, &local_error)) |
668 | 0 | { |
669 | 0 | g_propagate_error (error, local_error); |
670 | 0 | break; |
671 | 0 | } |
672 | 0 | } |
673 | |
|
674 | 0 | g_async_queue_unlock (retval->queue); |
675 | 0 | } |
676 | |
|
677 | 0 | return (GThreadPool*) retval; |
678 | 0 | } |
679 | | |
680 | | /** |
681 | | * g_thread_pool_push: |
682 | | * @pool: a #GThreadPool |
683 | | * @data: a new task for @pool |
684 | | * @error: return location for error, or %NULL |
685 | | * |
686 | | * Inserts @data into the list of tasks to be executed by @pool. |
687 | | * |
688 | | * When the number of currently running threads is lower than the |
689 | | * maximal allowed number of threads, a new thread is started (or |
690 | | * reused) with the properties given to g_thread_pool_new(). |
691 | | * Otherwise, @data stays in the queue until a thread in this pool |
692 | | * finishes its previous task and processes @data. |
693 | | * |
694 | | * @error can be %NULL to ignore errors, or non-%NULL to report |
695 | | * errors. An error can only occur when a new thread couldn't be |
696 | | * created. In that case @data is simply appended to the queue of |
697 | | * work to do. |
698 | | * |
699 | | * Before version 2.32, this function did not return a success status. |
700 | | * |
701 | | * Returns: %TRUE on success, %FALSE if an error occurred |
702 | | */ |
703 | | gboolean |
704 | | g_thread_pool_push (GThreadPool *pool, |
705 | | gpointer data, |
706 | | GError **error) |
707 | 0 | { |
708 | 0 | GRealThreadPool *real; |
709 | 0 | gboolean result; |
710 | |
|
711 | 0 | real = (GRealThreadPool*) pool; |
712 | |
|
713 | 0 | g_return_val_if_fail (real, FALSE); |
714 | 0 | g_return_val_if_fail (real->running, FALSE); |
715 | | |
716 | 0 | result = TRUE; |
717 | |
|
718 | 0 | g_async_queue_lock (real->queue); |
719 | |
|
720 | 0 | if (g_async_queue_length_unlocked (real->queue) >= 0) |
721 | 0 | { |
722 | | /* No thread is waiting in the queue */ |
723 | 0 | GError *local_error = NULL; |
724 | |
|
725 | 0 | if (!g_thread_pool_start_thread (real, &local_error)) |
726 | 0 | { |
727 | 0 | g_propagate_error (error, local_error); |
728 | 0 | result = FALSE; |
729 | 0 | } |
730 | 0 | } |
731 | |
|
732 | 0 | g_thread_pool_queue_push_unlocked (real, data); |
733 | 0 | g_async_queue_unlock (real->queue); |
734 | |
|
735 | 0 | return result; |
736 | 0 | } |
737 | | |
738 | | /** |
739 | | * g_thread_pool_set_max_threads: |
740 | | * @pool: a #GThreadPool |
741 | | * @max_threads: a new maximal number of threads for @pool, |
742 | | * or -1 for unlimited |
743 | | * @error: return location for error, or %NULL |
744 | | * |
745 | | * Sets the maximal allowed number of threads for @pool. |
746 | | * A value of -1 means that the maximal number of threads |
747 | | * is unlimited. If @pool is an exclusive thread pool, setting |
748 | | * the maximal number of threads to -1 is not allowed. |
749 | | * |
750 | | * Setting @max_threads to 0 means stopping all work for @pool. |
751 | | * It is effectively frozen until @max_threads is set to a non-zero |
752 | | * value again. |
753 | | * |
754 | | * A thread is never terminated while calling @func, as supplied by |
755 | | * g_thread_pool_new(). Instead the maximal number of threads only |
756 | | * has effect for the allocation of new threads in g_thread_pool_push(). |
757 | | * A new thread is allocated, whenever the number of currently |
758 | | * running threads in @pool is smaller than the maximal number. |
759 | | * |
760 | | * @error can be %NULL to ignore errors, or non-%NULL to report |
761 | | * errors. An error can only occur when a new thread couldn't be |
762 | | * created. |
763 | | * |
764 | | * Before version 2.32, this function did not return a success status. |
765 | | * |
766 | | * Returns: %TRUE on success, %FALSE if an error occurred |
767 | | */ |
768 | | gboolean |
769 | | g_thread_pool_set_max_threads (GThreadPool *pool, |
770 | | gint max_threads, |
771 | | GError **error) |
772 | 0 | { |
773 | 0 | GRealThreadPool *real; |
774 | 0 | gint to_start; |
775 | 0 | gboolean result; |
776 | |
|
777 | 0 | real = (GRealThreadPool*) pool; |
778 | |
|
779 | 0 | g_return_val_if_fail (real, FALSE); |
780 | 0 | g_return_val_if_fail (real->running, FALSE); |
781 | 0 | g_return_val_if_fail (!real->pool.exclusive || max_threads != -1, FALSE); |
782 | 0 | g_return_val_if_fail (max_threads >= -1, FALSE); |
783 | | |
784 | 0 | result = TRUE; |
785 | |
|
786 | 0 | g_async_queue_lock (real->queue); |
787 | |
|
788 | 0 | real->max_threads = max_threads; |
789 | |
|
790 | 0 | if (pool->exclusive) |
791 | 0 | to_start = real->max_threads - real->num_threads; |
792 | 0 | else |
793 | 0 | to_start = g_async_queue_length_unlocked (real->queue); |
794 | |
|
795 | 0 | for ( ; to_start > 0; to_start--) |
796 | 0 | { |
797 | 0 | GError *local_error = NULL; |
798 | |
|
799 | 0 | if (!g_thread_pool_start_thread (real, &local_error)) |
800 | 0 | { |
801 | 0 | g_propagate_error (error, local_error); |
802 | 0 | result = FALSE; |
803 | 0 | break; |
804 | 0 | } |
805 | 0 | } |
806 | |
|
807 | 0 | g_async_queue_unlock (real->queue); |
808 | |
|
809 | 0 | return result; |
810 | 0 | } |
811 | | |
812 | | /** |
813 | | * g_thread_pool_get_max_threads: |
814 | | * @pool: a #GThreadPool |
815 | | * |
816 | | * Returns the maximal number of threads for @pool. |
817 | | * |
818 | | * Returns: the maximal number of threads |
819 | | */ |
820 | | gint |
821 | | g_thread_pool_get_max_threads (GThreadPool *pool) |
822 | 0 | { |
823 | 0 | GRealThreadPool *real; |
824 | 0 | gint retval; |
825 | |
|
826 | 0 | real = (GRealThreadPool*) pool; |
827 | |
|
828 | 0 | g_return_val_if_fail (real, 0); |
829 | 0 | g_return_val_if_fail (real->running, 0); |
830 | | |
831 | 0 | g_async_queue_lock (real->queue); |
832 | 0 | retval = real->max_threads; |
833 | 0 | g_async_queue_unlock (real->queue); |
834 | |
|
835 | 0 | return retval; |
836 | 0 | } |
837 | | |
838 | | /** |
839 | | * g_thread_pool_get_num_threads: |
840 | | * @pool: a #GThreadPool |
841 | | * |
842 | | * Returns the number of threads currently running in @pool. |
843 | | * |
844 | | * Returns: the number of threads currently running |
845 | | */ |
846 | | guint |
847 | | g_thread_pool_get_num_threads (GThreadPool *pool) |
848 | 0 | { |
849 | 0 | GRealThreadPool *real; |
850 | 0 | guint retval; |
851 | |
|
852 | 0 | real = (GRealThreadPool*) pool; |
853 | |
|
854 | 0 | g_return_val_if_fail (real, 0); |
855 | 0 | g_return_val_if_fail (real->running, 0); |
856 | | |
857 | 0 | g_async_queue_lock (real->queue); |
858 | 0 | retval = real->num_threads; |
859 | 0 | g_async_queue_unlock (real->queue); |
860 | |
|
861 | 0 | return retval; |
862 | 0 | } |
863 | | |
864 | | /** |
865 | | * g_thread_pool_unprocessed: |
866 | | * @pool: a #GThreadPool |
867 | | * |
868 | | * Returns the number of tasks still unprocessed in @pool. |
869 | | * |
870 | | * Returns: the number of unprocessed tasks |
871 | | */ |
872 | | guint |
873 | | g_thread_pool_unprocessed (GThreadPool *pool) |
874 | 0 | { |
875 | 0 | GRealThreadPool *real; |
876 | 0 | gint unprocessed; |
877 | |
|
878 | 0 | real = (GRealThreadPool*) pool; |
879 | |
|
880 | 0 | g_return_val_if_fail (real, 0); |
881 | 0 | g_return_val_if_fail (real->running, 0); |
882 | | |
883 | 0 | unprocessed = g_async_queue_length (real->queue); |
884 | |
|
885 | 0 | return MAX (unprocessed, 0); |
886 | 0 | } |
887 | | |
888 | | /** |
889 | | * g_thread_pool_free: |
890 | | * @pool: a #GThreadPool |
891 | | * @immediate: should @pool shut down immediately? |
892 | | * @wait_: should the function wait for all tasks to be finished? |
893 | | * |
894 | | * Frees all resources allocated for @pool. |
895 | | * |
896 | | * If @immediate is %TRUE, no new task is processed for @pool. |
897 | | * Otherwise @pool is not freed before the last task is processed. |
898 | | * Note however, that no thread of this pool is interrupted while |
899 | | * processing a task. Instead at least all still running threads |
900 | | * can finish their tasks before the @pool is freed. |
901 | | * |
902 | | * If @wait_ is %TRUE, this function does not return before all |
903 | | * tasks to be processed (dependent on @immediate, whether all |
904 | | * or only the currently running) are ready. |
905 | | * Otherwise this function returns immediately. |
906 | | * |
907 | | * After calling this function @pool must not be used anymore. |
908 | | */ |
909 | | void |
910 | | g_thread_pool_free (GThreadPool *pool, |
911 | | gboolean immediate, |
912 | | gboolean wait_) |
913 | 0 | { |
914 | 0 | GRealThreadPool *real; |
915 | |
|
916 | 0 | real = (GRealThreadPool*) pool; |
917 | |
|
918 | 0 | g_return_if_fail (real); |
919 | 0 | g_return_if_fail (real->running); |
920 | | |
921 | | /* If there's no thread allowed here, there is not much sense in |
922 | | * not stopping this pool immediately, when it's not empty |
923 | | */ |
924 | 0 | g_return_if_fail (immediate || |
925 | 0 | real->max_threads != 0 || |
926 | 0 | g_async_queue_length (real->queue) == 0); |
927 | | |
928 | 0 | g_async_queue_lock (real->queue); |
929 | |
|
930 | 0 | real->running = FALSE; |
931 | 0 | real->immediate = immediate; |
932 | 0 | real->waiting = wait_; |
933 | |
|
934 | 0 | if (wait_) |
935 | 0 | { |
936 | 0 | while (g_async_queue_length_unlocked (real->queue) != (gint) -real->num_threads && |
937 | 0 | !(immediate && real->num_threads == 0)) |
938 | 0 | g_cond_wait (&real->cond, _g_async_queue_get_mutex (real->queue)); |
939 | 0 | } |
940 | |
|
941 | 0 | if (immediate || g_async_queue_length_unlocked (real->queue) == (gint) -real->num_threads) |
942 | 0 | { |
943 | | /* No thread is currently doing something (and nothing is left |
944 | | * to process in the queue) |
945 | | */ |
946 | 0 | if (real->num_threads == 0) |
947 | 0 | { |
948 | | /* No threads left, we clean up */ |
949 | 0 | g_async_queue_unlock (real->queue); |
950 | 0 | g_thread_pool_free_internal (real); |
951 | 0 | return; |
952 | 0 | } |
953 | | |
954 | 0 | g_thread_pool_wakeup_and_stop_all (real); |
955 | 0 | } |
956 | | |
957 | | /* The last thread should cleanup the pool */ |
958 | 0 | real->waiting = FALSE; |
959 | 0 | g_async_queue_unlock (real->queue); |
960 | 0 | } |
961 | | |
962 | | static void |
963 | | g_thread_pool_free_internal (GRealThreadPool* pool) |
964 | 0 | { |
965 | 0 | g_return_if_fail (pool); |
966 | 0 | g_return_if_fail (pool->running == FALSE); |
967 | 0 | g_return_if_fail (pool->num_threads == 0); |
968 | | |
969 | | /* Ensure the dummy item pushed on by g_thread_pool_wakeup_and_stop_all() is |
970 | | * removed, before it’s potentially passed to the user-provided |
971 | | * @item_free_func. */ |
972 | 0 | g_async_queue_remove (pool->queue, GUINT_TO_POINTER (1)); |
973 | |
|
974 | 0 | g_async_queue_unref (pool->queue); |
975 | 0 | g_cond_clear (&pool->cond); |
976 | |
|
977 | 0 | g_free (pool); |
978 | 0 | } |
979 | | |
980 | | static void |
981 | | g_thread_pool_wakeup_and_stop_all (GRealThreadPool *pool) |
982 | 0 | { |
983 | 0 | guint i; |
984 | |
|
985 | 0 | g_return_if_fail (pool); |
986 | 0 | g_return_if_fail (pool->running == FALSE); |
987 | 0 | g_return_if_fail (pool->num_threads != 0); |
988 | | |
989 | 0 | pool->immediate = TRUE; |
990 | | |
991 | | /* |
992 | | * So here we're sending bogus data to the pool threads, which |
993 | | * should cause them each to wake up, and check the above |
994 | | * pool->immediate condition. However we don't want that |
995 | | * data to be sorted (since it'll crash the sorter). |
996 | | */ |
997 | 0 | for (i = 0; i < pool->num_threads; i++) |
998 | 0 | g_async_queue_push_unlocked (pool->queue, GUINT_TO_POINTER (1)); |
999 | 0 | } |
1000 | | |
1001 | | /** |
1002 | | * g_thread_pool_set_max_unused_threads: |
1003 | | * @max_threads: maximal number of unused threads |
1004 | | * |
1005 | | * Sets the maximal number of unused threads to @max_threads. |
1006 | | * If @max_threads is -1, no limit is imposed on the number |
1007 | | * of unused threads. |
1008 | | * |
1009 | | * The default value is 2. |
1010 | | */ |
1011 | | void |
1012 | | g_thread_pool_set_max_unused_threads (gint max_threads) |
1013 | 0 | { |
1014 | 0 | g_return_if_fail (max_threads >= -1); |
1015 | | |
1016 | 0 | g_atomic_int_set (&max_unused_threads, max_threads); |
1017 | |
|
1018 | 0 | if (max_threads != -1) |
1019 | 0 | { |
1020 | 0 | max_threads -= g_atomic_int_get (&unused_threads); |
1021 | 0 | if (max_threads < 0) |
1022 | 0 | { |
1023 | 0 | g_atomic_int_set (&kill_unused_threads, -max_threads); |
1024 | 0 | g_atomic_int_inc (&wakeup_thread_serial); |
1025 | |
|
1026 | 0 | g_async_queue_lock (unused_thread_queue); |
1027 | |
|
1028 | 0 | do |
1029 | 0 | { |
1030 | 0 | g_async_queue_push_unlocked (unused_thread_queue, |
1031 | 0 | wakeup_thread_marker); |
1032 | 0 | } |
1033 | 0 | while (++max_threads); |
1034 | |
|
1035 | 0 | g_async_queue_unlock (unused_thread_queue); |
1036 | 0 | } |
1037 | 0 | } |
1038 | 0 | } |
1039 | | |
1040 | | /** |
1041 | | * g_thread_pool_get_max_unused_threads: |
1042 | | * |
1043 | | * Returns the maximal allowed number of unused threads. |
1044 | | * |
1045 | | * Returns: the maximal number of unused threads |
1046 | | */ |
1047 | | gint |
1048 | | g_thread_pool_get_max_unused_threads (void) |
1049 | 0 | { |
1050 | 0 | return g_atomic_int_get (&max_unused_threads); |
1051 | 0 | } |
1052 | | |
1053 | | /** |
1054 | | * g_thread_pool_get_num_unused_threads: |
1055 | | * |
1056 | | * Returns the number of currently unused threads. |
1057 | | * |
1058 | | * Returns: the number of currently unused threads |
1059 | | */ |
1060 | | guint |
1061 | | g_thread_pool_get_num_unused_threads (void) |
1062 | 0 | { |
1063 | 0 | return (guint) g_atomic_int_get (&unused_threads); |
1064 | 0 | } |
1065 | | |
1066 | | /** |
1067 | | * g_thread_pool_stop_unused_threads: |
1068 | | * |
1069 | | * Stops all currently unused threads. This does not change the |
1070 | | * maximal number of unused threads. This function can be used to |
1071 | | * regularly stop all unused threads e.g. from g_timeout_add(). |
1072 | | */ |
1073 | | void |
1074 | | g_thread_pool_stop_unused_threads (void) |
1075 | 0 | { |
1076 | 0 | guint oldval; |
1077 | |
|
1078 | 0 | oldval = g_thread_pool_get_max_unused_threads (); |
1079 | |
|
1080 | 0 | g_thread_pool_set_max_unused_threads (0); |
1081 | 0 | g_thread_pool_set_max_unused_threads (oldval); |
1082 | 0 | } |
1083 | | |
1084 | | /** |
1085 | | * g_thread_pool_set_sort_function: |
1086 | | * @pool: a #GThreadPool |
1087 | | * @func: the #GCompareDataFunc used to sort the list of tasks. |
1088 | | * This function is passed two tasks. It should return |
1089 | | * 0 if the order in which they are handled does not matter, |
1090 | | * a negative value if the first task should be processed before |
1091 | | * the second or a positive value if the second task should be |
1092 | | * processed first. |
1093 | | * @user_data: user data passed to @func |
1094 | | * |
1095 | | * Sets the function used to sort the list of tasks. This allows the |
1096 | | * tasks to be processed by a priority determined by @func, and not |
1097 | | * just in the order in which they were added to the pool. |
1098 | | * |
1099 | | * Note, if the maximum number of threads is more than 1, the order |
1100 | | * that threads are executed cannot be guaranteed 100%. Threads are |
1101 | | * scheduled by the operating system and are executed at random. It |
1102 | | * cannot be assumed that threads are executed in the order they are |
1103 | | * created. |
1104 | | * |
1105 | | * Since: 2.10 |
1106 | | */ |
1107 | | void |
1108 | | g_thread_pool_set_sort_function (GThreadPool *pool, |
1109 | | GCompareDataFunc func, |
1110 | | gpointer user_data) |
1111 | 0 | { |
1112 | 0 | GRealThreadPool *real; |
1113 | |
|
1114 | 0 | real = (GRealThreadPool*) pool; |
1115 | |
|
1116 | 0 | g_return_if_fail (real); |
1117 | 0 | g_return_if_fail (real->running); |
1118 | | |
1119 | 0 | g_async_queue_lock (real->queue); |
1120 | |
|
1121 | 0 | real->sort_func = func; |
1122 | 0 | real->sort_user_data = user_data; |
1123 | |
|
1124 | 0 | if (func) |
1125 | 0 | g_async_queue_sort_unlocked (real->queue, |
1126 | 0 | real->sort_func, |
1127 | 0 | real->sort_user_data); |
1128 | |
|
1129 | 0 | g_async_queue_unlock (real->queue); |
1130 | 0 | } |
1131 | | |
1132 | | /** |
1133 | | * g_thread_pool_move_to_front: |
1134 | | * @pool: a #GThreadPool |
1135 | | * @data: an unprocessed item in the pool |
1136 | | * |
1137 | | * Moves the item to the front of the queue of unprocessed |
1138 | | * items, so that it will be processed next. |
1139 | | * |
1140 | | * Returns: %TRUE if the item was found and moved |
1141 | | * |
1142 | | * Since: 2.46 |
1143 | | */ |
1144 | | gboolean |
1145 | | g_thread_pool_move_to_front (GThreadPool *pool, |
1146 | | gpointer data) |
1147 | 0 | { |
1148 | 0 | GRealThreadPool *real = (GRealThreadPool*) pool; |
1149 | 0 | gboolean found; |
1150 | |
|
1151 | 0 | g_async_queue_lock (real->queue); |
1152 | |
|
1153 | 0 | found = g_async_queue_remove_unlocked (real->queue, data); |
1154 | 0 | if (found) |
1155 | 0 | g_async_queue_push_front_unlocked (real->queue, data); |
1156 | |
|
1157 | 0 | g_async_queue_unlock (real->queue); |
1158 | |
|
1159 | 0 | return found; |
1160 | 0 | } |
1161 | | |
1162 | | /** |
1163 | | * g_thread_pool_set_max_idle_time: |
1164 | | * @interval: the maximum @interval (in milliseconds) |
1165 | | * a thread can be idle |
1166 | | * |
1167 | | * This function will set the maximum @interval that a thread |
1168 | | * waiting in the pool for new tasks can be idle for before |
1169 | | * being stopped. This function is similar to calling |
1170 | | * g_thread_pool_stop_unused_threads() on a regular timeout, |
1171 | | * except this is done on a per thread basis. |
1172 | | * |
1173 | | * By setting @interval to 0, idle threads will not be stopped. |
1174 | | * |
1175 | | * The default value is 15000 (15 seconds). |
1176 | | * |
1177 | | * Since: 2.10 |
1178 | | */ |
1179 | | void |
1180 | | g_thread_pool_set_max_idle_time (guint interval) |
1181 | 0 | { |
1182 | 0 | guint i; |
1183 | |
|
1184 | 0 | g_atomic_int_set (&max_idle_time, interval); |
1185 | |
|
1186 | 0 | i = (guint) g_atomic_int_get (&unused_threads); |
1187 | 0 | if (i > 0) |
1188 | 0 | { |
1189 | 0 | g_atomic_int_inc (&wakeup_thread_serial); |
1190 | 0 | g_async_queue_lock (unused_thread_queue); |
1191 | |
|
1192 | 0 | do |
1193 | 0 | { |
1194 | 0 | g_async_queue_push_unlocked (unused_thread_queue, |
1195 | 0 | wakeup_thread_marker); |
1196 | 0 | } |
1197 | 0 | while (--i); |
1198 | |
|
1199 | 0 | g_async_queue_unlock (unused_thread_queue); |
1200 | 0 | } |
1201 | 0 | } |
1202 | | |
1203 | | /** |
1204 | | * g_thread_pool_get_max_idle_time: |
1205 | | * |
1206 | | * This function will return the maximum @interval that a |
1207 | | * thread will wait in the thread pool for new tasks before |
1208 | | * being stopped. |
1209 | | * |
1210 | | * If this function returns 0, threads waiting in the thread |
1211 | | * pool for new work are not stopped. |
1212 | | * |
1213 | | * Returns: the maximum @interval (milliseconds) to wait |
1214 | | * for new tasks in the thread pool before stopping the |
1215 | | * thread |
1216 | | * |
1217 | | * Since: 2.10 |
1218 | | */ |
1219 | | guint |
1220 | | g_thread_pool_get_max_idle_time (void) |
1221 | 0 | { |
1222 | | return (guint) g_atomic_int_get (&max_idle_time); |
1223 | 0 | } |