hook gvariant vectors up to kdbus
[platform/upstream/glib.git] / gio / gioscheduler.c
index 61e1afc..4c826c6 100644 (file)
@@ -13,9 +13,7 @@
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General
- * Public License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
  *
  * Author: Alexander Larsson <alexl@redhat.com>
  */
 
 #include "gioscheduler.h"
 #include "gcancellable.h"
-
+#include "gtask.h"
 
 /**
  * SECTION:gioscheduler
  * @short_description: I/O Scheduler
  * @include: gio/gio.h
  * 
+ * As of GLib 2.36, #GIOScheduler is deprecated in favor of
+ * #GThreadPool and #GTask.
+ *
  * Schedules asynchronous I/O operations. #GIOScheduler integrates 
- * into the main event loop (#GMainLoop) and may use threads if they 
- * are available.
- * 
- * <para id="io-priority"><indexterm><primary>I/O priority</primary></indexterm>
- * Each I/O operation has a priority, and the scheduler uses the priorities
- * to determine the order in which operations are executed. They are 
- * <emphasis>not</emphasis> used to determine system-wide I/O scheduling.
- * Priorities are integers, with lower numbers indicating higher priority. 
- * It is recommended to choose priorities between %G_PRIORITY_LOW and 
- * %G_PRIORITY_HIGH, with %G_PRIORITY_DEFAULT as a default.
- * </para>
- **/
+ * into the main event loop (#GMainLoop) and uses threads.
+ */
 
 struct _GIOSchedulerJob {
-  GSList *active_link;
+  GList *active_link;
+  GTask *task;
+
   GIOSchedulerJobFunc job_func;
-  GSourceFunc cancel_func; /* Runs under job map lock */
   gpointer data;
   GDestroyNotify destroy_notify;
 
-  gint io_priority;
   GCancellable *cancellable;
+  gulong cancellable_id;
   GMainContext *context;
-
-  guint idle_tag;
 };
 
 G_LOCK_DEFINE_STATIC(active_jobs);
-static GSList *active_jobs = NULL;
-
-static GThreadPool *job_thread_pool = NULL;
-
-static void io_job_thread (gpointer data,
-                          gpointer user_data);
+static GList *active_jobs = NULL;
 
 static void
 g_io_job_free (GIOSchedulerJob *job)
 {
-  if (job->cancellable)
-    g_object_unref (job->cancellable);
-  if (job->context)
-    g_main_context_unref (job->context);
-  g_free (job);
-}
-
-static gint
-g_io_job_compare (gconstpointer a,
-                 gconstpointer b,
-                 gpointer      user_data)
-{
-  const GIOSchedulerJob *aa = a;
-  const GIOSchedulerJob *bb = b;
-
-  /* Cancelled jobs are set prio == -1, so that
-     they are executed as quickly as possible */
-  
-  /* Lower value => higher priority */
-  if (aa->io_priority < bb->io_priority)
-    return -1;
-  if (aa->io_priority == bb->io_priority)
-    return 0;
-  return 1;
-}
-
-static gpointer
-init_scheduler (gpointer arg)
-{
-  if (job_thread_pool == NULL)
-    {
-      /* TODO: thread_pool_new can fail */
-      job_thread_pool = g_thread_pool_new (io_job_thread,
-                                          NULL,
-                                          10,
-                                          FALSE,
-                                          NULL);
-      if (job_thread_pool != NULL)
-       {
-         g_thread_pool_set_sort_function (job_thread_pool,
-                                          g_io_job_compare,
-                                          NULL);
-         /* It's kinda weird that this is a global setting
-          * instead of per threadpool. However, we really
-          * want to cache some threads, but not keep around
-          * those threads forever. */
-         g_thread_pool_set_max_idle_time (15 * 1000);
-         g_thread_pool_set_max_unused_threads (2);
-       }
-    }
-  return NULL;
-}
+  if (job->destroy_notify)
+    job->destroy_notify (job->data);
 
-static void
-remove_active_job (GIOSchedulerJob *job)
-{
-  GIOSchedulerJob *other_job;
-  GSList *l;
-  gboolean resort_jobs;
-  
   G_LOCK (active_jobs);
-  active_jobs = g_slist_delete_link (active_jobs, job->active_link);
-  
-  resort_jobs = FALSE;
-  for (l = active_jobs; l != NULL; l = l->next)
-    {
-      other_job = l->data;
-      if (other_job->io_priority >= 0 &&
-         g_cancellable_is_cancelled (other_job->cancellable))
-       {
-         other_job->io_priority = -1;
-         resort_jobs = TRUE;
-       }
-    }
+  active_jobs = g_list_delete_link (active_jobs, job->active_link);
   G_UNLOCK (active_jobs);
-  
-  if (resort_jobs &&
-      job_thread_pool != NULL)
-    g_thread_pool_set_sort_function (job_thread_pool,
-                                    g_io_job_compare,
-                                    NULL);
 
+  if (job->cancellable)
+    g_object_unref (job->cancellable);
+  g_main_context_unref (job->context);
+  g_slice_free (GIOSchedulerJob, job);
 }
 
 static void
-job_destroy (gpointer data)
-{
-  GIOSchedulerJob *job = data;
-
-  if (job->destroy_notify)
-    job->destroy_notify (job->data);
-
-  remove_active_job (job);
-  g_io_job_free (job);
-}
-
-static void
-io_job_thread (gpointer data,
-              gpointer user_data)
+io_job_thread (GTask         *task,
+               gpointer       source_object,
+               gpointer       task_data,
+               GCancellable  *cancellable)
 {
-  GIOSchedulerJob *job = data;
+  GIOSchedulerJob *job = task_data;
   gboolean result;
 
   if (job->cancellable)
@@ -184,37 +88,18 @@ io_job_thread (gpointer data,
 
   if (job->cancellable)
     g_cancellable_pop_current (job->cancellable);
-
-  job_destroy (job);
-}
-
-static gboolean
-run_job_at_idle (gpointer data)
-{
-  GIOSchedulerJob *job = data;
-  gboolean result;
-
-  if (job->cancellable)
-    g_cancellable_push_current (job->cancellable);
-  
-  result = job->job_func (job, job->cancellable, job->data);
-  
-  if (job->cancellable)
-    g_cancellable_pop_current (job->cancellable);
-
-  return result;
 }
 
 /**
  * g_io_scheduler_push_job:
  * @job_func: a #GIOSchedulerJobFunc.
  * @user_data: data to pass to @job_func
- * @notify: a #GDestroyNotify for @user_data, or %NULL
- * @io_priority: the <link linkend="gioscheduler">I/O priority</link> 
+ * @notify: (allow-none): a #GDestroyNotify for @user_data, or %NULL
+ * @io_priority: the [I/O priority][io-priority]
  * of the request.
  * @cancellable: optional #GCancellable object, %NULL to ignore.
  *
- * Schedules the I/O job to run
+ * Schedules the I/O job to run in another thread.
  *
  * @notify will be called on @user_data after @job_func has returned,
  * regardless whether the job was cancelled or has run to completion.
@@ -222,6 +107,8 @@ run_job_at_idle (gpointer data)
  * If @cancellable is not %NULL, it can be used to cancel the I/O job
  * by calling g_cancellable_cancel() or by calling 
  * g_io_scheduler_cancel_all_jobs().
+ *
+ * Deprecated: use #GThreadPool or g_task_run_in_thread()
  **/
 void
 g_io_scheduler_push_job (GIOSchedulerJobFunc  job_func,
@@ -230,43 +117,31 @@ g_io_scheduler_push_job (GIOSchedulerJobFunc  job_func,
                         gint                 io_priority,
                         GCancellable        *cancellable)
 {
-  static GOnce once_init = G_ONCE_INIT;
   GIOSchedulerJob *job;
+  GTask *task;
 
   g_return_if_fail (job_func != NULL);
 
-  job = g_new0 (GIOSchedulerJob, 1);
+  job = g_slice_new0 (GIOSchedulerJob);
   job->job_func = job_func;
   job->data = user_data;
   job->destroy_notify = notify;
-  job->io_priority = io_priority;
-    
+
   if (cancellable)
     job->cancellable = g_object_ref (cancellable);
 
-  job->context = g_main_context_get_thread_default ();
-  if (job->context)
-    g_main_context_ref (job->context);
+  job->context = g_main_context_ref_thread_default ();
 
   G_LOCK (active_jobs);
-  active_jobs = g_slist_prepend (active_jobs, job);
+  active_jobs = g_list_prepend (active_jobs, job);
   job->active_link = active_jobs;
   G_UNLOCK (active_jobs);
 
-  if (g_thread_supported())
-    {
-      g_once (&once_init, init_scheduler, NULL);
-      g_thread_pool_push (job_thread_pool, job, NULL);
-    }
-  else
-    {
-      /* Threads not available, instead do the i/o sync inside a
-       * low prio idle handler
-       */
-      job->idle_tag = g_idle_add_full (io_priority,
-                                      run_job_at_idle,
-                                      job, job_destroy);
-    }
+  task = g_task_new (NULL, cancellable, NULL, NULL);
+  g_task_set_task_data (task, job, (GDestroyNotify)g_io_job_free);
+  g_task_set_priority (task, io_priority);
+  g_task_run_in_thread (task, io_job_thread);
+  g_object_unref (task);
 }
 
 /**
@@ -276,11 +151,15 @@ g_io_scheduler_push_job (GIOSchedulerJobFunc  job_func,
  *
  * A job is cancellable if a #GCancellable was passed into
  * g_io_scheduler_push_job().
+ *
+ * Deprecated: You should never call this function, since you don't
+ * know how other libraries in your program might be making use of
+ * gioscheduler.
  **/
 void
 g_io_scheduler_cancel_all_jobs (void)
 {
-  GSList *cancellable_list, *l;
+  GList *cancellable_list, *l;
   
   G_LOCK (active_jobs);
   cancellable_list = NULL;
@@ -288,8 +167,8 @@ g_io_scheduler_cancel_all_jobs (void)
     {
       GIOSchedulerJob *job = l->data;
       if (job->cancellable)
-       cancellable_list = g_slist_prepend (cancellable_list,
-                                           g_object_ref (job->cancellable));
+       cancellable_list = g_list_prepend (cancellable_list,
+                                          g_object_ref (job->cancellable));
     }
   G_UNLOCK (active_jobs);
 
@@ -299,7 +178,7 @@ g_io_scheduler_cancel_all_jobs (void)
       g_cancellable_cancel (c);
       g_object_unref (c);
     }
-  g_slist_free (cancellable_list);
+  g_list_free (cancellable_list);
 }
 
 typedef struct {
@@ -308,8 +187,9 @@ typedef struct {
   gpointer data;
   GDestroyNotify notify;
 
-  GMutex *ack_lock;
-  GCond *ack_condition;
+  GMutex ack_lock;
+  GCond ack_condition;
+  gboolean ack;
 } MainLoopProxy;
 
 static gboolean
@@ -321,26 +201,20 @@ mainloop_proxy_func (gpointer data)
 
   if (proxy->notify)
     proxy->notify (proxy->data);
-  
-  if (proxy->ack_lock)
-    {
-      g_mutex_lock (proxy->ack_lock);
-      g_cond_signal (proxy->ack_condition);
-      g_mutex_unlock (proxy->ack_lock);
-    }
-  
+
+  g_mutex_lock (&proxy->ack_lock);
+  proxy->ack = TRUE;
+  g_cond_signal (&proxy->ack_condition);
+  g_mutex_unlock (&proxy->ack_lock);
+
   return FALSE;
 }
 
 static void
 mainloop_proxy_free (MainLoopProxy *proxy)
 {
-  if (proxy->ack_lock)
-    {
-      g_mutex_free (proxy->ack_lock);
-      g_cond_free (proxy->ack_condition);
-    }
-  
+  g_mutex_clear (&proxy->ack_lock);
+  g_cond_clear (&proxy->ack_condition);
   g_free (proxy);
 }
 
@@ -349,13 +223,15 @@ mainloop_proxy_free (MainLoopProxy *proxy)
  * @job: a #GIOSchedulerJob
  * @func: a #GSourceFunc callback that will be called in the original thread
  * @user_data: data to pass to @func
- * @notify: a #GDestroyNotify for @user_data, or %NULL
+ * @notify: (allow-none): a #GDestroyNotify for @user_data, or %NULL
  * 
  * Used from an I/O job to send a callback to be run in the thread
  * that the job was started from, waiting for the result (and thus
  * blocking the I/O job).
  *
  * Returns: The return value of @func
+ *
+ * Deprecated: Use g_main_context_invoke().
  **/
 gboolean
 g_io_scheduler_job_send_to_mainloop (GIOSchedulerJob *job,
@@ -370,35 +246,26 @@ g_io_scheduler_job_send_to_mainloop (GIOSchedulerJob *job,
   g_return_val_if_fail (job != NULL, FALSE);
   g_return_val_if_fail (func != NULL, FALSE);
 
-  if (job->idle_tag)
-    {
-      /* We just immediately re-enter in the case of idles (non-threads)
-       * Anything else would just deadlock. If you can't handle this, enable threads.
-       */
-      ret_val = func (user_data);
-      if (notify)
-       notify (user_data);
-      return ret_val;
-    }
-  
   proxy = g_new0 (MainLoopProxy, 1);
   proxy->func = func;
   proxy->data = user_data;
   proxy->notify = notify;
-  proxy->ack_lock = g_mutex_new ();
-  proxy->ack_condition = g_cond_new ();
-  g_mutex_lock (proxy->ack_lock);
-  
+  g_mutex_init (&proxy->ack_lock);
+  g_cond_init (&proxy->ack_condition);
+  g_mutex_lock (&proxy->ack_lock);
+
   source = g_idle_source_new ();
   g_source_set_priority (source, G_PRIORITY_DEFAULT);
   g_source_set_callback (source, mainloop_proxy_func, proxy,
                         NULL);
+  g_source_set_name (source, "[gio] mainloop_proxy_func");
 
   g_source_attach (source, job->context);
   g_source_unref (source);
 
-  g_cond_wait (proxy->ack_condition, proxy->ack_lock);
-  g_mutex_unlock (proxy->ack_lock);
+  while (!proxy->ack)
+    g_cond_wait (&proxy->ack_condition, &proxy->ack_lock);
+  g_mutex_unlock (&proxy->ack_lock);
 
   ret_val = proxy->ret_val;
   mainloop_proxy_free (proxy);
@@ -411,7 +278,7 @@ g_io_scheduler_job_send_to_mainloop (GIOSchedulerJob *job,
  * @job: a #GIOSchedulerJob
  * @func: a #GSourceFunc callback that will be called in the original thread
  * @user_data: data to pass to @func
- * @notify: a #GDestroyNotify for @user_data, or %NULL
+ * @notify: (allow-none): a #GDestroyNotify for @user_data, or %NULL
  * 
  * Used from an I/O job to send a callback to be run asynchronously in
  * the thread that the job was started from. The callback will be run
@@ -422,6 +289,8 @@ g_io_scheduler_job_send_to_mainloop (GIOSchedulerJob *job,
  * on to this function you have to ensure that it is not freed before
  * @func is called, either by passing %NULL as @notify to 
  * g_io_scheduler_push_job() or by using refcounting for @user_data.
+ *
+ * Deprecated: Use g_main_context_invoke().
  **/
 void
 g_io_scheduler_job_send_to_mainloop_async (GIOSchedulerJob *job,
@@ -435,26 +304,18 @@ g_io_scheduler_job_send_to_mainloop_async (GIOSchedulerJob *job,
   g_return_if_fail (job != NULL);
   g_return_if_fail (func != NULL);
 
-  if (job->idle_tag)
-    {
-      /* We just immediately re-enter in the case of idles (non-threads)
-       * Anything else would just deadlock. If you can't handle this, enable threads.
-       */
-      func (user_data);
-      if (notify)
-       notify (user_data);
-      return;
-    }
-  
   proxy = g_new0 (MainLoopProxy, 1);
   proxy->func = func;
   proxy->data = user_data;
   proxy->notify = notify;
-  
+  g_mutex_init (&proxy->ack_lock);
+  g_cond_init (&proxy->ack_condition);
+
   source = g_idle_source_new ();
   g_source_set_priority (source, G_PRIORITY_DEFAULT);
   g_source_set_callback (source, mainloop_proxy_func, proxy,
                         (GDestroyNotify)mainloop_proxy_free);
+  g_source_set_name (source, "[gio] mainloop_proxy_func");
 
   g_source_attach (source, job->context);
   g_source_unref (source);