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:
commit
f63ae0553e
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user