src/atomic/SDL_atomic.c
author Ryan C. Gordon
Mon, 01 Jan 2018 19:16:51 -0500
changeset 11803 454f6dc9cb85
parent 11318 06fd8421e8f6
child 11811 5d94cb6b24d3
permissions -rw-r--r--
windows: Remove references to GetVersionExA (thanks, Andrew Pilley!).

"GetVersionExA is deprecated in windows 8.1 and above's SDK, causing a warning
when building against the win10 SDK. Attached patch cleans up the usage for a
warning-free build.

GetVersionExA was being used to test to see if SDL was running on win9x or
winnt. A quick chat with Ryan on twitter suggested that SDL doesn't
officially support win9x anymore, so the call to this can be outright removed.

As an aside, replacing the call to GetVersionExA with VerifyVersionInfoA (the
recommended path) would have been pointless, as VerifyVersionInfoA only
supports VER_PLATFORM_WIN32_NT and doesn't officially support any other value
for dwPlatformId currently. (And it's probable that win9x SDKs didn't have
VerifyVersionInfo* in them anyway.)"

Fixes Bugzilla #4019.
slouken@5003
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@10737
     3
  Copyright (C) 1997-2017 Sam Lantinga <slouken@libsdl.org>
slouken@5003
     4
slouken@5535
     5
  This software is provided 'as-is', without any express or implied
slouken@5535
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@5535
     7
  arising from the use of this software.
slouken@5003
     8
slouken@5535
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@5535
    10
  including commercial applications, and to alter it and redistribute it
slouken@5535
    11
  freely, subject to the following restrictions:
slouken@5003
    12
slouken@5535
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@5535
    14
     claim that you wrote the original software. If you use this software
slouken@5535
    15
     in a product, an acknowledgment in the product documentation would be
slouken@5535
    16
     appreciated but is not required.
slouken@5535
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@5535
    18
     misrepresented as being the original software.
slouken@5535
    19
  3. This notice may not be removed or altered from any source distribution.
slouken@5003
    20
*/
icculus@8093
    21
#include "../SDL_internal.h"
slouken@5003
    22
slouken@5003
    23
#include "SDL_atomic.h"
slouken@5003
    24
icculus@8088
    25
#if defined(_MSC_VER) && (_MSC_VER >= 1500)
icculus@8088
    26
#include <intrin.h>
icculus@8088
    27
#define HAVE_MSC_ATOMICS 1
icculus@8088
    28
#endif
icculus@8088
    29
icculus@8088
    30
#if defined(__MACOSX__)  /* !!! FIXME: should we favor gcc atomics? */
icculus@8088
    31
#include <libkern/OSAtomic.h>
icculus@8088
    32
#endif
slouken@7191
    33
binarycrusader@8979
    34
#if !defined(HAVE_GCC_ATOMICS) && defined(__SOLARIS__)
binarycrusader@8979
    35
#include <atomic.h>
binarycrusader@8979
    36
#endif
binarycrusader@8979
    37
icculus@10972
    38
/* The __atomic_load_n() intrinsic showed up in different times for different compilers. */
icculus@10972
    39
#if defined(HAVE_GCC_ATOMICS)
icculus@10972
    40
# if defined(__clang__)
icculus@10972
    41
#   if __has_builtin(__atomic_load_n)
icculus@10974
    42
      /* !!! FIXME: this advertises as available in the NDK but uses an external symbol we don't have.
icculus@10974
    43
         It might be in a later NDK or we might need an extra library? --ryan. */
icculus@10974
    44
#     if !defined(__ANDROID__)
icculus@10974
    45
#       define HAVE_ATOMIC_LOAD_N 1
icculus@10974
    46
#     endif
icculus@10972
    47
#   endif
icculus@10972
    48
# elif defined(__GNUC__)
icculus@10972
    49
#   if (__GNUC__ >= 5)
icculus@10972
    50
#     define HAVE_ATOMIC_LOAD_N 1
icculus@10972
    51
#   endif
icculus@10972
    52
# endif
icculus@10972
    53
