惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Martin Fowler
Martin Fowler
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
A
About on SuperTechFans
Apple Machine Learning Research
Apple Machine Learning Research
The Register - Security
The Register - Security
Vercel News
Vercel News
H
Hackread – Cybersecurity News, Data Breaches, AI and More
人人都是产品经理
人人都是产品经理
MyScale Blog
MyScale Blog
云风的 BLOG
云风的 BLOG
博客园_首页
U
Unit 42
T
Tailwind CSS Blog
G
GRAHAM CLULEY
F
Full Disclosure
V
Vulnerabilities – Threatpost
T
Tenable Blog
月光博客
月光博客
P
Privacy & Cybersecurity Law Blog
P
Privacy International News Feed
K
Kaspersky official blog
Scott Helme
Scott Helme
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
N
News and Events Feed by Topic
T
The Exploit Database - CXSecurity.com
N
News and Events Feed by Topic
有赞技术团队
有赞技术团队
Recent Commits to openclaw:main
Recent Commits to openclaw:main
L
LINUX DO - 最新话题
Recorded Future
Recorded Future
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Help Net Security
Help Net Security
The GitHub Blog
The GitHub Blog
Cisco Talos Blog
Cisco Talos Blog
SecWiki News
SecWiki News
P
Proofpoint News Feed
Security Latest
Security Latest
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
罗磊的独立博客
S
Security Affairs
M
MIT News - Artificial intelligence
L
LINUX DO - 热门话题
美团技术团队
Simon Willison's Weblog
Simon Willison's Weblog
T
Threat Research - Cisco Blogs
Stack Overflow Blog
Stack Overflow Blog
Forbes - Security
Forbes - Security
Hugging Face - Blog
Hugging Face - Blog
博客园 - Franky
V
Visual Studio Blog

博客园 - 喜欢Ⅰ

先避免毁灭性错误,再谈聪明决策 通往天堂的三个阶梯 人类随机数趣闻 - 即使人们会觉得它更随机,但实际上它更不随机 C 里面如何使用链表 list 项目代码套路 墨菲定律 - 人类一种误判心理 消息队列, 一种取舍的选择 Redis Stream CORS 跨域请求一种后端适配解决方案 自我的智慧 市场教父 André Kostolany Exception Handling Considered Harmful MySQL CREATE TABLE Template 模板设计简单交流 字符串转整型 HTTP 尝试获取 Client IP 对炒股看法 吃饱年代 我是个怎样的人 格林童话之祖父和孙子 Linux 守护进程 智慧 ~ 引子 ~ 三则故事 交易人生 大道至简
atomic 原子自增工程案例
喜欢Ⅰ · 2022-02-08 · via 博客园 - 喜欢Ⅰ

案例 1 : 简单用法

atomic_int id;
atomic_fetch_add(&id, 1)
atomic_uint id;
atomic_fetch_add(&id, 1)

以上是两种原子自增简单非常常见用法. 但和业务联动时候存在潜在问题. 例如第一段代码中 atomic_fetch_add(&id, 1) 这种 id++

加到最后会溢出, 需要看业务能否接受了. 第二种死板一点, 也是一样看业务取舍, 例如杂糅一些特殊业务值非常困难.

案例2: skynet 项目中 socket id 设计

https://github.com/cloudwu/skynet/blob/master/skynet-src/socket_server.c#L345-L371

static int
reserve_id(struct socket_server *ss) {
    int i;
    for (i=0;i<MAX_SOCKET;i++) {
        int id = ATOM_FINC(&(ss->alloc_id))+1;
        if (id < 0) {
            id = ATOM_FAND(&(ss->alloc_id), 0x7fffffff) & 0x7fffffff;
        }
        struct socket *s = &ss->slot[HASH_ID(id)];
        int type_invalid = ATOM_LOAD(&s->type);
        if (type_invalid == SOCKET_TYPE_INVALID) {
            if (ATOM_CAS(&s->type, type_invalid, SOCKET_TYPE_RESERVE)) {
                s->id = id;
                s->protocol = PROTOCOL_UNKNOWN;
                // socket_server_udp_connect may inc s->udpconncting directly (from other thread, before new_fd), 
                // so reset it to 0 here rather than in new_fd.
                ATOM_INIT(&s->udpconnecting, 0);
                s->fd = -1;
                return id;
            } else {
                // retry
                --i;
            }
        }
    }
    return -1;
}

其中 ATOM_FINC, ATOM_FAND 设计如下

https://github.com/cloudwu/skynet/blob/master/skynet-src/atomic.h 

#else

#include <stdatomic.h>

#define ATOM_INT atomic_int
#define ATOM_POINTER atomic_uintptr_t
#define ATOM_SIZET atomic_size_t
#define ATOM_ULONG atomic_ulong
#define ATOM_INIT(ref, v) atomic_init(ref, v)
#define ATOM_LOAD(ptr) atomic_load(ptr)
#define ATOM_STORE(ptr, v) atomic_store(ptr, v)
#define ATOM_CAS(ptr, oval, nval) atomic_compare_exchange_weak(ptr, &(oval), nval)
#define ATOM_CAS_POINTER(ptr, oval, nval) atomic_compare_exchange_weak(ptr, &(oval), nval)
#define ATOM_FINC(ptr) atomic_fetch_add(ptr, 1)
#define ATOM_FDEC(ptr) atomic_fetch_sub(ptr, 1)
#define ATOM_FADD(ptr,n) atomic_fetch_add(ptr, n)
#define ATOM_FSUB(ptr,n) atomic_fetch_sub(ptr, n)
#define ATOM_FAND(ptr,n) atomic_fetch_and(ptr, n)

