[PATCH weston 2/3] shell: fix race on desktop-shell exit

Derek Foreman derekf at osg.samsung.com
Wed Aug 27 11:16:56 PDT 2014


On 27/08/14 05:41 AM, Pekka Paalanen wrote:
> From: Pekka Paalanen <pekka.paalanen at collabora.co.uk>
> 
> The desktop shell plugin registers both a wl_client destroy signal
> listener, and a sigchld handler, when launching weston-desktop-shell.
> However, nothing guarantees in which order do the wl_client destructor
> and the sigchld handler run.
> 
> Luckily, the sigchld handler cannot interrupt any code, because we
> handle the signal via signalfd, which means it is handled like any event
> in the compositor's main event loop.
> 
> Still, shell.c has a race, that when lost, can cause a crash, as
> described in bug #82957.
> 
> If the sigchld handler happens to run first, it will try to launch a new
> weston-desktop-shell without removing the destroy listener from the old
> wl_client first. This leads to list corruption, that may cause a crash
> when the old wl_client gets destroyed.
> 
> Simply removing the destroy listener in the sigchld handler is not
> enough, because respawning sets shell->child.client pointer, and if
> the wl_client destructor runs after, it will reset it to NULL.
> 
> OTOH, the wl_client destroy handler cannot reset shell->child.process,
> because that would cause the sigchld handler in weston core to not find
> the process tracker anymore, and report that an unknown process exited.
> 
> Turns out, that to make everything work, we would need to wait for both
> the wl_client destructor and the sigchld handler to have run, before
> respawn. This gets tricky.
> 
> Instead, solve the problem by removing shell->child.process. Use the new
> weston_client_start() which automatically creates and manages the struct
> weston_process. The shell does not need to know about the process exit,
> it only needs to know about the client disconnect. Weston-desktop-shell
> will never attempt to reconnect, and it would not work even if it did,
> so disconnect is equivalent to weston-desktop-shell exiting.
> 
> This should permanently solve the race for weston-desktop-shell.
> 
> Bug: https://bugs.freedesktop.org/show_bug.cgi?id=82957
> Cc: Boyan Ding <stu_dby at 126.com>
> Cc: Derek Foreman <derekf at osg.samsung.com>
> Signed-off-by: Pekka Paalanen <pekka.paalanen at collabora.co.uk>
> ---
>  desktop-shell/shell.c | 35 +++++++++++++++++++++--------------
>  desktop-shell/shell.h |  1 -
>  2 files changed, 21 insertions(+), 15 deletions(-)
> 
> diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c
> index 99f3343..f82c47a 100644
> --- a/desktop-shell/shell.c
> +++ b/desktop-shell/shell.c
> @@ -5294,14 +5294,9 @@ shell_surface_configure(struct weston_surface *es, int32_t sx, int32_t sy)
>  static void launch_desktop_shell_process(void *data);
>  
>  static void
> -desktop_shell_sigchld(struct weston_process *process, int status)
> +respawn_desktop_shell_process(struct desktop_shell *shell)
>  {
>  	uint32_t time;
> -	struct desktop_shell *shell =
> -		container_of(process, struct desktop_shell, child.process);
> -
> -	shell->child.process.pid = 0;
> -	shell->child.client = NULL; /* already destroyed by wayland */
>  
>  	/* if desktop-shell dies more than 5 times in 30 seconds, give up */
>  	time = weston_compositor_get_time();
> @@ -5312,13 +5307,12 @@ desktop_shell_sigchld(struct weston_process *process, int status)
>  
>  	shell->child.deathcount++;
>  	if (shell->child.deathcount > 5) {
> -		weston_log("%s died, giving up.\n", shell->client);
> +		weston_log("%s disconnected, giving up.\n", shell->client);

Once again, being hugely pedantic - we'll get the disconnected message
for a failed exec - so we may be printing this when nothing has actually
connected in the first place.

Really not sure what the best way is to make these errors meaningful
though - assigning exit() codes?

Other than that, this fixes the problem as I understand it and passes my
simple test (launching the headless backend with --no-config and
WESTON_BUILD_DIR set to an invalid directory)

Cosmetics of log messages aside,

Reviewed-by: Derek Foreman <derekf at osg.samsung.com>

for all three patches...  I like the third one unconditionally. :)

>  		return;
>  	}
>  
> -	weston_log("%s died, respawning...\n", shell->client);
> +	weston_log("%s disconnected, respawning...\n", shell->client);
>  	launch_desktop_shell_process(shell);
> -	shell_fade_startup(shell);
>  }
>  
>  static void
> @@ -5329,7 +5323,18 @@ desktop_shell_client_destroy(struct wl_listener *listener, void *data)
>  	shell = container_of(listener, struct desktop_shell,
>  			     child.client_destroy_listener);
>  
> +	wl_list_remove(&shell->child.client_destroy_listener.link);
>  	shell->child.client = NULL;
> +	/*
> +	 * unbind_desktop_shell() will reset shell->child.desktop_shell
> +	 * before the respawned process has a chance to create a new
> +	 * desktop_shell object, because we are being called from the
> +	 * wl_client destructor which destroys all wl_resources before
> +	 * returning.
> +	 */
> +
> +	respawn_desktop_shell_process(shell);
> +	shell_fade_startup(shell);
>  }
>  
>  static void
> @@ -5337,10 +5342,8 @@ launch_desktop_shell_process(void *data)
>  {
>  	struct desktop_shell *shell = data;
>  
> -	shell->child.client = weston_client_launch(shell->compositor,
> -						 &shell->child.process,
> -						 shell->client,
> -						 desktop_shell_sigchld);
> +	shell->child.client = weston_client_start(shell->compositor,
> +						  shell->client);
>  
>  	if (!shell->child.client)
>  		weston_log("not able to start %s\n", shell->client);
> @@ -6152,8 +6155,12 @@ shell_destroy(struct wl_listener *listener, void *data)
>  
>  	/* Force state to unlocked so we don't try to fade */
>  	shell->locked = false;
> -	if (shell->child.client)
> +
> +	if (shell->child.client) {
> +		/* disable respawn */
> +		wl_list_remove(&shell->child.client_destroy_listener.link);
>  		wl_client_destroy(shell->child.client);
> +	}
>  
>  	wl_list_remove(&shell->idle_listener.link);
>  	wl_list_remove(&shell->wake_listener.link);
> diff --git a/desktop-shell/shell.h b/desktop-shell/shell.h
> index c6ea328..67c5f50 100644
> --- a/desktop-shell/shell.h
> +++ b/desktop-shell/shell.h
> @@ -134,7 +134,6 @@ struct desktop_shell {
>  	struct weston_surface *grab_surface;
>  
>  	struct {
> -		struct weston_process process;
>  		struct wl_client *client;
>  		struct wl_resource *desktop_shell;
>  		struct wl_listener client_destroy_listener;
> 


More information about the wayland-devel mailing list