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

推荐订阅源

N
News | PayPal Newsroom
IT之家
IT之家
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
大猫的无限游戏
大猫的无限游戏
GbyAI
GbyAI
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
L
LangChain Blog
S
SegmentFault 最新的问题
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Project Zero
Project Zero
P
Privacy & Cybersecurity Law Blog
V
Vulnerabilities – Threatpost
博客园 - 三生石上(FineUI控件)
Recorded Future
Recorded Future
The Hacker News
The Hacker News
C
CXSECURITY Database RSS Feed - CXSecurity.com
C
CERT Recently Published Vulnerability Notes
宝玉的分享
宝玉的分享
aimingoo的专栏
aimingoo的专栏
T
Tor Project blog
T
The Exploit Database - CXSecurity.com
Schneier on Security
Schneier on Security
H
Help Net Security
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
M
MIT News - Artificial intelligence
W
WeLiveSecurity
P
Proofpoint News Feed
A
About on SuperTechFans
S
Securelist
I
InfoQ
G
Google Developers Blog
博客园 - 司徒正美
博客园 - 叶小钗
Latest news
Latest news
F
Fortinet All Blogs
G
GRAHAM CLULEY
腾讯CDC
Jina AI
Jina AI
S
Schneier on Security
I
Intezer
V
Visual Studio Blog
美团技术团队
V2EX - 技术
V2EX - 技术
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
The Cloudflare Blog
Microsoft Security Blog
Microsoft Security Blog
Blog — PlanetScale
Blog — PlanetScale
P
Proofpoint News Feed
罗磊的独立博客
Y
Y Combinator 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 原子自增同时继续保持不重复性.

后记:

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