#include #include #include #include #include #include #include using namespace Hyprutils::Memory; #define SP CSharedPointer #define WP CWeakPointer #define UP CUniquePointer #define ASP CAtomicSharedPointer #define AWP CAtomicWeakPointer #define NTHREADS 8 #define ITERATIONS 10000 static void testAtomicImpl() { { // Using makeShared here could lead to invalid refcounts. ASP shared = makeAtomicShared(0); std::vector threads; threads.reserve(NTHREADS); for (size_t i = 0; i < NTHREADS; i++) { threads.emplace_back([shared]() { for (size_t j = 0; j < ITERATIONS; j++) { ASP strongRef = shared; (*shared)++; strongRef.reset(); } }); } for (auto& thread : threads) { thread.join(); } // Actual count is not incremented in a thread-safe manner here, so we can't check it. // We just want to check that the concurent refcounting doesn't cause any memory corruption. shared.reset(); EXPECT_EQ(shared, false); } { ASP shared = makeAtomicShared(0); AWP weak = shared; std::vector threads; threads.reserve(NTHREADS); for (size_t i = 0; i < NTHREADS; i++) { threads.emplace_back([weak]() { for (size_t j = 0; j < ITERATIONS; j++) { if (auto s = weak.lock(); s) { (*s)++; } } }); } std::this_thread::sleep_for(std::chrono::milliseconds(1)); shared.reset(); for (auto& thread : threads) { thread.join(); } EXPECT_EQ(shared.strongRef(), 0); EXPECT_EQ(weak.valid(), false); auto shared2 = weak.lock(); EXPECT_EQ(shared, false); EXPECT_EQ(shared2.get(), nullptr); EXPECT_EQ(shared.strongRef(), 0); EXPECT_EQ(weak.valid(), false); EXPECT_EQ(weak.expired(), true); } { // This tests recursive deletion. When foo will be deleted, bar will be deleted within the foo dtor. class CFoo { public: AWP bar; }; ASP foo = makeAtomicShared(); foo->bar = foo; } { // This tests destroying the data when storing the base class of a type class ITest { public: size_t num = 0; ITest() : num(1234) {}; }; class CA : public ITest { public: size_t num2 = 0; CA() : ITest(), num2(4321) {}; }; class CB : public ITest { public: int num2 = 0; CB() : ITest(), num2(-1) {}; }; ASP genericAtomic = nullptr; SP genericNormal = nullptr; { auto derivedAtomic = makeAtomicShared(); auto derivedNormal = makeShared(); genericAtomic = derivedAtomic; genericNormal = derivedNormal; } EXPECT_EQ(!!genericAtomic, true); EXPECT_EQ(!!genericNormal, true); } } TEST(Memory, memory) { SP intPtr = makeShared(10); SP intPtr2 = makeShared(-1337); UP intUnique = makeUnique(420); EXPECT_EQ(*intPtr, 10); EXPECT_EQ(intPtr.strongRef(), 1); EXPECT_EQ(*intUnique, 420); WP weak = intPtr; WP weakUnique = intUnique; EXPECT_EQ(*intPtr, 10); EXPECT_EQ(intPtr.strongRef(), 1); EXPECT_EQ(*weak, 10); EXPECT_EQ(weak.expired(), false); EXPECT_EQ(!!weak.lock(), true); EXPECT_EQ(*weakUnique, 420); EXPECT_EQ(weakUnique.expired(), false); EXPECT_EQ(intUnique.impl_->wref(), 1); SP sharedFromUnique = weakUnique.lock(); EXPECT_EQ(sharedFromUnique, nullptr); std::vector> sps; sps.push_back(intPtr); sps.emplace_back(intPtr); sps.push_back(intPtr2); sps.emplace_back(intPtr2); std::erase_if(sps, [intPtr](const auto& e) { return e == intPtr; }); intPtr.reset(); intUnique.reset(); EXPECT_EQ(weak.impl_->ref(), 0); EXPECT_EQ(weakUnique.impl_->ref(), 0); EXPECT_EQ(weakUnique.impl_->wref(), 1); EXPECT_EQ(intPtr2.strongRef(), 3); EXPECT_EQ(weak.expired(), true); EXPECT_EQ(weakUnique.expired(), true); auto intPtr2AsUint = reinterpretPointerCast(intPtr2); EXPECT_EQ(intPtr2.strongRef(), 4); EXPECT_EQ(intPtr2AsUint.strongRef(), 4); EXPECT_EQ(*intPtr2AsUint > 0, true); EXPECT_EQ(*intPtr2AsUint, (unsigned int)(int)-1337); *intPtr2AsUint = 10; EXPECT_EQ(*intPtr2AsUint, 10); EXPECT_EQ(*intPtr2, 10); testAtomicImpl(); }