gtask: free error on finalize if it's set
[platform/upstream/glib.git] / gio / gtask.c
index 48ddf6a..6c7222e 100644 (file)
@@ -584,6 +584,7 @@ struct _GTask {
   gboolean thread_cancelled;
   gboolean synchronous;
   gboolean thread_complete;
+  gboolean blocking_other_task;
 
   GError *error;
   union {
@@ -613,6 +614,8 @@ G_DEFINE_TYPE_WITH_CODE (GTask, g_task, G_TYPE_OBJECT,
                          g_task_thread_pool_init ();)
 
 static GThreadPool *task_pool;
+static GMutex task_pool_mutex;
+static GPrivate task_private = G_PRIVATE_INIT (NULL);
 
 static void
 g_task_init (GTask *task)
@@ -637,6 +640,9 @@ g_task_finalize (GObject *object)
   if (task->result_destroy && task->result.pointer)
     task->result_destroy (task->result.pointer);
 
+  if (task->error)
+      g_error_free (task->error);
+
   if (G_TASK_IS_THREADED (task))
     {
       g_mutex_clear (&task->lock);
@@ -648,7 +654,8 @@ g_task_finalize (GObject *object)
 
 /**
  * g_task_new:
- * @source_object: (allow-none): the #GObject that owns this task, or %NULL.
+ * @source_object: (allow-none) (type GObject): the #GObject that owns
+ *   this task, or %NULL.
  * @cancellable: (allow-none): optional #GCancellable object, %NULL to ignore.
  * @callback: (scope async): a #GAsyncReadyCallback.
  * @callback_data: (closure): user data passed to @callback.
@@ -700,7 +707,8 @@ g_task_new (gpointer              source_object,
 
 /**
  * g_task_report_error:
- * @source_object: (allow-none): the #GObject that owns this task, or %NULL.
+ * @source_object: (allow-none) (type GObject): the #GObject that owns
+ *   this task, or %NULL.
  * @callback: (scope async): a #GAsyncReadyCallback.
  * @callback_data: (closure): user data passed to @callback.
  * @source_tag: an opaque pointer indicating the source of this task
@@ -734,7 +742,8 @@ g_task_report_error (gpointer             source_object,
 
 /**
  * g_task_report_new_error:
- * @source_object: (allow-none): the #GObject that owns this task, or %NULL.
+ * @source_object: (allow-none) (type GObject): the #GObject that owns
+ *   this task, or %NULL.
  * @callback: (scope async): a #GAsyncReadyCallback.
  * @callback_data: (closure): user data passed to @callback.
  * @source_tag: an opaque pointer indicating the source of this task
@@ -954,7 +963,7 @@ g_task_set_source_tag (GTask    *task,
  * Gets the source object from @task. Like
  * g_async_result_get_source_object(), but does not ref the object.
  *
- * Returns: (transfer none): @task's source object, or %NULL
+ * Returns: (transfer none) (type GObject): @task's source object, or %NULL
  *
  * Since: 2.36
  */
@@ -1165,7 +1174,7 @@ g_task_return (GTask           *task,
 /**
  * GTaskThreadFunc:
  * @task: the #GTask
- * @source_object: @task's source object
+ * @source_object: (type GObject): @task's source object
  * @task_data: @task's task data
  * @cancellable: @task's #GCancellable, or %NULL
  *
@@ -1205,6 +1214,15 @@ g_task_thread_complete (GTask *task)
     }
 
   task->thread_complete = TRUE;
+
+  if (task->blocking_other_task)
+    {
+      g_mutex_lock (&task_pool_mutex);
+      g_thread_pool_set_max_threads (task_pool,
+                                     g_thread_pool_get_max_threads (task_pool) - 1,
+                                     NULL);
+      g_mutex_unlock (&task_pool_mutex);
+    }
   g_mutex_unlock (&task->lock);
 
   if (task->cancellable)
@@ -1222,9 +1240,13 @@ g_task_thread_pool_thread (gpointer thread_data,
 {
   GTask *task = thread_data;
 
+  g_private_set (&task_private, task);
+
   task->task_func (task, task->source_object, task->task_data,
                    task->cancellable);
   g_task_thread_complete (task);
+
+  g_private_set (&task_private, NULL);
   g_object_unref (task);
 }
 
@@ -1291,6 +1313,18 @@ g_task_start_task_thread (GTask           *task,
   g_thread_pool_push (task_pool, g_object_ref (task), &task->error);
   if (task->error)
     task->thread_complete = TRUE;
+  else if (g_private_get (&task_private))
+    {
+      /* This thread is being spawned from another GTask thread, so
+       * bump up max-threads so we don't starve.
+       */
+      g_mutex_lock (&task_pool_mutex);
+      if (g_thread_pool_set_max_threads (task_pool,
+                                         g_thread_pool_get_max_threads (task_pool) + 1,
+                                         NULL))
+        task->blocking_other_task = TRUE;
+      g_mutex_unlock (&task_pool_mutex);
+    }
 }
 
 /**
@@ -1713,8 +1747,8 @@ g_task_had_error (GTask *task)
 /**
  * g_task_is_valid:
  * @result: (type Gio.AsyncResult): A #GAsyncResult
- * @source_object: (allow-none): the source object expected to be
- *   associated with the task
+ * @source_object: (allow-none) (type GObject): the source object
+ *   expected to be associated with the task
  *
  * Checks that @result is a #GTask, and that @source_object is its
  * source object (or that @source_object is %NULL and @result has no
@@ -1744,12 +1778,19 @@ g_task_compare_priority (gconstpointer a,
   const GTask *tb = b;
   gboolean a_cancelled, b_cancelled;
 
+  /* Tasks that are causing other tasks to block have higher
+   * priority.
+   */
+  if (ta->blocking_other_task && !tb->blocking_other_task)
+    return -1;
+  else if (tb->blocking_other_task && !ta->blocking_other_task)
+    return 1;
+
+  /* Let already-cancelled tasks finish right away */
   a_cancelled = (ta->check_cancellable &&
                  g_cancellable_is_cancelled (ta->cancellable));
   b_cancelled = (tb->check_cancellable &&
                  g_cancellable_is_cancelled (tb->cancellable));
-
-  /* Let already-cancelled tasks finish right away */
   if (a_cancelled && !b_cancelled)
     return -1;
   else if (b_cancelled && !a_cancelled)