_WebTryThreadLock(bool) 例外の原因
Xamarin.iOSで開発中のアプリで、特定の操作で100%クラッシュするという報告をもらった。
操作としてはUIButtonなボタンとUITapGestureRecognizerを追加したUIViewと仮想キーボード上のキーの3カ所を同時に数回タップするというもの。発見した人凄い。
エラーの詳細
エラー発生時に得られる情報は以下となる。エラー毎にログの内容は多少異なるが、特長としては必ずWebThreadという名前のスレッド内で例外が起きる。
コンソールログ
bool _WebTryThreadLock(bool), 0x3ca430: Multiple locks on web thread not allowed! Please file a bug. Crashing now... 以下略
デバイスログ
(略) Exception Type: EXC_BAD_ACCESS (SIGABRT) Exception Codes: KERN_INVALID_ADDRESS at 0xbbadbeef Crashed Thread: 2 Thread 0 name: Dispatch queue: com.apple.main-thread Thread 0: 0 libsystem_kernel.dylib 0x30b0d004 0x30b0c000 + 4100 1 libsystem_kernel.dylib 0x30b0d1fa 0x30b0c000 + 4602 2 CoreFoundation 0x37e903ec 0x37e03000 + 578540 3 CoreFoundation 0x37e8f0ea 0x37e03000 + 573674 4 CoreFoundation 0x37e1249e 0x37e03000 + 62622 5 CoreFoundation 0x37e12366 0x37e03000 + 62310 6 GraphicsServices 0x362f5432 0x362f1000 + 17458 7 UIKit 0x30cc3cce 0x30c92000 + 203982 8 MYAPP 0x00189228 0x1000 + 1606184 9 MYAPP 0x0013d158 0x1000 + 1294680 10 MYAPP 0x0007e7c8 0x1000 + 513992 11 MYAPP 0x0073c6c4 0x1000 + 7583428 12 MYAPP 0x00f14168 0x1000 + 15806824 13 MYAPP 0x00f83494 0x1000 + 16262292 14 MYAPP 0x00f861b4 0x1000 + 16273844 15 MYAPP 0x00f86408 0x1000 + 16274440 16 MYAPP 0x00f171d0 0x1000 + 15819216 17 MYAPP 0x00f119be 0x1000 + 15796670 18 MYAPP 0x000021b0 0x1000 + 4528 Thread 1 name: Dispatch queue: com.apple.libdispatch-manager Thread 1: 0 libsystem_kernel.dylib 0x30b0d3a8 0x30b0c000 + 5032 1 libdispatch.dylib 0x347e7f04 0x347e4000 + 16132 2 libdispatch.dylib 0x347e7c22 0x347e4000 + 15394 Thread 2 name: WebThread Thread 2 Crashed: 0 libsystem_kernel.dylib 0x30b1d32c 0x30b0c000 + 70444 1 libsystem_c.dylib 0x330ae208 0x33061000 + 315912 2 libsystem_c.dylib 0x330a7298 0x33061000 + 287384 3 MYAPP 0x00f219aa 0x1000 + 15862186 4 MYAPP 0x00f12bb4 0x1000 + 15801268 5 libsystem_c.dylib 0x330b87e6 0x33061000 + 358374 6 JavaScriptCore 0x363e6fe8 0x36315000 + 860136 7 WebCore 0x311f77ec 0x311f1000 + 26604 8 CoreFoundation 0x37e90b14 0x37e03000 + 580372 9 CoreFoundation 0x37e8ed50 0x37e03000 + 572752 10 CoreFoundation 0x37e8f02a 0x37e03000 + 573482 11 CoreFoundation 0x37e1249e 0x37e03000 + 62622 12 CoreFoundation 0x37e12366 0x37e03000 + 62310 13 WebCore 0x3129ac9c 0x311f1000 + 695452 14 libsystem_c.dylib 0x3306f72e 0x33061000 + 59182 15 libsystem_c.dylib 0x3306f5e8 0x33061000 + 58856 (以下略)
原因究明
WebTryThreadLockキーワード
WebTryThreadLockというキーワードから検索出来る情報は「UIスレッド以外からのUIを操作した」というのが一番多い。しかしこの場合コンソールログに
Tried to obtain the web lock from a thread other than the main thread or the web thread. This may be a result of calling to UIKit from a secondary thread. Crashing now...
と表示されるので今回のエラーとは関係がない模様。そしてなによりこのアプリはマルチスレッドを使っていない。
WebThreadキーワード
iOSでは自動的に作られるスレッドなんだろうか?自分で制御しているスレッドではないのでどうにもならない。またこのキーワードからは何も得られなかった。
XCodeでデバッグ
Xamarin Studioからデバッグした場合、例外の発生と共にデバッグが終わってしまう。なのでXCodeでデバッグしてみた。
例外発生付近のアセンブラのコードをみているとスレッドロックが失敗して0x000000000なアドレスに0xbbadbeefというデータを書き込んでいた(Assert的処理だろうか)。不正なアドレスに書き込みしているのでEXC_BAD_ACCESS。0xbbadbeefはBad Beef(悪い牛肉)という意味だろうか。
つまりデバイスログにあるEXC_BAD_ACCESSは2次的なものであって、直接の例外理由ではないことが判った。
ではどうしてスレッドロックがおかしくなったのか?
さすがにこれ以上アセンブラレベルで解析するのは辛いので諦めた。
コードを精査
手間なので後回しにしていた「書いてあるコードを一行ずつコメントアウトして逐一動作をチェック」をおこなってようやくエラーがでなくなった。
場所はUIButtonのTouchUpInsideイベントを使っている箇所だった。
結局具体的な原因は分からないままである。
対処療法的になるがコード中のResponderChain系イベントを使っている箇所を、全てUIGestureRecognizerに置き換えることでエラーがでなくなった。