> func TestAndSet (n *int) int {
> // semantics:
> // if *n == 1 {
> // return 1
> // }
> // *n = 1
> // return 0
> }
Isn't this simply
! CompareAndSwapInt32(n, 0, 1)
?
> Modified version of CompareAndSwap (as in IBM series 370):
>
> func CSW (i, k *int, n int) int
> // semantics:
> // if *i == *k {
> // *k = n
> // return true
> // }
> // *k = *i
> // return false
As far as I know, modern processors do not have an atomic implementation
of this operation. I'm actually not sure how to implement it atomically
without using some sort of lock.
> Modified version of AddInt:
>
> func FetchAndAdd (k *int, n int) int
> // semantics:
> // old:= *k
> // *k += n
> // return old
Isn't this just
AddInt32(k, n) - n
?
> func Exchange (k, n *int)
> // semantics:
> // *k, *n = *n, *k
Again, as far as I know, modern processors do not have an atomic
implementation of this operation.
Ian
> Modified version of CompareAndSwap (as in IBM series 370):
>
> func CSW (i, k *int, n int) int
> // semantics:
> // if *i == *k {
> // *k = n
> // return true
> // }
> // *k = *i
> // return false
As far as I know, modern processors do not have an atomic implementation
of this operation. I'm actually not sure how to implement it atomically
without using some sort of lock.
for {tmp := *iif CompareAndSwapInt32(i, *k, n) {return true}if tmp != *k {*k = tmpreturn false}}
It's better to provide more general Exchange operation.
for Exchange(locked, 1) == 1 {
// do nothing, maybe sleep some usecs
}
But it is useful beyond that.
> 2.
>
> Modified version of CompareAndSwap (as in IBM series 370):
>
> func CSW (i, k *int, n int) int
> // semantics:
> // if *i == *k {
> // *k = n
> // return true
> // }
> // *k = *i
> // return false
It is really nice. I would do that in the first place.
> 3.
>
> Modified version of AddInt:
>
> func FetchAndAdd (k *int, n int) int
> // semantics:
> // old:= *k
> // *k += n
> // return old
>
> Reason:
> Fair implementation of Lock/Unlock by the
> Ticket spinlock algorithm (modulo overflow of counter)
>
> var ticket, turn int
>
> function Lock() {
> myturn:= FetchAndAdd(&ticket, 1)
> for myturn!= turn {
> // do nothing, maybe sleep some usecs
> }
> }
>
> func Unlock() {
> turn++
> }
You just do:
myturn := Add(&ticket, 1)-1
> 4.
>
> func Exchange (k, n *int)
> // semantics:
> // *k, *n = *n, *k
>
> Reason:
> Another nice spinlock implementation of Lock/Unlock:
>
> var global int
>
> func Lock() {
> local:= 1
> for local == 1 {
> Exchange (&global, &local)
> }
> }
>
> func Unlock() {
> local:= 0
> Exchange (&global, &local)
> }
OK, then TestAndSet is just not required.
IA-32/Intel64 CMPXCHG works exactly this way. And it is the best CAS
interface out there (C++11 uses it).
>> Modified version of AddInt:
>>
>> func FetchAndAdd (k *int, n int) int
>> // semantics:
>> // old:= *k
>> // *k += n
>> // return old
>
> Isn't this just
> AddInt32(k, n) - n
> ?
>
>
>> func Exchange (k, n *int)
>> // semantics:
>> // *k, *n = *n, *k
>
> Again, as far as I know, modern processors do not have an atomic
> implementation of this operation.
IA-32/Intel64 XCHG
>>> func Exchange (k, n *int)
>>> // semantics:
>>> // *k, *n = *n, *k
>>
>> Again, as far as I know, modern processors do not have an atomic
>> implementation of this operation.
>
>
> IA-32/Intel64 XCHG
XCHG only allows one operand to be a memory location, I believe.
Dave.
Lock is irrelevant here (processor loads the current value under lock
anyway). What is relevant is that you should never touch any mutable
shared location more than absolute minimum of times and CAS must
return the most actual value. In your implementation you touch the
location 3 times (read - it is now 3 times more expensive), then you
always return 100% outdated value, in the end if tmp != *k why you
execute CAS at all?
The CAS interface must be
CompareAndSwapInt32(addr, cmp *int32, xchg int32) bool
It is supported in hardware (IA-32/Intel64) in this form, and now it
is default interface in C1x/C++11 as well.
Definitely. I just understand it as 'n' is a local var, that is, the
interface as if:
func Exchange (k *int, n int) int
If both operands are shared memory locations, then it's indeed not
supported in commodity hardware, and it's not worth adding it to
atomic package.
I don't believe this is true. CMPXCHG uses one memory operand,
while this operation uses two. If you simplify this to just the single
memory operand:
func CSW(i *int, k, n int) int
then it is no different in power than the existing CompareAndSwap.
The original poster may have intended that the access to *k not be
atomic, but that is quite confusing to explain: one memory access
is atomic but this other one is not. The existing routine has the same
power as this half-atomic CSW but is easier to describe.
Russ
IBM370 CAS makes non-atomic access to *k.
With current CompareAndSwap one has to constantly reload the value
which is inconvenient and slow.
How slow is it compared to the atomic parts?
It seems like that should be in the noise.