blob: 6914a8bed828e7ed9fedd99c72f54d90d3bd7695 [file] [log] [blame]
Brian Silverman797e71e2013-09-06 17:29:39 -07001#include "aos/common/condition.h"
2
3#include <unistd.h>
4#include <sys/types.h>
5#include <sys/wait.h>
6
7#include "gtest/gtest.h"
8
Brian Silverman797e71e2013-09-06 17:29:39 -07009#include "aos/common/time.h"
10#include "aos/common/mutex.h"
11#include "aos/common/queue_testutils.h"
12#include "aos/common/type_traits.h"
Brian Silverman14fd0fb2014-01-14 21:42:01 -080013#include "aos/linux_code/ipc_lib/core_lib.h"
Brian Silverman797e71e2013-09-06 17:29:39 -070014#include "aos/common/logging/logging.h"
Brian Silverman2586a5d2013-09-13 13:45:52 -070015#include "aos/common/macros.h"
Brian Silverman8d2e56e2013-09-23 17:55:03 -070016#include "aos/common/die.h"
Brian Silverman797e71e2013-09-06 17:29:39 -070017
18using ::aos::time::Time;
19using ::aos::common::testing::GlobalCoreInstance;
20
21namespace aos {
22namespace testing {
23
24class ConditionTest : public ::testing::Test {
25 public:
26 struct Shared {
27 Shared() : condition(&mutex) {}
28
29 Mutex mutex;
30 Condition condition;
31 };
32 static_assert(shm_ok<Shared>::value,
33 "it's going to get shared between forked processes");
34
35 ConditionTest() : shared_(static_cast<Shared *>(shm_malloc(sizeof(Shared)))) {
36 new (shared_) Shared();
37 }
38
39 GlobalCoreInstance my_core;
40
41 Shared *const shared_;
42
43 void Settle() {
Brian Silverman4c04efd2013-09-07 14:51:33 -070044 time::SleepFor(::Time::InSeconds(0.008));
Brian Silverman797e71e2013-09-06 17:29:39 -070045 }
Brian Silverman2586a5d2013-09-13 13:45:52 -070046
Brian Silverman8d2e56e2013-09-23 17:55:03 -070047 protected:
48 void SetUp() override {
49 SetDieTestMode(true);
50 }
51
Brian Silverman2586a5d2013-09-13 13:45:52 -070052 private:
53 DISALLOW_COPY_AND_ASSIGN(ConditionTest);
Brian Silverman797e71e2013-09-06 17:29:39 -070054};
55
56class ConditionTestProcess {
57 public:
58 enum class Action {
59 kWaitLockStart, // lock, delay, wait, unlock
60 kWait, // delay, lock, wait, unlock
61 kWaitNoUnlock, // delay, lock, wait
Brian Silverman797e71e2013-09-06 17:29:39 -070062 };
63
64 // This amount gets added to any passed in delay to make the test repeatable.
65 static constexpr ::Time kMinimumDelay = ::Time::InSeconds(0.015);
Brian Silverman4c04efd2013-09-07 14:51:33 -070066 static constexpr ::Time kDefaultTimeout = ::Time::InSeconds(0.09);
Brian Silverman797e71e2013-09-06 17:29:39 -070067
68 // delay is how long to wait before doing action to condition.
69 // timeout is how long to wait after delay before deciding that it's hung.
70 ConditionTestProcess(const ::Time &delay, Action action, Condition *condition,
71 const ::Time &timeout = kDefaultTimeout)
72 : delay_(kMinimumDelay + delay), action_(action), condition_(condition),
73 timeout_(delay_ + timeout), child_(-1),
74 shared_(static_cast<Shared *>(shm_malloc(sizeof(Shared)))) {
75 new (shared_) Shared();
76 }
77 ~ConditionTestProcess() {
Brian Silvermanfe457de2014-05-26 22:04:08 -070078 CHECK_EQ(child_, -1);
Brian Silverman797e71e2013-09-06 17:29:39 -070079 }
80
81 void Start() {
82 ASSERT_FALSE(shared_->started);
83
84 child_ = fork();
85 if (child_ == 0) { // in child
Brian Silvermaneeb62ca2013-09-11 15:08:03 -070086 ::aos::common::testing::PreventExit();
Brian Silverman797e71e2013-09-06 17:29:39 -070087 Run();
88 exit(EXIT_SUCCESS);
89 } else { // in parent
Brian Silvermanfe457de2014-05-26 22:04:08 -070090 CHECK_NE(child_, -1);
Brian Silverman797e71e2013-09-06 17:29:39 -070091
92 shared_->ready.Lock();
93
94 shared_->started = true;
95 }
96 }
97
98 bool IsFinished() {
99 return shared_->finished;
100 }
101
102 ::testing::AssertionResult Hung() {
103 if (!shared_->started) {
104 ADD_FAILURE();
105 return ::testing::AssertionFailure() << "not started yet";
106 }
107 if (shared_->finished) {
108 Join();
109 return ::testing::AssertionFailure() << "already returned";
110 }
111 if (shared_->delayed) {
112 if (shared_->start_time > ::Time::Now() + timeout_) {
113 Kill();
114 return ::testing::AssertionSuccess() << "already been too long";
115 }
116 } else {
117 shared_->done_delaying.Lock();
118 }
119 time::SleepFor(::Time::InSeconds(0.01));
120 if (!shared_->finished) time::SleepUntil(shared_->start_time + timeout_);
121 if (shared_->finished) {
122 Join();
123 return ::testing::AssertionFailure() << "completed within timeout";
124 } else {
125 Kill();
126 return ::testing::AssertionSuccess() << "took too long";
127 }
128 }
129 ::testing::AssertionResult Test() {
130 Start();
131 return Hung();
132 }
133
134 private:
135 struct Shared {
136 Shared()
137 : started(false), delayed(false), start_time(0, 0), finished(false) {
138 done_delaying.Lock();
139 ready.Lock();
140 }
141
Brian Silvermaneeb62ca2013-09-11 15:08:03 -0700142 volatile bool started;
143 volatile bool delayed;
Brian Silverman797e71e2013-09-06 17:29:39 -0700144 Mutex done_delaying;
145 ::Time start_time;
Brian Silvermaneeb62ca2013-09-11 15:08:03 -0700146 volatile bool finished;
Brian Silverman797e71e2013-09-06 17:29:39 -0700147 Mutex ready;
148 };
149 static_assert(shm_ok<Shared>::value,
150 "it's going to get shared between forked processes");
151
152 void Run() {
153 if (action_ == Action::kWaitLockStart) {
154 shared_->ready.Unlock();
155 condition_->m()->Lock();
156 }
157 time::SleepFor(delay_);
158 shared_->start_time = ::Time::Now();
159 shared_->delayed = true;
160 shared_->done_delaying.Unlock();
Brian Silverman2586a5d2013-09-13 13:45:52 -0700161 if (action_ != Action::kWaitLockStart) {
162 shared_->ready.Unlock();
163 condition_->m()->Lock();
Brian Silverman797e71e2013-09-06 17:29:39 -0700164 }
Brian Silverman2586a5d2013-09-13 13:45:52 -0700165 condition_->Wait();
Brian Silverman797e71e2013-09-06 17:29:39 -0700166 shared_->finished = true;
Brian Silverman2586a5d2013-09-13 13:45:52 -0700167 if (action_ != Action::kWaitNoUnlock) {
Brian Silverman797e71e2013-09-06 17:29:39 -0700168 condition_->m()->Unlock();
169 }
170 }
171
172 void Join() {
Brian Silvermanfe457de2014-05-26 22:04:08 -0700173 CHECK_NE(child_, -1);
Brian Silverman797e71e2013-09-06 17:29:39 -0700174 int status;
175 do {
Brian Silvermanfe457de2014-05-26 22:04:08 -0700176 CHECK_EQ(waitpid(child_, &status, 0), child_);
Brian Silverman797e71e2013-09-06 17:29:39 -0700177 } while (!(WIFEXITED(status) || WIFSIGNALED(status)));
178 child_ = -1;
179 }
180 void Kill() {
Brian Silvermanfe457de2014-05-26 22:04:08 -0700181 CHECK_NE(child_, -1);
182 PCHECK(kill(child_, SIGTERM));
Brian Silverman797e71e2013-09-06 17:29:39 -0700183 Join();
184 }
185
186 const ::Time delay_;
187 const Action action_;
188 Condition *const condition_;
189 const ::Time timeout_;
190
191 pid_t child_;
192
193 Shared *const shared_;
Brian Silverman2586a5d2013-09-13 13:45:52 -0700194
195 DISALLOW_COPY_AND_ASSIGN(ConditionTestProcess);
Brian Silverman797e71e2013-09-06 17:29:39 -0700196};
197constexpr ::Time ConditionTestProcess::kMinimumDelay;
198constexpr ::Time ConditionTestProcess::kDefaultTimeout;
199
200// Makes sure that the testing framework and everything work for a really simple
201// Wait() and then Signal().
202TEST_F(ConditionTest, Basic) {
Brian Silverman2586a5d2013-09-13 13:45:52 -0700203 ConditionTestProcess child(::Time(0, 0),
204 ConditionTestProcess::Action::kWait,
205 &shared_->condition);
206 child.Start();
Brian Silverman797e71e2013-09-06 17:29:39 -0700207 Settle();
Brian Silverman2586a5d2013-09-13 13:45:52 -0700208 EXPECT_FALSE(child.IsFinished());
Brian Silverman797e71e2013-09-06 17:29:39 -0700209 shared_->condition.Signal();
Brian Silverman2586a5d2013-09-13 13:45:52 -0700210 EXPECT_FALSE(child.Hung());
Brian Silverman797e71e2013-09-06 17:29:39 -0700211}
212
Brian Silverman2586a5d2013-09-13 13:45:52 -0700213// Makes sure that the worker child locks before it tries to Wait() etc.
Brian Silverman797e71e2013-09-06 17:29:39 -0700214TEST_F(ConditionTest, Locking) {
Brian Silverman2586a5d2013-09-13 13:45:52 -0700215 ConditionTestProcess child(::Time(0, 0),
216 ConditionTestProcess::Action::kWait,
217 &shared_->condition);
Brian Silverman797e71e2013-09-06 17:29:39 -0700218 shared_->mutex.Lock();
Brian Silverman2586a5d2013-09-13 13:45:52 -0700219 child.Start();
Brian Silverman797e71e2013-09-06 17:29:39 -0700220 Settle();
Brian Silverman2586a5d2013-09-13 13:45:52 -0700221 // This Signal() shouldn't do anything because the child should still be
Brian Silverman797e71e2013-09-06 17:29:39 -0700222 // waiting to lock the mutex.
223 shared_->condition.Signal();
224 Settle();
225 shared_->mutex.Unlock();
Brian Silverman2586a5d2013-09-13 13:45:52 -0700226 EXPECT_TRUE(child.Hung());
Brian Silverman797e71e2013-09-06 17:29:39 -0700227}
228
Brian Silverman2586a5d2013-09-13 13:45:52 -0700229// Tests that the work child only catches a Signal() after the mutex gets
Brian Silverman797e71e2013-09-06 17:29:39 -0700230// unlocked.
231TEST_F(ConditionTest, LockFirst) {
Brian Silverman2586a5d2013-09-13 13:45:52 -0700232 ConditionTestProcess child(::Time(0, 0),
233 ConditionTestProcess::Action::kWait,
234 &shared_->condition);
Brian Silverman797e71e2013-09-06 17:29:39 -0700235 shared_->mutex.Lock();
Brian Silverman2586a5d2013-09-13 13:45:52 -0700236 child.Start();
Brian Silverman797e71e2013-09-06 17:29:39 -0700237 Settle();
238 shared_->condition.Signal();
239 Settle();
Brian Silverman2586a5d2013-09-13 13:45:52 -0700240 EXPECT_FALSE(child.IsFinished());
Brian Silverman797e71e2013-09-06 17:29:39 -0700241 shared_->mutex.Unlock();
242 Settle();
Brian Silverman2586a5d2013-09-13 13:45:52 -0700243 EXPECT_FALSE(child.IsFinished());
Brian Silverman797e71e2013-09-06 17:29:39 -0700244 shared_->condition.Signal();
Brian Silverman2586a5d2013-09-13 13:45:52 -0700245 EXPECT_FALSE(child.Hung());
Brian Silverman797e71e2013-09-06 17:29:39 -0700246}
247
248// Tests that the mutex gets relocked after Wait() returns.
249TEST_F(ConditionTest, Relocking) {
Brian Silverman2586a5d2013-09-13 13:45:52 -0700250 ConditionTestProcess child(::Time(0, 0),
251 ConditionTestProcess::Action::kWaitNoUnlock,
252 &shared_->condition);
253 child.Start();
Brian Silverman797e71e2013-09-06 17:29:39 -0700254 Settle();
255 shared_->condition.Signal();
Brian Silverman2586a5d2013-09-13 13:45:52 -0700256 EXPECT_FALSE(child.Hung());
Brian Silverman797e71e2013-09-06 17:29:39 -0700257 EXPECT_FALSE(shared_->mutex.TryLock());
258}
259
Brian Silverman2586a5d2013-09-13 13:45:52 -0700260// Tests that Signal() stops exactly 1 Wait()er.
261TEST_F(ConditionTest, SignalOne) {
262 ConditionTestProcess child1(::Time(0, 0),
263 ConditionTestProcess::Action::kWait,
264 &shared_->condition);
265 ConditionTestProcess child2(::Time(0, 0),
266 ConditionTestProcess::Action::kWait,
267 &shared_->condition);
268 ConditionTestProcess child3(::Time(0, 0),
269 ConditionTestProcess::Action::kWait,
270 &shared_->condition);
271 auto number_finished = [&]() { return (child1.IsFinished() ? 1 : 0) +
272 (child2.IsFinished() ? 1 : 0) + (child3.IsFinished() ? 1 : 0); };
273 child1.Start();
274 child2.Start();
275 child3.Start();
276 Settle();
277 EXPECT_EQ(0, number_finished());
278 shared_->condition.Signal();
279 Settle();
280 EXPECT_EQ(1, number_finished());
281 shared_->condition.Signal();
282 Settle();
283 EXPECT_EQ(2, number_finished());
284 shared_->condition.Signal();
285 Settle();
286 EXPECT_EQ(3, number_finished());
287 EXPECT_FALSE(child1.Hung());
288 EXPECT_FALSE(child2.Hung());
289 EXPECT_FALSE(child3.Hung());
290}
291
292// Tests that Brodcast() wakes multiple Wait()ers.
293TEST_F(ConditionTest, Broadcast) {
294 ConditionTestProcess child1(::Time(0, 0),
295 ConditionTestProcess::Action::kWait,
296 &shared_->condition);
297 ConditionTestProcess child2(::Time(0, 0),
298 ConditionTestProcess::Action::kWait,
299 &shared_->condition);
300 ConditionTestProcess child3(::Time(0, 0),
301 ConditionTestProcess::Action::kWait,
302 &shared_->condition);
303 child1.Start();
304 child2.Start();
305 child3.Start();
306 Settle();
307 shared_->condition.Broadcast();
308 EXPECT_FALSE(child1.Hung());
309 EXPECT_FALSE(child2.Hung());
310 EXPECT_FALSE(child3.Hung());
311}
312
Brian Silverman797e71e2013-09-06 17:29:39 -0700313} // namespace testing
314} // namespace aos