Changeset View
Changeset View
Standalone View
Standalone View
contrib/libcxxrt/guard.cc
Show First 20 Lines • Show All 64 Lines • ▼ Show 20 Lines | |||||
/* | /* | ||||
* The least significant bit of the guard variable indicates that the object | * The least significant bit of the guard variable indicates that the object | ||||
* has been initialised, the most significant bit is used for a spinlock. | * has been initialised, the most significant bit is used for a spinlock. | ||||
*/ | */ | ||||
#ifdef __arm__ | #ifdef __arm__ | ||||
// ARM ABI - 32-bit guards. | // ARM ABI - 32-bit guards. | ||||
typedef uint32_t guard_t; | typedef uint32_t guard_t; | ||||
static const uint32_t LOCKED = ((guard_t)1) << 31; | typedef uint32_t guard_lock_t; | ||||
static const uint32_t LOCKED = static_cast<guard_t>(1) << 31; | |||||
static const uint32_t INITIALISED = 1; | static const uint32_t INITIALISED = 1; | ||||
#else | #define LOCK_PART(guard) (guard) | ||||
#define INIT_PART(guard) (guard) | |||||
#elif defined(_LP64) | |||||
typedef uint64_t guard_t; | typedef uint64_t guard_t; | ||||
typedef uint64_t guard_lock_t; | |||||
# if defined(__LITTLE_ENDIAN__) | # if defined(__LITTLE_ENDIAN__) | ||||
static const guard_t LOCKED = ((guard_t)1) << 63; | static const guard_t LOCKED = static_cast<guard_t>(1) << 63; | ||||
static const guard_t INITIALISED = 1; | static const guard_t INITIALISED = 1; | ||||
# else | # else | ||||
static const guard_t LOCKED = 1; | static const guard_t LOCKED = 1; | ||||
static const guard_t INITIALISED = ((guard_t)1) << 56; | static const guard_t INITIALISED = static_cast<guard_t>(1) << 56; | ||||
# endif | # endif | ||||
#define LOCK_PART(guard) (guard) | |||||
#define INIT_PART(guard) (guard) | |||||
#else | |||||
typedef uint32_t guard_lock_t; | |||||
# if defined(__LITTLE_ENDIAN__) | |||||
typedef struct { | |||||
uint32_t init_half; | |||||
uint32_t lock_half; | |||||
} guard_t; | |||||
static const uint32_t LOCKED = static_cast<guard_lock_t>(1) << 31; | |||||
static const uint32_t INITIALISED = 1; | |||||
# else | |||||
typedef struct { | |||||
uint32_t init_half; | |||||
uint32_t lock_half; | |||||
} guard_t; | |||||
#if __cplusplus >= 201103L | |||||
static_assert(sizeof(guard_t) == sizeof(uint64_t), ""); | |||||
theraven: This should work if you change it to _Static_assert, which I think we support for all C/C++… | |||||
dimAuthorUnsubmitted Not Done Inline ActionsYes, that works. dim: Yes, that works. | |||||
#endif | #endif | ||||
static const uint32_t LOCKED = 1; | |||||
static const uint32_t INITIALISED = static_cast<guard_lock_t>(1) << 24; | |||||
# endif | |||||
#define LOCK_PART(guard) (&(guard)->lock_half) | |||||
#define INIT_PART(guard) (&(guard)->init_half) | |||||
#endif | |||||
static const guard_lock_t INITIAL = 0; | |||||
/** | /** | ||||
* Acquires a lock on a guard, returning 0 if the object has already been | * 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 | * 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. | * this function just needs to read a byte from memory and return. | ||||
*/ | */ | ||||
extern "C" int __cxa_guard_acquire(volatile guard_t *guard_object) | extern "C" int __cxa_guard_acquire(volatile guard_t *guard_object) | ||||
{ | { | ||||
guard_lock_t old; | |||||
// Not an atomic read, doesn't establish a happens-before relationship, but | // 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 | // 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 | // then it's a fast path, otherwise we'll do something more expensive than | ||||
// this test anyway... | // this test anyway... | ||||
if ((INITIALISED == *guard_object)) { return 0; } | if (INITIALISED == *INIT_PART(guard_object)) | ||||
return 0; | |||||
// Spin trying to do the initialisation | // Spin trying to do the initialisation | ||||
while (1) | for (;;) | ||||
{ | { | ||||
// Loop trying to move the value of the guard from 0 (not | // Loop trying to move the value of the guard from 0 (not | ||||
// locked, not initialised) to the locked-uninitialised | // locked, not initialised) to the locked-uninitialised | ||||
// position. | // position. | ||||
switch (__sync_val_compare_and_swap(guard_object, 0, LOCKED)) | old = __sync_val_compare_and_swap(LOCK_PART(guard_object), | ||||
{ | INITIAL, LOCKED); | ||||
// If the old value was 0, we succeeded, so continue | if (old == INITIAL) { | ||||
// initialising | // Lock obtained. If lock and init bit are | ||||
case 0: | // in separate words, check for init race. | ||||
if (INIT_PART(guard_object) == LOCK_PART(guard_object)) | |||||
return 1; | return 1; | ||||
// If this was already initialised, return and let the caller skip | if (INITIALISED != *INIT_PART(guard_object)) | ||||
// initialising it again. | return 1; | ||||
case INITIALISED: | |||||
// No need for a memory barrier here, | |||||
// see first comment. | |||||
*LOCK_PART(guard_object) = INITIAL; | |||||
return 0; | 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(); | |||||
} | } | ||||
} | // If lock and init bit are in the same word, check again | ||||
//__builtin_unreachable(); | // if we are done. | ||||
if (INIT_PART(guard_object) == LOCK_PART(guard_object) && | |||||
old == INITIALISED) | |||||
return 0; | return 0; | ||||
assert(old == LOCKED); | |||||
// Another thread holds the lock. | |||||
// If lock and init bit are in different words, check | |||||
// if we are done before yielding and looping. | |||||
if (INIT_PART(guard_object) != LOCK_PART(guard_object) && | |||||
INITIALISED == *INIT_PART(guard_object)) | |||||
return 0; | |||||
sched_yield(); | |||||
} | } | ||||
} | |||||
/** | /** | ||||
* Releases the lock without marking the object as initialised. This function | * Releases the lock without marking the object as initialised. This function | ||||
* is called if initialising a static causes an exception to be thrown. | * is called if initialising a static causes an exception to be thrown. | ||||
*/ | */ | ||||
extern "C" void __cxa_guard_abort(volatile guard_t *guard_object) | extern "C" void __cxa_guard_abort(volatile guard_t *guard_object) | ||||
{ | { | ||||
__attribute__((unused)) | __attribute__((unused)) | ||||
bool reset = __sync_bool_compare_and_swap(guard_object, LOCKED, 0); | bool reset = __sync_bool_compare_and_swap(LOCK_PART(guard_object), | ||||
LOCKED, INITIAL); | |||||
assert(reset); | assert(reset); | ||||
} | } | ||||
/** | /** | ||||
* Releases the guard and marks the object as initialised. This function is | * Releases the guard and marks the object as initialised. This function is | ||||
* called after successful initialisation of a static. | * called after successful initialisation of a static. | ||||
*/ | */ | ||||
extern "C" void __cxa_guard_release(volatile guard_t *guard_object) | extern "C" void __cxa_guard_release(volatile guard_t *guard_object) | ||||
{ | { | ||||
guard_lock_t old; | |||||
if (INIT_PART(guard_object) == LOCK_PART(guard_object)) | |||||
old = LOCKED; | |||||
else | |||||
old = INITIAL; | |||||
__attribute__((unused)) | __attribute__((unused)) | ||||
bool reset = __sync_bool_compare_and_swap(guard_object, LOCKED, INITIALISED); | bool reset = __sync_bool_compare_and_swap(INIT_PART(guard_object), | ||||
old, INITIALISED); | |||||
assert(reset); | assert(reset); | ||||
if (INIT_PART(guard_object) != LOCK_PART(guard_object)) | |||||
*LOCK_PART(guard_object) = INITIAL; | |||||
} | } | ||||
This should work if you change it to _Static_assert, which I think we support for all C/C++ versions.