src/atomic/SDL_atomic.c
author Sam Lantinga <slouken@libsdl.org>
Sat, 15 Jan 2011 12:41:59 -0800
changeset 5003 3a95a2b93eb3
child 5004 0c72ae7b7cb2
permissions -rw-r--r--
Updated the atomic API for better use cases
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@5003
    26
/* 
slouken@5003
    27
  If any of the operations are not provided then we must emulate some
slouken@5003
    28
  of them. That means we need a nice implementation of spin locks
slouken@5003
    29
  that avoids the "one big lock" problem. We use a vector of spin
slouken@5003
    30
  locks and pick which one to use based on the address of the operand
slouken@5003
    31
  of the function.
slouken@5003
    32
slouken@5003
    33
  To generate the index of the lock we first shift by 3 bits to get
slouken@5003
    34
  rid on the zero bits that result from 32 and 64 bit allignment of
slouken@5003
    35
  data. We then mask off all but 5 bits and use those 5 bits as an
slouken@5003
    36
  index into the table. 
slouken@5003
    37
slouken@5003
    38
  Picking the lock this way insures that accesses to the same data at
slouken@5003
    39
  the same time will go to the same lock. OTOH, accesses to different
slouken@5003
    40
  data have only a 1/32 chance of hitting the same lock. That should
slouken@5003
    41
  pretty much eliminate the chances of several atomic operations on
slouken@5003
    42
  different data from waiting on the same "big lock". If it isn't
slouken@5003
    43
  then the table of locks can be expanded to a new size so long as
slouken@5003
    44
  the new size is a power of two.
slouken@5003
    45
slouken@5003
    46
  Contributed by Bob Pendleton, bob@pendleton.com
slouken@5003
    47
*/
slouken@5003
    48
slouken@5003
    49
static SDL_SpinLock locks[32];
slouken@5003
    50
slouken@5003
    51
static __inline__ void
slouken@5003
    52
enterLock(void *a)
slouken@5003
    53
{
slouken@5003
    54
   uintptr_t index = ((((uintptr_t)a) >> 3) & 0x1f);
slouken@5003
    55
slouken@5003
    56
   SDL_AtomicLock(&locks[index]);
slouken@5003
    57
}
slouken@5003
    58
slouken@5003
    59
static __inline__ void
slouken@5003
    60
leaveLock(void *a)
slouken@5003
    61
{
slouken@5003
    62
   uintptr_t index = ((((uintptr_t)a) >> 3) & 0x1f);
slouken@5003
    63
slouken@5003
    64
   SDL_AtomicUnlock(&locks[index]);
slouken@5003
    65
}
slouken@5003
    66
slouken@5003
    67
#ifndef SDL_AtomicSet
slouken@5003
    68
int
slouken@5003
    69
SDL_AtomicSet(SDL_atomic_t *a, int value)
slouken@5003
    70
{
slouken@5003
    71
    int oldvalue;
slouken@5003
    72
slouken@5003
    73
    enterLock(a);
slouken@5003
    74
    oldvalue = a->value;
slouken@5003
    75
    a->value = value;
slouken@5003
    76
    leaveLock(a);
slouken@5003
    77
slouken@5003
    78
    return oldvalue;
slouken@5003
    79
}
slouken@5003
    80
#endif
slouken@5003
    81
slouken@5003
    82
#ifndef SDL_AtomicGet
slouken@5003
    83
int
slouken@5003
    84
SDL_AtomicGet(SDL_atomic_t *a)
slouken@5003
    85
{
slouken@5003
    86
    /* Assuming integral reads on this platform, we're safe here since the
slouken@5003
    87
       functions that set the variable have the necessary memory barriers.
slouken@5003
    88
    */
slouken@5003
    89
    return a->value;
slouken@5003
    90
}
slouken@5003
    91
#endif
slouken@5003
    92
slouken@5003
    93
#ifndef SDL_AtomicAdd
slouken@5003
    94
int
slouken@5003
    95
SDL_AtomicAdd(SDL_atomic_t *a, int value)
slouken@5003
    96
{
slouken@5003
    97
    int oldvalue;
slouken@5003
    98
slouken@5003
    99
    enterLock(a);
slouken@5003
   100
    oldvalue = a->value;
slouken@5003
   101
    a->value += value;
slouken@5003
   102
    leaveLock(a);
slouken@5003
   103
slouken@5003
   104
    return oldvalue;
slouken@5003
   105
}
slouken@5003
   106
#endif
slouken@5003
   107
slouken@5003
   108
#ifndef SDL_AtomicIncRef
slouken@5003
   109
void
slouken@5003
   110
SDL_AtomicIncRef(SDL_atomic_t *a)
slouken@5003
   111
{
slouken@5003
   112
    SDL_AtomicAdd(a, 1);
slouken@5003
   113
}
slouken@5003
   114
#endif
slouken@5003
   115
slouken@5003
   116
#ifndef SDL_AtomicDecRef
slouken@5003
   117
SDL_bool
slouken@5003
   118
SDL_AtomicDecRef(SDL_atomic_t *a)
slouken@5003
   119
{
slouken@5003
   120
    return SDL_AtomicAdd(a, -1) == 1;
slouken@5003
   121
}
slouken@5003
   122
#endif
slouken@5003
   123
slouken@5003
   124
#ifndef SDL_AtomicCAS
slouken@5003
   125
int
slouken@5003
   126
SDL_AtomicCAS(SDL_atomic_t *a, int oldval, int newval)
slouken@5003
   127
{
slouken@5003
   128
    int prevval;
slouken@5003
   129
slouken@5003
   130
    enterLock(a);
slouken@5003
   131
    prevval = a->value;
slouken@5003
   132
    if (prevval == oldval) {
slouken@5003
   133
        a->value = newval;
slouken@5003
   134
    }
slouken@5003
   135
    leaveLock(a);
slouken@5003
   136
slouken@5003
   137
    return prevval;
slouken@5003
   138
}
slouken@5003
   139
#endif
slouken@5003
   140
slouken@5003
   141
#ifndef 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@5003
   148
    } while (SDL_AtomicCASPtr(a, prevval, value) != prevval);
slouken@5003
   149
}
slouken@5003
   150
#endif
slouken@5003
   151
slouken@5003
   152
#ifndef SDL_AtomicGetPtr
slouken@5003
   153
void*
slouken@5003
   154
SDL_AtomicGetPtr(void** a)
slouken@5003
   155
{
slouken@5003
   156
    /* Assuming integral reads on this platform, we're safe here since the
slouken@5003
   157
       functions that set the pointer have the necessary memory barriers.
slouken@5003
   158
    */
slouken@5003
   159
    return *a;
slouken@5003
   160
}
slouken@5003
   161
#endif
slouken@5003
   162
slouken@5003
   163
#ifndef SDL_AtomicCASPtr
slouken@5003
   164
void* SDL_AtomicCASPtr(void **a, void *oldval, void *newval)
slouken@5003
   165
{
slouken@5003
   166
    void *prevval;
slouken@5003
   167
slouken@5003
   168
    enterLock(a);
slouken@5003
   169
    prevval = *a;
slouken@5003
   170
    if (*a == oldval) {
slouken@5003
   171
        *a = newval;
slouken@5003
   172
    }
slouken@5003
   173
    leaveLock(a);
slouken@5003
   174
slouken@5003
   175
    return prevval;
slouken@5003
   176
}
slouken@5003
   177
#endif
slouken@5003
   178
slouken@5003
   179
/* vi: set ts=4 sw=4 expandtab: */