上回處理過WebSocket導致Mac Safari當機,修復後狀況明顯改善,但有時仍重新載入網頁時Safari仍會當掉,而錯誤訊息模糊許多:
Process: com.apple.WebKit.WebContent [1527]
Path: /System/Library/PrivateFrameworks/WebKit2.framework/Versions/A/XPCServices
/com.apple.WebKit.WebContent.xpc/Contents/MacOS/com.apple.WebKit.WebContent
Identifier: com.apple.WebKit.WebContent
Version: 9537 (9537.71)
Build Info: WebKit2-7537071000000000~3
Code Type: X86-64 (Native)
Parent Process: ??? [1]
Responsible: Safari [1109]
User ID: 501Date/Time: 2014-04-11 17:09:51.606 +0800
OS Version: Mac OS X 10.9 (13A603)
Report Version: 11
Anonymous UUID: F8190022-15F6-1032-03AC-3B2053B998AE
Sleep/Wake UUID: EE71F795-BA3F-43AE-A1E6-143B4B4AFEBDCrashed Thread: 0 Dispatch queue: com.apple.main-thread
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: EXC_I386_GPFLTApplication Specific Information:
Bundle controller class:
BrowserBundleController
Process Model:
Multiple Web ProcessesThread 0 Crashed:: Dispatch queue: com.apple.main-thread
0 com.apple.WebCore 0x00007fff9190523a void std::__1::__push_heap_front<WebCore::TimerHeapLessThanFunction&, WebCore::TimerHeapIterator>(WebCore::TimerHeapIterator, WebCore::TimerHeapIterator, WebCore::TimerHeapLessThanFunction&, std::__1::iterator_traits<WebCore::TimerHeapIterator>::difference_type) + 186
1 com.apple.WebCore 0x00007fff918790c7 WebCore::TimerBase::heapPopMin() + 135
2 com.apple.WebCore 0x00007fff918783c8 WebCore::TimerBase::updateHeapIfNeeded(double) + 280
3 com.apple.WebCore 0x00007fff91875e00 WebCore::TimerBase::setNextFireTime(double) + 192
4 com.apple.WebCore 0x00007fff91907a1b WebCore::FrameView::layout(bool) + 107
5 com.apple.WebCore 0x00007fff91a0b681
(...以下省略...)
EXC_I386_GPFLT是所謂一般性失敗,EXC_BAD_ACCESS則程式存取範圍外的記憶體區塊,是程式錯亂的標準症狀,二者都沒什麼營養。唯一有價值的線索是Callstack有Timer字樣,讓我懷疑跟setTimer、setInterval有關,雖然這樣就把瀏覽器搞掛不合邏輯,我仍試著停用網頁的setTimer、setInterval碰碰運氣,反覆測試均不見效果,射手開始茫然...
瞎試一陣子後,發現重大情資 -- 網站裝在四個不同測試機上,只有兩台(A,B)會導致Safari當機,另外兩台測試台(C,D)從沒把Safari搞掛過! A,B,C是Windows 2012,D是"Windows 2008"。靈機一動,該不會又跟WebSocket有關吧? 檢查後證實,測試機C雖是Windows 2012因IIS設定問題,跟D一樣沒啟用WebSocket,而A,B有成功啟用WebSocket。這點意外成為破案關鍵,Safari當機的原因,繞了一大圈,最後又回到WebSocket上。
幾經嘗試,最後我找到的解法是在window.onbeforeunload事件加入一小段程式,在離開網頁前主動關閉SignalR連線。
window.onbeforeunload = function () {
try {
if ($.connection && $.connection.hub) {
$.connection.hub.stop();
}
}
catch (ex) { }
finally { }
}
從此之後,SignalR網頁就跟Safari過著幸福快樂的日子了... (希望如此啦)