8

私たちのプロジェクトでは、Windows、Mac、Android、IOS などのいくつかのプラットフォームで、boost 1.48 ライブラリをいくつか使用しています。IOS を使用すると、一貫して IOS バージョンのプロジェクトをクラッシュさせることができます (自明ではありませんが、確実に)。調査の結果、スレッドがまだ実行されている間にスレッドの thread_info で ~thread_data_base が呼び出されていることがわかります。

これは、スマート ポインターがゼロ カウントに達した結果として発生するようですが、スマート ポインターを作成し、要求された関数をスレッドで実行する thread_proxy 関数の範囲内にあることは明らかです。これはさまざまなケースで発生するようです。コール スタックはクラッシュ間で同一ではありませんが、一般的ないくつかのバリエーションがあります。

明確にするために、これには多くの場合、数百のスレッドを作成するコードを実行する必要がありますが、同時に実行されるスレッドは約 30 を超えることはありません。私は「運が良かった」ので、実行の非常に早い段階でそれを手に入れましたが、それはまれです. 実際にコードを現行犯でキャッチするデストラクタのバージョンを作成しました。

libs/thread/src/pthread/thread.cpp:

thread_data_base::~thread_data_base()
 {
   boost::detail::thread_data_base* const thread_info=detail::get_current_thread_data();
   void *void_thread_info = (void *) thread_info;
   void *void_this = (void *) this;
   // is somebody destructing the thread_data other than its own thread?
   // (remember that its own which should no longer point to it anyway,
   // because of the call to detail::set_current_thread_data(0) in thread_proxy)
   if (void_thread_info) { //  == void_this) {
     __builtin_trap();
   }
 }

(コメントアウトされたコードからわかるように)以前に void_thread_info == void_this であることを確認したことに注意してください。これは、スレッドの現在の thread_info が自分自身を強制終了している場合のみをチェックしていたためです。get_current_thread_data によって返される値がゼロ以外で、「this」とは異なるケースも見てきましたが、これは非常に奇妙です。

また、そのバージョンのコードを最初に書いたとき、次のように書きました。

if (((void*)thread_info) == ((void*)this)) 

実行時に、仮想関数テーブルまたはそのようなものについて何かを言った非常に奇妙な例外が発生しました-覚えていません。私は、このオブジェクト タイプに対して "==" を呼び出そうとしていて、それが気に入らなかったと判断したので、上記のように書き直して、void * への変換を別のコード行にしました。それ自体が私にはかなり疑わしいです。私は急いでコンパイラのせいにするつもりはありませんが...

また、このトラップの発生をキャッチしたときに、Xcode ソースのスタックに ~shared_count のデストラクタが 2 回連続して表示されたことにも注意してください。非常に二重奇妙です。分解を調べようとしましたが、ほとんどわかりませんでした。

繰り返しますが、これは常に、thread_info を所有する shared_ptr が所有しているように見える shared_count があまりにも早くゼロに達した結果であるように見えます。

更新: 状況が害を及ぼすことなく、上記のトラップに到達する状況に入る可能性があるようです。問題を修正して以来(回答を参照)、それが発生するのを見てきましたが、常に thread_info->run() の実行が終了した後です。方法はまだわかりません...しかし、それは機能しています。

いくつかの追加情報:

IOS のブーストをコンパイルするために一般的に使用される Pete Goodliffe の (および他の人によって変更された) boost.sh には、ヘッダーに次の注記があることに注意してください。

: ${EXTRA_CPPFLAGS:="-DBOOST_AC_USE_PTHREADS -DBOOST_SP_USE_PTHREADS"}
# The EXTRA_CPPFLAGS definition works around a thread race issue in
# shared_ptr. I encountered this historically and have not verified that
# the fix is no longer required. Without using the posix thread primitives
# an invalid compare-and-swap ARM instruction (non-thread-safe) was used for the
# shared_ptr use count causing nasty and subtle bugs.
#
# Should perhaps also consider/use instead: -BOOST_SP_USE_PTHREADS

私はそれらのフラグを使用しますが、役に立ちません。

