1 | // Copyright (C) 2001-2003 |
2 | // William E. Kempf |
3 | // |
4 | // Copyright Frank Mori Hess 2009 |
5 | // |
6 | // Use, modification and |
7 | // distribution is subject to the Boost Software License, Version |
8 | // 1.0. (See accompanying file LICENSE_1_0.txt or copy at |
9 | // http://www.boost.org/LICENSE_1_0.txt) |
10 | |
11 | // This is a simplified/modified version of libs/thread/test/test_mutex.cpp |
12 | // added to test boost::signals2::mutex. |
13 | // For more information, see http://www.boost.org |
14 | |
15 | // Note boost/test/minimal.hpp can cause windows.h to get included, which |
16 | // can screw up our checks of _WIN32_WINNT if it is included |
17 | // after boost/signals2/mutex.hpp. Frank Hess 2009-03-07. |
18 | // We now use boost/test/included/unit_test.hpp, not sure if above still |
19 | // applies, but might as well leave the include where it is. |
20 | #define BOOST_TEST_MODULE mutex_test |
21 | #include <boost/test/included/unit_test.hpp> |
22 | |
23 | #include <boost/bind/bind.hpp> |
24 | #include <boost/signals2/dummy_mutex.hpp> |
25 | #include <boost/signals2/mutex.hpp> |
26 | #include <boost/thread/locks.hpp> |
27 | #include <boost/thread/mutex.hpp> |
28 | #include <boost/thread/thread.hpp> |
29 | #include <boost/thread/thread_time.hpp> |
30 | #include <boost/thread/condition.hpp> |
31 | |
32 | using namespace boost::placeholders; |
33 | |
34 | class execution_monitor |
35 | { |
36 | public: |
37 | execution_monitor(int secs) |
38 | : done(false), m_secs(secs) { } |
39 | void start() |
40 | { |
41 | boost::mutex::scoped_lock lock(mutex); |
42 | done = false; |
43 | } |
44 | void finish() |
45 | { |
46 | boost::mutex::scoped_lock lock(mutex); |
47 | done = true; |
48 | cond.notify_one(); |
49 | } |
50 | bool wait() |
51 | { |
52 | boost::posix_time::time_duration timeout = boost::posix_time::seconds(m_secs); |
53 | boost::mutex::scoped_lock lock(mutex); |
54 | while (!done) { |
55 | if (!cond.timed_wait(m&: lock, wait_duration: timeout)) |
56 | break; |
57 | } |
58 | return done; |
59 | } |
60 | |
61 | private: |
62 | boost::mutex mutex; |
63 | boost::condition cond; |
64 | bool done; |
65 | int m_secs; |
66 | }; |
67 | |
68 | template <typename F> |
69 | class indirect_adapter |
70 | { |
71 | public: |
72 | indirect_adapter(F func, execution_monitor& monitor) |
73 | : m_func(func), m_monitor(monitor) { } |
74 | void operator()() const |
75 | { |
76 | try |
77 | { |
78 | boost::thread thrd(m_func); |
79 | thrd.join(); |
80 | } |
81 | catch (...) |
82 | { |
83 | m_monitor.finish(); |
84 | throw; |
85 | } |
86 | m_monitor.finish(); |
87 | } |
88 | |
89 | private: |
90 | F m_func; |
91 | execution_monitor& m_monitor; |
92 | void operator=(indirect_adapter&); |
93 | }; |
94 | |
95 | template <typename F> |
96 | void timed_test(F func, int secs) |
97 | { |
98 | execution_monitor monitor(secs); |
99 | indirect_adapter<F> ifunc(func, monitor); |
100 | monitor.start(); |
101 | boost::thread thrd(ifunc); |
102 | BOOST_REQUIRE(monitor.wait()); // Timed test didn't complete in time, possible deadlock |
103 | } |
104 | |
105 | template <typename M> |
106 | struct test_lock |
107 | { |
108 | typedef M mutex_type; |
109 | typedef typename boost::unique_lock<M> lock_type; |
110 | |
111 | void operator()() |
112 | { |
113 | mutex_type mutex; |
114 | boost::condition condition; |
115 | |
116 | // Test the lock's constructors. |
117 | { |
118 | lock_type lock(mutex, boost::defer_lock); |
119 | BOOST_CHECK(!lock); |
120 | } |
121 | lock_type lock(mutex); |
122 | BOOST_CHECK(lock ? true : false); |
123 | |
124 | // Construct a fast time out. |
125 | boost::posix_time::time_duration timeout = boost::posix_time::milliseconds(100); |
126 | |
127 | // Test the lock and the mutex with condition variables. |
128 | // No one is going to notify this condition variable. We expect to |
129 | // time out. |
130 | BOOST_CHECK(!condition.timed_wait(lock, timeout)); |
131 | BOOST_CHECK(lock ? true : false); |
132 | |
133 | // Test the lock and unlock methods. |
134 | lock.unlock(); |
135 | BOOST_CHECK(!lock); |
136 | lock.lock(); |
137 | BOOST_CHECK(lock ? true : false); |
138 | } |
139 | }; |
140 | |
141 | template <typename M> |
142 | struct test_trylock |
143 | { |
144 | typedef M mutex_type; |
145 | typedef typename boost::unique_lock<M> lock_type; |
146 | |
147 | void operator()() |
148 | { |
149 | mutex_type mutex; |
150 | boost::condition condition; |
151 | |
152 | // Test the lock's constructors. |
153 | { |
154 | lock_type lock(mutex, boost::try_to_lock); |
155 | BOOST_CHECK(lock ? true : false); |
156 | } |
157 | { |
158 | lock_type lock(mutex, boost::defer_lock); |
159 | BOOST_CHECK(!lock); |
160 | } |
161 | lock_type lock(mutex, boost::try_to_lock); |
162 | BOOST_CHECK(lock ? true : false); |
163 | |
164 | // Construct a fast time out. |
165 | boost::posix_time::time_duration timeout = boost::posix_time::milliseconds(100); |
166 | |
167 | // Test the lock and the mutex with condition variables. |
168 | // No one is going to notify this condition variable. We expect to |
169 | // time out. |
170 | BOOST_CHECK(!condition.timed_wait(lock, timeout)); |
171 | BOOST_CHECK(lock ? true : false); |
172 | |
173 | // Test the lock, unlock and trylock methods. |
174 | lock.unlock(); |
175 | BOOST_CHECK(!lock); |
176 | lock.lock(); |
177 | BOOST_CHECK(lock ? true : false); |
178 | lock.unlock(); |
179 | BOOST_CHECK(!lock); |
180 | BOOST_CHECK(lock.try_lock()); |
181 | BOOST_CHECK(lock ? true : false); |
182 | } |
183 | }; |
184 | |
185 | template<typename Mutex> |
186 | struct test_lock_exclusion |
187 | { |
188 | typedef boost::unique_lock<Mutex> Lock; |
189 | |
190 | Mutex m; |
191 | boost::mutex done_mutex; |
192 | bool done; |
193 | bool locked; |
194 | boost::condition_variable done_cond; |
195 | |
196 | test_lock_exclusion(): |
197 | done(false),locked(false) |
198 | {} |
199 | |
200 | void locking_thread() |
201 | { |
202 | Lock lock(m); |
203 | |
204 | boost::lock_guard<boost::mutex> lk(done_mutex); |
205 | locked=lock.owns_lock(); |
206 | done=true; |
207 | done_cond.notify_one(); |
208 | } |
209 | |
210 | bool is_done() const |
211 | { |
212 | return done; |
213 | } |
214 | |
215 | typedef test_lock_exclusion<Mutex> this_type; |
216 | |
217 | void do_test(void (this_type::*test_func)()) |
218 | { |
219 | Lock lock(m); |
220 | |
221 | { |
222 | boost::lock_guard<boost::mutex> lk(done_mutex); |
223 | locked=false; |
224 | } |
225 | |
226 | done=false; |
227 | |
228 | boost::thread t(test_func,this); |
229 | |
230 | try |
231 | { |
232 | { |
233 | boost::mutex::scoped_lock lk(done_mutex); |
234 | BOOST_CHECK(!done_cond.timed_wait(lk, boost::posix_time::seconds(1), |
235 | boost::bind(&this_type::is_done,this))); |
236 | } |
237 | lock.unlock(); |
238 | { |
239 | boost::mutex::scoped_lock lk(done_mutex); |
240 | BOOST_CHECK(done_cond.timed_wait(lk, boost::posix_time::seconds(1), |
241 | boost::bind(&this_type::is_done,this))); |
242 | } |
243 | t.join(); |
244 | BOOST_CHECK(locked); |
245 | } |
246 | catch(...) |
247 | { |
248 | lock.unlock(); |
249 | t.join(); |
250 | throw; |
251 | } |
252 | } |
253 | |
254 | |
255 | void operator()() |
256 | { |
257 | do_test(test_func: &this_type::locking_thread); |
258 | } |
259 | }; |
260 | |
261 | |
262 | void do_test_mutex() |
263 | { |
264 | test_lock<boost::signals2::mutex>()(); |
265 | // try_lock not supported on old versions of windows |
266 | #if !defined(BOOST_HAS_WINTHREADS) || (defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0400)) |
267 | test_trylock<boost::signals2::mutex>()(); |
268 | #endif |
269 | test_lock_exclusion<boost::signals2::mutex>()(); |
270 | } |
271 | |
272 | void test_mutex() |
273 | { |
274 | timed_test(func: &do_test_mutex, secs: 3); |
275 | } |
276 | |
277 | void do_test_dummy_mutex() |
278 | { |
279 | test_lock<boost::signals2::dummy_mutex>()(); |
280 | test_trylock<boost::signals2::dummy_mutex>()(); |
281 | } |
282 | |
283 | void test_dummy_mutex() |
284 | { |
285 | timed_test(func: &do_test_dummy_mutex, secs: 2); |
286 | } |
287 | |
288 | BOOST_AUTO_TEST_CASE(test_main) |
289 | { |
290 | test_mutex(); |
291 | test_dummy_mutex(); |
292 | } |
293 | |