1 /* Copyright 2021 The TensorFlow Authors. All Rights Reserved.
2
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6
7 http://www.apache.org/licenses/LICENSE-2.0
8
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15
16 #include "tensorflow/core/platform/intrusive_ptr.h"
17
18 #include "tensorflow/core/platform/refcount.h"
19 #include "tensorflow/core/platform/test.h"
20
21 namespace tensorflow {
22 namespace core {
23 namespace {
24
TEST(IntrusivePtr,ConstructorAddRefFalse)25 TEST(IntrusivePtr, ConstructorAddRefFalse) {
26 auto ptr = IntrusivePtr<RefCounted>(new RefCounted(), /*add_ref=*/false);
27 // This is needed so that the compiler does not optimize away dead code.
28 ASSERT_TRUE(ptr->RefCountIsOne());
29 // Test that there is no leak.
30 }
31
TEST(IntrusivePtr,ConstructorAddRefTrue)32 TEST(IntrusivePtr, ConstructorAddRefTrue) {
33 auto raw = new RefCounted();
34 auto ptr = IntrusivePtr<RefCounted>(raw, /*add_ref=*/true);
35 ASSERT_FALSE(raw->RefCountIsOne());
36 raw->Unref();
37 ASSERT_TRUE(raw->RefCountIsOne());
38 }
39
TEST(IntrusivePtr,CopyConstructor)40 TEST(IntrusivePtr, CopyConstructor) {
41 auto ptr1 = IntrusivePtr<RefCounted>(new RefCounted(), /*add_ref=*/false);
42 auto ptr2 = IntrusivePtr<RefCounted>(ptr1);
43 ASSERT_FALSE(ptr2->RefCountIsOne());
44 }
45
TEST(IntrusivePtr,CopyAssignment)46 TEST(IntrusivePtr, CopyAssignment) {
47 auto ptr1 = IntrusivePtr<RefCounted>(new RefCounted(), /*add_ref=*/false);
48 auto raw = new RefCounted();
49 auto ptr2 = IntrusivePtr<RefCounted>(raw, /*add_ref=*/true);
50 ptr2 = ptr1;
51 ASSERT_EQ(ptr1.get(), ptr2.get());
52 ASSERT_FALSE(ptr2->RefCountIsOne());
53 ASSERT_TRUE(raw->RefCountIsOne());
54 raw->Unref();
55 }
56
TEST(IntrusivePtr,CopyAssignmentIntoEmpty)57 TEST(IntrusivePtr, CopyAssignmentIntoEmpty) {
58 auto ptr1 = IntrusivePtr<RefCounted>(new RefCounted(), /*add_ref=*/false);
59 auto ptr2 = IntrusivePtr<RefCounted>();
60 ptr2 = ptr1;
61 ASSERT_FALSE(ptr2->RefCountIsOne());
62 }
63
TEST(IntrusivePtr,MoveConstructor)64 TEST(IntrusivePtr, MoveConstructor) {
65 auto ptr1 = IntrusivePtr<RefCounted>(new RefCounted(), /*add_ref=*/false);
66 auto ptr2 = IntrusivePtr<RefCounted>(std::move(ptr1));
67 ASSERT_TRUE(ptr2->RefCountIsOne());
68 ASSERT_EQ(ptr1.get(), nullptr); // NOLINT(bugprone-use-after-move)
69 }
70
TEST(IntrusivePtr,MoveAssignment)71 TEST(IntrusivePtr, MoveAssignment) {
72 auto ptr1 = IntrusivePtr<RefCounted>(new RefCounted(), /*add_ref=*/false);
73 auto ptr2 = IntrusivePtr<RefCounted>(new RefCounted(), /*add_ref=*/false);
74 ptr2 = std::move(ptr1);
75 ASSERT_TRUE(ptr2->RefCountIsOne());
76 ASSERT_EQ(ptr1.get(), nullptr); // NOLINT(bugprone-use-after-move)
77 }
78
TEST(IntrusivePtr,MoveAssignmentIntoEmpty)79 TEST(IntrusivePtr, MoveAssignmentIntoEmpty) {
80 auto ptr1 = IntrusivePtr<RefCounted>(new RefCounted(), /*add_ref=*/false);
81 auto ptr2 = IntrusivePtr<RefCounted>();
82 ptr2 = std::move(ptr1);
83 ASSERT_TRUE(ptr2->RefCountIsOne());
84 ASSERT_EQ(ptr1.get(), nullptr); // NOLINT(bugprone-use-after-move)
85 }
86
TEST(IntrusivePtr,MoveAssignmentAlias)87 TEST(IntrusivePtr, MoveAssignmentAlias) {
88 auto ptr = IntrusivePtr<RefCounted>(new RefCounted(), /*add_ref=*/false);
89 auto& ptr_alias = ptr;
90 ptr = std::move(ptr_alias);
91 ASSERT_TRUE(ptr->RefCountIsOne());
92 }
93
TEST(IntrusivePtr,Reset)94 TEST(IntrusivePtr, Reset) {
95 auto ptr = IntrusivePtr<RefCounted>(new RefCounted(), /*add_ref=*/false);
96 ptr.reset(new RefCounted(), /*add_ref=*/false);
97 ASSERT_TRUE(ptr->RefCountIsOne());
98 // Test no leak.
99 }
100
TEST(IntrusivePtr,ResetIntoEmpty)101 TEST(IntrusivePtr, ResetIntoEmpty) {
102 auto ptr = IntrusivePtr<RefCounted>();
103 ptr.reset(new RefCounted(), /*add_ref=*/false);
104 ASSERT_TRUE(ptr->RefCountIsOne());
105 // Test no leak.
106 }
107
TEST(IntrusivePtr,ResetAlias)108 TEST(IntrusivePtr, ResetAlias) {
109 auto ptr = IntrusivePtr<RefCounted>(new RefCounted(), /*add_ref=*/false);
110 ASSERT_TRUE(ptr->RefCountIsOne());
111 ptr.reset(ptr.get(), /*add_ref=*/false); // No-op.
112 ASSERT_TRUE(ptr->RefCountIsOne());
113 }
114
TEST(IntrusivePtr,ResetRefBeforeUnref)115 TEST(IntrusivePtr, ResetRefBeforeUnref) {
116 class Foo : public RefCounted {
117 public:
118 explicit Foo(char label, Foo* ptr = nullptr)
119 : label_(label), ptr_(ptr, false) {}
120 char label_;
121 IntrusivePtr<Foo> ptr_;
122 };
123 IntrusivePtr<Foo> x(new Foo{'a', new Foo{'b', new Foo{'c'}}}, false);
124 // This test ensures that reset calls Ref on the new handle before unreffing
125 // the current handle to avoid subtle use-after-delete bugs.
126 // Here if we were to call Unref first, we will Unref the "Foo" with the
127 // label 'b', thereby destroying it. This will in turn Unref 'c' and destroy
128 // that. So reset would try to Ref a deleted object. Calling
129 // x->ptr_->ptr_.Ref() before x->ptr_.Unref() avoids this.
130 x->ptr_ = x->ptr_->ptr_;
131 }
132
TEST(IntrusivePtr,ResetStealPtrBeforeUnref)133 TEST(IntrusivePtr, ResetStealPtrBeforeUnref) {
134 class Foo : public RefCounted {
135 public:
136 explicit Foo(char label, Foo* ptr = nullptr)
137 : label_(label), ptr_(ptr, false) {}
138 char label_;
139 IntrusivePtr<Foo> ptr_;
140 };
141 IntrusivePtr<Foo> x(new Foo{'a', new Foo{'b', new Foo{'c'}}}, false);
142 // This test ensures that move assignment clears the handle_ of the moved
143 // object before Unreffing the current handle_.
144 x->ptr_ = std::move(x->ptr_->ptr_);
145 }
146
TEST(IntrusivePtr,Detach)147 TEST(IntrusivePtr, Detach) {
148 auto ptr = IntrusivePtr<RefCounted>(new RefCounted(), /*add_ref=*/false);
149 ASSERT_TRUE(ptr->RefCountIsOne());
150 auto raw = ptr.detach();
151 ASSERT_TRUE(raw->RefCountIsOne());
152 raw->Unref();
153 }
154 } // namespace
155 } // namespace core
156 } // namespace tensorflow
157