首页 > 编程语言 >Go标准库源码分析: atomic.AddInt64

Go标准库源码分析: atomic.AddInt64

时间:2024-03-29 23:14:24浏览次数:36  
标签:AddInt64 tsan TSan add 源码 atomic fetch

atomic.AddInt64

介绍

原理

源码

看不到源码解释个勾八原理

源码里只有函数doc, 但是没有函数实现, 但是有一段注释

// AddInt64 atomically adds delta to *addr and returns the new value.
// Consider using the more ergonomic and less error-prone [Int64.Add] instead
// (particularly if you target 32-bit platforms; see the bugs section).

介绍了他的功能是原子性的对地址所指的数字 + delta, 需要注意一个问题, 在32位的平台上不应该使用, 会存在bug

在全局搜索过后, 一段特别的注释引起了我的注意

//go:linkname abigen_sync_atomic_AddInt64 sync/atomic.AddInt64

这条指令告诉编译器,虽然sync/atomic.AddInt64​函数定义在sync/atomic​包中,但是可以通过abigen_sync_atomic_AddInt64​这个别名在其他包中被直接调用,就好像它定义在那个包内一样。

好了, 我们已经找到了实际对应的源码位置, 但是奇怪的事情出现了, 此处依然没有实现

//go:linkname abigen_sync_atomic_AddInt64 sync/atomic.AddInt64
func abigen_sync_atomic_AddInt64(addr *int64, delta int64) (new int64)

在同级目录下, 存在这么一个文件

这就是他的实现源码了, 为了不同的平台的适配, 底层的实现使用了汇编, 在最后编译时在链接起来.

分析

函数签名

func abigen_sync_atomic_AddInt64(addr *int64, delta int64) (new int64)

栈帧布局

+----------------+ 
| addr           |
+----------------+
| delta          | 
+----------------+
| 返回值 (new)    |
+----------------+ 

变量对应

  • addr​: +0(FP)
  • delta​: +8(FP)
  • new​: +16(FP)

代码解释

TEXT	sync∕atomic·AddInt64(SB), NOSPLIT|NOFRAME, $0-24
	GO_ARGS
	MOVQ	$__tsan_go_atomic64_fetch_add(SB), AX
	CALL	racecallatomic<>(SB)
	MOVQ	add+8(FP), AX	// convert fetch_add to add_fetch
	ADDQ	AX, ret+16(FP)
	RET
  1. 载入函数__tsan_go_atomic64_fetch_add​到寄存器AX中

  2. 执行函数__tsan_go_atomic64_fetch_add​, 这一步执行的是fetch_add

    在并发编程中,fetch_add​和add_fetch​是两种常见的原子操作,用于实现对共享变量的原子加操作。它们的区别在于操作的顺序不同。

    1. fetch_add​:fetch_add​操作首先读取共享变量的当前值,然后将指定的值加到该变量上,并返回变量之前的值。换句话说,fetch_add​的顺序是先读取再相加。
    2. add_fetch​:与fetch_add​相反,add_fetch​操作首先将指定的值加到共享变量上,然后返回变量的新值。换句话说,add_fetch​的顺序是先相加再返回。

    举个简单例子,假设共享变量的初始值为0,执行以下操作:

    • fetch_add(3)​:首先读取变量的当前值为0,然后将3加到变量上,最后返回之前的值0。
    • add_fetch(3)​:首先将3加到变量上,变量的新值为3,然后返回新值3。
  3. 载入delta, 存放进AX寄存器, 需要注意的是此时的ret+16(FP)​存放的是__tsan_go_atomic64_fetch_add的结果, 是未执行加操作前的数值, 在外面在执行一遍加法, 保证一致, 函数结束.

汇编分析

image

我们需要注意看绿色部分的上半边内容

  1. 将 0x3f (63) 载入CX寄存器
  2. XADDQ 进行原子性的加法, 并将结果存入CX中
  3. 将CX结果移入0x10SP​, SP是调用栈, 偏移16个byte表示返回结果 (参见上方的栈帧布局)
  4. 返回.

