src/atomic/SDL_spinlock.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 07 Jul 2014 21:27:42 -0700
changeset 8991 13b0df1793e8
parent 8979 1e283b7a1580
child 9619 b94b6d0bff0f
permissions -rw-r--r--
Fixed bug 2618 - incomplete pthread-based lock support should be removed

binarycrusader

Since changeset 358696c354a8, SDL 2.0 has been broken on Solaris when compiling with the Solaris Studio compiler (which uses the pthread implementation of SDL_AtomicLock).

Notably, it gets stuck at the MemoryBarrierRelease in SDL_GetErrBuf:

6585 # 218
6586 if (!tls_errbuf && !tls_being_created) {
6587 SDL_AtomicLock_REAL ( & tls_lock );
6588 if (!tls_errbuf) {
6589 SDL_TLSID slot;
6590 tls_being_created = SDL_TRUE;
6591 slot = SDL_TLSCreate_REAL ( );
6592 tls_being_created = SDL_FALSE;
6593 { SDL_SpinLock _tmp = 0 ; SDL_AtomicLock_REAL ( & _tmp ) ; SDL_AtomicUnlock_REAL ( & _tmp ) ; };
^^^ loops forever above
6594 tls_errbuf = slot;
6595 }
6596 SDL_AtomicUnlock_REAL ( & tls_lock );
6597 }


Running: testthread
(process id 28926)
^Cdbx: warning: Interrupt ignored but forwarded to child.
signal INT (Interrupt) in __nanosleep at 0xfe52a875
0xfe52a875: __nanosleep+0x0015: jae __nanosleep+0x23 [ 0xfe52a883, .+0xe ]
Current function is SDL_Delay_REAL
204 was_error = nanosleep(&tv, &elapsed);
(dbx) where
[1] __nanosleep(0xfeffe848, 0xfeffe850, 0xfe75a5ac, 0xfe5169d8), at 0xfe52a875
[2] nanosleep(0xfeffe848, 0xfeffe850), at 0xfe516a3b
=>[3] SDL_Delay_REAL(ms = 0), line 204 in "SDL_systimer.c"
[4] SDL_AtomicLock_REAL(lock = 0xfeffe88c), line 104 in "SDL_spinlock.c"
[5] SDL_GetErrBuf(), line 225 in "SDL_thread.c"
[6] SDL_ClearError_REAL(), line 216 in "SDL_error.c"
[7] SDL_InitSubSystem_REAL(flags = 0), line 116 in "SDL.c"
[8] SDL_Init_REAL(flags = 0), line 244 in "SDL.c"
[9] SDL_Init(a = 0), line 89 in "SDL_dynapi_procs.h"
[10] main(argc = 1, argv = 0xfeffe948), line 65 in "testthread.c"

As far as I can tell, this is because pthread_spin_trylock() always returns EBUSY for this particular lock; since it works in other places, I'm suspicious.

Different Solaris Studio compiler versions seem to make no difference.

I've verified this is broken on Linux as well if SDL_spinlock.c is modified to use the pthread implementation.

This appears to be because pthread_spin_init() and pthread_spin_destroy() are not used with the locks as required.
slouken@8582
     1
/*
slouken@8582
     2
  Simple DirectMedia Layer
slouken@8149
     3
  Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
slouken@8582
     4
slouken@8582
     5
  This software is provided 'as-is', without any express or implied
slouken@8582
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@8582
     7
  arising from the use of this software.
slouken@8582
     8
slouken@8582
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@8582
    10
  including commercial applications, and to alter it and redistribute it
slouken@8582
    11
  freely, subject to the following restrictions:
slouken@8582
    12
slouken@8582
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@8582
    14
     claim that you wrote the original software. If you use this software
slouken@8582
    15
     in a product, an acknowledgment in the product documentation would be
slouken@8582
    16
     appreciated but is not required.
slouken@8582
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@8582
    18
     misrepresented as being the original software.
slouken@8582
    19
  3. This notice may not be removed or altered from any source distribution.
slouken@8582
    20
*/
icculus@8093
    21
#include "../SDL_internal.h"
slouken@8582
    22
slouken@8582
    23
#if defined(__WIN32__) || defined(__WINRT__)
slouken@8582
    24
#include "../core/windows/SDL_windows.h"
slouken@8582
    25
#endif
slouken@8582
    26
slouken@8582
    27
#include "SDL_atomic.h"
slouken@8582
    28
#include "SDL_mutex.h"
slouken@8582
    29
#include "SDL_timer.h"
slouken@8582
    30
binarycrusader@8979
    31
#if !defined(HAVE_GCC_ATOMICS) && defined(__SOLARIS__)
binarycrusader@8979
    32
#include <atomic.h>
binarycrusader@8979
    33
#endif
slouken@8582
    34
slouken@8582
    35
/* This function is where all the magic happens... */
slouken@8582
    36
SDL_bool
slouken@8582
    37
