170ebeabfSEric Fiselier //===----------------------------------------------------------------------===//
270ebeabfSEric Fiselier //
370ebeabfSEric Fiselier // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
470ebeabfSEric Fiselier // See https://llvm.org/LICENSE.txt for license information.
570ebeabfSEric Fiselier // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
670ebeabfSEric Fiselier //
770ebeabfSEric Fiselier //===----------------------------------------------------------------------===//
870ebeabfSEric Fiselier //
931cbe0f2SLouis Dionne // UNSUPPORTED: c++03
1070ebeabfSEric Fiselier 
11*851bfc07SLouis Dionne // Necessary because we include a private header of libc++abi, which
12*851bfc07SLouis Dionne // only understands _LIBCXXABI_HAS_NO_THREADS.
13*851bfc07SLouis Dionne #include "test_macros.h"
14*851bfc07SLouis Dionne #ifdef TEST_HAS_NO_THREADS
15*851bfc07SLouis Dionne # define _LIBCXXABI_HAS_NO_THREADS
16*851bfc07SLouis Dionne #endif
17*851bfc07SLouis Dionne 
1870ebeabfSEric Fiselier #define TESTING_CXA_GUARD
1970ebeabfSEric Fiselier #include "../src/cxa_guard_impl.h"
207568899bSDavid Zarzycki #include <cassert>
2170ebeabfSEric Fiselier 
223601ee6cSDaniel McIntosh #if defined(__clang__)
233601ee6cSDaniel McIntosh #  pragma clang diagnostic ignored "-Wtautological-pointer-compare"
243601ee6cSDaniel McIntosh #elif defined(__GNUC__)
258d313927SLouis Dionne #  pragma GCC diagnostic ignored "-Waddress"
268d313927SLouis Dionne #endif
278d313927SLouis Dionne 
2870ebeabfSEric Fiselier using namespace __cxxabiv1;
2970ebeabfSEric Fiselier 
3070ebeabfSEric Fiselier template <class GuardType, class Impl>
3170ebeabfSEric Fiselier struct Tests {
3270ebeabfSEric Fiselier private:
TestsTests3370ebeabfSEric Fiselier   Tests() : g{}, impl(&g) {}
3470ebeabfSEric Fiselier   GuardType g;
3570ebeabfSEric Fiselier   Impl impl;
3670ebeabfSEric Fiselier 
first_byteTests3770ebeabfSEric Fiselier   uint8_t first_byte() {
3870ebeabfSEric Fiselier     uint8_t first;
3970ebeabfSEric Fiselier     std::memcpy(&first, &g, 1);
4070ebeabfSEric Fiselier     return first;
4170ebeabfSEric Fiselier   }
4270ebeabfSEric Fiselier 
resetTests4370ebeabfSEric Fiselier   void reset() { g = {}; }
4470ebeabfSEric Fiselier 
4570ebeabfSEric Fiselier public:
4670ebeabfSEric Fiselier   // Test the post conditions on cxa_guard_acquire, cxa_guard_abort, and
4770ebeabfSEric Fiselier   // cxa_guard_release. Specifically, that they leave the first byte with
4870ebeabfSEric Fiselier   // the value 0 or 1 as specified by the ARM or Itanium specification.
testTests4970ebeabfSEric Fiselier   static void test() {
5070ebeabfSEric Fiselier     Tests tests;
5170ebeabfSEric Fiselier     tests.test_acquire();
5270ebeabfSEric Fiselier     tests.test_abort();
5370ebeabfSEric Fiselier     tests.test_release();
5470ebeabfSEric Fiselier   }
5570ebeabfSEric Fiselier 
test_acquireTests5670ebeabfSEric Fiselier   void test_acquire() {
5770ebeabfSEric Fiselier     {
5870ebeabfSEric Fiselier       reset();
5970ebeabfSEric Fiselier       assert(first_byte() == 0);
6070ebeabfSEric Fiselier       assert(impl.cxa_guard_acquire() == INIT_IS_PENDING);
6170ebeabfSEric Fiselier       assert(first_byte() == 0);
6270ebeabfSEric Fiselier     }
6370ebeabfSEric Fiselier     {
6470ebeabfSEric Fiselier       reset();
6570ebeabfSEric Fiselier       assert(first_byte() == 0);
6670ebeabfSEric Fiselier       assert(impl.cxa_guard_acquire() == INIT_IS_PENDING);
6770ebeabfSEric Fiselier       impl.cxa_guard_release();
6870ebeabfSEric Fiselier       assert(first_byte() == 1);
6970ebeabfSEric Fiselier       assert(impl.cxa_guard_acquire() == INIT_IS_DONE);
7070ebeabfSEric Fiselier     }
7170ebeabfSEric Fiselier   }
7270ebeabfSEric Fiselier 
test_releaseTests7370ebeabfSEric Fiselier   void test_release() {
7470ebeabfSEric Fiselier     {
7570ebeabfSEric Fiselier       reset();
7670ebeabfSEric Fiselier       assert(first_byte() == 0);
7770ebeabfSEric Fiselier       assert(impl.cxa_guard_acquire() == INIT_IS_PENDING);
7870ebeabfSEric Fiselier       assert(first_byte() == 0);
7970ebeabfSEric Fiselier       impl.cxa_guard_release();
8070ebeabfSEric Fiselier       assert(first_byte() == 1);
8170ebeabfSEric Fiselier     }
8270ebeabfSEric Fiselier   }
8370ebeabfSEric Fiselier 
test_abortTests8470ebeabfSEric Fiselier   void test_abort() {
8570ebeabfSEric Fiselier     {
8670ebeabfSEric Fiselier       reset();
8770ebeabfSEric Fiselier       assert(first_byte() == 0);
8870ebeabfSEric Fiselier       assert(impl.cxa_guard_acquire() == INIT_IS_PENDING);
8970ebeabfSEric Fiselier       assert(first_byte() == 0);
9070ebeabfSEric Fiselier       impl.cxa_guard_abort();
9170ebeabfSEric Fiselier       assert(first_byte() == 0);
9270ebeabfSEric Fiselier       assert(impl.cxa_guard_acquire() == INIT_IS_PENDING);
9370ebeabfSEric Fiselier       assert(first_byte() == 0);
9470ebeabfSEric Fiselier     }
9570ebeabfSEric Fiselier   }
9670ebeabfSEric Fiselier };
9770ebeabfSEric Fiselier 
9870ebeabfSEric Fiselier struct NopMutex {
lockNopMutex9970ebeabfSEric Fiselier   bool lock() {
10070ebeabfSEric Fiselier     assert(!is_locked);
10170ebeabfSEric Fiselier     is_locked = true;
10270ebeabfSEric Fiselier     return false;
10370ebeabfSEric Fiselier   }
unlockNopMutex10470ebeabfSEric Fiselier   bool unlock() {
10570ebeabfSEric Fiselier     assert(is_locked);
10670ebeabfSEric Fiselier     is_locked = false;
10770ebeabfSEric Fiselier     return false;
10870ebeabfSEric Fiselier   }
10970ebeabfSEric Fiselier 
11070ebeabfSEric Fiselier private:
11170ebeabfSEric Fiselier   bool is_locked = false;
11270ebeabfSEric Fiselier };
11327fd2f60SEric Fiselier NopMutex global_nop_mutex = {};
11470ebeabfSEric Fiselier 
11570ebeabfSEric Fiselier struct NopCondVar {
broadcastNopCondVar11670ebeabfSEric Fiselier   bool broadcast() { return false; }
waitNopCondVar11770ebeabfSEric Fiselier   bool wait(NopMutex&) { return false; }
11870ebeabfSEric Fiselier };
11927fd2f60SEric Fiselier NopCondVar global_nop_cond = {};
12070ebeabfSEric Fiselier 
NopFutexWait(int *,int)12170ebeabfSEric Fiselier void NopFutexWait(int*, int) { assert(false); }
NopFutexWake(int *)12270ebeabfSEric Fiselier void NopFutexWake(int*) { assert(false); }
MockGetThreadID()12370ebeabfSEric Fiselier uint32_t MockGetThreadID() { return 0; }
12470ebeabfSEric Fiselier 
main(int,char **)125504bc07dSLouis Dionne int main(int, char**) {
12670ebeabfSEric Fiselier   {
127*851bfc07SLouis Dionne #if defined(TEST_HAS_NO_THREADS)
12870ebeabfSEric Fiselier     static_assert(CurrentImplementation == Implementation::NoThreads, "");
129f011a53cSDaniel McIntosh     static_assert(std::is_same<SelectedImplementation, NoThreadsGuard>::value, "");
13070ebeabfSEric Fiselier #else
131e42eeb88SDaniel McIntosh     static_assert(CurrentImplementation == Implementation::GlobalMutex, "");
132f011a53cSDaniel McIntosh     static_assert(std::is_same<SelectedImplementation,
133f011a53cSDaniel McIntosh                                GlobalMutexGuard<LibcppMutex, LibcppCondVar, GlobalStatic<LibcppMutex>::instance,
134f011a53cSDaniel McIntosh                                                 GlobalStatic<LibcppCondVar>::instance>>::value,
13570ebeabfSEric Fiselier                   "");
13670ebeabfSEric Fiselier #endif
13770ebeabfSEric Fiselier   }
13870ebeabfSEric Fiselier   {
139*851bfc07SLouis Dionne #if (defined(__APPLE__) || defined(__linux__))  && !defined(TEST_HAS_NO_THREADS)
14070ebeabfSEric Fiselier     assert(PlatformThreadID);
14170ebeabfSEric Fiselier #endif
1423601ee6cSDaniel McIntosh     if (PlatformThreadID != nullptr) {
14370ebeabfSEric Fiselier       assert(PlatformThreadID() != 0);
14470ebeabfSEric Fiselier       assert(PlatformThreadID() == PlatformThreadID());
14570ebeabfSEric Fiselier     }
14670ebeabfSEric Fiselier   }
14770ebeabfSEric Fiselier   {
148f011a53cSDaniel McIntosh     Tests<uint32_t, NoThreadsGuard>::test();
149f011a53cSDaniel McIntosh     Tests<uint64_t, NoThreadsGuard>::test();
15070ebeabfSEric Fiselier   }
15170ebeabfSEric Fiselier   {
152f011a53cSDaniel McIntosh     using MutexImpl = GlobalMutexGuard<NopMutex, NopCondVar, global_nop_mutex, global_nop_cond, MockGetThreadID>;
15370ebeabfSEric Fiselier     Tests<uint32_t, MutexImpl>::test();
15470ebeabfSEric Fiselier     Tests<uint64_t, MutexImpl>::test();
15570ebeabfSEric Fiselier   }
15670ebeabfSEric Fiselier   {
157f011a53cSDaniel McIntosh     using FutexImpl = FutexGuard<&NopFutexWait, &NopFutexWake, &MockGetThreadID>;
15870ebeabfSEric Fiselier     Tests<uint32_t, FutexImpl>::test();
15970ebeabfSEric Fiselier     Tests<uint64_t, FutexImpl>::test();
16070ebeabfSEric Fiselier   }
161504bc07dSLouis Dionne 
162504bc07dSLouis Dionne   return 0;
16370ebeabfSEric Fiselier }
164