]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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 | ||
92f5a8d4 | 25 | #if defined(BOOST_THREAD_PLATFORM_WIN32) |
7c673cae FG |
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 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(&thread_handle, 0, &test_tss_thread_native, 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(thread,&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(&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(thread5); | |
193 | join_native_thread(thread4); | |
194 | join_native_thread(thread3); | |
195 | join_native_thread(thread2); | |
196 | join_native_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); | |
f67539c2 TL |
208 | #if !defined(__MINGW32__) |
209 | // This fails on MinGW, when using the static lib | |
7c673cae | 210 | BOOST_CHECK_MESSAGE(tss_instances == 0, "Support of automatic tss cleanup for native threading API not available"); |
f67539c2 | 211 | #endif |
7c673cae FG |
212 | BOOST_CHECK_EQUAL(tss_total, 5); |
213 | } | |
214 | ||
215 | BOOST_AUTO_TEST_CASE(test_tss) | |
216 | { | |
217 | timed_test(&do_test_tss, 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 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(&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 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(&do_test_tss_void_with_custom_cleanup, 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 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(&do_test_tss_with_custom_cleanup, 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(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(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(&do_test_tss_does_no_cleanup_after_release, 2); | |
461 | } | |
462 | ||
463 | BOOST_AUTO_TEST_CASE(test_tss_does_no_cleanup_with_null_cleanup_function) | |
464 | { | |
465 | timed_test(&do_test_tss_does_no_cleanup_with_null_cleanup_function, 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 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 Dummy); | |
491 | tss_cleanup_called=false; | |
492 | local_tss.reset(0); | |
493 | BOOST_CHECK(tss_cleanup_called); | |
494 | tss_cleanup_called=false; | |
495 | local_tss.reset(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 |