include/SDL_atomic.h
author Sam Lantinga <slouken@libsdl.org>
Sun, 16 Jan 2011 17:45:42 -0800
changeset 5006 8e8876e4aec6
parent 5005 7f0265279b68
child 5012 2e282002bac3
permissions -rw-r--r--
Include windows.h in SDL_atomic.h by default, but don't include the atomic API in SDL.h
This allows all SDL code to take advantage of the atomic intrinsics on Windows, but doesn't cause applications just including SDL.h to pull in windows.h
bob@3180
     1
/*
bob@3180
     2
    SDL - Simple DirectMedia Layer
slouken@3697
     3
    Copyright (C) 1997-2010 Sam Lantinga
bob@3180
     4
bob@3180
     5
    This library is free software; you can redistribute it and/or
bob@3180
     6
    modify it under the terms of the GNU Lesser General Public
bob@3180
     7
    License as published by the Free Software Foundation; either
bob@3180
     8
    version 2.1 of the License, or (at your option) any later version.
bob@3180
     9
bob@3180
    10
    This library is distributed in the hope that it will be useful,
bob@3180
    11
    but WITHOUT ANY WARRANTY; without even the implied warranty of
bob@3180
    12
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
bob@3180
    13
    Lesser General Public License for more details.
bob@3180
    14
bob@3180
    15
    You should have received a copy of the GNU Lesser General Public
bob@3180
    16
    License along with this library; if not, write to the Free Software
bob@3180
    17
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
bob@3180
    18
bob@3180
    19
    Sam Lantinga
bob@3180
    20
    slouken@libsdl.org
bob@3180
    21
 */
bob@3180
    22
bob@3180
    23
/**
slouken@5003
    24
 * \file SDL_atomic.h
slouken@5003
    25
 * 
slouken@5003
    26
 * Atomic operations.
slouken@5003
    27
 * 
slouken@5003
    28
 * IMPORTANT:
slouken@5003
    29
 * If you are not an expert in concurrent lockless programming, you should
slouken@5003
    30
 * only be using the atomic lock and reference counting functions in this
slouken@5003
    31
 * file.  In all other cases you should be protecting your data structures
slouken@5003
    32
 * with full mutexes.
slouken@5003
    33
 * 
slouken@5003
    34
 * The list of "safe" functions to use are:
slouken@5003
    35
 *  SDL_AtomicLock()
slouken@5003
    36
 *  SDL_AtomicUnlock()
slouken@5003
    37
 *  SDL_AtomicIncRef()
slouken@5003
    38
 *  SDL_AtomicDecRef()
slouken@5003
    39
 * 
slouken@5003
    40
 * Seriously, here be dragons!
slouken@5003
    41
 *
slouken@5003
    42
 * These operations may, or may not, actually be implemented using
slouken@5003
    43
 * processor specific atomic operations. When possible they are
slouken@5003
    44
 * implemented as true processor specific atomic operations. When that
slouken@5003
    45
 * is not possible the are implemented using locks that *do* use the
slouken@5003
    46
 * available atomic operations.
slouken@5003
    47
 *
slouken@5003
    48
 * All of the atomic operations that modify memory are full memory barriers.
bob@3180
    49
 */
bob@3180
    50
bob@3180
    51
#ifndef _SDL_atomic_h_
bob@3180
    52
#define _SDL_atomic_h_
bob@3180
    53
bob@3180
    54
#include "SDL_stdinc.h"
bob@3180
    55
#include "SDL_platform.h"
bob@3180
    56
bob@3180
    57
#include "begin_code.h"
bob@3180
    58
bob@3180
    59
/* Set up for C function definitions, even when using C++ */
bob@3180
    60
#ifdef __cplusplus
bob@3180
    61
/* *INDENT-OFF* */
bob@3180
    62
