query/test/core/object/ObjectCacheTest.cpp

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);
}