私たちのプロジェクトでは、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"
何か案は??