blob: 059d4cd2521deadafdea6f17e88762b6c93c5cb2 [file] [log] [blame]
Austin Schuh36244a12019-09-21 17:52:38 -07001// 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 <vector>
19
20#include "gtest/gtest.h"
21#include "absl/synchronization/mutex.h"
22
23namespace absl {
24
25// A thread-safe class that holds a counter.
26class ThreadSafeCounter {
27 public:
28 ThreadSafeCounter() : count_(0) {}
29
30 void Increment() {
31 MutexLock lock(&mutex_);
32 ++count_;
33 }
34
35 int Get() const {
36 MutexLock lock(&mutex_);
37 return count_;
38 }
39
40 void WaitUntilGreaterOrEqual(int n) {
41 MutexLock lock(&mutex_);
42 auto cond = [this, n]() { return count_ >= n; };
43 mutex_.Await(Condition(&cond));
44 }
45
46 private:
47 mutable Mutex mutex_;
48 int count_;
49};
50
51// Runs the |i|'th worker thread for the tests in BasicTests(). Increments the
52// |ready_counter|, waits on the |notification|, and then increments the
53// |done_counter|.
54static void RunWorker(int i, ThreadSafeCounter* ready_counter,
55 Notification* notification,
56 ThreadSafeCounter* done_counter) {
57 ready_counter->Increment();
58 notification->WaitForNotification();
59 done_counter->Increment();
60}
61
62// Tests that the |notification| properly blocks and awakens threads. Assumes
63// that the |notification| is not yet triggered. If |notify_before_waiting| is
64// true, the |notification| is triggered before any threads are created, so the
65// threads never block in WaitForNotification(). Otherwise, the |notification|
66// is triggered at a later point when most threads are likely to be blocking in
67// WaitForNotification().
68static void BasicTests(bool notify_before_waiting, Notification* notification) {
69 EXPECT_FALSE(notification->HasBeenNotified());
70 EXPECT_FALSE(
71 notification->WaitForNotificationWithTimeout(absl::Milliseconds(0)));
72 EXPECT_FALSE(notification->WaitForNotificationWithDeadline(absl::Now()));
73
74 const absl::Duration delay = absl::Milliseconds(50);
75 const absl::Time start = absl::Now();
76 EXPECT_FALSE(notification->WaitForNotificationWithTimeout(delay));
77 const absl::Duration elapsed = absl::Now() - start;
78
79 // Allow for a slight early return, to account for quality of implementation
80 // issues on various platforms.
81 const absl::Duration slop = absl::Microseconds(200);
82 EXPECT_LE(delay - slop, elapsed)
83 << "WaitForNotificationWithTimeout returned " << delay - elapsed
84 << " early (with " << slop << " slop), start time was " << start;
85
86 ThreadSafeCounter ready_counter;
87 ThreadSafeCounter done_counter;
88
89 if (notify_before_waiting) {
90 notification->Notify();
91 }
92
93 // Create a bunch of threads that increment the |done_counter| after being
94 // notified.
95 const int kNumThreads = 10;
96 std::vector<std::thread> workers;
97 for (int i = 0; i < kNumThreads; ++i) {
98 workers.push_back(std::thread(&RunWorker, i, &ready_counter, notification,
99 &done_counter));
100 }
101
102 if (!notify_before_waiting) {
103 ready_counter.WaitUntilGreaterOrEqual(kNumThreads);
104
105 // Workers have not been notified yet, so the |done_counter| should be
106 // unmodified.
107 EXPECT_EQ(0, done_counter.Get());
108
109 notification->Notify();
110 }
111
112 // After notifying and then joining the workers, both counters should be
113 // fully incremented.
114 notification->WaitForNotification(); // should exit immediately
115 EXPECT_TRUE(notification->HasBeenNotified());
116 EXPECT_TRUE(notification->WaitForNotificationWithTimeout(absl::Seconds(0)));
117 EXPECT_TRUE(notification->WaitForNotificationWithDeadline(absl::Now()));
118 for (std::thread& worker : workers) {
119 worker.join();
120 }
121 EXPECT_EQ(kNumThreads, ready_counter.Get());
122 EXPECT_EQ(kNumThreads, done_counter.Get());
123}
124
125TEST(NotificationTest, SanityTest) {
126 Notification local_notification1, local_notification2;
127 BasicTests(false, &local_notification1);
128 BasicTests(true, &local_notification2);
129}
130
131} // namespace absl