#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); } } class InterfaceA { public: virtual ~InterfaceA() = default; int m_ifaceAInt = 69; int m_ifaceAShit = 1; }; class InterfaceB { public: virtual ~InterfaceB() = default; int m_ifaceBInt = 2; int m_ifaceBShit = 3; }; class CChild : public InterfaceA, public InterfaceB { public: virtual ~CChild() = default; int m_childInt = 4; }; class CChildA : public InterfaceA { public: int m_childAInt = 4; }; static void testHierarchy() { // Same test for atomic and non-atomic { SP childA = makeShared(); auto ifaceA = SP(childA); EXPECT_TRUE(ifaceA); EXPECT_EQ(ifaceA->m_ifaceAInt, 69); auto ifaceB = dynamicPointerCast(SP{}); EXPECT_TRUE(!ifaceB); } { SP child = makeShared(); SP ifaceA = dynamicPointerCast(child); SP ifaceB = dynamicPointerCast(child); EXPECT_TRUE(ifaceA); EXPECT_TRUE(ifaceB); EXPECT_EQ(ifaceA->m_ifaceAInt, 69); EXPECT_EQ(ifaceB->m_ifaceBInt, 2); WP ifaceAWeak = ifaceA; child.reset(); EXPECT_TRUE(ifaceAWeak); EXPECT_TRUE(ifaceA); EXPECT_EQ(ifaceAWeak->m_ifaceAInt, 69); EXPECT_EQ(ifaceA->m_ifaceAInt, 69); ifaceA.reset(); EXPECT_TRUE(ifaceAWeak); EXPECT_EQ(ifaceAWeak->m_ifaceAInt, 69); EXPECT_TRUE(ifaceB); EXPECT_EQ(ifaceB->m_ifaceBInt, 2); ifaceB.reset(); EXPECT_TRUE(!ifaceAWeak); } // { ASP childA = makeAtomicShared(); auto ifaceA = ASP(childA); EXPECT_TRUE(ifaceA); EXPECT_EQ(ifaceA->m_ifaceAInt, 69); auto ifaceB = dynamicPointerCast(ASP{}); EXPECT_TRUE(!ifaceB); } { ASP child = makeAtomicShared(); ASP ifaceA = dynamicPointerCast(child); ASP ifaceB = dynamicPointerCast(child); EXPECT_TRUE(ifaceA); EXPECT_TRUE(ifaceB); EXPECT_EQ(ifaceA->m_ifaceAInt, 69); EXPECT_EQ(ifaceB->m_ifaceBInt, 2); AWP ifaceAWeak = ifaceA; AWP ifaceBWeak = dynamicPointerCast(ifaceA); child.reset(); EXPECT_TRUE(ifaceAWeak); EXPECT_TRUE(ifaceBWeak); EXPECT_TRUE(ifaceA); EXPECT_EQ(ifaceAWeak->m_ifaceAInt, 69); EXPECT_EQ(ifaceA->m_ifaceAInt, 69); EXPECT_EQ(ifaceBWeak->m_ifaceBInt, 2); ifaceA.reset(); EXPECT_TRUE(ifaceAWeak); EXPECT_EQ(ifaceAWeak->m_ifaceAInt, 69); EXPECT_TRUE(ifaceB); EXPECT_EQ(ifaceB->m_ifaceBInt, 2); EXPECT_EQ(ifaceBWeak->m_ifaceBInt, 2); ifaceB.reset(); EXPECT_TRUE(!ifaceAWeak); EXPECT_TRUE(!ifaceBWeak); } // test for leaks for (size_t i = 0; i < 10000; ++i) { auto child = makeAtomicShared(); auto child2 = makeShared(); } } 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(); testHierarchy(); }