私は非常に興味をそそる以下を見つけました - 彼らは同じ問題を抱えていたようですstd::thread:

http://llvm.org/bugs/show_bug.cgi?format=multiple&id=12730

これは、この問題に直接対処しているように見えるアーム プロセッサのブースト内で別の実装を使用することを示唆していました。 spinlock_gcc_arm.hpp

ブースト 1.48 に含まれているバージョンは、古いアーム アセンブリを使用しています。ブースト 1.52 から更新されたバージョンを取得しましたが、コンパイルに問題があります。次のエラーが表示されます: predicated instruction must be in IT block

ここで、この命令の同様の使用法と思われるものへの参照を見つけました: https://zeromq.jira.com/browse/LIBZMQ-414

次のようにコードを変更することで、同じアイデアを使用して 1.52 コードをコンパイルすることができました (適切な IT 命令を挿入しました)。

__asm__ __volatile__(
 "ldrex %0, [%2]; \n"
 "cmp %0, %1; \n"
 "it ne; \n"
 "strexne %0, %1, [%2]; \n"
 BOOST_SP_ARM_BARRIER :
 "=&r"( r ): // outputs
 "r"( 1 ), "r"( &v_ ): // inputs
 "memory", "cc" );

しかし、いずれにせよ、私の環境ではそのように定義されていないアームアーキテクチャを探すifdefがこのファイルにあります。ファイルを単純に編集して ARM 7 コードのみを残した後、コンパイラは BOOST_SP_ARM_BARRIER の定義について不平を言います。

./boost/smart_ptr/detail/spinlock.hpp:35 から含まれるファイル: ./boost/smart_ptr/detail/spinlock_gcc_arm.hpp:39:13: エラー: 命令には現在有効になっていない CPU 機能が必要です BOOST_SP_ARM_BARRIER : ^ ./boost /smart_ptr/detail/spinlock_gcc_arm.hpp:13:32: 注: マクロ 'BOOST_SP_ARM_BARRIER' から展開

# define BOOST_SP_ARM_BARRIER "dmb"

何か案は??

4

2 に答える 2

8

これを理解しました。質問で言及したboost.shスクリプトは、この問題に対処するために誤ったブーストフラグを選択したことが判明しました.IOSBOOST_SP_USE_PTHREADSBOOST_AC_USE_PTHREADS必要なものはBOOST_SP_USE_SPINLOCK. これにより、質問で言及されている std::thread の問題で使用されているものとほとんど同じソリューションが得られます。

ARM 7 を使用する最新の IOS デバイス用にコンパイルしているが、古いブースト (1.48 を使用) を使用している場合は、新しいブースト (1.52 など) からファイル spinlock_gcc_arm.hpp をコピーする必要があります。そのファイルは、さまざまなアーム アーキテクチャに対して #ifdef されていますが、探している定義がスクリプトを使用して IOS コンパイル環境で定義されているかどうかはわかりません。したがって、ファイルを編集するか (暴力的ですが効果的)、これを整理して修正する方法を見つけるために時間を費やすことができます。

いずれにせよ、上記の質問で行った追加のアセンブリ命令を挿入する必要がある場合があります。 .

しかし、まだ終わりではありません。このオプションのブーストで使用されるコードには、説明したように、ARM アセンブリ言語命令が含まれています。ARM チップは、特定のモジュールに混在させることができない 2 つの命令セットをサポートしています (スコープについては不明ですが、コンパイル時にファイルごとに許容できる粒度であることは明らかです)。このロックのためにブーストで使用される命令には非 Thumb 命令が含まれますが、IOS はデフォルトで Thumb 命令セットを使用します。ブースト コードは、命令セットの問題を認識しており、 armが有効になっているが thumb が有効になっていないことを確認しますが、IOS のデフォルトでは、thumbがオンになっています。

コンパイラに Thumb 以外の ARM コードを生成させる方法は、IOS で使用しているコンパイラ (Apple の LLVM または LLVM GCC) によって異なります。GCC は非推奨であり、XCode を使用する場合は Apple の LLVM がデフォルトです。

