xref: /aosp_15_r20/external/grpc-grpc/test/core/gprpp/ref_counted_test.cc (revision cc02d7e222339f7a4f6ba5f422e6413f4bd931f2)
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, &registry);
109   auto v2 = MakeRefCounted<Value>(2, &registry);
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(&registry);
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(&registry);
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(&registry);
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