From 6bf7a8fcecfe7c4aa903236f2f8a59df16ba8180 Mon Sep 17 00:00:00 2001 From: zhaoyingzhen Date: Tue, 29 Sep 2026 16:39:32 +0800 Subject: [PATCH] fix: avoid signal-handler deadlocks during session shutdown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove signal handlers that invoke Qt and D-Bus from interrupted code. Keep abnormal-exit cleanup in the existing systemd ExecStopPost and OnFailure paths, and remove the obsolete dde-dock service stop call. Keep desktop and Dock startup independent; let the tray target own its ordering after the Dock. Document the current session lifecycle. 移除在被中断代码中调用 Qt 和 D-Bus 的信号处理函数,避免重入死锁。 异常退出清理继续由已有的 systemd ExecStopPost 和 OnFailure 路径完成, 删除旧 dde-dock 服务停止调用。保留桌面与 Dock 独立启动,由托盘 target 维护其与 Dock 的顺序关系,并更新会话生命周期说明。 Log: avoid signal-handler deadlocks during session shutdown Pms: BUG-378589 Change-Id: I640d8c5541fee3df4bdb82bd789bacb8cd25a1f3 --- docs/design.md | 41 ++++++++++++++----- src/dde-session/impl/sessionmanager.cpp | 29 ------------- src/dde-session/impl/sessionmanager.h | 2 - src/utils/utils.h | 3 +- .../dde-shell@DDE.service | 9 ++-- systemd/dde-session-manager.service.in | 2 + 6 files changed, 39 insertions(+), 47 deletions(-) diff --git a/docs/design.md b/docs/design.md index cb285cd5..8f80ba1a 100644 --- a/docs/design.md +++ b/docs/design.md @@ -1,18 +1,39 @@ -lightdm 在认证通过以后,会启动 dde-session,dde-session 会通过 systemd 的 dbus 启动 org.deepin.dde.Session1.service +# 会话与 systemd 生命周期 -在 org.deepin.dde.Session1.service 中会执行 dde-session-ctl --systemd-service,启动 dde-session-x11.target. +LightDM 认证通过后启动 `dde-session`。入口进程导入会话环境,启动 +`dde-session.target`,并监听 `org.deepin.dde.Session1` 的注销。 +`dde-session-manager.service` 通过 loader wrapper 运行 +`dde-session --systemd-service`,注册 Session1 等 D-Bus 接口。 +会话按 pre、core、initialized 阶段启动,依赖关系以 `systemd/` 中的单元为准。 -注销会话有两个入口,一个是 lightdm 停止会话,那么就需要一种途径去关闭启动的 session 相关的 services。 -另外一个是主动启动 dde-session-shutdown.service,在 service 中会执行 dde-session-ctl --shutdown,去开启 dde-session-shutdown.target,在这个 -target中关联了所有服务的冲突,这样其它服务就会被关闭,等执行完毕,所有的服务都将会退出。 +## 桌面、Dock 和托盘 -在一些核心的服务上也会关联上 dde-session-shutdown.service。 +core 阶段并行启动桌面 `dde-shell-plugin@org.deepin.ds.desktop.service` 和 +任务栏 `dde-shell@DDE.service`;两者之间没有启动或停止顺序约束。 +任务栏属于 dde-shell 的 DDE 分组,不再由旧的 `dde-dock.service` 管理。 -在 dde-session-manager.service 的 ExecStop 执行 /usr/libexec/dde-session-ctl --logout,这个命令会 -调用 org.deepin.dde.Session1.Logout() 方法,把阻塞 lightdm 的 会话入口退出,当 org.deepin.dde.Session1 服务在 DBus 上消失时,关联的 org.deepin.dde.Session1.service 服务就会停止,从而执行 /usr/libexec/dde-session-ctl --shutdown,去启动 dde-session-shutdown.target,将所有 DDE 服务冲突掉,从而完成关闭,在最后阶段,dde-session-shutdown.target 会启动 dde-session-restart-dbus.service 去将 dbus.service 服务停止,完成最终的防止 dbus 服务进程逃逸。 +Dock 通过 `Wants=dde-tray-loader.target` 启动托盘。配套 dde-tray-loader 中, +target 通过 `After=` 等待 Dock 的 `Type=dbus` 启动完成:Dock 的内部合成器 +就绪后才注册 `org.deepin.dde.Dock1`。随后各托盘分组服务并行启动。 +target 通过 `BindsTo=` 和 `PartOf=` 绑定 Dock,无需直接绑定 core 或额外的 ready 服务。 +停止时依赖顺序反转,各托盘组并行退出后 Dock 才退出。桌面可以同时退出, +不会等待 Dock;慢托盘仍会延迟 Dock 停止。这些顺序不保证合成器窗口动画或 +屏幕残影的消失顺序。托盘生命周期测试见 dde-tray-loader 的 README。 +## 注销与异常退出 -systemd unit图: +正常注销经过 SessionManager 的注销准备流程。`dde-session-ctl --logout` +则调用 Session1 的 `Logout()`。管理进程退出后,systemd 执行 +`dde-session-manager.service` 的 `ExecStopPost=dde-session-ctl --shutdown`, +启动 `dde-session-shutdown.target`,利用冲突关系停止会话目标与服务。 +也可以通过 `dde-session-shutdown.service` 请求同一关闭流程。 -![](images/dde-session-services.png) +管理进程被终止或崩溃时,`ExecStopPost` 仍执行清理,`OnFailure` 还会触发 +shutdown target。因此无需在 POSIX 信号处理函数中调用 Qt 或 D-Bus; +此类重入可能等待被中断线程持有的锁,反而阻塞进程退出。 + +入口进程发现 Session1 总线名称消失后,启动 `dde-session-exit-task.service` +并退出,将控制权交回 LightDM。exit task 通过 `dde-session-ctl --session-exit` +在一秒延迟后停止用户 `dbus.service`。登录界面的后续启动与壁纸加载属于 +LightDM/greeter 流程,应与会话服务的停止耗时分别排查。 diff --git a/src/dde-session/impl/sessionmanager.cpp b/src/dde-session/impl/sessionmanager.cpp index 2999ea68..e873e989 100644 --- a/src/dde-session/impl/sessionmanager.cpp +++ b/src/dde-session/impl/sessionmanager.cpp @@ -22,7 +22,6 @@ #include #include -#include #include using namespace Dtk::Core; @@ -94,9 +93,6 @@ SessionManager::SessionManager(QObject *parent) { initConnections(); - // 处理异常退出的情况 - handleOSSignal(); - auto appearanceConfig = DConfig::create("org.deepin.dde.appearance", "org.deepin.dde.appearance", QString(), this); m_soundTheme = appearanceConfig->value("Sound_Theme", "deepin").toString(); connect(appearanceConfig, &DConfig::valueChanged, this, [this, appearanceConfig] (const QString &key) { @@ -178,7 +174,6 @@ void SessionManager::prepareLogout(bool force) stopBAMFDaemon(); stopRedshift(); stopObexService(); - stopDock(); // 防止注销时,蓝牙音频设置没有断开连接 disconnectAudioDevices(); @@ -704,12 +699,6 @@ void SessionManager::stopRedshift() VIEW_SERVICE(REDSHIFT_SERVICE); } -void SessionManager::stopDock() -{ - STOP_SERVICE(DDE_DOCK_SERVICE); - VIEW_SERVICE(DDE_DOCK_SERVICE); -} - void SessionManager::disconnectAudioDevices() { auto msg = QDBusInterface("org.deepin.dde.Bluetooth1" @@ -864,24 +853,6 @@ void SessionManager::setDPMSMode(bool on) } } -/** - * @brief sig_crash 意外退出时处理一些事情 - * @param sig 收到的异常退出信号类型 - */ -[[noreturn]] void sig_crash(int sig) { - Q_UNUSED(sig); - SessionManager::instance()->doLogout(); - exit(-1); -}; - -void SessionManager::handleOSSignal() -{ - signal(SIGINT, sig_crash); - signal(SIGABRT, sig_crash); - signal(SIGTERM, sig_crash); - signal(SIGSEGV, sig_crash); -} - void SessionManager::setTlpMode(const QString &mode) { qInfo() << "setTlpMode mode:" << mode; diff --git a/src/dde-session/impl/sessionmanager.h b/src/dde-session/impl/sessionmanager.h index 324d6f4f..c58062bd 100644 --- a/src/dde-session/impl/sessionmanager.h +++ b/src/dde-session/impl/sessionmanager.h @@ -89,7 +89,6 @@ public Q_SLOTS: void stopPulseAudioService(); void stopBAMFDaemon(); void stopRedshift(); - void stopDock(); void disconnectAudioDevices(); void preparePlayShutdownSound(); @@ -100,7 +99,6 @@ public Q_SLOTS: void startBlackWidget(); - void handleOSSignal(); void watchXConnection(); void shutdown(bool force); diff --git a/src/utils/utils.h b/src/utils/utils.h index 0e8bb826..c5689258 100644 --- a/src/utils/utils.h +++ b/src/utils/utils.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2021 - 2023 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2021 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -15,7 +15,6 @@ #define PIPEWIRE_PULSE_SOCKET "pipewire-pulse.socket" #define BAMFDAEMON_SERVICE "bamfdaemon.service" #define REDSHIFT_SERVICE "redshift.service" -#define DDE_DOCK_SERVICE "dde-dock.service" // sound #define SOUND_EFFECT_SCHEMA "com.deepin.dde.sound-effect" diff --git a/systemd/dde-session-core.target.wants/dde-shell@DDE.service b/systemd/dde-session-core.target.wants/dde-shell@DDE.service index 37b77566..fec9f562 100644 --- a/systemd/dde-session-core.target.wants/dde-shell@DDE.service +++ b/systemd/dde-session-core.target.wants/dde-shell@DDE.service @@ -8,6 +8,9 @@ CollectMode=inactive-or-failed Requisite=dde-session-pre.target After=dde-session-pre.target +# Start alongside the desktop; neither service waits for the other +# during startup or shutdown. + PartOf=dde-session-core.target Before=dde-session-core.target @@ -17,11 +20,9 @@ After=dbus.socket # AM Wants=org.desktopspec.ApplicationManager1.service -# Tray plugin loader services (owned by dde-tray-loader). BusName is acquired -# only after the QML DockCompositor is ready, and Before= here ensures the -# dockplugin Wayland socket exists before the loaders start. +# The tray target orders itself after this Type=dbus service, which becomes +# active once the dock's internal compositor is ready. Wants=dde-tray-loader.target -Before=dde-tray-loader.target [Service] Type=dbus diff --git a/systemd/dde-session-manager.service.in b/systemd/dde-session-manager.service.in index 4d2db4eb..fb88362a 100644 --- a/systemd/dde-session-manager.service.in +++ b/systemd/dde-session-manager.service.in @@ -13,6 +13,8 @@ Type=forking GuessMainPID=yes ExecStart=@CMAKE_INSTALL_FULL_LIBEXECDIR@/deepin/dde-session-loader-wrapper --systemd-service ExecStartPost=-/usr/bin/gdbus wait --session --timeout=30 org.deepin.dde.Session1 +# Keep shutdown cleanup out of process signal handlers: Qt/D-Bus calls can +# deadlock there. ExecStopPost also runs after signal termination or a crash. ExecStopPost=@CMAKE_INSTALL_FULL_LIBEXECDIR@/dde-session-ctl --shutdown ExecStopPost=-/bin/sh -c 'test "$SERVICE_RESULT" != "exec-condition" && systemctl --user unset-environment XDG_CURRENT_DESKTOP' ExecStopPost=-/bin/sh -c 'test "$SERVICE_RESULT" != "exec-condition" && systemctl --user unset-environment DISPLAY'