base_tests.cpp 3.25 KB
Newer Older
1
#include <catch.hpp>
2

3 4
#include "pls/internal/base/spin_lock.h"
#include "pls/internal/base/system_details.h"
5
#include "pls/internal/base/alignment.h"
6 7 8
#include "pls/internal/base/stack_allocator.h"

#include "test_helpers.h"
9

10
#include <mutex>
11
#include <atomic>
12
#include <thread>
13 14 15

using namespace pls::internal::base;

16 17
int base_tests_shared_counter;

18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
TEST_CASE("align helpers", "[internal/base/alignment.h") {
  system_details::pointer_t aligned_64 = 64;
  system_details::pointer_t aligned_32 = 32;
  system_details::pointer_t not_aligned_64 = 70;
  system_details::pointer_t not_aligned_32 = 60;

  REQUIRE(alignment::next_alignment(aligned_64, 64) == 64);
  REQUIRE(alignment::next_alignment(aligned_32, 32) == 32);
  REQUIRE(alignment::next_alignment(aligned_32, 64) == 64);

  REQUIRE(alignment::previous_alignment(not_aligned_64, 64) == 64);
  REQUIRE(alignment::next_alignment(not_aligned_64, 64) == 128);
  REQUIRE(alignment::previous_alignment(not_aligned_32, 32) == 32);
  REQUIRE(alignment::next_alignment(not_aligned_32, 32) == 64);
}

TEST_CASE("alignment wrapper", "[internal/base/alignment.h") {
  char filler1 = '\0';
  alignment::alignment_wrapper<int, 256> int_256{256};
  int filler2 = 0;
  alignment::alignment_wrapper<int, 1024> int_1024{1024};

  (void) filler1;
  (void) filler2;

  REQUIRE(int_256.object() == 256);
  REQUIRE((system_details::pointer_t) int_256.pointer() % 256 == 0);

  REQUIRE(int_1024.object() == 1024);
  REQUIRE((system_details::pointer_t) int_1024.pointer() % 1024 == 0);
}

50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
TEST_CASE("mmap stack allocator", "[internal/base/stack_allocator.h") {
  mmap_stack_allocator stack_allocator;

  char *stack = stack_allocator.allocate_stack(4096);

  SECTION("stack passes in valid range") {
    stack[0] = 'a';
    stack[4096 - 1] = 'a';

    REQUIRE(true);
  }

  SECTION("guard page sigsev on overflow") {
    REQUIRE(CHECK_ABORT([&]() { stack[-10] = 'a'; }));
  }

  stack_allocator.free_stack(4096, stack);

  SECTION("stack unmaps after free") {
    REQUIRE(CHECK_ABORT([&]() { stack[0] = 'a'; }));
  }
}

73
TEST_CASE("spinlock protects concurrent counter", "[internal/data_structures/spinlock.h]") {
74
  constexpr int num_iterations = 10000;
75
  base_tests_shared_counter = 0;
76 77
  std::atomic<int> barrier{2};

78 79 80
  spin_lock lock{};

  SECTION("lock can be used by itself") {
81
    std::thread t1{[&]() {
82 83 84
      barrier--;
      while (barrier != 0);

85 86 87 88 89
      for (int i = 0; i < num_iterations; i++) {
        lock.lock();
        base_tests_shared_counter++;
        lock.unlock();
      }
90
    }};
91
    std::thread t2{[&]() {
92
      barrier--;
93
      while (barrier != 0);
94

95 96 97 98 99
      for (int i = 0; i < num_iterations; i++) {
        lock.lock();
        base_tests_shared_counter--;
        lock.unlock();
      }
100
    }};
101 102 103 104 105 106 107 108

    t1.join();
    t2.join();

    REQUIRE(base_tests_shared_counter == 0);
  }

  SECTION("lock can be used with std::lock_guard") {
109
    std::thread t1{[&]() {
110 111 112 113
      for (int i = 0; i < num_iterations; i++) {
        std::lock_guard<spin_lock> my_lock{lock};
        base_tests_shared_counter++;
      }
114
    }};
115
    std::thread t2{[&]() {
116 117 118 119
      for (int i = 0; i < num_iterations; i++) {
        std::lock_guard<spin_lock> my_lock{lock};
        base_tests_shared_counter--;
      }
120
    }};
121 122 123 124 125 126

    t1.join();
    t2.join();

    REQUIRE(base_tests_shared_counter == 0);
  }
127
}