#endif

他的思路是, 通过 atomic_fetch_and 来避免 atomic_fetch_add 溢出问题. 但也存在一个问题, 就是 atomic_fetch_and 存在返回相同值情况,

所以返回的 id 会重复.   这里他借助 atomic_compare_exchange_weak + 业务判断 让重复 id 再次重试. 

这里还有个小彩蛋 if (ATOM_CAS(&s->type, type_invalid, SOCKET_TYPE_RESERVE))  使用 weak 版本而不是 strong 版本讨论

https://github.com/cloudwu/skynet/discussions/1532

业务上自己推荐 atomic_compare_exchange_strong 心里更稳一点. 我们再简单观察 glibc 和 gcc 中二者实现. 

#define atomic_compare_exchange_strong_explicit(PTR, VAL, DES, SUC, FAIL) \
  __extension__                                \
  ({                                    \
    __auto_type __atomic_compare_exchange_ptr = (PTR);            \
    __typeof__ (*__atomic_compare_exchange_ptr) __atomic_compare_exchange_tmp \
      = (DES);                                \
    __atomic_compare_exchange (__atomic_compare_exchange_ptr, (VAL),    \
                   &__atomic_compare_exchange_tmp, 0,    \
                   (SUC), (FAIL));                \
  })

#define atomic_compare_exchange_strong(PTR, VAL, DES)                \
  atomic_compare_exchange_strong_explicit (PTR, VAL, DES, __ATOMIC_SEQ_CST, \
                       __ATOMIC_SEQ_CST)

#define atomic_compare_exchange_weak_explicit(PTR, VAL, DES, SUC, FAIL) \
  __extension__                                \
  ({                                    \
    __auto_type __atomic_compare_exchange_ptr = (PTR);            \
    __typeof__ (*__atomic_compare_exchange_ptr) __atomic_compare_exchange_tmp \
      = (DES);                                \
    __atomic_compare_exchange (__atomic_compare_exchange_ptr, (VAL),    \
                   &__atomic_compare_exchange_tmp, 1,    \
                   (SUC), (FAIL));                \
  })

#define atomic_compare_exchange_weak(PTR, VAL, DES)            \
  atomic_compare_exchange_weak_explicit (PTR, VAL, DES, __ATOMIC_SEQ_CST, \
                     __ATOMIC_SEQ_CST)

区别在于 __atomic_compare_exchange 的 第 4 个参数上面, 用 1 标识 weak 调用, 0 默认 strong 调用.
例如 gcc 一种实现
https://code.woboq.org/gcc/libgcc/config/tilepro/atomic.c.html#164 

#define __atomic_exchange_methods(type, size) \
bool
_atomic_compare_exchange##size(volatile type* ptr, type* oldvalp,
type newval, bool weak __unused,
int models, int modelf __unused)

weak 是 unused 未被使用的. 所以有些平台最终殊途同归, 类似 PF_INET 和 AF_INET 茴香豆几种写法.

案例3: structc 中 id 设计

https://github.com/wangzhione/structc/blob/master/modular/test/timer.c.h#L28-L56

// timer_list 链表对象管理器
struct timer_list {
    atomic_int id;            // 当前 timer node id
    atomic_flag lock;         // 自旋锁
    volatile bool status;     // true is thread loop, false is stop
    struct timer_node * list; // timer list list
};

// 定时器管理单例对象
static struct timer_list timer = { .id = 1, .lock = ATOMIC_FLAG_INIT };

// get atomic int 1 -> INT_MAX -> 1
static inline int timer_list_id() {
    // 0 -> INT_MAX -> INT_MIN -> 0
    int id = atomic_fetch_add(&timer.id, 1) + 1;
    if (id < 0) {
        // INT_MAX + 1 -> INT_MIN
        // 0x7F FF FF FF + 1 -> 0x80 00 00 00

        // INT_MIN & INT_MAX => 0x80 00 00 00 & 0x7F FF FF FF => 0x00 00 00 00
        // id = atomic_fetch_and(&timer.id, INT_MAX) & INT_MAX;
        // Multiple operations atomic_fetch_and can ensure timer.id >= 0
        atomic_fetch_and(&timer.id, INT_MAX);

        // again can ensure id >= 1
        id = atomic_fetch_add(&timer.id, 1) + 1;
    }
    return id;
}

这个设计把 id 分为三段, < 0 , 0, 还有 > 0 . timer_list_id 函数返回 >= 1 id. 这里利用二次 atomic_fetch_add 原子自增同时继续保持不重复性.

后记:

技巧多为想法和需求服务, 欢迎补充更多原子操作用法.