]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | |
2 | // Copyright Oliver Kowalke 2009. | |
3 | // Distributed under the Boost Software License, Version 1.0. | |
4 | // (See accompanying file LICENSE_1_0.txt or copy at | |
5 | // http://www.boost.org/LICENSE_1_0.txt) | |
6 | ||
7 | #include <stdio.h> | |
8 | #include <stdlib.h> | |
9 | ||
10 | #include <cmath> | |
11 | #include <cstdint> | |
12 | #include <cstdio> | |
13 | #include <iostream> | |
14 | #include <memory> | |
15 | #include <sstream> | |
16 | #include <stdexcept> | |
17 | #include <string> | |
18 | #include <thread> | |
19 | #include <utility> | |
20 | #include <vector> | |
21 | ||
22 | #include <boost/array.hpp> | |
23 | #include <boost/assert.hpp> | |
24 | #include <boost/lexical_cast.hpp> | |
25 | #include <boost/test/unit_test.hpp> | |
26 | #include <boost/utility.hpp> | |
27 | #include <boost/variant.hpp> | |
28 | ||
29 | #include <boost/context/fiber.hpp> | |
30 | #include <boost/context/detail/config.hpp> | |
31 | ||
32 | #ifdef BOOST_WINDOWS | |
33 | #include <windows.h> | |
34 | #endif | |
35 | ||
36 | #if defined(BOOST_MSVC) | |
37 | # pragma warning(push) | |
38 | # pragma warning(disable: 4702 4723 4996) | |
39 | #endif | |
40 | ||
41 | typedef boost::variant<int,std::string> variant_t; | |
42 | ||
43 | namespace ctx = boost::context; | |
44 | ||
45 | int value1 = 0; | |
46 | std::string value2; | |
47 | double value3 = 0.; | |
48 | ||
49 | struct X { | |
50 | ctx::fiber foo( ctx::fiber && f, int i) { | |
51 | value1 = i; | |
52 | return std::move( f); | |
53 | } | |
54 | }; | |
55 | ||
56 | struct Y { | |
57 | Y() { | |
58 | value1 = 3; | |
59 | } | |
60 | ||
61 | Y( Y const&) = delete; | |
62 | Y & operator=( Y const&) = delete; | |
63 | ||
64 | ~Y() { | |
65 | value1 = 7; | |
66 | } | |
67 | }; | |
68 | ||
69 | class moveable { | |
70 | public: | |
71 | bool state; | |
72 | int value; | |
73 | ||
74 | moveable() : | |
75 | state( false), | |
76 | value( -1) { | |
77 | } | |
78 | ||
79 | moveable( int v) : | |
80 | state( true), | |
81 | value( v) { | |
82 | } | |
83 | ||
84 | moveable( moveable && other) : | |
85 | state( other.state), | |
86 | value( other.value) { | |
87 | other.state = false; | |
88 | other.value = -1; | |
89 | } | |
90 | ||
91 | moveable & operator=( moveable && other) { | |
92 | if ( this == & other) return * this; | |
93 | state = other.state; | |
94 | value = other.value; | |
95 | other.state = false; | |
96 | other.value = -1; | |
97 | return * this; | |
98 | } | |
99 | ||
100 | moveable( moveable const& other) = delete; | |
101 | moveable & operator=( moveable const& other) = delete; | |
102 | ||
103 | void operator()() { | |
104 | value1 = value; | |
105 | } | |
106 | }; | |
107 | ||
108 | struct my_exception : public std::runtime_error { | |
109 | ctx::fiber f; | |
110 | my_exception( ctx::fiber && f_, char const* what) : | |
111 | std::runtime_error( what), | |
112 | f{ std::move( f_) } { | |
113 | } | |
114 | }; | |
115 | ||
116 | #ifdef BOOST_MSVC | |
117 | // Optimizations can remove the integer-divide-by-zero here. | |
118 | #pragma optimize("", off) | |
119 | void seh( bool & catched) { | |
120 | __try { | |
121 | int i = 1; | |
122 | i /= 0; | |
123 | } __except( EXCEPTION_EXECUTE_HANDLER) { | |
124 | catched = true; | |
125 | } | |
126 | } | |
127 | #pragma optimize("", on) | |
128 | #endif | |
129 | ||
130 | void test_move() { | |
131 | value1 = 0; | |
132 | int i = 1; | |
133 | BOOST_CHECK_EQUAL( 0, value1); | |
134 | ctx::fiber f1{ | |
135 | [&i](ctx::fiber && f) { | |
136 | value1 = i; | |
137 | f = std::move( f).resume(); | |
138 | value1 = i; | |
139 | return std::move( f); | |
140 | }}; | |
141 | f1 = std::move( f1).resume(); | |
142 | BOOST_CHECK_EQUAL( 1, value1); | |
143 | BOOST_CHECK( f1); | |
144 | ctx::fiber f2; | |
145 | BOOST_CHECK( ! f2); | |
146 | f2 = std::move( f1); | |
147 | BOOST_CHECK( ! f1); | |
148 | BOOST_CHECK( f2); | |
149 | i = 3; | |
150 | f2 = std::move( f2).resume(); | |
151 | BOOST_CHECK_EQUAL( 3, value1); | |
152 | BOOST_CHECK( ! f1); | |
153 | BOOST_CHECK( ! f2); | |
154 | } | |
155 | ||
156 | void test_bind() { | |
157 | value1 = 0; | |
158 | X x; | |
159 | ctx::fiber f{ std::bind( & X::foo, x, std::placeholders::_1, 7) }; | |
160 | f = std::move( f).resume(); | |
161 | BOOST_CHECK_EQUAL( 7, value1); | |
162 | } | |
163 | ||
164 | void test_exception() { | |
165 | { | |
166 | const char * what = "hello world"; | |
167 | ctx::fiber f{ | |
168 | [&what](ctx::fiber && f) { | |
169 | try { | |
170 | throw std::runtime_error( what); | |
171 | } catch ( std::runtime_error const& e) { | |
172 | value2 = e.what(); | |
173 | } | |
174 | return std::move( f); | |
175 | }}; | |
176 | f = std::move( f).resume(); | |
177 | BOOST_CHECK_EQUAL( std::string( what), value2); | |
178 | BOOST_CHECK( ! f); | |
179 | } | |
180 | #ifdef BOOST_MSVC | |
181 | { | |
182 | bool catched = false; | |
183 | std::thread([&catched](){ | |
184 | ctx::fiber f{ [&catched](ctx::fiber && f){ | |
185 | seh( catched); | |
186 | return std::move( f); | |
187 | }}; | |
188 | BOOST_CHECK( f); | |
189 | f = std::move( f).resume(); | |
190 | }).join(); | |
191 | BOOST_CHECK( catched); | |
192 | } | |
193 | #endif | |
194 | } | |
195 | ||
196 | void test_fp() { | |
197 | value3 = 0.; | |
198 | double d = 7.13; | |
199 | ctx::fiber f{ | |
200 | [&d]( ctx::fiber && f) { | |
201 | d += 3.45; | |
202 | value3 = d; | |
203 | return std::move( f); | |
204 | }}; | |
205 | f = std::move( f).resume(); | |
206 | BOOST_CHECK_EQUAL( 10.58, value3); | |
207 | BOOST_CHECK( ! f); | |
208 | } | |
209 | ||
210 | void test_stacked() { | |
211 | value1 = 0; | |
212 | value3 = 0.; | |
213 | ctx::fiber f{ | |
214 | [](ctx::fiber && f) { | |
215 | ctx::fiber f1{ | |
216 | [](ctx::fiber && f) { | |
217 | value1 = 3; | |
218 | return std::move( f); | |
219 | }}; | |
220 | f1 = std::move( f1).resume(); | |
221 | value3 = 3.14; | |
222 | return std::move( f); | |
223 | }}; | |
224 | f = std::move( f).resume(); | |
225 | BOOST_CHECK_EQUAL( 3, value1); | |
226 | BOOST_CHECK_EQUAL( 3.14, value3); | |
227 | BOOST_CHECK( ! f); | |
228 | } | |
229 | ||
230 | void test_prealloc() { | |
231 | value1 = 0; | |
232 | ctx::default_stack alloc; | |
233 | ctx::stack_context sctx( alloc.allocate() ); | |
234 | void * sp = static_cast< char * >( sctx.sp) - 10; | |
235 | std::size_t size = sctx.size - 10; | |
236 | int i = 7; | |
237 | ctx::fiber f{ | |
238 | std::allocator_arg, ctx::preallocated( sp, size, sctx), alloc, | |
239 | [&i]( ctx::fiber && f) { | |
240 | value1 = i; | |
241 | return std::move( f); | |
242 | }}; | |
243 | f = std::move( f).resume(); | |
244 | BOOST_CHECK_EQUAL( 7, value1); | |
245 | BOOST_CHECK( ! f); | |
246 | } | |
247 | ||
248 | void test_ontop() { | |
249 | { | |
250 | int i = 3; | |
251 | ctx::fiber f{ [&i](ctx::fiber && f) { | |
252 | for (;;) { | |
253 | i *= 10; | |
254 | f = std::move( f).resume(); | |
255 | } | |
256 | return std::move( f); | |
257 | }}; | |
258 | f = std::move( f).resume(); | |
92f5a8d4 TL |
259 | // Pass fn by reference to see if the types are properly decayed. |
260 | auto fn = [&i](ctx::fiber && f){ | |
11fdf7f2 TL |
261 | i -= 10; |
262 | return std::move( f); | |
92f5a8d4 TL |
263 | }; |
264 | f = std::move( f).resume_with(fn); | |
11fdf7f2 TL |
265 | BOOST_CHECK( f); |
266 | BOOST_CHECK_EQUAL( i, 200); | |
267 | } | |
268 | { | |
269 | ctx::fiber f1; | |
270 | ctx::fiber f{ [&f1](ctx::fiber && f) { | |
271 | f = std::move( f).resume(); | |
272 | BOOST_CHECK( ! f); | |
273 | return std::move( f1); | |
274 | }}; | |
275 | f = std::move( f).resume(); | |
276 | f = std::move( f).resume_with( | |
277 | [&f1](ctx::fiber && f){ | |
278 | f1 = std::move( f); | |
279 | return std::move( f); | |
280 | }); | |
281 | } | |
282 | } | |
283 | ||
284 | void test_ontop_exception() { | |
285 | value1 = 0; | |
286 | value2 = ""; | |
287 | ctx::fiber f{ [](ctx::fiber && f){ | |
288 | for (;;) { | |
289 | value1 = 3; | |
290 | try { | |
291 | f = std::move( f).resume(); | |
292 | } catch ( my_exception & ex) { | |
293 | value2 = ex.what(); | |
92f5a8d4 | 294 | return std::move( ex.f); |
11fdf7f2 TL |
295 | } |
296 | } | |
297 | return std::move( f); | |
298 | }}; | |
299 | f = std::move( f).resume(); | |
300 | BOOST_CHECK_EQUAL( 3, value1); | |
301 | const char * what = "hello world"; | |
302 | f = std::move( f).resume_with( | |
303 | [what](ctx::fiber && f){ | |
304 | throw my_exception( std::move( f), what); | |
305 | return std::move( f); | |
306 | }); | |
307 | BOOST_CHECK_EQUAL( 3, value1); | |
308 | BOOST_CHECK_EQUAL( std::string( what), value2); | |
309 | } | |
310 | ||
92f5a8d4 | 311 | void test_termination1() { |
11fdf7f2 TL |
312 | { |
313 | value1 = 0; | |
314 | ctx::fiber f{ | |
315 | [](ctx::fiber && f){ | |
316 | Y y; | |
317 | f = std::move( f).resume(); | |
318 | return std::move(f); | |
319 | }}; | |
320 | f = std::move( f).resume(); | |
321 | BOOST_CHECK_EQUAL( 3, value1); | |
322 | } | |
323 | BOOST_CHECK_EQUAL( 7, value1); | |
324 | { | |
325 | value1 = 0; | |
326 | BOOST_CHECK_EQUAL( 0, value1); | |
327 | ctx::fiber f{ | |
328 | [](ctx::fiber && f) { | |
329 | value1 = 3; | |
330 | return std::move( f); | |
331 | }}; | |
332 | f = std::move( f).resume(); | |
333 | BOOST_CHECK_EQUAL( 3, value1); | |
334 | BOOST_CHECK( ! f); | |
335 | } | |
336 | { | |
337 | value1 = 0; | |
338 | BOOST_CHECK_EQUAL( 0, value1); | |
339 | int i = 3; | |
340 | ctx::fiber f{ | |
341 | [&i](ctx::fiber && f){ | |
342 | value1 = i; | |
343 | f = std::move( f).resume(); | |
344 | value1 = i; | |
345 | return std::move( f); | |
346 | }}; | |
347 | f = std::move( f).resume(); | |
348 | BOOST_CHECK( f); | |
349 | BOOST_CHECK_EQUAL( i, value1); | |
350 | BOOST_CHECK( f); | |
351 | i = 7; | |
352 | f = std::move( f).resume(); | |
353 | BOOST_CHECK( ! f); | |
354 | BOOST_CHECK_EQUAL( i, value1); | |
355 | } | |
356 | } | |
357 | ||
92f5a8d4 TL |
358 | void test_termination2() { |
359 | { | |
360 | value1 = 0; | |
361 | value3 = 0.0; | |
362 | ctx::fiber f{ | |
363 | [](ctx::fiber && f){ | |
364 | Y y; | |
365 | value1 = 3; | |
366 | value3 = 4.; | |
367 | f = std::move( f).resume(); | |
368 | value1 = 7; | |
369 | value3 = 8.; | |
370 | f = std::move( f).resume(); | |
371 | return std::move( f); | |
372 | }}; | |
373 | BOOST_CHECK_EQUAL( 0, value1); | |
374 | BOOST_CHECK_EQUAL( 0., value3); | |
375 | f = std::move( f).resume(); | |
376 | BOOST_CHECK_EQUAL( 3, value1); | |
377 | BOOST_CHECK_EQUAL( 4., value3); | |
378 | f = std::move( f).resume(); | |
379 | } | |
380 | BOOST_CHECK_EQUAL( 7, value1); | |
381 | BOOST_CHECK_EQUAL( 8., value3); | |
382 | } | |
383 | ||
11fdf7f2 TL |
384 | void test_sscanf() { |
385 | ctx::fiber{ | |
386 | []( ctx::fiber && f) { | |
387 | { | |
388 | double n1 = 0; | |
389 | double n2 = 0; | |
390 | sscanf("3.14 7.13", "%lf %lf", & n1, & n2); | |
391 | BOOST_CHECK( n1 == 3.14); | |
392 | BOOST_CHECK( n2 == 7.13); | |
393 | } | |
394 | { | |
395 | int n1=0; | |
396 | int n2=0; | |
397 | sscanf("1 23", "%d %d", & n1, & n2); | |
398 | BOOST_CHECK( n1 == 1); | |
399 | BOOST_CHECK( n2 == 23); | |
400 | } | |
401 | { | |
402 | int n1=0; | |
403 | int n2=0; | |
404 | sscanf("1 jjj 23", "%d %*[j] %d", & n1, & n2); | |
405 | BOOST_CHECK( n1 == 1); | |
406 | BOOST_CHECK( n2 == 23); | |
407 | } | |
408 | return std::move( f); | |
409 | }}.resume(); | |
410 | } | |
411 | ||
412 | void test_snprintf() { | |
413 | ctx::fiber{ | |
414 | []( ctx::fiber && f) { | |
415 | { | |
416 | const char *fmt = "sqrt(2) = %f"; | |
417 | char buf[19]; | |
418 | snprintf( buf, sizeof( buf), fmt, std::sqrt( 2) ); | |
419 | BOOST_CHECK( 0 < sizeof( buf) ); | |
420 | BOOST_ASSERT( std::string("sqrt(2) = 1.41") == std::string( buf, 14) ); | |
421 | } | |
422 | { | |
423 | std::uint64_t n = 0xbcdef1234567890; | |
424 | const char *fmt = "0x%016llX"; | |
425 | char buf[100]; | |
426 | snprintf( buf, sizeof( buf), fmt, n); | |
427 | BOOST_ASSERT( std::string("0x0BCDEF1234567890") == std::string( buf, 18) ); | |
428 | } | |
429 | return std::move( f); | |
430 | }}.resume(); | |
431 | } | |
432 | ||
433 | #ifdef BOOST_WINDOWS | |
434 | void test_bug12215() { | |
435 | ctx::fiber{ | |
436 | [](ctx::fiber && f) { | |
437 | char buffer[MAX_PATH]; | |
438 | GetModuleFileName( nullptr, buffer, MAX_PATH); | |
439 | return std::move( f); | |
440 | }}.resume(); | |
441 | } | |
442 | #endif | |
443 | ||
92f5a8d4 TL |
444 | void test_goodcatch() { |
445 | value1 = 0; | |
446 | value3 = 0.0; | |
447 | { | |
448 | ctx::fiber f{ | |
449 | []( ctx::fiber && f) { | |
450 | Y y; | |
451 | value3 = 2.; | |
452 | f = std::move( f).resume(); | |
453 | try { | |
454 | value3 = 3.; | |
455 | f = std::move( f).resume(); | |
456 | } catch ( boost::context::detail::forced_unwind const&) { | |
457 | value3 = 4.; | |
458 | throw; | |
459 | } catch (...) { | |
460 | value3 = 5.; | |
461 | } | |
462 | value3 = 6.; | |
463 | return std::move( f); | |
464 | }}; | |
465 | BOOST_CHECK_EQUAL( 0, value1); | |
466 | BOOST_CHECK_EQUAL( 0., value3); | |
467 | f = std::move( f).resume(); | |
468 | BOOST_CHECK_EQUAL( 3, value1); | |
469 | BOOST_CHECK_EQUAL( 2., value3); | |
470 | f = std::move( f).resume(); | |
471 | BOOST_CHECK_EQUAL( 3, value1); | |
472 | BOOST_CHECK_EQUAL( 3., value3); | |
473 | } | |
474 | BOOST_CHECK_EQUAL( 7, value1); | |
475 | BOOST_CHECK_EQUAL( 4., value3); | |
476 | } | |
477 | ||
478 | void test_badcatch() { | |
479 | #if 0 | |
480 | value1 = 0; | |
481 | value3 = 0.; | |
482 | { | |
483 | ctx::fiber f{ | |
484 | []( ctx::fiber && f) { | |
485 | Y y; | |
486 | try { | |
487 | value3 = 3.; | |
488 | f = std::move( f).resume(); | |
489 | } catch (...) { | |
490 | value3 = 5.; | |
491 | } | |
492 | return std::move( f); | |
493 | }}; | |
494 | BOOST_CHECK_EQUAL( 0, value1); | |
495 | BOOST_CHECK_EQUAL( 0., value3); | |
496 | f = std::move( f).resume(); | |
497 | BOOST_CHECK_EQUAL( 3, value1); | |
498 | BOOST_CHECK_EQUAL( 3., value3); | |
499 | // the destruction of ctx here will cause a forced_unwind to be thrown that is not caught | |
500 | // in fn19. That will trigger the "not caught" assertion in ~forced_unwind. Getting that | |
501 | // assertion to propogate bak here cleanly is non-trivial, and there seems to not be a good | |
502 | // way to hook directly into the assertion when it happens on an alternate stack. | |
503 | std::move( f); | |
504 | } | |
505 | BOOST_CHECK_EQUAL( 7, value1); | |
506 | BOOST_CHECK_EQUAL( 4., value3); | |
507 | #endif | |
508 | } | |
509 | ||
11fdf7f2 TL |
510 | boost::unit_test::test_suite * init_unit_test_suite( int, char* []) |
511 | { | |
512 | boost::unit_test::test_suite * test = | |
513 | BOOST_TEST_SUITE("Boost.Context: fiber test suite"); | |
514 | ||
515 | test->add( BOOST_TEST_CASE( & test_move) ); | |
516 | test->add( BOOST_TEST_CASE( & test_bind) ); | |
517 | test->add( BOOST_TEST_CASE( & test_exception) ); | |
518 | test->add( BOOST_TEST_CASE( & test_fp) ); | |
519 | test->add( BOOST_TEST_CASE( & test_stacked) ); | |
520 | test->add( BOOST_TEST_CASE( & test_prealloc) ); | |
521 | test->add( BOOST_TEST_CASE( & test_ontop) ); | |
522 | test->add( BOOST_TEST_CASE( & test_ontop_exception) ); | |
92f5a8d4 TL |
523 | test->add( BOOST_TEST_CASE( & test_termination1) ); |
524 | test->add( BOOST_TEST_CASE( & test_termination2) ); | |
11fdf7f2 TL |
525 | test->add( BOOST_TEST_CASE( & test_sscanf) ); |
526 | test->add( BOOST_TEST_CASE( & test_snprintf) ); | |
527 | #ifdef BOOST_WINDOWS | |
528 | test->add( BOOST_TEST_CASE( & test_bug12215) ); | |
529 | #endif | |
92f5a8d4 TL |
530 | test->add( BOOST_TEST_CASE( & test_goodcatch) ); |
531 | test->add( BOOST_TEST_CASE( & test_badcatch) ); | |
11fdf7f2 TL |
532 | |
533 | return test; | |
534 | } | |
535 | ||
536 | #if defined(BOOST_MSVC) | |
537 | # pragma warning(pop) | |
538 | #endif |