data_structures_test.cpp 9.87 KB
Newer Older
1 2
#include <catch.hpp>

3
#include "pls/internal/base/system_details.h"
4

5
#include "pls/internal/data_structures/aligned_stack.h"
6 7 8

#include <mutex>

9
using namespace pls::internal::scheduling::data_structures;
10 11 12
using namespace pls::internal::base;
using namespace std;

13 14 15 16 17 18 19 20 21 22
TEST_CASE("aligned stack stores objects correctly", "[internal/data_structures/aligned_stack.h]") {
  constexpr long data_size = 1024;
  char data[data_size];
  aligned_stack stack{data, data_size};

  SECTION("stack correctly pushes sub linesize objects") {
    std::array<char, 5> small_data_one{'a', 'b', 'c', 'd', 'e'};
    std::array<char, 64> small_data_two{};
    std::array<char, 1> small_data_three{'A'};

23 24 25
    auto pointer_one = stack.push<decltype(small_data_one)>(small_data_one);
    auto pointer_two = stack.push<decltype(small_data_two)>(small_data_two);
    auto pointer_three = stack.push<decltype(small_data_three)>(small_data_three);
26 27 28 29 30 31 32 33 34 35

    REQUIRE(reinterpret_cast<std::uintptr_t>(pointer_one) % system_details::CACHE_LINE_SIZE == 0);
    REQUIRE(reinterpret_cast<std::uintptr_t>(pointer_two) % system_details::CACHE_LINE_SIZE == 0);
    REQUIRE(reinterpret_cast<std::uintptr_t>(pointer_three) % system_details::CACHE_LINE_SIZE == 0);
  }

  SECTION("stack correctly pushes above linesize objects") {
    std::array<char, 5> small_data_one{'a', 'b', 'c', 'd', 'e'};
    std::array<char, system_details::CACHE_LINE_SIZE + 10> big_data_one{};

36 37
    auto big_pointer_one = stack.push<decltype(big_data_one)>(big_data_one);
    auto small_pointer_one = stack.push<decltype(small_data_one)>(small_data_one);
38 39 40 41 42 43 44 45

    REQUIRE(reinterpret_cast<std::uintptr_t>(big_pointer_one) % system_details::CACHE_LINE_SIZE == 0);
    REQUIRE(reinterpret_cast<std::uintptr_t>(small_pointer_one) % system_details::CACHE_LINE_SIZE == 0);
  }

  SECTION("stack correctly stores and retrieves objects") {
    std::array<char, 5> data_one{'a', 'b', 'c', 'd', 'e'};

46
    stack.push<decltype(data_one)>(data_one);
47 48 49 50 51 52 53 54 55 56
    auto retrieved_data = stack.pop<std::array<char, 5>>();

    REQUIRE(retrieved_data == std::array<char, 5>{'a', 'b', 'c', 'd', 'e'});
  }

  SECTION("stack can push and pop multiple times with correct alignment") {
    std::array<char, 5> small_data_one{'a', 'b', 'c', 'd', 'e'};
    std::array<char, 64> small_data_two{};
    std::array<char, 1> small_data_three{'A'};

57 58 59 60 61 62 63
    auto pointer_one = stack.push<decltype(small_data_one)>(small_data_one);
    auto pointer_two = stack.push<decltype(small_data_two)>(small_data_two);
    auto pointer_three = stack.push<decltype(small_data_three)>(small_data_three);
    stack.pop<decltype(small_data_three)>();
    stack.pop<decltype(small_data_two)>();
    auto pointer_four = stack.push<decltype(small_data_two)>(small_data_two);
    auto pointer_five = stack.push<decltype(small_data_three)>(small_data_three);
64 65 66 67 68 69 70 71 72 73

    REQUIRE(reinterpret_cast<std::uintptr_t>(pointer_one) % system_details::CACHE_LINE_SIZE == 0);
    REQUIRE(reinterpret_cast<std::uintptr_t>(pointer_two) % system_details::CACHE_LINE_SIZE == 0);
    REQUIRE(reinterpret_cast<std::uintptr_t>(pointer_three) % system_details::CACHE_LINE_SIZE == 0);
    REQUIRE(reinterpret_cast<std::uintptr_t>(pointer_four) % system_details::CACHE_LINE_SIZE == 0);
    REQUIRE(reinterpret_cast<std::uintptr_t>(pointer_five) % system_details::CACHE_LINE_SIZE == 0);

    REQUIRE(pointer_four == pointer_two);
    REQUIRE(pointer_five == pointer_three);
  }
74 75
}

