2

我的问题:JavaScript 调用插件,它分叉了一个打开 NSOpenPanel(或 NSSavePanel)对话框的线程。这通常有效,但有时它会在 Firefox 和 Chrome 上的“runModal”上崩溃。崩溃似乎在某些机器(可能是 10.7)上随机发生并且比其他机器更频繁。崩溃的堆栈位置各不相同,但一般发生在打开对话框的线程上。

我使用 POSIX 线程,因为它是跨平台代码,但我确实生成了 NSThread让 Cocoa 知道

NSThread* t = [[NSThread alloc] init];
[t start];
[t release];

打开对话框的代码:

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // necessary?
NSOpenPanel* openPanel = [NSOpenPanel openPanel];
[openPanel setAllowsMultipleSelection:_allowMultipleFiles];
[openPanel setCanChooseFiles:YES];
[openPanel setCanChooseDirectories:YES];
NSInteger result = [openPanel runModal]; // crash
... handle result ...
[pool release];

堆栈跟踪:

Process:         plugin-container [25536]
Path:            /Applications/Firefox.app/Contents/MacOS/plugin-container.app/Contents/MacOS/plugin-container
Identifier:      org.mozilla.plugincontainer
Version:         ??? (1.0)
Code Type:       X86-64 (Native)
Parent Process:  firefox-bin [25531]

Date/Time:       2011-12-09 17:12:04.271 -0800
OS Version:      Mac OS X 10.6.8 (10K549)
Report Version:  6

Exception Type:  EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x000000000000000d
Crashed Thread:  14

Thread 14 Crashed:
0   XUL                             0x0000000100d84ff4 mac_plugin_interposing_child_OnSetCursor + 38068
1   XUL                             0x0000000100dd38bd mac_plugin_interposing_child_OnSetCursor + 359805
2   XUL                             0x0000000100d7b247 mac_plugin_interposing_child_OnHideCursor + 1175
3   com.apple.AppKit                0x00007fff801cbfdb _NSCreateWindowWithOpaqueShape2 + 610
4   com.apple.AppKit                0x00007fff80160691 -[NSWindow _commonAwake] + 1214
5   com.apple.AppKit                0x00007fff803c282f -[NSWindow _setModal:] + 138
6   com.apple.AppKit                0x00007fff803c2183 -[NSApplication _orderFrontModalWindow:relativeToWindow:] + 482
7   com.apple.AppKit                0x00007fff803c1ce7 -[NSApplication _commonBeginModalSessionForWindow:relativeToWindow:modalDelegate:didEndSelector:contextInfo:] + 714
8   com.apple.AppKit                0x00007fff803c1a18 -[NSApplication beginModalSessionForWindow:] + 36
9   com.apple.AppKit                0x00007fff803c193a -[NSApplication runModalForWindow:] + 106
10  XUL                             0x0000000100d7af01 mac_plugin_interposing_child_OnHideCursor + 337
11  com.apple.AppKit                0x00007fff80627112 -[NSSavePanel runModal] + 318
12  com.aspera.AsperaWeb            0x000000010509f9d4 Connect::Web::MacOpenFolderDialogImpl::doShow() + 596
13  com.aspera.AsperaWeb            0x00000001050a0fa3 Connect::Web::IDialogImpl::show() + 45
14  com.aspera.AsperaWeb            0x00000001050a1e07 Connect::Web::OpenFolderDialog::showHelper(bool) + 141
15  com.aspera.AsperaWeb            0x00000001050a1ef0 Connect::Web::OpenFolderDialog::run() + 42
16  com.aspera.AsperaWeb            0x000000010509eb91 Connect::Web::SingleJobWorker::run() + 63
17  com.aspera.AsperaWeb            0x0000000105098160 start_helper + 38
18  libSystem.B.dylib               0x00007fff8593afd6 _pthread_start + 331
19  libSystem.B.dylib               0x00007fff8593ae89 thread_start + 13

Thread 14 crashed with X86 Thread State (64-bit):
  rax: 0x0000000000000000  rbx: 0x0000000102063840  rcx: 0x0000000000000000  rdx: 0x0000000101e27ee0
  rdi: 0x0000000101e0bcc8  rsi: 0x0000000000000000  rbp: 0x0000000101e27ef0  rsp: 0x000000011d5805e0
   r8: 0x0000000101e0bcc8   r9: 0x00000001040fc0a4  r10: 0x00007fffffe001a0  r11: 0x0000000000000202
  r12: 0x0000000101e27ee0  r13: 0x0000000000000001  r14: 0x000000011d5805e0  r15: 0x000000011d580754
  rip: 0x0000000100d84ff4  rfl: 0x0000000000010246  cr2: 0x000000000000000d

我猜在辅助线程上打开对话框存在问题。也许我需要在主线程上打开它?但是,我确实希望保持 JavaScript 调用异步。我正在查看NSNotificationQueueperformSelectorOnMainThread

4

3 回答 3

4

我在这里有一个我在FireBreath中使用的对话框抽象:https ://gist.github.com/1368648

事实证明,您不能在辅助线程上使用 runModal;它必须在应用程序的主线程上。这是 NPAPI 插件中的一个挑战,因为您也不能在 NPAPI 调用期间阻塞主线程,否则浏览器(至少 chrome 和 firefox 4+)可能会杀死您。

解决方案是让它在主线程上运行,但不作为 NPAPI 调用的一部分;您可以通过使用 performSelectorOnMainThread 来做到这一点,而不是等待它完成。如果您提供某种回调(一个 NPObject javascript 函数效果很好),您可以在对话完成时调用它。

FireBreath 将此方法用于其 BrowserHost::ScheduleOnMainThread 函数,这是我现在在我引用的那个要点中使用的。有人(你?)对要点发表了评论,询问它是否对我有用;评论时的要点没有,但我已经用修复更新了它(我昨天才发现)。由于我们将其更新为稍后使用 performSelectorOnMainThread 运行调用,因此我无法重现崩溃。

回想起来,你已经熟悉了这种方式回调主线程:How to callback plugin thread on Safari 5.1 on OSX?

您使用相同的技术;你会认为阻塞线程也会很危险,但是由于在模式对话框期间系统事件循环继续运行,你没关系;您的代码不会返回,但仍会分派其他事件。浏览器没有等待 NPAPI 调用返回,因此它似乎没有任何问题。

于 2011-12-10T05:14:18.940 回答
2

所有 Appkit 都不是线程安全的,所有 Appkit 方法都应该发生在主线程上。你可以使用 performselectoronmainthread... 方法来强制逻辑在主线程上。

于 2011-12-10T15:18:30.090 回答
0

无论如何,打开的面板应该是异步的,所以没有理由不能在主线程上显示打开的面板。根本不使用runModal. 理想情况下,将其显示为浏览器窗口上的工作表。

当它返回给您时,您可以通过您已经计划使用的任何方式将结果返回到 JavaScript 端。

于 2011-12-10T02:18:33.220 回答