|  | //===----------------------------------------------------------------------===// | 
|  | // | 
|  | // 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 | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  | // | 
|  | // UNSUPPORTED: c++03 | 
|  |  | 
|  | // Necessary because we include a private header of libc++abi, which | 
|  | // only understands _LIBCXXABI_HAS_NO_THREADS. | 
|  | #include "test_macros.h" | 
|  | #ifdef TEST_HAS_NO_THREADS | 
|  | # define _LIBCXXABI_HAS_NO_THREADS | 
|  | #endif | 
|  |  | 
|  | #define TESTING_CXA_GUARD | 
|  | #include "../src/cxa_guard_impl.h" | 
|  | #include <cassert> | 
|  | #include <type_traits> | 
|  |  | 
|  | #if defined(__clang__) | 
|  | #  pragma clang diagnostic ignored "-Wtautological-pointer-compare" | 
|  | #elif defined(__GNUC__) | 
|  | #  pragma GCC diagnostic ignored "-Waddress" | 
|  | #endif | 
|  |  | 
|  | using namespace __cxxabiv1; | 
|  |  | 
|  | template <class GuardType, class Impl> | 
|  | struct Tests { | 
|  | private: | 
|  | Tests() : g{}, impl(&g) {} | 
|  | GuardType g; | 
|  | Impl impl; | 
|  |  | 
|  | uint8_t first_byte() { | 
|  | uint8_t first; | 
|  | std::memcpy(&first, &g, 1); | 
|  | return first; | 
|  | } | 
|  |  | 
|  | void reset() { g = {}; } | 
|  |  | 
|  | public: | 
|  | // Test the post conditions on cxa_guard_acquire, cxa_guard_abort, and | 
|  | // cxa_guard_release. Specifically, that they leave the first byte with | 
|  | // the value 0 or 1 as specified by the ARM or Itanium specification. | 
|  | static void test() { | 
|  | Tests tests; | 
|  | tests.test_acquire(); | 
|  | tests.test_abort(); | 
|  | tests.test_release(); | 
|  | } | 
|  |  | 
|  | void test_acquire() { | 
|  | { | 
|  | reset(); | 
|  | assert(first_byte() == 0); | 
|  | assert(impl.cxa_guard_acquire() == INIT_IS_PENDING); | 
|  | assert(first_byte() == 0); | 
|  | } | 
|  | { | 
|  | reset(); | 
|  | assert(first_byte() == 0); | 
|  | assert(impl.cxa_guard_acquire() == INIT_IS_PENDING); | 
|  | impl.cxa_guard_release(); | 
|  | assert(first_byte() == 1); | 
|  | assert(impl.cxa_guard_acquire() == INIT_IS_DONE); | 
|  | } | 
|  | } | 
|  |  | 
|  | void test_release() { | 
|  | { | 
|  | reset(); | 
|  | assert(first_byte() == 0); | 
|  | assert(impl.cxa_guard_acquire() == INIT_IS_PENDING); | 
|  | assert(first_byte() == 0); | 
|  | impl.cxa_guard_release(); | 
|  | assert(first_byte() == 1); | 
|  | } | 
|  | } | 
|  |  | 
|  | void test_abort() { | 
|  | { | 
|  | reset(); | 
|  | assert(first_byte() == 0); | 
|  | assert(impl.cxa_guard_acquire() == INIT_IS_PENDING); | 
|  | assert(first_byte() == 0); | 
|  | impl.cxa_guard_abort(); | 
|  | assert(first_byte() == 0); | 
|  | assert(impl.cxa_guard_acquire() == INIT_IS_PENDING); | 
|  | assert(first_byte() == 0); | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | struct NopMutex { | 
|  | bool lock() { | 
|  | assert(!is_locked); | 
|  | is_locked = true; | 
|  | return false; | 
|  | } | 
|  | bool unlock() { | 
|  | assert(is_locked); | 
|  | is_locked = false; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | private: | 
|  | bool is_locked = false; | 
|  | }; | 
|  | NopMutex global_nop_mutex = {}; | 
|  |  | 
|  | struct NopCondVar { | 
|  | bool broadcast() { return false; } | 
|  | bool wait(NopMutex&) { return false; } | 
|  | }; | 
|  | NopCondVar global_nop_cond = {}; | 
|  |  | 
|  | void NopFutexWait(int*, int) { assert(false); } | 
|  | void NopFutexWake(int*) { assert(false); } | 
|  | uint32_t MockGetThreadID() { return 0; } | 
|  |  | 
|  | int main(int, char**) { | 
|  | { | 
|  | #if defined(TEST_HAS_NO_THREADS) | 
|  | static_assert(CurrentImplementation == Implementation::NoThreads, ""); | 
|  | static_assert(std::is_same<SelectedImplementation, NoThreadsGuard>::value, ""); | 
|  | #else | 
|  | static_assert(CurrentImplementation == Implementation::GlobalMutex, ""); | 
|  | static_assert(std::is_same<SelectedImplementation, | 
|  | GlobalMutexGuard<LibcppMutex, LibcppCondVar, GlobalStatic<LibcppMutex>::instance, | 
|  | GlobalStatic<LibcppCondVar>::instance>>::value, | 
|  | ""); | 
|  | #endif | 
|  | } | 
|  | { | 
|  | #if (defined(__APPLE__) || defined(__linux__))  && !defined(TEST_HAS_NO_THREADS) | 
|  | assert(PlatformThreadID); | 
|  | #endif | 
|  | if (PlatformThreadID != nullptr) { | 
|  | assert(PlatformThreadID() != 0); | 
|  | assert(PlatformThreadID() == PlatformThreadID()); | 
|  | } | 
|  | } | 
|  | { | 
|  | Tests<uint32_t, NoThreadsGuard>::test(); | 
|  | Tests<uint64_t, NoThreadsGuard>::test(); | 
|  | } | 
|  | { | 
|  | using MutexImpl = GlobalMutexGuard<NopMutex, NopCondVar, global_nop_mutex, global_nop_cond, MockGetThreadID>; | 
|  | Tests<uint32_t, MutexImpl>::test(); | 
|  | Tests<uint64_t, MutexImpl>::test(); | 
|  | } | 
|  | { | 
|  | using FutexImpl = FutexGuard<&NopFutexWait, &NopFutexWake, &MockGetThreadID>; | 
|  | Tests<uint32_t, FutexImpl>::test(); | 
|  | Tests<uint64_t, FutexImpl>::test(); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } |