Coverage Report

Created: 2026-07-12 07:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gstreamer/subprojects/glib-2.86.3/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 "glib-private.h"
34
#include "gmain.h"
35
#include "gtestutils.h"
36
#include "gthreadprivate.h"
37
#include "gtimer.h"
38
#include "gutils.h"
39
40
#define DEBUG_MSG(x)
41
/* #define DEBUG_MSG(args) g_printerr args ; g_printerr ("\n");    */
42
43
typedef struct _GRealThreadPool GRealThreadPool;
44
45
/**
46
 * GThreadPool:
47
 * @func: the function to execute in the threads of this pool
48
 * @user_data: the user data for the threads of this pool
49
 * @exclusive: are all threads exclusive to this pool
50
 *
51
 * The `GThreadPool` struct represents a thread pool.
52
 *
53
 * A thread pool is useful when you wish to asynchronously fork out the execution of work
54
 * and continue working in your own thread. If that will happen often, the overhead of starting
55
 * and destroying a thread each time might be too high. In such cases reusing already started
56
 * threads seems like a good idea. And it indeed is, but implementing this can be tedious
57
 * and error-prone.
58
 *
59
 * Therefore GLib provides thread pools for your convenience. An added advantage is, that the
60
 * threads can be shared between the different subsystems of your program, when they are using GLib.
61
 *
62
 * To create a new thread pool, you use [func@GLib.ThreadPool.new].
63
 * It is destroyed by [method@GLib.ThreadPool.free].
64
 *
65
 * If you want to execute a certain task within a thread pool, use [method@GLib.ThreadPool.push].
66
 *
67
 * To get the current number of running threads you call [method@GLib.ThreadPool.get_num_threads].
68
 * To get the number of still unprocessed tasks you call [method@GLib.ThreadPool.unprocessed].
69
 * To control the maximum number of threads for a thread pool, you use
70
 * [method@GLib.ThreadPool.get_max_threads]. and [method@GLib.ThreadPool.set_max_threads].
71
 *
72
 * Finally you can control the number of unused threads, that are kept alive by GLib for future use.
73
 * The current number can be fetched with [func@GLib.ThreadPool.get_num_unused_threads].
74
 * The maximum number can be controlled by [func@GLib.ThreadPool.get_max_unused_threads] and
75
 * [func@GLib.ThreadPool.set_max_unused_threads]. All currently unused threads
76
 * can be stopped by calling [func@GLib.ThreadPool.stop_unused_threads].
77
 */
78
struct _GRealThreadPool
79
{
80
  GThreadPool pool;
81
  GAsyncQueue *queue;
82
  GCond cond;
83
  gint max_threads;
84
  guint num_threads;
85
  gboolean running;
86
  gboolean immediate;
87
  gboolean waiting;
88
  GCompareDataFunc sort_func;
89
  gpointer sort_user_data;
90
};
91
92
/* The following is just an address to mark the wakeup order for a
93
 * thread, it could be any address (as long, as it isn't a valid
94
 * GThreadPool address)
95
 */
96
static const gpointer wakeup_thread_marker = (gpointer) &g_thread_pool_new;
97
static gint wakeup_thread_serial = 0;
98
99
/* Here all unused threads are waiting  */
100
static GAsyncQueue *unused_thread_queue = NULL;
101
static gint unused_threads = 0;
102
static gint max_unused_threads = 8;
103
static gint kill_unused_threads = 0;
104
static guint max_idle_time = 15 * 1000;
105
106
static int thread_counter = 0;
107
108
typedef struct
109
{
110
  /* Either thread or error are set in the end. Both transfer-full. */
111
  GThreadPool *pool;
112
  GThread *thread;
113
  GError *error;
114
} SpawnThreadData;
115
116
static GCond spawn_thread_cond;
117
static GAsyncQueue *spawn_thread_queue;
118
119
static void             g_thread_pool_queue_push_unlocked (GRealThreadPool  *pool,
120
                                                           gpointer          data);