注意

为什么要有Lock?

参考 https://stackoverflow.com/questions/30130752/assembly-does-xadd-instruction-need-lock

如果没有Lock, XADDQ依然可以保证原子性, 但是只能保证在单个core上的原子性, 无法提供全局保证.

注意

__tsan_go_atomic64_fetch_add​ 函数是 Go 语言运行时在使用数据竞态检测(ThreadSanitizer,简称 TSan)时的内部函数。它的实现细节通常是隐藏的,因为这个函数是由运行时的系统库提供的,不是由 Go 语言本身直接实现的。TSan 是一个用于检测多线程程序中数据竞态的工具,它会在运行时拦截所有的内存操作以检测潜在的数据竞态问题。

当开启 -race​ 模式编译一个 Go 程序时,编译器和链接器会将程序连接到一个包含 TSan 逻辑的特殊版本的运行时。在这个模式下,TSan 对原子操作的处理和普通模式下是不同的。

__tsan_go_atomic64_fetch_add​ 的实现概览:

确切的实现代码不是公开的,因为这部分代码属于 Go runtime 和 TSan 工具的一部分。但是,理解其大致行为和作用是有帮助的。以下是可能的实现步骤:

  1. 拦截操作:当一个原子操作被执行时,TSan 会拦截这个操作。在 Go 的 -race​ 模式下,这意味着代替调用标准的 sync/atomic​ 包函数,你的代码会调用特殊的 TSan 函数,如 __tsan_go_atomic64_fetch_add​。
  2. **数据竞态检测:TSan 使用 shadow memory 来追踪每个内存地址的访问历史,包括哪个线程访问了该内存,以及它是读操作还是写操作。当执行 __tsan_go_atomic64_fetch_add​ 时,TSan 会检查该内存地址是否可能存在数据竞态。
  3. 执行原子操作:在确保没有数据竞态后,TSan 会安全地执行原子加法操作。这通常是通过调用底层硬件支持的原子指令完成的,以确保整个操作是不可分割的。
  4. 更新监控数据**:操作完成后,TSan 会更新其监控数据,记录这次内存访问,以便于未来的数据竞态检测。
  5. 返回值__tsan_go_atomic64_fetch_add​ 会返回原始内存位置上的值(即操作前的值)。

示例伪代码:

下面是一个简化的、可能的 __tsan_go_atomic64_fetch_add​ 的伪代码实现,用于说明其功能,而不是实际的代码:

int64 __tsan_go_atomic64_fetch_add(int64 *addr, int64 delta) {
    // TSan 检测,确定没有数据竞态
    tsan_check(addr);

    // 执行原子加法操作,并获取原始值
    int64 original_value = atomic_fetch_add(addr, delta);

    // 更新 TSan 监控数据
    tsan_update(addr);

    // 返回操作前的原始值
    return original_value;
}

在这个伪代码中,tsan_check​ 用于确保当前的内存访问不会导致数据竞态,atomic_fetch_add​ 是底层的原子操作,而 tsan_update​ 会记录此次操作,以便于未来的监控。

请注意,实际的 __tsan_go_atomic64_fetch_add​ 函数实现更加复杂,因为它必须与 TSan 的其他部分交互,以实现完整的数据竞态检测和报告。实际上,你通常无法直接查看或修改这个函数的实现,因为它是由 Go 运行时和 TSan 的 C/C++ 代码实现的,并且在运行时被动态链接。

atomic.AddInt64 ​的使用还是比较简单的, 只需要传入一个指针, 同时指定delta就可以

atomic.AddInt64(&i, 64)

标签:AddInt64,tsan,TSan,add,源码,atomic,fetch
From: https://www.cnblogs.com/pDJJq/p/18104799/atomicaddint64-z1k1qgh

