per_thread_sem_test.cc
Go to the documentation of this file.
00001 // Copyright 2017 The Abseil Authors.
00002 //
00003 // Licensed under the Apache License, Version 2.0 (the "License");
00004 // you may not use this file except in compliance with the License.
00005 // You may obtain a copy of the License at
00006 //
00007 //      https://www.apache.org/licenses/LICENSE-2.0
00008 //
00009 // Unless required by applicable law or agreed to in writing, software
00010 // distributed under the License is distributed on an "AS IS" BASIS,
00011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00012 // See the License for the specific language governing permissions and
00013 // limitations under the License.
00014 
00015 #include "absl/synchronization/internal/per_thread_sem.h"
00016 
00017 #include <atomic>
00018 #include <condition_variable>  // NOLINT(build/c++11)
00019 #include <functional>
00020 #include <limits>
00021 #include <mutex>               // NOLINT(build/c++11)
00022 #include <string>
00023 #include <thread>              // NOLINT(build/c++11)
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 // In this test we explicitly avoid the use of synchronization
00033 // primitives which might use PerThreadSem, most notably absl::Mutex.
00034 
00035 namespace absl {
00036 namespace synchronization_internal {
00037 
00038 class SimpleSemaphore {
00039  public:
00040   SimpleSemaphore() : count_(0) {}
00041 
00042   // Decrements (locks) the semaphore. If the semaphore's value is
00043   // greater than zero, then the decrement proceeds, and the function
00044   // returns, immediately. If the semaphore currently has the value
00045   // zero, then the call blocks until it becomes possible to perform
00046   // the decrement.
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   // Increments (unlocks) the semaphore. If the semaphore's value
00055   // consequently becomes greater than zero, then another thread
00056   // blocked Wait() call will be woken up and proceed to lock the
00057   // semaphore.
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;                 // Number of replies to send.
00072   SimpleSemaphore identity2_written;  // Posted by thread writing identity2.
00073   base_internal::ThreadIdentity *identity1;  // First Post()-er.
00074   base_internal::ThreadIdentity *identity2;  // First Wait()-er.
00075   KernelTimeout timeout;
00076 };
00077 
00078 // Need friendship with PerThreadSem.
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))  // far in the future
00096         : KernelTimeout::Never();
00097     t.identity1 = GetOrCreateCurrentThreadIdentity();
00098 
00099     // We can't use the Thread class here because it uses the Mutex
00100     // class which will invoke PerThreadSem, so we use std::thread instead.
00101     std::thread partner_thread(std::bind(TimingThread, &t));
00102 
00103     // Wait for our partner thread to register their identity.
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   // convenience overload
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   // Allow for a slight early return, to account for quality of implementation
00159   // issues on various platforms.
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);  // trivially true :)
00168 
00169   Post(GetOrCreateCurrentThreadIdentity());
00170   // The wait here has an expired timeout, but we have a wake to consume,
00171   // so this should succeed
00172   EXPECT_TRUE(Wait(negative_timeout));
00173 }
00174 
00175 }  // namespace
00176 
00177 }  // namespace synchronization_internal
00178 }  // namespace absl


abseil_cpp
Author(s):
autogenerated on Wed Jun 19 2019 19:42:15