sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

SDL_spinlock.c (6321B)


      1 /*
      2   Simple DirectMedia Layer
      3   Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
      4 
      5   This software is provided 'as-is', without any express or implied
      6   warranty.  In no event will the authors be held liable for any damages
      7   arising from the use of this software.
      8 
      9   Permission is granted to anyone to use this software for any purpose,
     10   including commercial applications, and to alter it and redistribute it
     11   freely, subject to the following restrictions:
     12 
     13   1. The origin of this software must not be misrepresented; you must not
     14      claim that you wrote the original software. If you use this software
     15      in a product, an acknowledgment in the product documentation would be
     16      appreciated but is not required.
     17   2. Altered source versions must be plainly marked as such, and must not be
     18      misrepresented as being the original software.
     19   3. This notice may not be removed or altered from any source distribution.
     20 */
     21 #include "../SDL_internal.h"
     22 
     23 #if defined(__WIN32__) || defined(__WINRT__)
     24 #include "../core/windows/SDL_windows.h"
     25 #endif
     26 
     27 #include "SDL_atomic.h"
     28 #include "SDL_mutex.h"
     29 #include "SDL_timer.h"
     30 
     31 #if !defined(HAVE_GCC_ATOMICS) && defined(__SOLARIS__)
     32 #include <atomic.h>
     33 #endif
     34 
     35 #if !defined(HAVE_GCC_ATOMICS) && defined(__RISCOS__)
     36 #include <unixlib/local.h>
     37 #endif
     38 
     39 #if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))
     40 #include <xmmintrin.h>
     41 #endif
     42 
     43 #if defined(__WATCOMC__) && defined(__386__)
     44 SDL_COMPILE_TIME_ASSERT(locksize, 4==sizeof(SDL_SpinLock));
     45 extern _inline int _SDL_xchg_watcom(volatile int *a, int v);
     46 #pragma aux _SDL_xchg_watcom = \
     47   "lock xchg [ecx], eax" \
     48   parm [ecx] [eax] \
     49   value [eax] \
     50   modify exact [eax];
     51 #endif /* __WATCOMC__ && __386__ */
     52 
     53 /* This function is where all the magic happens... */
     54 SDL_bool
     55 SDL_AtomicTryLock(SDL_SpinLock *lock)
     56 {
     57 #if SDL_ATOMIC_DISABLED
     58     /* Terrible terrible damage */
     59     static SDL_mutex *_spinlock_mutex;
     60 
     61     if (!_spinlock_mutex) {
     62         /* Race condition on first lock... */
     63         _spinlock_mutex = SDL_CreateMutex();
     64     }
     65     SDL_LockMutex(_spinlock_mutex);
     66     if (*lock == 0) {
     67         *lock = 1;
     68         SDL_UnlockMutex(_spinlock_mutex);
     69         return SDL_TRUE;
     70     } else {
     71         SDL_UnlockMutex(_spinlock_mutex);
     72         return SDL_FALSE;
     73     }
     74 
     75 #elif HAVE_GCC_ATOMICS || HAVE_GCC_SYNC_LOCK_TEST_AND_SET
     76     return (__sync_lock_test_and_set(lock, 1) == 0);
     77 
     78 #elif defined(_MSC_VER)
     79     SDL_COMPILE_TIME_ASSERT(locksize, sizeof(*lock) == sizeof(long));
     80     return (InterlockedExchange((long*)lock, 1) == 0);
     81 
     82 #elif defined(__WATCOMC__) && defined(__386__)
     83     return _SDL_xchg_watcom(lock, 1) == 0;
     84 
     85 #elif defined(__GNUC__) && defined(__arm__) && \
     86         (defined(__ARM_ARCH_3__) || defined(__ARM_ARCH_3M__) || \
     87          defined(__ARM_ARCH_4__) || defined(__ARM_ARCH_4T__) || \
     88          defined(__ARM_ARCH_5__) || defined(__ARM_ARCH_5TE__) || \
     89          defined(__ARM_ARCH_5TEJ__))
     90     int result;
     91 
     92 #if defined(__RISCOS__)
     93     if (__cpucap_have_rex()) {
     94         __asm__ __volatile__ (
     95             "ldrex %0, [%2]\nteq   %0, #0\nstrexeq %0, %1, [%2]"
     96             : "=&r" (result) : "r" (1), "r" (lock) : "cc", "memory");
     97         return (result == 0);
     98     }
     99 #endif
    100 
    101     __asm__ __volatile__ (
    102         "swp %0, %1, [%2]\n"
    103         : "=&r,&r" (result) : "r,0" (1), "r,r" (lock) : "memory");
    104     return (result == 0);
    105 
    106 #elif defined(__GNUC__) && defined(__arm__)
    107     int result;
    108     __asm__ __volatile__ (
    109         "ldrex %0, [%2]\nteq   %0, #0\nstrexeq %0, %1, [%2]"
    110         : "=&r" (result) : "r" (1), "r" (lock) : "cc", "memory");
    111     return (result == 0);
    112 
    113 #elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
    114     int result;
    115     __asm__ __volatile__(
    116         "lock ; xchgl %0, (%1)\n"
    117         : "=r" (result) : "r" (lock), "0" (1) : "cc", "memory");
    118     return (result == 0);
    119 
    120 #elif defined(__MACOSX__) || defined(__IPHONEOS__)
    121     /* Maybe used for PowerPC, but the Intel asm or gcc atomics are favored. */
    122     return OSAtomicCompareAndSwap32Barrier(0, 1, lock);
    123 
    124 #elif defined(__SOLARIS__) && defined(_LP64)
    125     /* Used for Solaris with non-gcc compilers. */
    126     return (SDL_bool) ((int) atomic_cas_64((volatile uint64_t*)lock, 0, 1) == 0);
    127 
    128 #elif defined(__SOLARIS__) && !defined(_LP64)
    129     /* Used for Solaris with non-gcc compilers. */
    130     return (SDL_bool) ((int) atomic_cas_32((volatile uint32_t*)lock, 0, 1) == 0);
    131 
    132 #else
    133 #error Please implement for your platform.
    134     return SDL_FALSE;
    135 #endif
    136 }
    137 
    138 /* "REP NOP" is PAUSE, coded for tools that don't know it by that name. */
    139 #if (defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__))
    140     #define PAUSE_INSTRUCTION() __asm__ __volatile__("pause\n")  /* Some assemblers can't do REP NOP, so go with PAUSE. */
    141 #elif (defined(__arm__) && __ARM_ARCH__ >= 7) || defined(__aarch64__)
    142     #define PAUSE_INSTRUCTION() __asm__ __volatile__("yield" ::: "memory")
    143 #elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))
    144     #define PAUSE_INSTRUCTION() _mm_pause()  /* this is actually "rep nop" and not a SIMD instruction. No inline asm in MSVC x86-64! */
    145 #elif defined(__WATCOMC__) && defined(__386__)
    146     /* watcom assembler rejects PAUSE if CPU < i686, and it refuses REP NOP as an invalid combination. Hardcode the bytes.  */
    147     extern _inline void PAUSE_INSTRUCTION(void);
    148     #pragma aux PAUSE_INSTRUCTION = "db 0f3h,90h"
    149 #else
    150     #define PAUSE_INSTRUCTION()
    151 #endif
    152 
    153 void
    154 SDL_AtomicLock(SDL_SpinLock *lock)
    155 {
    156     int iterations = 0;
    157     /* FIXME: Should we have an eventual timeout? */
    158     while (!SDL_AtomicTryLock(lock)) {
    159         if (iterations < 32) {
    160             iterations++;
    161             PAUSE_INSTRUCTION();
    162         } else {
    163             /* !!! FIXME: this doesn't definitely give up the current timeslice, it does different things on various platforms. */
    164             SDL_Delay(0);
    165         }
    166     }
    167 }
    168 
    169 void
    170 SDL_AtomicUnlock(SDL_SpinLock *lock)
    171 {
    172 #if HAVE_GCC_ATOMICS || HAVE_GCC_SYNC_LOCK_TEST_AND_SET
    173     __sync_lock_release(lock);
    174 
    175 #elif defined(_MSC_VER)
    176     _ReadWriteBarrier();
    177     *lock = 0;
    178 
    179 #elif defined(__WATCOMC__) && defined(__386__)
    180     SDL_CompilerBarrier ();
    181     *lock = 0;
    182 
    183 #elif defined(__SOLARIS__)
    184     /* Used for Solaris when not using gcc. */
    185     *lock = 0;
    186     membar_producer();
    187 
    188 #else
    189     *lock = 0;
    190 #endif
    191 }
    192 
    193 /* vi: set ts=4 sw=4 expandtab: */