相关文章

  • Node.js毕业设计合同管理系统(Express+附源码)
    本系统(程序+源码)带文档lw万字以上  文末可获取本课题的源码和程序系统程序文件列表系统的选题背景和意义选题背景:在现代商业活动中,合同作为规范各方权利与义务的法律文件,扮演着至关重要的角色。随着经济全球化和商业交易的日益频繁,企业和个人需要处理和管理的合同数量急......
  • 【即插即用】GnConv递归门控卷积(附源码)
    论文地址:https://arxiv.org/abs/2207.14284源码地址:https://github.com/raoyongming/HorNetGnConvGnConvHorNet摘要简介:最近,视觉Transformer在各种任务中取得了巨大成功,这主要得益于基于点积自注意力的新型空间建模机制。在本文中,我们发现视觉Transformer的关键要素,即输......
  • Node.js毕业设计合同管理(Express+附源码)
    本系统(程序+源码)带文档lw万字以上  文末可获取本课题的源码和程序系统程序文件列表系统的选题背景和意义选题背景:在当今信息化快速发展的时代,合同管理系统作为企业日常运营中不可或缺的一部分,扮演着至关重要的角色。合同管理涉及合同的起草、审核、签订、执行以及存档等......
  • C#手术麻醉系统源码 可对接HIS LIS PACS 医疗系统各类设备 医院手麻系统源码
    C#手术麻醉系统源码可对接HIS LIS  PACS医疗系统各类设备手术麻醉信息管理系统主要还是为了手术室开发提供全面帮助的系统,其主要是由监护设备数据采集子系统和麻醉临床系统两个子部分组成。包括从手术申请到手术分配,再到术前访视、术中记录及术后恢复的全过程中都可以......
  • 2024年1000个计算机毕业设计项目推荐(源码+论文【万字】)
    2024年最新计算机毕业设计题目推荐,项目汇总!本科、专科。项目设计、项目定制、辅导、万字文档哈喽,大家好,大四的同学马上要开始做毕业设计了,大家做好准备了吗?博主给大家详细整理了计算机毕业设计最新项目,对项目有任何疑问,都可以问博主哦~技术栈包括但不限于:Java、JavaWeb......
  • 电子招标采购系统源码之从供应商管理到采购招投标、采购合同、采购执行的全过程数字化
    随着市场竞争的加剧和企业规模的扩大,招采管理逐渐成为企业核心竞争力的重要组成部分。为了提高招采工作的效率和质量,我们提出了一种基于电子化平台的解决方案。该方案旨在通过电子化招投标,使得招标采购的质量更高、速度更快,同时节约招标成本,提升企业的资金节约率。 项目说明......
  • 【附源码】JAVA计算机毕业设计在线考研刷题系统(springboot+mysql+开题+论文)
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着信息技术的飞速发展,计算机在教育领域的应用日益广泛。特别是在线教育平台,以其便捷性、灵活性和资源共享性受到了广大师生的青睐。近年来,考研热潮......
  • 【附源码】JAVA计算机毕业设计在线考试系统的设计与实现(springboot+mysql+开题+论文)
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着信息技术的迅猛发展和互联网的普及,传统的教育模式正面临着深刻的变革。在线考试系统作为教育信息化进程中的重要一环,正逐渐取代传统的纸质考试方......
  • 【附源码】JAVA计算机毕业设计在线考试答题系统(springboot+mysql+开题+论文)
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着互联网技术的迅猛发展,教育信息化已成为现代教育发展的重要趋势。在线考试答题系统作为教育信息化的重要组成部分,能够打破传统考试的时间和空间限......
  • Node.js毕业设计航空订票系统(Express+附源码)
    本系统(程序+源码)带文档lw万字以上  文末可获取本课题的源码和程序系统程序文件列表系统的选题背景和意义选题背景:随着航空业的迅速发展,越来越多的人选择飞机作为出行的主要交通方式。航空订票系统作为航空公司与旅客之间的桥梁,其重要性不言而喻。一个好的航空订票系统能......