include/SDL_atomic.h
author Ryan C. Gordon <icculus@icculus.org>
Sat, 15 Sep 2012 10:59:39 -0400
changeset 6430 48d519500f7e
parent 6138 4c64952a58fb
child 6463 051403ca44cc
permissions -rw-r--r--
Removed Windows CE support from SDL 2.0.

It's a long-dead platform, and we don't have any way to build for, test, or
maintain it, so there's no sense in doing acrobatics to support it.

If you need Windows CE support, use SDL 1.2. If you need Windows Phone support,
send SDL 2.0 patches for the newer Windows Mobile platform.
bob@3180
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@6138
     3
  Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
bob@3180
     4
slouken@5535
     5
  This software is provided 'as-is', without any express or implied
slouken@5535
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@5535
     7
  arising from the use of this software.
bob@3180
     8
slouken@5535
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@5535
    10
  including commercial applications, and to alter it and redistribute it
slouken@5535
    11
  freely, subject to the following restrictions:
bob@3180
    12
slouken@5535
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@5535
    14
     claim that you wrote the original software. If you use this software
slouken@5535
    15
     in a product, an acknowledgment in the product documentation would be
slouken@5535
    16
     appreciated but is not required.
slouken@5535
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@5535
    18
     misrepresented as being the original software.
slouken@5535
    19
  3. This notice may not be removed or altered from any source distribution.
slouken@5535
    20
*/
bob@3180
    21
bob@3180
    22
/**
slouken@5003
    23
 * \file SDL_atomic.h
slouken@5003
    24
 * 
slouken@5003
    25
 * Atomic operations.
slouken@5003
    26
 * 
slouken@5003
    27
 * IMPORTANT:
slouken@5003
    28
 * If you are not an expert in concurrent lockless programming, you should
slouken@5003
    29
 * only be using the atomic lock and reference counting functions in this
slouken@5003
    30
 * file.  In all other cases you should be protecting your data structures
slouken@5003
    31
 * with full mutexes.
slouken@5003
    32
 * 
slouken@5003
    33
 * The list of "safe" functions to use are:
slouken@5003
    34
 *  SDL_AtomicLock()
slouken@5003
    35
 *  SDL_AtomicUnlock()
slouken@5003
    36
 *  SDL_AtomicIncRef()
slouken@5003
    37
 *  SDL_AtomicDecRef()
slouken@5003
    38
 * 
slouken@5003
    39
 * Seriously, here be dragons!
slouken@5095
    40
 * ^^^^^^^^^^^^^^^^^^^^^^^^^^^
slouken@5003
    41
 *
slouken@5095
    42
 * You can find out a little more about lockless programming and the 
slouken@5095
    43
 * subtle issues that can arise here:
slouken@5095
    44
 * http://msdn.microsoft.com/en-us/library/ee418650%28v=vs.85%29.aspx
slouken@5095
    45
 *
slouken@5098
    46
 * There's also lots of good information here:
slouken@5098
    47
 * http://www.1024cores.net/home/lock-free-algorithms
slouken@5098
    48
 *
slouken@5095
    49
 * These operations may or may not actually be implemented using
slouken@5003
    50
 * processor specific atomic operations. When possible they are
slouken@5003
    51
 * implemented as true processor specific atomic operations. When that
slouken@5003
    52
 * is not possible the are implemented using locks that *do* use the
slouken@5003
    53
 * available atomic operations.
slouken@5003
    54
 *
slouken@5003
    55
 * All of the atomic operations that modify memory are full memory barriers.
bob@3180
    56
 */
bob@3180
    57
bob@3180
    58
#ifndef _SDL_atomic_h_
bob@3180
    59
#define _SDL_atomic_h_
bob@3180
    60
bob@3180
    61
#include "SDL_stdinc.h"
bob@3180
    62
#include "SDL_platform.h"
bob@3180
    63
bob@3180
    64
#include "begin_code.h"
bob@3180
    65
slouken@5073
    66
/* Need to do this here because intrin.h has C++ code in it */
slouken@5073
    67
/* Visual Studio 2005 has a bug where intrin.h conflicts with winnt.h */
icculus@6430
    68
#if defined(_MSC_VER) && (_MSC_VER >= 1500)
slouken@5073
    69
#include <intrin.h>
slouken@5096
    70
#define HAVE_MSC_ATOMICS 1
slouken@5073
    71
#endif
slouken@5073
    72
bob@3180
    73
/* Set up for C function definitions, even when using C++ */
bob@3180
    74
#ifdef __cplusplus
bob@3180
    75
/* *INDENT-OFF* */
bob@3180
    76
