Left: | ||
Right: |
LEFT | RIGHT |
---|---|
1 #include <gtest/gtest.h> | 1 #include <gtest/gtest.h> |
2 #include <queue> | 2 #include <queue> |
3 #include <vector> | |
3 | 4 |
4 #include "../src/Thread.h" | 5 #include "../src/Thread.h" |
5 | 6 |
6 namespace | 7 namespace |
7 { | 8 { |
8 #ifndef WIN32 | 9 #ifndef WIN32 |
9 void Sleep(const int millis) | 10 void Sleep(const int millis) |
10 { | 11 { |
11 usleep(millis * 1000); | 12 usleep(millis * 1000); |
12 } | 13 } |
13 #endif | 14 #endif |
14 | 15 |
15 class Mock : public AdblockPlus::Thread | 16 class Mock : public AdblockPlus::Thread |
16 { | 17 { |
17 public: | 18 public: |
18 int timesCalled; | 19 int timesCalled; |
20 AdblockPlus::Mutex mutex; | |
19 | 21 |
20 Mock() : timesCalled(0) | 22 Mock() : timesCalled(0) |
21 { | 23 { |
22 } | 24 } |
23 | 25 |
24 void Run() | 26 void Run() |
25 { | 27 { |
28 Sleep(5); | |
29 mutex.Lock(); | |
26 timesCalled++; | 30 timesCalled++; |
Wladimir Palant
2013/04/03 13:14:47
This statement isn't thread-safe, you need a mutex
Felix Dahlke
2013/04/03 16:27:59
I thought that's pretty unlikely here, but fair en
| |
31 mutex.Unlock(); | |
27 } | 32 } |
28 }; | 33 }; |
29 | 34 |
30 class LockingMock : public AdblockPlus::Thread | 35 class LockingMock : public AdblockPlus::Thread |
31 { | 36 { |
32 public: | 37 public: |
33 bool working; | 38 bool working; |
34 | 39 |
35 LockingMock(AdblockPlus::Thread::Mutex& mutex, const int millisToSleep) | 40 LockingMock(std::vector<std::string>& log, AdblockPlus::Mutex& logMutex) |
36 : mutex(mutex), millisToSleep(millisToSleep) | 41 : log(log), logMutex(logMutex) |
37 { | 42 { |
38 } | 43 } |
39 | 44 |
40 void Run() | 45 void Run() |
41 { | 46 { |
42 mutex.Lock(); | 47 logMutex.Lock(); |
43 working = true; | 48 log.push_back("started"); |
44 Sleep(millisToSleep); | 49 Sleep(5); |
45 working = false; | 50 log.push_back("ended"); |
46 mutex.Unlock(); | 51 logMutex.Unlock(); |
47 } | 52 } |
48 | 53 |
49 private: | 54 private: |
50 AdblockPlus::Thread::Mutex& mutex; | 55 const std::string name; |
51 int millisToSleep; | 56 std::vector<std::string>& log; |
57 AdblockPlus::Mutex& logMutex; | |
52 }; | 58 }; |
53 | 59 |
54 class Enqueuer : public AdblockPlus::Thread | 60 class Enqueuer : public AdblockPlus::Thread |
55 { | 61 { |
56 public: | 62 public: |
57 Enqueuer(std::queue<int>& queue, AdblockPlus::Thread::Mutex& mutex, | 63 Enqueuer(std::queue<int>& queue, AdblockPlus::Mutex& queueMutex, |
58 AdblockPlus::Thread::Condition& notEmpty) | 64 AdblockPlus::ConditionVariable& notEmpty) |
59 : queue(queue), mutex(mutex), notEmpty(notEmpty) | 65 : queue(queue), queueMutex(queueMutex), notEmpty(notEmpty) |
60 { | 66 { |
61 } | 67 } |
62 | 68 |
63 void Run() | 69 void Run() |
64 { | 70 { |
65 Sleep(5); | 71 queueMutex.Lock(); |
66 mutex.Lock(); | |
67 queue.push(1); | 72 queue.push(1); |
68 Sleep(10); | |
69 notEmpty.Signal(); | 73 notEmpty.Signal(); |
70 mutex.Unlock(); | 74 queueMutex.Unlock(); |
71 } | 75 } |
72 | 76 |
73 private: | 77 private: |
74 std::queue<int>& queue; | 78 std::queue<int>& queue; |
75 AdblockPlus::Thread::Mutex& mutex; | 79 AdblockPlus::Mutex& queueMutex; |
76 AdblockPlus::Thread::Condition& notEmpty; | 80 AdblockPlus::ConditionVariable& notEmpty; |
77 }; | 81 }; |
78 | 82 |
79 class Dequeuer : public AdblockPlus::Thread | 83 class Dequeuer : public AdblockPlus::Thread |
80 { | 84 { |
81 public: | 85 public: |
82 Dequeuer(std::queue<int>& queue, AdblockPlus::Thread::Mutex& mutex, | 86 Dequeuer(std::queue<int>& queue, AdblockPlus::Mutex& queueMutex, |
83 AdblockPlus::Thread::Condition& notEmpty) | 87 AdblockPlus::ConditionVariable& notEmpty) |
84 : queue(queue), mutex(mutex), notEmpty(notEmpty) | 88 : queue(queue), queueMutex(queueMutex), notEmpty(notEmpty) |
85 { | 89 { |
86 } | 90 } |
87 | 91 |
88 void Run() | 92 void Run() |
89 { | 93 { |
90 mutex.Lock(); | 94 queueMutex.Lock(); |
91 if (!queue.size()) | 95 if (!queue.size()) |
92 notEmpty.Wait(mutex); | 96 notEmpty.Wait(queueMutex); |
93 queue.pop(); | 97 queue.pop(); |
94 mutex.Unlock(); | 98 queueMutex.Unlock(); |
95 } | 99 } |
96 | 100 |
97 private: | 101 private: |
98 std::queue<int>& queue; | 102 std::queue<int>& queue; |
99 AdblockPlus::Thread::Mutex& mutex; | 103 AdblockPlus::Mutex& queueMutex; |
100 AdblockPlus::Thread::Condition& notEmpty; | 104 AdblockPlus::ConditionVariable& notEmpty; |
101 }; | 105 }; |
102 } | 106 } |
103 | 107 |
104 TEST(ThreadTest, Run) | 108 TEST(ThreadTest, Run) |
105 { | 109 { |
106 Mock mock; | 110 Mock mock; |
107 ASSERT_EQ(0, mock.timesCalled); | 111 ASSERT_EQ(0, mock.timesCalled); |
108 mock.Start(); | 112 mock.Start(); |
113 mock.mutex.Lock(); | |
109 ASSERT_EQ(0, mock.timesCalled); | 114 ASSERT_EQ(0, mock.timesCalled); |
Wladimir Palant
2013/04/03 13:14:47
This is a race condition - by the time you check t
Felix Dahlke
2013/04/03 16:27:59
Yes indeed, made it a bit more robust. It's still
| |
115 mock.mutex.Unlock(); | |
110 mock.Join(); | 116 mock.Join(); |
111 ASSERT_EQ(1, mock.timesCalled); | 117 ASSERT_EQ(1, mock.timesCalled); |
112 } | 118 } |
113 | 119 |
114 TEST(ThreadTest, Mutex) | 120 TEST(ThreadTest, Mutex) |
115 { | 121 { |
116 AdblockPlus::Thread::Mutex mutex; | 122 std::vector<std::string> log; |
117 LockingMock mock1(mutex, 10); | 123 AdblockPlus::Mutex logMutex; |
118 LockingMock mock2(mutex, 20); | 124 LockingMock mock1(log, logMutex); |
125 LockingMock mock2(log, logMutex); | |
119 mock1.Start(); | 126 mock1.Start(); |
120 mock2.Start(); | 127 mock2.Start(); |
121 Sleep(5); | |
122 ASSERT_TRUE(mock1.working); | |
123 ASSERT_FALSE(mock2.working); | |
Wladimir Palant
2013/04/03 13:14:47
Please note that Sleep() (both the Windows and the
Felix Dahlke
2013/04/03 16:27:59
Agreed, much more robust that way.
| |
124 Sleep(10); | |
125 ASSERT_TRUE(mock2.working); | |
126 ASSERT_FALSE(mock1.working); | |
127 mock1.Join(); | 128 mock1.Join(); |
128 mock2.Join(); | 129 mock2.Join(); |
130 ASSERT_EQ("started", log[0]); | |
131 ASSERT_EQ("ended", log[1]); | |
132 ASSERT_EQ("started", log[2]); | |
133 ASSERT_EQ("ended", log[3]); | |
129 } | 134 } |
130 | 135 |
131 TEST(ThreadTest, ConditionVariable) | 136 TEST(ThreadTest, ConditionVariable) |
132 { | 137 { |
133 std::queue<int> queue; | 138 std::queue<int> queue; |
134 AdblockPlus::Thread::Mutex mutex; | 139 AdblockPlus::Mutex queueMutex; |
135 AdblockPlus::Thread::Condition notEmpty; | 140 AdblockPlus::ConditionVariable notEmpty; |
136 Enqueuer enqueuer(queue, mutex, notEmpty); | 141 Dequeuer dequeuer(queue, queueMutex, notEmpty); |
137 Dequeuer dequeuer(queue, mutex, notEmpty); | 142 Enqueuer enqueuer(queue, queueMutex, notEmpty); |
143 dequeuer.Start(); | |
144 Sleep(5); | |
138 enqueuer.Start(); | 145 enqueuer.Start(); |
139 dequeuer.Start(); | |
140 Sleep(10); | |
141 ASSERT_EQ(1, queue.size()); | |
Wladimir Palant
2013/04/03 13:14:47
Same flaw here as above.
Felix Dahlke
2013/04/03 16:27:59
Fixed as well.
| |
142 Sleep(15); | |
143 ASSERT_EQ(0, queue.size()); | |
144 enqueuer.Join(); | 146 enqueuer.Join(); |
145 dequeuer.Join(); | 147 dequeuer.Join(); |
148 ASSERT_EQ(0, queue.size()); | |
146 } | 149 } |
LEFT | RIGHT |