121
static void             g_thread_pool_free_internal       (GRealThreadPool  *pool);
122
static gpointer         g_thread_pool_thread_proxy        (gpointer          data);
123
static gboolean         g_thread_pool_start_thread        (GRealThreadPool  *pool,
124
                                                           GError          **error);
125
static void             g_thread_pool_wakeup_and_stop_all (GRealThreadPool  *pool);
126
static GRealThreadPool* g_thread_pool_wait_for_new_pool   (void);
127
static gpointer         g_thread_pool_wait_for_new_task   (GRealThreadPool  *pool);
128
129
static void
130
g_thread_pool_queue_push_unlocked (GRealThreadPool *pool,
131
                                   gpointer         data)
132
16.2k
{
133
16.2k
  if (pool->sort_func)
134
0
    g_async_queue_push_sorted_unlocked (pool->queue,
135
0
                                        data,
136
0
                                        pool->sort_func,
137
0
                                        pool->sort_user_data);
138
16.2k
  else
139
16.2k
    g_async_queue_push_unlocked (pool->queue, data);
140
16.2k
}
141
142
static GRealThreadPool*
143
g_thread_pool_wait_for_new_pool (void)
144
0
{
145
0
  GRealThreadPool *pool;
146
0
  gint local_wakeup_thread_serial;
147
0
  guint local_max_unused_threads;
148
0
  gint local_max_idle_time;
149
0
  gint last_wakeup_thread_serial;
150
0
  gboolean have_relayed_thread_marker = FALSE;
151
152
0
  local_max_unused_threads = (guint) g_atomic_int_get (&max_unused_threads);
153
0
  local_max_idle_time = g_atomic_int_get (&max_idle_time);
154
0
  last_wakeup_thread_serial = g_atomic_int_get (&wakeup_thread_serial);
155
156
0
  do
157
0
    {
158
0
      if ((guint) g_atomic_int_get (&unused_threads) >= local_max_unused_threads)
159
0
        {
160
          /* If this is a superfluous thread, stop it. */
161
0
          pool = NULL;
162
0
        }
163
0
      else if (local_max_idle_time > 0)
164
0
        {
165
          /* If a maximal idle time is given, wait for the given time. */
166
0
          DEBUG_MSG (("thread %p waiting in global pool for %f seconds.",
167
0
                      g_thread_self (), local_max_idle_time / 1000.0));
168
169
0
          pool = g_async_queue_timeout_pop (unused_thread_queue,
170
0
              local_max_idle_time * 1000);
171
0
        }
172
0
      else
173
0
        {
174
          /* If no maximal idle time is given, wait indefinitely. */
175
0
          DEBUG_MSG (("thread %p waiting in global pool.", g_thread_self ()));
176
0
          pool = g_async_queue_pop (unused_thread_queue);
177
0
        }
178
179
0
      if (pool == wakeup_thread_marker)
180
0
        {
181
0
          local_wakeup_thread_serial = g_atomic_int_get (&wakeup_thread_serial);
182
0
          if (last_wakeup_thread_serial == local_wakeup_thread_serial)
183
0
            {
184
0
              if (!have_relayed_thread_marker)
185
0
              {
186
                /* If this wakeup marker has been received for
187
                 * the second time, relay it.
188
                 */
189
0
                DEBUG_MSG (("thread %p relaying wakeup message to "
190
0
                            "waiting thread with lower serial.",
191
0
                            g_thread_self ()));
192
193
0
                g_async_queue_push (unused_thread_queue, wakeup_thread_marker);
194
0
                have_relayed_thread_marker = TRUE;
195
196
                /* If a wakeup marker has been relayed, this thread
197
                 * will get out of the way for 100 microseconds to
198
                 * avoid receiving this marker again.
199
                 */
200
0
                g_usleep (100);
201
0
              }
202
0
            }
203
0
          else
204
0
            {
205
0
              if (g_atomic_int_add (&kill_unused_threads, -1) > 0)
206
0
                {
207
0
                  pool = NULL;
208
0
                  break;
209
0
                }
210
211
0
              DEBUG_MSG (("thread %p updating to new limits.",
212
0
                          g_thread_self ()));
213
214
0
              local_max_unused_threads = (guint) g_atomic_int_get (&max_unused_threads);
215
0
              local_max_idle_time = g_atomic_int_get (&max_idle_time);
216
0
              last_wakeup_thread_serial = local_wakeup_thread_serial;
217
218
0
              have_relayed_thread_marker = FALSE;
219
0
            }
220
0
        }
221
0
    }
222
0
  while (pool == wakeup_thread_marker);
223
224
0
  return pool;
225
0
}
226
227
static gpointer
228
g_thread_pool_wait_for_new_task (GRealThreadPool *pool)
229
16.4k
{
230
16.4k
  gpointer task = NULL;
231
232
16.4k
  if (pool->running || (!pool->immediate &&
233
0
                        g_async_queue_length_unlocked (pool->queue) > 0))
234
16.4k
    {
235
      /* This thread pool is still active. */
236
16.4k
      if (pool->max_threads != -1 && pool->num_threads > (guint) pool->max_threads)
237
0
        {
238
          /* This is a superfluous thread, so it goes to the global pool. */
239
0
          DEBUG_MSG (("superfluous thread %p in pool %p.",
240
0
                      g_thread_self (), pool));
241
0
        }
242
16.4k
      else if (pool->pool.exclusive)
243
0
        {
244
          /* Exclusive threads stay attached to the pool. */
245
0
          task = g_async_queue_pop_unlocked (pool->queue);
246
247
0
          DEBUG_MSG (("thread %p in exclusive pool %p waits for task "
248
0
                      "(%d running, %d unprocessed).",
249
0
                      g_thread_self (), pool, pool->num_threads,
250
0
                      g_async_queue_length_unlocked (pool->queue)));
251
0
        }
252
16.4k
      else
253
16.4k
        {
254
          /* A thread will wait for new tasks for at most 1/2
255
           * second before going to the global pool.
256
           */
257
16.4k
          DEBUG_MSG (("thread %p in pool %p waits for up to a 1/2 second for task "
258
16.4k
                      "(%d running, %d unprocessed).",
259
16.4k
                      g_thread_self (), pool, pool->num_threads,
260
16.4k
                      g_async_queue_length_unlocked (pool->queue)));
261
262
16.4k
          task = g_async_queue_timeout_pop_unlocked (pool->queue,
263
16.4k
                 G_USEC_PER_SEC / 2);
264
16.4k
        }
265
16.4k
    }
266
0
  else
267
0
    {
268
      /* This thread pool is inactive, it will no longer process tasks. */
269
0
      DEBUG_MSG (("pool %p not active, thread %p will go to global pool "
270
0
                  "(running: %s, immediate: %s, len: %d).",
271
0
                  pool, g_thread_self (),
272
0
                  pool->running ? "true" : "false",
273
0
                  pool->immediate ? "true" : "false",
274
0
                  g_async_queue_length_unlocked (pool->queue)));
275
0
    }
276
277
16.4k
  return task;
278
16.4k
}
279
280
static gpointer
281
g_thread_pool_spawn_thread (gpointer data)
282
2
{
283
139
  while (TRUE)
284
137
    {
285
137
      SpawnThreadData *spawn_thread_data;
286
137
      GThread *thread = NULL;
287
137
      GError *error = NULL;
288
137
      gchar name[16];
289
290
137
      g_snprintf (name, sizeof (name), "pool-%d", g_atomic_int_add (&thread_counter, 1));
291
292
137
      g_async_queue_lock (spawn_thread_queue);
293
      /* Spawn a new thread for the given pool and wake the requesting thread
294
       * up again with the result. This new thread will have the scheduler
295
       * settings inherited from this thread and in extension of the thread
296
       * that created the first non-exclusive thread-pool. */
297
137
      spawn_thread_data = g_async_queue_pop_unlocked (spawn_thread_queue);
298
137
      thread = g_thread_try_new (name, g_thread_pool_thread_proxy, spawn_thread_data->pool, &error);
299
300
137
      spawn_thread_data->thread = g_steal_pointer (&thread);
301
137
      spawn_thread_data->error = g_steal_pointer (&error);
302
303
137
      g_cond_broadcast (&spawn_thread_cond);
304
137
      g_async_queue_unlock (spawn_thread_queue);
305
137
    }
306
307
2
  return NULL;
308
2
}
309
310
static gpointer
311
g_thread_pool_thread_proxy (gpointer data)
312
135
{
313
135
  GRealThreadPool *pool;
314
315
135
  pool = data;
316
317
135
  DEBUG_MSG (("thread %p started for pool %p.", g_thread_self (), pool));
318
319
135
  g_async_queue_lock (pool->queue);
320
321
16.5k
  while (TRUE)
322
16.4k
    {
323
16.4k
      gpointer task;
324
325
16.4k
      task = g_thread_pool_wait_for_new_task (pool);
326
16.4k
      if (task)
327
16.2k
        {
328
16.2k
          if (pool->running || !pool->immediate)
329
16.2k
            {
330
              /* A task was received and the thread pool is active,
331
               * so execute the function.
332
               */
333
16.2k
              g_async_queue_unlock (pool->queue);
334
16.2k
              DEBUG_MSG (("thread %p in pool %p calling func.",
335
16.2k
                          g_thread_self (), pool));
336
16.2k
              pool->pool.func (task, pool->pool.user_data);
337
16.2k
              g_async_queue_lock (pool->queue);
338
16.2k
            }
339
16.2k
        }
340
135
      else
341
135
        {
342
          /* No task was received, so this thread goes to the global pool. */
343
135
          gboolean free_pool = FALSE;
344
345
135
          DEBUG_MSG (("thread %p leaving pool %p for global pool.",
346
135
                      g_thread_self (), pool));
347
135
          pool->num_threads--;
348
349
135
          if (!pool->running)
350
0
            {
351
0
              if (!pool->waiting)
352
0
                {
353
0
                  if (pool->num_threads == 0)
354
0
                    {
355
                      /* If the pool is not running and no other
356
                       * thread is waiting for this thread pool to
357
                       * finish and this is the last thread of this
358
                       * pool, free the pool.
359
                       */
360
0
                      free_pool = TRUE;
361
0
                    }
362
0
                  else
363
0
                    {
364
                      /* If the pool is not running and no other
365
                       * thread is waiting for this thread pool to
366
                       * finish and this is not the last thread of
367
                       * this pool and there are no tasks left in the
368
                       * queue, wakeup the remaining threads.
369
                       */
370
0
                      if (g_async_queue_length_unlocked (pool->queue) ==
371
0
                          (gint) -pool->num_threads)
372
0
                        g_thread_pool_wakeup_and_stop_all (pool);
373
0
                    }
374
0
                }
375
0
              else if (pool->immediate ||
376
0
                       g_async_queue_length_unlocked (pool->queue) <= 0)
377
0
                {
378
                  /* If the pool is not running and another thread is
379
                   * waiting for this thread pool to finish and there
380
                   * are either no tasks left or the pool shall stop
381
                   * immediately, inform the waiting thread of a change
382
                   * of the thread pool state.
383
                   */
384
0
                  g_cond_broadcast (&pool->cond);
385
0
                }
386
0
            }
387
388
135
          g_atomic_int_inc (&unused_threads);
389
135
          g_async_queue_unlock (pool->queue);
390
391
135
          if (free_pool)
392
0
            g_thread_pool_free_internal (pool);
393
394
135
          pool = g_thread_pool_wait_for_new_pool ();
395
135
          g_atomic_int_add (&unused_threads, -1);
396
397
135
          if (pool == NULL)
398
0
            break;
399
400
135
          g_async_queue_lock (pool->queue);
401
402
135
          DEBUG_MSG (("thread %p entering pool %p from global pool.",
403
135
                      g_thread_self (), pool));
404
405
          /* pool->num_threads++ is not done here, but in
406
           * g_thread_pool_start_thread to make the new started
407
           * thread known to the pool before itself can do it.
408
           */
409
135
        }
410
16.4k
    }
411
412
135
  return NULL;
413
135
}
414
415
static gboolean
416
g_thread_pool_start_thread (GRealThreadPool  *pool,
417
                            GError          **error)
