am 508d5579: am 782aa39f: Merge "Rewrite __cxa_guard.cpp with <stdatomic.h>."

* commit '508d5579cd0d3723662755ab418ac3085e9f30cd':
  Rewrite __cxa_guard.cpp with <stdatomic.h>.
This commit is contained in:
Yabin Cui 2015-01-29 20:35:44 +00:00 committed by Android Git Automerger
commit f63ae0553e

View File

@ -14,10 +14,13 @@
* limitations under the License. * limitations under the License.
*/ */
#include <stddef.h>
#include <endian.h> #include <endian.h>
#include <limits.h>
#undef _USING_LIBCXX // Prevent using of <atomic>.
#include <stdatomic.h>
#include <stddef.h>
#include "private/bionic_atomic_inline.h"
#include "private/bionic_futex.h" #include "private/bionic_futex.h"
// This file contains C++ ABI support functions for one time // This file contains C++ ABI support functions for one time
@ -49,66 +52,82 @@
// values. The LSB is tested by the compiler-generated code before calling // values. The LSB is tested by the compiler-generated code before calling
// __cxa_guard_acquire. // __cxa_guard_acquire.
union _guard_t { union _guard_t {
int volatile state; atomic_int state;
int32_t aligner; int32_t aligner;
}; };
const static int ready = 0x1;
const static int pending = 0x2;
const static int waiting = 0x6;
#else #else
// The Itanium/x86 C++ ABI (used by all other architectures) mandates that // The Itanium/x86 C++ ABI (used by all other architectures) mandates that
// guard variables are 64-bit aligned, 64-bit values. The LSB is tested by // guard variables are 64-bit aligned, 64-bit values. The LSB is tested by
// the compiler-generated code before calling __cxa_guard_acquire. // the compiler-generated code before calling __cxa_guard_acquire.
union _guard_t { union _guard_t {
int volatile state; atomic_int state;
int64_t aligner; int64_t aligner;
}; };
const static int ready = letoh32(0x1);
const static int pending = letoh32(0x100);
const static int waiting = letoh32(0x10000);
#endif #endif
// Set construction state values according to reference documentation.
// 0 is the initialization value.
// Arm requires ((*gv & 1) == 1) after __cxa_guard_release, ((*gv & 3) == 0) after __cxa_guard_abort.
// X86 requires first byte not modified by __cxa_guard_acquire, first byte is non-zero after
// __cxa_guard_release.
#define CONSTRUCTION_NOT_YET_STARTED 0
#define CONSTRUCTION_COMPLETE 1
#define CONSTRUCTION_UNDERWAY_WITHOUT_WAITER 0x100
#define CONSTRUCTION_UNDERWAY_WITH_WAITER 0x200
extern "C" int __cxa_guard_acquire(_guard_t* gv) { extern "C" int __cxa_guard_acquire(_guard_t* gv) {
// 0 -> pending, return 1 int old_value = atomic_load_explicit(&gv->state, memory_order_relaxed);
// pending -> waiting, wait and return 0
// waiting: untouched, wait and return 0
// ready: untouched, return 0
retry: while (true) {
if (__bionic_cmpxchg(0, pending, &gv->state) == 0) { if (old_value == CONSTRUCTION_COMPLETE) {
ANDROID_MEMBAR_FULL(); // A load_acquire operation is need before exiting with COMPLETE state, as we have to ensure
return 1; // that all the stores performed by the construction function are observable on this CPU
} // after we exit.
__bionic_cmpxchg(pending, waiting, &gv->state); // Indicate there is a waiter atomic_thread_fence(memory_order_acquire);
__futex_wait(&gv->state, waiting, NULL); return 0;
} else if (old_value == CONSTRUCTION_NOT_YET_STARTED) {
if (gv->state != ready) { if (!atomic_compare_exchange_weak_explicit(&gv->state, &old_value,
// __cxa_guard_abort was called, let every thread try since there is no return code for this condition CONSTRUCTION_UNDERWAY_WITHOUT_WAITER,
goto retry; memory_order_relaxed,
memory_order_relaxed)) {
continue;
}
// The acquire fence may not be needed. But as described in section 3.3.2 of
// the Itanium C++ ABI specification, it probably has to behave like the
// acquisition of a mutex, which needs an acquire fence.
atomic_thread_fence(memory_order_acquire);
return 1;
} else if (old_value == CONSTRUCTION_UNDERWAY_WITHOUT_WAITER) {
if (!atomic_compare_exchange_weak_explicit(&gv->state, &old_value,
CONSTRUCTION_UNDERWAY_WITH_WAITER,
memory_order_relaxed,
memory_order_relaxed)) {
continue;
}
} }
ANDROID_MEMBAR_FULL(); __futex_wait_ex(&gv->state, false, CONSTRUCTION_UNDERWAY_WITH_WAITER, NULL);
return 0; old_value = atomic_load_explicit(&gv->state, memory_order_relaxed);
}
} }
extern "C" void __cxa_guard_release(_guard_t* gv) { extern "C" void __cxa_guard_release(_guard_t* gv) {
// pending -> ready // Release fence is used to make all stores performed by the construction function
// waiting -> ready, and wake // visible in other threads.
int old_value = atomic_exchange_explicit(&gv->state, CONSTRUCTION_COMPLETE, memory_order_release);
ANDROID_MEMBAR_FULL(); if (old_value == CONSTRUCTION_UNDERWAY_WITH_WAITER) {
if (__bionic_cmpxchg(pending, ready, &gv->state) == 0) { __futex_wake_ex(&gv->state, false, INT_MAX);
return; }
}
gv->state = ready;
__futex_wake(&gv->state, 0x7fffffff);
} }
extern "C" void __cxa_guard_abort(_guard_t* gv) { extern "C" void __cxa_guard_abort(_guard_t* gv) {
ANDROID_MEMBAR_FULL(); // Release fence is used to make all stores performed by the construction function
gv->state= 0; // visible in other threads.
__futex_wake(&gv->state, 0x7fffffff); int old_value = atomic_exchange_explicit(&gv->state, CONSTRUCTION_NOT_YET_STARTED, memory_order_release);
if (old_value == CONSTRUCTION_UNDERWAY_WITH_WAITER) {
__futex_wake_ex(&gv->state, false, INT_MAX);
}
} }