summaryrefslogtreecommitdiffstats
path: root/util/generic/ptr_ut.cpp
diff options
context:
space:
mode:
authorDevtools Arcadia <[email protected]>2022-02-07 18:08:42 +0300
committerDevtools Arcadia <[email protected]>2022-02-07 18:08:42 +0300
commit1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch)
treee26c9fed0de5d9873cce7e00bc214573dc2195b7 /util/generic/ptr_ut.cpp
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'util/generic/ptr_ut.cpp')
-rw-r--r--util/generic/ptr_ut.cpp835
1 files changed, 835 insertions, 0 deletions
diff --git a/util/generic/ptr_ut.cpp b/util/generic/ptr_ut.cpp
new file mode 100644
index 00000000000..c2dcff23f6b
--- /dev/null
+++ b/util/generic/ptr_ut.cpp
@@ -0,0 +1,835 @@
+#include "ptr.h"
+#include "vector.h"
+#include "noncopyable.h"
+
+#include <library/cpp/testing/unittest/registar.h>
+
+#include <util/generic/hash_set.h>
+#include <util/generic/is_in.h>
+#include <util/stream/output.h>
+#include <util/system/thread.h>
+
+class TPointerTest: public TTestBase {
+ UNIT_TEST_SUITE(TPointerTest);
+ UNIT_TEST(TestTypedefs);
+ UNIT_TEST(TestSimpleIntrPtr);
+ UNIT_TEST(TestHolderPtr);
+ UNIT_TEST(TestHolderPtrMoveConstructor);
+ UNIT_TEST(TestHolderPtrMoveConstructorInheritance);
+ UNIT_TEST(TestHolderPtrMoveAssignment);
+ UNIT_TEST(TestHolderPtrMoveAssignmentInheritance);
+ UNIT_TEST(TestMakeHolder);
+ UNIT_TEST(TestTrulePtr);
+ UNIT_TEST(TestAutoToHolder);
+ UNIT_TEST(TestCopyPtr);
+ UNIT_TEST(TestIntrPtr);
+ UNIT_TEST(TestIntrusiveConvertion);
+ UNIT_TEST(TestIntrusiveConstConvertion);
+ UNIT_TEST(TestIntrusiveConstConstruction);
+ UNIT_TEST(TestMakeIntrusive);
+ UNIT_TEST(TestCopyOnWritePtr1);
+ UNIT_TEST(TestCopyOnWritePtr2);
+ UNIT_TEST(TestOperatorBool);
+ UNIT_TEST(TestMakeShared);
+ UNIT_TEST(TestComparison);
+ UNIT_TEST(TestSimpleIntrusivePtrCtorTsan);
+ UNIT_TEST(TestRefCountedPtrsInHashSet)
+ UNIT_TEST_SUITE_END();
+
+private:
+ void TestSimpleIntrusivePtrCtorTsan() {
+ struct S: public TAtomicRefCount<S> {
+ };
+
+ struct TLocalThread: public ISimpleThread {
+ void* ThreadProc() override {
+ TSimpleIntrusivePtr<S> ptr;
+ return nullptr;
+ }
+ };
+
+ // Create TSimpleIntrusivePtr in different threads
+ // Its constructor should be thread-safe
+
+ TLocalThread t1, t2;
+
+ t1.Start();
+ t2.Start();
+ t1.Join();
+ t2.Join();
+ }
+
+ inline void TestTypedefs() {
+ TAtomicSharedPtr<int>(new int(1));
+ TSimpleSharedPtr<int>(new int(1));
+ }
+
+ void TestSimpleIntrPtr();
+ void TestHolderPtr();
+ void TestHolderPtrMoveConstructor();
+ void TestHolderPtrMoveConstructorInheritance();
+ void TestHolderPtrMoveAssignment();
+ void TestHolderPtrMoveAssignmentInheritance();
+ void TestMakeHolder();
+ void TestTrulePtr();
+ void TestAutoToHolder();
+ void TestCopyPtr();
+ void TestIntrPtr();
+ void TestIntrusiveConvertion();
+ void TestIntrusiveConstConvertion();
+ void TestIntrusiveConstConstruction();
+ void TestMakeIntrusive();
+ void TestCopyOnWritePtr1();
+ void TestCopyOnWritePtr2();
+ void TestOperatorBool();
+ void TestMakeShared();
+ void TestComparison();
+ template <class T, class TRefCountedPtr>
+ void TestRefCountedPtrsInHashSetImpl();
+ void TestRefCountedPtrsInHashSet();
+};
+
+UNIT_TEST_SUITE_REGISTRATION(TPointerTest);
+
+static int cnt = 0;
+
+class A: public TAtomicRefCount<A> {
+public:
+ inline A() {
+ ++cnt;
+ }
+
+ inline A(const A&)
+ : TAtomicRefCount<A>(*this)
+ {
+ ++cnt;
+ }
+
+ inline ~A() {
+ --cnt;
+ }
+};
+
+static A* MakeA() {
+ return new A();
+}
+
+/*
+ * test compileability
+ */
+class B;
+static TSimpleIntrusivePtr<B> GetB() {
+ throw 1;
+}
+
+void Func() {
+ TSimpleIntrusivePtr<B> b = GetB();
+}
+
+void TPointerTest::TestSimpleIntrPtr() {
+ {
+ TSimpleIntrusivePtr<A> a1(MakeA());
+ TSimpleIntrusivePtr<A> a2(MakeA());
+ TSimpleIntrusivePtr<A> a3 = a2;
+
+ a1 = a2;
+ a2 = a3;
+ }
+
+ UNIT_ASSERT_VALUES_EQUAL(cnt, 0);
+}
+
+void TPointerTest::TestHolderPtr() {
+ {
+ THolder<A> a1(MakeA());
+ THolder<A> a2(a1.Release());
+ }
+
+ UNIT_ASSERT_VALUES_EQUAL(cnt, 0);
+}
+
+THolder<int> CreateInt(int value) {
+ THolder<int> res(new int);
+ *res = value;
+ return res;
+}
+
+void TPointerTest::TestHolderPtrMoveConstructor() {
+ THolder<int> h = CreateInt(42);
+ UNIT_ASSERT_VALUES_EQUAL(*h, 42);
+}
+
+void TPointerTest::TestHolderPtrMoveAssignment() {
+ THolder<int> h(new int);
+ h = CreateInt(42);
+ UNIT_ASSERT_VALUES_EQUAL(*h, 42);
+}
+
+struct TBase {
+ virtual ~TBase() = default;
+};
+
+struct TDerived: public TBase {
+};
+
+void TPointerTest::TestHolderPtrMoveConstructorInheritance() {
+ // compileability test
+ THolder<TBase> basePtr(THolder<TDerived>(new TDerived));
+}
+
+void TPointerTest::TestHolderPtrMoveAssignmentInheritance() {
+ // compileability test
+ THolder<TBase> basePtr;
+ basePtr = THolder<TDerived>(new TDerived);
+}
+
+void TPointerTest::TestMakeHolder() {
+ {
+ auto ptr = MakeHolder<int>(5);
+ UNIT_ASSERT_VALUES_EQUAL(*ptr, 5);
+ }
+ {
+ struct TRec {
+ int X, Y;
+ TRec()
+ : X(1)
+ , Y(2)
+ {
+ }
+ };
+ THolder<TRec> ptr = MakeHolder<TRec>();
+ UNIT_ASSERT_VALUES_EQUAL(ptr->X, 1);
+ UNIT_ASSERT_VALUES_EQUAL(ptr->Y, 2);
+ }
+ {
+ struct TRec {
+ int X, Y;
+ TRec(int x, int y)
+ : X(x)
+ , Y(y)
+ {
+ }
+ };
+ auto ptr = MakeHolder<TRec>(1, 2);
+ UNIT_ASSERT_VALUES_EQUAL(ptr->X, 1);
+ UNIT_ASSERT_VALUES_EQUAL(ptr->Y, 2);
+ }
+ {
+ class TRec {
+ private:
+ int X_, Y_;
+
+ public:
+ TRec(int x, int y)
+ : X_(x)
+ , Y_(y)
+ {
+ }
+
+ int GetX() const {
+ return X_;
+ }
+ int GetY() const {
+ return Y_;
+ }
+ };
+ auto ptr = MakeHolder<TRec>(1, 2);
+ UNIT_ASSERT_VALUES_EQUAL(ptr->GetX(), 1);
+ UNIT_ASSERT_VALUES_EQUAL(ptr->GetY(), 2);
+ }
+}
+
+void TPointerTest::TestTrulePtr() {
+ {
+ TAutoPtr<A> a1(MakeA());
+ TAutoPtr<A> a2(a1);
+ a1 = a2;
+ }
+
+ UNIT_ASSERT_VALUES_EQUAL(cnt, 0);
+}
+
+void TPointerTest::TestAutoToHolder() {
+ {
+ TAutoPtr<A> a1(MakeA());
+ THolder<A> a2(a1);
+
+ UNIT_ASSERT_EQUAL(a1.Get(), nullptr);
+ UNIT_ASSERT_VALUES_EQUAL(cnt, 1);
+ }
+
+ UNIT_ASSERT_VALUES_EQUAL(cnt, 0);
+
+ {
+ TAutoPtr<A> x(new A());
+ THolder<const A> y = x;
+ }
+
+ UNIT_ASSERT_VALUES_EQUAL(cnt, 0);
+
+ {
+ class B1: public A {
+ };
+
+ TAutoPtr<B1> x(new B1());
+ THolder<A> y = x;
+ }
+
+ UNIT_ASSERT_VALUES_EQUAL(cnt, 0);
+}
+
+void TPointerTest::TestCopyPtr() {
+ TCopyPtr<A> a1(MakeA());
+ {
+ TCopyPtr<A> a2(MakeA());
+ TCopyPtr<A> a3 = a2;
+ UNIT_ASSERT_VALUES_EQUAL(cnt, 3);
+
+ a1 = a2;
+ a2 = a3;
+ }
+ UNIT_ASSERT_VALUES_EQUAL(cnt, 1);
+ a1.Destroy();
+
+ UNIT_ASSERT_VALUES_EQUAL(cnt, 0);
+}
+
+class TOp: public TSimpleRefCount<TOp>, public TNonCopyable {
+public:
+ static int Cnt;
+
+public:
+ TOp() {
+ ++Cnt;
+ }
+ virtual ~TOp() {
+ --Cnt;
+ }
+};
+
+int TOp::Cnt = 0;
+
+class TOp2: public TOp {
+public:
+ TIntrusivePtr<TOp> Op;
+
+public:
+ TOp2(const TIntrusivePtr<TOp>& op)
+ : Op(op)
+ {
+ ++Cnt;
+ }
+ ~TOp2() override {
+ --Cnt;
+ }
+};
+
+class TOp3 {
+public:
+ TIntrusivePtr<TOp2> Op2;
+};
+
+void Attach(TOp3* op3, TIntrusivePtr<TOp>* op) {
+ TIntrusivePtr<TOp2> op2 = new TOp2(*op);
+ op3->Op2 = op2.Get();
+ *op = op2.Get();
+}
+
+void TPointerTest::TestIntrPtr() {
+ {
+ TIntrusivePtr<TOp> p, p2;
+ TOp3 op3;
+ {
+ TVector<TIntrusivePtr<TOp>> f1;
+ {
+ TVector<TIntrusivePtr<TOp>> f2;
+ f2.push_back(new TOp);
+ p = new TOp;
+ f2.push_back(p);
+ Attach(&op3, &f2[1]);
+ f1 = f2;
+ UNIT_ASSERT_VALUES_EQUAL(f1[0]->RefCount(), 2);
+ UNIT_ASSERT_VALUES_EQUAL(f1[1]->RefCount(), 3);
+ UNIT_ASSERT_EQUAL(f1[1].Get(), op3.Op2.Get());
+ UNIT_ASSERT_VALUES_EQUAL(op3.Op2->RefCount(), 3);
+ UNIT_ASSERT_VALUES_EQUAL(op3.Op2->Op->RefCount(), 2);
+ UNIT_ASSERT_VALUES_EQUAL(TOp::Cnt, 4);
+ }
+ p2 = p;
+ }
+ UNIT_ASSERT_VALUES_EQUAL(op3.Op2->RefCount(), 1);
+ UNIT_ASSERT_VALUES_EQUAL(op3.Op2->Op->RefCount(), 3);
+ UNIT_ASSERT_VALUES_EQUAL(TOp::Cnt, 3);
+ }
+ UNIT_ASSERT_VALUES_EQUAL(TOp::Cnt, 0);
+}
+
+namespace NTestIntrusiveConvertion {
+ struct TA: public TSimpleRefCount<TA> {
+ };
+ struct TAA: public TA {
+ };
+ struct TB: public TSimpleRefCount<TB> {
+ };
+
+ void Func(TIntrusivePtr<TA>) {
+ }
+
+ void Func(TIntrusivePtr<TB>) {
+ }
+
+ void Func(TIntrusiveConstPtr<TA>) {
+ }
+
+ void Func(TIntrusiveConstPtr<TB>) {
+ }
+}
+
+void TPointerTest::TestIntrusiveConvertion() {
+ using namespace NTestIntrusiveConvertion;
+
+ TIntrusivePtr<TAA> aa = new TAA;
+
+ UNIT_ASSERT_VALUES_EQUAL(aa->RefCount(), 1);
+ TIntrusivePtr<TA> a = aa;
+ UNIT_ASSERT_VALUES_EQUAL(aa->RefCount(), 2);
+ UNIT_ASSERT_VALUES_EQUAL(a->RefCount(), 2);
+ aa.Reset();
+ UNIT_ASSERT_VALUES_EQUAL(a->RefCount(), 1);
+
+ // test that Func(TIntrusivePtr<TB>) doesn't participate in overload resolution
+ Func(aa);
+}
+
+void TPointerTest::TestIntrusiveConstConvertion() {
+ using namespace NTestIntrusiveConvertion;
+
+ TIntrusiveConstPtr<TAA> aa = new TAA;
+
+ UNIT_ASSERT_VALUES_EQUAL(aa->RefCount(), 1);
+ TIntrusiveConstPtr<TA> a = aa;
+ UNIT_ASSERT_VALUES_EQUAL(aa->RefCount(), 2);
+ UNIT_ASSERT_VALUES_EQUAL(a->RefCount(), 2);
+ aa.Reset();
+ UNIT_ASSERT_VALUES_EQUAL(a->RefCount(), 1);
+
+ // test that Func(TIntrusiveConstPtr<TB>) doesn't participate in overload resolution
+ Func(aa);
+}
+
+void TPointerTest::TestMakeIntrusive() {
+ {
+ UNIT_ASSERT_VALUES_EQUAL(0, TOp::Cnt);
+ auto p = MakeIntrusive<TOp>();
+ UNIT_ASSERT_VALUES_EQUAL(1, p->RefCount());
+ UNIT_ASSERT_VALUES_EQUAL(1, TOp::Cnt);
+ }
+ UNIT_ASSERT_VALUES_EQUAL(TOp::Cnt, 0);
+}
+
+void TPointerTest::TestCopyOnWritePtr1() {
+ using TPtr = TCowPtr<TSimpleSharedPtr<int>>;
+ TPtr p1;
+ UNIT_ASSERT(!p1.Shared());
+
+ p1.Reset(new int(123));
+ UNIT_ASSERT(!p1.Shared());
+
+ {
+ TPtr pTmp = p1;
+
+ UNIT_ASSERT(p1.Shared());
+ UNIT_ASSERT(pTmp.Shared());
+ UNIT_ASSERT_EQUAL(p1.Get(), pTmp.Get());
+ }
+
+ UNIT_ASSERT(!p1.Shared());
+
+ TPtr p2 = p1;
+ TPtr p3;
+ p3 = p2;
+
+ UNIT_ASSERT(p2.Shared());
+ UNIT_ASSERT(p3.Shared());
+ UNIT_ASSERT_EQUAL(p1.Get(), p2.Get());
+ UNIT_ASSERT_EQUAL(p1.Get(), p3.Get());
+
+ *(p1.Mutable()) = 456;
+
+ UNIT_ASSERT(!p1.Shared());
+ UNIT_ASSERT(p2.Shared());
+ UNIT_ASSERT(p3.Shared());
+ UNIT_ASSERT_EQUAL(*p1, 456);
+ UNIT_ASSERT_EQUAL(*p2, 123);
+ UNIT_ASSERT_EQUAL(*p3, 123);
+ UNIT_ASSERT_UNEQUAL(p1.Get(), p2.Get());
+ UNIT_ASSERT_EQUAL(p2.Get(), p3.Get());
+
+ p2.Mutable();
+
+ UNIT_ASSERT(!p2.Shared());
+ UNIT_ASSERT(!p3.Shared());
+ UNIT_ASSERT_EQUAL(*p2, 123);
+ UNIT_ASSERT_EQUAL(*p3, 123);
+ UNIT_ASSERT_UNEQUAL(p2.Get(), p3.Get());
+}
+
+struct X: public TSimpleRefCount<X> {
+ inline X(int v = 0)
+ : V(v)
+ {
+ }
+
+ int V;
+};
+
+void TPointerTest::TestCopyOnWritePtr2() {
+ using TPtr = TCowPtr<TIntrusivePtr<X>>;
+ TPtr p1;
+ UNIT_ASSERT(!p1.Shared());
+
+ p1.Reset(new X(123));
+ UNIT_ASSERT(!p1.Shared());
+
+ {
+ TPtr pTmp = p1;
+
+ UNIT_ASSERT(p1.Shared());
+ UNIT_ASSERT(pTmp.Shared());
+ UNIT_ASSERT_EQUAL(p1.Get(), pTmp.Get());
+ }
+
+ UNIT_ASSERT(!p1.Shared());
+
+ TPtr p2 = p1;
+ TPtr p3;
+ p3 = p2;
+
+ UNIT_ASSERT(p2.Shared());
+ UNIT_ASSERT(p3.Shared());
+ UNIT_ASSERT_EQUAL(p1.Get(), p2.Get());
+ UNIT_ASSERT_EQUAL(p1.Get(), p3.Get());
+
+ p1.Mutable()->V = 456;
+
+ UNIT_ASSERT(!p1.Shared());
+ UNIT_ASSERT(p2.Shared());
+ UNIT_ASSERT(p3.Shared());
+ UNIT_ASSERT_EQUAL(p1->V, 456);
+ UNIT_ASSERT_EQUAL(p2->V, 123);
+ UNIT_ASSERT_EQUAL(p3->V, 123);
+ UNIT_ASSERT_UNEQUAL(p1.Get(), p2.Get());
+ UNIT_ASSERT_EQUAL(p2.Get(), p3.Get());
+
+ p2.Mutable();
+
+ UNIT_ASSERT(!p2.Shared());
+ UNIT_ASSERT(!p3.Shared());
+ UNIT_ASSERT_EQUAL(p2->V, 123);
+ UNIT_ASSERT_EQUAL(p3->V, 123);
+ UNIT_ASSERT_UNEQUAL(p2.Get(), p3.Get());
+}
+
+namespace {
+ template <class TFrom, class TTo>
+ struct TImplicitlyCastable {
+ struct RTYes {
+ char t[2];
+ };
+
+ using RTNo = char;
+
+ static RTYes Func(TTo);
+ static RTNo Func(...);
+ static TFrom Get();
+
+ /*
+ * Result == (TFrom could be converted to TTo implicitly)
+ */
+ enum {
+ Result = (sizeof(Func(Get())) != sizeof(RTNo))
+ };
+ };
+
+ struct TImplicitlyCastableToBool {
+ inline operator bool() const {
+ return true;
+ }
+ };
+
+}
+
+void TPointerTest::TestOperatorBool() {
+ using TVec = TVector<ui32>;
+
+ // to be sure TImplicitlyCastable works as expected
+ UNIT_ASSERT((TImplicitlyCastable<int, bool>::Result));
+ UNIT_ASSERT((TImplicitlyCastable<double, int>::Result));
+ UNIT_ASSERT((TImplicitlyCastable<int*, void*>::Result));
+ UNIT_ASSERT(!(TImplicitlyCastable<void*, int*>::Result));
+ UNIT_ASSERT((TImplicitlyCastable<TImplicitlyCastableToBool, bool>::Result));
+ UNIT_ASSERT((TImplicitlyCastable<TImplicitlyCastableToBool, int>::Result));
+ UNIT_ASSERT((TImplicitlyCastable<TImplicitlyCastableToBool, ui64>::Result));
+ UNIT_ASSERT(!(TImplicitlyCastable<TImplicitlyCastableToBool, void*>::Result));
+
+ // pointers
+ UNIT_ASSERT(!(TImplicitlyCastable<TSimpleSharedPtr<TVec>, int>::Result));
+ UNIT_ASSERT(!(TImplicitlyCastable<TAutoPtr<ui64>, ui64>::Result));
+ UNIT_ASSERT(!(TImplicitlyCastable<THolder<TVec>, bool>::Result)); // even this
+
+ {
+ // mostly a compilability test
+
+ THolder<TVec> a;
+ UNIT_ASSERT(!a);
+ UNIT_ASSERT(!bool(a));
+ if (a) {
+ UNIT_ASSERT(false);
+ }
+ if (!a) {
+ UNIT_ASSERT(true);
+ }
+
+ a.Reset(new TVec);
+ UNIT_ASSERT(a);
+ UNIT_ASSERT(bool(a));
+ if (a) {
+ UNIT_ASSERT(true);
+ }
+ if (!a) {
+ UNIT_ASSERT(false);
+ }
+
+ THolder<TVec> b(new TVec);
+ UNIT_ASSERT(a.Get() != b.Get());
+ UNIT_ASSERT(a != b);
+ if (a == b) {
+ UNIT_ASSERT(false);
+ }
+ if (a != b) {
+ UNIT_ASSERT(true);
+ }
+ if (!(a && b)) {
+ UNIT_ASSERT(false);
+ }
+ if (a && b) {
+ UNIT_ASSERT(true);
+ }
+
+ // int i = a; // does not compile
+ // bool c = (a < b); // does not compile
+ }
+}
+
+void TPointerTest::TestMakeShared() {
+ {
+ TSimpleSharedPtr<int> ptr = MakeSimpleShared<int>(5);
+ UNIT_ASSERT_VALUES_EQUAL(*ptr, 5);
+ }
+ {
+ struct TRec {
+ int X, Y;
+ TRec()
+ : X(1)
+ , Y(2)
+ {
+ }
+ };
+ auto ptr = MakeAtomicShared<TRec>();
+ UNIT_ASSERT_VALUES_EQUAL(ptr->X, 1);
+ UNIT_ASSERT_VALUES_EQUAL(ptr->Y, 2);
+ }
+ {
+ struct TRec {
+ int X, Y;
+ };
+ TAtomicSharedPtr<TRec> ptr = MakeAtomicShared<TRec>(1, 2);
+ UNIT_ASSERT_VALUES_EQUAL(ptr->X, 1);
+ UNIT_ASSERT_VALUES_EQUAL(ptr->Y, 2);
+ }
+ {
+ class TRec {
+ private:
+ int X_, Y_;
+
+ public:
+ TRec(int x, int y)
+ : X_(x)
+ , Y_(y)
+ {
+ }
+
+ int GetX() const {
+ return X_;
+ }
+ int GetY() const {
+ return Y_;
+ }
+ };
+ TSimpleSharedPtr<TRec> ptr = MakeSimpleShared<TRec>(1, 2);
+ UNIT_ASSERT_VALUES_EQUAL(ptr->GetX(), 1);
+ UNIT_ASSERT_VALUES_EQUAL(ptr->GetY(), 2);
+ }
+ {
+ enum EObjectState {
+ OS_NOT_CREATED,
+ OS_CREATED,
+ OS_DESTROYED,
+ };
+
+ struct TObject {
+ EObjectState& State;
+
+ TObject(EObjectState& state)
+ : State(state)
+ {
+ State = OS_CREATED;
+ }
+
+ ~TObject() {
+ State = OS_DESTROYED;
+ }
+ };
+
+ auto throwsException = []() {
+ throw yexception();
+ return 5;
+ };
+
+ auto testFunction = [](TSimpleSharedPtr<TObject>, int) {
+ };
+
+ EObjectState state = OS_NOT_CREATED;
+ try {
+ testFunction(MakeSimpleShared<TObject>(state), throwsException());
+ } catch (yexception&) {
+ }
+
+ UNIT_ASSERT(state == OS_NOT_CREATED || state == OS_DESTROYED);
+ }
+}
+
+template <class TPtr>
+void TestPtrComparison(const TPtr& ptr) {
+ UNIT_ASSERT(ptr == ptr);
+ UNIT_ASSERT(!(ptr != ptr));
+ UNIT_ASSERT(ptr == ptr.Get());
+ UNIT_ASSERT(!(ptr != ptr.Get()));
+}
+
+void TPointerTest::TestComparison() {
+ THolder<A> ptr1(new A);
+ TAutoPtr<A> ptr2;
+ TSimpleSharedPtr<int> ptr3(new int(6));
+ TIntrusivePtr<A> ptr4;
+ TIntrusiveConstPtr<A> ptr5 = ptr4;
+
+ UNIT_ASSERT(ptr1 != nullptr);
+ UNIT_ASSERT(ptr2 == nullptr);
+ UNIT_ASSERT(ptr3 != nullptr);
+ UNIT_ASSERT(ptr4 == nullptr);
+ UNIT_ASSERT(ptr5 == nullptr);
+
+ TestPtrComparison(ptr1);
+ TestPtrComparison(ptr2);
+ TestPtrComparison(ptr3);
+ TestPtrComparison(ptr4);
+ TestPtrComparison(ptr5);
+}
+
+template <class T, class TRefCountedPtr>
+void TPointerTest::TestRefCountedPtrsInHashSetImpl() {
+ THashSet<TRefCountedPtr> hashSet;
+ TRefCountedPtr p1(new T());
+ UNIT_ASSERT(!IsIn(hashSet, p1));
+ UNIT_ASSERT(hashSet.insert(p1).second);
+ UNIT_ASSERT(IsIn(hashSet, p1));
+ UNIT_ASSERT_VALUES_EQUAL(hashSet.size(), 1);
+ UNIT_ASSERT(!hashSet.insert(p1).second);
+
+ TRefCountedPtr p2(new T());
+ UNIT_ASSERT(!IsIn(hashSet, p2));
+ UNIT_ASSERT(hashSet.insert(p2).second);
+ UNIT_ASSERT(IsIn(hashSet, p2));
+ UNIT_ASSERT_VALUES_EQUAL(hashSet.size(), 2);
+}
+
+struct TCustomIntrusivePtrOps: TDefaultIntrusivePtrOps<A> {
+};
+
+struct TCustomDeleter: TDelete {
+};
+
+struct TCustomCounter: TSimpleCounter {
+ using TSimpleCounterTemplate::TSimpleCounterTemplate;
+};
+
+void TPointerTest::TestRefCountedPtrsInHashSet() {
+ // test common case
+ TestRefCountedPtrsInHashSetImpl<TString, TSimpleSharedPtr<TString>>();
+ TestRefCountedPtrsInHashSetImpl<TString, TAtomicSharedPtr<TString>>();
+ TestRefCountedPtrsInHashSetImpl<A, TIntrusivePtr<A>>();
+ TestRefCountedPtrsInHashSetImpl<A, TIntrusiveConstPtr<A>>();
+
+ // test with custom ops
+ TestRefCountedPtrsInHashSetImpl<TString, TSharedPtr<TString, TCustomCounter, TCustomDeleter>>();
+ TestRefCountedPtrsInHashSetImpl<A, TIntrusivePtr<A, TCustomIntrusivePtrOps>>();
+ TestRefCountedPtrsInHashSetImpl<A, TIntrusiveConstPtr<A, TCustomIntrusivePtrOps>>();
+}
+
+class TRefCountedWithStatistics: public TNonCopyable {
+public:
+ struct TExternalCounter {
+ TAtomic Counter{0};
+ TAtomic Increments{0};
+ };
+
+ TRefCountedWithStatistics(TExternalCounter& cnt)
+ : ExternalCounter_(cnt)
+ {
+ ExternalCounter_ = {}; // reset counters
+ }
+
+ void Ref() noexcept {
+ AtomicIncrement(ExternalCounter_.Counter);
+ AtomicIncrement(ExternalCounter_.Increments);
+ }
+
+ void UnRef() noexcept {
+ if (AtomicDecrement(ExternalCounter_.Counter) == 0) {
+ TDelete::Destroy(this);
+ }
+ }
+
+ void DecRef() noexcept {
+ Y_VERIFY(AtomicDecrement(ExternalCounter_.Counter) != 0);
+ }
+
+private:
+ TExternalCounter& ExternalCounter_;
+};
+
+void TPointerTest::TestIntrusiveConstConstruction() {
+ {
+ TRefCountedWithStatistics::TExternalCounter cnt;
+ UNIT_ASSERT_VALUES_EQUAL(AtomicGet(cnt.Counter), 0);
+ UNIT_ASSERT_VALUES_EQUAL(AtomicGet(cnt.Increments), 0);
+ TIntrusivePtr<TRefCountedWithStatistics> i{MakeIntrusive<TRefCountedWithStatistics>(cnt)};
+ UNIT_ASSERT_VALUES_EQUAL(AtomicGet(cnt.Counter), 1);
+ UNIT_ASSERT_VALUES_EQUAL(AtomicGet(cnt.Increments), 1);
+ i.Reset();
+ UNIT_ASSERT_VALUES_EQUAL(AtomicGet(cnt.Counter), 0);
+ UNIT_ASSERT_VALUES_EQUAL(AtomicGet(cnt.Increments), 1);
+ }
+ {
+ TRefCountedWithStatistics::TExternalCounter cnt;
+ UNIT_ASSERT_VALUES_EQUAL(AtomicGet(cnt.Counter), 0);
+ UNIT_ASSERT_VALUES_EQUAL(AtomicGet(cnt.Increments), 0);
+ TIntrusiveConstPtr<TRefCountedWithStatistics> c{MakeIntrusive<TRefCountedWithStatistics>(cnt)};
+ UNIT_ASSERT_VALUES_EQUAL(AtomicGet(cnt.Counter), 1);
+ UNIT_ASSERT_VALUES_EQUAL(AtomicGet(cnt.Increments), 1);
+ c.Reset();
+ UNIT_ASSERT_VALUES_EQUAL(AtomicGet(cnt.Counter), 0);
+ UNIT_ASSERT_VALUES_EQUAL(AtomicGet(cnt.Increments), 1);
+ }
+}