使用原子交换实现原子增量?

时间:2009-11-11 03:41:47

标签: multithreading assembly atomic

假设我正在编写(汇编)CPU,其唯一的原子操作是无条件交换 - 没有LL / SC,没有比较和交换,只是普通交换。 (ARM9就是这种野兽的一个例子。)有没有办法使用交换操作执行原子递增/递减操作?

有一个相对简单的答案,即使用swap来构建一个自旋锁,并使用它来提供一个关于普通增量和减量的关键部分。但这看起来很笨重,而且我知道如果CAS或LL / SC可用,它可以在没有锁定的情况下完成。所以我真正想知道的是,如果有一种方法可以在不使用锁的情况下完成它。

2 个答案:

答案 0 :(得分:3)

我想不出另一种方法,只是因为你需要交换和比较以检测你是否被允许继续。如果您没有compare-and-swap命令,则必须使用循环交换和比较来实现它,例如:

; Emulate atomic add/sub with atomic swap.
; On entry:
;   r0 contains address of variable
;   r1 contains value to add or subtract.

mutex:    defw    0           ; mutual exclusion semaphore (0=free, 1=busy).

chng:     push    r2          ; save variables.
          ld      r2,1        ; claiming value.
spin:     swap    r2,(mutex)  ; atomic swap (sounds like a good name for a band).
          bnz     spin        ; loop until you have it.
          add     (r0),r1     ; emulated atomic change.
          swap    r2,(mutex)  ; free mutex for everyone else.
          pop     r2          ; restore registers.
          ret

如果您在代码中的很多地方都这样做,那真的很笨拙。我经常发现将'klunky'代码隔离到一个函数(如上所述)会使它变得不那么笨重了,因为你最终会看到更多更简单的代码段:

myvar:    defw    0
          : : : : :
          ld      r0,myvar
          ld      r1,1        ; increment
          call    chng

或者,如果您希望代码更简单,请提供单独的incrdecr函数:

; Emulate atomic incr/decr with emulated atomic change.
; On entry:
;   r0 contains address of variable

incr:     push    r1          ; save registers.
          ld      r1,1        ; increment.
          call    chng        ; do it.
          pop     r1          ; restore registers.
          ret
decr:     push    r1          ; save registers.
          ld      r1,-1       ; decrement.
          call    chng        ; do it.
          pop     r1          ; restore registers.
          ret

然后您的代码序列变为:

          ld      r0,myvar
          call    incr

或者,如果你可以做宏,那就更简单了:

atincr:   defm                ; do this once to define macro
          ld      r0,&1
          call    incr
          endm

          atincr  myvar       ; do this in your code, as much as you like.

答案 1 :(得分:0)

如果你的CPU是单核,而不是你可以使用这种方式,而不需要交换,但你必须小心。 这是一个简单的案例:

//incrementing of variable
cli        //disable interrupts if we aren't on high priviliegied code execution level
//perform non atomic increment of variable
sti        //enable interrupts if we aren't on high priviliegied code execution level

//reading variable
cli        //disable interrupts if we aren't on high priviliegied code execution level
//perform non atomic read operation
sti        //enable interrupts if we aren't on high priviliegied code execution level

这种方法就像锁一样,但更聪明,更快。 这种方法的唯一弱点是可能的CPU中断延迟,但是如果你的代码在禁用中断的时间短而且快,那么这种延迟通常并不重要。