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