src/atomic/SDL_atomic.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 16 Jan 2011 15:16:39 -0800
changeset 5004 0c72ae7b7cb2
parent 5003 3a95a2b93eb3
child 5095 dceec93471e7
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
slouken@5003
     1
/*
slouken@5003
     2
  SDL - Simple DirectMedia Layer
slouken@5003
     3
  Copyright (C) 1997-2010 Sam Lantinga
slouken@5003
     4
slouken@5003
     5
  This library is free software; you can redistribute it and/or
slouken@5003
     6
  modify it under the terms of the GNU Lesser General Public
slouken@5003
     7
  License as published by the Free Software Foundation; either
slouken@5003
     8
  version 2.1 of the License, or (at your option) any later version.
slouken@5003
     9
slouken@5003
    10
  This library is distributed in the hope that it will be useful,
slouken@5003
    11
  but WITHOUT ANY WARRANTY; without even the implied warranty of
slouken@5003
    12
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
slouken@5003
    13
  Lesser General Public License for more details.
slouken@5003
    14
slouken@5003
    15
  You should have received a copy of the GNU Lesser General Public
slouken@5003
    16
  License along with this library; if not, write to the Free Software
slouken@5003
    17
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
slouken@5003
    18
slouken@5003
    19
  Sam Lantinga
slouken@5003
    20
  slouken@libsdl.org
slouken@5003
    21
*/
slouken@5003
    22
#include "SDL_stdinc.h"
slouken@5003
    23
slouken@5003
    24
#include "SDL_atomic.h"
slouken@5003
    25
slouken@5004
    26
/* Note that we undefine the atomic operations here, in case they are
slouken@5004
    27
   defined as compiler intrinsics while building SDL but the library user
slouken@5004
    28
   doesn't have that compiler.  That way we always have a working set of
slouken@5004
    29
   atomic operations built into the library.
slouken@5004
    30
*/
slouken@5004
    31
 
slouken@5003
    32
/* 
slouken@5003
    33
  If any of the operations are not provided then we must emulate some
slouken@5003
    34
  of them. That means we need a nice implementation of spin locks
slouken@5003
    35
  that avoids the "one big lock" problem. We use a vector of spin
slouken@5003
    36
  locks and pick which one to use based on the address of the operand
slouken@5003
    37
  of the function.
slouken@5003
    38
slouken@5003
    39
  To generate the index of the lock we first shift by 3 bits to get
slouken@5003
    40
  rid on the zero bits that result from 32 and 64 bit allignment of
slouken@5003
    41
  data. We then mask off all but 5 bits and use those 5 bits as an
slouken@5003
    42
  index into the table. 
slouken@5003
    43
slouken@5003
    44
  Picking the lock this way insures that accesses to the same data at
slouken@5003
    45
  the same time will go to the same lock. OTOH, accesses to different
slouken@5003
    46
  data have only a 1/32 chance of hitting the same lock. That should
slouken@5003
    47
  pretty much eliminate the chances of several atomic operations on
slouken@5003
    48
  different data from waiting on the same "big lock". If it isn't
slouken@5003
    49
  then the table of locks can be expanded to a new size so long as
slouken@5003
    50
  the new size is a power of two.
slouken@5003
    51
slouken@5003
    52
  Contributed by Bob Pendleton, bob@pendleton.com
slouken@5003
    53
*/
slouken@5003
    54
slouken@5003
    55
static SDL_SpinLock locks[32];
slouken@5003
    56
slouken@5003
    57
static __inline__ void
slouken@5003
    58
enterLock(void *a)
slouken@5003
    59
{
slouken@5004
    60
    uintptr_t index = ((((uintptr_t)a) >> 3) & 0x1f);
slouken@5003
    61
slouken@5004
    62
    SDL_AtomicLock(&locks[index]);
slouken@5003
    63
}
slouken@5003
    64
slouken@5003
    65
static __inline__ void
slouken@5003
    66
leaveLock(void *a)
slouken@5003
    67
{
slouken@5004
    68
    uintptr_t index = ((((uintptr_t)a) >> 3) & 0x1f);
slouken@5003
    69
slouken@5004
    70
    SDL_AtomicUnlock(&locks[index]);
slouken@5003
    71
}
slouken@5003
    72
slouken@5004
    73
#undef SDL_AtomicSet
slouken@5003
    74
int
slouken@5003
    75
SDL_AtomicSet(SDL_atomic_t *a, int value)
slouken@5003
    76
{
slouken@5003
    77
    int oldvalue;
slouken@5003
    78
slouken@5003
    79
    enterLock(a);
slouken@5003
    80
    oldvalue = a->value;
slouken@5003
    81
    a->value = value;
slouken@5003
    82
    leaveLock(a);
slouken@5003
    83
slouken@5003
    84
    return oldvalue;
slouken@5003
    85
}
slouken@5003
    86
