blob: d9327a1882e4e9bfab3aee0e15a30c5480de5954 [file] [log] [blame]
brians343bc112013-02-10 01:53:46 +00001#include "aos/common/mutex.h"
2
Brian Silvermand41b4422013-09-01 14:02:33 -07003#include <sched.h>
4#include <math.h>
5#include <pthread.h>
6#ifdef __VXWORKS__
7#include <taskLib.h>
8#endif
9
brians343bc112013-02-10 01:53:46 +000010#include "gtest/gtest.h"
11
Brian Silverman08661c72013-09-01 17:24:38 -070012#include "aos/atom_code/ipc_lib/aos_sync.h"
Brian Silvermand41b4422013-09-01 14:02:33 -070013
brians343bc112013-02-10 01:53:46 +000014namespace aos {
15namespace testing {
16
17class MutexTest : public ::testing::Test {
18 public:
19 Mutex test_mutex;
20};
21
22typedef MutexTest MutexDeathTest;
23
24TEST_F(MutexTest, TryLock) {
25 EXPECT_TRUE(test_mutex.TryLock());
26 EXPECT_FALSE(test_mutex.TryLock());
27}
28
29TEST_F(MutexTest, Lock) {
30 test_mutex.Lock();
31 EXPECT_FALSE(test_mutex.TryLock());
32}
33
34TEST_F(MutexTest, Unlock) {
35 test_mutex.Lock();
36 EXPECT_FALSE(test_mutex.TryLock());
37 test_mutex.Unlock();
38 EXPECT_TRUE(test_mutex.TryLock());
39}
40
41#ifndef __VXWORKS__
42// Sees what happens with multiple unlocks.
43TEST_F(MutexDeathTest, RepeatUnlock) {
44 test_mutex.Lock();
45 test_mutex.Unlock();
46 EXPECT_DEATH(test_mutex.Unlock(), ".*multiple unlock.*");
47}
48
49// Sees what happens if you unlock without ever locking (or unlocking) it.
50TEST_F(MutexDeathTest, NeverLock) {
51 EXPECT_DEATH(test_mutex.Unlock(), ".*multiple unlock.*");
52}
53#endif
54
55TEST_F(MutexTest, MutexLocker) {
56 {
57 aos::MutexLocker locker(&test_mutex);
58 EXPECT_FALSE(test_mutex.TryLock());
59 }
60 EXPECT_TRUE(test_mutex.TryLock());
61}
Brian Silverman797e71e2013-09-06 17:29:39 -070062
Brian Silvermand41b4422013-09-01 14:02:33 -070063TEST_F(MutexTest, MutexUnlocker) {
64 test_mutex.Lock();
65 {
66 aos::MutexUnlocker unlocker(&test_mutex);
67 // If this fails, then something weird is going on and the next line might
Brian Silverman797e71e2013-09-06 17:29:39 -070068 // hang, so fail immediately.
Brian Silvermand41b4422013-09-01 14:02:33 -070069 ASSERT_TRUE(test_mutex.TryLock());
70 test_mutex.Unlock();
71 }
Brian Silverman08661c72013-09-01 17:24:38 -070072 EXPECT_FALSE(test_mutex.TryLock());
Brian Silvermand41b4422013-09-01 14:02:33 -070073}
74
75// A worker thread for testing the fairness of the mutex implementation.
76class MutexFairnessWorkerThread {
77 public:
78 MutexFairnessWorkerThread(int *cycles, int index,
Brian Silverman08661c72013-09-01 17:24:38 -070079 Mutex *in_mutex, mutex *start)
80 : cycles_(cycles), index_(index), mutex_(in_mutex), start_(start) {}
Brian Silvermand41b4422013-09-01 14:02:33 -070081
82 static void *RunStatic(void *self_in) {
83 MutexFairnessWorkerThread *self =
84 static_cast<MutexFairnessWorkerThread *>(self_in);
85 self->Run();
86 delete self;
87 return NULL;
88 }
89
90 static void Reset(int cycles) {
91 cyclesRun = 0;
92 totalCycles = cycles;
93 }
94
95 private:
96 void Run() {
97 cycles_[index_] = 0;
Brian Silverman08661c72013-09-01 17:24:38 -070098 ASSERT_EQ(futex_wait(start_), 0);
Brian Silvermand41b4422013-09-01 14:02:33 -070099 while (cyclesRun < totalCycles) {
100 {
101 MutexLocker locker(mutex_);
102 ++cyclesRun;
103 }
104 ++cycles_[index_];
105 // Otherwise the fitpc implementation tends to just relock in the same
106 // thread.
107 sched_yield();
108 }
109
110#ifdef __VXWORKS__
111 // Without this, all of the "task ... deleted ..." messages come out at
112 // once, and it looks weird and triggers an socat bug (at least for
113 // Squeeze's version 1.7.1.3-1).
114 taskDelay(index_);
115#endif
116 }
117
118 int *cycles_;
119 int index_;
120 Mutex *mutex_;
Brian Silverman08661c72013-09-01 17:24:38 -0700121 mutex *start_;
Brian Silvermand41b4422013-09-01 14:02:33 -0700122 static int cyclesRun, totalCycles;
123};
124int MutexFairnessWorkerThread::cyclesRun;
125int MutexFairnessWorkerThread::totalCycles;
Brian Silverman797e71e2013-09-06 17:29:39 -0700126
Brian Silvermand41b4422013-09-01 14:02:33 -0700127// Tests the fairness of the implementation. It does this by repeatedly locking
128// and unlocking a mutex in multiple threads and then checking the standard
129// deviation of the number of times each one locks.
130//
131// It is safe to do this with threads because this is the test so it can change
132// if the implementations ever change to not support that. Fitpc logging calls
133// are not thread-safe, but it doesn't really matter because the only logging
134// call that would get made would be a LOG(FATAL) that would still terminate the
135// process.
136TEST_F(MutexTest, Fairness) {
137 static const int kThreads = 13;
138#ifdef __VXWORKS__
139 static const int kWarmupCycles = 1000, kRunCycles = 60000, kMaxDeviation = 20;
140#else
141 static const int kWarmupCycles = 30000, kRunCycles = 3000000, kMaxDeviation = 10000;
142#endif
143
144 int cycles[kThreads];
145 pthread_t workers[kThreads];
Brian Silverman08661c72013-09-01 17:24:38 -0700146 mutex start = 0;
Brian Silvermand41b4422013-09-01 14:02:33 -0700147
148 for (int repeats = 0; repeats < 2; ++repeats) {
Brian Silverman08661c72013-09-01 17:24:38 -0700149 futex_unset(&start);
Brian Silvermand41b4422013-09-01 14:02:33 -0700150 MutexFairnessWorkerThread::Reset(repeats ? kRunCycles : kWarmupCycles);
Brian Silvermand41b4422013-09-01 14:02:33 -0700151 for (int i = 0; i < kThreads; ++i) {
152 MutexFairnessWorkerThread *c = new MutexFairnessWorkerThread(cycles, i,
153 &test_mutex,
154 &start);
155 ASSERT_EQ(0, pthread_create(&workers[i], NULL,
156 MutexFairnessWorkerThread::RunStatic, c));
157 }
Brian Silverman08661c72013-09-01 17:24:38 -0700158 futex_set(&start);
Brian Silvermand41b4422013-09-01 14:02:33 -0700159 for (int i = 0; i < kThreads; ++i) {
160 ASSERT_EQ(0, pthread_join(workers[i], NULL));
161 }
162 }
163
164 double variance = 0;
165 int expected = kRunCycles / kThreads;
166 for (int i = 0; i < kThreads; ++i) {
167 variance += (cycles[i] - expected) * (cycles[i] - expected);
168 }
169 double deviation = sqrt(variance / kThreads);
170 printf("deviation=%f\n", deviation);
171 ASSERT_GT(deviation, 0);
172 EXPECT_LT(deviation, kMaxDeviation);
173}
brians343bc112013-02-10 01:53:46 +0000174
175} // namespace testing
176} // namespace aos