diff options
Diffstat (limited to 'src/boost/libs/context/test/test_fiber.cpp')
-rw-r--r-- | src/boost/libs/context/test/test_fiber.cpp | 538 |
1 files changed, 538 insertions, 0 deletions
diff --git a/src/boost/libs/context/test/test_fiber.cpp b/src/boost/libs/context/test/test_fiber.cpp new file mode 100644 index 00000000..e883d143 --- /dev/null +++ b/src/boost/libs/context/test/test_fiber.cpp @@ -0,0 +1,538 @@ + +// 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 <stdio.h> +#include <stdlib.h> + +#include <cmath> +#include <cstdint> +#include <cstdio> +#include <iostream> +#include <memory> +#include <sstream> +#include <stdexcept> +#include <string> +#include <thread> +#include <utility> +#include <vector> + +#include <boost/array.hpp> +#include <boost/assert.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/test/unit_test.hpp> +#include <boost/utility.hpp> +#include <boost/variant.hpp> + +#include <boost/context/fiber.hpp> +#include <boost/context/detail/config.hpp> + +#ifdef BOOST_WINDOWS +#include <windows.h> +#endif + +#if defined(BOOST_MSVC) +# pragma warning(push) +# pragma warning(disable: 4702 4723 4996) +#endif + +typedef boost::variant<int,std::string> variant_t; + +namespace ctx = boost::context; + +int value1 = 0; +std::string value2; +double value3 = 0.; + +struct X { + ctx::fiber foo( ctx::fiber && f, int i) { + value1 = i; + return std::move( f); + } +}; + +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 { + ctx::fiber f; + my_exception( ctx::fiber && f_, char const* what) : + std::runtime_error( what), + f{ std::move( f_) } { + } +}; + +#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 + +void test_move() { + value1 = 0; + int i = 1; + BOOST_CHECK_EQUAL( 0, value1); + ctx::fiber f1{ + [&i](ctx::fiber && f) { + value1 = i; + f = std::move( f).resume(); + value1 = i; + return std::move( f); + }}; + f1 = std::move( f1).resume(); + BOOST_CHECK_EQUAL( 1, value1); + BOOST_CHECK( f1); + ctx::fiber f2; + BOOST_CHECK( ! f2); + f2 = std::move( f1); + BOOST_CHECK( ! f1); + BOOST_CHECK( f2); + i = 3; + f2 = std::move( f2).resume(); + BOOST_CHECK_EQUAL( 3, value1); + BOOST_CHECK( ! f1); + BOOST_CHECK( ! f2); +} + +void test_bind() { + value1 = 0; + X x; + ctx::fiber f{ std::bind( & X::foo, x, std::placeholders::_1, 7) }; + f = std::move( f).resume(); + BOOST_CHECK_EQUAL( 7, value1); +} + +void test_exception() { + { + const char * what = "hello world"; + ctx::fiber f{ + [&what](ctx::fiber && f) { + try { + throw std::runtime_error( what); + } catch ( std::runtime_error const& e) { + value2 = e.what(); + } + return std::move( f); + }}; + f = std::move( f).resume(); + BOOST_CHECK_EQUAL( std::string( what), value2); + BOOST_CHECK( ! f); + } +#ifdef BOOST_MSVC + { + bool catched = false; + std::thread([&catched](){ + ctx::fiber f{ [&catched](ctx::fiber && f){ + seh( catched); + return std::move( f); + }}; + BOOST_CHECK( f); + f = std::move( f).resume(); + }).join(); + BOOST_CHECK( catched); + } +#endif +} + +void test_fp() { + value3 = 0.; + double d = 7.13; + ctx::fiber f{ + [&d]( ctx::fiber && f) { + d += 3.45; + value3 = d; + return std::move( f); + }}; + f = std::move( f).resume(); + BOOST_CHECK_EQUAL( 10.58, value3); + BOOST_CHECK( ! f); +} + +void test_stacked() { + value1 = 0; + value3 = 0.; + ctx::fiber f{ + [](ctx::fiber && f) { + ctx::fiber f1{ + [](ctx::fiber && f) { + value1 = 3; + return std::move( f); + }}; + f1 = std::move( f1).resume(); + value3 = 3.14; + return std::move( f); + }}; + f = std::move( f).resume(); + BOOST_CHECK_EQUAL( 3, value1); + BOOST_CHECK_EQUAL( 3.14, value3); + BOOST_CHECK( ! f); +} + +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; + int i = 7; + ctx::fiber f{ + std::allocator_arg, ctx::preallocated( sp, size, sctx), alloc, + [&i]( ctx::fiber && f) { + value1 = i; + return std::move( f); + }}; + f = std::move( f).resume(); + BOOST_CHECK_EQUAL( 7, value1); + BOOST_CHECK( ! f); +} + +void test_ontop() { + { + int i = 3; + ctx::fiber f{ [&i](ctx::fiber && f) { + for (;;) { + i *= 10; + f = std::move( f).resume(); + } + return std::move( f); + }}; + f = std::move( f).resume(); + // Pass fn by reference to see if the types are properly decayed. + auto fn = [&i](ctx::fiber && f){ + i -= 10; + return std::move( f); + }; + f = std::move( f).resume_with(fn); + BOOST_CHECK( f); + BOOST_CHECK_EQUAL( i, 200); + } + { + ctx::fiber f1; + ctx::fiber f{ [&f1](ctx::fiber && f) { + f = std::move( f).resume(); + BOOST_CHECK( ! f); + return std::move( f1); + }}; + f = std::move( f).resume(); + f = std::move( f).resume_with( + [&f1](ctx::fiber && f){ + f1 = std::move( f); + return std::move( f); + }); + } +} + +void test_ontop_exception() { + value1 = 0; + value2 = ""; + ctx::fiber f{ [](ctx::fiber && f){ + for (;;) { + value1 = 3; + try { + f = std::move( f).resume(); + } catch ( my_exception & ex) { + value2 = ex.what(); + return std::move( ex.f); + } + } + return std::move( f); + }}; + f = std::move( f).resume(); + BOOST_CHECK_EQUAL( 3, value1); + const char * what = "hello world"; + f = std::move( f).resume_with( + [what](ctx::fiber && f){ + throw my_exception( std::move( f), what); + return std::move( f); + }); + BOOST_CHECK_EQUAL( 3, value1); + BOOST_CHECK_EQUAL( std::string( what), value2); +} + +void test_termination1() { + { + value1 = 0; + ctx::fiber f{ + [](ctx::fiber && f){ + Y y; + f = std::move( f).resume(); + return std::move(f); + }}; + f = std::move( f).resume(); + BOOST_CHECK_EQUAL( 3, value1); + } + BOOST_CHECK_EQUAL( 7, value1); + { + value1 = 0; + BOOST_CHECK_EQUAL( 0, value1); + ctx::fiber f{ + [](ctx::fiber && f) { + value1 = 3; + return std::move( f); + }}; + f = std::move( f).resume(); + BOOST_CHECK_EQUAL( 3, value1); + BOOST_CHECK( ! f); + } + { + value1 = 0; + BOOST_CHECK_EQUAL( 0, value1); + int i = 3; + ctx::fiber f{ + [&i](ctx::fiber && f){ + value1 = i; + f = std::move( f).resume(); + value1 = i; + return std::move( f); + }}; + f = std::move( f).resume(); + BOOST_CHECK( f); + BOOST_CHECK_EQUAL( i, value1); + BOOST_CHECK( f); + i = 7; + f = std::move( f).resume(); + BOOST_CHECK( ! f); + BOOST_CHECK_EQUAL( i, value1); + } +} + +void test_termination2() { + { + value1 = 0; + value3 = 0.0; + ctx::fiber f{ + [](ctx::fiber && f){ + Y y; + value1 = 3; + value3 = 4.; + f = std::move( f).resume(); + value1 = 7; + value3 = 8.; + f = std::move( f).resume(); + return std::move( f); + }}; + BOOST_CHECK_EQUAL( 0, value1); + BOOST_CHECK_EQUAL( 0., value3); + f = std::move( f).resume(); + BOOST_CHECK_EQUAL( 3, value1); + BOOST_CHECK_EQUAL( 4., value3); + f = std::move( f).resume(); + } + BOOST_CHECK_EQUAL( 7, value1); + BOOST_CHECK_EQUAL( 8., value3); +} + +void test_sscanf() { + ctx::fiber{ + []( ctx::fiber && f) { + { + double n1 = 0; + double n2 = 0; + sscanf("3.14 7.13", "%lf %lf", & n1, & n2); + BOOST_CHECK( n1 == 3.14); + BOOST_CHECK( n2 == 7.13); + } + { + int n1=0; + int n2=0; + sscanf("1 23", "%d %d", & n1, & n2); + BOOST_CHECK( n1 == 1); + BOOST_CHECK( n2 == 23); + } + { + int n1=0; + int n2=0; + sscanf("1 jjj 23", "%d %*[j] %d", & n1, & n2); + BOOST_CHECK( n1 == 1); + BOOST_CHECK( n2 == 23); + } + return std::move( f); + }}.resume(); +} + +void test_snprintf() { + ctx::fiber{ + []( ctx::fiber && f) { + { + const char *fmt = "sqrt(2) = %f"; + char buf[19]; + snprintf( buf, sizeof( buf), fmt, std::sqrt( 2) ); + BOOST_CHECK( 0 < sizeof( buf) ); + BOOST_ASSERT( std::string("sqrt(2) = 1.41") == std::string( buf, 14) ); + } + { + std::uint64_t n = 0xbcdef1234567890; + const char *fmt = "0x%016llX"; + char buf[100]; + snprintf( buf, sizeof( buf), fmt, n); + BOOST_ASSERT( std::string("0x0BCDEF1234567890") == std::string( buf, 18) ); + } + return std::move( f); + }}.resume(); +} + +#ifdef BOOST_WINDOWS +void test_bug12215() { + ctx::fiber{ + [](ctx::fiber && f) { + char buffer[MAX_PATH]; + GetModuleFileName( nullptr, buffer, MAX_PATH); + return std::move( f); + }}.resume(); +} +#endif + +void test_goodcatch() { + value1 = 0; + value3 = 0.0; + { + ctx::fiber f{ + []( ctx::fiber && f) { + Y y; + value3 = 2.; + f = std::move( f).resume(); + try { + value3 = 3.; + f = std::move( f).resume(); + } catch ( boost::context::detail::forced_unwind const&) { + value3 = 4.; + throw; + } catch (...) { + value3 = 5.; + } + value3 = 6.; + return std::move( f); + }}; + BOOST_CHECK_EQUAL( 0, value1); + BOOST_CHECK_EQUAL( 0., value3); + f = std::move( f).resume(); + BOOST_CHECK_EQUAL( 3, value1); + BOOST_CHECK_EQUAL( 2., value3); + f = std::move( f).resume(); + 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::fiber f{ + []( ctx::fiber && f) { + Y y; + try { + value3 = 3.; + f = std::move( f).resume(); + } catch (...) { + value3 = 5.; + } + return std::move( f); + }}; + BOOST_CHECK_EQUAL( 0, value1); + BOOST_CHECK_EQUAL( 0., value3); + f = std::move( f).resume(); + 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. + std::move( f); + } + 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: fiber test suite"); + + test->add( BOOST_TEST_CASE( & test_move) ); + test->add( BOOST_TEST_CASE( & test_bind) ); + 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_sscanf) ); + test->add( BOOST_TEST_CASE( & test_snprintf) ); +#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 |