#endif
icculus@10972
    54
sezeroz@11318
    55
#if defined(__WATCOMC__) && defined(__386__)
sezeroz@11318
    56
#define HAVE_WATCOM_ATOMICS
sezeroz@11318
    57
extern _inline int _SDL_xchg_watcom(volatile int *a, int v);
sezeroz@11318
    58
#pragma aux _SDL_xchg_watcom = \
sezeroz@11318
    59
  "xchg [ecx], eax" \
sezeroz@11318
    60
  parm [ecx] [eax] \
sezeroz@11318
    61
  value [eax] \
sezeroz@11318
    62
  modify exact [eax];
sezeroz@11318
    63
sezeroz@11318
    64
extern _inline unsigned char _SDL_cmpxchg_watcom(volatile int *a, int newval, int oldval);
sezeroz@11318
    65
#pragma aux _SDL_cmpxchg_watcom = \
sezeroz@11318
    66
  "lock cmpxchg [edx], ecx" \
sezeroz@11318
    67
  "setz al" \
sezeroz@11318
    68
  parm [edx] [ecx] [eax] \
sezeroz@11318
    69
  value [al] \
sezeroz@11318
    70
  modify exact [eax];
sezeroz@11318
    71
sezeroz@11318
    72
extern _inline int _SDL_xadd_watcom(volatile int *a, int v);
sezeroz@11318
    73
#pragma aux _SDL_xadd_watcom = \
sezeroz@11318
    74
  "lock xadd [ecx], eax" \
sezeroz@11318
    75
  parm [ecx] [eax] \
sezeroz@11318
    76
  value [eax] \
sezeroz@11318
    77
  modify exact [eax];
sezeroz@11318
    78
#endif /* __WATCOMC__ && __386__ */
sezeroz@11318
    79
slouken@7191
    80
/*
slouken@5003
    81
  If any of the operations are not provided then we must emulate some
slouken@5003
    82
  of them. That means we need a nice implementation of spin locks
slouken@5003
    83
  that avoids the "one big lock" problem. We use a vector of spin
slouken@5003
    84
  locks and pick which one to use based on the address of the operand
slouken@5003
    85
  of the function.
slouken@5003
    86
slouken@5003
    87
  To generate the index of the lock we first shift by 3 bits to get
slouken@5003
    88
  rid on the zero bits that result from 32 and 64 bit allignment of
slouken@5003
    89
  data. We then mask off all but 5 bits and use those 5 bits as an
slouken@7191
    90
  index into the table.
slouken@5003
    91
slouken@5003
    92
  Picking the lock this way insures that accesses to the same data at
slouken@5003
    93
  the same time will go to the same lock. OTOH, accesses to different
slouken@5003
    94
  data have only a 1/32 chance of hitting the same lock. That should
slouken@5003
    95
  pretty much eliminate the chances of several atomic operations on
slouken@5003
    96
  different data from waiting on the same "big lock". If it isn't
slouken@5003
    97
  then the table of locks can be expanded to a new size so long as
slouken@5003
    98
  the new size is a power of two.
slouken@5003
    99
slouken@5003
   100
  Contributed by Bob Pendleton, bob@pendleton.com
slouken@5003
   101
*/
slouken@5003
   102
sezeroz@11318
   103
#if !defined(HAVE_MSC_ATOMICS) && !defined(HAVE_GCC_ATOMICS) && !defined(__MACOSX__) && !defined(__SOLARIS__) && !defined(HAVE_WATCOM_ATOMICS)
icculus@8088
   104
#define EMULATE_CAS 1
icculus@8088
   105
#endif
icculus@8088
   106
icculus@8088
   107
#if EMULATE_CAS
slouken@5003
   108
static SDL_SpinLock locks[32];
slouken@5003
   109
slouken@7860
   110
static SDL_INLINE void
slouken@5003
   111
enterLock(void *a)
slouken@5003
   112
{
slouken@5004
   113
    uintptr_t index = ((((uintptr_t)a) >> 3) & 0x1f);
slouken@5003
   114
slouken@5004
   115
    SDL_AtomicLock(&locks[index]);
slouken@5003
   116
}
slouken@5003
   117