76
TEST_CASE("work_stealing_deque functions correctly", "[internal/data_structures/work_stealing_deque.h]") {
77
  SECTION("add and remove items form the tail") {
78 79 80 81 82 83 84 85
    constexpr long data_size = 2 << 14;
    char data[data_size];
    aligned_stack stack{data, data_size};
    work_stealing_deque<int> deque{&stack};

    int one = 1, two = 2, three = 3, four = 4;

    SECTION("add and remove items form the tail") {
86 87 88 89 90 91 92 93 94 95
      deque.push_task<int>(one);
      deque.publish_last_task();
      deque.push_task<int>(two);
      deque.publish_last_task();
      deque.push_task<int>(three);
      deque.publish_last_task();

      REQUIRE(*deque.pop_local_task() == three);
      REQUIRE(*deque.pop_local_task() == two);
      REQUIRE(*deque.pop_local_task() == one);
96 97 98
    }

    SECTION("handles getting empty by popping the tail correctly") {
99 100 101
      deque.push_task<int>(one);
      deque.publish_last_task();
      REQUIRE(*deque.pop_local_task() == one);
102

103 104 105
      deque.push_task<int>(two);
      deque.publish_last_task();
      REQUIRE(*deque.pop_local_task() == two);
106 107 108
    }

    SECTION("remove items form the head") {
109 110 111 112 113 114 115 116 117 118
      deque.push_task<int>(one);
      deque.publish_last_task();
      deque.push_task<int>(two);
      deque.publish_last_task();
      deque.push_task<int>(three);
      deque.publish_last_task();

      REQUIRE(*deque.pop_external_task() == one);
      REQUIRE(*deque.pop_external_task() == two);
      REQUIRE(*deque.pop_external_task() == three);
119 120 121
    }

    SECTION("handles getting empty by popping the head correctly") {
122 123 124
      deque.push_task<int>(one);
      deque.publish_last_task();
      REQUIRE(*deque.pop_external_task() == one);
125

126 127 128
      deque.push_task<int>(two);
      deque.publish_last_task();
      REQUIRE(*deque.pop_external_task() == two);
129 130 131
    }

    SECTION("handles getting empty by popping the head and tail correctly") {
132 133 134
      deque.push_task<int>(one);
      deque.publish_last_task();
      REQUIRE(*deque.pop_local_task() == one);
135

136 137 138
      deque.push_task<int>(two);
      deque.publish_last_task();
      REQUIRE(*deque.pop_external_task() == two);
139

140 141 142
      deque.push_task<int>(three);
      deque.publish_last_task();
      REQUIRE(*deque.pop_local_task() == three);
143 144 145
    }

    SECTION("handles jumps bigger 1 correctly") {
146 147 148 149 150 151 152 153 154 155 156 157 158
      deque.push_task<int>(one);
      deque.publish_last_task();
      deque.push_task<int>(two);
      deque.publish_last_task();
      REQUIRE(*deque.pop_local_task() == two);

      deque.push_task<int>(three);
      deque.publish_last_task();
      deque.push_task<int>(four);
      deque.publish_last_task();
      REQUIRE(*deque.pop_external_task() == one);
      REQUIRE(*deque.pop_external_task() == three);
      REQUIRE(*deque.pop_external_task() == four);
159 160 161
    }

    SECTION("handles stack reset 1 correctly when emptied by tail") {
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
      deque.push_task<int>(one);
      deque.publish_last_task();
      auto state = deque.save_offset();
      deque.push_task<int>(two);
      deque.publish_last_task();
      REQUIRE(*deque.pop_local_task() == two);

      deque.reset_offset(state);
      REQUIRE(*deque.pop_local_task() == one);

      deque.push_task<int>(three);
      deque.publish_last_task();
      deque.push_task<int>(four);
      deque.publish_last_task();
      REQUIRE(*deque.pop_external_task() == three);
      REQUIRE(*deque.pop_local_task() == four);
178
    }
179
  }
180
}
181

