旋转锁与XCHG

时间:2016-04-19 23:23:39

标签: assembly x86 synchronization

维基百科提供的带有x86 XCHG命令的自旋锁的示例实现是:

; Intel syntax

locked:                      ; The lock variable. 1 = locked, 0 = unlocked.
     dd      0

spin_lock:
     mov     eax, 1          ; Set the EAX register to 1.

     xchg    eax, [locked]   ; Atomically swap the EAX register with
                             ;  the lock variable.
                             ; This will always store 1 to the lock, leaving
                             ;  the previous value in the EAX register.

     test    eax, eax        ; Test EAX with itself. Among other things, this will
                             ;  set the processor's Zero Flag if EAX is 0.
                             ; If EAX is 0, then the lock was unlocked and
                             ;  we just locked it.
                             ; Otherwise, EAX is 1 and we didn't acquire the lock.

     jnz     spin_lock       ; Jump back to the MOV instruction if the Zero Flag is
                             ;  not set; the lock was previously locked, and so
                             ; we need to spin until it becomes unlocked.

     ret                     ; The lock has been acquired, return to the calling
                             ;  function.

spin_unlock:
     mov     eax, 0          ; Set the EAX register to 0.

     xchg    eax, [locked]   ; Atomically swap the EAX register with
                             ;  the lock variable.

     ret                     ; The lock has been released.

从这里https://en.wikipedia.org/wiki/Spinlock#Example_implementation

我不明白为什么解锁需要是原子的。

有什么问题
spin_unlock:
     mov     [locked], 0  

2 个答案:

答案 0 :(得分:2)

解锁需要have release semantics才能正确保护关键部分。但它不需要顺序一致性。原子性不是问题(见下文)。

所以是的,在x86上,一个简单的商店是安全的, glibc's pthread_spin_unlock does so:

    movl    $1, (%rdi)
    xorl    %eax, %eax
    retq

参见一个简单但可能有用的x86 spinlock implementation I wrote in this answer,使用带pause指令的只读自旋循环。

可能这段代码是根据位字段版本改编的。

btr解锁为位域中的一个标志是不安全的,因为它是包含字节(or the containing naturally-aligned 4 byte dword or 2 byte word)的非原子读 - 修改 - 写入。

所以也许写这篇文章的人没有意识到simple stores to aligned addresses are atomic on x86, like on most ISAs。但是,x86具有弱排序的ISA并不是每个商店都有release semantics。释放锁的xchg使每个解锁成为一个完整的内存屏障,这超出了正常的锁定语义。 (虽然在x86上,采取锁定将是一个完整的障碍,因为没有xchg或其他lock指令,没有办法进行原子RMW或原子比较和交换,这些是完全障碍,如mfence。)

解锁存储在技术上并不需要是原子的,因为我们只存储零或1,所以只有低位字节很重要。例如我认为如果锁是未对齐的并且跨越缓存行边界分裂,它仍然可以工作。撕裂可能发生但无关紧要,真正发生的是锁定的低字节被原子地修改,操作总是将零置于高3字节。

如果你想返回旧的值以捕获双重解锁错误,更好的实现将分别加载和存储:

spin_unlock:
     ;; pre-condition: [locked] is non-zero

     mov     eax,  [locked]        ; old value, for debugging
     mov     dword [locked], 0     ; On x86, this is an atomic store with "release" semantics.

     ;test    eax,eax
     ;jz    double_unlocking_detected    ; or leave this to the caller
     ret

答案 1 :(得分:0)

要点

spin_unlock:
     mov     eax, 0          ; Set the EAX register to 0.

     xchg    eax, [locked]   ; Atomically swap the EAX register with
                             ;  the lock variable.

     ret                     ; The lock has been released.

不仅要解锁,还要用正确的返回值填充eax(如果解锁则为1,否则为0)

如果在调用spin_unlock之前未获得锁定([locked]在这种情况下保持值0),则spin_unlock应返回0