notification_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/notification.h"
00016 
00017 #include <thread>  // NOLINT(build/c++11)
00018 #include <vector>
00019 
00020 #include "gtest/gtest.h"
00021 #include "absl/synchronization/mutex.h"
00022 
00023 namespace absl {
00024 
00025 // A thread-safe class that holds a counter.
00026 class ThreadSafeCounter {
00027  public:
00028   ThreadSafeCounter() : count_(0) {}
00029 
00030   void Increment() {
00031     MutexLock lock(&mutex_);
00032     ++count_;
00033   }
00034 
00035   int Get() const {
00036     MutexLock lock(&mutex_);
00037     return count_;
00038   }
00039 
00040   void WaitUntilGreaterOrEqual(int n) {
00041     MutexLock lock(&mutex_);
00042     auto cond = [this, n]() { return count_ >= n; };
00043     mutex_.Await(Condition(&cond));
00044   }
00045 
00046  private:
00047   mutable Mutex mutex_;
00048   int count_;
00049 };
00050 
00051 // Runs the |i|'th worker thread for the tests in BasicTests().  Increments the
00052 // |ready_counter|, waits on the |notification|, and then increments the
00053 // |done_counter|.
00054 static void RunWorker(int i, ThreadSafeCounter* ready_counter,
00055                       Notification* notification,
00056                       ThreadSafeCounter* done_counter) {
00057   ready_counter->Increment();
00058   notification->WaitForNotification();
00059   done_counter->Increment();
00060 }
00061 
00062 // Tests that the |notification| properly blocks and awakens threads.  Assumes
00063 // that the |notification| is not yet triggered.  If |notify_before_waiting| is
00064 // true, the |notification| is triggered before any threads are created, so the
00065 // threads never block in WaitForNotification().  Otherwise, the |notification|
00066 // is triggered at a later point when most threads are likely to be blocking in
00067 // WaitForNotification().
00068 static void BasicTests(bool notify_before_waiting, Notification* notification) {
00069   EXPECT_FALSE(notification->HasBeenNotified());
00070   EXPECT_FALSE(
00071       notification->WaitForNotificationWithTimeout(absl::Milliseconds(0)));
00072   EXPECT_FALSE(notification->WaitForNotificationWithDeadline(absl::Now()));
00073 
00074   const absl::Duration delay = absl::Milliseconds(50);
00075   const absl::Time start = absl::Now();
00076   EXPECT_FALSE(notification->WaitForNotificationWithTimeout(delay));
00077   const absl::Duration elapsed = absl::Now() - start;
00078 
00079   // Allow for a slight early return, to account for quality of implementation
00080   // issues on various platforms.
00081   const absl::Duration slop = absl::Microseconds(200);
00082   EXPECT_LE(delay - slop, elapsed)
00083       << "WaitForNotificationWithTimeout returned " << delay - elapsed
00084       << " early (with " << slop << " slop), start time was " << start;
00085 
00086   ThreadSafeCounter ready_counter;
00087   ThreadSafeCounter done_counter;
00088 
00089   if (notify_before_waiting) {
00090     notification->Notify();
00091   }
00092 
00093   // Create a bunch of threads that increment the |done_counter| after being
00094   // notified.
00095   const int kNumThreads = 10;
00096   std::vector<std::thread> workers;
00097   for (int i = 0; i < kNumThreads; ++i) {
00098     workers.push_back(std::thread(&RunWorker, i, &ready_counter, notification,
00099                                   &done_counter));
00100   }
00101 
00102   if (!notify_before_waiting) {
00103     ready_counter.WaitUntilGreaterOrEqual(kNumThreads);
00104 
00105     // Workers have not been notified yet, so the |done_counter| should be
00106     // unmodified.
00107     EXPECT_EQ(0, done_counter.Get());
00108 
00109     notification->Notify();
00110   }
00111 
00112   // After notifying and then joining the workers, both counters should be
00113   // fully incremented.
00114   notification->WaitForNotification();  // should exit immediately
00115   EXPECT_TRUE(notification->HasBeenNotified());
00116   EXPECT_TRUE(notification->WaitForNotificationWithTimeout(absl::Seconds(0)));
00117   EXPECT_TRUE(notification->WaitForNotificationWithDeadline(absl::Now()));
00118   for (std::thread& worker : workers) {
00119     worker.join();
00120   }
00121   EXPECT_EQ(kNumThreads, ready_counter.Get());
00122   EXPECT_EQ(kNumThreads, done_counter.Get());
00123 }
00124 
00125 TEST(NotificationTest, SanityTest) {
00126   Notification local_notification1, local_notification2;
00127   BasicTests(false, &local_notification1);
00128   BasicTests(true, &local_notification2);
00129 }
00130 
00131 }  // namespace absl


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