extern "C" {
bob@3180
    77
/* *INDENT-ON* */
bob@3180
    78
#endif
bob@3180
    79
bob@3261
    80
/**
slouken@5003
    81
 * \name SDL AtomicLock
slouken@5003
    82
 * 
slouken@5003
    83
 * The atomic locks are efficient spinlocks using CPU instructions,
slouken@5003
    84
 * but are vulnerable to starvation and can spin forever if a thread
slouken@5003
    85
 * holding a lock has been terminated.  For this reason you should
slouken@5003
    86
 * minimize the code executed inside an atomic lock and never do
slouken@5003
    87
 * expensive things like API or system calls while holding them.
slouken@5003
    88
 *
slouken@5003
    89
 * The atomic locks are not safe to lock recursively.
slouken@5003
    90
 *
slouken@5003
    91
 * Porting Note:
slouken@5003
    92
 * The spin lock functions and type are required and can not be
slouken@5003
    93
 * emulated because they are used in the atomic emulation code.
bob@3261
    94
 */
slouken@3407
    95
/*@{*/
bob@3261
    96
slouken@5003
    97
typedef int SDL_SpinLock;
bob@3261
    98
bob@3261
    99
/**
slouken@5003
   100
 * \brief Try to lock a spin lock by setting it to a non-zero value.
slouken@5003
   101
 * 
slouken@5003
   102
 * \param lock Points to the lock.
slouken@5003
   103
 *
slouken@5003
   104
 * \return SDL_TRUE if the lock succeeded, SDL_FALSE if the lock is already held.
slouken@5003
   105
 */
slouken@5003
   106
extern DECLSPEC SDL_bool SDLCALL SDL_AtomicTryLock(SDL_SpinLock *lock);
slouken@5003
   107
slouken@5003
   108
/**
slouken@5003
   109
 * \brief Lock a spin lock by setting it to a non-zero value.
slouken@5003
   110
 * 
slouken@5003
   111
 * \param lock Points to the lock.
bob@3261
   112
 */
bob@3261
   113
extern DECLSPEC void SDLCALL SDL_AtomicLock(SDL_SpinLock *lock);
bob@3261
   114
bob@3261
   115
/**
slouken@5003
   116
 * \brief Unlock a spin lock by setting it to 0. Always returns immediately
bob@3261
   117
 *
slouken@5003
   118
 * \param lock Points to the lock.
bob@3261
   119
 */
bob@3261
   120
extern DECLSPEC void SDLCALL SDL_AtomicUnlock(SDL_SpinLock *lock);
bob@3261
   121
slouken@3407
   122
/*@}*//*SDL AtomicLock*/
bob@3202
   123
slouken@5095
   124
slouken@5135
   125
/**
slouken@5135
   126
 * The compiler barrier prevents the compiler from reordering
slouken@5135
   127
 * reads and writes to globally visible variables across the call.
slouken@5135
   128
 */
slouken@5095
   129
#ifdef _MSC_VER
slouken@5095
   130
void _ReadWriteBarrier(void);
slouken@5095
   131
#pragma intrinsic(_ReadWriteBarrier)
slouken@5095
   132
#define SDL_CompilerBarrier()   _ReadWriteBarrier()
slouken@5097
   133
#elif defined(__GNUC__)
slouken@5095
   134
#define SDL_CompilerBarrier()   __asm__ __volatile__ ("" : : : "memory")
slouken@5095
   135
#else
slouken@5095
   136
#define SDL_CompilerBarrier()   \
slouken@5095
   137
({ SDL_SpinLock _tmp = 0; SDL_AtomicLock(&_tmp); SDL_AtomicUnlock(&_tmp); })
slouken@5095
   138
#endif
slouken@5095
   139
slouken@5006
   140
/* Platform specific optimized versions of the atomic functions,
slouken@5006
   141
 * you can disable these by defining SDL_DISABLE_ATOMIC_INLINE
slouken@5006
   142
 */
icculus@5552
   143
#if defined(SDL_ATOMIC_DISABLED) && SDL_ATOMIC_DISABLED
slouken@5225
   144
#define SDL_DISABLE_ATOMIC_INLINE
slouken@5225
   145
#endif
slouken@5006
   146
#ifndef SDL_DISABLE_ATOMIC_INLINE
slouken@5006
   147
slouken@5097
   148
#ifdef HAVE_MSC_ATOMICS
slouken@5004
   149
slouken@5016
   150
#define SDL_AtomicSet(a, v)     _InterlockedExchange((long*)&(a)->value, (v))
slouken@5016
   151
#define SDL_AtomicAdd(a, v)     _InterlockedExchangeAdd((long*)&(a)->value, (v))
slouken@5016
   152
#define SDL_AtomicCAS(a, oldval, newval) (_InterlockedCompareExchange((long*)&(a)->value, (newval), (oldval)) == (oldval))
slouken@5095
   153
#define SDL_AtomicSetPtr(a, v)  _InterlockedExchangePointer((a), (v))
slouken@5012
   154
#if _M_IX86
slouken@5012
   155
#define SDL_AtomicCASPtr(a, oldval, newval) (_InterlockedCompareExchange((long*)(a), (long)(newval), (long)(oldval)) == (long)(oldval))
slouken@5012
   156
#else
slouken@5012
   157
#define SDL_AtomicCASPtr(a, oldval, newval) (_InterlockedCompareExchangePointer((a), (newval), (oldval)) == (oldval))
slouken@5012
   158
#endif
slouken@5004
   159
slouken@5097
   160
#elif defined(__MACOSX__)
slouken@5004
   161
#include <libkern/OSAtomic.h>
slouken@5004
   162
slouken@5095
   163
#define SDL_AtomicCAS(a, oldval, newval) OSAtomicCompareAndSwap32Barrier((oldval), (newval), &(a)->value)
slouken@5004
   164
#if SIZEOF_VOIDP == 4
slouken@5004
   165
#define SDL_AtomicCASPtr(a, oldval, newval) OSAtomicCompareAndSwap32Barrier((int32_t)(oldval), (int32_t)(newval), (int32_t*)(a))
slouken@5004
   166
#elif SIZEOF_VOIDP == 8
slouken@5004
   167
#define SDL_AtomicCASPtr(a, oldval, newval) OSAtomicCompareAndSwap64Barrier((int64_t)(oldval), (int64_t)(newval), (int64_t*)(a))
slouken@5004
   168
#endif
slouken@5004
   169
slouken@5097
   170
#elif defined(HAVE_GCC_ATOMICS)
slouken@5004
   171
slouken@5004
   172
#define SDL_AtomicSet(a, v)     __sync_lock_test_and_set(&(a)->value, v)
slouken@5004
   173
#define SDL_AtomicAdd(a, v)     __sync_fetch_and_add(&(a)->value, v)
slouken@5095
   174
#define SDL_AtomicSetPtr(a, v)  __sync_lock_test_and_set(a, v)
slouken@5004
   175
#define SDL_AtomicCAS(a, oldval, newval) __sync_bool_compare_and_swap(&(a)->value, oldval, newval)
slouken@5004
   176
#define SDL_AtomicCASPtr(a, oldval, newval) __sync_bool_compare_and_swap(a, oldval, newval)
slouken@5004
   177
slouken@5004
   178
#endif
slouken@3407
   179
slouken@5006
   180
#endif /* !SDL_DISABLE_ATOMIC_INLINE */
slouken@5006
   181
slouken@5006
   182
slouken@3407
   183
/**
slouken@5003
   184
 * \brief A type representing an atomic integer value.  It is a struct
slouken@5003
   185
 *        so people don't accidentally use numeric operations on it.
bob@3199
   186
 */
slouken@5003
   187
#ifndef SDL_atomic_t_defined
slouken@5003
   188
typedef struct { int value; } SDL_atomic_t;
slouken@5003
   189
#endif
bob@3237
   190
bob@3199
   191
/**
slouken@5097
   192
 * \brief Set an atomic variable to a new value if it is currently an old value.
slouken@5097
   193
 *
slouken@5097
   194
 * \return SDL_TRUE if the atomic variable was set, SDL_FALSE otherwise.
slouken@5097
   195
 *
slouken@5097
   196
 * \note If you don't know what this function is for, you shouldn't use it!
slouken@5097
   197
*/
slouken@5097
   198
#ifndef SDL_AtomicCAS
slouken@5097
   199
#define SDL_AtomicCAS SDL_AtomicCAS_
slouken@5097
   200
#endif
slouken@5097
   201
extern DECLSPEC SDL_bool SDLCALL SDL_AtomicCAS_(SDL_atomic_t *a, int oldval, int newval);
slouken@5097
   202
slouken@5097
   203
/**
slouken@5003
   204
 * \brief Set an atomic variable to a value.
slouken@5003
   205
 *
slouken@5003
   206
 * \return The previous value of the atomic variable.
bob@3199
   207
 */
slouken@5003
   208
#ifndef SDL_AtomicSet
slouken@5097
   209
static __inline__ int SDL_AtomicSet(SDL_atomic_t *a, int v)
slouken@5097
   210
{
slouken@5097
   211
    int value;
slouken@5097
   212
    do {
slouken@5097
   213
        value = a->value;
slouken@5097
   214
    } while (!SDL_AtomicCAS(a, value, v));
slouken@5097
   215
    return value;
slouken@5097
   216
}
slouken@5003
   217
#endif
bob@3237
   218
bob@3199
   219
/**
slouken@5003
   220
 * \brief Get the value of an atomic variable
bob@3199
   221
 */
slouken@5003
   222
#ifndef SDL_AtomicGet
slouken@5097
   223
static __inline__ int SDL_AtomicGet(SDL_atomic_t *a)
slouken@5097
   224
{
slouken@5097
   225
    int value = a->value;
slouken@5097
   226
    SDL_CompilerBarrier();
slouken@5097
   227
    return value;
slouken@5097
   228
}
slouken@5003
   229
#endif
bob@3237
   230
bob@3199
   231
/**
slouken@5095
   232
 * \brief Add to an atomic variable.
slouken@5003
   233
 *
slouken@5003
   234
 * \return The previous value of the atomic variable.
slouken@5095
   235
 *
slouken@5095
   236
 * \note This same style can be used for any number operation
bob@3199
   237
 */
slouken@5003
   238
#ifndef SDL_AtomicAdd
slouken@5097
   239
static __inline__ int SDL_AtomicAdd(SDL_atomic_t *a, int v)
slouken@5097
   240
{
slouken@5097
   241
    int value;
slouken@5097
   242
    do {
slouken@5097
   243
        value = a->value;
slouken@5097
   244
    } while (!SDL_AtomicCAS(a, value, (value + v)));
slouken@5097
   245
    return value;
slouken@5097
   246
}
slouken@5003
   247
#endif
bob@3237
   248
bob@3199
   249
/**
slouken@5003
   250
 * \brief Increment an atomic variable used as a reference count.
bob@3199
   251
 */
slouken@5003
   252
#ifndef SDL_AtomicIncRef
slouken@5095
   253
#define SDL_AtomicIncRef(a)    SDL_AtomicAdd(a, 1)
slouken@5003
   254
#endif
bob@3237
   255
bob@3199
   256
/**
slouken@5003
   257
 * \brief Decrement an atomic variable used as a reference count.
slouken@5003
   258
 *
slouken@5095
   259
 * \return SDL_TRUE if the variable reached zero after decrementing,
slouken@5003
   260
 *         SDL_FALSE otherwise
bob@3199
   261
 */
slouken@5003
   262
#ifndef SDL_AtomicDecRef
slouken@5095
   263
#define SDL_AtomicDecRef(a)    (SDL_AtomicAdd(a, -1) == 1)
slouken@5003
   264
#endif
bob@3237
   265
bob@3199
   266
/**
slouken@5003
   267
 * \brief Set a pointer to a new value if it is currently an old value.
slouken@5003
   268
 *
slouken@5004
   269
 * \return SDL_TRUE if the pointer was set, SDL_FALSE otherwise.
slouken@5003
   270
 *
slouken@5003
   271
 * \note If you don't know what this function is for, you shouldn't use it!
slouken@5003
   272
*/
slouken@5003
   273
#ifndef SDL_AtomicCASPtr
slouken@5095
   274
#define SDL_AtomicCASPtr SDL_AtomicCASPtr_
slouken@5003
   275
#endif
slouken@5136
   276
extern DECLSPEC SDL_bool SDLCALL SDL_AtomicCASPtr_(void* *a, void *oldval, void *newval);
slouken@3407
   277
slouken@5097
   278
/**
slouken@5097
   279
 * \brief Set a pointer to a value atomically.
slouken@5097
   280
 *
slouken@5097
   281
 * \return The previous value of the pointer.
slouken@5097
   282
 */
slouken@5097
   283
#ifndef SDL_AtomicSetPtr
slouken@5097
   284
static __inline__ void* SDL_AtomicSetPtr(void* *a, void* v)
slouken@5097
   285
{
slouken@5097
   286
    void* value;
slouken@5097
   287
    do {
slouken@5097
   288
        value = *a;
slouken@5097
   289
    } while (!SDL_AtomicCASPtr(a, value, v));
slouken@5097
   290
    return value;
slouken@5097
   291
}
slouken@5097
   292
#endif
slouken@5097
   293
slouken@5097
   294
/**
slouken@5097
   295
 * \brief Get the value of a pointer atomically.
slouken@5097
   296
 */
slouken@5097
   297
#ifndef SDL_AtomicGetPtr
slouken@5097
   298
static __inline__ void* SDL_AtomicGetPtr(void* *a)
slouken@5097
   299
{
slouken@5097
   300
    void* value = *a;
slouken@5097
   301
    SDL_CompilerBarrier();
slouken@5097
   302
    return value;
slouken@5097
   303
}
slouken@5097
   304
#endif
slouken@5097
   305
slouken@5097
   306
bob@3180
   307
/* Ends C function definitions when using C++ */
bob@3180
   308
#ifdef __cplusplus
bob@3180
   309
/* *INDENT-OFF* */
bob@3180
   310
}
bob@3180
   311
/* *INDENT-ON* */
bob@3180
   312
#endif
bob@3180
   313
bob@3180
   314
#include "close_code.h"
bob@3180
   315
bob@3180
   316
#endif /* _SDL_atomic_h_ */
bob@3180
   317
bob@3180
   318
/* vi: set ts=4 sw=4 expandtab: */