126 lines
3.2 KiB
C++
126 lines
3.2 KiB
C++
#include <catch2/catch_test_macros.hpp>
|
|
|
|
#include "matador/object/object_cache.hpp"
|
|
#include "matador/object/object_resolver.hpp"
|
|
#include "matador/utils/identifier.hpp"
|
|
|
|
#include "../test/models/person.hpp"
|
|
|
|
#include <atomic>
|
|
#include <condition_variable>
|
|
#include <memory>
|
|
#include <mutex>
|
|
#include <thread>
|
|
#include <vector>
|
|
|
|
using namespace matador::test;
|
|
|
|
namespace {
|
|
class dummy_resolver final : public matador::object::object_resolver<person> {
|
|
public:
|
|
explicit dummy_resolver(std::shared_ptr<person> shared, std::atomic_int &calls)
|
|
: shared_(std::move(shared))
|
|
, calls_(calls) {}
|
|
|
|
std::shared_ptr<person> resolve(const matador::utils::identifier & /*id*/) override {
|
|
++calls_;
|
|
return shared_;
|
|
}
|
|
|
|
private:
|
|
std::shared_ptr<person> shared_;
|
|
std::atomic_int &calls_;
|
|
};
|
|
|
|
class start_latch {
|
|
public:
|
|
explicit start_latch(int participants)
|
|
: participants_(participants) {}
|
|
|
|
void arrive_and_wait() {
|
|
std::unique_lock<std::mutex> lock(m_);
|
|
++arrived_;
|
|
if (arrived_ >= participants_) {
|
|
open_ = true;
|
|
cv_.notify_all();
|
|
return;
|
|
}
|
|
cv_.wait(lock, [&] { return open_; });
|
|
}
|
|
|
|
private:
|
|
std::mutex m_;
|
|
std::condition_variable cv_;
|
|
int participants_{0};
|
|
int arrived_{0};
|
|
bool open_{false};
|
|
};
|
|
} // namespace
|
|
|
|
TEST_CASE("object_cache: acquire_proxy returns the same proxy instance across threads", "[object][cache][threadsafe]") {
|
|
matador::object::object_cache cache;
|
|
const matador::utils::identifier id{123};
|
|
|
|
std::atomic_int calls{0};
|
|
auto entity = std::make_shared<person>();
|
|
auto resolver = std::make_shared<dummy_resolver>(entity, calls);
|
|
|
|
constexpr int threads = 16;
|
|
start_latch latch(threads);
|
|
|
|
std::vector<std::shared_ptr<matador::object::object_proxy<person>>> proxies(threads);
|
|
std::vector<std::thread> ts;
|
|
ts.reserve(threads);
|
|
|
|
for (int i = 0; i < threads; ++i) {
|
|
ts.emplace_back([&, i]() {
|
|
latch.arrive_and_wait();
|
|
proxies[i] = cache.acquire_proxy<person>(id, resolver);
|
|
});
|
|
}
|
|
for (auto &t : ts) t.join();
|
|
|
|
REQUIRE(proxies[0]);
|
|
const auto *p0 = proxies[0].get();
|
|
for (int i = 1; i < threads; ++i) {
|
|
REQUIRE(proxies[i]);
|
|
REQUIRE(proxies[i].get() == p0);
|
|
}
|
|
}
|
|
|
|
TEST_CASE("object_cache: attach_entity makes is_loaded/get_entity reflect presence", "[object][cache]") {
|
|
matador::object::object_cache cache;
|
|
const matador::utils::identifier id{42};
|
|
|
|
REQUIRE_FALSE(cache.is_loaded<person>(id));
|
|
REQUIRE(cache.get_entity<person>(id) == nullptr);
|
|
|
|
auto entity = std::make_shared<person>();
|
|
entity->name = "hans";
|
|
|
|
cache.attach_entity<person>(id, entity);
|
|
|
|
REQUIRE(cache.is_loaded<person>(id));
|
|
auto got = cache.get_entity<person>(id);
|
|
REQUIRE(got);
|
|
REQUIRE(got->name == "hans");
|
|
}
|
|
|
|
TEST_CASE("object_cache: erase invalidates existing proxies", "[object][cache]") {
|
|
matador::object::object_cache cache;
|
|
const matador::utils::identifier id{9};
|
|
|
|
std::atomic_int calls{0};
|
|
auto entity = std::make_shared<person>();
|
|
auto resolver = std::make_shared<dummy_resolver>(entity, calls);
|
|
|
|
auto proxy = cache.acquire_proxy<person>(id, resolver);
|
|
REQUIRE(proxy);
|
|
REQUIRE(proxy->valid());
|
|
|
|
cache.erase<person>(id);
|
|
|
|
// Proxy lebt noch (shared_ptr), aber ist invalidiert.
|
|
REQUIRE_FALSE(proxy->valid());
|
|
REQUIRE(proxy->pointer() == nullptr);
|
|
} |