slouken@7860
   118
static SDL_INLINE void
slouken@5003
   119
leaveLock(void *a)
slouken@5003
   120
{
slouken@5004
   121
    uintptr_t index = ((((uintptr_t)a) >> 3) & 0x1f);
slouken@5003
   122
slouken@5004
   123
    SDL_AtomicUnlock(&locks[index]);
slouken@5003
   124
}
icculus@8088
   125
#endif
slouken@5003
   126
icculus@8088
   127
icculus@8088
   128
SDL_bool
slouken@6978
   129
SDL_AtomicCAS(SDL_atomic_t *a, int oldval, int newval)
slouken@5003
   130
{
icculus@8088
   131
#ifdef HAVE_MSC_ATOMICS
icculus@8088
   132
    return (_InterlockedCompareExchange((long*)&a->value, (long)newval, (long)oldval) == (long)oldval);
sezeroz@11318
   133
#elif defined(HAVE_WATCOM_ATOMICS)
sezeroz@11318
   134
    return (SDL_bool) _SDL_cmpxchg_watcom(&a->value, newval, oldval);
icculus@8088
   135
#elif defined(HAVE_GCC_ATOMICS)
icculus@8088
   136
    return (SDL_bool) __sync_bool_compare_and_swap(&a->value, oldval, newval);
icculus@10973
   137
#elif defined(__MACOSX__)  /* this is deprecated in 10.12 sdk; favor gcc atomics. */
icculus@10973
   138
    return (SDL_bool) OSAtomicCompareAndSwap32Barrier(oldval, newval, &a->value);
binarycrusader@8979
   139
#elif defined(__SOLARIS__) && defined(_LP64)
binarycrusader@8979
   140
    return (SDL_bool) ((int) atomic_cas_64((volatile uint64_t*)&a->value, (uint64_t)oldval, (uint64_t)newval) == oldval);
binarycrusader@8979
   141
#elif defined(__SOLARIS__) && !defined(_LP64)
binarycrusader@8979
   142
    return (SDL_bool) ((int) atomic_cas_32((volatile uint32_t*)&a->value, (uint32_t)oldval, (uint32_t)newval) == oldval);
icculus@8088
   143
#elif EMULATE_CAS
slouken@5004
   144
    SDL_bool retval = SDL_FALSE;
slouken@5003
   145
slouken@5003
   146
    enterLock(a);
slouken@5004
   147
    if (a->value == oldval) {
slouken@5003
   148
        a->value = newval;
slouken@5004
   149
        retval = SDL_TRUE;
slouken@5003
   150
    }
slouken@5003
   151
    leaveLock(a);
slouken@5003
   152
slouken@5004
   153
    return retval;
icculus@8088
   154
#else
icculus@8088
   155
    #error Please define your platform.
icculus@8088
   156
#endif
slouken@5003
   157
}
slouken@5003
   158
icculus@8088
   159
SDL_bool
slouken@6978
   160