182
TEST_CASE("locking_deque functions correctly", "[internal/data_structures/locking_deque.h]") {
183
  SECTION("add and remove items form the tail") {
184 185 186 187 188 189 190 191
    constexpr long data_size = 2 << 14;
    char data[data_size];
    aligned_stack stack{data, data_size};
    locking_deque<int> deque{&stack};

    int one = 1, two = 2, three = 3, four = 4;

    SECTION("add and remove items form the tail") {
192 193 194 195 196 197
      deque.push_task<int>(one);
      deque.publish_last_task();
      deque.push_task<int>(two);
      deque.publish_last_task();
      deque.push_task<int>(three);
      deque.publish_last_task();
198

199 200 201
      REQUIRE(*deque.pop_local_task() == three);
      REQUIRE(*deque.pop_local_task() == two);
      REQUIRE(*deque.pop_local_task() == one);
202 203 204
    }

    SECTION("handles getting empty by popping the tail correctly") {
205 206 207
      deque.push_task<int>(one);
      deque.publish_last_task();
      REQUIRE(*deque.pop_local_task() == one);
208

209 210 211
      deque.push_task<int>(two);
      deque.publish_last_task();
      REQUIRE(*deque.pop_local_task() == two);
212 213 214
    }

    SECTION("remove items form the head") {
215 216 217 218 219 220
      deque.push_task<int>(one);
      deque.publish_last_task();
      deque.push_task<int>(two);
      deque.publish_last_task();
      deque.push_task<int>(three);
      deque.publish_last_task();
221

222 223 224
      REQUIRE(*deque.pop_external_task() == one);
      REQUIRE(*deque.pop_external_task() == two);
      REQUIRE(*deque.pop_external_task() == three);
225 226 227
    }

    SECTION("handles getting empty by popping the head correctly") {
228 229 230
      deque.push_task<int>(one);
      deque.publish_last_task();
      REQUIRE(*deque.pop_external_task() == one);
231

232 233 234
      deque.push_task<int>(two);
      deque.publish_last_task();
      REQUIRE(*deque.pop_external_task() == two);
235 236 237
    }

    SECTION("handles getting empty by popping the head and tail correctly") {
238 239 240
      deque.push_task<int>(one);
      deque.publish_last_task();
      REQUIRE(*deque.pop_local_task() == one);
241

242 243 244
      deque.push_task<int>(two);
      deque.publish_last_task();
      REQUIRE(*deque.pop_external_task() == two);
245

246 247 248
      deque.push_task<int>(three);
      deque.publish_last_task();
      REQUIRE(*deque.pop_local_task() == three);
249 250 251
    }

    SECTION("handles jumps bigger 1 correctly") {
252 253 254 255 256 257 258 259 260 261 262 263 264
      deque.push_task<int>(one);
      deque.publish_last_task();
      deque.push_task<int>(two);
      deque.publish_last_task();
      REQUIRE(*deque.pop_local_task() == two);

      deque.push_task<int>(three);
      deque.publish_last_task();
      deque.push_task<int>(four);
      deque.publish_last_task();
      REQUIRE(*deque.pop_external_task() == one);
      REQUIRE(*deque.pop_external_task() == three);
      REQUIRE(*deque.pop_external_task() == four);
265 266 267
    }

    SECTION("handles stack reset 1 correctly when emptied by tail") {
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
      deque.push_task<int>(one);
      deque.publish_last_task();
      auto state = deque.save_offset();
      deque.push_task<int>(two);
      deque.publish_last_task();
      REQUIRE(*deque.pop_local_task() == two);

      deque.reset_offset(state);
      REQUIRE(*deque.pop_local_task() == one);

      deque.push_task<int>(three);
      deque.publish_last_task();
      deque.push_task<int>(four);
      deque.publish_last_task();
      REQUIRE(*deque.pop_external_task() == three);
      REQUIRE(*deque.pop_local_task() == four);
284
    }
285 286
  }
}