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 <set>
22
23 #include <gmock/gmock.h>
24 #include <gtest/gtest.h>
25
26 #include "src/core/lib/gprpp/memory.h"
27 #include "test/core/util/test_config.h"
28
29 namespace grpc_core {
30 namespace testing {
31 namespace {
32
33 class Foo : public RefCounted<Foo> {
34 public:
Foo()35 Foo() {
36 static_assert(std::has_virtual_destructor<Foo>::value,
37 "PolymorphicRefCount doesn't have a virtual dtor");
38 }
39 };
40
TEST(RefCounted,Basic)41 TEST(RefCounted, Basic) {
42 Foo* foo = new Foo();
43 foo->Unref();
44 }
45
TEST(RefCounted,ExtraRef)46 TEST(RefCounted, ExtraRef) {
47 Foo* foo = new Foo();
48 RefCountedPtr<Foo> foop = foo->Ref();
49 foop.release();
50 foo->Unref();
51 foo->Unref();
52 }
53
54 class Value : public RefCounted<Value, PolymorphicRefCount, false> {
55 public:
Value(int value,std::set<std::unique_ptr<Value>> * registry)56 Value(int value, std::set<std::unique_ptr<Value>>* registry) : value_(value) {
57 registry->emplace(this);
58 }
59
value() const60 int value() const { return value_; }
61
62 private:
63 int value_;
64 };
65
GarbageCollectRegistry(std::set<std::unique_ptr<Value>> * registry)66 void GarbageCollectRegistry(std::set<std::unique_ptr<Value>>* registry) {
67 for (auto it = registry->begin(); it != registry->end();) {
68 RefCountedPtr<Value> v = (*it)->RefIfNonZero();
69 // Check if the object has any refs remaining.
70 if (v != nullptr) {
71 // It has refs remaining, so we do not delete it.
72 ++it;
73 } else {
74 // No refs remaining, so remove it from the registry.
75 it = registry->erase(it);
76 }
77 }
78 }
79
TEST(RefCounted,NoDeleteUponUnref)80 TEST(RefCounted, NoDeleteUponUnref) {
81 std::set<std::unique_ptr<Value>> registry;
82 // Add two objects to the registry.
83 auto v1 = MakeRefCounted<Value>(1, ®istry);
84 auto v2 = MakeRefCounted<Value>(2, ®istry);
85 EXPECT_THAT(registry,
86 ::testing::UnorderedElementsAre(
87 ::testing::Pointee(::testing::Property(&Value::value, 1)),
88 ::testing::Pointee(::testing::Property(&Value::value, 2))));
89 // Running garbage collection should not delete anything, since both
90 // entries still have refs.
91 GarbageCollectRegistry(®istry);
92 EXPECT_THAT(registry,
93 ::testing::UnorderedElementsAre(
94 ::testing::Pointee(::testing::Property(&Value::value, 1)),
95 ::testing::Pointee(::testing::Property(&Value::value, 2))));
96 // Unref v2 and run GC to remove it.
97 v2.reset();
98 GarbageCollectRegistry(®istry);
99 EXPECT_THAT(registry, ::testing::UnorderedElementsAre(::testing::Pointee(
100 ::testing::Property(&Value::value, 1))));
101 // Now unref v1 and run GC again.
102 v1.reset();
103 GarbageCollectRegistry(®istry);
104 EXPECT_THAT(registry, ::testing::UnorderedElementsAre());
105 }
106
107 class FooNonPolymorphic
108 : public RefCounted<FooNonPolymorphic, NonPolymorphicRefCount> {
109 public:
FooNonPolymorphic()110 FooNonPolymorphic() {
111 static_assert(!std::has_virtual_destructor<FooNonPolymorphic>::value,
112 "NonPolymorphicRefCount has a virtual dtor");
113 }
114 };
115
TEST(RefCountedNonPolymorphic,Basic)116 TEST(RefCountedNonPolymorphic, Basic) {
117 FooNonPolymorphic* foo = new FooNonPolymorphic();
118 foo->Unref();
119 }
120
TEST(RefCountedNonPolymorphic,ExtraRef)121 TEST(RefCountedNonPolymorphic, ExtraRef) {
122 FooNonPolymorphic* foo = new FooNonPolymorphic();
123 RefCountedPtr<FooNonPolymorphic> foop = foo->Ref();
124 foop.release();
125 foo->Unref();
126 foo->Unref();
127 }
128
129 class FooWithTracing : public RefCounted<FooWithTracing> {
130 public:
FooWithTracing()131 FooWithTracing() : RefCounted("Foo") {}
132 };
133
TEST(RefCountedWithTracing,Basic)134 TEST(RefCountedWithTracing, Basic) {
135 FooWithTracing* foo = new FooWithTracing();
136 RefCountedPtr<FooWithTracing> foop = foo->Ref(DEBUG_LOCATION, "extra_ref");
137 foop.release();
138 foo->Unref(DEBUG_LOCATION, "extra_ref");
139 // Can use the no-argument methods, too.
140 foop = foo->Ref();
141 foop.release();
142 foo->Unref();
143 foo->Unref(DEBUG_LOCATION, "original_ref");
144 }
145
146 class FooNonPolymorphicWithTracing
147 : public RefCounted<FooNonPolymorphicWithTracing, NonPolymorphicRefCount> {
148 public:
FooNonPolymorphicWithTracing()149 FooNonPolymorphicWithTracing() : RefCounted("FooNonPolymorphicWithTracing") {}
150 };
151
TEST(RefCountedNonPolymorphicWithTracing,Basic)152 TEST(RefCountedNonPolymorphicWithTracing, Basic) {
153 FooNonPolymorphicWithTracing* foo = new FooNonPolymorphicWithTracing();
154 RefCountedPtr<FooNonPolymorphicWithTracing> foop =
155 foo->Ref(DEBUG_LOCATION, "extra_ref");
156 foop.release();
157 foo->Unref(DEBUG_LOCATION, "extra_ref");
158 // Can use the no-argument methods, too.
159 foop = foo->Ref();
160 foop.release();
161 foo->Unref();
162 foo->Unref(DEBUG_LOCATION, "original_ref");
163 }
164
165 } // namespace
166 } // namespace testing
167 } // namespace grpc_core
168
main(int argc,char ** argv)169 int main(int argc, char** argv) {
170 grpc::testing::TestEnvironment env(argc, argv);
171 ::testing::InitGoogleTest(&argc, argv);
172 return RUN_ALL_TESTS();
173 }
174