guard.cc 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. /*
  2. * Copyright 2010-2012 PathScale, Inc. All rights reserved.
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions are met:
  6. *
  7. * 1. Redistributions of source code must retain the above copyright notice,
  8. * this list of conditions and the following disclaimer.
  9. *
  10. * 2. Redistributions in binary form must reproduce the above copyright notice,
  11. * this list of conditions and the following disclaimer in the documentation
  12. * and/or other materials provided with the distribution.
  13. *
  14. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
  15. * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  16. * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  17. * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
  18. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  19. * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  20. * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  21. * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  22. * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  23. * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  24. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25. */
  26. /**
  27. * guard.cc: Functions for thread-safe static initialisation.
  28. *
  29. * Static values in C++ can be initialised lazily their first use. This file
  30. * contains functions that are used to ensure that two threads attempting to
  31. * initialize the same static do not call the constructor twice. This is
  32. * important because constructors can have side effects, so calling the
  33. * constructor twice may be very bad.
  34. *
  35. * Statics that require initialisation are protected by a 64-bit value. Any
  36. * platform that can do 32-bit atomic test and set operations can use this
  37. * value as a low-overhead lock. Because statics (in most sane code) are
  38. * accessed far more times than they are initialised, this lock implementation
  39. * is heavily optimised towards the case where the static has already been
  40. * initialised.
  41. */
  42. #include <stdint.h>
  43. #include <assert.h>
  44. #include "se_cdefs.h"
  45. SGX_ACCESS_VERSION(tstdcxx, 3)
  46. #ifdef __arm__
  47. // ARM ABI - 32-bit guards.
  48. /**
  49. * Acquires a lock on a guard, returning 0 if the object has already been
  50. * initialised, and 1 if it has not. If the object is already constructed then
  51. * this function just needs to read a byte from memory and return.
  52. */
  53. extern "C" int __cxa_guard_acquire(volatile int32_t *guard_object)
  54. {
  55. if ((1<<31) == *guard_object) { return 0; }
  56. // If we can atomically move the value from 0 -> 1, then this is
  57. // uninitialised.
  58. if (__sync_bool_compare_and_swap(guard_object, 0, 1))
  59. {
  60. return 1;
  61. }
  62. // If the value is not 0, some other thread was initialising this. Spin
  63. // until it's finished.
  64. while (__sync_bool_compare_and_swap(guard_object, (1<<31), (1<<31)))
  65. {
  66. // If the other thread aborted, then we grab the lock
  67. if (__sync_bool_compare_and_swap(guard_object, 0, 1))
  68. {
  69. return 1;
  70. }
  71. sched_yield();
  72. }
  73. return 0;
  74. }
  75. /**
  76. * Releases the lock without marking the object as initialised. This function
  77. * is called if initialising a static causes an exception to be thrown.
  78. */
  79. extern "C" void __cxa_guard_abort(int32_t *guard_object)
  80. {
  81. assert(__sync_bool_compare_and_swap(guard_object, 1, 0));
  82. }
  83. /**
  84. * Releases the guard and marks the object as initialised. This function is
  85. * called after successful initialisation of a static.
  86. */
  87. extern "C" void __cxa_guard_release(int32_t *guard_object)
  88. {
  89. assert(__sync_bool_compare_and_swap(guard_object, 1, (1<<31)));
  90. }
  91. #else
  92. // Itanium ABI: 64-bit guards
  93. /**
  94. * Returns a pointer to the low 32 bits in a 64-bit value, respecting the
  95. * platform's byte order.
  96. */
  97. static int32_t *low_32_bits(volatile int64_t *ptr)
  98. {
  99. int32_t *low= (int32_t*)ptr;
  100. // Test if the machine is big endian - constant propagation at compile time
  101. // should eliminate this completely.
  102. int one = 1;
  103. if (*(char*)&one != 1)
  104. {
  105. low++;
  106. }
  107. return low;
  108. }
  109. /**
  110. * Acquires a lock on a guard, returning 0 if the object has already been
  111. * initialised, and 1 if it has not. If the object is already constructed then
  112. * this function just needs to read a byte from memory and return.
  113. */
  114. extern "C" int __cxa_guard_acquire(volatile int64_t *guard_object)
  115. {
  116. char first_byte = (*guard_object) >> 56;
  117. if (1 == first_byte) { return 0; }
  118. int32_t *lock = low_32_bits(guard_object);
  119. // Simple spin lock using the low 32 bits. We assume that concurrent
  120. // attempts to initialize statics are very rare, so we don't need to
  121. // optimise for the case where we have lots of threads trying to acquire
  122. // the lock at the same time.
  123. while (!__sync_bool_compare_and_swap_4(lock, 0, 1))
  124. {
  125. if (1 == ((*guard_object) >> 56))
  126. {
  127. break;
  128. }
  129. // sched_yield need to be implemented as a OCALL,
  130. // while we try to remove OCALLs in this library.
  131. //sched_yield();
  132. }
  133. // We have to test the guard again, in case another thread has performed
  134. // the initialisation while we were trying to acquire the lock.
  135. first_byte = (*guard_object) >> 56;
  136. return (1 != first_byte);
  137. }
  138. /**
  139. * Releases the lock without marking the object as initialised. This function
  140. * is called if initialising a static causes an exception to be thrown.
  141. */
  142. extern "C" void __cxa_guard_abort(int64_t *guard_object)
  143. {
  144. int32_t *lock = low_32_bits(guard_object);
  145. *lock = 0;
  146. }
  147. /**
  148. * Releases the guard and marks the object as initialised. This function is
  149. * called after successful initialisation of a static.
  150. */
  151. extern "C" void __cxa_guard_release(int64_t *guard_object)
  152. {
  153. // Set the first byte to 1
  154. *guard_object |= ((int64_t)1) << 56;
  155. __cxa_guard_abort(guard_object);
  156. }
  157. #endif