• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
15 #include "absl/strings/internal/cordz_sample_token.h"
16 
17 #include <memory>
18 #include <type_traits>
19 #include <vector>
20 
21 #include "gmock/gmock.h"
22 #include "gtest/gtest.h"
23 #include "absl/memory/memory.h"
24 #include "absl/random/random.h"
25 #include "absl/strings/cordz_test_helpers.h"
26 #include "absl/strings/internal/cord_rep_flat.h"
27 #include "absl/strings/internal/cordz_handle.h"
28 #include "absl/strings/internal/cordz_info.h"
29 #include "absl/synchronization/internal/thread_pool.h"
30 #include "absl/synchronization/notification.h"
31 #include "absl/time/clock.h"
32 #include "absl/time/time.h"
33 
34 namespace absl {
35 ABSL_NAMESPACE_BEGIN
36 namespace cord_internal {
37 namespace {
38 
39 using ::testing::ElementsAre;
40 using ::testing::Eq;
41 using ::testing::Ne;
42 
43 // Used test values
44 auto constexpr kTrackCordMethod = CordzUpdateTracker::kConstructorString;
45 
TEST(CordzSampleTokenTest,IteratorTraits)46 TEST(CordzSampleTokenTest, IteratorTraits) {
47   static_assert(std::is_copy_constructible<CordzSampleToken::Iterator>::value,
48                 "");
49   static_assert(std::is_copy_assignable<CordzSampleToken::Iterator>::value, "");
50   static_assert(std::is_move_constructible<CordzSampleToken::Iterator>::value,
51                 "");
52   static_assert(std::is_move_assignable<CordzSampleToken::Iterator>::value, "");
53   static_assert(
54       std::is_same<
55           std::iterator_traits<CordzSampleToken::Iterator>::iterator_category,
56           std::input_iterator_tag>::value,
57       "");
58   static_assert(
59       std::is_same<std::iterator_traits<CordzSampleToken::Iterator>::value_type,
60                    const CordzInfo&>::value,
61       "");
62   static_assert(
63       std::is_same<
64           std::iterator_traits<CordzSampleToken::Iterator>::difference_type,
65           ptrdiff_t>::value,
66       "");
67   static_assert(
68       std::is_same<std::iterator_traits<CordzSampleToken::Iterator>::pointer,
69                    const CordzInfo*>::value,
70       "");
71   static_assert(
72       std::is_same<std::iterator_traits<CordzSampleToken::Iterator>::reference,
73                    const CordzInfo&>::value,
74       "");
75 }
76 
TEST(CordzSampleTokenTest,IteratorEmpty)77 TEST(CordzSampleTokenTest, IteratorEmpty) {
78   CordzSampleToken token;
79   EXPECT_THAT(token.begin(), Eq(token.end()));
80 }
81 
TEST(CordzSampleTokenTest,Iterator)82 TEST(CordzSampleTokenTest, Iterator) {
83   TestCordData cord1, cord2, cord3;
84   CordzInfo::TrackCord(cord1.data, kTrackCordMethod);
85   CordzInfo* info1 = cord1.data.cordz_info();
86   CordzInfo::TrackCord(cord2.data, kTrackCordMethod);
87   CordzInfo* info2 = cord2.data.cordz_info();
88   CordzInfo::TrackCord(cord3.data, kTrackCordMethod);
89   CordzInfo* info3 = cord3.data.cordz_info();
90 
91   CordzSampleToken token;
92   std::vector<const CordzInfo*> found;
93   for (const CordzInfo& cord_info : token) {
94     found.push_back(&cord_info);
95   }
96 
97   EXPECT_THAT(found, ElementsAre(info3, info2, info1));
98 
99   info1->Untrack();
100   info2->Untrack();
101   info3->Untrack();
102 }
103 
TEST(CordzSampleTokenTest,IteratorEquality)104 TEST(CordzSampleTokenTest, IteratorEquality) {
105   TestCordData cord1;
106   TestCordData cord2;
107   TestCordData cord3;
108   CordzInfo::TrackCord(cord1.data, kTrackCordMethod);
109   CordzInfo* info1 = cord1.data.cordz_info();
110 
111   CordzSampleToken token1;
112   // lhs starts with the CordzInfo corresponding to cord1 at the head.
113   CordzSampleToken::Iterator lhs = token1.begin();
114 
115   CordzInfo::TrackCord(cord2.data, kTrackCordMethod);
116   CordzInfo* info2 = cord2.data.cordz_info();
117 
118   CordzSampleToken token2;
119   // rhs starts with the CordzInfo corresponding to cord2 at the head.
120   CordzSampleToken::Iterator rhs = token2.begin();
121 
122   CordzInfo::TrackCord(cord3.data, kTrackCordMethod);
123   CordzInfo* info3 = cord3.data.cordz_info();
124 
125   // lhs is on cord1 while rhs is on cord2.
126   EXPECT_THAT(lhs, Ne(rhs));
127 
128   rhs++;
129   // lhs and rhs are both on cord1, but they didn't come from the same
130   // CordzSampleToken.
131   EXPECT_THAT(lhs, Ne(rhs));
132 
133   lhs++;
134   rhs++;
135   // Both lhs and rhs are done, so they are on nullptr.
136   EXPECT_THAT(lhs, Eq(rhs));
137 
138   info1->Untrack();
139   info2->Untrack();
140   info3->Untrack();
141 }
142 
TEST(CordzSampleTokenTest,MultiThreaded)143 TEST(CordzSampleTokenTest, MultiThreaded) {
144   Notification stop;
145   static constexpr int kNumThreads = 4;
146   static constexpr int kNumCords = 3;
147   static constexpr int kNumTokens = 3;
148   absl::synchronization_internal::ThreadPool pool(kNumThreads);
149 
150   for (int i = 0; i < kNumThreads; ++i) {
151     pool.Schedule([&stop]() {
152       absl::BitGen gen;
153       TestCordData cords[kNumCords];
154       std::unique_ptr<CordzSampleToken> tokens[kNumTokens];
155 
156       while (!stop.HasBeenNotified()) {
157         // Randomly perform one of five actions:
158         //   1) Untrack
159         //   2) Track
160         //   3) Iterate over Cords visible to a token.
161         //   4) Unsample
162         //   5) Sample
163         int index = absl::Uniform(gen, 0, kNumCords);
164         if (absl::Bernoulli(gen, 0.5)) {
165           TestCordData& cord = cords[index];
166           // Track/untrack.
167           if (cord.data.is_profiled()) {
168             // 1) Untrack
169             cord.data.cordz_info()->Untrack();
170             cord.data.clear_cordz_info();;
171           } else {
172             // 2) Track
173             CordzInfo::TrackCord(cord.data, kTrackCordMethod);
174           }
175         } else {
176           std::unique_ptr<CordzSampleToken>& token = tokens[index];
177           if (token) {
178             if (absl::Bernoulli(gen, 0.5)) {
179               // 3) Iterate over Cords visible to a token.
180               for (const CordzInfo& info : *token) {
181                 // This is trivial work to allow us to compile the loop.
182                 EXPECT_THAT(info.Next(*token), Ne(&info));
183               }
184             } else {
185               // 4) Unsample
186               token = nullptr;
187             }
188           } else {
189             // 5) Sample
190             token = absl::make_unique<CordzSampleToken>();
191           }
192         }
193       }
194       for (TestCordData& cord : cords) {
195         CordzInfo::MaybeUntrackCord(cord.data.cordz_info());
196       }
197     });
198   }
199   // The threads will hammer away.  Give it a little bit of time for tsan to
200   // spot errors.
201   absl::SleepFor(absl::Seconds(3));
202   stop.Notify();
203 }
204 
205 }  // namespace
206 }  // namespace cord_internal
207 ABSL_NAMESPACE_END
208 }  // namespace absl
209