blob: 8d10b26ea16ee99a0235eec2b7b74d3e7a57cd0f [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
Brian Silverman71c55c52014-08-19 14:31:59 -0400244 EXPECT_DEATH(
245 {
Austin Schuh405ee6b2024-04-05 12:22:14 -0700246 std::thread thread([&]() { ASSERT_FALSE(mutex->Lock()); });
247 thread.join();
248
Austin Schuha0c41ba2020-09-10 22:59:14 -0700249 logging::SetImplementation(
250 std::make_shared<util::DeathTestLogImplementation>());
Brian Silverman71c55c52014-08-19 14:31:59 -0400251 MutexLocker locker(mutex);
252 },
253 ".*previous owner of mutex [^ ]+ died.*");
254
255 mutex->~Mutex();
256}
257
Brian Silvermandc1eb272014-08-19 14:25:59 -0400258TEST_F(IPCMutexLockerTest, Basic) {
259 {
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400260 aos::IPCMutexLocker locker(&test_mutex_);
Daniel Petti88a15662015-04-12 17:42:22 -0400261 EXPECT_EQ(Mutex::State::kLockFailed, test_mutex_.TryLock());
Brian Silvermandc1eb272014-08-19 14:25:59 -0400262 EXPECT_FALSE(locker.owner_died());
263 }
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400264 EXPECT_EQ(Mutex::State::kLocked, test_mutex_.TryLock());
Brian Silvermandc1eb272014-08-19 14:25:59 -0400265
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400266 test_mutex_.Unlock();
Brian Silvermandc1eb272014-08-19 14:25:59 -0400267}
268
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400269// Tests what happens when the caller doesn't check if the previous owner died
270// with an IPCMutexLocker.
Brian Silvermandc1eb272014-08-19 14:25:59 -0400271TEST_F(IPCMutexLockerDeathTest, NoCheckOwnerDied) {
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400272 EXPECT_DEATH({ aos::IPCMutexLocker locker(&test_mutex_); },
Brian Silvermandc1eb272014-08-19 14:25:59 -0400273 "nobody checked if the previous owner of mutex [^ ]+ died.*");
274}
275
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400276TEST_F(IPCRecursiveMutexLockerTest, Basic) {
277 {
278 aos::IPCRecursiveMutexLocker locker(&test_mutex_);
Daniel Petti88a15662015-04-12 17:42:22 -0400279 EXPECT_EQ(Mutex::State::kLockFailed, test_mutex_.TryLock());
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400280 EXPECT_FALSE(locker.owner_died());
281 }
282 EXPECT_EQ(Mutex::State::kLocked, test_mutex_.TryLock());
283
284 test_mutex_.Unlock();
285}
286
287// Tests actually locking a mutex recursively with IPCRecursiveMutexLocker.
288TEST_F(IPCRecursiveMutexLockerTest, RecursiveLock) {
289 {
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 {
293 aos::IPCRecursiveMutexLocker locker(&test_mutex_);
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 }
Daniel Petti88a15662015-04-12 17:42:22 -0400297 EXPECT_EQ(Mutex::State::kLockFailed, test_mutex_.TryLock());
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400298 EXPECT_FALSE(locker.owner_died());
299 }
300 EXPECT_EQ(Mutex::State::kLocked, test_mutex_.TryLock());
301
302 test_mutex_.Unlock();
303}
304
Brian Silverman71c55c52014-08-19 14:31:59 -0400305// Tests that IPCMutexLocker behaves correctly when the previous owner dies.
306TEST_F(IPCMutexLockerTest, OwnerDied) {
307 testing::TestSharedMemory my_shm;
308 Mutex *mutex =
309 static_cast<Mutex *>(shm_malloc_aligned(sizeof(Mutex), alignof(Mutex)));
310 new (mutex) Mutex();
311
Austin Schuha0c41ba2020-09-10 22:59:14 -0700312 std::thread thread([&]() { ASSERT_FALSE(mutex->Lock()); });
Kai Tinkessbf1385d2020-01-18 14:18:49 -0800313 thread.join();
Brian Silverman71c55c52014-08-19 14:31:59 -0400314 {
315 aos::IPCMutexLocker locker(mutex);
316 EXPECT_EQ(Mutex::State::kLockFailed, mutex->TryLock());
317 EXPECT_TRUE(locker.owner_died());
318 }
319 EXPECT_EQ(Mutex::State::kLocked, mutex->TryLock());
320
321 mutex->Unlock();
322 mutex->~Mutex();
323}
324
Stephan Pleinesf63bde82024-01-13 15:59:33 -0800325} // namespace aos::testing