blob: 9f8efbfe6b44882e673c4e859bb6fc738ae0eafa [file] [log] [blame]
John Park33858a32018-09-28 23:05:48 -07001#include "aos/mutex/mutex.h"
Brian Silverman653491d2014-05-13 16:53:29 -07002
Austin Schuhf2a50ba2016-12-24 16:16:26 -08003#include <chrono>
Stephan Pleines36fc0402024-05-30 20:28:02 -07004#include <memory>
5#include <new>
Brian Silverman119b3b12015-03-29 17:26:05 -04006#include <thread>
7
Philipp Schrader790cb542023-07-05 21:06:52 -07008#include "gtest/gtest.h"
9
John Park33858a32018-09-28 23:05:48 -070010#include "aos/die.h"
John Park398c74a2018-10-20 21:17:39 -070011#include "aos/ipc_lib/core_lib.h"
Stephan Pleines36fc0402024-05-30 20:28:02 -070012#include "aos/logging/implementations.h"
Brian Silvermanf5f8d8e2015-12-06 18:39:12 -050013#include "aos/testing/test_logging.h"
Brian Silverman71c55c52014-08-19 14:31:59 -040014#include "aos/testing/test_shm.h"
Alex Perrycb7da4b2019-08-28 19:35:56 -070015#include "aos/util/death_test_log_implementation.h"
Brian Silverman653491d2014-05-13 16:53:29 -070016
Stephan Pleinesf63bde82024-01-13 15:59:33 -080017namespace aos::testing {
Brian Silverman653491d2014-05-13 16:53:29 -070018
Austin Schuhf2a50ba2016-12-24 16:16:26 -080019namespace chrono = ::std::chrono;
20namespace this_thread = ::std::this_thread;
21
Brian Silverman653491d2014-05-13 16:53:29 -070022class MutexTest : public ::testing::Test {
23 public:
Brian Silverman1dfe48b2014-09-06 16:13:02 -040024 Mutex test_mutex_;
Brian Silverman653491d2014-05-13 16:53:29 -070025
26 protected:
27 void SetUp() override {
Brian Silvermanf5f8d8e2015-12-06 18:39:12 -050028 ::aos::testing::EnableTestLogging();
Brian Silverman653491d2014-05-13 16:53:29 -070029 SetDieTestMode(true);
30 }
31};
32
33typedef MutexTest MutexDeathTest;
Brian Silvermandc1eb272014-08-19 14:25:59 -040034typedef MutexTest MutexLockerTest;
35typedef MutexTest MutexLockerDeathTest;
36typedef MutexTest IPCMutexLockerTest;
37typedef MutexTest IPCMutexLockerDeathTest;
Brian Silverman1dfe48b2014-09-06 16:13:02 -040038typedef MutexTest IPCRecursiveMutexLockerTest;
Brian Silverman653491d2014-05-13 16:53:29 -070039
40TEST_F(MutexTest, TryLock) {
Brian Silverman1dfe48b2014-09-06 16:13:02 -040041 EXPECT_EQ(Mutex::State::kLocked, test_mutex_.TryLock());
Daniel Petti88a15662015-04-12 17:42:22 -040042 EXPECT_EQ(Mutex::State::kLockFailed, test_mutex_.TryLock());
Brian Silvermandc1eb272014-08-19 14:25:59 -040043
Brian Silverman1dfe48b2014-09-06 16:13:02 -040044 test_mutex_.Unlock();
Brian Silverman653491d2014-05-13 16:53:29 -070045}
46
47TEST_F(MutexTest, Lock) {
Brian Silverman1dfe48b2014-09-06 16:13:02 -040048 ASSERT_FALSE(test_mutex_.Lock());
Daniel Petti88a15662015-04-12 17:42:22 -040049 EXPECT_EQ(Mutex::State::kLockFailed, test_mutex_.TryLock());
Brian Silvermandc1eb272014-08-19 14:25:59 -040050
Brian Silverman1dfe48b2014-09-06 16:13:02 -040051 test_mutex_.Unlock();
Brian Silverman653491d2014-05-13 16:53:29 -070052}
53
54TEST_F(MutexTest, Unlock) {
Brian Silverman1dfe48b2014-09-06 16:13:02 -040055 ASSERT_FALSE(test_mutex_.Lock());
Daniel Petti88a15662015-04-12 17:42:22 -040056 EXPECT_EQ(Mutex::State::kLockFailed, test_mutex_.TryLock());
Brian Silverman1dfe48b2014-09-06 16:13:02 -040057 test_mutex_.Unlock();
58 EXPECT_EQ(Mutex::State::kLocked, test_mutex_.TryLock());
Brian Silvermandc1eb272014-08-19 14:25:59 -040059
Brian Silverman1dfe48b2014-09-06 16:13:02 -040060 test_mutex_.Unlock();
Brian Silverman653491d2014-05-13 16:53:29 -070061}
62
63// Sees what happens with multiple unlocks.
64TEST_F(MutexDeathTest, RepeatUnlock) {
Brian Silverman1dfe48b2014-09-06 16:13:02 -040065 ASSERT_FALSE(test_mutex_.Lock());
66 test_mutex_.Unlock();
Brian Silverman653491d2014-05-13 16:53:29 -070067 EXPECT_DEATH(
68 {
Austin Schuha0c41ba2020-09-10 22:59:14 -070069 logging::SetImplementation(
70 std::make_shared<util::DeathTestLogImplementation>());
Brian Silverman1dfe48b2014-09-06 16:13:02 -040071 test_mutex_.Unlock();
Brian Silverman653491d2014-05-13 16:53:29 -070072 },
73 ".*multiple unlock.*");
74}
75
76// Sees what happens if you unlock without ever locking (or unlocking) it.
77TEST_F(MutexDeathTest, NeverLock) {
Brian Silverman653491d2014-05-13 16:53:29 -070078 EXPECT_DEATH(
79 {
Austin Schuha0c41ba2020-09-10 22:59:14 -070080 logging::SetImplementation(
81 std::make_shared<util::DeathTestLogImplementation>());
Brian Silverman1dfe48b2014-09-06 16:13:02 -040082 test_mutex_.Unlock();
Brian Silverman653491d2014-05-13 16:53:29 -070083 },
84 ".*multiple unlock.*");
85}
86
Brian Silverman71c55c52014-08-19 14:31:59 -040087// Tests that locking a mutex multiple times from the same thread fails nicely.
Brian Silvermandc1eb272014-08-19 14:25:59 -040088TEST_F(MutexDeathTest, RepeatLock) {
89 EXPECT_DEATH(
90 {
Austin Schuha0c41ba2020-09-10 22:59:14 -070091 logging::SetImplementation(
92 std::make_shared<util::DeathTestLogImplementation>());
Brian Silverman1dfe48b2014-09-06 16:13:02 -040093 ASSERT_FALSE(test_mutex_.Lock());
94 ASSERT_FALSE(test_mutex_.Lock());
Brian Silvermandc1eb272014-08-19 14:25:59 -040095 },
96 ".*multiple lock.*");
Brian Silverman653491d2014-05-13 16:53:29 -070097}
98
Brian Silverman71c55c52014-08-19 14:31:59 -040099// Tests that Lock behaves correctly when the previous owner exits with the lock
100// held (which is the same as dying any other way).
101TEST_F(MutexTest, OwnerDiedDeathLock) {
102 testing::TestSharedMemory my_shm;
103 Mutex *mutex =
104 static_cast<Mutex *>(shm_malloc_aligned(sizeof(Mutex), alignof(Mutex)));
105 new (mutex) Mutex();
106
Austin Schuha0c41ba2020-09-10 22:59:14 -0700107 std::thread thread([&]() { ASSERT_FALSE(mutex->Lock()); });
Kai Tinkessbf1385d2020-01-18 14:18:49 -0800108 thread.join();
Brian Silverman71c55c52014-08-19 14:31:59 -0400109 EXPECT_TRUE(mutex->Lock());
110
111 mutex->Unlock();
112 mutex->~Mutex();
113}
114
115// Tests that TryLock behaves correctly when the previous owner dies.
116TEST_F(MutexTest, OwnerDiedDeathTryLock) {
117 testing::TestSharedMemory my_shm;
118 Mutex *mutex =
119 static_cast<Mutex *>(shm_malloc_aligned(sizeof(Mutex), alignof(Mutex)));
120 new (mutex) Mutex();
121
Austin Schuha0c41ba2020-09-10 22:59:14 -0700122 std::thread thread([&]() { ASSERT_FALSE(mutex->Lock()); });
Kai Tinkessbf1385d2020-01-18 14:18:49 -0800123 thread.join();
Brian Silverman71c55c52014-08-19 14:31:59 -0400124 EXPECT_EQ(Mutex::State::kOwnerDied, mutex->TryLock());
125
126 mutex->Unlock();
127 mutex->~Mutex();
128}
129
130// TODO(brians): Test owner dying by being SIGKILLed and SIGTERMed.
131
132// This sequence of mutex operations used to mess up the robust list and cause
133// one of the mutexes to not get owner-died like it should.
134TEST_F(MutexTest, DontCorruptRobustList) {
135 // I think this was the allocator lock in the original failure.
136 Mutex mutex1;
137 // This one should get owner-died afterwards (iff the kernel accepts the
138 // robust list and uses it). I think it was the task_death_notification lock
139 // in the original failure.
140 Mutex mutex2;
141
Kai Tinkessbf1385d2020-01-18 14:18:49 -0800142 std::thread thread([&]() {
Brian Silverman71c55c52014-08-19 14:31:59 -0400143 ASSERT_FALSE(mutex1.Lock());
144 ASSERT_FALSE(mutex2.Lock());
145 mutex1.Unlock();
146 });
Kai Tinkessbf1385d2020-01-18 14:18:49 -0800147 thread.join();
Brian Silverman71c55c52014-08-19 14:31:59 -0400148
149 EXPECT_EQ(Mutex::State::kLocked, mutex1.TryLock());
150 EXPECT_EQ(Mutex::State::kOwnerDied, mutex2.TryLock());
151
152 mutex1.Unlock();
153 mutex2.Unlock();
154}
155
Brian Silverman653491d2014-05-13 16:53:29 -0700156// Verifies that ThreadSanitizer understands that a contended mutex establishes
157// a happens-before relationship.
158TEST_F(MutexTest, ThreadSanitizerContended) {
159 int counter = 0;
Kai Tinkessbf1385d2020-01-18 14:18:49 -0800160 std::thread thread1([this, &counter]() {
161 std::this_thread::sleep_for(std::chrono::milliseconds(200));
162 MutexLocker locker(&test_mutex_);
163 ++counter;
164 });
165 std::thread thread2([this, &counter]() {
166 MutexLocker locker(&test_mutex_);
167 ++counter;
168 });
169 thread1.join();
170 thread2.join();
Brian Silverman653491d2014-05-13 16:53:29 -0700171 EXPECT_EQ(2, counter);
172}
173
Brian Silverman119b3b12015-03-29 17:26:05 -0400174// Verifiers that ThreadSanitizer understands how a mutex works.
175// For some reason this used to fail when the other tests didn't...
Brian Silverman71c55c52014-08-19 14:31:59 -0400176// The loops make it fail more reliably when it's going to.
Brian Silverman119b3b12015-03-29 17:26:05 -0400177TEST_F(MutexTest, ThreadSanitizerMutexLocker) {
Brian Silverman71c55c52014-08-19 14:31:59 -0400178 for (int i = 0; i < 100; ++i) {
179 int counter = 0;
180 ::std::thread thread([&counter, this]() {
181 for (int i = 0; i < 300; ++i) {
182 MutexLocker locker(&test_mutex_);
183 ++counter;
184 }
185 });
186 for (int i = 0; i < 300; ++i) {
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400187 MutexLocker locker(&test_mutex_);
Brian Silverman71c55c52014-08-19 14:31:59 -0400188 --counter;
Brian Silverman119b3b12015-03-29 17:26:05 -0400189 }
Brian Silverman71c55c52014-08-19 14:31:59 -0400190 thread.join();
191 EXPECT_EQ(0, counter);
Brian Silverman119b3b12015-03-29 17:26:05 -0400192 }
Brian Silverman119b3b12015-03-29 17:26:05 -0400193}
194
Brian Silverman653491d2014-05-13 16:53:29 -0700195// Verifies that ThreadSanitizer understands that an uncontended mutex
196// establishes a happens-before relationship.
197TEST_F(MutexTest, ThreadSanitizerUncontended) {
198 int counter = 0;
Kai Tinkessbf1385d2020-01-18 14:18:49 -0800199 std::thread thread1([this, &counter]() {
200 MutexLocker locker(&test_mutex_);
201 ++counter;
202 });
203 std::thread thread2([this, &counter]() {
204 std::this_thread::sleep_for(std::chrono::milliseconds(200));
205 MutexLocker locker(&test_mutex_);
206 ++counter;
207 });
208 thread1.join();
209 thread2.join();
Brian Silverman653491d2014-05-13 16:53:29 -0700210 EXPECT_EQ(2, counter);
211}
212
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400213// Makes sure that we don't SIGSEGV or something with multiple threads.
214TEST_F(MutexTest, MultiThreadedLock) {
Kai Tinkessbf1385d2020-01-18 14:18:49 -0800215 std::thread thread([this] {
216 ASSERT_FALSE(test_mutex_.Lock());
217 test_mutex_.Unlock();
218 });
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400219 ASSERT_FALSE(test_mutex_.Lock());
220 test_mutex_.Unlock();
Kai Tinkessbf1385d2020-01-18 14:18:49 -0800221 thread.join();
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400222}
223
Brian Silvermandc1eb272014-08-19 14:25:59 -0400224TEST_F(MutexLockerTest, Basic) {
225 {
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400226 aos::MutexLocker locker(&test_mutex_);
Daniel Petti88a15662015-04-12 17:42:22 -0400227 EXPECT_EQ(Mutex::State::kLockFailed, test_mutex_.TryLock());
Brian Silvermandc1eb272014-08-19 14:25:59 -0400228 }
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400229 EXPECT_EQ(Mutex::State::kLocked, test_mutex_.TryLock());
Brian Silvermandc1eb272014-08-19 14:25:59 -0400230
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400231 test_mutex_.Unlock();
Brian Silvermandc1eb272014-08-19 14:25:59 -0400232}
233
Brian Silverman71c55c52014-08-19 14:31:59 -0400234// Tests that MutexLocker behaves correctly when the previous owner dies.
235TEST_F(MutexLockerDeathTest, OwnerDied) {
236 testing::TestSharedMemory my_shm;
237 Mutex *mutex =
238 static_cast<Mutex *>(shm_malloc_aligned(sizeof(Mutex), alignof(Mutex)));
239 new (mutex) Mutex();
240
Brian Silverman71c55c52014-08-19 14:31:59 -0400241 EXPECT_DEATH(
242 {
Austin Schuh405ee6b2024-04-05 12:22:14 -0700243 std::thread thread([&]() { ASSERT_FALSE(mutex->Lock()); });
244 thread.join();
245
Austin Schuha0c41ba2020-09-10 22:59:14 -0700246 logging::SetImplementation(
247 std::make_shared<util::DeathTestLogImplementation>());
Brian Silverman71c55c52014-08-19 14:31:59 -0400248 MutexLocker locker(mutex);
249 },
250 ".*previous owner of mutex [^ ]+ died.*");
251
252 mutex->~Mutex();
253}
254
Brian Silvermandc1eb272014-08-19 14:25:59 -0400255TEST_F(IPCMutexLockerTest, Basic) {
256 {
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400257 aos::IPCMutexLocker locker(&test_mutex_);
Daniel Petti88a15662015-04-12 17:42:22 -0400258 EXPECT_EQ(Mutex::State::kLockFailed, test_mutex_.TryLock());
Brian Silvermandc1eb272014-08-19 14:25:59 -0400259 EXPECT_FALSE(locker.owner_died());
260 }
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400261 EXPECT_EQ(Mutex::State::kLocked, test_mutex_.TryLock());
Brian Silvermandc1eb272014-08-19 14:25:59 -0400262
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400263 test_mutex_.Unlock();
Brian Silvermandc1eb272014-08-19 14:25:59 -0400264}
265
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400266// Tests what happens when the caller doesn't check if the previous owner died
267// with an IPCMutexLocker.
Brian Silvermandc1eb272014-08-19 14:25:59 -0400268TEST_F(IPCMutexLockerDeathTest, NoCheckOwnerDied) {
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400269 EXPECT_DEATH({ aos::IPCMutexLocker locker(&test_mutex_); },
Brian Silvermandc1eb272014-08-19 14:25:59 -0400270 "nobody checked if the previous owner of mutex [^ ]+ died.*");
271}
272
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400273TEST_F(IPCRecursiveMutexLockerTest, Basic) {
274 {
275 aos::IPCRecursiveMutexLocker locker(&test_mutex_);
Daniel Petti88a15662015-04-12 17:42:22 -0400276 EXPECT_EQ(Mutex::State::kLockFailed, test_mutex_.TryLock());
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400277 EXPECT_FALSE(locker.owner_died());
278 }
279 EXPECT_EQ(Mutex::State::kLocked, test_mutex_.TryLock());
280
281 test_mutex_.Unlock();
282}
283
284// Tests actually locking a mutex recursively with IPCRecursiveMutexLocker.
285TEST_F(IPCRecursiveMutexLockerTest, RecursiveLock) {
286 {
287 aos::IPCRecursiveMutexLocker locker(&test_mutex_);
Daniel Petti88a15662015-04-12 17:42:22 -0400288 EXPECT_EQ(Mutex::State::kLockFailed, test_mutex_.TryLock());
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400289 {
290 aos::IPCRecursiveMutexLocker locker(&test_mutex_);
Daniel Petti88a15662015-04-12 17:42:22 -0400291 EXPECT_EQ(Mutex::State::kLockFailed, test_mutex_.TryLock());
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400292 EXPECT_FALSE(locker.owner_died());
293 }
Daniel Petti88a15662015-04-12 17:42:22 -0400294 EXPECT_EQ(Mutex::State::kLockFailed, test_mutex_.TryLock());
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400295 EXPECT_FALSE(locker.owner_died());
296 }
297 EXPECT_EQ(Mutex::State::kLocked, test_mutex_.TryLock());
298
299 test_mutex_.Unlock();
300}
301
Brian Silverman71c55c52014-08-19 14:31:59 -0400302// Tests that IPCMutexLocker behaves correctly when the previous owner dies.
303TEST_F(IPCMutexLockerTest, OwnerDied) {
304 testing::TestSharedMemory my_shm;
305 Mutex *mutex =
306 static_cast<Mutex *>(shm_malloc_aligned(sizeof(Mutex), alignof(Mutex)));
307 new (mutex) Mutex();
308
Austin Schuha0c41ba2020-09-10 22:59:14 -0700309 std::thread thread([&]() { ASSERT_FALSE(mutex->Lock()); });
Kai Tinkessbf1385d2020-01-18 14:18:49 -0800310 thread.join();
Brian Silverman71c55c52014-08-19 14:31:59 -0400311 {
312 aos::IPCMutexLocker locker(mutex);
313 EXPECT_EQ(Mutex::State::kLockFailed, mutex->TryLock());
314 EXPECT_TRUE(locker.owner_died());
315 }
316 EXPECT_EQ(Mutex::State::kLocked, mutex->TryLock());
317
318 mutex->Unlock();
319 mutex->~Mutex();
320}
321
Stephan Pleinesf63bde82024-01-13 15:59:33 -0800322} // namespace aos::testing