per_thread_sem_test.cc
Go to the documentation of this file.
1 // Copyright 2017 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 
16 
17 #include <atomic>
18 #include <condition_variable> // NOLINT(build/c++11)
19 #include <functional>
20 #include <limits>
21 #include <mutex> // NOLINT(build/c++11)
22 #include <string>
23 #include <thread> // NOLINT(build/c++11)
24 
25 #include "gtest/gtest.h"
28 #include "absl/strings/str_cat.h"
29 #include "absl/time/clock.h"
30 #include "absl/time/time.h"
31 
32 // In this test we explicitly avoid the use of synchronization
33 // primitives which might use PerThreadSem, most notably absl::Mutex.
34 
35 namespace absl {
36 namespace synchronization_internal {
37 
39  public:
41 
42  // Decrements (locks) the semaphore. If the semaphore's value is
43  // greater than zero, then the decrement proceeds, and the function
44  // returns, immediately. If the semaphore currently has the value
45  // zero, then the call blocks until it becomes possible to perform
46  // the decrement.
47  void Wait() {
48  std::unique_lock<std::mutex> lock(mu_);
49  cv_.wait(lock, [this]() { return count_ > 0; });
50  --count_;
51  cv_.notify_one();
52  }
53 
54  // Increments (unlocks) the semaphore. If the semaphore's value
55  // consequently becomes greater than zero, then another thread
56  // blocked Wait() call will be woken up and proceed to lock the
57  // semaphore.
58  void Post() {
59  std::lock_guard<std::mutex> lock(mu_);
60  ++count_;
61  cv_.notify_one();
62  }
63 
64  private:
65  std::mutex mu_;
66  std::condition_variable cv_;
67  int count_;
68 };
69 
70 struct ThreadData {
71  int num_iterations; // Number of replies to send.
72  SimpleSemaphore identity2_written; // Posted by thread writing identity2.
76 };
77 
78 // Need friendship with PerThreadSem.
79 class PerThreadSemTest : public testing::Test {
80  public:
81  static void TimingThread(ThreadData* t) {
84  while (t->num_iterations--) {
85  Wait(t->timeout);
86  Post(t->identity1);
87  }
88  }
89 
90  void TestTiming(const char *msg, bool timeout) {
91  static const int kNumIterations = 100;
92  ThreadData t;
93  t.num_iterations = kNumIterations;
94  t.timeout = timeout ?
95  KernelTimeout(absl::Now() + absl::Seconds(10000)) // far in the future
98 
99  // We can't use the Thread class here because it uses the Mutex
100  // class which will invoke PerThreadSem, so we use std::thread instead.
101  std::thread partner_thread(std::bind(TimingThread, &t));
102 
103  // Wait for our partner thread to register their identity.
105 
106  int64_t min_cycles = std::numeric_limits<int64_t>::max();
107  int64_t total_cycles = 0;
108  for (int i = 0; i < kNumIterations; ++i) {
110  int64_t cycles = base_internal::CycleClock::Now();
111  Post(t.identity2);
112  Wait(t.timeout);
113  cycles = base_internal::CycleClock::Now() - cycles;
114  min_cycles = std::min(min_cycles, cycles);
115  total_cycles += cycles;
116  }
117  std::string out = StrCat(
118  msg, "min cycle count=", min_cycles, " avg cycle count=",
119  absl::SixDigits(static_cast<double>(total_cycles) / kNumIterations));
120  printf("%s\n", out.c_str());
121 
122  partner_thread.join();
123  }
124 
125  protected:
127  PerThreadSem::Post(id);
128  }
129  static bool Wait(KernelTimeout t) {
130  return PerThreadSem::Wait(t);
131  }
132 
133  // convenience overload
134  static bool Wait(absl::Time t) {
135  return Wait(KernelTimeout(t));
136  }
137 
138  static void Tick(base_internal::ThreadIdentity *identity) {
139  PerThreadSem::Tick(identity);
140  }
141 };
142 
143 namespace {
144 
145 TEST_F(PerThreadSemTest, WithoutTimeout) {
146  PerThreadSemTest::TestTiming("Without timeout: ", false);
147 }
148 
149 TEST_F(PerThreadSemTest, WithTimeout) {
150  PerThreadSemTest::TestTiming("With timeout: ", true);
151 }
152 
153 TEST_F(PerThreadSemTest, Timeouts) {
154  const absl::Duration delay = absl::Milliseconds(50);
155  const absl::Time start = absl::Now();
156  EXPECT_FALSE(Wait(start + delay));
157  const absl::Duration elapsed = absl::Now() - start;
158  // Allow for a slight early return, to account for quality of implementation
159  // issues on various platforms.
160  const absl::Duration slop = absl::Microseconds(200);
161  EXPECT_LE(delay - slop, elapsed)
162  << "Wait returned " << delay - elapsed
163  << " early (with " << slop << " slop), start time was " << start;
164 
165  absl::Time negative_timeout = absl::UnixEpoch() - absl::Milliseconds(100);
166  EXPECT_FALSE(Wait(negative_timeout));
167  EXPECT_LE(negative_timeout, absl::Now() + slop); // trivially true :)
168 
170  // The wait here has an expired timeout, but we have a wake to consume,
171  // so this should succeed
172  EXPECT_TRUE(Wait(negative_timeout));
173 }
174 
175 } // namespace
176 
177 } // namespace synchronization_internal
178 } // namespace absl
void SleepFor(absl::Duration duration)
Definition: clock.h:68
Time Now()
Definition: clock.cc:37
void TestTiming(const char *msg, bool timeout)
std::string StrCat(const AlphaNum &a, const AlphaNum &b)
Definition: str_cat.cc:98
base_internal::ThreadIdentity * GetOrCreateCurrentThreadIdentity()
static void Post(base_internal::ThreadIdentity *identity)
constexpr Duration Microseconds(int64_t n)
Definition: time.h:1455
Definition: algorithm.h:29
constexpr Duration Milliseconds(int64_t n)
Definition: time.h:1458
base_internal::ThreadIdentity * identity2
strings_internal::AlphaNumBuffer< numbers_internal::kSixDigitsToBufferSize > SixDigits(double d)
Definition: str_cat.h:395
base_internal::ThreadIdentity * identity1
constexpr Time UnixEpoch()
Definition: time.h:686
static void Tick(base_internal::ThreadIdentity *identity)
static void Tick(base_internal::ThreadIdentity *identity)
TEST_F(GraphCyclesTest, NoCycle)
const char * msg
Definition: mutex.cc:254
constexpr Duration Seconds(int64_t n)
Definition: time.h:1461
char * out
Definition: mutex.h:1013
static void Post(base_internal::ThreadIdentity *id)


abseil_cpp
Author(s):
autogenerated on Mon Feb 28 2022 21:31:19