slouken@5004
    87
#undef SDL_AtomicGet
slouken@5003
    88
int
slouken@5003
    89
SDL_AtomicGet(SDL_atomic_t *a)
slouken@5003
    90
{
slouken@5003
    91
    /* Assuming integral reads on this platform, we're safe here since the
slouken@5003
    92
       functions that set the variable have the necessary memory barriers.
slouken@5003
    93
    */
slouken@5003
    94
    return a->value;
slouken@5003
    95
}
slouken@5003
    96
slouken@5004
    97
#undef SDL_AtomicAdd
slouken@5003
    98
int
slouken@5003
    99
SDL_AtomicAdd(SDL_atomic_t *a, int value)
slouken@5003
   100
{
slouken@5003
   101
    int oldvalue;
slouken@5003
   102
slouken@5003
   103
    enterLock(a);
slouken@5003
   104
    oldvalue = a->value;
slouken@5003
   105
    a->value += value;
slouken@5003
   106
    leaveLock(a);
slouken@5003
   107
slouken@5003
   108
    return oldvalue;
slouken@5003
   109
}
slouken@5003
   110
slouken@5004
   111
#undef SDL_AtomicIncRef
slouken@5003
   112
void
slouken@5003
   113
SDL_AtomicIncRef(SDL_atomic_t *a)
slouken@5003
   114
{
slouken@5003
   115
    SDL_AtomicAdd(a, 1);
slouken@5003
   116
}
slouken@5003
   117
slouken@5004
   118
#undef SDL_AtomicDecRef
slouken@5003
   119
SDL_bool
slouken@5003
   120
SDL_AtomicDecRef(SDL_atomic_t *a)
slouken@5003
   121
{
slouken@5003
   122
    return SDL_AtomicAdd(a, -1) == 1;
slouken@5003
   123
}
slouken@5003
   124
slouken@5004
   125
#undef SDL_AtomicCAS
slouken@5004
   126
SDL_bool
slouken@5003
   127
SDL_AtomicCAS(SDL_atomic_t *a, int oldval, int newval)
slouken@5003
   128
{
slouken@5004
   129
    SDL_bool retval = SDL_FALSE;
slouken@5003
   130
slouken@5003
   131
    enterLock(a);
slouken@5004
   132
    if (a->value == oldval) {
slouken@5003
   133
        a->value = newval;
slouken@5004
   134
        retval = SDL_TRUE;
slouken@5003
   135
    }
slouken@5003
   136
    leaveLock(a);
slouken@5003
   137
slouken@5004
   138
    return retval;
slouken@5003
   139
}
slouken@5003
   140
slouken@5004
   141
#undef SDL_AtomicSetPtr
slouken@5003
   142
void
slouken@5003
   143
SDL_AtomicSetPtr(void** a, void* value)
slouken@5003
   144
{
slouken@5003
   145
    void *prevval;
slouken@5003
   146
    do {
slouken@5003
   147
        prevval = *a;
slouken@5004
   148
    } while (!SDL_AtomicCASPtr(a, prevval, value));
slouken@5003
   149
}
slouken@5003
   150
slouken@5004
   151
#undef SDL_AtomicGetPtr
slouken@5003
   152
void*
slouken@5003
   153
SDL_AtomicGetPtr(void** a)
slouken@5003
   154
{
slouken@5003
   155
    /* Assuming integral reads on this platform, we're safe here since the
slouken@5003
   156
       functions that set the pointer have the necessary memory barriers.
slouken@5003
   157
    */
slouken@5003
   158
    return *a;
slouken@5003
   159
}
slouken@5003
   160
slouken@5004
   161
#undef SDL_AtomicCASPtr
slouken@5004
   162
SDL_bool SDL_AtomicCASPtr(void **a, void *oldval, void *newval)
slouken@5003
   163
{
slouken@5004
   164
    SDL_bool retval = SDL_FALSE;
slouken@5003
   165
slouken@5003
   166
    enterLock(a);
slouken@5003
   167
    if (*a == oldval) {
slouken@5003
   168
        *a = newval;
slouken@5004
   169
        retval = SDL_TRUE;
slouken@5003
   170
    }
slouken@5003
   171
    leaveLock(a);
slouken@5003
   172
slouken@5004
   173
    return retval;
slouken@5003
   174
}
slouken@5003
   175
slouken@5003
   176
/* vi: set ts=4 sw=4 expandtab: */