工作线程弹模态对话框 → 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。**

附录:代表性 dump 完整崩溃线程堆栈

样本 crash20282026-07-26 07_55_01__094cff93…(StreamGaGa,macOS 26.5.1

25F80,thread 43,共 35 帧)。帧 0-14 的 AppKit/CF/libobjc/libc++abi

为系统框架模块内偏移 —— 用户机是 Intel,本机(AS)Rosetta x86_64

dyld 缓存镜像与之 UUID 不一致,无法借本机缓存解出函数名(见日报

"环境备忘");帧 25-32 为主程序(含静态链接的 streameta),VMP 壳导致

minidump 无模块记录,任何符号库均不可解:


 0  AppKit                   +0x8b43e4     ← NSException 终止处理(报告签名帧)
 1  AppKit                   +0x381f93
 2  AppKit                   +0x8adba4
 3  CoreFoundation           +0x18f2a5
 4  libobjc.A.dylib          +0x1be44      ← objc 异常 rethrow
 5  libc++abi.dylib          +0x1111a      ← std::terminate 路径
 6  libc++abi.dylib          +0x13016
 7  libc++abi.dylib          +0x1dbb
 8  libobjc.A.dylib          +0x14130
 9  CoreFoundation           +0x10906a
10  AppKit                   +0x51331      ← NSWindow 创建中抛出 NSException
11  AppKit                   +0x1d77ab
12  AppKit                   +0x50cc6
13  AppKit                   +0x1d7764
14  AppKit                   +0x2ffa8f
15  libqcocoa.dylib          +0x75c92      ← Qt Cocoa 平台插件建窗
16  libqcocoa.dylib          +0x4a54f
17  libqcocoa.dylib          +0x430ba
18  libqcocoa.dylib          +0x42b4f
19  QtGui                    QWindowPrivate::create(bool, unsigned long long)
20  QtWidgets                QWidgetPrivate::create()
21  QtWidgets                QWidget::create(unsigned long long, bool, bool)
22  QtWidgets                QWidgetPrivate::setVisible(bool)
23  QtWidgets                QDialog::setVisible(bool)
24  QtWidgets                QDialog::exec()              ← 模态弹窗入口
25  主程序/streameta          0x1005113a3   ┐
26  主程序/streameta          0x1006af90e   │ 12 份 dump 此 4 帧地址
27  主程序/streameta          0x10134e546   │ 完全一致 = 单一调用点
28  主程序/streameta          0x102661124   ┘
29  主程序/streameta          0x102b6d97a
30  主程序/streameta          0x100dac02d
31  主程序/streameta          0x100dab8b3
32  主程序/streameta          0x10080703c   ← 线程例程入口
33  libsystem_pthread.dylib  +0x5d48       ← _pthread_start
34  libsystem_pthread.dylib  +0x182e

macOS 15 变体(AppKit+0x38ccd7 ×4)帧 25 起的主程序地址与上表逐帧

相同,仅帧 0-14 的系统框架偏移随 AppKit 版本不同。

附注

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