8

我们的项目在多个平台上使用了一些 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();
   }
 }

我应该注意到(从注释掉的代码中可以看出)我之前检查过 void_thread_info == void_this 因为我只检查线程的当前 thread_info 正在杀死自己的情况。我还看到 get_current_thread_data 返回的值非零且与“this”不同的情况,这真的很奇怪。

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

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

在运行时,我遇到了一些非常奇怪的异常,说我关于虚拟函数表或类似的东西 - 我不记得了。我决定它试图为这个对象类型调用“==”并且对此不满意,所以我如上所述重写,将转换为 void * 作为单独的代码行。这本身对我来说很可疑。我不是一个急于指责编译器的人,但是......

我还应该注意,当我们确实捕捉到这种情况发生的陷阱时,我们看到 ~shared_count 的析构函数在 Xcode 源代码的堆栈中连续出现两次。很奇怪。我们试图查看拆解,但无法从中得到太多。

再次 - 看起来这始终是 shared_count 的结果,它似乎由拥有 thread_info 的 shared_ptr 拥有,过早地达到零。

更新:似乎有可能进入达到上述陷阱的情况而不会造成任何伤害。自从解决了这个问题(见答案)我已经看到它发生了,但总是在 thread_info->run() 完成执行之后。还不明白如何......但它正在工作。

一些附加信息:

我应该注意到,通常用于为 IOS 编译 boost 的来自 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

这暗示了在 boost 中为 arm 处理器使用替代实现,这似乎也直接解决了这个问题: 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" );

但无论如何,这个文件中有 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: 错误:指令需要当前未启用的 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 脚本选择了不正确的 boost 标志来解决这个问题——而不是BOOST_SP_USE_PTHREADS(以及那里的另一个标志BOOST_AC_USE_PTHREADS),结果证明 IOS 上需要的是BOOST_SP_USE_SPINLOCK. 这最终给出了与问题中提到的 std::thread 问题中使用的几乎相同的解决方案。

如果您正在为任何使用 ARM 7 的现代 IOS 设备进行编译,但使用的是较旧的 bo​​ost(我们使用的是 1.48),您需要从较新的 boost(如 1.52)复制文件 spinlock_gcc_arm.hpp。该文件是针对不同 arm 架构的#ifdef,但我不清楚它正在寻找的定义是在 IOS 编译环境中使用脚本定义的。因此,您可以编辑文件(暴力但有效)或花一些时间来弄清楚如何使文件整洁和正确。

无论如何,您可能需要在问题中插入我在上面所做的额外汇编指令:“it ne; \n”我还没有回去看看我是否可以删除它,因为我有我的编译环境工作问题.

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

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

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

要像这样编译 boost,我认为您只需将 -mno-thumb 添加到脚本中的 EXTRA_CPP_FLAGS 即可。(我实验的时候直接修改了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 文件。在 boost 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 回答