// Copyright Oliver Kowalke 2009. // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef BOOST_WINDOWS #include #endif #if defined(BOOST_MSVC) # pragma warning(push) # pragma warning(disable: 4723) #endif typedef boost::variant variant_t; namespace ctx = boost::context; int value1 = 0; std::string value2; double value3 = 0.; struct X { ctx::execution_context< void > foo( int i, ctx::execution_context< void > && ctx) { value1 = i; return std::move( ctx); } }; struct Y { Y() { value1 = 3; } Y( Y const&) = delete; Y & operator=( Y const&) = delete; ~Y() { value1 = 7; } }; class moveable { public: bool state; int value; moveable() : state( false), value( -1) { } moveable( int v) : state( true), value( v) { } moveable( moveable && other) : state( other.state), value( other.value) { other.state = false; other.value = -1; } moveable & operator=( moveable && other) { if ( this == & other) return * this; state = other.state; value = other.value; other.state = false; other.value = -1; return * this; } moveable( moveable const& other) = delete; moveable & operator=( moveable const& other) = delete; void operator()() { value1 = value; } }; struct my_exception : public std::runtime_error { my_exception( char const* what) : std::runtime_error( what) { } }; #ifdef BOOST_MSVC // Optimizations can remove the integer-divide-by-zero here. #pragma optimize("", off) void seh( bool & catched) { __try { int i = 1; i /= 0; } __except( EXCEPTION_EXECUTE_HANDLER) { catched = true; } } #pragma optimize("", on) #endif ctx::execution_context< void > fn1( int i, ctx::execution_context< void > && ctx) { value1 = i; return std::move( ctx); } ctx::execution_context< void > fn2( const char * what, ctx::execution_context< void > && ctx) { try { throw std::runtime_error( what); } catch ( std::runtime_error const& e) { value2 = e.what(); } return std::move( ctx); } ctx::execution_context< void > fn3( double d, ctx::execution_context< void > && ctx) { d += 3.45; value3 = d; return std::move( ctx); } ctx::execution_context< void > fn5( ctx::execution_context< void > && ctx) { value1 = 3; return std::move( ctx); } ctx::execution_context< void > fn4( ctx::execution_context< void > && ctx) { ctx::execution_context< void > ctx1( fn5); ctx1(); value3 = 3.14; return std::move( ctx); } ctx::execution_context< void > fn6( ctx::execution_context< void > && ctx) { Y y; try { value1 = 3; value3 = 4.; ctx = ctx(); value1 = 7; value3 = 8.; ctx = ctx(); } catch ( my_exception & e) { value2 = e.what(); } return std::move( ctx); } ctx::execution_context< void > fn7( ctx::execution_context< void > && ctx) { Y y; return ctx(); } ctx::execution_context< int > fn8( ctx::execution_context< int > && ctx, int i) { value1 = i; return std::move( ctx); } ctx::execution_context< int > fn9( ctx::execution_context< int > && ctx, int i) { value1 = i; std::tie( ctx, i) = ctx( i); value1 = i; return std::move( ctx); } ctx::execution_context< int & > fn10( ctx::execution_context< int & > && ctx, int & i) { std::tie( ctx, i) = ctx( i); return std::move( ctx); } ctx::execution_context< moveable > fn11( ctx::execution_context< moveable > && ctx, moveable m) { std::tie( ctx, m) = ctx( std::move( m) ); return std::move( ctx); } ctx::execution_context< int, std::string > fn12( ctx::execution_context< int, std::string > && ctx, int i, std::string str) { std::tie( ctx, i, str) = ctx( i, str); return std::move( ctx); } ctx::execution_context< int, moveable > fn13( ctx::execution_context< int, moveable > && ctx, int i, moveable m) { std::tie( ctx, i, m) = ctx( i, std::move( m) ); return std::move( ctx); } ctx::execution_context< variant_t > fn14( ctx::execution_context< variant_t > && ctx, variant_t data) { int i = boost::get< int >( data); data = boost::lexical_cast< std::string >( i); std::tie( ctx, data) = ctx( data); return std::move( ctx); } ctx::execution_context< Y * > fn15( ctx::execution_context< Y * > && ctx, Y * py) { ctx( py); return std::move( ctx); } ctx::execution_context< int > fn16( ctx::execution_context< int > && ctx, int i) { value1 = i; std::tie( ctx, i) = ctx( i); value1 = i; return std::move( ctx); } ctx::execution_context< int, int > fn17( ctx::execution_context< int, int > && ctx, int i, int j) { for (;;) { std::tie( ctx, i, j) = ctx( i, j); } return std::move( ctx); } ctx::execution_context< void > fn18( ctx::execution_context< void > && ctx) { Y y; value3 = 2.; ctx = ctx(); try { value3 = 3.; ctx = ctx(); } catch ( boost::context::detail::forced_unwind const&) { value3 = 4.; throw; } catch (...) { value3 = 5.; } value3 = 6.; return std::move( ctx); } ctx::execution_context< void > fn19( ctx::execution_context< void > && ctx) { Y y; try { value3 = 3.; ctx = ctx(); } catch (...) { value3 = 5.; } return std::move( ctx); } void test_move() { value1 = 0; ctx::execution_context< void > ctx; BOOST_CHECK( ! ctx); ctx::execution_context< void > ctx1( fn1, 1); ctx::execution_context< void > ctx2( fn1, 3); BOOST_CHECK( ctx1); BOOST_CHECK( ctx2); ctx1 = std::move( ctx2); BOOST_CHECK( ctx1); BOOST_CHECK( ! ctx2); BOOST_CHECK_EQUAL( 0, value1); ctx1(); BOOST_CHECK_EQUAL( 3, value1); BOOST_CHECK( ! ctx1); BOOST_CHECK( ! ctx2); } void test_memfn() { value1 = 0; X x; ctx::execution_context< void > ctx( & X::foo, x, 7); ctx(); BOOST_CHECK_EQUAL( 7, value1); } void test_exception() { { const char * what = "hello world"; ctx::execution_context< void > ctx( fn2, what); BOOST_CHECK( ctx); ctx(); BOOST_CHECK_EQUAL( std::string( what), value2); BOOST_CHECK( ! ctx); } #ifdef BOOST_MSVC { bool catched = false; std::thread([&catched](){ ctx::execution_context< void > ctx([&catched](ctx::execution_context< void > && ctx){ seh( catched); return std::move( ctx); }); BOOST_CHECK( ctx); ctx(); }).join(); BOOST_CHECK( catched); } #endif } void test_fp() { double d = 7.13; ctx::execution_context< void > ctx( fn3, d); BOOST_CHECK( ctx); ctx(); BOOST_CHECK_EQUAL( 10.58, value3); BOOST_CHECK( ! ctx); } void test_stacked() { value1 = 0; value3 = 0.; ctx::execution_context< void > ctx( fn4); BOOST_CHECK( ctx); ctx(); BOOST_CHECK_EQUAL( 3, value1); BOOST_CHECK_EQUAL( 3.14, value3); BOOST_CHECK( ! ctx); } void test_prealloc() { value1 = 0; ctx::default_stack alloc; ctx::stack_context sctx( alloc.allocate() ); void * sp = static_cast< char * >( sctx.sp) - 10; std::size_t size = sctx.size - 10; ctx::execution_context< void > ctx( std::allocator_arg, ctx::preallocated( sp, size, sctx), alloc, fn1, 7); BOOST_CHECK( ctx); ctx(); BOOST_CHECK_EQUAL( 7, value1); BOOST_CHECK( ! ctx); } void test_ontop() { { int i = 3, j = 0; ctx::execution_context< int > ctx([]( ctx::execution_context< int > && ctx, int x) { for (;;) { std::tie( ctx, x) = ctx( x*10); } return std::move( ctx); }); std::tie( ctx, j) = ctx( ctx::exec_ontop_arg, []( int x){ return x-10; }, i); BOOST_CHECK( ctx); BOOST_CHECK_EQUAL( j, -70); } { int i = 3, j = 1; ctx::execution_context< int, int > ctx( fn17); std::tie( ctx, i, j) = ctx( i, j); std::tie( ctx, i, j) = ctx( ctx::exec_ontop_arg, []( int x, int y) { return std::make_tuple( x - y, x + y); }, i, j); BOOST_CHECK_EQUAL( i, 2); BOOST_CHECK_EQUAL( j, 4); } { moveable m1( 7), m2; BOOST_CHECK( 7 == m1.value); BOOST_CHECK( m1.state); BOOST_CHECK( -1 == m2.value); BOOST_CHECK( ! m2.state); ctx::execution_context< moveable > ctx( fn11); std::tie( ctx, m2) = ctx( ctx::exec_ontop_arg, []( moveable&& m){ return std::move( m); }, std::move( m1) ); BOOST_CHECK( -1 == m1.value); BOOST_CHECK( ! m1.state); BOOST_CHECK( 7 == m2.value); BOOST_CHECK( m2.state); } } void test_ontop_exception() { { value1 = 0; value2 = ""; ctx::execution_context< void > ctx([](ctx::execution_context< void > && ctx){ for (;;) { value1 = 3; try { ctx = ctx(); } catch ( ctx::ontop_error const& e) { try { std::rethrow_if_nested( e); } catch ( my_exception const& ex) { value2 = ex.what(); } return e.get_context< void >(); } } return std::move( ctx); }); ctx = ctx(); BOOST_CHECK_EQUAL( 3, value1); const char * what = "hello world"; ctx( ctx::exec_ontop_arg, [what](){ throw my_exception( what); }); BOOST_CHECK_EQUAL( 3, value1); BOOST_CHECK_EQUAL( std::string( what), value2); } { value2 = ""; int i = 3, j = 1; ctx::execution_context< int, int > ctx([]( ctx::execution_context< int, int > && ctx, int x, int y) { for (;;) { try { std::tie( ctx, x, y) = ctx( x+y,x-y); } catch ( ctx::ontop_error const& e) { try { std::rethrow_if_nested( e); } catch ( my_exception const& ex) { value2 = ex.what(); } return e.get_context< int, int >(); } } return std::move( ctx); }); std::tie( ctx, i, j) = ctx( i, j); BOOST_CHECK( ctx); BOOST_CHECK_EQUAL( i, 4); BOOST_CHECK_EQUAL( j, 2); const char * what = "hello world"; std::tie( ctx, i, j) = ctx( ctx::exec_ontop_arg, [what](int x, int y) { throw my_exception(what); return std::make_tuple( x*y, x/y); }, i, j); BOOST_CHECK_EQUAL( i, 4); BOOST_CHECK_EQUAL( j, 2); BOOST_CHECK_EQUAL( std::string( what), value2); } } void test_termination1() { { value1 = 0; ctx::execution_context< void > ctx( fn7); BOOST_CHECK_EQUAL( 0, value1); ctx = ctx(); BOOST_CHECK_EQUAL( 3, value1); } BOOST_CHECK_EQUAL( 7, value1); { value1 = 0; BOOST_CHECK_EQUAL( 0, value1); ctx::execution_context< void > ctx( fn5); BOOST_CHECK( ctx); ctx(); BOOST_CHECK_EQUAL( 3, value1); BOOST_CHECK( ! ctx); } { value1 = 0; BOOST_CHECK_EQUAL( 0, value1); int i = 3, j = 0; ctx::execution_context< int > ctx( fn9); BOOST_CHECK( ctx); std::tie( ctx, j) = ctx( i); BOOST_CHECK_EQUAL( i, value1); BOOST_CHECK( ctx); BOOST_CHECK_EQUAL( i, j); i = 7; std::tie( ctx, j) = ctx( i); BOOST_CHECK_EQUAL( i, value1); BOOST_CHECK( ! ctx); BOOST_CHECK_EQUAL( i, j); } } void test_termination2() { { value1 = 0; value3 = 0.; ctx::execution_context< void > ctx( fn6); BOOST_CHECK_EQUAL( 0, value1); BOOST_CHECK_EQUAL( 0., value3); ctx = ctx(); BOOST_CHECK_EQUAL( 3, value1); BOOST_CHECK_EQUAL( 4., value3); } BOOST_CHECK_EQUAL( 7, value1); BOOST_CHECK_EQUAL( 4., value3); } void test_one_arg() { { value1 = 0; ctx::execution_context< int > ctx( fn8); ctx( 7); BOOST_CHECK_EQUAL( 7, value1); } { int i = 3, j = 0; ctx::execution_context< int > ctx( fn9); std::tie( ctx, j) = ctx( i); BOOST_CHECK_EQUAL( i, j); } { int i = 3, j = 0; int & k = j; BOOST_CHECK( & i != & k); BOOST_CHECK( & j == & k); ctx::execution_context< int & > ctx( fn10); std::tie( ctx, k) = ctx( i); BOOST_CHECK( & i != & k); } { Y y; Y * py = nullptr; ctx::execution_context< Y * > ctx( fn15); std::tie( ctx, py) = ctx( & y); BOOST_CHECK( py == & y); } { moveable m1( 7), m2; BOOST_CHECK( 7 == m1.value); BOOST_CHECK( m1.state); BOOST_CHECK( -1 == m2.value); BOOST_CHECK( ! m2.state); ctx::execution_context< moveable > ctx( fn11); std::tie( ctx, m2) = ctx( std::move( m1) ); BOOST_CHECK( -1 == m1.value); BOOST_CHECK( ! m1.state); BOOST_CHECK( 7 == m2.value); BOOST_CHECK( m2.state); } } void test_two_args() { { int i1 = 3, i2 = 0; std::string str1("abc"), str2; ctx::execution_context< int, std::string > ctx( fn12); std::tie( ctx, i2, str2) = ctx( i1, str1); BOOST_CHECK_EQUAL( i1, i2); BOOST_CHECK_EQUAL( str1, str2); } { int i1 = 3, i2 = 0; moveable m1( 7), m2; BOOST_CHECK( 7 == m1.value); BOOST_CHECK( m1.state); BOOST_CHECK( -1 == m2.value); BOOST_CHECK( ! m2.state); ctx::execution_context< int, moveable > ctx( fn13); std::tie( ctx, i2, m2) = ctx( i1, std::move( m1) ); BOOST_CHECK_EQUAL( i1, i2); BOOST_CHECK( -1 == m1.value); BOOST_CHECK( ! m1.state); BOOST_CHECK( 7 == m2.value); BOOST_CHECK( m2.state); } } void test_variant() { { int i = 7; variant_t data1 = i, data2; ctx::execution_context< variant_t > ctx( fn14); std::tie( ctx, data2) = ctx( data1); std::string str = boost::get< std::string >( data2); BOOST_CHECK_EQUAL( std::string("7"), str); } } #ifdef BOOST_WINDOWS void test_bug12215() { ctx::execution_context< void > ctx( [](ctx::execution_context< void > && ctx) { char buffer[MAX_PATH]; GetModuleFileName( nullptr, buffer, MAX_PATH); return std::move( ctx); }); ctx(); } #endif void test_goodcatch() { value1 = 0; value3 = 0.; { ctx::execution_context< void > ctx( fn18); BOOST_CHECK_EQUAL( 0, value1); BOOST_CHECK_EQUAL( 0., value3); ctx = ctx(); BOOST_CHECK_EQUAL( 3, value1); BOOST_CHECK_EQUAL( 2., value3); ctx = ctx(); BOOST_CHECK_EQUAL( 3, value1); BOOST_CHECK_EQUAL( 3., value3); } BOOST_CHECK_EQUAL( 7, value1); BOOST_CHECK_EQUAL( 4., value3); } void test_badcatch() { #if 0 value1 = 0; value3 = 0.; { ctx::execution_context< void > * ctx = new ctx::execution_context< void >( fn19); BOOST_CHECK_EQUAL( 0, value1); BOOST_CHECK_EQUAL( 0., value3); (*ctx) = (*ctx)(); BOOST_CHECK_EQUAL( 3, value1); BOOST_CHECK_EQUAL( 3., value3); // the destruction of ctx here will cause a forced_unwind to be thrown that is not caught // in fn19. That will trigger the "not caught" assertion in ~forced_unwind. Getting that // assertion to propogate bak here cleanly is non-trivial, and there seems to not be a good // way to hook directly into the assertion when it happens on an alternate stack. delete ctx; } BOOST_CHECK_EQUAL( 7, value1); BOOST_CHECK_EQUAL( 4., value3); #endif } boost::unit_test::test_suite * init_unit_test_suite( int, char* []) { boost::unit_test::test_suite * test = BOOST_TEST_SUITE("Boost.Context: execution_context v2 test suite"); test->add( BOOST_TEST_CASE( & test_move) ); test->add( BOOST_TEST_CASE( & test_memfn) ); test->add( BOOST_TEST_CASE( & test_exception) ); test->add( BOOST_TEST_CASE( & test_fp) ); test->add( BOOST_TEST_CASE( & test_stacked) ); test->add( BOOST_TEST_CASE( & test_prealloc) ); test->add( BOOST_TEST_CASE( & test_ontop) ); test->add( BOOST_TEST_CASE( & test_ontop_exception) ); test->add( BOOST_TEST_CASE( & test_termination1) ); test->add( BOOST_TEST_CASE( & test_termination2) ); test->add( BOOST_TEST_CASE( & test_one_arg) ); test->add( BOOST_TEST_CASE( & test_two_args) ); test->add( BOOST_TEST_CASE( & test_variant) ); #ifdef BOOST_WINDOWS test->add( BOOST_TEST_CASE( & test_bug12215) ); #endif test->add( BOOST_TEST_CASE( & test_goodcatch) ); test->add( BOOST_TEST_CASE( & test_badcatch) ); return test; } #if defined(BOOST_MSVC) # pragma warning(pop) #endif