418
135
{
419
135
  gboolean success = FALSE;
420
421
135
  if (pool->max_threads != -1 && pool->num_threads >= (guint) pool->max_threads)
422
    /* Enough threads are already running */
423
0
    return TRUE;
424
425
135
  g_async_queue_lock (unused_thread_queue);
426
427
135
  if (g_async_queue_length_unlocked (unused_thread_queue) < 0)
428
0
    {
429
0
      g_async_queue_push_unlocked (unused_thread_queue, pool);
430
0
      success = TRUE;
431
0
    }
432
433
135
  g_async_queue_unlock (unused_thread_queue);
434
435
135
  if (!success)
436
135
    {
437
135
      GThread *thread;
438
439
      /* No thread was found, we have to start a new one */
440
135
      if (pool->pool.exclusive)
441
0
        {
442
          /* For exclusive thread-pools this is directly called from new() and
443
           * we simply start new threads that inherit the scheduler settings
444
           * from the current thread.
445
           */
446
0
          char name[16];
447
448
0
          g_snprintf (name, sizeof (name), "pool-%d", g_atomic_int_add (&thread_counter, 1));
449
450
0
          thread = g_thread_try_new (name, g_thread_pool_thread_proxy, pool, error);
451
0
        }
452
135
      else
453
135
        {
454
          /* For non-exclusive thread-pools this can be called at any time
455
           * when a new thread is needed. We make sure to create a new thread
456
           * here with the correct scheduler settings by going via our helper
457
           * thread.
458
           */
459
135
          SpawnThreadData spawn_thread_data = { (GThreadPool *) pool, NULL, NULL };
460
461
135
          g_async_queue_lock (spawn_thread_queue);
462
463
135
          g_async_queue_push_unlocked (spawn_thread_queue, &spawn_thread_data);
464
465
270
          while (!spawn_thread_data.thread && !spawn_thread_data.error)
466
135
            g_cond_wait (&spawn_thread_cond, _g_async_queue_get_mutex (spawn_thread_queue));
467
468
135
          thread = spawn_thread_data.thread;
469
135
          if (!thread)
470
0
            g_propagate_error (error, g_steal_pointer (&spawn_thread_data.error));
471
135
          g_async_queue_unlock (spawn_thread_queue);
472
135
        }
473
474
135
      if (thread == NULL)
475
0
        return FALSE;
476
477
135
      g_thread_unref (thread);
478
135
    }
479
480
  /* See comment in g_thread_pool_thread_proxy as to why this is done
481
   * here and not there
482
   */
483
135
  pool->num_threads++;
484
485
135
  return TRUE;
486
135
}
487
488
/**
489
 * g_thread_pool_new:
490
 * @func: a function to execute in the threads of the new thread pool
491
 * @user_data: user data that is handed over to @func every time it
492
 *     is called
493
 * @max_threads: the maximal number of threads to execute concurrently
494
 *     in  the new thread pool, -1 means no limit
495
 * @exclusive: should this thread pool be exclusive?
496
 * @error: return location for error, or %NULL
497
 *
498
 * This function creates a new thread pool.
499
 *
500
 * Whenever you call g_thread_pool_push(), either a new thread is
501
 * created or an unused one is reused. At most @max_threads threads
502
 * are running concurrently for this thread pool. @max_threads = -1
503
 * allows unlimited threads to be created for this thread pool. The
504
 * newly created or reused thread now executes the function @func
505
 * with the two arguments. The first one is the parameter to
506
 * g_thread_pool_push() and the second one is @user_data.
507
 *
508
 * Pass g_get_num_processors() to @max_threads to create as many threads as
509
 * there are logical processors on the system. This will not pin each thread to
510
 * a specific processor.
511
 *
512
 * The parameter @exclusive determines whether the thread pool owns
513
 * all threads exclusive or shares them with other thread pools.
514
 * If @exclusive is %TRUE, @max_threads threads are started
515
 * immediately and they will run exclusively for this thread pool
516
 * until it is destroyed by g_thread_pool_free(). If @exclusive is
517
 * %FALSE, threads are created when needed and shared between all
518
 * non-exclusive thread pools. This implies that @max_threads may
519
 * not be -1 for exclusive thread pools. Besides, exclusive thread
520
 * pools are not affected by g_thread_pool_set_max_idle_time()
521
 * since their threads are never considered idle and returned to the
522
 * global pool.
523
 *
524
 * Note that the threads used by exclusive thread pools will all inherit the
525
 * scheduler settings of the current thread while the threads used by
526
 * non-exclusive thread pools will inherit the scheduler settings from the
527
 * first thread that created such a thread pool.
528
 *
529
 * At least one thread will be spawned when this function is called, either to
530
 * create the @max_threads exclusive threads, or to preserve the scheduler
531
 * settings of the current thread for future spawns.
532
 *
533
 * @error can be %NULL to ignore errors, or non-%NULL to report
534
 * errors. An error can only occur when @exclusive is set to %TRUE
535
 * and not all @max_threads threads could be created.
536
 * See #GThreadError for possible errors that may occur.
537
 * Note, even in case of error a valid #GThreadPool is returned.
538
 *
539
 * Returns: the new #GThreadPool
540
 */
