1 /** 2 * guard.cc: Functions for thread-safe static initialisation. 3 * 4 * Static values in C++ can be initialised lazily their first use. This file 5 * contains functions that are used to ensure that two threads attempting to 6 * initialize the same static do not call the constructor twice. This is 7 * important because constructors can have side effects, so calling the 8 * constructor twice may be very bad. 9 * 10 * Statics that require initialisation are protected by a 64-bit value. Any 11 * platform that can do 32-bit atomic test and set operations can use this 12 * value as a low-overhead lock. Because statics (in most sane code) are 13 * accessed far more times than they are initialised, this lock implementation 14 * is heavily optimised towards the case where the static has already been 15 * initialised. 16 */ 17 #include <stdint.h> 18 #include <pthread.h> 19 #include <assert.h> 20 21 #ifdef __arm__ 22 // ARM ABI - 32-bit guards. 23 24 /** 25 * Acquires a lock on a guard, returning 0 if the object has already been 26 * initialised, and 1 if it has not. If the object is already constructed then 27 * this function just needs to read a byte from memory and return. 28 */ 29 extern "C" int __cxa_guard_acquire(volatile int32_t *guard_object) 30 { 31 if ((1<<31) == *guard_object) { return 0; } 32 // If we can atomically move the value from 0 -> 1, then this is 33 // uninitialised. 34 if (__sync_bool_compare_and_swap(guard_object, 0, 1)) 35 { 36 return 1; 37 } 38 // If the value is not 0, some other thread was initialising this. Spin 39 // until it's finished. 40 while (__sync_bool_compare_and_swap(guard_object, (1<<31), (1<<31))) 41 { 42 // If the other thread aborted, then we grab the lock 43 if (__sync_bool_compare_and_swap(guard_object, 0, 1)) 44 { 45 return 1; 46 } 47 sched_yield(); 48 } 49 return 0; 50 } 51 52 /** 53 * Releases the lock without marking the object as initialised. This function 54 * is called if initialising a static causes an exception to be thrown. 55 */ 56 extern "C" void __cxa_guard_abort(int32_t *guard_object) 57 { 58 assert(__sync_bool_compare_and_swap(guard_object, 1, 0)); 59 } 60 /** 61 * Releases the guard and marks the object as initialised. This function is 62 * called after successful initialisation of a static. 63 */ 64 extern "C" void __cxa_guard_release(int32_t *guard_object) 65 { 66 assert(__sync_bool_compare_and_swap(guard_object, 1, (1<<31))); 67 } 68 69 70 #else 71 // Itanium ABI: 64-bit guards 72 73 /** 74 * Returns a pointer to the low 32 bits in a 64-bit value, respecting the 75 * platform's byte order. 76 */ 77 static int32_t *low_32_bits(volatile int64_t *ptr) 78 { 79 int32_t *low= (int32_t*)ptr; 80 // Test if the machine is big endian - constant propagation at compile time 81 // should eliminate this completely. 82 int one = 1; 83 if (*(char*)&one != 1) 84 { 85 low++; 86 } 87 return low; 88 } 89 90 /** 91 * Acquires a lock on a guard, returning 0 if the object has already been 92 * initialised, and 1 if it has not. If the object is already constructed then 93 * this function just needs to read a byte from memory and return. 94 */ 95 extern "C" int __cxa_guard_acquire(volatile int64_t *guard_object) 96 { 97 char first_byte = (*guard_object) >> 56; 98 if (1 == first_byte) { return 0; } 99 int32_t *lock = low_32_bits(guard_object); 100 // Simple spin lock using the low 32 bits. We assume that concurrent 101 // attempts to initialize statics are very rare, so we don't need to 102 // optimise for the case where we have lots of threads trying to acquire 103 // the lock at the same time. 104 while (!__sync_bool_compare_and_swap_4(lock, 0, 1)) 105 { 106 sched_yield(); 107 } 108 // We have to test the guard again, in case another thread has performed 109 // the initialisation while we were trying to acquire the lock. 110 first_byte = (*guard_object) >> 56; 111 return (1 != first_byte); 112 } 113 114 /** 115 * Releases the lock without marking the object as initialised. This function 116 * is called if initialising a static causes an exception to be thrown. 117 */ 118 extern "C" void __cxa_guard_abort(int64_t *guard_object) 119 { 120 int32_t *lock = low_32_bits(guard_object); 121 *lock = 0; 122 } 123 /** 124 * Releases the guard and marks the object as initialised. This function is 125 * called after successful initialisation of a static. 126 */ 127 extern "C" void __cxa_guard_release(int64_t *guard_object) 128 { 129 // Set the first byte to 1 130 *guard_object |= ((int64_t)1) << 56; 131 __cxa_guard_abort(guard_object); 132 } 133 134 #endif 135