apr-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Colin <share-apa...@think42.com>
Subject Atomic Operations Assembly
Date Fri, 08 Sep 2006 14:10:37 GMT
Hallo List,

for one of my projects I was recently looking for implementations of some atomic primitives
for Sparc and PowerPC, and also stumbled over the code in apr_atomic.c. 

A few weeks later I am returning here with a bug report and an implementation for SPARC.

1) PowerPC Bug

The Bug is actually in the Chip, not in the APR code; as stated in erratum number 12 in

http://www-306.ibm.com/chips/techlib/techlib.nsf/techdocs/79B6E24422AA101287256E93006C957E/$file/PowerPC_970FX_errata_DD3.X_V1.7.pdf

one must not spin in a three-instruction lwarx/ldarx loop because there is obviously at least
one PowerPC chip that can enter a livelock...

(Unfortunately I don't have a solution for this one; I am on Mac OS X where the OS supplies
the spinlock primitive that I actually need; the Mac OS X version is documented as using exponential
back-off.)


2) Sparc/GCC Code

The following code was written by me and has survived both some testing and some real-world
usage; if there is interest in it, I would contribute it to apr under two conditions:

- Somebody reviews it.
- My name appears somewhere as contributor.

(Yes, I'm doing this a) for vanity and b) because I don't know anybody who can give me a real
review of the code.)

Obviously the typedef and the fact that C++ references are used would need to be changed to
use the APR data types and C pointers.

Regards, Colin


typedef uint32_t atomic_t;

inline bool atomic_compare_and_swap( const atomic_t old_value, atomic_t new_value, atomic_t
& the_value )
{
   __asm__ __volatile__ ( "cas [%1],%2,%0"
                          : "+r" ( new_value )
                          : "r" ( & the_value ), "r" ( old_value ) );

   return new_value == old_value;
}

inline atomic_t atomic_load( atomic_t & the_value )
{
   __asm__ __volatile__ ( "membar #StoreLoad | #LoadLoad" : : : "memory" );
   return * const_cast< volatile atomic_t * >( & the_value );
}

inline void atomic_store( const atomic_t new_value, atomic_t & the_value )
{
   * const_cast< volatile atomic_t * >( & the_value ) = new_value;
   __asm__ __volatile__ ( "membar #StoreStore | #StoreLoad" : : : "memory" );
}

inline atomic_t atomic_swap( const atomic_t new_value, atomic_t & the_value )
{
   atomic_t nrv;
   
   do {
      nrv = atomic_load( the_value );
   } while ( ! atomic_compare_and_swap( nrv, new_value, the_value ) );
   
   return nrv;
}

inline atomic_t atomic_add( const atomic_t increment, atomic_t & the_value )
{
   atomic_t nrv;
   
   do {
      nrv = atomic_load( the_value );
   } while ( ! atomic_compare_and_swap( nrv, nrv + increment, the_value ) );
   
   return nrv + increment;
}

static const atomic_t spin_lock_init = 0;

inline void spin_lock_lock( atomic_t & s )
{
   atomic_t tmp;
   
   do {
      __asm__ __volatile__( "ldstub %1,%0; membar #LoadLoad"
                            : "=r" ( tmp ), "+m" ( * & s )
                            :
                            : "memory" );
   } while ( tmp );
}

inline void spin_lock_unlock( atomic_t & s )
{
   __asm__ __volatile__( "membar #StoreStore | #LoadStore | #StoreLoad; stb %1,%0"
                         : "=m"( * & s )
                         : "r" ( 0 ) );
}

Mime
View raw message