SDL_AtomicCASPtr(void **a, void *oldval, void *newval)
slouken@5003
   161
{
icculus@8088
   162
#if defined(HAVE_MSC_ATOMICS) && (_M_IX86)
icculus@8088
   163
    return (_InterlockedCompareExchange((long*)a, (long)newval, (long)oldval) == (long)oldval);
icculus@8088
   164
#elif defined(HAVE_MSC_ATOMICS) && (!_M_IX86)
icculus@8088
   165
    return (_InterlockedCompareExchangePointer(a, newval, oldval) == oldval);
sezeroz@11318
   166
#elif defined(HAVE_WATCOM_ATOMICS)
sezeroz@11318
   167
    return (SDL_bool) _SDL_cmpxchg_watcom((int *)a, (long)newval, (long)oldval);
icculus@8088
   168
#elif defined(HAVE_GCC_ATOMICS)
icculus@8088
   169
    return __sync_bool_compare_and_swap(a, oldval, newval);
icculus@10973
   170
#elif defined(__MACOSX__) && defined(__LP64__)  /* this is deprecated in 10.12 sdk; favor gcc atomics. */
icculus@10973
   171
    return (SDL_bool) OSAtomicCompareAndSwap64Barrier((int64_t)oldval, (int64_t)newval, (int64_t*) a);
icculus@10973
   172
#elif defined(__MACOSX__) && !defined(__LP64__)  /* this is deprecated in 10.12 sdk; favor gcc atomics. */
icculus@10973
   173
    return (SDL_bool) OSAtomicCompareAndSwap32Barrier((int32_t)oldval, (int32_t)newval, (int32_t*) a);
binarycrusader@8979
   174
#elif defined(__SOLARIS__)
binarycrusader@8979
   175
    return (SDL_bool) (atomic_cas_ptr(a, oldval, newval) == oldval);
icculus@8088
   176
#elif EMULATE_CAS
slouken@5004
   177
    SDL_bool retval = SDL_FALSE;
slouken@5003
   178
slouken@5003
   179
    enterLock(a);
slouken@5003
   180
    if (*a == oldval) {
slouken@5003
   181
        *a = newval;
slouken@5004
   182
        retval = SDL_TRUE;
slouken@5003
   183
    }
slouken@5003
   184
    leaveLock(a);
slouken@5003
   185
slouken@5004
   186
    return retval;
icculus@8088
   187
#else
icculus@8088
   188
    #error Please define your platform.
icculus@8088
   189
#endif
icculus@8088
   190
}
icculus@8088
   191
icculus@8088
   192
int
icculus@8088
   193
SDL_AtomicSet(SDL_atomic_t *a, int v)
icculus@8088
   194
{
icculus@8088
   195
#ifdef HAVE_MSC_ATOMICS
icculus@8088
   196
    return _InterlockedExchange((long*)&a->value, v);
sezeroz@11318
   197
#elif defined(HAVE_WATCOM_ATOMICS)
sezeroz@11318
   198
    return _SDL_xchg_watcom(&a->value, v);
icculus@8088
   199
#elif defined(HAVE_GCC_ATOMICS)
icculus@8088
   200
    return __sync_lock_test_and_set(&a->value, v);
binarycrusader@8979
   201
#elif defined(__SOLARIS__) && defined(_LP64)
binarycrusader@8979
   202
    return (int) atomic_swap_64((volatile uint64_t*)&a->value, (uint64_t)v);
binarycrusader@8979
   203
#elif defined(__SOLARIS__) && !defined(_LP64)
binarycrusader@8979
   204
    return (int) atomic_swap_32((volatile uint32_t*)&a->value, (uint32_t)v);
icculus@8088
   205
#else
icculus@8088
   206
    int value;
icculus@8088
   207
    do {
icculus@8088
   208
        value = a->value;
icculus@8088
   209
    } while (!SDL_AtomicCAS(a, value, v));
icculus@8088
   210
    return value;
icculus@8088
   211
#endif
icculus@8088
   212
}
icculus@8088
   213
icculus@8088
   214
void*
icculus@8088
   215
SDL_AtomicSetPtr(void **a, void *v)
icculus@8088
   216
{
icculus@8092
   217
#if defined(HAVE_MSC_ATOMICS) && (_M_IX86)
icculus@8092
   218
    return (void *) _InterlockedExchange((long *)a, (long) v);
icculus@8092
   219
#elif defined(HAVE_MSC_ATOMICS) && (!_M_IX86)
icculus@8088
   220
    return _InterlockedExchangePointer(a, v);
sezeroz@11318
   221
#elif defined(HAVE_WATCOM_ATOMICS)
sezeroz@11318
   222
    return (void *) _SDL_xchg_watcom((int *)a, (long)v);
icculus@8088
   223
#elif defined(HAVE_GCC_ATOMICS)
icculus@8088
   224
    return __sync_lock_test_and_set(a, v);
binarycrusader@8979
   225
#elif defined(__SOLARIS__)
binarycrusader@8979
   226
    return atomic_swap_ptr(a, v);
icculus@8088
   227
#else
icculus@8088
   228
    void *value;
icculus@8088
   229
    do {
icculus@8088
   230
        value = *a;
icculus@8088
   231
    } while (!SDL_AtomicCASPtr(a, value, v));
icculus@8088
   232
    return value;
icculus@8088
   233
#endif
slouken@5003
   234
}
slouken@5003
   235
