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/util/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 #include "test/core/test_util/test_config.h"
29
30 namespace grpc_core {
31 namespace testing {
32 namespace {
33
34 class Foo : public RefCounted<Foo> {
35 public:
Foo()36 Foo() {
37 static_assert(std::has_virtual_destructor<Foo>::value,
38 "PolymorphicRefCount doesn't have a virtual dtor");
39 }
40 };
41
TEST(RefCounted,Basic)42 TEST(RefCounted, Basic) {
43 Foo* foo = new Foo();
44 foo->Unref();
45 }
46
TEST(RefCounted,ExtraRef)47 TEST(RefCounted, ExtraRef) {
48 Foo* foo = new Foo();
49 RefCountedPtr<Foo> foop = foo->Ref();
50 foop.release();
51 foo->Unref();
52 foo->Unref();
53 }
54
TEST(RefCounted,Const)55 TEST(RefCounted, Const) {
56 const Foo* foo = new Foo();
57 RefCountedPtr<const Foo> foop = foo->Ref();
58 foop.release();
59 foop = foo->RefIfNonZero();
60 foop.release();
61 foo->Unref();
62 foo->Unref();
63 foo->Unref();
64 }
65
TEST(RefCounted,SubclassOfRefCountedType)66 TEST(RefCounted, SubclassOfRefCountedType) {
67 class Bar : public Foo {};
68 Bar* bar = new Bar();
69 RefCountedPtr<Bar> barp = bar->RefAsSubclass<Bar>();
70 barp.release();
71 barp = bar->RefAsSubclass<Bar>(DEBUG_LOCATION, "whee");
72 barp.release();
73 bar->Unref();
74 bar->Unref();
75 bar->Unref();
76 }
77
78 class Value : public RefCounted<Value, PolymorphicRefCount, UnrefNoDelete> {
79 public:
Value(int value,std::set<std::unique_ptr<Value>> * registry)80 Value(int value, std::set<std::unique_ptr<Value>>* registry) : value_(value) {
81 registry->emplace(this);
82 }
83
value() const84 int value() const { return value_; }
85
86 private:
87 int value_;
88 };
89
GarbageCollectRegistry(std::set<std::unique_ptr<Value>> * registry)90 void GarbageCollectRegistry(std::set<std::unique_ptr<Value>>* registry) {
91 for (auto it = registry->begin(); it != registry->end();) {
92 RefCountedPtr<Value> v = (*it)->RefIfNonZero();
93 // Check if the object has any refs remaining.
94 if (v != nullptr) {
95 // It has refs remaining, so we do not delete it.
96 ++it;
97 } else {
98 // No refs remaining, so remove it from the registry.
99 it = registry->erase(it);
100 }
101 }
102 }
103
TEST(RefCounted,NoDeleteUponUnref)104 TEST(RefCounted, NoDeleteUponUnref) {
105 std::set<std::unique_ptr<Value>> registry;
106 // Add two objects to the registry.
107 auto v1 = MakeRefCounted<Value>(1, ®istry);
108 auto v2 = MakeRefCounted<Value>(2, ®istry);
109 EXPECT_THAT(registry,
110 ::testing::UnorderedElementsAre(
111 ::testing::Pointee(::testing::Property(&Value::value, 1)),
112 ::testing::Pointee(::testing::Property(&Value::value, 2))));
113 // Running garbage collection should not delete anything, since both
114 // entries still have refs.
115 GarbageCollectRegistry(®istry);
116 EXPECT_THAT(registry,
117 ::testing::UnorderedElementsAre(
118 ::testing::Pointee(::testing::Property(&Value::value, 1)),
119 ::testing::Pointee(::testing::Property(&Value::value, 2))));
120 // Unref v2 and run GC to remove it.
121 v2.reset();
122 GarbageCollectRegistry(®istry);
123 EXPECT_THAT(registry, ::testing::UnorderedElementsAre(::testing::Pointee(
124 ::testing::Property(&Value::value, 1))));
125 // Now unref v1 and run GC again.
126 v1.reset();
127 GarbageCollectRegistry(®istry);
128 EXPECT_THAT(registry, ::testing::UnorderedElementsAre());
129 }
130
131 class ValueInExternalAllocation
132 : public RefCounted<ValueInExternalAllocation, PolymorphicRefCount,
133 UnrefCallDtor> {
134 public:
ValueInExternalAllocation(int value)135 explicit ValueInExternalAllocation(int value) : value_(value) {}
136
value() const137 int value() const { return value_; }
138
139 private:
140 int value_;
141 };
142
TEST(RefCounted,CallDtorUponUnref)143 TEST(RefCounted, CallDtorUponUnref) {
144 alignas(ValueInExternalAllocation) char
145 storage[sizeof(ValueInExternalAllocation)];
146 RefCountedPtr<ValueInExternalAllocation> value(
147 new (&storage) ValueInExternalAllocation(5));
148 EXPECT_EQ(value->value(), 5);
149 }
150
151 class FooNonPolymorphic
152 : public RefCounted<FooNonPolymorphic, NonPolymorphicRefCount> {
153 public:
FooNonPolymorphic()154 FooNonPolymorphic() {
155 static_assert(!std::has_virtual_destructor<FooNonPolymorphic>::value,
156 "NonPolymorphicRefCount has a virtual dtor");
157 }
158 };
159
TEST(RefCountedNonPolymorphic,Basic)160 TEST(RefCountedNonPolymorphic, Basic) {
161 FooNonPolymorphic* foo = new FooNonPolymorphic();
162 foo->Unref();
163 }
164
TEST(RefCountedNonPolymorphic,ExtraRef)165 TEST(RefCountedNonPolymorphic, ExtraRef) {
166 FooNonPolymorphic* foo = new FooNonPolymorphic();
167 RefCountedPtr<FooNonPolymorphic> foop = foo->Ref();
168 foop.release();
169 foo->Unref();
170 foo->Unref();
171 }
172
173 class FooWithTracing : public RefCounted<FooWithTracing> {
174 public:
FooWithTracing()175 FooWithTracing() : RefCounted("Foo") {}
176 };
177
TEST(RefCountedWithTracing,Basic)178 TEST(RefCountedWithTracing, Basic) {
179 FooWithTracing* foo = new FooWithTracing();
180 RefCountedPtr<FooWithTracing> foop = foo->Ref(DEBUG_LOCATION, "extra_ref");
181 foop.release();
182 foo->Unref(DEBUG_LOCATION, "extra_ref");
183 // Can use the no-argument methods, too.
184 foop = foo->Ref();
185 foop.release();
186 foo->Unref();
187 foo->Unref(DEBUG_LOCATION, "original_ref");
188 }
189
190 class FooNonPolymorphicWithTracing
191 : public RefCounted<FooNonPolymorphicWithTracing, NonPolymorphicRefCount> {
192 public:
FooNonPolymorphicWithTracing()193 FooNonPolymorphicWithTracing() : RefCounted("FooNonPolymorphicWithTracing") {}
194 };
195
TEST(RefCountedNonPolymorphicWithTracing,Basic)196 TEST(RefCountedNonPolymorphicWithTracing, Basic) {
197 FooNonPolymorphicWithTracing* foo = new FooNonPolymorphicWithTracing();
198 RefCountedPtr<FooNonPolymorphicWithTracing> foop =
199 foo->Ref(DEBUG_LOCATION, "extra_ref");
200 foop.release();
201 foo->Unref(DEBUG_LOCATION, "extra_ref");
202 // Can use the no-argument methods, too.
203 foop = foo->Ref();
204 foop.release();
205 foo->Unref();
206 foo->Unref(DEBUG_LOCATION, "original_ref");
207 }
208
209 } // namespace
210 } // namespace testing
211 } // namespace grpc_core
212
main(int argc,char ** argv)213 int main(int argc, char** argv) {
214 grpc::testing::TestEnvironment env(&argc, argv);
215 ::testing::InitGoogleTest(&argc, argv);
216 return RUN_ALL_TESTS();
217 }
218