(root)/
glib-2.79.0/
gio/
gioscheduler.c
       1  /* GIO - GLib Input, Output and Streaming Library
       2   * 
       3   * Copyright (C) 2006-2007 Red Hat, Inc.
       4   *
       5   * SPDX-License-Identifier: LGPL-2.1-or-later
       6   *
       7   * This library is free software; you can redistribute it and/or
       8   * modify it under the terms of the GNU Lesser General Public
       9   * License as published by the Free Software Foundation; either
      10   * version 2.1 of the License, or (at your option) any later version.
      11   *
      12   * This library is distributed in the hope that it will be useful,
      13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15   * Lesser General Public License for more details.
      16   *
      17   * You should have received a copy of the GNU Lesser General
      18   * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
      19   *
      20   * Author: Alexander Larsson <alexl@redhat.com>
      21   */
      22  
      23  #include "config.h"
      24  
      25  #include "gioscheduler.h"
      26  #include "gcancellable.h"
      27  #include "gtask.h"
      28  
      29  struct _GIOSchedulerJob {
      30    GList *active_link;
      31    GTask *task;
      32  
      33    GIOSchedulerJobFunc job_func;
      34    gpointer data;
      35    GDestroyNotify destroy_notify;
      36  
      37    GCancellable *cancellable;
      38    gulong cancellable_id;
      39    GMainContext *context;
      40  };
      41  
      42  G_LOCK_DEFINE_STATIC(active_jobs);
      43  static GList *active_jobs = NULL;
      44  
      45  static void
      46  g_io_job_free (GIOSchedulerJob *job)
      47  {
      48    if (job->destroy_notify)
      49      job->destroy_notify (job->data);
      50  
      51    G_LOCK (active_jobs);
      52    active_jobs = g_list_delete_link (active_jobs, job->active_link);
      53    G_UNLOCK (active_jobs);
      54  
      55    if (job->cancellable)
      56      g_object_unref (job->cancellable);
      57    g_main_context_unref (job->context);
      58    g_slice_free (GIOSchedulerJob, job);
      59  }
      60  
      61  static void
      62  io_job_thread (GTask         *task,
      63                 gpointer       source_object,
      64                 gpointer       task_data,
      65                 GCancellable  *cancellable)
      66  {
      67    GIOSchedulerJob *job = task_data;
      68    gboolean result;
      69  
      70    if (job->cancellable)
      71      g_cancellable_push_current (job->cancellable);
      72  
      73    do 
      74      {
      75        result = job->job_func (job, job->cancellable, job->data);
      76      }
      77    while (result);
      78  
      79    if (job->cancellable)
      80      g_cancellable_pop_current (job->cancellable);
      81  }
      82  
      83  /**
      84   * g_io_scheduler_push_job:
      85   * @job_func: a #GIOSchedulerJobFunc.
      86   * @user_data: data to pass to @job_func
      87   * @notify: (nullable): a #GDestroyNotify for @user_data, or %NULL
      88   * @io_priority: the [I/O priority][io-priority]
      89   * of the request.
      90   * @cancellable: optional #GCancellable object, %NULL to ignore.
      91   *
      92   * Schedules the I/O job to run in another thread.
      93   *
      94   * @notify will be called on @user_data after @job_func has returned,
      95   * regardless whether the job was cancelled or has run to completion.
      96   * 
      97   * If @cancellable is not %NULL, it can be used to cancel the I/O job
      98   * by calling g_cancellable_cancel() or by calling 
      99   * g_io_scheduler_cancel_all_jobs().
     100   *
     101   * Deprecated: 2.36: use #GThreadPool or g_task_run_in_thread()
     102   **/
     103  void
     104  g_io_scheduler_push_job (GIOSchedulerJobFunc  job_func,
     105  			 gpointer             user_data,
     106  			 GDestroyNotify       notify,
     107  			 gint                 io_priority,
     108  			 GCancellable        *cancellable)
     109  {
     110    GIOSchedulerJob *job;
     111    GTask *task;
     112  
     113    g_return_if_fail (job_func != NULL);
     114  
     115    job = g_slice_new0 (GIOSchedulerJob);
     116    job->job_func = job_func;
     117    job->data = user_data;
     118    job->destroy_notify = notify;
     119  
     120    if (cancellable)
     121      job->cancellable = g_object_ref (cancellable);
     122  
     123    job->context = g_main_context_ref_thread_default ();
     124  
     125    G_LOCK (active_jobs);
     126    active_jobs = g_list_prepend (active_jobs, job);
     127    job->active_link = active_jobs;
     128    G_UNLOCK (active_jobs);
     129  
     130    task = g_task_new (NULL, cancellable, NULL, NULL);
     131  G_GNUC_BEGIN_IGNORE_DEPRECATIONS
     132    g_task_set_source_tag (task, g_io_scheduler_push_job);
     133  G_GNUC_END_IGNORE_DEPRECATIONS
     134    g_task_set_task_data (task, job, (GDestroyNotify)g_io_job_free);
     135    g_task_set_priority (task, io_priority);
     136    g_task_run_in_thread (task, io_job_thread);
     137    g_object_unref (task);
     138  }
     139  
     140  /**
     141   * g_io_scheduler_cancel_all_jobs:
     142   * 
     143   * Cancels all cancellable I/O jobs. 
     144   *
     145   * A job is cancellable if a #GCancellable was passed into
     146   * g_io_scheduler_push_job().
     147   *
     148   * Deprecated: 2.36: You should never call this function, since you don't
     149   * know how other libraries in your program might be making use of
     150   * gioscheduler.
     151   **/
     152  void
     153  g_io_scheduler_cancel_all_jobs (void)
     154  {
     155    GList *cancellable_list, *l;
     156    
     157    G_LOCK (active_jobs);
     158    cancellable_list = NULL;
     159    for (l = active_jobs; l != NULL; l = l->next)
     160      {
     161        GIOSchedulerJob *job = l->data;
     162        if (job->cancellable)
     163  	cancellable_list = g_list_prepend (cancellable_list,
     164  					   g_object_ref (job->cancellable));
     165      }
     166    G_UNLOCK (active_jobs);
     167  
     168    for (l = cancellable_list; l != NULL; l = l->next)
     169      {
     170        GCancellable *c = l->data;
     171        g_cancellable_cancel (c);
     172        g_object_unref (c);
     173      }
     174    g_list_free (cancellable_list);
     175  }
     176  
     177  typedef struct {
     178    GSourceFunc func;
     179    gboolean ret_val;
     180    gpointer data;
     181    GDestroyNotify notify;
     182  
     183    GMutex ack_lock;
     184    GCond ack_condition;
     185    gboolean ack;
     186  } MainLoopProxy;
     187  
     188  static gboolean
     189  mainloop_proxy_func (gpointer data)
     190  {
     191    MainLoopProxy *proxy = data;
     192  
     193    proxy->ret_val = proxy->func (proxy->data);
     194  
     195    if (proxy->notify)
     196      proxy->notify (proxy->data);
     197  
     198    g_mutex_lock (&proxy->ack_lock);
     199    proxy->ack = TRUE;
     200    g_cond_signal (&proxy->ack_condition);
     201    g_mutex_unlock (&proxy->ack_lock);
     202  
     203    return FALSE;
     204  }
     205  
     206  static void
     207  mainloop_proxy_free (MainLoopProxy *proxy)
     208  {
     209    g_mutex_clear (&proxy->ack_lock);
     210    g_cond_clear (&proxy->ack_condition);
     211    g_free (proxy);
     212  }
     213  
     214  /**
     215   * g_io_scheduler_job_send_to_mainloop:
     216   * @job: a #GIOSchedulerJob
     217   * @func: a #GSourceFunc callback that will be called in the original thread
     218   * @user_data: data to pass to @func
     219   * @notify: (nullable): a #GDestroyNotify for @user_data, or %NULL
     220   * 
     221   * Used from an I/O job to send a callback to be run in the thread
     222   * that the job was started from, waiting for the result (and thus
     223   * blocking the I/O job).
     224   *
     225   * Returns: The return value of @func
     226   *
     227   * Deprecated: 2.36: Use g_main_context_invoke().
     228   **/
     229  gboolean
     230  g_io_scheduler_job_send_to_mainloop (GIOSchedulerJob *job,
     231  				     GSourceFunc      func,
     232  				     gpointer         user_data,
     233  				     GDestroyNotify   notify)
     234  {
     235    GSource *source;
     236    MainLoopProxy *proxy;
     237    gboolean ret_val;
     238  
     239    g_return_val_if_fail (job != NULL, FALSE);
     240    g_return_val_if_fail (func != NULL, FALSE);
     241  
     242    proxy = g_new0 (MainLoopProxy, 1);
     243    proxy->func = func;
     244    proxy->data = user_data;
     245    proxy->notify = notify;
     246    g_mutex_init (&proxy->ack_lock);
     247    g_cond_init (&proxy->ack_condition);
     248    g_mutex_lock (&proxy->ack_lock);
     249  
     250    source = g_idle_source_new ();
     251    g_source_set_priority (source, G_PRIORITY_DEFAULT);
     252    g_source_set_callback (source, mainloop_proxy_func, proxy,
     253  			 NULL);
     254    g_source_set_static_name (source, "[gio] mainloop_proxy_func");
     255  
     256    g_source_attach (source, job->context);
     257    g_source_unref (source);
     258  
     259    while (!proxy->ack)
     260      g_cond_wait (&proxy->ack_condition, &proxy->ack_lock);
     261    g_mutex_unlock (&proxy->ack_lock);
     262  
     263    ret_val = proxy->ret_val;
     264    mainloop_proxy_free (proxy);
     265    
     266    return ret_val;
     267  }
     268  
     269  /**
     270   * g_io_scheduler_job_send_to_mainloop_async:
     271   * @job: a #GIOSchedulerJob
     272   * @func: a #GSourceFunc callback that will be called in the original thread
     273   * @user_data: data to pass to @func
     274   * @notify: (nullable): a #GDestroyNotify for @user_data, or %NULL
     275   * 
     276   * Used from an I/O job to send a callback to be run asynchronously in
     277   * the thread that the job was started from. The callback will be run
     278   * when the main loop is available, but at that time the I/O job might
     279   * have finished. The return value from the callback is ignored.
     280   *
     281   * Note that if you are passing the @user_data from g_io_scheduler_push_job()
     282   * on to this function you have to ensure that it is not freed before
     283   * @func is called, either by passing %NULL as @notify to 
     284   * g_io_scheduler_push_job() or by using refcounting for @user_data.
     285   *
     286   * Deprecated: 2.36: Use g_main_context_invoke().
     287   **/
     288  void
     289  g_io_scheduler_job_send_to_mainloop_async (GIOSchedulerJob *job,
     290  					   GSourceFunc      func,
     291  					   gpointer         user_data,
     292  					   GDestroyNotify   notify)
     293  {
     294    GSource *source;
     295    MainLoopProxy *proxy;
     296  
     297    g_return_if_fail (job != NULL);
     298    g_return_if_fail (func != NULL);
     299  
     300    proxy = g_new0 (MainLoopProxy, 1);
     301    proxy->func = func;
     302    proxy->data = user_data;
     303    proxy->notify = notify;
     304    g_mutex_init (&proxy->ack_lock);
     305    g_cond_init (&proxy->ack_condition);
     306  
     307    source = g_idle_source_new ();
     308    g_source_set_priority (source, G_PRIORITY_DEFAULT);
     309    g_source_set_callback (source, mainloop_proxy_func, proxy,
     310  			 (GDestroyNotify)mainloop_proxy_free);
     311    g_source_set_static_name (source, "[gio] mainloop_proxy_func");
     312  
     313    g_source_attach (source, job->context);
     314    g_source_unref (source);
     315  }