541
GThreadPool *
542
g_thread_pool_new (GFunc      func,
543
                   gpointer   user_data,
544
                   gint       max_threads,
545
                   gboolean   exclusive,
546
                   GError   **error)
547
2
{
548
2
  return g_thread_pool_new_full (func, user_data, NULL, max_threads, exclusive, error);
549
2
}
550
551
/**
552
 * g_thread_pool_new_full:
553
 * @func: a function to execute in the threads of the new thread pool
554
 * @user_data: user data that is handed over to @func every time it
555
 *     is called
556
 * @item_free_func: (nullable): used to pass as a free function to
557
 *     g_async_queue_new_full()
558
 * @max_threads: the maximal number of threads to execute concurrently
559
 *     in the new thread pool, `-1` means no limit
560
 * @exclusive: should this thread pool be exclusive?
561
 * @error: return location for error, or %NULL
562
 *
563
 * This function creates a new thread pool similar to g_thread_pool_new()
564
 * but allowing @item_free_func to be specified to free the data passed
565
 * to g_thread_pool_push() in the case that the #GThreadPool is stopped
566
 * and freed before all tasks have been executed.
567
 *
568
 * @item_free_func will *not* be called on items successfully passed to @func.
569
 * @func is responsible for freeing the items passed to it.
570
 *
571
 * Returns: (transfer full): the new #GThreadPool
572
 *
573
 * Since: 2.70
574
 */
