/* -*- C -*-
// -------------------------------------------------------------------
// MiniLock - a quick user space lock 
// 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

/* ------------------------------------------------------------
//  These are primitives to implement very quick locks.
//  
//  Non-blocking usage:
//    if (atomicAcquire(&lock)) {
//      ... // do something protected by the lock
//      atomicRelease(&lock);
//    }
//
//  Blocking usage:
//    atomicAcquireOrSpin(&lock);
//    // do something protected by the lock
//    atomicRelease(&lock);
//
//  Rules of thumb:
//  - Acquire and release from the same function with 
//    no intervening function calls.
//  - Do not use AcquireOrSpin for waiting a long time.
//    No more than a few microseconds please.
//
//  Memory ordering:
//  Viewed from another processor
//  - load/stores performed by this cpu after the acquire 
//    cannot appear to have happened before the acquire.
//  - load/stores performed by this cpu before the release 
//    cannot appear to have happened after the release.
//
//  Implementation:
//  All depends on the definitions from the initial include file.
//  To perform the non blocking operations:
//  - use intel builtins if available (icc, gcc>=4.1).
//  - use win32 interlocked operations (win32).
//  - use inline assembly code for some platforms.
//  - use pthreads
//  To perform the waiting when spinning takes to long:
//  - use win32 critical sections and events.
//  - use pthreads mutex and conditions.
//  This is controlled by the preprocessor symbols:
//    WIN32 
//    __GNUC__ __GNUC_MAJOR__ __GNUC_MINOR__  
//    __INTEL_COMPILER
//  and can be overriden by defining
//    HAVE_INTEL_ATOMIC_BUILTINS
//    OBEY_HAVE_INTEL_ATOMIC_BUILTINS
//  and by tweaking the files include in atomic.h.
// ------------------------------------------------------------ */


# ifdef __cplusplus
extern "C" {
#endif
#ifdef WIN32
#pragma warning( disable : 4251 )
# ifdef DLL_EXPORT
#  define DJVUAPI __declspec(dllexport)
# else
#  define DJVUAPI __declspec(dllimport)
# endif
#endif
#ifndef DJVUAPI
# define DJVUAPI
#endif

/* { int tmp = *lock; *lock = 1; return !tmp; }. */
int DJVUAPI atomicAcquire(int volatile *lock);
  
/* { while (!atomicAcquire(lock)) { spin/yield/wait } } */
void DJVUAPI atomicAcquireOrSpin(int volatile *lock);

/* { *lock = 0; } */
void DJVUAPI atomicRelease(int volatile *lock);

/* { *var += 1; return *var; } */
int DJVUAPI atomicIncrement(int volatile *var);

/* { *var -= 1; return *var; } */
int DJVUAPI atomicDecrement(int volatile *var);

/* { if (*var == oldval) { *var = newval; return TRUE; } return FALSE; } */
int DJVUAPI atomicCompareAndSwap(int volatile *var, int oldval, int newval);


# ifdef __cplusplus
}
#endif

#endif
