LevelDB 源码阅读:利用 Clang 的静态线程安全分析
LevelDB 中有一些宏比较有意思,平时自己写代码的时候,还基本没用过。这些宏在 thread_annotations.h 中定义,可以在编译时使用 Clang 编译器的线程安全分析工具,来检测潜在的线程安全问题。
比如下面这些宏,到底有什么作用呢?本文就一起来看看吧。
GUARDED_BY(x) // 表示变量必须在持有锁x时才能访问
PT_GUARDED_BY(x) // 指针类型的 GUARDED_BY
ACQUIRED_AFTER(...) // 指定锁的获取顺序,防止死锁
// ...
GUARDED_BY 锁保护
在很多类的成员变量定义中,都有 GUARDED_BY(mutex_)
这样的注解,有什么作用呢?比如 LRU Cache 的定义:
class LRUCache {public:// ...private:// ...mutable port::Mutex mutex_;size_t usage_ GUARDED_BY(mutex_);// ...HandleTable table_ GUARDED_BY(mutex_);
};
其实这就是 Clang 的线程安全注解,编译的时候,Clang 会检查所有对 usage_
和 table_
的访问是否都在持有 mutex_
锁的情况下进行。另外,在函数或代码块结束时,编译器还会检查所有应该释放的锁是否都已经释放,可以防止遗漏锁释放导致的资源泄露或死锁。
反观我们平时在写业务代码的时候,几乎没用过这些线程安全注解。顶多注释下这里不是线程安全的,要加锁访问,全靠开发的自觉。可想而知,业务中肯定会遇见各种奇怪的多线程数据竞争问题。
LevelDB 实现的时候,加了很多类似的线程安全注解,不仅可以明确告诉其他开发者这个变量需要锁保护,还可以在编译期就发现潜在的线程安全问题,从而减少多线程环境下可能出现的竞态条件、死锁等问题。
锁保护线程注解示例
下面通过一个完整的例子来看看 Clang 的线程安全注解作用。这里 SharedData 类中,counter_
变量需要锁保护,mutex_
是我们封装的一个锁实现。
// guard.cpp
#include <mutex>
#include <iostream>class __attribute__((capability("mutex"))) Mutex {
public:void lock() { mutex_.lock(); }void unlock() { mutex_.unlock(); }
private:std::mutex mutex_;
};class SharedData {
public:void Increment() {mutex_.lock();counter_++;mutex_.unlock();}// Wrong case: Accessing shared variable without holding the lockvoid UnsafeIncrement() {counter_++;}void UnsafeIncrement2() {mutex_.lock();counter_++;// Forgot to unlock, will trigger warning}private:Mutex mutex_;int counter_ __attribute__((guarded_by(mutex_)));
};int main() {SharedData data;data.Increment();data.UnsafeIncrement();data.UnsafeIncrement2();return 0;
}
当然这里的测试代码为了直接能运行,就没有依赖 LevelDB 中的宏定义 GUARDED_BY。下面的 __attribute__((guarded_by(mutex_)))
和宏展开的结果是一样的。
用 Clang 编译上面的代码,就能看到告警信息:
$ clang++ -pthread -Wthread-safety -std=c++17 guard.cpp -o guard
guard.cpp:16:9: warning: writing variable 'counter_' requires holding mutex 'mutex_' exclusively [-Wthread-safety-analysis]counter_++;^
guard.cpp:22:9: warning: writing variable 'counter_' requires holding mutex 'mutex_' exclusively [-Wthread-safety-analysis]counter_++;^
guard.cpp:27:9: warning: writing variable 'counter_' requires holding mutex 'mutex_' exclusively [-Wthread-safety-analysis]counter_++;^
3 warnings generated
可以看到,编译器在编译的时候,就发现了 counter_
变量在未持有 mutex_
锁的情况下被访问,从而告警。
PT_GUARDED_BY 指针保护
这里 GUARDED_BY 通常用在对象的非指针成员上,用来保护成员变量自身。而 PT_GUARDED_BY 则是用在指针和智能指针成员上,用来保护指针指向的数据。注意这里 PT_GUARDED_BY 只保护指针指向的数据,指针本身并没有约束的。可以看下面的例子:
Mutex mu;
int *p1 GUARDED_BY(mu);
int *p2 PT_GUARDED_BY(mu);
unique_ptr<int> p3 PT_GUARDED_BY(mu);void test() {p1 = 0; // Warning!*p2 = 42; // Warning!p2 = new int; // OK.*p3 = 42; // Warning!p3.reset(new int); // OK.
}
capability 属性注解
上面的例子中,我们没有直接用标准库的 mutex 互斥锁,而是简单封装了一个 Mutex
类。在类定义那里,用了 __attribute__((capability("mutex")))
注解。
这是因为 Clang 的线程安全分析需要知道哪些类型是锁,需要去追踪锁的获取和释放状态。而标准库的类型没有这些注解,不能直接用于 Clang 的线程安全分析。这里用到了 clang 的 capability("mutex")
属性,用来指定该类具有锁的特性。
LevelDB 中定义锁的代码也用到了注解,不过稍微不同,用的是 LOCKABLE
,代码如下:
class LOCKABLE Mutex {public:Mutex() = default;~Mutex() = default;Mutex(const Mutex&) = delete;Mutex& operator=(const Mutex&) = delete;...
这是因为早期版本的 Clang 使用 lockable 属性,后来引入了更通用的 capability 属性。为了向后兼容,lockable 被保留为 capability(“mutex”) 的别名。所以,这两者是等效的。
线程安全分析的能力
上面例子有点简单,其实从本质上来看,这里 clang 静态线程安全分析想做的事情,就是在编译器提供一种保护资源的能力。这里资源可以是数据成员,比如前面的 counter_
,也可以是提供对某些底层资源访问的函数/方法。clang 可以在编译期确保,除非某个线程有访问资源的能力,否则它无法访问资源。
这里线程安全分析使用属性来声明这里的资源约束,属性可以附加到类、方法和数据成员前面。Clang 官方也提供了一系列属性定义宏,可以直接拿来用。LevelDB 中定义了自己的宏,也可以参考。
前面给的例子中,注解主要用在数据成员上,其实也可以用在函数上。比如 LevelDB 中定义的锁对象 Mutex,在成员函数上用到了这些注解:
class LOCKABLE Mutex {// ...void Lock() EXCLUSIVE_LOCK_FUNCTION() { mu_.lock(); }void Unlock() UNLOCK_FUNCTION() { mu_.unlock(); }void AssertHeld() ASSERT_EXCLUSIVE_LOCK() {}// ...
};
这些注解主要用于标记锁对象的成员函数,告诉编译器这些函数会如何改变锁的状态:
- EXCLUSIVE_LOCK_FUNCTION: 表示函数会获取互斥锁的独占访问权,调用前锁必须是未持有状态,调用后锁会被当前线程独占;
- UNLOCK_FUNCTION: 表示函数会释放锁,调用前锁必须是被持有状态(可以是独占或共享),调用后锁会被释放;
- ASSERT_EXCLUSIVE_LOCK: 用于断言当前线程持有锁的独占权,通常用在调试代码中,确保代码运行在正确的加锁状态下。
当然这些是 clang 早期的线程安全注解,主要为了锁来命名。上面这几个现在可以用 ACQUIRE(…), ACQUIRE_SHARED(…), RELEASE(…), RELEASE_SHARED(…) 来替代。
此外,还有其他一些注解,可以参考 Clang 官方的文档 Thread Safety Analysis 了解更多细节。
相关文章:
LevelDB 源码阅读:利用 Clang 的静态线程安全分析
LevelDB 中有一些宏比较有意思,平时自己写代码的时候,还基本没用过。这些宏在 thread_annotations.h 中定义,可以在编译时使用 Clang 编译器的线程安全分析工具,来检测潜在的线程安全问题。 比如下面这些宏,到底有什么…...
不只是工具:ChatGPT写作在学术中的创新思维与深度思考
目录 1.数据选择与质量 2.Prompt技巧 1.明确任务 2.上下文信息 3.好的示例 3.后期编辑与润色 随着AIGC技术的迅猛发展和不断升级,AI写作正逐渐成为各行各业的新宠。然而不少宝子们们却发现了一个有趣的现象:虽然都是依赖AI生成文本,有些…...
全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之循环结构(for循环语句)(八continue语句和break语句)
在C中,continue语句和break语句都是用于控制循环的执行流程的关键字,但它们有不同的用途和行为。 1、break语句用于立即终止当前所在的循环或switch语句,并跳出循环体,继续执行循环之后的代码,break语句用于直接跳出循…...
sql server 动态执行sql
实例: execute sp_executesql Nselect count(1) FROM table X2 WITH(NOLOCK) WHERE X2.UPDDTTM > DT_START AND X2.UPDDTTM < DT_END , NDT_START datetime,DT_END datetime, DT_START2025-01-02 10:06:58.620,DT_END2025-01-02 10:09:35.457 参考&…...
【数据结构Ⅰ复习题】
如有错误欢迎指正,题目根据教材----------严蔚敏数据结构(c语言版 第2版)人民邮电电子版 数据结构Ⅰ复习题 一、填空题1.算法应该具备的5个重要特性有___有穷性___、确定性、可行性、输入和输出。2.非空单链表L中*p是头…...
经验证:将数据从索尼传输到Android的 4 种方法
概括 像Android Galaxy S20 这样的新型Android智能手机很酷,但除了将数据从索尼传输到Android之外。众所周知,旧的索尼手机上存储着大量的文件,因此将数据从旧的索尼手机传输到新的Android手机非常重要。为了解决这个问题,我们做…...
服务器端请求伪造之基本介绍
一.服务器端请求伪造漏洞基础 1.客户端请求 客户端请求指的是由客户端设备(如个人计算机、智能手机、平板电脑等)或软件(浏览器、各种APP)发出的请求,以获取指定的网页、图片、视频或其他资源。比如当用户在浏览器中输…...
Java反射详解(三)
上一篇博客:Java反射详解(二) 写在前面:大家好!我是晴空๓。如果博客中有不足或者的错误的地方欢迎在评论区或者私信我指正,感谢大家的不吝赐教。我的唯一博客更新地址是:https://ac-fun.blog.c…...
HTML——59. maxlength和disabled属性
<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>maxlength和disabled属性</title></head><body><!--input元素的type属性:(必须要有)1.指定输入内容的类型2.默认为text,单行文本框--> …...
Java中的函数式接口详解(一)
1. 函数式接口 1.1. 定义 函数式接口(Function Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。 函数式接口又称为:功能接口。 功能接口为 Lambda 表达式和方法引用(用双冒号 ::来进行方法调用)提供…...
Quo Vadis, Anomaly Detection? LLMs and VLMs in the Spotlight 论文阅读
文章信息: 原文链接:https://arxiv.org/abs/2412.18298 Abstract 视频异常检测(VAD)通过整合大语言模型(LLMs)和视觉语言模型(VLMs)取得了显著进展,解决了动态开放世界…...
redis的学习(二)
4 哈希表 哈希类型中的映射关系通常称为field-value,⽤于区分Redis整体的键值对(key-value), 注意这⾥的value是指field对应的值,不是键(key)对应的值, 4.1 操作命令 hsetÿ…...
简单使用linux
1.1 Linux的组成 Linux 内核:内核是系统的核心,是运行程序和管理 像磁盘和打印机等硬件设备的核心程序。 文件系统 : 文件存放在磁盘等存储设备上的组织方法。 Linux 能支持多种目前浒的文件系统,如 ext4 、 FAT 、 VFAT 、 ISO9660 、 NF…...
springboot541党员学习交流平台(论文+源码)_kaic
摘 要 如今社会上各行各业,都喜欢用自己行业的专属软件工作,互联网发展到这个时候,人们已经发现离不开了互联网。新技术的产生,往往能解决一些老技术的弊端问题。因为传统党员学习交流平台信息管理难度大,容错率低&am…...
心力衰竭相关临床记录数据分析开发技术概述
心力衰竭相关临床记录数据分析开发技术概述 心力衰竭临床记录数据分析的开发涉及多种技术,包括数据采集、处理、建模和可视化等方面。以下是从技术角度对整个开发流程的概述: 数据采集技术 1.1 数据来源 公开数据集:如 UCI 数据存储库、Clin…...
SpringMVC(六)拦截器
目录 1.什么是拦截器 2.拦截器和过滤器有哪些区别 3.拦截器方法 4.单个拦截器的执行流程 5.使用拦截器实现用户登录权限验证(实例) 1.先在html目录下写一个login.html文件 2.在controller包下写一个LoginController文件 3.加拦截器 1.创建一个conf…...
将simpletex 识别的公式 复制到ppt 中
1)点击 复制MathML(word) 2)右击粘贴到任意word 中 3)将word公式粘到 office (2019) 的ppt 中 线上识别链接:SimpleTex - Snip & Get!...
vs 2022 中xml 粘贴为Class 中,序列化出来的xml 的使用
上图是visual studio 2022 中使用的粘贴功能的菜单位置 在生成的xml 中,有些是类似如下类型的 [System.Serializable] [System.Xml.Serialization.XmlType] public class Item {private bool isVisibleField;private bool isVisibleFieldSpecified;[System.Xml.Se…...
短视频平台的视频水印怎么去除?
当你看到某个短视频,觉得内容非常有价值,想要个人收藏以便日后学习或回顾,但发现短视频平台无法直接下载且带有水印时,以下提供的几种方法将帮助你轻松去除水印,获取高清无水印的视频内容。 方法一:使用第…...
《Vue3实战教程》34:Vue3状态管理
如果您有疑问,请观看视频教程《Vue3实战教程》 状态管理 什么是状态管理? 理论上来说,每一个 Vue 组件实例都已经在“管理”它自己的响应式状态了。我们以一个简单的计数器组件为例: vue <script setup> import { r…...
AI大模型系列之七:Transformer架构讲解
目录 Transformer网络是什么? 输入模块结构: 编码器模块结构: 解码器模块: 输出模块结构: Transformer 具体是如何工作的? Transformer核心思想是什么? Transformer的代码架构 自注意力机制是什么…...
每天五分钟机器学习:凸集
本文重点 在SVM中,目标函数是一个凸函数,约束集合是一个凸集。因此,SVM问题可以转化为一个凸规划问题来求解。这使得SVM在实际应用中具有较高的计算效率和准确性。 凸集的定义 凸集是指一个集合中的任意两点之间的线段都完全包含在这个集合中。换句话说,给定集合C中的两…...
【智能算法】改进蚁狮优化算法【matlab】
目录 1 主要内容 2 部分程序 3 程序结果 下载链接 1 主要内容 该程序方法复现《改进蚁狮算法的无线传感器网络覆盖优化》两种改进算法模型,即原始ALO算法的基础上添加了两种改进策略: - 改进1:将原先的间断性边界收缩因子变为连续性边界…...
【Python】闭包
闭包(Closure)是指一个函数记住了并可以访问它的词法作用域(lexical scope),即使这个函数在词法作用域之外执行。 闭包其实就是延伸了作用域的函数,包括被延伸函数主体中引用的非全局变量和局部变量。这些…...
Python跨年烟花
目录 系列文章 写在前面 技术需求 完整代码 下载代码 代码分析 1. 程序初始化与显示设置 2. 烟花类 (Firework) 3. 粒子类 (Particle) 4. 痕迹类 (Trail) 5. 烟花更新与显示 6. 主函数 (fire) 7. 游戏循环 8. 总结 注意事项 写在后面 系列文章 序号直达链接爱…...
QT------------其他工具软件和技术
实现思路 多语言界面程序设计: 使用 QTranslator 类为 QT 应用程序提供多语言支持。将不同语言的翻译文件(.qm 文件)添加到应用程序中,根据用户的语言设置动态加载相应的翻译文件。 QT 样式表(QSS)&#x…...
数据结构9.3 - 文件基础(C++)
目录 1 打开文件字符读写关闭文件 上图源自:https://blog.csdn.net/LG1259156776/article/details/47035583 1 打开文件 法 1法 2ofstream file(path);ofstream file;file.open(path); #include<bits/stdc.h> using namespace std;int main() {char path[]…...
javaEE-文件操作和IO-文件
目录 一.什么是文件 1.文件就是硬盘(磁盘)上的文件。 2.计算机中存储数据的设备: 3.硬盘的物理特征 4.树型结构组织和⽬录 5.文件路径 文件路径有两种表示方式: 6.文件的分类 二、java中文件系统的操作 1.File类中的属性: 2.构造方…...
富芮坤FR800X系列之软件开发工具链(如IDE、编译器、调试器等)
文章目录 一、IDE(集成开发环境)二、编译器三、调试器四、其他辅助工具五、小结 FR800x系列作为一款低功耗蓝牙芯片,其软件开发工具链对于开发者来说至关重要。以下是对FR800x软件开发工具链的详细介绍,包括IDE(集成开…...
微服务-Eureka
Eureka的作用 使用RestTemplate完成远程调用需要手动的生命被调用者的ip和端口,从而能够发起http请求,但是如果有很多个实例也更加不能有效的处理,而且我们又该如何知道这些实例是否健康呢。所以就有了很多的注册中心比如Eureka、Nacos等等。…...
Elasticsearch: 高级搜索
一、match_all匹配所有文档 1、介绍: match_all查询是一个特殊的查询类型,它用于匹配索引中的所有文档,而不考虑任何特定的查询条件。 基本语法: GET /<your-index-name>/_search {"query": {"match_all…...
项目优化之策略模式
目录 策略模式基本概念 策略模式的应用场景 实际项目中具体应用 项目背景: 策略模式解决方案: 计费模块策略模式简要代码 策略模式基本概念 策略模式(Strategy Pattern) 是一种行为型设计模式,把算法的使用放到环境类中,而算…...
HTML——57. type和name属性
<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>type和name属性</title></head><body><!--1.input元素是最常用的表单控件--><!--2.input元素不仅可以在form标签内使用也可以在form标签外使用-…...
LabVIEW 实现自动对焦的开发
自动对焦(Autofocus, AF)技术是通过分析图像或传感器信号,动态调整焦点位置以实现清晰成像或高精度定位的过程。在LabVIEW中,可以通过集成信号采集、数据处理、控制算法和硬件接口模块,实现多种自动对焦方法࿰…...
Ruby 数据类型
Ruby 数据类型 Ruby,作为一种动态、开放源代码的编程语言,以其简洁明了的语法和强大的功能而闻名。在Ruby中,数据类型是编程的核心组成部分,它们决定了变量可以存储的信息种类以及可以对这些信息执行的操作。Ruby是一种类型安全的…...
【MySQL】--- 表的CRUD
Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk (๑•́ ₃ •̀๑) 文章专栏: MySQL CRUD : Create(创建), Retrieve(读取),Update(更新),Delete(删除)。 🏠 插入C 🧷 基本…...
算法13、基础二分查找的应用(木根切割等)
🌰1、方程求根 晴问算法 1️⃣即求f(x) x^3 x^2 x - a 0的根,又因为要求精确到0.01,所以eps至少设置为1e-3或者更小; 2️⃣求导得3x^2 2x 1 2x^2 x^2 2x 1 2x^2 (x1)^2 > 0, 所以f(x)是单调递增函数&…...
hive on spark报错解决(基于hive-3.1.3和spark-2.3.0)
相关配置可参考:https://blog.csdn.net/weixin_46389691/article/details/134126254 原作者:月亮给我抄代码 他写的很详细 ERROR : Job failed with java.lang.IllegalAccessError: tried to access method com.google.common.base.Stopwatch.<init&…...
CentOS — 目录管理
文章目录 一、目录结构二、切换目录三、查看目录四、创建目录五、复制目录六、剪切目录七、删除目录 目录也是一种文件。 蓝色目录,绿色可执行文件,红色压缩文件,浅蓝色链接文件,灰色其它文件, 点开头的是隐藏文件&…...
学AI编程的Prompt工程,豆包Marscode
学习链接:Datawhale-AI活动https://www.datawhale.cn/activity/116/23/95?rankingPage1 目录 一、如何使用 二、编写游戏 2.1 创意输入与代码生成 2.2 项目初始化与应用 2.3 创意优化与迭代 三、效果展示 一、如何使用 建议在在vscode上安装marscode插件&a…...
基于微信小程序的面部动作检测系统
引言 本技术文档旨在详细阐述一个基于微信小程序的面部动作检测系统的技术路线、实现方法及关键技术框架。系统的核心功能包括检测用户的左右转头、眨眼和张嘴动作,并根据检测结果逐步引导用户完成任务。为确保系统的安全性和准确性,特别是防止用户通过…...
Java网络套接字
在Java的开发中,有一个很重要!很重要!很重要!的东西,叫做网络套接字,它被广泛的用来二次开发服务,比如大数据中台的服务链路调用等。 它的实现原理是依靠三次握手来完成通信的建立,…...
mapbox基础,测面功能实现
👨⚕️ 主页: gis分享者 👨⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨⚕️ 收录于专栏:mapbox 从入门到精通 文章目录 一、🍀前言1.1 ☘️mapboxgl.Map 地图对象1.2 ☘️Turf 框架二、🍀测面功能实现1. ☘️实现思路2. ☘️代码样例一、🍀…...
如何通过设置失效时间清除本地存储的数据
一、使用localStorage和时间戳(JavaScript) 1. 原理 localStorage是浏览器提供的一种在本地存储数据的方式,数据没有过期时间限制。但是可以通过自己记录时间戳来模拟数据过期的功能。在存储数据时,同时存储一个时间戳ÿ…...
【QT】找不到qwt_plot.h
系统环境: linux 20.04 qt 6.7.2 cmake 3.22 原因: Qwt没有正式的FindQwt.cmake,Qwt也没有提供QwtConfig.cmake。而且cmake不支持qmake的配置特性,也不支持读取mkspecs (.prf)文件。也就是说cmake构建的qt项目不可用qwt。 解决步…...
程序员如何培养技术领导力?
文章精选推荐 1 JetBrains Ai assistant 编程工具让你的工作效率翻倍 2 Extra Icons:JetBrains IDE的图标增强神器 3 IDEA插件推荐-SequenceDiagram,自动生成时序图 4 BashSupport Pro 这个ides插件主要是用来干嘛的 ? 5 IDEA必装的插件&…...
C# 设计模式(创建型模式):原型模式
C# 设计模式(创建型模式):原型模式 引言 在面向对象的设计中,创建型模式关注于对象创建的方式和复杂度。原型模式(Prototype Pattern)是其中一种创建型设计模式,它允许通过复制现有的实例来创…...
Python自学 - 函数初步(内置函数、模块函数、自定义函数)
1 Python自学 - 函数初步(内置函数、模块函数、自定义函数) 1.1 内置函数 几乎所有的编程都会提供一些内置函数,以便完成一些最基本的任务,Python提供了丰富的内置函数,熟悉内置函数可以给工作带来极大便利。 Python官方的内置函数介绍网…...
Mono里运行C#脚本21—mono_image_init_name_cache
前面分析了怎么样加载mscorlib.dll文件,然后把文件数据读取到内存。 接着下来,就会遇到加载整个C#的类型系统,比如System. Object,大体类型如下图所示: 在对CIL编译之前,需要把这些类型全部加载到内存里,以便快捷地访问它们。 mono_image_init_name_cache函数就是完成…...
MySQL中distinct和group by去重的区别
MySQL中distinct和group by去重的区别 在MySQL中,我们经常需要对查询结果进行去重,而DISTINCT和GROUP BY是实现这一功能的两种常见方法。虽然它们在很多情况下可以互换使用,但它们之间还是存在一些差异的。接下来,我们将通过创建测…...