Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix crashes on exit caused by wlroots listener checks #8578

Open
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

Ferdi265
Copy link
Contributor

@Ferdi265 Ferdi265 commented Feb 17, 2025

This PR fixes sway crashing on exit due to event listeners not being removed from wlroots objects on exit.

Listeners fixed:

  • server: all objects created during server_init()
  • input/input-manager: wlr_backend.new_input, wlr_virtual_keyboard, wlr_virtual_pointer, wlr_keyboard_shortcuts_inhibit, wlr_transient_seat_manager
  • desktop/idle-inhibit: wlr_idle_inhibit_manager
  • input/text-input: wlr_text_input_manager and wlr_input_method_manager
  • tree/container: wlr_scene_buffer.output_enter and wlr_scene_buffer.output_leave

This doesn't fix all wlroots objects with listener checks, just those that lead to immediate crashes on exit. I'll wait for a review of my general method of fixing this before continuing.

@Ferdi265
Copy link
Contributor Author

Ferdi265 commented Feb 18, 2025

On second thought, it might be more useful to remove the bulk of the listeners in server_fini() before calling destroy, which avoids adding dozens of destroy listeners.

Destroy listeners are still needed for a bunch of other components that aren't directly created in server.c, but that reduces the clutter by a lot.

@kennylevinsen
Copy link
Member

On second thought, it might be more useful to remove the bulk of the listeners in server_fini() before calling destroy, which avoids adding dozens of destroy listeners.

If we could have a 1:1 mapping between what is set up in server_init and what is torn down in server_fini, that might be fine - but it is probably still easier to understand if all structs with listeners must have an appropriate destroy handler to remove them.

One thing is what is easiest to implement, another is what is easiest to maintain and catch errors within in the future.

@Ferdi265 Ferdi265 force-pushed the fix-wlr-listener-checks branch from 8ea9677 to 307ce0c Compare February 18, 2025 21:30
@Ferdi265
Copy link
Contributor Author

Ferdi265 commented Feb 18, 2025

On second thought, it might be more useful to remove the bulk of the listeners in server_fini() before calling destroy, which avoids adding dozens of destroy listeners.

If we could have a 1:1 mapping between what is set up in server_init and what is torn down in server_fini, that might be fine - but it is probably still easier to understand if all structs with listeners must have an appropriate destroy handler to remove them.

One thing is what is easiest to implement, another is what is easiest to maintain and catch errors within in the future.

IMO components that have _init() and _finish() functions that create protocol objects that can't go away on their own should probably avoid extra destroy handlers and just remove the listeners in finish. Components where objects are created in reference to an outside lifetime (e.g. in response to a seat being created) should probably create destroy listeners to remove those. input-manager has a different problem where it can't remove its own listeners in destroy handlers due to it not having access to the main server struct, needing a new _finish() method that is called from server_fini().

This is sometimes hard to distinguish though; text_input.c for example uses global objects initialized in server_init(), but is created in response to seat objects which are destroyed after the wlroots ones. This lifetime mismatch makes this hard to deal with, since it needs to be cleaned up both in response to a seat going away and in response to the wlroots objects being destroyed, with both happening at exit (which would cause a crash due to double wl_list_remove()).

I'm currently handling these by making the _finish() function robust against being called twice, but this doesn't feel optimal.

I think I'll try to get this PR far enough to exit sway without crashes with this method, keeping as much as possible in separate commits for reviewability, and we'll sort out what the best path forward is during review.

@Ferdi265 Ferdi265 force-pushed the fix-wlr-listener-checks branch 2 times, most recently from adfe709 to 8ee1fc7 Compare February 18, 2025 22:25
@Ferdi265
Copy link
Contributor Author

The current state makes sway exit correctly from a desktop containing a few konsole windows and a firefox window.

I'm not completely sure about the correct handling of all of these cases, and I'm sure there are a lot more objects with listeners that need to be removed before destroy, but those don't trigger on a regular exit in my setup. I've already started writing a script to search for all objects in wlroots that have listener checks and their corresponding usages in sway, but that might take a while.

In the meantime, I think it's better to mark this as reviewable now and see where we go afterwards.

@Ferdi265 Ferdi265 marked this pull request as ready for review February 18, 2025 22:28
@Ferdi265 Ferdi265 changed the title WIP: Fix crashes on exit caused by wlroots listener checks Fix crashes on exit caused by wlroots listener checks Feb 18, 2025
@Ferdi265 Ferdi265 force-pushed the fix-wlr-listener-checks branch from 8ee1fc7 to 6df3682 Compare February 18, 2025 22:31
@Ferdi265
Copy link
Contributor Author

Ferdi265 commented Feb 18, 2025

If we could have a 1:1 mapping between what is set up in server_init and what is torn down in server_fini, that might be fine

Addendum to this: this works fine for server_init, (and saves a ton of destroy handlers) since all those objects are created/destroyed only at init and fini, but this doesn't work for some of the more dynamic objects or listeners created elsewhere in sway.

The current implementation has this 1:1 mapping for objects created in server_init() in the first commit.

@Ferdi265
Copy link
Contributor Author

Diff to state before last force-push:

diff --git a/sway/server.c b/sway/server.c
index 683ce169..79c8f542 100644
--- a/sway/server.c
+++ b/sway/server.c
@@ -458,6 +458,7 @@ bool server_init(struct sway_server *server) {
 
 void server_fini(struct sway_server *server) {
        // remove listeners
+       wl_list_remove(&server->renderer_lost.link);
        wl_list_remove(&server->new_output.link);
        wl_list_remove(&server->layer_shell_surface.link);
        wl_list_remove(&server->xdg_shell_toplevel.link);
@@ -474,14 +475,12 @@ void server_fini(struct sway_server *server) {
        wl_list_remove(&server->xdg_activation_v1_request_activate.link);
        wl_list_remove(&server->xdg_activation_v1_new_token.link);
        wl_list_remove(&server->request_set_cursor_shape.link);
-#if WLR_HAS_XWAYLAND
-       wl_list_remove(&server->xwayland_surface.link);
-       wl_list_remove(&server->xwayland_ready.link);
-#endif
        input_manager_finish(server);
 
        // TODO: free sway-specific resources
 #if WLR_HAS_XWAYLAND
+       wl_list_remove(&server->xwayland_surface.link);
+       wl_list_remove(&server->xwayland_ready.link);
        wlr_xwayland_destroy(server->xwayland.wlr_xwayland);
 #endif
        wl_display_destroy_clients(server->wl_display);

Ferdi265 added 5 commits March 1, 2025 23:49
This fixes a crash in wlroots listener checks. See swaywm#8509.
This fixes a crash in wlroots listener checks. See swaywm#8509.
This fixes a crash in wlroots listener checks. See swaywm#8509.
sway_input_method_relay can be destroyed from two sources, either the
seat is destroyed or the manager protocol objects are destroyed due
compositor exit. Therefore, finish must check whether it has already
been called.

This fixes a crash in wlroots listener checks. See swaywm#8509.
Change begin_destroy to remove event listeners before the final destroy,
since otherwise event listeners would be removed twice, which crashes.

This fixes a crash in wlroots listener checks. See swaywm#8509.
@Ferdi265 Ferdi265 force-pushed the fix-wlr-listener-checks branch from ca19d20 to 8932123 Compare March 1, 2025 22:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

3 participants