SDL_AtomicTryLock(SDL_SpinLock *lock)
slouken@8582
    38
{
slouken@8582
    39
#if SDL_ATOMIC_DISABLED
slouken@8582
    40
    /* Terrible terrible damage */
slouken@8582
    41
    static SDL_mutex *_spinlock_mutex;
slouken@8582
    42
slouken@8582
    43
    if (!_spinlock_mutex) {
slouken@8582
    44
        /* Race condition on first lock... */
slouken@8582
    45
        _spinlock_mutex = SDL_CreateMutex();
slouken@8582
    46
    }
slouken@8582
    47
    SDL_LockMutex(_spinlock_mutex);
slouken@8582
    48
    if (*lock == 0) {
slouken@8582
    49
        *lock = 1;
slouken@8582
    50
        SDL_UnlockMutex(_spinlock_mutex);
slouken@8582
    51
        return SDL_TRUE;
slouken@8582
    52
    } else {
slouken@8582
    53
        SDL_UnlockMutex(_spinlock_mutex);
slouken@8582
    54
        return SDL_FALSE;
slouken@8582
    55
    }
slouken@8582
    56
slouken@8582
    57
#elif defined(_MSC_VER)
slouken@8582
    58
    SDL_COMPILE_TIME_ASSERT(locksize, sizeof(*lock) == sizeof(long));
slouken@8582
    59
    return (InterlockedExchange((long*)lock, 1) == 0);
slouken@8582
    60
slouken@8582
    61
#elif HAVE_GCC_ATOMICS || HAVE_GCC_SYNC_LOCK_TEST_AND_SET
slouken@8582
    62
    return (__sync_lock_test_and_set(lock, 1) == 0);
slouken@8582
    63
slouken@8582
    64
#elif defined(__GNUC__) && defined(__arm__) && \
slouken@8582
    65
        (defined(__ARM_ARCH_4__) || defined(__ARM_ARCH_4T__) || \
slouken@8582
    66
         defined(__ARM_ARCH_5__) || defined(__ARM_ARCH_5TE__) || \
slouken@8582
    67
         defined(__ARM_ARCH_5TEJ__))
slouken@8582
    68
    int result;
slouken@8582
    69
    __asm__ __volatile__ (
slouken@8582
    70
        "swp %0, %1, [%2]\n"
slouken@8582
    71
        : "=&r,&r" (result) : "r,0" (1), "r,r" (lock) : "memory");
slouken@8582
    72
    return (result == 0);
slouken@8582
    73
slouken@8582
    74
#elif defined(__GNUC__) && defined(__arm__)
slouken@8582
    75
    int result;
slouken@8582
    76
    __asm__ __volatile__ (
slouken@8582
    77
        "ldrex %0, [%2]\nteq   %0, #0\nstrexeq %0, %1, [%2]"
slouken@8582
    78
        : "=&r" (result) : "r" (1), "r" (lock) : "cc", "memory");
slouken@8582
    79
    return (result == 0);
slouken@8582
    80
slouken@8582
    81
#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
slouken@8582
    82
    int result;
slouken@8582
    83
    __asm__ __volatile__(
slouken@8582
    84
        "lock ; xchgl %0, (%1)\n"
slouken@8582
    85
        : "=r" (result) : "r" (lock), "0" (1) : "cc", "memory");
slouken@8582
    86
    return (result == 0);
slouken@8582
    87
slouken@8582
    88
#elif defined(__MACOSX__) || defined(__IPHONEOS__)
slouken@8582
    89
    /* Maybe used for PowerPC, but the Intel asm or gcc atomics are favored. */
slouken@8582
    90
    return OSAtomicCompareAndSwap32Barrier(0, 1, lock);
slouken@8582
    91
binarycrusader@8979
    92
#elif defined(__SOLARIS__) && defined(_LP64)
binarycrusader@8979
    93
    /* Used for Solaris with non-gcc compilers. */
binarycrusader@8979
    94
    return (SDL_bool) ((int) atomic_cas_64((volatile uint64_t*)lock, 0, 1) == 0);
binarycrusader@8979
    95
binarycrusader@8979
    96
#elif defined(__SOLARIS__) && !defined(_LP64)
binarycrusader@8979
    97
    /* Used for Solaris with non-gcc compilers. */
binarycrusader@8979
    98
    return (SDL_bool) ((int) atomic_cas_32((volatile uint32_t*)lock, 0, 1) == 0);
binarycrusader@8979
    99
slouken@8582
   100
#else
slouken@8582
   101
#error Please implement for your platform.
slouken@8582
   102
    return SDL_FALSE;
slouken@8582
   103
#endif
slouken@8582
   104
}
slouken@8582
   105
slouken@8582
   106
void
slouken@8582
   107
SDL_AtomicLock(SDL_SpinLock *lock)
slouken@8582
   108
{
slouken@8582
   109
    /* FIXME: Should we have an eventual timeout? */
slouken@8582
   110
    while (!SDL_AtomicTryLock(lock)) {
slouken@8582
   111
        SDL_Delay(0);
slouken@8582
   112
    }
slouken@8582
   113
}
slouken@8582
   114
slouken@8582
   115
void
slouken@8582
   116
SDL_AtomicUnlock(SDL_SpinLock *lock)
slouken@8582
   117
{
slouken@8582
   118
#if defined(_MSC_VER)
slouken@8582
   119
    _ReadWriteBarrier();
slouken@8582
   120
    *lock = 0;
slouken@8582
   121
slouken@8582
   122
#elif HAVE_GCC_ATOMICS || HAVE_GCC_SYNC_LOCK_TEST_AND_SET
slouken@8582
   123
    __sync_lock_release(lock);
slouken@8582
   124
binarycrusader@8979
   125
#elif defined(__SOLARIS__)
binarycrusader@8979
   126
    /* Used for Solaris when not using gcc. */
binarycrusader@8979
   127
    *lock = 0;
binarycrusader@8979
   128
    membar_producer();
binarycrusader@8979
   129
slouken@8582
   130
#else
slouken@8582
   131
    *lock = 0;
slouken@8582
   132
#endif
slouken@8582
   133
}
slouken@8582
   134
slouken@8582
   135
/* vi: set ts=4 sw=4 expandtab: */