#include #include "matador/object/object_cache.hpp" #include "matador/object/object_resolver.hpp" #include "matador/utils/identifier.hpp" #include "../test/models/person.hpp" #include #include #include #include #include #include using namespace matador::test; namespace { class dummy_resolver final : public matador::object::object_resolver { public: explicit dummy_resolver(std::shared_ptr shared, std::atomic_int &calls) : shared_(std::move(shared)) , calls_(calls) {} std::shared_ptr resolve(const matador::utils::identifier & /*id*/) override { ++calls_; return shared_; } private: std::shared_ptr shared_; std::atomic_int &calls_; }; class start_latch { public: explicit start_latch(int participants) : participants_(participants) {} void arrive_and_wait() { std::unique_lock 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(); auto resolver = std::make_shared(entity, calls); constexpr int threads = 16; start_latch latch(threads); std::vector>> proxies(threads); std::vector ts; ts.reserve(threads); for (int i = 0; i < threads; ++i) { ts.emplace_back([&, i]() { latch.arrive_and_wait(); proxies[i] = cache.acquire_proxy(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(id)); REQUIRE(cache.get_entity(id) == nullptr); auto entity = std::make_shared(); entity->name = "hans"; cache.attach_entity(id, entity); REQUIRE(cache.is_loaded(id)); auto got = cache.get_entity(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(); auto resolver = std::make_shared(entity, calls); auto proxy = cache.acquire_proxy(id, resolver); REQUIRE(proxy); REQUIRE(proxy->valid()); cache.erase(id); // Proxy lebt noch (shared_ptr), aber ist invalidiert. REQUIRE_FALSE(proxy->valid()); REQUIRE(proxy->pointer() == nullptr); }