Use weston_compositor_add_destroy_listener_once() in plugins
authorPekka Paalanen <pekka.paalanen@collabora.com>
Wed, 6 Nov 2019 10:59:32 +0000 (12:59 +0200)
committerDaniel Stone <daniel@fooishbar.org>
Thu, 21 Nov 2019 16:32:55 +0000 (16:32 +0000)
This introduces a new convention of checking through the compositor destroy
listener if the plugin is already initialized. If the plugin is already
initialized, then the plugin entry function succeeds as a no-op. This makes it
safe to load the same plugin multiple times in a running compositor.

Currently module loading functions return failure if a plugin is already
loaded, but that will change in the future. Therefore we need this other method
of ensuring we do not double-initialize a plugin which would lead to list
corruptions the very least.

All plugins are converted to use the new helper, except:
- those that do not have a destroy listener already, and
- hmi-controller which does the same open-coded as the common code pattern
  did not fit there.

Plugins should always have a compositor destroy listener registered since they
very least allocate a struct to hold their data. Hence omissions are
highlighted in code.

Backends do not need this because weston_compositor_load_backend() already
protects against double-init. GL-renderer does not export a standard module
init function so cannot be initialized the usual way and therefore is not
vulnerable to double-init.

Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.com>
15 files changed:
compositor/cms-colord.c
compositor/cms-static.c
compositor/screen-share.c
compositor/systemd-notify.c
desktop-shell/shell.c
fullscreen-shell/fullscreen-shell.c
include/libweston/libweston.h
ivi-shell/hmi-controller.c
ivi-shell/ivi-shell.c
libweston/compositor.c
pipewire/pipewire-plugin.c
remoting/remoting-plugin.c
tests/weston-test-desktop-shell.c
tests/weston-test.c
xwayland/launcher.c

