diff options
Diffstat (limited to 'src/boost/libs/fiber')
115 files changed, 22678 insertions, 0 deletions
diff --git a/src/boost/libs/fiber/README.md b/src/boost/libs/fiber/README.md new file mode 100644 index 00000000..959f8505 --- /dev/null +++ b/src/boost/libs/fiber/README.md @@ -0,0 +1,11 @@ +Boost.fiber
+===========
+
+
+Boost.fiber provides a framework for micro-/userland-threads (fibers) scheduled cooperatively. The API contains classes and functions to manage and synchronize fibers similar to boost.thread.
+
+A fiber is able to store the current execution state, including all registers and CPU flags, the instruction pointer, and the stack pointer and later restore this state. The idea is to have multiple execution paths running on a single thread using a sort of cooperative scheduling (threads are preemptively scheduled) - the running fiber decides explicitly when it yields to allow another fiber to run (context switching).
+
+A context switch between threads costs usually thousands of CPU cycles on x86 compared to a fiber switch with less than 100 cycles. A fiber can only run on a single thread at any point in time.
+
+Boost.fiber requires C++11!
diff --git a/src/boost/libs/fiber/build/Jamfile.v2 b/src/boost/libs/fiber/build/Jamfile.v2 new file mode 100644 index 00000000..f62e2f0f --- /dev/null +++ b/src/boost/libs/fiber/build/Jamfile.v2 @@ -0,0 +1,148 @@ +# Boost.Fiber Library Build Jamfile + +# Copyright Oliver Kowalke 2013. +# 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) + +import feature ; +import modules ; +import testing ; +import toolset ; +import ../../config/checks/config : requires ; + +feature.feature numa : on : optional propagated composite ; +feature.compose <numa>on : <define>BOOST_USE_NUMA ; + +project boost/fiber + : requirements + <library>/boost/context//boost_context + <library>/boost/filesystem//boost_filesystem + <target-os>solaris:<linkflags>"-llgrp" + <target-os>windows:<define>_WIN32_WINNT=0x0601 + <toolset>gcc,<segmented-stacks>on:<cxxflags>-fsplit-stack + <toolset>gcc,<segmented-stacks>on:<cxxflags>-DBOOST_USE_SEGMENTED_STACKS + <toolset>clang,<segmented-stacks>on:<cxxflags>-fsplit-stack + <toolset>clang,<segmented-stacks>on:<cxxflags>-DBOOST_USE_SEGMENTED_STACKS + <link>shared:<define>BOOST_FIBERS_DYN_LINK=1 + <define>BOOST_FIBERS_SOURCE + <threading>multi + : usage-requirements + <link>shared:<define>BOOST_FIBERS_DYN_LINK=1 + <optimization>speed:<define>BOOST_DISABLE_ASSERTS + <variant>release:<define>BOOST_DISABLE_ASSERTS + : source-location ../src + ; + +rule numa ( properties * ) +{ + local result ; + if ( <numa>on in $(properties) ) + { + result = <numa>on ; + } + else + { + result = <build>no ; + } + return $(result) ; +} + +lib boost_fiber + : algo/algorithm.cpp + algo/round_robin.cpp + algo/shared_work.cpp + algo/work_stealing.cpp + barrier.cpp + condition_variable.cpp + context.cpp + fiber.cpp + future.cpp + mutex.cpp + properties.cpp + recursive_mutex.cpp + recursive_timed_mutex.cpp + timed_mutex.cpp + scheduler.cpp + : <link>shared:<library>../../context/build//boost_context + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + ; + + +alias numa_sources + : numa/aix/pin_thread.cpp + numa/aix/topology.cpp + : <target-os>aix + ; + +alias numa_sources + : numa/freebsd/pin_thread.cpp + numa/freebsd/topology.cpp + : <target-os>freebsd + ; + +alias numa_sources + : numa/hpux/pin_thread.cpp + numa/hpux/topology.cpp + : <target-os>hpux + ; + +alias numa_sources + : numa/linux/pin_thread.cpp + numa/linux/topology.cpp + : <target-os>linux + ; + +alias numa_sources + : numa/solaris/pin_thread.cpp + numa/solaris/topology.cpp + : <target-os>solaris + ; + +alias numa_sources + : numa/windows/pin_thread.cpp + numa/windows/topology.cpp + : <target-os>windows + ; + +alias numa_sources + : numa/pin_thread.cpp + numa/topology.cpp + ; + +explicit numa_sources ; + +lib boost_fiber_numa + : numa_sources + numa/algo/work_stealing.cpp + : + <conditional>@numa + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + ; + + +boost-install boost_fiber boost_fiber_numa ; diff --git a/src/boost/libs/fiber/examples/Jamfile.v2 b/src/boost/libs/fiber/examples/Jamfile.v2 new file mode 100644 index 00000000..0ebe2daf --- /dev/null +++ b/src/boost/libs/fiber/examples/Jamfile.v2 @@ -0,0 +1,53 @@ +# Boost.Fiber Library Examples Jamfile + +# Copyright Oliver Kowalke 2013. +# 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) + +# For more information, see http://www.boost.org/ + +import common ; +import feature ; +import indirect ; +import modules ; +import os ; +import toolset ; + +project boost/fiber/example + : requirements + <library>../build//boost_fiber + <library>/boost/context//boost_context + <library>/boost/filesystem//boost_filesystem + <library>/boost/thread//boost_thread + <target-os>solaris:<linkflags>"-llgrp" + <target-os>solaris:<linkflags>"-lsocket" + <target-os>windows:<define>_WIN32_WINNT=0x0601 + <toolset>gcc,<segmented-stacks>on:<cxxflags>-fsplit-stack + <toolset>gcc,<segmented-stacks>on:<cxxflags>-DBOOST_USE_SEGMENTED_STACKS + <toolset>clang,<segmented-stacks>on:<cxxflags>-fsplit-stack + <toolset>clang,<segmented-stacks>on:<cxxflags>-DBOOST_USE_SEGMENTED_STACKS + <link>shared + <threading>multi + ; + +exe adapt_callbacks : adapt_callbacks.cpp ; +exe adapt_method_calls : adapt_method_calls.cpp ; +exe adapt_nonblocking : adapt_nonblocking.cpp ; +exe barrier : barrier.cpp ; +exe future : future.cpp ; +exe join : join.cpp ; +exe ping_pong : ping_pong.cpp ; +exe range_for : range_for.cpp ; +exe priority : priority.cpp ; +exe segmented_stack : segmented_stack.cpp ; +exe simple : simple.cpp ; +exe wait_stuff : wait_stuff.cpp ; +exe work_sharing : work_sharing.cpp ; +exe work_stealing : work_stealing.cpp ; + +exe asio/autoecho : asio/autoecho.cpp ; +exe asio/exchange : asio/exchange.cpp ; +exe asio/ps/publisher : asio/ps/publisher.cpp ; +exe asio/ps/server : asio/ps/server.cpp ; +exe asio/ps/subscriber : asio/ps/subscriber.cpp ; diff --git a/src/boost/libs/fiber/examples/adapt_callbacks.cpp b/src/boost/libs/fiber/examples/adapt_callbacks.cpp new file mode 100644 index 00000000..1c6bb502 --- /dev/null +++ b/src/boost/libs/fiber/examples/adapt_callbacks.cpp @@ -0,0 +1,316 @@ +// Copyright Nat Goodspeed 2015. +// 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 <cassert> +#include <chrono> +#include <exception> +#include <iostream> +#include <sstream> +#include <thread> +#include <tuple> // std::tie() + +#include <boost/context/detail/apply.hpp> +#include <boost/fiber/all.hpp> + +#if defined(BOOST_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES) +/***************************************************************************** +* helper code to help callback +*****************************************************************************/ +template< typename Fn, typename ... Args > +class helper { +private: + typename std::decay< Fn >::type fn_; + std::tuple< typename std::decay< Args >::type ... > args_; + +public: + helper( Fn && fn, Args && ... args) : + fn_( std::forward< Fn >( fn) ), + args_( std::make_tuple( std::forward< Args >( args) ... ) ) { + } + + helper( helper && other) = default; + helper & operator=( helper && other) = default; + + helper( helper const&) = default; + helper & operator=( helper const&) = default; + + void operator()() { + boost::context::detail::apply( fn_, args_); + } +}; + +template< typename Fn, typename ... Args > +helper< Fn, Args ... > help( Fn && fn, Args && ... args) { + return helper< Fn, Args ... >( std::forward< Fn >( fn), std::forward< Args >( args) ... ); +} +#endif + +/***************************************************************************** +* example async API +*****************************************************************************/ +//[AsyncAPI +class AsyncAPI { +public: + // constructor acquires some resource that can be read and written + AsyncAPI(); + + // callbacks accept an int error code; 0 == success + typedef int errorcode; + + // write callback only needs to indicate success or failure + template< typename Fn > + void init_write( std::string const& data, Fn && callback); + + // read callback needs to accept both errorcode and data + template< typename Fn > + void init_read( Fn && callback); + + // ... other operations ... +//<- + void inject_error( errorcode ec); + +private: + std::string data_; + errorcode injected_; +//-> +}; +//] + +/***************************************************************************** +* fake AsyncAPI implementation... pay no attention to the little man behind +* the curtain... +*****************************************************************************/ +AsyncAPI::AsyncAPI() : + injected_( 0) { +} + +void AsyncAPI::inject_error( errorcode ec) { + injected_ = ec; +} + +template< typename Fn > +void AsyncAPI::init_write( std::string const& data, Fn && callback) { + // make a local copy of injected_ + errorcode injected( injected_); + // reset it synchronously with caller + injected_ = 0; + // update data_ (this might be an echo service) + if ( ! injected) { + data_ = data; + } + // Simulate an asynchronous I/O operation by launching a detached thread + // that sleeps a bit before calling completion callback. Echo back to + // caller any previously-injected errorcode. +#if ! defined(BOOST_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES) + std::thread( [injected,callback=std::forward< Fn >( callback)]() mutable { + std::this_thread::sleep_for( std::chrono::milliseconds(100) ); + callback( injected); + }).detach(); +#else + std::thread( + std::move( + help( std::forward< Fn >( callback), injected) ) ).detach(); +#endif +} + +template< typename Fn > +void AsyncAPI::init_read( Fn && callback) { + // make a local copy of injected_ + errorcode injected( injected_); + // reset it synchronously with caller + injected_ = 0; + // local copy of data_ so we can capture in lambda + std::string data( data_); + // Simulate an asynchronous I/O operation by launching a detached thread + // that sleeps a bit before calling completion callback. Echo back to + // caller any previously-injected errorcode. +#if ! defined(BOOST_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES) + std::thread( [injected,callback=std::forward< Fn >( callback),data]() mutable { + std::this_thread::sleep_for( std::chrono::milliseconds(100) ); + callback( injected, data); + }).detach(); +#else + std::thread( + std::move( + help( std::forward< Fn >( callback), injected, data) ) ).detach(); +#endif +} + +/***************************************************************************** +* adapters +*****************************************************************************/ +// helper function used in a couple of the adapters +std::runtime_error make_exception( std::string const& desc, AsyncAPI::errorcode); + +//[callbacks_write_ec +AsyncAPI::errorcode write_ec( AsyncAPI & api, std::string const& data) { + boost::fibers::promise< AsyncAPI::errorcode > promise; + boost::fibers::future< AsyncAPI::errorcode > future( promise.get_future() ); + // In general, even though we block waiting for future::get() and therefore + // won't destroy 'promise' until promise::set_value() has been called, we + // are advised that with threads it's possible for ~promise() to be + // entered before promise::set_value() has returned. While that shouldn't + // happen with fibers::promise, a robust way to deal with the lifespan + // issue is to bind 'promise' into our lambda. Since promise is move-only, + // use initialization capture. +#if ! defined(BOOST_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES) + api.init_write( + data, + [promise=std::move( promise)]( AsyncAPI::errorcode ec) mutable { + promise.set_value( ec); + }); + +#else // defined(BOOST_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES) + api.init_write( + data, + std::bind([](boost::fibers::promise< AsyncAPI::errorcode > & promise, + AsyncAPI::errorcode ec) { + promise.set_value( ec); + }, + std::move( promise), + std::placeholders::_1) ); +#endif // BOOST_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES + + return future.get(); +} +//] + +//[callbacks_write +void write( AsyncAPI & api, std::string const& data) { + AsyncAPI::errorcode ec = write_ec( api, data); + if ( ec) { + throw make_exception("write", ec); + } +} +//] + +//[callbacks_read_ec +std::pair< AsyncAPI::errorcode, std::string > read_ec( AsyncAPI & api) { + typedef std::pair< AsyncAPI::errorcode, std::string > result_pair; + boost::fibers::promise< result_pair > promise; + boost::fibers::future< result_pair > future( promise.get_future() ); + // We promise that both 'promise' and 'future' will survive until our + // lambda has been called. +#if ! defined(BOOST_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES) + api.init_read([promise=std::move( promise)]( AsyncAPI::errorcode ec, std::string const& data) mutable { + promise.set_value( result_pair( ec, data) ); + }); +#else // defined(BOOST_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES) + api.init_read( + std::bind([]( boost::fibers::promise< result_pair > & promise, + AsyncAPI::errorcode ec, std::string const& data) mutable { + promise.set_value( result_pair( ec, data) ); + }, + std::move( promise), + std::placeholders::_1, + std::placeholders::_2) ); +#endif // BOOST_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES + return future.get(); +} +//] + +//[callbacks_read +std::string read( AsyncAPI & api) { + boost::fibers::promise< std::string > promise; + boost::fibers::future< std::string > future( promise.get_future() ); + // Both 'promise' and 'future' will survive until our lambda has been + // called. +#if ! defined(BOOST_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES) + api.init_read([&promise]( AsyncAPI::errorcode ec, std::string const& data) mutable { + if ( ! ec) { + promise.set_value( data); + } else { + promise.set_exception( + std::make_exception_ptr( + make_exception("read", ec) ) ); + } + }); +#else // defined(BOOST_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES) + api.init_read( + std::bind([]( boost::fibers::promise< std::string > & promise, + AsyncAPI::errorcode ec, std::string const& data) mutable { + if ( ! ec) { + promise.set_value( data); + } else { + promise.set_exception( + std::make_exception_ptr( + make_exception("read", ec) ) ); + } + }, + std::move( promise), + std::placeholders::_1, + std::placeholders::_2) ); +#endif // BOOST_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES + return future.get(); +} +//] + +/***************************************************************************** +* helpers +*****************************************************************************/ +std::runtime_error make_exception( std::string const& desc, AsyncAPI::errorcode ec) { + std::ostringstream buffer; + buffer << "Error in AsyncAPI::" << desc << "(): " << ec; + return std::runtime_error( buffer.str() ); +} + +/***************************************************************************** +* driving logic +*****************************************************************************/ +int main( int argc, char *argv[]) { + AsyncAPI api; + + // successful write(): prime AsyncAPI with some data + write( api, "abcd"); + // successful read(): retrieve it + std::string data( read( api) ); + assert( data == "abcd"); + + // successful write_ec() + AsyncAPI::errorcode ec( write_ec( api, "efgh") ); + assert( ec == 0); + + // write_ec() with error + api.inject_error(1); + ec = write_ec( api, "ijkl"); + assert( ec == 1); + + // write() with error + std::string thrown; + api.inject_error(2); + try { + write(api, "mnop"); + } catch ( std::exception const& e) { + thrown = e.what(); + } + assert( thrown == make_exception("write", 2).what() ); + + // successful read_ec() +//[callbacks_read_ec_call + std::tie( ec, data) = read_ec( api); +//] + assert( ! ec); + assert( data == "efgh"); // last successful write_ec() + + // read_ec() with error + api.inject_error(3); + std::tie( ec, data) = read_ec( api); + assert( ec == 3); + // 'data' in unspecified state, don't test + + // read() with error + thrown.clear(); + api.inject_error(4); + try { + data = read(api); + } catch ( std::exception const& e) { + thrown = e.what(); + } + assert( thrown == make_exception("read", 4).what() ); + + std::cout << "done." << std::endl; + + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/fiber/examples/adapt_method_calls.cpp b/src/boost/libs/fiber/examples/adapt_method_calls.cpp new file mode 100644 index 00000000..7cfd78df --- /dev/null +++ b/src/boost/libs/fiber/examples/adapt_method_calls.cpp @@ -0,0 +1,167 @@ +// Copyright Nat Goodspeed 2015. +// 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 <boost/fiber/all.hpp> +#include <memory> // std::shared_ptr +#include <thread> +#include <chrono> +#include <iostream> +#include <sstream> +#include <exception> +#include <cassert> + +/***************************************************************************** +* example async API +*****************************************************************************/ +// introduce class-scope typedef +struct AsyncAPIBase { + // error callback accepts an int error code; 0 == success + typedef int errorcode; +}; + +//[Response +// every async operation receives a subclass instance of this abstract base +// class through which to communicate its result +struct Response { + typedef std::shared_ptr< Response > ptr; + + // called if the operation succeeds + virtual void success( std::string const& data) = 0; + + // called if the operation fails + virtual void error( AsyncAPIBase::errorcode ec) = 0; +}; +//] + +// the actual async API +class AsyncAPI: public AsyncAPIBase { +public: + // constructor acquires some resource that can be read + AsyncAPI( std::string const& data); + +//[method_init_read + // derive Response subclass, instantiate, pass Response::ptr + void init_read( Response::ptr); +//] + + // ... other operations ... + void inject_error( errorcode ec); + +private: + std::string data_; + errorcode injected_; +}; + +/***************************************************************************** +* fake AsyncAPI implementation... pay no attention to the little man behind +* the curtain... +*****************************************************************************/ +AsyncAPI::AsyncAPI( std::string const& data) : + data_( data), + injected_( 0) { +} + +void AsyncAPI::inject_error( errorcode ec) { + injected_ = ec; +} + +void AsyncAPI::init_read( Response::ptr response) { + // make a local copy of injected_ + errorcode injected( injected_); + // reset it synchronously with caller + injected_ = 0; + // local copy of data_ so we can capture in lambda + std::string data( data_); + // Simulate an asynchronous I/O operation by launching a detached thread + // that sleeps a bit before calling either completion method. + std::thread( [injected, response, data](){ + std::this_thread::sleep_for( std::chrono::milliseconds(100) ); + if ( ! injected) { + // no error, call success() + response->success( data); + } else { + // injected error, call error() + response->error( injected); + } + }).detach(); +} + +/***************************************************************************** +* adapters +*****************************************************************************/ +// helper function +std::runtime_error make_exception( std::string const& desc, AsyncAPI::errorcode); + +//[PromiseResponse +class PromiseResponse: public Response { +public: + // called if the operation succeeds + virtual void success( std::string const& data) { + promise_.set_value( data); + } + + // called if the operation fails + virtual void error( AsyncAPIBase::errorcode ec) { + promise_.set_exception( + std::make_exception_ptr( + make_exception("read", ec) ) ); + } + + boost::fibers::future< std::string > get_future() { + return promise_.get_future(); + } + +private: + boost::fibers::promise< std::string > promise_; +}; +//] + +//[method_read +std::string read( AsyncAPI & api) { + // Because init_read() requires a shared_ptr, we must allocate our + // ResponsePromise on the heap, even though we know its lifespan. + auto promisep( std::make_shared< PromiseResponse >() ); + boost::fibers::future< std::string > future( promisep->get_future() ); + // Both 'promisep' and 'future' will survive until our lambda has been + // called. + api.init_read( promisep); + return future.get(); +} +//] + +/***************************************************************************** +* helpers +*****************************************************************************/ +std::runtime_error make_exception( std::string const& desc, AsyncAPI::errorcode ec) { + std::ostringstream buffer; + buffer << "Error in AsyncAPI::" << desc << "(): " << ec; + return std::runtime_error( buffer.str() ); +} + +/***************************************************************************** +* driving logic +*****************************************************************************/ +int main(int argc, char *argv[]) { + // prime AsyncAPI with some data + AsyncAPI api("abcd"); + + // successful read(): retrieve it + std::string data( read( api) ); + assert(data == "abcd"); + + // read() with error + std::string thrown; + api.inject_error(1); + try { + data = read( api); + } catch ( std::exception const& e) { + thrown = e.what(); + } + assert(thrown == make_exception("read", 1).what() ); + + std::cout << "done." << std::endl; + + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/fiber/examples/adapt_nonblocking.cpp b/src/boost/libs/fiber/examples/adapt_nonblocking.cpp new file mode 100644 index 00000000..879e1789 --- /dev/null +++ b/src/boost/libs/fiber/examples/adapt_nonblocking.cpp @@ -0,0 +1,208 @@ +// Copyright Nat Goodspeed 2015. +// 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 <boost/fiber/all.hpp> +#include <iostream> +#include <sstream> +#include <exception> +#include <string> +#include <algorithm> // std::min() +#include <errno.h> // EWOULDBLOCK +#include <cassert> +#include <cstdio> + +/***************************************************************************** +* example nonblocking API +*****************************************************************************/ +//[NonblockingAPI +class NonblockingAPI { +public: + NonblockingAPI(); + + // nonblocking operation: may return EWOULDBLOCK + int read( std::string & data, std::size_t desired); + +/*= ...*/ +//<- + // for simulating a real nonblocking API + void set_data( std::string const& data, std::size_t chunksize); + void inject_error( int ec); + +private: + std::string data_; + int injected_; + unsigned tries_; + std::size_t chunksize_; +//-> +}; +//] + +/***************************************************************************** +* fake NonblockingAPI implementation... pay no attention to the little man +* behind the curtain... +*****************************************************************************/ +NonblockingAPI::NonblockingAPI() : + injected_( 0), + tries_( 0), + chunksize_( 9999) { +} + +void NonblockingAPI::set_data( std::string const& data, std::size_t chunksize) { + data_ = data; + chunksize_ = chunksize; + // This delimits the start of a new test. Reset state. + injected_ = 0; + tries_ = 0; +} + +void NonblockingAPI::inject_error( int ec) { + injected_ = ec; +} + +int NonblockingAPI::read( std::string & data, std::size_t desired) { + // in case of error + data.clear(); + + if ( injected_) { + // copy injected_ because we're about to reset it + auto injected( injected_); + injected_ = 0; + // after an error situation, restart success count + tries_ = 0; + return injected; + } + + if ( ++tries_ < 5) { + // no injected error, but the resource isn't yet ready + return EWOULDBLOCK; + } + + // tell caller there's nothing left + if ( data_.empty() ) { + return EOF; + } + + // okay, finally have some data + // but return minimum of desired and chunksize_ + std::size_t size( ( std::min)( desired, chunksize_) ); + data = data_.substr( 0, size); + // strip off what we just returned + data_ = data_.substr( size); + // reset I/O retries count for next time + tries_ = 0; + // success + return 0; +} + +/***************************************************************************** +* adapters +*****************************************************************************/ +//[nonblocking_read_chunk +// guaranteed not to return EWOULDBLOCK +int read_chunk( NonblockingAPI & api, std::string & data, std::size_t desired) { + int error; + while ( EWOULDBLOCK == ( error = api.read( data, desired) ) ) { + // not ready yet -- try again on the next iteration of the + // application's main loop + boost::this_fiber::yield(); + } + return error; +} +//] + +//[nonblocking_read_desired +// keep reading until desired length, EOF or error +// may return both partial data and nonzero error +int read_desired( NonblockingAPI & api, std::string & data, std::size_t desired) { + // we're going to accumulate results into 'data' + data.clear(); + std::string chunk; + int error = 0; + while ( data.length() < desired && + ( error = read_chunk( api, chunk, desired - data.length() ) ) == 0) { + data.append( chunk); + } + return error; +} +//] + +//[nonblocking_IncompleteRead +// exception class augmented with both partially-read data and errorcode +class IncompleteRead : public std::runtime_error { +public: + IncompleteRead( std::string const& what, std::string const& partial, int ec) : + std::runtime_error( what), + partial_( partial), + ec_( ec) { + } + + std::string get_partial() const { + return partial_; + } + + int get_errorcode() const { + return ec_; + } + +private: + std::string partial_; + int ec_; +}; +//] + +//[nonblocking_read +// read all desired data or throw IncompleteRead +std::string read( NonblockingAPI & api, std::size_t desired) { + std::string data; + int ec( read_desired( api, data, desired) ); + + // for present purposes, EOF isn't a failure + if ( 0 == ec || EOF == ec) { + return data; + } + + // oh oh, partial read + std::ostringstream msg; + msg << "NonblockingAPI::read() error " << ec << " after " + << data.length() << " of " << desired << " characters"; + throw IncompleteRead( msg.str(), data, ec); +} +//] + +int main( int argc, char *argv[]) { + NonblockingAPI api; + const std::string sample_data("abcdefghijklmnopqrstuvwxyz"); + + // Try just reading directly from NonblockingAPI + api.set_data( sample_data, 5); + std::string data; + int ec = api.read( data, 13); + // whoops, underlying resource not ready + assert(ec == EWOULDBLOCK); + assert(data.empty()); + + // successful read() + api.set_data( sample_data, 5); + data = read( api, 13); + assert(data == "abcdefghijklm"); + + // read() with error + api.set_data( sample_data, 5); + // don't accidentally pick either EOF or EWOULDBLOCK + assert(EOF != 1); + assert(EWOULDBLOCK != 1); + api.inject_error(1); + int thrown = 0; + try { + data = read( api, 13); + } catch ( IncompleteRead const& e) { + thrown = e.get_errorcode(); + } + assert(thrown == 1); + + std::cout << "done." << std::endl; + + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/fiber/examples/asio/autoecho.cpp b/src/boost/libs/fiber/examples/asio/autoecho.cpp new file mode 100644 index 00000000..06b4027a --- /dev/null +++ b/src/boost/libs/fiber/examples/asio/autoecho.cpp @@ -0,0 +1,261 @@ +// Copyright 2003-2013 Christopher M. Kohlhoff +// Copyright Oliver Kowalke, Nat Goodspeed 2015. +// 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 <chrono> +#include <cstdlib> +#include <iomanip> +#include <iostream> +#include <map> +#include <memory> +#include <mutex> +#include <sstream> +#include <thread> + +#include <boost/asio.hpp> +#include <boost/bind.hpp> +#include <boost/shared_ptr.hpp> + +#include <boost/fiber/all.hpp> +#include "round_robin.hpp" +#include "yield.hpp" + +using boost::asio::ip::tcp; + +const int max_length = 1024; + +typedef boost::shared_ptr< tcp::socket > socket_ptr; + +const char* const alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + +/***************************************************************************** +* thread names +*****************************************************************************/ +class ThreadNames { +private: + std::map<std::thread::id, std::string> names_{}; + const char* next_{ alpha }; + std::mutex mtx_{}; + +public: + ThreadNames() = default; + + std::string lookup() { + std::unique_lock<std::mutex> lk( mtx_); + auto this_id( std::this_thread::get_id() ); + auto found = names_.find( this_id ); + if ( found != names_.end() ) { + return found->second; + } + BOOST_ASSERT( *next_); + std::string name(1, *next_++ ); + names_[ this_id ] = name; + return name; + } +}; + +ThreadNames thread_names; + +/***************************************************************************** +* fiber names +*****************************************************************************/ +class FiberNames { +private: + std::map<boost::fibers::fiber::id, std::string> names_{}; + unsigned next_{ 0 }; + boost::fibers::mutex mtx_{}; + +public: + FiberNames() = default; + + std::string lookup() { + std::unique_lock<boost::fibers::mutex> lk( mtx_); + auto this_id( boost::this_fiber::get_id() ); + auto found = names_.find( this_id ); + if ( found != names_.end() ) { + return found->second; + } + std::ostringstream out; + // Bake into the fiber's name the thread name on which we first + // lookup() its ID, to be able to spot when a fiber hops between + // threads. + out << thread_names.lookup() << next_++; + std::string name( out.str() ); + names_[ this_id ] = name; + return name; + } +}; + +FiberNames fiber_names; + +std::string tag() { + std::ostringstream out; + out << "Thread " << thread_names.lookup() << ": " + << std::setw(4) << fiber_names.lookup() << std::setw(0); + return out.str(); +} + +/***************************************************************************** +* message printing +*****************************************************************************/ +void print_( std::ostream& out) { + out << '\n'; +} + +template < typename T, typename... Ts > +void print_( std::ostream& out, T const& arg, Ts const&... args) { + out << arg; + print_(out, args...); +} + +template < typename... T > +void print( T const&... args ) { + std::ostringstream buffer; + print_( buffer, args...); + std::cout << buffer.str() << std::flush; +} + +/***************************************************************************** +* fiber function per server connection +*****************************************************************************/ +void session( socket_ptr sock) { + try { + for (;;) { + char data[max_length]; + boost::system::error_code ec; + std::size_t length = sock->async_read_some( + boost::asio::buffer( data), + boost::fibers::asio::yield[ec]); + if ( ec == boost::asio::error::eof) { + break; //connection closed cleanly by peer + } else if ( ec) { + throw boost::system::system_error( ec); //some other error + } + print( tag(), ": handled: ", std::string(data, length)); + boost::asio::async_write( + * sock, + boost::asio::buffer( data, length), + boost::fibers::asio::yield[ec]); + if ( ec == boost::asio::error::eof) { + break; //connection closed cleanly by peer + } else if ( ec) { + throw boost::system::system_error( ec); //some other error + } + } + print( tag(), ": connection closed"); + } catch ( std::exception const& ex) { + print( tag(), ": caught exception : ", ex.what()); + } +} + +/***************************************************************************** +* listening server +*****************************************************************************/ +void server( std::shared_ptr< boost::asio::io_service > const& io_svc, tcp::acceptor & a) { + print( tag(), ": echo-server started"); + try { + for (;;) { + socket_ptr socket( new tcp::socket( * io_svc) ); + boost::system::error_code ec; + a.async_accept( + * socket, + boost::fibers::asio::yield[ec]); + if ( ec) { + throw boost::system::system_error( ec); //some other error + } else { + boost::fibers::fiber( session, socket).detach(); + } + } + } catch ( std::exception const& ex) { + print( tag(), ": caught exception : ", ex.what()); + } + io_svc->stop(); + print( tag(), ": echo-server stopped"); +} + +/***************************************************************************** +* fiber function per client +*****************************************************************************/ +void client( std::shared_ptr< boost::asio::io_service > const& io_svc, tcp::acceptor & a, + boost::fibers::barrier& barrier, unsigned iterations) { + print( tag(), ": echo-client started"); + for (unsigned count = 0; count < iterations; ++count) { + tcp::resolver resolver( * io_svc); + tcp::resolver::query query( tcp::v4(), "127.0.0.1", "9999"); + tcp::resolver::iterator iterator = resolver.resolve( query); + tcp::socket s( * io_svc); + boost::asio::connect( s, iterator); + for (unsigned msg = 0; msg < 1; ++msg) { + std::ostringstream msgbuf; + msgbuf << "from " << fiber_names.lookup() << " " << count << "." << msg; + std::string message(msgbuf.str()); + print( tag(), ": Sending: ", message); + boost::system::error_code ec; + boost::asio::async_write( + s, + boost::asio::buffer( message), + boost::fibers::asio::yield[ec]); + if ( ec == boost::asio::error::eof) { + return; //connection closed cleanly by peer + } else if ( ec) { + throw boost::system::system_error( ec); //some other error + } + char reply[max_length]; + size_t reply_length = s.async_read_some( + boost::asio::buffer( reply, max_length), + boost::fibers::asio::yield[ec]); + if ( ec == boost::asio::error::eof) { + return; //connection closed cleanly by peer + } else if ( ec) { + throw boost::system::system_error( ec); //some other error + } + print( tag(), ": Reply : ", std::string( reply, reply_length)); + } + } + // done with all iterations, wait for rest of client fibers + if ( barrier.wait()) { + // exactly one barrier.wait() call returns true + // we're the lucky one + a.close(); + print( tag(), ": acceptor stopped"); + } + print( tag(), ": echo-client stopped"); +} + +/***************************************************************************** +* main +*****************************************************************************/ +int main( int argc, char* argv[]) { + try { +//[asio_rr_setup + std::shared_ptr< boost::asio::io_service > io_svc = std::make_shared< boost::asio::io_service >(); + boost::fibers::use_scheduling_algorithm< boost::fibers::asio::round_robin >( io_svc); +//] + print( "Thread ", thread_names.lookup(), ": started"); +//[asio_rr_launch_fibers + // server + tcp::acceptor a( * io_svc, tcp::endpoint( tcp::v4(), 9999) ); + boost::fibers::fiber( server, io_svc, std::ref( a) ).detach(); + // client + const unsigned iterations = 2; + const unsigned clients = 3; + boost::fibers::barrier b( clients); + for ( unsigned i = 0; i < clients; ++i) { + boost::fibers::fiber( + client, io_svc, std::ref( a), std::ref( b), iterations).detach(); + } +//] +//[asio_rr_run + io_svc->run(); +//] + print( tag(), ": io_service returned"); + print( "Thread ", thread_names.lookup(), ": stopping"); + std::cout << "done." << std::endl; + return EXIT_SUCCESS; + } catch ( std::exception const& e) { + print("Exception: ", e.what(), "\n"); + } + return EXIT_FAILURE; +} diff --git a/src/boost/libs/fiber/examples/asio/detail/yield.hpp b/src/boost/libs/fiber/examples/asio/detail/yield.hpp new file mode 100644 index 00000000..01b680b5 --- /dev/null +++ b/src/boost/libs/fiber/examples/asio/detail/yield.hpp @@ -0,0 +1,328 @@ +// Copyright Oliver Kowalke, Nat Goodspeed 2015. +// 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) + +#ifndef BOOST_FIBERS_ASIO_DETAIL_YIELD_HPP +#define BOOST_FIBERS_ASIO_DETAIL_YIELD_HPP + +#include <boost/asio/async_result.hpp> +#include <boost/asio/detail/config.hpp> +#include <boost/asio/handler_type.hpp> +#include <boost/assert.hpp> +#include <boost/atomic.hpp> +#include <boost/intrusive_ptr.hpp> +#include <boost/system/error_code.hpp> +#include <boost/system/system_error.hpp> +#include <boost/throw_exception.hpp> + +#include <boost/fiber/all.hpp> + +#include <mutex> // std::unique_lock + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { +namespace asio { +namespace detail { + +//[fibers_asio_yield_completion +// Bundle a completion bool flag with a spinlock to protect it. +struct yield_completion { + enum state_t { + init, + waiting, + complete + }; + + typedef fibers::detail::spinlock mutex_t; + typedef std::unique_lock< mutex_t > lock_t; + typedef boost::intrusive_ptr< yield_completion > ptr_t; + + std::atomic< std::size_t > use_count_{ 0 }; + mutex_t mtx_{}; + state_t state_{ init }; + + void wait() { + // yield_handler_base::operator()() will set state_ `complete` and + // attempt to wake a suspended fiber. It would be Bad if that call + // happened between our detecting (complete != state_) and suspending. + lock_t lk{ mtx_ }; + // If state_ is already set, we're done here: don't suspend. + if ( complete != state_) { + state_ = waiting; + // suspend(unique_lock<spinlock>) unlocks the lock in the act of + // resuming another fiber + fibers::context::active()->suspend( lk); + } + } + + friend void intrusive_ptr_add_ref( yield_completion * yc) noexcept { + BOOST_ASSERT( nullptr != yc); + yc->use_count_.fetch_add( 1, std::memory_order_relaxed); + } + + friend void intrusive_ptr_release( yield_completion * yc) noexcept { + BOOST_ASSERT( nullptr != yc); + if ( 1 == yc->use_count_.fetch_sub( 1, std::memory_order_release) ) { + std::atomic_thread_fence( std::memory_order_acquire); + delete yc; + } + } +}; +//] + +//[fibers_asio_yield_handler_base +// This class encapsulates common elements between yield_handler<T> (capturing +// a value to return from asio async function) and yield_handler<void> (no +// such value). See yield_handler<T> and its <void> specialization below. Both +// yield_handler<T> and yield_handler<void> are passed by value through +// various layers of asio functions. In other words, they're potentially +// copied multiple times. So key data such as the yield_completion instance +// must be stored in our async_result<yield_handler<>> specialization, which +// should be instantiated only once. +class yield_handler_base { +public: + yield_handler_base( yield_t const& y) : + // capture the context* associated with the running fiber + ctx_{ boost::fibers::context::active() }, + // capture the passed yield_t + yt_( y ) { + } + + // completion callback passing only (error_code) + void operator()( boost::system::error_code const& ec) { + BOOST_ASSERT_MSG( ycomp_, + "Must inject yield_completion* " + "before calling yield_handler_base::operator()()"); + BOOST_ASSERT_MSG( yt_.ec_, + "Must inject boost::system::error_code* " + "before calling yield_handler_base::operator()()"); + // If originating fiber is busy testing state_ flag, wait until it + // has observed (completed != state_). + yield_completion::lock_t lk{ ycomp_->mtx_ }; + yield_completion::state_t state = ycomp_->state_; + // Notify a subsequent yield_completion::wait() call that it need not + // suspend. + ycomp_->state_ = yield_completion::complete; + // set the error_code bound by yield_t + * yt_.ec_ = ec; + // unlock the lock that protects state_ + lk.unlock(); + // If ctx_ is still active, e.g. because the async operation + // immediately called its callback (this method!) before the asio + // async function called async_result_base::get(), we must not set it + // ready. + if ( yield_completion::waiting == state) { + // wake the fiber + fibers::context::active()->schedule( ctx_); + } + } + +//private: + boost::fibers::context * ctx_; + yield_t yt_; + // We depend on this pointer to yield_completion, which will be injected + // by async_result. + yield_completion::ptr_t ycomp_{}; +}; +//] + +//[fibers_asio_yield_handler_T +// asio uses handler_type<completion token type, signature>::type to decide +// what to instantiate as the actual handler. Below, we specialize +// handler_type< yield_t, ... > to indicate yield_handler<>. So when you pass +// an instance of yield_t as an asio completion token, asio selects +// yield_handler<> as the actual handler class. +template< typename T > +class yield_handler: public yield_handler_base { +public: + // asio passes the completion token to the handler constructor + explicit yield_handler( yield_t const& y) : + yield_handler_base{ y } { + } + + // completion callback passing only value (T) + void operator()( T t) { + // just like callback passing success error_code + (*this)( boost::system::error_code(), std::move(t) ); + } + + // completion callback passing (error_code, T) + void operator()( boost::system::error_code const& ec, T t) { + BOOST_ASSERT_MSG( value_, + "Must inject value ptr " + "before caling yield_handler<T>::operator()()"); + // move the value to async_result<> instance BEFORE waking up a + // suspended fiber + * value_ = std::move( t); + // forward the call to base-class completion handler + yield_handler_base::operator()( ec); + } + +//private: + // pointer to destination for eventual value + // this must be injected by async_result before operator()() is called + T * value_{ nullptr }; +}; +//] + +//[fibers_asio_yield_handler_void +// yield_handler<void> is like yield_handler<T> without value_. In fact it's +// just like yield_handler_base. +template<> +class yield_handler< void >: public yield_handler_base { +public: + explicit yield_handler( yield_t const& y) : + yield_handler_base{ y } { + } + + // nullary completion callback + void operator()() { + ( * this)( boost::system::error_code() ); + } + + // inherit operator()(error_code) overload from base class + using yield_handler_base::operator(); +}; +//] + +// Specialize asio_handler_invoke hook to ensure that any exceptions thrown +// from the handler are propagated back to the caller +template< typename Fn, typename T > +void asio_handler_invoke( Fn&& fn, yield_handler< T > *) { + fn(); +} + +//[fibers_asio_async_result_base +// Factor out commonality between async_result<yield_handler<T>> and +// async_result<yield_handler<void>> +class async_result_base { +public: + explicit async_result_base( yield_handler_base & h) : + ycomp_{ new yield_completion{} } { + // Inject ptr to our yield_completion instance into this + // yield_handler<>. + h.ycomp_ = this->ycomp_; + // if yield_t didn't bind an error_code, make yield_handler_base's + // error_code* point to an error_code local to this object so + // yield_handler_base::operator() can unconditionally store through + // its error_code* + if ( ! h.yt_.ec_) { + h.yt_.ec_ = & ec_; + } + } + + void get() { + // Unless yield_handler_base::operator() has already been called, + // suspend the calling fiber until that call. + ycomp_->wait(); + // The only way our own ec_ member could have a non-default value is + // if our yield_handler did not have a bound error_code AND the + // completion callback passed a non-default error_code. + if ( ec_) { + throw_exception( boost::system::system_error{ ec_ } ); + } + } + +private: + // If yield_t does not bind an error_code instance, store into here. + boost::system::error_code ec_{}; + yield_completion::ptr_t ycomp_; +}; +//] + +}}}} + +namespace boost { +namespace asio { + +//[fibers_asio_async_result_T +// asio constructs an async_result<> instance from the yield_handler specified +// by handler_type<>::type. A particular asio async method constructs the +// yield_handler, constructs this async_result specialization from it, then +// returns the result of calling its get() method. +template< typename T > +class async_result< boost::fibers::asio::detail::yield_handler< T > > : + public boost::fibers::asio::detail::async_result_base { +public: + // type returned by get() + typedef T type; + + explicit async_result( boost::fibers::asio::detail::yield_handler< T > & h) : + boost::fibers::asio::detail::async_result_base{ h } { + // Inject ptr to our value_ member into yield_handler<>: result will + // be stored here. + h.value_ = & value_; + } + + // asio async method returns result of calling get() + type get() { + boost::fibers::asio::detail::async_result_base::get(); + return std::move( value_); + } + +private: + type value_{}; +}; +//] + +//[fibers_asio_async_result_void +// Without the need to handle a passed value, our yield_handler<void> +// specialization is just like async_result_base. +template<> +class async_result< boost::fibers::asio::detail::yield_handler< void > > : + public boost::fibers::asio::detail::async_result_base { +public: + typedef void type; + + explicit async_result( boost::fibers::asio::detail::yield_handler< void > & h): + boost::fibers::asio::detail::async_result_base{ h } { + } +}; +//] + +// Handler type specialisation for fibers::asio::yield. +// When 'yield' is passed as a completion handler which accepts no parameters, +// use yield_handler<void>. +template< typename ReturnType > +struct handler_type< fibers::asio::yield_t, ReturnType() > +{ typedef fibers::asio::detail::yield_handler< void > type; }; + +// Handler type specialisation for fibers::asio::yield. +// When 'yield' is passed as a completion handler which accepts a data +// parameter, use yield_handler<parameter type> to return that parameter to +// the caller. +template< typename ReturnType, typename Arg1 > +struct handler_type< fibers::asio::yield_t, ReturnType( Arg1) > +{ typedef fibers::asio::detail::yield_handler< Arg1 > type; }; + +//[asio_handler_type +// Handler type specialisation for fibers::asio::yield. +// When 'yield' is passed as a completion handler which accepts only +// error_code, use yield_handler<void>. yield_handler will take care of the +// error_code one way or another. +template< typename ReturnType > +struct handler_type< fibers::asio::yield_t, ReturnType( boost::system::error_code) > +{ typedef fibers::asio::detail::yield_handler< void > type; }; +//] + +// Handler type specialisation for fibers::asio::yield. +// When 'yield' is passed as a completion handler which accepts a data +// parameter and an error_code, use yield_handler<parameter type> to return +// just the parameter to the caller. yield_handler will take care of the +// error_code one way or another. +template< typename ReturnType, typename Arg2 > +struct handler_type< fibers::asio::yield_t, ReturnType( boost::system::error_code, Arg2) > +{ typedef fibers::asio::detail::yield_handler< Arg2 > type; }; + +}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_FIBERS_ASIO_DETAIL_YIELD_HPP diff --git a/src/boost/libs/fiber/examples/asio/exchange.cpp b/src/boost/libs/fiber/examples/asio/exchange.cpp new file mode 100644 index 00000000..8d7482af --- /dev/null +++ b/src/boost/libs/fiber/examples/asio/exchange.cpp @@ -0,0 +1,56 @@ +// Copyright Arnaud Kapp, Oliver Kowalke 2016 +// 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 <iostream> +#include <memory> +#include <thread> + +#include <boost/asio.hpp> + +#include <boost/fiber/all.hpp> +#include "round_robin.hpp" + +std::shared_ptr< boost::fibers::unbuffered_channel< int > > c; + +void foo() { + auto io_ptr = std::make_shared< boost::asio::io_service >(); + boost::fibers::use_scheduling_algorithm< boost::fibers::asio::round_robin >( io_ptr); + boost::fibers::fiber([io_ptr](){ + for ( int i = 0; i < 10; ++i) { + std::cout << "push " << i << std::endl; + c->push( i); + } + c->close(); + io_ptr->stop(); + }).detach(); + io_ptr->run(); +} + +void bar() { + auto io_ptr = std::make_shared< boost::asio::io_service >(); + boost::fibers::use_scheduling_algorithm< boost::fibers::asio::round_robin >( io_ptr); + boost::fibers::fiber([io_ptr](){ + try { + for (;;) { + int i = c->value_pop(); + std::cout << "pop " << i << std::endl; + } + } catch ( std::exception const& e) { + std::cout << "exception: " << e.what() << std::endl; + } + io_ptr->stop(); + }).detach(); + io_ptr->run(); +} + +int main() { + c = std::make_shared< boost::fibers::unbuffered_channel< int > >(); + std::thread t1( foo); + std::thread t2( bar); + t2.join(); + t1.join(); + std::cout << "done." << std::endl; + return 0; +} diff --git a/src/boost/libs/fiber/examples/asio/ps/publisher.cpp b/src/boost/libs/fiber/examples/asio/ps/publisher.cpp new file mode 100644 index 00000000..76a63d77 --- /dev/null +++ b/src/boost/libs/fiber/examples/asio/ps/publisher.cpp @@ -0,0 +1,52 @@ +// +// blocking_tcp_echo_client.cpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// 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 <cstdlib> +#include <cstring> +#include <iostream> + +#include <boost/asio.hpp> + +using boost::asio::ip::tcp; + +enum { + max_length = 1024 +}; + +int main( int argc, char* argv[]) { + try { + if ( 3 != argc) { + std::cerr << "Usage: publisher <host> <queue>\n"; + return EXIT_FAILURE; + } + boost::asio::io_service io_service; + tcp::resolver resolver( io_service); + tcp::resolver::query query( tcp::v4(), argv[1], "9997"); + tcp::resolver::iterator iterator = resolver.resolve(query); + tcp::socket s( io_service); + boost::asio::connect( s, iterator); + char msg[max_length]; + std::string queue( argv[2]); + std::memset( msg, '\0', max_length); + std::memcpy( msg, queue.c_str(), queue.size() ); + boost::asio::write( s, boost::asio::buffer( msg, max_length) ); + for (;;) { + std::cout << "publish: "; + char request[max_length]; + std::cin.getline( request, max_length); + boost::asio::write( s, boost::asio::buffer( request, max_length) ); + } + return EXIT_SUCCESS; + } catch ( std::exception const& e) { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return EXIT_FAILURE; +} diff --git a/src/boost/libs/fiber/examples/asio/ps/server.cpp b/src/boost/libs/fiber/examples/asio/ps/server.cpp new file mode 100644 index 00000000..aeb58f23 --- /dev/null +++ b/src/boost/libs/fiber/examples/asio/ps/server.cpp @@ -0,0 +1,381 @@ +// Copyright Oliver Kowalke 2015. +// 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 <cstddef> +#include <cstdlib> +#include <map> +#include <memory> +#include <set> +#include <iostream> +#include <string> + +#include <boost/asio.hpp> +#include <boost/utility.hpp> + +#include <boost/fiber/all.hpp> +#include "../round_robin.hpp" +#include "../yield.hpp" + +using boost::asio::ip::tcp; + +const std::size_t max_length = 1024; + +class subscriber_session; +typedef std::shared_ptr< subscriber_session > subscriber_session_ptr; + +// a queue has n subscribers (subscriptions) +// this class holds a list of subcribers for one queue +class subscriptions { +public: + ~subscriptions(); + + // subscribe to this queue + void subscribe( subscriber_session_ptr const& s) { + subscribers_.insert( s); + } + + // unsubscribe from this queue + void unsubscribe( subscriber_session_ptr const& s) { + subscribers_.erase(s); + } + + // publish a message, e.g. push this message to all subscribers + void publish( std::string const& msg); + +private: + // list of subscribers + std::set< subscriber_session_ptr > subscribers_; +}; + +// a class to register queues and to subsribe clients to this queues +class registry : private boost::noncopyable { +private: + typedef std::map< std::string, std::shared_ptr< subscriptions > > queues_cont; + typedef queues_cont::iterator queues_iter; + + boost::fibers::mutex mtx_; + queues_cont queues_; + + void register_queue_( std::string const& queue) { + if ( queues_.end() != queues_.find( queue) ) { + throw std::runtime_error("queue already exists"); + } + queues_[queue] = std::make_shared< subscriptions >(); + std::cout << "new queue '" << queue << "' registered" << std::endl; + } + + void unregister_queue_( std::string const& queue) { + queues_.erase( queue); + std::cout << "queue '" << queue << "' unregistered" << std::endl; + } + + void subscribe_( std::string const& queue, subscriber_session_ptr s) { + queues_iter iter = queues_.find( queue); + if ( queues_.end() == iter ) { + throw std::runtime_error("queue does not exist"); + } + iter->second->subscribe( s); + std::cout << "new subscription to queue '" << queue << "'" << std::endl; + } + + void unsubscribe_( std::string const& queue, subscriber_session_ptr s) { + queues_iter iter = queues_.find( queue); + if ( queues_.end() != iter ) { + iter->second->unsubscribe( s); + } + } + + void publish_( std::string const& queue, std::string const& msg) { + queues_iter iter = queues_.find( queue); + if ( queues_.end() == iter ) { + throw std::runtime_error("queue does not exist"); + } + iter->second->publish( msg); + std::cout << "message '" << msg << "' to publish on queue '" << queue << "'" << std::endl; + } + +public: + // add a queue to registry + void register_queue( std::string const& queue) { + std::unique_lock< boost::fibers::mutex > lk( mtx_); + register_queue_( queue); + } + + // remove a queue from registry + void unregister_queue( std::string const& queue) { + std::unique_lock< boost::fibers::mutex > lk( mtx_); + unregister_queue_( queue); + } + + // subscribe to a queue + void subscribe( std::string const& queue, subscriber_session_ptr s) { + std::unique_lock< boost::fibers::mutex > lk( mtx_); + subscribe_( queue, s); + } + + // unsubscribe from a queue + void unsubscribe( std::string const& queue, subscriber_session_ptr s) { + std::unique_lock< boost::fibers::mutex > lk( mtx_); + unsubscribe_( queue, s); + } + + // publish a message to all subscribers registerd to the queue + void publish( std::string const& queue, std::string const& msg) { + std::unique_lock< boost::fibers::mutex > lk( mtx_); + publish_( queue, msg); + } +}; + +// a subscriber subscribes to a given queue in order to receive messages published on this queue +class subscriber_session : public std::enable_shared_from_this< subscriber_session > { +public: + explicit subscriber_session( std::shared_ptr< boost::asio::io_service > const& io_service, registry & reg) : + socket_( * io_service), + reg_( reg) { + } + + tcp::socket& socket() { + return socket_; + } + + // this function is executed inside the fiber + void run() { + std::string queue; + try { + boost::system::error_code ec; + // read first message == queue name + // async_ready() returns if the the complete message is read + // until this the fiber is suspended until the complete message + // is read int the given buffer 'data' + boost::asio::async_read( + socket_, + boost::asio::buffer( data_), + boost::fibers::asio::yield[ec]); + if ( ec) { + throw std::runtime_error("no queue from subscriber"); + } + // first message ist equal to the queue name the publisher + // publishes to + queue = data_; + // subscribe to new queue + reg_.subscribe( queue, shared_from_this() ); + // read published messages + for (;;) { + // wait for a conditon-variable for new messages + // the fiber will be suspended until the condtion + // becomes true and the fiber is resumed + // published message is stored in buffer 'data_' + std::unique_lock< boost::fibers::mutex > lk( mtx_); + cond_.wait( lk); + std::string data( data_); + lk.unlock(); + // message '<fini>' terminates subscription + if ( "<fini>" == data) { + break; + } + // async. write message to socket connected with + // subscriber + // async_write() returns if the complete message was writen + // the fiber is suspended in the meanwhile + boost::asio::async_write( + socket_, + boost::asio::buffer( data, data.size() ), + boost::fibers::asio::yield[ec]); + if ( ec == boost::asio::error::eof) { + break; //connection closed cleanly by peer + } else if ( ec) { + throw boost::system::system_error( ec); //some other error + } + std::cout << "subscriber::run(): '" << data << "' written" << std::endl; + } + } catch ( std::exception const& e) { + std::cerr << "subscriber [" << queue << "] failed: " << e.what() << std::endl; + } + // close socket + socket_.close(); + // unregister queue + reg_.unsubscribe( queue, shared_from_this() ); + } + + // called from publisher_session (running in other fiber) + void publish( std::string const& msg) { + std::unique_lock< boost::fibers::mutex > lk( mtx_); + std::memset( data_, '\0', sizeof( data_)); + std::memcpy( data_, msg.c_str(), (std::min)(max_length, msg.size())); + cond_.notify_one(); + } + +private: + tcp::socket socket_; + registry & reg_; + boost::fibers::mutex mtx_; + boost::fibers::condition_variable cond_; + // fixed size message + char data_[max_length]; +}; + + +subscriptions::~subscriptions() { + for ( subscriber_session_ptr s : subscribers_) { + s->publish("<fini>"); + } +} + +void +subscriptions::publish( std::string const& msg) { + for ( subscriber_session_ptr s : subscribers_) { + s->publish( msg); + } +} + +// a publisher publishes messages on its queue +// subscriber might register to this queue to get the published messages +class publisher_session : public std::enable_shared_from_this< publisher_session > { +public: + explicit publisher_session( std::shared_ptr< boost::asio::io_service > const& io_service, registry & reg) : + socket_( * io_service), + reg_( reg) { + } + + tcp::socket& socket() { + return socket_; + } + + // this function is executed inside the fiber + void run() { + std::string queue; + try { + boost::system::error_code ec; + // fixed size message + char data[max_length]; + // read first message == queue name + // async_ready() returns if the the complete message is read + // until this the fiber is suspended until the complete message + // is read int the given buffer 'data' + boost::asio::async_read( + socket_, + boost::asio::buffer( data), + boost::fibers::asio::yield[ec]); + if ( ec) { + throw std::runtime_error("no queue from publisher"); + } + // first message ist equal to the queue name the publisher + // publishes to + queue = data; + // register the new queue + reg_.register_queue( queue); + // start publishing messages + for (;;) { + // read message from publisher asyncronous + // async_read() suspends this fiber until the complete emssage is read + // and stored in the given buffer 'data' + boost::asio::async_read( + socket_, + boost::asio::buffer( data), + boost::fibers::asio::yield[ec]); + if ( ec == boost::asio::error::eof) { + break; //connection closed cleanly by peer + } else if ( ec) { + throw boost::system::system_error( ec); //some other error + } + // publish message to all subscribers + reg_.publish( queue, std::string( data) ); + } + } catch ( std::exception const& e) { + std::cerr << "publisher [" << queue << "] failed: " << e.what() << std::endl; + } + // close socket + socket_.close(); + // unregister queue + reg_.unregister_queue( queue); + } + +private: + tcp::socket socket_; + registry & reg_; +}; + +typedef std::shared_ptr< publisher_session > publisher_session_ptr; + +// function accepts connections requests from clients acting as a publisher +void accept_publisher( std::shared_ptr< boost::asio::io_service > const& io_service, + unsigned short port, + registry & reg) { + // create TCP-acceptor + tcp::acceptor acceptor( * io_service, tcp::endpoint( tcp::v4(), port) ); + // loop for accepting connection requests + for (;;) { + boost::system::error_code ec; + // create new publisher-session + // this instance will be associated with one publisher + publisher_session_ptr new_publisher_session = + std::make_shared< publisher_session >( io_service, std::ref( reg) ); + // async. accept of new connection request + // this function will suspend this execution context (fiber) until a + // connection was established, after returning from this function a new client (publisher) + // is connected + acceptor.async_accept( + new_publisher_session->socket(), + boost::fibers::asio::yield[ec]); + if ( ! ec) { + // run the new publisher in its own fiber (one fiber for one client) + boost::fibers::fiber( + std::bind( & publisher_session::run, new_publisher_session) ).detach(); + } + } +} + +// function accepts connections requests from clients acting as a subscriber +void accept_subscriber( std::shared_ptr< boost::asio::io_service > const& io_service, + unsigned short port, + registry & reg) { + // create TCP-acceptor + tcp::acceptor acceptor( * io_service, tcp::endpoint( tcp::v4(), port) ); + // loop for accepting connection requests + for (;;) { + boost::system::error_code ec; + // create new subscriber-session + // this instance will be associated with one subscriber + subscriber_session_ptr new_subscriber_session = + std::make_shared< subscriber_session >( io_service, std::ref( reg) ); + // async. accept of new connection request + // this function will suspend this execution context (fiber) until a + // connection was established, after returning from this function a new client (subscriber) + // is connected + acceptor.async_accept( + new_subscriber_session->socket(), + boost::fibers::asio::yield[ec]); + if ( ! ec) { + // run the new subscriber in its own fiber (one fiber for one client) + boost::fibers::fiber( + std::bind( & subscriber_session::run, new_subscriber_session) ).detach(); + } + } +} + + +int main( int argc, char* argv[]) { + try { + // create io_service for async. I/O + std::shared_ptr< boost::asio::io_service > io_service = std::make_shared< boost::asio::io_service >(); + // register asio scheduler + boost::fibers::use_scheduling_algorithm< boost::fibers::asio::round_robin >( io_service); + // registry for queues and its subscription + registry reg; + // create an acceptor for publishers, run it as fiber + boost::fibers::fiber( + accept_publisher, std::ref( io_service), 9997, std::ref( reg) ).detach(); + // create an acceptor for subscribers, run it as fiber + boost::fibers::fiber( + accept_subscriber, std::ref( io_service), 9998, std::ref( reg) ).detach(); + // dispatch + io_service->run(); + return EXIT_SUCCESS; + } catch ( std::exception const& e) { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return EXIT_FAILURE; +} diff --git a/src/boost/libs/fiber/examples/asio/ps/subscriber.cpp b/src/boost/libs/fiber/examples/asio/ps/subscriber.cpp new file mode 100644 index 00000000..04e583e2 --- /dev/null +++ b/src/boost/libs/fiber/examples/asio/ps/subscriber.cpp @@ -0,0 +1,53 @@ +// +// blocking_tcp_echo_client.cpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// 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 <cstdlib> +#include <cstring> +#include <iostream> + +#include <boost/asio.hpp> + +using boost::asio::ip::tcp; + +enum { + max_length = 1024 +}; + +int main( int argc, char* argv[]) { + try { + if ( 3 != argc) { + std::cerr << "Usage: subscriber <host> <queue>\n"; + return EXIT_FAILURE; + } + boost::asio::io_service io_service; + tcp::resolver resolver( io_service); + tcp::resolver::query query( tcp::v4(), argv[1], "9998"); + tcp::resolver::iterator iterator = resolver.resolve( query); + tcp::socket s( io_service); + boost::asio::connect( s, iterator); + char msg[max_length]; + std::string queue( argv[2]); + std::memset( msg, '\0', max_length); + std::memcpy( msg, queue.c_str(), queue.size() ); + boost::asio::write( s, boost::asio::buffer( msg, max_length) ); + for (;;) { + char reply[max_length]; + size_t reply_length = s.read_some( boost::asio::buffer( reply, max_length) ); + std::cout << "published: "; + std::cout.write( reply, reply_length); + std::cout << std::endl; + } + return EXIT_SUCCESS; + } catch ( std::exception const& e) { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return EXIT_FAILURE; +} diff --git a/src/boost/libs/fiber/examples/asio/round_robin.hpp b/src/boost/libs/fiber/examples/asio/round_robin.hpp new file mode 100644 index 00000000..b06bb35c --- /dev/null +++ b/src/boost/libs/fiber/examples/asio/round_robin.hpp @@ -0,0 +1,194 @@ +// Copyright Oliver Kowalke 2013. +// 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) + +#ifndef BOOST_FIBERS_ASIO_ROUND_ROBIN_H +#define BOOST_FIBERS_ASIO_ROUND_ROBIN_H + +#include <chrono> +#include <cstddef> +#include <memory> +#include <mutex> +#include <queue> + +#include <boost/asio.hpp> +#include <boost/assert.hpp> +#include <boost/asio/steady_timer.hpp> +#include <boost/config.hpp> + +#include <boost/fiber/condition_variable.hpp> +#include <boost/fiber/context.hpp> +#include <boost/fiber/mutex.hpp> +#include <boost/fiber/operations.hpp> +#include <boost/fiber/scheduler.hpp> + +#include "yield.hpp" + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { +namespace asio { + +class round_robin : public algo::algorithm { +private: +//[asio_rr_suspend_timer + std::shared_ptr< boost::asio::io_service > io_svc_; + boost::asio::steady_timer suspend_timer_; +//] + boost::fibers::scheduler::ready_queue_type rqueue_{}; + boost::fibers::mutex mtx_{}; + boost::fibers::condition_variable cnd_{}; + std::size_t counter_{ 0 }; + +public: +//[asio_rr_service_top + struct service : public boost::asio::io_service::service { + static boost::asio::io_service::id id; + + std::unique_ptr< boost::asio::io_service::work > work_; + + service( boost::asio::io_service & io_svc) : + boost::asio::io_service::service( io_svc), + work_{ new boost::asio::io_service::work( io_svc) } { + } + + virtual ~service() {} + + service( service const&) = delete; + service & operator=( service const&) = delete; + + void shutdown_service() override final { + work_.reset(); + } + }; +//] + +//[asio_rr_ctor + round_robin( std::shared_ptr< boost::asio::io_service > const& io_svc) : + io_svc_( io_svc), + suspend_timer_( * io_svc_) { + // We use add_service() very deliberately. This will throw + // service_already_exists if you pass the same io_service instance to + // more than one round_robin instance. + boost::asio::add_service( * io_svc_, new service( * io_svc_) ); + io_svc_->post([this]() mutable { +//] +//[asio_rr_service_lambda + while ( ! io_svc_->stopped() ) { + if ( has_ready_fibers() ) { + // run all pending handlers in round_robin + while ( io_svc_->poll() ); + // block this fiber till all pending (ready) fibers are processed + // == round_robin::suspend_until() has been called + std::unique_lock< boost::fibers::mutex > lk( mtx_); + cnd_.wait( lk); + } else { + // run one handler inside io_service + // if no handler available, block this thread + if ( ! io_svc_->run_one() ) { + break; + } + } + } +//] + }); + } + + void awakened( context * ctx) noexcept { + BOOST_ASSERT( nullptr != ctx); + BOOST_ASSERT( ! ctx->ready_is_linked() ); + ctx->ready_link( rqueue_); /*< fiber, enqueue on ready queue >*/ + if ( ! ctx->is_context( boost::fibers::type::dispatcher_context) ) { + ++counter_; + } + } + + context * pick_next() noexcept { + context * ctx( nullptr); + if ( ! rqueue_.empty() ) { /*< + pop an item from the ready queue + >*/ + ctx = & rqueue_.front(); + rqueue_.pop_front(); + BOOST_ASSERT( nullptr != ctx); + BOOST_ASSERT( context::active() != ctx); + if ( ! ctx->is_context( boost::fibers::type::dispatcher_context) ) { + --counter_; + } + } + return ctx; + } + + bool has_ready_fibers() const noexcept { + return 0 < counter_; + } + +//[asio_rr_suspend_until + void suspend_until( std::chrono::steady_clock::time_point const& abs_time) noexcept { + // Set a timer so at least one handler will eventually fire, causing + // run_one() to eventually return. + if ( (std::chrono::steady_clock::time_point::max)() != abs_time) { + // Each expires_at(time_point) call cancels any previous pending + // call. We could inadvertently spin like this: + // dispatcher calls suspend_until() with earliest wake time + // suspend_until() sets suspend_timer_ + // lambda loop calls run_one() + // some other asio handler runs before timer expires + // run_one() returns to lambda loop + // lambda loop yields to dispatcher + // dispatcher finds no ready fibers + // dispatcher calls suspend_until() with SAME wake time + // suspend_until() sets suspend_timer_ to same time, canceling + // previous async_wait() + // lambda loop calls run_one() + // asio calls suspend_timer_ handler with operation_aborted + // run_one() returns to lambda loop... etc. etc. + // So only actually set the timer when we're passed a DIFFERENT + // abs_time value. + suspend_timer_.expires_at( abs_time); + suspend_timer_.async_wait([](boost::system::error_code const&){ + this_fiber::yield(); + }); + } + cnd_.notify_one(); + } +//] + +//[asio_rr_notify + void notify() noexcept { + // Something has happened that should wake one or more fibers BEFORE + // suspend_timer_ expires. Reset the timer to cause it to fire + // immediately, causing the run_one() call to return. In theory we + // could use cancel() because we don't care whether suspend_timer_'s + // handler is called with operation_aborted or success. However -- + // cancel() doesn't change the expiration time, and we use + // suspend_timer_'s expiration time to decide whether it's already + // set. If suspend_until() set some specific wake time, then notify() + // canceled it, then suspend_until() was called again with the same + // wake time, it would match suspend_timer_'s expiration time and we'd + // refrain from setting the timer. So instead of simply calling + // cancel(), reset the timer, which cancels the pending sleep AND sets + // a new expiration time. This will cause us to spin the loop twice -- + // once for the operation_aborted handler, once for timer expiration + // -- but that shouldn't be a big problem. + suspend_timer_.async_wait([](boost::system::error_code const&){ + this_fiber::yield(); + }); + suspend_timer_.expires_at( std::chrono::steady_clock::now() ); + } +//] +}; + +boost::asio::io_service::id round_robin::service::id; + +}}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_FIBERS_ASIO_ROUND_ROBIN_H diff --git a/src/boost/libs/fiber/examples/asio/yield.hpp b/src/boost/libs/fiber/examples/asio/yield.hpp new file mode 100644 index 00000000..d76467f9 --- /dev/null +++ b/src/boost/libs/fiber/examples/asio/yield.hpp @@ -0,0 +1,63 @@ +// Copyright 2003-2013 Christopher M. Kohlhoff +// Copyright Oliver Kowalke, Nat Goodspeed 2015. +// 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) + + +#ifndef BOOST_FIBERS_ASIO_YIELD_HPP +#define BOOST_FIBERS_ASIO_YIELD_HPP + +#include <boost/config.hpp> + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { +namespace asio { + +//[fibers_asio_yield_t +class yield_t { +public: + yield_t() = default; + + /** + * @code + * static yield_t yield; + * boost::system::error_code myec; + * func(yield[myec]); + * @endcode + * @c yield[myec] returns an instance of @c yield_t whose @c ec_ points + * to @c myec. The expression @c yield[myec] "binds" @c myec to that + * (anonymous) @c yield_t instance, instructing @c func() to store any + * @c error_code it might produce into @c myec rather than throwing @c + * boost::system::system_error. + */ + yield_t operator[]( boost::system::error_code & ec) const { + yield_t tmp; + tmp.ec_ = & ec; + return tmp; + } + +//private: + // ptr to bound error_code instance if any + boost::system::error_code * ec_{ nullptr }; +}; +//] + +//[fibers_asio_yield +// canonical instance +thread_local yield_t yield{}; +//] + +}}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#include "detail/yield.hpp" + +#endif // BOOST_FIBERS_ASIO_YIELD_HPP diff --git a/src/boost/libs/fiber/examples/barrier.cpp b/src/boost/libs/fiber/examples/barrier.cpp new file mode 100644 index 00000000..dedec6c6 --- /dev/null +++ b/src/boost/libs/fiber/examples/barrier.cpp @@ -0,0 +1,98 @@ + +// Copyright Oliver Kowalke 2013. +// 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 <cstdlib> +#include <functional> +#include <iostream> +#include <stdexcept> + +#include <boost/assert.hpp> + +#include <boost/fiber/all.hpp> + +int value1 = 0; +int value2 = 0; + +inline +void fn1( boost::fibers::barrier & b) +{ + boost::fibers::fiber::id id = boost::this_fiber::get_id(); + std::cout << "fiber " << id << ": fn1 entered" << std::endl; + + ++value1; + std::cout << "fiber " << id << ": incremented value1: " << value1 << std::endl; + boost::this_fiber::yield(); + + std::cout << "fiber " << id << ": waits for barrier" << std::endl; + b.wait(); + std::cout << "fiber " << id << ": passed barrier" << std::endl; + + ++value1; + std::cout << "fiber " << id << ": incremented value1: " << value1 << std::endl; + boost::this_fiber::yield(); + + ++value1; + std::cout << "fiber " << id << ": incremented value1: " << value1 << std::endl; + boost::this_fiber::yield(); + + ++value1; + std::cout << "fiber " << id << ": incremented value1: " << value1 << std::endl; + boost::this_fiber::yield(); + + std::cout << "fiber " << id << ": fn1 returns" << std::endl; +} + +inline +void fn2( boost::fibers::barrier & b) +{ + boost::fibers::fiber::id id = boost::this_fiber::get_id(); + std::cout << "fiber " << id << ": fn2 entered" << std::endl; + + ++value2; + std::cout << "fiber " << id << ": incremented value2: " << value2 << std::endl; + boost::this_fiber::yield(); + + ++value2; + std::cout << "fiber " << id << ": incremented value2: " << value2 << std::endl; + boost::this_fiber::yield(); + + ++value2; + std::cout << "fiber " << id << ": incremented value2: " << value2 << std::endl; + boost::this_fiber::yield(); + + std::cout << "fiber " << id << ": waits for barrier" << std::endl; + b.wait(); + std::cout << "fiber " << id << ": passed barrier" << std::endl; + + ++value2; + std::cout << "fiber " << id << ": incremented value2: " << value2 << std::endl; + boost::this_fiber::yield(); + + std::cout << "fiber " << id << ": fn2 returns" << std::endl; +} + +int main() +{ + try + { + boost::fibers::barrier fb( 2); + + boost::fibers::fiber f1( & fn1, std::ref( fb) ); + boost::fibers::fiber f2( & fn2, std::ref( fb) ); + + f1.join(); + f2.join(); + + std::cout << "done." << std::endl; + + return EXIT_SUCCESS; + } + catch ( std::exception const& e) + { std::cerr << "exception: " << e.what() << std::endl; } + catch (...) + { std::cerr << "unhandled exception" << std::endl; } + return EXIT_FAILURE; +} diff --git a/src/boost/libs/fiber/examples/cuda/Makefile b/src/boost/libs/fiber/examples/cuda/Makefile new file mode 100644 index 00000000..60a3098e --- /dev/null +++ b/src/boost/libs/fiber/examples/cuda/Makefile @@ -0,0 +1,29 @@ +CUDA_PATH := /opt/cuda + +NVCC := $(CUDA_PATH)/bin/nvcc + +CPPFLAGS := -O2 -std=c++11 +LDFLAGS := -g -L/usr/local/lib +INCLUDES := -I/usr/local/include -I$(CUDA_PATH)/include +LIBRARIES := -lboost_fiber -lboost_context -lboost_system -lboost_filesystem + +all: build + +build: single_stream multiple_streams + +single_stream.o:single_stream.cu + $(NVCC) $(INCLUDES) $(CPPFLAGS) -o $@ -c $< + +single_stream: single_stream.o + $(NVCC) $(LDFLAGS) -o $@ $+ $(LIBRARIES) + +multiple_streams.o:multiple_streams.cu + $(NVCC) $(INCLUDES) $(CPPFLAGS) -o $@ -c $< + +multiple_streams: multiple_streams.o + $(NVCC) $(LDFLAGS) -o $@ $+ $(LIBRARIES) + +clean: + rm -f single_stream single_stream.o multiple_streams multiple_streams.o + +clobber: clean diff --git a/src/boost/libs/fiber/examples/cuda/multiple_streams.cu b/src/boost/libs/fiber/examples/cuda/multiple_streams.cu new file mode 100644 index 00000000..0c665e29 --- /dev/null +++ b/src/boost/libs/fiber/examples/cuda/multiple_streams.cu @@ -0,0 +1,112 @@ + +// Copyright Oliver Kowalke 2013. +// 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 <chrono> +#include <cstdlib> +#include <iostream> +#include <memory> +#include <random> +#include <tuple> + +#include <cuda.h> + +#include <boost/assert.hpp> +#include <boost/bind.hpp> +#include <boost/intrusive_ptr.hpp> + +#include <boost/fiber/all.hpp> +#include <boost/fiber/cuda/waitfor.hpp> + +__global__ +void vector_add( int * a, int * b, int * c, int size) { + int idx = threadIdx.x + blockIdx.x * blockDim.x; + if ( idx < size) { + c[idx] = a[idx] + b[idx]; + } +} + +int main() { + try { + bool done = false; + boost::fibers::fiber f1( [&done]{ + std::cout << "f1: entered" << std::endl; + try { + cudaStream_t stream0, stream1; + cudaStreamCreate( & stream0); + cudaStreamCreate( & stream1); + int size = 1024 * 1024; + int full_size = 20 * size; + int * host_a, * host_b, * host_c; + cudaHostAlloc( & host_a, full_size * sizeof( int), cudaHostAllocDefault); + cudaHostAlloc( & host_b, full_size * sizeof( int), cudaHostAllocDefault); + cudaHostAlloc( & host_c, full_size * sizeof( int), cudaHostAllocDefault); + int * dev_a0, * dev_b0, * dev_c0; + int * dev_a1, * dev_b1, * dev_c1; + cudaMalloc( & dev_a0, size * sizeof( int) ); + cudaMalloc( & dev_b0, size * sizeof( int) ); + cudaMalloc( & dev_c0, size * sizeof( int) ); + cudaMalloc( & dev_a1, size * sizeof( int) ); + cudaMalloc( & dev_b1, size * sizeof( int) ); + cudaMalloc( & dev_c1, size * sizeof( int) ); + std::minstd_rand generator; + std::uniform_int_distribution<> distribution(1, 6); + for ( int i = 0; i < full_size; ++i) { + host_a[i] = distribution( generator); + host_b[i] = distribution( generator); + } + for ( int i = 0; i < full_size; i += 2 * size) { + cudaMemcpyAsync( dev_a0, host_a + i, size * sizeof( int), cudaMemcpyHostToDevice, stream0); + cudaMemcpyAsync( dev_a1, host_a + i + size, size * sizeof( int), cudaMemcpyHostToDevice, stream1); + cudaMemcpyAsync( dev_b0, host_b + i, size * sizeof( int), cudaMemcpyHostToDevice, stream0); + cudaMemcpyAsync( dev_b1, host_b + i + size, size * sizeof( int), cudaMemcpyHostToDevice, stream1); + vector_add<<< size / 256, 256, 0, stream0 >>>( dev_a0, dev_b0, dev_c0, size); + vector_add<<< size / 256, 256, 0, stream1 >>>( dev_a1, dev_b1, dev_c1, size); + cudaMemcpyAsync( host_c + i, dev_c0, size * sizeof( int), cudaMemcpyDeviceToHost, stream0); + cudaMemcpyAsync( host_c + i + size, dev_c1, size * sizeof( int), cudaMemcpyDeviceToHost, stream1); + } + auto results = boost::fibers::cuda::waitfor_all( stream0, stream1); + for ( auto & result : results) { + BOOST_ASSERT( stream0 == std::get< 0 >( result) || stream1 == std::get< 0 >( result) ); + BOOST_ASSERT( cudaSuccess == std::get< 1 >( result) ); + } + std::cout << "f1: GPU computation finished" << std::endl; + cudaFreeHost( host_a); + cudaFreeHost( host_b); + cudaFreeHost( host_c); + cudaFree( dev_a0); + cudaFree( dev_b0); + cudaFree( dev_c0); + cudaFree( dev_a1); + cudaFree( dev_b1); + cudaFree( dev_c1); + cudaStreamDestroy( stream0); + cudaStreamDestroy( stream1); + done = true; + } catch ( std::exception const& ex) { + std::cerr << "exception: " << ex.what() << std::endl; + } + std::cout << "f1: leaving" << std::endl; + }); + boost::fibers::fiber f2([&done]{ + std::cout << "f2: entered" << std::endl; + while ( ! done) { + std::cout << "f2: sleeping" << std::endl; + boost::this_fiber::sleep_for( std::chrono::milliseconds( 1 ) ); + } + std::cout << "f2: leaving" << std::endl; + }); + f1.join(); + f2.join(); + std::cout << "done." << std::endl; + return EXIT_SUCCESS; + } catch ( std::exception const& e) { + std::cerr << "exception: " << e.what() << std::endl; + } catch (...) { + std::cerr << "unhandled exception" << std::endl; + } + return EXIT_FAILURE; +} diff --git a/src/boost/libs/fiber/examples/cuda/single_stream.cu b/src/boost/libs/fiber/examples/cuda/single_stream.cu new file mode 100644 index 00000000..79f398a1 --- /dev/null +++ b/src/boost/libs/fiber/examples/cuda/single_stream.cu @@ -0,0 +1,96 @@ + +// Copyright Oliver Kowalke 2017. +// 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 <chrono> +#include <cstdlib> +#include <iostream> +#include <memory> +#include <random> +#include <tuple> + +#include <cuda.h> + +#include <boost/assert.hpp> +#include <boost/bind.hpp> +#include <boost/intrusive_ptr.hpp> + +#include <boost/fiber/all.hpp> +#include <boost/fiber/cuda/waitfor.hpp> + +__global__ +void vector_add( int * a, int * b, int * c, int size) { + int idx = threadIdx.x + blockIdx.x * blockDim.x; + if ( idx < size) { + c[idx] = a[idx] + b[idx]; + } +} + +int main() { + try { + bool done = false; + boost::fibers::fiber f1([&done]{ + std::cout << "f1: entered" << std::endl; + try { + cudaStream_t stream; + cudaStreamCreate( & stream); + int size = 1024 * 1024; + int full_size = 20 * size; + int * host_a, * host_b, * host_c; + cudaHostAlloc( & host_a, full_size * sizeof( int), cudaHostAllocDefault); + cudaHostAlloc( & host_b, full_size * sizeof( int), cudaHostAllocDefault); + cudaHostAlloc( & host_c, full_size * sizeof( int), cudaHostAllocDefault); + int * dev_a, * dev_b, * dev_c; + cudaMalloc( & dev_a, size * sizeof( int) ); + cudaMalloc( & dev_b, size * sizeof( int) ); + cudaMalloc( & dev_c, size * sizeof( int) ); + std::minstd_rand generator; + std::uniform_int_distribution<> distribution(1, 6); + for ( int i = 0; i < full_size; ++i) { + host_a[i] = distribution( generator); + host_b[i] = distribution( generator); + } + for ( int i = 0; i < full_size; i += size) { + cudaMemcpyAsync( dev_a, host_a + i, size * sizeof( int), cudaMemcpyHostToDevice, stream); + cudaMemcpyAsync( dev_b, host_b + i, size * sizeof( int), cudaMemcpyHostToDevice, stream); + vector_add<<< size / 256, 256, 0, stream >>>( dev_a, dev_b, dev_c, size); + cudaMemcpyAsync( host_c + i, dev_c, size * sizeof( int), cudaMemcpyDeviceToHost, stream); + } + auto result = boost::fibers::cuda::waitfor_all( stream); + BOOST_ASSERT( stream == std::get< 0 >( result) ); + BOOST_ASSERT( cudaSuccess == std::get< 1 >( result) ); + std::cout << "f1: GPU computation finished" << std::endl; + cudaFreeHost( host_a); + cudaFreeHost( host_b); + cudaFreeHost( host_c); + cudaFree( dev_a); + cudaFree( dev_b); + cudaFree( dev_c); + cudaStreamDestroy( stream); + done = true; + } catch ( std::exception const& ex) { + std::cerr << "exception: " << ex.what() << std::endl; + } + std::cout << "f1: leaving" << std::endl; + }); + boost::fibers::fiber f2([&done]{ + std::cout << "f2: entered" << std::endl; + while ( ! done) { + std::cout << "f2: sleeping" << std::endl; + boost::this_fiber::sleep_for( std::chrono::milliseconds( 1 ) ); + } + std::cout << "f2: leaving" << std::endl; + }); + f1.join(); + f2.join(); + std::cout << "done." << std::endl; + return EXIT_SUCCESS; + } catch ( std::exception const& e) { + std::cerr << "exception: " << e.what() << std::endl; + } catch (...) { + std::cerr << "unhandled exception" << std::endl; + } + return EXIT_FAILURE; +} diff --git a/src/boost/libs/fiber/examples/future.cpp b/src/boost/libs/fiber/examples/future.cpp new file mode 100644 index 00000000..cc7c6b0f --- /dev/null +++ b/src/boost/libs/fiber/examples/future.cpp @@ -0,0 +1,50 @@ + +// Copyright Oliver Kowalke 2013. +// 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 <cstdlib> +#include <exception> +#include <functional> +#include <iostream> +#include <string> + +#include <boost/fiber/all.hpp> + +inline +int fn( std::string const& str, int n) +{ + for ( int i = 0; i < n; ++i) + { + std::cout << i << ": " << str << std::endl; + boost::this_fiber::yield(); + } + + return n; +} + +void start() +{ + boost::fibers::future< int > fi( + boost::fibers::async( + std::bind( fn, "abc", 5) ) ); + fi.wait(); + std::cout << "fn() returned " << fi.get() << std::endl; +} + +int main() +{ + try + { + boost::fibers::fiber( start).join(); + std::cout << "done." << std::endl; + + return EXIT_SUCCESS; + } + catch ( std::exception const& e) + { std::cerr << "exception: " << e.what() << std::endl; } + catch (...) + { std::cerr << "unhandled exception" << std::endl; } + return EXIT_FAILURE; +} diff --git a/src/boost/libs/fiber/examples/hip/Makefile b/src/boost/libs/fiber/examples/hip/Makefile new file mode 100644 index 00000000..d40e8c1a --- /dev/null +++ b/src/boost/libs/fiber/examples/hip/Makefile @@ -0,0 +1,29 @@ +HIP_PATH := /opt/rocm/hip + +HIPCC := $(HIP_PATH)/bin/hipcc + +CPPFLAGS := -O2 -std=c++11 +LDFLAGS := -L/usr/local/lib +INCLUDES := -I/usr/local/include -I$(HIP_PATH)/include +LIBRARIES := -lboost_fiber -lboost_context -lboost_system -lboost_filesystem + +all: build + +build: single_stream multiple_streams + +single_stream.o:single_stream.cpp + $(HIPCC) $(INCLUDES) $(CPPFLAGS) -o $@ -c $< + +single_stream: single_stream.o + $(HIPCC) $(LDFLAGS) -o $@ $+ $(LIBRARIES) + +multiple_streams.o:multiple_streams.cpp + $(HIPCC) $(INCLUDES) $(CPPFLAGS) -o $@ -c $< + +multiple_streams: multiple_streams.o + $(HIPCC) $(LDFLAGS) -o $@ $+ $(LIBRARIES) + +clean: + rm -f single_stream single_stream.o multiple_streams multiple_streams.o + +clobber: clean diff --git a/src/boost/libs/fiber/examples/hip/multiple_streams.cpp b/src/boost/libs/fiber/examples/hip/multiple_streams.cpp new file mode 100644 index 00000000..75a7449c --- /dev/null +++ b/src/boost/libs/fiber/examples/hip/multiple_streams.cpp @@ -0,0 +1,111 @@ + +// Copyright Oliver Kowalke 2017. +// 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 <chrono> +#include <cstdlib> +#include <iostream> +#include <memory> +#include <random> +#include <tuple> + +#include <hip/hip_runtime.h> + +#include <boost/assert.hpp> +#include <boost/bind.hpp> +#include <boost/intrusive_ptr.hpp> + +#include <boost/fiber/all.hpp> +#include <boost/fiber/hip/waitfor.hpp> + +__global__ +void vector_add(hipLaunchParm lp, int * a, int * b, int * c, int size) { + int idx = threadIdx.x + blockIdx.x * blockDim.x; + if ( idx < size) { + c[idx] = a[idx] + b[idx]; + } +} + +int main() { + try { + bool done = false; + boost::fibers::fiber f1( [&done]{ + std::cout << "f1: entered" << std::endl; + try { + hipStream_t stream0, stream1; + hipStreamCreate( & stream0); + hipStreamCreate( & stream1); + int size = 1024 * 1024; + int full_size = 20 * size; + int * host_a, * host_b, * host_c; + hipHostMalloc( & host_a, full_size * sizeof( int), hipHostMallocDefault); + hipHostMalloc( & host_b, full_size * sizeof( int), hipHostMallocDefault); + hipHostMalloc( & host_c, full_size * sizeof( int), hipHostMallocDefault); + int * dev_a0, * dev_b0, * dev_c0; + int * dev_a1, * dev_b1, * dev_c1; + hipMalloc( & dev_a0, size * sizeof( int) ); + hipMalloc( & dev_b0, size * sizeof( int) ); + hipMalloc( & dev_c0, size * sizeof( int) ); + hipMalloc( & dev_a1, size * sizeof( int) ); + hipMalloc( & dev_b1, size * sizeof( int) ); + hipMalloc( & dev_c1, size * sizeof( int) ); + std::minstd_rand generator; + std::uniform_int_distribution<> distribution(1, 6); + for ( int i = 0; i < full_size; ++i) { + host_a[i] = distribution( generator); + host_b[i] = distribution( generator); + } + for ( int i = 0; i < full_size; i += 2 * size) { + hipMemcpyAsync( dev_a0, host_a + i, size * sizeof( int), hipMemcpyHostToDevice, stream0); + hipMemcpyAsync( dev_a1, host_a + i + size, size * sizeof( int), hipMemcpyHostToDevice, stream1); + hipMemcpyAsync( dev_b0, host_b + i, size * sizeof( int), hipMemcpyHostToDevice, stream0); + hipMemcpyAsync( dev_b1, host_b + i + size, size * sizeof( int), hipMemcpyHostToDevice, stream1); + hipLaunchKernel( vector_add, dim3(size / 256), dim3(256), 0, stream0, dev_a0, dev_b0, dev_c0, size); + hipLaunchKernel( vector_add, dim3(size / 256), dim3(256), 0, stream1, dev_a1, dev_b1, dev_c1, size); + hipMemcpyAsync( host_c + i, dev_c0, size * sizeof( int), hipMemcpyDeviceToHost, stream0); + hipMemcpyAsync( host_c + i + size, dev_c1, size * sizeof( int), hipMemcpyDeviceToHost, stream1); + } + auto results = boost::fibers::hip::waitfor_all( stream0, stream1); + for ( auto & result : results) { + BOOST_ASSERT( stream0 == std::get< 0 >( result) || stream1 == std::get< 0 >( result) ); + BOOST_ASSERT( hipSuccess == std::get< 1 >( result) ); + } + std::cout << "f1: GPU computation finished" << std::endl; + hipHostFree( host_a); + hipHostFree( host_b); + hipHostFree( host_c); + hipFree( dev_a0); + hipFree( dev_b0); + hipFree( dev_c0); + hipFree( dev_a1); + hipFree( dev_b1); + hipFree( dev_c1); + hipStreamDestroy( stream0); + hipStreamDestroy( stream1); + done = true; + } catch ( std::exception const& ex) { + std::cerr << "exception: " << ex.what() << std::endl; + } + std::cout << "f1: leaving" << std::endl; + }); + boost::fibers::fiber f2([&done]{ + std::cout << "f2: entered" << std::endl; + while ( ! done) { + std::cout << "f2: sleeping" << std::endl; + boost::this_fiber::sleep_for( std::chrono::milliseconds( 1 ) ); + } + std::cout << "f2: leaving" << std::endl; + }); + f1.join(); + f2.join(); + std::cout << "done." << std::endl; + return EXIT_SUCCESS; + } catch ( std::exception const& e) { + std::cerr << "exception: " << e.what() << std::endl; + } catch (...) { + std::cerr << "unhandled exception" << std::endl; + } + return EXIT_FAILURE; +} diff --git a/src/boost/libs/fiber/examples/hip/single_stream.cpp b/src/boost/libs/fiber/examples/hip/single_stream.cpp new file mode 100644 index 00000000..1959528e --- /dev/null +++ b/src/boost/libs/fiber/examples/hip/single_stream.cpp @@ -0,0 +1,96 @@ + +// Copyright Oliver Kowalke 2017. +// 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 <chrono> +#include <cstdlib> +#include <iostream> +#include <memory> +#include <random> +#include <tuple> + +#include <hip/hip_runtime.h> + +#include <boost/assert.hpp> +#include <boost/bind.hpp> +#include <boost/intrusive_ptr.hpp> + +#include <boost/fiber/all.hpp> +#include <boost/fiber/hip/waitfor.hpp> + +__global__ +void vector_add(hipLaunchParm lp, int * a, int * b, int * c, int size) { + int idx = threadIdx.x + blockIdx.x * blockDim.x; + if ( idx < size) { + c[idx] = a[idx] + b[idx]; + } +} + +int main() { + try { + bool done = false; + boost::fibers::fiber f1([&done]{ + std::cout << "f1: entered" << std::endl; + try { + hipStream_t stream; + hipStreamCreate( & stream); + int size = 1024 * 1024; + int full_size = 20 * size; + int * host_a, * host_b, * host_c; + hipHostMalloc( & host_a, full_size * sizeof( int), hipHostMallocDefault); + hipHostMalloc( & host_b, full_size * sizeof( int), hipHostMallocDefault); + hipHostMalloc( & host_c, full_size * sizeof( int), hipHostMallocDefault); + int * dev_a, * dev_b, * dev_c; + hipMalloc( & dev_a, size * sizeof( int) ); + hipMalloc( & dev_b, size * sizeof( int) ); + hipMalloc( & dev_c, size * sizeof( int) ); + std::minstd_rand generator; + std::uniform_int_distribution<> distribution(1, 6); + for ( int i = 0; i < full_size; ++i) { + host_a[i] = distribution( generator); + host_b[i] = distribution( generator); + } + for ( int i = 0; i < full_size; i += size) { + hipMemcpyAsync( dev_a, host_a + i, size * sizeof( int), hipMemcpyHostToDevice, stream); + hipMemcpyAsync( dev_b, host_b + i, size * sizeof( int), hipMemcpyHostToDevice, stream); + hipLaunchKernel( vector_add, dim3(size / 256), dim3(256), 0, stream, dev_a, dev_b, dev_c, size); + hipMemcpyAsync( host_c + i, dev_c, size * sizeof( int), hipMemcpyDeviceToHost, stream); + } + auto result = boost::fibers::hip::waitfor_all( stream); + BOOST_ASSERT( stream == std::get< 0 >( result) ); + BOOST_ASSERT( hipSuccess == std::get< 1 >( result) ); + std::cout << "f1: GPU computation finished" << std::endl; + hipHostFree( host_a); + hipHostFree( host_b); + hipHostFree( host_c); + hipFree( dev_a); + hipFree( dev_b); + hipFree( dev_c); + hipStreamDestroy( stream); + done = true; + } catch ( std::exception const& ex) { + std::cerr << "exception: " << ex.what() << std::endl; + } + std::cout << "f1: leaving" << std::endl; + }); + boost::fibers::fiber f2([&done]{ + std::cout << "f2: entered" << std::endl; + while ( ! done) { + std::cout << "f2: sleeping" << std::endl; + boost::this_fiber::sleep_for( std::chrono::milliseconds( 1 ) ); + } + std::cout << "f2: leaving" << std::endl; + }); + f1.join(); + f2.join(); + std::cout << "done." << std::endl; + return EXIT_SUCCESS; + } catch ( std::exception const& e) { + std::cerr << "exception: " << e.what() << std::endl; + } catch (...) { + std::cerr << "unhandled exception" << std::endl; + } + return EXIT_FAILURE; +} diff --git a/src/boost/libs/fiber/examples/join.cpp b/src/boost/libs/fiber/examples/join.cpp new file mode 100644 index 00000000..7ece0500 --- /dev/null +++ b/src/boost/libs/fiber/examples/join.cpp @@ -0,0 +1,67 @@ + +// Copyright Oliver Kowalke 2013. +// 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 <cstdlib> +#include <functional> +#include <stdexcept> +#include <iostream> +#include <string> + +#include <boost/fiber/all.hpp> + +int value1 = 0; +int value2 = 0; + +void fn1() +{ + boost::fibers::fiber::id this_id = boost::this_fiber::get_id(); + for ( int i = 0; i < 5; ++i) + { + ++value1; + std::cout << "fiber " << this_id << " fn1: increment value1: " << value1 << std::endl; + boost::this_fiber::yield(); + } + std::cout << "fiber " << this_id << " fn1: returns" << std::endl; +} + +void fn2( boost::fibers::fiber & f) +{ + boost::fibers::fiber::id this_id = boost::this_fiber::get_id(); + for ( int i = 0; i < 5; ++i) + { + ++value2; + std::cout << "fiber " << this_id << " fn2: increment value2: " << value2 << std::endl; + if ( i == 1) + { + boost::fibers::fiber::id id = f.get_id(); + std::cout << "fiber " << this_id << " fn2: joins fiber " << id << std::endl; + f.join(); + std::cout << "fiber " << this_id << " fn2: joined fiber " << id << std::endl; + } + boost::this_fiber::yield(); + } + std::cout << "fiber " << this_id << " fn2: returns" << std::endl; +} + +int main() +{ + try + { + boost::fibers::fiber f1( fn1); + boost::fibers::fiber f2( fn2, std::ref( f1) ); + + f2.join(); + + std::cout << "done." << std::endl; + + return EXIT_SUCCESS; + } + catch ( std::exception const& e) + { std::cerr << "exception: " << e.what() << std::endl; } + catch (...) + { std::cerr << "unhandled exception" << std::endl; } + return EXIT_FAILURE; +} diff --git a/src/boost/libs/fiber/examples/numa/Jamfile.v2 b/src/boost/libs/fiber/examples/numa/Jamfile.v2 new file mode 100644 index 00000000..15420cb5 --- /dev/null +++ b/src/boost/libs/fiber/examples/numa/Jamfile.v2 @@ -0,0 +1,31 @@ +# Boost.Fiber Library Examples Jamfile + +# Copyright Oliver Kowalke 2017. +# 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) + +# For more information, see http://www.boost.org/ + +import common ; +import feature ; +import indirect ; +import modules ; +import os ; +import toolset ; + +project boost/fiber/example/numa + : requirements + <library>../../build//boost_fiber + <library>../../build//boost_fiber_numa + <target-os>solaris:<linkflags>"-llgrp" + <target-os>windows:<define>_WIN32_WINNT=0x0601 + <toolset>gcc,<segmented-stacks>on:<cxxflags>-fsplit-stack + <toolset>gcc,<segmented-stacks>on:<cxxflags>-DBOOST_USE_SEGMENTED_STACKS + <toolset>clang,<segmented-stacks>on:<cxxflags>-fsplit-stack + <toolset>clang,<segmented-stacks>on:<cxxflags>-DBOOST_USE_SEGMENTED_STACKS + <link>static + <threading>multi + ; + +exe topology : topology.cpp ; diff --git a/src/boost/libs/fiber/examples/numa/topology.cpp b/src/boost/libs/fiber/examples/numa/topology.cpp new file mode 100644 index 00000000..9b85e3f4 --- /dev/null +++ b/src/boost/libs/fiber/examples/numa/topology.cpp @@ -0,0 +1,37 @@ + +// Copyright Oliver Kowalke 2017. +// 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 <cstdlib> +#include <cstring> +#include <exception> +#include <iostream> +#include <vector> + +#include <boost/assert.hpp> +#include <boost/fiber/numa/topology.hpp> + +int main( int argc, char * argv[]) { + try { + std::vector< boost::fibers::numa::node > topo = boost::fibers::numa::topology(); + for ( auto n : topo) { + std::cout << "node: " << n.id << " | "; + std::cout << "cpus: "; + for ( auto cpu_id : n.logical_cpus) { + std::cout << cpu_id << " "; + } + std::cout << "| distance: "; + for ( auto d : n.distance) { + std::cout << d << " "; + } + std::cout << std::endl; + } + std::cout << "done" << std::endl; + return EXIT_SUCCESS; + } catch ( std::exception const& ex) { + std::cerr << "exception: " << ex.what() << std::endl; + } + return EXIT_FAILURE; +} diff --git a/src/boost/libs/fiber/examples/ping_pong.cpp b/src/boost/libs/fiber/examples/ping_pong.cpp new file mode 100644 index 00000000..aa56a411 --- /dev/null +++ b/src/boost/libs/fiber/examples/ping_pong.cpp @@ -0,0 +1,48 @@ + +// Copyright Oliver Kowalke 2013. +// 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 <cstdlib> +#include <iostream> +#include <stdexcept> +#include <string> + +#include <boost/fiber/all.hpp> + +int main() { + using channel_t = boost::fibers::buffered_channel< std::string >; + try { + channel_t chan1{ 2 }, chan2{ 2 }; + + boost::fibers::fiber fping([&chan1,&chan2]{ + chan1.push( "ping"); + std::cout << chan2.value_pop() << "\n"; + chan1.push( "ping"); + std::cout << chan2.value_pop() << "\n"; + chan1.push( "ping"); + std::cout << chan2.value_pop() << "\n"; + }); + boost::fibers::fiber fpong([&chan1,&chan2]{ + std::cout << chan1.value_pop() << "\n"; + chan2.push( "pong"); + std::cout << chan1.value_pop() << "\n"; + chan2.push( "pong"); + std::cout << chan1.value_pop() << "\n"; + chan2.push( "pong"); + }); + + fping.join(); + fpong.join(); + + std::cout << "done." << std::endl; + + return EXIT_SUCCESS; + } + catch ( std::exception const& e) + { std::cerr << "exception: " << e.what() << std::endl; } + catch (...) + { std::cerr << "unhandled exception" << std::endl; } + return EXIT_FAILURE; +} diff --git a/src/boost/libs/fiber/examples/priority.cpp b/src/boost/libs/fiber/examples/priority.cpp new file mode 100644 index 00000000..5f9c51e8 --- /dev/null +++ b/src/boost/libs/fiber/examples/priority.cpp @@ -0,0 +1,353 @@ +// Copyright Nat Goodspeed 2014. +// 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 <chrono> +#include <condition_variable> +#include <iostream> +#include <mutex> +#include <algorithm> // std::find_if() + +#include <boost/fiber/all.hpp> +#include <boost/fiber/scheduler.hpp> + +class Verbose { +public: + Verbose( std::string const& d, std::string const& s="stop") : + desc( d), + stop( s) { + std::cout << desc << " start" << std::endl; + } + + ~Verbose() { + std::cout << desc << ' ' << stop << std::endl; + } + + Verbose( Verbose const&) = delete; + Verbose & operator=( Verbose const&) = delete; + +private: + std::string desc; + std::string stop; +}; + +//[priority_props +class priority_props : public boost::fibers::fiber_properties { +public: + priority_props( boost::fibers::context * ctx): + fiber_properties( ctx), /*< Your subclass constructor must accept a + [^[class_link context]*] and pass it to + the `fiber_properties` constructor. >*/ + priority_( 0) { + } + + int get_priority() const { + return priority_; /*< Provide read access methods at your own discretion. >*/ + } + + // Call this method to alter priority, because we must notify + // priority_scheduler of any change. + void set_priority( int p) { /*< + It's important to call `notify()` on any + change in a property that can affect the + scheduler's behavior. Therefore, such + modifications should only be performed + through an access method. >*/ + // Of course, it's only worth reshuffling the queue and all if we're + // actually changing the priority. + if ( p != priority_) { + priority_ = p; + notify(); + } + } + + // The fiber name of course is solely for purposes of this example + // program; it has nothing to do with implementing scheduler priority. + // This is a public data member -- not requiring set/get access methods -- + // because we need not inform the scheduler of any change. + std::string name; /*< A property that does not affect the scheduler does + not need access methods. >*/ +private: + int priority_; +}; +//] + +//[priority_scheduler +class priority_scheduler : + public boost::fibers::algo::algorithm_with_properties< priority_props > { +private: + typedef boost::fibers::scheduler::ready_queue_type/*< See [link ready_queue_t]. >*/ rqueue_t; + + rqueue_t rqueue_; + std::mutex mtx_{}; + std::condition_variable cnd_{}; + bool flag_{ false }; + +public: + priority_scheduler() : + rqueue_() { + } + + // For a subclass of algorithm_with_properties<>, it's important to + // override the correct awakened() overload. + /*<< You must override the [member_link algorithm_with_properties..awakened] + method. This is how your scheduler receives notification of a + fiber that has become ready to run. >>*/ + virtual void awakened( boost::fibers::context * ctx, priority_props & props) noexcept { + int ctx_priority = props.get_priority(); /*< `props` is the instance of + priority_props associated + with the passed fiber `ctx`. >*/ + // With this scheduler, fibers with higher priority values are + // preferred over fibers with lower priority values. But fibers with + // equal priority values are processed in round-robin fashion. So when + // we're handed a new context*, put it at the end of the fibers + // with that same priority. In other words: search for the first fiber + // in the queue with LOWER priority, and insert before that one. + rqueue_t::iterator i( std::find_if( rqueue_.begin(), rqueue_.end(), + [ctx_priority,this]( boost::fibers::context & c) + { return properties( &c ).get_priority() < ctx_priority; })); + // Now, whether or not we found a fiber with lower priority, + // insert this new fiber here. + rqueue_.insert( i, * ctx); +//<- + + std::cout << "awakened(" << props.name << "): "; + describe_ready_queue(); +//-> + } + + /*<< You must override the [member_link algorithm_with_properties..pick_next] + method. This is how your scheduler actually advises the fiber manager + of the next fiber to run. >>*/ + virtual boost::fibers::context * pick_next() noexcept { + // if ready queue is empty, just tell caller + if ( rqueue_.empty() ) { + return nullptr; + } + boost::fibers::context * ctx( & rqueue_.front() ); + rqueue_.pop_front(); +//<- + std::cout << "pick_next() resuming " << properties( ctx).name << ": "; + describe_ready_queue(); +//-> + return ctx; + } + + /*<< You must override [member_link algorithm_with_properties..has_ready_fibers] + to inform the fiber manager of the state of your ready queue. >>*/ + virtual bool has_ready_fibers() const noexcept { + return ! rqueue_.empty(); + } + + /*<< Overriding [member_link algorithm_with_properties..property_change] + is optional. This override handles the case in which the running + fiber changes the priority of another ready fiber: a fiber already in + our queue. In that case, move the updated fiber within the queue. >>*/ + virtual void property_change( boost::fibers::context * ctx, priority_props & props) noexcept { + // Although our priority_props class defines multiple properties, only + // one of them (priority) actually calls notify() when changed. The + // point of a property_change() override is to reshuffle the ready + // queue according to the updated priority value. +//<- + std::cout << "property_change(" << props.name << '(' << props.get_priority() + << ")): "; +//-> + + // 'ctx' might not be in our queue at all, if caller is changing the + // priority of (say) the running fiber. If it's not there, no need to + // move it: we'll handle it next time it hits awakened(). + if ( ! ctx->ready_is_linked()) { /*< + Your `property_change()` override must be able to + handle the case in which the passed `ctx` is not in + your ready queue. It might be running, or it might be + blocked. >*/ +//<- + // hopefully user will distinguish this case by noticing that + // the fiber with which we were called does not appear in the + // ready queue at all + describe_ready_queue(); +//-> + return; + } + + // Found ctx: unlink it + ctx->ready_unlink(); + + // Here we know that ctx was in our ready queue, but we've unlinked + // it. We happen to have a method that will (re-)add a context* to the + // right place in the ready queue. + awakened( ctx, props); + } +//<- + + void describe_ready_queue() { + if ( rqueue_.empty() ) { + std::cout << "[empty]"; + } else { + const char * delim = ""; + for ( boost::fibers::context & ctx : rqueue_) { + priority_props & props( properties( & ctx) ); + std::cout << delim << props.name << '(' << props.get_priority() << ')'; + delim = ", "; + } + } + std::cout << std::endl; + } +//-> + + void suspend_until( std::chrono::steady_clock::time_point const& time_point) noexcept { + if ( (std::chrono::steady_clock::time_point::max)() == time_point) { + std::unique_lock< std::mutex > lk( mtx_); + cnd_.wait( lk, [this](){ return flag_; }); + flag_ = false; + } else { + std::unique_lock< std::mutex > lk( mtx_); + cnd_.wait_until( lk, time_point, [this](){ return flag_; }); + flag_ = false; + } + } + + void notify() noexcept { + std::unique_lock< std::mutex > lk( mtx_); + flag_ = true; + lk.unlock(); + cnd_.notify_all(); + } +}; +//] + +//[launch +template< typename Fn > +boost::fibers::fiber launch( Fn && func, std::string const& name, int priority) { + boost::fibers::fiber fiber( func); + priority_props & props( fiber.properties< priority_props >() ); + props.name = name; + props.set_priority( priority); + return fiber; +} +//] + +void yield_fn() { + std::string name( boost::this_fiber::properties< priority_props >().name); + Verbose v( std::string("fiber ") + name); + for ( int i = 0; i < 3; ++i) { + std::cout << "fiber " << name << " yielding" << std::endl; + boost::this_fiber::yield(); + } +} + +void barrier_fn( boost::fibers::barrier & barrier) { + std::string name( boost::this_fiber::properties< priority_props >().name); + Verbose v( std::string("fiber ") + name); + std::cout << "fiber " << name << " waiting on barrier" << std::endl; + barrier.wait(); + std::cout << "fiber " << name << " yielding" << std::endl; + boost::this_fiber::yield(); +} + +//[change_fn +void change_fn( boost::fibers::fiber & other, + int other_priority, + boost::fibers::barrier& barrier) { + std::string name( boost::this_fiber::properties< priority_props >().name); + Verbose v( std::string("fiber ") + name); + +//<- + std::cout << "fiber " << name << " waiting on barrier" << std::endl; +//-> + barrier.wait(); + // We assume a couple things about 'other': + // - that it was also waiting on the same barrier + // - that it has lower priority than this fiber. + // If both are true, 'other' is now ready to run but is sitting in + // priority_scheduler's ready queue. Change its priority. + priority_props & other_props( + other.properties< priority_props >() ); +//<- + std::cout << "fiber " << name << " changing priority of " << other_props.name + << " to " << other_priority << std::endl; +//-> + other_props.set_priority( other_priority); +} +//] + +//[main +int main( int argc, char *argv[]) { + // make sure we use our priority_scheduler rather than default round_robin + boost::fibers::use_scheduling_algorithm< priority_scheduler >(); +/*= ...*/ +/*=}*/ +//] + Verbose v("main()"); + + // for clarity + std::cout << "main() setting name" << std::endl; +//[main_name + boost::this_fiber::properties< priority_props >().name = "main"; +//] + std::cout << "main() running tests" << std::endl; + + { + Verbose v("high-priority first", "stop\n"); + // verify that high-priority fiber always gets scheduled first + boost::fibers::fiber low( launch( yield_fn, "low", 1) ); + boost::fibers::fiber med( launch( yield_fn, "medium", 2) ); + boost::fibers::fiber hi( launch( yield_fn, "high", 3) ); + std::cout << "main: high.join()" << std::endl; + hi.join(); + std::cout << "main: medium.join()" << std::endl; + med.join(); + std::cout << "main: low.join()" << std::endl; + low.join(); + } + + { + Verbose v("same priority round-robin", "stop\n"); + // fibers of same priority are scheduled in round-robin order + boost::fibers::fiber a( launch( yield_fn, "a", 0) ); + boost::fibers::fiber b( launch( yield_fn, "b", 0) ); + boost::fibers::fiber c( launch( yield_fn, "c", 0) ); + std::cout << "main: a.join()" << std::endl; + a.join(); + std::cout << "main: b.join()" << std::endl; + b.join(); + std::cout << "main: c.join()" << std::endl; + c.join(); + } + + { + Verbose v("barrier wakes up all", "stop\n"); + // using a barrier wakes up all waiting fibers at the same time + boost::fibers::barrier barrier( 3); + boost::fibers::fiber low( launch( [&barrier](){ barrier_fn( barrier); }, "low", 1) ); + boost::fibers::fiber med( launch( [&barrier](){ barrier_fn( barrier); }, "medium", 2) ); + boost::fibers::fiber hi( launch( [&barrier](){ barrier_fn( barrier); }, "high", 3) ); + std::cout << "main: low.join()" << std::endl; + low.join(); + std::cout << "main: medium.join()" << std::endl; + med.join(); + std::cout << "main: high.join()" << std::endl; + hi.join(); + } + + { + Verbose v("change priority", "stop\n"); + // change priority of a fiber in priority_scheduler's ready queue + boost::fibers::barrier barrier( 3); + boost::fibers::fiber c( launch( [&barrier](){ barrier_fn( barrier); }, "c", 1) ); + boost::fibers::fiber a( launch( [&c,&barrier]() { change_fn( c, 3, barrier); }, "a", 3) ); + boost::fibers::fiber b( launch( [&barrier](){ barrier_fn( barrier); }, "b", 2) ); + std::cout << "main: a.join()" << std::endl; + std::cout << "main: a.join()" << std::endl; + a.join(); + std::cout << "main: b.join()" << std::endl; + b.join(); + std::cout << "main: c.join()" << std::endl; + c.join(); + } + + std::cout << "done." << std::endl; + + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/fiber/examples/range_for.cpp b/src/boost/libs/fiber/examples/range_for.cpp new file mode 100644 index 00000000..8a1dbba1 --- /dev/null +++ b/src/boost/libs/fiber/examples/range_for.cpp @@ -0,0 +1,53 @@ + +// Copyright Oliver Kowalke 2013. +// 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 <cstdlib> +#include <iostream> +#include <stdexcept> +#include <string> + +#include <boost/fiber/all.hpp> + +typedef boost::fibers::unbuffered_channel< unsigned int > channel_t; + +void foo( channel_t & chan) { + chan.push( 1); + chan.push( 1); + chan.push( 2); + chan.push( 3); + chan.push( 5); + chan.push( 8); + chan.push( 12); + chan.close(); +} + +void bar( channel_t & chan) { + for ( unsigned int value : chan) { + std::cout << value << " "; + } + std::cout << std::endl; +} + +int main() { + try { + channel_t chan; + + boost::fibers::fiber f1( & foo, std::ref( chan) ); + boost::fibers::fiber f2( & bar, std::ref( chan) ); + + f1.join(); + f2.join(); + + std::cout << "done." << std::endl; + + return EXIT_SUCCESS; + } catch ( std::exception const& e) { + std::cerr << "exception: " << e.what() << std::endl; + } catch (...) { + std::cerr << "unhandled exception" << std::endl; + } + return EXIT_FAILURE; +} diff --git a/src/boost/libs/fiber/examples/segmented_stack.cpp b/src/boost/libs/fiber/examples/segmented_stack.cpp new file mode 100644 index 00000000..ce52ee10 --- /dev/null +++ b/src/boost/libs/fiber/examples/segmented_stack.cpp @@ -0,0 +1,74 @@ + +// Copyright Oliver Kowalke 2013. +// 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 <iostream> +#include <thread> + +#include <boost/assert.hpp> +#include <boost/fiber/all.hpp> + +int count = 384; + +#ifdef BOOST_MSVC //MS VisualStudio +__declspec(noinline) void access( char *buf); +#else // GCC +void access( char *buf) __attribute__ ((noinline)); +#endif +void access( char *buf) +{ + buf[0] = '\0'; +} + +void bar( int i) +{ + char buf[4 * 1024]; + + if ( i > 0) + { + access( buf); + std::cout << i << ". iteration" << std::endl; + bar( i - 1); + } +} + +void foo() +{ + bar( count); + boost::this_fiber::yield(); +} + +void thread_fn() +{ + { + boost::fibers::fiber f( +#if defined(BOOST_USE_SEGMENTED_STACKS) + std::allocator_arg, + boost::fibers::segmented_stack( + boost::fibers::segmented_stack::traits_type::default_size() ), +#endif + foo); + f.join(); + } +} + +int main( int argc, char * argv[]) +{ +#if defined(BOOST_USE_SEGMENTED_STACKS) + std::cout << "using segmented_stack stacks: allocates " << count << " * 4kB == " << 4 * count << "kB on stack, "; + std::cout << "initial stack size = " << boost::fibers::segmented_stack::traits_type::default_size() / 1024 << "kB" << std::endl; + std::cout << "application should not fail" << std::endl; +#else + std::cout << "using standard stacks: allocates " << count << " * 4kB == " << 4 * count << "kB on stack, "; + std::cout << "initial stack size = " << boost::fibers::fixedsize_stack::traits_type::default_size() / 1024 << "kB" << std::endl; + std::cout << "application might fail" << std::endl; +#endif + + std::thread( thread_fn).join(); + + std::cout << "done." << std::endl; + + return 0; +} diff --git a/src/boost/libs/fiber/examples/simple.cpp b/src/boost/libs/fiber/examples/simple.cpp new file mode 100644 index 00000000..07b7b6fd --- /dev/null +++ b/src/boost/libs/fiber/examples/simple.cpp @@ -0,0 +1,39 @@ + +// Copyright Oliver Kowalke 2013. +// 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 <cstdlib> +#include <iostream> +#include <memory> +#include <string> +#include <thread> + +#include <boost/intrusive_ptr.hpp> + +#include <boost/fiber/all.hpp> + +inline +void fn( std::string const& str, int n) { + for ( int i = 0; i < n; ++i) { + std::cout << i << ": " << str << std::endl; + boost::this_fiber::yield(); + } +} + +int main() { + try { + boost::fibers::fiber f1( fn, "abc", 5); + std::cerr << "f1 : " << f1.get_id() << std::endl; + f1.join(); + std::cout << "done." << std::endl; + + return EXIT_SUCCESS; + } catch ( std::exception const& e) { + std::cerr << "exception: " << e.what() << std::endl; + } catch (...) { + std::cerr << "unhandled exception" << std::endl; + } + return EXIT_FAILURE; +} diff --git a/src/boost/libs/fiber/examples/wait_stuff.cpp b/src/boost/libs/fiber/examples/wait_stuff.cpp new file mode 100644 index 00000000..00f64788 --- /dev/null +++ b/src/boost/libs/fiber/examples/wait_stuff.cpp @@ -0,0 +1,984 @@ +// Copyright Nat Goodspeed 2015. +// 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 <algorithm> +#include <cassert> +#include <chrono> +#include <iostream> +#include <memory> +#include <sstream> +#include <string> +#include <type_traits> +#include <utility> +#include <vector> + +#include <boost/fiber/all.hpp> +#include <boost/variant/variant.hpp> +#include <boost/variant/get.hpp> + +// These are wait_something() functions rather than when_something() +// functions. A big part of the point of the Fiber library is to model +// sequencing using the processor's instruction pointer rather than chains of +// callbacks. The future-oriented when_all() / when_any() functions are still +// based on chains of callbacks. With Fiber, we can do better. + +/***************************************************************************** +* Verbose +*****************************************************************************/ +class Verbose { +public: + Verbose( std::string const& d): + desc( d) { + std::cout << desc << " start" << std::endl; + } + + ~Verbose() { + std::cout << desc << " stop" << std::endl; + } + + Verbose( Verbose const&) = delete; + Verbose & operator=( Verbose const&) = delete; + +private: + const std::string desc; +}; + +/***************************************************************************** +* Runner and Example +*****************************************************************************/ +// collect and ultimately run every Example +class Runner { + typedef std::vector< std::pair< std::string, std::function< void() > > > function_list; + +public: + void add( std::string const& desc, std::function< void() > const& func) { + functions_.push_back( function_list::value_type( desc, func) ); + } + + void run() { + for ( function_list::value_type const& pair : functions_) { + Verbose v( pair.first); + pair.second(); + } + } + +private: + function_list functions_; +}; + +Runner runner; + +// Example allows us to embed Runner::add() calls at module scope +struct Example { + Example( Runner & runner, std::string const& desc, std::function< void() > const& func) { + runner.add( desc, func); + } +}; + +/***************************************************************************** +* example task functions +*****************************************************************************/ +//[wait_sleeper +template< typename T > +T sleeper_impl( T item, int ms, bool thrw = false) { + std::ostringstream descb, funcb; + descb << item; + std::string desc( descb.str() ); + funcb << " sleeper(" << item << ")"; + Verbose v( funcb.str() ); + + boost::this_fiber::sleep_for( std::chrono::milliseconds( ms) ); + if ( thrw) { + throw std::runtime_error( desc); + } + return item; +} +//] + +inline +std::string sleeper( std::string const& item, int ms, bool thrw = false) { + return sleeper_impl( item, ms, thrw); +} + +inline +double sleeper( double item, int ms, bool thrw = false) { + return sleeper_impl( item, ms, thrw); +} + +inline +int sleeper(int item, int ms, bool thrw = false) { + return sleeper_impl( item, ms, thrw); +} + +/***************************************************************************** +* Done +*****************************************************************************/ +//[wait_done +// Wrap canonical pattern for condition_variable + bool flag +struct Done { +private: + boost::fibers::condition_variable cond; + boost::fibers::mutex mutex; + bool ready = false; + +public: + typedef std::shared_ptr< Done > ptr; + + void wait() { + std::unique_lock< boost::fibers::mutex > lock( mutex); + cond.wait( lock, [this](){ return ready; }); + } + + void notify() { + { + std::unique_lock< boost::fibers::mutex > lock( mutex); + ready = true; + } // release mutex + cond.notify_one(); + } +}; +//] + +/***************************************************************************** +* when_any, simple completion +*****************************************************************************/ +//[wait_first_simple_impl +// Degenerate case: when there are no functions to wait for, return +// immediately. +void wait_first_simple_impl( Done::ptr) { +} + +// When there's at least one function to wait for, launch it and recur to +// process the rest. +template< typename Fn, typename ... Fns > +void wait_first_simple_impl( Done::ptr done, Fn && function, Fns && ... functions) { + boost::fibers::fiber( [done, function](){ + function(); + done->notify(); + }).detach(); + wait_first_simple_impl( done, std::forward< Fns >( functions) ... ); +} +//] + +// interface function: instantiate Done, launch tasks, wait for Done +//[wait_first_simple +template< typename ... Fns > +void wait_first_simple( Fns && ... functions) { + // Use shared_ptr because each function's fiber will bind it separately, + // and we're going to return before the last of them completes. + auto done( std::make_shared< Done >() ); + wait_first_simple_impl( done, std::forward< Fns >( functions) ... ); + done->wait(); +} +//] + +// example usage +Example wfs( runner, "wait_first_simple()", [](){ +//[wait_first_simple_ex + wait_first_simple( + [](){ sleeper("wfs_long", 150); }, + [](){ sleeper("wfs_medium", 100); }, + [](){ sleeper("wfs_short", 50); }); +//] +}); + +/***************************************************************************** +* when_any, return value +*****************************************************************************/ +// When there's only one function, call this overload +//[wait_first_value_impl +template< typename T, typename Fn > +void wait_first_value_impl( std::shared_ptr< boost::fibers::buffered_channel< T > > chan, + Fn && function) { + boost::fibers::fiber( [chan, function](){ + // Ignore channel_op_status returned by push(): + // might be closed; we simply don't care. + chan->push( function() ); + }).detach(); +} +//] + +// When there are two or more functions, call this overload +template< typename T, typename Fn0, typename Fn1, typename ... Fns > +void wait_first_value_impl( std::shared_ptr< boost::fibers::buffered_channel< T > > chan, + Fn0 && function0, + Fn1 && function1, + Fns && ... functions) { + // process the first function using the single-function overload + wait_first_value_impl< T >( chan, + std::forward< Fn0 >( function0) ); + // then recur to process the rest + wait_first_value_impl< T >( chan, + std::forward< Fn1 >( function1), + std::forward< Fns >( functions) ... ); +} + +//[wait_first_value +// Assume that all passed functions have the same return type. The return type +// of wait_first_value() is the return type of the first passed function. It is +// simply invalid to pass NO functions. +template< typename Fn, typename ... Fns > +typename std::result_of< Fn() >::type +wait_first_value( Fn && function, Fns && ... functions) { + typedef typename std::result_of< Fn() >::type return_t; + typedef boost::fibers::buffered_channel< return_t > channel_t; + auto chanp( std::make_shared< channel_t >( 64) ); + // launch all the relevant fibers + wait_first_value_impl< return_t >( chanp, + std::forward< Fn >( function), + std::forward< Fns >( functions) ... ); + // retrieve the first value + return_t value( chanp->value_pop() ); + // close the channel: no subsequent push() has to succeed + chanp->close(); + return value; +} +//] + +// example usage +Example wfv( runner, "wait_first_value()", [](){ +//[wait_first_value_ex + std::string result = wait_first_value( + [](){ return sleeper("wfv_third", 150); }, + [](){ return sleeper("wfv_second", 100); }, + [](){ return sleeper("wfv_first", 50); }); + std::cout << "wait_first_value() => " << result << std::endl; + assert(result == "wfv_first"); +//] +}); + +/***************************************************************************** +* when_any, produce first outcome, whether result or exception +*****************************************************************************/ +// When there's only one function, call this overload. +//[wait_first_outcome_impl +template< typename T, typename CHANP, typename Fn > +void wait_first_outcome_impl( CHANP chan, Fn && function) { + boost::fibers::fiber( + // Use std::bind() here for C++11 compatibility. C++11 lambda capture + // can't move a move-only Fn type, but bind() can. Let bind() move the + // channel pointer and the function into the bound object, passing + // references into the lambda. + std::bind( + []( CHANP & chan, + typename std::decay< Fn >::type & function) { + // Instantiate a packaged_task to capture any exception thrown by + // function. + boost::fibers::packaged_task< T() > task( function); + // Immediately run this packaged_task on same fiber. We want + // function() to have completed BEFORE we push the future. + task(); + // Pass the corresponding future to consumer. Ignore + // channel_op_status returned by push(): might be closed; we + // simply don't care. + chan->push( task.get_future() ); + }, + chan, + std::forward< Fn >( function) + )).detach(); +} +//] + +// When there are two or more functions, call this overload +template< typename T, typename CHANP, typename Fn0, typename Fn1, typename ... Fns > +void wait_first_outcome_impl( CHANP chan, + Fn0 && function0, + Fn1 && function1, + Fns && ... functions) { + // process the first function using the single-function overload + wait_first_outcome_impl< T >( chan, + std::forward< Fn0 >( function0) ); + // then recur to process the rest + wait_first_outcome_impl< T >( chan, + std::forward< Fn1 >( function1), + std::forward< Fns >( functions) ... ); +} + +// Assume that all passed functions have the same return type. The return type +// of wait_first_outcome() is the return type of the first passed function. It is +// simply invalid to pass NO functions. +//[wait_first_outcome +template< typename Fn, typename ... Fns > +typename std::result_of< Fn() >::type +wait_first_outcome( Fn && function, Fns && ... functions) { + // In this case, the value we pass through the channel is actually a + // future -- which is already ready. future can carry either a value or an + // exception. + typedef typename std::result_of< Fn() >::type return_t; + typedef boost::fibers::future< return_t > future_t; + typedef boost::fibers::buffered_channel< future_t > channel_t; + auto chanp(std::make_shared< channel_t >( 64) ); + // launch all the relevant fibers + wait_first_outcome_impl< return_t >( chanp, + std::forward< Fn >( function), + std::forward< Fns >( functions) ... ); + // retrieve the first future + future_t future( chanp->value_pop() ); + // close the channel: no subsequent push() has to succeed + chanp->close(); + // either return value or throw exception + return future.get(); +} +//] + +// example usage +Example wfo( runner, "wait_first_outcome()", [](){ +//[wait_first_outcome_ex + std::string result = wait_first_outcome( + [](){ return sleeper("wfos_first", 50); }, + [](){ return sleeper("wfos_second", 100); }, + [](){ return sleeper("wfos_third", 150); }); + std::cout << "wait_first_outcome(success) => " << result << std::endl; + assert(result == "wfos_first"); + + std::string thrown; + try { + result = wait_first_outcome( + [](){ return sleeper("wfof_first", 50, true); }, + [](){ return sleeper("wfof_second", 100); }, + [](){ return sleeper("wfof_third", 150); }); + } catch ( std::exception const& e) { + thrown = e.what(); + } + std::cout << "wait_first_outcome(fail) threw '" << thrown + << "'" << std::endl; + assert(thrown == "wfof_first"); +//] +}); + +/***************************************************************************** +* when_any, collect exceptions until success; throw exception_list if no +* success +*****************************************************************************/ +// define an exception to aggregate exception_ptrs; prefer +// std::exception_list (N4407 et al.) once that becomes available +//[exception_list +class exception_list : public std::runtime_error { +public: + exception_list( std::string const& what) : + std::runtime_error( what) { + } + + typedef std::vector< std::exception_ptr > bundle_t; + + // N4407 proposed std::exception_list API + typedef bundle_t::const_iterator iterator; + + std::size_t size() const noexcept { + return bundle_.size(); + } + + iterator begin() const noexcept { + return bundle_.begin(); + } + + iterator end() const noexcept { + return bundle_.end(); + } + + // extension to populate + void add( std::exception_ptr ep) { + bundle_.push_back( ep); + } + +private: + bundle_t bundle_; +}; +//] + +// Assume that all passed functions have the same return type. The return type +// of wait_first_success() is the return type of the first passed function. It is +// simply invalid to pass NO functions. +//[wait_first_success +template< typename Fn, typename ... Fns > +typename std::result_of< Fn() >::type +wait_first_success( Fn && function, Fns && ... functions) { + std::size_t count( 1 + sizeof ... ( functions) ); + // In this case, the value we pass through the channel is actually a + // future -- which is already ready. future can carry either a value or an + // exception. + typedef typename std::result_of< typename std::decay< Fn >::type() >::type return_t; + typedef boost::fibers::future< return_t > future_t; + typedef boost::fibers::buffered_channel< future_t > channel_t; + auto chanp( std::make_shared< channel_t >( 64) ); + // launch all the relevant fibers + wait_first_outcome_impl< return_t >( chanp, + std::forward< Fn >( function), + std::forward< Fns >( functions) ... ); + // instantiate exception_list, just in case + exception_list exceptions("wait_first_success() produced only errors"); + // retrieve up to 'count' results -- but stop there! + for ( std::size_t i = 0; i < count; ++i) { + // retrieve the next future + future_t future( chanp->value_pop() ); + // retrieve exception_ptr if any + std::exception_ptr error( future.get_exception_ptr() ); + // if no error, then yay, return value + if ( ! error) { + // close the channel: no subsequent push() has to succeed + chanp->close(); + // show caller the value we got + return future.get(); + } + + // error is non-null: collect + exceptions.add( error); + } + // We only arrive here when every passed function threw an exception. + // Throw our collection to inform caller. + throw exceptions; +} +//] + +// example usage +Example wfss( runner, "wait_first_success()", [](){ +//[wait_first_success_ex + std::string result = wait_first_success( + [](){ return sleeper("wfss_first", 50, true); }, + [](){ return sleeper("wfss_second", 100); }, + [](){ return sleeper("wfss_third", 150); }); + std::cout << "wait_first_success(success) => " << result << std::endl; + assert(result == "wfss_second"); +//] + + std::string thrown; + std::size_t count = 0; + try { + result = wait_first_success( + [](){ return sleeper("wfsf_first", 50, true); }, + [](){ return sleeper("wfsf_second", 100, true); }, + [](){ return sleeper("wfsf_third", 150, true); }); + } catch ( exception_list const& e) { + thrown = e.what(); + count = e.size(); + } catch ( std::exception const& e) { + thrown = e.what(); + } + std::cout << "wait_first_success(fail) threw '" << thrown << "': " + << count << " errors" << std::endl; + assert(thrown == "wait_first_success() produced only errors"); + assert(count == 3); +}); + +/***************************************************************************** +* when_any, heterogeneous +*****************************************************************************/ +//[wait_first_value_het +// No need to break out the first Fn for interface function: let the compiler +// complain if empty. +// Our functions have different return types, and we might have to return any +// of them. Use a variant, expanding std::result_of<Fn()>::type for each Fn in +// parameter pack. +template< typename ... Fns > +boost::variant< typename std::result_of< Fns() >::type ... > +wait_first_value_het( Fns && ... functions) { + // Use buffered_channel<boost::variant<T1, T2, ...>>; see remarks above. + typedef boost::variant< typename std::result_of< Fns() >::type ... > return_t; + typedef boost::fibers::buffered_channel< return_t > channel_t; + auto chanp( std::make_shared< channel_t >( 64) ); + // launch all the relevant fibers + wait_first_value_impl< return_t >( chanp, + std::forward< Fns >( functions) ... ); + // retrieve the first value + return_t value( chanp->value_pop() ); + // close the channel: no subsequent push() has to succeed + chanp->close(); + return value; +} +//] + +// example usage +Example wfvh( runner, "wait_first_value_het()", [](){ +//[wait_first_value_het_ex + boost::variant< std::string, double, int > result = + wait_first_value_het( + [](){ return sleeper("wfvh_third", 150); }, + [](){ return sleeper(3.14, 100); }, + [](){ return sleeper(17, 50); }); + std::cout << "wait_first_value_het() => " << result << std::endl; + assert(boost::get< int >( result) == 17); +//] +}); + +/***************************************************************************** +* when_all, simple completion +*****************************************************************************/ +// Degenerate case: when there are no functions to wait for, return +// immediately. +void wait_all_simple_impl( std::shared_ptr< boost::fibers::barrier >) { +} + +// When there's at least one function to wait for, launch it and recur to +// process the rest. +//[wait_all_simple_impl +template< typename Fn, typename ... Fns > +void wait_all_simple_impl( std::shared_ptr< boost::fibers::barrier > barrier, + Fn && function, Fns && ... functions) { + boost::fibers::fiber( + std::bind( + []( std::shared_ptr< boost::fibers::barrier > & barrier, + typename std::decay< Fn >::type & function) mutable { + function(); + barrier->wait(); + }, + barrier, + std::forward< Fn >( function) + )).detach(); + wait_all_simple_impl( barrier, std::forward< Fns >( functions) ... ); +} +//] + +// interface function: instantiate barrier, launch tasks, wait for barrier +//[wait_all_simple +template< typename ... Fns > +void wait_all_simple( Fns && ... functions) { + std::size_t count( sizeof ... ( functions) ); + // Initialize a barrier(count+1) because we'll immediately wait on it. We + // don't want to wake up until 'count' more fibers wait on it. Even though + // we'll stick around until the last of them completes, use shared_ptr + // anyway because it's easier to be confident about lifespan issues. + auto barrier( std::make_shared< boost::fibers::barrier >( count + 1) ); + wait_all_simple_impl( barrier, std::forward< Fns >( functions) ... ); + barrier->wait(); +} +//] + +// example usage +Example was( runner, "wait_all_simple()", [](){ +//[wait_all_simple_ex + wait_all_simple( + [](){ sleeper("was_long", 150); }, + [](){ sleeper("was_medium", 100); }, + [](){ sleeper("was_short", 50); }); +//] +}); + +/***************************************************************************** +* when_all, return values +*****************************************************************************/ +//[wait_nchannel +// Introduce a channel facade that closes the channel once a specific number +// of items has been pushed. This allows an arbitrary consumer to read until +// 'closed' without itself having to count items. +template< typename T > +class nchannel { +public: + nchannel( std::shared_ptr< boost::fibers::buffered_channel< T > > chan, + std::size_t lm): + chan_( chan), + limit_( lm) { + assert(chan_); + if ( 0 == limit_) { + chan_->close(); + } + } + + boost::fibers::channel_op_status push( T && va) { + boost::fibers::channel_op_status ok = + chan_->push( std::forward< T >( va) ); + if ( ok == boost::fibers::channel_op_status::success && + --limit_ == 0) { + // after the 'limit_'th successful push, close the channel + chan_->close(); + } + return ok; + } + +private: + std::shared_ptr< boost::fibers::buffered_channel< T > > chan_; + std::size_t limit_; +}; +//] + +// When there's only one function, call this overload +//[wait_all_values_impl +template< typename T, typename Fn > +void wait_all_values_impl( std::shared_ptr< nchannel< T > > chan, + Fn && function) { + boost::fibers::fiber( [chan, function](){ + chan->push(function()); + }).detach(); +} +//] + +// When there are two or more functions, call this overload +template< typename T, typename Fn0, typename Fn1, typename ... Fns > +void wait_all_values_impl( std::shared_ptr< nchannel< T > > chan, + Fn0 && function0, + Fn1 && function1, + Fns && ... functions) { + // process the first function using the single-function overload + wait_all_values_impl< T >( chan, std::forward< Fn0 >( function0) ); + // then recur to process the rest + wait_all_values_impl< T >( chan, + std::forward< Fn1 >( function1), + std::forward< Fns >( functions) ... ); +} + +//[wait_all_values_source +// Return a shared_ptr<buffered_channel<T>> from which the caller can +// retrieve each new result as it arrives, until 'closed'. +template< typename Fn, typename ... Fns > +std::shared_ptr< boost::fibers::buffered_channel< typename std::result_of< Fn() >::type > > +wait_all_values_source( Fn && function, Fns && ... functions) { + std::size_t count( 1 + sizeof ... ( functions) ); + typedef typename std::result_of< Fn() >::type return_t; + typedef boost::fibers::buffered_channel< return_t > channel_t; + // make the channel + auto chanp( std::make_shared< channel_t >( 64) ); + // and make an nchannel facade to close it after 'count' items + auto ncp( std::make_shared< nchannel< return_t > >( chanp, count) ); + // pass that nchannel facade to all the relevant fibers + wait_all_values_impl< return_t >( ncp, + std::forward< Fn >( function), + std::forward< Fns >( functions) ... ); + // then return the channel for consumer + return chanp; +} +//] + +// When all passed functions have completed, return vector<T> containing +// collected results. Assume that all passed functions have the same return +// type. It is simply invalid to pass NO functions. +//[wait_all_values +template< typename Fn, typename ... Fns > +std::vector< typename std::result_of< Fn() >::type > +wait_all_values( Fn && function, Fns && ... functions) { + std::size_t count( 1 + sizeof ... ( functions) ); + typedef typename std::result_of< Fn() >::type return_t; + typedef std::vector< return_t > vector_t; + vector_t results; + results.reserve( count); + + // get channel + std::shared_ptr< boost::fibers::buffered_channel< return_t > > chan = + wait_all_values_source( std::forward< Fn >( function), + std::forward< Fns >( functions) ... ); + // fill results vector + return_t value; + while ( boost::fibers::channel_op_status::success == chan->pop(value) ) { + results.push_back( value); + } + // return vector to caller + return results; +} +//] + +Example wav( runner, "wait_all_values()", [](){ +//[wait_all_values_source_ex + std::shared_ptr< boost::fibers::buffered_channel< std::string > > chan = + wait_all_values_source( + [](){ return sleeper("wavs_third", 150); }, + [](){ return sleeper("wavs_second", 100); }, + [](){ return sleeper("wavs_first", 50); }); + std::string value; + while ( boost::fibers::channel_op_status::success == chan->pop(value) ) { + std::cout << "wait_all_values_source() => '" << value + << "'" << std::endl; + } +//] + +//[wait_all_values_ex + std::vector< std::string > values = + wait_all_values( + [](){ return sleeper("wav_late", 150); }, + [](){ return sleeper("wav_middle", 100); }, + [](){ return sleeper("wav_early", 50); }); +//] + std::cout << "wait_all_values() =>"; + for ( std::string const& v : values) { + std::cout << " '" << v << "'"; + } + std::cout << std::endl; +}); + +/***************************************************************************** +* when_all, throw first exception +*****************************************************************************/ +//[wait_all_until_error_source +// Return a shared_ptr<buffered_channel<future<T>>> from which the caller can +// get() each new result as it arrives, until 'closed'. +template< typename Fn, typename ... Fns > +std::shared_ptr< + boost::fibers::buffered_channel< + boost::fibers::future< + typename std::result_of< Fn() >::type > > > +wait_all_until_error_source( Fn && function, Fns && ... functions) { + std::size_t count( 1 + sizeof ... ( functions) ); + typedef typename std::result_of< Fn() >::type return_t; + typedef boost::fibers::future< return_t > future_t; + typedef boost::fibers::buffered_channel< future_t > channel_t; + // make the channel + auto chanp( std::make_shared< channel_t >( 64) ); + // and make an nchannel facade to close it after 'count' items + auto ncp( std::make_shared< nchannel< future_t > >( chanp, count) ); + // pass that nchannel facade to all the relevant fibers + wait_first_outcome_impl< return_t >( ncp, + std::forward< Fn >( function), + std::forward< Fns >( functions) ... ); + // then return the channel for consumer + return chanp; +} +//] + +// When all passed functions have completed, return vector<T> containing +// collected results, or throw the first exception thrown by any of the passed +// functions. Assume that all passed functions have the same return type. It +// is simply invalid to pass NO functions. +//[wait_all_until_error +template< typename Fn, typename ... Fns > +std::vector< typename std::result_of< Fn() >::type > +wait_all_until_error( Fn && function, Fns && ... functions) { + std::size_t count( 1 + sizeof ... ( functions) ); + typedef typename std::result_of< Fn() >::type return_t; + typedef typename boost::fibers::future< return_t > future_t; + typedef std::vector< return_t > vector_t; + vector_t results; + results.reserve( count); + + // get channel + std::shared_ptr< + boost::fibers::buffered_channel< future_t > > chan( + wait_all_until_error_source( std::forward< Fn >( function), + std::forward< Fns >( functions) ... ) ); + // fill results vector + future_t future; + while ( boost::fibers::channel_op_status::success == chan->pop( future) ) { + results.push_back( future.get() ); + } + // return vector to caller + return results; +} +//] + +Example waue( runner, "wait_all_until_error()", [](){ +//[wait_all_until_error_source_ex + typedef boost::fibers::future< std::string > future_t; + std::shared_ptr< boost::fibers::buffered_channel< future_t > > chan = + wait_all_until_error_source( + [](){ return sleeper("wauess_third", 150); }, + [](){ return sleeper("wauess_second", 100); }, + [](){ return sleeper("wauess_first", 50); }); + future_t future; + while ( boost::fibers::channel_op_status::success == chan->pop( future) ) { + std::string value( future.get() ); + std::cout << "wait_all_until_error_source(success) => '" << value + << "'" << std::endl; + } +//] + + chan = wait_all_until_error_source( + [](){ return sleeper("wauesf_third", 150); }, + [](){ return sleeper("wauesf_second", 100, true); }, + [](){ return sleeper("wauesf_first", 50); }); +//[wait_all_until_error_ex + std::string thrown; +//<- + try { + while ( boost::fibers::channel_op_status::success == chan->pop( future) ) { + std::string value( future.get() ); + std::cout << "wait_all_until_error_source(fail) => '" << value + << "'" << std::endl; + } + } catch ( std::exception const& e) { + thrown = e.what(); + } + std::cout << "wait_all_until_error_source(fail) threw '" << thrown + << "'" << std::endl; + + thrown.clear(); +//-> + try { + std::vector< std::string > values = wait_all_until_error( + [](){ return sleeper("waue_late", 150); }, + [](){ return sleeper("waue_middle", 100, true); }, + [](){ return sleeper("waue_early", 50); }); +//<- + std::cout << "wait_all_until_error(fail) =>"; + for ( std::string const& v : values) { + std::cout << " '" << v << "'"; + } + std::cout << std::endl; +//-> + } catch ( std::exception const& e) { + thrown = e.what(); + } + std::cout << "wait_all_until_error(fail) threw '" << thrown + << "'" << std::endl; +//] +}); + +/***************************************************************************** +* when_all, collect exceptions +*****************************************************************************/ +// When all passed functions have succeeded, return vector<T> containing +// collected results, or throw exception_list containing all exceptions thrown +// by any of the passed functions. Assume that all passed functions have the +// same return type. It is simply invalid to pass NO functions. +//[wait_all_collect_errors +template< typename Fn, typename ... Fns > +std::vector< typename std::result_of< Fn() >::type > +wait_all_collect_errors( Fn && function, Fns && ... functions) { + std::size_t count( 1 + sizeof ... ( functions) ); + typedef typename std::result_of< Fn() >::type return_t; + typedef typename boost::fibers::future< return_t > future_t; + typedef std::vector< return_t > vector_t; + vector_t results; + results.reserve( count); + exception_list exceptions("wait_all_collect_errors() exceptions"); + + // get channel + std::shared_ptr< + boost::fibers::buffered_channel< future_t > > chan( + wait_all_until_error_source( std::forward< Fn >( function), + std::forward< Fns >( functions) ... ) ); + // fill results and/or exceptions vectors + future_t future; + while ( boost::fibers::channel_op_status::success == chan->pop( future) ) { + std::exception_ptr exp = future.get_exception_ptr(); + if ( ! exp) { + results.push_back( future.get() ); + } else { + exceptions.add( exp); + } + } + // if there were any exceptions, throw + if ( exceptions.size() ) { + throw exceptions; + } + // no exceptions: return vector to caller + return results; +} +//] + +Example wace( runner, "wait_all_collect_errors()", [](){ + std::vector< std::string > values = wait_all_collect_errors( + [](){ return sleeper("waces_late", 150); }, + [](){ return sleeper("waces_middle", 100); }, + [](){ return sleeper("waces_early", 50); }); + std::cout << "wait_all_collect_errors(success) =>"; + for ( std::string const& v : values) { + std::cout << " '" << v << "'"; + } + std::cout << std::endl; + + std::string thrown; + std::size_t errors = 0; + try { + values = wait_all_collect_errors( + [](){ return sleeper("wacef_late", 150, true); }, + [](){ return sleeper("wacef_middle", 100, true); }, + [](){ return sleeper("wacef_early", 50); }); + std::cout << "wait_all_collect_errors(fail) =>"; + for ( std::string const& v : values) { + std::cout << " '" << v << "'"; + } + std::cout << std::endl; + } catch ( exception_list const& e) { + thrown = e.what(); + errors = e.size(); + } catch ( std::exception const& e) { + thrown = e.what(); + } + std::cout << "wait_all_collect_errors(fail) threw '" << thrown + << "': " << errors << " errors" << std::endl; +}); + +/***************************************************************************** +* when_all, heterogeneous +*****************************************************************************/ +//[wait_all_members_get +template< typename Result, typename ... Futures > +Result wait_all_members_get( Futures && ... futures) { + // Fetch the results from the passed futures into Result's initializer + // list. It's true that the get() calls here will block the implicit + // iteration over futures -- but that doesn't matter because we won't be + // done until the slowest of them finishes anyway. As results are + // processed in argument-list order rather than order of completion, the + // leftmost get() to throw an exception will cause that exception to + // propagate to the caller. + return Result{ futures.get() ... }; +} +//] + +//[wait_all_members +// Explicitly pass Result. This can be any type capable of being initialized +// from the results of the passed functions, such as a struct. +template< typename Result, typename ... Fns > +Result wait_all_members( Fns && ... functions) { + // Run each of the passed functions on a separate fiber, passing all their + // futures to helper function for processing. + return wait_all_members_get< Result >( + boost::fibers::async( std::forward< Fns >( functions) ) ... ); +} +//] + +// used by following example +//[wait_Data +struct Data { + std::string str; + double inexact; + int exact; + + friend std::ostream& operator<<( std::ostream& out, Data const& data)/*=; + ...*/ +//<- + { + return out << "Data{str='" << data.str << "', inexact=" << data.inexact + << ", exact=" << data.exact << "}"; + } +//-> +}; +//] + +// example usage +Example wam( runner, "wait_all_members()", [](){ +//[wait_all_members_data_ex + Data data = wait_all_members< Data >( + [](){ return sleeper("wams_left", 100); }, + [](){ return sleeper(3.14, 150); }, + [](){ return sleeper(17, 50); }); + std::cout << "wait_all_members<Data>(success) => " << data << std::endl; +//] + + std::string thrown; + try { + data = wait_all_members< Data >( + [](){ return sleeper("wamf_left", 100, true); }, + [](){ return sleeper(3.14, 150); }, + [](){ return sleeper(17, 50, true); }); + std::cout << "wait_all_members<Data>(fail) => " << data << std::endl; + } catch ( std::exception const& e) { + thrown = e.what(); + } + std::cout << "wait_all_members<Data>(fail) threw '" << thrown + << '"' << std::endl; + +//[wait_all_members_vector_ex + // If we don't care about obtaining results as soon as they arrive, and we + // prefer a result vector in passed argument order rather than completion + // order, wait_all_members() is another possible implementation of + // wait_all_until_error(). + auto strings = wait_all_members< std::vector< std::string > >( + [](){ return sleeper("wamv_left", 150); }, + [](){ return sleeper("wamv_middle", 100); }, + [](){ return sleeper("wamv_right", 50); }); + std::cout << "wait_all_members<vector>() =>"; + for ( std::string const& str : strings) { + std::cout << " '" << str << "'"; + } + std::cout << std::endl; +//] +}); + + +/***************************************************************************** +* main() +*****************************************************************************/ +int main( int argc, char *argv[]) { + runner.run(); + std::cout << "done." << std::endl; + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/fiber/examples/work_sharing.cpp b/src/boost/libs/fiber/examples/work_sharing.cpp new file mode 100644 index 00000000..f5e31e51 --- /dev/null +++ b/src/boost/libs/fiber/examples/work_sharing.cpp @@ -0,0 +1,130 @@ +// Copyright Nat Goodspeed + Oliver Kowalke 2015. +// 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 <chrono> +#include <condition_variable> +#include <cstddef> +#include <deque> +#include <iomanip> +#include <iostream> +#include <mutex> +#include <sstream> +#include <string> +#include <thread> + +#include <boost/assert.hpp> + +#include <boost/fiber/all.hpp> + +#include <boost/fiber/detail/thread_barrier.hpp> + +static std::size_t fiber_count{ 0 }; +static std::mutex mtx_count{}; +static boost::fibers::condition_variable_any cnd_count{}; +typedef std::unique_lock< std::mutex > lock_type; + +/***************************************************************************** +* example fiber function +*****************************************************************************/ +//[fiber_fn_ws +void whatevah( char me) { + try { + std::thread::id my_thread = std::this_thread::get_id(); /*< get ID of initial thread >*/ + { + std::ostringstream buffer; + buffer << "fiber " << me << " started on thread " << my_thread << '\n'; + std::cout << buffer.str() << std::flush; + } + for ( unsigned i = 0; i < 10; ++i) { /*< loop ten times >*/ + boost::this_fiber::yield(); /*< yield to other fibers >*/ + std::thread::id new_thread = std::this_thread::get_id(); /*< get ID of current thread >*/ + if ( new_thread != my_thread) { /*< test if fiber was migrated to another thread >*/ + my_thread = new_thread; + std::ostringstream buffer; + buffer << "fiber " << me << " switched to thread " << my_thread << '\n'; + std::cout << buffer.str() << std::flush; + } + } + } catch ( ... ) { + } + lock_type lk( mtx_count); + if ( 0 == --fiber_count) { /*< Decrement fiber counter for each completed fiber. >*/ + lk.unlock(); + cnd_count.notify_all(); /*< Notify all fibers waiting on `cnd_count`. >*/ + } +} +//] + +/***************************************************************************** +* example thread function +*****************************************************************************/ +//[thread_fn_ws +void thread( boost::fibers::detail::thread_barrier * b) { + std::ostringstream buffer; + buffer << "thread started " << std::this_thread::get_id() << std::endl; + std::cout << buffer.str() << std::flush; + boost::fibers::use_scheduling_algorithm< boost::fibers::algo::shared_work >(); /*< + Install the scheduling algorithm `boost::fibers::algo::shared_work` in order to + join the work sharing. + >*/ + b->wait(); /*< sync with other threads: allow them to start processing >*/ + lock_type lk( mtx_count); + cnd_count.wait( lk, [](){ return 0 == fiber_count; } ); /*< + Suspend main fiber and resume worker fibers in the meanwhile. + Main fiber gets resumed (e.g returns from `condition_variable_any::wait()`) + if all worker fibers are complete. + >*/ + BOOST_ASSERT( 0 == fiber_count); +} +//] + +/***************************************************************************** +* main() +*****************************************************************************/ +int main( int argc, char *argv[]) { + std::cout << "main thread started " << std::this_thread::get_id() << std::endl; +//[main_ws + boost::fibers::use_scheduling_algorithm< boost::fibers::algo::shared_work >(); /*< + Install the scheduling algorithm `boost::fibers::algo::shared_work` in the main thread + too, so each new fiber gets launched into the shared pool. + >*/ + + for ( char c : std::string("abcdefghijklmnopqrstuvwxyz")) { /*< + Launch a number of worker fibers; each worker fiber picks up a character + that is passed as parameter to fiber-function `whatevah`. + Each worker fiber gets detached. + >*/ + boost::fibers::fiber([c](){ whatevah( c); }).detach(); + ++fiber_count; /*< Increment fiber counter for each new fiber. >*/ + } + boost::fibers::detail::thread_barrier b( 4); + std::thread threads[] = { /*< + Launch a couple of threads that join the work sharing. + >*/ + std::thread( thread, & b), + std::thread( thread, & b), + std::thread( thread, & b) + }; + b.wait(); /*< sync with other threads: allow them to start processing >*/ + { + lock_type/*< `lock_type` is typedef'ed as __unique_lock__< [@http://en.cppreference.com/w/cpp/thread/mutex `std::mutex`] > >*/ lk( mtx_count); + cnd_count.wait( lk, [](){ return 0 == fiber_count; } ); /*< + Suspend main fiber and resume worker fibers in the meanwhile. + Main fiber gets resumed (e.g returns from `condition_variable_any::wait()`) + if all worker fibers are complete. + >*/ + } /*< + Releasing lock of mtx_count is required before joining the threads, otherwise + the other threads would be blocked inside condition_variable::wait() and + would never return (deadlock). + >*/ + BOOST_ASSERT( 0 == fiber_count); + for ( std::thread & t : threads) { /*< wait for threads to terminate >*/ + t.join(); + } +//] + std::cout << "done." << std::endl; + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/fiber/examples/work_stealing.cpp b/src/boost/libs/fiber/examples/work_stealing.cpp new file mode 100644 index 00000000..42c99491 --- /dev/null +++ b/src/boost/libs/fiber/examples/work_stealing.cpp @@ -0,0 +1,126 @@ +// Copyright Nat Goodspeed + Oliver Kowalke 2015. +// 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 <chrono> +#include <condition_variable> +#include <cstddef> +#include <deque> +#include <iomanip> +#include <iostream> +#include <mutex> +#include <sstream> +#include <string> +#include <thread> + +#include <boost/assert.hpp> + +#include <boost/fiber/all.hpp> + +#include <boost/fiber/detail/thread_barrier.hpp> + +static std::size_t fiber_count{ 0 }; +static std::mutex mtx_count{}; +static boost::fibers::condition_variable_any cnd_count{}; +typedef std::unique_lock< std::mutex > lock_type; + +/***************************************************************************** +* example fiber function +*****************************************************************************/ +//[fiber_fn_ws +void whatevah( char me) { + try { + std::thread::id my_thread = std::this_thread::get_id(); /*< get ID of initial thread >*/ + { + std::ostringstream buffer; + buffer << "fiber " << me << " started on thread " << my_thread << '\n'; + std::cout << buffer.str() << std::flush; + } + for ( unsigned i = 0; i < 10; ++i) { /*< loop ten times >*/ + boost::this_fiber::yield(); /*< yield to other fibers >*/ + std::thread::id new_thread = std::this_thread::get_id(); /*< get ID of current thread >*/ + if ( new_thread != my_thread) { /*< test if fiber was migrated to another thread >*/ + my_thread = new_thread; + std::ostringstream buffer; + buffer << "fiber " << me << " switched to thread " << my_thread << '\n'; + std::cout << buffer.str() << std::flush; + } + } + } catch ( ... ) { + } + lock_type lk( mtx_count); + if ( 0 == --fiber_count) { /*< Decrement fiber counter for each completed fiber. >*/ + lk.unlock(); + cnd_count.notify_all(); /*< Notify all fibers waiting on `cnd_count`. >*/ + } +} +//] + +/***************************************************************************** +* example thread function +*****************************************************************************/ +//[thread_fn_ws +void thread() { + std::ostringstream buffer; + buffer << "thread started " << std::this_thread::get_id() << std::endl; + std::cout << buffer.str() << std::flush; + boost::fibers::use_scheduling_algorithm< boost::fibers::algo::work_stealing >( 4); /*< + Install the scheduling algorithm `boost::fibers::algo::work_stealing` in order to + join the work sharing. + >*/ + lock_type lk( mtx_count); + cnd_count.wait( lk, [](){ return 0 == fiber_count; } ); /*< + Suspend main fiber and resume worker fibers in the meanwhile. + Main fiber gets resumed (e.g returns from `condition_variable_any::wait()`) + if all worker fibers are complete. + >*/ + BOOST_ASSERT( 0 == fiber_count); +} +//] + +/***************************************************************************** +* main() +*****************************************************************************/ +int main( int argc, char *argv[]) { + std::cout << "main thread started " << std::this_thread::get_id() << std::endl; +//[main_ws + for ( char c : std::string("abcdefghijklmnopqrstuvwxyz")) { /*< + Launch a number of worker fibers; each worker fiber picks up a character + that is passed as parameter to fiber-function `whatevah`. + Each worker fiber gets detached. + >*/ + boost::fibers::fiber([c](){ whatevah( c); }).detach(); + ++fiber_count; /*< Increment fiber counter for each new fiber. >*/ + } + std::thread threads[] = { /*< + Launch a couple of threads that join the work sharing. + >*/ + std::thread( thread), + std::thread( thread), + std::thread( thread) + }; + boost::fibers::use_scheduling_algorithm< boost::fibers::algo::work_stealing >( 4); /*< + Install the scheduling algorithm `boost::fibers::algo::work_stealing` in the main thread + too, so each new fiber gets launched into the shared pool. + >*/ + { + lock_type/*< `lock_type` is typedef'ed as __unique_lock__< [@http://en.cppreference.com/w/cpp/thread/mutex `std::mutex`] > >*/ lk( mtx_count); + cnd_count.wait( lk, [](){ return 0 == fiber_count; } ); /*< + Suspend main fiber and resume worker fibers in the meanwhile. + Main fiber gets resumed (e.g returns from `condition_variable_any::wait()`) + if all worker fibers are complete. + >*/ + } /*< + Releasing lock of mtx_count is required before joining the threads, otherwise + the other threads would be blocked inside condition_variable::wait() and + would never return (deadlock). + >*/ + BOOST_ASSERT( 0 == fiber_count); + for ( std::thread & t : threads) { /*< wait for threads to terminate >*/ + t.join(); + } +//] + std::cout << "done." << std::endl; + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/fiber/index.html b/src/boost/libs/fiber/index.html new file mode 100644 index 00000000..0ade6cbd --- /dev/null +++ b/src/boost/libs/fiber/index.html @@ -0,0 +1,14 @@ +<html> +<head> +<meta http-equiv="refresh" content="0; URL=doc/html/index.html"> +</head> +<body> +Automatic redirection failed, please go to +<a href="doc/html/index.html">doc/html/index.html</a> +<hr> +<p>© Copyright Beman Dawes, 2001</p> +<p> Distributed under the Boost Software +License, Version 1.0. (See accompanying file <a href="../../LICENSE_1_0.txt">LICENSE_1_0.txt</a> or copy at <a href="http://www.boost.org/LICENSE_1_0.txt"> +www.boost.org/LICENSE_1_0.txt</a>)</p> +</body> +</html> diff --git a/src/boost/libs/fiber/meta/libraries.json b/src/boost/libs/fiber/meta/libraries.json new file mode 100644 index 00000000..3389840e --- /dev/null +++ b/src/boost/libs/fiber/meta/libraries.json @@ -0,0 +1,15 @@ +{ + "key": "fiber", + "name": "Fiber", + "authors": [ + "Oliver Kowalke" + ], + "description": "(C++11) Userland threads library.", + "category": [ + "Concurrent", + "System" + ], + "maintainers": [ + "Oliver Kowalke <oliver.kowalke -at- gmail.com>" + ] +} diff --git a/src/boost/libs/fiber/performance/clock.hpp b/src/boost/libs/fiber/performance/clock.hpp new file mode 100644 index 00000000..00a1bb18 --- /dev/null +++ b/src/boost/libs/fiber/performance/clock.hpp @@ -0,0 +1,44 @@ + +// 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) +// +#ifndef CLOCK_H +#define CLOCK_H + +#include <algorithm> +#include <chrono> +#include <cstddef> +#include <cstdint> +#include <numeric> +#include <vector> + +#include <boost/assert.hpp> + +typedef std::chrono::steady_clock clock_type; +typedef clock_type::duration duration_type; +typedef clock_type::time_point time_point_type; + +struct clock_overhead +{ + std::uint64_t operator()() + { + time_point_type start( clock_type::now() ); + return ( clock_type::now() - start).count(); + } +}; + +duration_type overhead_clock() +{ + std::size_t iterations( 10); + std::vector< std::uint64_t > overhead( iterations, 0); + for ( std::size_t i = 0; i < iterations; ++i) + std::generate( + overhead.begin(), overhead.end(), + clock_overhead() ); + BOOST_ASSERT( overhead.begin() != overhead.end() ); + return duration_type( std::accumulate( overhead.begin(), overhead.end(), 0) / iterations); +} + +#endif // CLOCK_H diff --git a/src/boost/libs/fiber/performance/fiber/Jamfile.v2 b/src/boost/libs/fiber/performance/fiber/Jamfile.v2 new file mode 100644 index 00000000..7de178db --- /dev/null +++ b/src/boost/libs/fiber/performance/fiber/Jamfile.v2 @@ -0,0 +1,52 @@ + +# 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) + +# For more information, see http://www.boost.org/ + +import common ; +import feature ; +import indirect ; +import modules ; +import os ; +import toolset ; + +project boost/fiber/performance/fiber + : requirements + <library>/boost/fiber//boost_fiber + <library>/boost/fiber//boost_fiber_numa + <target-os>solaris:<linkflags>"-llgrp" + <target-os>windows:<define>_WIN32_WINNT=0x0601 + <toolset>gcc,<segmented-stacks>on:<cxxflags>-fsplit-stack + <toolset>gcc,<segmented-stacks>on:<cxxflags>-DBOOST_USE_SEGMENTED_STACKS + <toolset>clang,<segmented-stacks>on:<cxxflags>-fsplit-stack + <toolset>clang,<segmented-stacks>on:<cxxflags>-DBOOST_USE_SEGMENTED_STACKS + <link>static + <numa>on + <threading>multi + <optimization>speed + <variant>release + ; + +exe skynet_join : + skynet_join.cpp ; + +exe skynet_detach : + skynet_detach.cpp ; + +exe skynet_shared_join : + skynet_shared_join.cpp ; + +exe skynet_shared_detach : + skynet_shared_detach.cpp ; + +exe skynet_stealing_join : + skynet_stealing_join.cpp ; + +exe skynet_stealing_detach : + skynet_stealing_detach.cpp ; + +exe skynet_stealing_async : + skynet_stealing_async.cpp ; diff --git a/src/boost/libs/fiber/performance/fiber/barrier.hpp b/src/boost/libs/fiber/performance/fiber/barrier.hpp new file mode 100644 index 00000000..dcbd7e98 --- /dev/null +++ b/src/boost/libs/fiber/performance/fiber/barrier.hpp @@ -0,0 +1,50 @@ + +// Copyright Oliver Kowalke 2013. +// 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) + +#ifndef BARRIER_H +#define BARRIER_H + +#include <cstddef> +#include <condition_variable> +#include <mutex> + +#include <boost/assert.hpp> + +class barrier { +private: + std::size_t initial_; + std::size_t current_; + bool cycle_{ true }; + std::mutex mtx_{}; + std::condition_variable cond_{}; + +public: + explicit barrier( std::size_t initial) : + initial_{ initial }, + current_{ initial_ } { + BOOST_ASSERT ( 0 != initial); + } + + barrier( barrier const&) = delete; + barrier & operator=( barrier const&) = delete; + + bool wait() { + std::unique_lock< std::mutex > lk( mtx_); + const bool cycle = cycle_; + if ( 0 == --current_) { + cycle_ = ! cycle_; + current_ = initial_; + lk.unlock(); // no pessimization + cond_.notify_all(); + return true; + } else { + cond_.wait( lk, [&](){ return cycle != cycle_; }); + } + return false; + } +}; + +#endif // BARRIER_H diff --git a/src/boost/libs/fiber/performance/fiber/numa/Jamfile.v2 b/src/boost/libs/fiber/performance/fiber/numa/Jamfile.v2 new file mode 100644 index 00000000..30f3a7f9 --- /dev/null +++ b/src/boost/libs/fiber/performance/fiber/numa/Jamfile.v2 @@ -0,0 +1,32 @@ + +# 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) + +# For more information, see http://www.boost.org/ + +import common ; +import feature ; +import indirect ; +import modules ; +import os ; +import toolset ; + +project boost/fiber/performance/fiber/numa + : requirements + <library>/boost/fiber//boost_fiber + <target-os>solaris:<linkflags>"-llgrp" + <target-os>windows:<define>_WIN32_WINNT=0x0601 + <toolset>gcc,<segmented-stacks>on:<cxxflags>-fsplit-stack + <toolset>gcc,<segmented-stacks>on:<cxxflags>-DBOOST_USE_SEGMENTED_STACKS + <toolset>clang,<segmented-stacks>on:<cxxflags>-fsplit-stack + <toolset>clang,<segmented-stacks>on:<cxxflags>-DBOOST_USE_SEGMENTED_STACKS + <link>static + <threading>multi + <optimization>speed + <variant>release + ; + +exe skynet_stealing_detach : + skynet_stealing_detach.cpp ; diff --git a/src/boost/libs/fiber/performance/fiber/numa/skynet_stealing_detach.cpp b/src/boost/libs/fiber/performance/fiber/numa/skynet_stealing_detach.cpp new file mode 100644 index 00000000..5d41b9c7 --- /dev/null +++ b/src/boost/libs/fiber/performance/fiber/numa/skynet_stealing_detach.cpp @@ -0,0 +1,121 @@ + +// Copyright Oliver Kowalke 2015. +// 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) + +// based on https://github.com/atemerev/skynet from Alexander Temerev + +#include <algorithm> +#include <cassert> +#include <chrono> +#include <cmath> +#include <condition_variable> +#include <cstddef> +#include <cstdint> +#include <cstdlib> +#include <queue> +#include <iostream> +#include <memory> +#include <mutex> +#include <numeric> +#include <random> +#include <sstream> +#include <vector> + +#include <boost/fiber/all.hpp> +#include <boost/fiber/numa/all.hpp> +#include <boost/predef.h> + +#include "../barrier.hpp" + +using clock_type = std::chrono::steady_clock; +using duration_type = clock_type::duration; +using time_point_type = clock_type::time_point; +using channel_type = boost::fibers::buffered_channel< std::uint64_t >; +using allocator_type = boost::fibers::fixedsize_stack; +using lock_type = std::unique_lock< std::mutex >; + +static bool done = false; +static std::mutex mtx{}; +static boost::fibers::condition_variable_any cnd{}; + +std::uint32_t hardware_concurrency( std::vector< boost::fibers::numa::node > const& topo) { + std::uint32_t cpus = 0; + for ( auto & node : topo) { + cpus += node.logical_cpus.size(); + } + return cpus; +} + +// microbenchmark +void skynet( allocator_type & salloc, channel_type & c, std::size_t num, std::size_t size, std::size_t div) { + if ( 1 == size) { + c.push( num); + } else { + channel_type rc{ 16 }; + for ( std::size_t i = 0; i < div; ++i) { + auto sub_num = num + i * size / div; + boost::fibers::fiber{ boost::fibers::launch::dispatch, + std::allocator_arg, salloc, + skynet, + std::ref( salloc), std::ref( rc), sub_num, size / div, div }.detach(); + } + std::uint64_t sum{ 0 }; + for ( std::size_t i = 0; i < div; ++i) { + sum += rc.value_pop(); + } + c.push( sum); + } +} + +void thread( std::uint32_t cpu_id, std::uint32_t node_id, std::vector< boost::fibers::numa::node > const& topo) { + boost::fibers::use_scheduling_algorithm< boost::fibers::numa::algo::work_stealing >( cpu_id, node_id, topo); + lock_type lk( mtx); + cnd.wait( lk, [](){ return done; }); + BOOST_ASSERT( done); +} + +int main() { + try { + std::vector< boost::fibers::numa::node > topo = boost::fibers::numa::topology(); + auto node = topo[0]; + auto main_cpu_id = * node.logical_cpus.begin(); + std::size_t size{ 1000000 }; + std::size_t div{ 10 }; + allocator_type salloc{ 2*allocator_type::traits_type::page_size() }; + std::uint64_t result{ 0 }; + channel_type rc{ 2 }; + std::vector< std::thread > threads; + for ( auto && node : topo) { + for ( std::uint32_t cpu_id : node.logical_cpus) { + // exclude main-thread + if ( main_cpu_id != cpu_id) { + threads.emplace_back( thread, cpu_id, node.id, std::cref( topo) ); + } + } + } + boost::fibers::use_scheduling_algorithm< boost::fibers::numa::algo::work_stealing >( main_cpu_id, node.id, topo); + time_point_type start{ clock_type::now() }; + skynet( salloc, rc, 0, size, div); + result = rc.value_pop(); + if ( 499999500000 != result) { + throw std::runtime_error("invalid result"); + } + auto duration = clock_type::now() - start; + lock_type lk( mtx); + done = true; + lk.unlock(); + cnd.notify_all(); + for ( std::thread & t : threads) { + t.join(); + } + std::cout << "duration: " << duration.count() / 1000000 << " ms" << std::endl; + return EXIT_SUCCESS; + } catch ( std::exception const& e) { + std::cerr << "exception: " << e.what() << std::endl; + } catch (...) { + std::cerr << "unhandled exception" << std::endl; + } + return EXIT_FAILURE; +} diff --git a/src/boost/libs/fiber/performance/fiber/skynet_detach.cpp b/src/boost/libs/fiber/performance/fiber/skynet_detach.cpp new file mode 100644 index 00000000..5e6d4052 --- /dev/null +++ b/src/boost/libs/fiber/performance/fiber/skynet_detach.cpp @@ -0,0 +1,80 @@ + +// Copyright Oliver Kowalke 2015. +// 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) + +// based on https://github.com/atemerev/skynet from Alexander Temerev + +#include <algorithm> +#include <cassert> +#include <chrono> +#include <cmath> +#include <cstddef> +#include <cstdint> +#include <cstdlib> +#include <iostream> +#include <memory> +#include <numeric> +#include <vector> + +#include <boost/fiber/all.hpp> +#include <boost/predef.h> + +using allocator_type = boost::fibers::fixedsize_stack; +using channel_type = boost::fibers::buffered_channel< std::uint64_t >; +using clock_type = std::chrono::steady_clock; +using duration_type = clock_type::duration; +using time_point_type = clock_type::time_point; + +// microbenchmark +void skynet( allocator_type & salloc, channel_type & c, std::size_t num, std::size_t size, std::size_t div) { + if ( 1 == size) { + c.push( num); + } else { + channel_type rc{ 16 }; + for ( std::size_t i = 0; i < div; ++i) { + auto sub_num = num + i * size / div; + boost::fibers::fiber{ boost::fibers::launch::dispatch, + std::allocator_arg, salloc, + skynet, + std::ref( salloc), std::ref( rc), sub_num, size / div, div }.detach(); + } + std::uint64_t sum{ 0 }; + for ( std::size_t i = 0; i < div; ++i) { + sum += rc.value_pop(); + } + c.push( sum); + } +} + +int main() { + try { + std::size_t size{ 1000000 }; + std::size_t div{ 10 }; + // Windows 10 and FreeBSD require a fiber stack of 8kb + // otherwise the stack gets exhausted + // stack requirements must be checked for other OS too +#if BOOST_OS_WINDOWS || BOOST_OS_BSD + allocator_type salloc{ 2*allocator_type::traits_type::page_size() }; +#else + allocator_type salloc{ allocator_type::traits_type::page_size() }; +#endif + std::uint64_t result{ 0 }; + channel_type rc{ 2 }; + time_point_type start{ clock_type::now() }; + skynet( salloc, rc, 0, size, div); + result = rc.value_pop(); + if ( 499999500000 != result) { + throw std::runtime_error("invalid result"); + } + auto duration = clock_type::now() - start; + std::cout << "duration: " << duration.count() / 1000000 << " ms" << std::endl; + return EXIT_SUCCESS; + } catch ( std::exception const& e) { + std::cerr << "exception: " << e.what() << std::endl; + } catch (...) { + std::cerr << "unhandled exception" << std::endl; + } + return EXIT_FAILURE; +} diff --git a/src/boost/libs/fiber/performance/fiber/skynet_join.cpp b/src/boost/libs/fiber/performance/fiber/skynet_join.cpp new file mode 100644 index 00000000..543ee03f --- /dev/null +++ b/src/boost/libs/fiber/performance/fiber/skynet_join.cpp @@ -0,0 +1,84 @@ + +// Copyright Oliver Kowalke 2015. +// 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) + +// based on https://github.com/atemerev/skynet from Alexander Temerev + +#include <algorithm> +#include <cassert> +#include <chrono> +#include <cmath> +#include <cstddef> +#include <cstdint> +#include <cstdlib> +#include <iostream> +#include <memory> +#include <numeric> +#include <vector> + +#include <boost/fiber/all.hpp> +#include <boost/predef.h> + +using allocator_type = boost::fibers::fixedsize_stack; +using channel_type = boost::fibers::buffered_channel< std::uint64_t >; +using clock_type = std::chrono::steady_clock; +using duration_type = clock_type::duration; +using time_point_type = clock_type::time_point; + +// microbenchmark +void skynet( allocator_type & salloc, channel_type & c, std::size_t num, std::size_t size, std::size_t div) { + if ( 1 == size) { + c.push( num); + } else { + channel_type rc{ 16 }; + std::vector< boost::fibers::fiber > fibers; + for ( std::size_t i = 0; i < div; ++i) { + auto sub_num = num + i * size / div; + fibers.emplace_back( boost::fibers::launch::dispatch, + std::allocator_arg, salloc, + skynet, + std::ref( salloc), std::ref( rc), sub_num, size / div, div); + } + for ( auto & f: fibers) { + f.join(); + } + std::uint64_t sum{ 0 }; + for ( std::size_t i = 0; i < div; ++i) { + sum += rc.value_pop(); + } + c.push( sum); + } +} + +int main() { + try { + std::size_t size{ 1000000 }; + std::size_t div{ 10 }; + // Windows 10 and FreeBSD require a fiber stack of 8kb + // otherwise the stack gets exhausted + // stack requirements must be checked for other OS too +#if BOOST_OS_WINDOWS || BOOST_OS_BSD + allocator_type salloc{ 2*allocator_type::traits_type::page_size() }; +#else + allocator_type salloc{ allocator_type::traits_type::page_size() }; +#endif + std::uint64_t result{ 0 }; + channel_type rc{ 2 }; + time_point_type start{ clock_type::now() }; + skynet( salloc, rc, 0, size, div); + result = rc.value_pop(); + if ( 499999500000 != result) { + throw std::runtime_error("invalid result"); + } + auto duration = clock_type::now() - start; + std::cout << "duration: " << duration.count() / 1000000 << " ms" << std::endl; + return EXIT_SUCCESS; + } catch ( std::exception const& e) { + std::cerr << "exception: " << e.what() << std::endl; + } catch (...) { + std::cerr << "unhandled exception" << std::endl; + } + return EXIT_FAILURE; +} diff --git a/src/boost/libs/fiber/performance/fiber/skynet_shared_detach.cpp b/src/boost/libs/fiber/performance/fiber/skynet_shared_detach.cpp new file mode 100644 index 00000000..12fd5653 --- /dev/null +++ b/src/boost/libs/fiber/performance/fiber/skynet_shared_detach.cpp @@ -0,0 +1,109 @@ + +// Copyright Oliver Kowalke 2015. +// 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) + +// based on https://github.com/atemerev/skynet from Alexander Temerev + +#include <algorithm> +#include <cassert> +#include <chrono> +#include <cmath> +#include <condition_variable> +#include <cstddef> +#include <cstdint> +#include <cstdlib> +#include <deque> +#include <iostream> +#include <memory> +#include <mutex> +#include <numeric> +#include <vector> + +#include <boost/fiber/all.hpp> +#include <boost/fiber/numa/pin_thread.hpp> +#include <boost/predef.h> + +#include "barrier.hpp" + +using allocator_type = boost::fibers::fixedsize_stack; +using channel_type = boost::fibers::buffered_channel< std::uint64_t >; +using clock_type = std::chrono::steady_clock; +using duration_type = clock_type::duration; +using lock_type = std::unique_lock< std::mutex >; +using time_point_type = clock_type::time_point; + +static bool done = false; +static std::mutex mtx{}; +static boost::fibers::condition_variable_any cnd{}; + +// microbenchmark +void skynet( allocator_type & salloc, channel_type & c, std::size_t num, std::size_t size, std::size_t div) { + if ( 1 == size) { + c.push( num); + } else { + channel_type rc{ 16 }; + for ( std::size_t i = 0; i < div; ++i) { + auto sub_num = num + i * size / div; + boost::fibers::fiber{ boost::fibers::launch::dispatch, + std::allocator_arg, salloc, + skynet, + std::ref( salloc), std::ref( rc), sub_num, size / div, div }.detach(); + } + std::uint64_t sum{ 0 }; + for ( std::size_t i = 0; i < div; ++i) { + sum += rc.value_pop(); + } + c.push( sum); + } +} + +void thread( unsigned int idx, barrier * b) { + boost::fibers::numa::pin_thread( idx); + boost::fibers::use_scheduling_algorithm< boost::fibers::algo::shared_work >(); + b->wait(); + lock_type lk( mtx); + cnd.wait( lk, [](){ return done; }); + BOOST_ASSERT( done); +} + +int main() { + try { + boost::fibers::use_scheduling_algorithm< boost::fibers::algo::shared_work >(); + unsigned int n = std::thread::hardware_concurrency(); + barrier b( n); + boost::fibers::numa::pin_thread( n - 1); + std::size_t size{ 1000000 }; + std::size_t div{ 10 }; + std::vector< std::thread > threads; + for ( unsigned int i = 1; i < n; ++i) { + threads.emplace_back( thread, i - 1, & b); + }; + allocator_type salloc{ 2*allocator_type::traits_type::page_size() }; + std::uint64_t result{ 0 }; + channel_type rc{ 2 }; + b.wait(); + time_point_type start{ clock_type::now() }; + skynet( salloc, rc, 0, size, div); + result = rc.value_pop(); + if ( 499999500000 != result) { + throw std::runtime_error("invalid result"); + } + auto duration = clock_type::now() - start; + lock_type lk( mtx); + done = true; + lk.unlock(); + cnd.notify_all(); + for ( std::thread & t : threads) { + t.join(); + } + std::cout << "duration: " << duration.count() / 1000000 << " ms" << std::endl; + return EXIT_SUCCESS; + } catch ( std::exception const& e) { + std::cerr << "exception: " << e.what() << std::endl; + } catch (...) { + std::cerr << "unhandled exception" << std::endl; + } + return EXIT_FAILURE; +} diff --git a/src/boost/libs/fiber/performance/fiber/skynet_shared_join.cpp b/src/boost/libs/fiber/performance/fiber/skynet_shared_join.cpp new file mode 100644 index 00000000..c26530f2 --- /dev/null +++ b/src/boost/libs/fiber/performance/fiber/skynet_shared_join.cpp @@ -0,0 +1,113 @@ + +// Copyright Oliver Kowalke 2015. +// 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) + +// based on https://github.com/atemerev/skynet from Alexander Temerev + +#include <algorithm> +#include <cassert> +#include <chrono> +#include <cmath> +#include <condition_variable> +#include <cstddef> +#include <cstdint> +#include <cstdlib> +#include <deque> +#include <iostream> +#include <memory> +#include <mutex> +#include <numeric> +#include <vector> + +#include <boost/fiber/all.hpp> +#include <boost/fiber/numa/pin_thread.hpp> +#include <boost/predef.h> + +#include "barrier.hpp" + +using allocator_type = boost::fibers::fixedsize_stack; +using channel_type = boost::fibers::buffered_channel< std::uint64_t >; +using clock_type = std::chrono::steady_clock; +using duration_type = clock_type::duration; +using lock_type = std::unique_lock< std::mutex >; +using time_point_type = clock_type::time_point; + +static bool done = false; +static std::mutex mtx{}; +static boost::fibers::condition_variable_any cnd{}; + +// microbenchmark +void skynet( allocator_type & salloc, channel_type & c, std::size_t num, std::size_t size, std::size_t div) { + if ( 1 == size) { + c.push( num); + } else { + channel_type rc{ 16 }; + std::vector< boost::fibers::fiber > fibers; + for ( std::size_t i = 0; i < div; ++i) { + auto sub_num = num + i * size / div; + fibers.emplace_back( boost::fibers::launch::dispatch, + std::allocator_arg, salloc, + skynet, + std::ref( salloc), std::ref( rc), sub_num, size / div, div); + } + for ( auto & f: fibers) { + f.join(); + } + std::uint64_t sum{ 0 }; + for ( std::size_t i = 0; i < div; ++i) { + sum += rc.value_pop(); + } + c.push( sum); + } +} + +void thread( unsigned int idx, barrier * b) { + boost::fibers::numa::pin_thread( idx); + boost::fibers::use_scheduling_algorithm< boost::fibers::algo::shared_work >(); + b->wait(); + lock_type lk( mtx); + cnd.wait( lk, [](){ return done; }); + BOOST_ASSERT( done); +} + +int main() { + try { + boost::fibers::use_scheduling_algorithm< boost::fibers::algo::shared_work >(); + unsigned int n = std::thread::hardware_concurrency(); + barrier b( n); + boost::fibers::numa::pin_thread( n - 1); + std::size_t size{ 1000000 }; + std::size_t div{ 10 }; + std::vector< std::thread > threads; + for ( unsigned int i = 1; i < n; ++i) { + threads.emplace_back( thread, i - 1, & b); + }; + allocator_type salloc{ 2*allocator_type::traits_type::page_size() }; + std::uint64_t result{ 0 }; + channel_type rc{ 2 }; + b.wait(); + time_point_type start{ clock_type::now() }; + skynet( salloc, rc, 0, size, div); + result = rc.value_pop(); + if ( 499999500000 != result) { + throw std::runtime_error("invalid result"); + } + auto duration = clock_type::now() - start; + lock_type lk( mtx); + done = true; + lk.unlock(); + cnd.notify_all(); + for ( std::thread & t : threads) { + t.join(); + } + std::cout << "duration: " << duration.count() / 1000000 << " ms" << std::endl; + return EXIT_SUCCESS; + } catch ( std::exception const& e) { + std::cerr << "exception: " << e.what() << std::endl; + } catch (...) { + std::cerr << "unhandled exception" << std::endl; + } + return EXIT_FAILURE; +} diff --git a/src/boost/libs/fiber/performance/fiber/skynet_stealing_async.cpp b/src/boost/libs/fiber/performance/fiber/skynet_stealing_async.cpp new file mode 100644 index 00000000..3701253b --- /dev/null +++ b/src/boost/libs/fiber/performance/fiber/skynet_stealing_async.cpp @@ -0,0 +1,114 @@ + +// Copyright Oliver Kowalke 2015. +// 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) + +// based on https://github.com/atemerev/skynet from Alexander Temerev + +#include <algorithm> +#include <cassert> +#include <chrono> +#include <cmath> +#include <condition_variable> +#include <cstddef> +#include <cstdint> +#include <cstdlib> +#include <queue> +#include <iostream> +#include <memory> +#include <mutex> +#include <numeric> +#include <random> +#include <sstream> +#include <vector> + +#include <boost/fiber/all.hpp> +#include <boost/predef.h> + +#include "barrier.hpp" + +using clock_type = std::chrono::steady_clock; +using duration_type = clock_type::duration; +using time_point_type = clock_type::time_point; +using channel_type = boost::fibers::buffered_channel< std::uint64_t >; +using allocator_type = boost::fibers::fixedsize_stack; +using lock_type = std::unique_lock< std::mutex >; + +static bool done = false; +static std::mutex mtx{}; +static boost::fibers::condition_variable_any cnd{}; + +// microbenchmark +std::uint64_t skynet(allocator_type& salloc, std::uint64_t num, std::uint64_t size, std::uint64_t div) { + if ( size != 1){ + size /= div; + + std::vector<boost::fibers::future<std::uint64_t> > results; + results.reserve( div); + + for ( std::uint64_t i = 0; i != div; ++i) { + std::uint64_t sub_num = num + i * size; + results.emplace_back(boost::fibers::async( + boost::fibers::launch::dispatch + , std::allocator_arg, salloc + , skynet + , std::ref( salloc), sub_num, size, div)); + } + + std::uint64_t sum = 0; + for ( auto& f : results) + sum += f.get(); + + return sum; + } + + return num; +} + +void thread( std::uint32_t thread_count) { + // thread registers itself at work-stealing scheduler + boost::fibers::use_scheduling_algorithm< boost::fibers::algo::work_stealing >( thread_count); + lock_type lk( mtx); + cnd.wait( lk, [](){ return done; }); + BOOST_ASSERT( done); +} + +int main() { + try { + // count of logical ids + std::uint32_t thread_count = std::thread::hardware_concurrency(); + std::size_t size{ 1000000 }; + std::size_t div{ 10 }; + allocator_type salloc{ 2*allocator_type::traits_type::page_size() }; + std::uint64_t result{ 0 }; + channel_type rc{ 2 }; + std::vector< std::thread > threads; + for ( std::uint32_t i = 1 /* count main-thread */; i < thread_count; ++i) { + // spawn thread + threads.emplace_back( thread, thread_count); + } + // main-thread registers itself at work-stealing scheduler + boost::fibers::use_scheduling_algorithm< boost::fibers::algo::work_stealing >( thread_count); + time_point_type start{ clock_type::now() }; + result = skynet( salloc, 0, size, div); + if ( 499999500000 != result) { + throw std::runtime_error("invalid result"); + } + auto duration = clock_type::now() - start; + lock_type lk( mtx); + done = true; + lk.unlock(); + cnd.notify_all(); + for ( std::thread & t : threads) { + t.join(); + } + std::cout << "duration: " << duration.count() / 1000000 << " ms" << std::endl; + return EXIT_SUCCESS; + } catch ( std::exception const& e) { + std::cerr << "exception: " << e.what() << std::endl; + } catch (...) { + std::cerr << "unhandled exception" << std::endl; + } + return EXIT_FAILURE; +} diff --git a/src/boost/libs/fiber/performance/fiber/skynet_stealing_detach.cpp b/src/boost/libs/fiber/performance/fiber/skynet_stealing_detach.cpp new file mode 100644 index 00000000..fbc1b205 --- /dev/null +++ b/src/boost/libs/fiber/performance/fiber/skynet_stealing_detach.cpp @@ -0,0 +1,109 @@ + +// Copyright Oliver Kowalke 2015. +// 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) + +// based on https://github.com/atemerev/skynet from Alexander Temerev + +#include <algorithm> +#include <cassert> +#include <chrono> +#include <cmath> +#include <condition_variable> +#include <cstddef> +#include <cstdint> +#include <cstdlib> +#include <queue> +#include <iostream> +#include <memory> +#include <mutex> +#include <numeric> +#include <random> +#include <sstream> +#include <vector> + +#include <boost/fiber/all.hpp> +#include <boost/predef.h> + +#include "barrier.hpp" + +using clock_type = std::chrono::steady_clock; +using duration_type = clock_type::duration; +using time_point_type = clock_type::time_point; +using channel_type = boost::fibers::buffered_channel< std::uint64_t >; +using allocator_type = boost::fibers::fixedsize_stack; +using lock_type = std::unique_lock< std::mutex >; + +static bool done = false; +static std::mutex mtx{}; +static boost::fibers::condition_variable_any cnd{}; + +// microbenchmark +void skynet( allocator_type & salloc, channel_type & c, std::size_t num, std::size_t size, std::size_t div) { + if ( 1 == size) { + c.push( num); + } else { + channel_type rc{ 16 }; + for ( std::size_t i = 0; i < div; ++i) { + auto sub_num = num + i * size / div; + boost::fibers::fiber{ boost::fibers::launch::dispatch, + std::allocator_arg, salloc, + skynet, + std::ref( salloc), std::ref( rc), sub_num, size / div, div }.detach(); + } + std::uint64_t sum{ 0 }; + for ( std::size_t i = 0; i < div; ++i) { + sum += rc.value_pop(); + } + c.push( sum); + } +} + +void thread( std::uint32_t thread_count) { + // thread registers itself at work-stealing scheduler + boost::fibers::use_scheduling_algorithm< boost::fibers::algo::work_stealing >( thread_count); + lock_type lk( mtx); + cnd.wait( lk, [](){ return done; }); + BOOST_ASSERT( done); +} + +int main() { + try { + // count of logical cpus + std::uint32_t thread_count = std::thread::hardware_concurrency(); + std::size_t size{ 1000000 }; + std::size_t div{ 10 }; + allocator_type salloc{ 2*allocator_type::traits_type::page_size() }; + std::uint64_t result{ 0 }; + channel_type rc{ 2 }; + std::vector< std::thread > threads; + for ( std::uint32_t i = 1 /* count main-thread */; i < thread_count; ++i) { + // spawn thread + threads.emplace_back( thread, thread_count); + } + // main-thread registers itself at work-stealing scheduler + boost::fibers::use_scheduling_algorithm< boost::fibers::algo::work_stealing >( thread_count); + time_point_type start{ clock_type::now() }; + skynet( salloc, rc, 0, size, div); + result = rc.value_pop(); + if ( 499999500000 != result) { + throw std::runtime_error("invalid result"); + } + auto duration = clock_type::now() - start; + lock_type lk( mtx); + done = true; + lk.unlock(); + cnd.notify_all(); + for ( std::thread & t : threads) { + t.join(); + } + std::cout << "duration: " << duration.count() / 1000000 << " ms" << std::endl; + return EXIT_SUCCESS; + } catch ( std::exception const& e) { + std::cerr << "exception: " << e.what() << std::endl; + } catch (...) { + std::cerr << "unhandled exception" << std::endl; + } + return EXIT_FAILURE; +} diff --git a/src/boost/libs/fiber/performance/fiber/skynet_stealing_join.cpp b/src/boost/libs/fiber/performance/fiber/skynet_stealing_join.cpp new file mode 100644 index 00000000..5acb1f7d --- /dev/null +++ b/src/boost/libs/fiber/performance/fiber/skynet_stealing_join.cpp @@ -0,0 +1,113 @@ + +// Copyright Oliver Kowalke 2015. +// 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) + +// based on https://github.com/atemerev/skynet from Alexander Temerev + +#include <algorithm> +#include <cassert> +#include <chrono> +#include <cmath> +#include <condition_variable> +#include <cstddef> +#include <cstdint> +#include <cstdlib> +#include <queue> +#include <iostream> +#include <memory> +#include <mutex> +#include <numeric> +#include <random> +#include <sstream> +#include <vector> + +#include <boost/fiber/all.hpp> +#include <boost/predef.h> + +#include "barrier.hpp" + +using clock_type = std::chrono::steady_clock; +using duration_type = clock_type::duration; +using time_point_type = clock_type::time_point; +using channel_type = boost::fibers::buffered_channel< std::uint64_t >; +using allocator_type = boost::fibers::fixedsize_stack; +using lock_type = std::unique_lock< std::mutex >; + +static bool done = false; +static std::mutex mtx{}; +static boost::fibers::condition_variable_any cnd{}; + +// microbenchmark +void skynet( allocator_type & salloc, channel_type & c, std::size_t num, std::size_t size, std::size_t div) { + if ( 1 == size) { + c.push( num); + } else { + channel_type rc{ 16 }; + std::vector< boost::fibers::fiber > fibers; + for ( std::size_t i = 0; i < div; ++i) { + auto sub_num = num + i * size / div; + fibers.emplace_back( boost::fibers::launch::dispatch, + std::allocator_arg, salloc, + skynet, + std::ref( salloc), std::ref( rc), sub_num, size / div, div); + } + std::uint64_t sum{ 0 }; + for ( std::size_t i = 0; i < div; ++i) { + sum += rc.value_pop(); + } + c.push( sum); + for ( auto & f : fibers) { + f.join(); + } + } +} + +void thread( std::uint32_t thread_count) { + // thread registers itself at work-stealing scheduler + boost::fibers::use_scheduling_algorithm< boost::fibers::algo::work_stealing >( thread_count); + lock_type lk( mtx); + cnd.wait( lk, [](){ return done; }); + BOOST_ASSERT( done); +} + +int main() { + try { + // count of logical cpus + std::uint32_t thread_count = std::thread::hardware_concurrency(); + std::size_t size{ 1000000 }; + std::size_t div{ 10 }; + allocator_type salloc{ 2*allocator_type::traits_type::page_size() }; + std::uint64_t result{ 0 }; + channel_type rc{ 2 }; + std::vector< std::thread > threads; + for ( std::uint32_t i = 1 /* count main-thread */; i < thread_count; ++i) { + // spawn thread + threads.emplace_back( thread, thread_count); + } + // main-thread registers itself at work-stealing scheduler + boost::fibers::use_scheduling_algorithm< boost::fibers::algo::work_stealing >( thread_count); + time_point_type start{ clock_type::now() }; + skynet( salloc, rc, 0, size, div); + result = rc.value_pop(); + if ( 499999500000 != result) { + throw std::runtime_error("invalid result"); + } + auto duration = clock_type::now() - start; + lock_type lk( mtx); + done = true; + lk.unlock(); + cnd.notify_all(); + for ( std::thread & t : threads) { + t.join(); + } + std::cout << "duration: " << duration.count() / 1000000 << " ms" << std::endl; + return EXIT_SUCCESS; + } catch ( std::exception const& e) { + std::cerr << "exception: " << e.what() << std::endl; + } catch (...) { + std::cerr << "unhandled exception" << std::endl; + } + return EXIT_FAILURE; +} diff --git a/src/boost/libs/fiber/performance/thread/Jamfile.v2 b/src/boost/libs/fiber/performance/thread/Jamfile.v2 new file mode 100644 index 00000000..baa18210 --- /dev/null +++ b/src/boost/libs/fiber/performance/thread/Jamfile.v2 @@ -0,0 +1,34 @@ + +# 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) + +# For more information, see http://www.boost.org/ + +import common ; +import feature ; +import indirect ; +import modules ; +import os ; +import toolset ; + +project boost/fiber/performance/thread + : requirements + <link>static + <threading>multi + <optimization>speed + <variant>release + ; + +exe skynet_async + : skynet_async.cpp + ; + +exe skynet_std + : skynet_std.cpp + ; + +exe skynet_pthread + : skynet_pthread.cpp + ; diff --git a/src/boost/libs/fiber/performance/thread/buffered_channel.hpp b/src/boost/libs/fiber/performance/thread/buffered_channel.hpp new file mode 100644 index 00000000..7f021dfc --- /dev/null +++ b/src/boost/libs/fiber/performance/thread/buffered_channel.hpp @@ -0,0 +1,228 @@ + +// Copyright Oliver Kowalke 2016. +// 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) +// +// based on Dmitry Vyukov's MPMC queue +// (http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue) + +#ifndef BUFFERED_CHANNEL_H +#define BUFFERED_CHANNEL_H + +#include <atomic> +#include <chrono> +#include <condition_variable> +#include <cstddef> +#include <cstdint> +#include <memory> +#include <mutex> +#include <stdexcept> +#include <type_traits> + +#include <boost/assert.hpp> +#include <boost/config.hpp> +#include <boost/fiber/detail/config.hpp> + +enum class channel_op_status { + success = 0, + empty, + full, + closed, + timeout +}; + +template< typename T > +class buffered_channel { +public: + typedef T value_type; + +private: + typedef typename std::aligned_storage< sizeof( T), alignof( T) >::type storage_type; + + struct alignas(cache_alignment) slot { + std::atomic< std::size_t > cycle{ 0 }; + storage_type storage{}; + + slot() = default; + }; + + // procuder cacheline + alignas(cache_alignment) std::atomic< std::size_t > producer_idx_{ 0 }; + // consumer cacheline + alignas(cache_alignment) std::atomic< std::size_t > consumer_idx_{ 0 }; + // shared write cacheline + alignas(cache_alignment) std::atomic_bool closed_{ false }; + mutable std::mutex mtx_{}; + std::condition_variable not_full_cnd_{}; + std::condition_variable not_empty_cnd_{}; + // shared read cacheline + alignas(cache_alignment) slot * slots_{ nullptr }; + std::size_t capacity_; + char pad_[cacheline_length]; + std::size_t waiting_consumer_{ 0 }; + + bool is_full_() { + std::size_t idx{ producer_idx_.load( std::memory_order_relaxed) }; + return 0 > static_cast< std::intptr_t >( slots_[idx & (capacity_ - 1)].cycle.load( std::memory_order_acquire) ) - static_cast< std::intptr_t >( idx); + } + + bool is_empty_() { + std::size_t idx{ consumer_idx_.load( std::memory_order_relaxed) }; + return 0 > static_cast< std::intptr_t >( slots_[idx & (capacity_ - 1)].cycle.load( std::memory_order_acquire) ) - static_cast< std::intptr_t >( idx + 1); + } + + template< typename ValueType > + channel_op_status try_push_( ValueType && value) { + slot * s{ nullptr }; + std::size_t idx{ producer_idx_.load( std::memory_order_relaxed) }; + for (;;) { + s = & slots_[idx & (capacity_ - 1)]; + std::size_t cycle{ s->cycle.load( std::memory_order_acquire) }; + std::intptr_t diff{ static_cast< std::intptr_t >( cycle) - static_cast< std::intptr_t >( idx) }; + if ( 0 == diff) { + if ( producer_idx_.compare_exchange_weak( idx, idx + 1, std::memory_order_relaxed) ) { + break; + } + } else if ( 0 > diff) { + return channel_op_status::full; + } else { + idx = producer_idx_.load( std::memory_order_relaxed); + } + } + ::new ( static_cast< void * >( std::addressof( s->storage) ) ) value_type( std::forward< ValueType >( value) ); + s->cycle.store( idx + 1, std::memory_order_release); + return channel_op_status::success; + } + + channel_op_status try_value_pop_( slot *& s, std::size_t & idx) { + idx = consumer_idx_.load( std::memory_order_relaxed); + for (;;) { + s = & slots_[idx & (capacity_ - 1)]; + std::size_t cycle = s->cycle.load( std::memory_order_acquire); + std::intptr_t diff{ static_cast< std::intptr_t >( cycle) - static_cast< std::intptr_t >( idx + 1) }; + if ( 0 == diff) { + if ( consumer_idx_.compare_exchange_weak( idx, idx + 1, std::memory_order_relaxed) ) { + break; + } + } else if ( 0 > diff) { + return channel_op_status::empty; + } else { + idx = consumer_idx_.load( std::memory_order_relaxed); + } + } + // incrementing the slot cycle must be deferred till the value has been consumed + // slot cycle tells procuders that the cell can be re-used (store new value) + return channel_op_status::success; + } + + channel_op_status try_pop_( value_type & value) { + slot * s{ nullptr }; + std::size_t idx{ 0 }; + channel_op_status status{ try_value_pop_( s, idx) }; + if ( channel_op_status::success == status) { + value = std::move( * reinterpret_cast< value_type * >( std::addressof( s->storage) ) ); + s->cycle.store( idx + capacity_, std::memory_order_release); + } + return status; + } + +public: + explicit buffered_channel( std::size_t capacity) : + capacity_{ capacity } { + if ( 0 == capacity_ || 0 != ( capacity_ & (capacity_ - 1) ) ) { + throw std::runtime_error{ "boost fiber: buffer capacity is invalid" }; + } + slots_ = new slot[capacity_](); + for ( std::size_t i = 0; i < capacity_; ++i) { + slots_[i].cycle.store( i, std::memory_order_relaxed); + } + } + + ~buffered_channel() { + close(); + for (;;) { + slot * s{ nullptr }; + std::size_t idx{ 0 }; + if ( channel_op_status::success == try_value_pop_( s, idx) ) { + reinterpret_cast< value_type * >( std::addressof( s->storage) )->~value_type(); + s->cycle.store( idx + capacity_, std::memory_order_release); + } else { + break; + } + } + delete [] slots_; + } + + buffered_channel( buffered_channel const&) = delete; + buffered_channel & operator=( buffered_channel const&) = delete; + + bool is_closed() const noexcept { + return closed_.load( std::memory_order_acquire); + } + + void close() noexcept { + std::unique_lock< std::mutex > lk{ mtx_ }; + closed_.store( true, std::memory_order_release); + not_full_cnd_.notify_all(); + not_empty_cnd_.notify_all(); + } + + channel_op_status push( value_type const& value) { + for (;;) { + if ( is_closed() ) { + return channel_op_status::closed; + } + channel_op_status status{ try_push_( value) }; + if ( channel_op_status::success == status) { + std::unique_lock< std::mutex > lk{ mtx_ }; + if ( 0 < waiting_consumer_) { + not_empty_cnd_.notify_one(); + } + return status; + } else if ( channel_op_status::full == status) { + std::unique_lock< std::mutex > lk{ mtx_ }; + if ( is_closed() ) { + return channel_op_status::closed; + } + if ( ! is_full_() ) { + continue; + } + not_full_cnd_.wait( lk, [this]{ return is_closed() || ! is_full_(); }); + } else { + BOOST_ASSERT( channel_op_status::closed == status); + return status; + } + } + } + + value_type value_pop() { + for (;;) { + slot * s{ nullptr }; + std::size_t idx{ 0 }; + channel_op_status status{ try_value_pop_( s, idx) }; + if ( channel_op_status::success == status) { + value_type value{ std::move( * reinterpret_cast< value_type * >( std::addressof( s->storage) ) ) }; + s->cycle.store( idx + capacity_, std::memory_order_release); + not_full_cnd_.notify_one(); + return std::move( value); + } else if ( channel_op_status::empty == status) { + std::unique_lock< std::mutex > lk{ mtx_ }; + ++waiting_consumer_; + if ( is_closed() ) { + throw std::runtime_error{ "boost fiber: channel is closed" }; + } + if ( ! is_empty_() ) { + continue; + } + not_empty_cnd_.wait( lk, [this](){ return is_closed() || ! is_empty_(); }); + --waiting_consumer_; + } else { + BOOST_ASSERT( channel_op_status::closed == status); + throw std::runtime_error{ "boost fiber: channel is closed" }; + } + } + } +}; + +#endif // BUFFERED_CHANNEL_H diff --git a/src/boost/libs/fiber/performance/thread/skynet_async.cpp b/src/boost/libs/fiber/performance/thread/skynet_async.cpp new file mode 100644 index 00000000..b752e020 --- /dev/null +++ b/src/boost/libs/fiber/performance/thread/skynet_async.cpp @@ -0,0 +1,76 @@ + +// Copyright Oliver Kowalke 2015. +// 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) + +// based on https://github.com/atemerev/skynet from Alexander Temerev + +#include <algorithm> +#include <cassert> +#include <chrono> +#include <condition_variable> +#include <cstddef> +#include <cstdint> +#include <cstdlib> +#include <queue> +#include <future> +#include <iostream> +#include <memory> +#include <mutex> +#include <numeric> +#include <random> +#include <sstream> +#include <vector> + +#include "buffered_channel.hpp" + +using channel_type = buffered_channel< std::uint64_t >; +using clock_type = std::chrono::steady_clock; +using duration_type = clock_type::duration; +using time_point_type = clock_type::time_point; + +// microbenchmark +std::uint64_t skynet( std::uint64_t num, std::uint64_t size, std::uint64_t div) +{ + if ( size != 1){ + size /= div; + + std::vector<std::future<std::uint64_t> > results; + results.reserve( div); + + for ( std::uint64_t i = 0; i != div; ++i) { + std::uint64_t sub_num = num + i * size; + results.emplace_back( + std::async( skynet, sub_num, size, div) ); + } + + std::uint64_t sum = 0; + for ( auto& f : results) + sum += f.get(); + + return sum; + } + + return num; +} + +int main() { + try { + std::size_t size{ 10000 }; + std::size_t div{ 10 }; + std::uint64_t result{ 0 }; + duration_type duration{ duration_type::zero() }; + time_point_type start{ clock_type::now() }; + result = skynet( 0, size, div); + duration = clock_type::now() - start; + std::cout << "Result: " << result << " in " << duration.count() / 1000000 << " ms" << std::endl; + std::cout << "done." << std::endl; + return EXIT_SUCCESS; + } catch ( std::exception const& e) { + std::cerr << "exception: " << e.what() << std::endl; + } catch (...) { + std::cerr << "unhandled exception" << std::endl; + } + return EXIT_FAILURE; +} diff --git a/src/boost/libs/fiber/performance/thread/skynet_pthread.cpp b/src/boost/libs/fiber/performance/thread/skynet_pthread.cpp new file mode 100644 index 00000000..43f55693 --- /dev/null +++ b/src/boost/libs/fiber/performance/thread/skynet_pthread.cpp @@ -0,0 +1,90 @@ +#include <algorithm> +#include <cassert> +#include <chrono> +#include <cstddef> +#include <cstdint> +#include <cstdlib> +#include <iostream> +#include <memory> +#include <numeric> +#include <stdexcept> +#include <vector> + +extern "C" { +#include <pthread.h> +#include <signal.h> +} + +#include "buffered_channel.hpp" + +using channel_type = buffered_channel< std::uint64_t >; +using clock_type = std::chrono::steady_clock; +using duration_type = clock_type::duration; +using time_point_type = clock_type::time_point; + +struct thread_args { + channel_type & c; + std::size_t num; + std::size_t size; + std::size_t div; +}; + +// microbenchmark +void * skynet( void * vargs) { + thread_args * args = static_cast< thread_args * >( vargs); + if ( 1 == args->size) { + args->c.push( args->num); + } else { + channel_type rc{ 16 }; + for ( std::size_t i = 0; i < args->div; ++i) { + auto sub_num = args->num + i * args->size / args->div; + auto sub_size = args->size / args->div; + auto size = 8 * 1024; + pthread_attr_t tattr; + if ( 0 != ::pthread_attr_init( & tattr) ) { + std::runtime_error("pthread_attr_init() failed"); + } + if ( 0 != ::pthread_attr_setstacksize( & tattr, size) ) { + std::runtime_error("pthread_attr_setstacksize() failed"); + } + thread_args * targs = new thread_args{ rc, sub_num, sub_size, args->div }; + pthread_t tid; + if ( 0 != ::pthread_create( & tid, & tattr, & skynet, targs) ) { + std::runtime_error("pthread_create() failed"); + } + if ( 0 != ::pthread_detach( tid) ) { + std::runtime_error("pthread_detach() failed"); + } + } + std::uint64_t sum{ 0 }; + for ( std::size_t i = 0; i < args->div; ++i) { + sum += rc.value_pop(); + } + args->c.push( sum); + } + delete args; + return nullptr; +} + +int main() { + try { + std::size_t size{ 10000 }; + std::size_t div{ 10 }; + std::uint64_t result{ 0 }; + duration_type duration{ duration_type::zero() }; + channel_type rc{ 2 }; + thread_args * targs = new thread_args{ rc, 0, size, div }; + time_point_type start{ clock_type::now() }; + skynet( targs); + result = rc.value_pop(); + duration = clock_type::now() - start; + std::cout << "Result: " << result << " in " << duration.count() / 1000000 << " ms" << std::endl; + std::cout << "done." << std::endl; + return EXIT_SUCCESS; + } catch ( std::exception const& e) { + std::cerr << "exception: " << e.what() << std::endl; + } catch (...) { + std::cerr << "unhandled exception" << std::endl; + } + return EXIT_FAILURE; +} diff --git a/src/boost/libs/fiber/performance/thread/skynet_std.cpp b/src/boost/libs/fiber/performance/thread/skynet_std.cpp new file mode 100644 index 00000000..93248f2e --- /dev/null +++ b/src/boost/libs/fiber/performance/thread/skynet_std.cpp @@ -0,0 +1,58 @@ +#include <algorithm> +#include <cassert> +#include <chrono> +#include <cstddef> +#include <cstdint> +#include <cstdlib> +#include <iostream> +#include <memory> +#include <numeric> +#include <thread> +#include <vector> + +#include "buffered_channel.hpp" + +using channel_type = buffered_channel< std::uint64_t >; +using clock_type = std::chrono::steady_clock; +using duration_type = clock_type::duration; +using time_point_type = clock_type::time_point; + +// microbenchmark +void skynet( channel_type & c, std::size_t num, std::size_t size, std::size_t div) { + if ( 1 == size) { + c.push( num); + } else { + channel_type rc{ 16 }; + for ( std::size_t i = 0; i < div; ++i) { + auto sub_num = num + i * size / div; + std::thread{ skynet, std::ref( rc), sub_num, size / div, div }.detach(); + } + std::uint64_t sum{ 0 }; + for ( std::size_t i = 0; i < div; ++i) { + sum += rc.value_pop(); + } + c.push( sum); + } +} + +int main() { + try { + std::size_t size{ 10000 }; + std::size_t div{ 10 }; + std::uint64_t result{ 0 }; + duration_type duration{ duration_type::zero() }; + channel_type rc{ 2 }; + time_point_type start{ clock_type::now() }; + skynet( rc, 0, size, div); + result = rc.value_pop(); + duration = clock_type::now() - start; + std::cout << "Result: " << result << " in " << duration.count() / 1000000 << " ms" << std::endl; + std::cout << "done." << std::endl; + return EXIT_SUCCESS; + } catch ( std::exception const& e) { + std::cerr << "exception: " << e.what() << std::endl; + } catch (...) { + std::cerr << "unhandled exception" << std::endl; + } + return EXIT_FAILURE; +} diff --git a/src/boost/libs/fiber/src/algo/algorithm.cpp b/src/boost/libs/fiber/src/algo/algorithm.cpp new file mode 100644 index 00000000..e567cf59 --- /dev/null +++ b/src/boost/libs/fiber/src/algo/algorithm.cpp @@ -0,0 +1,35 @@ + +// Copyright Oliver Kowalke / Nat Goodspeed 2015. +// 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 "boost/fiber/algo/algorithm.hpp" + +#include "boost/fiber/context.hpp" + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { +namespace algo { + +//static +fiber_properties * +algorithm_with_properties_base::get_properties( context * ctx) noexcept { + return ctx->get_properties(); +} + +//static +void +algorithm_with_properties_base::set_properties( context * ctx, fiber_properties * props) noexcept { + ctx->set_properties( props); +} + +}}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif diff --git a/src/boost/libs/fiber/src/algo/round_robin.cpp b/src/boost/libs/fiber/src/algo/round_robin.cpp new file mode 100644 index 00000000..5ff1af2b --- /dev/null +++ b/src/boost/libs/fiber/src/algo/round_robin.cpp @@ -0,0 +1,72 @@ + +// Copyright Oliver Kowalke 2013. +// 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 "boost/fiber/algo/round_robin.hpp" + +#include <boost/assert.hpp> +#include <boost/context/detail/prefetch.hpp> + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { +namespace algo { + +void +round_robin::awakened( context * ctx) noexcept { + BOOST_ASSERT( nullptr != ctx); + BOOST_ASSERT( ! ctx->ready_is_linked() ); + BOOST_ASSERT( ctx->is_resumable() ); + ctx->ready_link( rqueue_); +} + +context * +round_robin::pick_next() noexcept { + context * victim = nullptr; + if ( ! rqueue_.empty() ) { + victim = & rqueue_.front(); + rqueue_.pop_front(); + boost::context::detail::prefetch_range( victim, sizeof( context) ); + BOOST_ASSERT( nullptr != victim); + BOOST_ASSERT( ! victim->ready_is_linked() ); + BOOST_ASSERT( victim->is_resumable() ); + } + return victim; +} + +bool +round_robin::has_ready_fibers() const noexcept { + return ! rqueue_.empty(); +} + +void +round_robin::suspend_until( std::chrono::steady_clock::time_point const& time_point) noexcept { + if ( (std::chrono::steady_clock::time_point::max)() == time_point) { + std::unique_lock< std::mutex > lk{ mtx_ }; + cnd_.wait( lk, [&](){ return flag_; }); + flag_ = false; + } else { + std::unique_lock< std::mutex > lk{ mtx_ }; + cnd_.wait_until( lk, time_point, [&](){ return flag_; }); + flag_ = false; + } +} + +void +round_robin::notify() noexcept { + std::unique_lock< std::mutex > lk{ mtx_ }; + flag_ = true; + lk.unlock(); + cnd_.notify_all(); +} + +}}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif diff --git a/src/boost/libs/fiber/src/algo/shared_work.cpp b/src/boost/libs/fiber/src/algo/shared_work.cpp new file mode 100644 index 00000000..a055c1e9 --- /dev/null +++ b/src/boost/libs/fiber/src/algo/shared_work.cpp @@ -0,0 +1,101 @@ + +// Copyright Oliver Kowalke 2013. +// 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 "boost/fiber/algo/shared_work.hpp" + +#include <boost/assert.hpp> + +#include "boost/fiber/type.hpp" + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { +namespace algo { + +//[awakened_ws +void +shared_work::awakened( context * ctx) noexcept { + if ( ctx->is_context( type::pinned_context) ) { /*< + recognize when we're passed this thread's main fiber (or an + implicit library helper fiber): never put those on the shared + queue + >*/ + lqueue_.push_back( * ctx); + } else { + ctx->detach(); + std::unique_lock< std::mutex > lk{ rqueue_mtx_ }; /*< + worker fiber, enqueue on shared queue + >*/ + rqueue_.push_back( ctx); + } +} +//] + +//[pick_next_ws +context * +shared_work::pick_next() noexcept { + context * ctx = nullptr; + std::unique_lock< std::mutex > lk{ rqueue_mtx_ }; + if ( ! rqueue_.empty() ) { /*< + pop an item from the ready queue + >*/ + ctx = rqueue_.front(); + rqueue_.pop_front(); + lk.unlock(); + BOOST_ASSERT( nullptr != ctx); + context::active()->attach( ctx); /*< + attach context to current scheduler via the active fiber + of this thread + >*/ + } else { + lk.unlock(); + if ( ! lqueue_.empty() ) { /*< + nothing in the ready queue, return main or dispatcher fiber + >*/ + ctx = & lqueue_.front(); + lqueue_.pop_front(); + } + } + return ctx; +} +//] + +void +shared_work::suspend_until( std::chrono::steady_clock::time_point const& time_point) noexcept { + if ( suspend_) { + if ( (std::chrono::steady_clock::time_point::max)() == time_point) { + std::unique_lock< std::mutex > lk{ mtx_ }; + cnd_.wait( lk, [this](){ return flag_; }); + flag_ = false; + } else { + std::unique_lock< std::mutex > lk{ mtx_ }; + cnd_.wait_until( lk, time_point, [this](){ return flag_; }); + flag_ = false; + } + } +} + +void +shared_work::notify() noexcept { + if ( suspend_) { + std::unique_lock< std::mutex > lk{ mtx_ }; + flag_ = true; + lk.unlock(); + cnd_.notify_all(); + } +} + +shared_work::rqueue_type shared_work::rqueue_{}; +std::mutex shared_work::rqueue_mtx_{}; + +}}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif diff --git a/src/boost/libs/fiber/src/algo/work_stealing.cpp b/src/boost/libs/fiber/src/algo/work_stealing.cpp new file mode 100644 index 00000000..ae9ece8d --- /dev/null +++ b/src/boost/libs/fiber/src/algo/work_stealing.cpp @@ -0,0 +1,120 @@ + +// Copyright Oliver Kowalke 2015. +// 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 "boost/fiber/algo/work_stealing.hpp" + +#include <random> + +#include <boost/assert.hpp> +#include <boost/context/detail/prefetch.hpp> + +#include "boost/fiber/detail/thread_barrier.hpp" +#include "boost/fiber/type.hpp" + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { +namespace algo { + +std::atomic< std::uint32_t > work_stealing::counter_{ 0 }; +std::vector< intrusive_ptr< work_stealing > > work_stealing::schedulers_{}; + +void +work_stealing::init_( std::uint32_t thread_count, + std::vector< intrusive_ptr< work_stealing > > & schedulers) { + // resize array of schedulers to thread_count, initilized with nullptr + std::vector< intrusive_ptr< work_stealing > >{ thread_count, nullptr }.swap( schedulers); +} + +work_stealing::work_stealing( std::uint32_t thread_count, bool suspend) : + id_{ counter_++ }, + thread_count_{ thread_count }, + suspend_{ suspend } { + static boost::fibers::detail::thread_barrier b{ thread_count }; + // initialize the array of schedulers + static std::once_flag flag; + std::call_once( flag, & work_stealing::init_, thread_count_, std::ref( schedulers_) ); + // register pointer of this scheduler + schedulers_[id_] = this; + b.wait(); +} + +void +work_stealing::awakened( context * ctx) noexcept { + if ( ! ctx->is_context( type::pinned_context) ) { + ctx->detach(); + } + rqueue_.push( ctx); +} + +context * +work_stealing::pick_next() noexcept { + context * victim = rqueue_.pop(); + if ( nullptr != victim) { + boost::context::detail::prefetch_range( victim, sizeof( context) ); + if ( ! victim->is_context( type::pinned_context) ) { + context::active()->attach( victim); + } + } else { + std::uint32_t id = 0; + std::size_t count = 0, size = schedulers_.size(); + static thread_local std::minstd_rand generator{ std::random_device{}() }; + std::uniform_int_distribution< std::uint32_t > distribution{ + 0, static_cast< std::uint32_t >( thread_count_ - 1) }; + do { + do { + ++count; + // random selection of one logical cpu + // that belongs to the local NUMA node + id = distribution( generator); + // prevent stealing from own scheduler + } while ( id == id_); + // steal context from other scheduler + victim = schedulers_[id]->steal(); + } while ( nullptr == victim && count < size); + if ( nullptr != victim) { + boost::context::detail::prefetch_range( victim, sizeof( context) ); + BOOST_ASSERT( ! victim->is_context( type::pinned_context) ); + context::active()->attach( victim); + } + } + return victim; +} + +void +work_stealing::suspend_until( std::chrono::steady_clock::time_point const& time_point) noexcept { + if ( suspend_) { + if ( (std::chrono::steady_clock::time_point::max)() == time_point) { + std::unique_lock< std::mutex > lk{ mtx_ }; + cnd_.wait( lk, [this](){ return flag_; }); + flag_ = false; + } else { + std::unique_lock< std::mutex > lk{ mtx_ }; + cnd_.wait_until( lk, time_point, [this](){ return flag_; }); + flag_ = false; + } + } +} + +void +work_stealing::notify() noexcept { + if ( suspend_) { + std::unique_lock< std::mutex > lk{ mtx_ }; + flag_ = true; + lk.unlock(); + cnd_.notify_all(); + } +} + +}}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif diff --git a/src/boost/libs/fiber/src/barrier.cpp b/src/boost/libs/fiber/src/barrier.cpp new file mode 100644 index 00000000..a8aaf36c --- /dev/null +++ b/src/boost/libs/fiber/src/barrier.cpp @@ -0,0 +1,50 @@ + +// Copyright Oliver Kowalke 2013. +// 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 "boost/fiber/barrier.hpp" + +#include <mutex> +#include <system_error> + +#include "boost/fiber/exceptions.hpp" + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { + +barrier::barrier( std::size_t initial) : + initial_{ initial }, + current_{ initial_ } { + if ( BOOST_UNLIKELY( 0 == initial) ) { + throw fiber_error{ std::make_error_code( std::errc::invalid_argument), + "boost fiber: zero initial barrier count" }; + } +} + +bool +barrier::wait() { + std::unique_lock< mutex > lk{ mtx_ }; + const std::size_t cycle = cycle_; + if ( 0 == --current_) { + ++cycle_; + current_ = initial_; + lk.unlock(); // no pessimization + cond_.notify_all(); + return true; + } else { + cond_.wait( lk, [&]{ return cycle != cycle_; }); + } + return false; +} + +}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif diff --git a/src/boost/libs/fiber/src/condition_variable.cpp b/src/boost/libs/fiber/src/condition_variable.cpp new file mode 100644 index 00000000..c391b8c5 --- /dev/null +++ b/src/boost/libs/fiber/src/condition_variable.cpp @@ -0,0 +1,65 @@ + +// Copyright Oliver Kowalke 2013. +// 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 "boost/fiber/condition_variable.hpp" + +#include "boost/fiber/context.hpp" + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { + +void +condition_variable_any::notify_one() noexcept { + context * active_ctx = context::active(); + // get one context' from wait-queue + detail::spinlock_lock lk{ wait_queue_splk_ }; + while ( ! wait_queue_.empty() ) { + context * ctx = & wait_queue_.front(); + wait_queue_.pop_front(); + std::intptr_t expected = reinterpret_cast< std::intptr_t >( this); + if ( ctx->twstatus.compare_exchange_strong( expected, static_cast< std::intptr_t >( -1), std::memory_order_acq_rel) ) { + // notify context + active_ctx->schedule( ctx); + break; + } else if ( static_cast< std::intptr_t >( 0) == expected) { + // no timed-wait op. + // notify context + active_ctx->schedule( ctx); + break; + } + } +} + +void +condition_variable_any::notify_all() noexcept { + context * active_ctx = context::active(); + // get all context' from wait-queue + detail::spinlock_lock lk{ wait_queue_splk_ }; + // notify all context' + while ( ! wait_queue_.empty() ) { + context * ctx = & wait_queue_.front(); + wait_queue_.pop_front(); + std::intptr_t expected = reinterpret_cast< std::intptr_t >( this); + if ( ctx->twstatus.compare_exchange_strong( expected, static_cast< std::intptr_t >( -1), std::memory_order_acq_rel) ) { + // notify context + active_ctx->schedule( ctx); + } else if ( static_cast< std::intptr_t >( 0) == expected) { + // no timed-wait op. + // notify context + active_ctx->schedule( ctx); + } + } +} + +}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif diff --git a/src/boost/libs/fiber/src/context.cpp b/src/boost/libs/fiber/src/context.cpp new file mode 100644 index 00000000..c0de8443 --- /dev/null +++ b/src/boost/libs/fiber/src/context.cpp @@ -0,0 +1,402 @@ + +// Copyright Oliver Kowalke 2013. +// 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 "boost/fiber/context.hpp" + +#include <cstdlib> +#include <mutex> +#include <new> + +#include "boost/fiber/exceptions.hpp" +#include "boost/fiber/scheduler.hpp" + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { + +class main_context final : public context { +public: + main_context() noexcept : + context{ 1, type::main_context, launch::post } { + } +}; + +class dispatcher_context final : public context { +private: + boost::context::fiber + run_( boost::context::fiber && c) { +#if (defined(BOOST_USE_UCONTEXT)||defined(BOOST_USE_WINFIB)) + std::move( c).resume(); +#endif + // execute scheduler::dispatch() + return get_scheduler()->dispatch(); + } + +public: + dispatcher_context( boost::context::preallocated const& palloc, default_stack && salloc) : + context{ 0, type::dispatcher_context, launch::post } { + c_ = boost::context::fiber{ std::allocator_arg, palloc, salloc, + std::bind( & dispatcher_context::run_, this, std::placeholders::_1) }; +#if (defined(BOOST_USE_UCONTEXT)||defined(BOOST_USE_WINFIB)) + c_ = std::move( c_).resume(); +#endif + } +}; + +static intrusive_ptr< context > make_dispatcher_context() { + default_stack salloc; // use default satck-size + auto sctx = salloc.allocate(); + // reserve space for control structure + void * storage = reinterpret_cast< void * >( + ( reinterpret_cast< uintptr_t >( sctx.sp) - static_cast< uintptr_t >( sizeof( dispatcher_context) ) ) + & ~ static_cast< uintptr_t >( 0xff) ); + void * stack_bottom = reinterpret_cast< void * >( + reinterpret_cast< uintptr_t >( sctx.sp) - static_cast< uintptr_t >( sctx.size) ); + const std::size_t size = reinterpret_cast< uintptr_t >( storage) - reinterpret_cast< uintptr_t >( stack_bottom); + // placement new of context on top of fiber's stack + return intrusive_ptr< context >{ + new ( storage) dispatcher_context{ + boost::context::preallocated{ storage, size, sctx }, std::move( salloc) } }; +} + +// schwarz counter +struct context_initializer { + static thread_local context * active_; + static thread_local std::size_t counter_; + + context_initializer() { + if ( 0 == counter_++) { + // main fiber context of this thread + context * main_ctx = new main_context{}; + // scheduler of this thread + scheduler * sched = new scheduler{}; + // attach main context to scheduler + sched->attach_main_context( main_ctx); + // create and attach dispatcher context to scheduler + sched->attach_dispatcher_context( make_dispatcher_context() ); + // make main context to active context + active_ = main_ctx; + } + } + + ~context_initializer() { + if ( 0 == --counter_) { + context * main_ctx = active_; + BOOST_ASSERT( main_ctx->is_context( type::main_context) ); + scheduler * sched = main_ctx->get_scheduler(); + delete sched; + delete main_ctx; + } + } +}; + +// zero-initialization +thread_local context * context_initializer::active_{ nullptr }; +thread_local std::size_t context_initializer::counter_{ 0 }; + +context * +context::active() noexcept { + // initialized the first time control passes; per thread + thread_local static context_initializer ctx_initializer; + return context_initializer::active_; +} + +void +context::reset_active() noexcept { + context_initializer::active_ = nullptr; +} + +context::~context() { + // protect for concurrent access + std::unique_lock< detail::spinlock > lk{ splk_ }; + BOOST_ASSERT( ! ready_is_linked() ); + BOOST_ASSERT( ! remote_ready_is_linked() ); + BOOST_ASSERT( ! sleep_is_linked() ); + BOOST_ASSERT( ! wait_is_linked() ); + if ( is_context( type::dispatcher_context) ) { + // dispatcher-context is resumed by main-context + // while the scheduler is deconstructed +#ifdef BOOST_DISABLE_ASSERTS + wait_queue_.pop_front(); +#else + context * ctx = & wait_queue_.front(); + wait_queue_.pop_front(); + BOOST_ASSERT( ctx->is_context( type::main_context) ); + BOOST_ASSERT( nullptr == active() ); +#endif + } + BOOST_ASSERT( wait_queue_.empty() ); + delete properties_; +} + +context::id +context::get_id() const noexcept { + return id{ const_cast< context * >( this) }; +} + +void +context::resume() noexcept { + context * prev = this; + // context_initializer::active_ will point to `this` + // prev will point to previous active context + std::swap( context_initializer::active_, prev); + // pass pointer to the context that resumes `this` + std::move( c_).resume_with([prev](boost::context::fiber && c){ + prev->c_ = std::move( c); + return boost::context::fiber{}; + }); +} + +void +context::resume( detail::spinlock_lock & lk) noexcept { + context * prev = this; + // context_initializer::active_ will point to `this` + // prev will point to previous active context + std::swap( context_initializer::active_, prev); + // pass pointer to the context that resumes `this` + std::move( c_).resume_with([prev,&lk](boost::context::fiber && c){ + prev->c_ = std::move( c); + lk.unlock(); + return boost::context::fiber{}; + }); +} + +void +context::resume( context * ready_ctx) noexcept { + context * prev = this; + // context_initializer::active_ will point to `this` + // prev will point to previous active context + std::swap( context_initializer::active_, prev); + // pass pointer to the context that resumes `this` + std::move( c_).resume_with([prev,ready_ctx](boost::context::fiber && c){ + prev->c_ = std::move( c); + context::active()->schedule( ready_ctx); + return boost::context::fiber{}; + }); +} + +void +context::suspend() noexcept { + get_scheduler()->suspend(); +} + +void +context::suspend( detail::spinlock_lock & lk) noexcept { + get_scheduler()->suspend( lk); +} + +void +context::join() { + // get active context + context * active_ctx = context::active(); + // protect for concurrent access + std::unique_lock< detail::spinlock > lk{ splk_ }; + // wait for context which is not terminated + if ( ! terminated_) { + // push active context to wait-queue, member + // of the context which has to be joined by + // the active context + active_ctx->wait_link( wait_queue_); + // suspend active context + active_ctx->get_scheduler()->suspend( lk); + // active context resumed + BOOST_ASSERT( context::active() == active_ctx); + } +} + +void +context::yield() noexcept { + // yield active context + get_scheduler()->yield( context::active() ); +} + +boost::context::fiber +context::suspend_with_cc() noexcept { + context * prev = this; + // context_initializer::active_ will point to `this` + // prev will point to previous active context + std::swap( context_initializer::active_, prev); + // pass pointer to the context that resumes `this` + return std::move( c_).resume_with([prev](boost::context::fiber && c){ + prev->c_ = std::move( c); + return boost::context::fiber{}; + }); +} + +boost::context::fiber +context::terminate() noexcept { + // protect for concurrent access + std::unique_lock< detail::spinlock > lk{ splk_ }; + // mark as terminated + terminated_ = true; + // notify all waiting fibers + while ( ! wait_queue_.empty() ) { + context * ctx = & wait_queue_.front(); + // remove fiber from wait-queue + wait_queue_.pop_front(); + // notify scheduler + schedule( ctx); + } + BOOST_ASSERT( wait_queue_.empty() ); + // release fiber-specific-data + for ( fss_data_t::value_type & data : fss_data_) { + data.second.do_cleanup(); + } + fss_data_.clear(); + // switch to another context + return get_scheduler()->terminate( lk, this); +} + +bool +context::wait_until( std::chrono::steady_clock::time_point const& tp) noexcept { + BOOST_ASSERT( nullptr != get_scheduler() ); + BOOST_ASSERT( this == active() ); + return get_scheduler()->wait_until( this, tp); +} + +bool +context::wait_until( std::chrono::steady_clock::time_point const& tp, + detail::spinlock_lock & lk) noexcept { + BOOST_ASSERT( nullptr != get_scheduler() ); + BOOST_ASSERT( this == active() ); + return get_scheduler()->wait_until( this, tp, lk); +} + +void +context::schedule( context * ctx) noexcept { + //BOOST_ASSERT( nullptr != ctx); + BOOST_ASSERT( this != ctx); + BOOST_ASSERT( nullptr != get_scheduler() ); + BOOST_ASSERT( nullptr != ctx->get_scheduler() ); +#if ! defined(BOOST_FIBERS_NO_ATOMICS) + // FIXME: comparing scheduler address' must be synchronized? + // what if ctx is migrated between threads + // (other scheduler assigned) + if ( scheduler_ == ctx->get_scheduler() ) { + // local + get_scheduler()->schedule( ctx); + } else { + // remote + ctx->get_scheduler()->schedule_from_remote( ctx); + } +#else + BOOST_ASSERT( get_scheduler() == ctx->get_scheduler() ); + get_scheduler()->schedule( ctx); +#endif +} + +void * +context::get_fss_data( void const * vp) const { + uintptr_t key = reinterpret_cast< uintptr_t >( vp); + fss_data_t::const_iterator i = fss_data_.find( key); + return fss_data_.end() != i ? i->second.vp : nullptr; +} + +void +context::set_fss_data( void const * vp, + detail::fss_cleanup_function::ptr_t const& cleanup_fn, + void * data, + bool cleanup_existing) { + BOOST_ASSERT( cleanup_fn); + uintptr_t key = reinterpret_cast< uintptr_t >( vp); + fss_data_t::iterator i = fss_data_.find( key); + if ( fss_data_.end() != i) { + if( cleanup_existing) { + i->second.do_cleanup(); + } + if ( nullptr != data) { + i->second = fss_data{ data, cleanup_fn }; + } else { + fss_data_.erase( i); + } + } else { + fss_data_.insert( + std::make_pair( + key, + fss_data{ data, cleanup_fn } ) ); + } +} + +void +context::set_properties( fiber_properties * props) noexcept { + delete properties_; + properties_ = props; +} + +bool +context::worker_is_linked() const noexcept { + return worker_hook_.is_linked(); +} + +bool +context::ready_is_linked() const noexcept { + return ready_hook_.is_linked(); +} + +bool +context::remote_ready_is_linked() const noexcept { + return remote_ready_hook_.is_linked(); +} + +bool +context::sleep_is_linked() const noexcept { + return sleep_hook_.is_linked(); +} + +bool +context::terminated_is_linked() const noexcept { + return terminated_hook_.is_linked(); +} + +bool +context::wait_is_linked() const noexcept { + return wait_hook_.is_linked(); +} + +void +context::worker_unlink() noexcept { + BOOST_ASSERT( worker_is_linked() ); + worker_hook_.unlink(); +} + +void +context::ready_unlink() noexcept { + BOOST_ASSERT( ready_is_linked() ); + ready_hook_.unlink(); +} + +void +context::sleep_unlink() noexcept { + BOOST_ASSERT( sleep_is_linked() ); + sleep_hook_.unlink(); +} + +void +context::wait_unlink() noexcept { + BOOST_ASSERT( wait_is_linked() ); + wait_hook_.unlink(); +} + +void +context::detach() noexcept { + BOOST_ASSERT( context::active() != this); + get_scheduler()->detach_worker_context( this); +} + +void +context::attach( context * ctx) noexcept { + BOOST_ASSERT( nullptr != ctx); + get_scheduler()->attach_worker_context( ctx); +} + +}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif diff --git a/src/boost/libs/fiber/src/fiber.cpp b/src/boost/libs/fiber/src/fiber.cpp new file mode 100644 index 00000000..d360812f --- /dev/null +++ b/src/boost/libs/fiber/src/fiber.cpp @@ -0,0 +1,71 @@ + +// Copyright Oliver Kowalke 2013. +// 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 "boost/fiber/fiber.hpp" + +#include <system_error> + +#include <boost/assert.hpp> + +#include "boost/fiber/exceptions.hpp" +#include "boost/fiber/scheduler.hpp" + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { + +void +fiber::start_() noexcept { + context * ctx = context::active(); + ctx->attach( impl_.get() ); + switch ( impl_->get_policy() ) { + case launch::post: + // push new fiber to ready-queue + // resume executing current fiber + ctx->get_scheduler()->schedule( impl_.get() ); + break; + case launch::dispatch: + // resume new fiber and push current fiber + // to ready-queue + impl_->resume( ctx); + break; + default: + BOOST_ASSERT_MSG( false, "unknown launch-policy"); + } +} + +void +fiber::join() { + // FIXME: must fiber::join() be synchronized? + if ( BOOST_UNLIKELY( context::active()->get_id() == get_id() ) ) { + throw fiber_error{ std::make_error_code( std::errc::resource_deadlock_would_occur), + "boost fiber: trying to join itself" }; + } + if ( BOOST_UNLIKELY( ! joinable() ) ) { + throw fiber_error{ std::make_error_code( std::errc::invalid_argument), + "boost fiber: fiber not joinable" }; + } + impl_->join(); + impl_.reset(); +} + +void +fiber::detach() { + if ( BOOST_UNLIKELY( ! joinable() ) ) { + throw fiber_error{ std::make_error_code( std::errc::invalid_argument), + "boost fiber: fiber not joinable" }; + } + impl_.reset(); +} + +}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif diff --git a/src/boost/libs/fiber/src/future.cpp b/src/boost/libs/fiber/src/future.cpp new file mode 100644 index 00000000..d8f9b5c4 --- /dev/null +++ b/src/boost/libs/fiber/src/future.cpp @@ -0,0 +1,71 @@ + +// Copyright Oliver Kowalke 2013. +// 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 "boost/fiber/exceptions.hpp" + +namespace boost { +namespace fibers { + +class future_error_category : public std::error_category { +public: + virtual const char* name() const noexcept { + return "fiber-future"; + } + + virtual std::error_condition default_error_condition( int ev) const noexcept { + switch ( static_cast< future_errc >( ev) ) { + case future_errc::broken_promise: + return std::error_condition{ + static_cast< int >( future_errc::broken_promise), + future_category() }; + case future_errc::future_already_retrieved: + return std::error_condition{ + static_cast< int >( future_errc::future_already_retrieved), + future_category() }; + case future_errc::promise_already_satisfied: + return std::error_condition{ + static_cast< int >( future_errc::promise_already_satisfied), + future_category() }; + case future_errc::no_state: + return std::error_condition{ + static_cast< + int >( future_errc::no_state), + future_category() }; + default: + return std::error_condition{ ev, * this }; + } + } + + virtual bool equivalent( std::error_code const& code, int condition) const noexcept { + return * this == code.category() && + static_cast< int >( default_error_condition( code.value() ).value() ) == condition; + } + + virtual std::string message( int ev) const { + switch ( static_cast< future_errc >( ev) ) { + case future_errc::broken_promise: + return std::string{ "The associated promise has been destructed prior " + "to the associated state becoming ready." }; + case future_errc::future_already_retrieved: + return std::string{ "The future has already been retrieved from " + "the promise or packaged_task." }; + case future_errc::promise_already_satisfied: + return std::string{ "The state of the promise has already been set." }; + case future_errc::no_state: + return std::string{ "Operation not permitted on an object without " + "an associated state." }; + } + return std::string{ "unspecified future_errc value\n" }; + } +}; + +BOOST_FIBERS_DECL +std::error_category const& future_category() noexcept { + static fibers::future_error_category cat; + return cat; +} + +}} diff --git a/src/boost/libs/fiber/src/mutex.cpp b/src/boost/libs/fiber/src/mutex.cpp new file mode 100644 index 00000000..ad67cbd1 --- /dev/null +++ b/src/boost/libs/fiber/src/mutex.cpp @@ -0,0 +1,83 @@ + +// Copyright Oliver Kowalke 2013. +// 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 "boost/fiber/mutex.hpp" + +#include <algorithm> +#include <functional> +#include <system_error> + +#include "boost/fiber/exceptions.hpp" +#include "boost/fiber/scheduler.hpp" + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { + +void +mutex::lock() { + while ( true) { + context * active_ctx = context::active(); + // store this fiber in order to be notified later + detail::spinlock_lock lk{ wait_queue_splk_ }; + if ( BOOST_UNLIKELY( active_ctx == owner_) ) { + throw lock_error{ + std::make_error_code( std::errc::resource_deadlock_would_occur), + "boost fiber: a deadlock is detected" }; + } else if ( nullptr == owner_) { + owner_ = active_ctx; + return; + } + BOOST_ASSERT( ! active_ctx->wait_is_linked() ); + active_ctx->wait_link( wait_queue_); + // suspend this fiber + active_ctx->suspend( lk); + BOOST_ASSERT( ! active_ctx->wait_is_linked() ); + } +} + +bool +mutex::try_lock() { + context * active_ctx = context::active(); + detail::spinlock_lock lk{ wait_queue_splk_ }; + if ( BOOST_UNLIKELY( active_ctx == owner_) ) { + throw lock_error{ + std::make_error_code( std::errc::resource_deadlock_would_occur), + "boost fiber: a deadlock is detected" }; + } else if ( nullptr == owner_) { + owner_ = active_ctx; + } + lk.unlock(); + // let other fiber release the lock + active_ctx->yield(); + return active_ctx == owner_; +} + +void +mutex::unlock() { + context * active_ctx = context::active(); + detail::spinlock_lock lk{ wait_queue_splk_ }; + if ( BOOST_UNLIKELY( active_ctx != owner_) ) { + throw lock_error{ + std::make_error_code( std::errc::operation_not_permitted), + "boost fiber: no privilege to perform the operation" }; + } + owner_ = nullptr; + if ( ! wait_queue_.empty() ) { + context * ctx = & wait_queue_.front(); + wait_queue_.pop_front(); + active_ctx->schedule( ctx); + } +} + +}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif diff --git a/src/boost/libs/fiber/src/numa/aix/pin_thread.cpp b/src/boost/libs/fiber/src/numa/aix/pin_thread.cpp new file mode 100644 index 00000000..133d9691 --- /dev/null +++ b/src/boost/libs/fiber/src/numa/aix/pin_thread.cpp @@ -0,0 +1,43 @@ + +// Copyright Oliver Kowalke 2017. +// 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 "boost/fiber/numa/pin_thread.hpp" + +extern "C" { +#include <sys/errno.h> +#include <sys/processor.h> +#include <sys/thread.h> +} + +#include <system_error> + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { +namespace numa { + +BOOST_FIBERS_DECL +void pin_thread( std::uint32_t cpuid) { + pin_thread( cpuid, ::thread_self() ); +} + +BOOST_FIBERS_DECL +void pin_thread( std::uint32_t cpuid, std::thread::native_handle_type h) { + if ( BOOST_UNLIKELY( -1 == ::bindprocessor( BINDTHREAD, h, static_cast< cpu_t >( cpuid) ) ) ) { + throw std::system_error( + std::error_code( errno, std::system_category() ), + "bindprocessor() failed"); + } +} + +}}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif diff --git a/src/boost/libs/fiber/src/numa/aix/topology.cpp b/src/boost/libs/fiber/src/numa/aix/topology.cpp new file mode 100644 index 00000000..107f070f --- /dev/null +++ b/src/boost/libs/fiber/src/numa/aix/topology.cpp @@ -0,0 +1,83 @@ + +// Copyright Oliver Kowalke 2017. +// 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 "boost/fiber/numa/topology.hpp" + +extern "C" { +#include <errno.h> +#include <sys/rset.h> +#include <sys/types.h> +} + +#include <system_error> + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace { + +void explore( int sdl, std::vector< boost::fibers::numa::node > & topo) { + rsethandle_t rset = ::rs_alloc( RS_PARTITION); + rsethandle_t rad = ::rs_alloc( RS_EMPTY); + int maxnodes = ::rs_numrads( rset, sdl, 0); + if ( BOOST_UNLIKELY( -1 == maxnodes) ) { + throw std::system_error{ + std::error_code{ errno, std::system_category() }, + "rs_numrads() failed" }; + } + for ( int node_id = 0; node_id < maxnodes; ++node_id) { + if ( ::rs_getrad( rset, rad, sdl, node_id, 0) ) { + continue; + } + if ( ! ::rs_getinfo( rad, R_NUMPROCS, 0) ) { + continue; + } + boost::fibers::numa::node n; + n.id = static_cast< std::uint32_t >( node_id); + int maxcpus = ::rs_getinfo( rad, R_MAXPROCS, 0); + for ( int cpu_id = 0; cpu_id < maxcpus; ++cpu_id) { + if ( ::rs_op( RS_TESTRESOURCE, rad, nullptr, R_PROCS, cpu_id) ) { + n.logical_cpus.insert( static_cast< std::uint32_t >( cpu_id) ); + } + } + topo.push_back( n); + } + ::rs_free( rset); + ::rs_free( rad); +} + +} + +namespace boost { +namespace fibers { +namespace numa { + +BOOST_FIBERS_DECL +std::vector< node > topology() { + std::vector< node > topo; + int maxlevel = ::rs_getinfo( nullptr, R_MAXSDL, 0); + for ( int i = 0; i <= maxlevel; ++i) { + if ( i == ::rs_getinfo( nullptr, R_MCMSDL, 0) ) { + explore( i, topo); + break; + } + } + // fake NUMA distance + std::size_t size = topo.size(); + for ( auto & n : topo) { + for ( std::size_t i = 0; i < size; ++i) { + n.distance.push_back( n.id == i ? 10 : 20); + } + } + return topo; +} + +}}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif diff --git a/src/boost/libs/fiber/src/numa/algo/work_stealing.cpp b/src/boost/libs/fiber/src/numa/algo/work_stealing.cpp new file mode 100644 index 00000000..d3653d61 --- /dev/null +++ b/src/boost/libs/fiber/src/numa/algo/work_stealing.cpp @@ -0,0 +1,189 @@ + +// Copyright Oliver Kowalke 2015. +// 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 "boost/fiber/numa/algo/work_stealing.hpp" + +#include <cmath> +#include <random> + +#include <boost/assert.hpp> +#include <boost/context/detail/prefetch.hpp> + +#include "boost/fiber/detail/thread_barrier.hpp" +#include "boost/fiber/type.hpp" + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { +namespace numa { +namespace algo { + +std::vector< intrusive_ptr< work_stealing > > work_stealing::schedulers_{}; + +std::vector< std::uint32_t > get_local_cpus( std::uint32_t node_id, std::vector< boost::fibers::numa::node > const& topo) { + for ( auto & node : topo) { + if ( node_id == node.id) { + // store IDs of logical cpus that belong to this local NUMA node + return std::vector< std::uint32_t >{ node.logical_cpus.begin(), node.logical_cpus.end() }; + } + } + return std::vector< std::uint32_t >{}; +} + +std::vector< std::uint32_t > get_remote_cpus( std::uint32_t node_id, std::vector< boost::fibers::numa::node > const& topo) { + std::vector< std::uint32_t > remote_cpus; + for ( auto & node : topo) { + if ( node_id != node.id) { + // store IDs of logical cpus that belong to a remote NUMA node + // no ordering regarding to the NUMA distance + remote_cpus.insert( remote_cpus.end(), node.logical_cpus.begin(), node.logical_cpus.end() ); + } + } + return remote_cpus; +} + +void +work_stealing::init_( std::vector< boost::fibers::numa::node > const& topo, + std::vector< intrusive_ptr< work_stealing > > & schedulers) { + std::uint32_t max_cpu_id = 0; + for ( auto & node : topo) { + max_cpu_id = (std::max)( max_cpu_id, * node.logical_cpus.rbegin() ); + } + // resize array of schedulers to max. CPU ID, initilized with nullptr + // CPU ID acts as the index in the scheduler array + // if a logical cpus is offline, schedulers_ will contain a nullptr + // logical cpus index starts at `0` -> add 1 + std::vector< intrusive_ptr< work_stealing > >{ max_cpu_id + 1, nullptr }.swap( schedulers); +} + +work_stealing::work_stealing( + std::uint32_t cpu_id, + std::uint32_t node_id, + std::vector< boost::fibers::numa::node > const& topo, + bool suspend) : + cpu_id_{ cpu_id }, + local_cpus_{ get_local_cpus( node_id, topo) }, + remote_cpus_{ get_remote_cpus( node_id, topo) }, + suspend_{ suspend } { + // pin current thread to logical cpu + boost::fibers::numa::pin_thread( cpu_id_); + // calculate thread count + std::size_t thread_count = 0; + for ( auto & node : topo) { + thread_count += node.logical_cpus.size(); + } + static boost::fibers::detail::thread_barrier b{ thread_count }; + // initialize the array of schedulers + static std::once_flag flag; + std::call_once( flag, & work_stealing::init_, topo, std::ref( schedulers_) ); + // register pointer of this scheduler + schedulers_[cpu_id_] = this; + b.wait(); +} + +void +work_stealing::awakened( context * ctx) noexcept { + if ( ! ctx->is_context( type::pinned_context) ) { + ctx->detach(); + } + rqueue_.push( ctx); +} + +context * +work_stealing::pick_next() noexcept { + context * victim = rqueue_.pop(); + if ( nullptr != victim) { + boost::context::detail::prefetch_range( victim, sizeof( context) ); + if ( ! victim->is_context( type::pinned_context) ) { + context::active()->attach( victim); + } + } else { + std::uint32_t cpu_id = 0; + std::size_t count = 0, size = local_cpus_.size(); + static thread_local std::minstd_rand generator{ std::random_device{}() }; + std::uniform_int_distribution< std::uint32_t > local_distribution{ + 0, static_cast< std::uint32_t >( local_cpus_.size() - 1) }; + std::uniform_int_distribution< std::uint32_t > remote_distribution{ + 0, static_cast< std::uint32_t >( remote_cpus_.size() - 1) }; + do { + do { + ++count; + // random selection of one logical cpu + // that belongs to the local NUMA node + cpu_id = local_cpus_[local_distribution( generator)]; + // prevent stealing from own scheduler + } while ( cpu_id == cpu_id_); + // steal context from other scheduler + // schedulers_[cpu_id] should never contain a nullptr + BOOST_ASSERT( nullptr != schedulers_[cpu_id]); + victim = schedulers_[cpu_id]->steal(); + } while ( nullptr == victim && count < size); + if ( nullptr != victim) { + boost::context::detail::prefetch_range( victim, sizeof( context) ); + BOOST_ASSERT( ! victim->is_context( type::pinned_context) ); + context::active()->attach( victim); + } else if ( ! remote_cpus_.empty() ) { + cpu_id = 0; + count = 0; + size = remote_cpus_.size(); + do { + ++count; + // random selection of one logical cpu + // that belongs to a remote NUMA node + cpu_id = remote_cpus_[remote_distribution( generator)]; + // remote cpu ID should never be equal to local cpu ID + BOOST_ASSERT( cpu_id != cpu_id_); + // schedulers_[cpu_id] should never contain a nullptr + BOOST_ASSERT( nullptr != schedulers_[cpu_id]); + // steal context from other scheduler + victim = schedulers_[cpu_id]->steal(); + } while ( nullptr == victim && count < size); + if ( nullptr != victim) { + boost::context::detail::prefetch_range( victim, sizeof( context) ); + BOOST_ASSERT( ! victim->is_context( type::pinned_context) ); + // move memory from remote NUMA-node to + // memory of local NUMA-node + context::active()->attach( victim); + } + } + } + return victim; +} + +void +work_stealing::suspend_until( std::chrono::steady_clock::time_point const& time_point) noexcept { + if ( suspend_) { + if ( (std::chrono::steady_clock::time_point::max)() == time_point) { + std::unique_lock< std::mutex > lk{ mtx_ }; + cnd_.wait( lk, [this](){ return flag_; }); + flag_ = false; + } else { + std::unique_lock< std::mutex > lk{ mtx_ }; + cnd_.wait_until( lk, time_point, [this](){ return flag_; }); + flag_ = false; + } + } +} + +void +work_stealing::notify() noexcept { + if ( suspend_) { + std::unique_lock< std::mutex > lk{ mtx_ }; + flag_ = true; + lk.unlock(); + cnd_.notify_all(); + } +} + +}}}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif diff --git a/src/boost/libs/fiber/src/numa/freebsd/pin_thread.cpp b/src/boost/libs/fiber/src/numa/freebsd/pin_thread.cpp new file mode 100644 index 00000000..0ff03d0a --- /dev/null +++ b/src/boost/libs/fiber/src/numa/freebsd/pin_thread.cpp @@ -0,0 +1,46 @@ + +// Copyright Oliver Kowalke 2017. +// 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 "boost/fiber/numa/pin_thread.hpp" + +extern "C" { +#include <pthread.h> +#include <pthread_np.h> +} + +#include <system_error> + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { +namespace numa { + +BOOST_FIBERS_DECL +void pin_thread( std::uint32_t cpuid) { + pin_thread( cpuid, ::pthread_self() ); +} + +BOOST_FIBERS_DECL +void pin_thread( std::uint32_t cpuid, std::thread::native_handle_type h) { + cpuset_t set; + CPU_ZERO( & set); + CPU_SET( cpuid, & set); + int err = 0; + if ( BOOST_UNLIKELY( 0 != ( err = ::pthread_setaffinity_np( h, sizeof( set), & set) ) ) ) { + throw std::system_error( + std::error_code( err, std::system_category() ), + "pthread_setaffinity_np() failed"); + } +} + +}}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif diff --git a/src/boost/libs/fiber/src/numa/freebsd/topology.cpp b/src/boost/libs/fiber/src/numa/freebsd/topology.cpp new file mode 100644 index 00000000..848aea3f --- /dev/null +++ b/src/boost/libs/fiber/src/numa/freebsd/topology.cpp @@ -0,0 +1,57 @@ + +// Copyright Oliver Kowalke 2017. +// 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 "boost/fiber/numa/topology.hpp" + +extern "C" { +#include <errno.h> +#include <sys/param.h> +#include <sys/cpuset.h> +} + +#include <system_error> + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { +namespace numa { + +BOOST_FIBERS_DECL +std::vector< node > topology() { + std::vector< node > topo; + cpuset_t mask; + CPU_ZERO( & mask); + if ( BOOST_UNLIKELY( 0 != ::cpuset_getaffinity( CPU_LEVEL_WHICH, CPU_WHICH_CPUSET, -1, sizeof( mask), & mask) ) ) { + throw std::system_error{ + std::error_code{ errno, std::system_category() }, + "::cpuset_getaffinity() failed" }; + } + node n; + n.id = 0; // FreeBSD does not support NUMA + for ( int cpu_id = 0; cpu_id < CPU_SETSIZE; ++cpu_id) { + if ( CPU_ISSET( cpu_id, & mask) ) { + n.logical_cpus.insert( static_cast< std::uint32_t >( cpu_id) ); + } + } + topo.push_back( n); + // fake NUMA distance + std::size_t size = topo.size(); + for ( auto & n : topo) { + for ( std::size_t i = 0; i < size; ++i) { + n.distance.push_back( n.id == i ? 10 : 20); + } + } + return topo; +} + +}}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif diff --git a/src/boost/libs/fiber/src/numa/hpux/pin_thread.cpp b/src/boost/libs/fiber/src/numa/hpux/pin_thread.cpp new file mode 100644 index 00000000..1de3fc48 --- /dev/null +++ b/src/boost/libs/fiber/src/numa/hpux/pin_thread.cpp @@ -0,0 +1,46 @@ + +// Copyright Oliver Kowalke 2017. +// 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 "boost/fiber/numa/pin_thread.hpp" + +extern "C" { +#include <sys/pthread.h> +} + +#include <system_error> + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { +namespace numa { + +BOOST_FIBERS_DECL +void pin_thread( std::uint32_t cpuid) { + pin_thread( cpuid, PTHREAD_SELFTID_NP); +} + +BOOST_FIBERS_DECL +void pin_thread( std::uint32_t cpuid, std::thread::native_handle_type h) { + pthread_spu_t spu; + int err = ::pthread_processor_bind_np( PTHREAD_BIND_FORCED_NP, + & spu, + static_cast< pthread_spu_t >( cpuid), + h); + if ( BOOST_UNLIKELY( 0 != err) ) + throw std::system_error( + std::error_code( err, std::system_category() ), + "pthread_processor_bind_np() failed"); + } +} + +}}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif diff --git a/src/boost/libs/fiber/src/numa/hpux/topology.cpp b/src/boost/libs/fiber/src/numa/hpux/topology.cpp new file mode 100644 index 00000000..63a40275 --- /dev/null +++ b/src/boost/libs/fiber/src/numa/hpux/topology.cpp @@ -0,0 +1,64 @@ + +// Copyright Oliver Kowalke 2017. +// 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 "boost/fiber/numa/topology.hpp" + +extern "C" { +#include <errno.h> +#include <sys/mpctl.h> +#include <sys/types.h> +#include <types.h> +} + +#include <map> +#include <system_error> + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { +namespace numa { + +BOOST_FIBERS_DECL +std::vector< node > topology() { + std::vector< node > topo; + // HP/UX has NUMA enabled + if ( 1 == ::sysconf( _SC_CCNUMA_SUPPORT) ) { + std::map< std::uint32_t, node > map; + // enumerate CPU sets + int cpu_id = ::mpctl( MPC_GETFIRSTSPU_SYS, 0, 0); + while ( -1 != cpu_id) { + int node_id = ::mpctl( MPC_SPUTOLDOM, cpu_id, 0); + if ( BOOST_UNLIKELY( -1 == node_id) ) { + throw std::system_errors{ + std::error_codes{ errno, std::system_category() }, + "mpctl() failed" }; + } + map[id].id = static_cast< std::uint32_t >( node_id); + map[id].logical_cpus.insert( static_cast< std::uint32_t >( cpu_id) ); + cpu_id = ::mpctl( MPC_GETNEXTSPU_SYS, cpu_id, 0); + } + for ( auto entry : map) { + topo.push_back( entry.second); + } + } + // fake NUMA distance + std::size_t size = topo.size(); + for ( auto & n : topo) { + for ( std::size_t i = 0; i < size; ++i) { + n.distance.push_back( n.id == i ? 10 : 20); + } + } + return topo; +} + +}}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif diff --git a/src/boost/libs/fiber/src/numa/linux/pin_thread.cpp b/src/boost/libs/fiber/src/numa/linux/pin_thread.cpp new file mode 100644 index 00000000..730f2f5f --- /dev/null +++ b/src/boost/libs/fiber/src/numa/linux/pin_thread.cpp @@ -0,0 +1,46 @@ + +// Copyright Oliver Kowalke 2017. +// 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 "boost/fiber/numa/pin_thread.hpp" + +extern "C" { +#include <pthread.h> +#include <sched.h> +} + +#include <system_error> + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { +namespace numa { + +BOOST_FIBERS_DECL +void pin_thread( std::uint32_t cpuid) { + pin_thread( cpuid, ::pthread_self() ); +} + +BOOST_FIBERS_DECL +void pin_thread( std::uint32_t cpuid, std::thread::native_handle_type h) { + cpu_set_t set; + CPU_ZERO( & set); + CPU_SET( cpuid, & set); + int err = 0; + if ( BOOST_UNLIKELY( 0 != ( err = ::pthread_setaffinity_np( h, sizeof( set), & set) ) ) ) { + throw std::system_error( + std::error_code( err, std::system_category() ), + "pthread_setaffinity_np() failed"); + } +} + +}}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif diff --git a/src/boost/libs/fiber/src/numa/linux/topology.cpp b/src/boost/libs/fiber/src/numa/linux/topology.cpp new file mode 100644 index 00000000..f30d59d0 --- /dev/null +++ b/src/boost/libs/fiber/src/numa/linux/topology.cpp @@ -0,0 +1,230 @@ + +// Copyright Oliver Kowalke 2017. +// 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 "boost/fiber/numa/topology.hpp" + +#include <exception> +#include <map> +#include <regex> +#include <set> +#include <string> +#include <utility> + +#include <boost/algorithm/string.hpp> +#include <boost/assert.hpp> +#include <boost/config.hpp> +#include <boost/filesystem.hpp> +#include <boost/filesystem/fstream.hpp> +#include <boost/format.hpp> + +#include "boost/fiber/exceptions.hpp" + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +#if !defined(BOOST_NO_CXX11_HDR_REGEX) + +namespace al = boost::algorithm; +namespace fs = boost::filesystem; + +namespace { + +class directory_iterator { +private: + fs::directory_iterator i_; + fs::directory_iterator e_; + std::regex exp_; + std::pair< std::uint32_t, fs::path > idx_; + + bool eval_( fs::directory_entry const& entry) { + std::string filename( entry.path().filename().string() ); + std::smatch what; + if ( ! std::regex_search( filename, what, exp_) ) { + return false; + } + idx_ = std::make_pair( std::stoul( what[1].str() ), entry.path() ); + return true; + } + +public: + typedef std::input_iterator_tag iterator_category; + typedef const std::pair< std::uint32_t, fs::path > value_type; + typedef std::ptrdiff_t difference_type; + typedef value_type * pointer; + typedef value_type & reference; + + directory_iterator() : + i_(), e_(), exp_(), idx_() { + } + + directory_iterator( fs::path const& dir, std::string const& exp) : + i_( dir), e_(), exp_( exp), idx_() { + while ( i_ != e_ && ! eval_( * i_) ) { + ++i_; + } + } + + bool operator==( directory_iterator const& other) { + return i_ == other.i_; + } + + bool operator!=( directory_iterator const& other) { + return i_ != other.i_; + } + + directory_iterator & operator++() { + do { + ++i_; + } while ( i_ != e_ && ! eval_( * i_) ); + return * this; + } + + directory_iterator operator++( int) { + directory_iterator tmp( * this); + ++*this; + return tmp; + } + + reference operator*() const { + return idx_; + } + + pointer operator->() const { + return & idx_; + } +}; + +std::set< std::uint32_t > ids_from_line( std::string const& content) { + std::set< std::uint32_t > ids; + std::vector< std::string > v1; + al::split( v1, content, al::is_any_of(",") ); + for ( std::string entry : v1) { + al::trim( entry); + std::vector< std::string > v2; + al::split( v2, entry, al::is_any_of("-") ); + BOOST_ASSERT( ! v2.empty() ); + if ( 1 == v2.size() ) { + // only one ID + ids.insert( std::stoul( v2[0]) ); + } else { + // range of IDs + std::uint32_t first = std::stoul( * v2.begin() ); + std::uint32_t last = std::stoul( * v2.rbegin() ); + for ( std::uint32_t i = first; i <= last; ++i) { + ids.insert( i); + } + } + } + return ids; +} + +std::vector< std::uint32_t > distance_from_line( std::string const& content) { + std::vector< std::uint32_t > distance; + std::vector< std::string > v1; + al::split( v1, content, al::is_any_of(" ") ); + for ( std::string entry : v1) { + al::trim( entry); + BOOST_ASSERT( ! entry.empty() ); + distance.push_back( std::stoul( entry) ); + } + return distance; +} + +} + +namespace boost { +namespace fibers { +namespace numa { + +BOOST_FIBERS_DECL +std::vector< node > topology() { + std::vector< node > topo; + // 1. parse list of CPUs which are online + fs::ifstream fs_online{ fs::path("/sys/devices/system/cpu/online") }; + std::string content; + std::getline( fs_online, content); + std::set< std::uint32_t > cpus = ids_from_line( content); + if ( cpus.empty() ) { + // parsing cpus failed + return topo; + } + std::map< std::uint32_t, node > map; + // iterate list of logical cpus + for ( std::uint32_t cpu_id : cpus) { + fs::path cpu_path{ + boost::str( + boost::format("/sys/devices/system/cpu/cpu%d/") % cpu_id) }; + BOOST_ASSERT( fs::exists( cpu_path) ); + // 2. to witch NUMA node the CPU belongs to + directory_iterator e; + for ( directory_iterator i{ cpu_path, "^node([0-9]+)$" }; + i != e; ++i) { + std::uint32_t node_id = i->first; + map[node_id].id = node_id; + map[node_id].logical_cpus.insert( cpu_id); + // assigned to only one NUMA node + break; + } + } + if ( map.empty() ) { + // maybe /sys/devices/system/cpu/cpu[0-9]/node[0-9] was not defined + // put all CPUs to NUMA node 0 + map[0].id = 0; + for ( std::uint32_t cpu_id : cpus) { + map[0].logical_cpus.insert( cpu_id); + } + } + for ( auto entry : map) { + // NUMA-node distance + fs::path distance_path{ + boost::str( + boost::format("/sys/devices/system/node/node%d/distance") % entry.second.id) }; + if ( fs::exists( distance_path) ) { + fs::ifstream fs_distance{ distance_path }; + std::string content; + std::getline( fs_distance, content); + entry.second.distance = distance_from_line( content); + topo.push_back( entry.second); + } else { + // fake NUMA distance + entry.second.distance.push_back( 10); + topo.push_back( entry.second); + } + } + return topo; +} + +}}} + +#else + +namespace boost { +namespace fibers { +namespace numa { + +#if BOOST_COMP_CLANG || \ + BOOST_COMP_GNUC || \ + BOOST_COMP_INTEL || \ + BOOST_COMP_MSVC +# pragma message "topology() not supported without <regex>" +#endif + +BOOST_FIBERS_DECL +std::vector< node > topology() { + throw fiber_error{ + std::make_error_code( std::errc::function_not_supported), + "boost fiber: topology() not supported without <regex>" }; + return std::vector< node >{}; +} + +}}} + +#endif + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif diff --git a/src/boost/libs/fiber/src/numa/pin_thread.cpp b/src/boost/libs/fiber/src/numa/pin_thread.cpp new file mode 100644 index 00000000..8938b167 --- /dev/null +++ b/src/boost/libs/fiber/src/numa/pin_thread.cpp @@ -0,0 +1,46 @@ + +// Copyright Oliver Kowalke 2017. +// 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 "boost/fiber/numa/pin_thread.hpp" + +#include <system_error> + +#include "boost/fiber/exceptions.hpp" + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { +namespace numa { + +#if BOOST_COMP_CLANG || \ + BOOST_COMP_GNUC || \ + BOOST_COMP_INTEL || \ + BOOST_COMP_MSVC +# pragma message "pin_thread() not supported" +#endif + +BOOST_FIBERS_DECL +void pin_thread( std::uint32_t) { + throw fiber_error{ + std::make_error_code( std::errc::function_not_supported), + "boost fiber: pin_thread() not supported" }; +} + +BOOST_FIBERS_DECL +void pin_thread( std::uint32_t cpuid, std::thread::native_handle_type h) { + throw fiber_error{ + std::make_error_code( std::errc::function_not_supported), + "boost fiber: pin_thread() not supported" }; +} + +}}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif diff --git a/src/boost/libs/fiber/src/numa/solaris/pin_thread.cpp b/src/boost/libs/fiber/src/numa/solaris/pin_thread.cpp new file mode 100644 index 00000000..0443f1e7 --- /dev/null +++ b/src/boost/libs/fiber/src/numa/solaris/pin_thread.cpp @@ -0,0 +1,47 @@ + +// Copyright Oliver Kowalke 2017. +// 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 "boost/fiber/numa/pin_thread.hpp" + +extern "C" { +#include <errno.h> +#include <sys/types.h> +#include <sys/processor.h> +#include <sys/procset.h> +} + +#include <system_error> + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { +namespace numa { + +BOOST_FIBERS_DECL +void pin_thread( std::uint32_t cpuid) { + pin_thread( cpuid, P_MYID); +} + +BOOST_FIBERS_DECL +void pin_thread( std::uint32_t cpuid, std::thread::native_handle_type h) { + if ( BOOST_UNLIKELY( -1 == ::processor_bind( P_LWPID, + h, + static_cast< processorid_t >( cpuid), + 0) ) ) { + throw std::system_error( + std::error_code( errno, std::system_category() ), + "processor_bind() failed"); + } +} + +}}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif diff --git a/src/boost/libs/fiber/src/numa/solaris/topology.cpp b/src/boost/libs/fiber/src/numa/solaris/topology.cpp new file mode 100644 index 00000000..51a12eee --- /dev/null +++ b/src/boost/libs/fiber/src/numa/solaris/topology.cpp @@ -0,0 +1,80 @@ + +// Copyright Oliver Kowalke 2017. +// 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 "boost/fiber/numa/topology.hpp" + +extern "C" { +#include <errno.h> +#include <sys/lgrp_user.h> +} + +#include <system_error> + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace { + +void explore( std::vector< boost::fibers::numa::node > & topo, lgrp_cookie_t cookie, lgrp_id_t node) { + int size = ::lgrp_cpus( cookie, node, nullptr, 0, LGRP_CONTENT_HIERARCHY); + if ( -1 == size) { + return; + } + // is node a NUMA Node? + if ( 0 < ::lgrp_mem_size( cookie, node, LGRP_MEM_SZ_INSTALLED, LGRP_CONTENT_DIRECT) ) { + std::vector< processorid_t > cpus; + cpus.resize( size); + ::lgrp_cpus( cookie, node, cpus.data(), size, LGRP_CONTENT_HIERARCHY); + boost::fibers::numa::node n; + n.id = static_cast< std::uint32_t >( node); + for ( auto cpu_id : cpus) { + n.logical_cpus.insert( static_cast< std::uint32_t >( cpu_id) ); + } + topo.push_back( n); + } + size = ::lgrp_children( cookie, node, nullptr, 0); + std::vector< lgrp_id_t > nodes; + nodes.resize( size); + ::lgrp_children( cookie, node, nodes.data(), size); + for ( auto node : nodes) { + explore( topo, cookie, node); + } +} + +} + +namespace boost { +namespace fibers { +namespace numa { + +BOOST_FIBERS_DECL +std::vector< node > topology() { + std::vector< node > topo; + lgrp_cookie_t cookie = ::lgrp_init( LGRP_VIEW_OS); + if ( BOOST_UNLIKELY( LGRP_COOKIE_NONE == cookie) ) { + throw std::system_error{ + std::error_code{ errno, std::system_category() }, + "lprp_init() failed" }; + } + lgrp_id_t root = ::lgrp_root( cookie); + explore( topo, cookie, root); + ::lgrp_fini( cookie); + // fake NUMA distance + std::size_t size = topo.size(); + for ( auto & n : topo) { + for ( std::size_t i = 0; i < size; ++i) { + n.distance.push_back( n.id == i ? 10 : 20); + } + } + return topo; +} + +}}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif diff --git a/src/boost/libs/fiber/src/numa/topology.cpp b/src/boost/libs/fiber/src/numa/topology.cpp new file mode 100644 index 00000000..5b4d4623 --- /dev/null +++ b/src/boost/libs/fiber/src/numa/topology.cpp @@ -0,0 +1,40 @@ + +// Copyright Oliver Kowalke 2017. +// 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 "boost/fiber/numa/topology.hpp" + +#include <system_error> + +#include "boost/fiber/exceptions.hpp" + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { +namespace numa { + +#if BOOST_COMP_CLANG || \ + BOOST_COMP_GNUC || \ + BOOST_COMP_INTEL || \ + BOOST_COMP_MSVC +# pragma message "topology() not supported" +#endif + +BOOST_FIBERS_DECL +std::vector< node > topology() { + throw fiber_error{ + std::make_error_code( std::errc::function_not_supported), + "boost fiber: topology() not supported" }; + return std::vector< node >{}; +} + +}}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif diff --git a/src/boost/libs/fiber/src/numa/windows/pin_thread.cpp b/src/boost/libs/fiber/src/numa/windows/pin_thread.cpp new file mode 100644 index 00000000..347c611b --- /dev/null +++ b/src/boost/libs/fiber/src/numa/windows/pin_thread.cpp @@ -0,0 +1,51 @@ + +// Copyright Oliver Kowalke 2017. +// 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 "boost/fiber/numa/pin_thread.hpp" + +extern "C" { +#include <windows.h> +} + +#include <cstring> +#include <system_error> + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { +namespace numa { + +BOOST_FIBERS_DECL +void pin_thread( std::uint32_t cpuid) { + pin_thread( cpuid, ::GetCurrentThread() ); +} + +BOOST_FIBERS_DECL +void pin_thread( std::uint32_t cpuid, std::thread::native_handle_type h) { + GROUP_AFFINITY affinity; + std::memset( & affinity, 0, sizeof( affinity) ); + // compute processor group + // a group contains max 64 logical CPUs + affinity.Group = static_cast< WORD >(cpuid / 64); + // compute the ID of the logical CPU in the group + uint32_t id = cpuid % 64; + // set the bit mask of the logical CPU + affinity.Mask = static_cast< KAFFINITY >( 1) << id; + if ( BOOST_UNLIKELY( 0 == ::SetThreadGroupAffinity( h, & affinity, nullptr) ) ) { + throw std::system_error( + std::error_code( ::GetLastError(), std::system_category() ), + "::SetThreadiGroupAffinity() failed"); + } +} + +}}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif diff --git a/src/boost/libs/fiber/src/numa/windows/topology.cpp b/src/boost/libs/fiber/src/numa/windows/topology.cpp new file mode 100644 index 00000000..618ddf62 --- /dev/null +++ b/src/boost/libs/fiber/src/numa/windows/topology.cpp @@ -0,0 +1,133 @@ + +// Copyright Oliver Kowalke 2017. +// 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 "boost/fiber/numa/topology.hpp" + +extern "C" { +#include <windows.h> +} + +#include <map> +#include <set> +#include <system_error> +#include <vector> + +#include <boost/assert.hpp> + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace { + +class procinfo_iterator { +private: + using SLPI = SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX; + + SLPI * buffer_{ nullptr }; + SLPI * procinfo_{ nullptr }; + DWORD length_{ 0 }; + +public: + procinfo_iterator() = default; + + procinfo_iterator( LOGICAL_PROCESSOR_RELATIONSHIP relship) { + if ( ::GetLogicalProcessorInformationEx( relship, nullptr, & length_) ) { + return; + } + if ( BOOST_UNLIKELY( ERROR_INSUFFICIENT_BUFFER != ::GetLastError() ) ) { + throw std::system_error{ + std::error_code{ static_cast< int >( ::GetLastError() ), std::system_category() }, + "::GetLogicalProcessorInformation() failed" }; + } + buffer_ = reinterpret_cast< SLPI * >( LocalAlloc( LMEM_FIXED, length_) ); + if ( BOOST_UNLIKELY( nullptr == buffer_) ) { + throw std::bad_alloc(); + } + if ( BOOST_UNLIKELY( ! ::GetLogicalProcessorInformationEx( relship, buffer_, & length_) ) ) { + throw std::system_error{ + std::error_code{ static_cast< int >( ::GetLastError() ), std::system_category() }, + "::GetLogicalProcessorInformation() failed" }; + } + procinfo_ = buffer_; + } + + procinfo_iterator & operator++() noexcept { + if ( nullptr != procinfo_) { + length_ -= procinfo_->Size; + if ( 0 != length_) { + procinfo_ = reinterpret_cast< SLPI * >( reinterpret_cast< BYTE * >( procinfo_) + procinfo_->Size); + } else { + LocalFree( buffer_); + buffer_ = nullptr; + procinfo_ = nullptr; + } + } + return * this; + } + + procinfo_iterator operator++( int) { + procinfo_iterator tmp( * this); + operator++(); + return tmp; + } + + SLPI * operator->() noexcept { + return procinfo_; + } + + bool operator==( procinfo_iterator const& other) const noexcept { + return other.buffer_ == buffer_ && other.procinfo_ == procinfo_ && other.length_ == length_; + } + + bool operator!=( procinfo_iterator const& other) const noexcept { + return ! ( * this == other); + } +}; + +std::set< std::uint32_t > compute_cpu_set( WORD group_id, KAFFINITY mask) { + std::set< std::uint32_t > cpus; + for ( int i = 0; i < sizeof( mask) * 8; ++i) { + if ( mask & ( static_cast< KAFFINITY >( 1) << i) ) { + cpus.insert( static_cast< std::uint32_t >( 64 * group_id + i) ); + } + } + return cpus; +} + +} + +namespace boost { +namespace fibers { +namespace numa { + +std::vector< node > topology() { + std::vector< node > topo; + procinfo_iterator e; + for ( procinfo_iterator i{ RelationNumaNode }; i != e; ++i) { + node n; + n.id = static_cast< std::uint32_t >( i->NumaNode.NodeNumber); + auto cpus = compute_cpu_set( i->NumaNode.GroupMask.Group, i->NumaNode.GroupMask.Mask); + for ( auto cpu_id : cpus) { + n.logical_cpus.insert( static_cast< std::uint32_t >( cpu_id) ); + } + topo.push_back( n); + } + // fake NUMA distance + std::size_t size = topo.size(); + for ( auto & n : topo) { + for ( std::size_t i = 0; i < size; ++i) { + n.distance.push_back( n.id == i ? 10 : 20); + } + } + return topo; +} + +}}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif diff --git a/src/boost/libs/fiber/src/properties.cpp b/src/boost/libs/fiber/src/properties.cpp new file mode 100644 index 00000000..c8d14501 --- /dev/null +++ b/src/boost/libs/fiber/src/properties.cpp @@ -0,0 +1,40 @@ +// Copyright Oliver Kowalke 2013. +// 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 "boost/fiber/properties.hpp" + +#include <boost/assert.hpp> + +#include "boost/fiber/algo/algorithm.hpp" +#include "boost/fiber/scheduler.hpp" +#include "boost/fiber/context.hpp" + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { + +void +fiber_properties::notify() noexcept { + BOOST_ASSERT( nullptr != algo_); + // Application code might change an important property for any fiber at + // any time. The fiber in question might be ready, running or waiting. + // Significantly, only a fiber which is ready but not actually running is + // in the sched_algorithm's ready queue. Don't bother the sched_algorithm + // with a change to a fiber it's not currently tracking: it will do the + // right thing next time the fiber is passed to its awakened() method. + if ( ctx_->ready_is_linked() ) { + static_cast< algo::algorithm_with_properties_base * >( algo_)-> + property_change_( ctx_, this); + } +} + +}} // boost::fibers + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif diff --git a/src/boost/libs/fiber/src/recursive_mutex.cpp b/src/boost/libs/fiber/src/recursive_mutex.cpp new file mode 100644 index 00000000..081559d3 --- /dev/null +++ b/src/boost/libs/fiber/src/recursive_mutex.cpp @@ -0,0 +1,83 @@ + +// Copyright Oliver Kowalke 2013. +// 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 "boost/fiber/recursive_mutex.hpp" + +#include <algorithm> +#include <functional> + +#include "boost/fiber/exceptions.hpp" +#include "boost/fiber/scheduler.hpp" + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { + +void +recursive_mutex::lock() { + while ( true) { + context * active_ctx = context::active(); + // store this fiber in order to be notified later + detail::spinlock_lock lk{ wait_queue_splk_ }; + if ( active_ctx == owner_) { + ++count_; + return; + } else if ( nullptr == owner_) { + owner_ = active_ctx; + count_ = 1; + return; + } + BOOST_ASSERT( ! active_ctx->wait_is_linked() ); + active_ctx->wait_link( wait_queue_); + // suspend this fiber + active_ctx->suspend( lk); + BOOST_ASSERT( ! active_ctx->wait_is_linked() ); + } +} + +bool +recursive_mutex::try_lock() noexcept { + context * active_ctx = context::active(); + detail::spinlock_lock lk{ wait_queue_splk_ }; + if ( nullptr == owner_) { + owner_ = active_ctx; + count_ = 1; + } else if ( active_ctx == owner_) { + ++count_; + } + lk.unlock(); + // let other fiber release the lock + context::active()->yield(); + return active_ctx == owner_; +} + +void +recursive_mutex::unlock() { + context * active_ctx = context::active(); + detail::spinlock_lock lk( wait_queue_splk_); + if ( BOOST_UNLIKELY( active_ctx != owner_) ) { + throw lock_error( + std::make_error_code( std::errc::operation_not_permitted), + "boost fiber: no privilege to perform the operation"); + } + if ( 0 == --count_) { + owner_ = nullptr; + if ( ! wait_queue_.empty() ) { + context * ctx = & wait_queue_.front(); + wait_queue_.pop_front(); + active_ctx->schedule( ctx); + } + } +} + +}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif diff --git a/src/boost/libs/fiber/src/recursive_timed_mutex.cpp b/src/boost/libs/fiber/src/recursive_timed_mutex.cpp new file mode 100644 index 00000000..08e48d27 --- /dev/null +++ b/src/boost/libs/fiber/src/recursive_timed_mutex.cpp @@ -0,0 +1,123 @@ + +// Copyright Oliver Kowalke 2013. +// 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 "boost/fiber/recursive_timed_mutex.hpp" + +#include <algorithm> +#include <functional> + +#include "boost/fiber/exceptions.hpp" +#include "boost/fiber/scheduler.hpp" + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { + +bool +recursive_timed_mutex::try_lock_until_( std::chrono::steady_clock::time_point const& timeout_time) noexcept { + while ( true) { + if ( std::chrono::steady_clock::now() > timeout_time) { + return false; + } + context * active_ctx = context::active(); + // store this fiber in order to be notified later + detail::spinlock_lock lk{ wait_queue_splk_ }; + if ( active_ctx == owner_) { + ++count_; + return true; + } else if ( nullptr == owner_) { + owner_ = active_ctx; + count_ = 1; + return true; + } + BOOST_ASSERT( ! active_ctx->wait_is_linked() ); + active_ctx->wait_link( wait_queue_); + active_ctx->twstatus.store( reinterpret_cast< std::intptr_t >( this), std::memory_order_release); + // suspend this fiber until notified or timed-out + if ( ! active_ctx->wait_until( timeout_time, lk) ) { + // remove fiber from wait-queue + lk.lock(); + wait_queue_.remove( * active_ctx); + return false; + } + BOOST_ASSERT( ! active_ctx->wait_is_linked() ); + } +} + +void +recursive_timed_mutex::lock() { + while ( true) { + context * active_ctx = context::active(); + // store this fiber in order to be notified later + detail::spinlock_lock lk{ wait_queue_splk_ }; + if ( active_ctx == owner_) { + ++count_; + return; + } else if ( nullptr == owner_) { + owner_ = active_ctx; + count_ = 1; + return; + } + BOOST_ASSERT( ! active_ctx->wait_is_linked() ); + active_ctx->twstatus.store( static_cast< std::intptr_t >( 0), std::memory_order_release); + active_ctx->wait_link( wait_queue_); + // suspend this fiber + active_ctx->suspend( lk); + BOOST_ASSERT( ! active_ctx->wait_is_linked() ); + } +} + +bool +recursive_timed_mutex::try_lock() noexcept { + context * active_ctx = context::active(); + detail::spinlock_lock lk{ wait_queue_splk_ }; + if ( nullptr == owner_) { + owner_ = active_ctx; + count_ = 1; + } else if ( active_ctx == owner_) { + ++count_; + } + lk.unlock(); + // let other fiber release the lock + active_ctx->yield(); + return active_ctx == owner_; +} + +void +recursive_timed_mutex::unlock() { + context * active_ctx = context::active(); + detail::spinlock_lock lk{ wait_queue_splk_ }; + if ( BOOST_UNLIKELY( active_ctx != owner_) ) { + throw lock_error{ + std::make_error_code( std::errc::operation_not_permitted), + "boost fiber: no privilege to perform the operation" }; + } + if ( 0 == --count_) { + owner_ = nullptr; + if ( ! wait_queue_.empty() ) { + context * ctx = & wait_queue_.front(); + wait_queue_.pop_front(); + std::intptr_t expected = reinterpret_cast< std::intptr_t >( this); + if ( ctx->twstatus.compare_exchange_strong( expected, static_cast< std::intptr_t >( -1), std::memory_order_acq_rel) ) { + // notify context + active_ctx->schedule( ctx); + } else if ( static_cast< std::intptr_t >( 0) == expected) { + // no timed-wait op. + // notify context + active_ctx->schedule( ctx); + } + } + } +} + +}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif diff --git a/src/boost/libs/fiber/src/scheduler.cpp b/src/boost/libs/fiber/src/scheduler.cpp new file mode 100644 index 00000000..1d8f992a --- /dev/null +++ b/src/boost/libs/fiber/src/scheduler.cpp @@ -0,0 +1,419 @@ + +// Copyright Oliver Kowalke 2013. +// 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 "boost/fiber/scheduler.hpp" + +#include <chrono> +#include <mutex> + +#include <boost/assert.hpp> + +#include "boost/fiber/algo/round_robin.hpp" +#include "boost/fiber/context.hpp" +#include "boost/fiber/exceptions.hpp" + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { + +void +scheduler::release_terminated_() noexcept { + while ( ! terminated_queue_.empty() ) { + context * ctx = & terminated_queue_.front(); + terminated_queue_.pop_front(); + BOOST_ASSERT( ctx->is_context( type::worker_context) ); + BOOST_ASSERT( ! ctx->is_context( type::pinned_context) ); + BOOST_ASSERT( this == ctx->get_scheduler() ); + BOOST_ASSERT( ctx->is_resumable() ); + BOOST_ASSERT( ! ctx->worker_is_linked() ); + BOOST_ASSERT( ! ctx->ready_is_linked() ); +#if ! defined(BOOST_FIBERS_NO_ATOMICS) + BOOST_ASSERT( ! ctx->remote_ready_is_linked() ); +#endif + BOOST_ASSERT( ! ctx->sleep_is_linked() ); + BOOST_ASSERT( ! ctx->wait_is_linked() ); + BOOST_ASSERT( ctx->wait_queue_.empty() ); + BOOST_ASSERT( ctx->terminated_); + // if last reference, e.g. fiber::join() or fiber::detach() + // have been already called, this will call ~context(), + // the context is automatically removeid from worker-queue + intrusive_ptr_release( ctx); + } +} + +#if ! defined(BOOST_FIBERS_NO_ATOMICS) +void +scheduler::remote_ready2ready_() noexcept { + remote_ready_queue_type tmp; + detail::spinlock_lock lk{ remote_ready_splk_ }; + remote_ready_queue_.swap( tmp); + lk.unlock(); + // get context from remote ready-queue + while ( ! tmp.empty() ) { + context * ctx = & tmp.front(); + tmp.pop_front(); + // ctx was signaled from remote (other thread) + // ctx might have been already resumed because of + // its wait-op. has been already timed out and + // thus it was already pushed to the ready-queue + if ( ! ctx->ready_is_linked() ) { + // store context in local queues + schedule( ctx); + } + } +} +#endif + +void +scheduler::sleep2ready_() noexcept { + // move context which the deadline has reached + // to ready-queue + // sleep-queue is sorted (ascending) + std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); + sleep_queue_type::iterator e = sleep_queue_.end(); + for ( sleep_queue_type::iterator i = sleep_queue_.begin(); i != e;) { + context * ctx = & ( * i); + // dipatcher context must never be pushed to sleep-queue + BOOST_ASSERT( ! ctx->is_context( type::dispatcher_context) ); + BOOST_ASSERT( main_ctx_ == ctx || ctx->worker_is_linked() ); + BOOST_ASSERT( ! ctx->ready_is_linked() ); +#if ! defined(BOOST_FIBERS_NO_ATOMICS) + BOOST_ASSERT( ! ctx->remote_ready_is_linked() ); +#endif + BOOST_ASSERT( ! ctx->terminated_is_linked() ); + // set fiber to state_ready if deadline was reached + if ( ctx->tp_ <= now) { + // remove context from sleep-queue + i = sleep_queue_.erase( i); + // reset sleep-tp + ctx->tp_ = (std::chrono::steady_clock::time_point::max)(); + std::intptr_t prev = ctx->twstatus.exchange( -2); + if ( static_cast< std::intptr_t >( -1) == prev) { + // timed-wait op.: timeout after notify + continue; + } + // prev == 0: no timed-wait op. + // prev == <any>: timed-wait op., timeout before notify + // store context in local queues + schedule( ctx); + } else { + break; // first context with now < deadline + } + } +} + +scheduler::scheduler() noexcept : + algo_{ new algo::round_robin() } { +} + +scheduler::~scheduler() { + BOOST_ASSERT( nullptr != main_ctx_); + BOOST_ASSERT( nullptr != dispatcher_ctx_.get() ); + BOOST_ASSERT( context::active() == main_ctx_); + // signal dispatcher-context termination + shutdown_ = true; + // resume pending fibers + // by joining dispatcher-context + dispatcher_ctx_->join(); + // no context' in worker-queue + BOOST_ASSERT( worker_queue_.empty() ); + BOOST_ASSERT( terminated_queue_.empty() ); + BOOST_ASSERT( sleep_queue_.empty() ); + // set active context to nullptr + context::reset_active(); + // deallocate dispatcher-context + BOOST_ASSERT( ! dispatcher_ctx_->ready_is_linked() ); + dispatcher_ctx_.reset(); + // set main-context to nullptr + main_ctx_ = nullptr; +} + +boost::context::fiber +scheduler::dispatch() noexcept { + BOOST_ASSERT( context::active() == dispatcher_ctx_); + for (;;) { + if ( shutdown_) { + // notify sched-algorithm about termination + algo_->notify(); + if ( worker_queue_.empty() ) { + break; + } + } + // release terminated context' + release_terminated_(); +#if ! defined(BOOST_FIBERS_NO_ATOMICS) + // get context' from remote ready-queue + remote_ready2ready_(); +#endif + // get sleeping context' + // must be called after remote_ready2ready_() + sleep2ready_(); + // get next ready context + context * ctx = algo_->pick_next(); + if ( nullptr != ctx) { + BOOST_ASSERT( ctx->is_resumable() ); + BOOST_ASSERT( ! ctx->ready_is_linked() ); +#if ! defined(BOOST_FIBERS_NO_ATOMICS) + BOOST_ASSERT( ! ctx->remote_ready_is_linked() ); +#endif + BOOST_ASSERT( ! ctx->sleep_is_linked() ); + BOOST_ASSERT( ! ctx->terminated_is_linked() ); + // no test for '! ctx->wait_is_linked()' because + // context is registered in wait-queue of sync. primitives + // via wait_for()/wait_until() + // push dispatcher-context to ready-queue + // so that ready-queue never becomes empty + ctx->resume( dispatcher_ctx_.get() ); + BOOST_ASSERT( context::active() == dispatcher_ctx_.get() ); + } else { + // no ready context, wait till signaled + // set deadline to highest value + std::chrono::steady_clock::time_point suspend_time = + (std::chrono::steady_clock::time_point::max)(); + // get lowest deadline from sleep-queue + sleep_queue_type::iterator i = sleep_queue_.begin(); + if ( sleep_queue_.end() != i) { + suspend_time = i->tp_; + } + // no ready context, wait till signaled + algo_->suspend_until( suspend_time); + } + } + // release termianted context' + release_terminated_(); + // return to main-context + return main_ctx_->suspend_with_cc(); +} + +void +scheduler::schedule( context * ctx) noexcept { + BOOST_ASSERT( nullptr != ctx); + BOOST_ASSERT( ! ctx->ready_is_linked() ); +#if ! defined(BOOST_FIBERS_NO_ATOMICS) + BOOST_ASSERT( ! ctx->remote_ready_is_linked() ); +#endif + BOOST_ASSERT( ! ctx->terminated_is_linked() ); + // remove context ctx from sleep-queue + // (might happen if blocked in timed_mutex::try_lock_until()) + if ( ctx->sleep_is_linked() ) { + // unlink it from sleep-queue + ctx->sleep_unlink(); + } + // push new context to ready-queue + algo_->awakened( ctx); +} + +#if ! defined(BOOST_FIBERS_NO_ATOMICS) +void +scheduler::schedule_from_remote( context * ctx) noexcept { + BOOST_ASSERT( nullptr != ctx); + // another thread might signal the main-context of this thread + BOOST_ASSERT( ! ctx->is_context( type::dispatcher_context) ); + BOOST_ASSERT( this == ctx->get_scheduler() ); + BOOST_ASSERT( ! ctx->ready_is_linked() ); + BOOST_ASSERT( ! ctx->remote_ready_is_linked() ); + BOOST_ASSERT( ! ctx->terminated_is_linked() ); + BOOST_ASSERT( ! ctx->wait_is_linked() ); + // protect for concurrent access + detail::spinlock_lock lk{ remote_ready_splk_ }; + BOOST_ASSERT( ! shutdown_); + BOOST_ASSERT( nullptr != main_ctx_); + BOOST_ASSERT( nullptr != dispatcher_ctx_.get() ); + // push new context to remote ready-queue + ctx->remote_ready_link( remote_ready_queue_); + lk.unlock(); + // notify scheduler + algo_->notify(); +} +#endif + +boost::context::fiber +scheduler::terminate( detail::spinlock_lock & lk, context * ctx) noexcept { + BOOST_ASSERT( nullptr != ctx); + BOOST_ASSERT( context::active() == ctx); + BOOST_ASSERT( this == ctx->get_scheduler() ); + BOOST_ASSERT( ctx->is_context( type::worker_context) ); + BOOST_ASSERT( ! ctx->is_context( type::pinned_context) ); + BOOST_ASSERT( ! ctx->ready_is_linked() ); +#if ! defined(BOOST_FIBERS_NO_ATOMICS) + BOOST_ASSERT( ! ctx->remote_ready_is_linked() ); +#endif + BOOST_ASSERT( ! ctx->sleep_is_linked() ); + BOOST_ASSERT( ! ctx->terminated_is_linked() ); + BOOST_ASSERT( ! ctx->wait_is_linked() ); + BOOST_ASSERT( ctx->wait_queue_.empty() ); + // store the terminated fiber in the terminated-queue + // the dispatcher-context will call + ctx->terminated_link( terminated_queue_); + // remove from the worker-queue + ctx->worker_unlink(); + // release lock + lk.unlock(); + // resume another fiber + return algo_->pick_next()->suspend_with_cc(); +} + +void +scheduler::yield( context * ctx) noexcept { + BOOST_ASSERT( nullptr != ctx); + BOOST_ASSERT( context::active() == ctx); + BOOST_ASSERT( ctx->is_context( type::worker_context) || ctx->is_context( type::main_context) ); + BOOST_ASSERT( ! ctx->ready_is_linked() ); +#if ! defined(BOOST_FIBERS_NO_ATOMICS) + BOOST_ASSERT( ! ctx->remote_ready_is_linked() ); +#endif + BOOST_ASSERT( ! ctx->sleep_is_linked() ); + BOOST_ASSERT( ! ctx->terminated_is_linked() ); + BOOST_ASSERT( ! ctx->wait_is_linked() ); + // resume another fiber + algo_->pick_next()->resume( ctx); +} + +bool +scheduler::wait_until( context * ctx, + std::chrono::steady_clock::time_point const& sleep_tp) noexcept { + BOOST_ASSERT( nullptr != ctx); + BOOST_ASSERT( context::active() == ctx); + BOOST_ASSERT( ctx->is_context( type::worker_context) || ctx->is_context( type::main_context) ); + BOOST_ASSERT( ! ctx->ready_is_linked() ); +#if ! defined(BOOST_FIBERS_NO_ATOMICS) + BOOST_ASSERT( ! ctx->remote_ready_is_linked() ); +#endif + BOOST_ASSERT( ! ctx->sleep_is_linked() ); + BOOST_ASSERT( ! ctx->terminated_is_linked() ); + BOOST_ASSERT( ! ctx->wait_is_linked() ); + ctx->tp_ = sleep_tp; + ctx->sleep_link( sleep_queue_); + // resume another context + algo_->pick_next()->resume(); + // context has been resumed + // check if deadline has reached + return std::chrono::steady_clock::now() < sleep_tp; +} + +bool +scheduler::wait_until( context * ctx, + std::chrono::steady_clock::time_point const& sleep_tp, + detail::spinlock_lock & lk) noexcept { + BOOST_ASSERT( nullptr != ctx); + BOOST_ASSERT( context::active() == ctx); + BOOST_ASSERT( ctx->is_context( type::worker_context) || ctx->is_context( type::main_context) ); + BOOST_ASSERT( ! ctx->ready_is_linked() ); +#if ! defined(BOOST_FIBERS_NO_ATOMICS) + BOOST_ASSERT( ! ctx->remote_ready_is_linked() ); +#endif + BOOST_ASSERT( ! ctx->sleep_is_linked() ); + BOOST_ASSERT( ! ctx->terminated_is_linked() ); + // ctx->wait_is_linked() might return true + // if context was locked inside timed_mutex::try_lock_until() + // push active context to sleep-queue + ctx->tp_ = sleep_tp; + ctx->sleep_link( sleep_queue_); + // resume another context + algo_->pick_next()->resume( lk); + // context has been resumed + // check if deadline has reached + return std::chrono::steady_clock::now() < sleep_tp; +} + +void +scheduler::suspend() noexcept { + // resume another context + algo_->pick_next()->resume(); +} + +void +scheduler::suspend( detail::spinlock_lock & lk) noexcept { + // resume another context + algo_->pick_next()->resume( lk); +} + +bool +scheduler::has_ready_fibers() const noexcept { + return algo_->has_ready_fibers(); +} + +void +scheduler::set_algo( algo::algorithm::ptr_t algo) noexcept { + // move remaining cotnext in current scheduler to new one + while ( algo_->has_ready_fibers() ) { + algo->awakened( algo_->pick_next() ); + } + algo_ = std::move( algo); +} + +void +scheduler::attach_main_context( context * ctx) noexcept { + BOOST_ASSERT( nullptr != ctx); + // main-context represents the execution context created + // by the system, e.g. main()- or thread-context + // should not be in worker-queue + main_ctx_ = ctx; + main_ctx_->scheduler_ = this; +} + +void +scheduler::attach_dispatcher_context( intrusive_ptr< context > ctx) noexcept { + BOOST_ASSERT( ctx); + // dispatcher context has to handle + // - remote ready context' + // - sleeping context' + // - extern event-loops + // - suspending the thread if ready-queue is empty (waiting on external event) + // should not be in worker-queue + dispatcher_ctx_.swap( ctx); + // add dispatcher-context to ready-queue + // so it is the first element in the ready-queue + // if the main context tries to suspend the first time + // the dispatcher-context is resumed and + // scheduler::dispatch() is executed + dispatcher_ctx_->scheduler_ = this; + algo_->awakened( dispatcher_ctx_.get() ); +} + +void +scheduler::attach_worker_context( context * ctx) noexcept { + BOOST_ASSERT( nullptr != ctx); + BOOST_ASSERT( nullptr == ctx->get_scheduler() ); + BOOST_ASSERT( ! ctx->ready_is_linked() ); +#if ! defined(BOOST_FIBERS_NO_ATOMICS) + BOOST_ASSERT( ! ctx->remote_ready_is_linked() ); +#endif + BOOST_ASSERT( ! ctx->sleep_is_linked() ); + BOOST_ASSERT( ! ctx->terminated_is_linked() ); + BOOST_ASSERT( ! ctx->wait_is_linked() ); + BOOST_ASSERT( ! ctx->worker_is_linked() ); + ctx->worker_link( worker_queue_); + ctx->scheduler_ = this; + // an attached context must belong at least to worker-queue +} + +void +scheduler::detach_worker_context( context * ctx) noexcept { + BOOST_ASSERT( nullptr != ctx); + BOOST_ASSERT( ! ctx->ready_is_linked() ); +#if ! defined(BOOST_FIBERS_NO_ATOMICS) + BOOST_ASSERT( ! ctx->remote_ready_is_linked() ); +#endif + BOOST_ASSERT( ! ctx->sleep_is_linked() ); + BOOST_ASSERT( ! ctx->terminated_is_linked() ); + BOOST_ASSERT( ! ctx->wait_is_linked() ); + BOOST_ASSERT( ctx->worker_is_linked() ); + BOOST_ASSERT( ! ctx->is_context( type::pinned_context) ); + ctx->worker_unlink(); + BOOST_ASSERT( ! ctx->worker_is_linked() ); + ctx->scheduler_ = nullptr; + // a detached context must not belong to any queue +} + +}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif diff --git a/src/boost/libs/fiber/src/timed_mutex.cpp b/src/boost/libs/fiber/src/timed_mutex.cpp new file mode 100644 index 00000000..1cbaf313 --- /dev/null +++ b/src/boost/libs/fiber/src/timed_mutex.cpp @@ -0,0 +1,118 @@ + +// Copyright Oliver Kowalke 2013. +// 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 "boost/fiber/timed_mutex.hpp" + +#include <algorithm> +#include <functional> + +#include "boost/fiber/exceptions.hpp" +#include "boost/fiber/scheduler.hpp" + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { + +bool +timed_mutex::try_lock_until_( std::chrono::steady_clock::time_point const& timeout_time) noexcept { + while ( true) { + if ( std::chrono::steady_clock::now() > timeout_time) { + return false; + } + context * active_ctx = context::active(); + // store this fiber in order to be notified later + detail::spinlock_lock lk{ wait_queue_splk_ }; + if ( nullptr == owner_) { + owner_ = active_ctx; + return true; + } + BOOST_ASSERT( ! active_ctx->wait_is_linked() ); + active_ctx->wait_link( wait_queue_); + active_ctx->twstatus.store( reinterpret_cast< std::intptr_t >( this), std::memory_order_release); + // suspend this fiber until notified or timed-out + if ( ! active_ctx->wait_until( timeout_time, lk) ) { + // remove fiber from wait-queue + lk.lock(); + wait_queue_.remove( * active_ctx); + return false; + } + BOOST_ASSERT( ! active_ctx->wait_is_linked() ); + } +} + +void +timed_mutex::lock() { + while ( true) { + context * active_ctx = context::active(); + // store this fiber in order to be notified later + detail::spinlock_lock lk{ wait_queue_splk_ }; + if ( BOOST_UNLIKELY( active_ctx == owner_) ) { + throw lock_error{ + std::make_error_code( std::errc::resource_deadlock_would_occur), + "boost fiber: a deadlock is detected" }; + } else if ( nullptr == owner_) { + owner_ = active_ctx; + return; + } + BOOST_ASSERT( ! active_ctx->wait_is_linked() ); + active_ctx->wait_link( wait_queue_); + active_ctx->twstatus.store( static_cast< std::intptr_t >( 0), std::memory_order_release); + // suspend this fiber + active_ctx->suspend( lk); + BOOST_ASSERT( ! active_ctx->wait_is_linked() ); + } +} + +bool +timed_mutex::try_lock() { + context * active_ctx = context::active(); + detail::spinlock_lock lk{ wait_queue_splk_ }; + if ( BOOST_UNLIKELY( active_ctx == owner_) ) { + throw lock_error{ + std::make_error_code( std::errc::resource_deadlock_would_occur), + "boost fiber: a deadlock is detected" }; + } else if ( nullptr == owner_) { + owner_ = active_ctx; + } + lk.unlock(); + // let other fiber release the lock + active_ctx->yield(); + return active_ctx == owner_; +} + +void +timed_mutex::unlock() { + context * active_ctx = context::active(); + detail::spinlock_lock lk{ wait_queue_splk_ }; + if ( BOOST_UNLIKELY( active_ctx != owner_) ) { + throw lock_error{ + std::make_error_code( std::errc::operation_not_permitted), + "boost fiber: no privilege to perform the operation" }; + } + owner_ = nullptr; + if ( ! wait_queue_.empty() ) { + context * ctx = & wait_queue_.front(); + wait_queue_.pop_front(); + std::intptr_t expected = reinterpret_cast< std::intptr_t >( this); + if ( ctx->twstatus.compare_exchange_strong( expected, static_cast< std::intptr_t >( -1), std::memory_order_acq_rel) ) { + // notify context + active_ctx->schedule( ctx); + } else if ( static_cast< std::intptr_t >( 0) == expected) { + // no timed-wait op. + // notify context + active_ctx->schedule( ctx); + } + } +} + +}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif diff --git a/src/boost/libs/fiber/test/Jamfile.v2 b/src/boost/libs/fiber/test/Jamfile.v2 new file mode 100644 index 00000000..fac5c9e2 --- /dev/null +++ b/src/boost/libs/fiber/test/Jamfile.v2 @@ -0,0 +1,1294 @@ +# Boost.Fiber Library Tests Jamfile + +# Copyright Oliver Kowalke 2013. +# 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) + +import common ; +import feature ; +import indirect ; +import modules ; +import os ; +import path ; +import testing ; +import toolset ; +import ../../config/checks/config : requires ; + +project boost/fiber/test + : requirements + <library>../../test/build//boost_unit_test_framework + <library>/boost/context//boost_context + <library>/boost/fiber//boost_fiber + <library>/boost/thread//boost_thread + <target-os>solaris:<linkflags>"-llgrp" + <target-os>windows:<define>_WIN32_WINNT=0x0601 + <toolset>gcc,<segmented-stacks>on:<cxxflags>-fsplit-stack + <toolset>gcc,<segmented-stacks>on:<cxxflags>-DBOOST_USE_SEGMENTED_STACKS + <toolset>clang,<segmented-stacks>on:<cxxflags>-fsplit-stack + <toolset>clang,<segmented-stacks>on:<cxxflags>-DBOOST_USE_SEGMENTED_STACKS + <link>static + <threading>multi + <optimization>speed + <variant>release + ; + + +rule native-impl ( properties * ) +{ + local result ; + if ( <target-os>darwin in $(properties) || <target-os>android in $(properties) ) + { + result = <build>no ; + } + else if ( ! ( <target-os>windows in $(properties) ) ) + { + result = <context-impl>ucontext ; + } + else + { + result = <context-impl>winfib ; + } + return $(result) ; +} + + +# tests using assembler API +test-suite asm : +[ run test_fiber_post.cpp : + : : + <context-impl>fcontext + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_fiber_post_asm ] + +[ run test_fiber_dispatch.cpp : + : : + <context-impl>fcontext + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_fiber_dispatch_asm ] + +[ run test_mutex_post.cpp : + : : + <context-impl>fcontext + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_mutex_post_asm ] + +[ run test_mutex_dispatch.cpp : + : : + <context-impl>fcontext + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_mutex_dispatch_asm ] + +[ run test_condition_variable_any_post.cpp : + : : + <context-impl>fcontext + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_condition_variable_any_post_asm ] + +[ run test_condition_variable_any_dispatch.cpp : + : : + <context-impl>fcontext + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_condition_variable_any_dispatch_asm ] + +[ run test_condition_variable_post.cpp : + : : + <context-impl>fcontext + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_condition_variable_post_asm ] + +[ run test_condition_variable_dispatch.cpp : + : : + <context-impl>fcontext + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_condition_variable_dispatch_asm ] + +[ run test_barrier_post.cpp : + : : + <context-impl>fcontext + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_barrier_post_asm ] + +[ run test_barrier_dispatch.cpp : + : : + <context-impl>fcontext + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_barrier_dispatch_asm ] + +[ run test_buffered_channel_post.cpp : + : : + <context-impl>fcontext + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_buffered_channel_post_asm ] + +[ run test_buffered_channel_dispatch.cpp : + : : + <context-impl>fcontext + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_buffered_channel_dispatch_asm ] + +[ run test_unbuffered_channel_post.cpp : + : : + <context-impl>fcontext + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_unbuffered_channel_post_asm ] + +[ run test_unbuffered_channel_dispatch.cpp : + : : + <context-impl>fcontext + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_unbuffered_channel_dispatch_asm ] + +[ run test_fss_post.cpp : + : : + <context-impl>fcontext + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_fss_post_asm ] + +[ run test_fss_dispatch.cpp : + : : + <context-impl>fcontext + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_fss_dispatch_asm ] + +[ run test_promise_post.cpp : + : : + <context-impl>fcontext + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_promise_post_asm ] + +[ run test_promise_dispatch.cpp : + : : + <context-impl>fcontext + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_promise_dispatch_asm ] + +[ run test_future_post.cpp : + : : + <context-impl>fcontext + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_future_post_asm ] + +[ run test_future_dispatch.cpp : + : : + <context-impl>fcontext + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_future_dispatch_asm ] + +[ run test_shared_future_post.cpp : + : : + <context-impl>fcontext + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_shared_future_post_asm ] + +[ run test_shared_future_dispatch.cpp : + : : + <context-impl>fcontext + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_shared_future_dispatch_asm ] + +[ run test_packaged_task_post.cpp : + : : + <context-impl>fcontext + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_packaged_task_post_asm ] + +[ run test_packaged_task_dispatch.cpp : + : : + <context-impl>fcontext + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_packaged_task_dispatch_asm ] + +[ run test_async_post.cpp : + : : + <context-impl>fcontext + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_async_post_asm ] + +[ run test_async_dispatch.cpp : + : : + <context-impl>fcontext + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_async_dispatch_asm ] ; + + +# tests using native API +test-suite native : +[ run test_fiber_post.cpp : + : : + <conditional>@native-impl + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_fiber_post_native ] + +[ run test_fiber_dispatch.cpp : + : : + <conditional>@native-impl + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_fiber_dispatch_native ] + +[ run test_mutex_post.cpp : + : : + <conditional>@native-impl + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_mutex_post_native ] + +[ run test_mutex_dispatch.cpp : + : : + <conditional>@native-impl + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_mutex_dispatch_native ] + +[ run test_condition_variable_any_post.cpp : + : : + <conditional>@native-impl + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_cond_var_any_post_native ] + +[ run test_condition_variable_any_dispatch.cpp : + : : + <conditional>@native-impl + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_cond_vare_any_dispatch_native ] + +[ run test_condition_variable_post.cpp : + : : + <conditional>@native-impl + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_cond_var_post_native ] + +[ run test_condition_variable_dispatch.cpp : + : : + <conditional>@native-impl + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_cond_var_dispatch_native ] + +[ run test_barrier_post.cpp : + : : + <conditional>@native-impl + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_barrier_post_native ] + +[ run test_barrier_dispatch.cpp : + : : + <conditional>@native-impl + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_barrier_dispatch_native ] + +[ run test_buffered_channel_post.cpp : + : : + <conditional>@native-impl + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_buf_channel_post_native ] + +[ run test_buffered_channel_dispatch.cpp : + : : + <conditional>@native-impl + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_buf_channel_dispatch_native ] + +[ run test_unbuffered_channel_post.cpp : + : : + <conditional>@native-impl + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_unbuf_channel_post_native ] + +[ run test_unbuffered_channel_dispatch.cpp : + : : + <conditional>@native-impl + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_unbuf_channel_dispatch_native ] + +[ run test_fss_post.cpp : + : : + <conditional>@native-impl + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_fss_post_native ] + +[ run test_fss_dispatch.cpp : + : : + <conditional>@native-impl + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_fss_dispatch_native ] + +[ run test_promise_post.cpp : + : : + <conditional>@native-impl + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_promise_post_native ] + +[ run test_promise_dispatch.cpp : + : : + <conditional>@native-impl + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_promise_dispatch_native ] + +[ run test_future_post.cpp : + : : + <conditional>@native-impl + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_future_post_native ] + +[ run test_future_dispatch.cpp : + : : + <conditional>@native-impl + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_future_dispatch_native ] + +[ run test_shared_future_post.cpp : + : : + <conditional>@native-impl + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_shared_future_post_native ] + +[ run test_shared_future_dispatch.cpp : + : : + <conditional>@native-impl + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_shared_future_dispatch_native ] + +[ run test_packaged_task_post.cpp : + : : + <conditional>@native-impl + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_packaged_task_post_native ] + +[ run test_packaged_task_dispatch.cpp : + : : + <conditional>@native-impl + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_packaged_task_dispatch_native ] + +[ run test_async_post.cpp : + : : + <conditional>@native-impl + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_async_post_native ] + +[ run test_async_dispatch.cpp : + : : + <conditional>@native-impl + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_async_dispatch_native ] ; + + +#etra tests using asm API +test-suite extra-asm : +[ run test_mutex_mt_post.cpp : + : : + <context-impl>fcontext + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_mutex_mt_post_asm ] + +[ run test_mutex_mt_dispatch.cpp : + : : + <context-impl>fcontext + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_mutex_mt_dispatch_asm ] + +[ run test_condition_mt_post.cpp : + : : + <context-impl>fcontext + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_condition_mt_post_asm ] + +[ run test_condition_mt_dispatch.cpp : + : : + <context-impl>fcontext + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_condition_mt_dispatch_asm ] + +[ run test_future_mt_post.cpp : + : : + <context-impl>fcontext + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_future_mt_post_asm ] + +[ run test_future_mt_dispatch.cpp : + : : + <context-impl>fcontext + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_future_mt_dispatch_asm ] ; + + +#etra tests using native API +test-suite extra-native : +[ run test_mutex_mt_post.cpp : + : : + <conditional>@native-impl + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_mutex_mt_post_native ] + +[ run test_mutex_mt_dispatch.cpp : + : : + <conditional>@native-impl + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_mutex_mt_dispatch_native ] + +[ run test_condition_mt_post.cpp : + : : + <conditional>@native-impl + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_condition_mt_post_native ] + +[ run test_condition_mt_dispatch.cpp : + : : + <conditional>@native-impl + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_condition_mt_dispatch_native ] + +[ run test_future_mt_post.cpp : + : : + <conditional>@native-impl + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_future_mt_post_native ] + +[ run test_future_mt_dispatch.cpp : + : : + <conditional>@native-impl + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_mutex + cxx11_hdr_thread + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_thread_local + cxx11_variadic_templates ] + : test_future_mt_dispatch_native ] ; + + +test-suite minimal : + asm native ; + +test-suite extra : + extra-asm extra-native ; + +explicit minmal ; +explicit extra ; + +test-suite full : + minimal extra ; diff --git a/src/boost/libs/fiber/test/test_async_dispatch.cpp b/src/boost/libs/fiber/test/test_async_dispatch.cpp new file mode 100644 index 00000000..55e050e2 --- /dev/null +++ b/src/boost/libs/fiber/test/test_async_dispatch.cpp @@ -0,0 +1,157 @@ +// (C) Copyright 2008-10 Anthony Williams +// 2015 Oliver Kowalke +// +// 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 <utility> +#include <memory> +#include <stdexcept> +#include <string> + +#include <boost/test/unit_test.hpp> + +#include <boost/fiber/all.hpp> + +struct A { + A() = default; + + A( A const&) = delete; + A & operator=( A const&) = delete; + + A( A && other) : + value{ other.value } { + other.value = 0; + } + + A & operator=( A && other) { + if ( this == & other) return * this; + value = other.value; + other.value = 0; + return * this; + } + + int value{ 0 }; +}; + +struct X { + int value; + + void foo( int i) { + value = i; + } +}; + +void fn1() { +} + +int fn2( int i) { + return i; +} + +int & fn3( int & i) { + return i; +} + +A fn4( A && a) { + return std::forward< A >( a); +} + +void test_async_1() { + boost::fibers::future< void > f1 = boost::fibers::async( boost::fibers::launch::dispatch, fn1); + BOOST_CHECK( f1.valid() ); + + f1.get(); +} + +void test_async_2() { + int i = 3; + boost::fibers::future< int > f1 = boost::fibers::async( boost::fibers::launch::dispatch, fn2, i); + BOOST_CHECK( f1.valid() ); + + BOOST_CHECK( i == f1.get()); +} + +void test_async_3() { + int i = 7; + boost::fibers::future< int& > f1 = boost::fibers::async( boost::fibers::launch::dispatch, fn3, std::ref( i) ); + BOOST_CHECK( f1.valid() ); + + BOOST_CHECK( & i == & f1.get()); +} + +void test_async_4() { + A a1; + a1.value = 7; + boost::fibers::future< A > f1 = boost::fibers::async( boost::fibers::launch::dispatch, fn4, std::move( a1) ); + BOOST_CHECK( f1.valid() ); + + A a2 = f1.get(); + BOOST_CHECK( 7 == a2.value); +} + +void test_async_5() { + X x = {0}; + BOOST_CHECK( 0 == x.value); + boost::fibers::future< void > f1 = boost::fibers::async( + boost::fibers::launch::dispatch, + std::bind( & X::foo, std::ref( x), 3) ); + BOOST_CHECK( f1.valid() ); + + f1.get(); + BOOST_CHECK( 3 == x.value); +} + +void test_async_6() { + X x = {0}; + BOOST_CHECK( 0 == x.value); + boost::fibers::future< void > f1 = boost::fibers::async( + boost::fibers::launch::dispatch, + std::bind( & X::foo, std::ref( x), std::placeholders::_1), 3); + BOOST_CHECK( f1.valid() ); + + f1.get(); + BOOST_CHECK( 3 == x.value); +} + +void test_async_stack_alloc() { + boost::fibers::future< void > f1 = boost::fibers::async( + boost::fibers::launch::dispatch, + std::allocator_arg, + boost::fibers::fixedsize_stack{}, + fn1); + BOOST_CHECK( f1.valid() ); + + f1.get(); +} + +void test_async_std_alloc() { + struct none {}; + boost::fibers::future< void > f1 = boost::fibers::async( + boost::fibers::launch::dispatch, + std::allocator_arg, + boost::fibers::fixedsize_stack{}, + std::allocator< none >{}, + fn1); + BOOST_CHECK( f1.valid() ); + + f1.get(); +} + + +boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) { + boost::unit_test_framework::test_suite* test = + BOOST_TEST_SUITE("Boost.Fiber: async test suite"); + + test->add(BOOST_TEST_CASE(test_async_1)); + test->add(BOOST_TEST_CASE(test_async_2)); + test->add(BOOST_TEST_CASE(test_async_3)); + test->add(BOOST_TEST_CASE(test_async_4)); + test->add(BOOST_TEST_CASE(test_async_5)); + test->add(BOOST_TEST_CASE(test_async_6)); + test->add(BOOST_TEST_CASE(test_async_stack_alloc)); + test->add(BOOST_TEST_CASE(test_async_std_alloc)); + + return test; +} diff --git a/src/boost/libs/fiber/test/test_async_post.cpp b/src/boost/libs/fiber/test/test_async_post.cpp new file mode 100644 index 00000000..95ab36ed --- /dev/null +++ b/src/boost/libs/fiber/test/test_async_post.cpp @@ -0,0 +1,131 @@ +// (C) Copyright 2008-10 Anthony Williams +// 2015 Oliver Kowalke +// +// 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 <utility> +#include <memory> +#include <stdexcept> +#include <string> + +#include <boost/test/unit_test.hpp> + +#include <boost/fiber/all.hpp> + +struct A { + A() = default; + + A( A const&) = delete; + A & operator=( A const&) = delete; + + A( A && other) : + value{ other.value } { + other.value = 0; + } + + A & operator=( A && other) { + if ( this == & other) return * this; + value = other.value; + other.value = 0; + return * this; + } + + int value{ 0 }; +}; + +struct X { + int value; + + void foo( int i) { + value = i; + } +}; + +void fn1() { +} + +int fn2( int i) { + return i; +} + +int & fn3( int & i) { + return i; +} + +A fn4( A && a) { + return std::forward< A >( a); +} + +void test_async_1() { + boost::fibers::future< void > f1 = boost::fibers::async( boost::fibers::launch::post, fn1); + BOOST_CHECK( f1.valid() ); + + f1.get(); +} + +void test_async_2() { + int i = 3; + boost::fibers::future< int > f1 = boost::fibers::async( boost::fibers::launch::post, fn2, i); + BOOST_CHECK( f1.valid() ); + + BOOST_CHECK( i == f1.get()); +} + +void test_async_3() { + int i = 7; + boost::fibers::future< int& > f1 = boost::fibers::async( boost::fibers::launch::post, fn3, std::ref( i) ); + BOOST_CHECK( f1.valid() ); + + BOOST_CHECK( & i == & f1.get()); +} + +void test_async_4() { + A a1; + a1.value = 7; + boost::fibers::future< A > f1 = boost::fibers::async( boost::fibers::launch::post, fn4, std::move( a1) ); + BOOST_CHECK( f1.valid() ); + + A a2 = f1.get(); + BOOST_CHECK( 7 == a2.value); +} + +void test_async_5() { + X x = {0}; + BOOST_CHECK( 0 == x.value); + boost::fibers::future< void > f1 = boost::fibers::async( + boost::fibers::launch::post, + std::bind( & X::foo, std::ref( x), 3) ); + BOOST_CHECK( f1.valid() ); + + f1.get(); + BOOST_CHECK( 3 == x.value); +} + +void test_async_6() { + X x = {0}; + BOOST_CHECK( 0 == x.value); + boost::fibers::future< void > f1 = boost::fibers::async( + boost::fibers::launch::post, + std::bind( & X::foo, std::ref( x), std::placeholders::_1), 3); + BOOST_CHECK( f1.valid() ); + + f1.get(); + BOOST_CHECK( 3 == x.value); +} + + +boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) { + boost::unit_test_framework::test_suite* test = + BOOST_TEST_SUITE("Boost.Fiber: async test suite"); + + test->add(BOOST_TEST_CASE(test_async_1)); + test->add(BOOST_TEST_CASE(test_async_2)); + test->add(BOOST_TEST_CASE(test_async_3)); + test->add(BOOST_TEST_CASE(test_async_4)); + test->add(BOOST_TEST_CASE(test_async_5)); + test->add(BOOST_TEST_CASE(test_async_6)); + + return test; +} diff --git a/src/boost/libs/fiber/test/test_barrier_dispatch.cpp b/src/boost/libs/fiber/test/test_barrier_dispatch.cpp new file mode 100644 index 00000000..8f1716d5 --- /dev/null +++ b/src/boost/libs/fiber/test/test_barrier_dispatch.cpp @@ -0,0 +1,71 @@ + +// Copyright Oliver Kowalke 2013. +// 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) +// +// This test is based on the tests of Boost.Thread + +#include <sstream> +#include <string> + +#include <boost/test/unit_test.hpp> + +#include <boost/fiber/all.hpp> + +int value1 = 0; +int value2 = 0; + +void fn1( boost::fibers::barrier & b) { + ++value1; + boost::this_fiber::yield(); + + b.wait(); + + ++value1; + boost::this_fiber::yield(); + ++value1; + boost::this_fiber::yield(); + ++value1; + boost::this_fiber::yield(); + ++value1; +} + +void fn2( boost::fibers::barrier & b) { + ++value2; + boost::this_fiber::yield(); + ++value2; + boost::this_fiber::yield(); + ++value2; + boost::this_fiber::yield(); + + b.wait(); + + ++value2; + boost::this_fiber::yield(); + ++value2; +} + +void test_barrier() { + value1 = 0; + value2 = 0; + + boost::fibers::barrier b( 2); + boost::fibers::fiber f1( boost::fibers::launch::dispatch, fn1, std::ref( b) ); + boost::fibers::fiber f2( boost::fibers::launch::dispatch, fn2, std::ref( b) ); + + f1.join(); + f2.join(); + + BOOST_CHECK_EQUAL( 5, value1); + BOOST_CHECK_EQUAL( 5, value2); +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) { + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Fiber: barrier test suite"); + + test->add( BOOST_TEST_CASE( & test_barrier) ); + + return test; +} diff --git a/src/boost/libs/fiber/test/test_barrier_post.cpp b/src/boost/libs/fiber/test/test_barrier_post.cpp new file mode 100644 index 00000000..15354dde --- /dev/null +++ b/src/boost/libs/fiber/test/test_barrier_post.cpp @@ -0,0 +1,71 @@ + +// Copyright Oliver Kowalke 2013. +// 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) +// +// This test is based on the tests of Boost.Thread + +#include <sstream> +#include <string> + +#include <boost/test/unit_test.hpp> + +#include <boost/fiber/all.hpp> + +int value1 = 0; +int value2 = 0; + +void fn1( boost::fibers::barrier & b) { + ++value1; + boost::this_fiber::yield(); + + b.wait(); + + ++value1; + boost::this_fiber::yield(); + ++value1; + boost::this_fiber::yield(); + ++value1; + boost::this_fiber::yield(); + ++value1; +} + +void fn2( boost::fibers::barrier & b) { + ++value2; + boost::this_fiber::yield(); + ++value2; + boost::this_fiber::yield(); + ++value2; + boost::this_fiber::yield(); + + b.wait(); + + ++value2; + boost::this_fiber::yield(); + ++value2; +} + +void test_barrier() { + value1 = 0; + value2 = 0; + + boost::fibers::barrier b( 2); + boost::fibers::fiber f1( boost::fibers::launch::post, fn1, std::ref( b) ); + boost::fibers::fiber f2( boost::fibers::launch::post, fn2, std::ref( b) ); + + f1.join(); + f2.join(); + + BOOST_CHECK_EQUAL( 5, value1); + BOOST_CHECK_EQUAL( 5, value2); +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) { + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Fiber: barrier test suite"); + + test->add( BOOST_TEST_CASE( & test_barrier) ); + + return test; +} diff --git a/src/boost/libs/fiber/test/test_buffered_channel_dispatch.cpp b/src/boost/libs/fiber/test/test_buffered_channel_dispatch.cpp new file mode 100644 index 00000000..980fdb91 --- /dev/null +++ b/src/boost/libs/fiber/test/test_buffered_channel_dispatch.cpp @@ -0,0 +1,531 @@ + +// Copyright Oliver Kowalke 2013. +// 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 <chrono> +#include <sstream> +#include <string> +#include <vector> + +#include <boost/assert.hpp> +#include <boost/test/unit_test.hpp> + +#include <boost/fiber/all.hpp> + +struct moveable { + 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; + other.state = false; + value = other.value; + other.value = -1; + return * this; + } +}; + +void test_zero_wm() { + bool thrown = false; + try { + boost::fibers::buffered_channel< int > c( 0); + } catch ( boost::fibers::fiber_error const&) { + thrown = true; + } + BOOST_CHECK( thrown); +} + +void test_push() { + boost::fibers::buffered_channel< int > c( 16); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 1) ); +} + +void test_push_closed() { + boost::fibers::buffered_channel< int > c( 16); + c.close(); + BOOST_CHECK( boost::fibers::channel_op_status::closed == c.push( 1) ); +} + +void test_try_push() { + boost::fibers::buffered_channel< int > c( 2); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 1) ); +} + +void test_try_push_closed() { + boost::fibers::buffered_channel< int > c( 2); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.try_push( 1) ); + c.close(); + BOOST_CHECK( boost::fibers::channel_op_status::closed == c.try_push( 2) ); +} + +void test_try_push_full() { + boost::fibers::buffered_channel< int > c( 2); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.try_push( 1) ); + BOOST_CHECK( boost::fibers::channel_op_status::full == c.try_push( 1) ); +} + +void test_push_wait_for() { + boost::fibers::buffered_channel< int > c( 2); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push_wait_for( 1, std::chrono::seconds( 1) ) ); +} + +void test_push_wait_for_closed() { + boost::fibers::buffered_channel< int > c( 2); + c.close(); + BOOST_CHECK( boost::fibers::channel_op_status::closed == c.push_wait_for( 1, std::chrono::seconds( 1) ) ); +} + +void test_push_wait_for_timeout() { + boost::fibers::buffered_channel< int > c( 2); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push_wait_for( 1, std::chrono::seconds( 1) ) ); + BOOST_CHECK( boost::fibers::channel_op_status::timeout == c.push_wait_for( 1, std::chrono::seconds( 1) ) ); +} + +void test_push_wait_until() { + boost::fibers::buffered_channel< int > c( 2); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push_wait_until( 1, + std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); +} + +void test_push_wait_until_closed() { + boost::fibers::buffered_channel< int > c( 2); + c.close(); + BOOST_CHECK( boost::fibers::channel_op_status::closed == c.push_wait_until( 1, + std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); +} + +void test_push_wait_until_timeout() { + boost::fibers::buffered_channel< int > c( 2); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push_wait_until( 1, + std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); + BOOST_CHECK( boost::fibers::channel_op_status::timeout == c.push_wait_until( 1, + std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); +} + +void test_pop() { + boost::fibers::buffered_channel< int > c( 16); + int v1 = 2, v2 = 0; + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop( v2) ); + BOOST_CHECK_EQUAL( v1, v2); +} + +void test_pop_closed() { + boost::fibers::buffered_channel< int > c( 16); + int v1 = 2, v2 = 0; + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + c.close(); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop( v2) ); + BOOST_CHECK_EQUAL( v1, v2); + BOOST_CHECK( boost::fibers::channel_op_status::closed == c.pop( v2) ); +} + +void test_pop_success() { + boost::fibers::buffered_channel< int > c( 16); + int v1 = 2, v2 = 0; + boost::fibers::fiber f1( boost::fibers::launch::dispatch, [&c,&v2](){ + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop( v2) ); + }); + boost::fibers::fiber f2( boost::fibers::launch::dispatch, [&c,v1](){ + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + }); + f1.join(); + f2.join(); + BOOST_CHECK_EQUAL( v1, v2); +} + +void test_value_pop() { + boost::fibers::buffered_channel< int > c( 16); + int v1 = 2, v2 = 0; + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + v2 = c.value_pop(); + BOOST_CHECK_EQUAL( v1, v2); +} + +void test_value_pop_closed() { + boost::fibers::buffered_channel< int > c( 16); + int v1 = 2, v2 = 0; + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + c.close(); + v2 = c.value_pop(); + BOOST_CHECK_EQUAL( v1, v2); + bool thrown = false; + try { + c.value_pop(); + } catch ( boost::fibers::fiber_error const&) { + thrown = true; + } + BOOST_CHECK( thrown); +} + +void test_value_pop_success() { + boost::fibers::buffered_channel< int > c( 16); + int v1 = 2, v2 = 0; + boost::fibers::fiber f1( boost::fibers::launch::dispatch, [&c,&v2](){ + v2 = c.value_pop(); + }); + boost::fibers::fiber f2( boost::fibers::launch::dispatch, [&c,v1](){ + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + }); + f1.join(); + f2.join(); + BOOST_CHECK_EQUAL( v1, v2); +} + +void test_try_pop() { + boost::fibers::buffered_channel< int > c( 16); + int v1 = 2, v2 = 0; + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.try_pop( v2) ); + BOOST_CHECK_EQUAL( v1, v2); +} + +void test_try_pop_closed() { + boost::fibers::buffered_channel< int > c( 16); + int v1 = 2, v2 = 0; + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + c.close(); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.try_pop( v2) ); + BOOST_CHECK_EQUAL( v1, v2); + BOOST_CHECK( boost::fibers::channel_op_status::closed == c.try_pop( v2) ); +} + +void test_try_pop_success() { + boost::fibers::buffered_channel< int > c( 16); + int v1 = 2, v2 = 0; + boost::fibers::fiber f1( boost::fibers::launch::dispatch, [&c,&v2](){ + while ( boost::fibers::channel_op_status::success != c.try_pop( v2) ) { + boost::this_fiber::yield(); + } + }); + boost::fibers::fiber f2( boost::fibers::launch::dispatch, [&c,v1](){ + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + }); + f1.join(); + f2.join(); + BOOST_CHECK_EQUAL( v1, v2); +} + +void test_pop_wait_for() { + boost::fibers::buffered_channel< int > c( 16); + int v1 = 2, v2 = 0; + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); + BOOST_CHECK_EQUAL( v1, v2); +} + +void test_pop_wait_for_closed() { + boost::fibers::buffered_channel< int > c( 16); + int v1 = 2, v2 = 0; + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + c.close(); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); + BOOST_CHECK_EQUAL( v1, v2); + BOOST_CHECK( boost::fibers::channel_op_status::closed == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); +} + +void test_pop_wait_for_success() { + boost::fibers::buffered_channel< int > c( 16); + int v1 = 2, v2 = 0; + boost::fibers::fiber f1( boost::fibers::launch::dispatch, [&c,&v2](){ + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); + }); + boost::fibers::fiber f2( boost::fibers::launch::dispatch, [&c,v1](){ + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + }); + f1.join(); + f2.join(); + BOOST_CHECK_EQUAL( v1, v2); +} + +void test_pop_wait_for_timeout() { + boost::fibers::buffered_channel< int > c( 16); + int v = 0; + boost::fibers::fiber f( boost::fibers::launch::dispatch, [&c,&v](){ + BOOST_CHECK( boost::fibers::channel_op_status::timeout == c.pop_wait_for( v, std::chrono::seconds( 1) ) ); + }); + f.join(); +} + +void test_pop_wait_until() { + boost::fibers::buffered_channel< int > c( 16); + int v1 = 2, v2 = 0; + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_until( v2, + std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); + BOOST_CHECK_EQUAL( v1, v2); +} + +void test_pop_wait_until_closed() { + boost::fibers::buffered_channel< int > c( 16); + int v1 = 2, v2 = 0; + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + c.close(); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_until( v2, + std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); + BOOST_CHECK_EQUAL( v1, v2); + BOOST_CHECK( boost::fibers::channel_op_status::closed == c.pop_wait_until( v2, + std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); +} + +void test_pop_wait_until_success() { + boost::fibers::buffered_channel< int > c( 16); + int v1 = 2, v2 = 0; + boost::fibers::fiber f1( boost::fibers::launch::dispatch, [&c,&v2](){ + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_until( v2, + std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); + }); + boost::fibers::fiber f2( boost::fibers::launch::dispatch, [&c,v1](){ + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + }); + f1.join(); + f2.join(); + BOOST_CHECK_EQUAL( v1, v2); +} + +void test_pop_wait_until_timeout() { + boost::fibers::buffered_channel< int > c( 16); + int v = 0; + boost::fibers::fiber f( boost::fibers::launch::dispatch, [&c,&v](){ + BOOST_CHECK( boost::fibers::channel_op_status::timeout == c.pop_wait_until( v, + std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); + }); + f.join(); +} + +void test_wm_1() { + boost::fibers::buffered_channel< int > c( 4); + std::vector< boost::fibers::fiber::id > ids; + boost::fibers::fiber f1( boost::fibers::launch::dispatch, [&c,&ids](){ + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 1) ); + + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 2) ); + + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 3) ); + + ids.push_back( boost::this_fiber::get_id() ); + // would be blocked because channel is full + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 4) ); + + ids.push_back( boost::this_fiber::get_id() ); + // would be blocked because channel is full + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 5) ); + + ids.push_back( boost::this_fiber::get_id() ); + }); + boost::fibers::fiber f2( boost::fibers::launch::dispatch, [&c,&ids](){ + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK_EQUAL( 1, c.value_pop() ); + + // let other fiber run + boost::this_fiber::yield(); + + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK_EQUAL( 2, c.value_pop() ); + + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK_EQUAL( 3, c.value_pop() ); + + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK_EQUAL( 4, c.value_pop() ); + + ids.push_back( boost::this_fiber::get_id() ); + // would block because channel is empty + BOOST_CHECK_EQUAL( 5, c.value_pop() ); + + ids.push_back( boost::this_fiber::get_id() ); + }); + boost::fibers::fiber::id id1 = f1.get_id(); + boost::fibers::fiber::id id2 = f2.get_id(); + f1.join(); + f2.join(); + BOOST_CHECK_EQUAL( 12u, ids.size() ); + BOOST_CHECK_EQUAL( id1, ids[0]); + BOOST_CHECK_EQUAL( id1, ids[1]); + BOOST_CHECK_EQUAL( id1, ids[2]); + BOOST_CHECK_EQUAL( id1, ids[3]); + BOOST_CHECK_EQUAL( id2, ids[4]); + BOOST_CHECK_EQUAL( id1, ids[5]); + BOOST_CHECK_EQUAL( id2, ids[6]); + BOOST_CHECK_EQUAL( id2, ids[7]); + BOOST_CHECK_EQUAL( id2, ids[8]); + BOOST_CHECK_EQUAL( id2, ids[9]); + BOOST_CHECK_EQUAL( id1, ids[10]); + BOOST_CHECK_EQUAL( id2, ids[11]); +} + +void test_wm_2() { + boost::fibers::buffered_channel< int > c( 4); + std::vector< boost::fibers::fiber::id > ids; + boost::fibers::fiber f1( boost::fibers::launch::dispatch, [&c,&ids](){ + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 1) ); + + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 2) ); + + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 3) ); + + ids.push_back( boost::this_fiber::get_id() ); + // would be blocked because channel is full + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 4) ); + + ids.push_back( boost::this_fiber::get_id() ); + // would be blocked because channel is full + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 5) ); + + ids.push_back( boost::this_fiber::get_id() ); + }); + boost::fibers::fiber f2( boost::fibers::launch::dispatch, [&c,&ids](){ + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK_EQUAL( 1, c.value_pop() ); + + // let other fiber run + boost::this_fiber::yield(); + + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK_EQUAL( 2, c.value_pop() ); + + // let other fiber run + boost::this_fiber::yield(); + + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK_EQUAL( 3, c.value_pop() ); + + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK_EQUAL( 4, c.value_pop() ); + + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK_EQUAL( 5, c.value_pop() ); + + ids.push_back( boost::this_fiber::get_id() ); + }); + boost::fibers::fiber::id id1 = f1.get_id(); + boost::fibers::fiber::id id2 = f2.get_id(); + f1.join(); + f2.join(); + BOOST_CHECK_EQUAL( (std::size_t)12, ids.size() ); + BOOST_CHECK_EQUAL( id1, ids[0]); + BOOST_CHECK_EQUAL( id1, ids[1]); + BOOST_CHECK_EQUAL( id1, ids[2]); + BOOST_CHECK_EQUAL( id1, ids[3]); + BOOST_CHECK_EQUAL( id2, ids[4]); + BOOST_CHECK_EQUAL( id1, ids[5]); + BOOST_CHECK_EQUAL( id2, ids[6]); + BOOST_CHECK_EQUAL( id1, ids[7]); + BOOST_CHECK_EQUAL( id2, ids[8]); + BOOST_CHECK_EQUAL( id2, ids[9]); + BOOST_CHECK_EQUAL( id2, ids[10]); + BOOST_CHECK_EQUAL( id2, ids[11]); +} + +void test_moveable() { + boost::fibers::buffered_channel< moveable > c( 16); + moveable m1( 3), m2; + BOOST_CHECK( m1.state); + BOOST_CHECK_EQUAL( 3, m1.value); + BOOST_CHECK( ! m2.state); + BOOST_CHECK_EQUAL( -1, m2.value); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( std::move( m1) ) ); + BOOST_CHECK( ! m1.state); + BOOST_CHECK( ! m2.state); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop( m2) ); + BOOST_CHECK( ! m1.state); + BOOST_CHECK_EQUAL( -1, m1.value); + BOOST_CHECK( m2.state); + BOOST_CHECK_EQUAL( 3, m2.value); +} + +void test_rangefor() { + boost::fibers::buffered_channel< int > chan{ 4 }; + std::vector< int > vec; + boost::fibers::fiber f1( boost::fibers::launch::dispatch, [&chan]{ + chan.push( 1); + chan.push( 1); + chan.push( 2); + chan.push( 3); + chan.push( 5); + chan.push( 8); + chan.push( 12); + chan.close(); + }); + boost::fibers::fiber f2( boost::fibers::launch::dispatch, [&vec,&chan]{ + for ( int value : chan) { + vec.push_back( value); + } + }); + f1.join(); + f2.join(); + BOOST_CHECK_EQUAL( 1, vec[0]); + BOOST_CHECK_EQUAL( 1, vec[1]); + BOOST_CHECK_EQUAL( 2, vec[2]); + BOOST_CHECK_EQUAL( 3, vec[3]); + BOOST_CHECK_EQUAL( 5, vec[4]); + BOOST_CHECK_EQUAL( 8, vec[5]); + BOOST_CHECK_EQUAL( 12, vec[6]); +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) { + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Fiber: buffered_channel test suite"); + + test->add( BOOST_TEST_CASE( & test_zero_wm) ); + test->add( BOOST_TEST_CASE( & test_push) ); + test->add( BOOST_TEST_CASE( & test_push_closed) ); + test->add( BOOST_TEST_CASE( & test_try_push) ); + test->add( BOOST_TEST_CASE( & test_try_push_closed) ); + test->add( BOOST_TEST_CASE( & test_try_push_full) ); + test->add( BOOST_TEST_CASE( & test_push_wait_for) ); + test->add( BOOST_TEST_CASE( & test_push_wait_for_closed) ); + test->add( BOOST_TEST_CASE( & test_push_wait_for_timeout) ); + test->add( BOOST_TEST_CASE( & test_push_wait_until) ); + test->add( BOOST_TEST_CASE( & test_push_wait_until_closed) ); + test->add( BOOST_TEST_CASE( & test_push_wait_until_timeout) ); + test->add( BOOST_TEST_CASE( & test_pop) ); + test->add( BOOST_TEST_CASE( & test_pop_closed) ); + test->add( BOOST_TEST_CASE( & test_pop_success) ); + test->add( BOOST_TEST_CASE( & test_value_pop) ); + test->add( BOOST_TEST_CASE( & test_value_pop_closed) ); + test->add( BOOST_TEST_CASE( & test_value_pop_success) ); + test->add( BOOST_TEST_CASE( & test_try_pop) ); + test->add( BOOST_TEST_CASE( & test_try_pop_closed) ); + test->add( BOOST_TEST_CASE( & test_try_pop_success) ); + test->add( BOOST_TEST_CASE( & test_pop_wait_for) ); + test->add( BOOST_TEST_CASE( & test_pop_wait_for_closed) ); + test->add( BOOST_TEST_CASE( & test_pop_wait_for_success) ); + test->add( BOOST_TEST_CASE( & test_pop_wait_for_timeout) ); + test->add( BOOST_TEST_CASE( & test_pop_wait_until) ); + test->add( BOOST_TEST_CASE( & test_pop_wait_until_closed) ); + test->add( BOOST_TEST_CASE( & test_pop_wait_until_success) ); + test->add( BOOST_TEST_CASE( & test_pop_wait_until_timeout) ); + test->add( BOOST_TEST_CASE( & test_wm_1) ); + test->add( BOOST_TEST_CASE( & test_wm_2) ); + test->add( BOOST_TEST_CASE( & test_moveable) ); + test->add( BOOST_TEST_CASE( & test_rangefor) ); + + return test; +} diff --git a/src/boost/libs/fiber/test/test_buffered_channel_post.cpp b/src/boost/libs/fiber/test/test_buffered_channel_post.cpp new file mode 100644 index 00000000..ffc14a7b --- /dev/null +++ b/src/boost/libs/fiber/test/test_buffered_channel_post.cpp @@ -0,0 +1,529 @@ + +// Copyright Oliver Kowalke 2013. +// 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 <chrono> +#include <sstream> +#include <string> +#include <vector> + +#include <boost/assert.hpp> +#include <boost/test/unit_test.hpp> + +#include <boost/fiber/all.hpp> + +struct moveable { + 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; + other.state = false; + value = other.value; + other.value = -1; + return * this; + } +}; + +void test_zero_wm() { + bool thrown = false; + try { + boost::fibers::buffered_channel< int > c( 0); + } catch ( boost::fibers::fiber_error const&) { + thrown = true; + } + BOOST_CHECK( thrown); +} + +void test_push() { + boost::fibers::buffered_channel< int > c( 16); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 1) ); +} + +void test_push_closed() { + boost::fibers::buffered_channel< int > c( 16); + c.close(); + BOOST_CHECK( boost::fibers::channel_op_status::closed == c.push( 1) ); +} + +void test_try_push() { + boost::fibers::buffered_channel< int > c( 2); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 1) ); +} + +void test_try_push_closed() { + boost::fibers::buffered_channel< int > c( 2); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.try_push( 1) ); + c.close(); + BOOST_CHECK( boost::fibers::channel_op_status::closed == c.try_push( 2) ); +} + +void test_try_push_full() { + boost::fibers::buffered_channel< int > c( 2); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.try_push( 1) ); + BOOST_CHECK( boost::fibers::channel_op_status::full == c.try_push( 2) ); +} + +void test_push_wait_for() { + boost::fibers::buffered_channel< int > c( 2); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push_wait_for( 1, std::chrono::seconds( 1) ) ); +} + +void test_push_wait_for_closed() { + boost::fibers::buffered_channel< int > c( 2); + c.close(); + BOOST_CHECK( boost::fibers::channel_op_status::closed == c.push_wait_for( 1, std::chrono::seconds( 1) ) ); +} + +void test_push_wait_for_timeout() { + boost::fibers::buffered_channel< int > c( 2); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push_wait_for( 1, std::chrono::seconds( 1) ) ); + BOOST_CHECK( boost::fibers::channel_op_status::timeout == c.push_wait_for( 2, std::chrono::seconds( 1) ) ); +} + +void test_push_wait_until() { + boost::fibers::buffered_channel< int > c( 2); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push_wait_until( 1, + std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); +} + +void test_push_wait_until_closed() { + boost::fibers::buffered_channel< int > c( 2); + c.close(); + BOOST_CHECK( boost::fibers::channel_op_status::closed == c.push_wait_until( 1, + std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); +} + +void test_push_wait_until_timeout() { + boost::fibers::buffered_channel< int > c( 2); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push_wait_until( 1, + std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); + BOOST_CHECK( boost::fibers::channel_op_status::timeout == c.push_wait_until( 2, + std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); +} + +void test_pop() { + boost::fibers::buffered_channel< int > c( 16); + int v1 = 2, v2 = 0; + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop( v2) ); + BOOST_CHECK_EQUAL( v1, v2); +} + +void test_pop_closed() { + boost::fibers::buffered_channel< int > c( 16); + int v1 = 2, v2 = 0; + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + c.close(); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop( v2) ); + BOOST_CHECK_EQUAL( v1, v2); + BOOST_CHECK( boost::fibers::channel_op_status::closed == c.pop( v2) ); +} + +void test_pop_success() { + boost::fibers::buffered_channel< int > c( 16); + int v1 = 2, v2 = 0; + boost::fibers::fiber f1( boost::fibers::launch::post, [&c,&v2](){ + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop( v2) ); + }); + boost::fibers::fiber f2( boost::fibers::launch::post, [&c,v1](){ + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + }); + f1.join(); + f2.join(); + BOOST_CHECK_EQUAL( v1, v2); +} + +void test_value_pop() { + boost::fibers::buffered_channel< int > c( 16); + int v1 = 2, v2 = 0; + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + v2 = c.value_pop(); + BOOST_CHECK_EQUAL( v1, v2); +} + +void test_value_pop_closed() { + boost::fibers::buffered_channel< int > c( 16); + int v1 = 2, v2 = 0; + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + c.close(); + v2 = c.value_pop(); + BOOST_CHECK_EQUAL( v1, v2); + bool thrown = false; + try { + c.value_pop(); + } catch ( boost::fibers::fiber_error const&) { + thrown = true; + } + BOOST_CHECK( thrown); +} + +void test_value_pop_success() { + boost::fibers::buffered_channel< int > c( 16); + int v1 = 2, v2 = 0; + boost::fibers::fiber f1( boost::fibers::launch::post, [&c,&v2](){ + v2 = c.value_pop(); + }); + boost::fibers::fiber f2( boost::fibers::launch::post, [&c,v1](){ + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + }); + f1.join(); + f2.join(); + BOOST_CHECK_EQUAL( v1, v2); +} + +void test_try_pop() { + boost::fibers::buffered_channel< int > c( 16); + int v1 = 2, v2 = 0; + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.try_pop( v2) ); + BOOST_CHECK_EQUAL( v1, v2); +} + +void test_try_pop_closed() { + boost::fibers::buffered_channel< int > c( 16); + int v1 = 2, v2 = 0; + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + c.close(); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.try_pop( v2) ); + BOOST_CHECK_EQUAL( v1, v2); + BOOST_CHECK( boost::fibers::channel_op_status::closed == c.try_pop( v2) ); +} + +void test_try_pop_success() { + boost::fibers::buffered_channel< int > c( 16); + int v1 = 2, v2 = 0; + boost::fibers::fiber f1( boost::fibers::launch::post, [&c,&v2](){ + while ( boost::fibers::channel_op_status::success != c.try_pop( v2) ) { + boost::this_fiber::yield(); + } + }); + boost::fibers::fiber f2( boost::fibers::launch::post, [&c,v1](){ + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + }); + f1.join(); + f2.join(); + BOOST_CHECK_EQUAL( v1, v2); +} + +void test_pop_wait_for() { + boost::fibers::buffered_channel< int > c( 16); + int v1 = 2, v2 = 0; + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); + BOOST_CHECK_EQUAL( v1, v2); +} + +void test_pop_wait_for_closed() { + boost::fibers::buffered_channel< int > c( 16); + int v1 = 2, v2 = 0; + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + c.close(); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); + BOOST_CHECK_EQUAL( v1, v2); + BOOST_CHECK( boost::fibers::channel_op_status::closed == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); +} + +void test_pop_wait_for_success() { + boost::fibers::buffered_channel< int > c( 16); + int v1 = 2, v2 = 0; + boost::fibers::fiber f1( boost::fibers::launch::post, [&c,&v2](){ + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); + }); + boost::fibers::fiber f2( boost::fibers::launch::post, [&c,v1](){ + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + }); + f1.join(); + f2.join(); + BOOST_CHECK_EQUAL( v1, v2); +} + +void test_pop_wait_for_timeout() { + boost::fibers::buffered_channel< int > c( 16); + int v = 0; + boost::fibers::fiber f( boost::fibers::launch::post, [&c,&v](){ + BOOST_CHECK( boost::fibers::channel_op_status::timeout == c.pop_wait_for( v, std::chrono::seconds( 1) ) ); + }); + f.join(); +} + +void test_pop_wait_until() { + boost::fibers::buffered_channel< int > c( 16); + int v1 = 2, v2 = 0; + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_until( v2, + std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); + BOOST_CHECK_EQUAL( v1, v2); +} + +void test_pop_wait_until_closed() { + boost::fibers::buffered_channel< int > c( 16); + int v1 = 2, v2 = 0; + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + c.close(); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_until( v2, + std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); + BOOST_CHECK_EQUAL( v1, v2); + BOOST_CHECK( boost::fibers::channel_op_status::closed == c.pop_wait_until( v2, + std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); +} + +void test_pop_wait_until_success() { + boost::fibers::buffered_channel< int > c( 16); + int v1 = 2, v2 = 0; + boost::fibers::fiber f1( boost::fibers::launch::post, [&c,&v2](){ + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_until( v2, + std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); + }); + boost::fibers::fiber f2( boost::fibers::launch::post, [&c,v1](){ + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + }); + f1.join(); + f2.join(); + BOOST_CHECK_EQUAL( v1, v2); +} + +void test_pop_wait_until_timeout() { + boost::fibers::buffered_channel< int > c( 16); + int v = 0; + boost::fibers::fiber f( boost::fibers::launch::post, [&c,&v](){ + BOOST_CHECK( boost::fibers::channel_op_status::timeout == c.pop_wait_until( v, + std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); + }); + f.join(); +} + +void test_wm_1() { + boost::fibers::buffered_channel< int > c( 4); + std::vector< boost::fibers::fiber::id > ids; + boost::fibers::fiber f1( boost::fibers::launch::post, [&c,&ids](){ + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 1) ); + + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 2) ); + + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 3) ); + + ids.push_back( boost::this_fiber::get_id() ); + // would be blocked because channel is full + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 4) ); + + ids.push_back( boost::this_fiber::get_id() ); + // would be blocked because channel is full + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 5) ); + + ids.push_back( boost::this_fiber::get_id() ); + }); + boost::fibers::fiber f2( boost::fibers::launch::post, [&c,&ids](){ + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK_EQUAL( 1, c.value_pop() ); + + // let other fiber run + boost::this_fiber::yield(); + + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK_EQUAL( 2, c.value_pop() ); + + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK_EQUAL( 3, c.value_pop() ); + + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK_EQUAL( 4, c.value_pop() ); + + ids.push_back( boost::this_fiber::get_id() ); + // would block because channel is empty + BOOST_CHECK_EQUAL( 5, c.value_pop() ); + + ids.push_back( boost::this_fiber::get_id() ); + }); + boost::fibers::fiber::id id1 = f1.get_id(); + boost::fibers::fiber::id id2 = f2.get_id(); + f1.join(); + f2.join(); + BOOST_CHECK_EQUAL( (std::size_t)12, ids.size() ); + BOOST_CHECK_EQUAL( id1, ids[0]); + BOOST_CHECK_EQUAL( id1, ids[1]); + BOOST_CHECK_EQUAL( id1, ids[2]); + BOOST_CHECK_EQUAL( id1, ids[3]); + BOOST_CHECK_EQUAL( id2, ids[4]); + BOOST_CHECK_EQUAL( id1, ids[5]); + BOOST_CHECK_EQUAL( id2, ids[6]); + BOOST_CHECK_EQUAL( id2, ids[7]); + BOOST_CHECK_EQUAL( id2, ids[8]); + BOOST_CHECK_EQUAL( id2, ids[9]); + BOOST_CHECK_EQUAL( id1, ids[10]); + BOOST_CHECK_EQUAL( id2, ids[11]); +} + +void test_wm_2() { + boost::fibers::buffered_channel< int > c( 8); + std::vector< boost::fibers::fiber::id > ids; + boost::fibers::fiber f1( boost::fibers::launch::post, [&c,&ids](){ + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 1) ); + + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 2) ); + + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 3) ); + + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 4) ); + + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 5) ); + + ids.push_back( boost::this_fiber::get_id() ); + }); + boost::fibers::fiber f2( boost::fibers::launch::post, [&c,&ids](){ + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK_EQUAL( 1, c.value_pop() ); + + // let other fiber run + boost::this_fiber::yield(); + + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK_EQUAL( 2, c.value_pop() ); + + // let other fiber run + boost::this_fiber::yield(); + + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK_EQUAL( 3, c.value_pop() ); + + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK_EQUAL( 4, c.value_pop() ); + + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK_EQUAL( 5, c.value_pop() ); + + ids.push_back( boost::this_fiber::get_id() ); + }); + boost::fibers::fiber::id id1 = f1.get_id(); + boost::fibers::fiber::id id2 = f2.get_id(); + f1.join(); + f2.join(); + BOOST_CHECK_EQUAL( (std::size_t)12, ids.size() ); + BOOST_CHECK_EQUAL( id1, ids[0]); + BOOST_CHECK_EQUAL( id1, ids[1]); + BOOST_CHECK_EQUAL( id1, ids[2]); + BOOST_CHECK_EQUAL( id1, ids[3]); + BOOST_CHECK_EQUAL( id1, ids[4]); + BOOST_CHECK_EQUAL( id1, ids[5]); + BOOST_CHECK_EQUAL( id2, ids[6]); + BOOST_CHECK_EQUAL( id2, ids[7]); + BOOST_CHECK_EQUAL( id2, ids[8]); + BOOST_CHECK_EQUAL( id2, ids[9]); + BOOST_CHECK_EQUAL( id2, ids[10]); + BOOST_CHECK_EQUAL( id2, ids[11]); +} + +void test_moveable() { + boost::fibers::buffered_channel< moveable > c( 16); + moveable m1( 3), m2; + BOOST_CHECK( m1.state); + BOOST_CHECK_EQUAL( 3, m1.value); + BOOST_CHECK( ! m2.state); + BOOST_CHECK_EQUAL( -1, m2.value); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( std::move( m1) ) ); + BOOST_CHECK( ! m1.state); + BOOST_CHECK( ! m2.state); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop( m2) ); + BOOST_CHECK( ! m1.state); + BOOST_CHECK_EQUAL( -1, m1.value); + BOOST_CHECK( m2.state); + BOOST_CHECK_EQUAL( 3, m2.value); +} + +void test_rangefor() { + boost::fibers::buffered_channel< int > chan{ 2 }; + std::vector< int > vec; + boost::fibers::fiber f1([&chan]{ + chan.push( 1); + chan.push( 1); + chan.push( 2); + chan.push( 3); + chan.push( 5); + chan.push( 8); + chan.push( 12); + chan.close(); + }); + boost::fibers::fiber f2([&vec,&chan]{ + for ( int value : chan) { + vec.push_back( value); + } + }); + f1.join(); + f2.join(); + BOOST_CHECK_EQUAL( 1, vec[0]); + BOOST_CHECK_EQUAL( 1, vec[1]); + BOOST_CHECK_EQUAL( 2, vec[2]); + BOOST_CHECK_EQUAL( 3, vec[3]); + BOOST_CHECK_EQUAL( 5, vec[4]); + BOOST_CHECK_EQUAL( 8, vec[5]); + BOOST_CHECK_EQUAL( 12, vec[6]); +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) { + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Fiber: buffered_channel test suite"); + + test->add( BOOST_TEST_CASE( & test_zero_wm) ); + test->add( BOOST_TEST_CASE( & test_push) ); + test->add( BOOST_TEST_CASE( & test_push_closed) ); + test->add( BOOST_TEST_CASE( & test_try_push) ); + test->add( BOOST_TEST_CASE( & test_try_push_closed) ); + test->add( BOOST_TEST_CASE( & test_try_push_full) ); + test->add( BOOST_TEST_CASE( & test_push_wait_for) ); + test->add( BOOST_TEST_CASE( & test_push_wait_for_closed) ); + test->add( BOOST_TEST_CASE( & test_push_wait_for_timeout) ); + test->add( BOOST_TEST_CASE( & test_push_wait_until) ); + test->add( BOOST_TEST_CASE( & test_push_wait_until_closed) ); + test->add( BOOST_TEST_CASE( & test_push_wait_until_timeout) ); + test->add( BOOST_TEST_CASE( & test_pop) ); + test->add( BOOST_TEST_CASE( & test_pop_closed) ); + test->add( BOOST_TEST_CASE( & test_pop_success) ); + test->add( BOOST_TEST_CASE( & test_value_pop) ); + test->add( BOOST_TEST_CASE( & test_value_pop_closed) ); + test->add( BOOST_TEST_CASE( & test_value_pop_success) ); + test->add( BOOST_TEST_CASE( & test_try_pop) ); + test->add( BOOST_TEST_CASE( & test_try_pop_closed) ); + test->add( BOOST_TEST_CASE( & test_try_pop_success) ); + test->add( BOOST_TEST_CASE( & test_pop_wait_for) ); + test->add( BOOST_TEST_CASE( & test_pop_wait_for_closed) ); + test->add( BOOST_TEST_CASE( & test_pop_wait_for_success) ); + test->add( BOOST_TEST_CASE( & test_pop_wait_for_timeout) ); + test->add( BOOST_TEST_CASE( & test_pop_wait_until) ); + test->add( BOOST_TEST_CASE( & test_pop_wait_until_closed) ); + test->add( BOOST_TEST_CASE( & test_pop_wait_until_success) ); + test->add( BOOST_TEST_CASE( & test_pop_wait_until_timeout) ); + test->add( BOOST_TEST_CASE( & test_wm_1) ); + test->add( BOOST_TEST_CASE( & test_wm_2) ); + test->add( BOOST_TEST_CASE( & test_moveable) ); + test->add( BOOST_TEST_CASE( & test_rangefor) ); + + return test; +} diff --git a/src/boost/libs/fiber/test/test_condition_mt_dispatch.cpp b/src/boost/libs/fiber/test/test_condition_mt_dispatch.cpp new file mode 100644 index 00000000..e57fd087 --- /dev/null +++ b/src/boost/libs/fiber/test/test_condition_mt_dispatch.cpp @@ -0,0 +1,164 @@ + +// Copyright Oliver Kowalke 2013. +// 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) +// +// This test is based on the tests of Boost.Thread + +#include <cstdio> +#include <cstdlib> +#include <iostream> +#include <map> +#include <mutex> +#include <stdexcept> +#include <vector> + +#include <boost/atomic.hpp> +#include <boost/bind.hpp> +#include <boost/chrono.hpp> +#include <boost/cstdint.hpp> +#include <boost/function.hpp> +#include <boost/ref.hpp> +#include <boost/test/unit_test.hpp> +#include <boost/thread.hpp> +#include <boost/utility.hpp> + +#include <boost/fiber/all.hpp> + +typedef boost::chrono::milliseconds ms; + +boost::atomic< int > value1; + +void wait_fn( boost::barrier & b, + boost::fibers::mutex & mtx, + boost::fibers::condition_variable & cond, + bool & flag) { + b.wait(); + std::unique_lock< boost::fibers::mutex > lk( mtx); + cond.wait( lk, [&flag](){ return flag; }); + ++value1; +} + +void notify_one_fn( boost::barrier & b, + boost::fibers::mutex & mtx, + boost::fibers::condition_variable & cond, + bool & flag) { + b.wait(); + std::unique_lock< boost::fibers::mutex > lk( mtx); + flag = true; + lk.unlock(); + cond.notify_one(); +} + +void notify_all_fn( boost::barrier & b, + boost::fibers::mutex & mtx, + boost::fibers::condition_variable & cond, + bool & flag) { + b.wait(); + std::unique_lock< boost::fibers::mutex > lk( mtx); + flag = true; + lk.unlock(); + cond.notify_all(); +} + +void fn1( boost::barrier & b, + boost::fibers::mutex & mtx, + boost::fibers::condition_variable & cond, + bool & flag) { + boost::fibers::fiber( + boost::fibers::launch::dispatch, + wait_fn, + std::ref( b), + std::ref( mtx), + std::ref( cond), + std::ref( flag) ).join(); +} + +void fn2( boost::barrier & b, + boost::fibers::mutex & mtx, + boost::fibers::condition_variable & cond, + bool & flag) { + boost::fibers::fiber( + boost::fibers::launch::dispatch, + notify_one_fn, + std::ref( b), + std::ref( mtx), + std::ref( cond), + std::ref( flag) ).join(); +} + +void fn3( boost::barrier & b, + boost::fibers::mutex & mtx, + boost::fibers::condition_variable & cond, + bool & flag) { + boost::fibers::fiber( + boost::fibers::launch::dispatch, + notify_all_fn, + std::ref( b), + std::ref( mtx), + std::ref( cond), + std::ref( flag) ).join(); +} + +void test_one_waiter_notify_one() { + for ( int i = 0; i < 10; ++i) { + boost::barrier b( 2); + + bool flag = false; + value1 = 0; + boost::fibers::mutex mtx; + boost::fibers::condition_variable cond; + + BOOST_CHECK( 0 == value1); + + boost::thread t1(std::bind( fn1, std::ref( b), std::ref( mtx), std::ref( cond), std::ref( flag) ) ); + boost::thread t2(std::bind( fn2, std::ref( b), std::ref( mtx), std::ref( cond), std::ref( flag) ) ); + + t1.join(); + t2.join(); + + BOOST_CHECK( 1 == value1); + } +} + +void test_two_waiter_notify_all() { + for ( int i = 0; i < 10; ++i) { + boost::barrier b( 3); + + bool flag = false; + value1 = 0; + boost::fibers::mutex mtx; + boost::fibers::condition_variable cond; + + BOOST_CHECK( 0 == value1); + + boost::thread t1(std::bind( fn1, std::ref( b), std::ref( mtx), std::ref( cond), std::ref( flag) ) ); + boost::thread t2(std::bind( fn1, std::ref( b), std::ref( mtx), std::ref( cond), std::ref( flag) ) ); + boost::thread t3(std::bind( fn3, std::ref( b), std::ref( mtx), std::ref( cond), std::ref( flag) ) ); + + t1.join(); + t2.join(); + t3.join(); + + BOOST_CHECK( 2 == value1); + } +} + +void test_dummy() { +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) +{ + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Fiber: multithreaded condition_variable test suite"); + +#if ! defined(BOOST_FIBERS_NO_ATOMICS) + test->add( BOOST_TEST_CASE( & test_one_waiter_notify_one) ); + test->add( BOOST_TEST_CASE( & test_two_waiter_notify_all) ); +#else + test->add( BOOST_TEST_CASE( & test_dummy) ); +#endif + + return test; +} diff --git a/src/boost/libs/fiber/test/test_condition_mt_post.cpp b/src/boost/libs/fiber/test/test_condition_mt_post.cpp new file mode 100644 index 00000000..b85647eb --- /dev/null +++ b/src/boost/libs/fiber/test/test_condition_mt_post.cpp @@ -0,0 +1,164 @@ + +// Copyright Oliver Kowalke 2013. +// 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) +// +// This test is based on the tests of Boost.Thread + +#include <cstdio> +#include <cstdlib> +#include <iostream> +#include <map> +#include <mutex> +#include <stdexcept> +#include <vector> + +#include <boost/atomic.hpp> +#include <boost/bind.hpp> +#include <boost/chrono.hpp> +#include <boost/cstdint.hpp> +#include <boost/function.hpp> +#include <boost/ref.hpp> +#include <boost/test/unit_test.hpp> +#include <boost/thread.hpp> +#include <boost/utility.hpp> + +#include <boost/fiber/all.hpp> + +typedef boost::chrono::milliseconds ms; + +boost::atomic< int > value1; + +void wait_fn( boost::barrier & b, + boost::fibers::mutex & mtx, + boost::fibers::condition_variable & cond, + bool & flag) { + b.wait(); + std::unique_lock< boost::fibers::mutex > lk( mtx); + cond.wait( lk, [&flag](){ return flag; }); + ++value1; +} + +void notify_one_fn( boost::barrier & b, + boost::fibers::mutex & mtx, + boost::fibers::condition_variable & cond, + bool & flag) { + b.wait(); + std::unique_lock< boost::fibers::mutex > lk( mtx); + flag = true; + lk.unlock(); + cond.notify_one(); +} + +void notify_all_fn( boost::barrier & b, + boost::fibers::mutex & mtx, + boost::fibers::condition_variable & cond, + bool & flag) { + b.wait(); + std::unique_lock< boost::fibers::mutex > lk( mtx); + flag = true; + lk.unlock(); + cond.notify_all(); +} + +void fn1( boost::barrier & b, + boost::fibers::mutex & mtx, + boost::fibers::condition_variable & cond, + bool & flag) { + boost::fibers::fiber( + boost::fibers::launch::post, + wait_fn, + std::ref( b), + std::ref( mtx), + std::ref( cond), + std::ref( flag) ).join(); +} + +void fn2( boost::barrier & b, + boost::fibers::mutex & mtx, + boost::fibers::condition_variable & cond, + bool & flag) { + boost::fibers::fiber( + boost::fibers::launch::post, + notify_one_fn, + std::ref( b), + std::ref( mtx), + std::ref( cond), + std::ref( flag) ).join(); +} + +void fn3( boost::barrier & b, + boost::fibers::mutex & mtx, + boost::fibers::condition_variable & cond, + bool & flag) { + boost::fibers::fiber( + boost::fibers::launch::post, + notify_all_fn, + std::ref( b), + std::ref( mtx), + std::ref( cond), + std::ref( flag) ).join(); +} + +void test_one_waiter_notify_one() { + for ( int i = 0; i < 10; ++i) { + boost::barrier b( 2); + + bool flag = false; + value1 = 0; + boost::fibers::mutex mtx; + boost::fibers::condition_variable cond; + + BOOST_CHECK( 0 == value1); + + boost::thread t1(std::bind( fn1, std::ref( b), std::ref( mtx), std::ref( cond), std::ref( flag) ) ); + boost::thread t2(std::bind( fn2, std::ref( b), std::ref( mtx), std::ref( cond), std::ref( flag) ) ); + + t1.join(); + t2.join(); + + BOOST_CHECK( 1 == value1); + } +} + +void test_two_waiter_notify_all() { + for ( int i = 0; i < 10; ++i) { + boost::barrier b( 3); + + bool flag = false; + value1 = 0; + boost::fibers::mutex mtx; + boost::fibers::condition_variable cond; + + BOOST_CHECK( 0 == value1); + + boost::thread t1(std::bind( fn1, std::ref( b), std::ref( mtx), std::ref( cond), std::ref( flag) ) ); + boost::thread t2(std::bind( fn1, std::ref( b), std::ref( mtx), std::ref( cond), std::ref( flag) ) ); + boost::thread t3(std::bind( fn3, std::ref( b), std::ref( mtx), std::ref( cond), std::ref( flag) ) ); + + t1.join(); + t2.join(); + t3.join(); + + BOOST_CHECK( 2 == value1); + } +} + +void test_dummy() { +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) +{ + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Fiber: multithreaded condition_variable test suite"); + +#if ! defined(BOOST_FIBERS_NO_ATOMICS) + test->add( BOOST_TEST_CASE( & test_one_waiter_notify_one) ); + test->add( BOOST_TEST_CASE( & test_two_waiter_notify_all) ); +#else + test->add( BOOST_TEST_CASE( & test_dummy) ); +#endif + + return test; +} diff --git a/src/boost/libs/fiber/test/test_condition_variable_any_dispatch.cpp b/src/boost/libs/fiber/test/test_condition_variable_any_dispatch.cpp new file mode 100644 index 00000000..e227fd45 --- /dev/null +++ b/src/boost/libs/fiber/test/test_condition_variable_any_dispatch.cpp @@ -0,0 +1,501 @@ + +// Copyright Oliver Kowalke 2013. +// 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) +// +// This test is based on the tests of Boost.Thread + +#include <chrono> +#include <cstdlib> +#include <cstdio> +#include <iostream> +#include <map> +#include <stdexcept> +#include <vector> + +#include <boost/test/unit_test.hpp> + +#include <boost/fiber/all.hpp> + +typedef std::chrono::nanoseconds ns; +typedef std::chrono::milliseconds ms; + +int value1 = 0; + +inline +std::chrono::system_clock::time_point delay(int secs, int msecs = 0, int /*nsecs*/ = 0) { + std::chrono::system_clock::time_point t = std::chrono::system_clock::now(); + t += std::chrono::seconds( secs); + t += std::chrono::milliseconds( msecs); + //t += std::chrono::nanoseconds( nsecs); + + return t; +} + +struct condition_test_data { + condition_test_data() : notified(0), awoken(0) { } + + boost::fibers::mutex mutex; + boost::fibers::condition_variable_any cond; + int notified; + int awoken; +}; + +void condition_test_fiber(condition_test_data* data) { + try { + data->mutex.lock(); + while (!(data->notified > 0)) + data->cond.wait(data->mutex); + data->awoken++; + } catch ( ... ) { + } + data->mutex.unlock(); +} + +struct cond_predicate { + cond_predicate(int& var, int val) : _var(var), _val(val) { } + + bool operator()() { return _var == _val; } + + int& _var; + int _val; +private: + void operator=(cond_predicate&); + +}; + +void notify_one_fn( boost::fibers::condition_variable_any & cond) { + cond.notify_one(); +} + +void notify_all_fn( boost::fibers::condition_variable_any & cond) { + cond.notify_all(); +} + +void wait_fn( + boost::fibers::mutex & mtx, + boost::fibers::condition_variable_any & cond) { + mtx.lock(); + cond.wait( mtx); + ++value1; + mtx.unlock(); +} + +void test_one_waiter_notify_one() { + value1 = 0; + boost::fibers::mutex mtx; + boost::fibers::condition_variable_any cond; + + boost::fibers::fiber f1( + boost::fibers::launch::dispatch, + wait_fn, + std::ref( mtx), + std::ref( cond) ); + BOOST_CHECK_EQUAL( 0, value1); + + boost::fibers::fiber f2( + boost::fibers::launch::dispatch, + notify_one_fn, + std::ref( cond) ); + + BOOST_CHECK_EQUAL( 0, value1); + + f1.join(); + f2.join(); + + BOOST_CHECK_EQUAL( 1, value1); +} + +void test_two_waiter_notify_one() { + value1 = 0; + boost::fibers::mutex mtx; + boost::fibers::condition_variable_any cond; + + boost::fibers::fiber f1( + boost::fibers::launch::dispatch, + wait_fn, + std::ref( mtx), + std::ref( cond) ); + BOOST_CHECK_EQUAL( 0, value1); + + boost::fibers::fiber f2( + boost::fibers::launch::dispatch, + wait_fn, + std::ref( mtx), + std::ref( cond) ); + BOOST_CHECK_EQUAL( 0, value1); + + boost::fibers::fiber f3( + boost::fibers::launch::dispatch, + notify_one_fn, + std::ref( cond) ); + BOOST_CHECK_EQUAL( 0, value1); + + boost::fibers::fiber f4( + boost::fibers::launch::dispatch, + notify_one_fn, + std::ref( cond) ); + BOOST_CHECK_EQUAL( 1, value1); + + f1.join(); + f2.join(); + f3.join(); + f4.join(); + + BOOST_CHECK_EQUAL( 2, value1); +} + +void test_two_waiter_notify_all() { + value1 = 0; + boost::fibers::mutex mtx; + boost::fibers::condition_variable_any cond; + + boost::fibers::fiber f1( + boost::fibers::launch::dispatch, + wait_fn, + std::ref( mtx), + std::ref( cond) ); + BOOST_CHECK_EQUAL( 0, value1); + + boost::fibers::fiber f2( + boost::fibers::launch::dispatch, + wait_fn, + std::ref( mtx), + std::ref( cond) ); + BOOST_CHECK_EQUAL( 0, value1); + + boost::fibers::fiber f3( + boost::fibers::launch::dispatch, + notify_all_fn, + std::ref( cond) ); + BOOST_CHECK_EQUAL( 0, value1); + + boost::fibers::fiber f4( + boost::fibers::launch::dispatch, + wait_fn, + std::ref( mtx), + std::ref( cond) ); + BOOST_CHECK_EQUAL( 2, value1); + + boost::fibers::fiber f5( + boost::fibers::launch::dispatch, + notify_all_fn, + std::ref( cond) ); + BOOST_CHECK_EQUAL( 2, value1); + + f1.join(); + f2.join(); + f3.join(); + f4.join(); + f5.join(); + + BOOST_CHECK_EQUAL( 3, value1); +} + +int test1 = 0; +int test2 = 0; + +int runs = 0; + +void fn1( boost::fibers::mutex & m, boost::fibers::condition_variable_any & cv) { + m.lock(); + BOOST_CHECK(test2 == 0); + test1 = 1; + cv.notify_one(); + while (test2 == 0) { + cv.wait(m); + } + BOOST_CHECK(test2 != 0); + m.unlock(); +} + +void fn2( boost::fibers::mutex & m, boost::fibers::condition_variable_any & cv) { + m.lock(); + BOOST_CHECK(test2 == 0); + test1 = 1; + cv.notify_one(); + std::chrono::system_clock::time_point t0 = std::chrono::system_clock::now(); + std::chrono::system_clock::time_point t = t0 + ms(250); + int count=0; + while (test2 == 0 && cv.wait_until(m, t) == boost::fibers::cv_status::no_timeout) + count++; + std::chrono::system_clock::time_point t1 = std::chrono::system_clock::now(); + if (runs == 0) { + BOOST_CHECK(t1 - t0 < ms(250)); + BOOST_CHECK(test2 != 0); + } else { + BOOST_CHECK(t1 - t0 - ms(250) < ms(count*250+100+1000)); + BOOST_CHECK(test2 == 0); + } + ++runs; + m.unlock(); +} + +class Pred { + int & i_; + +public: + explicit Pred(int& i) : + i_(i) + {} + + bool operator()() + { return i_ != 0; } +}; + +void fn3( boost::fibers::mutex & m, boost::fibers::condition_variable_any & cv) { + m.lock(); + BOOST_CHECK(test2 == 0); + test1 = 1; + cv.notify_one(); + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + std::chrono::steady_clock::time_point t = t0 + ms(250); + bool r = cv.wait_until(m, t, Pred(test2)); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + if (runs == 0) { + BOOST_CHECK(t1 - t0 < ms(250)); + BOOST_CHECK(test2 != 0); + BOOST_CHECK(r); + } else { + BOOST_CHECK(t1 - t0 - ms(250) < ms(250+100)); + BOOST_CHECK(test2 == 0); + BOOST_CHECK(!r); + } + ++runs; + m.unlock(); +} + +void fn4( boost::fibers::mutex & m, boost::fibers::condition_variable_any & cv) { + m.lock(); + BOOST_CHECK(test2 == 0); + test1 = 1; + cv.notify_one(); + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + int count=0; + while (test2 == 0 && cv.wait_for(m, ms(250)) == boost::fibers::cv_status::no_timeout) + count++; + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + if (runs == 0) { + BOOST_CHECK(t1 - t0 < ms(250)); + BOOST_CHECK(test2 != 0); + } else { + BOOST_CHECK(t1 - t0 - ms(250) < ms(count*250+100+1000)); + BOOST_CHECK(test2 == 0); + } + ++runs; + m.unlock(); +} + +void fn5( boost::fibers::mutex & m, boost::fibers::condition_variable_any & cv) { + m.lock(); + BOOST_CHECK(test2 == 0); + test1 = 1; + cv.notify_one(); + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + int count=0; + cv.wait_for(m, ms(250), Pred(test2)); + count++; + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + if (runs == 0) { + BOOST_CHECK(t1 - t0 < ms(250+1000)); + BOOST_CHECK(test2 != 0); + } else { + BOOST_CHECK(t1 - t0 - ms(250) < ms(count*250+100)); + BOOST_CHECK(test2 == 0); + } + ++runs; + m.unlock(); +} + +void do_test_condition_wait() { + test1 = 0; + test2 = 0; + runs = 0; + + boost::fibers::mutex m; + boost::fibers::condition_variable_any cv; + m.lock(); + boost::fibers::fiber f( boost::fibers::launch::dispatch, & fn1, std::ref( m), std::ref( cv) ); + BOOST_CHECK(test1 == 0); + while (test1 == 0) + cv.wait(m); + BOOST_CHECK(test1 != 0); + test2 = 1; + m.unlock(); + cv.notify_one(); + f.join(); +} + +void test_condition_wait() { + boost::fibers::fiber( boost::fibers::launch::dispatch, & do_test_condition_wait).join(); + do_test_condition_wait(); +} + +void do_test_condition_wait_until() { + test1 = 0; + test2 = 0; + runs = 0; + + boost::fibers::mutex m; + boost::fibers::condition_variable_any cv; + { + m.lock(); + boost::fibers::fiber f( boost::fibers::launch::dispatch, & fn2, std::ref( m), std::ref( cv) ); + BOOST_CHECK(test1 == 0); + while (test1 == 0) + cv.wait(m); + BOOST_CHECK(test1 != 0); + test2 = 1; + m.unlock(); + cv.notify_one(); + f.join(); + } + test1 = 0; + test2 = 0; + { + m.lock(); + boost::fibers::fiber f( boost::fibers::launch::dispatch, & fn2, std::ref( m), std::ref( cv) ); + BOOST_CHECK(test1 == 0); + while (test1 == 0) + cv.wait(m); + BOOST_CHECK(test1 != 0); + m.unlock(); + f.join(); + } +} + +void test_condition_wait_until() { + boost::fibers::fiber( boost::fibers::launch::dispatch, & do_test_condition_wait_until).join(); + do_test_condition_wait_until(); +} + +void do_test_condition_wait_until_pred() { + test1 = 0; + test2 = 0; + runs = 0; + + boost::fibers::mutex m; + boost::fibers::condition_variable_any cv; + { + m.lock(); + boost::fibers::fiber f( boost::fibers::launch::dispatch, & fn3, std::ref( m), std::ref( cv) ); + BOOST_CHECK(test1 == 0); + while (test1 == 0) + cv.wait(m); + BOOST_CHECK(test1 != 0); + test2 = 1; + m.unlock(); + cv.notify_one(); + f.join(); + } + test1 = 0; + test2 = 0; + { + m.lock(); + boost::fibers::fiber f( boost::fibers::launch::dispatch, & fn3, std::ref( m), std::ref( cv) ); + BOOST_CHECK(test1 == 0); + while (test1 == 0) + cv.wait(m); + BOOST_CHECK(test1 != 0); + m.unlock(); + f.join(); + } +} + +void test_condition_wait_until_pred() { + boost::fibers::fiber( boost::fibers::launch::dispatch, & do_test_condition_wait_until_pred).join(); + do_test_condition_wait_until_pred(); +} + +void do_test_condition_wait_for() { + test1 = 0; + test2 = 0; + runs = 0; + + boost::fibers::mutex m; + boost::fibers::condition_variable_any cv; + { + m.lock(); + boost::fibers::fiber f( boost::fibers::launch::dispatch, & fn4, std::ref( m), std::ref( cv) ); + BOOST_CHECK(test1 == 0); + while (test1 == 0) + cv.wait(m); + BOOST_CHECK(test1 != 0); + test2 = 1; + m.unlock(); + cv.notify_one(); + f.join(); + } + test1 = 0; + test2 = 0; + { + m.lock(); + boost::fibers::fiber f( boost::fibers::launch::dispatch, & fn4, std::ref( m), std::ref( cv) ); + BOOST_CHECK(test1 == 0); + while (test1 == 0) + cv.wait(m); + BOOST_CHECK(test1 != 0); + m.unlock(); + f.join(); + } +} + +void test_condition_wait_for() { + boost::fibers::fiber( boost::fibers::launch::dispatch, & do_test_condition_wait_for).join(); + do_test_condition_wait_for(); +} + +void do_test_condition_wait_for_pred() { + test1 = 0; + test2 = 0; + runs = 0; + + boost::fibers::mutex m; + boost::fibers::condition_variable_any cv; + { + m.lock(); + boost::fibers::fiber f( boost::fibers::launch::dispatch, & fn5, std::ref( m), std::ref( cv) ); + BOOST_CHECK(test1 == 0); + while (test1 == 0) + cv.wait(m); + BOOST_CHECK(test1 != 0); + test2 = 1; + m.unlock(); + cv.notify_one(); + f.join(); + } + test1 = 0; + test2 = 0; + { + m.lock(); + boost::fibers::fiber f( boost::fibers::launch::dispatch, & fn5, std::ref( m), std::ref( cv) ); + BOOST_CHECK(test1 == 0); + while (test1 == 0) + cv.wait(m); + BOOST_CHECK(test1 != 0); + m.unlock(); + f.join(); + } +} + +void test_condition_wait_for_pred() { + boost::fibers::fiber( boost::fibers::launch::dispatch, & do_test_condition_wait_for_pred).join(); + do_test_condition_wait_for_pred(); +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) { + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Fiber: condition_variable_any test suite"); + + test->add( BOOST_TEST_CASE( & test_one_waiter_notify_one) ); + test->add( BOOST_TEST_CASE( & test_two_waiter_notify_one) ); + test->add( BOOST_TEST_CASE( & test_two_waiter_notify_all) ); + test->add( BOOST_TEST_CASE( & test_condition_wait) ); + test->add( BOOST_TEST_CASE( & test_condition_wait_until) ); + test->add( BOOST_TEST_CASE( & test_condition_wait_until_pred) ); + test->add( BOOST_TEST_CASE( & test_condition_wait_for) ); + test->add( BOOST_TEST_CASE( & test_condition_wait_for_pred) ); + + return test; +} diff --git a/src/boost/libs/fiber/test/test_condition_variable_any_post.cpp b/src/boost/libs/fiber/test/test_condition_variable_any_post.cpp new file mode 100644 index 00000000..da6d882a --- /dev/null +++ b/src/boost/libs/fiber/test/test_condition_variable_any_post.cpp @@ -0,0 +1,501 @@ + +// Copyright Oliver Kowalke 2013. +// 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) +// +// This test is based on the tests of Boost.Thread + +#include <chrono> +#include <cstdlib> +#include <cstdio> +#include <iostream> +#include <map> +#include <stdexcept> +#include <vector> + +#include <boost/test/unit_test.hpp> + +#include <boost/fiber/all.hpp> + +typedef std::chrono::nanoseconds ns; +typedef std::chrono::milliseconds ms; + +int value1 = 0; + +inline +std::chrono::system_clock::time_point delay(int secs, int msecs=0, int /*nsecs*/=0) { + std::chrono::system_clock::time_point t = std::chrono::system_clock::now(); + t += std::chrono::seconds( secs); + t += std::chrono::milliseconds( msecs); + //t += std::chrono::nanoseconds( nsecs); + + return t; +} + +struct condition_test_data { + condition_test_data() : notified(0), awoken(0) { } + + boost::fibers::mutex mutex; + boost::fibers::condition_variable_any cond; + int notified; + int awoken; +}; + +void condition_test_fiber(condition_test_data* data) { + try { + data->mutex.lock(); + while (!(data->notified > 0)) + data->cond.wait(data->mutex); + data->awoken++; + } catch ( ... ) { + } + data->mutex.unlock(); +} + +struct cond_predicate { + cond_predicate(int& var, int val) : _var(var), _val(val) { } + + bool operator()() { return _var == _val; } + + int& _var; + int _val; +private: + void operator=(cond_predicate&); + +}; + +void notify_one_fn( boost::fibers::condition_variable_any & cond) { + cond.notify_one(); +} + +void notify_all_fn( boost::fibers::condition_variable_any & cond) { + cond.notify_all(); +} + +void wait_fn( + boost::fibers::mutex & mtx, + boost::fibers::condition_variable_any & cond) { + mtx.lock(); + cond.wait( mtx); + ++value1; + mtx.unlock(); +} + +void test_one_waiter_notify_one() { + value1 = 0; + boost::fibers::mutex mtx; + boost::fibers::condition_variable_any cond; + + boost::fibers::fiber f1( + boost::fibers::launch::post, + wait_fn, + std::ref( mtx), + std::ref( cond) ); + BOOST_CHECK_EQUAL( 0, value1); + + boost::fibers::fiber f2( + boost::fibers::launch::post, + notify_one_fn, + std::ref( cond) ); + + BOOST_CHECK_EQUAL( 0, value1); + + f1.join(); + f2.join(); + + BOOST_CHECK_EQUAL( 1, value1); +} + +void test_two_waiter_notify_one() { + value1 = 0; + boost::fibers::mutex mtx; + boost::fibers::condition_variable_any cond; + + boost::fibers::fiber f1( + boost::fibers::launch::post, + wait_fn, + std::ref( mtx), + std::ref( cond) ); + BOOST_CHECK_EQUAL( 0, value1); + + boost::fibers::fiber f2( + boost::fibers::launch::post, + wait_fn, + std::ref( mtx), + std::ref( cond) ); + BOOST_CHECK_EQUAL( 0, value1); + + boost::fibers::fiber f3( + boost::fibers::launch::post, + notify_one_fn, + std::ref( cond) ); + BOOST_CHECK_EQUAL( 0, value1); + + boost::fibers::fiber f4( + boost::fibers::launch::post, + notify_one_fn, + std::ref( cond) ); + BOOST_CHECK_EQUAL( 0, value1); + + f1.join(); + f2.join(); + f3.join(); + f4.join(); + + BOOST_CHECK_EQUAL( 2, value1); +} + +void test_two_waiter_notify_all() { + value1 = 0; + boost::fibers::mutex mtx; + boost::fibers::condition_variable_any cond; + + boost::fibers::fiber f1( + boost::fibers::launch::post, + wait_fn, + std::ref( mtx), + std::ref( cond) ); + BOOST_CHECK_EQUAL( 0, value1); + + boost::fibers::fiber f2( + boost::fibers::launch::post, + wait_fn, + std::ref( mtx), + std::ref( cond) ); + BOOST_CHECK_EQUAL( 0, value1); + + boost::fibers::fiber f3( + boost::fibers::launch::post, + notify_all_fn, + std::ref( cond) ); + BOOST_CHECK_EQUAL( 0, value1); + + boost::fibers::fiber f4( + boost::fibers::launch::post, + wait_fn, + std::ref( mtx), + std::ref( cond) ); + BOOST_CHECK_EQUAL( 0, value1); + + boost::fibers::fiber f5( + boost::fibers::launch::post, + notify_all_fn, + std::ref( cond) ); + BOOST_CHECK_EQUAL( 0, value1); + + f1.join(); + f2.join(); + f3.join(); + f4.join(); + f5.join(); + + BOOST_CHECK_EQUAL( 3, value1); +} + +int test1 = 0; +int test2 = 0; + +int runs = 0; + +void fn1( boost::fibers::mutex & m, boost::fibers::condition_variable_any & cv) { + m.lock(); + BOOST_CHECK(test2 == 0); + test1 = 1; + cv.notify_one(); + while (test2 == 0) { + cv.wait(m); + } + BOOST_CHECK(test2 != 0); + m.unlock(); +} + +void fn2( boost::fibers::mutex & m, boost::fibers::condition_variable_any & cv) { + m.lock(); + BOOST_CHECK(test2 == 0); + test1 = 1; + cv.notify_one(); + std::chrono::system_clock::time_point t0 = std::chrono::system_clock::now(); + std::chrono::system_clock::time_point t = t0 + ms(250); + int count=0; + while (test2 == 0 && cv.wait_until(m, t) == boost::fibers::cv_status::no_timeout) + count++; + std::chrono::system_clock::time_point t1 = std::chrono::system_clock::now(); + if (runs == 0) { + BOOST_CHECK(t1 - t0 < ms(250)); + BOOST_CHECK(test2 != 0); + } else { + BOOST_CHECK(t1 - t0 - ms(250) < ms(count*250+100+1000)); + BOOST_CHECK(test2 == 0); + } + ++runs; + m.unlock(); +} + +class Pred { + int & i_; + +public: + explicit Pred(int& i) : + i_(i) + {} + + bool operator()() + { return i_ != 0; } +}; + +void fn3( boost::fibers::mutex & m, boost::fibers::condition_variable_any & cv) { + m.lock(); + BOOST_CHECK(test2 == 0); + test1 = 1; + cv.notify_one(); + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + std::chrono::steady_clock::time_point t = t0 + ms(250); + bool r = cv.wait_until(m, t, Pred(test2)); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + if (runs == 0) { + BOOST_CHECK(t1 - t0 < ms(250)); + BOOST_CHECK(test2 != 0); + BOOST_CHECK(r); + } else { + BOOST_CHECK(t1 - t0 - ms(250) < ms(250+100)); + BOOST_CHECK(test2 == 0); + BOOST_CHECK(!r); + } + ++runs; + m.unlock(); +} + +void fn4( boost::fibers::mutex & m, boost::fibers::condition_variable_any & cv) { + m.lock(); + BOOST_CHECK(test2 == 0); + test1 = 1; + cv.notify_one(); + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + int count=0; + while (test2 == 0 && cv.wait_for(m, ms(250)) == boost::fibers::cv_status::no_timeout) + count++; + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + if (runs == 0) { + BOOST_CHECK(t1 - t0 < ms(250)); + BOOST_CHECK(test2 != 0); + } else { + BOOST_CHECK(t1 - t0 - ms(250) < ms(count*250+100+1000)); + BOOST_CHECK(test2 == 0); + } + ++runs; + m.unlock(); +} + +void fn5( boost::fibers::mutex & m, boost::fibers::condition_variable_any & cv) { + m.lock(); + BOOST_CHECK(test2 == 0); + test1 = 1; + cv.notify_one(); + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + int count=0; + cv.wait_for(m, ms(250), Pred(test2)); + count++; + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + if (runs == 0) { + BOOST_CHECK(t1 - t0 < ms(250+1000)); + BOOST_CHECK(test2 != 0); + } else { + BOOST_CHECK(t1 - t0 - ms(250) < ms(count*250+100)); + BOOST_CHECK(test2 == 0); + } + ++runs; + m.unlock(); +} + +void do_test_condition_wait() { + test1 = 0; + test2 = 0; + runs = 0; + + boost::fibers::mutex m; + boost::fibers::condition_variable_any cv; + m.lock(); + boost::fibers::fiber f( boost::fibers::launch::post, & fn1, std::ref( m), std::ref( cv) ); + BOOST_CHECK(test1 == 0); + while (test1 == 0) + cv.wait(m); + BOOST_CHECK(test1 != 0); + test2 = 1; + m.unlock(); + cv.notify_one(); + f.join(); +} + +void test_condition_wait() { + boost::fibers::fiber( boost::fibers::launch::post, & do_test_condition_wait).join(); + do_test_condition_wait(); +} + +void do_test_condition_wait_until() { + test1 = 0; + test2 = 0; + runs = 0; + + boost::fibers::mutex m; + boost::fibers::condition_variable_any cv; + { + m.lock(); + boost::fibers::fiber f( boost::fibers::launch::post, & fn2, std::ref( m), std::ref( cv) ); + BOOST_CHECK(test1 == 0); + while (test1 == 0) + cv.wait(m); + BOOST_CHECK(test1 != 0); + test2 = 1; + m.unlock(); + cv.notify_one(); + f.join(); + } + test1 = 0; + test2 = 0; + { + m.lock(); + boost::fibers::fiber f( boost::fibers::launch::post, & fn2, std::ref( m), std::ref( cv) ); + BOOST_CHECK(test1 == 0); + while (test1 == 0) + cv.wait(m); + BOOST_CHECK(test1 != 0); + m.unlock(); + f.join(); + } +} + +void test_condition_wait_until() { + boost::fibers::fiber( boost::fibers::launch::post, & do_test_condition_wait_until).join(); + do_test_condition_wait_until(); +} + +void do_test_condition_wait_until_pred() { + test1 = 0; + test2 = 0; + runs = 0; + + boost::fibers::mutex m; + boost::fibers::condition_variable_any cv; + { + m.lock(); + boost::fibers::fiber f( boost::fibers::launch::post, & fn3, std::ref( m), std::ref( cv) ); + BOOST_CHECK(test1 == 0); + while (test1 == 0) + cv.wait(m); + BOOST_CHECK(test1 != 0); + test2 = 1; + m.unlock(); + cv.notify_one(); + f.join(); + } + test1 = 0; + test2 = 0; + { + m.lock(); + boost::fibers::fiber f( boost::fibers::launch::post, & fn3, std::ref( m), std::ref( cv) ); + BOOST_CHECK(test1 == 0); + while (test1 == 0) + cv.wait(m); + BOOST_CHECK(test1 != 0); + m.unlock(); + f.join(); + } +} + +void test_condition_wait_until_pred() { + boost::fibers::fiber( boost::fibers::launch::post, & do_test_condition_wait_until_pred).join(); + do_test_condition_wait_until_pred(); +} + +void do_test_condition_wait_for() { + test1 = 0; + test2 = 0; + runs = 0; + + boost::fibers::mutex m; + boost::fibers::condition_variable_any cv; + { + m.lock(); + boost::fibers::fiber f( boost::fibers::launch::post, & fn4, std::ref( m), std::ref( cv) ); + BOOST_CHECK(test1 == 0); + while (test1 == 0) + cv.wait(m); + BOOST_CHECK(test1 != 0); + test2 = 1; + m.unlock(); + cv.notify_one(); + f.join(); + } + test1 = 0; + test2 = 0; + { + m.lock(); + boost::fibers::fiber f( boost::fibers::launch::post, & fn4, std::ref( m), std::ref( cv) ); + BOOST_CHECK(test1 == 0); + while (test1 == 0) + cv.wait(m); + BOOST_CHECK(test1 != 0); + m.unlock(); + f.join(); + } +} + +void test_condition_wait_for() { + boost::fibers::fiber( boost::fibers::launch::post, & do_test_condition_wait_for).join(); + do_test_condition_wait_for(); +} + +void do_test_condition_wait_for_pred() { + test1 = 0; + test2 = 0; + runs = 0; + + boost::fibers::mutex m; + boost::fibers::condition_variable_any cv; + { + m.lock(); + boost::fibers::fiber f( boost::fibers::launch::post, & fn5, std::ref( m), std::ref( cv) ); + BOOST_CHECK(test1 == 0); + while (test1 == 0) + cv.wait(m); + BOOST_CHECK(test1 != 0); + test2 = 1; + m.unlock(); + cv.notify_one(); + f.join(); + } + test1 = 0; + test2 = 0; + { + m.lock(); + boost::fibers::fiber f( boost::fibers::launch::post, & fn5, std::ref( m), std::ref( cv) ); + BOOST_CHECK(test1 == 0); + while (test1 == 0) + cv.wait(m); + BOOST_CHECK(test1 != 0); + m.unlock(); + f.join(); + } +} + +void test_condition_wait_for_pred() { + boost::fibers::fiber( boost::fibers::launch::post, & do_test_condition_wait_for_pred).join(); + do_test_condition_wait_for_pred(); +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) { + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Fiber: condition_variable_any test suite"); + + test->add( BOOST_TEST_CASE( & test_one_waiter_notify_one) ); + test->add( BOOST_TEST_CASE( & test_two_waiter_notify_one) ); + test->add( BOOST_TEST_CASE( & test_two_waiter_notify_all) ); + test->add( BOOST_TEST_CASE( & test_condition_wait) ); + test->add( BOOST_TEST_CASE( & test_condition_wait_until) ); + test->add( BOOST_TEST_CASE( & test_condition_wait_until_pred) ); + test->add( BOOST_TEST_CASE( & test_condition_wait_for) ); + test->add( BOOST_TEST_CASE( & test_condition_wait_for_pred) ); + + return test; +} diff --git a/src/boost/libs/fiber/test/test_condition_variable_dispatch.cpp b/src/boost/libs/fiber/test/test_condition_variable_dispatch.cpp new file mode 100644 index 00000000..dc5e00fe --- /dev/null +++ b/src/boost/libs/fiber/test/test_condition_variable_dispatch.cpp @@ -0,0 +1,495 @@ + +// Copyright Oliver Kowalke 2013. +// 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) +// +// This test is based on the tests of Boost.Thread + +#include <chrono> +#include <cstdio> +#include <cstdlib> +#include <iostream> +#include <map> +#include <mutex> +#include <stdexcept> +#include <vector> + +#include <boost/test/unit_test.hpp> + +#include <boost/fiber/all.hpp> + +typedef std::chrono::nanoseconds ns; +typedef std::chrono::milliseconds ms; + +int value1 = 0; + +inline +std::chrono::system_clock::time_point delay(int secs, int msecs = 0, int /*nsecs*/ = 0) { + std::chrono::system_clock::time_point t = std::chrono::system_clock::now(); + t += std::chrono::seconds( secs); + t += std::chrono::milliseconds( msecs); + //t += std::chrono::nanoseconds( nsecs); + + return t; +} + +struct condition_test_data { + condition_test_data() : notified(0), awoken(0) { } + + boost::fibers::mutex mutex; + boost::fibers::condition_variable cond; + int notified; + int awoken; +}; + +void condition_test_fiber(condition_test_data* data) { + std::unique_lock<boost::fibers::mutex> lock(data->mutex); + BOOST_CHECK(lock ? true : false); + while (!(data->notified > 0)) + data->cond.wait(lock); + BOOST_CHECK(lock ? true : false); + data->awoken++; +} + +struct cond_predicate { + cond_predicate(int& var, int val) : _var(var), _val(val) { } + + bool operator()() { return _var == _val; } + + int& _var; + int _val; +private: + void operator=(cond_predicate&); + +}; + +void notify_one_fn( boost::fibers::condition_variable & cond) { + cond.notify_one(); +} + +void notify_all_fn( boost::fibers::condition_variable & cond) { + cond.notify_all(); +} + +void wait_fn( + boost::fibers::mutex & mtx, + boost::fibers::condition_variable & cond) { + std::unique_lock< boost::fibers::mutex > lk( mtx); + cond.wait( lk); + ++value1; +} + +void test_one_waiter_notify_one() { + value1 = 0; + boost::fibers::mutex mtx; + boost::fibers::condition_variable cond; + + boost::fibers::fiber f1( + boost::fibers::launch::dispatch, + wait_fn, + std::ref( mtx), + std::ref( cond) ); + BOOST_CHECK_EQUAL( 0, value1); + + boost::fibers::fiber f2( + boost::fibers::launch::dispatch, + notify_one_fn, + std::ref( cond) ); + + BOOST_CHECK_EQUAL( 0, value1); + + f1.join(); + f2.join(); + + BOOST_CHECK_EQUAL( 1, value1); +} + +void test_two_waiter_notify_one() { + value1 = 0; + boost::fibers::mutex mtx; + boost::fibers::condition_variable cond; + + boost::fibers::fiber f1( + boost::fibers::launch::dispatch, + wait_fn, + std::ref( mtx), + std::ref( cond) ); + BOOST_CHECK_EQUAL( 0, value1); + + boost::fibers::fiber f2( + boost::fibers::launch::dispatch, + wait_fn, + std::ref( mtx), + std::ref( cond) ); + BOOST_CHECK_EQUAL( 0, value1); + + boost::fibers::fiber f3( + boost::fibers::launch::dispatch, + notify_one_fn, + std::ref( cond) ); + BOOST_CHECK_EQUAL( 0, value1); + + boost::fibers::fiber f4( + boost::fibers::launch::dispatch, + notify_one_fn, + std::ref( cond) ); + BOOST_CHECK_EQUAL( 1, value1); + + f1.join(); + f2.join(); + f3.join(); + f4.join(); + + BOOST_CHECK_EQUAL( 2, value1); +} + +void test_two_waiter_notify_all() { + value1 = 0; + boost::fibers::mutex mtx; + boost::fibers::condition_variable cond; + + boost::fibers::fiber f1( + boost::fibers::launch::dispatch, + wait_fn, + std::ref( mtx), + std::ref( cond) ); + BOOST_CHECK_EQUAL( 0, value1); + + boost::fibers::fiber f2( + boost::fibers::launch::dispatch, + wait_fn, + std::ref( mtx), + std::ref( cond) ); + BOOST_CHECK_EQUAL( 0, value1); + + boost::fibers::fiber f3( + boost::fibers::launch::dispatch, + notify_all_fn, + std::ref( cond) ); + BOOST_CHECK_EQUAL( 0, value1); + + boost::fibers::fiber f4( + boost::fibers::launch::dispatch, + wait_fn, + std::ref( mtx), + std::ref( cond) ); + BOOST_CHECK_EQUAL( 2, value1); + + boost::fibers::fiber f5( + boost::fibers::launch::dispatch, + notify_all_fn, + std::ref( cond) ); + BOOST_CHECK_EQUAL( 2, value1); + + f1.join(); + f2.join(); + f3.join(); + f4.join(); + f5.join(); + + BOOST_CHECK_EQUAL( 3, value1); +} + +int test1 = 0; +int test2 = 0; + +int runs = 0; + +void fn1( boost::fibers::mutex & m, boost::fibers::condition_variable & cv) { + std::unique_lock< boost::fibers::mutex > lk( m); + BOOST_CHECK(test2 == 0); + test1 = 1; + cv.notify_one(); + while (test2 == 0) { + cv.wait(lk); + } + BOOST_CHECK(test2 != 0); +} + +void fn2( boost::fibers::mutex & m, boost::fibers::condition_variable & cv) { + std::unique_lock< boost::fibers::mutex > lk( m); + BOOST_CHECK(test2 == 0); + test1 = 1; + cv.notify_one(); + std::chrono::system_clock::time_point t0 = std::chrono::system_clock::now(); + std::chrono::system_clock::time_point t = t0 + ms(250); + int count=0; + while (test2 == 0 && cv.wait_until(lk, t) == boost::fibers::cv_status::no_timeout) + count++; + std::chrono::system_clock::time_point t1 = std::chrono::system_clock::now(); + if (runs == 0) { + BOOST_CHECK(t1 - t0 < ms(250)); + BOOST_CHECK(test2 != 0); + } else { + BOOST_CHECK(t1 - t0 - ms(250) < ms(count*250+100+1000)); + BOOST_CHECK(test2 == 0); + } + ++runs; +} + +class Pred { + int & i_; + +public: + explicit Pred(int& i) : + i_(i) + {} + + bool operator()() + { return i_ != 0; } +}; + +void fn3( boost::fibers::mutex & m, boost::fibers::condition_variable & cv) { + std::unique_lock< boost::fibers::mutex > lk( m); + BOOST_CHECK(test2 == 0); + test1 = 1; + cv.notify_one(); + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + std::chrono::steady_clock::time_point t = t0 + ms(250); + bool r = cv.wait_until(lk, t, Pred(test2)); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + if (runs == 0) { + BOOST_CHECK(t1 - t0 < ms(250)); + BOOST_CHECK(test2 != 0); + BOOST_CHECK(r); + } else { + BOOST_CHECK(t1 - t0 - ms(250) < ms(250+100)); + BOOST_CHECK(test2 == 0); + BOOST_CHECK(!r); + } + ++runs; +} + +void fn4( boost::fibers::mutex & m, boost::fibers::condition_variable & cv) { + std::unique_lock< boost::fibers::mutex > lk( m); + BOOST_CHECK(test2 == 0); + test1 = 1; + cv.notify_one(); + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + int count=0; + while (test2 == 0 && cv.wait_for(lk, ms(250)) == boost::fibers::cv_status::no_timeout) + count++; + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + if (runs == 0) { + BOOST_CHECK(t1 - t0 < ms(250)); + BOOST_CHECK(test2 != 0); + } else { + BOOST_CHECK(t1 - t0 - ms(250) < ms(count*250+100+1000)); + BOOST_CHECK(test2 == 0); + } + ++runs; +} + +void fn5( boost::fibers::mutex & m, boost::fibers::condition_variable & cv) { + std::unique_lock< boost::fibers::mutex > lk( m); + BOOST_CHECK(test2 == 0); + test1 = 1; + cv.notify_one(); + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + int count=0; + cv.wait_for(lk, ms(250), Pred(test2)); + count++; + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + if (runs == 0) { + BOOST_CHECK(t1 - t0 < ms(250+1000)); + BOOST_CHECK(test2 != 0); + } else { + BOOST_CHECK(t1 - t0 - ms(250) < ms(count*250+100)); + BOOST_CHECK(test2 == 0); + } + ++runs; +} + +void do_test_condition_wait() { + test1 = 0; + test2 = 0; + runs = 0; + + boost::fibers::mutex m; + boost::fibers::condition_variable cv; + std::unique_lock< boost::fibers::mutex > lk( m); + boost::fibers::fiber f( boost::fibers::launch::dispatch, & fn1, std::ref( m), std::ref( cv) ); + BOOST_CHECK(test1 == 0); + while (test1 == 0) + cv.wait(lk); + BOOST_CHECK(test1 != 0); + test2 = 1; + lk.unlock(); + cv.notify_one(); + f.join(); +} + +void test_condition_wait() { + boost::fibers::fiber( boost::fibers::launch::dispatch, & do_test_condition_wait).join(); + do_test_condition_wait(); +} + +void do_test_condition_wait_until() { + test1 = 0; + test2 = 0; + runs = 0; + + boost::fibers::mutex m; + boost::fibers::condition_variable cv; + { + std::unique_lock< boost::fibers::mutex > lk( m); + boost::fibers::fiber f( boost::fibers::launch::dispatch, & fn2, std::ref( m), std::ref( cv) ); + BOOST_CHECK(test1 == 0); + while (test1 == 0) + cv.wait(lk); + BOOST_CHECK(test1 != 0); + test2 = 1; + lk.unlock(); + cv.notify_one(); + f.join(); + } + test1 = 0; + test2 = 0; + { + std::unique_lock< boost::fibers::mutex > lk( m); + boost::fibers::fiber f( boost::fibers::launch::dispatch, & fn2, std::ref( m), std::ref( cv) ); + BOOST_CHECK(test1 == 0); + while (test1 == 0) + cv.wait(lk); + BOOST_CHECK(test1 != 0); + lk.unlock(); + f.join(); + } +} + +void test_condition_wait_until() { + boost::fibers::fiber( boost::fibers::launch::dispatch, & do_test_condition_wait_until).join(); + do_test_condition_wait_until(); +} + +void do_test_condition_wait_until_pred() { + test1 = 0; + test2 = 0; + runs = 0; + + boost::fibers::mutex m; + boost::fibers::condition_variable cv; + { + std::unique_lock< boost::fibers::mutex > lk( m); + boost::fibers::fiber f( boost::fibers::launch::dispatch, & fn3, std::ref( m), std::ref( cv) ); + BOOST_CHECK(test1 == 0); + while (test1 == 0) + cv.wait(lk); + BOOST_CHECK(test1 != 0); + test2 = 1; + lk.unlock(); + cv.notify_one(); + f.join(); + } + test1 = 0; + test2 = 0; + { + std::unique_lock< boost::fibers::mutex > lk( m); + boost::fibers::fiber f( boost::fibers::launch::dispatch, & fn3, std::ref( m), std::ref( cv) ); + BOOST_CHECK(test1 == 0); + while (test1 == 0) + cv.wait(lk); + BOOST_CHECK(test1 != 0); + lk.unlock(); + f.join(); + } +} + +void test_condition_wait_until_pred() { + boost::fibers::fiber( boost::fibers::launch::dispatch, & do_test_condition_wait_until_pred).join(); + do_test_condition_wait_until_pred(); +} + +void do_test_condition_wait_for() { + test1 = 0; + test2 = 0; + runs = 0; + + boost::fibers::mutex m; + boost::fibers::condition_variable cv; + { + std::unique_lock< boost::fibers::mutex > lk( m); + boost::fibers::fiber f( boost::fibers::launch::dispatch, & fn4, std::ref( m), std::ref( cv) ); + BOOST_CHECK(test1 == 0); + while (test1 == 0) + cv.wait(lk); + BOOST_CHECK(test1 != 0); + test2 = 1; + lk.unlock(); + cv.notify_one(); + f.join(); + } + test1 = 0; + test2 = 0; + { + std::unique_lock< boost::fibers::mutex > lk( m); + boost::fibers::fiber f( boost::fibers::launch::dispatch, & fn4, std::ref( m), std::ref( cv) ); + BOOST_CHECK(test1 == 0); + while (test1 == 0) + cv.wait(lk); + BOOST_CHECK(test1 != 0); + lk.unlock(); + f.join(); + } +} + +void test_condition_wait_for() { + boost::fibers::fiber( boost::fibers::launch::dispatch, & do_test_condition_wait_for).join(); + do_test_condition_wait_for(); +} + +void do_test_condition_wait_for_pred() { + test1 = 0; + test2 = 0; + runs = 0; + + boost::fibers::mutex m; + boost::fibers::condition_variable cv; + { + std::unique_lock< boost::fibers::mutex > lk( m); + boost::fibers::fiber f( boost::fibers::launch::dispatch, & fn5, std::ref( m), std::ref( cv) ); + BOOST_CHECK(test1 == 0); + while (test1 == 0) + cv.wait(lk); + BOOST_CHECK(test1 != 0); + test2 = 1; + lk.unlock(); + cv.notify_one(); + f.join(); + } + test1 = 0; + test2 = 0; + { + std::unique_lock< boost::fibers::mutex > lk( m); + boost::fibers::fiber f( boost::fibers::launch::dispatch, & fn5, std::ref( m), std::ref( cv) ); + BOOST_CHECK(test1 == 0); + while (test1 == 0) + cv.wait(lk); + BOOST_CHECK(test1 != 0); + lk.unlock(); + f.join(); + } +} + +void test_condition_wait_for_pred() { + boost::fibers::fiber( boost::fibers::launch::dispatch, & do_test_condition_wait_for_pred).join(); + do_test_condition_wait_for_pred(); +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) +{ + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Fiber: condition_variable test suite"); + + test->add( BOOST_TEST_CASE( & test_one_waiter_notify_one) ); + test->add( BOOST_TEST_CASE( & test_two_waiter_notify_one) ); + test->add( BOOST_TEST_CASE( & test_two_waiter_notify_all) ); + test->add( BOOST_TEST_CASE( & test_condition_wait) ); + test->add( BOOST_TEST_CASE( & test_condition_wait_until) ); + test->add( BOOST_TEST_CASE( & test_condition_wait_until_pred) ); + test->add( BOOST_TEST_CASE( & test_condition_wait_for) ); + test->add( BOOST_TEST_CASE( & test_condition_wait_for_pred) ); + + return test; +} diff --git a/src/boost/libs/fiber/test/test_condition_variable_post.cpp b/src/boost/libs/fiber/test/test_condition_variable_post.cpp new file mode 100644 index 00000000..3f043a7b --- /dev/null +++ b/src/boost/libs/fiber/test/test_condition_variable_post.cpp @@ -0,0 +1,495 @@ + +// Copyright Oliver Kowalke 2013. +// 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) +// +// This test is based on the tests of Boost.Thread + +#include <chrono> +#include <cstdio> +#include <cstdlib> +#include <iostream> +#include <map> +#include <mutex> +#include <stdexcept> +#include <vector> + +#include <boost/test/unit_test.hpp> + +#include <boost/fiber/all.hpp> + +typedef std::chrono::nanoseconds ns; +typedef std::chrono::milliseconds ms; + +int value1 = 0; + +inline +std::chrono::system_clock::time_point delay(int secs, int msecs = 0, int /*nsecs*/ = 0) { + std::chrono::system_clock::time_point t = std::chrono::system_clock::now(); + t += std::chrono::seconds( secs); + t += std::chrono::milliseconds( msecs); + //t += std::chrono::nanoseconds( nsecs); + + return t; +} + +struct condition_test_data { + condition_test_data() : notified(0), awoken(0) { } + + boost::fibers::mutex mutex; + boost::fibers::condition_variable cond; + int notified; + int awoken; +}; + +void condition_test_fiber(condition_test_data* data) { + std::unique_lock<boost::fibers::mutex> lock(data->mutex); + BOOST_CHECK(lock ? true : false); + while (!(data->notified > 0)) + data->cond.wait(lock); + BOOST_CHECK(lock ? true : false); + data->awoken++; +} + +struct cond_predicate { + cond_predicate(int& var, int val) : _var(var), _val(val) { } + + bool operator()() { return _var == _val; } + + int& _var; + int _val; +private: + void operator=(cond_predicate&); + +}; + +void notify_one_fn( boost::fibers::condition_variable & cond) { + cond.notify_one(); +} + +void notify_all_fn( boost::fibers::condition_variable & cond) { + cond.notify_all(); +} + +void wait_fn( + boost::fibers::mutex & mtx, + boost::fibers::condition_variable & cond) { + std::unique_lock< boost::fibers::mutex > lk( mtx); + cond.wait( lk); + ++value1; +} + +void test_one_waiter_notify_one() { + value1 = 0; + boost::fibers::mutex mtx; + boost::fibers::condition_variable cond; + + boost::fibers::fiber f1( + boost::fibers::launch::post, + wait_fn, + std::ref( mtx), + std::ref( cond) ); + BOOST_CHECK_EQUAL( 0, value1); + + boost::fibers::fiber f2( + boost::fibers::launch::post, + notify_one_fn, + std::ref( cond) ); + + BOOST_CHECK_EQUAL( 0, value1); + + f1.join(); + f2.join(); + + BOOST_CHECK_EQUAL( 1, value1); +} + +void test_two_waiter_notify_one() { + value1 = 0; + boost::fibers::mutex mtx; + boost::fibers::condition_variable cond; + + boost::fibers::fiber f1( + boost::fibers::launch::post, + wait_fn, + std::ref( mtx), + std::ref( cond) ); + BOOST_CHECK_EQUAL( 0, value1); + + boost::fibers::fiber f2( + boost::fibers::launch::post, + wait_fn, + std::ref( mtx), + std::ref( cond) ); + BOOST_CHECK_EQUAL( 0, value1); + + boost::fibers::fiber f3( + boost::fibers::launch::post, + notify_one_fn, + std::ref( cond) ); + BOOST_CHECK_EQUAL( 0, value1); + + boost::fibers::fiber f4( + boost::fibers::launch::post, + notify_one_fn, + std::ref( cond) ); + BOOST_CHECK_EQUAL( 0, value1); + + f1.join(); + f2.join(); + f3.join(); + f4.join(); + + BOOST_CHECK_EQUAL( 2, value1); +} + +void test_two_waiter_notify_all() { + value1 = 0; + boost::fibers::mutex mtx; + boost::fibers::condition_variable cond; + + boost::fibers::fiber f1( + boost::fibers::launch::post, + wait_fn, + std::ref( mtx), + std::ref( cond) ); + BOOST_CHECK_EQUAL( 0, value1); + + boost::fibers::fiber f2( + boost::fibers::launch::post, + wait_fn, + std::ref( mtx), + std::ref( cond) ); + BOOST_CHECK_EQUAL( 0, value1); + + boost::fibers::fiber f3( + boost::fibers::launch::post, + notify_all_fn, + std::ref( cond) ); + BOOST_CHECK_EQUAL( 0, value1); + + boost::fibers::fiber f4( + boost::fibers::launch::post, + wait_fn, + std::ref( mtx), + std::ref( cond) ); + BOOST_CHECK_EQUAL( 0, value1); + + boost::fibers::fiber f5( + boost::fibers::launch::post, + notify_all_fn, + std::ref( cond) ); + BOOST_CHECK_EQUAL( 0, value1); + + f1.join(); + f2.join(); + f3.join(); + f4.join(); + f5.join(); + + BOOST_CHECK_EQUAL( 3, value1); +} + +int test1 = 0; +int test2 = 0; + +int runs = 0; + +void fn1( boost::fibers::mutex & m, boost::fibers::condition_variable & cv) { + std::unique_lock< boost::fibers::mutex > lk( m); + BOOST_CHECK(test2 == 0); + test1 = 1; + cv.notify_one(); + while (test2 == 0) { + cv.wait(lk); + } + BOOST_CHECK(test2 != 0); +} + +void fn2( boost::fibers::mutex & m, boost::fibers::condition_variable & cv) { + std::unique_lock< boost::fibers::mutex > lk( m); + BOOST_CHECK(test2 == 0); + test1 = 1; + cv.notify_one(); + std::chrono::system_clock::time_point t0 = std::chrono::system_clock::now(); + std::chrono::system_clock::time_point t = t0 + ms(250); + int count=0; + while (test2 == 0 && cv.wait_until(lk, t) == boost::fibers::cv_status::no_timeout) + count++; + std::chrono::system_clock::time_point t1 = std::chrono::system_clock::now(); + if (runs == 0) { + BOOST_CHECK(t1 - t0 < ms(250)); + BOOST_CHECK(test2 != 0); + } else { + BOOST_CHECK(t1 - t0 - ms(250) < ms(count*250+100+1000)); + BOOST_CHECK(test2 == 0); + } + ++runs; +} + +class Pred { + int & i_; + +public: + explicit Pred(int& i) : + i_(i) + {} + + bool operator()() + { return i_ != 0; } +}; + +void fn3( boost::fibers::mutex & m, boost::fibers::condition_variable & cv) { + std::unique_lock< boost::fibers::mutex > lk( m); + BOOST_CHECK(test2 == 0); + test1 = 1; + cv.notify_one(); + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + std::chrono::steady_clock::time_point t = t0 + ms(250); + bool r = cv.wait_until(lk, t, Pred(test2)); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + if (runs == 0) { + BOOST_CHECK(t1 - t0 < ms(250)); + BOOST_CHECK(test2 != 0); + BOOST_CHECK(r); + } else { + BOOST_CHECK(t1 - t0 - ms(250) < ms(250+100)); + BOOST_CHECK(test2 == 0); + BOOST_CHECK(!r); + } + ++runs; +} + +void fn4( boost::fibers::mutex & m, boost::fibers::condition_variable & cv) { + std::unique_lock< boost::fibers::mutex > lk( m); + BOOST_CHECK(test2 == 0); + test1 = 1; + cv.notify_one(); + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + int count=0; + while (test2 == 0 && cv.wait_for(lk, ms(250)) == boost::fibers::cv_status::no_timeout) + count++; + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + if (runs == 0) { + BOOST_CHECK(t1 - t0 < ms(250)); + BOOST_CHECK(test2 != 0); + } else { + BOOST_CHECK(t1 - t0 - ms(250) < ms(count*250+100+1000)); + BOOST_CHECK(test2 == 0); + } + ++runs; +} + +void fn5( boost::fibers::mutex & m, boost::fibers::condition_variable & cv) { + std::unique_lock< boost::fibers::mutex > lk( m); + BOOST_CHECK(test2 == 0); + test1 = 1; + cv.notify_one(); + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + int count=0; + cv.wait_for(lk, ms(250), Pred(test2)); + count++; + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + if (runs == 0) { + BOOST_CHECK(t1 - t0 < ms(250+1000)); + BOOST_CHECK(test2 != 0); + } else { + BOOST_CHECK(t1 - t0 - ms(250) < ms(count*250+100)); + BOOST_CHECK(test2 == 0); + } + ++runs; +} + +void do_test_condition_wait() { + test1 = 0; + test2 = 0; + runs = 0; + + boost::fibers::mutex m; + boost::fibers::condition_variable cv; + std::unique_lock< boost::fibers::mutex > lk( m); + boost::fibers::fiber f( boost::fibers::launch::post, & fn1, std::ref( m), std::ref( cv) ); + BOOST_CHECK(test1 == 0); + while (test1 == 0) + cv.wait(lk); + BOOST_CHECK(test1 != 0); + test2 = 1; + lk.unlock(); + cv.notify_one(); + f.join(); +} + +void test_condition_wait() { + boost::fibers::fiber( boost::fibers::launch::post, & do_test_condition_wait).join(); + do_test_condition_wait(); +} + +void do_test_condition_wait_until() { + test1 = 0; + test2 = 0; + runs = 0; + + boost::fibers::mutex m; + boost::fibers::condition_variable cv; + { + std::unique_lock< boost::fibers::mutex > lk( m); + boost::fibers::fiber f( boost::fibers::launch::post, & fn2, std::ref( m), std::ref( cv) ); + BOOST_CHECK(test1 == 0); + while (test1 == 0) + cv.wait(lk); + BOOST_CHECK(test1 != 0); + test2 = 1; + lk.unlock(); + cv.notify_one(); + f.join(); + } + test1 = 0; + test2 = 0; + { + std::unique_lock< boost::fibers::mutex > lk( m); + boost::fibers::fiber f( boost::fibers::launch::post, & fn2, std::ref( m), std::ref( cv) ); + BOOST_CHECK(test1 == 0); + while (test1 == 0) + cv.wait(lk); + BOOST_CHECK(test1 != 0); + lk.unlock(); + f.join(); + } +} + +void test_condition_wait_until() { + boost::fibers::fiber( boost::fibers::launch::post, & do_test_condition_wait_until).join(); + do_test_condition_wait_until(); +} + +void do_test_condition_wait_until_pred() { + test1 = 0; + test2 = 0; + runs = 0; + + boost::fibers::mutex m; + boost::fibers::condition_variable cv; + { + std::unique_lock< boost::fibers::mutex > lk( m); + boost::fibers::fiber f( boost::fibers::launch::post, & fn3, std::ref( m), std::ref( cv) ); + BOOST_CHECK(test1 == 0); + while (test1 == 0) + cv.wait(lk); + BOOST_CHECK(test1 != 0); + test2 = 1; + lk.unlock(); + cv.notify_one(); + f.join(); + } + test1 = 0; + test2 = 0; + { + std::unique_lock< boost::fibers::mutex > lk( m); + boost::fibers::fiber f( boost::fibers::launch::post, & fn3, std::ref( m), std::ref( cv) ); + BOOST_CHECK(test1 == 0); + while (test1 == 0) + cv.wait(lk); + BOOST_CHECK(test1 != 0); + lk.unlock(); + f.join(); + } +} + +void test_condition_wait_until_pred() { + boost::fibers::fiber( boost::fibers::launch::post, & do_test_condition_wait_until_pred).join(); + do_test_condition_wait_until_pred(); +} + +void do_test_condition_wait_for() { + test1 = 0; + test2 = 0; + runs = 0; + + boost::fibers::mutex m; + boost::fibers::condition_variable cv; + { + std::unique_lock< boost::fibers::mutex > lk( m); + boost::fibers::fiber f( boost::fibers::launch::post, & fn4, std::ref( m), std::ref( cv) ); + BOOST_CHECK(test1 == 0); + while (test1 == 0) + cv.wait(lk); + BOOST_CHECK(test1 != 0); + test2 = 1; + lk.unlock(); + cv.notify_one(); + f.join(); + } + test1 = 0; + test2 = 0; + { + std::unique_lock< boost::fibers::mutex > lk( m); + boost::fibers::fiber f( boost::fibers::launch::post, & fn4, std::ref( m), std::ref( cv) ); + BOOST_CHECK(test1 == 0); + while (test1 == 0) + cv.wait(lk); + BOOST_CHECK(test1 != 0); + lk.unlock(); + f.join(); + } +} + +void test_condition_wait_for() { + boost::fibers::fiber( boost::fibers::launch::post, & do_test_condition_wait_for).join(); + do_test_condition_wait_for(); +} + +void do_test_condition_wait_for_pred() { + test1 = 0; + test2 = 0; + runs = 0; + + boost::fibers::mutex m; + boost::fibers::condition_variable cv; + { + std::unique_lock< boost::fibers::mutex > lk( m); + boost::fibers::fiber f( boost::fibers::launch::post, & fn5, std::ref( m), std::ref( cv) ); + BOOST_CHECK(test1 == 0); + while (test1 == 0) + cv.wait(lk); + BOOST_CHECK(test1 != 0); + test2 = 1; + lk.unlock(); + cv.notify_one(); + f.join(); + } + test1 = 0; + test2 = 0; + { + std::unique_lock< boost::fibers::mutex > lk( m); + boost::fibers::fiber f( boost::fibers::launch::post, & fn5, std::ref( m), std::ref( cv) ); + BOOST_CHECK(test1 == 0); + while (test1 == 0) + cv.wait(lk); + BOOST_CHECK(test1 != 0); + lk.unlock(); + f.join(); + } +} + +void test_condition_wait_for_pred() { + boost::fibers::fiber( boost::fibers::launch::post, & do_test_condition_wait_for_pred).join(); + do_test_condition_wait_for_pred(); +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) +{ + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Fiber: condition_variable test suite"); + + test->add( BOOST_TEST_CASE( & test_one_waiter_notify_one) ); + test->add( BOOST_TEST_CASE( & test_two_waiter_notify_one) ); + test->add( BOOST_TEST_CASE( & test_two_waiter_notify_all) ); + test->add( BOOST_TEST_CASE( & test_condition_wait) ); + test->add( BOOST_TEST_CASE( & test_condition_wait_until) ); + test->add( BOOST_TEST_CASE( & test_condition_wait_until_pred) ); + test->add( BOOST_TEST_CASE( & test_condition_wait_for) ); + test->add( BOOST_TEST_CASE( & test_condition_wait_for_pred) ); + + return test; +} diff --git a/src/boost/libs/fiber/test/test_fiber_dispatch.cpp b/src/boost/libs/fiber/test/test_fiber_dispatch.cpp new file mode 100644 index 00000000..1126b001 --- /dev/null +++ b/src/boost/libs/fiber/test/test_fiber_dispatch.cpp @@ -0,0 +1,440 @@ + +// Copyright Oliver Kowalke 2013. +// 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) +// +// This test is based on the tests of Boost.Thread + +#include <chrono> +#include <mutex> +#include <sstream> +#include <string> + +#include <boost/assert.hpp> +#include <boost/test/unit_test.hpp> + +#include <boost/fiber/all.hpp> + +int value1 = 0; +std::string value2 = ""; + +struct X { + int value; + + void foo( int i) { + value = i; + } +}; + +class copyable { +public: + bool state; + int value; + + copyable() : + state( false), + value( -1) { + } + + copyable( int v) : + state( true), + value( v) { + } + + void operator()() { + value1 = value; + } +}; + +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; + } +}; + +class detachable { +private: + int alive_count_; + +public: + static int alive_count; + static bool was_running; + + detachable() : + alive_count_( 1) { + ++alive_count; + } + + detachable( detachable const& g) : + alive_count_( g.alive_count_) { + ++alive_count; + } + + ~detachable() { + alive_count_ = 0; + --alive_count; + } + + void operator()() { + BOOST_CHECK_EQUAL(1, alive_count_); + was_running = true; + } +}; + +int detachable::alive_count = 0; +bool detachable::was_running = false; + +void fn1() { + value1 = 1; +} + +void fn2( int i, std::string const& s) { + value1 = i; + value2 = s; +} + +void fn3( int & i) { + i = 1; + boost::this_fiber::yield(); + i = 1; + boost::this_fiber::yield(); + i = 2; + boost::this_fiber::yield(); + i = 3; + boost::this_fiber::yield(); + i = 5; + boost::this_fiber::yield(); + i = 8; +} + +void fn4() { + boost::this_fiber::yield(); +} + +void fn5() { + boost::fibers::fiber f( boost::fibers::launch::dispatch, fn4); + BOOST_CHECK( f.joinable() ); + f.join(); + BOOST_CHECK( ! f.joinable() ); +} + +void test_scheduler_dtor() { + boost::fibers::context * ctx( + boost::fibers::context::active() ); + (void)ctx; +} + +void test_join_fn() { + { + value1 = 0; + boost::fibers::fiber f( boost::fibers::launch::dispatch, fn1); + f.join(); + BOOST_CHECK_EQUAL( value1, 1); + } + { + value1 = 0; + value2 = ""; + boost::fibers::fiber f( boost::fibers::launch::dispatch, fn2, 3, "abc"); + f.join(); + BOOST_CHECK_EQUAL( value1, 3); + BOOST_CHECK_EQUAL( value2, "abc"); + } +} + +void test_join_memfn() { + X x = {0}; + BOOST_CHECK_EQUAL( x.value, 0); + boost::fibers::fiber( boost::fibers::launch::dispatch, & X::foo, & x, 3).join(); + BOOST_CHECK_EQUAL( x.value, 3); +} + +void test_join_copyable() { + value1 = 0; + copyable cp( 3); + BOOST_CHECK( cp.state); + BOOST_CHECK_EQUAL( value1, 0); + boost::fibers::fiber f( boost::fibers::launch::dispatch, cp); + f.join(); + BOOST_CHECK( cp.state); + BOOST_CHECK_EQUAL( value1, 3); +} + +void test_join_moveable() { + value1 = 0; + moveable mv( 7); + BOOST_CHECK( mv.state); + BOOST_CHECK_EQUAL( value1, 0); + boost::fibers::fiber f( boost::fibers::launch::dispatch, std::move( mv) ); + f.join(); + BOOST_CHECK( ! mv.state); + BOOST_CHECK_EQUAL( value1, 7); +} + +void test_join_lambda() { + { + value1 = 0; + value2 = ""; + int i = 3; + std::string abc("abc"); + boost::fibers::fiber f( + boost::fibers::launch::dispatch, [i,abc]() { + value1 = i; + value2 = abc; + }); + f.join(); + BOOST_CHECK_EQUAL( value1, 3); + BOOST_CHECK_EQUAL( value2, "abc"); + } + { + value1 = 0; + value2 = ""; + int i = 3; + std::string abc("abc"); + boost::fibers::fiber f( + boost::fibers::launch::dispatch, [](int i, std::string const& abc) { + value1 = i; + value2 = abc; + }, + i, abc); + f.join(); + BOOST_CHECK_EQUAL( value1, 3); + BOOST_CHECK_EQUAL( value2, "abc"); + } +} + +void test_join_bind() { + { + value1 = 0; + value2 = ""; + int i = 3; + std::string abc("abc"); + boost::fibers::fiber f( + boost::fibers::launch::dispatch, std::bind( + [i,abc]() { + value1 = i; + value2 = abc; + } + )); + f.join(); + BOOST_CHECK_EQUAL( value1, 3); + BOOST_CHECK_EQUAL( value2, "abc"); + } + { + value1 = 0; + value2 = ""; + std::string abc("abc"); + boost::fibers::fiber f( + boost::fibers::launch::dispatch, std::bind( + [](std::string & str) { + value1 = 3; + value2 = str; + }, + abc + )); + f.join(); + BOOST_CHECK_EQUAL( value1, 3); + BOOST_CHECK_EQUAL( value2, "abc"); + } + { + value1 = 0; + value2 = ""; + std::string abc("abc"); + boost::fibers::fiber f( + boost::fibers::launch::dispatch, std::bind( + []( std::string & str) { + value1 = 3; + value2 = str; + }, + std::placeholders::_1 + ), + std::ref( abc) ); + f.join(); + BOOST_CHECK_EQUAL( value1, 3); + BOOST_CHECK_EQUAL( value2, "abc"); + } +} + +void test_join_in_fiber() { + // spawn fiber f + // f spawns an new fiber f' in its fiber-fn + // f' yields in its fiber-fn + // f joins s' and gets suspended (waiting on s') + boost::fibers::fiber f( boost::fibers::launch::dispatch, fn5); + BOOST_CHECK( f.joinable() ); + // join() resumes f + f' which completes + f.join(); + BOOST_CHECK( ! f.joinable() ); +} + +void test_move_fiber() { + boost::fibers::fiber f1; + BOOST_CHECK( ! f1.joinable() ); + boost::fibers::fiber f2( boost::fibers::launch::dispatch, fn1); + BOOST_CHECK( f2.joinable() ); + f1 = std::move( f2); + BOOST_CHECK( f1.joinable() ); + BOOST_CHECK( ! f2.joinable() ); + f1.join(); + BOOST_CHECK( ! f1.joinable() ); + BOOST_CHECK( ! f2.joinable() ); +} + +void test_id() { + boost::fibers::fiber f1; + boost::fibers::fiber f2( boost::fibers::launch::dispatch, fn1); + BOOST_CHECK( ! f1.joinable() ); + BOOST_CHECK( f2.joinable() ); + + BOOST_CHECK_EQUAL( boost::fibers::fiber::id(), f1.get_id() ); + BOOST_CHECK( boost::fibers::fiber::id() != f2.get_id() ); + + boost::fibers::fiber f3( boost::fibers::launch::dispatch, fn1); + BOOST_CHECK( f2.get_id() != f3.get_id() ); + + f1 = std::move( f2); + BOOST_CHECK( f1.joinable() ); + BOOST_CHECK( ! f2.joinable() ); + + BOOST_CHECK( boost::fibers::fiber::id() != f1.get_id() ); + BOOST_CHECK_EQUAL( boost::fibers::fiber::id(), f2.get_id() ); + + BOOST_CHECK( ! f2.joinable() ); + + f1.join(); + f3.join(); +} + +void test_yield() { + int v1 = 0, v2 = 0; + BOOST_CHECK_EQUAL( 0, v1); + BOOST_CHECK_EQUAL( 0, v2); + boost::fibers::fiber f1( boost::fibers::launch::dispatch, fn3, std::ref( v1) ); + boost::fibers::fiber f2( boost::fibers::launch::dispatch, fn3, std::ref( v2) ); + f1.join(); + f2.join(); + BOOST_CHECK( ! f1.joinable() ); + BOOST_CHECK( ! f2.joinable() ); + BOOST_CHECK_EQUAL( 8, v1); + BOOST_CHECK_EQUAL( 8, v2); +} + +void test_sleep_for() { + typedef std::chrono::system_clock Clock; + typedef Clock::time_point time_point; + std::chrono::milliseconds ms(500); + time_point t0 = Clock::now(); + boost::this_fiber::sleep_for(ms); + time_point t1 = Clock::now(); + std::chrono::nanoseconds ns = (t1 - t0) - ms; + std::chrono::nanoseconds err = ms / 10; + // This test is spurious as it depends on the time the fiber system switches the fiber + BOOST_CHECK((std::max)(ns.count(), -ns.count()) < (err+std::chrono::milliseconds(1000)).count()); +} + +void test_sleep_until() { + { + typedef std::chrono::steady_clock Clock; + typedef Clock::time_point time_point; + std::chrono::milliseconds ms(500); + time_point t0 = Clock::now(); + boost::this_fiber::sleep_until(t0 + ms); + time_point t1 = Clock::now(); + std::chrono::nanoseconds ns = (t1 - t0) - ms; + std::chrono::nanoseconds err = ms / 10; + // This test is spurious as it depends on the time the thread system switches the threads + BOOST_CHECK((std::max)(ns.count(), -ns.count()) < (err+std::chrono::milliseconds(1000)).count()); + } + { + typedef std::chrono::system_clock Clock; + typedef Clock::time_point time_point; + std::chrono::milliseconds ms(500); + time_point t0 = Clock::now(); + boost::this_fiber::sleep_until(t0 + ms); + time_point t1 = Clock::now(); + std::chrono::nanoseconds ns = (t1 - t0) - ms; + std::chrono::nanoseconds err = ms / 10; + // This test is spurious as it depends on the time the thread system switches the threads + BOOST_CHECK((std::max)(ns.count(), -ns.count()) < (err+std::chrono::milliseconds(1000)).count()); + } +} + +void do_wait( boost::fibers::barrier* b) { + b->wait(); +} + +void test_detach() { + { + boost::fibers::fiber f( boost::fibers::launch::dispatch, (detachable()) ); + BOOST_CHECK( f.joinable() ); + f.detach(); + BOOST_CHECK( ! f.joinable() ); + boost::this_fiber::sleep_for( std::chrono::milliseconds(250) ); + BOOST_CHECK( detachable::was_running); + BOOST_CHECK_EQUAL( 0, detachable::alive_count); + } + { + boost::fibers::fiber f( boost::fibers::launch::dispatch, (detachable()) ); + BOOST_CHECK( f.joinable() ); + boost::this_fiber::yield(); + f.detach(); + BOOST_CHECK( ! f.joinable() ); + boost::this_fiber::sleep_for( std::chrono::milliseconds(250) ); + BOOST_CHECK( detachable::was_running); + BOOST_CHECK_EQUAL( 0, detachable::alive_count); + } +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) { + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Fiber: fiber test suite"); + + test->add( BOOST_TEST_CASE( & test_scheduler_dtor) ); + test->add( BOOST_TEST_CASE( & test_join_fn) ); + test->add( BOOST_TEST_CASE( & test_join_memfn) ); + test->add( BOOST_TEST_CASE( & test_join_copyable) ); + test->add( BOOST_TEST_CASE( & test_join_moveable) ); + test->add( BOOST_TEST_CASE( & test_join_lambda) ); + test->add( BOOST_TEST_CASE( & test_join_bind) ); + test->add( BOOST_TEST_CASE( & test_join_in_fiber) ); + test->add( BOOST_TEST_CASE( & test_move_fiber) ); + test->add( BOOST_TEST_CASE( & test_yield) ); + test->add( BOOST_TEST_CASE( & test_sleep_for) ); + test->add( BOOST_TEST_CASE( & test_sleep_until) ); + test->add( BOOST_TEST_CASE( & test_detach) ); + + return test; +} diff --git a/src/boost/libs/fiber/test/test_fiber_post.cpp b/src/boost/libs/fiber/test/test_fiber_post.cpp new file mode 100644 index 00000000..01a7e74c --- /dev/null +++ b/src/boost/libs/fiber/test/test_fiber_post.cpp @@ -0,0 +1,440 @@ + +// Copyright Oliver Kowalke 2013. +// 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) +// +// This test is based on the tests of Boost.Thread + +#include <chrono> +#include <mutex> +#include <sstream> +#include <string> + +#include <boost/assert.hpp> +#include <boost/test/unit_test.hpp> + +#include <boost/fiber/all.hpp> + +int value1 = 0; +std::string value2 = ""; + +struct X { + int value; + + void foo( int i) { + value = i; + } +}; + +class copyable { +public: + bool state; + int value; + + copyable() : + state( false), + value( -1) { + } + + copyable( int v) : + state( true), + value( v) { + } + + void operator()() { + value1 = value; + } +}; + +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; + } +}; + +class detachable { +private: + int alive_count_; + +public: + static int alive_count; + static bool was_running; + + detachable() : + alive_count_( 1) { + ++alive_count; + } + + detachable( detachable const& g) : + alive_count_( g.alive_count_) { + ++alive_count; + } + + ~detachable() { + alive_count_ = 0; + --alive_count; + } + + void operator()() { + BOOST_CHECK_EQUAL(1, alive_count_); + was_running = true; + } +}; + +int detachable::alive_count = 0; +bool detachable::was_running = false; + +void fn1() { + value1 = 1; +} + +void fn2( int i, std::string const& s) { + value1 = i; + value2 = s; +} + +void fn3( int & i) { + i = 1; + boost::this_fiber::yield(); + i = 1; + boost::this_fiber::yield(); + i = 2; + boost::this_fiber::yield(); + i = 3; + boost::this_fiber::yield(); + i = 5; + boost::this_fiber::yield(); + i = 8; +} + +void fn4() { + boost::this_fiber::yield(); +} + +void fn5() { + boost::fibers::fiber f( boost::fibers::launch::post, fn4); + BOOST_CHECK( f.joinable() ); + f.join(); + BOOST_CHECK( ! f.joinable() ); +} + +void test_scheduler_dtor() { + boost::fibers::context * ctx( + boost::fibers::context::active() ); + (void)ctx; +} + +void test_join_fn() { + { + value1 = 0; + boost::fibers::fiber f( boost::fibers::launch::post, fn1); + f.join(); + BOOST_CHECK_EQUAL( value1, 1); + } + { + value1 = 0; + value2 = ""; + boost::fibers::fiber f( boost::fibers::launch::post, fn2, 3, "abc"); + f.join(); + BOOST_CHECK_EQUAL( value1, 3); + BOOST_CHECK_EQUAL( value2, "abc"); + } +} + +void test_join_memfn() { + X x = {0}; + BOOST_CHECK_EQUAL( x.value, 0); + boost::fibers::fiber( boost::fibers::launch::post, & X::foo, & x, 3).join(); + BOOST_CHECK_EQUAL( x.value, 3); +} + +void test_join_copyable() { + value1 = 0; + copyable cp( 3); + BOOST_CHECK( cp.state); + BOOST_CHECK_EQUAL( value1, 0); + boost::fibers::fiber f( boost::fibers::launch::post, cp); + f.join(); + BOOST_CHECK( cp.state); + BOOST_CHECK_EQUAL( value1, 3); +} + +void test_join_moveable() { + value1 = 0; + moveable mv( 7); + BOOST_CHECK( mv.state); + BOOST_CHECK_EQUAL( value1, 0); + boost::fibers::fiber f( boost::fibers::launch::post, std::move( mv) ); + f.join(); + BOOST_CHECK( ! mv.state); + BOOST_CHECK_EQUAL( value1, 7); +} + +void test_join_lambda() { + { + value1 = 0; + value2 = ""; + int i = 3; + std::string abc("abc"); + boost::fibers::fiber f( + boost::fibers::launch::post, [i,abc]() { + value1 = i; + value2 = abc; + }); + f.join(); + BOOST_CHECK_EQUAL( value1, 3); + BOOST_CHECK_EQUAL( value2, "abc"); + } + { + value1 = 0; + value2 = ""; + int i = 3; + std::string abc("abc"); + boost::fibers::fiber f( + boost::fibers::launch::post, [](int i, std::string const& abc) { + value1 = i; + value2 = abc; + }, + i, abc); + f.join(); + BOOST_CHECK_EQUAL( value1, 3); + BOOST_CHECK_EQUAL( value2, "abc"); + } +} + +void test_join_bind() { + { + value1 = 0; + value2 = ""; + int i = 3; + std::string abc("abc"); + boost::fibers::fiber f( + boost::fibers::launch::post, std::bind( + [i,abc]() { + value1 = i; + value2 = abc; + } + )); + f.join(); + BOOST_CHECK_EQUAL( value1, 3); + BOOST_CHECK_EQUAL( value2, "abc"); + } + { + value1 = 0; + value2 = ""; + std::string abc("abc"); + boost::fibers::fiber f( + boost::fibers::launch::post, std::bind( + [](std::string & str) { + value1 = 3; + value2 = str; + }, + abc + )); + f.join(); + BOOST_CHECK_EQUAL( value1, 3); + BOOST_CHECK_EQUAL( value2, "abc"); + } + { + value1 = 0; + value2 = ""; + std::string abc("abc"); + boost::fibers::fiber f( + boost::fibers::launch::post, std::bind( + []( std::string & str) { + value1 = 3; + value2 = str; + }, + std::placeholders::_1 + ), + std::ref( abc) ); + f.join(); + BOOST_CHECK_EQUAL( value1, 3); + BOOST_CHECK_EQUAL( value2, "abc"); + } +} + +void test_join_in_fiber() { + // spawn fiber f + // f spawns an new fiber f' in its fiber-fn + // f' yields in its fiber-fn + // f joins s' and gets suspended (waiting on s') + boost::fibers::fiber f( boost::fibers::launch::post, fn5); + BOOST_CHECK( f.joinable() ); + // join() resumes f + f' which completes + f.join(); + BOOST_CHECK( ! f.joinable() ); +} + +void test_move_fiber() { + boost::fibers::fiber f1; + BOOST_CHECK( ! f1.joinable() ); + boost::fibers::fiber f2( boost::fibers::launch::post, fn1); + BOOST_CHECK( f2.joinable() ); + f1 = std::move( f2); + BOOST_CHECK( f1.joinable() ); + BOOST_CHECK( ! f2.joinable() ); + f1.join(); + BOOST_CHECK( ! f1.joinable() ); + BOOST_CHECK( ! f2.joinable() ); +} + +void test_id() { + boost::fibers::fiber f1; + boost::fibers::fiber f2( boost::fibers::launch::post, fn1); + BOOST_CHECK( ! f1.joinable() ); + BOOST_CHECK( f2.joinable() ); + + BOOST_CHECK_EQUAL( boost::fibers::fiber::id(), f1.get_id() ); + BOOST_CHECK( boost::fibers::fiber::id() != f2.get_id() ); + + boost::fibers::fiber f3( boost::fibers::launch::post, fn1); + BOOST_CHECK( f2.get_id() != f3.get_id() ); + + f1 = std::move( f2); + BOOST_CHECK( f1.joinable() ); + BOOST_CHECK( ! f2.joinable() ); + + BOOST_CHECK( boost::fibers::fiber::id() != f1.get_id() ); + BOOST_CHECK_EQUAL( boost::fibers::fiber::id(), f2.get_id() ); + + BOOST_CHECK( ! f2.joinable() ); + + f1.join(); + f3.join(); +} + +void test_yield() { + int v1 = 0, v2 = 0; + BOOST_CHECK_EQUAL( 0, v1); + BOOST_CHECK_EQUAL( 0, v2); + boost::fibers::fiber f1( boost::fibers::launch::post, fn3, std::ref( v1) ); + boost::fibers::fiber f2( boost::fibers::launch::post, fn3, std::ref( v2) ); + f1.join(); + f2.join(); + BOOST_CHECK( ! f1.joinable() ); + BOOST_CHECK( ! f2.joinable() ); + BOOST_CHECK_EQUAL( 8, v1); + BOOST_CHECK_EQUAL( 8, v2); +} + +void test_sleep_for() { + typedef std::chrono::system_clock Clock; + typedef Clock::time_point time_point; + std::chrono::milliseconds ms(500); + time_point t0 = Clock::now(); + boost::this_fiber::sleep_for(ms); + time_point t1 = Clock::now(); + std::chrono::nanoseconds ns = (t1 - t0) - ms; + std::chrono::nanoseconds err = ms / 10; + // This test is spurious as it depends on the time the fiber system switches the fiber + BOOST_CHECK((std::max)(ns.count(), -ns.count()) < (err+std::chrono::milliseconds(1000)).count()); +} + +void test_sleep_until() { + { + typedef std::chrono::steady_clock Clock; + typedef Clock::time_point time_point; + std::chrono::milliseconds ms(500); + time_point t0 = Clock::now(); + boost::this_fiber::sleep_until(t0 + ms); + time_point t1 = Clock::now(); + std::chrono::nanoseconds ns = (t1 - t0) - ms; + std::chrono::nanoseconds err = ms / 10; + // This test is spurious as it depends on the time the thread system switches the threads + BOOST_CHECK((std::max)(ns.count(), -ns.count()) < (err+std::chrono::milliseconds(1000)).count()); + } + { + typedef std::chrono::system_clock Clock; + typedef Clock::time_point time_point; + std::chrono::milliseconds ms(500); + time_point t0 = Clock::now(); + boost::this_fiber::sleep_until(t0 + ms); + time_point t1 = Clock::now(); + std::chrono::nanoseconds ns = (t1 - t0) - ms; + std::chrono::nanoseconds err = ms / 10; + // This test is spurious as it depends on the time the thread system switches the threads + BOOST_CHECK((std::max)(ns.count(), -ns.count()) < (err+std::chrono::milliseconds(1000)).count()); + } +} + +void do_wait( boost::fibers::barrier* b) { + b->wait(); +} + +void test_detach() { + { + boost::fibers::fiber f( boost::fibers::launch::post, (detachable()) ); + BOOST_CHECK( f.joinable() ); + f.detach(); + BOOST_CHECK( ! f.joinable() ); + boost::this_fiber::sleep_for( std::chrono::milliseconds(250) ); + BOOST_CHECK( detachable::was_running); + BOOST_CHECK_EQUAL( 0, detachable::alive_count); + } + { + boost::fibers::fiber f( boost::fibers::launch::post, (detachable()) ); + BOOST_CHECK( f.joinable() ); + boost::this_fiber::yield(); + f.detach(); + BOOST_CHECK( ! f.joinable() ); + boost::this_fiber::sleep_for( std::chrono::milliseconds(250) ); + BOOST_CHECK( detachable::was_running); + BOOST_CHECK_EQUAL( 0, detachable::alive_count); + } +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) { + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Fiber: fiber test suite"); + + test->add( BOOST_TEST_CASE( & test_scheduler_dtor) ); + test->add( BOOST_TEST_CASE( & test_join_fn) ); + test->add( BOOST_TEST_CASE( & test_join_memfn) ); + test->add( BOOST_TEST_CASE( & test_join_copyable) ); + test->add( BOOST_TEST_CASE( & test_join_moveable) ); + test->add( BOOST_TEST_CASE( & test_join_lambda) ); + test->add( BOOST_TEST_CASE( & test_join_bind) ); + test->add( BOOST_TEST_CASE( & test_join_in_fiber) ); + test->add( BOOST_TEST_CASE( & test_move_fiber) ); + test->add( BOOST_TEST_CASE( & test_yield) ); + test->add( BOOST_TEST_CASE( & test_sleep_for) ); + test->add( BOOST_TEST_CASE( & test_sleep_until) ); + test->add( BOOST_TEST_CASE( & test_detach) ); + + return test; +} diff --git a/src/boost/libs/fiber/test/test_fss_dispatch.cpp b/src/boost/libs/fiber/test/test_fss_dispatch.cpp new file mode 100644 index 00000000..367c6aa1 --- /dev/null +++ b/src/boost/libs/fiber/test/test_fss_dispatch.cpp @@ -0,0 +1,237 @@ +// Copyright (C) 2001-2003 +// William E. Kempf +// Copyright (C) 2007 Anthony Williams +// +// 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 <iostream> +#include <mutex> + +#include <boost/test/unit_test.hpp> + +#include <boost/fiber/all.hpp> + +boost::fibers::mutex check_mutex; +boost::fibers::mutex fss_mutex; +int fss_instances = 0; +int fss_total = 0; + +struct fss_value_t { + fss_value_t() { + std::unique_lock<boost::fibers::mutex> lock(fss_mutex); + ++fss_instances; + ++fss_total; + value = 0; + } + ~fss_value_t() { + std::unique_lock<boost::fibers::mutex> lock(fss_mutex); + --fss_instances; + } + int value; +}; + +boost::fibers::fiber_specific_ptr<fss_value_t> fss_value; + +void fss_fiber() { + fss_value.reset(new fss_value_t()); + for (int i=0; i<1000; ++i) { + int& n = fss_value->value; + if (n != i) { + std::unique_lock<boost::fibers::mutex> lock(check_mutex); + BOOST_CHECK_EQUAL(n, i); + } + ++n; + } +} + +void fss() { + fss_instances = 0; + fss_total = 0; + + boost::fibers::fiber f1( boost::fibers::launch::dispatch, fss_fiber); + boost::fibers::fiber f2( boost::fibers::launch::dispatch, fss_fiber); + boost::fibers::fiber f3( boost::fibers::launch::dispatch, fss_fiber); + boost::fibers::fiber f4( boost::fibers::launch::dispatch, fss_fiber); + boost::fibers::fiber f5( boost::fibers::launch::dispatch, fss_fiber); + f1.join(); + f2.join(); + f3.join(); + f4.join(); + f5.join(); + + std::cout + << "fss_instances = " << fss_instances + << "; fss_total = " << fss_total + << "\n"; + std::cout.flush(); + + BOOST_CHECK_EQUAL(fss_instances, 0); + BOOST_CHECK_EQUAL(fss_total, 5); +} + +void test_fss() { + boost::fibers::fiber( boost::fibers::launch::dispatch, fss).join(); +} + +bool fss_cleanup_called=false; + +struct Dummy { +}; + +void fss_custom_cleanup(Dummy* d) { + delete d; + fss_cleanup_called=true; +} + +boost::fibers::fiber_specific_ptr<Dummy> fss_with_cleanup(fss_custom_cleanup); + +void fss_fiber_with_custom_cleanup() { + fss_with_cleanup.reset(new Dummy); +} + +void fss_with_custom_cleanup() { + boost::fibers::fiber f( boost::fibers::launch::dispatch, fss_fiber_with_custom_cleanup); + try { + f.join(); + } catch(...) { + f.join(); + throw; + } + + BOOST_CHECK(fss_cleanup_called); +} + +void test_fss_with_custom_cleanup() { + boost::fibers::fiber( boost::fibers::launch::dispatch, fss_with_custom_cleanup).join(); +} + +Dummy* fss_object=new Dummy; + +void fss_fiber_with_custom_cleanup_and_release() { + fss_with_cleanup.reset(fss_object); + fss_with_cleanup.release(); +} + +void do_test_fss_does_no_cleanup_after_release() { + fss_cleanup_called=false; + boost::fibers::fiber f( boost::fibers::launch::dispatch, fss_fiber_with_custom_cleanup_and_release); + try { + f.join(); + } catch(...) { + f.join(); + throw; + } + + BOOST_CHECK(!fss_cleanup_called); + if(!fss_cleanup_called) { + delete fss_object; + } +} + +struct dummy_class_tracks_deletions { + static unsigned deletions; + + ~dummy_class_tracks_deletions() { + ++deletions; + } +}; + +unsigned dummy_class_tracks_deletions::deletions=0; + +boost::fibers::fiber_specific_ptr<dummy_class_tracks_deletions> fss_with_null_cleanup(NULL); + +void fss_fiber_with_null_cleanup(dummy_class_tracks_deletions* delete_tracker) { + fss_with_null_cleanup.reset(delete_tracker); +} + +void do_test_fss_does_no_cleanup_with_null_cleanup_function() { + dummy_class_tracks_deletions* delete_tracker=new dummy_class_tracks_deletions; + boost::fibers::fiber f( boost::fibers::launch::dispatch, [&delete_tracker](){ + fss_fiber_with_null_cleanup( delete_tracker); }); + try { + f.join(); + } catch(...) { + f.join(); + throw; + } + + BOOST_CHECK(!dummy_class_tracks_deletions::deletions); + if(!dummy_class_tracks_deletions::deletions) { + delete delete_tracker; + } +} + +void test_fss_does_no_cleanup_after_release() { + boost::fibers::fiber( boost::fibers::launch::dispatch, do_test_fss_does_no_cleanup_after_release).join(); +} + +void test_fss_does_no_cleanup_with_null_cleanup_function() { + boost::fibers::fiber( boost::fibers::launch::dispatch, do_test_fss_does_no_cleanup_with_null_cleanup_function).join(); +} + + +void fiber_with_local_fss_ptr() { + { + boost::fibers::fiber_specific_ptr<Dummy> local_fss(fss_custom_cleanup); + + local_fss.reset(new Dummy); + } + BOOST_CHECK(fss_cleanup_called); + fss_cleanup_called=false; +} + +void fss_does_not_call_cleanup_after_ptr_destroyed() { + boost::fibers::fiber( boost::fibers::launch::dispatch, fiber_with_local_fss_ptr).join(); + BOOST_CHECK(!fss_cleanup_called); +} + +void test_fss_does_not_call_cleanup_after_ptr_destroyed() { + boost::fibers::fiber( boost::fibers::launch::dispatch, fss_does_not_call_cleanup_after_ptr_destroyed).join(); +} + + +void fss_cleanup_not_called_for_null_pointer() { + boost::fibers::fiber_specific_ptr<Dummy> local_fss(fss_custom_cleanup); + local_fss.reset(new Dummy); + fss_cleanup_called=false; + local_fss.reset(0); + BOOST_CHECK(fss_cleanup_called); + fss_cleanup_called=false; + local_fss.reset(new Dummy); + BOOST_CHECK(!fss_cleanup_called); +} + +void test_fss_cleanup_not_called_for_null_pointer() { + boost::fibers::fiber( boost::fibers::launch::dispatch, fss_cleanup_not_called_for_null_pointer).join(); +} + + +void fss_at_the_same_adress() { + for(int i=0; i<2; i++) { + boost::fibers::fiber_specific_ptr<Dummy> local_fss(fss_custom_cleanup); + local_fss.reset(new Dummy); + fss_cleanup_called=false; + BOOST_CHECK(fss_cleanup_called); + fss_cleanup_called=false; + BOOST_CHECK(!fss_cleanup_called); + } +} + +void test_fss_at_the_same_adress() { + boost::fibers::fiber( boost::fibers::launch::dispatch, fss_at_the_same_adress).join(); +} + +boost::unit_test::test_suite* init_unit_test_suite(int, char*[]) { + boost::unit_test::test_suite* test = + BOOST_TEST_SUITE("Boost.Fiber: fss test suite"); + + test->add(BOOST_TEST_CASE(test_fss)); + test->add(BOOST_TEST_CASE(test_fss_with_custom_cleanup)); + test->add(BOOST_TEST_CASE(test_fss_does_no_cleanup_after_release)); + test->add(BOOST_TEST_CASE(test_fss_does_no_cleanup_with_null_cleanup_function)); + test->add(BOOST_TEST_CASE(test_fss_does_not_call_cleanup_after_ptr_destroyed)); + test->add(BOOST_TEST_CASE(test_fss_cleanup_not_called_for_null_pointer)); + + return test; +} diff --git a/src/boost/libs/fiber/test/test_fss_post.cpp b/src/boost/libs/fiber/test/test_fss_post.cpp new file mode 100644 index 00000000..166c4ea1 --- /dev/null +++ b/src/boost/libs/fiber/test/test_fss_post.cpp @@ -0,0 +1,237 @@ +// Copyright (C) 2001-2003 +// William E. Kempf +// Copyright (C) 2007 Anthony Williams +// +// 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 <iostream> +#include <mutex> + +#include <boost/test/unit_test.hpp> + +#include <boost/fiber/all.hpp> + +boost::fibers::mutex check_mutex; +boost::fibers::mutex fss_mutex; +int fss_instances = 0; +int fss_total = 0; + +struct fss_value_t { + fss_value_t() { + std::unique_lock<boost::fibers::mutex> lock(fss_mutex); + ++fss_instances; + ++fss_total; + value = 0; + } + ~fss_value_t() { + std::unique_lock<boost::fibers::mutex> lock(fss_mutex); + --fss_instances; + } + int value; +}; + +boost::fibers::fiber_specific_ptr<fss_value_t> fss_value; + +void fss_fiber() { + fss_value.reset(new fss_value_t()); + for (int i=0; i<1000; ++i) { + int& n = fss_value->value; + if (n != i) { + std::unique_lock<boost::fibers::mutex> lock(check_mutex); + BOOST_CHECK_EQUAL(n, i); + } + ++n; + } +} + +void fss() { + fss_instances = 0; + fss_total = 0; + + boost::fibers::fiber f1( boost::fibers::launch::post, fss_fiber); + boost::fibers::fiber f2( boost::fibers::launch::post, fss_fiber); + boost::fibers::fiber f3( boost::fibers::launch::post, fss_fiber); + boost::fibers::fiber f4( boost::fibers::launch::post, fss_fiber); + boost::fibers::fiber f5( boost::fibers::launch::post, fss_fiber); + f1.join(); + f2.join(); + f3.join(); + f4.join(); + f5.join(); + + std::cout + << "fss_instances = " << fss_instances + << "; fss_total = " << fss_total + << "\n"; + std::cout.flush(); + + BOOST_CHECK_EQUAL(fss_instances, 0); + BOOST_CHECK_EQUAL(fss_total, 5); +} + +void test_fss() { + boost::fibers::fiber( boost::fibers::launch::post, fss).join(); +} + +bool fss_cleanup_called=false; + +struct Dummy { +}; + +void fss_custom_cleanup(Dummy* d) { + delete d; + fss_cleanup_called=true; +} + +boost::fibers::fiber_specific_ptr<Dummy> fss_with_cleanup(fss_custom_cleanup); + +void fss_fiber_with_custom_cleanup() { + fss_with_cleanup.reset(new Dummy); +} + +void fss_with_custom_cleanup() { + boost::fibers::fiber f( boost::fibers::launch::post, fss_fiber_with_custom_cleanup); + try { + f.join(); + } catch(...) { + f.join(); + throw; + } + + BOOST_CHECK(fss_cleanup_called); +} + +void test_fss_with_custom_cleanup() { + boost::fibers::fiber( boost::fibers::launch::post, fss_with_custom_cleanup).join(); +} + +Dummy* fss_object=new Dummy; + +void fss_fiber_with_custom_cleanup_and_release() { + fss_with_cleanup.reset(fss_object); + fss_with_cleanup.release(); +} + +void do_test_fss_does_no_cleanup_after_release() { + fss_cleanup_called=false; + boost::fibers::fiber f( boost::fibers::launch::post, fss_fiber_with_custom_cleanup_and_release); + try { + f.join(); + } catch(...) { + f.join(); + throw; + } + + BOOST_CHECK(!fss_cleanup_called); + if(!fss_cleanup_called) { + delete fss_object; + } +} + +struct dummy_class_tracks_deletions { + static unsigned deletions; + + ~dummy_class_tracks_deletions() { + ++deletions; + } +}; + +unsigned dummy_class_tracks_deletions::deletions=0; + +boost::fibers::fiber_specific_ptr<dummy_class_tracks_deletions> fss_with_null_cleanup(NULL); + +void fss_fiber_with_null_cleanup(dummy_class_tracks_deletions* delete_tracker) { + fss_with_null_cleanup.reset(delete_tracker); +} + +void do_test_fss_does_no_cleanup_with_null_cleanup_function() { + dummy_class_tracks_deletions* delete_tracker=new dummy_class_tracks_deletions; + boost::fibers::fiber f( boost::fibers::launch::post, [&delete_tracker](){ + fss_fiber_with_null_cleanup( delete_tracker); }); + try { + f.join(); + } catch(...) { + f.join(); + throw; + } + + BOOST_CHECK(!dummy_class_tracks_deletions::deletions); + if(!dummy_class_tracks_deletions::deletions) { + delete delete_tracker; + } +} + +void test_fss_does_no_cleanup_after_release() { + boost::fibers::fiber( boost::fibers::launch::post, do_test_fss_does_no_cleanup_after_release).join(); +} + +void test_fss_does_no_cleanup_with_null_cleanup_function() { + boost::fibers::fiber( boost::fibers::launch::post, do_test_fss_does_no_cleanup_with_null_cleanup_function).join(); +} + + +void fiber_with_local_fss_ptr() { + { + boost::fibers::fiber_specific_ptr<Dummy> local_fss(fss_custom_cleanup); + + local_fss.reset(new Dummy); + } + BOOST_CHECK(fss_cleanup_called); + fss_cleanup_called=false; +} + +void fss_does_not_call_cleanup_after_ptr_destroyed() { + boost::fibers::fiber( boost::fibers::launch::post, fiber_with_local_fss_ptr).join(); + BOOST_CHECK(!fss_cleanup_called); +} + +void test_fss_does_not_call_cleanup_after_ptr_destroyed() { + boost::fibers::fiber( boost::fibers::launch::post, fss_does_not_call_cleanup_after_ptr_destroyed).join(); +} + + +void fss_cleanup_not_called_for_null_pointer() { + boost::fibers::fiber_specific_ptr<Dummy> local_fss(fss_custom_cleanup); + local_fss.reset(new Dummy); + fss_cleanup_called=false; + local_fss.reset(0); + BOOST_CHECK(fss_cleanup_called); + fss_cleanup_called=false; + local_fss.reset(new Dummy); + BOOST_CHECK(!fss_cleanup_called); +} + +void test_fss_cleanup_not_called_for_null_pointer() { + boost::fibers::fiber( boost::fibers::launch::post, fss_cleanup_not_called_for_null_pointer).join(); +} + + +void fss_at_the_same_adress() { + for(int i=0; i<2; i++) { + boost::fibers::fiber_specific_ptr<Dummy> local_fss(fss_custom_cleanup); + local_fss.reset(new Dummy); + fss_cleanup_called=false; + BOOST_CHECK(fss_cleanup_called); + fss_cleanup_called=false; + BOOST_CHECK(!fss_cleanup_called); + } +} + +void test_fss_at_the_same_adress() { + boost::fibers::fiber( boost::fibers::launch::post, fss_at_the_same_adress).join(); +} + +boost::unit_test::test_suite* init_unit_test_suite(int, char*[]) { + boost::unit_test::test_suite* test = + BOOST_TEST_SUITE("Boost.Fiber: fss test suite"); + + test->add(BOOST_TEST_CASE(test_fss)); + test->add(BOOST_TEST_CASE(test_fss_with_custom_cleanup)); + test->add(BOOST_TEST_CASE(test_fss_does_no_cleanup_after_release)); + test->add(BOOST_TEST_CASE(test_fss_does_no_cleanup_with_null_cleanup_function)); + test->add(BOOST_TEST_CASE(test_fss_does_not_call_cleanup_after_ptr_destroyed)); + test->add(BOOST_TEST_CASE(test_fss_cleanup_not_called_for_null_pointer)); + + return test; +} diff --git a/src/boost/libs/fiber/test/test_future_dispatch.cpp b/src/boost/libs/fiber/test/test_future_dispatch.cpp new file mode 100644 index 00000000..2990ec41 --- /dev/null +++ b/src/boost/libs/fiber/test/test_future_dispatch.cpp @@ -0,0 +1,569 @@ +// (C) Copyright 2008-10 Anthony Williams +// 2015 Oliver Kowalke +// +// 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 <chrono> +#include <memory> +#include <stdexcept> +#include <string> +#include <utility> + +#include <boost/test/unit_test.hpp> + +#include <boost/fiber/all.hpp> + +typedef std::chrono::milliseconds ms; +typedef std::chrono::high_resolution_clock Clock; + +int gi = 7; + +struct my_exception : public std::runtime_error { + my_exception() : + std::runtime_error("my_exception") { + } +}; + +struct A { + A() = default; + + A( A const&) = delete; + A( A &&) = default; + + A & operator=( A const&) = delete; + A & operator=( A &&) = default; + + int value; +}; + +void fn1( boost::fibers::promise< int > * p, int i) { + boost::this_fiber::yield(); + p->set_value( i); +} + +void fn2() { + boost::fibers::promise< int > p; + boost::fibers::future< int > f( p.get_future() ); + boost::this_fiber::yield(); + boost::fibers::fiber( boost::fibers::launch::dispatch, fn1, & p, 7).detach(); + boost::this_fiber::yield(); + BOOST_CHECK( 7 == f.get() ); +} + +int fn3() { + return 3; +} + +void fn4() { +} + +int fn5() { + boost::throw_exception( my_exception() ); + return 3; +} + +void fn6() { + boost::throw_exception( my_exception() ); +} + +int & fn7() { + return gi; +} + +int fn8( int i) { + return i; +} + +A fn9() { + A a; + a.value = 3; + return a; +} + +A fn10() { + boost::throw_exception( my_exception() ); + return A(); +} + +void fn11( boost::fibers::promise< int > p) { + boost::this_fiber::sleep_for( ms(500) ); + p.set_value(3); +} + +void fn12( boost::fibers::promise< int& > p) { + boost::this_fiber::sleep_for( ms(500) ); + gi = 5; + p.set_value( gi); +} + +void fn13( boost::fibers::promise< void > p) { + boost::this_fiber::sleep_for( ms(400) ); + p.set_value(); +} + +// future +void test_future_create() { + // default constructed future is not valid + boost::fibers::future< int > f1; + BOOST_CHECK( ! f1.valid() ); + + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int > p2; + boost::fibers::future< int > f2 = p2.get_future(); + BOOST_CHECK( f2.valid() ); +} + +void test_future_create_ref() { + // default constructed future is not valid + boost::fibers::future< int& > f1; + BOOST_CHECK( ! f1.valid() ); + + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int& > p2; + boost::fibers::future< int& > f2 = p2.get_future(); + BOOST_CHECK( f2.valid() ); +} + +void test_future_create_void() { + // default constructed future is not valid + boost::fibers::future< void > f1; + BOOST_CHECK( ! f1.valid() ); + + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< void > p2; + boost::fibers::future< void > f2 = p2.get_future(); + BOOST_CHECK( f2.valid() ); +} + +void test_future_move() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int > p1; + boost::fibers::future< int > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + + // move construction + boost::fibers::future< int > f2( std::move( f1) ); + BOOST_CHECK( ! f1.valid() ); + BOOST_CHECK( f2.valid() ); + + // move assignment + f1 = std::move( f2); + BOOST_CHECK( f1.valid() ); + BOOST_CHECK( ! f2.valid() ); +} + +void test_future_move_ref() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int& > p1; + boost::fibers::future< int& > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + + // move construction + boost::fibers::future< int& > f2( std::move( f1) ); + BOOST_CHECK( ! f1.valid() ); + BOOST_CHECK( f2.valid() ); + + // move assignment + f1 = std::move( f2); + BOOST_CHECK( f1.valid() ); + BOOST_CHECK( ! f2.valid() ); +} + +void test_future_move_void() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< void > p1; + boost::fibers::future< void > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + + // move construction + boost::fibers::future< void > f2( std::move( f1) ); + BOOST_CHECK( ! f1.valid() ); + BOOST_CHECK( f2.valid() ); + + // move assignment + f1 = std::move( f2); + BOOST_CHECK( f1.valid() ); + BOOST_CHECK( ! f2.valid() ); +} + +void test_future_get() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int > p1; + p1.set_value( 7); + + boost::fibers::future< int > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + + // get + BOOST_CHECK( ! f1.get_exception_ptr() ); + BOOST_CHECK( 7 == f1.get() ); + BOOST_CHECK( ! f1.valid() ); + + // throw broken_promise if promise is destroyed without set + { + boost::fibers::promise< int > p2; + f1 = p2.get_future(); + } + bool thrown = false; + try { + f1.get(); + } catch ( boost::fibers::broken_promise const&) { + thrown = true; + } + BOOST_CHECK( ! f1.valid() ); + BOOST_CHECK( thrown); +} + +void test_future_get_move() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< A > p1; + A a; a.value = 7; + p1.set_value( std::move( a) ); + + boost::fibers::future< A > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + + // get + BOOST_CHECK( ! f1.get_exception_ptr() ); + BOOST_CHECK( 7 == f1.get().value); + BOOST_CHECK( ! f1.valid() ); + + // throw broken_promise if promise is destroyed without set + { + boost::fibers::promise< A > p2; + f1 = p2.get_future(); + } + bool thrown = false; + try { + f1.get(); + } catch ( boost::fibers::broken_promise const&) { + thrown = true; + } + BOOST_CHECK( ! f1.valid() ); + BOOST_CHECK( thrown); +} + +void test_future_get_ref() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int& > p1; + int i = 7; + p1.set_value( i); + + boost::fibers::future< int& > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + + // get + BOOST_CHECK( ! f1.get_exception_ptr() ); + int & j = f1.get(); + BOOST_CHECK( &i == &j); + BOOST_CHECK( ! f1.valid() ); + + // throw broken_promise if promise is destroyed without set + { + boost::fibers::promise< int& > p2; + f1 = p2.get_future(); + } + bool thrown = false; + try { + f1.get(); + } catch ( boost::fibers::broken_promise const&) { + thrown = true; + } + BOOST_CHECK( ! f1.valid() ); + BOOST_CHECK( thrown); +} + + +void test_future_get_void() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< void > p1; + p1.set_value(); + + boost::fibers::future< void > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + + // get + BOOST_CHECK( ! f1.get_exception_ptr() ); + f1.get(); + BOOST_CHECK( ! f1.valid() ); + + // throw broken_promise if promise is destroyed without set + { + boost::fibers::promise< void > p2; + f1 = p2.get_future(); + } + bool thrown = false; + try { + f1.get(); + } catch ( boost::fibers::broken_promise const&) { + thrown = true; + } + BOOST_CHECK( ! f1.valid() ); + BOOST_CHECK( thrown); +} + +void test_future_share() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int > p1; + int i = 7; + p1.set_value( i); + + boost::fibers::future< int > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + + // share + boost::fibers::shared_future< int > sf1 = f1.share(); + BOOST_CHECK( sf1.valid() ); + BOOST_CHECK( ! f1.valid() ); + + // get + BOOST_CHECK( ! sf1.get_exception_ptr() ); + int j = sf1.get(); + BOOST_CHECK_EQUAL( i, j); + BOOST_CHECK( sf1.valid() ); +} + +void test_future_share_ref() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int& > p1; + int i = 7; + p1.set_value( i); + + boost::fibers::future< int& > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + + // share + boost::fibers::shared_future< int& > sf1 = f1.share(); + BOOST_CHECK( sf1.valid() ); + BOOST_CHECK( ! f1.valid() ); + + // get + BOOST_CHECK( ! sf1.get_exception_ptr() ); + int & j = sf1.get(); + BOOST_CHECK( &i == &j); + BOOST_CHECK( sf1.valid() ); +} + +void test_future_share_void() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< void > p1; + p1.set_value(); + + boost::fibers::future< void > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + + // share + boost::fibers::shared_future< void > sf1 = f1.share(); + BOOST_CHECK( sf1.valid() ); + BOOST_CHECK( ! f1.valid() ); + + // get + BOOST_CHECK( ! sf1.get_exception_ptr() ); + sf1.get(); + BOOST_CHECK( sf1.valid() ); +} + +void test_future_wait() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int > p1; + boost::fibers::future< int > f1 = p1.get_future(); + + // wait on future + p1.set_value( 7); + f1.wait(); + BOOST_CHECK( 7 == f1.get() ); +} + +void test_future_wait_ref() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int& > p1; + boost::fibers::future< int& > f1 = p1.get_future(); + + // wait on future + int i = 7; + p1.set_value( i); + f1.wait(); + int & j = f1.get(); + BOOST_CHECK( &i == &j); +} + +void test_future_wait_void() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< void > p1; + boost::fibers::future< void > f1 = p1.get_future(); + + // wait on future + p1.set_value(); + f1.wait(); + f1.get(); + BOOST_CHECK( ! f1.valid() ); +} + +void test_future_wait_for() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int > p1; + boost::fibers::future< int > f1 = p1.get_future(); + + boost::fibers::fiber( boost::fibers::launch::dispatch, fn11, std::move( p1) ).detach(); + + // wait on future + BOOST_CHECK( f1.valid() ); + boost::fibers::future_status status = f1.wait_for( ms(300) ); + BOOST_CHECK( boost::fibers::future_status::timeout == status); + + BOOST_CHECK( f1.valid() ); + status = f1.wait_for( ms(400) ); + BOOST_CHECK( boost::fibers::future_status::ready == status); + + BOOST_CHECK( f1.valid() ); + f1.wait(); +} + +void test_future_wait_for_ref() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int& > p1; + boost::fibers::future< int& > f1 = p1.get_future(); + + boost::fibers::fiber( boost::fibers::launch::dispatch, fn12, std::move( p1) ).detach(); + + // wait on future + BOOST_CHECK( f1.valid() ); + boost::fibers::future_status status = f1.wait_for( ms(300) ); + BOOST_CHECK( boost::fibers::future_status::timeout == status); + + BOOST_CHECK( f1.valid() ); + status = f1.wait_for( ms(400) ); + BOOST_CHECK( boost::fibers::future_status::ready == status); + + BOOST_CHECK( f1.valid() ); + f1.wait(); +} + +void test_future_wait_for_void() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< void > p1; + boost::fibers::future< void > f1 = p1.get_future(); + + boost::fibers::fiber( boost::fibers::launch::dispatch, fn13, std::move( p1) ).detach(); + + // wait on future + BOOST_CHECK( f1.valid() ); + boost::fibers::future_status status = f1.wait_for( ms(300) ); + BOOST_CHECK( boost::fibers::future_status::timeout == status); + + BOOST_CHECK( f1.valid() ); + status = f1.wait_for( ms(400) ); + BOOST_CHECK( boost::fibers::future_status::ready == status); + + BOOST_CHECK( f1.valid() ); + f1.wait(); +} + +void test_future_wait_until() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int > p1; + boost::fibers::future< int > f1 = p1.get_future(); + + boost::fibers::fiber( boost::fibers::launch::dispatch, fn11, std::move( p1) ).detach(); + + // wait on future + BOOST_CHECK( f1.valid() ); + boost::fibers::future_status status = f1.wait_until( Clock::now() + ms(300) ); + BOOST_CHECK( boost::fibers::future_status::timeout == status); + + BOOST_CHECK( f1.valid() ); + status = f1.wait_until( Clock::now() + ms(400) ); + BOOST_CHECK( boost::fibers::future_status::ready == status); + + BOOST_CHECK( f1.valid() ); + f1.wait(); +} + +void test_future_wait_until_ref() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int& > p1; + boost::fibers::future< int& > f1 = p1.get_future(); + + boost::fibers::fiber( boost::fibers::launch::dispatch, fn12, std::move( p1) ).detach(); + + // wait on future + BOOST_CHECK( f1.valid() ); + boost::fibers::future_status status = f1.wait_until( Clock::now() + ms(300) ); + BOOST_CHECK( boost::fibers::future_status::timeout == status); + + BOOST_CHECK( f1.valid() ); + status = f1.wait_until( Clock::now() + ms(400) ); + BOOST_CHECK( boost::fibers::future_status::ready == status); + + BOOST_CHECK( f1.valid() ); + f1.wait(); +} + +void test_future_wait_until_void() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< void > p1; + boost::fibers::future< void > f1 = p1.get_future(); + + boost::fibers::fiber( boost::fibers::launch::dispatch, fn13, std::move( p1) ).detach(); + + // wait on future + BOOST_CHECK( f1.valid() ); + boost::fibers::future_status status = f1.wait_until( Clock::now() + ms(300) ); + BOOST_CHECK( boost::fibers::future_status::timeout == status); + + BOOST_CHECK( f1.valid() ); + status = f1.wait_until( Clock::now() + ms(400) ); + BOOST_CHECK( boost::fibers::future_status::ready == status); + + BOOST_CHECK( f1.valid() ); + f1.wait(); +} + +void test_future_wait_with_fiber_1() { + boost::fibers::promise< int > p1; + boost::fibers::fiber( boost::fibers::launch::dispatch, fn1, & p1, 7).detach(); + + boost::fibers::future< int > f1 = p1.get_future(); + + // wait on future + BOOST_CHECK( 7 == f1.get() ); +} + +void test_future_wait_with_fiber_2() { + boost::fibers::fiber( boost::fibers::launch::dispatch, fn2).join(); +} + + +boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) { + boost::unit_test_framework::test_suite* test = + BOOST_TEST_SUITE("Boost.Fiber: future test suite"); + + test->add(BOOST_TEST_CASE(test_future_create)); + test->add(BOOST_TEST_CASE(test_future_create_ref)); + test->add(BOOST_TEST_CASE(test_future_create_void)); + test->add(BOOST_TEST_CASE(test_future_move)); + test->add(BOOST_TEST_CASE(test_future_move_ref)); + test->add(BOOST_TEST_CASE(test_future_move_void)); + test->add(BOOST_TEST_CASE(test_future_get)); + test->add(BOOST_TEST_CASE(test_future_get_move)); + test->add(BOOST_TEST_CASE(test_future_get_ref)); + test->add(BOOST_TEST_CASE(test_future_get_void)); + test->add(BOOST_TEST_CASE(test_future_share)); + test->add(BOOST_TEST_CASE(test_future_share_ref)); + test->add(BOOST_TEST_CASE(test_future_share_void)); + test->add(BOOST_TEST_CASE(test_future_wait)); + test->add(BOOST_TEST_CASE(test_future_wait_ref)); + test->add(BOOST_TEST_CASE(test_future_wait_void)); + test->add(BOOST_TEST_CASE(test_future_wait_for)); + test->add(BOOST_TEST_CASE(test_future_wait_for_ref)); + test->add(BOOST_TEST_CASE(test_future_wait_for_void)); + test->add(BOOST_TEST_CASE(test_future_wait_until)); + test->add(BOOST_TEST_CASE(test_future_wait_until_ref)); + test->add(BOOST_TEST_CASE(test_future_wait_until_void)); + test->add(BOOST_TEST_CASE(test_future_wait_with_fiber_1)); + test->add(BOOST_TEST_CASE(test_future_wait_with_fiber_2)); + + return test; +} diff --git a/src/boost/libs/fiber/test/test_future_mt_dispatch.cpp b/src/boost/libs/fiber/test/test_future_mt_dispatch.cpp new file mode 100644 index 00000000..16585bb0 --- /dev/null +++ b/src/boost/libs/fiber/test/test_future_mt_dispatch.cpp @@ -0,0 +1,50 @@ +// (C) Copyright 2008-10 Anthony Williams +// +// 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 <utility> +#include <memory> +#include <stdexcept> +#include <string> +#include <thread> + +#include <boost/fiber/all.hpp> +#include <boost/test/unit_test.hpp> + +int fn( int i) { + return i; +} + +void test_async() { + for ( int i = 0; i < 10; ++i) { + int n = 3; + boost::fibers::packaged_task< int( int) > pt( fn); + boost::fibers::future< int > f( pt.get_future() ); + std::thread t( + std::bind( + [n](boost::fibers::packaged_task< int( int) > & pt) mutable -> void { + boost::fibers::fiber( boost::fibers::launch::dispatch, std::move( pt), n).join(); + }, + std::move( pt) ) ); + int result = f.get(); + BOOST_CHECK_EQUAL( n, result); + t.join(); + } +} + +void test_dummy() {} + +boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) { + boost::unit_test_framework::test_suite* test = + BOOST_TEST_SUITE("Boost.Fiber: futures-mt test suite"); + +#if ! defined(BOOST_FIBERS_NO_ATOMICS) + test->add(BOOST_TEST_CASE(test_async)); +#else + test->add(BOOST_TEST_CASE(test_dummy)); +#endif + + return test; +} diff --git a/src/boost/libs/fiber/test/test_future_mt_post.cpp b/src/boost/libs/fiber/test/test_future_mt_post.cpp new file mode 100644 index 00000000..ff708a3b --- /dev/null +++ b/src/boost/libs/fiber/test/test_future_mt_post.cpp @@ -0,0 +1,50 @@ +// (C) Copyright 2008-10 Anthony Williams +// +// 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 <utility> +#include <memory> +#include <stdexcept> +#include <string> +#include <thread> + +#include <boost/fiber/all.hpp> +#include <boost/test/unit_test.hpp> + +int fn( int i) { + return i; +} + +void test_async() { + for ( int i = 0; i < 10; ++i) { + int n = 3; + boost::fibers::packaged_task< int( int) > pt( fn); + boost::fibers::future< int > f( pt.get_future() ); + std::thread t( + std::bind( + [n](boost::fibers::packaged_task< int( int) > & pt) mutable -> void { + boost::fibers::fiber( boost::fibers::launch::post, std::move( pt), n).join(); + }, + std::move( pt) ) ); + int result = f.get(); + BOOST_CHECK_EQUAL( n, result); + t.join(); + } +} + +void test_dummy() {} + +boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) { + boost::unit_test_framework::test_suite* test = + BOOST_TEST_SUITE("Boost.Fiber: futures-mt test suite"); + +#if ! defined(BOOST_FIBERS_NO_ATOMICS) + test->add(BOOST_TEST_CASE(test_async)); +#else + test->add(BOOST_TEST_CASE(test_dummy)); +#endif + + return test; +} diff --git a/src/boost/libs/fiber/test/test_future_post.cpp b/src/boost/libs/fiber/test/test_future_post.cpp new file mode 100644 index 00000000..01deb26f --- /dev/null +++ b/src/boost/libs/fiber/test/test_future_post.cpp @@ -0,0 +1,569 @@ +// (C) Copyright 2008-10 Anthony Williams +// 2015 Oliver Kowalke +// +// 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 <chrono> +#include <memory> +#include <stdexcept> +#include <string> +#include <utility> + +#include <boost/test/unit_test.hpp> + +#include <boost/fiber/all.hpp> + +typedef std::chrono::milliseconds ms; +typedef std::chrono::high_resolution_clock Clock; + +int gi = 7; + +struct my_exception : public std::runtime_error { + my_exception() : + std::runtime_error("my_exception") { + } +}; + +struct A { + A() = default; + + A( A const&) = delete; + A( A &&) = default; + + A & operator=( A const&) = delete; + A & operator=( A &&) = default; + + int value; +}; + +void fn1( boost::fibers::promise< int > * p, int i) { + boost::this_fiber::yield(); + p->set_value( i); +} + +void fn2() { + boost::fibers::promise< int > p; + boost::fibers::future< int > f( p.get_future() ); + boost::this_fiber::yield(); + boost::fibers::fiber( boost::fibers::launch::post, fn1, & p, 7).detach(); + boost::this_fiber::yield(); + BOOST_CHECK( 7 == f.get() ); +} + +int fn3() { + return 3; +} + +void fn4() { +} + +int fn5() { + boost::throw_exception( my_exception() ); + return 3; +} + +void fn6() { + boost::throw_exception( my_exception() ); +} + +int & fn7() { + return gi; +} + +int fn8( int i) { + return i; +} + +A fn9() { + A a; + a.value = 3; + return a; +} + +A fn10() { + boost::throw_exception( my_exception() ); + return A(); +} + +void fn11( boost::fibers::promise< int > p) { + boost::this_fiber::sleep_for( ms(500) ); + p.set_value(3); +} + +void fn12( boost::fibers::promise< int& > p) { + boost::this_fiber::sleep_for( ms(500) ); + gi = 5; + p.set_value( gi); +} + +void fn13( boost::fibers::promise< void > p) { + boost::this_fiber::sleep_for( ms(400) ); + p.set_value(); +} + +// future +void test_future_create() { + // default constructed future is not valid + boost::fibers::future< int > f1; + BOOST_CHECK( ! f1.valid() ); + + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int > p2; + boost::fibers::future< int > f2 = p2.get_future(); + BOOST_CHECK( f2.valid() ); +} + +void test_future_create_ref() { + // default constructed future is not valid + boost::fibers::future< int& > f1; + BOOST_CHECK( ! f1.valid() ); + + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int& > p2; + boost::fibers::future< int& > f2 = p2.get_future(); + BOOST_CHECK( f2.valid() ); +} + +void test_future_create_void() { + // default constructed future is not valid + boost::fibers::future< void > f1; + BOOST_CHECK( ! f1.valid() ); + + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< void > p2; + boost::fibers::future< void > f2 = p2.get_future(); + BOOST_CHECK( f2.valid() ); +} + +void test_future_move() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int > p1; + boost::fibers::future< int > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + + // move construction + boost::fibers::future< int > f2( std::move( f1) ); + BOOST_CHECK( ! f1.valid() ); + BOOST_CHECK( f2.valid() ); + + // move assignment + f1 = std::move( f2); + BOOST_CHECK( f1.valid() ); + BOOST_CHECK( ! f2.valid() ); +} + +void test_future_move_ref() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int& > p1; + boost::fibers::future< int& > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + + // move construction + boost::fibers::future< int& > f2( std::move( f1) ); + BOOST_CHECK( ! f1.valid() ); + BOOST_CHECK( f2.valid() ); + + // move assignment + f1 = std::move( f2); + BOOST_CHECK( f1.valid() ); + BOOST_CHECK( ! f2.valid() ); +} + +void test_future_move_void() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< void > p1; + boost::fibers::future< void > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + + // move construction + boost::fibers::future< void > f2( std::move( f1) ); + BOOST_CHECK( ! f1.valid() ); + BOOST_CHECK( f2.valid() ); + + // move assignment + f1 = std::move( f2); + BOOST_CHECK( f1.valid() ); + BOOST_CHECK( ! f2.valid() ); +} + +void test_future_get() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int > p1; + p1.set_value( 7); + + boost::fibers::future< int > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + + // get + BOOST_CHECK( ! f1.get_exception_ptr() ); + BOOST_CHECK( 7 == f1.get() ); + BOOST_CHECK( ! f1.valid() ); + + // throw broken_promise if promise is destroyed without set + { + boost::fibers::promise< int > p2; + f1 = p2.get_future(); + } + bool thrown = false; + try { + f1.get(); + } catch ( boost::fibers::broken_promise const&) { + thrown = true; + } + BOOST_CHECK( ! f1.valid() ); + BOOST_CHECK( thrown); +} + +void test_future_get_move() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< A > p1; + A a; a.value = 7; + p1.set_value( std::move( a) ); + + boost::fibers::future< A > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + + // get + BOOST_CHECK( ! f1.get_exception_ptr() ); + BOOST_CHECK( 7 == f1.get().value); + BOOST_CHECK( ! f1.valid() ); + + // throw broken_promise if promise is destroyed without set + { + boost::fibers::promise< A > p2; + f1 = p2.get_future(); + } + bool thrown = false; + try { + f1.get(); + } catch ( boost::fibers::broken_promise const&) { + thrown = true; + } + BOOST_CHECK( ! f1.valid() ); + BOOST_CHECK( thrown); +} + +void test_future_get_ref() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int& > p1; + int i = 7; + p1.set_value( i); + + boost::fibers::future< int& > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + + // get + BOOST_CHECK( ! f1.get_exception_ptr() ); + int & j = f1.get(); + BOOST_CHECK( &i == &j); + BOOST_CHECK( ! f1.valid() ); + + // throw broken_promise if promise is destroyed without set + { + boost::fibers::promise< int& > p2; + f1 = p2.get_future(); + } + bool thrown = false; + try { + f1.get(); + } catch ( boost::fibers::broken_promise const&) { + thrown = true; + } + BOOST_CHECK( ! f1.valid() ); + BOOST_CHECK( thrown); +} + + +void test_future_get_void() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< void > p1; + p1.set_value(); + + boost::fibers::future< void > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + + // get + BOOST_CHECK( ! f1.get_exception_ptr() ); + f1.get(); + BOOST_CHECK( ! f1.valid() ); + + // throw broken_promise if promise is destroyed without set + { + boost::fibers::promise< void > p2; + f1 = p2.get_future(); + } + bool thrown = false; + try { + f1.get(); + } catch ( boost::fibers::broken_promise const&) { + thrown = true; + } + BOOST_CHECK( ! f1.valid() ); + BOOST_CHECK( thrown); +} + +void test_future_share() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int > p1; + int i = 7; + p1.set_value( i); + + boost::fibers::future< int > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + + // share + boost::fibers::shared_future< int > sf1 = f1.share(); + BOOST_CHECK( sf1.valid() ); + BOOST_CHECK( ! f1.valid() ); + + // get + BOOST_CHECK( ! sf1.get_exception_ptr() ); + int j = sf1.get(); + BOOST_CHECK_EQUAL( i, j); + BOOST_CHECK( sf1.valid() ); +} + +void test_future_share_ref() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int& > p1; + int i = 7; + p1.set_value( i); + + boost::fibers::future< int& > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + + // share + boost::fibers::shared_future< int& > sf1 = f1.share(); + BOOST_CHECK( sf1.valid() ); + BOOST_CHECK( ! f1.valid() ); + + // get + BOOST_CHECK( ! sf1.get_exception_ptr() ); + int & j = sf1.get(); + BOOST_CHECK( &i == &j); + BOOST_CHECK( sf1.valid() ); +} + +void test_future_share_void() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< void > p1; + p1.set_value(); + + boost::fibers::future< void > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + + // share + boost::fibers::shared_future< void > sf1 = f1.share(); + BOOST_CHECK( sf1.valid() ); + BOOST_CHECK( ! f1.valid() ); + + // get + BOOST_CHECK( ! sf1.get_exception_ptr() ); + sf1.get(); + BOOST_CHECK( sf1.valid() ); +} + +void test_future_wait() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int > p1; + boost::fibers::future< int > f1 = p1.get_future(); + + // wait on future + p1.set_value( 7); + f1.wait(); + BOOST_CHECK( 7 == f1.get() ); +} + +void test_future_wait_ref() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int& > p1; + boost::fibers::future< int& > f1 = p1.get_future(); + + // wait on future + int i = 7; + p1.set_value( i); + f1.wait(); + int & j = f1.get(); + BOOST_CHECK( &i == &j); +} + +void test_future_wait_void() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< void > p1; + boost::fibers::future< void > f1 = p1.get_future(); + + // wait on future + p1.set_value(); + f1.wait(); + f1.get(); + BOOST_CHECK( ! f1.valid() ); +} + +void test_future_wait_for() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int > p1; + boost::fibers::future< int > f1 = p1.get_future(); + + boost::fibers::fiber( boost::fibers::launch::post, fn11, std::move( p1) ).detach(); + + // wait on future + BOOST_CHECK( f1.valid() ); + boost::fibers::future_status status = f1.wait_for( ms(300) ); + BOOST_CHECK( boost::fibers::future_status::timeout == status); + + BOOST_CHECK( f1.valid() ); + status = f1.wait_for( ms(400) ); + BOOST_CHECK( boost::fibers::future_status::ready == status); + + BOOST_CHECK( f1.valid() ); + f1.wait(); +} + +void test_future_wait_for_ref() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int& > p1; + boost::fibers::future< int& > f1 = p1.get_future(); + + boost::fibers::fiber( boost::fibers::launch::post, fn12, std::move( p1) ).detach(); + + // wait on future + BOOST_CHECK( f1.valid() ); + boost::fibers::future_status status = f1.wait_for( ms(300) ); + BOOST_CHECK( boost::fibers::future_status::timeout == status); + + BOOST_CHECK( f1.valid() ); + status = f1.wait_for( ms(400) ); + BOOST_CHECK( boost::fibers::future_status::ready == status); + + BOOST_CHECK( f1.valid() ); + f1.wait(); +} + +void test_future_wait_for_void() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< void > p1; + boost::fibers::future< void > f1 = p1.get_future(); + + boost::fibers::fiber( boost::fibers::launch::post, fn13, std::move( p1) ).detach(); + + // wait on future + BOOST_CHECK( f1.valid() ); + boost::fibers::future_status status = f1.wait_for( ms(300) ); + BOOST_CHECK( boost::fibers::future_status::timeout == status); + + BOOST_CHECK( f1.valid() ); + status = f1.wait_for( ms(400) ); + BOOST_CHECK( boost::fibers::future_status::ready == status); + + BOOST_CHECK( f1.valid() ); + f1.wait(); +} + +void test_future_wait_until() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int > p1; + boost::fibers::future< int > f1 = p1.get_future(); + + boost::fibers::fiber( boost::fibers::launch::post, fn11, std::move( p1) ).detach(); + + // wait on future + BOOST_CHECK( f1.valid() ); + boost::fibers::future_status status = f1.wait_until( Clock::now() + ms(300) ); + BOOST_CHECK( boost::fibers::future_status::timeout == status); + + BOOST_CHECK( f1.valid() ); + status = f1.wait_until( Clock::now() + ms(400) ); + BOOST_CHECK( boost::fibers::future_status::ready == status); + + BOOST_CHECK( f1.valid() ); + f1.wait(); +} + +void test_future_wait_until_ref() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int& > p1; + boost::fibers::future< int& > f1 = p1.get_future(); + + boost::fibers::fiber( boost::fibers::launch::post, fn12, std::move( p1) ).detach(); + + // wait on future + BOOST_CHECK( f1.valid() ); + boost::fibers::future_status status = f1.wait_until( Clock::now() + ms(300) ); + BOOST_CHECK( boost::fibers::future_status::timeout == status); + + BOOST_CHECK( f1.valid() ); + status = f1.wait_until( Clock::now() + ms(400) ); + BOOST_CHECK( boost::fibers::future_status::ready == status); + + BOOST_CHECK( f1.valid() ); + f1.wait(); +} + +void test_future_wait_until_void() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< void > p1; + boost::fibers::future< void > f1 = p1.get_future(); + + boost::fibers::fiber( boost::fibers::launch::post, fn13, std::move( p1) ).detach(); + + // wait on future + BOOST_CHECK( f1.valid() ); + boost::fibers::future_status status = f1.wait_until( Clock::now() + ms(300) ); + BOOST_CHECK( boost::fibers::future_status::timeout == status); + + BOOST_CHECK( f1.valid() ); + status = f1.wait_until( Clock::now() + ms(400) ); + BOOST_CHECK( boost::fibers::future_status::ready == status); + + BOOST_CHECK( f1.valid() ); + f1.wait(); +} + +void test_future_wait_with_fiber_1() { + boost::fibers::promise< int > p1; + boost::fibers::fiber( boost::fibers::launch::post, fn1, & p1, 7).detach(); + + boost::fibers::future< int > f1 = p1.get_future(); + + // wait on future + BOOST_CHECK( 7 == f1.get() ); +} + +void test_future_wait_with_fiber_2() { + boost::fibers::fiber( boost::fibers::launch::post, fn2).join(); +} + + +boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) { + boost::unit_test_framework::test_suite* test = + BOOST_TEST_SUITE("Boost.Fiber: future test suite"); + + test->add(BOOST_TEST_CASE(test_future_create)); + test->add(BOOST_TEST_CASE(test_future_create_ref)); + test->add(BOOST_TEST_CASE(test_future_create_void)); + test->add(BOOST_TEST_CASE(test_future_move)); + test->add(BOOST_TEST_CASE(test_future_move_ref)); + test->add(BOOST_TEST_CASE(test_future_move_void)); + test->add(BOOST_TEST_CASE(test_future_get)); + test->add(BOOST_TEST_CASE(test_future_get_move)); + test->add(BOOST_TEST_CASE(test_future_get_ref)); + test->add(BOOST_TEST_CASE(test_future_get_void)); + test->add(BOOST_TEST_CASE(test_future_share)); + test->add(BOOST_TEST_CASE(test_future_share_ref)); + test->add(BOOST_TEST_CASE(test_future_share_void)); + test->add(BOOST_TEST_CASE(test_future_wait)); + test->add(BOOST_TEST_CASE(test_future_wait_ref)); + test->add(BOOST_TEST_CASE(test_future_wait_void)); + test->add(BOOST_TEST_CASE(test_future_wait_for)); + test->add(BOOST_TEST_CASE(test_future_wait_for_ref)); + test->add(BOOST_TEST_CASE(test_future_wait_for_void)); + test->add(BOOST_TEST_CASE(test_future_wait_until)); + test->add(BOOST_TEST_CASE(test_future_wait_until_ref)); + test->add(BOOST_TEST_CASE(test_future_wait_until_void)); + test->add(BOOST_TEST_CASE(test_future_wait_with_fiber_1)); + test->add(BOOST_TEST_CASE(test_future_wait_with_fiber_2)); + + return test; +} diff --git a/src/boost/libs/fiber/test/test_mutex_dispatch.cpp b/src/boost/libs/fiber/test/test_mutex_dispatch.cpp new file mode 100644 index 00000000..dec8ed4d --- /dev/null +++ b/src/boost/libs/fiber/test/test_mutex_dispatch.cpp @@ -0,0 +1,447 @@ + +// Copyright Oliver Kowalke 2013. +// 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) +// +// This test is based on the tests of Boost.Thread + +#include <chrono> +#include <cstdlib> +#include <iostream> +#include <map> +#include <mutex> +#include <stdexcept> +#include <vector> + +#include <boost/test/unit_test.hpp> + +#include <boost/fiber/all.hpp> + +typedef std::chrono::nanoseconds ns; +typedef std::chrono::milliseconds ms; + +int value1 = 0; +int value2 = 0; + +template< typename M > +void fn1( M & mtx) { + typedef M mutex_type; + typename std::unique_lock< mutex_type > lk( mtx); + ++value1; + for ( int i = 0; i < 3; ++i) + boost::this_fiber::yield(); +} + +template< typename M > +void fn2( M & mtx) { + typedef M mutex_type; + ++value2; + typename std::unique_lock< mutex_type > lk( mtx); + ++value2; +} + +void fn3( boost::fibers::timed_mutex & m) { + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + m.lock(); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + m.unlock(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ns(2500000)+ms(2000)); // within 2.5 ms +} + +void fn4( boost::fibers::timed_mutex & m) { + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + while ( ! m.try_lock() ); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + m.unlock(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ns(50000000)+ms(2000)); // within 50 ms +} + +void fn5( boost::fibers::timed_mutex & m) { + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + BOOST_CHECK( m.try_lock_for(ms(300)+ms(2000)) == true); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + m.unlock(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ns(5000000)+ms(2000)); // within 5 ms +} + +void fn6( boost::fibers::timed_mutex & m) { + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + BOOST_CHECK(m.try_lock_for(ms(250)) == false); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ns(5000000)+ms(2000)); // within 5 ms +} + +void fn7( boost::fibers::timed_mutex & m) { + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + BOOST_CHECK(m.try_lock_until(std::chrono::steady_clock::now() + ms(300) + ms(1000)) == true); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + m.unlock(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ns(5000000)+ms(2000)); // within 5ms +} + +void fn8( boost::fibers::timed_mutex & m) { + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + BOOST_CHECK(m.try_lock_until(std::chrono::steady_clock::now() + ms(250)) == false); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + ns d = t1 - t0 - ms(250); + ns r = ns(5000000)+ms(2000); // within 6ms + BOOST_CHECK(d < r); // within 6ms +} + +void fn9( boost::fibers::recursive_timed_mutex & m) { + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + m.lock(); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + m.lock(); + m.unlock(); + m.unlock(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ms(2500)+ms(2000)); // within 2.5 ms +} + +void fn10( boost::fibers::recursive_timed_mutex & m) { + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + while (!m.try_lock()) ; + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + BOOST_CHECK(m.try_lock()); + m.unlock(); + m.unlock(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ms(50000)+ms(2000)); // within 50 ms +} + +void fn11( boost::fibers::recursive_timed_mutex & m) { + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + BOOST_CHECK(m.try_lock_for(ms(300)+ms(1000)) == true); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + BOOST_CHECK(m.try_lock()); + m.unlock(); + m.unlock(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ns(5000000)+ms(2000)); // within 5 ms +} + +void fn12( boost::fibers::recursive_timed_mutex & m) { + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + BOOST_CHECK(m.try_lock_for(ms(250)) == false); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ms(5000)+ms(2000)); // within 5 ms +} + +void fn13( boost::fibers::recursive_timed_mutex & m) { + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + BOOST_CHECK(m.try_lock_until(std::chrono::steady_clock::now() + ms(300) + ms(1000)) == true); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + m.unlock(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ns(5000000)+ms(2000)); // within 5 ms +} + +void fn14( boost::fibers::recursive_timed_mutex & m) { + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + BOOST_CHECK(m.try_lock_until(std::chrono::steady_clock::now() + ms(250)) == false); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ns(5000000)+ms(2000)); // within 5 ms +} + +void fn15( boost::fibers::recursive_mutex & m) { + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + m.lock(); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + m.lock(); + m.unlock(); + m.unlock(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ns(2500000)+ms(2000)); // within 2.5 ms +} + +void fn16( boost::fibers::recursive_mutex & m) { + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + while (!m.try_lock()); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + BOOST_CHECK(m.try_lock()); + m.unlock(); + m.unlock(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ns(50000000)+ms(2000)); // within 50 ms +} + +void fn17( boost::fibers::mutex & m) { + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + m.lock(); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + m.unlock(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ms(2500)+ms(2000)); // within 2.5 ms +} + +void fn18( boost::fibers::mutex & m) { + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + while (!m.try_lock()) ; + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + m.unlock(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ns(50000000)+ms(2000)); // within 50 ms +} + +template< typename M > +struct test_lock { + typedef M mutex_type; + typedef typename std::unique_lock< M > lock_type; + + void operator()() { + mutex_type mtx; + + // Test the lock's constructors. + { + lock_type lk(mtx, std::defer_lock); + BOOST_CHECK(!lk); + } + lock_type lk(mtx); + BOOST_CHECK(lk ? true : false); + + // Test the lock and unlock methods. + lk.unlock(); + BOOST_CHECK(!lk); + lk.lock(); + BOOST_CHECK(lk ? true : false); + } +}; + +template< typename M > +struct test_exclusive { + typedef M mutex_type; + typedef typename std::unique_lock< M > lock_type; + + void operator()() { + value1 = 0; + value2 = 0; + BOOST_CHECK_EQUAL( 0, value1); + BOOST_CHECK_EQUAL( 0, value2); + + mutex_type mtx; + boost::fibers::fiber f1( boost::fibers::launch::dispatch, & fn1< mutex_type >, std::ref( mtx) ); + boost::fibers::fiber f2( boost::fibers::launch::dispatch, & fn2< mutex_type >, std::ref( mtx) ); + BOOST_ASSERT( f1.joinable() ); + BOOST_ASSERT( f2.joinable() ); + + f1.join(); + f2.join(); + BOOST_CHECK_EQUAL( 1, value1); + BOOST_CHECK_EQUAL( 2, value2); + } +}; + +template< typename M > +struct test_recursive_lock { + typedef M mutex_type; + typedef typename std::unique_lock< M > lock_type; + + void operator()() { + mutex_type mx; + lock_type lock1(mx); + lock_type lock2(mx); + } +}; + +void do_test_mutex() { + test_lock< boost::fibers::mutex >()(); + test_exclusive< boost::fibers::mutex >()(); + + { + boost::fibers::mutex mtx; + mtx.lock(); + boost::fibers::fiber f( boost::fibers::launch::dispatch, & fn17, std::ref( mtx) ); + boost::this_fiber::sleep_for( ms(250) ); + mtx.unlock(); + f.join(); + } + + { + boost::fibers::mutex mtx; + mtx.lock(); + boost::fibers::fiber f( boost::fibers::launch::dispatch, & fn18, std::ref( mtx) ); + boost::this_fiber::sleep_for( ms(250) ); + mtx.unlock(); + f.join(); + } +} + +void test_mutex() { + boost::fibers::fiber( boost::fibers::launch::dispatch, & do_test_mutex).join(); +} + +void do_test_recursive_mutex() { + test_lock< boost::fibers::recursive_mutex >()(); + test_exclusive< boost::fibers::recursive_mutex >()(); + test_recursive_lock< boost::fibers::recursive_mutex >()(); + + { + boost::fibers::recursive_mutex mtx; + mtx.lock(); + boost::fibers::fiber f( boost::fibers::launch::dispatch, & fn15, std::ref( mtx) ); + boost::this_fiber::sleep_for( ms(250) ); + mtx.unlock(); + f.join(); + } + + { + boost::fibers::recursive_mutex mtx; + mtx.lock(); + boost::fibers::fiber f( boost::fibers::launch::dispatch, & fn16, std::ref( mtx) ); + boost::this_fiber::sleep_for( ms(250) ); + mtx.unlock(); + f.join(); + } +} + +void test_recursive_mutex() { + boost::fibers::fiber( boost::fibers::launch::dispatch, do_test_recursive_mutex).join(); +} + +void do_test_timed_mutex() { + test_lock< boost::fibers::timed_mutex >()(); + test_exclusive< boost::fibers::timed_mutex >()(); + + { + boost::fibers::timed_mutex timed_mtx; + timed_mtx.lock(); + boost::fibers::fiber f( boost::fibers::launch::dispatch, & fn3, std::ref( timed_mtx) ); + boost::this_fiber::sleep_for( ms(250) ); + timed_mtx.unlock(); + f.join(); + } + + { + boost::fibers::timed_mutex timed_mtx; + timed_mtx.lock(); + boost::fibers::fiber f( boost::fibers::launch::dispatch, & fn4, std::ref( timed_mtx) ); + boost::this_fiber::sleep_for( ms(250) ); + timed_mtx.unlock(); + f.join(); + } + + { + boost::fibers::timed_mutex timed_mtx; + timed_mtx.lock(); + boost::fibers::fiber f( boost::fibers::launch::dispatch, & fn5, std::ref( timed_mtx) ); + boost::this_fiber::sleep_for( ms(250) ); + timed_mtx.unlock(); + f.join(); + } + + { + boost::fibers::timed_mutex timed_mtx; + timed_mtx.lock(); + boost::fibers::fiber f( boost::fibers::launch::dispatch, & fn6, std::ref( timed_mtx) ); + boost::this_fiber::sleep_for( ms(300) ); + timed_mtx.unlock(); + f.join(); + } + + { + boost::fibers::timed_mutex timed_mtx; + timed_mtx.lock(); + boost::fibers::fiber f( boost::fibers::launch::dispatch, & fn7, std::ref( timed_mtx) ); + boost::this_fiber::sleep_for( ms(250) ); + timed_mtx.unlock(); + f.join(); + } + + { + boost::fibers::timed_mutex timed_mtx; + timed_mtx.lock(); + boost::fibers::fiber f( boost::fibers::launch::dispatch, & fn8, std::ref( timed_mtx) ); + boost::this_fiber::sleep_for( ms(300) + ms(1000) ); + timed_mtx.unlock(); + f.join(); + } +} + +void test_timed_mutex() { + boost::fibers::fiber( boost::fibers::launch::dispatch, & do_test_timed_mutex).join(); +} + +void do_test_recursive_timed_mutex() { + test_lock< boost::fibers::recursive_timed_mutex >()(); + test_exclusive< boost::fibers::recursive_timed_mutex >()(); + test_recursive_lock< boost::fibers::recursive_timed_mutex >()(); + + { + boost::fibers::recursive_timed_mutex timed_mtx; + timed_mtx.lock(); + boost::fibers::fiber f( boost::fibers::launch::dispatch, & fn9, std::ref( timed_mtx) ); + boost::this_fiber::sleep_for( ms(250) ); + timed_mtx.unlock(); + f.join(); + } + + { + boost::fibers::recursive_timed_mutex timed_mtx; + timed_mtx.lock(); + boost::fibers::fiber f( boost::fibers::launch::dispatch, & fn10, std::ref( timed_mtx) ); + boost::this_fiber::sleep_for( ms(250) ); + timed_mtx.unlock(); + f.join(); + } + + { + boost::fibers::recursive_timed_mutex timed_mtx; + timed_mtx.lock(); + boost::fibers::fiber f( boost::fibers::launch::dispatch, & fn11, std::ref( timed_mtx) ); + boost::this_fiber::sleep_for( ms(250) ); + timed_mtx.unlock(); + f.join(); + } + + { + boost::fibers::recursive_timed_mutex timed_mtx; + timed_mtx.lock(); + boost::fibers::fiber f( boost::fibers::launch::dispatch, & fn12, std::ref( timed_mtx) ); + boost::this_fiber::sleep_for( ms(400) ); + timed_mtx.unlock(); + f.join(); + } + + { + boost::fibers::recursive_timed_mutex timed_mtx; + timed_mtx.lock(); + boost::fibers::fiber f( boost::fibers::launch::dispatch, & fn13, std::ref( timed_mtx) ); + boost::this_fiber::sleep_for( ms(250) ); + timed_mtx.unlock(); + f.join(); + } + + { + boost::fibers::recursive_timed_mutex timed_mtx; + timed_mtx.lock(); + boost::fibers::fiber f( boost::fibers::launch::dispatch, & fn14, std::ref( timed_mtx) ); + boost::this_fiber::sleep_for( ms(300) ); + timed_mtx.unlock(); + f.join(); + } +} + +void test_recursive_timed_mutex() { + boost::fibers::fiber( boost::fibers::launch::dispatch, & do_test_recursive_timed_mutex).join(); +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) { + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Fiber: mutex test suite"); + + test->add( BOOST_TEST_CASE( & test_mutex) ); + test->add( BOOST_TEST_CASE( & test_recursive_mutex) ); + test->add( BOOST_TEST_CASE( & test_timed_mutex) ); + test->add( BOOST_TEST_CASE( & test_recursive_timed_mutex) ); + + return test; +} diff --git a/src/boost/libs/fiber/test/test_mutex_mt_dispatch.cpp b/src/boost/libs/fiber/test/test_mutex_mt_dispatch.cpp new file mode 100644 index 00000000..2087d17c --- /dev/null +++ b/src/boost/libs/fiber/test/test_mutex_mt_dispatch.cpp @@ -0,0 +1,139 @@ + +// Copyright Oliver Kowalke 2013. +// 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) +// +// This test is based on the tests of Boost.Thread + +#include <cstdlib> +#include <iostream> +#include <map> +#include <stdexcept> +#include <vector> + +#include <boost/chrono.hpp> +#include <boost/test/unit_test.hpp> +#include <boost/thread/barrier.hpp> +#include <boost/thread/thread.hpp> + +#include <boost/fiber/all.hpp> + +typedef boost::chrono::nanoseconds ns; +typedef boost::chrono::milliseconds ms; + +int value1 = 0; +int value2 = 0; + +template< typename Mtx > +void g( boost::barrier & b, Mtx & m) { + b.wait(); + m.lock(); + value1 = 3; + m.unlock(); +} + +template< typename Mtx > +void f( boost::barrier & b, Mtx & m) { + b.wait(); + m.lock(); + value2 = 7; + m.unlock(); +} + +template< typename Mtx > +void fn1( boost::barrier & b, Mtx & m) { + boost::fibers::fiber( boost::fibers::launch::dispatch, g< Mtx >, std::ref( b), std::ref( m) ).join(); +} + +template< typename Mtx > +void fn2( boost::barrier & b, Mtx & m) { + boost::fibers::fiber( boost::fibers::launch::dispatch, f< Mtx >, std::ref( b), std::ref( m) ).join(); +} + +void test_mutex() { + for ( int i = 0; i < 10; ++i) { + boost::fibers::mutex mtx; + mtx.lock(); + boost::barrier b( 3); + boost::thread t1( fn1< boost::fibers::mutex >, std::ref( b), std::ref( mtx) ); + boost::thread t2( fn2< boost::fibers::mutex >, std::ref( b), std::ref( mtx) ); + b.wait(); + boost::this_thread::sleep_for( ms( 250) ); + mtx.unlock(); + t1.join(); + t2.join(); + BOOST_CHECK( 3 == value1); + BOOST_CHECK( 7 == value2); + } +} + +void test_recursive_mutex() { + for ( int i = 0; i < 10; ++i) { + boost::fibers::recursive_mutex mtx; + mtx.lock(); + boost::barrier b( 3); + boost::thread t1( fn1< boost::fibers::recursive_mutex >, std::ref( b), std::ref( mtx) ); + boost::thread t2( fn2< boost::fibers::recursive_mutex >, std::ref( b), std::ref( mtx) ); + b.wait(); + boost::this_thread::sleep_for( ms( 250) ); + mtx.unlock(); + t1.join(); + t2.join(); + BOOST_CHECK( 3 == value1); + BOOST_CHECK( 7 == value2); + } +} + +void test_timed_mutex() { + for ( int i = 0; i < 10; ++i) { + boost::fibers::timed_mutex mtx; + mtx.lock(); + boost::barrier b( 3); + boost::thread t1( fn1< boost::fibers::timed_mutex >, std::ref( b), std::ref( mtx) ); + boost::thread t2( fn2< boost::fibers::timed_mutex >, std::ref( b), std::ref( mtx) ); + b.wait(); + boost::this_thread::sleep_for( ms( 250) ); + mtx.unlock(); + t1.join(); + t2.join(); + BOOST_CHECK( 3 == value1); + BOOST_CHECK( 7 == value2); + } +} + +void test_recursive_timed_mutex() { + for ( int i = 0; i < 10; ++i) { + boost::fibers::recursive_timed_mutex mtx; + mtx.lock(); + boost::barrier b( 3); + boost::thread t1( fn1< boost::fibers::recursive_timed_mutex >, std::ref( b), std::ref( mtx) ); + boost::thread t2( fn2< boost::fibers::recursive_timed_mutex >, std::ref( b), std::ref( mtx) ); + b.wait(); + boost::this_thread::sleep_for( ms( 250) ); + mtx.unlock(); + t1.join(); + t2.join(); + BOOST_CHECK( 3 == value1); + BOOST_CHECK( 7 == value2); + } +} + +void test_dummy() { +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) { + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Fiber: multithreaded mutex test suite"); + +#if ! defined(BOOST_FIBERS_NO_ATOMICS) + test->add( BOOST_TEST_CASE( & test_mutex) ); + test->add( BOOST_TEST_CASE( & test_recursive_mutex) ); + test->add( BOOST_TEST_CASE( & test_timed_mutex) ); + test->add( BOOST_TEST_CASE( & test_recursive_timed_mutex) ); +#else + test->add( BOOST_TEST_CASE( & test_dummy) ); +#endif + + return test; +} diff --git a/src/boost/libs/fiber/test/test_mutex_mt_post.cpp b/src/boost/libs/fiber/test/test_mutex_mt_post.cpp new file mode 100644 index 00000000..758acf9d --- /dev/null +++ b/src/boost/libs/fiber/test/test_mutex_mt_post.cpp @@ -0,0 +1,139 @@ + +// Copyright Oliver Kowalke 2013. +// 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) +// +// This test is based on the tests of Boost.Thread + +#include <cstdlib> +#include <iostream> +#include <map> +#include <stdexcept> +#include <vector> + +#include <boost/chrono.hpp> +#include <boost/test/unit_test.hpp> +#include <boost/thread/barrier.hpp> +#include <boost/thread/thread.hpp> + +#include <boost/fiber/all.hpp> + +typedef boost::chrono::nanoseconds ns; +typedef boost::chrono::milliseconds ms; + +int value1 = 0; +int value2 = 0; + +template< typename Mtx > +void g( boost::barrier & b, Mtx & m) { + b.wait(); + m.lock(); + value1 = 3; + m.unlock(); +} + +template< typename Mtx > +void f( boost::barrier & b, Mtx & m) { + b.wait(); + m.lock(); + value2 = 7; + m.unlock(); +} + +template< typename Mtx > +void fn1( boost::barrier & b, Mtx & m) { + boost::fibers::fiber( boost::fibers::launch::post, g< Mtx >, std::ref( b), std::ref( m) ).join(); +} + +template< typename Mtx > +void fn2( boost::barrier & b, Mtx & m) { + boost::fibers::fiber( boost::fibers::launch::post, f< Mtx >, std::ref( b), std::ref( m) ).join(); +} + +void test_mutex() { + for ( int i = 0; i < 10; ++i) { + boost::fibers::mutex mtx; + mtx.lock(); + boost::barrier b( 3); + boost::thread t1( fn1< boost::fibers::mutex >, std::ref( b), std::ref( mtx) ); + boost::thread t2( fn2< boost::fibers::mutex >, std::ref( b), std::ref( mtx) ); + b.wait(); + boost::this_thread::sleep_for( ms( 250) ); + mtx.unlock(); + t1.join(); + t2.join(); + BOOST_CHECK( 3 == value1); + BOOST_CHECK( 7 == value2); + } +} + +void test_recursive_mutex() { + for ( int i = 0; i < 10; ++i) { + boost::fibers::recursive_mutex mtx; + mtx.lock(); + boost::barrier b( 3); + boost::thread t1( fn1< boost::fibers::recursive_mutex >, std::ref( b), std::ref( mtx) ); + boost::thread t2( fn2< boost::fibers::recursive_mutex >, std::ref( b), std::ref( mtx) ); + b.wait(); + boost::this_thread::sleep_for( ms( 250) ); + mtx.unlock(); + t1.join(); + t2.join(); + BOOST_CHECK( 3 == value1); + BOOST_CHECK( 7 == value2); + } +} + +void test_timed_mutex() { + for ( int i = 0; i < 10; ++i) { + boost::fibers::timed_mutex mtx; + mtx.lock(); + boost::barrier b( 3); + boost::thread t1( fn1< boost::fibers::timed_mutex >, std::ref( b), std::ref( mtx) ); + boost::thread t2( fn2< boost::fibers::timed_mutex >, std::ref( b), std::ref( mtx) ); + b.wait(); + boost::this_thread::sleep_for( ms( 250) ); + mtx.unlock(); + t1.join(); + t2.join(); + BOOST_CHECK( 3 == value1); + BOOST_CHECK( 7 == value2); + } +} + +void test_recursive_timed_mutex() { + for ( int i = 0; i < 10; ++i) { + boost::fibers::recursive_timed_mutex mtx; + mtx.lock(); + boost::barrier b( 3); + boost::thread t1( fn1< boost::fibers::recursive_timed_mutex >, std::ref( b), std::ref( mtx) ); + boost::thread t2( fn2< boost::fibers::recursive_timed_mutex >, std::ref( b), std::ref( mtx) ); + b.wait(); + boost::this_thread::sleep_for( ms( 250) ); + mtx.unlock(); + t1.join(); + t2.join(); + BOOST_CHECK( 3 == value1); + BOOST_CHECK( 7 == value2); + } +} + +void test_dummy() { +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) { + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Fiber: multithreaded mutex test suite"); + +#if ! defined(BOOST_FIBERS_NO_ATOMICS) + test->add( BOOST_TEST_CASE( & test_mutex) ); + test->add( BOOST_TEST_CASE( & test_recursive_mutex) ); + test->add( BOOST_TEST_CASE( & test_timed_mutex) ); + test->add( BOOST_TEST_CASE( & test_recursive_timed_mutex) ); +#else + test->add( BOOST_TEST_CASE( & test_dummy) ); +#endif + + return test; +} diff --git a/src/boost/libs/fiber/test/test_mutex_post.cpp b/src/boost/libs/fiber/test/test_mutex_post.cpp new file mode 100644 index 00000000..f88a571f --- /dev/null +++ b/src/boost/libs/fiber/test/test_mutex_post.cpp @@ -0,0 +1,447 @@ + +// Copyright Oliver Kowalke 2013. +// 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) +// +// This test is based on the tests of Boost.Thread + +#include <chrono> +#include <cstdlib> +#include <iostream> +#include <map> +#include <mutex> +#include <stdexcept> +#include <vector> + +#include <boost/test/unit_test.hpp> + +#include <boost/fiber/all.hpp> + +typedef std::chrono::nanoseconds ns; +typedef std::chrono::milliseconds ms; + +int value1 = 0; +int value2 = 0; + +template< typename M > +void fn1( M & mtx) { + typedef M mutex_type; + typename std::unique_lock< mutex_type > lk( mtx); + ++value1; + for ( int i = 0; i < 3; ++i) + boost::this_fiber::yield(); +} + +template< typename M > +void fn2( M & mtx) { + typedef M mutex_type; + ++value2; + typename std::unique_lock< mutex_type > lk( mtx); + ++value2; +} + +void fn3( boost::fibers::timed_mutex & m) { + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + m.lock(); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + m.unlock(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ns(2500000)+ms(2000)); // within 2.5 ms +} + +void fn4( boost::fibers::timed_mutex & m) { + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + while ( ! m.try_lock() ); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + m.unlock(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ns(50000000)+ms(2000)); // within 50 ms +} + +void fn5( boost::fibers::timed_mutex & m) { + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + BOOST_CHECK( m.try_lock_for(ms(300)+ms(2000)) == true); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + m.unlock(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ns(5000000)+ms(2000)); // within 5 ms +} + +void fn6( boost::fibers::timed_mutex & m) { + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + BOOST_CHECK(m.try_lock_for(ms(250)) == false); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ns(5000000)+ms(2000)); // within 5 ms +} + +void fn7( boost::fibers::timed_mutex & m) { + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + BOOST_CHECK(m.try_lock_until(std::chrono::steady_clock::now() + ms(300) + ms(1000)) == true); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + m.unlock(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ns(5000000)+ms(2000)); // within 5ms +} + +void fn8( boost::fibers::timed_mutex & m) { + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + BOOST_CHECK(m.try_lock_until(std::chrono::steady_clock::now() + ms(250)) == false); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + ns d = t1 - t0 - ms(250); + ns r = ns(5000000)+ms(2000); // within 6ms + BOOST_CHECK(d < r); // within 6ms +} + +void fn9( boost::fibers::recursive_timed_mutex & m) { + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + m.lock(); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + m.lock(); + m.unlock(); + m.unlock(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ms(2500)+ms(2000)); // within 2.5 ms +} + +void fn10( boost::fibers::recursive_timed_mutex & m) { + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + while (!m.try_lock()) ; + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + BOOST_CHECK(m.try_lock()); + m.unlock(); + m.unlock(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ns(50000000)+ms(2000)); // within 50 ms +} + +void fn11( boost::fibers::recursive_timed_mutex & m) { + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + BOOST_CHECK(m.try_lock_for(ms(300)+ms(1000)) == true); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + BOOST_CHECK(m.try_lock()); + m.unlock(); + m.unlock(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ns(5000000)+ms(2000)); // within 5 ms +} + +void fn12( boost::fibers::recursive_timed_mutex & m) { + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + BOOST_CHECK(m.try_lock_for(ms(250)) == false); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ms(5000)+ms(2000)); // within 5 ms +} + +void fn13( boost::fibers::recursive_timed_mutex & m) { + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + BOOST_CHECK(m.try_lock_until(std::chrono::steady_clock::now() + ms(300) + ms(1000)) == true); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + m.unlock(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ns(5000000)+ms(2000)); // within 5 ms +} + +void fn14( boost::fibers::recursive_timed_mutex & m) { + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + BOOST_CHECK(m.try_lock_until(std::chrono::steady_clock::now() + ms(250)) == false); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ns(5000000)+ms(2000)); // within 5 ms +} + +void fn15( boost::fibers::recursive_mutex & m) { + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + m.lock(); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + m.lock(); + m.unlock(); + m.unlock(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ns(2500000)+ms(2000)); // within 2.5 ms +} + +void fn16( boost::fibers::recursive_mutex & m) { + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + while (!m.try_lock()); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + BOOST_CHECK(m.try_lock()); + m.unlock(); + m.unlock(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ns(50000000)+ms(2000)); // within 50 ms +} + +void fn17( boost::fibers::mutex & m) { + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + m.lock(); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + m.unlock(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ns(2500000)+ms(2000)); // within 2.5 ms +} + +void fn18( boost::fibers::mutex & m) { + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + while (!m.try_lock()) ; + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + m.unlock(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ns(50000000)+ms(2000)); // within 50 ms +} + +template< typename M > +struct test_lock { + typedef M mutex_type; + typedef typename std::unique_lock< M > lock_type; + + void operator()() { + mutex_type mtx; + + // Test the lock's constructors. + { + lock_type lk(mtx, std::defer_lock); + BOOST_CHECK(!lk); + } + lock_type lk(mtx); + BOOST_CHECK(lk ? true : false); + + // Test the lock and unlock methods. + lk.unlock(); + BOOST_CHECK(!lk); + lk.lock(); + BOOST_CHECK(lk ? true : false); + } +}; + +template< typename M > +struct test_exclusive { + typedef M mutex_type; + typedef typename std::unique_lock< M > lock_type; + + void operator()() { + value1 = 0; + value2 = 0; + BOOST_CHECK_EQUAL( 0, value1); + BOOST_CHECK_EQUAL( 0, value2); + + mutex_type mtx; + boost::fibers::fiber f1( boost::fibers::launch::post, & fn1< mutex_type >, std::ref( mtx) ); + boost::fibers::fiber f2( boost::fibers::launch::post, & fn2< mutex_type >, std::ref( mtx) ); + BOOST_ASSERT( f1.joinable() ); + BOOST_ASSERT( f2.joinable() ); + + f1.join(); + f2.join(); + BOOST_CHECK_EQUAL( 1, value1); + BOOST_CHECK_EQUAL( 2, value2); + } +}; + +template< typename M > +struct test_recursive_lock { + typedef M mutex_type; + typedef typename std::unique_lock< M > lock_type; + + void operator()() { + mutex_type mx; + lock_type lock1(mx); + lock_type lock2(mx); + } +}; + +void do_test_mutex() { + test_lock< boost::fibers::mutex >()(); + test_exclusive< boost::fibers::mutex >()(); + + { + boost::fibers::mutex mtx; + mtx.lock(); + boost::fibers::fiber f( boost::fibers::launch::post, & fn17, std::ref( mtx) ); + boost::this_fiber::sleep_for( ms(250) ); + mtx.unlock(); + f.join(); + } + + { + boost::fibers::mutex mtx; + mtx.lock(); + boost::fibers::fiber f( boost::fibers::launch::post, & fn18, std::ref( mtx) ); + boost::this_fiber::sleep_for( ms(250) ); + mtx.unlock(); + f.join(); + } +} + +void test_mutex() { + boost::fibers::fiber( boost::fibers::launch::post, & do_test_mutex).join(); +} + +void do_test_recursive_mutex() { + test_lock< boost::fibers::recursive_mutex >()(); + test_exclusive< boost::fibers::recursive_mutex >()(); + test_recursive_lock< boost::fibers::recursive_mutex >()(); + + { + boost::fibers::recursive_mutex mtx; + mtx.lock(); + boost::fibers::fiber f( boost::fibers::launch::post, & fn15, std::ref( mtx) ); + boost::this_fiber::sleep_for( ms(250) ); + mtx.unlock(); + f.join(); + } + + { + boost::fibers::recursive_mutex mtx; + mtx.lock(); + boost::fibers::fiber f( boost::fibers::launch::post, & fn16, std::ref( mtx) ); + boost::this_fiber::sleep_for( ms(250) ); + mtx.unlock(); + f.join(); + } +} + +void test_recursive_mutex() { + boost::fibers::fiber( boost::fibers::launch::post, do_test_recursive_mutex).join(); +} + +void do_test_timed_mutex() { + test_lock< boost::fibers::timed_mutex >()(); + test_exclusive< boost::fibers::timed_mutex >()(); + + { + boost::fibers::timed_mutex timed_mtx; + timed_mtx.lock(); + boost::fibers::fiber f( boost::fibers::launch::post, & fn3, std::ref( timed_mtx) ); + boost::this_fiber::sleep_for( ms(250) ); + timed_mtx.unlock(); + f.join(); + } + + { + boost::fibers::timed_mutex timed_mtx; + timed_mtx.lock(); + boost::fibers::fiber f( boost::fibers::launch::post, & fn4, std::ref( timed_mtx) ); + boost::this_fiber::sleep_for( ms(250) ); + timed_mtx.unlock(); + f.join(); + } + + { + boost::fibers::timed_mutex timed_mtx; + timed_mtx.lock(); + boost::fibers::fiber f( boost::fibers::launch::post, & fn5, std::ref( timed_mtx) ); + boost::this_fiber::sleep_for( ms(250) ); + timed_mtx.unlock(); + f.join(); + } + + { + boost::fibers::timed_mutex timed_mtx; + timed_mtx.lock(); + boost::fibers::fiber f( boost::fibers::launch::post, & fn6, std::ref( timed_mtx) ); + boost::this_fiber::sleep_for( ms(300) ); + timed_mtx.unlock(); + f.join(); + } + + { + boost::fibers::timed_mutex timed_mtx; + timed_mtx.lock(); + boost::fibers::fiber f( boost::fibers::launch::post, & fn7, std::ref( timed_mtx) ); + boost::this_fiber::sleep_for( ms(250) ); + timed_mtx.unlock(); + f.join(); + } + + { + boost::fibers::timed_mutex timed_mtx; + timed_mtx.lock(); + boost::fibers::fiber f( boost::fibers::launch::post, & fn8, std::ref( timed_mtx) ); + boost::this_fiber::sleep_for( ms(300) + ms(1000) ); + timed_mtx.unlock(); + f.join(); + } +} + +void test_timed_mutex() { + boost::fibers::fiber( boost::fibers::launch::post, & do_test_timed_mutex).join(); +} + +void do_test_recursive_timed_mutex() { + test_lock< boost::fibers::recursive_timed_mutex >()(); + test_exclusive< boost::fibers::recursive_timed_mutex >()(); + test_recursive_lock< boost::fibers::recursive_timed_mutex >()(); + + { + boost::fibers::recursive_timed_mutex timed_mtx; + timed_mtx.lock(); + boost::fibers::fiber f( boost::fibers::launch::post, & fn9, std::ref( timed_mtx) ); + boost::this_fiber::sleep_for( ms(250) ); + timed_mtx.unlock(); + f.join(); + } + + { + boost::fibers::recursive_timed_mutex timed_mtx; + timed_mtx.lock(); + boost::fibers::fiber f( boost::fibers::launch::post, & fn10, std::ref( timed_mtx) ); + boost::this_fiber::sleep_for( ms(250) ); + timed_mtx.unlock(); + f.join(); + } + + { + boost::fibers::recursive_timed_mutex timed_mtx; + timed_mtx.lock(); + boost::fibers::fiber f( boost::fibers::launch::post, & fn11, std::ref( timed_mtx) ); + boost::this_fiber::sleep_for( ms(250) ); + timed_mtx.unlock(); + f.join(); + } + + { + boost::fibers::recursive_timed_mutex timed_mtx; + timed_mtx.lock(); + boost::fibers::fiber f( boost::fibers::launch::post, & fn12, std::ref( timed_mtx) ); + boost::this_fiber::sleep_for( ms(400) ); + timed_mtx.unlock(); + f.join(); + } + + { + boost::fibers::recursive_timed_mutex timed_mtx; + timed_mtx.lock(); + boost::fibers::fiber f( boost::fibers::launch::post, & fn13, std::ref( timed_mtx) ); + boost::this_fiber::sleep_for( ms(250) ); + timed_mtx.unlock(); + f.join(); + } + + { + boost::fibers::recursive_timed_mutex timed_mtx; + timed_mtx.lock(); + boost::fibers::fiber f( boost::fibers::launch::post, & fn14, std::ref( timed_mtx) ); + boost::this_fiber::sleep_for( ms(300) ); + timed_mtx.unlock(); + f.join(); + } +} + +void test_recursive_timed_mutex() { + boost::fibers::fiber( boost::fibers::launch::post, & do_test_recursive_timed_mutex).join(); +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) { + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Fiber: mutex test suite"); + + test->add( BOOST_TEST_CASE( & test_mutex) ); + test->add( BOOST_TEST_CASE( & test_recursive_mutex) ); + test->add( BOOST_TEST_CASE( & test_timed_mutex) ); + test->add( BOOST_TEST_CASE( & test_recursive_timed_mutex) ); + + return test; +} diff --git a/src/boost/libs/fiber/test/test_packaged_task_dispatch.cpp b/src/boost/libs/fiber/test/test_packaged_task_dispatch.cpp new file mode 100644 index 00000000..36475583 --- /dev/null +++ b/src/boost/libs/fiber/test/test_packaged_task_dispatch.cpp @@ -0,0 +1,679 @@ +// (C) Copyright 2008-10 Anthony Williams +// +// 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 <utility> +#include <memory> +#include <stdexcept> +#include <string> + +#include <boost/test/unit_test.hpp> + +#include <boost/fiber/all.hpp> + +int gi = 7; + +struct my_exception : public std::runtime_error { + my_exception() : + std::runtime_error("my_exception") { + } +}; + +struct A { + A() = default; + + A( A const&) = delete; + A( A &&) = default; + + A & operator=( A const&) = delete; + A & operator=( A &&) = default; + + int value; +}; + +struct B { + bool bset{ false }; + + B() = default; + + B( bool set) : + bset{ set } { + gi = 3; + } + + ~B() { + if ( bset) { + gi = -1; + } + } + + B( B && other) : + bset{ other.bset } { + other.bset = false; + } + + B & operator=( B && other) { + if ( this == & other) return * this; + bset = other.bset; + other.bset = false; + return * this; + } + + B( B const&) = delete; + B & operator=( B const&) = delete; +}; + +void fn1( boost::fibers::promise< int > * p, int i) { + boost::this_fiber::yield(); + p->set_value( i); +} + +void fn2() { + boost::fibers::promise< int > p; + boost::fibers::future< int > f( p.get_future() ); + boost::this_fiber::yield(); + boost::fibers::fiber( boost::fibers::launch::dispatch, fn1, & p, 7).detach(); + boost::this_fiber::yield(); + BOOST_CHECK( 7 == f.get() ); +} + +int fn3() { + return 3; +} + +void fn4() { +} + +int fn5() { + boost::throw_exception( my_exception() ); + return 3; +} + +void fn6() { + boost::throw_exception( my_exception() ); +} + +int & fn7() { + return gi; +} + +int fn8( int i) { + return i; +} + +A fn9() { + A a; + a.value = 3; + return a; +} + +A fn10() { + boost::throw_exception( my_exception() ); + return A(); +} + +B fn11( bool set) { + B b( set); + return b; +} + +// packaged_task +void test_packaged_task_create() { + // default constructed packaged_task is not valid + boost::fibers::packaged_task< int() > t1; + BOOST_CHECK( ! t1.valid() ); + + // packaged_task from function + boost::fibers::packaged_task< int() > t2( fn3); + BOOST_CHECK( t2.valid() ); +} + +// packaged_task +void test_packaged_task_create_move() { + // default constructed packaged_task is not valid + boost::fibers::packaged_task< A() > t1; + BOOST_CHECK( ! t1.valid() ); + + // packaged_task from function + boost::fibers::packaged_task< A() > t2( fn9); + BOOST_CHECK( t2.valid() ); +} + +void test_packaged_task_create_void() { + // default constructed packaged_task is not valid + boost::fibers::packaged_task< void() > t1; + BOOST_CHECK( ! t1.valid() ); + + // packaged_task from function + boost::fibers::packaged_task< void() > t2( fn4); + BOOST_CHECK( t2.valid() ); +} + +void test_packaged_task_move() { + boost::fibers::packaged_task< int() > t1( fn3); + BOOST_CHECK( t1.valid() ); + + // move construction + boost::fibers::packaged_task< int() > t2( std::move( t1) ); + BOOST_CHECK( ! t1.valid() ); + BOOST_CHECK( t2.valid() ); + + // move assignment + t1 = std::move( t2); + BOOST_CHECK( t1.valid() ); + BOOST_CHECK( ! t2.valid() ); +} + +void test_packaged_task_move_move() { + boost::fibers::packaged_task< A() > t1( fn9); + BOOST_CHECK( t1.valid() ); + + // move construction + boost::fibers::packaged_task< A() > t2( std::move( t1) ); + BOOST_CHECK( ! t1.valid() ); + BOOST_CHECK( t2.valid() ); + + // move assignment + t1 = std::move( t2); + BOOST_CHECK( t1.valid() ); + BOOST_CHECK( ! t2.valid() ); +} + +void test_packaged_task_move_void() { + boost::fibers::packaged_task< void() > t1( fn4); + BOOST_CHECK( t1.valid() ); + + // move construction + boost::fibers::packaged_task< void() > t2( std::move( t1) ); + BOOST_CHECK( ! t1.valid() ); + BOOST_CHECK( t2.valid() ); + + // move assignment + t1 = std::move( t2); + BOOST_CHECK( t1.valid() ); + BOOST_CHECK( ! t2.valid() ); +} + +void test_packaged_task_swap() { + boost::fibers::packaged_task< int() > t1( fn3); + BOOST_CHECK( t1.valid() ); + + boost::fibers::packaged_task< int() > t2; + BOOST_CHECK( ! t2.valid() ); + + // swap + t1.swap( t2); + BOOST_CHECK( ! t1.valid() ); + BOOST_CHECK( t2.valid() ); +} + +void test_packaged_task_swap_move() { + boost::fibers::packaged_task< A() > t1( fn9); + BOOST_CHECK( t1.valid() ); + + boost::fibers::packaged_task< A() > t2; + BOOST_CHECK( ! t2.valid() ); + + // swap + t1.swap( t2); + BOOST_CHECK( ! t1.valid() ); + BOOST_CHECK( t2.valid() ); +} + +void test_packaged_task_swap_void() { + boost::fibers::packaged_task< void() > t1( fn4); + BOOST_CHECK( t1.valid() ); + + boost::fibers::packaged_task< void() > t2; + BOOST_CHECK( ! t2.valid() ); + + // swap + t1.swap( t2); + BOOST_CHECK( ! t1.valid() ); + BOOST_CHECK( t2.valid() ); +} + +void test_packaged_task_reset() { + { + boost::fibers::packaged_task< int() > p( fn3); + boost::fibers::future< int > f( p.get_future() ); + BOOST_CHECK( p.valid() ); + + p(); + BOOST_CHECK( 3 == f.get() ); + + // reset + p.reset(); + p(); + f = p.get_future(); + BOOST_CHECK( 3 == f.get() ); + } + { + boost::fibers::packaged_task< int() > p; + + bool thrown = false; + try { + p.reset(); + } catch ( boost::fibers::packaged_task_uninitialized const&) { + thrown = true; + } + BOOST_CHECK( thrown); + } +} + +void test_packaged_task_reset_destruction() { + gi = 0; + boost::fibers::packaged_task< B( bool) > p( fn11); + BOOST_CHECK( p.valid() ); + + BOOST_CHECK( 0 == gi); + p( true); + BOOST_CHECK( 3 == gi); + + // reset + p.reset(); + BOOST_CHECK( -1 == gi); + p( false); + BOOST_CHECK( 3 == gi); + + // reset + p.reset(); + BOOST_CHECK( 3 == gi); +} + +void test_packaged_task_reset_move() { + { + boost::fibers::packaged_task< A() > p( fn9); + boost::fibers::future< A > f( p.get_future() ); + BOOST_CHECK( p.valid() ); + + p(); + BOOST_CHECK( 3 == f.get().value); + + // reset + p.reset(); + p(); + f = p.get_future(); + BOOST_CHECK( 3 == f.get().value); + } + { + boost::fibers::packaged_task< A() > p; + + bool thrown = false; + try { + p.reset(); + } catch ( boost::fibers::packaged_task_uninitialized const&) { + thrown = true; + } + BOOST_CHECK( thrown); + } +} + +void test_packaged_task_reset_void() { + { + boost::fibers::packaged_task< void() > p( fn4); + boost::fibers::future< void > f( p.get_future() ); + BOOST_CHECK( p.valid() ); + + p(); + f.get(); + + // reset + p.reset(); + p(); + f = p.get_future(); + f.get(); + } + { + boost::fibers::packaged_task< void() > p; + + bool thrown = false; + try { + p.reset(); + } catch ( boost::fibers::packaged_task_uninitialized const&) { + thrown = true; + } + BOOST_CHECK( thrown); + } +} + +void test_packaged_task_get_future() { + boost::fibers::packaged_task< int() > t1( fn3); + BOOST_CHECK( t1.valid() ); + + // retrieve future + boost::fibers::future< int > f1 = t1.get_future(); + BOOST_CHECK( f1.valid() ); + + // retrieve future a second time + bool thrown = false; + try { + f1 = t1.get_future(); + } catch ( boost::fibers::future_already_retrieved const&) { + thrown = true; + } + BOOST_CHECK( thrown); + + // move construction + boost::fibers::packaged_task< int() > t2( std::move( t1) ); + BOOST_CHECK( ! t1.valid() ); + BOOST_CHECK( t2.valid() ); + + // retrieve future from uninitialized + thrown = false; + try { + f1 = t1.get_future(); + } catch ( boost::fibers::packaged_task_uninitialized const&) { + thrown = true; + } + BOOST_CHECK( thrown); +} + +void test_packaged_task_get_future_move() { + boost::fibers::packaged_task< A() > t1( fn9); + BOOST_CHECK( t1.valid() ); + + // retrieve future + boost::fibers::future< A > f1 = t1.get_future(); + BOOST_CHECK( f1.valid() ); + + // retrieve future a second time + bool thrown = false; + try { + f1 = t1.get_future(); + } catch ( boost::fibers::future_already_retrieved const&) { + thrown = true; + } + BOOST_CHECK( thrown); + + // move construction + boost::fibers::packaged_task< A() > t2( std::move( t1) ); + BOOST_CHECK( ! t1.valid() ); + BOOST_CHECK( t2.valid() ); + + // retrieve future from uninitialized + thrown = false; + try { + f1 = t1.get_future(); + } catch ( boost::fibers::packaged_task_uninitialized const&) { + thrown = true; + } + BOOST_CHECK( thrown); +} + +void test_packaged_task_get_future_void() { + boost::fibers::packaged_task< void() > t1( fn4); + BOOST_CHECK( t1.valid() ); + + // retrieve future + boost::fibers::future< void > f1 = t1.get_future(); + BOOST_CHECK( f1.valid() ); + + // retrieve future a second time + bool thrown = false; + try { + f1 = t1.get_future(); + } catch ( boost::fibers::future_already_retrieved const&) { + thrown = true; + } + BOOST_CHECK( thrown); + + // move construction + boost::fibers::packaged_task< void() > t2( std::move( t1) ); + BOOST_CHECK( ! t1.valid() ); + BOOST_CHECK( t2.valid() ); + + // retrieve future from uninitialized + thrown = false; + try { + f1 = t1.get_future(); + } catch ( boost::fibers::packaged_task_uninitialized const&) { + thrown = true; + } + BOOST_CHECK( thrown); +} + +void test_packaged_task_exec() { + // promise takes a copyable as return type + boost::fibers::packaged_task< int() > t1( fn3); + BOOST_CHECK( t1.valid() ); + boost::fibers::future< int > f1 = t1.get_future(); + BOOST_CHECK( f1.valid() ); + + // exec + t1(); + BOOST_CHECK( 3 == f1.get() ); + + // exec a second time + bool thrown = false; + try { + t1(); + } catch ( boost::fibers::promise_already_satisfied const&) { + thrown = true; + } + BOOST_CHECK( thrown); +} + +void test_packaged_task_exec_move() { + // promise takes a copyable as return type + boost::fibers::packaged_task< A() > t1( fn9); + BOOST_CHECK( t1.valid() ); + boost::fibers::future< A > f1 = t1.get_future(); + BOOST_CHECK( f1.valid() ); + + // exec + t1(); + BOOST_CHECK( 3 == f1.get().value); + + // exec a second time + bool thrown = false; + try { + t1(); + } catch ( boost::fibers::promise_already_satisfied const&) { + thrown = true; + } + BOOST_CHECK( thrown); +} + +void test_packaged_task_exec_param() { + // promise takes a copyable as return type + boost::fibers::packaged_task< int( int) > t1( fn8); + BOOST_CHECK( t1.valid() ); + boost::fibers::future< int > f1 = t1.get_future(); + BOOST_CHECK( f1.valid() ); + + // exec + t1( 3); + BOOST_CHECK( 3 == f1.get() ); + + // exec a second time + bool thrown = false; + try { + t1( 7); + } catch ( boost::fibers::promise_already_satisfied const&) { + thrown = true; + } + BOOST_CHECK( thrown); + + //TODO: packaged_task returns a moveable-only as return type +} + +void test_packaged_task_exec_ref() { + // promise takes a copyable as return type + boost::fibers::packaged_task< int&() > t1( fn7); + BOOST_CHECK( t1.valid() ); + boost::fibers::future< int& > f1 = t1.get_future(); + BOOST_CHECK( f1.valid() ); + + // exec + t1(); + int & i = f1.get(); + BOOST_CHECK( &gi == &i); + + // exec a second time + bool thrown = false; + try { + t1(); + } catch ( boost::fibers::promise_already_satisfied const&) { + thrown = true; + } + BOOST_CHECK( thrown); + + //TODO: packaged_task returns a moveable-only as return type +} + +void test_packaged_task_exec_void() { + // promise takes a copyable as return type + boost::fibers::packaged_task< void() > t1( fn4); + BOOST_CHECK( t1.valid() ); + boost::fibers::future< void > f1 = t1.get_future(); + BOOST_CHECK( f1.valid() ); + + // set void + t1(); + f1.get(); + + // exec a second time + bool thrown = false; + try { + t1(); + } catch ( boost::fibers::promise_already_satisfied const&) { + thrown = true; + } + BOOST_CHECK( thrown); +} + +void test_packaged_task_exception() { + // promise takes a copyable as return type + boost::fibers::packaged_task< int() > t1( fn5); + BOOST_CHECK( t1.valid() ); + boost::fibers::future< int > f1 = t1.get_future(); + BOOST_CHECK( f1.valid() ); + + // exec + t1(); + bool thrown = false; + try { + f1.get(); + } catch ( my_exception const&) { + thrown = true; + } + BOOST_CHECK( thrown); + + boost::fibers::packaged_task< int() > t2( fn5); + BOOST_CHECK( t2.valid() ); + boost::fibers::future< int > f2 = t2.get_future(); + BOOST_CHECK( f2.valid() ); + + // exec + t2(); + BOOST_CHECK( f2.get_exception_ptr() ); + thrown = false; + try + { std::rethrow_exception( f2.get_exception_ptr() ); } + catch ( my_exception const&) + { thrown = true; } + BOOST_CHECK( thrown); +} + +void test_packaged_task_exception_move() { + // promise takes a moveable as return type + boost::fibers::packaged_task< A() > t1( fn10); + BOOST_CHECK( t1.valid() ); + boost::fibers::future< A > f1 = t1.get_future(); + BOOST_CHECK( f1.valid() ); + + // exec + t1(); + bool thrown = false; + try { + f1.get(); + } catch ( my_exception const&) { + thrown = true; + } + BOOST_CHECK( thrown); + + boost::fibers::packaged_task< A() > t2( fn10); + BOOST_CHECK( t2.valid() ); + boost::fibers::future< A > f2 = t2.get_future(); + BOOST_CHECK( f2.valid() ); + + // exec + t2(); + BOOST_CHECK( f2.get_exception_ptr() ); + thrown = false; + try + { std::rethrow_exception( f2.get_exception_ptr() ); } + catch ( my_exception const&) + { thrown = true; } + BOOST_CHECK( thrown); +} + +void test_packaged_task_exception_void() { + // promise takes a copyable as return type + boost::fibers::packaged_task< void() > t1( fn6); + BOOST_CHECK( t1.valid() ); + boost::fibers::future< void > f1 = t1.get_future(); + BOOST_CHECK( f1.valid() ); + + // set void + t1(); + bool thrown = false; + try { + f1.get(); + } catch ( my_exception const&) { + thrown = true; + } + BOOST_CHECK( thrown); + + boost::fibers::packaged_task< void() > t2( fn6); + BOOST_CHECK( t2.valid() ); + boost::fibers::future< void > f2 = t2.get_future(); + BOOST_CHECK( f2.valid() ); + + // exec + t2(); + BOOST_CHECK( f2.get_exception_ptr() ); + thrown = false; + try { + std::rethrow_exception( f2.get_exception_ptr() ); + } catch ( my_exception const&) { + thrown = true; + } + BOOST_CHECK( thrown); +} + + +boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) { + boost::unit_test_framework::test_suite* test = + BOOST_TEST_SUITE("Boost.Fiber: packaged_task test suite"); + + test->add(BOOST_TEST_CASE(test_packaged_task_create)); + test->add(BOOST_TEST_CASE(test_packaged_task_create_move)); + test->add(BOOST_TEST_CASE(test_packaged_task_create_void)); + test->add(BOOST_TEST_CASE(test_packaged_task_move)); + test->add(BOOST_TEST_CASE(test_packaged_task_move_move)); + test->add(BOOST_TEST_CASE(test_packaged_task_move_void)); + test->add(BOOST_TEST_CASE(test_packaged_task_swap)); + test->add(BOOST_TEST_CASE(test_packaged_task_swap_move)); + test->add(BOOST_TEST_CASE(test_packaged_task_swap_void)); + test->add(BOOST_TEST_CASE(test_packaged_task_reset)); + test->add(BOOST_TEST_CASE(test_packaged_task_reset_destruction)); + test->add(BOOST_TEST_CASE(test_packaged_task_reset_move)); + test->add(BOOST_TEST_CASE(test_packaged_task_reset_void)); + test->add(BOOST_TEST_CASE(test_packaged_task_get_future)); + test->add(BOOST_TEST_CASE(test_packaged_task_get_future_move)); + test->add(BOOST_TEST_CASE(test_packaged_task_get_future_void)); + test->add(BOOST_TEST_CASE(test_packaged_task_exec)); + test->add(BOOST_TEST_CASE(test_packaged_task_exec_move)); + test->add(BOOST_TEST_CASE(test_packaged_task_exec_param)); + test->add(BOOST_TEST_CASE(test_packaged_task_exec_ref)); + test->add(BOOST_TEST_CASE(test_packaged_task_exec_void)); + test->add(BOOST_TEST_CASE(test_packaged_task_exception)); + test->add(BOOST_TEST_CASE(test_packaged_task_exception_move)); + test->add(BOOST_TEST_CASE(test_packaged_task_exception_void)); + + return test; +} diff --git a/src/boost/libs/fiber/test/test_packaged_task_post.cpp b/src/boost/libs/fiber/test/test_packaged_task_post.cpp new file mode 100644 index 00000000..53c42d85 --- /dev/null +++ b/src/boost/libs/fiber/test/test_packaged_task_post.cpp @@ -0,0 +1,679 @@ +// (C) Copyright 2008-10 Anthony Williams +// +// 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 <utility> +#include <memory> +#include <stdexcept> +#include <string> + +#include <boost/test/unit_test.hpp> + +#include <boost/fiber/all.hpp> + +int gi = 7; + +struct my_exception : public std::runtime_error { + my_exception() : + std::runtime_error("my_exception") { + } +}; + +struct A { + A() = default; + + A( A const&) = delete; + A( A &&) = default; + + A & operator=( A const&) = delete; + A & operator=( A &&) = default; + + int value; +}; + +struct B { + bool bset{ false }; + + B() = default; + + B( bool set) : + bset{ set } { + gi = 3; + } + + ~B() { + if ( bset) { + gi = -1; + } + } + + B( B && other) : + bset{ other.bset } { + other.bset = false; + } + + B & operator=( B && other) { + if ( this == & other) return * this; + bset = other.bset; + other.bset = false; + return * this; + } + + B( B const&) = delete; + B & operator=( B const&) = delete; +}; + +void fn1( boost::fibers::promise< int > * p, int i) { + boost::this_fiber::yield(); + p->set_value( i); +} + +void fn2() { + boost::fibers::promise< int > p; + boost::fibers::future< int > f( p.get_future() ); + boost::this_fiber::yield(); + boost::fibers::fiber( boost::fibers::launch::post, fn1, & p, 7).detach(); + boost::this_fiber::yield(); + BOOST_CHECK( 7 == f.get() ); +} + +int fn3() { + return 3; +} + +void fn4() { +} + +int fn5() { + boost::throw_exception( my_exception() ); + return 3; +} + +void fn6() { + boost::throw_exception( my_exception() ); +} + +int & fn7() { + return gi; +} + +int fn8( int i) { + return i; +} + +A fn9() { + A a; + a.value = 3; + return a; +} + +A fn10() { + boost::throw_exception( my_exception() ); + return A(); +} + +B fn11( bool set) { + B b( set); + return b; +} + +// packaged_task +void test_packaged_task_create() { + // default constructed packaged_task is not valid + boost::fibers::packaged_task< int() > t1; + BOOST_CHECK( ! t1.valid() ); + + // packaged_task from function + boost::fibers::packaged_task< int() > t2( fn3); + BOOST_CHECK( t2.valid() ); +} + +// packaged_task +void test_packaged_task_create_move() { + // default constructed packaged_task is not valid + boost::fibers::packaged_task< A() > t1; + BOOST_CHECK( ! t1.valid() ); + + // packaged_task from function + boost::fibers::packaged_task< A() > t2( fn9); + BOOST_CHECK( t2.valid() ); +} + +void test_packaged_task_create_void() { + // default constructed packaged_task is not valid + boost::fibers::packaged_task< void() > t1; + BOOST_CHECK( ! t1.valid() ); + + // packaged_task from function + boost::fibers::packaged_task< void() > t2( fn4); + BOOST_CHECK( t2.valid() ); +} + +void test_packaged_task_move() { + boost::fibers::packaged_task< int() > t1( fn3); + BOOST_CHECK( t1.valid() ); + + // move construction + boost::fibers::packaged_task< int() > t2( std::move( t1) ); + BOOST_CHECK( ! t1.valid() ); + BOOST_CHECK( t2.valid() ); + + // move assignment + t1 = std::move( t2); + BOOST_CHECK( t1.valid() ); + BOOST_CHECK( ! t2.valid() ); +} + +void test_packaged_task_move_move() { + boost::fibers::packaged_task< A() > t1( fn9); + BOOST_CHECK( t1.valid() ); + + // move construction + boost::fibers::packaged_task< A() > t2( std::move( t1) ); + BOOST_CHECK( ! t1.valid() ); + BOOST_CHECK( t2.valid() ); + + // move assignment + t1 = std::move( t2); + BOOST_CHECK( t1.valid() ); + BOOST_CHECK( ! t2.valid() ); +} + +void test_packaged_task_move_void() { + boost::fibers::packaged_task< void() > t1( fn4); + BOOST_CHECK( t1.valid() ); + + // move construction + boost::fibers::packaged_task< void() > t2( std::move( t1) ); + BOOST_CHECK( ! t1.valid() ); + BOOST_CHECK( t2.valid() ); + + // move assignment + t1 = std::move( t2); + BOOST_CHECK( t1.valid() ); + BOOST_CHECK( ! t2.valid() ); +} + +void test_packaged_task_swap() { + boost::fibers::packaged_task< int() > t1( fn3); + BOOST_CHECK( t1.valid() ); + + boost::fibers::packaged_task< int() > t2; + BOOST_CHECK( ! t2.valid() ); + + // swap + t1.swap( t2); + BOOST_CHECK( ! t1.valid() ); + BOOST_CHECK( t2.valid() ); +} + +void test_packaged_task_swap_move() { + boost::fibers::packaged_task< A() > t1( fn9); + BOOST_CHECK( t1.valid() ); + + boost::fibers::packaged_task< A() > t2; + BOOST_CHECK( ! t2.valid() ); + + // swap + t1.swap( t2); + BOOST_CHECK( ! t1.valid() ); + BOOST_CHECK( t2.valid() ); +} + +void test_packaged_task_swap_void() { + boost::fibers::packaged_task< void() > t1( fn4); + BOOST_CHECK( t1.valid() ); + + boost::fibers::packaged_task< void() > t2; + BOOST_CHECK( ! t2.valid() ); + + // swap + t1.swap( t2); + BOOST_CHECK( ! t1.valid() ); + BOOST_CHECK( t2.valid() ); +} + +void test_packaged_task_reset() { + { + boost::fibers::packaged_task< int() > p( fn3); + boost::fibers::future< int > f( p.get_future() ); + BOOST_CHECK( p.valid() ); + + p(); + BOOST_CHECK( 3 == f.get() ); + + // reset + p.reset(); + p(); + f = p.get_future(); + BOOST_CHECK( 3 == f.get() ); + } + { + boost::fibers::packaged_task< int() > p; + + bool thrown = false; + try { + p.reset(); + } catch ( boost::fibers::packaged_task_uninitialized const&) { + thrown = true; + } + BOOST_CHECK( thrown); + } +} + +void test_packaged_task_reset_destruction() { + gi = 0; + boost::fibers::packaged_task< B( bool) > p( fn11); + BOOST_CHECK( p.valid() ); + + BOOST_CHECK( 0 == gi); + p( true); + BOOST_CHECK( 3 == gi); + + // reset + p.reset(); + BOOST_CHECK( -1 == gi); + p( false); + BOOST_CHECK( 3 == gi); + + // reset + p.reset(); + BOOST_CHECK( 3 == gi); +} + +void test_packaged_task_reset_move() { + { + boost::fibers::packaged_task< A() > p( fn9); + boost::fibers::future< A > f( p.get_future() ); + BOOST_CHECK( p.valid() ); + + p(); + BOOST_CHECK( 3 == f.get().value); + + // reset + p.reset(); + p(); + f = p.get_future(); + BOOST_CHECK( 3 == f.get().value); + } + { + boost::fibers::packaged_task< A() > p; + + bool thrown = false; + try { + p.reset(); + } catch ( boost::fibers::packaged_task_uninitialized const&) { + thrown = true; + } + BOOST_CHECK( thrown); + } +} + +void test_packaged_task_reset_void() { + { + boost::fibers::packaged_task< void() > p( fn4); + boost::fibers::future< void > f( p.get_future() ); + BOOST_CHECK( p.valid() ); + + p(); + f.get(); + + // reset + p.reset(); + p(); + f = p.get_future(); + f.get(); + } + { + boost::fibers::packaged_task< void() > p; + + bool thrown = false; + try { + p.reset(); + } catch ( boost::fibers::packaged_task_uninitialized const&) { + thrown = true; + } + BOOST_CHECK( thrown); + } +} + +void test_packaged_task_get_future() { + boost::fibers::packaged_task< int() > t1( fn3); + BOOST_CHECK( t1.valid() ); + + // retrieve future + boost::fibers::future< int > f1 = t1.get_future(); + BOOST_CHECK( f1.valid() ); + + // retrieve future a second time + bool thrown = false; + try { + f1 = t1.get_future(); + } catch ( boost::fibers::future_already_retrieved const&) { + thrown = true; + } + BOOST_CHECK( thrown); + + // move construction + boost::fibers::packaged_task< int() > t2( std::move( t1) ); + BOOST_CHECK( ! t1.valid() ); + BOOST_CHECK( t2.valid() ); + + // retrieve future from uninitialized + thrown = false; + try { + f1 = t1.get_future(); + } catch ( boost::fibers::packaged_task_uninitialized const&) { + thrown = true; + } + BOOST_CHECK( thrown); +} + +void test_packaged_task_get_future_move() { + boost::fibers::packaged_task< A() > t1( fn9); + BOOST_CHECK( t1.valid() ); + + // retrieve future + boost::fibers::future< A > f1 = t1.get_future(); + BOOST_CHECK( f1.valid() ); + + // retrieve future a second time + bool thrown = false; + try { + f1 = t1.get_future(); + } catch ( boost::fibers::future_already_retrieved const&) { + thrown = true; + } + BOOST_CHECK( thrown); + + // move construction + boost::fibers::packaged_task< A() > t2( std::move( t1) ); + BOOST_CHECK( ! t1.valid() ); + BOOST_CHECK( t2.valid() ); + + // retrieve future from uninitialized + thrown = false; + try { + f1 = t1.get_future(); + } catch ( boost::fibers::packaged_task_uninitialized const&) { + thrown = true; + } + BOOST_CHECK( thrown); +} + +void test_packaged_task_get_future_void() { + boost::fibers::packaged_task< void() > t1( fn4); + BOOST_CHECK( t1.valid() ); + + // retrieve future + boost::fibers::future< void > f1 = t1.get_future(); + BOOST_CHECK( f1.valid() ); + + // retrieve future a second time + bool thrown = false; + try { + f1 = t1.get_future(); + } catch ( boost::fibers::future_already_retrieved const&) { + thrown = true; + } + BOOST_CHECK( thrown); + + // move construction + boost::fibers::packaged_task< void() > t2( std::move( t1) ); + BOOST_CHECK( ! t1.valid() ); + BOOST_CHECK( t2.valid() ); + + // retrieve future from uninitialized + thrown = false; + try { + f1 = t1.get_future(); + } catch ( boost::fibers::packaged_task_uninitialized const&) { + thrown = true; + } + BOOST_CHECK( thrown); +} + +void test_packaged_task_exec() { + // promise takes a copyable as return type + boost::fibers::packaged_task< int() > t1( fn3); + BOOST_CHECK( t1.valid() ); + boost::fibers::future< int > f1 = t1.get_future(); + BOOST_CHECK( f1.valid() ); + + // exec + t1(); + BOOST_CHECK( 3 == f1.get() ); + + // exec a second time + bool thrown = false; + try { + t1(); + } catch ( boost::fibers::promise_already_satisfied const&) { + thrown = true; + } + BOOST_CHECK( thrown); +} + +void test_packaged_task_exec_move() { + // promise takes a copyable as return type + boost::fibers::packaged_task< A() > t1( fn9); + BOOST_CHECK( t1.valid() ); + boost::fibers::future< A > f1 = t1.get_future(); + BOOST_CHECK( f1.valid() ); + + // exec + t1(); + BOOST_CHECK( 3 == f1.get().value); + + // exec a second time + bool thrown = false; + try { + t1(); + } catch ( boost::fibers::promise_already_satisfied const&) { + thrown = true; + } + BOOST_CHECK( thrown); +} + +void test_packaged_task_exec_param() { + // promise takes a copyable as return type + boost::fibers::packaged_task< int( int) > t1( fn8); + BOOST_CHECK( t1.valid() ); + boost::fibers::future< int > f1 = t1.get_future(); + BOOST_CHECK( f1.valid() ); + + // exec + t1( 3); + BOOST_CHECK( 3 == f1.get() ); + + // exec a second time + bool thrown = false; + try { + t1( 7); + } catch ( boost::fibers::promise_already_satisfied const&) { + thrown = true; + } + BOOST_CHECK( thrown); + + //TODO: packaged_task returns a moveable-only as return type +} + +void test_packaged_task_exec_ref() { + // promise takes a copyable as return type + boost::fibers::packaged_task< int&() > t1( fn7); + BOOST_CHECK( t1.valid() ); + boost::fibers::future< int& > f1 = t1.get_future(); + BOOST_CHECK( f1.valid() ); + + // exec + t1(); + int & i = f1.get(); + BOOST_CHECK( &gi == &i); + + // exec a second time + bool thrown = false; + try { + t1(); + } catch ( boost::fibers::promise_already_satisfied const&) { + thrown = true; + } + BOOST_CHECK( thrown); + + //TODO: packaged_task returns a moveable-only as return type +} + +void test_packaged_task_exec_void() { + // promise takes a copyable as return type + boost::fibers::packaged_task< void() > t1( fn4); + BOOST_CHECK( t1.valid() ); + boost::fibers::future< void > f1 = t1.get_future(); + BOOST_CHECK( f1.valid() ); + + // set void + t1(); + f1.get(); + + // exec a second time + bool thrown = false; + try { + t1(); + } catch ( boost::fibers::promise_already_satisfied const&) { + thrown = true; + } + BOOST_CHECK( thrown); +} + +void test_packaged_task_exception() { + // promise takes a copyable as return type + boost::fibers::packaged_task< int() > t1( fn5); + BOOST_CHECK( t1.valid() ); + boost::fibers::future< int > f1 = t1.get_future(); + BOOST_CHECK( f1.valid() ); + + // exec + t1(); + bool thrown = false; + try { + f1.get(); + } catch ( my_exception const&) { + thrown = true; + } + BOOST_CHECK( thrown); + + boost::fibers::packaged_task< int() > t2( fn5); + BOOST_CHECK( t2.valid() ); + boost::fibers::future< int > f2 = t2.get_future(); + BOOST_CHECK( f2.valid() ); + + // exec + t2(); + BOOST_CHECK( f2.get_exception_ptr() ); + thrown = false; + try + { std::rethrow_exception( f2.get_exception_ptr() ); } + catch ( my_exception const&) + { thrown = true; } + BOOST_CHECK( thrown); +} + +void test_packaged_task_exception_move() { + // promise takes a moveable as return type + boost::fibers::packaged_task< A() > t1( fn10); + BOOST_CHECK( t1.valid() ); + boost::fibers::future< A > f1 = t1.get_future(); + BOOST_CHECK( f1.valid() ); + + // exec + t1(); + bool thrown = false; + try { + f1.get(); + } catch ( my_exception const&) { + thrown = true; + } + BOOST_CHECK( thrown); + + boost::fibers::packaged_task< A() > t2( fn10); + BOOST_CHECK( t2.valid() ); + boost::fibers::future< A > f2 = t2.get_future(); + BOOST_CHECK( f2.valid() ); + + // exec + t2(); + BOOST_CHECK( f2.get_exception_ptr() ); + thrown = false; + try + { std::rethrow_exception( f2.get_exception_ptr() ); } + catch ( my_exception const&) + { thrown = true; } + BOOST_CHECK( thrown); +} + +void test_packaged_task_exception_void() { + // promise takes a copyable as return type + boost::fibers::packaged_task< void() > t1( fn6); + BOOST_CHECK( t1.valid() ); + boost::fibers::future< void > f1 = t1.get_future(); + BOOST_CHECK( f1.valid() ); + + // set void + t1(); + bool thrown = false; + try { + f1.get(); + } catch ( my_exception const&) { + thrown = true; + } + BOOST_CHECK( thrown); + + boost::fibers::packaged_task< void() > t2( fn6); + BOOST_CHECK( t2.valid() ); + boost::fibers::future< void > f2 = t2.get_future(); + BOOST_CHECK( f2.valid() ); + + // exec + t2(); + BOOST_CHECK( f2.get_exception_ptr() ); + thrown = false; + try { + std::rethrow_exception( f2.get_exception_ptr() ); + } catch ( my_exception const&) { + thrown = true; + } + BOOST_CHECK( thrown); +} + + +boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) { + boost::unit_test_framework::test_suite* test = + BOOST_TEST_SUITE("Boost.Fiber: packaged_task test suite"); + + test->add(BOOST_TEST_CASE(test_packaged_task_create)); + test->add(BOOST_TEST_CASE(test_packaged_task_create_move)); + test->add(BOOST_TEST_CASE(test_packaged_task_create_void)); + test->add(BOOST_TEST_CASE(test_packaged_task_move)); + test->add(BOOST_TEST_CASE(test_packaged_task_move_move)); + test->add(BOOST_TEST_CASE(test_packaged_task_move_void)); + test->add(BOOST_TEST_CASE(test_packaged_task_swap)); + test->add(BOOST_TEST_CASE(test_packaged_task_swap_move)); + test->add(BOOST_TEST_CASE(test_packaged_task_swap_void)); + test->add(BOOST_TEST_CASE(test_packaged_task_reset)); + test->add(BOOST_TEST_CASE(test_packaged_task_reset_destruction)); + test->add(BOOST_TEST_CASE(test_packaged_task_reset_move)); + test->add(BOOST_TEST_CASE(test_packaged_task_reset_void)); + test->add(BOOST_TEST_CASE(test_packaged_task_get_future)); + test->add(BOOST_TEST_CASE(test_packaged_task_get_future_move)); + test->add(BOOST_TEST_CASE(test_packaged_task_get_future_void)); + test->add(BOOST_TEST_CASE(test_packaged_task_exec)); + test->add(BOOST_TEST_CASE(test_packaged_task_exec_move)); + test->add(BOOST_TEST_CASE(test_packaged_task_exec_param)); + test->add(BOOST_TEST_CASE(test_packaged_task_exec_ref)); + test->add(BOOST_TEST_CASE(test_packaged_task_exec_void)); + test->add(BOOST_TEST_CASE(test_packaged_task_exception)); + test->add(BOOST_TEST_CASE(test_packaged_task_exception_move)); + test->add(BOOST_TEST_CASE(test_packaged_task_exception_void)); + + return test; +} diff --git a/src/boost/libs/fiber/test/test_promise_dispatch.cpp b/src/boost/libs/fiber/test/test_promise_dispatch.cpp new file mode 100644 index 00000000..d3f90f22 --- /dev/null +++ b/src/boost/libs/fiber/test/test_promise_dispatch.cpp @@ -0,0 +1,445 @@ +// (C) Copyright 2008-10 Anthony Williams +// +// 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 <utility> +#include <memory> +#include <stdexcept> +#include <string> + +#include <boost/test/unit_test.hpp> + +#include <boost/fiber/all.hpp> + +int gi = 7; + +struct my_exception : public std::runtime_error { + my_exception() : + std::runtime_error("my_exception") { + } +}; + +struct A { + A() = default; + + A( A const&) = delete; + A( A &&) = default; + + A & operator=( A const&) = delete; + A & operator=( A &&) = default; + + int value{ 0 }; +}; + +void fn1( boost::fibers::promise< int > * p, int i) { + boost::this_fiber::yield(); + p->set_value( i); +} + +void fn2() { + boost::fibers::promise< int > p; + boost::fibers::future< int > f( p.get_future() ); + boost::this_fiber::yield(); + boost::fibers::fiber( boost::fibers::launch::dispatch, fn1, & p, 7).detach(); + boost::this_fiber::yield(); + BOOST_CHECK( 7 == f.get() ); +} + +int fn3() { + return 3; +} + +void fn4() { +} + +int fn5() { + boost::throw_exception( my_exception() ); + return 3; +} + +void fn6() { + boost::throw_exception( my_exception() ); +} + +int & fn7() { + return gi; +} + +int fn8( int i) { + return i; +} + +A fn9() { + A a; + a.value = 3; + return a; +} + +A fn10() { + boost::throw_exception( my_exception() ); + return A(); +} + +// promise +void test_promise_create() { + // use std::allocator<> as default + boost::fibers::promise< int > p1; + + // use std::allocator<> as user defined + std::allocator< boost::fibers::promise< int > > alloc; + boost::fibers::promise< int > p2( std::allocator_arg, alloc); +} + +void test_promise_create_ref() { + // use std::allocator<> as default + boost::fibers::promise< int& > p1; + + // use std::allocator<> as user defined + std::allocator< boost::fibers::promise< int& > > alloc; + boost::fibers::promise< int& > p2( std::allocator_arg, alloc); +} + +void test_promise_create_void() { + // use std::allocator<> as default + boost::fibers::promise< void > p1; + + // use std::allocator<> as user defined + std::allocator< boost::fibers::promise< void > > alloc; + boost::fibers::promise< void > p2( std::allocator_arg, alloc); +} + +void test_promise_move() { + boost::fibers::promise< int > p1; + + // move construction + boost::fibers::promise< int > p2( std::move( p1) ); + + // move assigment + p1 = std::move( p2); +} + +void test_promise_move_ref() { + boost::fibers::promise< int& > p1; + + // move construction + boost::fibers::promise< int& > p2( std::move( p1) ); + + // move assigment + p1 = std::move( p2); +} + +void test_promise_move_void() { + boost::fibers::promise< void > p1; + + // move construction + boost::fibers::promise< void > p2( std::move( p1) ); + + // move assigment + p1 = std::move( p2); +} + +void test_promise_swap() { + boost::fibers::promise< int > p1; + + // move construction + boost::fibers::promise< int > p2( std::move( p1) ); + + // swap + p1.swap( p2); +} + +void test_promise_swap_ref() { + boost::fibers::promise< int& > p1; + + // move construction + boost::fibers::promise< int& > p2( std::move( p1) ); + + // swap + p1.swap( p2); +} + +void test_promise_swap_void() { + boost::fibers::promise< void > p1; + + // move construction + boost::fibers::promise< void > p2( std::move( p1) ); + + // swap + p1.swap( p2); +} + +void test_promise_get_future() { + boost::fibers::promise< int > p1; + + // retrieve future + boost::fibers::future< int > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + + // retrieve future a second time + bool thrown = false; + try { + f1 = p1.get_future(); + } catch ( boost::fibers::future_already_retrieved const&) { + thrown = true; + } + BOOST_CHECK( thrown); + + // move construction + boost::fibers::promise< int > p2( std::move( p1) ); + + // retrieve future from uninitialized + thrown = false; + try { + f1 = p1.get_future(); + } catch ( boost::fibers::promise_uninitialized const&) { + thrown = true; + } + BOOST_CHECK( thrown); +} + +void test_promise_get_future_ref() { + boost::fibers::promise< int& > p1; + + // retrieve future + boost::fibers::future< int& > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + + // retrieve future a second time + bool thrown = false; + try { + f1 = p1.get_future(); + } catch ( boost::fibers::future_already_retrieved const&) { + thrown = true; + } + BOOST_CHECK( thrown); + + // move construction + boost::fibers::promise< int& > p2( std::move( p1) ); + + // retrieve future from uninitialized + thrown = false; + try { + f1 = p1.get_future(); + } catch ( boost::fibers::promise_uninitialized const&) { + thrown = true; + } + BOOST_CHECK( thrown); +} + +void test_promise_get_future_void() { + boost::fibers::promise< void > p1; + + // retrieve future + boost::fibers::future< void > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + + // retrieve future a second time + bool thrown = false; + try { + f1 = p1.get_future(); + } catch ( boost::fibers::future_already_retrieved const&) { + thrown = true; + } + BOOST_CHECK( thrown); + + // move construction + boost::fibers::promise< void > p2( std::move( p1) ); + + // retrieve future from uninitialized + thrown = false; + try { + f1 = p1.get_future(); + } catch ( boost::fibers::promise_uninitialized const&) { + thrown = true; + } + BOOST_CHECK( thrown); +} + +void test_promise_set_value() { + // promise takes a copyable as return type + boost::fibers::promise< int > p1; + boost::fibers::future< int > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + + // copy value + p1.set_value( 7); + BOOST_CHECK( 7 == f1.get() ); + + // set value a second time + bool thrown = false; + try { + p1.set_value( 11); + } catch ( boost::fibers::promise_already_satisfied const&) { + thrown = true; + } + BOOST_CHECK( thrown); +} + +void test_promise_set_value_move() { + // promise takes a copyable as return type + boost::fibers::promise< A > p1; + boost::fibers::future< A > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + + // move value + A a1; a1.value = 7; + p1.set_value( std::move( a1) ); + A a2 = f1.get(); + BOOST_CHECK( 7 == a2.value); + + // set value a second time + bool thrown = false; + try { + A a; + p1.set_value( std::move( a) ); + } catch ( boost::fibers::promise_already_satisfied const&) { + thrown = true; + } + BOOST_CHECK( thrown); +} + +void test_promise_set_value_ref() { + // promise takes a reference as return type + boost::fibers::promise< int& > p1; + boost::fibers::future< int& > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + + // copy value + int i = 7; + p1.set_value( i); + int & j = f1.get(); + BOOST_CHECK( &i == &j); + + // set value a second time + bool thrown = false; + try { + p1.set_value( i); + } catch ( boost::fibers::promise_already_satisfied const&) { + thrown = true; + } + BOOST_CHECK( thrown); +} + +void test_promise_set_value_void() { + // promise takes a copyable as return type + boost::fibers::promise< void > p1; + boost::fibers::future< void > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + + // set void + p1.set_value(); + f1.get(); + + // set value a second time + bool thrown = false; + try { + p1.set_value(); + } catch ( boost::fibers::promise_already_satisfied const&) { + thrown = true; + } + BOOST_CHECK( thrown); +} + +void test_promise_set_exception() { + boost::fibers::promise< int > p1; + boost::fibers::future< int > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + p1.set_exception( std::make_exception_ptr( my_exception() ) ); + + // set exception a second time + bool thrown = false; + try { + p1.set_exception( std::make_exception_ptr( my_exception() ) ); + } catch ( boost::fibers::promise_already_satisfied const&) { + thrown = true; + } + BOOST_CHECK( thrown); + + // set value + thrown = false; + try + { p1.set_value( 11); } + catch ( boost::fibers::promise_already_satisfied const&) + { thrown = true; } + BOOST_CHECK( thrown); +} + +void test_promise_set_exception_ref() { + boost::fibers::promise< int& > p1; + boost::fibers::future< int& > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + p1.set_exception( std::make_exception_ptr( my_exception() ) ); + + // set exception a second time + bool thrown = false; + try { + p1.set_exception( std::make_exception_ptr( my_exception() ) ); + } catch ( boost::fibers::promise_already_satisfied const&) { + thrown = true; + } + BOOST_CHECK( thrown); + + // set value + thrown = false; + int i = 11; + try { + p1.set_value( i); + } catch ( boost::fibers::promise_already_satisfied const&) { + thrown = true; + } + BOOST_CHECK( thrown); +} + +void test_promise_set_exception_void() { + boost::fibers::promise< void > p1; + boost::fibers::future< void > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + p1.set_exception( std::make_exception_ptr( my_exception() ) ); + + // set exception a second time + bool thrown = false; + try { + p1.set_exception( std::make_exception_ptr( my_exception() ) ); + } catch ( boost::fibers::promise_already_satisfied const&) { + thrown = true; + } + BOOST_CHECK( thrown); + + // set value + thrown = false; + try { + p1.set_value(); + } catch ( boost::fibers::promise_already_satisfied const&) { + thrown = true; + } + BOOST_CHECK( thrown); +} + +boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) { + boost::unit_test_framework::test_suite* test = + BOOST_TEST_SUITE("Boost.Fiber: promise test suite"); + + test->add(BOOST_TEST_CASE(test_promise_create)); + test->add(BOOST_TEST_CASE(test_promise_create_ref)); + test->add(BOOST_TEST_CASE(test_promise_create_void)); + test->add(BOOST_TEST_CASE(test_promise_move)); + test->add(BOOST_TEST_CASE(test_promise_move_ref)); + test->add(BOOST_TEST_CASE(test_promise_move_void)); + test->add(BOOST_TEST_CASE(test_promise_swap)); + test->add(BOOST_TEST_CASE(test_promise_swap_ref)); + test->add(BOOST_TEST_CASE(test_promise_swap_void)); + test->add(BOOST_TEST_CASE(test_promise_get_future)); + test->add(BOOST_TEST_CASE(test_promise_get_future_ref)); + test->add(BOOST_TEST_CASE(test_promise_get_future_void)); + test->add(BOOST_TEST_CASE(test_promise_set_value)); + test->add(BOOST_TEST_CASE(test_promise_set_value_move)); + test->add(BOOST_TEST_CASE(test_promise_set_value_ref)); + test->add(BOOST_TEST_CASE(test_promise_set_value_void)); + test->add(BOOST_TEST_CASE(test_promise_set_exception)); + test->add(BOOST_TEST_CASE(test_promise_set_exception_ref)); + test->add(BOOST_TEST_CASE(test_promise_set_exception_void)); + + return test; +} diff --git a/src/boost/libs/fiber/test/test_promise_post.cpp b/src/boost/libs/fiber/test/test_promise_post.cpp new file mode 100644 index 00000000..f14936f7 --- /dev/null +++ b/src/boost/libs/fiber/test/test_promise_post.cpp @@ -0,0 +1,445 @@ +// (C) Copyright 2008-10 Anthony Williams +// +// 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 <utility> +#include <memory> +#include <stdexcept> +#include <string> + +#include <boost/test/unit_test.hpp> + +#include <boost/fiber/all.hpp> + +int gi = 7; + +struct my_exception : public std::runtime_error { + my_exception() : + std::runtime_error("my_exception") { + } +}; + +struct A { + A() = default; + + A( A const&) = delete; + A( A &&) = default; + + A & operator=( A const&) = delete; + A & operator=( A &&) = default; + + int value{ 0 }; +}; + +void fn1( boost::fibers::promise< int > * p, int i) { + boost::this_fiber::yield(); + p->set_value( i); +} + +void fn2() { + boost::fibers::promise< int > p; + boost::fibers::future< int > f( p.get_future() ); + boost::this_fiber::yield(); + boost::fibers::fiber( boost::fibers::launch::post, fn1, & p, 7).detach(); + boost::this_fiber::yield(); + BOOST_CHECK( 7 == f.get() ); +} + +int fn3() { + return 3; +} + +void fn4() { +} + +int fn5() { + boost::throw_exception( my_exception() ); + return 3; +} + +void fn6() { + boost::throw_exception( my_exception() ); +} + +int & fn7() { + return gi; +} + +int fn8( int i) { + return i; +} + +A fn9() { + A a; + a.value = 3; + return a; +} + +A fn10() { + boost::throw_exception( my_exception() ); + return A(); +} + +// promise +void test_promise_create() { + // use std::allocator<> as default + boost::fibers::promise< int > p1; + + // use std::allocator<> as user defined + std::allocator< boost::fibers::promise< int > > alloc; + boost::fibers::promise< int > p2( std::allocator_arg, alloc); +} + +void test_promise_create_ref() { + // use std::allocator<> as default + boost::fibers::promise< int& > p1; + + // use std::allocator<> as user defined + std::allocator< boost::fibers::promise< int& > > alloc; + boost::fibers::promise< int& > p2( std::allocator_arg, alloc); +} + +void test_promise_create_void() { + // use std::allocator<> as default + boost::fibers::promise< void > p1; + + // use std::allocator<> as user defined + std::allocator< boost::fibers::promise< void > > alloc; + boost::fibers::promise< void > p2( std::allocator_arg, alloc); +} + +void test_promise_move() { + boost::fibers::promise< int > p1; + + // move construction + boost::fibers::promise< int > p2( std::move( p1) ); + + // move assigment + p1 = std::move( p2); +} + +void test_promise_move_ref() { + boost::fibers::promise< int& > p1; + + // move construction + boost::fibers::promise< int& > p2( std::move( p1) ); + + // move assigment + p1 = std::move( p2); +} + +void test_promise_move_void() { + boost::fibers::promise< void > p1; + + // move construction + boost::fibers::promise< void > p2( std::move( p1) ); + + // move assigment + p1 = std::move( p2); +} + +void test_promise_swap() { + boost::fibers::promise< int > p1; + + // move construction + boost::fibers::promise< int > p2( std::move( p1) ); + + // swap + p1.swap( p2); +} + +void test_promise_swap_ref() { + boost::fibers::promise< int& > p1; + + // move construction + boost::fibers::promise< int& > p2( std::move( p1) ); + + // swap + p1.swap( p2); +} + +void test_promise_swap_void() { + boost::fibers::promise< void > p1; + + // move construction + boost::fibers::promise< void > p2( std::move( p1) ); + + // swap + p1.swap( p2); +} + +void test_promise_get_future() { + boost::fibers::promise< int > p1; + + // retrieve future + boost::fibers::future< int > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + + // retrieve future a second time + bool thrown = false; + try { + f1 = p1.get_future(); + } catch ( boost::fibers::future_already_retrieved const&) { + thrown = true; + } + BOOST_CHECK( thrown); + + // move construction + boost::fibers::promise< int > p2( std::move( p1) ); + + // retrieve future from uninitialized + thrown = false; + try { + f1 = p1.get_future(); + } catch ( boost::fibers::promise_uninitialized const&) { + thrown = true; + } + BOOST_CHECK( thrown); +} + +void test_promise_get_future_ref() { + boost::fibers::promise< int& > p1; + + // retrieve future + boost::fibers::future< int& > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + + // retrieve future a second time + bool thrown = false; + try { + f1 = p1.get_future(); + } catch ( boost::fibers::future_already_retrieved const&) { + thrown = true; + } + BOOST_CHECK( thrown); + + // move construction + boost::fibers::promise< int& > p2( std::move( p1) ); + + // retrieve future from uninitialized + thrown = false; + try { + f1 = p1.get_future(); + } catch ( boost::fibers::promise_uninitialized const&) { + thrown = true; + } + BOOST_CHECK( thrown); +} + +void test_promise_get_future_void() { + boost::fibers::promise< void > p1; + + // retrieve future + boost::fibers::future< void > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + + // retrieve future a second time + bool thrown = false; + try { + f1 = p1.get_future(); + } catch ( boost::fibers::future_already_retrieved const&) { + thrown = true; + } + BOOST_CHECK( thrown); + + // move construction + boost::fibers::promise< void > p2( std::move( p1) ); + + // retrieve future from uninitialized + thrown = false; + try { + f1 = p1.get_future(); + } catch ( boost::fibers::promise_uninitialized const&) { + thrown = true; + } + BOOST_CHECK( thrown); +} + +void test_promise_set_value() { + // promise takes a copyable as return type + boost::fibers::promise< int > p1; + boost::fibers::future< int > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + + // copy value + p1.set_value( 7); + BOOST_CHECK( 7 == f1.get() ); + + // set value a second time + bool thrown = false; + try { + p1.set_value( 11); + } catch ( boost::fibers::promise_already_satisfied const&) { + thrown = true; + } + BOOST_CHECK( thrown); +} + +void test_promise_set_value_move() { + // promise takes a copyable as return type + boost::fibers::promise< A > p1; + boost::fibers::future< A > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + + // move value + A a1; a1.value = 7; + p1.set_value( std::move( a1) ); + A a2 = f1.get(); + BOOST_CHECK( 7 == a2.value); + + // set value a second time + bool thrown = false; + try { + A a; + p1.set_value( std::move( a) ); + } catch ( boost::fibers::promise_already_satisfied const&) { + thrown = true; + } + BOOST_CHECK( thrown); +} + +void test_promise_set_value_ref() { + // promise takes a reference as return type + boost::fibers::promise< int& > p1; + boost::fibers::future< int& > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + + // copy value + int i = 7; + p1.set_value( i); + int & j = f1.get(); + BOOST_CHECK( &i == &j); + + // set value a second time + bool thrown = false; + try { + p1.set_value( i); + } catch ( boost::fibers::promise_already_satisfied const&) { + thrown = true; + } + BOOST_CHECK( thrown); +} + +void test_promise_set_value_void() { + // promise takes a copyable as return type + boost::fibers::promise< void > p1; + boost::fibers::future< void > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + + // set void + p1.set_value(); + f1.get(); + + // set value a second time + bool thrown = false; + try { + p1.set_value(); + } catch ( boost::fibers::promise_already_satisfied const&) { + thrown = true; + } + BOOST_CHECK( thrown); +} + +void test_promise_set_exception() { + boost::fibers::promise< int > p1; + boost::fibers::future< int > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + p1.set_exception( std::make_exception_ptr( my_exception() ) ); + + // set exception a second time + bool thrown = false; + try { + p1.set_exception( std::make_exception_ptr( my_exception() ) ); + } catch ( boost::fibers::promise_already_satisfied const&) { + thrown = true; + } + BOOST_CHECK( thrown); + + // set value + thrown = false; + try + { p1.set_value( 11); } + catch ( boost::fibers::promise_already_satisfied const&) + { thrown = true; } + BOOST_CHECK( thrown); +} + +void test_promise_set_exception_ref() { + boost::fibers::promise< int& > p1; + boost::fibers::future< int& > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + p1.set_exception( std::make_exception_ptr( my_exception() ) ); + + // set exception a second time + bool thrown = false; + try { + p1.set_exception( std::make_exception_ptr( my_exception() ) ); + } catch ( boost::fibers::promise_already_satisfied const&) { + thrown = true; + } + BOOST_CHECK( thrown); + + // set value + thrown = false; + int i = 11; + try { + p1.set_value( i); + } catch ( boost::fibers::promise_already_satisfied const&) { + thrown = true; + } + BOOST_CHECK( thrown); +} + +void test_promise_set_exception_void() { + boost::fibers::promise< void > p1; + boost::fibers::future< void > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + p1.set_exception( std::make_exception_ptr( my_exception() ) ); + + // set exception a second time + bool thrown = false; + try { + p1.set_exception( std::make_exception_ptr( my_exception() ) ); + } catch ( boost::fibers::promise_already_satisfied const&) { + thrown = true; + } + BOOST_CHECK( thrown); + + // set value + thrown = false; + try { + p1.set_value(); + } catch ( boost::fibers::promise_already_satisfied const&) { + thrown = true; + } + BOOST_CHECK( thrown); +} + +boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) { + boost::unit_test_framework::test_suite* test = + BOOST_TEST_SUITE("Boost.Fiber: promise test suite"); + + test->add(BOOST_TEST_CASE(test_promise_create)); + test->add(BOOST_TEST_CASE(test_promise_create_ref)); + test->add(BOOST_TEST_CASE(test_promise_create_void)); + test->add(BOOST_TEST_CASE(test_promise_move)); + test->add(BOOST_TEST_CASE(test_promise_move_ref)); + test->add(BOOST_TEST_CASE(test_promise_move_void)); + test->add(BOOST_TEST_CASE(test_promise_swap)); + test->add(BOOST_TEST_CASE(test_promise_swap_ref)); + test->add(BOOST_TEST_CASE(test_promise_swap_void)); + test->add(BOOST_TEST_CASE(test_promise_get_future)); + test->add(BOOST_TEST_CASE(test_promise_get_future_ref)); + test->add(BOOST_TEST_CASE(test_promise_get_future_void)); + test->add(BOOST_TEST_CASE(test_promise_set_value)); + test->add(BOOST_TEST_CASE(test_promise_set_value_move)); + test->add(BOOST_TEST_CASE(test_promise_set_value_ref)); + test->add(BOOST_TEST_CASE(test_promise_set_value_void)); + test->add(BOOST_TEST_CASE(test_promise_set_exception)); + test->add(BOOST_TEST_CASE(test_promise_set_exception_ref)); + test->add(BOOST_TEST_CASE(test_promise_set_exception_void)); + + return test; +} diff --git a/src/boost/libs/fiber/test/test_shared_future_dispatch.cpp b/src/boost/libs/fiber/test/test_shared_future_dispatch.cpp new file mode 100644 index 00000000..2dd327ff --- /dev/null +++ b/src/boost/libs/fiber/test/test_shared_future_dispatch.cpp @@ -0,0 +1,608 @@ +// (C) Copyright 2008-10 Anthony Williams +// 2015 Oliver Kowalke +// +// 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 <chrono> +#include <memory> +#include <stdexcept> +#include <string> +#include <utility> + +#include <boost/test/unit_test.hpp> + +#include <boost/fiber/all.hpp> + +typedef std::chrono::milliseconds ms; +typedef std::chrono::high_resolution_clock Clock; + +int gi = 7; + +struct my_exception : public std::runtime_error { + my_exception() : + std::runtime_error("my_exception") { + } +}; + +struct A { + A() = default; + + A( A const&) = delete; + A( A &&) = default; + + A & operator=( A const&) = delete; + A & operator=( A &&) = default; + + int value; +}; + +void fn1( boost::fibers::promise< int > * p, int i) { + boost::this_fiber::yield(); + p->set_value( i); +} + +void fn2() { + boost::fibers::promise< int > p; + boost::fibers::shared_future< int > f( p.get_future().share() ); + boost::this_fiber::yield(); + boost::fibers::fiber( boost::fibers::launch::dispatch, fn1, & p, 7).detach(); + boost::this_fiber::yield(); + BOOST_CHECK( 7 == f.get() ); +} + +int fn3() { + return 3; +} + +void fn4() { +} + +int fn5() { + boost::throw_exception( my_exception() ); + return 3; +} + +void fn6() { + boost::throw_exception( my_exception() ); +} + +int & fn7() { + return gi; +} + +int fn8( int i) { + return i; +} + +A fn9() { + A a; + a.value = 3; + return a; +} + +A fn10() { + boost::throw_exception( my_exception() ); + return A(); +} + +void fn11( boost::fibers::promise< int > p) { + boost::this_fiber::sleep_for( ms(500) ); + p.set_value(3); +} + +void fn12( boost::fibers::promise< int& > p) { + boost::this_fiber::sleep_for( ms(500) ); + gi = 5; + p.set_value( gi); +} + +void fn13( boost::fibers::promise< void > p) { + boost::this_fiber::sleep_for( ms(500) ); + p.set_value(); +} + +// shared_future +void test_shared_future_create() { + { + // default constructed and assigned shared_future is not valid + boost::fibers::shared_future< int > f1; + boost::fibers::shared_future< int > f2 = f1; + BOOST_CHECK( ! f1.valid() ); + BOOST_CHECK( ! f2.valid() ); + } + + { + // shared_future retrieved from promise is valid + boost::fibers::promise< int > p; + boost::fibers::shared_future< int > f1 = p.get_future(); + boost::fibers::shared_future< int > f2 = f1; + BOOST_CHECK( f1.valid() ); + BOOST_CHECK( f2.valid() ); + } +} + +void test_shared_future_create_ref() { + { + // default constructed and assigned shared_future is not valid + boost::fibers::shared_future< int& > f1; + boost::fibers::shared_future< int& > f2 = f1; + BOOST_CHECK( ! f1.valid() ); + BOOST_CHECK( ! f2.valid() ); + } + + { + // shared_future retrieved from promise is valid + boost::fibers::promise< int& > p; + boost::fibers::shared_future< int& > f1 = p.get_future(); + boost::fibers::shared_future< int& > f2 = f1; + BOOST_CHECK( f1.valid() ); + BOOST_CHECK( f2.valid() ); + } +} + +void test_shared_future_create_void() { + { + // default constructed and assigned shared_future is not valid + boost::fibers::shared_future< void > f1; + boost::fibers::shared_future< void > f2 = f1; + BOOST_CHECK( ! f1.valid() ); + BOOST_CHECK( ! f2.valid() ); + } + + { + // shared_future retrieved from promise is valid + boost::fibers::promise< void > p; + boost::fibers::shared_future< void > f1 = p.get_future(); + boost::fibers::shared_future< void > f2 = f1; + BOOST_CHECK( f1.valid() ); + BOOST_CHECK( f2.valid() ); + } +} + +void test_shared_future_get() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int > p1; + p1.set_value( 7); + + boost::fibers::shared_future< int > f1 = p1.get_future().share(); + BOOST_CHECK( f1.valid() ); + + // get + BOOST_CHECK( ! f1.get_exception_ptr() ); + BOOST_CHECK( 7 == f1.get() ); + BOOST_CHECK( f1.valid() ); + + // throw broken_promise if promise is destroyed without set + { + boost::fibers::promise< int > p2; + f1 = p2.get_future().share(); + } + bool thrown = false; + try { + f1.get(); + } catch ( boost::fibers::broken_promise const&) { + thrown = true; + } + BOOST_CHECK( f1.valid() ); + BOOST_CHECK( thrown); +} + +void test_shared_future_get_move() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< A > p1; + A a; a.value = 7; + p1.set_value( std::move( a) ); + + boost::fibers::shared_future< A > f1 = p1.get_future().share(); + BOOST_CHECK( f1.valid() ); + + // get + BOOST_CHECK( ! f1.get_exception_ptr() ); + BOOST_CHECK( 7 == f1.get().value); + BOOST_CHECK( f1.valid() ); + + // throw broken_promise if promise is destroyed without set + { + boost::fibers::promise< A > p2; + f1 = p2.get_future().share(); + } + bool thrown = false; + try { + f1.get(); + } catch ( boost::fibers::broken_promise const&) { + thrown = true; + } + BOOST_CHECK( f1.valid() ); + BOOST_CHECK( thrown); +} + +void test_shared_future_get_ref() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int& > p1; + int i = 7; + p1.set_value( i); + + boost::fibers::shared_future< int& > f1 = p1.get_future().share(); + BOOST_CHECK( f1.valid() ); + + // get + BOOST_CHECK( ! f1.get_exception_ptr() ); + int & j = f1.get(); + BOOST_CHECK( &i == &j); + BOOST_CHECK( f1.valid() ); + + // throw broken_promise if promise is destroyed without set + { + boost::fibers::promise< int& > p2; + f1 = p2.get_future().share(); + } + bool thrown = false; + try { + f1.get(); + } catch ( boost::fibers::broken_promise const&) { + thrown = true; + } + BOOST_CHECK( f1.valid() ); + BOOST_CHECK( thrown); +} + + +void test_shared_future_get_void() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< void > p1; + p1.set_value(); + + boost::fibers::shared_future< void > f1 = p1.get_future().share(); + BOOST_CHECK( f1.valid() ); + + // get + BOOST_CHECK( ! f1.get_exception_ptr() ); + f1.get(); + BOOST_CHECK( f1.valid() ); + + // throw broken_promise if promise is destroyed without set + { + boost::fibers::promise< void > p2; + f1 = p2.get_future().share(); + } + bool thrown = false; + try { + f1.get(); + } catch ( boost::fibers::broken_promise const&) { + thrown = true; + } + BOOST_CHECK( f1.valid() ); + BOOST_CHECK( thrown); +} + +void test_future_share() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int > p1; + int i = 7; + p1.set_value( i); + + boost::fibers::future< int > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + + // share + boost::fibers::shared_future< int > sf1 = f1.share(); + BOOST_CHECK( sf1.valid() ); + BOOST_CHECK( ! f1.valid() ); + + // get + BOOST_CHECK( ! sf1.get_exception_ptr() ); + int j = sf1.get(); + BOOST_CHECK_EQUAL( i, j); + BOOST_CHECK( sf1.valid() ); +} + +void test_future_share_ref() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int& > p1; + int i = 7; + p1.set_value( i); + + boost::fibers::future< int& > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + + // share + boost::fibers::shared_future< int& > sf1 = f1.share(); + BOOST_CHECK( sf1.valid() ); + BOOST_CHECK( ! f1.valid() ); + + // get + BOOST_CHECK( ! sf1.get_exception_ptr() ); + int & j = sf1.get(); + BOOST_CHECK( &i == &j); + BOOST_CHECK( sf1.valid() ); +} + +void test_future_share_void() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< void > p1; + p1.set_value(); + + boost::fibers::future< void > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + + // share + boost::fibers::shared_future< void > sf1 = f1.share(); + BOOST_CHECK( sf1.valid() ); + BOOST_CHECK( ! f1.valid() ); + + // get + BOOST_CHECK( ! sf1.get_exception_ptr() ); + sf1.get(); + BOOST_CHECK( sf1.valid() ); +} + +void test_shared_future_wait() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int > p1; + boost::fibers::shared_future< int > f1 = p1.get_future().share(); + + // wait on future + p1.set_value( 7); + f1.wait(); + BOOST_CHECK( 7 == f1.get() ); +} + +void test_shared_future_wait_ref() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int& > p1; + boost::fibers::shared_future< int& > f1 = p1.get_future().share(); + + // wait on future + int i = 7; + p1.set_value( i); + f1.wait(); + int & j = f1.get(); + BOOST_CHECK( &i == &j); +} + +void test_shared_future_wait_void() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< void > p1; + boost::fibers::shared_future< void > f1 = p1.get_future().share(); + + // wait on future + p1.set_value(); + f1.wait(); + f1.get(); + BOOST_CHECK( f1.valid() ); +} + +void test_shared_future_wait_for() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int > p1; + boost::fibers::shared_future< int > f1 = p1.get_future().share(); + + boost::fibers::fiber( boost::fibers::launch::dispatch, fn11, std::move( p1) ).detach(); + + // wait on future + BOOST_CHECK( f1.valid() ); + boost::fibers::future_status status = f1.wait_for( ms(300) ); + BOOST_CHECK( boost::fibers::future_status::timeout == status); + + BOOST_CHECK( f1.valid() ); + status = f1.wait_for( ms(300) ); + BOOST_CHECK( boost::fibers::future_status::ready == status); + + BOOST_CHECK( f1.valid() ); + f1.wait(); +} + +void test_shared_future_wait_for_ref() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int& > p1; + boost::fibers::shared_future< int& > f1 = p1.get_future().share(); + + boost::fibers::fiber( boost::fibers::launch::dispatch, fn12, std::move( p1) ).detach(); + + // wait on future + BOOST_CHECK( f1.valid() ); + boost::fibers::future_status status = f1.wait_for( ms(300) ); + BOOST_CHECK( boost::fibers::future_status::timeout == status); + + BOOST_CHECK( f1.valid() ); + status = f1.wait_for( ms(300) ); + BOOST_CHECK( boost::fibers::future_status::ready == status); + + BOOST_CHECK( f1.valid() ); + f1.wait(); +} + +void test_shared_future_wait_for_void() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< void > p1; + boost::fibers::shared_future< void > f1 = p1.get_future().share(); + + boost::fibers::fiber( boost::fibers::launch::dispatch, fn13, std::move( p1) ).detach(); + + // wait on future + BOOST_CHECK( f1.valid() ); + boost::fibers::future_status status = f1.wait_for( ms(300) ); + BOOST_CHECK( boost::fibers::future_status::timeout == status); + + BOOST_CHECK( f1.valid() ); + status = f1.wait_for( ms(300) ); + BOOST_CHECK( boost::fibers::future_status::ready == status); + + BOOST_CHECK( f1.valid() ); + f1.wait(); +} + +void test_shared_future_wait_until() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int > p1; + boost::fibers::shared_future< int > f1 = p1.get_future().share(); + + boost::fibers::fiber( boost::fibers::launch::dispatch, fn11, std::move( p1) ).detach(); + + // wait on future + BOOST_CHECK( f1.valid() ); + boost::fibers::future_status status = f1.wait_until( Clock::now() + ms(300) ); + BOOST_CHECK( boost::fibers::future_status::timeout == status); + + BOOST_CHECK( f1.valid() ); + status = f1.wait_until( Clock::now() + ms(300) ); + BOOST_CHECK( boost::fibers::future_status::ready == status); + + BOOST_CHECK( f1.valid() ); + f1.wait(); +} + +void test_shared_future_wait_until_ref() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int& > p1; + boost::fibers::shared_future< int& > f1 = p1.get_future().share(); + + boost::fibers::fiber( boost::fibers::launch::dispatch, fn12, std::move( p1) ).detach(); + + // wait on future + BOOST_CHECK( f1.valid() ); + boost::fibers::future_status status = f1.wait_until( Clock::now() + ms(300) ); + BOOST_CHECK( boost::fibers::future_status::timeout == status); + + BOOST_CHECK( f1.valid() ); + status = f1.wait_until( Clock::now() + ms(300) ); + BOOST_CHECK( boost::fibers::future_status::ready == status); + + BOOST_CHECK( f1.valid() ); + f1.wait(); +} + +void test_shared_future_wait_until_void() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< void > p1; + boost::fibers::shared_future< void > f1 = p1.get_future().share(); + + boost::fibers::fiber( boost::fibers::launch::dispatch, fn13, std::move( p1) ).detach(); + + // wait on future + BOOST_CHECK( f1.valid() ); + boost::fibers::future_status status = f1.wait_until( Clock::now() + ms(300) ); + BOOST_CHECK( boost::fibers::future_status::timeout == status); + + BOOST_CHECK( f1.valid() ); + status = f1.wait_until( Clock::now() + ms(300) ); + BOOST_CHECK( boost::fibers::future_status::ready == status); + + BOOST_CHECK( f1.valid() ); + f1.wait(); +} + +void test_shared_future_wait_with_fiber_1() { + boost::fibers::promise< int > p1; + boost::fibers::fiber( boost::fibers::launch::dispatch, fn1, & p1, 7).detach(); + + boost::fibers::shared_future< int > f1 = p1.get_future().share(); + + // wait on future + BOOST_CHECK( 7 == f1.get() ); +} + +void test_shared_future_wait_with_fiber_2() { + boost::fibers::fiber( boost::fibers::launch::dispatch, fn2).join(); +} + +void test_shared_future_move() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int > p1; + boost::fibers::shared_future< int > f1 = p1.get_future().share(); + BOOST_CHECK( f1.valid() ); + + // move construction + boost::fibers::shared_future< int > f2( std::move( f1) ); + BOOST_CHECK( ! f1.valid() ); + BOOST_CHECK( f2.valid() ); + + // move assignment + f1 = std::move( f2); + BOOST_CHECK( f1.valid() ); + BOOST_CHECK( ! f2.valid() ); +} + +void test_shared_future_move_move() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< A > p1; + boost::fibers::shared_future< A > f1 = p1.get_future().share(); + BOOST_CHECK( f1.valid() ); + + // move construction + boost::fibers::shared_future< A > f2( std::move( f1) ); + BOOST_CHECK( ! f1.valid() ); + BOOST_CHECK( f2.valid() ); + + // move assignment + f1 = std::move( f2); + BOOST_CHECK( f1.valid() ); + BOOST_CHECK( ! f2.valid() ); +} + +void test_shared_future_move_ref() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int& > p1; + boost::fibers::shared_future< int& > f1 = p1.get_future().share(); + BOOST_CHECK( f1.valid() ); + + // move construction + boost::fibers::shared_future< int& > f2( std::move( f1) ); + BOOST_CHECK( ! f1.valid() ); + BOOST_CHECK( f2.valid() ); + + // move assignment + f1 = std::move( f2); + BOOST_CHECK( f1.valid() ); + BOOST_CHECK( ! f2.valid() ); +} + +void test_shared_future_move_void() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< void > p1; + boost::fibers::shared_future< void > f1 = p1.get_future().share(); + BOOST_CHECK( f1.valid() ); + + // move construction + boost::fibers::shared_future< void > f2( std::move( f1) ); + BOOST_CHECK( ! f1.valid() ); + BOOST_CHECK( f2.valid() ); + + // move assignment + f1 = std::move( f2); + BOOST_CHECK( f1.valid() ); + BOOST_CHECK( ! f2.valid() ); +} + + +boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) { + boost::unit_test_framework::test_suite* test = + BOOST_TEST_SUITE("Boost.Fiber: shared_future test suite"); + + test->add(BOOST_TEST_CASE(test_shared_future_create)); + test->add(BOOST_TEST_CASE(test_shared_future_create_ref)); + test->add(BOOST_TEST_CASE(test_shared_future_create_void)); + test->add(BOOST_TEST_CASE(test_shared_future_move)); + test->add(BOOST_TEST_CASE(test_shared_future_move_move)); + test->add(BOOST_TEST_CASE(test_shared_future_move_ref)); + test->add(BOOST_TEST_CASE(test_shared_future_move_void)); + test->add(BOOST_TEST_CASE(test_shared_future_get)); + test->add(BOOST_TEST_CASE(test_shared_future_get_move)); + test->add(BOOST_TEST_CASE(test_shared_future_get_ref)); + test->add(BOOST_TEST_CASE(test_shared_future_get_void)); + test->add(BOOST_TEST_CASE(test_shared_future_wait)); + test->add(BOOST_TEST_CASE(test_shared_future_wait_ref)); + test->add(BOOST_TEST_CASE(test_shared_future_wait_void)); + test->add(BOOST_TEST_CASE(test_shared_future_wait_for)); + test->add(BOOST_TEST_CASE(test_shared_future_wait_for_ref)); + test->add(BOOST_TEST_CASE(test_shared_future_wait_for_void)); + test->add(BOOST_TEST_CASE(test_shared_future_wait_until)); + test->add(BOOST_TEST_CASE(test_shared_future_wait_until_ref)); + test->add(BOOST_TEST_CASE(test_shared_future_wait_until_void)); + test->add(BOOST_TEST_CASE(test_shared_future_wait_with_fiber_1)); + test->add(BOOST_TEST_CASE(test_shared_future_wait_with_fiber_2)); + + return test; +} diff --git a/src/boost/libs/fiber/test/test_shared_future_post.cpp b/src/boost/libs/fiber/test/test_shared_future_post.cpp new file mode 100644 index 00000000..2e429f31 --- /dev/null +++ b/src/boost/libs/fiber/test/test_shared_future_post.cpp @@ -0,0 +1,608 @@ +// (C) Copyright 2008-10 Anthony Williams +// 2015 Oliver Kowalke +// +// 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 <chrono> +#include <memory> +#include <stdexcept> +#include <string> +#include <utility> + +#include <boost/test/unit_test.hpp> + +#include <boost/fiber/all.hpp> + +typedef std::chrono::milliseconds ms; +typedef std::chrono::high_resolution_clock Clock; + +int gi = 7; + +struct my_exception : public std::runtime_error { + my_exception() : + std::runtime_error("my_exception") { + } +}; + +struct A { + A() = default; + + A( A const&) = delete; + A( A &&) = default; + + A & operator=( A const&) = delete; + A & operator=( A &&) = default; + + int value; +}; + +void fn1( boost::fibers::promise< int > * p, int i) { + boost::this_fiber::yield(); + p->set_value( i); +} + +void fn2() { + boost::fibers::promise< int > p; + boost::fibers::shared_future< int > f( p.get_future().share() ); + boost::this_fiber::yield(); + boost::fibers::fiber( boost::fibers::launch::post, fn1, & p, 7).detach(); + boost::this_fiber::yield(); + BOOST_CHECK( 7 == f.get() ); +} + +int fn3() { + return 3; +} + +void fn4() { +} + +int fn5() { + boost::throw_exception( my_exception() ); + return 3; +} + +void fn6() { + boost::throw_exception( my_exception() ); +} + +int & fn7() { + return gi; +} + +int fn8( int i) { + return i; +} + +A fn9() { + A a; + a.value = 3; + return a; +} + +A fn10() { + boost::throw_exception( my_exception() ); + return A(); +} + +void fn11( boost::fibers::promise< int > p) { + boost::this_fiber::sleep_for( ms(500) ); + p.set_value(3); +} + +void fn12( boost::fibers::promise< int& > p) { + boost::this_fiber::sleep_for( ms(500) ); + gi = 5; + p.set_value( gi); +} + +void fn13( boost::fibers::promise< void > p) { + boost::this_fiber::sleep_for( ms(500) ); + p.set_value(); +} + +// shared_future +void test_shared_future_create() { + { + // default constructed and assigned shared_future is not valid + boost::fibers::shared_future< int > f1; + boost::fibers::shared_future< int > f2 = f1; + BOOST_CHECK( ! f1.valid() ); + BOOST_CHECK( ! f2.valid() ); + } + + { + // shared_future retrieved from promise is valid + boost::fibers::promise< int > p; + boost::fibers::shared_future< int > f1 = p.get_future(); + boost::fibers::shared_future< int > f2 = f1; + BOOST_CHECK( f1.valid() ); + BOOST_CHECK( f2.valid() ); + } +} + +void test_shared_future_create_ref() { + { + // default constructed and assigned shared_future is not valid + boost::fibers::shared_future< int& > f1; + boost::fibers::shared_future< int& > f2 = f1; + BOOST_CHECK( ! f1.valid() ); + BOOST_CHECK( ! f2.valid() ); + } + + { + // shared_future retrieved from promise is valid + boost::fibers::promise< int& > p; + boost::fibers::shared_future< int& > f1 = p.get_future(); + boost::fibers::shared_future< int& > f2 = f1; + BOOST_CHECK( f1.valid() ); + BOOST_CHECK( f2.valid() ); + } +} + +void test_shared_future_create_void() { + { + // default constructed and assigned shared_future is not valid + boost::fibers::shared_future< void > f1; + boost::fibers::shared_future< void > f2 = f1; + BOOST_CHECK( ! f1.valid() ); + BOOST_CHECK( ! f2.valid() ); + } + + { + // shared_future retrieved from promise is valid + boost::fibers::promise< void > p; + boost::fibers::shared_future< void > f1 = p.get_future(); + boost::fibers::shared_future< void > f2 = f1; + BOOST_CHECK( f1.valid() ); + BOOST_CHECK( f2.valid() ); + } +} + +void test_shared_future_get() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int > p1; + p1.set_value( 7); + + boost::fibers::shared_future< int > f1 = p1.get_future().share(); + BOOST_CHECK( f1.valid() ); + + // get + BOOST_CHECK( ! f1.get_exception_ptr() ); + BOOST_CHECK( 7 == f1.get() ); + BOOST_CHECK( f1.valid() ); + + // throw broken_promise if promise is destroyed without set + { + boost::fibers::promise< int > p2; + f1 = p2.get_future().share(); + } + bool thrown = false; + try { + f1.get(); + } catch ( boost::fibers::broken_promise const&) { + thrown = true; + } + BOOST_CHECK( f1.valid() ); + BOOST_CHECK( thrown); +} + +void test_shared_future_get_move() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< A > p1; + A a; a.value = 7; + p1.set_value( std::move( a) ); + + boost::fibers::shared_future< A > f1 = p1.get_future().share(); + BOOST_CHECK( f1.valid() ); + + // get + BOOST_CHECK( ! f1.get_exception_ptr() ); + BOOST_CHECK( 7 == f1.get().value); + BOOST_CHECK( f1.valid() ); + + // throw broken_promise if promise is destroyed without set + { + boost::fibers::promise< A > p2; + f1 = p2.get_future().share(); + } + bool thrown = false; + try { + f1.get(); + } catch ( boost::fibers::broken_promise const&) { + thrown = true; + } + BOOST_CHECK( f1.valid() ); + BOOST_CHECK( thrown); +} + +void test_shared_future_get_ref() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int& > p1; + int i = 7; + p1.set_value( i); + + boost::fibers::shared_future< int& > f1 = p1.get_future().share(); + BOOST_CHECK( f1.valid() ); + + // get + BOOST_CHECK( ! f1.get_exception_ptr() ); + int & j = f1.get(); + BOOST_CHECK( &i == &j); + BOOST_CHECK( f1.valid() ); + + // throw broken_promise if promise is destroyed without set + { + boost::fibers::promise< int& > p2; + f1 = p2.get_future().share(); + } + bool thrown = false; + try { + f1.get(); + } catch ( boost::fibers::broken_promise const&) { + thrown = true; + } + BOOST_CHECK( f1.valid() ); + BOOST_CHECK( thrown); +} + + +void test_shared_future_get_void() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< void > p1; + p1.set_value(); + + boost::fibers::shared_future< void > f1 = p1.get_future().share(); + BOOST_CHECK( f1.valid() ); + + // get + BOOST_CHECK( ! f1.get_exception_ptr() ); + f1.get(); + BOOST_CHECK( f1.valid() ); + + // throw broken_promise if promise is destroyed without set + { + boost::fibers::promise< void > p2; + f1 = p2.get_future().share(); + } + bool thrown = false; + try { + f1.get(); + } catch ( boost::fibers::broken_promise const&) { + thrown = true; + } + BOOST_CHECK( f1.valid() ); + BOOST_CHECK( thrown); +} + +void test_future_share() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int > p1; + int i = 7; + p1.set_value( i); + + boost::fibers::future< int > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + + // share + boost::fibers::shared_future< int > sf1 = f1.share(); + BOOST_CHECK( sf1.valid() ); + BOOST_CHECK( ! f1.valid() ); + + // get + BOOST_CHECK( ! sf1.get_exception_ptr() ); + int j = sf1.get(); + BOOST_CHECK_EQUAL( i, j); + BOOST_CHECK( sf1.valid() ); +} + +void test_future_share_ref() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int& > p1; + int i = 7; + p1.set_value( i); + + boost::fibers::future< int& > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + + // share + boost::fibers::shared_future< int& > sf1 = f1.share(); + BOOST_CHECK( sf1.valid() ); + BOOST_CHECK( ! f1.valid() ); + + // get + BOOST_CHECK( ! sf1.get_exception_ptr() ); + int & j = sf1.get(); + BOOST_CHECK( &i == &j); + BOOST_CHECK( sf1.valid() ); +} + +void test_future_share_void() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< void > p1; + p1.set_value(); + + boost::fibers::future< void > f1 = p1.get_future(); + BOOST_CHECK( f1.valid() ); + + // share + boost::fibers::shared_future< void > sf1 = f1.share(); + BOOST_CHECK( sf1.valid() ); + BOOST_CHECK( ! f1.valid() ); + + // get + BOOST_CHECK( ! sf1.get_exception_ptr() ); + sf1.get(); + BOOST_CHECK( sf1.valid() ); +} + +void test_shared_future_wait() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int > p1; + boost::fibers::shared_future< int > f1 = p1.get_future().share(); + + // wait on future + p1.set_value( 7); + f1.wait(); + BOOST_CHECK( 7 == f1.get() ); +} + +void test_shared_future_wait_ref() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int& > p1; + boost::fibers::shared_future< int& > f1 = p1.get_future().share(); + + // wait on future + int i = 7; + p1.set_value( i); + f1.wait(); + int & j = f1.get(); + BOOST_CHECK( &i == &j); +} + +void test_shared_future_wait_void() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< void > p1; + boost::fibers::shared_future< void > f1 = p1.get_future().share(); + + // wait on future + p1.set_value(); + f1.wait(); + f1.get(); + BOOST_CHECK( f1.valid() ); +} + +void test_shared_future_wait_for() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int > p1; + boost::fibers::shared_future< int > f1 = p1.get_future().share(); + + boost::fibers::fiber( boost::fibers::launch::post, fn11, std::move( p1) ).detach(); + + // wait on future + BOOST_CHECK( f1.valid() ); + boost::fibers::future_status status = f1.wait_for( ms(300) ); + BOOST_CHECK( boost::fibers::future_status::timeout == status); + + BOOST_CHECK( f1.valid() ); + status = f1.wait_for( ms(300) ); + BOOST_CHECK( boost::fibers::future_status::ready == status); + + BOOST_CHECK( f1.valid() ); + f1.wait(); +} + +void test_shared_future_wait_for_ref() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int& > p1; + boost::fibers::shared_future< int& > f1 = p1.get_future().share(); + + boost::fibers::fiber( boost::fibers::launch::post, fn12, std::move( p1) ).detach(); + + // wait on future + BOOST_CHECK( f1.valid() ); + boost::fibers::future_status status = f1.wait_for( ms(300) ); + BOOST_CHECK( boost::fibers::future_status::timeout == status); + + BOOST_CHECK( f1.valid() ); + status = f1.wait_for( ms(300) ); + BOOST_CHECK( boost::fibers::future_status::ready == status); + + BOOST_CHECK( f1.valid() ); + f1.wait(); +} + +void test_shared_future_wait_for_void() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< void > p1; + boost::fibers::shared_future< void > f1 = p1.get_future().share(); + + boost::fibers::fiber( boost::fibers::launch::post, fn13, std::move( p1) ).detach(); + + // wait on future + BOOST_CHECK( f1.valid() ); + boost::fibers::future_status status = f1.wait_for( ms(300) ); + BOOST_CHECK( boost::fibers::future_status::timeout == status); + + BOOST_CHECK( f1.valid() ); + status = f1.wait_for( ms(300) ); + BOOST_CHECK( boost::fibers::future_status::ready == status); + + BOOST_CHECK( f1.valid() ); + f1.wait(); +} + +void test_shared_future_wait_until() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int > p1; + boost::fibers::shared_future< int > f1 = p1.get_future().share(); + + boost::fibers::fiber( boost::fibers::launch::post, fn11, std::move( p1) ).detach(); + + // wait on future + BOOST_CHECK( f1.valid() ); + boost::fibers::future_status status = f1.wait_until( Clock::now() + ms(300) ); + BOOST_CHECK( boost::fibers::future_status::timeout == status); + + BOOST_CHECK( f1.valid() ); + status = f1.wait_until( Clock::now() + ms(300) ); + BOOST_CHECK( boost::fibers::future_status::ready == status); + + BOOST_CHECK( f1.valid() ); + f1.wait(); +} + +void test_shared_future_wait_until_ref() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int& > p1; + boost::fibers::shared_future< int& > f1 = p1.get_future().share(); + + boost::fibers::fiber( boost::fibers::launch::post, fn12, std::move( p1) ).detach(); + + // wait on future + BOOST_CHECK( f1.valid() ); + boost::fibers::future_status status = f1.wait_until( Clock::now() + ms(300) ); + BOOST_CHECK( boost::fibers::future_status::timeout == status); + + BOOST_CHECK( f1.valid() ); + status = f1.wait_until( Clock::now() + ms(300) ); + BOOST_CHECK( boost::fibers::future_status::ready == status); + + BOOST_CHECK( f1.valid() ); + f1.wait(); +} + +void test_shared_future_wait_until_void() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< void > p1; + boost::fibers::shared_future< void > f1 = p1.get_future().share(); + + boost::fibers::fiber( boost::fibers::launch::post, fn13, std::move( p1) ).detach(); + + // wait on future + BOOST_CHECK( f1.valid() ); + boost::fibers::future_status status = f1.wait_until( Clock::now() + ms(300) ); + BOOST_CHECK( boost::fibers::future_status::timeout == status); + + BOOST_CHECK( f1.valid() ); + status = f1.wait_until( Clock::now() + ms(300) ); + BOOST_CHECK( boost::fibers::future_status::ready == status); + + BOOST_CHECK( f1.valid() ); + f1.wait(); +} + +void test_shared_future_wait_with_fiber_1() { + boost::fibers::promise< int > p1; + boost::fibers::fiber( boost::fibers::launch::post, fn1, & p1, 7).detach(); + + boost::fibers::shared_future< int > f1 = p1.get_future().share(); + + // wait on future + BOOST_CHECK( 7 == f1.get() ); +} + +void test_shared_future_wait_with_fiber_2() { + boost::fibers::fiber( boost::fibers::launch::post, fn2).join(); +} + +void test_shared_future_move() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int > p1; + boost::fibers::shared_future< int > f1 = p1.get_future().share(); + BOOST_CHECK( f1.valid() ); + + // move construction + boost::fibers::shared_future< int > f2( std::move( f1) ); + BOOST_CHECK( ! f1.valid() ); + BOOST_CHECK( f2.valid() ); + + // move assignment + f1 = std::move( f2); + BOOST_CHECK( f1.valid() ); + BOOST_CHECK( ! f2.valid() ); +} + +void test_shared_future_move_move() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< A > p1; + boost::fibers::shared_future< A > f1 = p1.get_future().share(); + BOOST_CHECK( f1.valid() ); + + // move construction + boost::fibers::shared_future< A > f2( std::move( f1) ); + BOOST_CHECK( ! f1.valid() ); + BOOST_CHECK( f2.valid() ); + + // move assignment + f1 = std::move( f2); + BOOST_CHECK( f1.valid() ); + BOOST_CHECK( ! f2.valid() ); +} + +void test_shared_future_move_ref() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< int& > p1; + boost::fibers::shared_future< int& > f1 = p1.get_future().share(); + BOOST_CHECK( f1.valid() ); + + // move construction + boost::fibers::shared_future< int& > f2( std::move( f1) ); + BOOST_CHECK( ! f1.valid() ); + BOOST_CHECK( f2.valid() ); + + // move assignment + f1 = std::move( f2); + BOOST_CHECK( f1.valid() ); + BOOST_CHECK( ! f2.valid() ); +} + +void test_shared_future_move_void() { + // future retrieved from promise is valid (if it is the first) + boost::fibers::promise< void > p1; + boost::fibers::shared_future< void > f1 = p1.get_future().share(); + BOOST_CHECK( f1.valid() ); + + // move construction + boost::fibers::shared_future< void > f2( std::move( f1) ); + BOOST_CHECK( ! f1.valid() ); + BOOST_CHECK( f2.valid() ); + + // move assignment + f1 = std::move( f2); + BOOST_CHECK( f1.valid() ); + BOOST_CHECK( ! f2.valid() ); +} + + +boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) { + boost::unit_test_framework::test_suite* test = + BOOST_TEST_SUITE("Boost.Fiber: shared_future test suite"); + + test->add(BOOST_TEST_CASE(test_shared_future_create)); + test->add(BOOST_TEST_CASE(test_shared_future_create_ref)); + test->add(BOOST_TEST_CASE(test_shared_future_create_void)); + test->add(BOOST_TEST_CASE(test_shared_future_move)); + test->add(BOOST_TEST_CASE(test_shared_future_move_move)); + test->add(BOOST_TEST_CASE(test_shared_future_move_ref)); + test->add(BOOST_TEST_CASE(test_shared_future_move_void)); + test->add(BOOST_TEST_CASE(test_shared_future_get)); + test->add(BOOST_TEST_CASE(test_shared_future_get_move)); + test->add(BOOST_TEST_CASE(test_shared_future_get_ref)); + test->add(BOOST_TEST_CASE(test_shared_future_get_void)); + test->add(BOOST_TEST_CASE(test_shared_future_wait)); + test->add(BOOST_TEST_CASE(test_shared_future_wait_ref)); + test->add(BOOST_TEST_CASE(test_shared_future_wait_void)); + test->add(BOOST_TEST_CASE(test_shared_future_wait_for)); + test->add(BOOST_TEST_CASE(test_shared_future_wait_for_ref)); + test->add(BOOST_TEST_CASE(test_shared_future_wait_for_void)); + test->add(BOOST_TEST_CASE(test_shared_future_wait_until)); + test->add(BOOST_TEST_CASE(test_shared_future_wait_until_ref)); + test->add(BOOST_TEST_CASE(test_shared_future_wait_until_void)); + test->add(BOOST_TEST_CASE(test_shared_future_wait_with_fiber_1)); + test->add(BOOST_TEST_CASE(test_shared_future_wait_with_fiber_2)); + + return test; +} diff --git a/src/boost/libs/fiber/test/test_unbuffered_channel_dispatch.cpp b/src/boost/libs/fiber/test/test_unbuffered_channel_dispatch.cpp new file mode 100644 index 00000000..22650576 --- /dev/null +++ b/src/boost/libs/fiber/test/test_unbuffered_channel_dispatch.cpp @@ -0,0 +1,451 @@ + +// Copyright Oliver Kowalke 2013. +// 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 <chrono> +#include <sstream> +#include <string> +#include <vector> + +#include <boost/assert.hpp> +#include <boost/test/unit_test.hpp> + +#include <boost/fiber/all.hpp> + +struct moveable { + 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; + other.state = false; + value = other.value; + other.value = -1; + return * this; + } +}; + +void test_push() { + boost::fibers::unbuffered_channel< int > c; + boost::fibers::fiber f( boost::fibers::launch::dispatch, [&c](){ + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 1) ); + }); + int value = 0; + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop( value) ); + BOOST_CHECK_EQUAL( 1, value); + f.join(); +} + +void test_push_closed() { + boost::fibers::unbuffered_channel< int > c; + c.close(); + BOOST_CHECK( boost::fibers::channel_op_status::closed == c.push( 1) ); +} + + +void test_push_wait_for() { + boost::fibers::unbuffered_channel< int > c; + boost::fibers::fiber f( boost::fibers::launch::dispatch, [&c](){ + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push_wait_for( 1, std::chrono::seconds( 1) ) ); + }); + int value = 0; + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop( value) ); + BOOST_CHECK_EQUAL( 1, value); + f.join(); +} + +void test_push_wait_for_closed() { + boost::fibers::unbuffered_channel< int > c; + c.close(); + BOOST_CHECK( boost::fibers::channel_op_status::closed == c.push_wait_for( 1, std::chrono::seconds( 1) ) ); +} + +void test_push_wait_for_timeout() { + boost::fibers::unbuffered_channel< int > c; + boost::fibers::fiber f( boost::fibers::launch::dispatch, [&c](){ + int value = 0; + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop( value) ); + BOOST_CHECK_EQUAL( 1, value); + }); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push_wait_for( 1, std::chrono::seconds( 1) ) ); + BOOST_CHECK( boost::fibers::channel_op_status::timeout == c.push_wait_for( 1, std::chrono::seconds( 1) ) ); + f.join(); +} + +void test_push_wait_until() { + boost::fibers::unbuffered_channel< int > c; + boost::fibers::fiber f( boost::fibers::launch::dispatch, [&c](){ + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push_wait_until( 1, + std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); + }); + int value = 0; + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop( value) ); + BOOST_CHECK_EQUAL( 1, value); + f.join(); +} + +void test_push_wait_until_closed() { + boost::fibers::unbuffered_channel< int > c; + c.close(); + BOOST_CHECK( boost::fibers::channel_op_status::closed == c.push_wait_until( 1, + std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); +} + +void test_push_wait_until_timeout() { + boost::fibers::unbuffered_channel< int > c; + boost::fibers::fiber f( boost::fibers::launch::dispatch, [&c](){ + int value = 0; + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop( value) ); + BOOST_CHECK_EQUAL( 1, value); + }); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push_wait_until( 1, + std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); + BOOST_CHECK( boost::fibers::channel_op_status::timeout == c.push_wait_until( 1, + std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); + f.join(); +} + +void test_pop() { + boost::fibers::unbuffered_channel< int > c; + int v1 = 2, v2 = 0; + boost::fibers::fiber f( boost::fibers::launch::dispatch, [&v1,&c](){ + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + }); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop( v2) ); + BOOST_CHECK_EQUAL( v1, v2); + f.join(); +} + +void test_pop_closed() { + boost::fibers::unbuffered_channel< int > c; + int v1 = 2, v2 = 0; + boost::fibers::fiber f( boost::fibers::launch::dispatch, [&v1,&c](){ + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + c.close(); + }); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop( v2) ); + BOOST_CHECK_EQUAL( v1, v2); + BOOST_CHECK( boost::fibers::channel_op_status::closed == c.pop( v2) ); + f.join(); +} + +void test_pop_success() { + boost::fibers::unbuffered_channel< int > c; + int v1 = 2, v2 = 0; + boost::fibers::fiber f( boost::fibers::launch::dispatch, [&c,&v2](){ + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop( v2) ); + }); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + f.join(); + BOOST_CHECK_EQUAL( v1, v2); +} + +void test_value_pop() { + boost::fibers::unbuffered_channel< int > c; + int v1 = 2, v2 = 0; + boost::fibers::fiber f( boost::fibers::launch::dispatch, [&c,&v1](){ + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + }); + v2 = c.value_pop(); + f.join(); + BOOST_CHECK_EQUAL( v1, v2); +} + +void test_value_pop_closed() { + boost::fibers::unbuffered_channel< int > c; + int v1 = 2; + boost::fibers::fiber f( boost::fibers::launch::dispatch, [&c,&v1](){ + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + c.close(); + }); + int v2 = c.value_pop(); + BOOST_CHECK_EQUAL( v1, v2); + f.join(); + bool thrown = false; + try { + c.value_pop(); + } catch ( boost::fibers::fiber_error const&) { + thrown = true; + } + BOOST_CHECK( thrown); +} + +void test_value_pop_success() { + boost::fibers::unbuffered_channel< int > c; + int v1 = 2, v2 = 0; + boost::fibers::fiber f( boost::fibers::launch::dispatch, [&c,&v2](){ + v2 = c.value_pop(); + }); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + f.join(); + BOOST_CHECK_EQUAL( v1, v2); +} + +void test_pop_wait_for() { + boost::fibers::unbuffered_channel< int > c; + int v1 = 2, v2 = 0; + boost::fibers::fiber f( boost::fibers::launch::dispatch, [&c,&v1](){ + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + }); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); + f.join(); + BOOST_CHECK_EQUAL( v1, v2); +} + +void test_pop_wait_for_closed() { + boost::fibers::unbuffered_channel< int > c; + int v1 = 2, v2 = 0; + boost::fibers::fiber f( boost::fibers::launch::dispatch, [&c,&v1](){ + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + c.close(); + }); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); + BOOST_CHECK_EQUAL( v1, v2); + BOOST_CHECK( boost::fibers::channel_op_status::closed == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); + f.join(); +} + +void test_pop_wait_for_success() { + boost::fibers::unbuffered_channel< int > c; + int v1 = 2, v2 = 0; + boost::fibers::fiber f( boost::fibers::launch::dispatch, [&c,&v2](){ + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); + }); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + f.join(); + BOOST_CHECK_EQUAL( v1, v2); +} + +void test_pop_wait_for_timeout() { + boost::fibers::unbuffered_channel< int > c; + int v = 0; + boost::fibers::fiber f( boost::fibers::launch::dispatch, [&c,&v](){ + BOOST_CHECK( boost::fibers::channel_op_status::timeout == c.pop_wait_for( v, std::chrono::seconds( 1) ) ); + }); + f.join(); +} + +void test_pop_wait_until() { + boost::fibers::unbuffered_channel< int > c; + int v1 = 2, v2 = 0; + boost::fibers::fiber f( boost::fibers::launch::dispatch, [&c,&v1](){ + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + }); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_until( v2, + std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); + BOOST_CHECK_EQUAL( v1, v2); + f.join(); +} + +void test_pop_wait_until_closed() { + boost::fibers::unbuffered_channel< int > c; + int v1 = 2, v2 = 0; + boost::fibers::fiber f( boost::fibers::launch::dispatch, [&c,&v1](){ + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + c.close(); + }); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_until( v2, + std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); + BOOST_CHECK_EQUAL( v1, v2); + BOOST_CHECK( boost::fibers::channel_op_status::closed == c.pop_wait_until( v2, + std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); + f.join(); +} + +void test_pop_wait_until_success() { + boost::fibers::unbuffered_channel< int > c; + int v1 = 2, v2 = 0; + boost::fibers::fiber f( boost::fibers::launch::dispatch, [&c,&v2](){ + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_until( v2, + std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); + }); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + f.join(); + BOOST_CHECK_EQUAL( v1, v2); +} + +void test_pop_wait_until_timeout() { + boost::fibers::unbuffered_channel< int > c; + int v = 0; + BOOST_CHECK( + boost::fibers::channel_op_status::timeout == c.pop_wait_until( v, + std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); +} + +void test_wm_1() { + boost::fibers::unbuffered_channel< int > c; + std::vector< boost::fibers::fiber::id > ids; + boost::fibers::fiber f1( boost::fibers::launch::dispatch, [&c,&ids](){ + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 1) ); + + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 2) ); + + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 3) ); + + ids.push_back( boost::this_fiber::get_id() ); + // would be blocked because channel is full + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 4) ); + + ids.push_back( boost::this_fiber::get_id() ); + // would be blocked because channel is full + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 5) ); + + ids.push_back( boost::this_fiber::get_id() ); + }); + boost::fibers::fiber f2( boost::fibers::launch::dispatch, [&c,&ids](){ + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK_EQUAL( 1, c.value_pop() ); + + // let other fiber run + boost::this_fiber::yield(); + + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK_EQUAL( 2, c.value_pop() ); + + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK_EQUAL( 3, c.value_pop() ); + + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK_EQUAL( 4, c.value_pop() ); + + ids.push_back( boost::this_fiber::get_id() ); + // would block because channel is empty + BOOST_CHECK_EQUAL( 5, c.value_pop() ); + + ids.push_back( boost::this_fiber::get_id() ); + }); + boost::fibers::fiber::id id1 = f1.get_id(); + boost::fibers::fiber::id id2 = f2.get_id(); + f1.join(); + f2.join(); + BOOST_CHECK_EQUAL( 12u, ids.size() ); + BOOST_CHECK_EQUAL( id1, ids[0]); + BOOST_CHECK_EQUAL( id2, ids[1]); + BOOST_CHECK_EQUAL( id1, ids[2]); + BOOST_CHECK_EQUAL( id2, ids[3]); + BOOST_CHECK_EQUAL( id2, ids[4]); + BOOST_CHECK_EQUAL( id1, ids[5]); + BOOST_CHECK_EQUAL( id2, ids[6]); + BOOST_CHECK_EQUAL( id1, ids[7]); + BOOST_CHECK_EQUAL( id2, ids[8]); + BOOST_CHECK_EQUAL( id1, ids[9]); + BOOST_CHECK_EQUAL( id2, ids[10]); + BOOST_CHECK_EQUAL( id1, ids[11]); +} + +void test_moveable() { + boost::fibers::unbuffered_channel< moveable > c; + boost::fibers::fiber f( boost::fibers::launch::dispatch, [&c]{ + moveable m1( 3); + BOOST_CHECK( m1.state); + BOOST_CHECK_EQUAL( 3, m1.value); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( std::move( m1) ) ); + }); + moveable m2; + BOOST_CHECK( ! m2.state); + BOOST_CHECK_EQUAL( -1, m2.value); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop( m2) ); + BOOST_CHECK( m2.state); + BOOST_CHECK_EQUAL( 3, m2.value); + f.join(); +} + +void test_rangefor() { + boost::fibers::unbuffered_channel< int > chan; + std::vector< int > vec; + boost::fibers::fiber f1( boost::fibers::launch::dispatch, [&chan]{ + chan.push( 1); + chan.push( 1); + chan.push( 2); + chan.push( 3); + chan.push( 5); + chan.push( 8); + chan.push( 12); + chan.close(); + }); + boost::fibers::fiber f2( boost::fibers::launch::dispatch, [&vec,&chan]{ + for ( int value : chan) { + vec.push_back( value); + } + }); + f1.join(); + f2.join(); + BOOST_CHECK_EQUAL( 1, vec[0]); + BOOST_CHECK_EQUAL( 1, vec[1]); + BOOST_CHECK_EQUAL( 2, vec[2]); + BOOST_CHECK_EQUAL( 3, vec[3]); + BOOST_CHECK_EQUAL( 5, vec[4]); + BOOST_CHECK_EQUAL( 8, vec[5]); + BOOST_CHECK_EQUAL( 12, vec[6]); +} + +void test_issue_181() { + boost::fibers::unbuffered_channel< int > chan; + boost::fibers::fiber f1( boost::fibers::launch::dispatch, [&chan]() { + auto state = chan.push( 1); + BOOST_CHECK( boost::fibers::channel_op_status::closed == state); + }); + boost::fibers::fiber f2( boost::fibers::launch::dispatch, [&chan]() { + boost::this_fiber::sleep_for( std::chrono::milliseconds( 100) ); + chan.close(); + }); + f2.join(); + f1.join(); +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) { + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Fiber: unbuffered_channel test suite"); + + test->add( BOOST_TEST_CASE( & test_push) ); + test->add( BOOST_TEST_CASE( & test_push_closed) ); + test->add( BOOST_TEST_CASE( & test_push_wait_for) ); + test->add( BOOST_TEST_CASE( & test_push_wait_for_closed) ); + test->add( BOOST_TEST_CASE( & test_push_wait_for_timeout) ); + test->add( BOOST_TEST_CASE( & test_push_wait_until) ); + test->add( BOOST_TEST_CASE( & test_push_wait_until_closed) ); + test->add( BOOST_TEST_CASE( & test_push_wait_until_timeout) ); + test->add( BOOST_TEST_CASE( & test_pop) ); + test->add( BOOST_TEST_CASE( & test_pop_closed) ); + test->add( BOOST_TEST_CASE( & test_pop_success) ); + test->add( BOOST_TEST_CASE( & test_value_pop) ); + test->add( BOOST_TEST_CASE( & test_value_pop_closed) ); + test->add( BOOST_TEST_CASE( & test_value_pop_success) ); + test->add( BOOST_TEST_CASE( & test_pop_wait_for) ); + test->add( BOOST_TEST_CASE( & test_pop_wait_for_closed) ); + test->add( BOOST_TEST_CASE( & test_pop_wait_for_success) ); + test->add( BOOST_TEST_CASE( & test_pop_wait_for_timeout) ); + test->add( BOOST_TEST_CASE( & test_pop_wait_until) ); + test->add( BOOST_TEST_CASE( & test_pop_wait_until_closed) ); + test->add( BOOST_TEST_CASE( & test_pop_wait_until_success) ); + test->add( BOOST_TEST_CASE( & test_pop_wait_until_timeout) ); + test->add( BOOST_TEST_CASE( & test_wm_1) ); + test->add( BOOST_TEST_CASE( & test_moveable) ); + test->add( BOOST_TEST_CASE( & test_rangefor) ); + test->add( BOOST_TEST_CASE( & test_issue_181) ); + + return test; +} diff --git a/src/boost/libs/fiber/test/test_unbuffered_channel_post.cpp b/src/boost/libs/fiber/test/test_unbuffered_channel_post.cpp new file mode 100644 index 00000000..ff0f01de --- /dev/null +++ b/src/boost/libs/fiber/test/test_unbuffered_channel_post.cpp @@ -0,0 +1,451 @@ + +// Copyright Oliver Kowalke 2013. +// 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 <chrono> +#include <sstream> +#include <string> +#include <vector> + +#include <boost/assert.hpp> +#include <boost/test/unit_test.hpp> + +#include <boost/fiber/all.hpp> + +struct moveable { + 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; + other.state = false; + value = other.value; + other.value = -1; + return * this; + } +}; + +void test_push() { + boost::fibers::unbuffered_channel< int > c; + boost::fibers::fiber f( boost::fibers::launch::post, [&c](){ + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 1) ); + }); + int value = 0; + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop( value) ); + BOOST_CHECK_EQUAL( 1, value); + f.join(); +} + +void test_push_closed() { + boost::fibers::unbuffered_channel< int > c; + c.close(); + BOOST_CHECK( boost::fibers::channel_op_status::closed == c.push( 1) ); +} + + +void test_push_wait_for() { + boost::fibers::unbuffered_channel< int > c; + boost::fibers::fiber f( boost::fibers::launch::post, [&c](){ + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push_wait_for( 1, std::chrono::seconds( 1) ) ); + }); + int value = 0; + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop( value) ); + BOOST_CHECK_EQUAL( 1, value); + f.join(); +} + +void test_push_wait_for_closed() { + boost::fibers::unbuffered_channel< int > c; + c.close(); + BOOST_CHECK( boost::fibers::channel_op_status::closed == c.push_wait_for( 1, std::chrono::seconds( 1) ) ); +} + +void test_push_wait_for_timeout() { + boost::fibers::unbuffered_channel< int > c; + boost::fibers::fiber f( boost::fibers::launch::post, [&c](){ + int value = 0; + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop( value) ); + BOOST_CHECK_EQUAL( 1, value); + }); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push_wait_for( 1, std::chrono::seconds( 1) ) ); + BOOST_CHECK( boost::fibers::channel_op_status::timeout == c.push_wait_for( 1, std::chrono::seconds( 1) ) ); + f.join(); +} + +void test_push_wait_until() { + boost::fibers::unbuffered_channel< int > c; + boost::fibers::fiber f( boost::fibers::launch::post, [&c](){ + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push_wait_until( 1, + std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); + }); + int value = 0; + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop( value) ); + BOOST_CHECK_EQUAL( 1, value); + f.join(); +} + +void test_push_wait_until_closed() { + boost::fibers::unbuffered_channel< int > c; + c.close(); + BOOST_CHECK( boost::fibers::channel_op_status::closed == c.push_wait_until( 1, + std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); +} + +void test_push_wait_until_timeout() { + boost::fibers::unbuffered_channel< int > c; + boost::fibers::fiber f( boost::fibers::launch::post, [&c](){ + int value = 0; + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop( value) ); + BOOST_CHECK_EQUAL( 1, value); + }); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push_wait_until( 1, + std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); + BOOST_CHECK( boost::fibers::channel_op_status::timeout == c.push_wait_until( 1, + std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); + f.join(); +} + +void test_pop() { + boost::fibers::unbuffered_channel< int > c; + int v1 = 2, v2 = 0; + boost::fibers::fiber f( boost::fibers::launch::post, [&v1,&c](){ + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + }); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop( v2) ); + BOOST_CHECK_EQUAL( v1, v2); + f.join(); +} + +void test_pop_closed() { + boost::fibers::unbuffered_channel< int > c; + int v1 = 2, v2 = 0; + boost::fibers::fiber f( boost::fibers::launch::post, [&v1,&c](){ + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + c.close(); + }); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop( v2) ); + BOOST_CHECK_EQUAL( v1, v2); + BOOST_CHECK( boost::fibers::channel_op_status::closed == c.pop( v2) ); + f.join(); +} + +void test_pop_success() { + boost::fibers::unbuffered_channel< int > c; + int v1 = 2, v2 = 0; + boost::fibers::fiber f( boost::fibers::launch::post, [&c,&v2](){ + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop( v2) ); + }); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + f.join(); + BOOST_CHECK_EQUAL( v1, v2); +} + +void test_value_pop() { + boost::fibers::unbuffered_channel< int > c; + int v1 = 2, v2 = 0; + boost::fibers::fiber f( boost::fibers::launch::post, [&c,&v1](){ + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + }); + v2 = c.value_pop(); + f.join(); + BOOST_CHECK_EQUAL( v1, v2); +} + +void test_value_pop_closed() { + boost::fibers::unbuffered_channel< int > c; + int v1 = 2; + boost::fibers::fiber f( boost::fibers::launch::post, [&c,&v1](){ + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + c.close(); + }); + int v2 = c.value_pop(); + BOOST_CHECK_EQUAL( v1, v2); + f.join(); + bool thrown = false; + try { + c.value_pop(); + } catch ( boost::fibers::fiber_error const&) { + thrown = true; + } + BOOST_CHECK( thrown); +} + +void test_value_pop_success() { + boost::fibers::unbuffered_channel< int > c; + int v1 = 2, v2 = 0; + boost::fibers::fiber f( boost::fibers::launch::post, [&c,&v2](){ + v2 = c.value_pop(); + }); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + f.join(); + BOOST_CHECK_EQUAL( v1, v2); +} + +void test_pop_wait_for() { + boost::fibers::unbuffered_channel< int > c; + int v1 = 2, v2 = 0; + boost::fibers::fiber f( boost::fibers::launch::post, [&c,&v1](){ + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + }); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); + f.join(); + BOOST_CHECK_EQUAL( v1, v2); +} + +void test_pop_wait_for_closed() { + boost::fibers::unbuffered_channel< int > c; + int v1 = 2, v2 = 0; + boost::fibers::fiber f( boost::fibers::launch::post, [&c,&v1](){ + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + c.close(); + }); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); + BOOST_CHECK_EQUAL( v1, v2); + BOOST_CHECK( boost::fibers::channel_op_status::closed == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); + f.join(); +} + +void test_pop_wait_for_success() { + boost::fibers::unbuffered_channel< int > c; + int v1 = 2, v2 = 0; + boost::fibers::fiber f( boost::fibers::launch::post, [&c,&v2](){ + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_for( v2, std::chrono::seconds( 1) ) ); + }); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + f.join(); + BOOST_CHECK_EQUAL( v1, v2); +} + +void test_pop_wait_for_timeout() { + boost::fibers::unbuffered_channel< int > c; + int v = 0; + boost::fibers::fiber f( boost::fibers::launch::post, [&c,&v](){ + BOOST_CHECK( boost::fibers::channel_op_status::timeout == c.pop_wait_for( v, std::chrono::seconds( 1) ) ); + }); + f.join(); +} + +void test_pop_wait_until() { + boost::fibers::unbuffered_channel< int > c; + int v1 = 2, v2 = 0; + boost::fibers::fiber f( boost::fibers::launch::post, [&c,&v1](){ + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + }); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_until( v2, + std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); + BOOST_CHECK_EQUAL( v1, v2); + f.join(); +} + +void test_pop_wait_until_closed() { + boost::fibers::unbuffered_channel< int > c; + int v1 = 2, v2 = 0; + boost::fibers::fiber f( boost::fibers::launch::post, [&c,&v1](){ + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + c.close(); + }); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_until( v2, + std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); + BOOST_CHECK_EQUAL( v1, v2); + BOOST_CHECK( boost::fibers::channel_op_status::closed == c.pop_wait_until( v2, + std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); + f.join(); +} + +void test_pop_wait_until_success() { + boost::fibers::unbuffered_channel< int > c; + int v1 = 2, v2 = 0; + boost::fibers::fiber f( boost::fibers::launch::post, [&c,&v2](){ + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop_wait_until( v2, + std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); + }); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( v1) ); + f.join(); + BOOST_CHECK_EQUAL( v1, v2); +} + +void test_pop_wait_until_timeout() { + boost::fibers::unbuffered_channel< int > c; + int v = 0; + BOOST_CHECK( + boost::fibers::channel_op_status::timeout == c.pop_wait_until( v, + std::chrono::system_clock::now() + std::chrono::seconds( 1) ) ); +} + +void test_wm_1() { + boost::fibers::unbuffered_channel< int > c; + std::vector< boost::fibers::fiber::id > ids; + boost::fibers::fiber f1( boost::fibers::launch::post, [&c,&ids](){ + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 1) ); + + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 2) ); + + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 3) ); + + ids.push_back( boost::this_fiber::get_id() ); + // would be blocked because channel is full + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 4) ); + + ids.push_back( boost::this_fiber::get_id() ); + // would be blocked because channel is full + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 5) ); + + ids.push_back( boost::this_fiber::get_id() ); + }); + boost::fibers::fiber f2( boost::fibers::launch::post, [&c,&ids](){ + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK_EQUAL( 1, c.value_pop() ); + + // let other fiber run + boost::this_fiber::yield(); + + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK_EQUAL( 2, c.value_pop() ); + + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK_EQUAL( 3, c.value_pop() ); + + ids.push_back( boost::this_fiber::get_id() ); + BOOST_CHECK_EQUAL( 4, c.value_pop() ); + + ids.push_back( boost::this_fiber::get_id() ); + // would block because channel is empty + BOOST_CHECK_EQUAL( 5, c.value_pop() ); + + ids.push_back( boost::this_fiber::get_id() ); + }); + boost::fibers::fiber::id id1 = f1.get_id(); + boost::fibers::fiber::id id2 = f2.get_id(); + f1.join(); + f2.join(); + BOOST_CHECK_EQUAL( 12u, ids.size() ); + BOOST_CHECK_EQUAL( id1, ids[0]); + BOOST_CHECK_EQUAL( id2, ids[1]); + BOOST_CHECK_EQUAL( id1, ids[2]); + BOOST_CHECK_EQUAL( id2, ids[3]); + BOOST_CHECK_EQUAL( id2, ids[4]); + BOOST_CHECK_EQUAL( id1, ids[5]); + BOOST_CHECK_EQUAL( id2, ids[6]); + BOOST_CHECK_EQUAL( id1, ids[7]); + BOOST_CHECK_EQUAL( id2, ids[8]); + BOOST_CHECK_EQUAL( id1, ids[9]); + BOOST_CHECK_EQUAL( id2, ids[10]); + BOOST_CHECK_EQUAL( id1, ids[11]); +} + +void test_moveable() { + boost::fibers::unbuffered_channel< moveable > c; + boost::fibers::fiber f( boost::fibers::launch::post, [&c]{ + moveable m1( 3); + BOOST_CHECK( m1.state); + BOOST_CHECK_EQUAL( 3, m1.value); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( std::move( m1) ) ); + }); + moveable m2; + BOOST_CHECK( ! m2.state); + BOOST_CHECK_EQUAL( -1, m2.value); + BOOST_CHECK( boost::fibers::channel_op_status::success == c.pop( m2) ); + BOOST_CHECK( m2.state); + BOOST_CHECK_EQUAL( 3, m2.value); + f.join(); +} + +void test_rangefor() { + boost::fibers::unbuffered_channel< int > chan; + std::vector< int > vec; + boost::fibers::fiber f1( boost::fibers::launch::post, [&chan]{ + chan.push( 1); + chan.push( 1); + chan.push( 2); + chan.push( 3); + chan.push( 5); + chan.push( 8); + chan.push( 12); + chan.close(); + }); + boost::fibers::fiber f2( boost::fibers::launch::post, [&vec,&chan]{ + for ( int value : chan) { + vec.push_back( value); + } + }); + f1.join(); + f2.join(); + BOOST_CHECK_EQUAL( 1, vec[0]); + BOOST_CHECK_EQUAL( 1, vec[1]); + BOOST_CHECK_EQUAL( 2, vec[2]); + BOOST_CHECK_EQUAL( 3, vec[3]); + BOOST_CHECK_EQUAL( 5, vec[4]); + BOOST_CHECK_EQUAL( 8, vec[5]); + BOOST_CHECK_EQUAL( 12, vec[6]); +} + +void test_issue_181() { + boost::fibers::unbuffered_channel< int > chan; + boost::fibers::fiber f1( boost::fibers::launch::post, [&chan]() { + auto state = chan.push( 1); + BOOST_CHECK( boost::fibers::channel_op_status::closed == state); + }); + boost::fibers::fiber f2( boost::fibers::launch::post, [&chan]() { + boost::this_fiber::sleep_for( std::chrono::milliseconds( 100) ); + chan.close(); + }); + f2.join(); + f1.join(); +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) { + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Fiber: unbuffered_channel test suite"); + + test->add( BOOST_TEST_CASE( & test_push) ); + test->add( BOOST_TEST_CASE( & test_push_closed) ); + test->add( BOOST_TEST_CASE( & test_push_wait_for) ); + test->add( BOOST_TEST_CASE( & test_push_wait_for_closed) ); + test->add( BOOST_TEST_CASE( & test_push_wait_for_timeout) ); + test->add( BOOST_TEST_CASE( & test_push_wait_until) ); + test->add( BOOST_TEST_CASE( & test_push_wait_until_closed) ); + test->add( BOOST_TEST_CASE( & test_push_wait_until_timeout) ); + test->add( BOOST_TEST_CASE( & test_pop) ); + test->add( BOOST_TEST_CASE( & test_pop_closed) ); + test->add( BOOST_TEST_CASE( & test_pop_success) ); + test->add( BOOST_TEST_CASE( & test_value_pop) ); + test->add( BOOST_TEST_CASE( & test_value_pop_closed) ); + test->add( BOOST_TEST_CASE( & test_value_pop_success) ); + test->add( BOOST_TEST_CASE( & test_pop_wait_for) ); + test->add( BOOST_TEST_CASE( & test_pop_wait_for_closed) ); + test->add( BOOST_TEST_CASE( & test_pop_wait_for_success) ); + test->add( BOOST_TEST_CASE( & test_pop_wait_for_timeout) ); + test->add( BOOST_TEST_CASE( & test_pop_wait_until) ); + test->add( BOOST_TEST_CASE( & test_pop_wait_until_closed) ); + test->add( BOOST_TEST_CASE( & test_pop_wait_until_success) ); + test->add( BOOST_TEST_CASE( & test_pop_wait_until_timeout) ); + test->add( BOOST_TEST_CASE( & test_wm_1) ); + test->add( BOOST_TEST_CASE( & test_moveable) ); + test->add( BOOST_TEST_CASE( & test_rangefor) ); + test->add( BOOST_TEST_CASE( & test_issue_181) ); + + return test; +} |