575
GThreadPool *
576
g_thread_pool_new_full (GFunc           func,
577
                        gpointer        user_data,
578
                        GDestroyNotify  item_free_func,
579
                        gint            max_threads,
580
                        gboolean        exclusive,
581
                        GError        **error)
582
2
{
583
2
  GRealThreadPool *retval;
584
2
  G_LOCK_DEFINE_STATIC (init);
585
2
  GError *local_error = NULL;
586
587
2
  g_return_val_if_fail (func, NULL);
588
2
  g_return_val_if_fail (!exclusive || max_threads != -1, NULL);
589
2
  g_return_val_if_fail (max_threads >= -1, NULL);
590
591
2
  retval = g_new (GRealThreadPool, 1);
592
593
2
  retval->pool.func = func;
594
2
  retval->pool.user_data = user_data;
595
2
  retval->pool.exclusive = exclusive;
596
2
  retval->queue = g_async_queue_new_full (item_free_func);
597
2
  g_cond_init (&retval->cond);
598
2
  retval->max_threads = max_threads;
599
2
  retval->num_threads = 0;
600
2
  retval->running = TRUE;
601
2
  retval->immediate = FALSE;
602
2
  retval->waiting = FALSE;
603
2
  retval->sort_func = NULL;
604
2
  retval->sort_user_data = NULL;
605
606
2
  G_LOCK (init);
607
2
  if (!unused_thread_queue)
608
2
      unused_thread_queue = g_async_queue_new ();
609
610
  /*
611
   * Spawn a helper thread that is only responsible for spawning new threads
612
   * with the scheduler settings of the current thread.
613
   *
614
   * This is then used for making sure that all threads created on the
615
   * non-exclusive thread-pool have the same scheduler settings, and more
616
   * importantly don't just inherit them from the thread that just happened to
617
   * push a new task and caused a new thread to be created.
618
   *
619
   * Not doing so could cause real-time priority threads or otherwise
620
   * threads with problematic scheduler settings to be part of the
621
   * non-exclusive thread-pools.
622
   *
623
   * For exclusive thread-pools this is not required as all threads are
624
   * created immediately below and are running forever, so they will
625
   * automatically inherit the scheduler settings from this very thread.
626
   */
627
2
  if (!exclusive && !spawn_thread_queue)
628
2
    {
629
2
      GThread *pool_spawner = NULL;
630
631
2
      spawn_thread_queue = g_async_queue_new ();
632
2
      g_cond_init (&spawn_thread_cond);
633
2
      pool_spawner = g_thread_try_new ("pool-spawner", g_thread_pool_spawn_thread, NULL, &local_error);
634
2
      if (pool_spawner == NULL)
635
0
        {
636
          /* The only way to know that the pool_spawner exists is
637
           * if (spawn_thread_queue != NULL), so if creating the pool_spawner
638
           * failed, we must destroy the queue.
639
           */
640
0
          g_clear_pointer (&spawn_thread_queue, g_async_queue_unref);
641
          /* We must also clear spawn_thread_cond, so that a future attempt
642
           * to create a non-exclusive pool can safely initialize it.
643
           */
644
0
          g_cond_clear (&spawn_thread_cond);
645
0
        }
646
2
      g_ignore_leak (pool_spawner);
647
2
    }
648
2
  G_UNLOCK (init);
649
650
2
  if (retval->pool.exclusive && local_error == NULL)
651
0
    {
652
0
      g_async_queue_lock (retval->queue);
653
654
0
      while (retval->num_threads < (guint) retval->max_threads)
655
0
        {
656
0
          if (!g_thread_pool_start_thread (retval, &local_error))
657
0
            {
658
0
              break;
659
0
            }
660
0
        }
661
662
0
      g_async_queue_unlock (retval->queue);
663
0
    }
664
665
2
  if (local_error != NULL)
666
0
    {
667
      /* Failed to create pool spawner or failed to start a thread,
668
       * so we must return NULL */
669
0
      g_propagate_error (error, local_error);
670
671
0
      g_clear_pointer (&retval->queue, g_async_queue_unref);
672
0
      g_cond_clear (&retval->cond);
673
674
0
      g_clear_pointer (&retval, g_free);
675
0
    }
676
677
2
  return (GThreadPool*) retval;
678
2
}
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
16.2k
{
708
16.2k
  GRealThreadPool *real;
709
16.2k
  gboolean result;
710
711
16.2k
  real = (GRealThreadPool*) pool;
712
713
16.2k
  g_return_val_if_fail (real, FALSE);
714
16.2k
  g_return_val_if_fail (real->running, FALSE);
715
716
16.2k
  result = TRUE;
717
718
16.2k
  g_async_queue_lock (real->queue);
719
720
16.2k
  if (g_async_queue_length_unlocked (real->queue) >= 0)
721
135
    {
722
      /* No thread is waiting in the queue */
723
135
      GError *local_error = NULL;
724
725
135
      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
135
    }
731
732
16.2k
  g_thread_pool_queue_push_unlocked (real, data);
733
16.2k
  g_async_queue_unlock (real->queue);
734
735
16.2k
  return result;
736
16.2k
}
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 8 since GLib 2.84. Previously the default value was 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
}