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