chromium/third_party/libc++abi/src/src/cxa_guard_impl.h

//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LIBCXXABI_SRC_INCLUDE_CXA_GUARD_IMPL_H
#define LIBCXXABI_SRC_INCLUDE_CXA_GUARD_IMPL_H

/* cxa_guard_impl.h - Implements the C++ runtime support for function local
 * static guards.
 * The layout of the guard object is the same across ARM and Itanium.
 *
 * The first "guard byte" (which is checked by the compiler) is set only upon
 * the completion of cxa release.
 *
 * The second "init byte" does the rest of the bookkeeping. It tracks if
 * initialization is complete or pending, and if there are waiting threads.
 *
 * If the guard variable is 64-bits and the platforms supplies a 32-bit thread
 * identifier, it is used to detect recursive initialization. The thread ID of
 * the thread currently performing initialization is stored in the second word.
 *
 *  Guard Object Layout:
 * ---------------------------------------------------------------------------
 * | a+0: guard byte | a+1: init byte | a+2: unused ... | a+4: thread-id ... |
 * ---------------------------------------------------------------------------
 *
 * Note that we don't do what the ABI docs suggest (put a mutex in the guard
 * object which we acquire in cxa_guard_acquire and release in
 * cxa_guard_release). Instead we use the init byte to imitate that behaviour,
 * but without actually holding anything mutex related between aquire and
 * release/abort.
 *
 *  Access Protocol:
 *    For each implementation the guard byte is checked and set before accessing
 *    the init byte.
 *
 *  Overall Design:
 *    The implementation was designed to allow each implementation to be tested
 *    independent of the C++ runtime or platform support.
 *
 */

#include "__cxxabi_config.h"
#include "include/atomic_support.h" // from libc++
#if defined(__has_include)
#  if __has_include(<sys/futex.h>)
#    include <sys/futex.h>
#  endif
#  if __has_include(<sys/syscall.h>)
#    include <sys/syscall.h>
#  endif
#  if __has_include(<unistd.h>)
#    include <unistd.h>
#  endif
#endif

#include <__thread/support.h>
#include <cstdint>
#include <cstring>
#include <limits.h>
#include <stdlib.h>

#ifndef _LIBCXXABI_HAS_NO_THREADS
#  if defined(__ELF__) && defined(_LIBCXXABI_LINK_PTHREAD_LIB)
#    pragma comment(lib, "pthread")
#  endif
#endif

#if defined(__clang__)
#  pragma clang diagnostic push
#  pragma clang diagnostic ignored "-Wtautological-pointer-compare"
#elif defined(__GNUC__)
#  pragma GCC diagnostic push
#  pragma GCC diagnostic ignored "-Waddress"
#endif

// To make testing possible, this header is included from both cxa_guard.cpp
// and a number of tests.
//
// For this reason we place everything in an anonymous namespace -- even though
// we're in a header. We want the actual implementation and the tests to have
// unique definitions of the types in this header (since the tests may depend
// on function local statics).
//
// To enforce this either `BUILDING_CXA_GUARD` or `TESTING_CXA_GUARD` must be
// defined when including this file. Only `src/cxa_guard.cpp` should define
// the former.
#ifdef BUILDING_CXA_GUARD
#  include "abort_message.h"
#define ABORT_WITH_MESSAGE(...)
#elif defined(TESTING_CXA_GUARD)
#define ABORT_WITH_MESSAGE
#else
#  error "Either BUILDING_CXA_GUARD or TESTING_CXA_GUARD must be defined"
#endif

#if __has_feature(thread_sanitizer)
extern "C" void __tsan_acquire(void*);
extern "C" void __tsan_release(void*);
#else
#define __tsan_acquire(addr)
#define __tsan_release(addr)
#endif

