Add GCancellable handling to LUCStarter
authorJonathan Maw <jonathan.maw@codethink.co.uk>
Wed, 20 Jun 2012 15:40:55 +0000 (16:40 +0100)
committerJonathan Maw <jonathan.maw@codethink.co.uk>
Thu, 21 Jun 2012 11:03:24 +0000 (12:03 +0100)
When systemd is told to start an app, it is passed a GCancellable object
so that the LUCStarter can cancel the start call.

Internally, the LUCStarter has a GHashTable associating each app with
its GCancellable.

Because start_app_finish requires the LUCStarter to start the next group
of apps, the LUCStarter must persist until all start jobs have finished.
For this reason, the start_app method increases the reference count to
the LUCStarter, and the start_app_finish method decreases the reference
count.

boot-manager/luc-starter.c

index f930f00..891e988 100644 (file)
@@ -70,6 +70,8 @@ struct _LUCStarter
 
   GList              *start_order;
   GHashTable         *start_groups;
+  
+  GHashTable         *cancellables;
 };
 
 
@@ -115,6 +117,7 @@ luc_starter_class_init (LUCStarterClass *klass)
 static void
 luc_starter_init (LUCStarter *starter)
 {
+  
 }
 
 
@@ -135,12 +138,16 @@ luc_starter_finalize (GObject *object)
 {
   LUCStarter *starter = LUC_STARTER (object);
 
-  /* release start order and groups */
+  /* release start order, and groups */
   if (starter->start_order != NULL)
     g_list_free (starter->start_order);
   if (starter->start_groups != NULL)
     g_hash_table_unref (starter->start_groups);
 
+  /* release the cancellables */
+  if (starter->cancellables != NULL)
+    g_hash_table_unref (starter->cancellables);
+
   /* free the prioritised types array */
   g_strfreev (starter->prioritised_types);
 
@@ -264,14 +271,24 @@ static void
 luc_starter_start_app (const gchar *app,
                        LUCStarter  *starter)
 {
+  GCancellable *cancellable;
+
   g_return_if_fail (app != NULL && *app != '\0');
   g_return_if_fail (IS_LUC_STARTER (starter));
 
   g_debug ("start app '%s'", app);
 
-  boot_manager_service_start (starter->boot_manager, app, NULL,
+  /* create the new cancellable */
+  cancellable = g_cancellable_new ();
+
+  /* store an association between each app and its cancellable, so that it is possible to
+   * call g_cancellable_cancel() for each respective app. */
+  g_hash_table_insert (starter->cancellables, g_strdup (app), cancellable);
+
+  /* start the service, passing the cancellable */
+  boot_manager_service_start (starter->boot_manager, app, cancellable,
                               luc_starter_start_app_finish,
-                              starter);
+                              g_object_ref (starter));
 }
 
 
@@ -329,6 +346,13 @@ luc_starter_start_app_finish (BootManagerService *service,
             luc_starter_start_next_group (starter);
         }
     }
+
+  /* remove the association between an app and its cancellable, because the app has
+   * finished starting, so cannot be cancelled any more */
+  g_hash_table_remove (starter->cancellables, unit);
+  
+  /* release the LUCStarter because the operation is finished */
+  g_object_unref (starter);
 }
 
 
@@ -382,6 +406,18 @@ luc_starter_start_groups (LUCStarter *starter)
                                g_free, (GDestroyNotify) g_ptr_array_free);
     }
 
+  /* clear the mapping between apps and their cancellables */
+  if (starter->cancellables != NULL)
+    {
+      g_hash_table_remove_all (starter->cancellables);
+    }
+  else
+    {
+      starter->cancellables =
+        g_hash_table_new_full (g_str_hash, g_str_equal,
+                               g_free, (GDestroyNotify) g_object_unref);
+    }
+
   /* get the current last user context */
   context = luc_handler_get_last_user_context (starter->luc_handler);
   type = g_variant_print (context, TRUE);