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)
15 #include <boost/array.hpp>
16 #include <boost/assert.hpp>
17 #include <boost/lexical_cast.hpp>
18 #include <boost/test/unit_test.hpp>
19 #include <boost/utility.hpp>
20 #include <boost/variant.hpp>
22 #include <boost/context/execution_context.hpp>
23 #include <boost/context/detail/config.hpp>
29 #if defined(BOOST_MSVC)
30 # pragma warning(push)
31 # pragma warning(disable: 4723)
34 typedef boost::variant<int,std::string> variant_t;
36 namespace ctx = boost::context;
43 ctx::execution_context< void > foo( int i, ctx::execution_context< void > && ctx) {
45 return std::move( ctx);
54 Y( Y const&) = delete;
55 Y & operator=( Y const&) = delete;
77 moveable( moveable && other) :
84 moveable & operator=( moveable && other) {
85 if ( this == & other) return * this;
93 moveable( moveable const& other) = delete;
94 moveable & operator=( moveable const& other) = delete;
101 struct my_exception : public std::runtime_error {
102 my_exception( char const* what) :
103 std::runtime_error( what) {
108 // Optimizations can remove the integer-divide-by-zero here.
109 #pragma optimize("", off)
110 void seh( bool & catched) {
114 } __except( EXCEPTION_EXECUTE_HANDLER) {
118 #pragma optimize("", on)
121 ctx::execution_context< void > fn1( int i, ctx::execution_context< void > && ctx) {
123 return std::move( ctx);
126 ctx::execution_context< void > fn2( const char * what, ctx::execution_context< void > && ctx) {
128 throw std::runtime_error( what);
129 } catch ( std::runtime_error const& e) {
132 return std::move( ctx);
135 ctx::execution_context< void > fn3( double d, ctx::execution_context< void > && ctx) {
138 return std::move( ctx);
141 ctx::execution_context< void > fn5( ctx::execution_context< void > && ctx) {
143 return std::move( ctx);
146 ctx::execution_context< void > fn4( ctx::execution_context< void > && ctx) {
147 ctx::execution_context< void > ctx1( fn5);
150 return std::move( ctx);
153 ctx::execution_context< void > fn6( ctx::execution_context< void > && ctx) {
162 } catch ( my_exception & e) {
165 return std::move( ctx);
168 ctx::execution_context< void > fn7( ctx::execution_context< void > && ctx) {
173 ctx::execution_context< int > fn8( ctx::execution_context< int > && ctx, int i) {
175 return std::move( ctx);
178 ctx::execution_context< int > fn9( ctx::execution_context< int > && ctx, int i) {
180 std::tie( ctx, i) = ctx( i);
182 return std::move( ctx);
185 ctx::execution_context< int & > fn10( ctx::execution_context< int & > && ctx, int & i) {
186 std::tie( ctx, i) = ctx( i);
187 return std::move( ctx);
190 ctx::execution_context< moveable > fn11( ctx::execution_context< moveable > && ctx, moveable m) {
191 std::tie( ctx, m) = ctx( std::move( m) );
192 return std::move( ctx);
195 ctx::execution_context< int, std::string > fn12( ctx::execution_context< int, std::string > && ctx, int i, std::string str) {
196 std::tie( ctx, i, str) = ctx( i, str);
197 return std::move( ctx);
200 ctx::execution_context< int, moveable > fn13( ctx::execution_context< int, moveable > && ctx, int i, moveable m) {
201 std::tie( ctx, i, m) = ctx( i, std::move( m) );
202 return std::move( ctx);
205 ctx::execution_context< variant_t > fn14( ctx::execution_context< variant_t > && ctx, variant_t data) {
206 int i = boost::get< int >( data);
207 data = boost::lexical_cast< std::string >( i);
208 std::tie( ctx, data) = ctx( data);
209 return std::move( ctx);
212 ctx::execution_context< Y * > fn15( ctx::execution_context< Y * > && ctx, Y * py) {
214 return std::move( ctx);
217 ctx::execution_context< int > fn16( ctx::execution_context< int > && ctx, int i) {
219 std::tie( ctx, i) = ctx( i);
221 return std::move( ctx);
224 ctx::execution_context< int, int > fn17( ctx::execution_context< int, int > && ctx, int i, int j) {
226 std::tie( ctx, i, j) = ctx( i, j);
228 return std::move( ctx);
231 ctx::execution_context< void > fn18( ctx::execution_context< void > && ctx) {
238 } catch ( boost::context::detail::forced_unwind const&) {
245 return std::move( ctx);
248 ctx::execution_context< void > fn19( ctx::execution_context< void > && ctx) {
256 return std::move( ctx);
262 ctx::execution_context< void > ctx;
264 ctx::execution_context< void > ctx1( fn1, 1);
265 ctx::execution_context< void > ctx2( fn1, 3);
268 ctx1 = std::move( ctx2);
270 BOOST_CHECK( ! ctx2);
271 BOOST_CHECK_EQUAL( 0, value1);
273 BOOST_CHECK_EQUAL( 3, value1);
274 BOOST_CHECK( ! ctx1);
275 BOOST_CHECK( ! ctx2);
281 ctx::execution_context< void > ctx( & X::foo, x, 7);
283 BOOST_CHECK_EQUAL( 7, value1);
286 void test_exception() {
288 const char * what = "hello world";
289 ctx::execution_context< void > ctx( fn2, what);
292 BOOST_CHECK_EQUAL( std::string( what), value2);
297 bool catched = false;
298 std::thread([&catched](){
299 ctx::execution_context< void > ctx([&catched](ctx::execution_context< void > && ctx){
301 return std::move( ctx);
306 BOOST_CHECK( catched);
313 ctx::execution_context< void > ctx( fn3, d);
316 BOOST_CHECK_EQUAL( 10.58, value3);
320 void test_stacked() {
323 ctx::execution_context< void > ctx( fn4);
326 BOOST_CHECK_EQUAL( 3, value1);
327 BOOST_CHECK_EQUAL( 3.14, value3);
331 void test_prealloc() {
333 ctx::default_stack alloc;
334 ctx::stack_context sctx( alloc.allocate() );
335 void * sp = static_cast< char * >( sctx.sp) - 10;
336 std::size_t size = sctx.size - 10;
337 ctx::execution_context< void > ctx( std::allocator_arg, ctx::preallocated( sp, size, sctx), alloc, fn1, 7);
340 BOOST_CHECK_EQUAL( 7, value1);
347 ctx::execution_context< int > ctx([]( ctx::execution_context< int > && ctx, int x) {
349 std::tie( ctx, x) = ctx( x*10);
351 return std::move( ctx);
353 std::tie( ctx, j) = ctx( ctx::exec_ontop_arg,
359 BOOST_CHECK_EQUAL( j, -70);
363 ctx::execution_context< int, int > ctx( fn17);
364 std::tie( ctx, i, j) = ctx( i, j);
365 std::tie( ctx, i, j) = ctx( ctx::exec_ontop_arg,
367 return std::make_tuple( x - y, x + y);
370 BOOST_CHECK_EQUAL( i, 2);
371 BOOST_CHECK_EQUAL( j, 4);
375 BOOST_CHECK( 7 == m1.value);
376 BOOST_CHECK( m1.state);
377 BOOST_CHECK( -1 == m2.value);
378 BOOST_CHECK( ! m2.state);
379 ctx::execution_context< moveable > ctx( fn11);
380 std::tie( ctx, m2) = ctx( ctx::exec_ontop_arg,
382 return std::move( m);
385 BOOST_CHECK( -1 == m1.value);
386 BOOST_CHECK( ! m1.state);
387 BOOST_CHECK( 7 == m2.value);
388 BOOST_CHECK( m2.state);
392 void test_ontop_exception() {
396 ctx::execution_context< void > ctx([](ctx::execution_context< void > && ctx){
401 } catch ( ctx::ontop_error const& e) {
403 std::rethrow_if_nested( e);
404 } catch ( my_exception const& ex) {
407 return e.get_context< void >();
410 return std::move( ctx);
413 BOOST_CHECK_EQUAL( 3, value1);
414 const char * what = "hello world";
415 ctx( ctx::exec_ontop_arg,
417 throw my_exception( what);
419 BOOST_CHECK_EQUAL( 3, value1);
420 BOOST_CHECK_EQUAL( std::string( what), value2);
425 ctx::execution_context< int, int > ctx([]( ctx::execution_context< int, int > && ctx, int x, int y) {
428 std::tie( ctx, x, y) = ctx( x+y,x-y);
429 } catch ( ctx::ontop_error const& e) {
431 std::rethrow_if_nested( e);
432 } catch ( my_exception const& ex) {
435 return e.get_context< int, int >();
438 return std::move( ctx);
440 std::tie( ctx, i, j) = ctx( i, j);
442 BOOST_CHECK_EQUAL( i, 4);
443 BOOST_CHECK_EQUAL( j, 2);
444 const char * what = "hello world";
445 std::tie( ctx, i, j) = ctx( ctx::exec_ontop_arg,
446 [what](int x, int y) {
447 throw my_exception(what);
448 return std::make_tuple( x*y, x/y);
451 BOOST_CHECK_EQUAL( i, 4);
452 BOOST_CHECK_EQUAL( j, 2);
453 BOOST_CHECK_EQUAL( std::string( what), value2);
457 void test_termination1() {
460 ctx::execution_context< void > ctx( fn7);
461 BOOST_CHECK_EQUAL( 0, value1);
463 BOOST_CHECK_EQUAL( 3, value1);
465 BOOST_CHECK_EQUAL( 7, value1);
468 BOOST_CHECK_EQUAL( 0, value1);
469 ctx::execution_context< void > ctx( fn5);
472 BOOST_CHECK_EQUAL( 3, value1);
477 BOOST_CHECK_EQUAL( 0, value1);
479 ctx::execution_context< int > ctx( fn9);
481 std::tie( ctx, j) = ctx( i);
482 BOOST_CHECK_EQUAL( i, value1);
484 BOOST_CHECK_EQUAL( i, j);
486 std::tie( ctx, j) = ctx( i);
487 BOOST_CHECK_EQUAL( i, value1);
489 BOOST_CHECK_EQUAL( i, j);
493 void test_termination2() {
497 ctx::execution_context< void > ctx( fn6);
498 BOOST_CHECK_EQUAL( 0, value1);
499 BOOST_CHECK_EQUAL( 0., value3);
501 BOOST_CHECK_EQUAL( 3, value1);
502 BOOST_CHECK_EQUAL( 4., value3);
504 BOOST_CHECK_EQUAL( 7, value1);
505 BOOST_CHECK_EQUAL( 4., value3);
508 void test_one_arg() {
511 ctx::execution_context< int > ctx( fn8);
513 BOOST_CHECK_EQUAL( 7, value1);
517 ctx::execution_context< int > ctx( fn9);
518 std::tie( ctx, j) = ctx( i);
519 BOOST_CHECK_EQUAL( i, j);
524 BOOST_CHECK( & i != & k);
525 BOOST_CHECK( & j == & k);
526 ctx::execution_context< int & > ctx( fn10);
527 std::tie( ctx, k) = ctx( i);
528 BOOST_CHECK( & i != & k);
533 ctx::execution_context< Y * > ctx( fn15);
534 std::tie( ctx, py) = ctx( & y);
535 BOOST_CHECK( py == & y);
539 BOOST_CHECK( 7 == m1.value);
540 BOOST_CHECK( m1.state);
541 BOOST_CHECK( -1 == m2.value);
542 BOOST_CHECK( ! m2.state);
543 ctx::execution_context< moveable > ctx( fn11);
544 std::tie( ctx, m2) = ctx( std::move( m1) );
545 BOOST_CHECK( -1 == m1.value);
546 BOOST_CHECK( ! m1.state);
547 BOOST_CHECK( 7 == m2.value);
548 BOOST_CHECK( m2.state);
552 void test_two_args() {
555 std::string str1("abc"), str2;
556 ctx::execution_context< int, std::string > ctx( fn12);
557 std::tie( ctx, i2, str2) = ctx( i1, str1);
558 BOOST_CHECK_EQUAL( i1, i2);
559 BOOST_CHECK_EQUAL( str1, str2);
564 BOOST_CHECK( 7 == m1.value);
565 BOOST_CHECK( m1.state);
566 BOOST_CHECK( -1 == m2.value);
567 BOOST_CHECK( ! m2.state);
568 ctx::execution_context< int, moveable > ctx( fn13);
569 std::tie( ctx, i2, m2) = ctx( i1, std::move( m1) );
570 BOOST_CHECK_EQUAL( i1, i2);
571 BOOST_CHECK( -1 == m1.value);
572 BOOST_CHECK( ! m1.state);
573 BOOST_CHECK( 7 == m2.value);
574 BOOST_CHECK( m2.state);
578 void test_variant() {
581 variant_t data1 = i, data2;
582 ctx::execution_context< variant_t > ctx( fn14);
583 std::tie( ctx, data2) = ctx( data1);
584 std::string str = boost::get< std::string >( data2);
585 BOOST_CHECK_EQUAL( std::string("7"), str);
590 void test_bug12215() {
591 ctx::execution_context< void > ctx(
592 [](ctx::execution_context< void > && ctx) {
593 char buffer[MAX_PATH];
594 GetModuleFileName( nullptr, buffer, MAX_PATH);
595 return std::move( ctx);
602 void test_goodcatch() {
606 ctx::execution_context< void > ctx( fn18);
607 BOOST_CHECK_EQUAL( 0, value1);
608 BOOST_CHECK_EQUAL( 0., value3);
610 BOOST_CHECK_EQUAL( 3, value1);
611 BOOST_CHECK_EQUAL( 2., value3);
613 BOOST_CHECK_EQUAL( 3, value1);
614 BOOST_CHECK_EQUAL( 3., value3);
616 BOOST_CHECK_EQUAL( 7, value1);
617 BOOST_CHECK_EQUAL( 4., value3);
620 void test_badcatch() {
625 ctx::execution_context< void > * ctx = new ctx::execution_context< void >( fn19);
626 BOOST_CHECK_EQUAL( 0, value1);
627 BOOST_CHECK_EQUAL( 0., value3);
629 BOOST_CHECK_EQUAL( 3, value1);
630 BOOST_CHECK_EQUAL( 3., value3);
631 // the destruction of ctx here will cause a forced_unwind to be thrown that is not caught
632 // in fn19. That will trigger the "not caught" assertion in ~forced_unwind. Getting that
633 // assertion to propogate bak here cleanly is non-trivial, and there seems to not be a good
634 // way to hook directly into the assertion when it happens on an alternate stack.
637 BOOST_CHECK_EQUAL( 7, value1);
638 BOOST_CHECK_EQUAL( 4., value3);
642 boost::unit_test::test_suite * init_unit_test_suite( int, char* [])
644 boost::unit_test::test_suite * test =
645 BOOST_TEST_SUITE("Boost.Context: execution_context v2 test suite");
647 test->add( BOOST_TEST_CASE( & test_move) );
648 test->add( BOOST_TEST_CASE( & test_memfn) );
649 test->add( BOOST_TEST_CASE( & test_exception) );
650 test->add( BOOST_TEST_CASE( & test_fp) );
651 test->add( BOOST_TEST_CASE( & test_stacked) );
652 test->add( BOOST_TEST_CASE( & test_prealloc) );
653 test->add( BOOST_TEST_CASE( & test_ontop) );
654 test->add( BOOST_TEST_CASE( & test_ontop_exception) );
655 test->add( BOOST_TEST_CASE( & test_termination1) );
656 test->add( BOOST_TEST_CASE( & test_termination2) );
657 test->add( BOOST_TEST_CASE( & test_one_arg) );
658 test->add( BOOST_TEST_CASE( & test_two_args) );
659 test->add( BOOST_TEST_CASE( & test_variant) );
661 test->add( BOOST_TEST_CASE( & test_bug12215) );
663 test->add( BOOST_TEST_CASE( & test_goodcatch) );
664 test->add( BOOST_TEST_CASE( & test_badcatch) );
669 #if defined(BOOST_MSVC)
670 # pragma warning(pop)