123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641 |
- #include <library/cpp/testing/gtest/gtest.h>
- #include <library/cpp/yt/memory/new.h>
- #include <library/cpp/yt/memory/ref_counted.h>
- #include <library/cpp/yt/memory/serialize.h>
- #include <util/generic/buffer.h>
- #include <util/stream/buffer.h>
- #include <util/ysaveload.h>
- namespace NYT {
- namespace {
- ////////////////////////////////////////////////////////////////////////////////
- using ::testing::IsNull;
- using ::testing::NotNull;
- using ::testing::InSequence;
- using ::testing::MockFunction;
- using ::testing::StrictMock;
- ////////////////////////////////////////////////////////////////////////////////
- // Auxiliary types and functions.
- ////////////////////////////////////////////////////////////////////////////////
- // This object tracks number of increments and decrements
- // to the reference counter (see traits specialization below).
- struct TIntricateObject
- : private TNonCopyable
- {
- int Increments = 0;
- int Decrements = 0;
- int Zeros = 0;
- void Ref()
- {
- ++Increments;
- }
- void Unref()
- {
- ++Decrements;
- if (Increments == Decrements) {
- ++Zeros;
- }
- }
- };
- using TIntricateObjectPtr = TIntrusivePtr<TIntricateObject>;
- void Ref(TIntricateObject* obj, int /*n*/ = 1)
- {
- obj->Ref();
- }
- void Unref(TIntricateObject* obj, int /*n*/ = 1)
- {
- obj->Unref();
- }
- MATCHER_P3(HasRefCounts, increments, decrements, zeros,
- "Reference counter " \
- "was incremented " + ::testing::PrintToString(increments) + " times, " +
- "was decremented " + ::testing::PrintToString(decrements) + " times, " +
- "vanished to zero " + ::testing::PrintToString(zeros) + " times")
- {
- Y_UNUSED(result_listener);
- return
- arg.Increments == increments &&
- arg.Decrements == decrements &&
- arg.Zeros == zeros;
- }
- void PrintTo(const TIntricateObject& arg, ::std::ostream* os)
- {
- *os << arg.Increments << " increments, "
- << arg.Decrements << " decrements and "
- << arg.Zeros << " times vanished";
- }
- // This is an object which creates intrusive pointers to the self
- // during its construction.
- class TObjectWithSelfPointers
- : public TRefCounted
- {
- public:
- explicit TObjectWithSelfPointers(IOutputStream* output)
- : Output_(output)
- {
- *Output_ << "Cb";
- for (int i = 0; i < 3; ++i) {
- *Output_ << '!';
- TIntrusivePtr<TObjectWithSelfPointers> ptr(this);
- }
- *Output_ << "Ca";
- }
- virtual ~TObjectWithSelfPointers()
- {
- *Output_ << 'D';
- }
- private:
- IOutputStream* const Output_;
- };
- // This is a simple object with simple reference counting.
- class TObjectWithSimpleRC
- : public TRefCounted
- {
- public:
- explicit TObjectWithSimpleRC(IOutputStream* output)
- : Output_(output)
- {
- *Output_ << 'C';
- }
- virtual ~TObjectWithSimpleRC()
- {
- *Output_ << 'D';
- }
- void DoSomething()
- {
- *Output_ << '!';
- }
- private:
- IOutputStream* const Output_;
- };
- // This is a simple object with full-fledged reference counting.
- class TObjectWithFullRC
- : public TRefCounted
- {
- public:
- explicit TObjectWithFullRC(IOutputStream* output)
- : Output_(output)
- {
- *Output_ << 'C';
- }
- virtual ~TObjectWithFullRC()
- {
- *Output_ << 'D';
- }
- void DoSomething()
- {
- *Output_ << '!';
- }
- private:
- IOutputStream* const Output_;
- };
- class TObjectWithExceptionInConstructor
- : public TRefCounted
- {
- public:
- TObjectWithExceptionInConstructor()
- {
- throw int(1);
- }
- volatile int Number = 0;
- };
- ////////////////////////////////////////////////////////////////////////////////
- TEST(TIntrusivePtrTest, Empty)
- {
- TIntricateObjectPtr emptyPointer;
- EXPECT_EQ(nullptr, emptyPointer.Get());
- }
- TEST(TIntrusivePtrTest, Basic)
- {
- TIntricateObject object;
- EXPECT_THAT(object, HasRefCounts(0, 0, 0));
- {
- TIntricateObjectPtr owningPointer(&object);
- EXPECT_THAT(object, HasRefCounts(1, 0, 0));
- EXPECT_EQ(&object, owningPointer.Get());
- }
- EXPECT_THAT(object, HasRefCounts(1, 1, 1));
- {
- TIntricateObjectPtr nonOwningPointer(&object, false);
- EXPECT_THAT(object, HasRefCounts(1, 1, 1));
- EXPECT_EQ(&object, nonOwningPointer.Get());
- }
- EXPECT_THAT(object, HasRefCounts(1, 2, 1));
- }
- TEST(TIntrusivePtrTest, ResetToNull)
- {
- TIntricateObject object;
- TIntricateObjectPtr ptr(&object);
- EXPECT_THAT(object, HasRefCounts(1, 0, 0));
- EXPECT_EQ(&object, ptr.Get());
- ptr.Reset();
- EXPECT_THAT(object, HasRefCounts(1, 1, 1));
- EXPECT_EQ(nullptr, ptr.Get());
- }
- TEST(TIntrusivePtrTest, ResetToOtherObject)
- {
- TIntricateObject firstObject;
- TIntricateObject secondObject;
- TIntricateObjectPtr ptr(&firstObject);
- EXPECT_THAT(firstObject, HasRefCounts(1, 0, 0));
- EXPECT_THAT(secondObject, HasRefCounts(0, 0, 0));
- EXPECT_EQ(&firstObject, ptr.Get());
- ptr.Reset(&secondObject);
- EXPECT_THAT(firstObject, HasRefCounts(1, 1, 1));
- EXPECT_THAT(secondObject, HasRefCounts(1, 0, 0));
- EXPECT_EQ(&secondObject, ptr.Get());
- }
- TEST(TIntrusivePtrTest, CopySemantics)
- {
- TIntricateObject object;
- TIntricateObjectPtr foo(&object);
- EXPECT_THAT(object, HasRefCounts(1, 0, 0));
- {
- TIntricateObjectPtr bar(foo);
- EXPECT_THAT(object, HasRefCounts(2, 0, 0));
- EXPECT_EQ(&object, foo.Get());
- EXPECT_EQ(&object, bar.Get());
- }
- EXPECT_THAT(object, HasRefCounts(2, 1, 0));
- {
- TIntricateObjectPtr bar;
- bar = foo;
- EXPECT_THAT(object, HasRefCounts(3, 1, 0));
- EXPECT_EQ(&object, foo.Get());
- EXPECT_EQ(&object, bar.Get());
- }
- EXPECT_THAT(object, HasRefCounts(3, 2, 0));
- }
- TEST(TIntrusivePtrTest, MoveSemantics)
- {
- TIntricateObject object;
- TIntricateObjectPtr foo(&object);
- EXPECT_THAT(object, HasRefCounts(1, 0, 0));
- {
- TIntricateObjectPtr bar(std::move(foo));
- EXPECT_THAT(object, HasRefCounts(1, 0, 0));
- EXPECT_THAT(foo.Get(), IsNull());
- EXPECT_EQ(&object, bar.Get());
- }
- EXPECT_THAT(object, HasRefCounts(1, 1, 1));
- foo.Reset(&object);
- EXPECT_THAT(object, HasRefCounts(2, 1, 1));
- {
- TIntricateObjectPtr bar;
- bar = std::move(foo);
- EXPECT_THAT(object, HasRefCounts(2, 1, 1));
- EXPECT_THAT(foo.Get(), IsNull());
- EXPECT_EQ(&object, bar.Get());
- }
- }
- TEST(TIntrusivePtrTest, Swap)
- {
- TIntricateObject object;
- TIntricateObjectPtr foo(&object);
- TIntricateObjectPtr bar;
- EXPECT_THAT(object, HasRefCounts(1, 0, 0));
- EXPECT_THAT(foo.Get(), NotNull());
- EXPECT_THAT(bar.Get(), IsNull());
- foo.Swap(bar);
- EXPECT_THAT(object, HasRefCounts(1, 0, 0));
- EXPECT_THAT(foo.Get(), IsNull());
- EXPECT_THAT(bar.Get(), NotNull());
- foo.Swap(bar);
- EXPECT_THAT(object, HasRefCounts(1, 0, 0));
- EXPECT_THAT(foo.Get(), NotNull());
- EXPECT_THAT(bar.Get(), IsNull());
- }
- TEST(TIntrusivePtrTest, UpCast)
- {
- //! This is a simple typical reference-counted object.
- class TSimpleObject
- : public TRefCounted
- { };
- //! This is a simple inherited reference-counted object.
- class TAnotherObject
- : public TSimpleObject
- { };
- auto foo = New<TSimpleObject>();
- auto bar = New<TAnotherObject>();
- auto baz = New<TAnotherObject>();
- foo = baz;
- EXPECT_TRUE(foo == baz);
- }
- TEST(TIntrusivePtrTest, DownCast)
- {
- class TBaseObject
- : public TRefCounted
- { };
- class TDerivedObject
- : public TBaseObject
- { };
- //! This is a simple inherited reference-counted object.
- class TAnotherObject
- : public TBaseObject
- { };
- TIntrusivePtr<TBaseObject> foo = New<TDerivedObject>();
- TIntrusivePtr<TBaseObject> bar = New<TAnotherObject>();
- {
- auto baz = StaticPointerCast<TDerivedObject>(foo);
- EXPECT_TRUE(foo == baz);
- }
- {
- auto baz = StaticPointerCast<TDerivedObject>(TIntrusivePtr<TBaseObject>{foo});
- EXPECT_TRUE(foo == baz);
- }
- {
- auto baz = DynamicPointerCast<TDerivedObject>(foo);
- EXPECT_TRUE(foo == baz);
- }
- {
- auto baz = DynamicPointerCast<TDerivedObject>(bar);
- EXPECT_TRUE(nullptr == baz);
- }
- {
- auto baz = ConstPointerCast<const TBaseObject>(foo);
- EXPECT_TRUE(foo.Get() == baz.Get());
- }
- {
- auto baz = ConstPointerCast<const TBaseObject>(TIntrusivePtr<TBaseObject>{foo});
- EXPECT_TRUE(foo.Get() == baz.Get());
- }
- }
- TEST(TIntrusivePtrTest, UnspecifiedBoolType)
- {
- TIntricateObject object;
- TIntricateObjectPtr foo;
- TIntricateObjectPtr bar(&object);
- EXPECT_FALSE(foo);
- EXPECT_TRUE(bar);
- }
- TEST(TIntrusivePtrTest, ObjectIsNotDestroyedPrematurely)
- {
- TStringStream output;
- New<TObjectWithSelfPointers>(&output);
- // TObject... appends symbols to the output; see definitions.
- EXPECT_STREQ("Cb!!!CaD", output.Str().c_str());
- }
- TEST(TIntrusivePtrTest, EqualityOperator)
- {
- TIntricateObject object, anotherObject;
- TIntricateObjectPtr emptyPointer;
- TIntricateObjectPtr somePointer(&object);
- TIntricateObjectPtr samePointer(&object);
- TIntricateObjectPtr anotherPointer(&anotherObject);
- EXPECT_FALSE(somePointer == emptyPointer);
- EXPECT_FALSE(samePointer == emptyPointer);
- EXPECT_TRUE(somePointer != emptyPointer);
- EXPECT_TRUE(samePointer != emptyPointer);
- EXPECT_TRUE(somePointer == samePointer);
- EXPECT_TRUE(&object == somePointer);
- EXPECT_TRUE(&object == samePointer);
- EXPECT_FALSE(somePointer == anotherPointer);
- EXPECT_TRUE(somePointer != anotherPointer);
- EXPECT_TRUE(&anotherObject == anotherPointer);
- }
- TEST(TIntrusivePtrTest, Reset)
- {
- TIntricateObject object;
- TIntricateObjectPtr pointer(&object);
- EXPECT_THAT(object, HasRefCounts(1, 0, 0));
- EXPECT_EQ(&object, pointer.Release());
- EXPECT_THAT(object, HasRefCounts(1, 0, 0));
- }
- TEST(TIntrusivePtrTest, CompareWithNullptr)
- {
- TIntricateObjectPtr pointer1;
- EXPECT_TRUE(nullptr == pointer1);
- EXPECT_FALSE(nullptr != pointer1);
- TIntricateObject object;
- TIntricateObjectPtr pointer2(&object);
- EXPECT_TRUE(pointer2 != nullptr);
- EXPECT_FALSE(pointer2 == nullptr);
- }
- template <class T>
- void TestIntrusivePtrBehavior()
- {
- using TMyPtr = TIntrusivePtr<T>;
- TStringStream output;
- {
- TMyPtr ptr(New<T>(&output));
- {
- TMyPtr anotherPtr(ptr);
- anotherPtr->DoSomething();
- }
- {
- TMyPtr anotherPtr(ptr);
- anotherPtr->DoSomething();
- }
- ptr->DoSomething();
- }
- // TObject... appends symbols to the output; see definitions.
- EXPECT_STREQ("C!!!D", output.Str().c_str());
- }
- TEST(TIntrusivePtrTest, SimpleRCBehaviour)
- {
- TestIntrusivePtrBehavior<TObjectWithSimpleRC>();
- }
- TEST(TIntrusivePtrTest, FullRCBehaviour)
- {
- TestIntrusivePtrBehavior<TObjectWithFullRC>();
- }
- TEST(TIntrusivePtrTest, ObjectAlignment)
- {
- struct TObject
- : public TRefCounted
- {
- alignas(64) ui64 Data;
- };
- struct TPODObject final
- {
- alignas(64) ui64 Data;
- };
- auto foo = New<TObject>();
- auto bar = New<TPODObject>();
- EXPECT_TRUE(reinterpret_cast<uintptr_t>(foo.Get()) % 64 == 0);
- EXPECT_TRUE(reinterpret_cast<uintptr_t>(bar.Get()) % 64 == 0);
- }
- TEST(TIntrusivePtrTest, InitStruct)
- {
- struct TObj1 final
- {
- const int A;
- const int B;
- };
- New<TObj1>(1, 2);
- struct TExplicitObj final
- {
- explicit TExplicitObj(int a = 0)
- : A(a)
- { }
- const int A;
- };
- New<TExplicitObj>();
- New<TExplicitObj>(1);
- struct TObj2 final
- {
- TObj2(i64 a = 0)
- : A(a)
- { }
- const i64 A;
- };
- New<TObj2>(123);
- struct TObj3 final
- {
- TObj3(ui64 a = 0)
- : A(a)
- { }
- const ui64 A;
- };
- New<TObj3>(123);
- struct TObj4 final
- {
- TObj4(int a, ui64 b = 0)
- : A(a)
- , B(b)
- { }
- int A;
- const ui64 B;
- };
- New<TObj4>(123);
- New<TObj4>(123, 123);
- struct TObj5 final
- {
- TExplicitObj E;
- int B;
- };
- New<TObj5>();
- struct TObj6 final
- {
- TObj2 O;
- int B;
- };
- New<TObj6>();
- New<TObj6>(1, 2);
- }
- TEST(TIntrusivePtrTest, Serialize)
- {
- TBufferStream stream;
- struct TObject
- : public TRefCounted
- {
- ui64 Data;
- TObject()
- : Data(0)
- { }
- TObject(ui64 value)
- : Data(value)
- { }
- inline void Save(IOutputStream* out) const
- {
- ::Save(out, Data);
- }
- inline void Load(IInputStream* in)
- {
- ::Load(in, Data);
- }
- };
- ::Save(&stream, TIntrusivePtr<TObject>(nullptr));
- bool hasValue = true;
- ::Load(&stream, hasValue);
- EXPECT_FALSE(hasValue);
- EXPECT_EQ(stream.Buffer().Size(), 1ull);
- auto data = New<TObject>(42ull);
- ::Save(&stream, data);
- ::Load(&stream, hasValue);
- EXPECT_TRUE(hasValue);
- data->Data = 0;
- ::Load(&stream, data->Data);
- EXPECT_EQ(data->Data, 42ull);
- ::Save(&stream, data);
- TIntrusivePtr<TObject> ptr;
- ::Load(&stream, ptr);
- EXPECT_TRUE(ptr);
- EXPECT_EQ(data->Data, ptr->Data);
- ptr = nullptr;
- ::Save(&stream, TIntrusivePtr<TObject>(nullptr));
- ::Load(&stream, ptr);
- EXPECT_FALSE(ptr);
- }
- TEST(TIntrusivePtrTest, TestObjectConstructionFail)
- {
- ASSERT_THROW(New<TObjectWithExceptionInConstructor>(), int);
- }
- ////////////////////////////////////////////////////////////////////////////////
- } // namespace
- } // namespace NYT
|