/ test / guard_test_basic.pass.cpp
guard_test_basic.pass.cpp
  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-exception
  6  //
  7  //===----------------------------------------------------------------------===//
  8  //
  9  // UNSUPPORTED: c++03
 10  
 11  #define TESTING_CXA_GUARD
 12  #include "../src/cxa_guard_impl.h"
 13  #include <cassert>
 14  
 15  // Disable GCC warning about tautological comparison of a function's address
 16  #if defined(__GNUC__) && !defined(__clang__)
 17  # pragma GCC diagnostic ignored "-Waddress"
 18  #endif
 19  
 20  using namespace __cxxabiv1;
 21  
 22  template <class GuardType, class Impl>
 23  struct Tests {
 24  private:
 25    Tests() : g{}, impl(&g) {}
 26    GuardType g;
 27    Impl impl;
 28  
 29    uint8_t first_byte() {
 30      uint8_t first;
 31      std::memcpy(&first, &g, 1);
 32      return first;
 33    }
 34  
 35    void reset() { g = {}; }
 36  
 37  public:
 38    // Test the post conditions on cxa_guard_acquire, cxa_guard_abort, and
 39    // cxa_guard_release. Specifically, that they leave the first byte with
 40    // the value 0 or 1 as specified by the ARM or Itanium specification.
 41    static void test() {
 42      Tests tests;
 43      tests.test_acquire();
 44      tests.test_abort();
 45      tests.test_release();
 46    }
 47  
 48    void test_acquire() {
 49      {
 50        reset();
 51        assert(first_byte() == 0);
 52        assert(impl.cxa_guard_acquire() == INIT_IS_PENDING);
 53        assert(first_byte() == 0);
 54      }
 55      {
 56        reset();
 57        assert(first_byte() == 0);
 58        assert(impl.cxa_guard_acquire() == INIT_IS_PENDING);
 59        impl.cxa_guard_release();
 60        assert(first_byte() == 1);
 61        assert(impl.cxa_guard_acquire() == INIT_IS_DONE);
 62      }
 63    }
 64  
 65    void test_release() {
 66      {
 67        reset();
 68        assert(first_byte() == 0);
 69        assert(impl.cxa_guard_acquire() == INIT_IS_PENDING);
 70        assert(first_byte() == 0);
 71        impl.cxa_guard_release();
 72        assert(first_byte() == 1);
 73      }
 74    }
 75  
 76    void test_abort() {
 77      {
 78        reset();
 79        assert(first_byte() == 0);
 80        assert(impl.cxa_guard_acquire() == INIT_IS_PENDING);
 81        assert(first_byte() == 0);
 82        impl.cxa_guard_abort();
 83        assert(first_byte() == 0);
 84        assert(impl.cxa_guard_acquire() == INIT_IS_PENDING);
 85        assert(first_byte() == 0);
 86      }
 87    }
 88  };
 89  
 90  struct NopMutex {
 91    bool lock() {
 92      assert(!is_locked);
 93      is_locked = true;
 94      return false;
 95    }
 96    bool unlock() {
 97      assert(is_locked);
 98      is_locked = false;
 99      return false;
100    }
101  
102  private:
103    bool is_locked = false;
104  };
105  NopMutex global_nop_mutex = {};
106  
107  struct NopCondVar {
108    bool broadcast() { return false; }
109    bool wait(NopMutex&) { return false; }
110  };
111  NopCondVar global_nop_cond = {};
112  
113  void NopFutexWait(int*, int) { assert(false); }
114  void NopFutexWake(int*) { assert(false); }
115  uint32_t MockGetThreadID() { return 0; }
116  
117  int main(int, char**) {
118    {
119  #if defined(_LIBCXXABI_HAS_NO_THREADS)
120      static_assert(CurrentImplementation == Implementation::NoThreads, "");
121      static_assert(
122          std::is_same<SelectedImplementation, InitByteNoThreads>::value, "");
123  #else
124      static_assert(CurrentImplementation == Implementation::GlobalLock, "");
125      static_assert(
126          std::is_same<
127              SelectedImplementation,
128              InitByteGlobalMutex<LibcppMutex, LibcppCondVar,
129                                  GlobalStatic<LibcppMutex>::instance,
130                                  GlobalStatic<LibcppCondVar>::instance>>::value,
131          "");
132  #endif
133    }
134    {
135  #if (defined(__APPLE__) || defined(__linux__))  && !defined(_LIBCXXABI_HAS_NO_THREADS)
136      assert(PlatformThreadID);
137  #endif
138      if (PlatformSupportsThreadID()) {
139        assert(PlatformThreadID() != 0);
140        assert(PlatformThreadID() == PlatformThreadID());
141      }
142    }
143    {
144      Tests<uint32_t, InitByteNoThreads>::test();
145      Tests<uint64_t, InitByteNoThreads>::test();
146    }
147    {
148      using MutexImpl =
149          InitByteGlobalMutex<NopMutex, NopCondVar, global_nop_mutex,
150                              global_nop_cond, MockGetThreadID>;
151      Tests<uint32_t, MutexImpl>::test();
152      Tests<uint64_t, MutexImpl>::test();
153    }
154    {
155      using FutexImpl =
156          InitByteFutex<&NopFutexWait, &NopFutexWake, &MockGetThreadID>;
157      Tests<uint32_t, FutexImpl>::test();
158      Tests<uint64_t, FutexImpl>::test();
159    }
160  
161    return 0;
162  }