fix(recordtime-plugin): replace heartbeat polling with QDBusServiceWa…#846
Conversation
…tcher The dock plugin previously detected main-process death by counting incoming onRecording() heartbeats from the recorder's emitRecording() loop and starting a 2s checkTimer to compare m_count vs m_nextCount. Any transient stall in the main process (audio capture blocked, IO backpressure, scheduler starvation on low-end machines) silenced the heartbeat and made the plugin falsely conclude the recorder had stopped — triggering onStop() -> clear() and destroying the timer widget, so the next heartbeat would re-create it from zero and the displayed time visibly restarted. Switch to event-driven liveness: install a QDBusServiceWatcher on "com.deepin.ScreenRecorder" with WatchForUnregistration. dbus-daemon auto-unregisters the bus name only when the owning process actually exits, so a busy-but-alive recorder no longer trips the watchdog. Normal stop still arrives via the explicit DBus onStop() call; the watcher only covers crash cleanup. Also guard onStop() against a null m_timeWidget — a stop-then-crash sequence can fire the watcher after clear() has already nulled it. 将托盘插件的"主程序存活检测"从基于心跳计数的轮询定时器改为 QDBusServiceWatcher 事件订阅。原方案在主程序因音频卡顿等原因 暂停心跳时会误判为崩溃,触发 onStop -> clear 销毁计时控件, 下一次心跳到来时重新初始化导致显示时间从零重启。改用 watcher 监听 bus name 注销事件后,仅主程序真实退出时才触发清理;同时 为 onStop 增加 m_timeWidget 空指针保护以应对 stop 后再触发的 边缘时序。 Log: 修复录屏过程中主程序短暂卡顿导致任务栏插件计时清零重启的问题 PMS: BUG-330567 Influence: 修复后主程序在音频抓取等场景下短暂卡顿不再触发插件误判停止, 任务栏录制时间显示连续不重置;主程序真实崩溃时仍能由 watcher 及时清理。
deepin pr auto review你好!我是CodeGeeX。我已仔细审查了你提供的Git Diff。 这次代码变更的核心逻辑是将基于心跳定时器的崩溃检测机制替换为基于 不过,在语法逻辑、代码质量和安全性方面,还有一些需要改进和注意的地方。以下是详细的审查意见: 一、 语法与逻辑1.
2.
二、 代码质量1. 对象生命周期管理(智能指针的运用)
2. 魔法字符串
三、 代码性能1. 定时器精度的优化
四、 代码安全1. D-Bus服务名欺骗/冒用风险
综合改进后的代码建议结合以上意见,我为你修改了部分代码,供参考: recordtimeplugin.h // ... 其他代码不变 ...
public slots:
QPointer<DBusService> m_dBusService;
bool m_bshow;
/**
* @brief 监听主进程服务名的注销事件,替换原心跳定时器。
* 使用 QPointer 避免悬空指针,无需手动 deleteLater 和置空。
*/
QPointer<QDBusServiceWatcher> m_serviceWatcher;
static const QString MAIN_RECORDER_SERVICE; // 避免魔法字符串
// ... 其他代码不变 ...recordtimeplugin.cpp // 初始化静态变量
const QString RecordTimePlugin::MAIN_RECORDER_SERVICE = "com.deepin.ScreenRecorder";
RecordTimePlugin::RecordTimePlugin(QObject *parent)
: QObject(parent)
{
qCDebug(dsrApp) << "RecordTimePlugin constructor called.";
m_timer = nullptr;
m_timeWidget = nullptr;
// m_serviceWatcher 默认构造为 nullptr,无需手动初始化
}
void RecordTimePlugin::clear()
{
if (m_timeWidget) {
qCDebug(dsrApp) << "Deleting time widget";
m_timeWidget->deleteLater();
m_timeWidget = nullptr;
}
if (m_serviceWatcher) {
qCDebug(dsrApp) << "Deleting service watcher";
// QPointer 管理下,直接 delete 即可,QPointer 自动置空
// 如果依赖事件循环销毁,也可用 deleteLater(),但这里直接 delete 更干净
delete m_serviceWatcher.data();
}
qCDebug(dsrApp) << "clear method finished.";
}
void RecordTimePlugin::onStart(bool resetTime)
{
// ... 前面代码不变 ...
m_timer->start(1000); // 修正:如果是1秒刷新,改为1000ms
// ... 后面代码不变 ...
}
void RecordTimePlugin::onStop()
{
qCDebug(dsrApp) << "onStop method called.";
// 1. 防止重入:如果已经在处理停止逻辑,直接返回
// 2. 空指针保护
if (!m_timeWidget || !m_timeWidget->enabled()) {
return;
}
qInfo() << "unload plugin";
qCDebug(dsrApp) << "Time widget enabled, unloading plugin.";
m_proxyInter->itemRemoved(this, pluginName());
m_bshow = false;
clear();
qInfo() << "stop record time";
}
void RecordTimePlugin::onRecording()
{
qCDebug(dsrApp) << "onRecording method called.";
// 增加 m_timeWidget 的判空保护
if (m_timeWidget && m_timeWidget->enabled() && m_bshow) {
qCDebug(dsrApp) << "Time widget enabled and visible.";
if (nullptr == m_serviceWatcher) {
qCDebug(dsrApp) << "Installing QDBusServiceWatcher for main recorder process";
m_serviceWatcher = new QDBusServiceWatcher(
MAIN_RECORDER_SERVICE,
QDBusConnection::sessionBus(),
QDBusServiceWatcher::WatchForUnregistration,
this);
// 使用 Qt5 的 lambda 上下文对象机制,防止生命周期异常导致悬空调用
connect(m_serviceWatcher, &QDBusServiceWatcher::serviceUnregistered,
this, [this](const QString &serviceName) {
qCInfo(dsrApp) << "Main recorder service unregistered, stopping plugin";
// 断开连接,防止 D-Bus 信号重入
if (m_serviceWatcher) {
disconnect(m_serviceWatcher, &QDBusServiceWatcher::serviceUnregistered, this, nullptr);
}
onStop();
});
}
}
qCDebug(dsrApp) << "onRecording method finished.";
}主要修改点总结:
希望这些意见对你有所帮助!如果有任何疑问,欢迎继续探讨。 |
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: dengzhongyuan365-dev, lzwind The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
|
/forcemerge |
|
/merge |
1 similar comment
|
/merge |
|
/forcemerge |
|
This pr force merged! (status: unknown) |
…tcher
The dock plugin previously detected main-process death by counting incoming onRecording() heartbeats from the recorder's emitRecording() loop and starting a 2s checkTimer to compare m_count vs m_nextCount. Any transient stall in the main process (audio capture blocked, IO backpressure, scheduler starvation on low-end machines) silenced the heartbeat and made the plugin falsely conclude the recorder had stopped — triggering onStop() -> clear() and destroying the timer widget, so the next heartbeat would re-create it from zero and the displayed time visibly restarted.
Switch to event-driven liveness: install a QDBusServiceWatcher on "com.deepin.ScreenRecorder" with WatchForUnregistration. dbus-daemon auto-unregisters the bus name only when the owning process actually exits, so a busy-but-alive recorder no longer trips the watchdog. Normal stop still arrives via the explicit DBus onStop() call; the watcher only covers crash cleanup.
Also guard onStop() against a null m_timeWidget — a stop-then-crash sequence can fire the watcher after clear() has already nulled it.
将托盘插件的"主程序存活检测"从基于心跳计数的轮询定时器改为
QDBusServiceWatcher 事件订阅。原方案在主程序因音频卡顿等原因
暂停心跳时会误判为崩溃,触发 onStop -> clear 销毁计时控件,
下一次心跳到来时重新初始化导致显示时间从零重启。改用 watcher
监听 bus name 注销事件后,仅主程序真实退出时才触发清理;同时
为 onStop 增加 m_timeWidget 空指针保护以应对 stop 后再触发的
边缘时序。
Log: 修复录屏过程中主程序短暂卡顿导致任务栏插件计时清零重启的问题
PMS: BUG-330567
Influence: 修复后主程序在音频抓取等场景下短暂卡顿不再触发插件误判停止,
任务栏录制时间显示连续不重置;主程序真实崩溃时仍能由 watcher 及时清理。