tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

notification_test.cc (7227B)


      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/notification.h"
     16 
     17 #include <thread>  // NOLINT(build/c++11)
     18 #include <tuple>
     19 #include <vector>
     20 
     21 #include "gtest/gtest.h"
     22 #include "absl/base/attributes.h"
     23 #include "absl/base/config.h"
     24 #include "absl/base/internal/tracing.h"
     25 #include "absl/synchronization/mutex.h"
     26 #include "absl/time/time.h"
     27 
     28 namespace absl {
     29 ABSL_NAMESPACE_BEGIN
     30 
     31 // A thread-safe class that holds a counter.
     32 class ThreadSafeCounter {
     33 public:
     34  ThreadSafeCounter() : count_(0) {}
     35 
     36  void Increment() {
     37    MutexLock lock(&mutex_);
     38    ++count_;
     39  }
     40 
     41  int Get() const {
     42    MutexLock lock(&mutex_);
     43    return count_;
     44  }
     45 
     46  void WaitUntilGreaterOrEqual(int n) {
     47    MutexLock lock(&mutex_);
     48    auto cond = [this, n]() { return count_ >= n; };
     49    mutex_.Await(Condition(&cond));
     50  }
     51 
     52 private:
     53  mutable Mutex mutex_;
     54  int count_;
     55 };
     56 
     57 // Runs the |i|'th worker thread for the tests in BasicTests().  Increments the
     58 // |ready_counter|, waits on the |notification|, and then increments the
     59 // |done_counter|.
     60 static void RunWorker(int i, ThreadSafeCounter* ready_counter,
     61                      Notification* notification,
     62                      ThreadSafeCounter* done_counter) {
     63  ready_counter->Increment();
     64  notification->WaitForNotification();
     65  done_counter->Increment();
     66 }
     67 
     68 // Tests that the |notification| properly blocks and awakens threads.  Assumes
     69 // that the |notification| is not yet triggered.  If |notify_before_waiting| is
     70 // true, the |notification| is triggered before any threads are created, so the
     71 // threads never block in WaitForNotification().  Otherwise, the |notification|
     72 // is triggered at a later point when most threads are likely to be blocking in
     73 // WaitForNotification().
     74 static void BasicTests(bool notify_before_waiting, Notification* notification) {
     75  EXPECT_FALSE(notification->HasBeenNotified());
     76  EXPECT_FALSE(
     77      notification->WaitForNotificationWithTimeout(absl::Milliseconds(0)));
     78  EXPECT_FALSE(notification->WaitForNotificationWithDeadline(absl::Now()));
     79 
     80  const absl::Duration delay = absl::Milliseconds(50);
     81  const absl::Time start = absl::Now();
     82  EXPECT_FALSE(notification->WaitForNotificationWithTimeout(delay));
     83  const absl::Duration elapsed = absl::Now() - start;
     84 
     85  // Allow for a slight early return, to account for quality of implementation
     86  // issues on various platforms.
     87  absl::Duration slop = absl::Milliseconds(5);
     88 #ifdef _MSC_VER
     89  // Avoid flakiness on MSVC.
     90  slop = absl::Milliseconds(15);
     91 #endif
     92  EXPECT_LE(delay - slop, elapsed)
     93      << "WaitForNotificationWithTimeout returned " << delay - elapsed
     94      << " early (with " << slop << " slop), start time was " << start;
     95 
     96  ThreadSafeCounter ready_counter;
     97  ThreadSafeCounter done_counter;
     98 
     99  if (notify_before_waiting) {
    100    notification->Notify();
    101  }
    102 
    103  // Create a bunch of threads that increment the |done_counter| after being
    104  // notified.
    105  const int kNumThreads = 10;
    106  std::vector<std::thread> workers;
    107  for (int i = 0; i < kNumThreads; ++i) {
    108    workers.push_back(std::thread(&RunWorker, i, &ready_counter, notification,
    109                                  &done_counter));
    110  }
    111 
    112  if (!notify_before_waiting) {
    113    ready_counter.WaitUntilGreaterOrEqual(kNumThreads);
    114 
    115    // Workers have not been notified yet, so the |done_counter| should be
    116    // unmodified.
    117    EXPECT_EQ(0, done_counter.Get());
    118 
    119    notification->Notify();
    120  }
    121 
    122  // After notifying and then joining the workers, both counters should be
    123  // fully incremented.
    124  notification->WaitForNotification();  // should exit immediately
    125  EXPECT_TRUE(notification->HasBeenNotified());
    126  EXPECT_TRUE(notification->WaitForNotificationWithTimeout(absl::Seconds(0)));
    127  EXPECT_TRUE(notification->WaitForNotificationWithDeadline(absl::Now()));
    128  for (std::thread& worker : workers) {
    129    worker.join();
    130  }
    131  EXPECT_EQ(kNumThreads, ready_counter.Get());
    132  EXPECT_EQ(kNumThreads, done_counter.Get());
    133 }
    134 
    135 TEST(NotificationTest, SanityTest) {
    136  Notification local_notification1, local_notification2;
    137  BasicTests(false, &local_notification1);
    138  BasicTests(true, &local_notification2);
    139 }
    140 
    141 #if ABSL_HAVE_ATTRIBUTE_WEAK
    142 
    143 namespace base_internal {
    144 
    145 namespace {
    146 
    147 using TraceRecord = std::tuple<const void*, ObjectKind>;
    148 
    149 thread_local TraceRecord tls_signal;
    150 thread_local TraceRecord tls_wait;
    151 thread_local TraceRecord tls_continue;
    152 thread_local TraceRecord tls_observed;
    153 
    154 }  // namespace
    155 
    156 // Strong extern "C" implementation.
    157 extern "C" {
    158 
    159 void ABSL_INTERNAL_C_SYMBOL(AbslInternalTraceWait)(const void* object,
    160                                                   ObjectKind kind) {
    161  tls_wait = {object, kind};
    162 }
    163 
    164 void ABSL_INTERNAL_C_SYMBOL(AbslInternalTraceContinue)(const void* object,
    165                                                       ObjectKind kind) {
    166  tls_continue = {object, kind};
    167 }
    168 
    169 void ABSL_INTERNAL_C_SYMBOL(AbslInternalTraceSignal)(const void* object,
    170                                                     ObjectKind kind) {
    171  tls_signal = {object, kind};
    172 }
    173 
    174 void ABSL_INTERNAL_C_SYMBOL(AbslInternalTraceObserved)(const void* object,
    175                                                       ObjectKind kind) {
    176  tls_observed = {object, kind};
    177 }
    178 
    179 }  // extern "C"
    180 
    181 TEST(NotificationTest, TracesNotify) {
    182  Notification n;
    183  tls_signal = {};
    184  n.Notify();
    185  EXPECT_EQ(tls_signal, TraceRecord(&n, ObjectKind::kNotification));
    186 }
    187 
    188 TEST(NotificationTest, TracesWaitForNotification) {
    189  Notification n;
    190  n.Notify();
    191  tls_wait = tls_continue = {};
    192  n.WaitForNotification();
    193  EXPECT_EQ(tls_wait, TraceRecord(&n, ObjectKind::kNotification));
    194  EXPECT_EQ(tls_continue, TraceRecord(&n, ObjectKind::kNotification));
    195 }
    196 
    197 TEST(NotificationTest, TracesWaitForNotificationWithTimeout) {
    198  Notification n;
    199 
    200  tls_wait = tls_continue = {};
    201  n.WaitForNotificationWithTimeout(absl::Milliseconds(1));
    202  EXPECT_EQ(tls_wait, TraceRecord(&n, ObjectKind::kNotification));
    203  EXPECT_EQ(tls_continue, TraceRecord(nullptr, ObjectKind::kNotification));
    204 
    205  n.Notify();
    206  tls_wait = tls_continue = {};
    207  n.WaitForNotificationWithTimeout(absl::Milliseconds(1));
    208  EXPECT_EQ(tls_wait, TraceRecord(&n, ObjectKind::kNotification));
    209  EXPECT_EQ(tls_continue, TraceRecord(&n, ObjectKind::kNotification));
    210 }
    211 
    212 TEST(NotificationTest, TracesHasBeenNotified) {
    213  Notification n;
    214 
    215  tls_observed = {};
    216  ASSERT_FALSE(n.HasBeenNotified());
    217  EXPECT_EQ(tls_observed, TraceRecord(nullptr, ObjectKind::kUnknown));
    218 
    219  n.Notify();
    220  tls_observed = {};
    221  ASSERT_TRUE(n.HasBeenNotified());
    222  EXPECT_EQ(tls_observed, TraceRecord(&n, ObjectKind::kNotification));
    223 }
    224 
    225 }  // namespace base_internal
    226 
    227 #endif  // ABSL_HAVE_ATTRIBUTE_WEAK
    228 
    229 ABSL_NAMESPACE_END
    230 }  // namespace absl