You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Boost intrusive_ptr时触发TSAN线程竞态报错的问题咨询

问题判定

这个TSAN报错属于误报,不存在真实的数据竞态。
你的引用计数实现逻辑是完全正确的:引用计数减1时使用memory_order_release语义,保证对象所有前置读写操作不会被重排到引用计数修改之后;检测到引用计数归零时加的memory_order_acquire屏障,会同步其他线程所有release语义的写入,确保删除对象前所有对对象的访问都已经完成,不存在访问已释放内存的风险。
换成std::shared_ptr后报错消失,本质是TSAN对标准库原子实现有专门的插桩识别,不会误判,不代表boost::intrusive_ptr的实现有问题。

报错根因

GCC 7.3.1版本内置的ThreadSanitizer无法正确识别Boost 1.72.0版本中boost::atomic的gcc_atomic底层实现:TSAN没有把boost::atomic<int>::fetch_sub识别为原子操作,反而当成了普通的4字节内存写入,因此判定后续主线程执行delete时的8字节内存写入和这个操作存在竞态,属于工具检测的漏识别问题,不是代码逻辑问题。

解决方案

你可以根据实际场景选以下任意一种方案消除误报:

  • 把Shared结构体里的boost::atomic<int>替换为std::atomic<int>,内存序枚举换成std命名空间下的对应值,TSAN可以原生识别std::atomic的原子操作,不会再误报
  • 升级GCC版本到9及以上,高版本GCC配套的TSAN对Boost.Atomic的适配更完善,这类误报不会再出现
  • 编译时添加宏定义-DBOOST_ATOMIC_FORCE_LIBATOMIC,强制Boost.Atomic调用libatomic的标准原子接口,TSAN可以正确识别这类接口的原子属性
  • 编写TSAN抑制文件,添加规则忽略intrusive_ptr_release函数内delete操作和refcount_访问的竞态报告

相关环境与复现信息

  • 环境版本:GCC 7.3.1,Boost 1.72.0
  • 编译命令:
    g++ -ggdb -I /usr/local/boost_1_72_0 -O3 -fsanitize=thread TSan_Intr_Ptr.cpp -lpthread
  • 复现代码:
#include <boost/smart_ptr/intrusive_ptr.hpp>
#include <boost/smart_ptr/detail/spinlock.hpp>
#include <boost/atomic.hpp>
#include <thread>
#include <iostream>
#include <vector>
#include <atomic>
#include <unistd.h>
using namespace std;

struct Shared
{
        mutable boost::atomic<int> refcount_;

        //From Boost official atomic usage example
        friend void intrusive_ptr_add_ref(const Shared * x)
        {
                x->refcount_.fetch_add(1, boost::memory_order_relaxed);
        }

        friend void intrusive_ptr_release(const Shared* x)
        {
                if (x->refcount_.fetch_sub(1, boost::memory_order_release) == 1)
                {
                        boost::atomic_thread_fence(boost::memory_order_acquire);
                        delete x;
                }
        }
};

vector<boost::intrusive_ptr<Shared const>>  g_vec;
boost::detail::spinlock g_lock = BOOST_DETAIL_SPINLOCK_INIT;

void consumer()
{
        while(true)
        {
                g_lock.lock();
                g_vec.clear();
                g_lock.unlock();
                usleep(10);
        }

}

int main()
{
        thread thd(consumer);

        while(true)
        {
                boost::intrusive_ptr<Shared const> p(new Shared);
                g_lock.lock();
                g_vec.push_back(p);
                g_lock.unlock();
                usleep(1);
        }
        return 0;
}
  • TSAN报错片段:
WARNING: ThreadSanitizer: data race (pid=14513)
  Write of size 8 at 0x7b0400000010 by main thread:
    #0 operator delete(void*) <null> (libtsan.so.0+0x00000006fae4)
    #1 intrusive_ptr_release(Shared const*) /Test/TSan_Intr_Ptr_Min.cpp:25 (a.out+0x000000401195)
    #2 boost::intrusive_ptr<Shared const>::~intrusive_ptr() /boost_1_72_0/boost/smart_ptr/intrusive_ptr.hpp:98 (a.out+0x000000401195)
    #3 main /x01/exch/Test/TSan_Intr_Ptr_Min.cpp:51 (a.out+0x000000401195)

Previous atomic write of size 4 at 0x7b0400000010 by thread T1:
    #0 __tsan_atomic32_fetch_sub <null> (libtsan.so.0+0x00000006576f)
    #1 boost::atomics::detail::gcc_atomic_operations<4ul, true>::fetch_sub(unsigned int volatile&, unsigned int, boost::memory_order) /boost_1_72_0/boost/atomic/detail/ops_gcc_atomic.hpp:116 (a.out+0x000000401481)
    #2 boost::atomics::detail::base_atomic<int, int>::fetch_sub(int, boost::memory_order) volatile /usr/local/boost_1_72_0/boost/atomic/detail/atomic_template.hpp:348 (a.out+0x000000401481)
    #3 intrusive_ptr_release(Shared const*) /Test/TSan_Intr_Ptr_Min.cpp:22 (a.out+0x000000401481)
...

内容的提问来源于stack exchange,提问作者aKumara

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.02 04:12:56