summaryrefslogtreecommitdiffstats
path: root/src/boost/libs/context/test/test_fiber.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/boost/libs/context/test/test_fiber.cpp')
-rw-r--r--src/boost/libs/context/test/test_fiber.cpp538
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