blob: 28b4adc337879bae9d501f3f2117039c607c0d97 [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
9#include "aos/common/util/thread.h"
10#include "aos/common/time.h"
11#include "aos/common/mutex.h"
12#include "aos/common/queue_testutils.h"
13#include "aos/common/type_traits.h"
14#include "aos/atom_code/ipc_lib/core_lib.h"
15#include "aos/common/logging/logging.h"
16
17using ::aos::time::Time;
18using ::aos::common::testing::GlobalCoreInstance;
19
20namespace aos {
21namespace testing {
22
23class ConditionTest : public ::testing::Test {
24 public:
25 struct Shared {
26 Shared() : condition(&mutex) {}
27
28 Mutex mutex;
29 Condition condition;
30 };
31 static_assert(shm_ok<Shared>::value,
32 "it's going to get shared between forked processes");
33
34 ConditionTest() : shared_(static_cast<Shared *>(shm_malloc(sizeof(Shared)))) {
35 new (shared_) Shared();
36 }
37
38 GlobalCoreInstance my_core;
39
40 Shared *const shared_;
41
42 void Settle() {
Brian Silverman4c04efd2013-09-07 14:51:33 -070043 time::SleepFor(::Time::InSeconds(0.008));
Brian Silverman797e71e2013-09-06 17:29:39 -070044 }
45};
46
47class ConditionTestProcess {
48 public:
49 enum class Action {
50 kWaitLockStart, // lock, delay, wait, unlock
51 kWait, // delay, lock, wait, unlock
52 kWaitNoUnlock, // delay, lock, wait
53 kSignal, // delay, signal
54 kBroadcast, // delay, broadcast
55 };
56
57 // This amount gets added to any passed in delay to make the test repeatable.
58 static constexpr ::Time kMinimumDelay = ::Time::InSeconds(0.015);
Brian Silverman4c04efd2013-09-07 14:51:33 -070059 static constexpr ::Time kDefaultTimeout = ::Time::InSeconds(0.09);
Brian Silverman797e71e2013-09-06 17:29:39 -070060
61 // delay is how long to wait before doing action to condition.
62 // timeout is how long to wait after delay before deciding that it's hung.
63 ConditionTestProcess(const ::Time &delay, Action action, Condition *condition,
64 const ::Time &timeout = kDefaultTimeout)
65 : delay_(kMinimumDelay + delay), action_(action), condition_(condition),
66 timeout_(delay_ + timeout), child_(-1),
67 shared_(static_cast<Shared *>(shm_malloc(sizeof(Shared)))) {
68 new (shared_) Shared();
69 }
70 ~ConditionTestProcess() {
71 assert(child_ == -1);
72 }
73
74 void Start() {
75 ASSERT_FALSE(shared_->started);
76
77 child_ = fork();
78 if (child_ == 0) { // in child
Brian Silvermaneeb62ca2013-09-11 15:08:03 -070079 ::aos::common::testing::PreventExit();
Brian Silverman797e71e2013-09-06 17:29:39 -070080 Run();
81 exit(EXIT_SUCCESS);
82 } else { // in parent
83 assert(child_ != -1);
84
85 shared_->ready.Lock();
86
87 shared_->started = true;
88 }
89 }
90
91 bool IsFinished() {
92 return shared_->finished;
93 }
94
95 ::testing::AssertionResult Hung() {
96 if (!shared_->started) {
97 ADD_FAILURE();
98 return ::testing::AssertionFailure() << "not started yet";
99 }
100 if (shared_->finished) {
101 Join();
102 return ::testing::AssertionFailure() << "already returned";
103 }
104 if (shared_->delayed) {
105 if (shared_->start_time > ::Time::Now() + timeout_) {
106 Kill();
107 return ::testing::AssertionSuccess() << "already been too long";
108 }
109 } else {
110 shared_->done_delaying.Lock();
111 }
112 time::SleepFor(::Time::InSeconds(0.01));
113 if (!shared_->finished) time::SleepUntil(shared_->start_time + timeout_);
114 if (shared_->finished) {
115 Join();
116 return ::testing::AssertionFailure() << "completed within timeout";
117 } else {
118 Kill();
119 return ::testing::AssertionSuccess() << "took too long";
120 }
121 }
122 ::testing::AssertionResult Test() {
123 Start();
124 return Hung();
125 }
126
127 private:
128 struct Shared {
129 Shared()
130 : started(false), delayed(false), start_time(0, 0), finished(false) {
131 done_delaying.Lock();
132 ready.Lock();
133 }
134
Brian Silvermaneeb62ca2013-09-11 15:08:03 -0700135 volatile bool started;
136 volatile bool delayed;
Brian Silverman797e71e2013-09-06 17:29:39 -0700137 Mutex done_delaying;
138 ::Time start_time;
Brian Silvermaneeb62ca2013-09-11 15:08:03 -0700139 volatile bool finished;
Brian Silverman797e71e2013-09-06 17:29:39 -0700140 Mutex ready;
141 };
142 static_assert(shm_ok<Shared>::value,
143 "it's going to get shared between forked processes");
144
145 void Run() {
146 if (action_ == Action::kWaitLockStart) {
147 shared_->ready.Unlock();
148 condition_->m()->Lock();
149 }
150 time::SleepFor(delay_);
151 shared_->start_time = ::Time::Now();
152 shared_->delayed = true;
153 shared_->done_delaying.Unlock();
154 switch (action_) {
155 case Action::kWait:
156 case Action::kWaitNoUnlock:
157 shared_->ready.Unlock();
158 condition_->m()->Lock();
159 case Action::kWaitLockStart:
160 condition_->Wait();
161 break;
162 case Action::kSignal:
163 shared_->ready.Unlock();
164 condition_->Signal();
165 break;
166 case Action::kBroadcast:
167 shared_->ready.Unlock();
168 condition_->Broadcast();
169 break;
170 }
171 shared_->finished = true;
172 if (action_ == Action::kWait || action_ == Action::kWaitLockStart) {
173 condition_->m()->Unlock();
174 }
175 }
176
177 void Join() {
178 assert(child_ != -1);
179 int status;
180 do {
181 assert(waitpid(child_, &status, 0) == child_);
182 } while (!(WIFEXITED(status) || WIFSIGNALED(status)));
183 child_ = -1;
184 }
185 void Kill() {
186 assert(child_ != -1);
187 assert(kill(child_, SIGTERM) == 0);
188 Join();
189 }
190
191 const ::Time delay_;
192 const Action action_;
193 Condition *const condition_;
194 const ::Time timeout_;
195
196 pid_t child_;
197
198 Shared *const shared_;
199};
200constexpr ::Time ConditionTestProcess::kMinimumDelay;
201constexpr ::Time ConditionTestProcess::kDefaultTimeout;
202
203// Makes sure that the testing framework and everything work for a really simple
204// Wait() and then Signal().
205TEST_F(ConditionTest, Basic) {
206 ConditionTestProcess thread(::Time(0, 0),
207 ConditionTestProcess::Action::kWait,
208 &shared_->condition);
209 thread.Start();
210 Settle();
211 EXPECT_FALSE(thread.IsFinished());
212 shared_->condition.Signal();
213 EXPECT_FALSE(thread.Hung());
214}
215
216// Makes sure that the worker thread locks before it tries to Wait() etc.
217TEST_F(ConditionTest, Locking) {
218 ConditionTestProcess thread(::Time(0, 0),
219 ConditionTestProcess::Action::kWait,
220 &shared_->condition);
221 shared_->mutex.Lock();
222 thread.Start();
223 Settle();
224 // This Signal() shouldn't do anything because the thread should still be
225 // waiting to lock the mutex.
226 shared_->condition.Signal();
227 Settle();
228 shared_->mutex.Unlock();
229 EXPECT_TRUE(thread.Hung());
230}
231
232// Tests that the work thread only catches a Signal() after the mutex gets
233// unlocked.
234TEST_F(ConditionTest, LockFirst) {
235 ConditionTestProcess thread(::Time(0, 0),
236 ConditionTestProcess::Action::kWait,
237 &shared_->condition);
238 shared_->mutex.Lock();
239 thread.Start();
240 Settle();
241 shared_->condition.Signal();
242 Settle();
243 EXPECT_FALSE(thread.IsFinished());
244 shared_->mutex.Unlock();
245 Settle();
246 EXPECT_FALSE(thread.IsFinished());
247 shared_->condition.Signal();
248 EXPECT_FALSE(thread.Hung());
249}
250
251// Tests that the mutex gets relocked after Wait() returns.
252TEST_F(ConditionTest, Relocking) {
253 ConditionTestProcess thread(::Time(0, 0),
254 ConditionTestProcess::Action::kWaitNoUnlock,
255 &shared_->condition);
256 thread.Start();
257 Settle();
258 shared_->condition.Signal();
259 EXPECT_FALSE(thread.Hung());
260 EXPECT_FALSE(shared_->mutex.TryLock());
261}
262
263} // namespace testing
264} // namespace aos