index 57a77f44f8e52072a5db36f70a9b5d57bb067edc..d4efdb433c1a98821b484661e9fd9082a86a7102 100644 (file)
@@ -528,6 +528,14 @@ wet_module_init(struct weston_compositor *ec,
        if (cms == NULL)
                return -1;
        cms->ec = ec;
+
+       if (!weston_compositor_add_destroy_listener_once(ec,
+                                                        &cms->destroy_listener,
+                                                        colord_notifier_destroy)) {
+               free(cms);
+               return 0;
+       }
+
 #if !GLIB_CHECK_VERSION(2,36,0)
        g_type_init();
 #endif
@@ -543,10 +551,6 @@ wet_module_init(struct weston_compositor *ec,
        cms->devices = g_hash_table_new_full(g_str_hash, g_str_equal,
                                             g_free, colord_cms_output_destroy);
 
-       /* destroy */
-       cms->destroy_listener.notify = colord_notifier_destroy;
-       wl_signal_add(&ec->destroy_signal, &cms->destroy_listener);
-
        /* devices added */
        cms->output_created_listener.notify = colord_notifier_output_created;
        wl_signal_add(&ec->output_created_signal, &cms->output_created_listener);
index 2f357c41c7d9a36742fbf97a5dc84ae46fb3f29e..540d6ad3828a202559b2bc66cb5a48a252d1057d 100644 (file)
@@ -105,8 +105,13 @@ wet_module_init(struct weston_compositor *ec,
                return -1;
 
        cms->ec = ec;
-       cms->destroy_listener.notify = cms_notifier_destroy;
-       wl_signal_add(&ec->destroy_signal, &cms->destroy_listener);
+
+       if (!weston_compositor_add_destroy_listener_once(ec,
+                                                        &cms->destroy_listener,
+                                                        cms_notifier_destroy)) {
+               free(cms);
+               return 0;
+       }
 
        cms->output_created_listener.notify = cms_notifier_output_created;
        wl_signal_add(&ec->output_created_signal, &cms->output_created_listener);
index a5d6fbcf77329047d4ea6bd27133a1e17463cc03..d6fc1228a57b98b7d6921df727053ef95bc9e1f8 100644 (file)
@@ -114,6 +114,9 @@ struct ss_shm_buffer {
 
 struct screen_share {
        struct weston_compositor *compositor;
+       /* XXX: missing compositor destroy listener
+        * https://gitlab.freedesktop.org/wayland/weston/issues/298
+        */
        char *command;
 };
 
index 6284a6d9fc0e7ca236eb3d6056039dd8e791b9ed..61196d8182224427479dbe88676c0379d11eab24 100644 (file)
@@ -127,10 +127,12 @@ wet_module_init(struct weston_compositor *compositor,
        if (notifier == NULL)
                return -1;
 
-       notifier->compositor_destroy_listener.notify =
-               weston_compositor_destroy_listener;
-       wl_signal_add(&compositor->destroy_signal,
-                     &notifier->compositor_destroy_listener);
+       if (!weston_compositor_add_destroy_listener_once(compositor,
+                                                        &notifier->compositor_destroy_listener,
+                                                        weston_compositor_destroy_listener)) {
+               free(notifier);
+               return 0;
+       }
 
        if (add_systemd_sockets(compositor) < 0)
                return -1;
index 00ee1079c34a7db80f888c7700dfe046263adf60..337e168c5c63819ab2a31eadad7296cf6173201b 100644 (file)
@@ -5090,8 +5090,13 @@ wet_shell_init(struct weston_compositor *ec,
 
        shell->compositor = ec;
 
-       shell->destroy_listener.notify = shell_destroy;
-       wl_signal_add(&ec->destroy_signal, &shell->destroy_listener);
+       if (!weston_compositor_add_destroy_listener_once(ec,
+                                                        &shell->destroy_listener,
+                                                        shell_destroy)) {
+               free(shell);
+               return 0;
+       }
+
        shell->idle_listener.notify = idle_handler;
        wl_signal_add(&ec->idle_signal, &shell->idle_listener);
        shell->wake_listener.notify = wake_handler;
index 6c04cf3dc9a15f713cac5b415dd4131c83e8d329..e785246ab87988db2d06f92cea9ca22d3271fbdb 100644 (file)
@@ -42,6 +42,9 @@ struct fullscreen_shell {
        struct wl_client *client;
        struct wl_listener client_destroyed;
        struct weston_compositor *compositor;
+       /* XXX: missing compositor destroy listener
+        * https://gitlab.freedesktop.org/wayland/weston/issues/299
+        */
 
        struct weston_layer layer;
        struct wl_list output_list;
index e914d6a30d53cd1da9b73261b316a6d5e6a25a0a..6b5cc4bf1c39e44078acecf4c9abee9a58ba8182 100644 (file)
@@ -1773,6 +1773,11 @@ struct weston_compositor *
 weston_compositor_create(struct wl_display *display,
                         struct weston_log_context *log_ctx, void *user_data);
 
+bool
+weston_compositor_add_destroy_listener_once(struct weston_compositor *compositor,
+                                           struct wl_listener *listener,
+                                           wl_notify_func_t destroy_handler);
+
 enum weston_compositor_backend {
        WESTON_BACKEND_DRM,
        WESTON_BACKEND_FBDEV,
index 4f90e0acbf9d6609e380cd57f05091975fdae4bd..230e788ba6954ac4393998c235194c99d99b2380 100644 (file)
@@ -1981,6 +1981,10 @@ wet_module_init(struct weston_compositor *ec,
        struct hmi_controller *hmi_ctrl = NULL;
        struct wl_event_loop *loop = NULL;
 
+       /* ad hoc weston_compositor_add_destroy_listener_once() */
+       if (wl_signal_get(&ec->destroy_signal, hmi_controller_destroy))
+               return 0;
+
        hmi_ctrl = hmi_controller_create(ec);
        if (hmi_ctrl == NULL)
                return -1;
index 99431d0bde0fcf4c564bf4c197bd5088a0641c51..d3b5cde2bfaf419bf631dc824870eb95b8f351b3 100644 (file)
@@ -623,10 +623,14 @@ wet_shell_init(struct weston_compositor *compositor,
        if (shell == NULL)
                return -1;
 
-       init_ivi_shell(compositor, shell);
+       if (!weston_compositor_add_destroy_listener_once(compositor,
+                                                        &shell->destroy_listener,
+                                                        shell_destroy)) {
+               free(shell);
+               return 0;
+       }
 
-       shell->destroy_listener.notify = shell_destroy;
-       wl_signal_add(&compositor->destroy_signal, &shell->destroy_listener);
+       init_ivi_shell(compositor, shell);
 
        shell->wake_listener.notify = wake_handler;
        wl_signal_add(&compositor->wake_signal, &shell->wake_listener);
index 3531a21315f4a8a339782fc86e3ba2eaf8475138..af0da062998031c3557a62d1b253d8615c92bdf1 100644 (file)
@@ -7639,6 +7639,41 @@ weston_load_module(const char *name, const char *entrypoint)
        return init;
 }
 
+/** Add a compositor destroy listener only once
+ *
+ * \param compositor The compositor whose destroy to watch for.
+ * \param listener The listener struct to initialize.
+ * \param destroy_handler The callback when compositor is destroyed.
+ * \return True if listener is added, or false if there already is a listener
+ * with the given \c destroy_handler.
+ *
+ * This function does nothing and returns false if the given callback function
+ * is already present in the weston_compositor destroy callbacks list.
+ * Otherwise, this function initializes the given listener with the given
+ * callback pointer and adds it to the compositor's destroy callbacks list.
+ *
+ * This can be used to ensure that plugin initialization is done only once
+ * in case the same plugin is loaded multiple times. If this function returns
+ * false, the plugin should be already initialized successfully.
+ *
+ * All plugins should register a destroy listener for cleaning up. Note, that
+ * the plugin destruction order is not guaranteed: plugins that depend on other
+ * plugins must be able to be torn down in arbitrary order.
+ *
+ * \sa weston_compositor_tear_down, weston_compositor_destroy
+ */
+WL_EXPORT bool
+weston_compositor_add_destroy_listener_once(struct weston_compositor *compositor,
+                                           struct wl_listener *listener,
+                                           wl_notify_func_t destroy_handler)
+{
+       if (wl_signal_get(&compositor->destroy_signal, destroy_handler))
+               return false;
+
+       listener->notify = destroy_handler;
+       wl_signal_add(&compositor->destroy_signal, listener);
+       return true;
+}
 
 /** Tear down the compositor.
  *
index 1eb5de573d6d49f0e1144abadda9b2c779fd8c63..552f5745d20c37e58ee7b524a3aefdc64e7634dc 100644 (file)
@@ -797,6 +797,13 @@ weston_module_init(struct weston_compositor *compositor)
        if (!pipewire)
                return -1;
 
+       if (!weston_compositor_add_destroy_listener_once(compositor,
+                                                        &pipewire->destroy_listener,
+                                                        weston_pipewire_destroy)) {
+               free(pipewire);
+               return 0;
+       }
+
        pipewire->virtual_output_api = api;
        pipewire->compositor = compositor;
        wl_list_init(&pipewire->output_list);
@@ -821,11 +828,10 @@ weston_module_init(struct weston_compositor *compositor)
                        "Debug messages from pipewire plugin\n",
                        NULL, NULL, NULL);
 
-       pipewire->destroy_listener.notify = weston_pipewire_destroy;
-       wl_signal_add(&compositor->destroy_signal, &pipewire->destroy_listener);
        return 0;
 
 failed:
+       wl_list_remove(&pipewire->destroy_listener.link);
        free(pipewire);
        return -1;
 }
index 8b82572a18718d3c1fd1eb0e22391fdab069f4b6..0928a6b10121f05b56f52d09ab6c7a924717ab1c 100644 (file)
@@ -913,6 +913,13 @@ weston_module_init(struct weston_compositor *compositor)
        if (!remoting)
                return -1;
 
+       if (!weston_compositor_add_destroy_listener_once(compositor,
+                                                        &remoting->destroy_listener,
+                                                        weston_remoting_destroy)) {
+               free(remoting);
+               return 0;
+       }
+
        remoting->virtual_output_api = api;
        remoting->compositor = compositor;
        wl_list_init(&remoting->output_list);
@@ -932,11 +939,10 @@ weston_module_init(struct weston_compositor *compositor)
                goto failed;
        }
 
-       remoting->destroy_listener.notify = weston_remoting_destroy;
-       wl_signal_add(&compositor->destroy_signal, &remoting->destroy_listener);
        return 0;
 
 failed:
+       wl_list_remove(&remoting->destroy_listener.link);
        free(remoting);
        return -1;
 }
index ac0ece7b657c17d07b855ab7a82ecf18ab7a7956..f3d881b803c02d87518ef59225afb58b65cf4ff7 100644 (file)
@@ -181,8 +181,12 @@ wet_shell_init(struct weston_compositor *ec,
        if (!dts)
                return -1;
 
-       dts->compositor_destroy_listener.notify = shell_destroy;
-       wl_signal_add(&ec->destroy_signal, &dts->compositor_destroy_listener);
+       if (!weston_compositor_add_destroy_listener_once(ec,
+                                                        &dts->compositor_destroy_listener,
+                                                        shell_destroy)) {
+               free(dts);
+               return 0;
+       }
 
        weston_layer_init(&dts->layer, ec);
        weston_layer_init(&dts->background_layer, ec);
index 38505f0b2dbfdb2c590f8acc37ac857f0227abfe..4b9f113576cb8954557c583b6badcf8763c9f3dc 100644 (file)
@@ -46,6 +46,9 @@
 
 struct weston_test {
        struct weston_compositor *compositor;
+       /* XXX: missing compositor destroy listener
+        * https://gitlab.freedesktop.org/wayland/weston/issues/300
+        */
        struct weston_layer layer;
        struct weston_process process;
        struct weston_seat seat;
index 1d51ee8fc1c5c5752237f647066bdb26fb5f7f4c..2f2cebbefb9d273da35f83bfc967fa84949a9fa2 100644 (file)
@@ -367,19 +367,24 @@ weston_module_init(struct weston_compositor *compositor)
        wxs->wl_display = display;
        wxs->compositor = compositor;
 
+       if (!weston_compositor_add_destroy_listener_once(compositor,
+                                                        &wxs->destroy_listener,
+                                                        weston_xserver_destroy)) {
+               free(wxs);
+               return 0;
+       }
+
        if (weston_xwayland_get_api(compositor) != NULL ||
            weston_xwayland_surface_get_api(compositor) != NULL) {
                weston_log("The xwayland module APIs are already loaded.\n");
-               free(wxs);
-               return -1;
+               goto out_free;
        }
 
        ret = weston_plugin_api_register(compositor, WESTON_XWAYLAND_API_NAME,
                                         &api, sizeof(api));
        if (ret < 0) {
                weston_log("Failed to register the xwayland module API.\n");
-               free(wxs);
-               return -1;
+               goto out_free;
        }
 
        ret = weston_plugin_api_register(compositor,
@@ -387,13 +392,9 @@ weston_module_init(struct weston_compositor *compositor)
                                         &surface_api, sizeof(surface_api));
        if (ret < 0) {
                weston_log("Failed to register the xwayland surface API.\n");
-               free(wxs);
-               return -1;
+               goto out_free;
        }
 
-       wxs->destroy_listener.notify = weston_xserver_destroy;
-       wl_signal_add(&compositor->destroy_signal, &wxs->destroy_listener);
-
        wxs->wm_debug =
                weston_compositor_add_log_scope(wxs->compositor->weston_log_ctx,
                                                "xwm-wm-x11",
@@ -401,4 +402,9 @@ weston_module_init(struct weston_compositor *compositor)
                                                NULL, NULL, NULL);
 
        return 0;
+
+out_free:
+       wl_list_remove(&wxs->destroy_listener.link);
+       free(wxs);
+       return -1;
 }