gpt4 book ai didi

c++ - Boost 线程 : in IOS, thread_info 对象正在线程完成执行之前被破坏

转载 作者:可可西里 更新时间:2023-11-01 03:09:35 27 4
gpt4 key购买 nike

我们的项目在多个平台上使用了一些 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 返回的值不为零的情况,并且
不同于“这个”,这真的很奇怪。

同样,当我第一次编写该版本的代码时,我写道:
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 附带的版本使用过时的臂组件。
我从 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" );

但无论如何,该文件中有 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"

有任何想法吗??

最佳答案

想通了这一点。事实证明,我在问题中提到的 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 指令集。知道指令集问题的 boost 代码会检查您是否有 ARM 已启用但未启用 拇指 , 但在 IOS 中默认为 拇指 正在。

让编译器生成非拇指 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 线程用户。我希望能更新 boost.sh IOS 脚本上的各种变体。我打算稍后再添加一些指向此答案的链接。

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

享受!

关于c++ - Boost 线程 : in IOS, thread_info 对象正在线程完成执行之前被破坏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14692802/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com