工作线程弹模态对话框 → AppKit NSException 崩溃(×12,两签名同根)

证据链

12 份 dump 的崩溃线程栈完全一致(主程序段返回地址逐帧相同

0x1005113a3 → 0x1006af90e → 0x10134e546 → 0x102661124,单一调用点):


pthread 入口
 → 主程序无符号帧 ×8   ← 实为静态链接进主程序的 streameta 下载引擎代码
 → QDialog::exec() → QDialog::setVisible → QWidget::create
 → QWindowPrivate::create → libqcocoa → AppKit NSWindow 创建
 → NSException(非主线程创建窗口)→ objc rethrow → libc++abi → terminate

macOS 15 与 26 均崩(26 上 AppKit 校验更严,7/25 起激增)。

根因(源码审计,置信度:高)

streameta 是 STATIC 库链接进主程序(CMakeLists.txt:1414),其工作线程

通过纯虚接口回调主程序的 *Forward 桥接类,后者**不做线程编组直接

exec()**:

1. 首选嫌疑: iNormalMessageBoxFactory::showMessageBoxWithOk

→ `utility_tool/DVDFabStream/Stream/NormalMessageBoxFactory.cpp:39

box.exec()(经 NormalMessageBoxFactoryForward` :276-290 透传)。

streameta 里 30+ 处工作线程直接调用(meta 解析失败、下载失败

提示等,如 StreamDownloader.cpp:936、ResponseFilterControl.cpp:2537),

全部无线程守卫。工作线程来源:WVreqlicAMZ/Thread.cpp:45 的

KThread(裸 pthread_create,无线程名,40+ 个 CWVreqlic* 许可证

类继承)及 30+ 处裸 std::thread remux 线程 —— 与 dump 的无名

线程特征吻合;桥接层帧数(≈8 层)与栈吻合。

2. 次选: BatchDownloadManager.cpp:981 dlg.exec()(downloadBetterVersion,

ResponseFilterControl.cpp:2470 从工作线程发起,busy-wait 自证)。

3. 同类隐患: LoadingDlg.cpp:405/475/485/504/517 五个 Forward 方法

落点无守卫(与已修复的 checkPath :421 紧邻但漏修)。

代码库中已有 3 处正确示范(LoadingDlg.cpp:421 用

BlockingQueuedConnection 编组回主线程,注释明写"工作线程会 freeze

或 crash",对应历史 issue #186)—— 本类 bug 已被踩过,只修了个别点。

修复方案(pattern-level group fix,MR 候选)

在主程序侧 Forward 桥接层统一加主线程编组守卫(照抄

LoadingDlg.cpp:421-427 的既有写法),单点覆盖全部 lib 调用点:

仓库/分支: utility/utility_tool @ dev_vs2019(Fix Scope 内,改动局部、

低风险、有仓内既有模式可抄)。**当前 HOLD_MR 观察模式,仅列为 MR

候选,未创建 worktree/MR。**

附注

上 MessageBox 跨线程宽容 —— 解释了为何 Windows 版无此类上报。