しばやん雑記

Azure とメイドさんが大好きなフリーランスのプログラマーのブログ

Xamarin.iOS と TestFlight を組み合わせた時に復帰のタイミングで落ちて困った話

開発中の Xamarin.iOS を使ったアプリに TestFlight SDK を組み込んでテスト配布していたところ、端末がスリープ後に復帰させたタイミングでクラッシュするという現象に悩まされました。

ちなみに、以下が TestFlight 側で確認できたクラッシュログになります。

f:id:shiba-yan:20140812103402p:plain

明らかに TestFlight SDK が持っている TFLog で落ちているようにしか見えないので、とりあえず TestFlight SDK のバージョンを 3.0.0 から 3.2.0 へアップデートしました。バインディングライブラリのビルド方法に関しては Xamarin.iOS で作ったアプリに TestFlight SDK を組み込む方法 - しばやん雑記 を参照してください。

しかし、これだけだと自分の環境では改善が見られなかったため、普段使っている TestFlight.TakeOff メソッドではなく、スレッドセーフ版のメソッドを使うようにしました。

public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
    TestFlight.TakeOffThreadSafe("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx");
}

TakeOff を TakeOffThreadSafe に修正するだけで、スレッドセーフ版になります。ここまで行うことで TFLog での謎クラッシュは発生しなくなりました。

TestFlight のバインディングライブラリは、何をやっているのか興味を持ったので少し調べてみると、基本的には半自動生成が行われたようなコードになっていましたが、スレッドセーフ版は少し異なっていました。

public static void TakeOffThreadSafe(String applicationToken)
{
    IntPtr sigbus = Marshal.AllocHGlobal (512);
    IntPtr sigsegv = Marshal.AllocHGlobal (512);
    IntPtr sigpipe = Marshal.AllocHGlobal (512);

    // Store Mono SIGSEGV and SIGBUS handlers
    sigaction (Signal.SIGBUS, IntPtr.Zero, sigbus);
    sigaction (Signal.SIGSEGV, IntPtr.Zero, sigsegv);
    sigaction (Signal.SIGPIPE, IntPtr.Zero, sigpipe);

    // Enable crash reporting libraries
    //MonoTouch.TestFlight.TestFlight.SetDeviceIdentifier(UIDevice.CurrentDevice.UniqueIdentifier);
    //MonoTouch.TestFlight.TestFlight.SetDeviceIdentifier(MonoTouch.AdSupport.ASIdentifierManager.SharedManager.AdvertisingIdentifier.ToString());
    TestFlight.TakeOff(applicationToken);

    // Restore Mono SIGSEGV and SIGBUS handlers
    sigaction (Signal.SIGBUS, sigbus, IntPtr.Zero);
    sigaction (Signal.SIGSEGV, sigsegv, IntPtr.Zero);
    sigaction (Signal.SIGPIPE, sigpipe, IntPtr.Zero);

    Marshal.FreeHGlobal (sigbus);
    Marshal.FreeHGlobal (sigsegv);
    Marshal.FreeHGlobal (sigpipe);
}
monotouch-bindings/testflight-threadsafe.cs at master · mono/monotouch-bindings · GitHub

残念ながら Linux のシステムコール周りには詳しくないのですが、コメントから読み取るに sigaction を TestFlight の TakeOff メソッドを呼び出す前に Mono が既にセットしているハンドラを退避させて、TakeOff メソッド呼び出し後に元に戻すという処理をしているだけのようです。

これで何故スレッドセーフになるのか、いまいち理解が追い付いていないですが、とりあえず TFLog でのクラッシュが改善されたので良いことにします。

Using TestFlight.dll with your own iOS App

Simply add TestFlight.dll to your project's References in MonoDevelop and you are good to go!

To use the thread safe TakeOff methode. Change TestFlight.TakeOff(token) to TestFlight.TakeOffThreadSafe(token) This works much better with MonoTouch. Because it doesn't override the Build-in (.NET) Error handling.

monotouch-bindings/README.md at master · mono/monotouch-bindings · GitHub

後からよく見たら、公式にスレッドセーフ版の TakeOffThreadSafe を使った方が良いと書かれていました。さっきのシステムコールをいろいろ呼び出している部分は、Mono のエラーハンドリングをオーバーライドさせないためのコードだったようですね。