extern "C" {
bob@3180
    63
/* *INDENT-ON* */
bob@3180
    64
#endif
bob@3180
    65
bob@3261
    66
/**
slouken@5003
    67
 * \name SDL AtomicLock
slouken@5003
    68
 * 
slouken@5003
    69
 * The atomic locks are efficient spinlocks using CPU instructions,
slouken@5003
    70
 * but are vulnerable to starvation and can spin forever if a thread
slouken@5003
    71
 * holding a lock has been terminated.  For this reason you should
slouken@5003
    72
 * minimize the code executed inside an atomic lock and never do
slouken@5003
    73
 * expensive things like API or system calls while holding them.
slouken@5003
    74
 *
slouken@5003
    75
 * The atomic locks are not safe to lock recursively.
slouken@5003
    76
 *
slouken@5003
    77
 * Porting Note:
slouken@5003
    78
 * The spin lock functions and type are required and can not be
slouken@5003
    79
 * emulated because they are used in the atomic emulation code.
bob@3261
    80
 */
slouken@3407
    81
/*@{*/
bob@3261
    82
slouken@5003
    83
typedef int SDL_SpinLock;
bob@3261
    84
bob@3261
    85
/**
slouken@5003
    86
 * \brief Try to lock a spin lock by setting it to a non-zero value.
slouken@5003
    87
 * 
slouken@5003
    88
 * \param lock Points to the lock.
slouken@5003
    89
 *
slouken@5003
    90
 * \return SDL_TRUE if the lock succeeded, SDL_FALSE if the lock is already held.
slouken@5003
    91
 */
slouken@5003
    92
extern DECLSPEC SDL_bool SDLCALL SDL_AtomicTryLock(SDL_SpinLock *lock);
slouken@5003
    93
slouken@5003
    94
/**
slouken@5003
    95
 * \brief Lock a spin lock by setting it to a non-zero value.
slouken@5003
    96
 * 
slouken@5003
    97
 * \param lock Points to the lock.
bob@3261
    98
 */
bob@3261
    99
extern DECLSPEC void SDLCALL SDL_AtomicLock(SDL_SpinLock *lock);
bob@3261
   100
bob@3261
   101
/**
slouken@5003
   102
 * \brief Unlock a spin lock by setting it to 0. Always returns immediately
bob@3261
   103
 *
slouken@5003
   104
 * \param lock Points to the lock.
bob@3261
   105
 */
bob@3261
   106
extern DECLSPEC void SDLCALL SDL_AtomicUnlock(SDL_SpinLock *lock);
bob@3261
   107
slouken@3407
   108
/*@}*//*SDL AtomicLock*/
bob@3202
   109
slouken@5006
   110
/* Platform specific optimized versions of the atomic functions,
slouken@5006
   111
 * you can disable these by defining SDL_DISABLE_ATOMIC_INLINE
slouken@5006
   112
 */
slouken@5006
   113
#ifndef SDL_DISABLE_ATOMIC_INLINE
slouken@5006
   114
slouken@5006
   115
#if defined(__WIN32__)
slouken@5005
   116
/* Don't include windows.h, since it may hose code that isn't expecting it,
slouken@5005
   117
   but if someone has already included it, this is fair game... */
slouken@5004
   118
#define WIN32_LEAN_AND_MEAN
slouken@5004
   119
#include <windows.h>
slouken@5004
   120
slouken@5004
   121
#define SDL_AtomicSet(a, v)     InterlockedExchange(&(a)->value, v)
slouken@5004
   122
#define SDL_AtomicGet(a)        ((a)->value)
slouken@5004
   123
#define SDL_AtomicAdd(a, v)     InterlockedAdd(&(a)->value, v)
slouken@5004
   124
#define SDL_AtomicCAS(a, oldval, newval) (InterlockedCompareExchange(&(a)->value, newval, oldval) == (oldval))
slouken@5004
   125
#define SDL_AtomicSetPtr(a, v)  InterlockedExchangePointer(a, v)
slouken@5004
   126
#define SDL_AtomicGetPtr(a)     (*(a))
slouken@5004
   127
#define SDL_AtomicCASPtr(a, oldval, newval) (InterlockedCompareExchangePointer(a, newval, oldval) == (oldval))
slouken@5004
   128
slouken@5004
   129
#elif defined(__MACOSX__)
slouken@5004
   130
#include <libkern/OSAtomic.h>
slouken@5004
   131
slouken@5004
   132
#define SDL_AtomicSet(a, v) \
slouken@5004
   133
({                          \
slouken@5004
   134
    int oldvalue;           \
slouken@5004
   135
                            \
slouken@5004
   136
    do {                    \
slouken@5004
   137
        oldvalue = (a)->value; \
slouken@5004
   138
    } while (!OSAtomicCompareAndSwap32Barrier(oldvalue, v, &(a)->value)); \
slouken@5004
   139
                            \
slouken@5004
   140
    oldvalue;               \
slouken@5004
   141
})
slouken@5004
   142
#define SDL_AtomicGet(a)        ((a)->value)
slouken@5004
   143
#define SDL_AtomicAdd(a, v) \
slouken@5004
   144
({                          \
slouken@5004
   145
    int oldvalue;           \
slouken@5004
   146
                            \
slouken@5004
   147
    do {                    \
slouken@5004
   148
        oldvalue = (a)->value; \
slouken@5004
   149
    } while (!OSAtomicCompareAndSwap32Barrier(oldvalue, oldvalue+v, &(a)->value)); \
slouken@5004
   150
                            \
slouken@5004
   151
    oldvalue;               \
slouken@5004
   152
})
slouken@5004
   153
#define SDL_AtomicCAS(a, oldval, newval) OSAtomicCompareAndSwap32Barrier(oldval, newval, &(a)->value)
slouken@5004
   154
#define SDL_AtomicSetPtr(a, v)  (*(a) = v, OSMemoryBarrier())
slouken@5004
   155
#define SDL_AtomicGetPtr(a)     (*(a))
slouken@5004
   156
#if SIZEOF_VOIDP == 4
slouken@5004
   157
#define SDL_AtomicCASPtr(a, oldval, newval) OSAtomicCompareAndSwap32Barrier((int32_t)(oldval), (int32_t)(newval), (int32_t*)(a))
slouken@5004
   158
#elif SIZEOF_VOIDP == 8
slouken@5004
   159
#define SDL_AtomicCASPtr(a, oldval, newval) OSAtomicCompareAndSwap64Barrier((int64_t)(oldval), (int64_t)(newval), (int64_t*)(a))
slouken@5004
   160
#endif
slouken@5004
   161
slouken@5004
   162
#elif defined(HAVE_GCC_ATOMICS)
slouken@5004
   163
slouken@5004
   164
#define SDL_AtomicSet(a, v)     __sync_lock_test_and_set(&(a)->value, v)
slouken@5004
   165
#define SDL_AtomicGet(a)        ((a)->value)
slouken@5004
   166
#define SDL_AtomicAdd(a, v)     __sync_fetch_and_add(&(a)->value, v)
slouken@5004
   167
#define SDL_AtomicCAS(a, oldval, newval) __sync_bool_compare_and_swap(&(a)->value, oldval, newval)
slouken@5004
   168
#define SDL_AtomicSetPtr(a, v)  (*(a) = v, __sync_synchronize())
slouken@5004
   169
#define SDL_AtomicGetPtr(a)     (*(a))
slouken@5004
   170
#define SDL_AtomicCASPtr(a, oldval, newval) __sync_bool_compare_and_swap(a, oldval, newval)
slouken@5004
   171
slouken@5004
   172
#endif
slouken@3407
   173
slouken@5006
   174
#endif /* !SDL_DISABLE_ATOMIC_INLINE */
slouken@5006
   175
slouken@5006
   176
slouken@3407
   177
/**
slouken@5003
   178
 * \brief A type representing an atomic integer value.  It is a struct
slouken@5003
   179
 *        so people don't accidentally use numeric operations on it.
bob@3199
   180
 */
slouken@5003
   181
#ifndef SDL_atomic_t_defined
slouken@5003
   182
typedef struct { int value; } SDL_atomic_t;
slouken@5003
   183
#endif
bob@3237
   184
bob@3199
   185
/**
slouken@5003
   186
 * \brief Set an atomic variable to a value.
slouken@5003
   187
 *
slouken@5003
   188
 * \return The previous value of the atomic variable.
bob@3199
   189
 */
slouken@5003
   190
#ifndef SDL_AtomicSet
slouken@5003
   191
extern DECLSPEC int SDLCALL SDL_AtomicSet(SDL_atomic_t *a, int value);
slouken@5003
   192
#endif
bob@3237
   193
bob@3199
   194
/**
slouken@5003
   195
 * \brief Get the value of an atomic variable
bob@3199
   196
 */
slouken@5003
   197
#ifndef SDL_AtomicGet
slouken@5003
   198
extern DECLSPEC int SDLCALL SDL_AtomicGet(SDL_atomic_t *a);
slouken@5003
   199
#endif
bob@3237
   200
bob@3199
   201
/**
slouken@5003
   202
 * \brief  Add to an atomic variable.
slouken@5003
   203
 *
slouken@5003
   204
 * \return The previous value of the atomic variable.
bob@3199
   205
 */
slouken@5003
   206
#ifndef SDL_AtomicAdd
slouken@5003
   207
extern DECLSPEC int SDLCALL SDL_AtomicAdd(SDL_atomic_t *a, int value);
slouken@5003
   208
#endif
bob@3237
   209
bob@3199
   210
/**
slouken@5003
   211
 * \brief Increment an atomic variable used as a reference count.
bob@3199
   212
 */
slouken@5003
   213
#ifndef SDL_AtomicIncRef
slouken@5003
   214
extern DECLSPEC void SDLCALL SDL_AtomicIncRef(SDL_atomic_t *a);
slouken@5003
   215
#endif
bob@3237
   216
bob@3199
   217
/**
slouken@5003
   218
 * \brief Decrement an atomic variable used as a reference count.
slouken@5003
   219
 *
slouken@5003
   220
 * \return SDL_TRUE if the variable has reached zero after decrementing,
slouken@5003
   221
 *         SDL_FALSE otherwise
bob@3199
   222
 */
slouken@5003
   223
#ifndef SDL_AtomicDecRef
slouken@5003
   224
extern DECLSPEC SDL_bool SDLCALL SDL_AtomicDecRef(SDL_atomic_t *a);
slouken@5003
   225
#endif
bob@3237
   226
bob@3199
   227
/**
slouken@5003
   228
 * \brief Set an atomic variable to a new value if it is currently an old value.
slouken@5003
   229
 *
slouken@5004
   230
 * \return SDL_TRUE if the atomic variable was set, SDL_FALSE otherwise.
slouken@5003
   231
 *
slouken@5003
   232
 * \note If you don't know what this function is for, you shouldn't use it!
slouken@5003
   233
*/
slouken@5003
   234
#ifndef SDL_AtomicCAS
slouken@5004
   235
extern DECLSPEC SDL_bool SDLCALL SDL_AtomicCAS(SDL_atomic_t *a, int oldval, int newval);
slouken@5003
   236
#endif
bob@3237
   237
bob@3199
   238
/**
slouken@5003
   239
 * \brief Set a pointer to a value atomically.
bob@3199
   240
 */
slouken@5003
   241
#ifndef SDL_AtomicSetPtr
slouken@5003
   242
extern DECLSPEC void SDLCALL SDL_AtomicSetPtr(void** a, void* value);
slouken@5003
   243
#endif
bob@3237
   244
bob@3199
   245
/**
slouken@5003
   246
 * \brief Get the value of a pointer atomically.
bob@3199
   247
 */
slouken@5003
   248
#ifndef SDL_AtomicGetPtr
slouken@5003
   249
extern DECLSPEC void* SDLCALL SDL_AtomicGetPtr(void** a);
slouken@5003
   250
#endif
bob@3237
   251
bob@3199
   252
/**
slouken@5003
   253
 * \brief Set a pointer to a new value if it is currently an old value.
slouken@5003
   254
 *
slouken@5004
   255
 * \return SDL_TRUE if the pointer was set, SDL_FALSE otherwise.
slouken@5003
   256
 *
slouken@5003
   257
 * \note If you don't know what this function is for, you shouldn't use it!
slouken@5003
   258
*/
slouken@5003
   259
#ifndef SDL_AtomicCASPtr
slouken@5004
   260
extern DECLSPEC SDL_bool SDLCALL SDL_AtomicCASPtr(void **a, void *oldval, void *newval);
slouken@5003
   261
#endif
slouken@3407
   262
bob@3180
   263
/* Ends C function definitions when using C++ */
bob@3180
   264
#ifdef __cplusplus
bob@3180
   265
/* *INDENT-OFF* */
bob@3180
   266
}
bob@3180
   267
/* *INDENT-ON* */
bob@3180
   268
#endif
bob@3180
   269
bob@3180
   270
#include "close_code.h"
bob@3180
   271
bob@3180
   272
#endif /* _SDL_atomic_h_ */
bob@3180
   273
bob@3180
   274
/* vi: set ts=4 sw=4 expandtab: */