【问题标题】:Boost threads: in IOS, thread_info object is being destructed before the thread finishes executing提升线程:在 IOS 中,thread_info 对象在线程完成执行之前被破坏
【发布时间】:2013-01-19 12:14:58
【问题描述】:

我们的项目在多个平台上使用了一些 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() 完成执行之后。还不明白如何......但它正在工作。

一些附加信息:

我应该注意到 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

我使用这些标志,但无济于事。

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

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

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

boost 1.48 包含的版本使用过时的 arm 组件。 我从 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"

有什么想法吗??

【问题讨论】:

  • Shared_ptrs 提前达到零 use_count 可能有两个我能想到的原因:1 有人弄乱了 use_count,例如通过访问无主内存。我认为这不太可能,因为这意味着暴力访问只是将 use_count 减少了一次或几次,只是为了使其介于 0 和实际 use_count 之间。 2 通过违反 shared_ptrs 来弄乱使用计数,例如通过绕过它们的构造/复制机制。我的第一个猜测是 memcopy'ing 一个 shared_ptr。也许添加一个保存的 this 指向 shared_ptr 的指针并在销毁时检查它。

标签: c++ ios multithreading boost


【解决方案1】:

想通了。事实证明,我在问题中提到的 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 编译环境中使用脚本定义的。因此,您可以编辑文件(暴力但有效)或花一些时间来弄清楚如何使其整洁和正确。

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

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

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

对于默认的 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 脚本中,在函数 writeBjamUserConfig() 中,您应该删除对 -arch armv6 的引用。

如果有人知道如何更一般、更干净地执行此操作,我相信我们都会受益。目前,这就是我已经到达的地方,我希望这将有助于其他 IOS 提升线程用户。我希望对 boost.sh IOS 脚本的各种变体进行更新。我计划稍后在此答案中添加更多链接。

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

享受吧!

【讨论】:

    【解决方案2】:

    我在iOS平台上使用boost.asio、boost.thread、boost.smart_ptr等,应用在release模式下运行总是崩溃,抛出signal 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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-11-02
      • 2016-02-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多