include/SDL_atomic.h
author Sam Lantinga <slouken@libsdl.org>
Sun, 16 Jan 2011 15:16:39 -0800
changeset 5004 0c72ae7b7cb2
parent 5003 3a95a2b93eb3
child 5005 7f0265279b68
permissions -rw-r--r--
Added native atomic operations for Windows, Mac OS X, and gcc compiler intrinsics.
Changed the CAS return value to bool, so it's efficient with OSAtomicCompareAndSwap32Barrier()
Added an atomic test adapted from code by Michael Davidsaver
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@5003
   110
/* Platform specific optimized versions of the atomic functions */
slouken@5004
   111
#if defined(__WIN32__)
slouken@5004
   112
#define WIN32_LEAN_AND_MEAN
slouken@5004
   113
#include <windows.h>
slouken@5004
   114
slouken@5004
   115
#define SDL_AtomicSet(a, v)     InterlockedExchange(&(a)->value, v)
slouken@5004
   116
#define SDL_AtomicGet(a)        ((a)->value)
slouken@5004
   117
#define SDL_AtomicAdd(a, v)     InterlockedAdd(&(a)->value, v)
slouken@5004
   118
#define SDL_AtomicCAS(a, oldval, newval) (InterlockedCompareExchange(&(a)->value, newval, oldval) == (oldval))
slouken@5004
   119
#define SDL_AtomicSetPtr(a, v)  InterlockedExchangePointer(a, v)
slouken@5004
   120
#define SDL_AtomicGetPtr(a)     (*(a))
slouken@5004
   121
#define SDL_AtomicCASPtr(a, oldval, newval) (InterlockedCompareExchangePointer(a, newval, oldval) == (oldval))
slouken@5004
   122
slouken@5004
   123
#elif defined(__MACOSX__)
slouken@5004
   124
#include <libkern/OSAtomic.h>
slouken@5004
   125
slouken@5004
   126
#define SDL_AtomicSet(a, v) \
slouken@5004
   127
({                          \
slouken@5004
   128
    int oldvalue;           \
slouken@5004
   129
                            \
slouken@5004
   130
    do {                    \
slouken@5004
   131
        oldvalue = (a)->value; \
slouken@5004
   132
    } while (!OSAtomicCompareAndSwap32Barrier(oldvalue, v, &(a)->value)); \
slouken@5004
   133
                            \
slouken@5004
   134
    oldvalue;               \
slouken@5004
   135
})
slouken@5004
   136
#define SDL_AtomicGet(a)        ((a)->value)
slouken@5004
   137
#define SDL_AtomicAdd(a, v) \
slouken@5004
   138
({                          \
slouken@5004
   139
    int oldvalue;           \
slouken@5004
   140
                            \
slouken@5004
   141
    do {                    \
slouken@5004
   142
        oldvalue = (a)->value; \
slouken@5004
   143
    } while (!OSAtomicCompareAndSwap32Barrier(oldvalue, oldvalue+v, &(a)->value)); \
slouken@5004
   144
                            \
slouken@5004
   145
    oldvalue;               \
slouken@5004
   146
})
slouken@5004
   147
#define SDL_AtomicCAS(a, oldval, newval) OSAtomicCompareAndSwap32Barrier(oldval, newval, &(a)->value)
slouken@5004
   148
#define SDL_AtomicSetPtr(a, v)  (*(a) = v, OSMemoryBarrier())
slouken@5004
   149
#define SDL_AtomicGetPtr(a)     (*(a))
slouken@5004
   150
#if SIZEOF_VOIDP == 4
slouken@5004
   151
#define SDL_AtomicCASPtr(a, oldval, newval) OSAtomicCompareAndSwap32Barrier((int32_t)(oldval), (int32_t)(newval), (int32_t*)(a))
slouken@5004
   152
#elif SIZEOF_VOIDP == 8
slouken@5004
   153
#define SDL_AtomicCASPtr(a, oldval, newval) OSAtomicCompareAndSwap64Barrier((int64_t)(oldval), (int64_t)(newval), (int64_t*)(a))
slouken@5004
   154
#endif
slouken@5004
   155
slouken@5004
   156
#elif defined(HAVE_GCC_ATOMICS)
slouken@5004
   157
slouken@5004
   158
#define SDL_AtomicSet(a, v)     __sync_lock_test_and_set(&(a)->value, v)
slouken@5004
   159
#define SDL_AtomicGet(a)        ((a)->value)
slouken@5004
   160
#define SDL_AtomicAdd(a, v)     __sync_fetch_and_add(&(a)->value, v)
slouken@5004
   161
#define SDL_AtomicCAS(a, oldval, newval) __sync_bool_compare_and_swap(&(a)->value, oldval, newval)
slouken@5004
   162
#define SDL_AtomicSetPtr(a, v)  (*(a) = v, __sync_synchronize())
slouken@5004
   163
#define SDL_AtomicGetPtr(a)     (*(a))
slouken@5004
   164
#define SDL_AtomicCASPtr(a, oldval, newval) __sync_bool_compare_and_swap(a, oldval, newval)
slouken@5004
   165
slouken@5004
   166
#endif
slouken@3407
   167
slouken@3407
   168
/**
slouken@5003
   169
 * \brief A type representing an atomic integer value.  It is a struct
slouken@5003
   170
 *        so people don't accidentally use numeric operations on it.
bob@3199
   171
 */
