LCOV - code coverage report
Current view: top level - glib - gthreadpool.c (source / functions) Coverage Total Hit
Test: unnamed Lines: 93.0 % 300 279
Test Date: 2026-01-06 05:14:48 Functions: 100.0 % 24 24
Branches: - 0 0

             Branch data     Line data    Source code
       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                 :      105069 : g_thread_pool_queue_push_unlocked (GRealThreadPool *pool,
     131                 :             :                                    gpointer         data)
     132                 :             : {
     133                 :      105069 :   if (pool->sort_func)
     134                 :        3584 :     g_async_queue_push_sorted_unlocked (pool->queue,
     135                 :             :                                         data,
     136                 :             :                                         pool->sort_func,
     137                 :             :                                         pool->sort_user_data);
     138                 :             :   else
     139                 :      101485 :     g_async_queue_push_unlocked (pool->queue, data);
     140                 :      105069 : }
     141                 :             : 
     142                 :             : static GRealThreadPool*
     143                 :         274 : g_thread_pool_wait_for_new_pool (void)
     144                 :             : {
     145                 :             :   GRealThreadPool *pool;
     146                 :             :   gint local_wakeup_thread_serial;
     147                 :             :   guint local_max_unused_threads;
     148                 :             :   gint local_max_idle_time;
     149                 :             :   gint last_wakeup_thread_serial;
     150                 :         274 :   gboolean have_relayed_thread_marker = FALSE;
     151                 :             : 
     152                 :         274 :   local_max_unused_threads = (guint) g_atomic_int_get (&max_unused_threads);
     153                 :         274 :   local_max_idle_time = g_atomic_int_get (&max_idle_time);
     154                 :         274 :   last_wakeup_thread_serial = g_atomic_int_get (&wakeup_thread_serial);
     155                 :             : 
     156                 :             :   do
     157                 :             :     {
     158                 :         274 :       if ((guint) g_atomic_int_get (&unused_threads) >= local_max_unused_threads)
     159                 :             :         {
     160                 :             :           /* If this is a superfluous thread, stop it. */
     161                 :          33 :           pool = NULL;
     162                 :             :         }
     163                 :         241 :       else if (local_max_idle_time > 0)
     164                 :             :         {
     165                 :             :           /* If a maximal idle time is given, wait for the given time. */
     166                 :             :           DEBUG_MSG (("thread %p waiting in global pool for %f seconds.",
     167                 :             :                       g_thread_self (), local_max_idle_time / 1000.0));
     168                 :             : 
     169                 :          78 :           pool = g_async_queue_timeout_pop (unused_thread_queue,
     170                 :          78 :                                             local_max_idle_time * 1000);
     171                 :             :         }
     172                 :             :       else
     173                 :             :         {
     174                 :             :           /* If no maximal idle time is given, wait indefinitely. */
     175                 :             :           DEBUG_MSG (("thread %p waiting in global pool.", g_thread_self ()));
     176                 :         163 :           pool = g_async_queue_pop (unused_thread_queue);
     177                 :             :         }
     178                 :             : 
     179                 :         250 :       if (pool == wakeup_thread_marker)
     180                 :             :         {
     181                 :         144 :           local_wakeup_thread_serial = g_atomic_int_get (&wakeup_thread_serial);
     182                 :         144 :           if (last_wakeup_thread_serial == local_wakeup_thread_serial)
     183                 :             :             {
     184                 :           0 :               if (!have_relayed_thread_marker)
     185                 :             :               {
     186                 :             :                 /* If this wakeup marker has been received for
     187                 :             :                  * the second time, relay it.
     188                 :             :                  */
     189                 :             :                 DEBUG_MSG (("thread %p relaying wakeup message to "
     190                 :             :                             "waiting thread with lower serial.",
     191                 :             :                             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                 :             :               }
     202                 :             :             }
     203                 :             :           else
     204                 :             :             {
     205                 :         144 :               if (g_atomic_int_add (&kill_unused_threads, -1) > 0)
     206                 :             :                 {
     207                 :         144 :                   pool = NULL;
     208                 :         144 :                   break;
     209                 :             :                 }
     210                 :             : 
     211                 :             :               DEBUG_MSG (("thread %p updating to new limits.",
     212                 :             :                           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                 :             :             }
     220                 :             :         }
     221                 :             :     }
     222                 :         106 :   while (pool == wakeup_thread_marker);
     223                 :             : 
     224                 :         250 :   return pool;
     225                 :             : }
     226                 :             : 
     227                 :             : static gpointer
     228                 :      105008 : g_thread_pool_wait_for_new_task (GRealThreadPool *pool)
     229                 :             : {
     230                 :      105008 :   gpointer task = NULL;
     231                 :             : 
     232                 :      114633 :   if (pool->running || (!pool->immediate &&
     233                 :        9625 :                         g_async_queue_length_unlocked (pool->queue) > 0))
     234                 :             :     {
     235                 :             :       /* This thread pool is still active. */
     236                 :      104912 :       if (pool->max_threads != -1 && pool->num_threads > (guint) pool->max_threads)
     237                 :             :         {
     238                 :             :           /* This is a superfluous thread, so it goes to the global pool. */
     239                 :             :           DEBUG_MSG (("superfluous thread %p in pool %p.",
     240                 :             :                       g_thread_self (), pool));
     241                 :             :         }
     242                 :      104906 :       else if (pool->pool.exclusive)
     243                 :             :         {
     244                 :             :           /* Exclusive threads stay attached to the pool. */
     245                 :      100627 :           task = g_async_queue_pop_unlocked (pool->queue);
     246                 :             : 
     247                 :             :           DEBUG_MSG (("thread %p in exclusive pool %p waits for task "
     248                 :             :                       "(%d running, %d unprocessed).",
     249                 :             :                       g_thread_self (), pool, pool->num_threads,
     250                 :             :                       g_async_queue_length_unlocked (pool->queue)));
     251                 :             :         }
     252                 :             :       else
     253                 :             :         {
     254                 :             :           /* A thread will wait for new tasks for at most 1/2
     255                 :             :            * second before going to the global pool.
     256                 :             :            */
     257                 :             :           DEBUG_MSG (("thread %p in pool %p waits for up to a 1/2 second for task "
     258                 :             :                       "(%d running, %d unprocessed).",
     259                 :             :                       g_thread_self (), pool, pool->num_threads,
     260                 :             :                       g_async_queue_length_unlocked (pool->queue)));
     261                 :             : 
     262                 :        4279 :           task = g_async_queue_timeout_pop_unlocked (pool->queue,
     263                 :             :                                                      G_USEC_PER_SEC / 2);
     264                 :             :         }
     265                 :             :     }
     266                 :             :   else
     267                 :             :     {
     268                 :             :       /* This thread pool is inactive, it will no longer process tasks. */
     269                 :             :       DEBUG_MSG (("pool %p not active, thread %p will go to global pool "
     270                 :             :                   "(running: %s, immediate: %s, len: %d).",
     271                 :             :                   pool, g_thread_self (),
     272                 :             :                   pool->running ? "true" : "false",
     273                 :             :                   pool->immediate ? "true" : "false",
     274                 :             :                   g_async_queue_length_unlocked (pool->queue)));
     275                 :             :     }
     276                 :             : 
     277                 :      104893 :   return task;
     278                 :             : }
     279                 :             : 
     280                 :             : static gpointer
     281                 :         177 : g_thread_pool_spawn_thread (gpointer data)
     282                 :             : {
     283                 :             :   while (TRUE)
     284                 :         281 :     {
     285                 :             :       SpawnThreadData *spawn_thread_data;
     286                 :         458 :       GThread *thread = NULL;
     287                 :         458 :       GError *error = NULL;
     288                 :             :       gchar name[16];
     289                 :             : 
     290                 :         458 :       g_snprintf (name, sizeof (name), "pool-%d", g_atomic_int_add (&thread_counter, 1));
     291                 :             : 
     292                 :         458 :       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                 :         458 :       spawn_thread_data = g_async_queue_pop_unlocked (spawn_thread_queue);
     298                 :         281 :       thread = g_thread_try_new (name, g_thread_pool_thread_proxy, spawn_thread_data->pool, &error);
     299                 :             : 
     300                 :         281 :       spawn_thread_data->thread = g_steal_pointer (&thread);
     301                 :         281 :       spawn_thread_data->error = g_steal_pointer (&error);
     302                 :             : 
     303                 :         281 :       g_cond_broadcast (&spawn_thread_cond);
     304                 :         281 :       g_async_queue_unlock (spawn_thread_queue);
     305                 :             :     }
     306                 :             : 
     307                 :             :   return NULL;
     308                 :             : }
     309                 :             : 
     310                 :             : static gpointer
     311                 :         326 : g_thread_pool_thread_proxy (gpointer data)
     312                 :             : {
     313                 :             :   GRealThreadPool *pool;
     314                 :             : 
     315                 :         326 :   pool = data;
     316                 :             : 
     317                 :             :   DEBUG_MSG (("thread %p started for pool %p.", g_thread_self (), pool));
     318                 :             : 
     319                 :         326 :   g_async_queue_lock (pool->queue);
     320                 :             : 
     321                 :             :   while (TRUE)
     322                 :      104682 :     {
     323                 :             :       gpointer task;
     324                 :             : 
     325                 :      105008 :       task = g_thread_pool_wait_for_new_task (pool);
     326                 :      104893 :       if (task)
     327                 :             :         {
     328                 :      104619 :           if (pool->running || !pool->immediate)
     329                 :             :             {
     330                 :             :               /* A task was received and the thread pool is active,
     331                 :             :                * so execute the function.
     332                 :             :                */
     333                 :      104576 :               g_async_queue_unlock (pool->queue);
     334                 :             :               DEBUG_MSG (("thread %p in pool %p calling func.",
     335                 :             :                           g_thread_self (), pool));
     336                 :      104576 :               pool->pool.func (task, pool->pool.user_data);
     337                 :      104576 :               g_async_queue_lock (pool->queue);
     338                 :             :             }
     339                 :             :         }
     340                 :             :       else
     341                 :             :         {
     342                 :             :           /* No task was received, so this thread goes to the global pool. */
     343                 :         274 :           gboolean free_pool = FALSE;
     344                 :             : 
     345                 :             :           DEBUG_MSG (("thread %p leaving pool %p for global pool.",
     346                 :             :                       g_thread_self (), pool));
     347                 :         274 :           pool->num_threads--;
     348                 :             : 
     349                 :         274 :           if (!pool->running)
     350                 :             :             {
     351                 :          96 :               if (!pool->waiting)
     352                 :             :                 {
     353                 :          47 :                   if (pool->num_threads == 0)
     354                 :             :                     {
     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                 :          25 :                       free_pool = TRUE;
     361                 :             :                     }
     362                 :             :                   else
     363                 :             :                     {
     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                 :          22 :                       if (g_async_queue_length_unlocked (pool->queue) ==
     371                 :          22 :                           (gint) -pool->num_threads)
     372                 :           0 :                         g_thread_pool_wakeup_and_stop_all (pool);
     373                 :             :                     }
     374                 :             :                 }
     375                 :          78 :               else if (pool->immediate ||
     376                 :          29 :                        g_async_queue_length_unlocked (pool->queue) <= 0)
     377                 :             :                 {
     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                 :          49 :                   g_cond_broadcast (&pool->cond);
     385                 :             :                 }
     386                 :             :             }
     387                 :             : 
     388                 :         274 :           g_atomic_int_inc (&unused_threads);
     389                 :         274 :           g_async_queue_unlock (pool->queue);
     390                 :             : 
     391                 :         274 :           if (free_pool)
     392                 :          25 :             g_thread_pool_free_internal (pool);
     393                 :             : 
     394                 :         274 :           pool = g_thread_pool_wait_for_new_pool ();
     395                 :         250 :           g_atomic_int_add (&unused_threads, -1);
     396                 :             : 
     397                 :         250 :           if (pool == NULL)
     398                 :         187 :             break;
     399                 :             : 
     400                 :          63 :           g_async_queue_lock (pool->queue);
     401                 :             : 
     402                 :             :           DEBUG_MSG (("thread %p entering pool %p from global pool.",
     403                 :             :                       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                 :             :         }
     410                 :             :     }
     411                 :             : 
     412                 :         187 :   return NULL;
     413                 :             : }
     414                 :             : 
     415                 :             : static gboolean
     416                 :      569739 : g_thread_pool_start_thread (GRealThreadPool  *pool,
     417                 :             :                             GError          **error)
     418                 :             : {
     419                 :      569739 :   gboolean success = FALSE;
     420                 :             : 
     421                 :      569739 :   if (pool->max_threads != -1 && pool->num_threads >= (guint) pool->max_threads)
     422                 :             :     /* Enough threads are already running */
     423                 :      569349 :     return TRUE;
     424                 :             : 
     425                 :         390 :   g_async_queue_lock (unused_thread_queue);
     426                 :             : 
     427                 :         390 :   if (g_async_queue_length_unlocked (unused_thread_queue) < 0)
     428                 :             :     {
     429                 :          63 :       g_async_queue_push_unlocked (unused_thread_queue, pool);
     430                 :          63 :       success = TRUE;
     431                 :             :     }
     432                 :             : 
     433                 :         390 :   g_async_queue_unlock (unused_thread_queue);
     434                 :             : 
     435                 :         390 :   if (!success)
     436                 :             :     {
     437                 :             :       GThread *thread;
     438                 :             : 
     439                 :             :       /* No thread was found, we have to start a new one */
     440                 :         327 :       if (pool->pool.exclusive)
     441                 :             :         {
     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                 :             :           char name[16];
     447                 :             : 
     448                 :          46 :           g_snprintf (name, sizeof (name), "pool-%d", g_atomic_int_add (&thread_counter, 1));
     449                 :             : 
     450                 :          46 :           thread = g_thread_try_new (name, g_thread_pool_thread_proxy, pool, error);
     451                 :             :         }
     452                 :             :       else
     453                 :             :         {
     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                 :         281 :           SpawnThreadData spawn_thread_data = { (GThreadPool *) pool, NULL, NULL };
     460                 :             : 
     461                 :         281 :           g_async_queue_lock (spawn_thread_queue);
     462                 :             : 
     463                 :         281 :           g_async_queue_push_unlocked (spawn_thread_queue, &spawn_thread_data);
     464                 :             : 
     465                 :         562 :           while (!spawn_thread_data.thread && !spawn_thread_data.error)
     466                 :         281 :             g_cond_wait (&spawn_thread_cond, _g_async_queue_get_mutex (spawn_thread_queue));
     467                 :             : 
     468                 :         281 :           thread = spawn_thread_data.thread;
     469                 :         281 :           if (!thread)
     470                 :           0 :             g_propagate_error (error, g_steal_pointer (&spawn_thread_data.error));
     471                 :         281 :           g_async_queue_unlock (spawn_thread_queue);
     472                 :             :         }
     473                 :             : 
     474                 :         327 :       if (thread == NULL)
     475                 :           1 :         return FALSE;
     476                 :             : 
     477                 :         326 :       g_thread_unref (thread);
     478                 :             :     }
     479                 :             : 
     480                 :             :   /* See comment in g_thread_pool_thread_proxy as to why this is done
     481                 :             :    * here and not there
     482                 :             :    */
     483                 :         389 :   pool->num_threads++;
     484                 :             : 
     485                 :         389 :   return TRUE;
     486                 :             : }
     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                 :         228 : g_thread_pool_new (GFunc      func,
     543                 :             :                    gpointer   user_data,
     544                 :             :                    gint       max_threads,
     545                 :             :                    gboolean   exclusive,
     546                 :             :                    GError   **error)
     547                 :             : {
     548                 :         228 :   return g_thread_pool_new_full (func, user_data, NULL, max_threads, exclusive, error);
     549                 :             : }
     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                 :         235 : 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                 :             : {
     583                 :             :   GRealThreadPool *retval;
     584                 :             :   G_LOCK_DEFINE_STATIC (init);
     585                 :         235 :   GError *local_error = NULL;
     586                 :             : 
     587                 :         235 :   g_return_val_if_fail (func, NULL);
     588                 :         235 :   g_return_val_if_fail (!exclusive || max_threads != -1, NULL);
     589                 :         235 :   g_return_val_if_fail (max_threads >= -1, NULL);
     590                 :             : 
     591                 :         235 :   retval = g_new (GRealThreadPool, 1);
     592                 :             : 
     593                 :         235 :   retval->pool.func = func;
     594                 :         235 :   retval->pool.user_data = user_data;
     595                 :         235 :   retval->pool.exclusive = exclusive;
     596                 :         235 :   retval->queue = g_async_queue_new_full (item_free_func);
     597                 :         235 :   g_cond_init (&retval->cond);
     598                 :         235 :   retval->max_threads = max_threads;
     599                 :         235 :   retval->num_threads = 0;
     600                 :         235 :   retval->running = TRUE;
     601                 :         235 :   retval->immediate = FALSE;
     602                 :         235 :   retval->waiting = FALSE;
     603                 :         235 :   retval->sort_func = NULL;
     604                 :         235 :   retval->sort_user_data = NULL;
     605                 :             : 
     606                 :         235 :   G_LOCK (init);
     607                 :         235 :   if (!unused_thread_queue)
     608                 :         178 :       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                 :         235 :   if (!exclusive && !spawn_thread_queue)
     628                 :             :     {
     629                 :         178 :       GThread *pool_spawner = NULL;
     630                 :             : 
     631                 :         178 :       spawn_thread_queue = g_async_queue_new ();
     632                 :         178 :       g_cond_init (&spawn_thread_cond);
     633                 :         178 :       pool_spawner = g_thread_try_new ("pool-spawner", g_thread_pool_spawn_thread, NULL, &local_error);
     634                 :         178 :       if (pool_spawner == NULL)
     635                 :             :         {
     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                 :           1 :           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                 :           1 :           g_cond_clear (&spawn_thread_cond);
     645                 :             :         }
     646                 :         178 :       g_ignore_leak (pool_spawner);
     647                 :             :     }
     648                 :         235 :   G_UNLOCK (init);
     649                 :             : 
     650                 :         235 :   if (retval->pool.exclusive && local_error == NULL)
     651                 :             :     {
     652                 :           9 :       g_async_queue_lock (retval->queue);
     653                 :             : 
     654                 :          68 :       while (retval->num_threads < (guint) retval->max_threads)
     655                 :             :         {
     656                 :          60 :           if (!g_thread_pool_start_thread (retval, &local_error))
     657                 :             :             {
     658                 :           1 :               break;
     659                 :             :             }
     660                 :             :         }
     661                 :             : 
     662                 :           9 :       g_async_queue_unlock (retval->queue);
     663                 :             :     }
     664                 :             : 
     665                 :         235 :   if (local_error != NULL)
     666                 :             :     {
     667                 :             :       /* Failed to create pool spawner or failed to start a thread,
     668                 :             :        * so we must return NULL */
     669                 :           2 :       g_propagate_error (error, local_error);
     670                 :             : 
     671                 :           2 :       g_clear_pointer (&retval->queue, g_async_queue_unref);
     672                 :           2 :       g_cond_clear (&retval->cond);
     673                 :             : 
     674                 :           2 :       g_clear_pointer (&retval, g_free);
     675                 :             :     }
     676                 :             : 
     677                 :         235 :   return (GThreadPool*) retval;
     678                 :             : }
     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                 :      105069 : g_thread_pool_push (GThreadPool  *pool,
     705                 :             :                     gpointer      data,
     706                 :             :                     GError      **error)
     707                 :             : {
     708                 :             :   GRealThreadPool *real;
     709                 :             :   gboolean result;
     710                 :             : 
     711                 :      105069 :   real = (GRealThreadPool*) pool;
     712                 :             : 
     713                 :      105069 :   g_return_val_if_fail (real, FALSE);
     714                 :      105069 :   g_return_val_if_fail (real->running, FALSE);
     715                 :             : 
     716                 :      105069 :   result = TRUE;
     717                 :             : 
     718                 :      105069 :   g_async_queue_lock (real->queue);
     719                 :             : 
     720                 :      105069 :   if (g_async_queue_length_unlocked (real->queue) >= 0)
     721                 :             :     {
     722                 :             :       /* No thread is waiting in the queue */
     723                 :       50771 :       GError *local_error = NULL;
     724                 :             : 
     725                 :       50771 :       if (!g_thread_pool_start_thread (real, &local_error))
     726                 :             :         {
     727                 :           0 :           g_propagate_error (error, local_error);
     728                 :           0 :           result = FALSE;
     729                 :             :         }
     730                 :             :     }
     731                 :             : 
     732                 :      105069 :   g_thread_pool_queue_push_unlocked (real, data);
     733                 :      105069 :   g_async_queue_unlock (real->queue);
     734                 :             : 
     735                 :      105069 :   return result;
     736                 :             : }
     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                 :        3492 : g_thread_pool_set_max_threads (GThreadPool  *pool,
     770                 :             :                                gint          max_threads,
     771                 :             :                                GError      **error)
     772                 :             : {
     773                 :             :   GRealThreadPool *real;
     774                 :             :   gint to_start;
     775                 :             :   gboolean result;
     776                 :             : 
     777                 :        3492 :   real = (GRealThreadPool*) pool;
     778                 :             : 
     779                 :        3492 :   g_return_val_if_fail (real, FALSE);
     780                 :        3492 :   g_return_val_if_fail (real->running, FALSE);
     781                 :        3492 :   g_return_val_if_fail (!real->pool.exclusive || max_threads != -1, FALSE);
     782                 :        3492 :   g_return_val_if_fail (max_threads >= -1, FALSE);
     783                 :             : 
     784                 :        3492 :   result = TRUE;
     785                 :             : 
     786                 :        3492 :   g_async_queue_lock (real->queue);
     787                 :             : 
     788                 :        3492 :   real->max_threads = max_threads;
     789                 :             : 
     790                 :        3492 :   if (pool->exclusive)
     791                 :           0 :     to_start = real->max_threads - real->num_threads;
     792                 :             :   else
     793                 :        3492 :     to_start = g_async_queue_length_unlocked (real->queue);
     794                 :             : 
     795                 :      522400 :   for ( ; to_start > 0; to_start--)
     796                 :             :     {
     797                 :      518908 :       GError *local_error = NULL;
     798                 :             : 
     799                 :      518908 :       if (!g_thread_pool_start_thread (real, &local_error))
     800                 :             :         {
     801                 :           0 :           g_propagate_error (error, local_error);
     802                 :           0 :           result = FALSE;
     803                 :           0 :           break;
     804                 :             :         }
     805                 :             :     }
     806                 :             : 
     807                 :        3492 :   g_async_queue_unlock (real->queue);
     808                 :             : 
     809                 :        3492 :   return result;
     810                 :             : }
     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                 :           5 : g_thread_pool_get_max_threads (GThreadPool *pool)
     822                 :             : {
     823                 :             :   GRealThreadPool *real;
     824                 :             :   gint retval;
     825                 :             : 
     826                 :           5 :   real = (GRealThreadPool*) pool;
     827                 :             : 
     828                 :           5 :   g_return_val_if_fail (real, 0);
     829                 :           5 :   g_return_val_if_fail (real->running, 0);
     830                 :             : 
     831                 :           5 :   g_async_queue_lock (real->queue);
     832                 :           5 :   retval = real->max_threads;
     833                 :           5 :   g_async_queue_unlock (real->queue);
     834                 :             : 
     835                 :           5 :   return retval;
     836                 :             : }
     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                 :     3679548 : g_thread_pool_get_num_threads (GThreadPool *pool)
     848                 :             : {
     849                 :             :   GRealThreadPool *real;
     850                 :             :   guint retval;
     851                 :             : 
     852                 :     3679548 :   real = (GRealThreadPool*) pool;
     853                 :             : 
     854                 :     3679548 :   g_return_val_if_fail (real, 0);
     855                 :     3679548 :   g_return_val_if_fail (real->running, 0);
     856                 :             : 
     857                 :     3679548 :   g_async_queue_lock (real->queue);
     858                 :     3679548 :   retval = real->num_threads;
     859                 :     3679548 :   g_async_queue_unlock (real->queue);
     860                 :             : 
     861                 :     3679548 :   return retval;
     862                 :             : }
     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                 :        3764 : g_thread_pool_unprocessed (GThreadPool *pool)
     874                 :             : {
     875                 :             :   GRealThreadPool *real;
     876                 :             :   gint unprocessed;
     877                 :             : 
     878                 :        3764 :   real = (GRealThreadPool*) pool;
     879                 :             : 
     880                 :        3764 :   g_return_val_if_fail (real, 0);
     881                 :        3764 :   g_return_val_if_fail (real->running, 0);
     882                 :             : 
     883                 :        3764 :   unprocessed = g_async_queue_length (real->queue);
     884                 :             : 
     885                 :        3764 :   return MAX (unprocessed, 0);
     886                 :             : }
     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                 :          48 : g_thread_pool_free (GThreadPool *pool,
     911                 :             :                     gboolean     immediate,
     912                 :             :                     gboolean     wait_)
     913                 :             : {
     914                 :             :   GRealThreadPool *real;
     915                 :             : 
     916                 :          48 :   real = (GRealThreadPool*) pool;
     917                 :             : 
     918                 :          48 :   g_return_if_fail (real);
     919                 :          48 :   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                 :          48 :   g_return_if_fail (immediate ||
     925                 :             :                     real->max_threads != 0 ||
     926                 :             :                     g_async_queue_length (real->queue) == 0);
     927                 :             : 
     928                 :          48 :   g_async_queue_lock (real->queue);
     929                 :             : 
     930                 :          48 :   real->running = FALSE;
     931                 :          48 :   real->immediate = immediate;
     932                 :          48 :   real->waiting = wait_;
     933                 :             : 
     934                 :          48 :   if (wait_)
     935                 :             :     {
     936                 :          62 :       while (g_async_queue_length_unlocked (real->queue) != (gint) -real->num_threads &&
     937                 :          25 :              !(immediate && real->num_threads == 0))
     938                 :          37 :         g_cond_wait (&real->cond, _g_async_queue_get_mutex (real->queue));
     939                 :             :     }
     940                 :             : 
     941                 :          48 :   if (immediate || g_async_queue_length_unlocked (real->queue) == (gint) -real->num_threads)
     942                 :             :     {
     943                 :             :       /* No thread is currently doing something (and nothing is left
     944                 :             :        * to process in the queue)
     945                 :             :        */
     946                 :          46 :       if (real->num_threads == 0)
     947                 :             :         {
     948                 :             :           /* No threads left, we clean up */
     949                 :          23 :           g_async_queue_unlock (real->queue);
     950                 :          23 :           g_thread_pool_free_internal (real);
     951                 :          23 :           return;
     952                 :             :         }
     953                 :             : 
     954                 :          23 :       g_thread_pool_wakeup_and_stop_all (real);
     955                 :             :     }
     956                 :             : 
     957                 :             :   /* The last thread should cleanup the pool */
     958                 :          25 :   real->waiting = FALSE;
     959                 :          25 :   g_async_queue_unlock (real->queue);
     960                 :             : }
     961                 :             : 
     962                 :             : static void
     963                 :          48 : g_thread_pool_free_internal (GRealThreadPool* pool)
     964                 :             : {
     965                 :          48 :   g_return_if_fail (pool);
     966                 :          48 :   g_return_if_fail (pool->running == FALSE);
     967                 :          48 :   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                 :          48 :   g_async_queue_remove (pool->queue, GUINT_TO_POINTER (1));
     973                 :             : 
     974                 :          48 :   g_async_queue_unref (pool->queue);
     975                 :          48 :   g_cond_clear (&pool->cond);
     976                 :             : 
     977                 :          48 :   g_free (pool);
     978                 :             : }
     979                 :             : 
     980                 :             : static void
     981                 :          23 : g_thread_pool_wakeup_and_stop_all (GRealThreadPool *pool)
     982                 :             : {
     983                 :             :   guint i;
     984                 :             : 
     985                 :          23 :   g_return_if_fail (pool);
     986                 :          23 :   g_return_if_fail (pool->running == FALSE);
     987                 :          23 :   g_return_if_fail (pool->num_threads != 0);
     988                 :             : 
     989                 :          23 :   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                 :          68 :   for (i = 0; i < pool->num_threads; i++)
     998                 :          45 :     g_async_queue_push_unlocked (pool->queue, GUINT_TO_POINTER (1));
     999                 :             : }
    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                 :          20 : g_thread_pool_set_max_unused_threads (gint max_threads)
    1013                 :             : {
    1014                 :          20 :   g_return_if_fail (max_threads >= -1);
    1015                 :             : 
    1016                 :          20 :   g_atomic_int_set (&max_unused_threads, max_threads);
    1017                 :             : 
    1018                 :          20 :   if (max_threads != -1)
    1019                 :             :     {
    1020                 :          13 :       max_threads -= g_atomic_int_get (&unused_threads);
    1021                 :          13 :       if (max_threads < 0)
    1022                 :             :         {
    1023                 :           3 :           g_atomic_int_set (&kill_unused_threads, -max_threads);
    1024                 :           3 :           g_atomic_int_inc (&wakeup_thread_serial);
    1025                 :             : 
    1026                 :           3 :           g_async_queue_lock (unused_thread_queue);
    1027                 :             : 
    1028                 :             :           do
    1029                 :             :             {
    1030                 :         144 :               g_async_queue_push_unlocked (unused_thread_queue,
    1031                 :             :                                            wakeup_thread_marker);
    1032                 :             :             }
    1033                 :         144 :           while (++max_threads);
    1034                 :             : 
    1035                 :           3 :           g_async_queue_unlock (unused_thread_queue);
    1036                 :             :         }
    1037                 :             :     }
    1038                 :             : }
    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                 :           7 : g_thread_pool_get_max_unused_threads (void)
    1049                 :             : {
    1050                 :           7 :   return g_atomic_int_get (&max_unused_threads);
    1051                 :             : }
    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                 :         183 : g_thread_pool_get_num_unused_threads (void)
    1062                 :             : {
    1063                 :         183 :   return (guint) g_atomic_int_get (&unused_threads);
    1064                 :             : }
    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                 :           5 : g_thread_pool_stop_unused_threads (void)
    1075                 :             : {
    1076                 :             :   guint oldval;
    1077                 :             : 
    1078                 :           5 :   oldval = g_thread_pool_get_max_unused_threads ();
    1079                 :             : 
    1080                 :           5 :   g_thread_pool_set_max_unused_threads (0);
    1081                 :           5 :   g_thread_pool_set_max_unused_threads (oldval);
    1082                 :           5 : }
    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                 :         174 : g_thread_pool_set_sort_function (GThreadPool      *pool,
    1109                 :             :                                  GCompareDataFunc  func,
    1110                 :             :                                  gpointer          user_data)
    1111                 :             : {
    1112                 :             :   GRealThreadPool *real;
    1113                 :             : 
    1114                 :         174 :   real = (GRealThreadPool*) pool;
    1115                 :             : 
    1116                 :         174 :   g_return_if_fail (real);
    1117                 :         174 :   g_return_if_fail (real->running);
    1118                 :             : 
    1119                 :         174 :   g_async_queue_lock (real->queue);
    1120                 :             : 
    1121                 :         174 :   real->sort_func = func;
    1122                 :         174 :   real->sort_user_data = user_data;
    1123                 :             : 
    1124                 :         174 :   if (func)
    1125                 :         174 :     g_async_queue_sort_unlocked (real->queue,
    1126                 :             :                                  real->sort_func,
    1127                 :             :                                  real->sort_user_data);
    1128                 :             : 
    1129                 :         174 :   g_async_queue_unlock (real->queue);
    1130                 :             : }
    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                 :        1036 : g_thread_pool_move_to_front (GThreadPool *pool,
    1146                 :             :                              gpointer     data)
    1147                 :             : {
    1148                 :        1036 :   GRealThreadPool *real = (GRealThreadPool*) pool;
    1149                 :             :   gboolean found;
    1150                 :             : 
    1151                 :        1036 :   g_async_queue_lock (real->queue);
    1152                 :             : 
    1153                 :        1036 :   found = g_async_queue_remove_unlocked (real->queue, data);
    1154                 :        1036 :   if (found)
    1155                 :        1015 :     g_async_queue_push_front_unlocked (real->queue, data);
    1156                 :             : 
    1157                 :        1036 :   g_async_queue_unlock (real->queue);
    1158                 :             : 
    1159                 :        1036 :   return found;
    1160                 :             : }
    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                 :           3 : g_thread_pool_set_max_idle_time (guint interval)
    1181                 :             : {
    1182                 :             :   guint i;
    1183                 :             : 
    1184                 :           3 :   g_atomic_int_set (&max_idle_time, interval);
    1185                 :             : 
    1186                 :           3 :   i = (guint) g_atomic_int_get (&unused_threads);
    1187                 :           3 :   if (i > 0)
    1188                 :             :     {
    1189                 :           0 :       g_atomic_int_inc (&wakeup_thread_serial);
    1190                 :           0 :       g_async_queue_lock (unused_thread_queue);
    1191                 :             : 
    1192                 :             :       do
    1193                 :             :         {
    1194                 :           0 :           g_async_queue_push_unlocked (unused_thread_queue,
    1195                 :             :                                        wakeup_thread_marker);
    1196                 :             :         }
    1197                 :           0 :       while (--i);
    1198                 :             : 
    1199                 :           0 :       g_async_queue_unlock (unused_thread_queue);
    1200                 :             :     }
    1201                 :           3 : }
    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                 :           3 : g_thread_pool_get_max_idle_time (void)
    1221                 :             : {
    1222                 :           3 :   return (guint) g_atomic_int_get (&max_idle_time);
    1223                 :             : }
        

Generated by: LCOV version 2.0-1