// This file defines some atomic operations.
// For most platforms the gcc builtins are used, using macros.
// ARM is treated differently because it only has one atomic instruction, and
// the others have to be emulated.  Because of this:
//
// - When swap is the only atomic operation that a particular algorithm needs,
//   it can use simple_atomic_swap() and do reads and writes using normal assignments.
// - When an algorithm needs other atomic operations (e.g. increment) it must
//   use the atomic_* operations including atomic_read() and atomic_write.  It
//   must not use simple_atomic_swap or do reads and writes using assignments.


#ifdef __arm__

// On ARM we only have atomic swap, and that's not available as a gcc builtin.
// So first some asm to do a swap:

static inline int simple_atomic_swap(int& mem, int newval)
{
  int oldval;
  asm volatile ("swp\t%0, %1, [%2]"
               :"=&r"(oldval)
               :"r"  (newval),
                "r"  (&mem)
               :"memory");
  return oldval;
}

// The other atomic operations are emulated by means of a sentinel value, -1:
// the variable is first swapped with the sentinel, the operation is performed and
// the modified value is written back.  Any thread that wants to read the
// variable must re-try if it reads the sentinel.

static inline int atomic_read_and_lock(int& mem)
{
  do {
    int val = simple_atomic_swap(mem,-1);
    if (val != -1) {
      return val;
    }
  } while (1);
}

static inline int atomic_post_inc(int& mem)
{
  int val = atomic_read_and_lock(mem);
  mem = val + 1;
  return val;
}

static inline int atomic_post_dec(int& mem)
{
  int val = atomic_read_and_lock(mem);
  mem = val - 1;
  return val;
}

static inline int atomic_swap(int& mem, int newval)
{
  int oldval = atomic_read_and_lock(mem);
  mem = newval;
  return oldval;
}

static inline int atomic_compare_and_swap(int& mem, int expect, int newval)
{
  int oldval = atomic_read_and_lock(mem);
  if (oldval==expect) {
    mem = newval;
  } else {
    mem = oldval;
  }
  return oldval;
}

static inline int atomic_read(volatile int& mem)
{
  do {
    int val = mem;
    if (val != -1) {
      return val;
    }
  } while (1);
}  

static inline int atomic_write(int& mem, int newval)
{
  atomic_read_and_lock(mem);
  mem = newval;
  return newval;
}



#else

// Other architectures:

#define simple_atomic_swap(var,newval)             __sync_lock_test_and_set(&var,newval)
#define atomic_swap(var,newval)                    simple_atomic_swap(var,newval)
#define atomic_post_inc(var)                       __sync_fetch_and_add(&var,1)
#define atomic_post_dec(var)                       __sync_fetch_and_sub(&var,1)
#define atomic_compare_and_swap(var,expect,newval) __sync_val_compare_and_swap(&var,expect,newval)
#define atomic_read(var)                           (var)
#define atomic_write(var,val)                      var=(val)


#endif


// This is the same on all architectures:

#define barrier() __sync_synchronize()

