• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &registry);
108   auto v2 = MakeRefCounted<Value>(2, &registry);
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(&registry);
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(&registry);
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(&registry);
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