1 | // Copyright (C) 2001-2003 |
2 | // William E. Kempf |
3 | // Copyright (C) 2007 Anthony Williams |
4 | // |
5 | // Distributed under the Boost Software License, Version 1.0. (See accompanying |
6 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |
7 | |
8 | #define BOOST_THREAD_VERSION 2 |
9 | #define BOOST_THREAD_PROVIDES_INTERRUPTIONS |
10 | #define BOOST_TEST_MODULE Boost.Threads: tss test suite |
11 | |
12 | #include <boost/thread/detail/config.hpp> |
13 | #include <boost/predef/platform.h> |
14 | |
15 | #include <boost/thread/tss.hpp> |
16 | #include <boost/thread/mutex.hpp> |
17 | #include <boost/thread/thread.hpp> |
18 | |
19 | #include <boost/test/unit_test.hpp> |
20 | |
21 | #include "./util.inl" |
22 | |
23 | #include <iostream> |
24 | |
25 | #if defined(BOOST_THREAD_PLATFORM_WIN32) |
26 | #define WIN32_LEAN_AND_MEAN |
27 | #include <windows.h> |
28 | #endif |
29 | |
30 | boost::mutex check_mutex; |
31 | boost::mutex tss_mutex; |
32 | int tss_instances = 0; |
33 | int tss_total = 0; |
34 | |
35 | struct tss_value_t |
36 | { |
37 | tss_value_t() |
38 | { |
39 | boost::unique_lock<boost::mutex> lock(tss_mutex); |
40 | ++tss_instances; |
41 | ++tss_total; |
42 | value = 0; |
43 | } |
44 | ~tss_value_t() |
45 | { |
46 | boost::unique_lock<boost::mutex> lock(tss_mutex); |
47 | --tss_instances; |
48 | } |
49 | int value; |
50 | }; |
51 | |
52 | boost::thread_specific_ptr<tss_value_t> tss_value; |
53 | |
54 | void test_tss_thread() |
55 | { |
56 | tss_value.reset(new_value: new tss_value_t()); |
57 | for (int i=0; i<1000; ++i) |
58 | { |
59 | int& n = tss_value->value; |
60 | // Don't call BOOST_CHECK_EQUAL directly, as it doesn't appear to |
61 | // be thread safe. Must evaluate further. |
62 | if (n != i) |
63 | { |
64 | boost::unique_lock<boost::mutex> lock(check_mutex); |
65 | BOOST_CHECK_EQUAL(n, i); |
66 | } |
67 | ++n; |
68 | } |
69 | } |
70 | |
71 | |
72 | |
73 | #if defined(BOOST_THREAD_PLATFORM_WIN32) |
74 | #if BOOST_PLAT_WINDOWS_RUNTIME |
75 | typedef std::shared_ptr<std::thread> native_thread_t; |
76 | |
77 | BOOST_AUTO_TEST_CASE(test_tss_thread_native) |
78 | { |
79 | test_tss_thread(); |
80 | } |
81 | |
82 | native_thread_t create_native_thread() |
83 | { |
84 | return std::make_shared<std::thread>(test_tss_thread_native); |
85 | } |
86 | |
87 | void join_native_thread(native_thread_t thread) |
88 | { |
89 | thread->join(); |
90 | } |
91 | |
92 | #else |
93 | typedef HANDLE native_thread_t; |
94 | |
95 | DWORD WINAPI test_tss_thread_native(LPVOID /*lpParameter*/) |
96 | { |
97 | test_tss_thread(); |
98 | return 0; |
99 | } |
100 | |
101 | native_thread_t create_native_thread(void) |
102 | { |
103 | native_thread_t const res=CreateThread( |
104 | 0, //security attributes (0 = not inheritable) |
105 | 0, //stack size (0 = default) |
106 | &test_tss_thread_native, //function to execute |
107 | 0, //parameter to pass to function |
108 | 0, //creation flags (0 = run immediately) |
109 | 0 //thread id (0 = thread id not returned) |
110 | ); |
111 | BOOST_CHECK(res!=0); |
112 | return res; |
113 | } |
114 | |
115 | void join_native_thread(native_thread_t thread) |
116 | { |
117 | DWORD res = WaitForSingleObject(thread, INFINITE); |
118 | BOOST_CHECK(res == WAIT_OBJECT_0); |
119 | |
120 | res = CloseHandle(thread); |
121 | BOOST_CHECK(SUCCEEDED(res)); |
122 | } |
123 | #endif |
124 | #elif defined(BOOST_THREAD_PLATFORM_PTHREAD) |
125 | typedef pthread_t native_thread_t; |
126 | |
127 | extern "C" |
128 | { |
129 | void* test_tss_thread_native(void* ) |
130 | { |
131 | test_tss_thread(); |
132 | return 0; |
133 | } |
134 | } |
135 | |
136 | native_thread_t create_native_thread() |
137 | { |
138 | native_thread_t thread_handle; |
139 | |
140 | int const res = pthread_create(newthread: &thread_handle, attr: 0, start_routine: &test_tss_thread_native, arg: 0); |
141 | BOOST_CHECK(!res); |
142 | return thread_handle; |
143 | } |
144 | |
145 | void join_native_thread(native_thread_t thread) |
146 | { |
147 | void* result=0; |
148 | int const res=pthread_join(th: thread,thread_return: &result); |
149 | BOOST_CHECK(!res); |
150 | } |
151 | #endif |
152 | |
153 | void do_test_tss() |
154 | { |
155 | tss_instances = 0; |
156 | tss_total = 0; |
157 | |
158 | const int NUMTHREADS=5; |
159 | boost::thread_group threads; |
160 | try |
161 | { |
162 | for (int i=0; i<NUMTHREADS; ++i) |
163 | threads.create_thread(threadfunc: &test_tss_thread); |
164 | threads.join_all(); |
165 | } |
166 | catch(...) |
167 | { |
168 | threads.interrupt_all(); |
169 | threads.join_all(); |
170 | throw; |
171 | } |
172 | |
173 | |
174 | std::cout |
175 | << "tss_instances = " << tss_instances |
176 | << "; tss_total = " << tss_total |
177 | << "\n" ; |
178 | std::cout.flush(); |
179 | |
180 | BOOST_CHECK_EQUAL(tss_instances, 0); |
181 | BOOST_CHECK_EQUAL(tss_total, 5); |
182 | |
183 | tss_instances = 0; |
184 | tss_total = 0; |
185 | |
186 | native_thread_t thread1 = create_native_thread(); |
187 | native_thread_t thread2 = create_native_thread(); |
188 | native_thread_t thread3 = create_native_thread(); |
189 | native_thread_t thread4 = create_native_thread(); |
190 | native_thread_t thread5 = create_native_thread(); |
191 | |
192 | join_native_thread(thread: thread5); |
193 | join_native_thread(thread: thread4); |
194 | join_native_thread(thread: thread3); |
195 | join_native_thread(thread: thread2); |
196 | join_native_thread(thread: thread1); |
197 | |
198 | std::cout |
199 | << "tss_instances = " << tss_instances |
200 | << "; tss_total = " << tss_total |
201 | << "\n" ; |
202 | std::cout.flush(); |
203 | |
204 | // The following is not really an error. TSS cleanup support still is available for boost threads. |
205 | // Also this usually will be triggered only when bound to the static version of thread lib. |
206 | // 2006-10-02 Roland Schwarz |
207 | //BOOST_CHECK_EQUAL(tss_instances, 0); |
208 | #if !defined(__MINGW32__) |
209 | // This fails on MinGW, when using the static lib |
210 | BOOST_CHECK_MESSAGE(tss_instances == 0, "Support of automatic tss cleanup for native threading API not available" ); |
211 | #endif |
212 | BOOST_CHECK_EQUAL(tss_total, 5); |
213 | } |
214 | |
215 | BOOST_AUTO_TEST_CASE(test_tss) |
216 | { |
217 | timed_test(func: &do_test_tss, secs: 2); |
218 | } |
219 | |
220 | |
221 | bool tss_void_cleanup_called=false; |
222 | |
223 | void tss_void_custom_cleanup(void* d) |
224 | { |
225 | std::cout << d << std::endl; |
226 | delete reinterpret_cast<tss_value_t*>(d); |
227 | tss_void_cleanup_called=true; |
228 | } |
229 | |
230 | boost::thread_specific_ptr<void> tss_void(tss_void_custom_cleanup); |
231 | |
232 | void test_tss_void_thread() |
233 | { |
234 | tss_void.reset(new_value: new tss_value_t()); |
235 | for (int i=0; i<10; ++i) |
236 | { |
237 | int& n = static_cast<tss_value_t*>(tss_value.get())->value; |
238 | *tss_value; |
239 | // Don't call BOOST_CHECK_EQUAL directly, as it doesn't appear to |
240 | // be thread safe. Must evaluate further. |
241 | if (n != i) |
242 | { |
243 | boost::unique_lock<boost::mutex> lock(check_mutex); |
244 | BOOST_CHECK_EQUAL(n, i); |
245 | } |
246 | ++n; |
247 | } |
248 | } |
249 | void do_test_tss_void() |
250 | { |
251 | tss_instances = 0; |
252 | tss_total = 0; |
253 | |
254 | const int NUMTHREADS=5; |
255 | boost::thread_group threads; |
256 | try |
257 | { |
258 | for (int i=0; i<NUMTHREADS; ++i) |
259 | threads.create_thread(threadfunc: &test_tss_void_thread); |
260 | threads.join_all(); |
261 | } |
262 | catch(...) |
263 | { |
264 | threads.interrupt_all(); |
265 | threads.join_all(); |
266 | throw; |
267 | } |
268 | |
269 | |
270 | std::cout |
271 | << "tss_instances = " << tss_instances |
272 | << "; tss_total = " << tss_total |
273 | << "\n" ; |
274 | std::cout.flush(); |
275 | |
276 | BOOST_CHECK_EQUAL(tss_instances, 0); |
277 | BOOST_CHECK_EQUAL(tss_total, 5); |
278 | |
279 | // tss_instances = 0; |
280 | // tss_total = 0; |
281 | // |
282 | // native_thread_t thread1 = create_native_thread(); |
283 | // native_thread_t thread2 = create_native_thread(); |
284 | // native_thread_t thread3 = create_native_thread(); |
285 | // native_thread_t thread4 = create_native_thread(); |
286 | // native_thread_t thread5 = create_native_thread(); |
287 | // |
288 | // join_native_thread(thread5); |
289 | // join_native_thread(thread4); |
290 | // join_native_thread(thread3); |
291 | // join_native_thread(thread2); |
292 | // join_native_thread(thread1); |
293 | // |
294 | // std::cout |
295 | // << "tss_instances = " << tss_instances |
296 | // << "; tss_total = " << tss_total |
297 | // << "\n"; |
298 | // std::cout.flush(); |
299 | // |
300 | // // The following is not really an error. TSS cleanup support still is available for boost threads. |
301 | // // Also this usually will be triggered only when bound to the static version of thread lib. |
302 | // // 2006-10-02 Roland Schwarz |
303 | // //BOOST_CHECK_EQUAL(tss_instances, 0); |
304 | // BOOST_CHECK_MESSAGE(tss_instances == 0, "Support of automatic tss cleanup for native threading API not available"); |
305 | // BOOST_CHECK_EQUAL(tss_total, 5); |
306 | } |
307 | |
308 | //BOOST_AUTO_TEST_CASE(test_tss_void) |
309 | //{ |
310 | // timed_test(&do_test_tss_void, 2); |
311 | //} |
312 | |
313 | |
314 | boost::thread_specific_ptr<void> tss_void_with_cleanup(tss_void_custom_cleanup); |
315 | |
316 | void tss_void_thread_with_custom_cleanup() |
317 | { |
318 | tss_void_with_cleanup.reset(new_value: new tss_value_t); |
319 | } |
320 | |
321 | void do_test_tss_void_with_custom_cleanup() |
322 | { |
323 | boost::thread t(tss_void_thread_with_custom_cleanup); |
324 | try |
325 | { |
326 | t.join(); |
327 | } |
328 | catch(...) |
329 | { |
330 | t.interrupt(); |
331 | t.join(); |
332 | throw; |
333 | } |
334 | |
335 | BOOST_CHECK(tss_void_cleanup_called); |
336 | } |
337 | |
338 | |
339 | BOOST_AUTO_TEST_CASE(test_tss_void_with_custom_cleanup) |
340 | { |
341 | timed_test(func: &do_test_tss_void_with_custom_cleanup, secs: 2); |
342 | } |
343 | |
344 | |
345 | bool tss_cleanup_called=false; |
346 | |
347 | struct Dummy |
348 | {}; |
349 | |
350 | void tss_custom_cleanup(Dummy* d) |
351 | { |
352 | delete d; |
353 | tss_cleanup_called=true; |
354 | } |
355 | |
356 | boost::thread_specific_ptr<Dummy> tss_with_cleanup(tss_custom_cleanup); |
357 | |
358 | void tss_thread_with_custom_cleanup() |
359 | { |
360 | tss_with_cleanup.reset(new_value: new Dummy); |
361 | } |
362 | |
363 | void do_test_tss_with_custom_cleanup() |
364 | { |
365 | boost::thread t(tss_thread_with_custom_cleanup); |
366 | try |
367 | { |
368 | t.join(); |
369 | } |
370 | catch(...) |
371 | { |
372 | t.interrupt(); |
373 | t.join(); |
374 | throw; |
375 | } |
376 | |
377 | BOOST_CHECK(tss_cleanup_called); |
378 | } |
379 | |
380 | |
381 | BOOST_AUTO_TEST_CASE(test_tss_with_custom_cleanup) |
382 | { |
383 | timed_test(func: &do_test_tss_with_custom_cleanup, secs: 2); |
384 | } |
385 | |
386 | Dummy* tss_object=new Dummy; |
387 | |
388 | void tss_thread_with_custom_cleanup_and_release() |
389 | { |
390 | tss_with_cleanup.reset(new_value: tss_object); |
391 | tss_with_cleanup.release(); |
392 | } |
393 | |
394 | void do_test_tss_does_no_cleanup_after_release() |
395 | { |
396 | tss_cleanup_called=false; |
397 | boost::thread t(tss_thread_with_custom_cleanup_and_release); |
398 | try |
399 | { |
400 | t.join(); |
401 | } |
402 | catch(...) |
403 | { |
404 | t.interrupt(); |
405 | t.join(); |
406 | throw; |
407 | } |
408 | |
409 | BOOST_CHECK(!tss_cleanup_called); |
410 | if(!tss_cleanup_called) |
411 | { |
412 | delete tss_object; |
413 | } |
414 | } |
415 | |
416 | struct dummy_class_tracks_deletions |
417 | { |
418 | static unsigned deletions; |
419 | |
420 | ~dummy_class_tracks_deletions() |
421 | { |
422 | ++deletions; |
423 | } |
424 | |
425 | }; |
426 | |
427 | unsigned dummy_class_tracks_deletions::deletions=0; |
428 | |
429 | boost::thread_specific_ptr<dummy_class_tracks_deletions> tss_with_null_cleanup(NULL); |
430 | |
431 | void tss_thread_with_null_cleanup(dummy_class_tracks_deletions* delete_tracker) |
432 | { |
433 | tss_with_null_cleanup.reset(new_value: delete_tracker); |
434 | } |
435 | |
436 | void do_test_tss_does_no_cleanup_with_null_cleanup_function() |
437 | { |
438 | dummy_class_tracks_deletions* delete_tracker=new dummy_class_tracks_deletions; |
439 | boost::thread t(tss_thread_with_null_cleanup,delete_tracker); |
440 | try |
441 | { |
442 | t.join(); |
443 | } |
444 | catch(...) |
445 | { |
446 | t.interrupt(); |
447 | t.join(); |
448 | throw; |
449 | } |
450 | |
451 | BOOST_CHECK(!dummy_class_tracks_deletions::deletions); |
452 | if(!dummy_class_tracks_deletions::deletions) |
453 | { |
454 | delete delete_tracker; |
455 | } |
456 | } |
457 | |
458 | BOOST_AUTO_TEST_CASE(test_tss_does_no_cleanup_after_release) |
459 | { |
460 | timed_test(func: &do_test_tss_does_no_cleanup_after_release, secs: 2); |
461 | } |
462 | |
463 | BOOST_AUTO_TEST_CASE(test_tss_does_no_cleanup_with_null_cleanup_function) |
464 | { |
465 | timed_test(func: &do_test_tss_does_no_cleanup_with_null_cleanup_function, secs: 2); |
466 | } |
467 | |
468 | void thread_with_local_tss_ptr() |
469 | { |
470 | { |
471 | boost::thread_specific_ptr<Dummy> local_tss(tss_custom_cleanup); |
472 | |
473 | local_tss.reset(new_value: new Dummy); |
474 | } |
475 | BOOST_CHECK(tss_cleanup_called); |
476 | tss_cleanup_called=false; |
477 | } |
478 | |
479 | |
480 | BOOST_AUTO_TEST_CASE(test_tss_does_not_call_cleanup_after_ptr_destroyed) |
481 | { |
482 | boost::thread t(thread_with_local_tss_ptr); |
483 | t.join(); |
484 | BOOST_CHECK(!tss_cleanup_called); |
485 | } |
486 | |
487 | BOOST_AUTO_TEST_CASE(test_tss_cleanup_not_called_for_null_pointer) |
488 | { |
489 | boost::thread_specific_ptr<Dummy> local_tss(tss_custom_cleanup); |
490 | local_tss.reset(new_value: new Dummy); |
491 | tss_cleanup_called=false; |
492 | local_tss.reset(new_value: 0); |
493 | BOOST_CHECK(tss_cleanup_called); |
494 | tss_cleanup_called=false; |
495 | local_tss.reset(new_value: new Dummy); |
496 | BOOST_CHECK(!tss_cleanup_called); |
497 | } |
498 | |
499 | //BOOST_AUTO_TEST_CASE(test_tss_at_the_same_adress) |
500 | //{ |
501 | // for(int i=0; i<2; i++) |
502 | // { |
503 | // boost::thread_specific_ptr<Dummy> local_tss(tss_custom_cleanup); |
504 | // local_tss.reset(new Dummy); |
505 | // tss_cleanup_called=false; |
506 | // BOOST_CHECK(tss_cleanup_called); |
507 | // tss_cleanup_called=false; |
508 | // BOOST_CHECK(!tss_cleanup_called); |
509 | // } |
510 | //} |
511 | |
512 | |