1 //
2 //
3 // Copyright 2017 gRPC authors.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 //
18
19 #include "src/core/lib/gprpp/ref_counted.h"
20
21 #include <memory>
22 #include <new>
23 #include <set>
24 #include <type_traits>
25
26 #include "gmock/gmock.h"
27 #include "gtest/gtest.h"
28
29 #include "test/core/util/test_config.h"
30
31 namespace grpc_core {
32 namespace testing {
33 namespace {
34
35 class Foo : public RefCounted<Foo> {
36 public:
Foo()37 Foo() {
38 static_assert(std::has_virtual_destructor<Foo>::value,
39 "PolymorphicRefCount doesn't have a virtual dtor");
40 }
41 };
42
TEST(RefCounted,Basic)43 TEST(RefCounted, Basic) {
44 Foo* foo = new Foo();
45 foo->Unref();
46 }
47
TEST(RefCounted,ExtraRef)48 TEST(RefCounted, ExtraRef) {
49 Foo* foo = new Foo();
50 RefCountedPtr<Foo> foop = foo->Ref();
51 foop.release();
52 foo->Unref();
53 foo->Unref();
54 }
55
TEST(RefCounted,Const)56 TEST(RefCounted, Const) {
57 const Foo* foo = new Foo();
58 RefCountedPtr<const Foo> foop = foo->Ref();
59 foop.release();
60 foop = foo->RefIfNonZero();
61 foop.release();
62 foo->Unref();
63 foo->Unref();
64 foo->Unref();
65 }
66
TEST(RefCounted,SubclassOfRefCountedType)67 TEST(RefCounted, SubclassOfRefCountedType) {
68 class Bar : public Foo {};
69 Bar* bar = new Bar();
70 RefCountedPtr<Bar> barp = bar->RefAsSubclass<Bar>();
71 barp.release();
72 barp = bar->RefAsSubclass<Bar>(DEBUG_LOCATION, "whee");
73 barp.release();
74 bar->Unref();
75 bar->Unref();
76 bar->Unref();
77 }
78
79 class Value : public RefCounted<Value, PolymorphicRefCount, UnrefNoDelete> {
80 public:
Value(int value,std::set<std::unique_ptr<Value>> * registry)81 Value(int value, std::set<std::unique_ptr<Value>>* registry) : value_(value) {
82 registry->emplace(this);
83 }
84
value() const85 int value() const { return value_; }
86
87 private:
88 int value_;
89 };
90
GarbageCollectRegistry(std::set<std::unique_ptr<Value>> * registry)91 void GarbageCollectRegistry(std::set<std::unique_ptr<Value>>* registry) {
92 for (auto it = registry->begin(); it != registry->end();) {
93 RefCountedPtr<Value> v = (*it)->RefIfNonZero();
94 // Check if the object has any refs remaining.
95 if (v != nullptr) {
96 // It has refs remaining, so we do not delete it.
97 ++it;
98 } else {
99 // No refs remaining, so remove it from the registry.
100 it = registry->erase(it);
101 }
102 }
103 }
104
TEST(RefCounted,NoDeleteUponUnref)105 TEST(RefCounted, NoDeleteUponUnref) {
106 std::set<std::unique_ptr<Value>> registry;
107 // Add two objects to the registry.
108 auto v1 = MakeRefCounted<Value>(1, ®istry);
109 auto v2 = MakeRefCounted<Value>(2, ®istry);
110 EXPECT_THAT(registry,
111 ::testing::UnorderedElementsAre(
112 ::testing::Pointee(::testing::Property(&Value::value, 1)),
113 ::testing::Pointee(::testing::Property(&Value::value, 2))));
114 // Running garbage collection should not delete anything, since both
115 // entries still have refs.
116 GarbageCollectRegistry(®istry);
117 EXPECT_THAT(registry,
118 ::testing::UnorderedElementsAre(
119 ::testing::Pointee(::testing::Property(&Value::value, 1)),
120 ::testing::Pointee(::testing::Property(&Value::value, 2))));
121 // Unref v2 and run GC to remove it.
122 v2.reset();
123 GarbageCollectRegistry(®istry);
124 EXPECT_THAT(registry, ::testing::UnorderedElementsAre(::testing::Pointee(
125 ::testing::Property(&Value::value, 1))));
126 // Now unref v1 and run GC again.
127 v1.reset();
128 GarbageCollectRegistry(®istry);
129 EXPECT_THAT(registry, ::testing::UnorderedElementsAre());
130 }
131
132 class ValueInExternalAllocation
133 : public RefCounted<ValueInExternalAllocation, PolymorphicRefCount,
134 UnrefCallDtor> {
135 public:
ValueInExternalAllocation(int value)136 explicit ValueInExternalAllocation(int value) : value_(value) {}
137
value() const138 int value() const { return value_; }
139
140 private:
141 int value_;
142 };
143
TEST(RefCounted,CallDtorUponUnref)144 TEST(RefCounted, CallDtorUponUnref) {
145 alignas(ValueInExternalAllocation) char
146 storage[sizeof(ValueInExternalAllocation)];
147 RefCountedPtr<ValueInExternalAllocation> value(
148 new (&storage) ValueInExternalAllocation(5));
149 EXPECT_EQ(value->value(), 5);
150 }
151
152 class FooNonPolymorphic
153 : public RefCounted<FooNonPolymorphic, NonPolymorphicRefCount> {
154 public:
FooNonPolymorphic()155 FooNonPolymorphic() {
156 static_assert(!std::has_virtual_destructor<FooNonPolymorphic>::value,
157 "NonPolymorphicRefCount has a virtual dtor");
158 }
159 };
160
TEST(RefCountedNonPolymorphic,Basic)161 TEST(RefCountedNonPolymorphic, Basic) {
162 FooNonPolymorphic* foo = new FooNonPolymorphic();
163 foo->Unref();
164 }
165
TEST(RefCountedNonPolymorphic,ExtraRef)166 TEST(RefCountedNonPolymorphic, ExtraRef) {
167 FooNonPolymorphic* foo = new FooNonPolymorphic();
168 RefCountedPtr<FooNonPolymorphic> foop = foo->Ref();
169 foop.release();
170 foo->Unref();
171 foo->Unref();
172 }
173
174 class FooWithTracing : public RefCounted<FooWithTracing> {
175 public:
FooWithTracing()176 FooWithTracing() : RefCounted("Foo") {}
177 };
178
TEST(RefCountedWithTracing,Basic)179 TEST(RefCountedWithTracing, Basic) {
180 FooWithTracing* foo = new FooWithTracing();
181 RefCountedPtr<FooWithTracing> foop = foo->Ref(DEBUG_LOCATION, "extra_ref");
182 foop.release();
183 foo->Unref(DEBUG_LOCATION, "extra_ref");
184 // Can use the no-argument methods, too.
185 foop = foo->Ref();
186 foop.release();
187 foo->Unref();
188 foo->Unref(DEBUG_LOCATION, "original_ref");
189 }
190
191 class FooNonPolymorphicWithTracing
192 : public RefCounted<FooNonPolymorphicWithTracing, NonPolymorphicRefCount> {
193 public:
FooNonPolymorphicWithTracing()194 FooNonPolymorphicWithTracing() : RefCounted("FooNonPolymorphicWithTracing") {}
195 };
196
TEST(RefCountedNonPolymorphicWithTracing,Basic)197 TEST(RefCountedNonPolymorphicWithTracing, Basic) {
198 FooNonPolymorphicWithTracing* foo = new FooNonPolymorphicWithTracing();
199 RefCountedPtr<FooNonPolymorphicWithTracing> foop =
200 foo->Ref(DEBUG_LOCATION, "extra_ref");
201 foop.release();
202 foo->Unref(DEBUG_LOCATION, "extra_ref");
203 // Can use the no-argument methods, too.
204 foop = foo->Ref();
205 foop.release();
206 foo->Unref();
207 foo->Unref(DEBUG_LOCATION, "original_ref");
208 }
209
210 } // namespace
211 } // namespace testing
212 } // namespace grpc_core
213
main(int argc,char ** argv)214 int main(int argc, char** argv) {
215 grpc::testing::TestEnvironment env(&argc, argv);
216 ::testing::InitGoogleTest(&argc, argv);
217 return RUN_ALL_TESTS();
218 }
219