提升线程:在IOS中,thread_info对象在线程完成执行之前被破坏

And*_*ein 8 c++ multithreading boost ios

我们的项目在几个平台上使用了几个boost 1.48库,包括Windows,Mac,Android和IOS.我们能够在使用IOS时始终让项目的IOS版本崩溃(非常可靠但可靠),并且从我们的调查中我们看到〜thread_data_base在线程的thread_info上被调用,而其线程仍然在运行.

这似乎是由于智能指针达到零计数而发生的,即使它显然仍在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();
   }
 }
Run Code Online (Sandbox Code Playgroud)

我应该注意(从已注释掉的代码中看到)我之前检查过void_thread_info == void_this,因为我只检查线程的当前thread_info是否自杀的情况.我也看到过get_current_thread_data返回的值非零且与"this"不同的情况,这真的很奇怪.

当我第一次编写该版本的代码时,我写道:

if (((void*)thread_info) == ((void*)this)) 
Run Code Online (Sandbox Code Playgroud)

在运行时,我得到了一些非常奇怪的异常,我说了一些虚拟功能表或类似的东西 - 我不记得了.我决定尝试为这个对象类型调用"=="并且对此不满意,所以我重写了上面的内容,将转换为void*作为单独的代码行.这本身对我很怀疑.我不是一个急于责怪编译器的人,但......

我还应该注意到,当我们发现陷阱发生这种情况时,我们看到~cred_count的析构函数在Xcode源代码中连续出现两次.非常双重.我们试图查看反汇编,但无法从中得到很多.

再次 - 看起来这总是shared_count的结果,而shared_count似乎由拥有thread_info的shared_ptr拥有得太早.

更新:似乎有可能进入上述陷阱的情况,而不会造成任何伤害.自从修复问题(参见答案)以来,我已经看到它发生了,但总是在thread_info-> run()完成执行之后.还不知道怎么......但是它有效.

一些额外的信息:

我应该注意,来自Pete Goodliffe的boost.sh(以及其他人修改过的)通常用于编译IOS的boost,在标题中有以下注释:

: ${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
Run Code Online (Sandbox Code Playgroud)

我使用那些标志,但无济于事.

我发现以下内容非常诱人 - 看起来他们有同样的问题 std::thread:

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

这暗示了在arm处理器中使用boost内部的替代实现,这似乎也直接解决了这个问题: spinlock_gcc_arm.hpp

boost 1.48附带的版本使用过时的臂组件.我从boost 1.52中获取了更新版本,但是我在编译时遇到了问题.我收到以下错误:谓词指令必须在IT块中

我在这里找到了对该指令的类似用法的引用: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" );
Run Code Online (Sandbox Code Playgroud)

但无论如何,在这个文件中有ifdef寻找arm架构,这在我的环境中没有这样定义.在我编辑文件以便只剩下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"
Run Code Online (Sandbox Code Playgroud)

有任何想法吗??

And*_*ein 8

想出这个.事实证明,我在问题中提到的boost.sh脚本选择了不正确的boost标志来解决这个问题 - 而不是BOOST_SP_USE_PTHREADS(以及其中的另一个标志BOOST_AC_USE_PTHREADS),事实证明IOS需要的是BOOST_SP_USE_SPINLOCK.这最终给出了问题中提到的std :: thread问题中使用的相同解决方案.

如果您正在编译使用ARM 7的任何现代IOS设备,但使用较旧的提升(我们使用的是1.48),则需要从最新的提升(如1.52)复制文件spinlock_gcc_arm.hpp.该文件对于不同的arm架构是#ifdef'd,但我不清楚它所寻找的定义是在IOS编译环境中使用脚本定义的.所以你可以编辑文件(暴力但有效)或投入一些时间来弄清楚如何使这个整洁和正确.

在任何情况下,您可能需要在问题中插入我在上面执行的额外汇编指令:"它ne; \n"我还没有回去看看我是否可以删除现在我的编译环境工作问题.

但是,我们还没有完成.如上所述,此选项的boost中使用的代码包括ARM汇编语言指令.ARM芯片支持两个不能在给定模块中混合的指令集(不确定范围,但显然逐个文件在编译时是可接受的粒度).boost中用于此锁定的指令包括非Thumb指令,但默认情况下IOS使用Thumb指令集.升级代码,知道指令集问题,检查你是否已启用arm但不是拇指,但默认情况下在IOS中,拇指已启用.

让编译器生成非拇指ARM代码取决于您在IOS中使用的编译器 - Apple的LLVM或LLVM GCC.GCC已弃用,Apple的LLVM是您使用XCode时的默认设置.

对于默认的Clang + Apple LLVM 4.1,您需要使用-mno-thumb标志进行编译.您的IOS应用程序中使用智能指针的任何升级部分的任何文件也必须使用-mno-thumb进行编译.

要像这样编译boost,我想你可以在脚本中的EXTRA_CPP_FLAGS中添加-mno-thumb.(我在试验时直接修改了user-config.jam,还没有回去清理.)

对于您的应用程序,在Xcode中您需要选择目标,然后进入Build Phases选项卡,然后选择Compile sources.在那里你可以选择添加编译标志,因此对于每个相关文件(包括boost),添加-mno-thumb标志.您也可以在project.pbxproj中直接执行此操作,其中每个文件都有

settings = { COMPILER_FLAGS = ""; };   
Run Code Online (Sandbox Code Playgroud)

你只需将其更改为

settings = { COMPILER_FLAGS = "-mno-thumb"; }; 
Run Code Online (Sandbox Code Playgroud)

但还有一点.您还必须修改tools/build/v2/tools目录中的darwin.jam文件.在boost 1.48中,有一个代码表示:

    case arm :
    {
        options = -arch armv6;
    }
Run Code Online (Sandbox Code Playgroud)

这必须修改为

    case arm :
    {
        options = -arch armv7 ;
    }        
Run Code Online (Sandbox Code Playgroud)

最后,在boost.sh脚本中,在函数中writeBjamUserConfig(),您应该删除对-arch armv6的引用.

如果有人知道如何更普遍和干净地做到这一点,我相信我们一切都会受益.现在,这是我所要做的,我希望这将有助于其他IOS提升线程用户.我希望boost.sh IOS脚本的各种变体都会更新.我打算稍后再添加一些链接到这个答案.

更新:有关描述处理器级别问题的精彩文章,
请参见此处:http: //preshing.com/20121019/this-is-why-they-call-it-a-weakly-ordered-cpu

请享用!