slouken@5003
   172
#ifndef SDL_atomic_t_defined
slouken@5003
   173
typedef struct { int value; } SDL_atomic_t;
slouken@5003
   174
#endif
bob@3237
   175
bob@3199
   176
/**
slouken@5003
   177
 * \brief Set an atomic variable to a value.
slouken@5003
   178
 *
slouken@5003
   179
 * \return The previous value of the atomic variable.
bob@3199
   180
 */
slouken@5003
   181
#ifndef SDL_AtomicSet
slouken@5003
   182
extern DECLSPEC int SDLCALL SDL_AtomicSet(SDL_atomic_t *a, int value);
slouken@5003
   183
#endif
bob@3237
   184
bob@3199
   185
/**
slouken@5003
   186
 * \brief Get the value of an atomic variable
bob@3199
   187
 */
slouken@5003
   188
#ifndef SDL_AtomicGet
slouken@5003
   189
extern DECLSPEC int SDLCALL SDL_AtomicGet(SDL_atomic_t *a);
slouken@5003
   190
#endif
bob@3237
   191
bob@3199
   192
/**
slouken@5003
   193
 * \brief  Add to an atomic variable.
slouken@5003
   194
 *
slouken@5003
   195
 * \return The previous value of the atomic variable.
bob@3199
   196
 */
slouken@5003
   197
#ifndef SDL_AtomicAdd
slouken@5003
   198
extern DECLSPEC int SDLCALL SDL_AtomicAdd(SDL_atomic_t *a, int value);
slouken@5003
   199
#endif
bob@3237
   200
bob@3199
   201
/**
slouken@5003
   202
 * \brief Increment an atomic variable used as a reference count.
bob@3199
   203
 */
slouken@5003
   204
#ifndef SDL_AtomicIncRef
slouken@5003
   205
extern DECLSPEC void SDLCALL SDL_AtomicIncRef(SDL_atomic_t *a);
slouken@5003
   206
#endif
bob@3237
   207
bob@3199
   208
/**
slouken@5003
   209
 * \brief Decrement an atomic variable used as a reference count.
slouken@5003
   210
 *
slouken@5003
   211
 * \return SDL_TRUE if the variable has reached zero after decrementing,
slouken@5003
   212
 *         SDL_FALSE otherwise
bob@3199
   213
 */
slouken@5003
   214
#ifndef SDL_AtomicDecRef
slouken@5003
   215
extern DECLSPEC SDL_bool SDLCALL SDL_AtomicDecRef(SDL_atomic_t *a);
slouken@5003
   216
#endif
bob@3237
   217
bob@3199
   218
/**
slouken@5003
   219
 * \brief Set an atomic variable to a new value if it is currently an old value.
slouken@5003
   220
 *
slouken@5004
   221
 * \return SDL_TRUE if the atomic variable was set, SDL_FALSE otherwise.
slouken@5003
   222
 *
slouken@5003
   223
 * \note If you don't know what this function is for, you shouldn't use it!
slouken@5003
   224
*/
slouken@5003
   225
#ifndef SDL_AtomicCAS
slouken@5004
   226
extern DECLSPEC SDL_bool SDLCALL SDL_AtomicCAS(SDL_atomic_t *a, int oldval, int newval);
slouken@5003
   227
#endif
bob@3237
   228
bob@3199
   229
/**
slouken@5003
   230
 * \brief Set a pointer to a value atomically.
bob@3199
   231
 */
slouken@5003
   232
#ifndef SDL_AtomicSetPtr
slouken@5003
   233
extern DECLSPEC void SDLCALL SDL_AtomicSetPtr(void** a, void* value);
slouken@5003
   234
#endif
bob@3237
   235
bob@3199
   236
/**
slouken@5003
   237
 * \brief Get the value of a pointer atomically.
bob@3199
   238
 */
slouken@5003
   239
#ifndef SDL_AtomicGetPtr
slouken@5003
   240
extern DECLSPEC void* SDLCALL SDL_AtomicGetPtr(void** a);
slouken@5003
   241
#endif
bob@3237
   242
bob@3199
   243
/**
slouken@5003
   244
 * \brief Set a pointer to a new value if it is currently an old value.
slouken@5003
   245
 *
slouken@5004
   246
 * \return SDL_TRUE if the pointer was set, SDL_FALSE otherwise.
slouken@5003
   247
 *
slouken@5003
   248
 * \note If you don't know what this function is for, you shouldn't use it!
slouken@5003
   249
*/
slouken@5003
   250
#ifndef SDL_AtomicCASPtr
slouken@5004
   251
extern DECLSPEC SDL_bool SDLCALL SDL_AtomicCASPtr(void **a, void *oldval, void *newval);
slouken@5003
   252
#endif
slouken@3407
   253
bob@3180
   254
/* Ends C function definitions when using C++ */
bob@3180
   255
#ifdef __cplusplus
bob@3180
   256
/* *INDENT-OFF* */
bob@3180
   257
}
bob@3180
   258
/* *INDENT-ON* */
bob@3180
   259
#endif
bob@3180
   260
bob@3180
   261
#include "close_code.h"
bob@3180
   262
bob@3180
   263
#endif /* _SDL_atomic_h_ */
bob@3180
   264
bob@3180
   265
/* vi: set ts=4 sw=4 expandtab: */