diff options
Diffstat (limited to 'contrib/libcxxrt/guard.cc')
-rw-r--r-- | contrib/libcxxrt/guard.cc | 164 |
1 files changed, 76 insertions, 88 deletions
diff --git a/contrib/libcxxrt/guard.cc b/contrib/libcxxrt/guard.cc index b14a13b..f0c26ab 100644 --- a/contrib/libcxxrt/guard.cc +++ b/contrib/libcxxrt/guard.cc @@ -41,124 +41,112 @@ * initialised. */ #include <stdint.h> +#include <stdlib.h> +#include <stdio.h> #include <pthread.h> #include <assert.h> +#include "atomic.h" -#ifdef __arm__ -// ARM ABI - 32-bit guards. +// Older GCC doesn't define __LITTLE_ENDIAN__ +#ifndef __LITTLE_ENDIAN__ + // If __BYTE_ORDER__ is defined, use that instead +# ifdef __BYTE_ORDER__ +# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +# define __LITTLE_ENDIAN__ +# endif + // x86 and ARM are the most common little-endian CPUs, so let's have a + // special case for them (ARM is already special cased). Assume everything + // else is big endian. +# elif defined(__x86_64) || defined(__i386) +# define __LITTLE_ENDIAN__ +# endif +#endif -/** - * Acquires a lock on a guard, returning 0 if the object has already been - * initialised, and 1 if it has not. If the object is already constructed then - * this function just needs to read a byte from memory and return. - */ -extern "C" int __cxa_guard_acquire(volatile int32_t *guard_object) -{ - if ((1<<31) == *guard_object) { return 0; } - // If we can atomically move the value from 0 -> 1, then this is - // uninitialised. - if (__sync_bool_compare_and_swap(guard_object, 0, 1)) - { - return 1; - } - // If the value is not 0, some other thread was initialising this. Spin - // until it's finished. - while (__sync_bool_compare_and_swap(guard_object, (1<<31), (1<<31))) - { - // If the other thread aborted, then we grab the lock - if (__sync_bool_compare_and_swap(guard_object, 0, 1)) - { - return 1; - } - sched_yield(); - } - return 0; -} -/** - * Releases the lock without marking the object as initialised. This function - * is called if initialising a static causes an exception to be thrown. +/* + * The least significant bit of the guard variable indicates that the object + * has been initialised, the most significant bit is used for a spinlock. */ -extern "C" void __cxa_guard_abort(int32_t *guard_object) -{ - assert(__sync_bool_compare_and_swap(guard_object, 1, 0)); -} -/** - * Releases the guard and marks the object as initialised. This function is - * called after successful initialisation of a static. - */ -extern "C" void __cxa_guard_release(int32_t *guard_object) -{ - assert(__sync_bool_compare_and_swap(guard_object, 1, (1<<31))); -} - - +#ifdef __arm__ +// ARM ABI - 32-bit guards. +typedef uint32_t guard_t; +static const uint32_t LOCKED = ((guard_t)1) << 31; +static const uint32_t INITIALISED = 1; #else -// Itanium ABI: 64-bit guards - -/** - * Returns a pointer to the low 32 bits in a 64-bit value, respecting the - * platform's byte order. - */ -static int32_t *low_32_bits(volatile int64_t *ptr) -{ - int32_t *low= (int32_t*)ptr; - // Test if the machine is big endian - constant propagation at compile time - // should eliminate this completely. - int one = 1; - if (*(char*)&one != 1) - { - low++; - } - return low; -} +typedef uint64_t guard_t; +# if defined(__LITTLE_ENDIAN__) +static const guard_t LOCKED = ((guard_t)1) << 63; +static const guard_t INITIALISED = 1; +# else +static const guard_t LOCKED = 1; +static const guard_t INITIALISED = ((guard_t)1) << 56; +# endif +#endif /** * Acquires a lock on a guard, returning 0 if the object has already been * initialised, and 1 if it has not. If the object is already constructed then * this function just needs to read a byte from memory and return. */ -extern "C" int __cxa_guard_acquire(volatile int64_t *guard_object) +extern "C" int __cxa_guard_acquire(volatile guard_t *guard_object) { - char first_byte = (*guard_object) >> 56; - if (1 == first_byte) { return 0; } - int32_t *lock = low_32_bits(guard_object); - // Simple spin lock using the low 32 bits. We assume that concurrent - // attempts to initialize statics are very rare, so we don't need to - // optimise for the case where we have lots of threads trying to acquire - // the lock at the same time. - while (!__sync_bool_compare_and_swap_4(lock, 0, 1)) + // Not an atomic read, doesn't establish a happens-before relationship, but + // if one is already established and we end up seeing an initialised state + // then it's a fast path, otherwise we'll do something more expensive than + // this test anyway... + if ((INITIALISED == *guard_object)) { return 0; } + // Spin trying to do the initialisation + while (1) { - if (1 == ((*guard_object) >> 56)) + // Loop trying to move the value of the guard from 0 (not + // locked, not initialised) to the locked-uninitialised + // position. + switch (__sync_val_compare_and_swap(guard_object, 0, LOCKED)) { - break; + // If the old value was 0, we succeeded, so continue + // initialising + case 0: + return 1; + // If this was already initialised, return and let the caller skip + // initialising it again. + case INITIALISED: + return 0; + // If it is locked by another thread, relinquish the CPU and try + // again later. + case LOCKED: + case LOCKED | INITIALISED: + sched_yield(); + break; + // If it is some other value, then something has gone badly wrong. + // Give up. + default: + fprintf(stderr, "Invalid state detected attempting to lock static initialiser.\n"); + abort(); } - sched_yield(); } - // We have to test the guard again, in case another thread has performed - // the initialisation while we were trying to acquire the lock. - first_byte = (*guard_object) >> 56; - return (1 != first_byte); + //__builtin_unreachable(); + return 0; } /** * Releases the lock without marking the object as initialised. This function * is called if initialising a static causes an exception to be thrown. */ -extern "C" void __cxa_guard_abort(int64_t *guard_object) +extern "C" void __cxa_guard_abort(volatile guard_t *guard_object) { - int32_t *lock = low_32_bits(guard_object); - *lock = 0; + __attribute__((unused)) + bool reset = __sync_bool_compare_and_swap(guard_object, LOCKED, 0); + assert(reset); } /** * Releases the guard and marks the object as initialised. This function is * called after successful initialisation of a static. */ -extern "C" void __cxa_guard_release(int64_t *guard_object) +extern "C" void __cxa_guard_release(volatile guard_t *guard_object) { - // Set the first byte to 1 - *guard_object |= ((int64_t)1) << 56; - __cxa_guard_abort(guard_object); + __attribute__((unused)) + bool reset = __sync_bool_compare_and_swap(guard_object, LOCKED, INITIALISED); + assert(reset); } -#endif + |