Coverage Report

Created: 2026-07-11 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}