brintos

brintos / llvm-project-archived public Read only

0
0
Text · 24.5 KiB · 191a589 Raw
689 lines · c
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#ifndef LIBCXXABI_SRC_INCLUDE_CXA_GUARD_IMPL_H10#define LIBCXXABI_SRC_INCLUDE_CXA_GUARD_IMPL_H11 12/* cxa_guard_impl.h - Implements the C++ runtime support for function local13 * static guards.14 * The layout of the guard object is the same across ARM and Itanium.15 *16 * The first "guard byte" (which is checked by the compiler) is set only upon17 * the completion of cxa release.18 *19 * The second "init byte" does the rest of the bookkeeping. It tracks if20 * initialization is complete or pending, and if there are waiting threads.21 *22 * If the guard variable is 64-bits and the platforms supplies a 32-bit thread23 * identifier, it is used to detect recursive initialization. The thread ID of24 * the thread currently performing initialization is stored in the second word.25 *26 *  Guard Object Layout:27 * ---------------------------------------------------------------------------28 * | a+0: guard byte | a+1: init byte | a+2: unused ... | a+4: thread-id ... |29 * ---------------------------------------------------------------------------30 *31 * Note that we don't do what the ABI docs suggest (put a mutex in the guard32 * object which we acquire in cxa_guard_acquire and release in33 * cxa_guard_release). Instead we use the init byte to imitate that behaviour,34 * but without actually holding anything mutex related between aquire and35 * release/abort.36 *37 *  Access Protocol:38 *    For each implementation the guard byte is checked and set before accessing39 *    the init byte.40 *41 *  Overall Design:42 *    The implementation was designed to allow each implementation to be tested43 *    independent of the C++ runtime or platform support.44 *45 */46 47#include "__cxxabi_config.h"48#include "include/atomic_support.h" // from libc++49#if defined(__has_include)50#  if __has_include(<sys/futex.h>)51#    include <sys/futex.h>52#  endif53#  if __has_include(<sys/syscall.h>)54#    include <sys/syscall.h>55#  endif56#  if __has_include(<unistd.h>)57#    include <unistd.h>58#  endif59#endif60 61#include <__thread/support.h>62#include <cstdint>63#include <cstring>64#include <limits.h>65#include <stdlib.h>66 67#ifndef _LIBCXXABI_HAS_NO_THREADS68#  if defined(__ELF__) && defined(_LIBCXXABI_LINK_PTHREAD_LIB)69#    pragma comment(lib, "pthread")70#  endif71#endif72 73#if defined(__clang__)74#  pragma clang diagnostic push75#  pragma clang diagnostic ignored "-Wtautological-pointer-compare"76#elif defined(__GNUC__)77#  pragma GCC diagnostic push78#  pragma GCC diagnostic ignored "-Waddress"79#endif80 81// To make testing possible, this header is included from both cxa_guard.cpp82// and a number of tests.83//84// For this reason we place everything in an anonymous namespace -- even though85// we're in a header. We want the actual implementation and the tests to have86// unique definitions of the types in this header (since the tests may depend87// on function local statics).88//89// To enforce this either `BUILDING_CXA_GUARD` or `TESTING_CXA_GUARD` must be90// defined when including this file. Only `src/cxa_guard.cpp` should define91// the former.92#ifdef BUILDING_CXA_GUARD93#  include "abort_message.h"94#  define ABORT_WITH_MESSAGE(...) ::__abort_message(__VA_ARGS__)95#elif defined(TESTING_CXA_GUARD)96#  define ABORT_WITH_MESSAGE(...) ::abort()97#else98#  error "Either BUILDING_CXA_GUARD or TESTING_CXA_GUARD must be defined"99#endif100 101#if __has_feature(thread_sanitizer)102extern "C" void __tsan_acquire(void*);103extern "C" void __tsan_release(void*);104#else105#  define __tsan_acquire(addr) ((void)0)106#  define __tsan_release(addr) ((void)0)107#endif108 109namespace __cxxabiv1 {110// Use an anonymous namespace to ensure that the tests and actual implementation111// have unique definitions of these symbols.112namespace {113 114//===----------------------------------------------------------------------===//115//                          Misc Utilities116//===----------------------------------------------------------------------===//117 118template <class T, T (*Init)()>119struct LazyValue {120  LazyValue() : is_init(false) {}121 122  T& get() {123    if (!is_init) {124      value = Init();125      is_init = true;126    }127    return value;128  }129 130private:131  T value;132  bool is_init = false;133};134 135template <class IntType>136class AtomicInt {137public:138  using MemoryOrder = std::__libcpp_atomic_order;139 140  explicit AtomicInt(IntType* b) : b_(b) {}141  AtomicInt(AtomicInt const&) = delete;142  AtomicInt& operator=(AtomicInt const&) = delete;143 144  IntType load(MemoryOrder ord) { return std::__libcpp_atomic_load(b_, ord); }145  void store(IntType val, MemoryOrder ord) { std::__libcpp_atomic_store(b_, val, ord); }146  IntType exchange(IntType new_val, MemoryOrder ord) { return std::__libcpp_atomic_exchange(b_, new_val, ord); }147  bool compare_exchange(IntType* expected, IntType desired, MemoryOrder ord_success, MemoryOrder ord_failure) {148    return std::__libcpp_atomic_compare_exchange(b_, expected, desired, ord_success, ord_failure);149  }150 151private:152  IntType* b_;153};154 155//===----------------------------------------------------------------------===//156//                       PlatformGetThreadID157//===----------------------------------------------------------------------===//158 159#if defined(__APPLE__) && _LIBCPP_HAS_THREAD_API_PTHREAD160uint32_t PlatformThreadID() {161  static_assert(sizeof(mach_port_t) == sizeof(uint32_t), "");162  return static_cast<uint32_t>(pthread_mach_thread_np(std::__libcpp_thread_get_current_id()));163}164#elif defined(SYS_gettid) && _LIBCPP_HAS_THREAD_API_PTHREAD165uint32_t PlatformThreadID() {166  static_assert(sizeof(pid_t) == sizeof(uint32_t), "");167  return static_cast<uint32_t>(syscall(SYS_gettid));168}169#else170constexpr uint32_t (*PlatformThreadID)() = nullptr;171#endif172 173//===----------------------------------------------------------------------===//174//                          GuardByte175//===----------------------------------------------------------------------===//176 177static constexpr uint8_t UNSET = 0;178static constexpr uint8_t COMPLETE_BIT = (1 << 0);179static constexpr uint8_t PENDING_BIT = (1 << 1);180static constexpr uint8_t WAITING_BIT = (1 << 2);181 182/// Manages reads and writes to the guard byte.183struct GuardByte {184  GuardByte() = delete;185  GuardByte(GuardByte const&) = delete;186  GuardByte& operator=(GuardByte const&) = delete;187 188  explicit GuardByte(uint8_t* const guard_byte_address) : guard_byte(guard_byte_address) {}189 190public:191  /// The guard byte portion of cxa_guard_acquire. Returns true if192  /// initialization has already been completed.193  bool acquire() {194    // if guard_byte is non-zero, we have already completed initialization195    // (i.e. release has been called)196    return guard_byte.load(std::_AO_Acquire) != UNSET;197  }198 199  /// The guard byte portion of cxa_guard_release.200  void release() { guard_byte.store(COMPLETE_BIT, std::_AO_Release); }201 202  /// The guard byte portion of cxa_guard_abort.203  void abort() {} // Nothing to do204 205private:206  AtomicInt<uint8_t> guard_byte;207};208 209//===----------------------------------------------------------------------===//210//                       InitByte Implementations211//===----------------------------------------------------------------------===//212//213// Each initialization byte implementation supports the following methods:214//215//  InitByte(uint8_t* _init_byte_address, uint32_t* _thread_id_address)216//    Construct the InitByte object, initializing our member variables217//218//  bool acquire()219//    Called before we start the initialization. Check if someone else has already started, and if220//    not to signal our intent to start it ourselves. We determine the current status from the init221//    byte, which is one of 4 possible values:222//      COMPLETE:           Initialization was finished by somebody else. Return true.223//      PENDING:            Somebody has started the initialization already, set the WAITING bit,224//                          then wait for the init byte to get updated with a new value.225//      (PENDING|WAITING):  Somebody has started the initialization already, and we're not the226//                          first one waiting. Wait for the init byte to get updated.227//      UNSET:              Initialization hasn't successfully completed, and nobody is currently228//                          performing the initialization. Set the PENDING bit to indicate our229//                          intention to start the initialization, and return false.230//    The return value indicates whether initialization has already been completed.231//232//  void release()233//    Called after successfully completing the initialization. Update the init byte to reflect234//    that, then if anybody else is waiting, wake them up.235//236//  void abort()237//    Called after an error is thrown during the initialization. Reset the init byte to UNSET to238//    indicate that we're no longer performing the initialization, then if anybody is waiting, wake239//    them up so they can try performing the initialization.240//241 242//===----------------------------------------------------------------------===//243//                    Single Threaded Implementation244//===----------------------------------------------------------------------===//245 246/// InitByteNoThreads - Doesn't use any inter-thread synchronization when247/// managing reads and writes to the init byte.248struct InitByteNoThreads {249  InitByteNoThreads() = delete;250  InitByteNoThreads(InitByteNoThreads const&) = delete;251  InitByteNoThreads& operator=(InitByteNoThreads const&) = delete;252 253  explicit InitByteNoThreads(uint8_t* _init_byte_address, uint32_t*) : init_byte_address(_init_byte_address) {}254 255  /// The init byte portion of cxa_guard_acquire. Returns true if256  /// initialization has already been completed.257  bool acquire() {258    if (*init_byte_address == COMPLETE_BIT)259      return true;260    if (*init_byte_address & PENDING_BIT)261      ABORT_WITH_MESSAGE("__cxa_guard_acquire detected recursive initialization: do you have a function-local static variable whose initialization depends on that function?");262    *init_byte_address = PENDING_BIT;263    return false;264  }265 266  /// The init byte portion of cxa_guard_release.267  void release() { *init_byte_address = COMPLETE_BIT; }268  /// The init byte portion of cxa_guard_abort.269  void abort() { *init_byte_address = UNSET; }270 271private:272  /// The address of the byte used during initialization.273  uint8_t* const init_byte_address;274};275 276//===----------------------------------------------------------------------===//277//                     Global Mutex Implementation278//===----------------------------------------------------------------------===//279 280struct LibcppMutex;281struct LibcppCondVar;282 283#ifndef _LIBCXXABI_HAS_NO_THREADS284struct LibcppMutex {285  LibcppMutex() = default;286  LibcppMutex(LibcppMutex const&) = delete;287  LibcppMutex& operator=(LibcppMutex const&) = delete;288 289  bool lock() { return std::__libcpp_mutex_lock(&mutex); }290  bool unlock() { return std::__libcpp_mutex_unlock(&mutex); }291 292private:293  friend struct LibcppCondVar;294  std::__libcpp_mutex_t mutex = _LIBCPP_MUTEX_INITIALIZER;295};296 297struct LibcppCondVar {298  LibcppCondVar() = default;299  LibcppCondVar(LibcppCondVar const&) = delete;300  LibcppCondVar& operator=(LibcppCondVar const&) = delete;301 302  bool wait(LibcppMutex& mut) { return std::__libcpp_condvar_wait(&cond, &mut.mutex); }303  bool broadcast() { return std::__libcpp_condvar_broadcast(&cond); }304 305private:306  std::__libcpp_condvar_t cond = _LIBCPP_CONDVAR_INITIALIZER;307};308#else309struct LibcppMutex {};310struct LibcppCondVar {};311#endif // !defined(_LIBCXXABI_HAS_NO_THREADS)312 313/// InitByteGlobalMutex - Uses a global mutex and condition variable (common to314/// all static local variables) to manage reads and writes to the init byte.315template <class Mutex, class CondVar, Mutex& global_mutex, CondVar& global_cond,316          uint32_t (*GetThreadID)() = PlatformThreadID>317struct InitByteGlobalMutex {318 319  explicit InitByteGlobalMutex(uint8_t* _init_byte_address, uint32_t* _thread_id_address)320      : init_byte_address(_init_byte_address), thread_id_address(_thread_id_address),321        has_thread_id_support(_thread_id_address != nullptr && GetThreadID != nullptr) {}322 323public:324  /// The init byte portion of cxa_guard_acquire. Returns true if325  /// initialization has already been completed.326  bool acquire() {327    LockGuard g("__cxa_guard_acquire");328    // Check for possible recursive initialization.329    if (has_thread_id_support && (*init_byte_address & PENDING_BIT)) {330      if (*thread_id_address == current_thread_id.get())331        ABORT_WITH_MESSAGE("__cxa_guard_acquire detected recursive initialization: do you have a function-local static variable whose initialization depends on that function?");332    }333 334    // Wait until the pending bit is not set.335    while (*init_byte_address & PENDING_BIT) {336      *init_byte_address |= WAITING_BIT;337      global_cond.wait(global_mutex);338    }339 340    if (*init_byte_address == COMPLETE_BIT)341      return true;342 343    if (has_thread_id_support)344      *thread_id_address = current_thread_id.get();345 346    *init_byte_address = PENDING_BIT;347    return false;348  }349 350  /// The init byte portion of cxa_guard_release.351  void release() {352    bool has_waiting;353    {354      LockGuard g("__cxa_guard_release");355      has_waiting = *init_byte_address & WAITING_BIT;356      *init_byte_address = COMPLETE_BIT;357    }358    if (has_waiting) {359      if (global_cond.broadcast()) {360        ABORT_WITH_MESSAGE("%s failed to broadcast", "__cxa_guard_release");361      }362    }363  }364 365  /// The init byte portion of cxa_guard_abort.366  void abort() {367    bool has_waiting;368    {369      LockGuard g("__cxa_guard_abort");370      if (has_thread_id_support)371        *thread_id_address = 0;372      has_waiting = *init_byte_address & WAITING_BIT;373      *init_byte_address = UNSET;374    }375    if (has_waiting) {376      if (global_cond.broadcast()) {377        ABORT_WITH_MESSAGE("%s failed to broadcast", "__cxa_guard_abort");378      }379    }380  }381 382private:383  /// The address of the byte used during initialization.384  uint8_t* const init_byte_address;385  /// An optional address storing an identifier for the thread performing initialization.386  /// It's used to detect recursive initialization.387  uint32_t* const thread_id_address;388 389  const bool has_thread_id_support;390  LazyValue<uint32_t, GetThreadID> current_thread_id;391 392private:393  struct LockGuard {394    LockGuard() = delete;395    LockGuard(LockGuard const&) = delete;396    LockGuard& operator=(LockGuard const&) = delete;397 398    explicit LockGuard(const char* calling_func) : calling_func_(calling_func) {399      if (global_mutex.lock())400        ABORT_WITH_MESSAGE("%s failed to acquire mutex", calling_func_);401    }402 403    ~LockGuard() {404      if (global_mutex.unlock())405        ABORT_WITH_MESSAGE("%s failed to release mutex", calling_func_);406    }407 408  private:409    const char* const calling_func_;410  };411};412 413//===----------------------------------------------------------------------===//414//                         Futex Implementation415//===----------------------------------------------------------------------===//416 417#if defined(__OpenBSD__)418void PlatformFutexWait(int* addr, int expect) {419  constexpr int WAIT = 0;420  futex(reinterpret_cast<volatile uint32_t*>(addr), WAIT, expect, NULL, NULL);421  __tsan_acquire(addr);422}423void PlatformFutexWake(int* addr) {424  constexpr int WAKE = 1;425  __tsan_release(addr);426  futex(reinterpret_cast<volatile uint32_t*>(addr), WAKE, INT_MAX, NULL, NULL);427}428#elif defined(SYS_futex)429void PlatformFutexWait(int* addr, int expect) {430  constexpr int WAIT = 0;431  syscall(SYS_futex, addr, WAIT, expect, 0);432  __tsan_acquire(addr);433}434void PlatformFutexWake(int* addr) {435  constexpr int WAKE = 1;436  __tsan_release(addr);437  syscall(SYS_futex, addr, WAKE, INT_MAX);438}439#else440constexpr void (*PlatformFutexWait)(int*, int) = nullptr;441constexpr void (*PlatformFutexWake)(int*) = nullptr;442#endif443 444constexpr bool PlatformSupportsFutex() { return +PlatformFutexWait != nullptr; }445 446/// InitByteFutex - Uses a futex to manage reads and writes to the init byte.447template <void (*Wait)(int*, int) = PlatformFutexWait, void (*Wake)(int*) = PlatformFutexWake,448          uint32_t (*GetThreadIDArg)() = PlatformThreadID>449struct InitByteFutex {450 451  explicit InitByteFutex(uint8_t* _init_byte_address, uint32_t* _thread_id_address)452      : init_byte(_init_byte_address),453        has_thread_id_support(_thread_id_address != nullptr && GetThreadIDArg != nullptr),454        thread_id(_thread_id_address),455        base_address(reinterpret_cast<int*>(/*_init_byte_address & ~0x3*/ _init_byte_address - 1)) {}456 457public:458  /// The init byte portion of cxa_guard_acquire. Returns true if459  /// initialization has already been completed.460  bool acquire() {461    while (true) {462      uint8_t last_val = UNSET;463      if (init_byte.compare_exchange(&last_val, PENDING_BIT, std::_AO_Acq_Rel, std::_AO_Acquire)) {464        if (has_thread_id_support) {465          thread_id.store(current_thread_id.get(), std::_AO_Relaxed);466        }467        return false;468      }469 470      if (last_val == COMPLETE_BIT)471        return true;472 473      if (last_val & PENDING_BIT) {474 475        // Check for recursive initialization476        if (has_thread_id_support && thread_id.load(std::_AO_Relaxed) == current_thread_id.get()) {477          ABORT_WITH_MESSAGE("__cxa_guard_acquire detected recursive initialization: do you have a function-local static variable whose initialization depends on that function?");478        }479 480        if ((last_val & WAITING_BIT) == 0) {481          // This compare exchange can fail for several reasons482          // (1) another thread finished the whole thing before we got here483          // (2) another thread set the waiting bit we were trying to thread484          // (3) another thread had an exception and failed to finish485          if (!init_byte.compare_exchange(&last_val, PENDING_BIT | WAITING_BIT, std::_AO_Acq_Rel, std::_AO_Release)) {486            // (1) success, via someone else's work!487            if (last_val == COMPLETE_BIT)488              return true;489 490            // (3) someone else, bailed on doing the work, retry from the start!491            if (last_val == UNSET)492              continue;493 494            // (2) the waiting bit got set, so we are happy to keep waiting495          }496        }497        wait_on_initialization();498      }499    }500  }501 502  /// The init byte portion of cxa_guard_release.503  void release() {504    uint8_t old = init_byte.exchange(COMPLETE_BIT, std::_AO_Acq_Rel);505    if (old & WAITING_BIT)506      wake_all();507  }508 509  /// The init byte portion of cxa_guard_abort.510  void abort() {511    if (has_thread_id_support)512      thread_id.store(0, std::_AO_Relaxed);513 514    uint8_t old = init_byte.exchange(UNSET, std::_AO_Acq_Rel);515    if (old & WAITING_BIT)516      wake_all();517  }518 519private:520  /// Use the futex to wait on the current guard variable. Futex expects a521  /// 32-bit 4-byte aligned address as the first argument, so we use the 4-byte522  /// aligned address that encompasses the init byte (i.e. the address of the523  /// raw guard object that was passed to __cxa_guard_acquire/release/abort).524  void wait_on_initialization() { Wait(base_address, expected_value_for_futex(PENDING_BIT | WAITING_BIT)); }525  void wake_all() { Wake(base_address); }526 527private:528  AtomicInt<uint8_t> init_byte;529 530  const bool has_thread_id_support;531  // Unsafe to use unless has_thread_id_support532  AtomicInt<uint32_t> thread_id;533  LazyValue<uint32_t, GetThreadIDArg> current_thread_id;534 535  /// the 4-byte-aligned address that encompasses the init byte (i.e. the536  /// address of the raw guard object).537  int* const base_address;538 539  /// Create the expected integer value for futex `wait(int* addr, int expected)`.540  /// We pass the base address as the first argument, So this function creates541  /// an zero-initialized integer  with `b` copied at the correct offset.542  static int expected_value_for_futex(uint8_t b) {543    int dest_val = 0;544    std::memcpy(reinterpret_cast<char*>(&dest_val) + 1, &b, 1);545    return dest_val;546  }547 548  static_assert(Wait != nullptr && Wake != nullptr, "");549};550 551//===----------------------------------------------------------------------===//552//                          GuardObject553//===----------------------------------------------------------------------===//554 555enum class AcquireResult {556  INIT_IS_DONE,557  INIT_IS_PENDING,558};559constexpr AcquireResult INIT_IS_DONE = AcquireResult::INIT_IS_DONE;560constexpr AcquireResult INIT_IS_PENDING = AcquireResult::INIT_IS_PENDING;561 562/// Co-ordinates between GuardByte and InitByte.563template <class InitByteT>564struct GuardObject {565  GuardObject() = delete;566  GuardObject(GuardObject const&) = delete;567  GuardObject& operator=(GuardObject const&) = delete;568 569private:570  GuardByte guard_byte;571  InitByteT init_byte;572 573public:574  /// ARM Constructor575  explicit GuardObject(uint32_t* raw_guard_object)576      : guard_byte(reinterpret_cast<uint8_t*>(raw_guard_object)),577        init_byte(reinterpret_cast<uint8_t*>(raw_guard_object) + 1, nullptr) {}578 579  /// Itanium Constructor580  explicit GuardObject(uint64_t* raw_guard_object)581      : guard_byte(reinterpret_cast<uint8_t*>(raw_guard_object)),582        init_byte(reinterpret_cast<uint8_t*>(raw_guard_object) + 1, reinterpret_cast<uint32_t*>(raw_guard_object) + 1) {583  }584 585  /// Implements __cxa_guard_acquire.586  AcquireResult cxa_guard_acquire() {587    // Use short-circuit evaluation to avoid calling init_byte.acquire when588    // guard_byte.acquire returns true. (i.e. don't call it when we know from589    // the guard byte that initialization has already been completed)590    if (guard_byte.acquire() || init_byte.acquire())591      return INIT_IS_DONE;592    return INIT_IS_PENDING;593  }594 595  /// Implements __cxa_guard_release.596  void cxa_guard_release() {597    // Update guard byte first, so if somebody is woken up by init_byte.release598    // and comes all the way back around to __cxa_guard_acquire again, they see599    // it as having completed initialization.600    guard_byte.release();601    init_byte.release();602  }603 604  /// Implements __cxa_guard_abort.605  void cxa_guard_abort() {606    guard_byte.abort();607    init_byte.abort();608  }609};610 611//===----------------------------------------------------------------------===//612//                          Convenience Classes613//===----------------------------------------------------------------------===//614 615/// NoThreadsGuard - Manages initialization without performing any inter-thread616/// synchronization.617using NoThreadsGuard = GuardObject<InitByteNoThreads>;618 619/// GlobalMutexGuard - Manages initialization using a global mutex and620/// condition variable.621template <class Mutex, class CondVar, Mutex& global_mutex, CondVar& global_cond,622          uint32_t (*GetThreadID)() = PlatformThreadID>623using GlobalMutexGuard = GuardObject<InitByteGlobalMutex<Mutex, CondVar, global_mutex, global_cond, GetThreadID>>;624 625/// FutexGuard - Manages initialization using atomics and the futex syscall for626/// waiting and waking.627template <void (*Wait)(int*, int) = PlatformFutexWait, void (*Wake)(int*) = PlatformFutexWake,628          uint32_t (*GetThreadIDArg)() = PlatformThreadID>629using FutexGuard = GuardObject<InitByteFutex<Wait, Wake, GetThreadIDArg>>;630 631//===----------------------------------------------------------------------===//632//633//===----------------------------------------------------------------------===//634 635template <class T>636struct GlobalStatic {637  static T instance;638};639template <class T>640_LIBCPP_CONSTINIT T GlobalStatic<T>::instance = {};641 642enum class Implementation { NoThreads, GlobalMutex, Futex };643 644template <Implementation Impl>645struct SelectImplementation;646 647template <>648struct SelectImplementation<Implementation::NoThreads> {649  using type = NoThreadsGuard;650};651 652template <>653struct SelectImplementation<Implementation::GlobalMutex> {654  using type = GlobalMutexGuard<LibcppMutex, LibcppCondVar, GlobalStatic<LibcppMutex>::instance,655                                GlobalStatic<LibcppCondVar>::instance, PlatformThreadID>;656};657 658template <>659struct SelectImplementation<Implementation::Futex> {660  using type = FutexGuard<PlatformFutexWait, PlatformFutexWake, PlatformThreadID>;661};662 663// TODO(EricWF): We should prefer the futex implementation when available. But664// it should be done in a separate step from adding the implementation.665constexpr Implementation CurrentImplementation =666#if defined(_LIBCXXABI_HAS_NO_THREADS)667    Implementation::NoThreads;668#elif defined(_LIBCXXABI_USE_FUTEX)669    Implementation::Futex;670#else671    Implementation::GlobalMutex;672#endif673 674static_assert(CurrentImplementation != Implementation::Futex || PlatformSupportsFutex(),675              "Futex selected but not supported");676 677using SelectedImplementation = SelectImplementation<CurrentImplementation>::type;678 679} // namespace680} // namespace __cxxabiv1681 682#if defined(__clang__)683#  pragma clang diagnostic pop684#elif defined(__GNUC__)685#  pragma GCC diagnostic pop686#endif687 688#endif // LIBCXXABI_SRC_INCLUDE_CXA_GUARD_IMPL_H689