Repository navigation
fix(deviceinfo): reduce CPU contention on USB hotplug to avoid audio stutter - #971
Conversation
…stutter USB 热插拔时 org.deepin.DeviceInfo 插件会反复 spawn hwinfo/lshw 等重量级 命令并把 CPU 占满数百毫秒到数秒,正在播放的 deepin-mudio 音频缓冲 (92.88ms) 撑不过去导致 XRUN、卡顿数秒。按负责人验证通过的补丁修改: - detectthread: 用纯 sysfs 就绪探测 (waitKernelUsbReady) 替代反复执行 `hwinfo --usb --keyboard --mouse` 的忙等轮询,不再 spawn 子进程 - monitorusb: 对一批 udev 事件去抖合并,平息 500ms 后只执行一次 hwinfo --usb;select 超时改 200ms 以便及时触发合并 - threadpool: 新增 updateHotplugInfo() 只刷新与插拔相关的少量命令; loadDeviceInfo/updateDeviceInfo 的忙等自旋改为 waitForDone(10000) - deviceinfomanager: getInfo/isPathExisted 改用 constFind,避免 operator[] 误插入空条目导致 isInfoExisted 误判、canNotReplace 命令 永不重试 - threadpooltask: 删除未使用的 runCmd(cmd)/runCmdToFile 等死代码; 命令输出为空时跳过缓存,避免污染 isInfoExisted - mainjob: 热插拔走局部刷新 (HOTPLUG),客户端主动刷新/驱动操作走全量 刷新 (slotRefreshRequest);去掉 executeClientInstruction 中重复的 thread()->msleep(1000) Bug: https://pms.uniontech.com/bug-view-381051.html
|
Skipping CI for Draft Pull Request. |
|
Warning
详情 {
"lshw": {
"deepin-devicemanager-server/deepin-deviceinfo/src/loadinfo/threadpool.cpp": [
" // \u4e00\u6b21\u63d2\u62d4\u53ea\u9700\u8981\u66f4\u65b0\"\u4f1a\u53d8\"\u7684\u4fe1\u606f\u3002lshw / upower / lscpu / lspci /"
],
"deepin-devicemanager-server/deepin-deviceinfo/src/loadinfo/threadpool.h": [
" * USB \u63d2\u62d4\u5f88\u9891\u7e41\uff0c\u6ca1\u6709\u5fc5\u8981\u6bcf\u6b21\u90fd\u628a\u6574\u673a\u4fe1\u606f\u91cd\u65b0\u91c7\u96c6\u4e00\u904d\uff1alshw\u3001upower\u3001"
],
"deepin-devicemanager-server/deepin-deviceinfo/src/mainjob.cpp": [
" // USB \u70ed\u63d2\u62d4\u8d70\u5c40\u90e8\u5237\u65b0\uff0c\u7701\u6389\u4e0e\u63d2\u62d4\u65e0\u5173\u7684\u547d\u4ee4\uff08lshw / upower / lscpu /"
]
}
} |
Reviewer's GuideAPIChange DeviceInfoManager lookups to avoid inserting missing cache entries and return copied values so callers cannot retain references beyond the mutex lock.
Add the ThreadPool hotplug-refresh entry point and its command-list storage so callers can request a scoped update.
Uses of the APIRoute USB hotplug events to the scoped refresh while keeping explicit client refreshes and driver operations on the full-refresh path.
ImplementationReplace repeated hwinfo-based USB readiness checks with a bounded, sleeping sysfs probe and remove the old cached hwinfo comparison machinery.
Debounce batches of udev add/remove events and collect USB information once per settled batch before applying control and wakeup updates.
Replace thread-pool busy-wait loops with waitForDone and implement the scoped hotplug command list while retaining full-update command lists.
Remove unused command/file helpers and avoid caching empty command output so failed or unsupported commands can be retried.
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
Update SPDX-FileCopyrightText year from 2023 to 2026 in the files modified by this branch, per review residual risk feedback. Bug: https://pms.uniontech.com/bug-view-381051.html
|
Warning
详情 {
"lshw": {
"deepin-devicemanager-server/deepin-deviceinfo/src/loadinfo/threadpool.cpp": [
" // \u4e00\u6b21\u63d2\u62d4\u53ea\u9700\u8981\u66f4\u65b0\"\u4f1a\u53d8\"\u7684\u4fe1\u606f\u3002lshw / upower / lscpu / lspci /"
],
"deepin-devicemanager-server/deepin-deviceinfo/src/loadinfo/threadpool.h": [
" * USB \u63d2\u62d4\u5f88\u9891\u7e41\uff0c\u6ca1\u6709\u5fc5\u8981\u6bcf\u6b21\u90fd\u628a\u6574\u673a\u4fe1\u606f\u91cd\u65b0\u91c7\u96c6\u4e00\u904d\uff1alshw\u3001upower\u3001"
],
"deepin-devicemanager-server/deepin-deviceinfo/src/mainjob.cpp": [
" // USB \u70ed\u63d2\u62d4\u8d70\u5c40\u90e8\u5237\u65b0\uff0c\u7701\u6389\u4e0e\u63d2\u62d4\u65e0\u5173\u7684\u547d\u4ee4\uff08lshw / upower / lscpu /"
]
}
} |
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: GongHeng2017, max-lvs, wyu71 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 |
There was a problem hiding this comment.
Hey - I've found 7 issues
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="deepin-devicemanager-server/deepin-deviceinfo/src/loadinfo/threadpooltask.cpp" line_range="156" />
<code_context>
+ // 命令执行失败(如权限不足、工具缺失、硬件不支持)时输出为空。
+ // 此时不写入缓存,否则 isInfoExisted() 会认为该信息已经采集过,
+ // 对于 canNotReplace 的命令来说就永远不会再重试了。
+ if (info.isEmpty()) {
+ qCWarning(appLog) << "command produced empty output, skip caching:" << cmd;
+ return;
</code_context>
<issue_to_address>
**Removed devices remain visible**
When a successful refresh returns an empty result after a previous result contained devices, `runCmdToCache()` returns before updating `DeviceInfoManager`, so the previous nonempty value remains cached and clients continue seeing devices that are no longer present, including Bluetooth adapters and `/dev/sg*` devices.
Distinguish successful empty results from command failures and replace the cached value with an empty result when a successful refresh finds no devices.
Also at `deepin-devicemanager-server/deepin-deviceinfo/src/loadinfo/threadpooltask.cpp:157-159`.
</issue_to_address>
### Comment 2
<location path="deepin-devicemanager-server/deepin-deviceinfo/src/loadinfo/threadpool.cpp" line_range="52" />
<code_context>
+ // 原实现是 while (true) + activeThreadCount() 的忙等自旋,不 sleep,
+ // 会白占一个 CPU 核心最多 10 秒;音频等实时线程正好被它挤掉时间片。
+ // QThreadPool::waitForDone() 内部用条件变量等待,不消耗 CPU。
+ waitForDone(10000);
}
</code_context>
<issue_to_address>
**Overlapping refreshes overwrite newer state**
When a refresh task takes longer than 10 seconds and another refresh starts before it finishes, `waitForDone(10000)` returns while tasks are still running, and `updateInfo()` lets the caller proceed without waiting for them; later refreshes can then write the same cache keys, and an older task that finishes last overwrites newer device state or leaves callers seeing a partially refreshed cache.
Keep refreshes from proceeding or starting another batch until all tasks from the current batch have finished.
</issue_to_address>
### Comment 3
<location path="deepin-devicemanager-server/deepin-deviceinfo/src/mainjob.cpp" line_range="112" />
<code_context>
+ // USB 热插拔走局部刷新,省掉与插拔无关的命令(lshw / upower / lscpu /
+ // lspci / lpstat / bluetoothctl / hwinfo --framebuffer)。
+ // 客户端主动刷新与驱动操作完成走 slotRefreshRequest(),那里是全量刷新。
+ executeClientInstruction("HOTPLUG");
+}
+
</code_context>
<issue_to_address>
**Bluetooth pairing lists stay stale**
When a Bluetooth link event reports a pairing or removal change, `slotUsbChanged()` routes the event through `HOTPLUG`, but `updateHotplugInfo()` does not refresh `bt_device`; clients therefore continue seeing the previous paired-device list until a full refresh.
Include the paired-device task in the hotplug refresh.
Also at `deepin-devicemanager-server/deepin-deviceinfo/src/loadinfo/threadpool.cpp:230-234`, `deepin-devicemanager-server/deepin-deviceinfo/src/mainjob.cpp:154`.
</issue_to_address>
### Comment 4
<location path="deepin-devicemanager-server/deepin-deviceinfo/src/hotplug/detectthread.cpp" line_range="51" />
<code_context>
- end = QDateTime::currentMSecsSinceEpoch();
- }
- qCInfo(appLog) << " 此次判断插拔是否完成的时间为 ************ " << QDateTime::currentMSecsSinceEpoch() - begin;
+ const bool ready = waitKernelUsbReady(kUsbSettleMaxMs);
+ qCInfo(appLog) << " usb settle time:" << QDateTime::currentMSecsSinceEpoch() - begin
+ << " ready:" << ready;
</code_context>
<issue_to_address>
**Incomplete USB devices get refreshed**
When a USB device is still being enumerated or has an interface without a driver after the 1-second wait, `slotUsbChanged` emits `usbChanged` even when `waitKernelUsbReady` returns false, so `MainJob` refreshes device caches while enumeration or driver probing is incomplete; the new device can be omitted until another refresh.
When readiness times out, defer or retry the cache refresh instead of emitting `usbChanged` immediately.
</issue_to_address>
### Comment 5
<location path="deepin-devicemanager-server/deepin-deviceinfo/src/mainjob.cpp" line_range="148" />
<code_context>
+ // 原实现在这里用 thread()->msleep(1000) 硬睡主线程。DetectThread 已经
+ // 轮询确认内核处理完插拔、MonitorUsb 也已等待 1s 去抖,这 1 秒纯属重复,
+ // 而且会阻塞 DeviceInterface 的 D-Bus 方法派发,直接去掉。
updateAllDevice();
+ } else if (instructions.startsWith("HOTPLUG")) {
+ if (m_firstUpdate) {
</code_context>
<issue_to_address>
**Immediate refresh reads unsettled devices**
When a client calls `refreshInfo()` immediately after a device change, before the kernel has finished enumerating the device, `refreshInfo()` emits `sigUpdate`, which reaches `slotRefreshRequest()` and runs `updateAllDevice()` immediately; when the caller requests a refresh just after a device change, discovery commands run before the kernel finishes enumerating it, so the returned device information is stale until another refresh.
Keep a readiness wait for explicit refresh requests that can race device enumeration, or make those requests wait for the hotplug readiness path before collecting device data.
</issue_to_address>
### Comment 6
<location path="deepin-devicemanager-server/deepin-deviceinfo/src/loadinfo/threadpool.cpp" line_range="230-233" />
<code_context>
+ // 毫秒到数秒,并发执行时会把 CPU 占满,正在播放的音频因此丢帧(滋滋声)。
+ // 未列入的命令不执行,其缓存保持上一次的值,不会丢失数据。
+ // ---------------------------------------------------------------------
+ m_ListHotplug.append(cmdLsblk); // 新增/移除磁盘
+ m_ListHotplug.append(cmdLssg); // 新增/移除 SCSI 通用设备
+ m_ListHotplug.append(cmdHciconfig); // 蓝牙适配器状态
+ m_ListHotplug.append(cmdHwinfo); // 含 --sound:声卡拓扑也可能变化
}
</code_context>
<issue_to_address>
**New USB disks lack details**
When a USB disk is plugged in after the initial full refresh, `updateHotplugInfo` refreshes `lsblk` but leaves the `lshw` cache unchanged, so `DeviceGenerator::getDiskInfoFromLshw` cannot match newly attached disks to current `lshw_disk` data and their displayed storage metadata remains incomplete.
Refresh the relevant `lshw` storage data during hotplug, or avoid using its stale cache to enrich the refreshed storage list.
</issue_to_address>
### Comment 7
<location path="deepin-devicemanager-server/deepin-deviceinfo/src/loadinfo/threadpool.cpp" line_range="217" />
<code_context>
Cmd cmdHwinfoMonitor; //hwinfo --framebuffer --monitor
- cmdHwinfoMonitor.cmd = QString("%1 %2%3").arg("hwinfo --framebuffer --monitor > ").arg(PATH).arg("hwinfo_monitor.txt");
+ cmdHwinfoMonitor.cmd = QString("%1 %2%3").arg("hwinfo --monitor > ").arg(PATH).arg("hwinfo_monitor.txt");
cmdHwinfoMonitor.file = "hwinfo_monitor.txt";
cmdHwinfoMonitor.canNotReplace = false;
m_ListCmd.append(cmdHwinfoMonitor);
</code_context>
<issue_to_address>
**Full refresh omits framebuffer data**
When a client requests a full refresh or startup collection runs, the full-refresh command now runs only `hwinfo --monitor`, so framebuffer information previously collected by `hwinfo --framebuffer --monitor` is absent from `hwinfo_monitor.txt`; callers performing a full refresh no longer receive that data.
Keep `--framebuffer` in the full-refresh command; omit it only from hotplug refreshes if it is unnecessary there.
</issue_to_address>| // 命令执行失败(如权限不足、工具缺失、硬件不支持)时输出为空。 | ||
| // 此时不写入缓存,否则 isInfoExisted() 会认为该信息已经采集过, | ||
| // 对于 canNotReplace 的命令来说就永远不会再重试了。 | ||
| if (info.isEmpty()) { |
There was a problem hiding this comment.
🟡 Medium · Removed devices remain visible
When a successful refresh returns an empty result after a previous result contained devices, runCmdToCache() returns before updating DeviceInfoManager, so the previous nonempty value remains cached and clients continue seeing devices that are no longer present, including Bluetooth adapters and /dev/sg* devices.
Distinguish successful empty results from command failures and replace the cached value with an empty result when a successful refresh finds no devices.
Also at deepin-devicemanager-server/deepin-deviceinfo/src/loadinfo/threadpooltask.cpp:157-159.
Prompt for AI agents
In `deepin-devicemanager-server/deepin-deviceinfo/src/loadinfo/threadpooltask.cpp` at line 156:
**Removed devices remain visible**
When a successful refresh returns an empty result after a previous result contained devices, `runCmdToCache()` returns before updating `DeviceInfoManager`, so the previous nonempty value remains cached and clients continue seeing devices that are no longer present, including Bluetooth adapters and `/dev/sg*` devices.
Distinguish successful empty results from command failures and replace the cached value with an empty result when a successful refresh finds no devices.
Also at `deepin-devicemanager-server/deepin-deviceinfo/src/loadinfo/threadpooltask.cpp:157-159`.| // 原实现是 while (true) + activeThreadCount() 的忙等自旋,不 sleep, | ||
| // 会白占一个 CPU 核心最多 10 秒;音频等实时线程正好被它挤掉时间片。 | ||
| // QThreadPool::waitForDone() 内部用条件变量等待,不消耗 CPU。 | ||
| waitForDone(10000); |
There was a problem hiding this comment.
🟡 Medium · Overlapping refreshes overwrite newer state
When a refresh task takes longer than 10 seconds and another refresh starts before it finishes, waitForDone(10000) returns while tasks are still running, and updateInfo() lets the caller proceed without waiting for them; later refreshes can then write the same cache keys, and an older task that finishes last overwrites newer device state or leaves callers seeing a partially refreshed cache.
Keep refreshes from proceeding or starting another batch until all tasks from the current batch have finished.
Prompt for AI agents
In `deepin-devicemanager-server/deepin-deviceinfo/src/loadinfo/threadpool.cpp` at line 52:
**Overlapping refreshes overwrite newer state**
When a refresh task takes longer than 10 seconds and another refresh starts before it finishes, `waitForDone(10000)` returns while tasks are still running, and `updateInfo()` lets the caller proceed without waiting for them; later refreshes can then write the same cache keys, and an older task that finishes last overwrites newer device state or leaves callers seeing a partially refreshed cache.
Keep refreshes from proceeding or starting another batch until all tasks from the current batch have finished.| // USB 热插拔走局部刷新,省掉与插拔无关的命令(lshw / upower / lscpu / | ||
| // lspci / lpstat / bluetoothctl / hwinfo --framebuffer)。 | ||
| // 客户端主动刷新与驱动操作完成走 slotRefreshRequest(),那里是全量刷新。 | ||
| executeClientInstruction("HOTPLUG"); |
There was a problem hiding this comment.
🟡 Medium · Bluetooth pairing lists stay stale
When a Bluetooth link event reports a pairing or removal change, slotUsbChanged() routes the event through HOTPLUG, but updateHotplugInfo() does not refresh bt_device; clients therefore continue seeing the previous paired-device list until a full refresh.
Include the paired-device task in the hotplug refresh.
Also at deepin-devicemanager-server/deepin-deviceinfo/src/loadinfo/threadpool.cpp:230-234, deepin-devicemanager-server/deepin-deviceinfo/src/mainjob.cpp:154.
Prompt for AI agents
In `deepin-devicemanager-server/deepin-deviceinfo/src/mainjob.cpp` at line 112:
**Bluetooth pairing lists stay stale**
When a Bluetooth link event reports a pairing or removal change, `slotUsbChanged()` routes the event through `HOTPLUG`, but `updateHotplugInfo()` does not refresh `bt_device`; clients therefore continue seeing the previous paired-device list until a full refresh.
Include the paired-device task in the hotplug refresh.
Also at `deepin-devicemanager-server/deepin-deviceinfo/src/loadinfo/threadpool.cpp:230-234`, `deepin-devicemanager-server/deepin-deviceinfo/src/mainjob.cpp:154`.| end = QDateTime::currentMSecsSinceEpoch(); | ||
| } | ||
| qCInfo(appLog) << " 此次判断插拔是否完成的时间为 ************ " << QDateTime::currentMSecsSinceEpoch() - begin; | ||
| const bool ready = waitKernelUsbReady(kUsbSettleMaxMs); |
There was a problem hiding this comment.
🟡 Medium · Incomplete USB devices get refreshed
When a USB device is still being enumerated or has an interface without a driver after the 1-second wait, slotUsbChanged emits usbChanged even when waitKernelUsbReady returns false, so MainJob refreshes device caches while enumeration or driver probing is incomplete; the new device can be omitted until another refresh.
When readiness times out, defer or retry the cache refresh instead of emitting usbChanged immediately.
Prompt for AI agents
In `deepin-devicemanager-server/deepin-deviceinfo/src/hotplug/detectthread.cpp` at line 51:
**Incomplete USB devices get refreshed**
When a USB device is still being enumerated or has an interface without a driver after the 1-second wait, `slotUsbChanged` emits `usbChanged` even when `waitKernelUsbReady` returns false, so `MainJob` refreshes device caches while enumeration or driver probing is incomplete; the new device can be omitted until another refresh.
When readiness times out, defer or retry the cache refresh instead of emitting `usbChanged` immediately.| // 原实现在这里用 thread()->msleep(1000) 硬睡主线程。DetectThread 已经 | ||
| // 轮询确认内核处理完插拔、MonitorUsb 也已等待 1s 去抖,这 1 秒纯属重复, | ||
| // 而且会阻塞 DeviceInterface 的 D-Bus 方法派发,直接去掉。 | ||
| updateAllDevice(); |
There was a problem hiding this comment.
🟡 Medium · Immediate refresh reads unsettled devices
When a client calls refreshInfo() immediately after a device change, before the kernel has finished enumerating the device, refreshInfo() emits sigUpdate, which reaches slotRefreshRequest() and runs updateAllDevice() immediately; when the caller requests a refresh just after a device change, discovery commands run before the kernel finishes enumerating it, so the returned device information is stale until another refresh.
Keep a readiness wait for explicit refresh requests that can race device enumeration, or make those requests wait for the hotplug readiness path before collecting device data.
Prompt for AI agents
In `deepin-devicemanager-server/deepin-deviceinfo/src/mainjob.cpp` at line 148:
**Immediate refresh reads unsettled devices**
When a client calls `refreshInfo()` immediately after a device change, before the kernel has finished enumerating the device, `refreshInfo()` emits `sigUpdate`, which reaches `slotRefreshRequest()` and runs `updateAllDevice()` immediately; when the caller requests a refresh just after a device change, discovery commands run before the kernel finishes enumerating it, so the returned device information is stale until another refresh.
Keep a readiness wait for explicit refresh requests that can race device enumeration, or make those requests wait for the hotplug readiness path before collecting device data.| m_ListHotplug.append(cmdLsblk); // 新增/移除磁盘 | ||
| m_ListHotplug.append(cmdLssg); // 新增/移除 SCSI 通用设备 | ||
| m_ListHotplug.append(cmdHciconfig); // 蓝牙适配器状态 | ||
| m_ListHotplug.append(cmdHwinfo); // 含 --sound:声卡拓扑也可能变化 |
There was a problem hiding this comment.
🟡 Medium · New USB disks lack details
When a USB disk is plugged in after the initial full refresh, updateHotplugInfo refreshes lsblk but leaves the lshw cache unchanged, so DeviceGenerator::getDiskInfoFromLshw cannot match newly attached disks to current lshw_disk data and their displayed storage metadata remains incomplete.
Refresh the relevant lshw storage data during hotplug, or avoid using its stale cache to enrich the refreshed storage list.
Prompt for AI agents
In `deepin-devicemanager-server/deepin-deviceinfo/src/loadinfo/threadpool.cpp` at lines 230-233:
**New USB disks lack details**
When a USB disk is plugged in after the initial full refresh, `updateHotplugInfo` refreshes `lsblk` but leaves the `lshw` cache unchanged, so `DeviceGenerator::getDiskInfoFromLshw` cannot match newly attached disks to current `lshw_disk` data and their displayed storage metadata remains incomplete.
Refresh the relevant `lshw` storage data during hotplug, or avoid using its stale cache to enrich the refreshed storage list.| Cmd cmdHwinfoMonitor; //hwinfo --framebuffer --monitor | ||
| cmdHwinfoMonitor.cmd = QString("%1 %2%3").arg("hwinfo --framebuffer --monitor > ").arg(PATH).arg("hwinfo_monitor.txt"); | ||
| cmdHwinfoMonitor.cmd = QString("%1 %2%3").arg("hwinfo --monitor > ").arg(PATH).arg("hwinfo_monitor.txt"); | ||
| cmdHwinfoMonitor.file = "hwinfo_monitor.txt"; |
There was a problem hiding this comment.
🟡 Medium · Full refresh omits framebuffer data
When a client requests a full refresh or startup collection runs, the full-refresh command now runs only hwinfo --monitor, so framebuffer information previously collected by hwinfo --framebuffer --monitor is absent from hwinfo_monitor.txt; callers performing a full refresh no longer receive that data.
Keep --framebuffer in the full-refresh command; omit it only from hotplug refreshes if it is unnecessary there.
Prompt for AI agents
In `deepin-devicemanager-server/deepin-deviceinfo/src/loadinfo/threadpool.cpp` at line 217:
**Full refresh omits framebuffer data**
When a client requests a full refresh or startup collection runs, the full-refresh command now runs only `hwinfo --monitor`, so framebuffer information previously collected by `hwinfo --framebuffer --monitor` is absent from `hwinfo_monitor.txt`; callers performing a full refresh no longer receive that data.
Keep `--framebuffer` in the full-refresh command; omit it only from hotplug refreshes if it is unnecessary there.
deepin pr auto review🤖 AI 代码审查报告📊 总体评价
🔍 详细分析1. 语法逻辑 ✅评价: 语法正确,逻辑清晰 ✅ 通过 潜在问题: 建议: ["waitKernelUsbReady() 中对含 ':' 的 sysfs 条目,可仅将缺失 /driver 视为非致命条件(continue 而非 break),或仅检查 usb_device 的 idVendor 是否就绪,避免对无驱动接口的误判", '考虑将 waitKernelUsbReady() 的等待逻辑移至 DetectThread::run()(工作线程)中执行,避免阻塞主线程事件循环'] 2. 代码质量 ✅评价: 代码结构清晰,注释完整 ✅ 通过 潜在问题: 建议: ["更新 threadpool.cpp 第215行注释为 '//hwinfo --monitor',与实际命令一致", "将 getInfo() 返回类型从 'const QString' 改为 'QString'", '在 applyUsbControl() 中增加 hwinfo 空输出校验,与 threadpooltask.cpp 的处理方式保持一致'] 3. 代码性能 ✅评价: 性能良好,资源使用合理 ✅ 通过 潜在问题: 建议: ["为热插拔场景创建更窄的 hwinfo 命令,如 'hwinfo --usb --sound --disk --keyboard --mouse',排除与插拔无关的 --netcard --cdrom --display --fingerprint", '将 applyUsbControl() 中的 waitForFinished(-1) 改为 waitForFinished(10000) 并在超时时 kill 进程', '在 executeClientInstruction() 的 HOTPLUG 分支中,updateHotplugInfo() 之后增加 m_pool->waitForDone(30000) 作为安全网'] 4. 代码安全 🔒评价: 存在0个安全漏洞 ✅ 通过
安全漏洞详情: 建议: ['无安全相关建议,当前实现安全合规'] 💡 改进建议代码示例// === 1. detectthread.cpp: waitKernelUsbReady() 修复无驱动接口误判 ===
bool DetectThread::waitKernelUsbReady(int maxWaitMs)
{
const qint64 begin = QDateTime::currentMSecsSinceEpoch();
while (true) {
bool ready = true;
QDir dir("/sys/bus/usb/devices");
const QFileInfoList entries = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name);
for (const QFileInfo &entry : entries) {
const QString path = entry.absoluteFilePath();
if (entry.fileName().contains(':')) {
// usb_interface:某些接口(vendor-specific、libusb 等)可能永远不会
// 绑定内核驱动,不将其视为未就绪,仅依赖 usb_device 的 idVendor 做就绪判断
continue;
} else if (QFileInfo::exists(path + "/idVendor")) {
// usb_device:已枚举
continue;
} else {
ready = false;
break;
}
}
if (ready)
return true;
if (QDateTime::currentMSecsSinceEpoch() - begin >= maxWaitMs)
return false;
msleep(kUsbSettleStepMs);
}
}
// === 2. monitorusb.cpp: applyUsbControl() 增加超时与空输出校验 ===
void MonitorUsb::applyUsbControl()
{
const bool needControl = m_NeedControl;
const bool needWakeup = m_NeedWakeup;
m_NeedControl = false;
m_NeedWakeup = false;
QProcess process;
process.start("hwinfo --usb");
if (!process.waitForFinished(10000)) {
qCWarning(appLog) << "hwinfo --usb timed out, skipping control update";
process.kill();
process.waitForFinished(3000);
return;
}
const QString info = process.readAllStandardOutput();
if (info.isEmpty()) {
qCWarning(appLog) << "hwinfo --usb produced empty output, skipping control update";
return;
}
if (needControl)
ControlInterface::getInstance()->disableOutDevice(info);
if (needWakeup)
ControlInterface::getInstance()->updateWakeup(info);
}
// === 3. deviceinfomanager.h: 移除 const 返回修饰 ===
QString getInfo(const QString &key);
// === 4. threadpool.cpp: 更新陈旧注释 ===
Cmd cmdHwinfoMonitor; //hwinfo --monitor本报告由 AI 代码审查工具自动生成 |
|
/forcemerge |
|
This pr force merged! (status: unstable) |
1b2eb8e
into
linuxdeepin:develop/eagle
背景
PMS 381051:播放音频时插拔 U 盘,音频卡顿数秒。
https://pms.uniontech.com/bug-view-381051.html
根因
USB 热插拔时
org.deepin.DeviceInfo插件会反复 spawnhwinfo/lshw等重量级命令并把 CPU 占满数百毫秒到数秒,正在播放的 deepin-music 音频缓冲 (92.88ms) 撑不过去
导致 XRUN、卡顿数秒。本仓库侧需减少热插拔时的 CPU 争用(deepin-music 侧另提 PR
移除 SDL 音频回调中的同步 DBus 调用)。
修改内容(按负责人验证通过的补丁,未自行缩减)
waitKernelUsbReady()替代反复执行hwinfo --usb --keyboard --mouse的忙等轮询,不再 spawn 子进程;构造函数中删除初始化期的
curHwinfoUsbInfo调用hwinfo --usb(新增applyUsbControl());select超时由 1s 改 200ms 以便及时触发合并
updateHotplugInfo()只刷新与插拔相关的少量命令(lsblk/lssg/hciconfig/hwinfo);
loadDeviceInfo/updateDeviceInfo的忙等自旋改为
waitForDone(10000)(条件变量等待,不占 CPU)getInfo/isPathExisted改用constFind,避免operator[]误插入空条目导致isInfoExisted误判、canNotReplace命令永不重试;getInfo返回值改为const QString(不再返回引用)runCmd(cmd)/runCmdToFile/loadSmartctlInfoToFile/loadLspciVSInfoToFile等死代码;命令输出为空时跳过缓存,避免污染isInfoExistedHOTPLUG),客户端主动刷新/驱动操作走全量刷新(
slotRefreshRequest);去掉executeClientInstruction中重复的thread()->msleep(1000)自测
develop/eagle @ 670ebb86上git apply --check通过,应用后
git diff与补丁逐字节一致updateHotplugInfo/applyUsbControl/waitKernelUsbReady/slotRefreshRequest在头文件中均有声明关联
Bug: https://pms.uniontech.com/bug-view-381051.html
Summary by Sourcery
Reduce device-information processing overhead during USB hotplug events to avoid CPU contention and audio stutter.
Bug Fixes:
Enhancements: