diff src/atomic/SDL_atomic.c @ 5003:3a95a2b93eb3

Updated the atomic API for better use cases
author Sam Lantinga <slouken@libsdl.org>
date Sat, 15 Jan 2011 12:41:59 -0800
parents
children 0c72ae7b7cb2
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/atomic/SDL_atomic.c	Sat Jan 15 12:41:59 2011 -0800
@@ -0,0 +1,179 @@
+/*
+  SDL - Simple DirectMedia Layer
+  Copyright (C) 1997-2010 Sam Lantinga
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+  Sam Lantinga
+  slouken@libsdl.org
+*/
+#include "SDL_stdinc.h"
+
+#include "SDL_atomic.h"
+
+/* 
+  If any of the operations are not provided then we must emulate some
+  of them. That means we need a nice implementation of spin locks
+  that avoids the "one big lock" problem. We use a vector of spin
+  locks and pick which one to use based on the address of the operand
+  of the function.
+
+  To generate the index of the lock we first shift by 3 bits to get
+  rid on the zero bits that result from 32 and 64 bit allignment of
+  data. We then mask off all but 5 bits and use those 5 bits as an
+  index into the table. 
+
+  Picking the lock this way insures that accesses to the same data at
+  the same time will go to the same lock. OTOH, accesses to different
+  data have only a 1/32 chance of hitting the same lock. That should
+  pretty much eliminate the chances of several atomic operations on
+  different data from waiting on the same "big lock". If it isn't
+  then the table of locks can be expanded to a new size so long as
+  the new size is a power of two.
+
+  Contributed by Bob Pendleton, bob@pendleton.com
+*/
+
+static SDL_SpinLock locks[32];
+
+static __inline__ void
+enterLock(void *a)
+{
+   uintptr_t index = ((((uintptr_t)a) >> 3) & 0x1f);
+
+   SDL_AtomicLock(&locks[index]);
+}
+
+static __inline__ void
+leaveLock(void *a)
+{
+   uintptr_t index = ((((uintptr_t)a) >> 3) & 0x1f);
+
+   SDL_AtomicUnlock(&locks[index]);
+}
+
+#ifndef SDL_AtomicSet
+int
+SDL_AtomicSet(SDL_atomic_t *a, int value)
+{
+    int oldvalue;
+
+    enterLock(a);
+    oldvalue = a->value;
+    a->value = value;
+    leaveLock(a);
+
+    return oldvalue;
+}
+#endif
+
+#ifndef SDL_AtomicGet
+int
+SDL_AtomicGet(SDL_atomic_t *a)
+{
+    /* Assuming integral reads on this platform, we're safe here since the
+       functions that set the variable have the necessary memory barriers.
+    */
+    return a->value;
+}
+#endif
+
+#ifndef SDL_AtomicAdd
+int
+SDL_AtomicAdd(SDL_atomic_t *a, int value)
+{
+    int oldvalue;
+
+    enterLock(a);
+    oldvalue = a->value;
+    a->value += value;
+    leaveLock(a);
+
+    return oldvalue;
+}
+#endif
+
+#ifndef SDL_AtomicIncRef
+void
+SDL_AtomicIncRef(SDL_atomic_t *a)
+{
+    SDL_AtomicAdd(a, 1);
+}
+#endif
+
+#ifndef SDL_AtomicDecRef
+SDL_bool
+SDL_AtomicDecRef(SDL_atomic_t *a)
+{
+    return SDL_AtomicAdd(a, -1) == 1;
+}
+#endif
+
+#ifndef SDL_AtomicCAS
+int
+SDL_AtomicCAS(SDL_atomic_t *a, int oldval, int newval)
+{
+    int prevval;
+
+    enterLock(a);
+    prevval = a->value;
+    if (prevval == oldval) {
+        a->value = newval;
+    }
+    leaveLock(a);
+
+    return prevval;
+}
+#endif
+
+#ifndef SDL_AtomicSetPtr
+void
+SDL_AtomicSetPtr(void** a, void* value)
+{
+    void *prevval;
+    do {
+        prevval = *a;
+    } while (SDL_AtomicCASPtr(a, prevval, value) != prevval);
+}
+#endif
+
+#ifndef SDL_AtomicGetPtr
+void*
+SDL_AtomicGetPtr(void** a)
+{
+    /* Assuming integral reads on this platform, we're safe here since the
+       functions that set the pointer have the necessary memory barriers.
+    */
+    return *a;
+}
+#endif
+
+#ifndef SDL_AtomicCASPtr
+void* SDL_AtomicCASPtr(void **a, void *oldval, void *newval)
+{
+    void *prevval;
+
+    enterLock(a);
+    prevval = *a;
+    if (*a == oldval) {
+        *a = newval;
+    }
+    leaveLock(a);
+
+    return prevval;
+}
+#endif
+
+/* vi: set ts=4 sw=4 expandtab: */