namespace __cxxabiv1 {
// Use an anonymous namespace to ensure that the tests and actual implementation
// have unique definitions of these symbols.
namespace {

//===----------------------------------------------------------------------===//
//                          Misc Utilities
//===----------------------------------------------------------------------===//

template <class T, T (*Init)()>
struct LazyValue {};

template <class IntType>
class AtomicInt {};

//===----------------------------------------------------------------------===//
//                       PlatformGetThreadID
//===----------------------------------------------------------------------===//

#if defined(__APPLE__) && defined(_LIBCPP_HAS_THREAD_API_PTHREAD)
uint32_t PlatformThreadID() {
  static_assert(sizeof(mach_port_t) == sizeof(uint32_t), "");
  return static_cast<uint32_t>(pthread_mach_thread_np(std::__libcpp_thread_get_current_id()));
}
#elif defined(SYS_gettid) && defined(_LIBCPP_HAS_THREAD_API_PTHREAD)
uint32_t PlatformThreadID() {}
#else
constexpr uint32_t (*PlatformThreadID)() = nullptr;
#endif

//===----------------------------------------------------------------------===//
//                          GuardByte
//===----------------------------------------------------------------------===//

static constexpr uint8_t UNSET =;
static constexpr uint8_t COMPLETE_BIT =;
static constexpr uint8_t PENDING_BIT =;
static constexpr uint8_t WAITING_BIT =;

/// Manages reads and writes to the guard byte.
struct GuardByte {};

//===----------------------------------------------------------------------===//
//                       InitByte Implementations
//===----------------------------------------------------------------------===//
//
// Each initialization byte implementation supports the following methods:
//
//  InitByte(uint8_t* _init_byte_address, uint32_t* _thread_id_address)
//    Construct the InitByte object, initializing our member variables
//
//  bool acquire()
//    Called before we start the initialization. Check if someone else has already started, and if
//    not to signal our intent to start it ourselves. We determine the current status from the init
//    byte, which is one of 4 possible values:
//      COMPLETE:           Initialization was finished by somebody else. Return true.
//      PENDING:            Somebody has started the initialization already, set the WAITING bit,
//                          then wait for the init byte to get updated with a new value.
//      (PENDING|WAITING):  Somebody has started the initialization already, and we're not the
//                          first one waiting. Wait for the init byte to get updated.
//      UNSET:              Initialization hasn't successfully completed, and nobody is currently
//                          performing the initialization. Set the PENDING bit to indicate our
//                          intention to start the initialization, and return false.
//    The return value indicates whether initialization has already been completed.
//
//  void release()
//    Called after successfully completing the initialization. Update the init byte to reflect
//    that, then if anybody else is waiting, wake them up.
//
//  void abort()
//    Called after an error is thrown during the initialization. Reset the init byte to UNSET to
//    indicate that we're no longer performing the initialization, then if anybody is waiting, wake
//    them up so they can try performing the initialization.
//

//===----------------------------------------------------------------------===//
//                    Single Threaded Implementation
//===----------------------------------------------------------------------===//

/// InitByteNoThreads - Doesn't use any inter-thread synchronization when
/// managing reads and writes to the init byte.
struct InitByteNoThreads {};

//===----------------------------------------------------------------------===//
//                     Global Mutex Implementation
//===----------------------------------------------------------------------===//

struct LibcppMutex;
struct LibcppCondVar;

#ifndef _LIBCXXABI_HAS_NO_THREADS
struct LibcppMutex {};

struct LibcppCondVar {};
#else
struct LibcppMutex {};
struct LibcppCondVar {};
#endif // !defined(_LIBCXXABI_HAS_NO_THREADS)

/// InitByteGlobalMutex - Uses a global mutex and condition variable (common to
/// all static local variables) to manage reads and writes to the init byte.
template <class Mutex, class CondVar, Mutex& global_mutex, CondVar& global_cond,
          uint32_t (*GetThreadID)() = PlatformThreadID>
struct InitByteGlobalMutex {};

//===----------------------------------------------------------------------===//
//                         Futex Implementation
//===----------------------------------------------------------------------===//

#if defined(__OpenBSD__)
void PlatformFutexWait(int* addr, int expect) {
  constexpr int WAIT = 0;
  futex(reinterpret_cast<volatile uint32_t*>(addr), WAIT, expect, NULL, NULL);
  __tsan_acquire(addr);
}
void PlatformFutexWake(int* addr) {
  constexpr int WAKE = 1;
  __tsan_release(addr);
  futex(reinterpret_cast<volatile uint32_t*>(addr), WAKE, INT_MAX, NULL, NULL);
}
#elif defined(SYS_futex)
void PlatformFutexWait(int* addr, int expect) {}
void PlatformFutexWake(int* addr) {}
#else
constexpr void (*PlatformFutexWait)(int*, int) = nullptr;
constexpr void (*PlatformFutexWake)(int*) = nullptr;
#endif

constexpr bool PlatformSupportsFutex() {}

/// InitByteFutex - Uses a futex to manage reads and writes to the init byte.
template <void (*Wait)(int*, int) = PlatformFutexWait, void (*Wake)(int*) = PlatformFutexWake,
          uint32_t (*GetThreadIDArg)() = PlatformThreadID>
struct InitByteFutex {};

//===----------------------------------------------------------------------===//
//                          GuardObject
//===----------------------------------------------------------------------===//

enum class AcquireResult {};
constexpr AcquireResult INIT_IS_DONE =;
constexpr AcquireResult INIT_IS_PENDING =;

/// Co-ordinates between GuardByte and InitByte.
template <class InitByteT>
struct GuardObject {};

//===----------------------------------------------------------------------===//
//                          Convenience Classes
//===----------------------------------------------------------------------===//

/// NoThreadsGuard - Manages initialization without performing any inter-thread
/// synchronization.
NoThreadsGuard;

/// GlobalMutexGuard - Manages initialization using a global mutex and
/// condition variable.
GlobalMutexGuard;

/// FutexGuard - Manages initialization using atomics and the futex syscall for
/// waiting and waking.
FutexGuard;

//===----------------------------------------------------------------------===//
//
//===----------------------------------------------------------------------===//

template <class T>
struct GlobalStatic {};
template <class T>
_LIBCPP_CONSTINIT T GlobalStatic<T>::instance =;

enum class Implementation {};

template <Implementation Impl>
struct SelectImplementation;

template <>
struct SelectImplementation<Implementation::NoThreads> {};

template <>
struct SelectImplementation<Implementation::GlobalMutex> {};

template <>
struct SelectImplementation<Implementation::Futex> {};

// TODO(EricWF): We should prefer the futex implementation when available. But
// it should be done in a separate step from adding the implementation.
constexpr Implementation CurrentImplementation =Implementation::NoThreads;
#elif defined(_LIBCXXABI_USE_FUTEX)
    Implementation::Futex;
#else
    Implementation::GlobalMutex;
#endif

static_assert;

SelectedImplementation;

} // end namespace
} // end namespace __cxxabiv1

#if defined(__clang__)
#  pragma clang diagnostic pop
#elif defined(__GNUC__)
#  pragma GCC diagnostic pop
#endif

#endif // LIBCXXABI_SRC_INCLUDE_CXA_GUARD_IMPL_H