/* -*- C -*-
// -------------------------------------------------------------------
// Atomic primitives
// Copyright (c) 2008 Leon Bottou. All rights reserved
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// ------------------------------------------------------------------- */
#ifndef ATOMIC_H
#define ATOMIC_H
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
/* This file defines macros or functions performing
// the following atomic operations with a full memory barrier.
//
// int atomicIncrement(int volatile *var)
// { *var += 1; return *var; }
//
// int atomicDecrement(int volatile *var);
// { *var -= 1; return *var; }
//
// int atomicCompareAndSwap(int volatile *var, int oldval, int newval);
// { int val = *var; if (val == oldval) { *var = newval }; returl val; }
//
// int atomicExchange(int volatile *var, int val);
// { int tmp = *var; *var = val; return tmp; }
//
// void* atomicExchangePointer(void* volatile *var, int val);
// { void* tmp = *var; *var = val; return tmp; }
*/
#ifdef __cplusplus
extern "C" {
#endif
#if !defined(ATOMIC_MACROS) && defined(_WIN64)
# define ATOMIC_MACROS "WIN64"
# include <windows.h>
# define atomicIncrement(var) \
(int)(InterlockedIncrement((LONG volatile*)(var)))
# define atomicDecrement(var) \
(int)(InterlockedDecrement((LONG volatile*)(var)))
# define atomicCompareAndSwap(var,ov,nv) \
(InterlockedCompareExchange((LONG volatile*)(var),(LONG)(nv),(LONG)(ov)))
# define atomicExchange(var,nv) \
(int)(InterlockedExchange((LONG volatile*)(var),(LONG)(nv)))
# define atomicExchangePointer(var,nv) \
(void*)(InterlockedExchangePointer((PVOID volatile*)(var),(PVOID)(nv)))
#endif
#if !defined(ATOMIC_MACROS) && defined(_WIN32)
# define ATOMIC_MACROS "WIN32"
# include <windows.h>
# define atomicIncrement(var) \
(int)(InterlockedIncrement((LONG volatile*)(var)))
# define atomicDecrement(var) \
(int)(InterlockedDecrement((LONG volatile*)(var)))
# define atomicCompareAndSwap(var,ov,nv) \
(InterlockedCompareExchange((LONG volatile*)(var),(LONG)(nv),(LONG)(ov)))
# define atomicExchange(var,nv) \
(int)(InterlockedExchange((LONG volatile*)(var),(LONG)(nv)))
# define atomicExchangePointer(var,nv) \
(void*)(InterlockedExchange((LONG volatile*)(var),(LONG)(nv)))
#endif
#if !defined(ATOMIC_MACROS) && defined(HAVE_INTEL_ATOMIC_BUILTINS)
# define ATOMIC_MACROS "INTEL"
# define atomicIncrement(var) \
(__sync_add_and_fetch((int volatile *)(var), 1))
# define atomicDecrement(var) \
(__sync_add_and_fetch((int volatile *)(var), -1))
# define atomicCompareAndSwap(var,ov,nv) \
(__sync_val_compare_and_swap((int volatile*)(var),(int)(ov),(int)(nv)))
# if defined(__i386__) || defined(__x86_64__) || defined(__amd64__)
# define atomicExchange(var,nv) \
(__sync_lock_test_and_set((int volatile*)(var),(int)(nv)))
# define atomicExchangePointer(var,nv) \
(__sync_lock_test_and_set((void* volatile*)(var),(void*)(nv)))
# else
static inline int atomicExchange(int volatile *var, int nv) {
int ov; do { ov = *var; /* overkill */
} while (! __sync_bool_compare_and_swap(var, ov, nv));
return ov;
}
static inline void* atomicExchangePointer(void* volatile *var, void* nv) {
void *ov; do { ov = *var; /* overkill */
} while (! __sync_bool_compare_and_swap(var, ov, nv));
return ov;
}
# endif
#endif
#if !defined(ATOMIC_MACROS) && defined(__GNUC__)
# if defined(__i386__) || defined(__amd64__) || defined(__x86_64__)
# define ATOMIC_MACROS "GNU86"
static inline int atomicIncrement(int volatile *var) {
int ov; __asm__ __volatile__ ("lock; xaddl %0, %1"
: "=r" (ov), "=m" (*var) : "0" (1), "m" (*var) : "cc" );
return ov + 1;
}
static inline int atomicDecrement(int volatile *var) {
int ov; __asm__ __volatile__ ("lock; xaddl %0, %1"
: "=r" (ov), "=m" (*var) : "0" (-1), "m" (*var) : "cc" );
return ov - 1;
}
static inline int atomicExchange(int volatile *var, int nv) {
int ov; __asm__ __volatile__ ("xchgl %0, %1"
: "=r" (ov), "=m" (*var) : "0" (nv), "m" (*var));
return ov;
}
static inline int atomicCompareAndSwap(int volatile *var, int ov, int nv) {
int rv; __asm __volatile ("lock; cmpxchgl %2, %1"
: "=a" (rv), "=m" (*var) : "r" (nv), "0" (ov), "m" (*var) : "cc");
return rv;
}
static inline void *atomicExchangePointer(void * volatile *var, void *nv) {
void *ov; __asm__ __volatile__ (
# if defined(__x86_64__) || defined(__amd64__)
"xchgq %0, %1"
# else
"xchgl %0, %1"
# endif
: "=r" (ov), "=m" (*var) : "0" (nv), "m" (*var));
return ov;
}
# endif
#endif
#ifndef ATOMIC_MACROS
/* emulation */
extern int atomicIncrement(int volatile *var);
extern int atomicDecrement(int volatile *var);
extern int atomicCompareAndSwap(int volatile *var, int ov, int nv);
extern int atomicExchange(int volatile *var, int nv);
extern void* atomicExchangePointer(void* volatile *var, void* nv);
#endif
# ifdef __cplusplus
}
# endif
#endif