Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "absl/synchronization/internal/per_thread_sem.h"
00016
00017 #include <atomic>
00018 #include <condition_variable>
00019 #include <functional>
00020 #include <limits>
00021 #include <mutex>
00022 #include <string>
00023 #include <thread>
00024
00025 #include "gtest/gtest.h"
00026 #include "absl/base/internal/cycleclock.h"
00027 #include "absl/base/internal/thread_identity.h"
00028 #include "absl/strings/str_cat.h"
00029 #include "absl/time/clock.h"
00030 #include "absl/time/time.h"
00031
00032
00033
00034
00035 namespace absl {
00036 namespace synchronization_internal {
00037
00038 class SimpleSemaphore {
00039 public:
00040 SimpleSemaphore() : count_(0) {}
00041
00042
00043
00044
00045
00046
00047 void Wait() {
00048 std::unique_lock<std::mutex> lock(mu_);
00049 cv_.wait(lock, [this]() { return count_ > 0; });
00050 --count_;
00051 cv_.notify_one();
00052 }
00053
00054
00055
00056
00057
00058 void Post() {
00059 std::lock_guard<std::mutex> lock(mu_);
00060 ++count_;
00061 cv_.notify_one();
00062 }
00063
00064 private:
00065 std::mutex mu_;
00066 std::condition_variable cv_;
00067 int count_;
00068 };
00069
00070 struct ThreadData {
00071 int num_iterations;
00072 SimpleSemaphore identity2_written;
00073 base_internal::ThreadIdentity *identity1;
00074 base_internal::ThreadIdentity *identity2;
00075 KernelTimeout timeout;
00076 };
00077
00078
00079 class PerThreadSemTest : public testing::Test {
00080 public:
00081 static void TimingThread(ThreadData* t) {
00082 t->identity2 = GetOrCreateCurrentThreadIdentity();
00083 t->identity2_written.Post();
00084 while (t->num_iterations--) {
00085 Wait(t->timeout);
00086 Post(t->identity1);
00087 }
00088 }
00089
00090 void TestTiming(const char *msg, bool timeout) {
00091 static const int kNumIterations = 100;
00092 ThreadData t;
00093 t.num_iterations = kNumIterations;
00094 t.timeout = timeout ?
00095 KernelTimeout(absl::Now() + absl::Seconds(10000))
00096 : KernelTimeout::Never();
00097 t.identity1 = GetOrCreateCurrentThreadIdentity();
00098
00099
00100
00101 std::thread partner_thread(std::bind(TimingThread, &t));
00102
00103
00104 t.identity2_written.Wait();
00105
00106 int64_t min_cycles = std::numeric_limits<int64_t>::max();
00107 int64_t total_cycles = 0;
00108 for (int i = 0; i < kNumIterations; ++i) {
00109 absl::SleepFor(absl::Milliseconds(20));
00110 int64_t cycles = base_internal::CycleClock::Now();
00111 Post(t.identity2);
00112 Wait(t.timeout);
00113 cycles = base_internal::CycleClock::Now() - cycles;
00114 min_cycles = std::min(min_cycles, cycles);
00115 total_cycles += cycles;
00116 }
00117 std::string out = StrCat(
00118 msg, "min cycle count=", min_cycles, " avg cycle count=",
00119 absl::SixDigits(static_cast<double>(total_cycles) / kNumIterations));
00120 printf("%s\n", out.c_str());
00121
00122 partner_thread.join();
00123 }
00124
00125 protected:
00126 static void Post(base_internal::ThreadIdentity *id) {
00127 PerThreadSem::Post(id);
00128 }
00129 static bool Wait(KernelTimeout t) {
00130 return PerThreadSem::Wait(t);
00131 }
00132
00133
00134 static bool Wait(absl::Time t) {
00135 return Wait(KernelTimeout(t));
00136 }
00137
00138 static void Tick(base_internal::ThreadIdentity *identity) {
00139 PerThreadSem::Tick(identity);
00140 }
00141 };
00142
00143 namespace {
00144
00145 TEST_F(PerThreadSemTest, WithoutTimeout) {
00146 PerThreadSemTest::TestTiming("Without timeout: ", false);
00147 }
00148
00149 TEST_F(PerThreadSemTest, WithTimeout) {
00150 PerThreadSemTest::TestTiming("With timeout: ", true);
00151 }
00152
00153 TEST_F(PerThreadSemTest, Timeouts) {
00154 const absl::Duration delay = absl::Milliseconds(50);
00155 const absl::Time start = absl::Now();
00156 EXPECT_FALSE(Wait(start + delay));
00157 const absl::Duration elapsed = absl::Now() - start;
00158
00159
00160 const absl::Duration slop = absl::Microseconds(200);
00161 EXPECT_LE(delay - slop, elapsed)
00162 << "Wait returned " << delay - elapsed
00163 << " early (with " << slop << " slop), start time was " << start;
00164
00165 absl::Time negative_timeout = absl::UnixEpoch() - absl::Milliseconds(100);
00166 EXPECT_FALSE(Wait(negative_timeout));
00167 EXPECT_LE(negative_timeout, absl::Now() + slop);
00168
00169 Post(GetOrCreateCurrentThreadIdentity());
00170
00171
00172 EXPECT_TRUE(Wait(negative_timeout));
00173 }
00174
00175 }
00176
00177 }
00178 }