Atomic swap

Marcel Moolenaar marcel at xcllnt.net
Thu Aug 7 11:55:09 PDT 2003


On Thu, Aug 07, 2003 at 01:44:18PM -0400, Daniel Eischen wrote:
> How about this?
> 
> static __inline void
> atomic_swap_long(volatile long *dst, long val, long *res)
> {
> 	u_int64_t result, temp;
> 
> 	__asm __volatile (
> 		"1:\tldq %1, %3\n\t"	/* load value to store */
> 		"ldq_l %0, %2\n\t"	/* load current value, asserting lock */
> 		"stq_c %1, %2\n\t"	/* attempt to store */
> 		"beq %1, 2f\n\t"	/* if the store failed, spin */
> 		"br 3f\n"		/* it worked, exit */
> 		"2:\tbr 1b\n"		/* *dst not updated, loop */
> 		"3:\n"			/* it worked */
> 		: "=&r" (result), "=&r" (temp)
> 		: "m" (*dst), "m" (val)
> 		: "memory");
> 
> 	*res = result;
> }

The first instruction is wrong. "val" isn't memory. Also,
forget about the branch prediction optimization. It just
makes the code unreadable and we don't even know if it
makes a difference.

The following has been written down without testing (I
dropped the cosmetic \t and instead indented by hand to
make the source code readable, not what is given to the
assembler (per se):

static __inline void
atomic_swap_long(volatile long *dst, long val, long *res)
{
	__asm (	"1: ldq_l   t0,%0\n"
		"   mov     %1,t1\n"
		"   stq_c   t1,%0\n"
		"   beq     t1,1b\n"
		"   stq     t0,%3\n"
	    :: "m"(*dst), "r"(val), "m"(*res) : "memory");
}

In words:
o  Read the current value at *dst in register t0 and lock the address.
o  Since stq_c clobbers it's input and we may need to loop, save val
   in temporary register t1.
o  Store val at *dst (by means of t1) and if the lock failed, retry.
o  Write the old value in register t0 to *res.

-- 
 Marcel Moolenaar	  USPA: A-39004		 marcel at xcllnt.net


More information about the freebsd-alpha mailing list