1 // Copyright 2019 The Abseil Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 #include "absl/strings/internal/cordz_handle.h"
15
16 #include <random>
17
18 #include "gmock/gmock.h"
19 #include "gtest/gtest.h"
20 #include "absl/memory/memory.h"
21 #include "absl/synchronization/internal/thread_pool.h"
22 #include "absl/synchronization/notification.h"
23 #include "absl/time/clock.h"
24 #include "absl/time/time.h"
25
26 namespace absl {
27 ABSL_NAMESPACE_BEGIN
28 namespace cord_internal {
29 namespace {
30
31 using ::testing::ElementsAre;
32 using ::testing::Gt;
33 using ::testing::IsEmpty;
34 using ::testing::SizeIs;
35
36 // Local less verbose helper
DeleteQueue()37 std::vector<const CordzHandle*> DeleteQueue() {
38 return CordzHandle::DiagnosticsGetDeleteQueue();
39 }
40
41 struct CordzHandleDeleteTracker : public CordzHandle {
42 bool* deleted;
CordzHandleDeleteTrackerabsl::cord_internal::__anon49a956b10111::CordzHandleDeleteTracker43 explicit CordzHandleDeleteTracker(bool* deleted) : deleted(deleted) {}
~CordzHandleDeleteTrackerabsl::cord_internal::__anon49a956b10111::CordzHandleDeleteTracker44 ~CordzHandleDeleteTracker() override { *deleted = true; }
45 };
46
TEST(CordzHandleTest,DeleteQueueIsEmpty)47 TEST(CordzHandleTest, DeleteQueueIsEmpty) {
48 EXPECT_THAT(DeleteQueue(), SizeIs(0));
49 }
50
TEST(CordzHandleTest,CordzHandleCreateDelete)51 TEST(CordzHandleTest, CordzHandleCreateDelete) {
52 bool deleted = false;
53 auto* handle = new CordzHandleDeleteTracker(&deleted);
54 EXPECT_FALSE(handle->is_snapshot());
55 EXPECT_TRUE(handle->SafeToDelete());
56 EXPECT_THAT(DeleteQueue(), SizeIs(0));
57
58 CordzHandle::Delete(handle);
59 EXPECT_THAT(DeleteQueue(), SizeIs(0));
60 EXPECT_TRUE(deleted);
61 }
62
TEST(CordzHandleTest,CordzSnapshotCreateDelete)63 TEST(CordzHandleTest, CordzSnapshotCreateDelete) {
64 auto* snapshot = new CordzSnapshot();
65 EXPECT_TRUE(snapshot->is_snapshot());
66 EXPECT_TRUE(snapshot->SafeToDelete());
67 EXPECT_THAT(DeleteQueue(), ElementsAre(snapshot));
68 delete snapshot;
69 EXPECT_THAT(DeleteQueue(), SizeIs(0));
70 }
71
TEST(CordzHandleTest,CordzHandleCreateDeleteWithSnapshot)72 TEST(CordzHandleTest, CordzHandleCreateDeleteWithSnapshot) {
73 bool deleted = false;
74 auto* snapshot = new CordzSnapshot();
75 auto* handle = new CordzHandleDeleteTracker(&deleted);
76 EXPECT_FALSE(handle->SafeToDelete());
77
78 CordzHandle::Delete(handle);
79 EXPECT_THAT(DeleteQueue(), ElementsAre(handle, snapshot));
80 EXPECT_FALSE(deleted);
81 EXPECT_FALSE(handle->SafeToDelete());
82
83 delete snapshot;
84 EXPECT_THAT(DeleteQueue(), SizeIs(0));
85 EXPECT_TRUE(deleted);
86 }
87
TEST(CordzHandleTest,MultiSnapshot)88 TEST(CordzHandleTest, MultiSnapshot) {
89 bool deleted[3] = {false, false, false};
90
91 CordzSnapshot* snapshot[3];
92 CordzHandleDeleteTracker* handle[3];
93 for (int i = 0; i < 3; ++i) {
94 snapshot[i] = new CordzSnapshot();
95 handle[i] = new CordzHandleDeleteTracker(&deleted[i]);
96 CordzHandle::Delete(handle[i]);
97 }
98
99 EXPECT_THAT(DeleteQueue(), ElementsAre(handle[2], snapshot[2], handle[1],
100 snapshot[1], handle[0], snapshot[0]));
101 EXPECT_THAT(deleted, ElementsAre(false, false, false));
102
103 delete snapshot[1];
104 EXPECT_THAT(DeleteQueue(), ElementsAre(handle[2], snapshot[2], handle[1],
105 handle[0], snapshot[0]));
106 EXPECT_THAT(deleted, ElementsAre(false, false, false));
107
108 delete snapshot[0];
109 EXPECT_THAT(DeleteQueue(), ElementsAre(handle[2], snapshot[2]));
110 EXPECT_THAT(deleted, ElementsAre(true, true, false));
111
112 delete snapshot[2];
113 EXPECT_THAT(DeleteQueue(), SizeIs(0));
114 EXPECT_THAT(deleted, ElementsAre(true, true, deleted));
115 }
116
TEST(CordzHandleTest,DiagnosticsHandleIsSafeToInspect)117 TEST(CordzHandleTest, DiagnosticsHandleIsSafeToInspect) {
118 CordzSnapshot snapshot1;
119 EXPECT_TRUE(snapshot1.DiagnosticsHandleIsSafeToInspect(nullptr));
120
121 auto* handle1 = new CordzHandle();
122 EXPECT_TRUE(snapshot1.DiagnosticsHandleIsSafeToInspect(handle1));
123
124 CordzHandle::Delete(handle1);
125 EXPECT_TRUE(snapshot1.DiagnosticsHandleIsSafeToInspect(handle1));
126
127 CordzSnapshot snapshot2;
128 auto* handle2 = new CordzHandle();
129 EXPECT_TRUE(snapshot1.DiagnosticsHandleIsSafeToInspect(handle1));
130 EXPECT_TRUE(snapshot1.DiagnosticsHandleIsSafeToInspect(handle2));
131 EXPECT_FALSE(snapshot2.DiagnosticsHandleIsSafeToInspect(handle1));
132 EXPECT_TRUE(snapshot2.DiagnosticsHandleIsSafeToInspect(handle2));
133
134 CordzHandle::Delete(handle2);
135 EXPECT_TRUE(snapshot1.DiagnosticsHandleIsSafeToInspect(handle1));
136 }
137
TEST(CordzHandleTest,DiagnosticsGetSafeToInspectDeletedHandles)138 TEST(CordzHandleTest, DiagnosticsGetSafeToInspectDeletedHandles) {
139 EXPECT_THAT(DeleteQueue(), IsEmpty());
140
141 auto* handle = new CordzHandle();
142 auto* snapshot1 = new CordzSnapshot();
143
144 // snapshot1 should be able to see handle.
145 EXPECT_THAT(DeleteQueue(), ElementsAre(snapshot1));
146 EXPECT_TRUE(snapshot1->DiagnosticsHandleIsSafeToInspect(handle));
147 EXPECT_THAT(snapshot1->DiagnosticsGetSafeToInspectDeletedHandles(),
148 IsEmpty());
149
150 // This handle will be safe to inspect as long as snapshot1 is alive. However,
151 // since only snapshot1 can prove that it's alive, it will be hidden from
152 // snapshot2.
153 CordzHandle::Delete(handle);
154
155 // This snapshot shouldn't be able to see handle because handle was already
156 // sent to Delete.
157 auto* snapshot2 = new CordzSnapshot();
158
159 // DeleteQueue elements are LIFO order.
160 EXPECT_THAT(DeleteQueue(), ElementsAre(snapshot2, handle, snapshot1));
161
162 EXPECT_TRUE(snapshot1->DiagnosticsHandleIsSafeToInspect(handle));
163 EXPECT_FALSE(snapshot2->DiagnosticsHandleIsSafeToInspect(handle));
164
165 EXPECT_THAT(snapshot1->DiagnosticsGetSafeToInspectDeletedHandles(),
166 ElementsAre(handle));
167 EXPECT_THAT(snapshot2->DiagnosticsGetSafeToInspectDeletedHandles(),
168 IsEmpty());
169
170 CordzHandle::Delete(snapshot1);
171 EXPECT_THAT(DeleteQueue(), ElementsAre(snapshot2));
172
173 CordzHandle::Delete(snapshot2);
174 EXPECT_THAT(DeleteQueue(), IsEmpty());
175 }
176
177 // Create and delete CordzHandle and CordzSnapshot objects in multiple threads
178 // so that tsan has some time to chew on it and look for memory problems.
TEST(CordzHandleTest,MultiThreaded)179 TEST(CordzHandleTest, MultiThreaded) {
180 Notification stop;
181 static constexpr int kNumThreads = 4;
182 // Keep the number of handles relatively small so that the test will naturally
183 // transition to an empty delete queue during the test. If there are, say, 100
184 // handles, that will virtually never happen. With 10 handles and around 50k
185 // iterations in each of 4 threads, the delete queue appears to become empty
186 // around 200 times.
187 static constexpr int kNumHandles = 10;
188
189 // Each thread is going to pick a random index and atomically swap its
190 // CordzHandle with one in handles. This way, each thread can avoid
191 // manipulating a CordzHandle that might be operated upon in another thread.
192 std::vector<std::atomic<CordzHandle*>> handles(kNumHandles);
193
194 // global bool which is set when any thread did get some 'safe to inspect'
195 // handles. On some platforms and OSS tests, we might risk that some pool
196 // threads are starved, stalled, or just got a few unlikely random 'handle'
197 // coin tosses, so we satisfy this test with simply observing 'some' thread
198 // did something meaningful, which should minimize the potential for flakes.
199 std::atomic<bool> found_safe_to_inspect(false);
200
201 {
202 absl::synchronization_internal::ThreadPool pool(kNumThreads);
203 for (int i = 0; i < kNumThreads; ++i) {
204 pool.Schedule([&stop, &handles, &found_safe_to_inspect]() {
205 std::minstd_rand gen;
206 std::uniform_int_distribution<int> dist_type(0, 2);
207 std::uniform_int_distribution<int> dist_handle(0, kNumHandles - 1);
208
209 while (!stop.HasBeenNotified()) {
210 CordzHandle* handle;
211 switch (dist_type(gen)) {
212 case 0:
213 handle = new CordzHandle();
214 break;
215 case 1:
216 handle = new CordzSnapshot();
217 break;
218 default:
219 handle = nullptr;
220 break;
221 }
222 CordzHandle* old_handle = handles[dist_handle(gen)].exchange(handle);
223 if (old_handle != nullptr) {
224 std::vector<const CordzHandle*> safe_to_inspect =
225 old_handle->DiagnosticsGetSafeToInspectDeletedHandles();
226 for (const CordzHandle* handle : safe_to_inspect) {
227 // We're in a tight loop, so don't generate too many error
228 // messages.
229 ASSERT_FALSE(handle->is_snapshot());
230 }
231 if (!safe_to_inspect.empty()) {
232 found_safe_to_inspect.store(true);
233 }
234 CordzHandle::Delete(old_handle);
235 }
236 }
237
238 // Have each thread attempt to clean up everything. Some thread will be
239 // the last to reach this cleanup code, and it will be guaranteed to
240 // clean up everything because nothing remains to create new handles.
241 for (auto& h : handles) {
242 if (CordzHandle* handle = h.exchange(nullptr)) {
243 CordzHandle::Delete(handle);
244 }
245 }
246 });
247 }
248
249 // The threads will hammer away. Give it a little bit of time for tsan to
250 // spot errors.
251 absl::SleepFor(absl::Seconds(3));
252 stop.Notify();
253 }
254
255 // Confirm that the test did *something*. This check will be satisfied as
256 // long as any thread has deleted a CordzSnapshot object and a non-snapshot
257 // CordzHandle was deleted after the CordzSnapshot was created.
258 // See also comments on `found_safe_to_inspect`
259 EXPECT_TRUE(found_safe_to_inspect.load());
260 }
261
262 } // namespace
263 } // namespace cord_internal
264 ABSL_NAMESPACE_END
265 } // namespace absl
266