abseil-cpp/absl/synchronization/internal/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 
15 #include "absl/synchronization/internal/per_thread_sem.h"
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"
26 #include "absl/base/config.h"
27 #include "absl/base/internal/cycleclock.h"
28 #include "absl/base/internal/thread_identity.h"
29 #include "absl/strings/str_cat.h"
30 #include "absl/time/clock.h"
31 #include "absl/time/time.h"
32 
33 // In this test we explicitly avoid the use of synchronization
34 // primitives which might use PerThreadSem, most notably absl::Mutex.
35 
36 namespace absl {
38 namespace synchronization_internal {
39 
41  public:
43 
44  // Decrements (locks) the semaphore. If the semaphore's value is
45  // greater than zero, then the decrement proceeds, and the function
46  // returns, immediately. If the semaphore currently has the value
47  // zero, then the call blocks until it becomes possible to perform
48  // the decrement.
49  void Wait() {
50  std::unique_lock<std::mutex> lock(mu_);
51  cv_.wait(lock, [this]() { return count_ > 0; });
52  --count_;
53  cv_.notify_one();
54  }
55 
56  // Increments (unlocks) the semaphore. If the semaphore's value
57  // consequently becomes greater than zero, then another thread
58  // blocked Wait() call will be woken up and proceed to lock the
59  // semaphore.
60  void Post() {
61  std::lock_guard<std::mutex> lock(mu_);
62  ++count_;
63  cv_.notify_one();
64  }
65 
66  private:
68  std::condition_variable cv_;
69  int count_;
70 };
71 
72 struct ThreadData {
73  int num_iterations; // Number of replies to send.
74  SimpleSemaphore identity2_written; // Posted by thread writing identity2.
78 };
79 
80 // Need friendship with PerThreadSem.
82  public:
83  static void TimingThread(ThreadData* t) {
84  t->identity2 = GetOrCreateCurrentThreadIdentity();
85  t->identity2_written.Post();
86  while (t->num_iterations--) {
87  Wait(t->timeout);
88  Post(t->identity1);
89  }
90  }
91 
92  void TestTiming(const char *msg, bool timeout) {
93  static const int kNumIterations = 100;
94  ThreadData t;
95  t.num_iterations = kNumIterations;
96  t.timeout = timeout ?
97  KernelTimeout(absl::Now() + absl::Seconds(10000)) // far in the future
99  t.identity1 = GetOrCreateCurrentThreadIdentity();
100 
101  // We can't use the Thread class here because it uses the Mutex
102  // class which will invoke PerThreadSem, so we use std::thread instead.
103  std::thread partner_thread(std::bind(TimingThread, &t));
104 
105  // Wait for our partner thread to register their identity.
106  t.identity2_written.Wait();
107 
109  int64_t total_cycles = 0;
110  for (int i = 0; i < kNumIterations; ++i) {
113  Post(t.identity2);
114  Wait(t.timeout);
115  cycles = base_internal::CycleClock::Now() - cycles;
116  min_cycles = std::min(min_cycles, cycles);
117  total_cycles += cycles;
118  }
120  msg, "min cycle count=", min_cycles, " avg cycle count=",
121  absl::SixDigits(static_cast<double>(total_cycles) / kNumIterations));
122  printf("%s\n", out.c_str());
123 
124  partner_thread.join();
125  }
126 
127  protected:
129  PerThreadSem::Post(id);
130  }
131  static bool Wait(KernelTimeout t) {
132  return PerThreadSem::Wait(t);
133  }
134 
135  // convenience overload
136  static bool Wait(absl::Time t) {
137  return Wait(KernelTimeout(t));
138  }
139 
140  static void Tick(base_internal::ThreadIdentity *identity) {
141  PerThreadSem::Tick(identity);
142  }
143 };
144 
145 namespace {
146 
147 TEST_F(PerThreadSemTest, WithoutTimeout) {
148  PerThreadSemTest::TestTiming("Without timeout: ", false);
149 }
150 
151 TEST_F(PerThreadSemTest, WithTimeout) {
152  PerThreadSemTest::TestTiming("With timeout: ", true);
153 }
154 
155 TEST_F(PerThreadSemTest, Timeouts) {
156  const absl::Duration delay = absl::Milliseconds(50);
157  const absl::Time start = absl::Now();
158  EXPECT_FALSE(Wait(start + delay));
159  const absl::Duration elapsed = absl::Now() - start;
160  // Allow for a slight early return, to account for quality of implementation
161  // issues on various platforms.
162  const absl::Duration slop = absl::Milliseconds(1);
163  EXPECT_LE(delay - slop, elapsed)
164  << "Wait returned " << delay - elapsed
165  << " early (with " << slop << " slop), start time was " << start;
166 
167  absl::Time negative_timeout = absl::UnixEpoch() - absl::Milliseconds(100);
168  EXPECT_FALSE(Wait(negative_timeout));
169  EXPECT_LE(negative_timeout, absl::Now() + slop); // trivially true :)
170 
172  // The wait here has an expired timeout, but we have a wake to consume,
173  // so this should succeed
174  EXPECT_TRUE(Wait(negative_timeout));
175 }
176 
177 TEST_F(PerThreadSemTest, ThreadIdentityReuse) {
178  // Create a base_internal::ThreadIdentity object and keep reusing it. There
179  // should be no memory or resource leaks.
180  for (int i = 0; i < 10000; i++) {
182  t.join();
183  }
184 }
185 
186 } // namespace
187 
188 } // namespace synchronization_internal
190 } // namespace absl
EXPECT_FALSE
#define EXPECT_FALSE(condition)
Definition: bloaty/third_party/googletest/googletest/include/gtest/gtest.h:1970
absl::synchronization_internal::GetOrCreateCurrentThreadIdentity
base_internal::ThreadIdentity * GetOrCreateCurrentThreadIdentity()
Definition: abseil-cpp/absl/synchronization/internal/create_thread_identity.h:43
absl::synchronization_internal::ThreadData::timeout
KernelTimeout timeout
Definition: abseil-cpp/absl/synchronization/internal/per_thread_sem_test.cc:77
absl::synchronization_internal::ThreadData::identity2_written
SimpleSemaphore identity2_written
Definition: abseil-cpp/absl/synchronization/internal/per_thread_sem_test.cc:74
absl::base_internal::ThreadIdentity
Definition: abseil-cpp/absl/base/internal/thread_identity.h:137
absl::StrCat
std::string StrCat(const AlphaNum &a, const AlphaNum &b)
Definition: abseil-cpp/absl/strings/str_cat.cc:98
absl::Time
Definition: third_party/abseil-cpp/absl/time/time.h:642
mutex
static uv_mutex_t mutex
Definition: threadpool.c:34
printf
_Use_decl_annotations_ int __cdecl printf(const char *_Format,...)
Definition: cs_driver.c:91
testing::internal::string
::std::string string
Definition: bloaty/third_party/protobuf/third_party/googletest/googletest/include/gtest/internal/gtest-port.h:881
absl::synchronization_internal::SimpleSemaphore::cv_
std::condition_variable cv_
Definition: abseil-cpp/absl/synchronization/internal/per_thread_sem_test.cc:68
absl::synchronization_internal::PerThreadSem::Tick
static void Tick(base_internal::ThreadIdentity *identity)
Definition: abseil-cpp/absl/synchronization/internal/per_thread_sem.cc:50
EXPECT_LE
#define EXPECT_LE(val1, val2)
Definition: bloaty/third_party/googletest/googletest/include/gtest/gtest.h:2030
absl::SleepFor
void SleepFor(absl::Duration duration)
Definition: abseil-cpp/absl/time/clock.h:70
ABSL_NAMESPACE_END
#define ABSL_NAMESPACE_END
Definition: third_party/abseil-cpp/absl/base/config.h:171
testing::Test
Definition: bloaty/third_party/googletest/googletest/include/gtest/gtest.h:402
absl::synchronization_internal::SimpleSemaphore
Definition: abseil-cpp/absl/synchronization/internal/per_thread_sem_test.cc:40
absl::Milliseconds
constexpr Duration Milliseconds(T n)
Definition: third_party/abseil-cpp/absl/time/time.h:415
start
static uint64_t start
Definition: benchmark-pound.c:74
ABSL_NAMESPACE_BEGIN
#define ABSL_NAMESPACE_BEGIN
Definition: third_party/abseil-cpp/absl/base/config.h:170
absl::msg
const char * msg
Definition: abseil-cpp/absl/synchronization/mutex.cc:272
absl::synchronization_internal::SimpleSemaphore::SimpleSemaphore
SimpleSemaphore()
Definition: abseil-cpp/absl/synchronization/internal/per_thread_sem_test.cc:42
int64_t
signed __int64 int64_t
Definition: stdint-msvc2008.h:89
max
int max
Definition: bloaty/third_party/zlib/examples/enough.c:170
absl::synchronization_internal::TEST_F
TEST_F(GraphCyclesTest, NoCycle)
Definition: abseil-cpp/absl/synchronization/internal/graphcycles_test.cc:406
absl::synchronization_internal::PerThreadSemTest::Wait
static bool Wait(absl::Time t)
Definition: abseil-cpp/absl/synchronization/internal/per_thread_sem_test.cc:136
absl::synchronization_internal::SimpleSemaphore::Post
void Post()
Definition: abseil-cpp/absl/synchronization/internal/per_thread_sem_test.cc:60
absl::Duration
Definition: third_party/abseil-cpp/absl/time/time.h:159
absl::synchronization_internal::PerThreadSemTest::Tick
static void Tick(base_internal::ThreadIdentity *identity)
Definition: abseil-cpp/absl/synchronization/internal/per_thread_sem_test.cc:140
absl::synchronization_internal::KernelTimeout
Definition: abseil-cpp/absl/synchronization/internal/kernel_timeout.h:44
absl::synchronization_internal::PerThreadSemTest::Wait
static bool Wait(KernelTimeout t)
Definition: abseil-cpp/absl/synchronization/internal/per_thread_sem_test.cc:131
absl::base_internal::CycleClock::Now
static int64_t Now()
Definition: abseil-cpp/absl/base/internal/cycleclock.cc:63
absl::UnixEpoch
constexpr Time UnixEpoch()
Definition: third_party/abseil-cpp/absl/time/time.h:744
min
#define min(a, b)
Definition: qsort.h:83
absl::synchronization_internal::PerThreadSemTest
Definition: abseil-cpp/absl/synchronization/internal/per_thread_sem_test.cc:81
absl::synchronization_internal::PerThreadSemTest::Post
static void Post(base_internal::ThreadIdentity *id)
Definition: abseil-cpp/absl/synchronization/internal/per_thread_sem_test.cc:128
absl::SixDigits
strings_internal::AlphaNumBuffer< numbers_internal::kSixDigitsToBufferSize > SixDigits(double d)
Definition: abseil-cpp/absl/strings/str_cat.h:405
absl::synchronization_internal::ThreadData
Definition: abseil-cpp/absl/synchronization/internal/per_thread_sem_test.cc:72
absl::Seconds
constexpr Duration Seconds(T n)
Definition: third_party/abseil-cpp/absl/time/time.h:419
absl::Now
ABSL_NAMESPACE_BEGIN Time Now()
Definition: abseil-cpp/absl/time/clock.cc:39
absl::synchronization_internal::PerThreadSem::Wait
static bool Wait(KernelTimeout t)
Definition: abseil-cpp/absl/synchronization/internal/per_thread_sem.h:105
absl::synchronization_internal::ThreadData::num_iterations
int num_iterations
Definition: abseil-cpp/absl/synchronization/internal/per_thread_sem_test.cc:73
absl::str_format_internal::LengthMod::t
@ t
absl::synchronization_internal::KernelTimeout::Never
static KernelTimeout Never()
Definition: abseil-cpp/absl/synchronization/internal/kernel_timeout.h:54
absl::synchronization_internal::SimpleSemaphore::mu_
std::mutex mu_
Definition: abseil-cpp/absl/synchronization/internal/per_thread_sem_test.cc:67
absl::synchronization_internal::SimpleSemaphore::Wait
void Wait()
Definition: abseil-cpp/absl/synchronization/internal/per_thread_sem_test.cc:49
EXPECT_TRUE
#define EXPECT_TRUE(condition)
Definition: bloaty/third_party/googletest/googletest/include/gtest/gtest.h:1967
absl::synchronization_internal::PerThreadSem::Post
static void Post(base_internal::ThreadIdentity *identity)
Definition: abseil-cpp/absl/synchronization/internal/per_thread_sem.h:100
absl::synchronization_internal::SimpleSemaphore::count_
int count_
Definition: abseil-cpp/absl/synchronization/internal/per_thread_sem_test.cc:69
absl
Definition: abseil-cpp/absl/algorithm/algorithm.h:31
absl::synchronization_internal::PerThreadSemTest::TimingThread
static void TimingThread(ThreadData *t)
Definition: abseil-cpp/absl/synchronization/internal/per_thread_sem_test.cc:83
absl::out
char * out
Definition: abseil-cpp/absl/synchronization/mutex.h:1048
absl::synchronization_internal::ThreadData::identity2
base_internal::ThreadIdentity * identity2
Definition: abseil-cpp/absl/synchronization/internal/per_thread_sem_test.cc:76
absl::synchronization_internal::PerThreadSemTest::TestTiming
void TestTiming(const char *msg, bool timeout)
Definition: abseil-cpp/absl/synchronization/internal/per_thread_sem_test.cc:92
absl::synchronization_internal::ThreadData::identity1
base_internal::ThreadIdentity * identity1
Definition: abseil-cpp/absl/synchronization/internal/per_thread_sem_test.cc:75
thread
static uv_thread_t thread
Definition: test-async-null-cb.c:29
timeout
uv_timer_t timeout
Definition: libuv/docs/code/uvwget/main.c:9
i
uint64_t i
Definition: abseil-cpp/absl/container/btree_benchmark.cc:230


grpc
Author(s):
autogenerated on Fri May 16 2025 02:59:41