blob: 5bf76f6727ac295ea2216893f5b94ededaa74cea [file] [log] [blame]
Kyle Qiana01e9302018-08-25 00:07:421// Copyright (c) 2018 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chromeos/components/nearby/condition_variable_impl.h"
6
7#include <memory>
8
Sebastien Marchand6d0558fd2019-01-25 16:49:379#include "base/bind.h"
Kyle Qiana01e9302018-08-25 00:07:4210#include "base/single_thread_task_runner.h"
11#include "base/task/post_task.h"
12#include "base/test/gtest_util.h"
13#include "base/test/scoped_task_environment.h"
14#include "base/test/test_timeouts.h"
15#include "base/threading/platform_thread.h"
16#include "base/threading/thread_restrictions.h"
17#include "chromeos/components/nearby/lock_base.h"
18#include "testing/gtest/include/gtest/gtest.h"
19
20namespace chromeos {
21
22namespace nearby {
23
24namespace {
25
26class FakeLock : public LockBase {
27 public:
28 FakeLock() : condition_variable_(&lock_) {}
29 ~FakeLock() override = default;
30
31 // location::nearby::Lock:
32 void lock() override {
33 base::AutoLock al(lock_);
34 ++num_locks_;
35 condition_variable_.Signal();
36 }
37
38 void unlock() override {
39 base::AutoLock al(lock_);
40 ++num_unlocks_;
41 condition_variable_.Signal();
42 }
43
44 // chromeos::nearby::LockBase:
45 bool IsHeldByCurrentThread() override { return is_held_by_current_thread_; }
46
47 void set_is_held_by_current_thread(bool value) {
48 is_held_by_current_thread_ = value;
49 }
50
51 int num_locks() {
52 base::AutoLock al(lock_);
53 return num_locks_;
54 }
55
56 int num_unlocks() {
57 base::AutoLock al(lock_);
58 return num_unlocks_;
59 }
60
61 void WaitForLocksAndUnlocks(int expected_num_locks,
62 int expected_num_unlocks) {
63 base::AutoLock al(lock_);
64 while (expected_num_locks != num_locks_ ||
65 expected_num_unlocks != num_unlocks_) {
66 condition_variable_.Wait();
67 }
68 }
69
70 private:
71 base::Lock lock_;
72 base::ConditionVariable condition_variable_;
73 bool is_held_by_current_thread_ = false;
74 int num_locks_ = 0;
75 int num_unlocks_ = 0;
76
77 DISALLOW_COPY_AND_ASSIGN(FakeLock);
78};
79
80} // namespace
81
82class ConditionVariableImplTest : public testing::Test {
83 protected:
84 ConditionVariableImplTest()
85 : fake_lock_(std::make_unique<FakeLock>()),
86 condition_variable_(
87 std::make_unique<ConditionVariableImpl>(fake_lock_.get())) {}
88
89 // testing::Test
90 void TearDown() override {
Gabriel Charette694c3c332019-08-19 14:53:0591 task_environment_.RunUntilIdle();
Kyle Qiana01e9302018-08-25 00:07:4292 EXPECT_EQ(fake_lock_->num_locks(), fake_lock_->num_unlocks());
93 }
94
95 location::nearby::ConditionVariable* condition_variable() {
96 return condition_variable_.get();
97 }
98
99 FakeLock* fake_lock() { return fake_lock_.get(); }
100
101 void WaitOnConditionVariableFromParallelSequence(bool should_succeed) {
102 base::PostTask(
103 FROM_HERE,
104 base::BindOnce(&ConditionVariableImplTest::WaitOnConditionVariable,
105 base::Unretained(this), should_succeed));
106 }
107
108 // Invoked whenever attempting to verify that a parallel task has indeed
109 // blocked, since there's no way to deterministically find out if that task
110 // will ever unblock.
111 void TinyTimeout() {
112 base::PlatformThread::Sleep(TestTimeouts::tiny_timeout());
113 }
114
115 void VerifyNumLocksAndUnlocks(int expected_num_locks,
116 int expected_num_unlocks) {
117 EXPECT_EQ(expected_num_locks, fake_lock_->num_locks());
118 EXPECT_EQ(expected_num_unlocks, fake_lock_->num_unlocks());
119 }
120
121 // To ensure that |condition_variable_| is blocking as expected, wait until
122 // its |fake_lock_| has been locked and unlocked once per blocked sequence.
123 // Then manually wait, and verify afterwards that the number of locks and
124 // unlocks remain unchanged.
125 void VerifyBlockedConditionVariable(int expected_num_blocked_sequences) {
126 fake_lock()->WaitForLocksAndUnlocks(
127 expected_num_blocked_sequences /* expected_num_locks */,
128 expected_num_blocked_sequences /* expected_num_unlocks */);
129 TinyTimeout();
130 VerifyNumLocksAndUnlocks(
131 expected_num_blocked_sequences /* expected_num_locks */,
132 expected_num_blocked_sequences /* expected_num_unlocks */);
133 }
134
Gabriel Charette694c3c332019-08-19 14:53:05135 base::test::TaskEnvironment task_environment_;
Kyle Qiana01e9302018-08-25 00:07:42136
137 private:
138 void WaitOnConditionVariable(bool should_succeed) {
139 const base::ScopedAllowBaseSyncPrimitivesForTesting allow_sync_primitives;
140
141 // ConditionVariable::wait() should only be called by a thread that already
142 // owns the associated Lock. This is not tested as the expected behavior is
143 // either undefined or specified by the particular Lock implementation.
144 fake_lock_->lock();
145
146 if (should_succeed)
147 condition_variable_->wait();
148 else
149 EXPECT_DCHECK_DEATH(condition_variable_->wait());
150
151 // ConditionVariable::wait() will call Lock::lock() after unblocking, so
152 // this undoes that.
153 fake_lock_->unlock();
154 }
155
156 std::unique_ptr<FakeLock> fake_lock_;
157 std::unique_ptr<location::nearby::ConditionVariable> condition_variable_;
158
159 DISALLOW_COPY_AND_ASSIGN(ConditionVariableImplTest);
160};
161
162TEST_F(ConditionVariableImplTest,
163 SingleSequence_BlocksOnWaitAndUnblocksOnNotify) {
164 WaitOnConditionVariableFromParallelSequence(true /* should_succeed */);
165 VerifyBlockedConditionVariable(1 /* expected_num_blocked_sequences */);
166
167 // Should unblock after notify().
168 condition_variable()->notify();
Gabriel Charette694c3c332019-08-19 14:53:05169 task_environment_.RunUntilIdle();
Kyle Qiana01e9302018-08-25 00:07:42170 VerifyNumLocksAndUnlocks(2 /* expected_num_locks */,
171 2 /* expected_num_unlocks */);
172}
173
174TEST_F(ConditionVariableImplTest,
175 MultipleSequences_BlocksOnWaitAndUnblocksOnNotify) {
176 WaitOnConditionVariableFromParallelSequence(true /* should_succeed */);
177 WaitOnConditionVariableFromParallelSequence(true /* should_succeed */);
178 WaitOnConditionVariableFromParallelSequence(true /* should_succeed */);
179 VerifyBlockedConditionVariable(3 /* expected_num_blocked_sequences */);
180
181 // All should unblock after notify().
182 condition_variable()->notify();
Gabriel Charette694c3c332019-08-19 14:53:05183 task_environment_.RunUntilIdle();
Kyle Qiana01e9302018-08-25 00:07:42184 VerifyNumLocksAndUnlocks(6 /* expected_num_locks */,
185 6 /* expected_num_unlocks */);
186}
187
188TEST_F(ConditionVariableImplTest, ThreadCannotWaitIfStillOwnsLock) {
189 fake_lock()->set_is_held_by_current_thread(true);
190 WaitOnConditionVariableFromParallelSequence(false /* should_succeed */);
191}
192
193} // namespace nearby
194
195} // namespace chromeos