cmpxchg
的操作伪代码如下(英特尔®;64和IA-32体系结构软件开发人员手册,第2A卷:指令集参考,A-M,2010(:
IF accumulator = DEST
THEN
ZF ← 1;
DEST ← SRC;
ELSE
ZF ← 0;
accumulator ← DEST;
FI;
至少在第一眼中,累加器更改其值如果(且仅当(ZF = 0
。那么,是安全还是完全忽略ZF,只观察累加器值的变化来判断操作是否成功?
换句话说,我可以安全地使用变体#2而不是#1吗?
#1:
mov eax, r8d
lock cmpxchg [rdx], ecx
jz @success
#2:
mov eax, r8d
lock cmpxchg [rdx], ecx
cmp eax, r8d
jz @success
我的意思是,是否有一些非常特殊的情况,只有寻找ZF才能真正显示操作是否成功?这可能是一个微不足道的问题,但无锁多任务处理几乎不可能调试,所以我必须有101%的把握。
您的推理在我看来是正确的。
浪费重新生成ZF的指令不会导致正确性问题,而且如果cmp可以与JCC融合,只会损失代码大小。不过,与只替换EAX中的旧值相比,还需要额外的寄存器。
这可能就是为什么GNU C的旧式__sync
内建(被采用内存顺序参数的__atomic
内建所淘汰(只提供返回值或布尔成功结果的__sync_val_compare_and_swap
和__sync_bool_compare_and_swap
是可以的,而没有一个内建同时返回这两个值。
(较新的__atomic_compare_exchange_n
更类似于C11/C++11 API,通过引用更新expected
,并返回bool
。这可以允许GCC不浪费具有cmp
的指令。(