デフォルトの Clang + Apple LLVM 4.1 の場合、-mno-thumb フラグを使用してコンパイルする必要があります。また、スマート ポインターを使用するブーストの一部を使用する IOS アプリ内のファイルも、-mno-thumb を使用してコンパイルする必要があります。

このようにブーストをコンパイルするには、スクリプトの EXTRA_CPP_FLAGS に -mno-thumb を追加するだけでよいと思います。(実験中に user-config.jam を直接変更しましたが、まだクリーンアップに戻っていません。)

アプリの場合、Xcode でターゲットを選択し、[Build Phases] タブに移動して、[Compile sources] を選択する必要があります。コンパイル フラグを追加するオプションがあるため、関連するファイル (boost を含む) ごとに -mno-thumb フラグを追加します。これは、各ファイルがある project.pbxproj で直接行うこともできます

settings = { COMPILER_FLAGS = ""; };   

これを次のように変更するだけです

settings = { COMPILER_FLAGS = "-mno-thumb"; }; 

しかし、もう少しあります。tools/build/v2/tools ディレクトリにある darwin.jam ファイルも変更する必要があります。ブースト 1.48 には、次のようなコードがあります。

    case arm :
    {
        options = -arch armv6;
    }

これは次のように変更する必要があります

    case arm :
    {
        options = -arch armv7 ;
    }        

最後に、boost.sh スクリプトの functionwriteBjamUserConfig()で、-arch armv6 への参照を削除する必要があります。

誰かがこれをもう少し一般的かつきれいに行う方法を知っていれば、私たち全員が恩恵を受けると確信しています。今のところ、これが私がたどり着いたところです。これが他の IOS ブースト スレッド ユーザーに役立つことを願っています。boost.sh IOS スクリプトのさまざまなバリアントが更新されることを願っています。後でこの回答にさらにリンクを追加する予定です。

更新:プロセッサ レベルの問題を説明する素晴らしい記事については、
こちらを参照してください: http://preshing.com/20121019/this-is-why-they-call-it-a-weakly-ordered-cpu

楽しみ!

于 2013-02-06T20:53:36.140 に答える
0

iOSプラットフォームでboost.asio、boost.thread、boost.smart_ptrなどを使用していますが、リリースモードで実行するとアプリが常にクラッシュし、シグナルsigabrtがスローされます。クラッシュ コール スタックは次のとおりです。

__stack_chk_fail
boost::asio::detail::completion_handle
boost::asio::detail::task_ios_service_operation::complete
boost::asio::detail::task_io_service::do_run_one
boost::asio::detail::task_ios_service::run
boost::asio::io_service::run
![when create a asio work with creating new thread and io_service][1]

問題を解決しようとしたときに、次の記事を見つけました。

[boost-thread-threads-not-starting-on-the-iphone-ipad-in-release-build][2]

[The issue of spin_lock and thumb on iOS][3]

次に、プロジェクトにコンパイル フラグを追加しようとする-mno-thumbと、リリース モードで発生した問題はなくなりました。

ただし、新しいバグが発生します: EXC_ARM_DA_ALIGN、ネットワーク データをホスト エンディアンに変換しようとした場所でクラッシュしました。

[この記事][4] で述べられているように、ARM命令は、メモリ データをアラインする必要があることを厳密に規定しています。

記事に従って、ポインターから直接変換するのではなく、データ変換に[Exc_arm_da_align][5]使用して修正します。memcpy

  [1]: http://i.stack.imgur.com/3ijF4.png
  [2]: http://stackoverflow.com/questions/4201262/boost-thread-threads-not-starting-on-the-iphone-ipad-in-release-builds/4245821#4245821
  [3]: http://groups.google.com/group/boost-list/browse_thread/thread/7dc1e80659182ab3
  [4]: https://brewx.qualcomm.com/bws/content/gi/common/appseng/en/knowledgebase/docs/kb95.html
  [5]: http://www.cnblogs.com/unionfind/archive/2013/02/25/2932262.html
于 2013-08-17T08:12:04.143 に答える