blob: 54e9201395b5bcc0ba0480330e9ce565c60b8ee1 [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 <pthread.h>
Austin Schuhf2a50ba2016-12-24 16:16:26 -08004#include <sched.h>
Brian Silverman653491d2014-05-13 16:53:29 -07005
Austin Schuhf2a50ba2016-12-24 16:16:26 -08006#include <chrono>
Tyler Chatowbf0609c2021-07-31 16:13:27 -07007#include <cmath>
Brian Silverman119b3b12015-03-29 17:26:05 -04008#include <thread>
9
Philipp Schrader790cb542023-07-05 21:06:52 -070010#include "gtest/gtest.h"
11
John Park33858a32018-09-28 23:05:48 -070012#include "aos/die.h"
John Park398c74a2018-10-20 21:17:39 -070013#include "aos/ipc_lib/aos_sync.h"
14#include "aos/ipc_lib/core_lib.h"
Brian Silvermanf5f8d8e2015-12-06 18:39:12 -050015#include "aos/testing/test_logging.h"
Brian Silverman71c55c52014-08-19 14:31:59 -040016#include "aos/testing/test_shm.h"
Alex Perrycb7da4b2019-08-28 19:35:56 -070017#include "aos/time/time.h"
18#include "aos/util/death_test_log_implementation.h"
Brian Silverman653491d2014-05-13 16:53:29 -070019
Stephan Pleinesf63bde82024-01-13 15:59:33 -080020namespace aos::testing {
Brian Silverman653491d2014-05-13 16:53:29 -070021
Austin Schuhf2a50ba2016-12-24 16:16:26 -080022namespace chrono = ::std::chrono;
23namespace this_thread = ::std::this_thread;
24
Brian Silverman653491d2014-05-13 16:53:29 -070025class MutexTest : public ::testing::Test {
26 public:
Brian Silverman1dfe48b2014-09-06 16:13:02 -040027 Mutex test_mutex_;
Brian Silverman653491d2014-05-13 16:53:29 -070028
29 protected:
30 void SetUp() override {
Brian Silvermanf5f8d8e2015-12-06 18:39:12 -050031 ::aos::testing::EnableTestLogging();
Brian Silverman653491d2014-05-13 16:53:29 -070032 SetDieTestMode(true);
33 }
34};
35
36typedef MutexTest MutexDeathTest;
Brian Silvermandc1eb272014-08-19 14:25:59 -040037typedef MutexTest MutexLockerTest;
38typedef MutexTest MutexLockerDeathTest;
39typedef MutexTest IPCMutexLockerTest;
40typedef MutexTest IPCMutexLockerDeathTest;
Brian Silverman1dfe48b2014-09-06 16:13:02 -040041typedef MutexTest IPCRecursiveMutexLockerTest;
Brian Silverman653491d2014-05-13 16:53:29 -070042
43TEST_F(MutexTest, TryLock) {
Brian Silverman1dfe48b2014-09-06 16:13:02 -040044 EXPECT_EQ(Mutex::State::kLocked, test_mutex_.TryLock());
Daniel Petti88a15662015-04-12 17:42:22 -040045 EXPECT_EQ(Mutex::State::kLockFailed, test_mutex_.TryLock());
Brian Silvermandc1eb272014-08-19 14:25:59 -040046
Brian Silverman1dfe48b2014-09-06 16:13:02 -040047 test_mutex_.Unlock();
Brian Silverman653491d2014-05-13 16:53:29 -070048}
49
50TEST_F(MutexTest, Lock) {
Brian Silverman1dfe48b2014-09-06 16:13:02 -040051 ASSERT_FALSE(test_mutex_.Lock());
Daniel Petti88a15662015-04-12 17:42:22 -040052 EXPECT_EQ(Mutex::State::kLockFailed, test_mutex_.TryLock());
Brian Silvermandc1eb272014-08-19 14:25:59 -040053
Brian Silverman1dfe48b2014-09-06 16:13:02 -040054 test_mutex_.Unlock();
Brian Silverman653491d2014-05-13 16:53:29 -070055}
56
57TEST_F(MutexTest, Unlock) {
Brian Silverman1dfe48b2014-09-06 16:13:02 -040058 ASSERT_FALSE(test_mutex_.Lock());
Daniel Petti88a15662015-04-12 17:42:22 -040059 EXPECT_EQ(Mutex::State::kLockFailed, test_mutex_.TryLock());
Brian Silverman1dfe48b2014-09-06 16:13:02 -040060 test_mutex_.Unlock();
61 EXPECT_EQ(Mutex::State::kLocked, test_mutex_.TryLock());
Brian Silvermandc1eb272014-08-19 14:25:59 -040062
Brian Silverman1dfe48b2014-09-06 16:13:02 -040063 test_mutex_.Unlock();
Brian Silverman653491d2014-05-13 16:53:29 -070064}
65
66// Sees what happens with multiple unlocks.
67TEST_F(MutexDeathTest, RepeatUnlock) {
Brian Silverman1dfe48b2014-09-06 16:13:02 -040068 ASSERT_FALSE(test_mutex_.Lock());
69 test_mutex_.Unlock();
Brian Silverman653491d2014-05-13 16:53:29 -070070 EXPECT_DEATH(
71 {
Austin Schuha0c41ba2020-09-10 22:59:14 -070072 logging::SetImplementation(
73 std::make_shared<util::DeathTestLogImplementation>());
Brian Silverman1dfe48b2014-09-06 16:13:02 -040074 test_mutex_.Unlock();
Brian Silverman653491d2014-05-13 16:53:29 -070075 },
76 ".*multiple unlock.*");
77}
78
79// Sees what happens if you unlock without ever locking (or unlocking) it.
80TEST_F(MutexDeathTest, NeverLock) {
Brian Silverman653491d2014-05-13 16:53:29 -070081 EXPECT_DEATH(
82 {
Austin Schuha0c41ba2020-09-10 22:59:14 -070083 logging::SetImplementation(
84 std::make_shared<util::DeathTestLogImplementation>());
Brian Silverman1dfe48b2014-09-06 16:13:02 -040085 test_mutex_.Unlock();
Brian Silverman653491d2014-05-13 16:53:29 -070086 },
87 ".*multiple unlock.*");
88}
89
Brian Silverman71c55c52014-08-19 14:31:59 -040090// Tests that locking a mutex multiple times from the same thread fails nicely.
Brian Silvermandc1eb272014-08-19 14:25:59 -040091TEST_F(MutexDeathTest, RepeatLock) {
92 EXPECT_DEATH(
93 {
Austin Schuha0c41ba2020-09-10 22:59:14 -070094 logging::SetImplementation(
95 std::make_shared<util::DeathTestLogImplementation>());
Brian Silverman1dfe48b2014-09-06 16:13:02 -040096 ASSERT_FALSE(test_mutex_.Lock());
97 ASSERT_FALSE(test_mutex_.Lock());
Brian Silvermandc1eb272014-08-19 14:25:59 -040098 },
99 ".*multiple lock.*");
Brian Silverman653491d2014-05-13 16:53:29 -0700100}
101
Brian Silverman71c55c52014-08-19 14:31:59 -0400102// Tests that Lock behaves correctly when the previous owner exits with the lock
103// held (which is the same as dying any other way).
104TEST_F(MutexTest, OwnerDiedDeathLock) {
105 testing::TestSharedMemory my_shm;
106 Mutex *mutex =
107 static_cast<Mutex *>(shm_malloc_aligned(sizeof(Mutex), alignof(Mutex)));
108 new (mutex) Mutex();
109
Austin Schuha0c41ba2020-09-10 22:59:14 -0700110 std::thread thread([&]() { ASSERT_FALSE(mutex->Lock()); });
Kai Tinkessbf1385d2020-01-18 14:18:49 -0800111 thread.join();
Brian Silverman71c55c52014-08-19 14:31:59 -0400112 EXPECT_TRUE(mutex->Lock());
113
114 mutex->Unlock();
115 mutex->~Mutex();
116}
117
118// Tests that TryLock behaves correctly when the previous owner dies.
119TEST_F(MutexTest, OwnerDiedDeathTryLock) {
120 testing::TestSharedMemory my_shm;
121 Mutex *mutex =
122 static_cast<Mutex *>(shm_malloc_aligned(sizeof(Mutex), alignof(Mutex)));
123 new (mutex) Mutex();
124
Austin Schuha0c41ba2020-09-10 22:59:14 -0700125 std::thread thread([&]() { ASSERT_FALSE(mutex->Lock()); });
Kai Tinkessbf1385d2020-01-18 14:18:49 -0800126 thread.join();
Brian Silverman71c55c52014-08-19 14:31:59 -0400127 EXPECT_EQ(Mutex::State::kOwnerDied, mutex->TryLock());
128
129 mutex->Unlock();
130 mutex->~Mutex();
131}
132
133// TODO(brians): Test owner dying by being SIGKILLed and SIGTERMed.
134
135// This sequence of mutex operations used to mess up the robust list and cause
136// one of the mutexes to not get owner-died like it should.
137TEST_F(MutexTest, DontCorruptRobustList) {
138 // I think this was the allocator lock in the original failure.
139 Mutex mutex1;
140 // This one should get owner-died afterwards (iff the kernel accepts the
141 // robust list and uses it). I think it was the task_death_notification lock
142 // in the original failure.
143 Mutex mutex2;
144
Kai Tinkessbf1385d2020-01-18 14:18:49 -0800145 std::thread thread([&]() {
Brian Silverman71c55c52014-08-19 14:31:59 -0400146 ASSERT_FALSE(mutex1.Lock());
147 ASSERT_FALSE(mutex2.Lock());
148 mutex1.Unlock();
149 });
Kai Tinkessbf1385d2020-01-18 14:18:49 -0800150 thread.join();
Brian Silverman71c55c52014-08-19 14:31:59 -0400151
152 EXPECT_EQ(Mutex::State::kLocked, mutex1.TryLock());
153 EXPECT_EQ(Mutex::State::kOwnerDied, mutex2.TryLock());
154
155 mutex1.Unlock();
156 mutex2.Unlock();
157}
158
Brian Silverman653491d2014-05-13 16:53:29 -0700159// Verifies that ThreadSanitizer understands that a contended mutex establishes
160// a happens-before relationship.
161TEST_F(MutexTest, ThreadSanitizerContended) {
162 int counter = 0;
Kai Tinkessbf1385d2020-01-18 14:18:49 -0800163 std::thread thread1([this, &counter]() {
164 std::this_thread::sleep_for(std::chrono::milliseconds(200));
165 MutexLocker locker(&test_mutex_);
166 ++counter;
167 });
168 std::thread thread2([this, &counter]() {
169 MutexLocker locker(&test_mutex_);
170 ++counter;
171 });
172 thread1.join();
173 thread2.join();
Brian Silverman653491d2014-05-13 16:53:29 -0700174 EXPECT_EQ(2, counter);
175}
176
Brian Silverman119b3b12015-03-29 17:26:05 -0400177// Verifiers that ThreadSanitizer understands how a mutex works.
178// For some reason this used to fail when the other tests didn't...
Brian Silverman71c55c52014-08-19 14:31:59 -0400179// The loops make it fail more reliably when it's going to.
Brian Silverman119b3b12015-03-29 17:26:05 -0400180TEST_F(MutexTest, ThreadSanitizerMutexLocker) {
Brian Silverman71c55c52014-08-19 14:31:59 -0400181 for (int i = 0; i < 100; ++i) {
182 int counter = 0;
183 ::std::thread thread([&counter, this]() {
184 for (int i = 0; i < 300; ++i) {
185 MutexLocker locker(&test_mutex_);
186 ++counter;
187 }
188 });
189 for (int i = 0; i < 300; ++i) {
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400190 MutexLocker locker(&test_mutex_);
Brian Silverman71c55c52014-08-19 14:31:59 -0400191 --counter;
Brian Silverman119b3b12015-03-29 17:26:05 -0400192 }
Brian Silverman71c55c52014-08-19 14:31:59 -0400193 thread.join();
194 EXPECT_EQ(0, counter);
Brian Silverman119b3b12015-03-29 17:26:05 -0400195 }
Brian Silverman119b3b12015-03-29 17:26:05 -0400196}
197
Brian Silverman653491d2014-05-13 16:53:29 -0700198// Verifies that ThreadSanitizer understands that an uncontended mutex
199// establishes a happens-before relationship.
200TEST_F(MutexTest, ThreadSanitizerUncontended) {
201 int counter = 0;
Kai Tinkessbf1385d2020-01-18 14:18:49 -0800202 std::thread thread1([this, &counter]() {
203 MutexLocker locker(&test_mutex_);
204 ++counter;
205 });
206 std::thread thread2([this, &counter]() {
207 std::this_thread::sleep_for(std::chrono::milliseconds(200));
208 MutexLocker locker(&test_mutex_);
209 ++counter;
210 });
211 thread1.join();
212 thread2.join();
Brian Silverman653491d2014-05-13 16:53:29 -0700213 EXPECT_EQ(2, counter);
214}
215
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400216// Makes sure that we don't SIGSEGV or something with multiple threads.
217TEST_F(MutexTest, MultiThreadedLock) {
Kai Tinkessbf1385d2020-01-18 14:18:49 -0800218 std::thread thread([this] {
219 ASSERT_FALSE(test_mutex_.Lock());
220 test_mutex_.Unlock();
221 });
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400222 ASSERT_FALSE(test_mutex_.Lock());
223 test_mutex_.Unlock();
Kai Tinkessbf1385d2020-01-18 14:18:49 -0800224 thread.join();
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400225}
226
Brian Silvermandc1eb272014-08-19 14:25:59 -0400227TEST_F(MutexLockerTest, Basic) {
228 {
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400229 aos::MutexLocker locker(&test_mutex_);
Daniel Petti88a15662015-04-12 17:42:22 -0400230 EXPECT_EQ(Mutex::State::kLockFailed, test_mutex_.TryLock());
Brian Silvermandc1eb272014-08-19 14:25:59 -0400231 }
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400232 EXPECT_EQ(Mutex::State::kLocked, test_mutex_.TryLock());
Brian Silvermandc1eb272014-08-19 14:25:59 -0400233
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400234 test_mutex_.Unlock();
Brian Silvermandc1eb272014-08-19 14:25:59 -0400235}
236
Brian Silverman71c55c52014-08-19 14:31:59 -0400237// Tests that MutexLocker behaves correctly when the previous owner dies.
238TEST_F(MutexLockerDeathTest, OwnerDied) {
239 testing::TestSharedMemory my_shm;
240 Mutex *mutex =
241 static_cast<Mutex *>(shm_malloc_aligned(sizeof(Mutex), alignof(Mutex)));
242 new (mutex) Mutex();
243
Austin Schuha0c41ba2020-09-10 22:59:14 -0700244 std::thread thread([&]() { ASSERT_FALSE(mutex->Lock()); });
Kai Tinkessbf1385d2020-01-18 14:18:49 -0800245 thread.join();
Brian Silverman71c55c52014-08-19 14:31:59 -0400246 EXPECT_DEATH(
247 {
Austin Schuha0c41ba2020-09-10 22:59:14 -0700248 logging::SetImplementation(
249 std::make_shared<util::DeathTestLogImplementation>());
Brian Silverman71c55c52014-08-19 14:31:59 -0400250 MutexLocker locker(mutex);
251 },
252 ".*previous owner of mutex [^ ]+ died.*");
253
254 mutex->~Mutex();
255}
256
Brian Silvermandc1eb272014-08-19 14:25:59 -0400257TEST_F(IPCMutexLockerTest, Basic) {
258 {
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400259 aos::IPCMutexLocker locker(&test_mutex_);
Daniel Petti88a15662015-04-12 17:42:22 -0400260 EXPECT_EQ(Mutex::State::kLockFailed, test_mutex_.TryLock());
Brian Silvermandc1eb272014-08-19 14:25:59 -0400261 EXPECT_FALSE(locker.owner_died());
262 }
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400263 EXPECT_EQ(Mutex::State::kLocked, test_mutex_.TryLock());
Brian Silvermandc1eb272014-08-19 14:25:59 -0400264
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400265 test_mutex_.Unlock();
Brian Silvermandc1eb272014-08-19 14:25:59 -0400266}
267
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400268// Tests what happens when the caller doesn't check if the previous owner died
269// with an IPCMutexLocker.
Brian Silvermandc1eb272014-08-19 14:25:59 -0400270TEST_F(IPCMutexLockerDeathTest, NoCheckOwnerDied) {
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400271 EXPECT_DEATH({ aos::IPCMutexLocker locker(&test_mutex_); },
Brian Silvermandc1eb272014-08-19 14:25:59 -0400272 "nobody checked if the previous owner of mutex [^ ]+ died.*");
273}
274
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400275TEST_F(IPCRecursiveMutexLockerTest, Basic) {
276 {
277 aos::IPCRecursiveMutexLocker locker(&test_mutex_);
Daniel Petti88a15662015-04-12 17:42:22 -0400278 EXPECT_EQ(Mutex::State::kLockFailed, test_mutex_.TryLock());
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400279 EXPECT_FALSE(locker.owner_died());
280 }
281 EXPECT_EQ(Mutex::State::kLocked, test_mutex_.TryLock());
282
283 test_mutex_.Unlock();
284}
285
286// Tests actually locking a mutex recursively with IPCRecursiveMutexLocker.
287TEST_F(IPCRecursiveMutexLockerTest, RecursiveLock) {
288 {
289 aos::IPCRecursiveMutexLocker locker(&test_mutex_);
Daniel Petti88a15662015-04-12 17:42:22 -0400290 EXPECT_EQ(Mutex::State::kLockFailed, test_mutex_.TryLock());
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400291 {
292 aos::IPCRecursiveMutexLocker locker(&test_mutex_);
Daniel Petti88a15662015-04-12 17:42:22 -0400293 EXPECT_EQ(Mutex::State::kLockFailed, test_mutex_.TryLock());
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400294 EXPECT_FALSE(locker.owner_died());
295 }
Daniel Petti88a15662015-04-12 17:42:22 -0400296 EXPECT_EQ(Mutex::State::kLockFailed, test_mutex_.TryLock());
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400297 EXPECT_FALSE(locker.owner_died());
298 }
299 EXPECT_EQ(Mutex::State::kLocked, test_mutex_.TryLock());
300
301 test_mutex_.Unlock();
302}
303
Brian Silverman71c55c52014-08-19 14:31:59 -0400304// Tests that IPCMutexLocker behaves correctly when the previous owner dies.
305TEST_F(IPCMutexLockerTest, OwnerDied) {
306 testing::TestSharedMemory my_shm;
307 Mutex *mutex =
308 static_cast<Mutex *>(shm_malloc_aligned(sizeof(Mutex), alignof(Mutex)));
309 new (mutex) Mutex();
310
Austin Schuha0c41ba2020-09-10 22:59:14 -0700311 std::thread thread([&]() { ASSERT_FALSE(mutex->Lock()); });
Kai Tinkessbf1385d2020-01-18 14:18:49 -0800312 thread.join();
Brian Silverman71c55c52014-08-19 14:31:59 -0400313 {
314 aos::IPCMutexLocker locker(mutex);
315 EXPECT_EQ(Mutex::State::kLockFailed, mutex->TryLock());
316 EXPECT_TRUE(locker.owner_died());
317 }
318 EXPECT_EQ(Mutex::State::kLocked, mutex->TryLock());
319
320 mutex->Unlock();
321 mutex->~Mutex();
322}
323
Stephan Pleinesf63bde82024-01-13 15:59:33 -0800324} // namespace aos::testing