blob: 9d371b73f6f43b6529303ce9c05d6b580fed70e1 [file] [log] [blame]
John Park33858a32018-09-28 23:05:48 -07001#include "aos/mutex/mutex.h"
Brian Silverman653491d2014-05-13 16:53:29 -07002
Brian Silverman653491d2014-05-13 16:53:29 -07003#include <math.h>
4#include <pthread.h>
Austin Schuhf2a50ba2016-12-24 16:16:26 -08005#include <sched.h>
Brian Silverman653491d2014-05-13 16:53:29 -07006
Austin Schuhf2a50ba2016-12-24 16:16:26 -08007#include <chrono>
Brian Silverman119b3b12015-03-29 17:26:05 -04008#include <thread>
9
Brian Silverman653491d2014-05-13 16:53:29 -070010#include "gtest/gtest.h"
11
John Park33858a32018-09-28 23:05:48 -070012#include "aos/die.h"
13#include "aos/time/time.h"
14#include "aos/util/death_test_log_implementation.h"
15#include "aos/util/thread.h"
John Park398c74a2018-10-20 21:17:39 -070016#include "aos/ipc_lib/aos_sync.h"
17#include "aos/ipc_lib/core_lib.h"
Brian Silvermanf5f8d8e2015-12-06 18:39:12 -050018#include "aos/testing/test_logging.h"
Brian Silverman71c55c52014-08-19 14:31:59 -040019#include "aos/testing/test_shm.h"
Brian Silverman653491d2014-05-13 16:53:29 -070020
21namespace aos {
22namespace testing {
23
Austin Schuhf2a50ba2016-12-24 16:16:26 -080024namespace chrono = ::std::chrono;
25namespace this_thread = ::std::this_thread;
26
Brian Silverman653491d2014-05-13 16:53:29 -070027class MutexTest : public ::testing::Test {
28 public:
Brian Silverman1dfe48b2014-09-06 16:13:02 -040029 Mutex test_mutex_;
Brian Silverman653491d2014-05-13 16:53:29 -070030
31 protected:
32 void SetUp() override {
Brian Silvermanf5f8d8e2015-12-06 18:39:12 -050033 ::aos::testing::EnableTestLogging();
Brian Silverman653491d2014-05-13 16:53:29 -070034 SetDieTestMode(true);
35 }
36};
37
38typedef MutexTest MutexDeathTest;
Brian Silvermandc1eb272014-08-19 14:25:59 -040039typedef MutexTest MutexLockerTest;
40typedef MutexTest MutexLockerDeathTest;
41typedef MutexTest IPCMutexLockerTest;
42typedef MutexTest IPCMutexLockerDeathTest;
Brian Silverman1dfe48b2014-09-06 16:13:02 -040043typedef MutexTest IPCRecursiveMutexLockerTest;
Brian Silverman653491d2014-05-13 16:53:29 -070044
45TEST_F(MutexTest, TryLock) {
Brian Silverman1dfe48b2014-09-06 16:13:02 -040046 EXPECT_EQ(Mutex::State::kLocked, test_mutex_.TryLock());
Daniel Petti88a15662015-04-12 17:42:22 -040047 EXPECT_EQ(Mutex::State::kLockFailed, test_mutex_.TryLock());
Brian Silvermandc1eb272014-08-19 14:25:59 -040048
Brian Silverman1dfe48b2014-09-06 16:13:02 -040049 test_mutex_.Unlock();
Brian Silverman653491d2014-05-13 16:53:29 -070050}
51
52TEST_F(MutexTest, Lock) {
Brian Silverman1dfe48b2014-09-06 16:13:02 -040053 ASSERT_FALSE(test_mutex_.Lock());
Daniel Petti88a15662015-04-12 17:42:22 -040054 EXPECT_EQ(Mutex::State::kLockFailed, test_mutex_.TryLock());
Brian Silvermandc1eb272014-08-19 14:25:59 -040055
Brian Silverman1dfe48b2014-09-06 16:13:02 -040056 test_mutex_.Unlock();
Brian Silverman653491d2014-05-13 16:53:29 -070057}
58
59TEST_F(MutexTest, Unlock) {
Brian Silverman1dfe48b2014-09-06 16:13:02 -040060 ASSERT_FALSE(test_mutex_.Lock());
Daniel Petti88a15662015-04-12 17:42:22 -040061 EXPECT_EQ(Mutex::State::kLockFailed, test_mutex_.TryLock());
Brian Silverman1dfe48b2014-09-06 16:13:02 -040062 test_mutex_.Unlock();
63 EXPECT_EQ(Mutex::State::kLocked, test_mutex_.TryLock());
Brian Silvermandc1eb272014-08-19 14:25:59 -040064
Brian Silverman1dfe48b2014-09-06 16:13:02 -040065 test_mutex_.Unlock();
Brian Silverman653491d2014-05-13 16:53:29 -070066}
67
68// Sees what happens with multiple unlocks.
69TEST_F(MutexDeathTest, RepeatUnlock) {
Brian Silverman5c201e22014-06-12 22:40:28 -070070 logging::Init();
Brian Silverman1dfe48b2014-09-06 16:13:02 -040071 ASSERT_FALSE(test_mutex_.Lock());
72 test_mutex_.Unlock();
Brian Silverman653491d2014-05-13 16:53:29 -070073 EXPECT_DEATH(
74 {
75 logging::AddImplementation(new util::DeathTestLogImplementation());
Brian Silverman1dfe48b2014-09-06 16:13:02 -040076 test_mutex_.Unlock();
Brian Silverman653491d2014-05-13 16:53:29 -070077 },
78 ".*multiple unlock.*");
79}
80
81// Sees what happens if you unlock without ever locking (or unlocking) it.
82TEST_F(MutexDeathTest, NeverLock) {
Brian Silverman5c201e22014-06-12 22:40:28 -070083 logging::Init();
Brian Silverman653491d2014-05-13 16:53:29 -070084 EXPECT_DEATH(
85 {
86 logging::AddImplementation(new util::DeathTestLogImplementation());
Brian Silverman1dfe48b2014-09-06 16:13:02 -040087 test_mutex_.Unlock();
Brian Silverman653491d2014-05-13 16:53:29 -070088 },
89 ".*multiple unlock.*");
90}
91
Brian Silverman71c55c52014-08-19 14:31:59 -040092// Tests that locking a mutex multiple times from the same thread fails nicely.
Brian Silvermandc1eb272014-08-19 14:25:59 -040093TEST_F(MutexDeathTest, RepeatLock) {
94 EXPECT_DEATH(
95 {
96 logging::AddImplementation(new util::DeathTestLogImplementation());
Brian Silverman1dfe48b2014-09-06 16:13:02 -040097 ASSERT_FALSE(test_mutex_.Lock());
98 ASSERT_FALSE(test_mutex_.Lock());
Brian Silvermandc1eb272014-08-19 14:25:59 -040099 },
100 ".*multiple lock.*");
Brian Silverman653491d2014-05-13 16:53:29 -0700101}
102
Brian Silverman71c55c52014-08-19 14:31:59 -0400103// Tests that Lock behaves correctly when the previous owner exits with the lock
104// held (which is the same as dying any other way).
105TEST_F(MutexTest, OwnerDiedDeathLock) {
106 testing::TestSharedMemory my_shm;
107 Mutex *mutex =
108 static_cast<Mutex *>(shm_malloc_aligned(sizeof(Mutex), alignof(Mutex)));
109 new (mutex) Mutex();
110
111 util::FunctionThread::RunInOtherThread([&]() {
112 ASSERT_FALSE(mutex->Lock());
113 });
114 EXPECT_TRUE(mutex->Lock());
115
116 mutex->Unlock();
117 mutex->~Mutex();
118}
119
120// Tests that TryLock behaves correctly when the previous owner dies.
121TEST_F(MutexTest, OwnerDiedDeathTryLock) {
122 testing::TestSharedMemory my_shm;
123 Mutex *mutex =
124 static_cast<Mutex *>(shm_malloc_aligned(sizeof(Mutex), alignof(Mutex)));
125 new (mutex) Mutex();
126
127 util::FunctionThread::RunInOtherThread([&]() {
128 ASSERT_FALSE(mutex->Lock());
129 });
130 EXPECT_EQ(Mutex::State::kOwnerDied, mutex->TryLock());
131
132 mutex->Unlock();
133 mutex->~Mutex();
134}
135
136// TODO(brians): Test owner dying by being SIGKILLed and SIGTERMed.
137
138// This sequence of mutex operations used to mess up the robust list and cause
139// one of the mutexes to not get owner-died like it should.
140TEST_F(MutexTest, DontCorruptRobustList) {
141 // I think this was the allocator lock in the original failure.
142 Mutex mutex1;
143 // This one should get owner-died afterwards (iff the kernel accepts the
144 // robust list and uses it). I think it was the task_death_notification lock
145 // in the original failure.
146 Mutex mutex2;
147
148 util::FunctionThread::RunInOtherThread([&]() {
149 ASSERT_FALSE(mutex1.Lock());
150 ASSERT_FALSE(mutex2.Lock());
151 mutex1.Unlock();
152 });
153
154 EXPECT_EQ(Mutex::State::kLocked, mutex1.TryLock());
155 EXPECT_EQ(Mutex::State::kOwnerDied, mutex2.TryLock());
156
157 mutex1.Unlock();
158 mutex2.Unlock();
159}
160
Brian Silverman653491d2014-05-13 16:53:29 -0700161namespace {
162
163class AdderThread : public ::aos::util::Thread {
164 public:
Austin Schuhf2a50ba2016-12-24 16:16:26 -0800165 AdderThread(int *counter, Mutex *mutex,
166 monotonic_clock::duration sleep_before_time,
167 monotonic_clock::duration sleep_after_time)
Brian Silverman653491d2014-05-13 16:53:29 -0700168 : counter_(counter),
169 mutex_(mutex),
170 sleep_before_time_(sleep_before_time),
171 sleep_after_time_(sleep_after_time) {}
Brian Silverman71c55c52014-08-19 14:31:59 -0400172
173 private:
Brian Silverman653491d2014-05-13 16:53:29 -0700174 virtual void Run() override {
Austin Schuhf2a50ba2016-12-24 16:16:26 -0800175 this_thread::sleep_for(sleep_before_time_);
Brian Silverman653491d2014-05-13 16:53:29 -0700176 MutexLocker locker(mutex_);
177 ++(*counter_);
Austin Schuhf2a50ba2016-12-24 16:16:26 -0800178 this_thread::sleep_for(sleep_after_time_);
Brian Silverman653491d2014-05-13 16:53:29 -0700179 }
180
Brian Silverman653491d2014-05-13 16:53:29 -0700181 int *const counter_;
182 Mutex *const mutex_;
Austin Schuhf2a50ba2016-12-24 16:16:26 -0800183 const monotonic_clock::duration sleep_before_time_, sleep_after_time_;
Brian Silverman653491d2014-05-13 16:53:29 -0700184};
185
186} // namespace
187
188// Verifies that ThreadSanitizer understands that a contended mutex establishes
189// a happens-before relationship.
190TEST_F(MutexTest, ThreadSanitizerContended) {
191 int counter = 0;
192 AdderThread threads[2]{
Austin Schuhf2a50ba2016-12-24 16:16:26 -0800193 {&counter, &test_mutex_, chrono::milliseconds(200),
194 chrono::milliseconds(0)},
195 {&counter, &test_mutex_, chrono::milliseconds(0),
196 chrono::milliseconds(0)},
197 };
Brian Silverman653491d2014-05-13 16:53:29 -0700198 for (auto &c : threads) {
199 c.Start();
200 }
201 for (auto &c : threads) {
202 c.WaitUntilDone();
203 }
204 EXPECT_EQ(2, counter);
205}
206
Brian Silverman119b3b12015-03-29 17:26:05 -0400207// Verifiers that ThreadSanitizer understands how a mutex works.
208// For some reason this used to fail when the other tests didn't...
Brian Silverman71c55c52014-08-19 14:31:59 -0400209// The loops make it fail more reliably when it's going to.
Brian Silverman119b3b12015-03-29 17:26:05 -0400210TEST_F(MutexTest, ThreadSanitizerMutexLocker) {
Brian Silverman71c55c52014-08-19 14:31:59 -0400211 for (int i = 0; i < 100; ++i) {
212 int counter = 0;
213 ::std::thread thread([&counter, this]() {
214 for (int i = 0; i < 300; ++i) {
215 MutexLocker locker(&test_mutex_);
216 ++counter;
217 }
218 });
219 for (int i = 0; i < 300; ++i) {
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400220 MutexLocker locker(&test_mutex_);
Brian Silverman71c55c52014-08-19 14:31:59 -0400221 --counter;
Brian Silverman119b3b12015-03-29 17:26:05 -0400222 }
Brian Silverman71c55c52014-08-19 14:31:59 -0400223 thread.join();
224 EXPECT_EQ(0, counter);
Brian Silverman119b3b12015-03-29 17:26:05 -0400225 }
Brian Silverman119b3b12015-03-29 17:26:05 -0400226}
227
Brian Silverman653491d2014-05-13 16:53:29 -0700228// Verifies that ThreadSanitizer understands that an uncontended mutex
229// establishes a happens-before relationship.
230TEST_F(MutexTest, ThreadSanitizerUncontended) {
231 int counter = 0;
232 AdderThread threads[2]{
Austin Schuhf2a50ba2016-12-24 16:16:26 -0800233 {&counter, &test_mutex_, chrono::milliseconds(0),
234 chrono::milliseconds(0)},
235 {&counter, &test_mutex_, chrono::milliseconds(200),
236 chrono::milliseconds(0)}, };
Brian Silverman653491d2014-05-13 16:53:29 -0700237 for (auto &c : threads) {
238 c.Start();
239 }
240 for (auto &c : threads) {
241 c.WaitUntilDone();
242 }
243 EXPECT_EQ(2, counter);
244}
245
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400246namespace {
247
248class LockerThread : public util::Thread {
249 public:
250 LockerThread(Mutex *mutex, bool lock, bool unlock)
251 : mutex_(mutex), lock_(lock), unlock_(unlock) {}
252
253 private:
254 virtual void Run() override {
255 if (lock_) ASSERT_FALSE(mutex_->Lock());
256 if (unlock_) mutex_->Unlock();
257 }
258
259 Mutex *const mutex_;
260 const bool lock_, unlock_;
261};
262
263} // namespace
264
265// Makes sure that we don't SIGSEGV or something with multiple threads.
266TEST_F(MutexTest, MultiThreadedLock) {
267 LockerThread t(&test_mutex_, true, true);
268 t.Start();
269 ASSERT_FALSE(test_mutex_.Lock());
270 test_mutex_.Unlock();
271 t.Join();
272}
273
Brian Silvermandc1eb272014-08-19 14:25:59 -0400274TEST_F(MutexLockerTest, Basic) {
275 {
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400276 aos::MutexLocker locker(&test_mutex_);
Daniel Petti88a15662015-04-12 17:42:22 -0400277 EXPECT_EQ(Mutex::State::kLockFailed, test_mutex_.TryLock());
Brian Silvermandc1eb272014-08-19 14:25:59 -0400278 }
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400279 EXPECT_EQ(Mutex::State::kLocked, test_mutex_.TryLock());
Brian Silvermandc1eb272014-08-19 14:25:59 -0400280
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400281 test_mutex_.Unlock();
Brian Silvermandc1eb272014-08-19 14:25:59 -0400282}
283
Brian Silverman71c55c52014-08-19 14:31:59 -0400284// Tests that MutexLocker behaves correctly when the previous owner dies.
285TEST_F(MutexLockerDeathTest, OwnerDied) {
286 testing::TestSharedMemory my_shm;
287 Mutex *mutex =
288 static_cast<Mutex *>(shm_malloc_aligned(sizeof(Mutex), alignof(Mutex)));
289 new (mutex) Mutex();
290
291 util::FunctionThread::RunInOtherThread([&]() {
292 ASSERT_FALSE(mutex->Lock());
293 });
294 EXPECT_DEATH(
295 {
296 logging::AddImplementation(new util::DeathTestLogImplementation());
297 MutexLocker locker(mutex);
298 },
299 ".*previous owner of mutex [^ ]+ died.*");
300
301 mutex->~Mutex();
302}
303
Brian Silvermandc1eb272014-08-19 14:25:59 -0400304TEST_F(IPCMutexLockerTest, Basic) {
305 {
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400306 aos::IPCMutexLocker locker(&test_mutex_);
Daniel Petti88a15662015-04-12 17:42:22 -0400307 EXPECT_EQ(Mutex::State::kLockFailed, test_mutex_.TryLock());
Brian Silvermandc1eb272014-08-19 14:25:59 -0400308 EXPECT_FALSE(locker.owner_died());
309 }
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400310 EXPECT_EQ(Mutex::State::kLocked, test_mutex_.TryLock());
Brian Silvermandc1eb272014-08-19 14:25:59 -0400311
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400312 test_mutex_.Unlock();
Brian Silvermandc1eb272014-08-19 14:25:59 -0400313}
314
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400315// Tests what happens when the caller doesn't check if the previous owner died
316// with an IPCMutexLocker.
Brian Silvermandc1eb272014-08-19 14:25:59 -0400317TEST_F(IPCMutexLockerDeathTest, NoCheckOwnerDied) {
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400318 EXPECT_DEATH({ aos::IPCMutexLocker locker(&test_mutex_); },
Brian Silvermandc1eb272014-08-19 14:25:59 -0400319 "nobody checked if the previous owner of mutex [^ ]+ died.*");
320}
321
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400322TEST_F(IPCRecursiveMutexLockerTest, Basic) {
323 {
324 aos::IPCRecursiveMutexLocker locker(&test_mutex_);
Daniel Petti88a15662015-04-12 17:42:22 -0400325 EXPECT_EQ(Mutex::State::kLockFailed, test_mutex_.TryLock());
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400326 EXPECT_FALSE(locker.owner_died());
327 }
328 EXPECT_EQ(Mutex::State::kLocked, test_mutex_.TryLock());
329
330 test_mutex_.Unlock();
331}
332
333// Tests actually locking a mutex recursively with IPCRecursiveMutexLocker.
334TEST_F(IPCRecursiveMutexLockerTest, RecursiveLock) {
335 {
336 aos::IPCRecursiveMutexLocker locker(&test_mutex_);
Daniel Petti88a15662015-04-12 17:42:22 -0400337 EXPECT_EQ(Mutex::State::kLockFailed, test_mutex_.TryLock());
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400338 {
339 aos::IPCRecursiveMutexLocker locker(&test_mutex_);
Daniel Petti88a15662015-04-12 17:42:22 -0400340 EXPECT_EQ(Mutex::State::kLockFailed, test_mutex_.TryLock());
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400341 EXPECT_FALSE(locker.owner_died());
342 }
Daniel Petti88a15662015-04-12 17:42:22 -0400343 EXPECT_EQ(Mutex::State::kLockFailed, test_mutex_.TryLock());
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400344 EXPECT_FALSE(locker.owner_died());
345 }
346 EXPECT_EQ(Mutex::State::kLocked, test_mutex_.TryLock());
347
348 test_mutex_.Unlock();
349}
350
Brian Silverman71c55c52014-08-19 14:31:59 -0400351// Tests that IPCMutexLocker behaves correctly when the previous owner dies.
352TEST_F(IPCMutexLockerTest, OwnerDied) {
353 testing::TestSharedMemory my_shm;
354 Mutex *mutex =
355 static_cast<Mutex *>(shm_malloc_aligned(sizeof(Mutex), alignof(Mutex)));
356 new (mutex) Mutex();
357
358 util::FunctionThread::RunInOtherThread([&]() {
359 ASSERT_FALSE(mutex->Lock());
360 });
361 {
362 aos::IPCMutexLocker locker(mutex);
363 EXPECT_EQ(Mutex::State::kLockFailed, mutex->TryLock());
364 EXPECT_TRUE(locker.owner_died());
365 }
366 EXPECT_EQ(Mutex::State::kLocked, mutex->TryLock());
367
368 mutex->Unlock();
369 mutex->~Mutex();
370}
371
Brian Silverman653491d2014-05-13 16:53:29 -0700372} // namespace testing
373} // namespace aos