icculus@8088
   236
int
icculus@8088
   237
SDL_AtomicAdd(SDL_atomic_t *a, int v)
icculus@8088
   238
{
icculus@8088
   239
#ifdef HAVE_MSC_ATOMICS
icculus@8088
   240
    return _InterlockedExchangeAdd((long*)&a->value, v);
sezeroz@11318
   241
#elif defined(HAVE_WATCOM_ATOMICS)
sezeroz@11318
   242
    return _SDL_xadd_watcom(&a->value, v);
icculus@8088
   243
#elif defined(HAVE_GCC_ATOMICS)
icculus@8088
   244
    return __sync_fetch_and_add(&a->value, v);
binarycrusader@8979
   245
#elif defined(__SOLARIS__)
binarycrusader@8979
   246
    int pv = a->value;
binarycrusader@8979
   247
    membar_consumer();
binarycrusader@8979
   248
#if defined(_LP64)
binarycrusader@8979
   249
    atomic_add_64((volatile uint64_t*)&a->value, v);
binarycrusader@8979
   250
#elif !defined(_LP64)
binarycrusader@8979
   251
    atomic_add_32((volatile uint32_t*)&a->value, v);
binarycrusader@8979
   252
#endif
binarycrusader@8979
   253
    return pv;
icculus@8088
   254
#else
icculus@8088
   255
    int value;
icculus@8088
   256
    do {
icculus@8088
   257
        value = a->value;
icculus@8088
   258
    } while (!SDL_AtomicCAS(a, value, (value + v)));
icculus@8088
   259
    return value;
icculus@8088
   260
#endif
icculus@8088
   261
}
icculus@8088
   262
icculus@8088
   263
int
icculus@8088
   264
SDL_AtomicGet(SDL_atomic_t *a)
icculus@8088
   265
{
icculus@10972
   266
#ifdef HAVE_ATOMIC_LOAD_N
jlegg@10946
   267
    return __atomic_load_n(&a->value, __ATOMIC_SEQ_CST);
jlegg@10946
   268
#else
icculus@8088
   269
    int value;
icculus@8088
   270
    do {
icculus@8088
   271
        value = a->value;
icculus@8088
   272
    } while (!SDL_AtomicCAS(a, value, value));
icculus@8088
   273
    return value;
jlegg@10946
   274
#endif
icculus@8088
   275
}
icculus@8088
   276
icculus@8088
   277
void *
icculus@8088
   278
SDL_AtomicGetPtr(void **a)
icculus@8088
   279
{
icculus@10972
   280
#ifdef HAVE_ATOMIC_LOAD_N
jlegg@10946
   281
    return __atomic_load_n(a, __ATOMIC_SEQ_CST);
jlegg@10946
   282
#else
icculus@8088
   283
    void *value;
icculus@8088
   284
    do {
icculus@8088
   285
        value = *a;
icculus@8088
   286
    } while (!SDL_AtomicCASPtr(a, value, value));
icculus@8088
   287
    return value;
jlegg@10946
   288
#endif
icculus@8088
   289
}
icculus@8088
   290
slouken@10879
   291
void
slouken@10879
   292
SDL_MemoryBarrierReleaseFunction(void)
slouken@10879
   293
{
slouken@10879
   294
    SDL_MemoryBarrierRelease();
slouken@10879
   295
}
slouken@10879
   296
slouken@10879
   297
void
slouken@10879
   298
SDL_MemoryBarrierAcquireFunction(void)
slouken@10879
   299
{
slouken@10879
   300
    SDL_MemoryBarrierAcquire();
slouken@10879
   301
}
slouken@7394
   302
slouken@5003
   303
/* vi: set ts=4 sw=4 expandtab: */