From b43f288469e4337a581dab8b06c92da81f8b8fe3 Mon Sep 17 00:00:00 2001 From: Oliver Kowalke Date: Tue, 22 Sep 2015 19:17:08 +0200 Subject: [PATCH] support for futures added --- build/Jamfile.v2 | 1 + include/boost/fiber/all.hpp | 1 + include/boost/fiber/future.hpp | 10 + include/boost/fiber/future/async.hpp | 49 + .../fiber/future/detail/shared_state.hpp | 512 ++++++++ .../future/detail/shared_state_object.hpp | 54 + .../boost/fiber/future/detail/task_base.hpp | 39 + .../boost/fiber/future/detail/task_object.hpp | 109 ++ include/boost/fiber/future/future.hpp | 606 +++++++++ include/boost/fiber/future/future_status.hpp | 25 + include/boost/fiber/future/packaged_task.hpp | 141 ++ include/boost/fiber/future/promise.hpp | 308 +++++ src/future.cpp | 72 ++ test/Jamfile.v2 | 30 + test/test_future.cpp | 1152 +++++++++++++++++ test/test_future_mt.cpp | 49 + 16 files changed, 3158 insertions(+) create mode 100644 include/boost/fiber/future.hpp create mode 100644 include/boost/fiber/future/async.hpp create mode 100644 include/boost/fiber/future/detail/shared_state.hpp create mode 100644 include/boost/fiber/future/detail/shared_state_object.hpp create mode 100644 include/boost/fiber/future/detail/task_base.hpp create mode 100644 include/boost/fiber/future/detail/task_object.hpp create mode 100644 include/boost/fiber/future/future.hpp create mode 100644 include/boost/fiber/future/future_status.hpp create mode 100644 include/boost/fiber/future/packaged_task.hpp create mode 100644 include/boost/fiber/future/promise.hpp create mode 100644 src/future.cpp create mode 100644 test/test_future.cpp create mode 100644 test/test_future_mt.cpp diff --git a/build/Jamfile.v2 b/build/Jamfile.v2 index 2b087ffe..b366915b 100644 --- a/build/Jamfile.v2 +++ b/build/Jamfile.v2 @@ -32,6 +32,7 @@ lib boost_fiber context.cpp detail/spinlock.cpp fiber.cpp + future.cpp interruption.cpp mutex.cpp recursive_mutex.cpp diff --git a/include/boost/fiber/all.hpp b/include/boost/fiber/all.hpp index e7a6ac65..7ab008e6 100644 --- a/include/boost/fiber/all.hpp +++ b/include/boost/fiber/all.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include diff --git a/include/boost/fiber/future.hpp b/include/boost/fiber/future.hpp new file mode 100644 index 00000000..2e5d3b77 --- /dev/null +++ b/include/boost/fiber/future.hpp @@ -0,0 +1,10 @@ + +// 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 +#include +#include +#include diff --git a/include/boost/fiber/future/async.hpp b/include/boost/fiber/future/async.hpp new file mode 100644 index 00000000..b89fc4b3 --- /dev/null +++ b/include/boost/fiber/future/async.hpp @@ -0,0 +1,49 @@ + +// 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_ASYNC_HPP +#define BOOST_FIBERS_ASYNC_HPP + +#include +#include +#include +#include + +#include + +#include +#include + +namespace boost { +namespace fibers { + +template< typename Fn, typename ... Args > +future< typename std::result_of< Fn( Args ... ) >::type > +async( Fn && fn, Args && ... args) { + typedef typename std::result_of< Fn( Args ... ) >::type result_type; + + packaged_task< result_type( typename std::decay< Args >::type ... ) > pt( + std::forward< Fn >( fn) ); + future< result_type > f( pt.get_future() ); + fiber( std::move( pt), std::forward< Args >( args) ... ).detach(); + return f; +} + +template< typename StackAllocator, typename Fn, typename ... Args > +future< typename std::result_of< Fn( Args ... ) >::type > +async( std::allocator_arg_t, StackAllocator salloc, Fn && fn, Args && ... args) { + typedef typename std::result_of< Fn( Args ... ) >::type result_type; + + packaged_task< result_type( typename std::decay< Args >::type ... ) > pt( + std::forward< Fn >( fn) ); + future< result_type > f( pt.get_future() ); + fiber( salloc, std::move( pt), std::forward< Args >( args) ... ).detach(); + return f; +} + +}} + +#endif // BOOST_FIBERS_ASYNC_HPP diff --git a/include/boost/fiber/future/detail/shared_state.hpp b/include/boost/fiber/future/detail/shared_state.hpp new file mode 100644 index 00000000..e068279b --- /dev/null +++ b/include/boost/fiber/future/detail/shared_state.hpp @@ -0,0 +1,512 @@ + +// 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_DETAIL_SHARED_STATE_H +#define BOOST_FIBERS_DETAIL_SHARED_STATE_H + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { +namespace detail { + +template< typename R > +class shared_state { +private: + std::atomic< std::size_t > use_count_; + mutable mutex mtx_; + mutable condition waiters_; + bool ready_; + optional< R > value_; + std::exception_ptr except_; + + void mark_ready_and_notify_( std::unique_lock< mutex > & lk) { + ready_ = true; + lk.unlock(); + waiters_.notify_all(); + } + + void owner_destroyed_( std::unique_lock< mutex > & lk) { + if ( ! ready_) { + set_exception_( + std::make_exception_ptr( broken_promise() ), + lk); + } + } + + void set_value_( R const& value, std::unique_lock< mutex > & lk) { + if ( ready_) { + throw promise_already_satisfied(); + } + value_ = value; + mark_ready_and_notify_( lk); + } + + void set_value_( R && value, std::unique_lock< mutex > & lk) { + if ( ready_) { + throw promise_already_satisfied(); + } + value_ = std::move( value); + mark_ready_and_notify_( lk); + } + + void set_exception_( std::exception_ptr except, std::unique_lock< mutex > & lk) { + if ( ready_) { + throw promise_already_satisfied(); + } + except_ = except; + mark_ready_and_notify_( lk); + } + + R const& get_( std::unique_lock< mutex > & lk) { + wait_( lk); + if ( except_) { + std::rethrow_exception( except_); + } + return value_.get(); + } + + std::exception_ptr get_exception_ptr_( std::unique_lock< mutex > & lk) { + wait_( lk); + return except_; + } + + void wait_( std::unique_lock< mutex > & lk) const { + waiters_.wait( lk, [&](){ return ready_; }); + } + + template< class Rep, class Period > + future_status wait_for_( std::unique_lock< mutex > & lk, + std::chrono::duration< Rep, Period > const& timeout_duration) const { + return waiters_.wait_for( lk, timeout_duration, [&](){ return ready_; }) + ? future_status::ready + : future_status::timeout; + } + + template< typename Clock, typename Duration > + future_status wait_until_( std::unique_lock< mutex > & lk, + std::chrono::time_point< Clock, Duration > const& timeout_time) const { + return waiters_.wait_until( lk, timeout_time, [&](){ return ready_; }) + ? future_status::ready + : future_status::timeout; + } + +protected: + virtual void deallocate_future() = 0; + +public: + typedef intrusive_ptr< shared_state > ptr_t; + + shared_state() : + use_count_( 0), mtx_(), ready_( false), + value_(), except_() { + } + + virtual ~shared_state() noexcept { + } + + shared_state( shared_state const&) = delete; + shared_state & operator=( shared_state const&) = delete; + + void owner_destroyed() { + std::unique_lock< mutex > lk( mtx_); + owner_destroyed_( lk); + } + + void set_value( R const& value) { + std::unique_lock< mutex > lk( mtx_); + set_value_( value, lk); + } + + void set_value( R && value) { + std::unique_lock< mutex > lk( mtx_); + set_value_( std::move( value), lk); + } + + void set_exception( std::exception_ptr except) { + std::unique_lock< mutex > lk( mtx_); + set_exception_( except, lk); + } + + R const& get() { + std::unique_lock< mutex > lk( mtx_); + return get_( lk); + } + + std::exception_ptr get_exception_ptr() { + std::unique_lock< mutex > lk( mtx_); + return get_exception_ptr_( lk); + } + + void wait() const { + std::unique_lock< mutex > lk( mtx_); + wait_( lk); + } + + template< class Rep, class Period > + future_status wait_for( std::chrono::duration< Rep, Period > const& timeout_duration) const { + std::unique_lock< mutex > lk( mtx_); + return wait_for_( lk, timeout_duration); + } + + template< typename Clock, typename Duration > + future_status wait_until( std::chrono::time_point< Clock, Duration > const& timeout_time) const { + std::unique_lock< mutex > lk( mtx_); + return wait_until_( lk, timeout_time); + } + + void reset() { + ready_ = false; + } + + friend inline + void intrusive_ptr_add_ref( shared_state * p) noexcept { + ++p->use_count_; + } + + friend inline + void intrusive_ptr_release( shared_state * p) { + if ( 0 == --p->use_count_) { + p->deallocate_future(); + } + } +}; + +template< typename R > +class shared_state< R & > { +private: + std::atomic< std::size_t > use_count_; + mutable mutex mtx_; + mutable condition waiters_; + bool ready_; + R * value_; + std::exception_ptr except_; + + void mark_ready_and_notify_( std::unique_lock< mutex > & lk) { + ready_ = true; + lk.unlock(); + waiters_.notify_all(); + } + + void owner_destroyed_( std::unique_lock< mutex > & lk) { + if ( ! ready_) { + set_exception_( + std::make_exception_ptr( broken_promise() ), + lk); + } + } + + void set_value_( R & value, std::unique_lock< mutex > & lk) { + if ( ready_) { + throw promise_already_satisfied(); + } + value_ = & value; + mark_ready_and_notify_( lk); + } + + void set_exception_( std::exception_ptr except, std::unique_lock< mutex > & lk) { + if ( ready_) { + throw promise_already_satisfied(); + } + except_ = except; + mark_ready_and_notify_( lk); + } + + R & get_( std::unique_lock< mutex > & lk) { + wait_( lk); + if ( except_) { + std::rethrow_exception( except_); + } + return * value_; + } + + std::exception_ptr get_exception_ptr_( std::unique_lock< mutex > & lk) { + wait_( lk); + return except_; + } + + void wait_( std::unique_lock< mutex > & lk) const { + waiters_.wait( lk, [&](){ return ready_; }); + } + + template< class Rep, class Period > + future_status wait_for_( std::unique_lock< mutex > & lk, + std::chrono::duration< Rep, Period > const& timeout_duration) const { + return waiters_.wait_for( lk, timeout_duration, [&](){ return ready_; }) + ? future_status::ready + : future_status::timeout; + } + + template< typename Clock, typename Duration > + future_status wait_until_( std::unique_lock< mutex > & lk, + std::chrono::time_point< Clock, Duration > const& timeout_time) const { + return waiters_.wait_until( lk, timeout_time, [&](){ return ready_; }) + ? future_status::ready + : future_status::timeout; + } + +protected: + virtual void deallocate_future() = 0; + +public: + typedef intrusive_ptr< shared_state > ptr_t; + + shared_state() : + use_count_( 0), mtx_(), ready_( false), + value_( 0), except_() { + } + + virtual ~shared_state() noexcept { + } + + shared_state( shared_state const&) = delete; + shared_state & operator=( shared_state const&) = delete; + + void owner_destroyed() { + std::unique_lock< mutex > lk( mtx_); + owner_destroyed_( lk); + } + + void set_value( R & value) { + std::unique_lock< mutex > lk( mtx_); + set_value_( value, lk); + } + + void set_exception( std::exception_ptr except) { + std::unique_lock< mutex > lk( mtx_); + set_exception_( except, lk); + } + + R & get() { + std::unique_lock< mutex > lk( mtx_); + return get_( lk); + } + + std::exception_ptr get_exception_ptr() { + std::unique_lock< mutex > lk( mtx_); + return get_exception_ptr_( lk); + } + + void wait() const { + std::unique_lock< mutex > lk( mtx_); + wait_( lk); + } + + template< class Rep, class Period > + future_status wait_for( std::chrono::duration< Rep, Period > const& timeout_duration) const { + std::unique_lock< mutex > lk( mtx_); + return wait_for_( lk, timeout_duration); + } + + template< typename Clock, typename Duration > + future_status wait_until( std::chrono::time_point< Clock, Duration > const& timeout_time) const { + std::unique_lock< mutex > lk( mtx_); + return wait_until_( lk, timeout_time); + } + + void reset() { + ready_ = false; + } + + friend inline + void intrusive_ptr_add_ref( shared_state * p) noexcept { + ++p->use_count_; + } + + friend inline + void intrusive_ptr_release( shared_state * p) { + if ( 0 == --p->use_count_) { + p->deallocate_future(); + } + } +}; + +template<> +class shared_state< void > { +private: + std::atomic< std::size_t > use_count_; + mutable mutex mtx_; + mutable condition waiters_; + bool ready_; + std::exception_ptr except_; + + inline + void mark_ready_and_notify_( std::unique_lock< mutex > & lk) { + ready_ = true; + lk.unlock(); + waiters_.notify_all(); + } + + inline + void owner_destroyed_( std::unique_lock< mutex > & lk) { + if ( ! ready_) { + set_exception_( + std::make_exception_ptr( broken_promise() ), + lk); + } + } + + inline + void set_value_( std::unique_lock< mutex > & lk) { + if ( ready_) { + throw promise_already_satisfied(); + } + mark_ready_and_notify_( lk); + } + + inline + void set_exception_( std::exception_ptr except, std::unique_lock< mutex > & lk) { + if ( ready_) { + throw promise_already_satisfied(); + } + except_ = except; + mark_ready_and_notify_( lk); + } + + inline + void get_( std::unique_lock< mutex > & lk) { + wait_( lk); + if ( except_) { + std::rethrow_exception( except_); + } + } + + inline + std::exception_ptr get_exception_ptr_( std::unique_lock< mutex > & lk) { + wait_( lk); + return except_; + } + + inline + void wait_( std::unique_lock< mutex > & lk) const { + waiters_.wait( lk, [&](){ return ready_; }); + } + + template< class Rep, class Period > + future_status wait_for_( std::unique_lock< mutex > & lk, + std::chrono::duration< Rep, Period > const& timeout_duration) const { + return waiters_.wait_for( lk, timeout_duration, [&](){ return ready_; }) + ? future_status::ready + : future_status::timeout; + } + + template< typename Clock, typename Duration > + future_status wait_until_( std::unique_lock< mutex > & lk, + std::chrono::time_point< Clock, Duration > const& timeout_time) const { + return waiters_.wait_until( lk, timeout_time, [&](){ return ready_; }) + ? future_status::ready + : future_status::timeout; + } + +protected: + virtual void deallocate_future() = 0; + +public: + typedef intrusive_ptr< shared_state > ptr_t; + + shared_state() : + use_count_( 0), mtx_(), ready_( false), except_() { + } + + virtual ~shared_state() noexcept { + } + + shared_state( shared_state const&) = delete; + shared_state & operator=( shared_state const&) = delete; + + inline + void owner_destroyed() { + std::unique_lock< mutex > lk( mtx_); + owner_destroyed_( lk); + } + + inline + void set_value() { + std::unique_lock< mutex > lk( mtx_); + set_value_( lk); + } + + inline + void set_exception( std::exception_ptr except) { + std::unique_lock< mutex > lk( mtx_); + set_exception_( except, lk); + } + + inline + void get() { + std::unique_lock< mutex > lk( mtx_); + get_( lk); + } + + inline + std::exception_ptr get_exception_ptr() { + std::unique_lock< mutex > lk( mtx_); + return get_exception_ptr_( lk); + } + + inline + void wait() const { + std::unique_lock< mutex > lk( mtx_); + wait_( lk); + } + + template< class Rep, class Period > + future_status wait_for( std::chrono::duration< Rep, Period > const& timeout_duration) const { + std::unique_lock< mutex > lk( mtx_); + return wait_for_( lk, timeout_duration); + } + + template< typename Clock, typename Duration > + future_status wait_until( std::chrono::time_point< Clock, Duration > const& timeout_time) const { + std::unique_lock< mutex > lk( mtx_); + return wait_until_( lk, timeout_time); + } + + inline + void reset() { + ready_ = false; + } + + friend inline + void intrusive_ptr_add_ref( shared_state * p) noexcept { + ++p->use_count_; + } + + friend inline + void intrusive_ptr_release( shared_state * p) { + if ( 0 == --p->use_count_) { + p->deallocate_future(); + } + } +}; + +}}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_FIBERS_DETAIL_SHARED_STATE_H diff --git a/include/boost/fiber/future/detail/shared_state_object.hpp b/include/boost/fiber/future/detail/shared_state_object.hpp new file mode 100644 index 00000000..8def4ed7 --- /dev/null +++ b/include/boost/fiber/future/detail/shared_state_object.hpp @@ -0,0 +1,54 @@ + +// 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_DETAIL_SHARED_STATE_OBJECT_H +#define BOOST_FIBERS_DETAIL_SHARED_STATE_OBJECT_H + +#include + +#include +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { +namespace detail { + +template< typename R, typename Allocator > +class shared_state_object : public shared_state< R > { +public: + typedef typename Allocator::template rebind< + shared_state_object< R, Allocator > + >::other allocator_t; + + shared_state_object( allocator_t const& alloc) : + shared_state< R >(), alloc_( alloc) { + } + +protected: + void deallocate_future() { + destroy_( alloc_, this); + } + +private: + allocator_t alloc_; + + static void destroy_( allocator_t & alloc, shared_state_object * p) { + alloc.destroy( p); + alloc.deallocate( p, 1); + } +}; + +}}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_FIBERS_DETAIL_SHARED_STATE_OBJECT_H diff --git a/include/boost/fiber/future/detail/task_base.hpp b/include/boost/fiber/future/detail/task_base.hpp new file mode 100644 index 00000000..a5692baa --- /dev/null +++ b/include/boost/fiber/future/detail/task_base.hpp @@ -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) + +#ifndef BOOST_FIBERS_DETAIL_TASK_BASE_H +#define BOOST_FIBERS_DETAIL_TASK_BASE_H + +#include +#include + +#include +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { +namespace detail { + +template< typename R, typename ... Args > +struct task_base : public shared_state< R > { + typedef intrusive_ptr< task_base > ptr_t; + + virtual ~task_base() noexcept { + } + + virtual void run( Args && ... args) = 0; +}; + +}}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_FIBERS_DETAIL_TASK_BASE_H diff --git a/include/boost/fiber/future/detail/task_object.hpp b/include/boost/fiber/future/detail/task_object.hpp new file mode 100644 index 00000000..ac191f5b --- /dev/null +++ b/include/boost/fiber/future/detail/task_object.hpp @@ -0,0 +1,109 @@ + +// 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_DETAIL_TASK_OBJECT_H +#define BOOST_FIBERS_DETAIL_TASK_OBJECT_H + +#include +#include // std::forward() + +#include +#include + +#include +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { +namespace detail { + +template< typename Fn, typename Allocator, typename R, typename ... Args > +class task_object : public task_base< R, Args ... > { +public: + typedef typename Allocator::template rebind< + task_object< Fn, Allocator, R, Args ... > + >::other allocator_t; + + explicit task_object( allocator_t const& alloc, Fn && fn) : + task_base< R, Args ... >(), + fn_( std::forward< Fn >( fn) ), + alloc_( alloc) { + } + + void run( Args && ... args) { + try { + this->set_value( + boost::context::detail::invoke_helper( + std::move( fn_), std::make_tuple( std::forward< Args >( args) ... ) ) ); + } catch (...) { + this->set_exception( std::current_exception() ); + } + } + +protected: + void deallocate_future() { + destroy_( alloc_, this); + } + +private: + Fn fn_; + allocator_t alloc_; + + static void destroy_( allocator_t & alloc, task_object * p) { + alloc.destroy( p); + alloc.deallocate( p, 1); + } +}; + +template< typename Fn, typename Allocator, typename ... Args > +class task_object< Fn, Allocator, void, Args ... > : public task_base< void, Args ... > { +public: + typedef typename Allocator::template rebind< + task_object< Fn, Allocator, void, Args ... > + >::other allocator_t; + + explicit task_object( allocator_t const& alloc, Fn && fn) : + task_base< void, Args ... >(), + fn_( std::forward< Fn >( fn) ), + alloc_( alloc) { + } + + void run( Args && ... args) { + try { + boost::context::detail::invoke_helper( + std::move( fn_), std::make_tuple( std::forward< Args >( args) ... ) ); + this->set_value(); + } catch (...) { + this->set_exception( std::current_exception() ); + } + } + +protected: + void deallocate_future() { + destroy_( alloc_, this); + } + +private: + Fn fn_; + allocator_t alloc_; + + static void destroy_( allocator_t & alloc, task_object * p) { + alloc.destroy( p); + alloc.deallocate( p, 1); + } +}; + +}}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_FIBERS_DETAIL_TASK_OBJECT_H diff --git a/include/boost/fiber/future/future.hpp b/include/boost/fiber/future/future.hpp new file mode 100644 index 00000000..e3576c88 --- /dev/null +++ b/include/boost/fiber/future/future.hpp @@ -0,0 +1,606 @@ + +// 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_FUTURE_HPP +#define BOOST_FIBERS_FUTURE_HPP + +#include +#include +#include + +#include + +#include +#include +#include +#include + +namespace boost { +namespace fibers { + +template< typename R > +class packaged_task; + +template< typename R > +class promise; + +template< typename R > +class shared_future; + +template< typename R > +class future { +private: + typedef typename detail::shared_state< R >::ptr_t ptr_t; + + friend class shared_future< R >; + + ptr_t state_; + +public: + future() noexcept : + state_() { + } + + explicit future( ptr_t const& p) noexcept : + state_( p) { + } + + ~future() noexcept { + } + + future( future const&) = delete; + future & operator=( future const&) = delete; + + future( future< R > && other) noexcept : + state_( std::move( other.state_) ) { + } + + future & operator=( future< R > && other) noexcept { + if ( this != & other) { + state_ = std::move( other.state_); + } + return * this; + } + + bool valid() const noexcept { + return nullptr != state_.get(); + } + + shared_future< R > share(); + + R get() { + if ( ! valid() ) { + throw future_uninitialized(); + } + ptr_t tmp; + tmp.swap( state_); + return tmp->get(); + } + + std::exception_ptr get_exception_ptr() { + if ( ! valid() ) { + throw future_uninitialized(); + } + return state_->get_exception_ptr(); + } + + void wait() const { + if ( ! valid() ) { + throw future_uninitialized(); + } + state_->wait(); + } + + template< class Rep, class Period > + future_status wait_for( std::chrono::duration< Rep, Period > const& timeout_duration) const { + if ( ! valid() ) { + throw future_uninitialized(); + } + return state_->wait_for( timeout_duration); + } + + template< typename Clock, typename Duration > + future_status wait_until( std::chrono::time_point< Clock, Duration > const& timeout_time) const { + if ( ! valid() ) { + throw future_uninitialized(); + } + return state_->wait_until( timeout_time); + } +}; + +template< typename R > +class future< R & > { +private: + typedef typename detail::shared_state< R & >::ptr_t ptr_t; + + friend class shared_future< R & >; + + ptr_t state_; + +public: + future() noexcept : + state_() { + } + + explicit future( ptr_t const& p) noexcept : + state_( p) { + } + + ~future() noexcept { + } + + future( future const&) = delete; + future & operator=( future const&) = delete; + + future( future< R & > && other) noexcept : + state_( std::move( other.state_) ) { + } + + future & operator=( future< R & > && other) noexcept { + if ( this != & other) { + state_ = std::move( other.state_); + } + return * this; + } + + bool valid() const noexcept { + return nullptr != state_.get(); + } + + shared_future< R & > share(); + + R & get() { + if ( ! valid() ) { + throw future_uninitialized(); + } + ptr_t tmp; + tmp.swap( state_); + return tmp->get(); + } + + std::exception_ptr get_exception_ptr() { + if ( ! valid() ) { + throw future_uninitialized(); + } + return state_->get_exception_ptr(); + } + + void wait() const { + if ( ! valid() ) { + throw future_uninitialized(); + } + state_->wait(); + } + + template< class Rep, class Period > + future_status wait_for( std::chrono::duration< Rep, Period > const& timeout_duration) const { + if ( ! valid() ) { + throw future_uninitialized(); + } + return state_->wait_for( timeout_duration); + } + + template< typename Clock, typename Duration > + future_status wait_until( std::chrono::time_point< Clock, Duration > const& timeout_time) const { + if ( ! valid() ) { + throw future_uninitialized(); + } + return state_->wait_until( timeout_time); + } +}; + +template<> +class future< void > { +private: + typedef detail::shared_state< void >::ptr_t ptr_t; + + friend class shared_future< void >; + + ptr_t state_; + +public: + future() noexcept : + state_() { + } + + explicit future( ptr_t const& p) noexcept : + state_( p) { + } + + ~future() noexcept { + } + + future( future const&) = delete; + future & operator=( future const&) = delete; + + inline + future( future< void > && other) noexcept : + state_( std::move( other.state_) ) { + } + + inline + future & operator=( future< void > && other) noexcept { + if ( this != & other) { + state_ = std::move( other.state_); + } + return * this; + } + + inline + bool valid() const noexcept { + return nullptr != state_.get(); + } + + shared_future< void > share(); + + inline + void get() { + if ( ! valid() ) { + throw future_uninitialized(); + } + ptr_t tmp; + tmp.swap( state_); + tmp->get(); + } + + inline + std::exception_ptr get_exception_ptr() { + if ( ! valid() ) { + throw future_uninitialized(); + } + return state_->get_exception_ptr(); + } + + inline + void wait() const { + if ( ! valid() ) { + throw future_uninitialized(); + } + state_->wait(); + } + + template< class Rep, class Period > + future_status wait_for( std::chrono::duration< Rep, Period > const& timeout_duration) const { + if ( ! valid() ) { + throw future_uninitialized(); + } + return state_->wait_for( timeout_duration); + } + + template< typename Clock, typename Duration > + future_status wait_until( std::chrono::time_point< Clock, Duration > const& timeout_time) const { + if ( ! valid() ) { + throw future_uninitialized(); + } + return state_->wait_until( timeout_time); + } +}; + + +template< typename R > +class shared_future { +private: + typedef typename detail::shared_state< R >::ptr_t ptr_t; + + friend class future< R >; + + ptr_t state_; + + explicit shared_future( ptr_t const& p) noexcept : + state_( p) { + } + +public: + shared_future() noexcept : + state_() { + } + + ~shared_future() noexcept { + } + + shared_future( shared_future const& other) : + state_( other.state_) { + } + + shared_future( shared_future && other) noexcept : + state_( std::move( other.state_) ) { + } + + shared_future( future< R > && other) noexcept : + state_( std::move( other.state_) ) { + } + + shared_future & operator=( shared_future const& other) noexcept { + if ( this != & other) { + state_ = other.state_; + } + return * this; + } + + shared_future & operator=( shared_future && other) noexcept { + if ( this != & other) { + state_= std::move( other.state_); + } + return * this; + } + + shared_future & operator=( future< R > && other) noexcept { + state_ = std::move( other.state_); + return * this; + } + + bool valid() const noexcept { + return nullptr != state_.get(); + } + + R const& get() const { + if ( ! valid() ) { + throw future_uninitialized(); + } + return state_->get(); + } + + std::exception_ptr get_exception_ptr() { + if ( ! valid() ) { + throw future_uninitialized(); + } + return state_->get_exception_ptr(); + } + + void wait() const { + if ( ! valid() ) { + throw future_uninitialized(); + } + state_->wait(); + } + + template< class Rep, class Period > + future_status wait_for( std::chrono::duration< Rep, Period > const& timeout_duration) const { + if ( ! valid() ) { + throw future_uninitialized(); + } + return state_->wait_for( timeout_duration); + } + + template< typename Clock, typename Duration > + future_status wait_until( std::chrono::time_point< Clock, Duration > const& timeout_time) const { + if ( ! valid() ) { + throw future_uninitialized(); + } + return state_->wait_until( timeout_time); + } +}; + +template< typename R > +class shared_future< R & > { +private: + typedef typename detail::shared_state< R & >::ptr_t ptr_t; + + friend class future< R & >; + + ptr_t state_; + + explicit shared_future( ptr_t const& p) noexcept : + state_( p) { + } + +public: + shared_future() noexcept : + state_() { + } + + ~shared_future() noexcept { + } + + shared_future( shared_future const& other) : + state_( other.state_) { + } + + shared_future( shared_future && other) noexcept : + state_( std::move( other.state_) ) { + } + + shared_future( future< R & > && other) noexcept : + state_( std::move( other.state_) ) { + } + + shared_future & operator=( shared_future const& other) noexcept { + if ( this != & other) { + state_ = other.state_; + } + return * this; + } + + shared_future & operator=( shared_future && other) noexcept { + if ( this != & other) { + state_ = std::move( other.state_); + } + return * this; + } + + shared_future & operator=( future< R & > && other) noexcept { + state_ = std::move( other.state_); + return * this; + } + + bool valid() const noexcept { + return nullptr != state_.get(); + } + + R & get() const { + if ( ! valid() ) { + throw future_uninitialized(); + } + return state_->get(); + } + + std::exception_ptr get_exception_ptr() { + if ( ! valid() ) { + throw future_uninitialized(); + } + return state_->get_exception_ptr(); + } + + void wait() const { + if ( ! valid() ) { + throw future_uninitialized(); + } + state_->wait(); + } + + template< class Rep, class Period > + future_status wait_for( std::chrono::duration< Rep, Period > const& timeout_duration) const { + if ( ! valid() ) { + throw future_uninitialized(); + } + return state_->wait_for( timeout_duration); + } + + template< typename Clock, typename Duration > + future_status wait_until( std::chrono::time_point< Clock, Duration > const& timeout_time) const { + if ( ! valid() ) { + throw future_uninitialized(); + } + return state_->wait_until( timeout_time); + } +}; + +template<> +class shared_future< void > { +private: + typedef detail::shared_state< void >::ptr_t ptr_t; + + friend class future< void >; + + ptr_t state_; + + shared_future( ptr_t const& p) noexcept : + state_( p) { + } + +public: + shared_future() noexcept : + state_() { + } + + ~shared_future() noexcept { + } + + inline + shared_future( shared_future const& other) : + state_( other.state_) { + } + + inline + shared_future( shared_future && other) noexcept : + state_( std::move( other.state_) ) { + } + + inline + shared_future( future< void > && other) noexcept : + state_( std::move( other.state_) ) { + } + + inline + shared_future & operator=( shared_future const& other) noexcept + { + if ( this != & other) { + state_ = other.state_; + } + return * this; + } + + inline + shared_future & operator=( shared_future && other) noexcept { + if ( this != & other) { + state_ = std::move( other.state_); + } + return * this; + } + + inline + shared_future & operator=( future< void > && other) noexcept { + state_ = std::move( other.state_); + return * this; + } + + inline + bool valid() const noexcept { + return nullptr != state_.get(); + } + + inline + void get() const { + if ( ! valid() ) { + throw future_uninitialized(); + } + state_->get(); + } + + inline + std::exception_ptr get_exception_ptr() { + if ( ! valid() ) { + throw future_uninitialized(); + } + return state_->get_exception_ptr(); + } + + inline + void wait() const { + if ( ! valid() ) { + throw future_uninitialized(); + } + state_->wait(); + } + + template< class Rep, class Period > + future_status wait_for( std::chrono::duration< Rep, Period > const& timeout_duration) const { + if ( ! valid() ) { + throw future_uninitialized(); + } + return state_->wait_for( timeout_duration); + } + + template< typename Clock, typename Duration > + future_status wait_until( std::chrono::time_point< Clock, Duration > const& timeout_time) const { + if ( ! valid() ) { + throw future_uninitialized(); + } + return state_->wait_until( timeout_time); + } +}; + + +template< typename R > +shared_future< R > +future< R >::share() { + if ( ! valid() ) { + throw future_uninitialized(); + } + return shared_future< R >( std::move( * this) ); +} + +template< typename R > +shared_future< R & > +future< R & >::share() { + if ( ! valid() ) { + throw future_uninitialized(); + } + return shared_future< R & >( std::move( * this) ); +} + +inline +shared_future< void > +future< void >::share() { + if ( ! valid() ) { + throw future_uninitialized(); + } + return shared_future< void >( std::move( * this) ); +} + +}} + +#endif diff --git a/include/boost/fiber/future/future_status.hpp b/include/boost/fiber/future/future_status.hpp new file mode 100644 index 00000000..351b73d0 --- /dev/null +++ b/include/boost/fiber/future/future_status.hpp @@ -0,0 +1,25 @@ + +// 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_FUTURE_STATUS_HPP +#define BOOST_FIBERS_FUTURE_STATUS_HPP + +#include + +#include + +namespace boost { +namespace fibers { + +enum class future_status { + ready = 1, + timeout, + deferred +}; + +}} + +#endif // BOOST_FIBERS_FUTURE_STATUS_HPP diff --git a/include/boost/fiber/future/packaged_task.hpp b/include/boost/fiber/future/packaged_task.hpp new file mode 100644 index 00000000..4f98d0d8 --- /dev/null +++ b/include/boost/fiber/future/packaged_task.hpp @@ -0,0 +1,141 @@ + +// 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_PACKAGED_TASK_HPP +#define BOOST_FIBERS_PACKAGED_TASK_HPP + +#include +#include +#include + +#include + +#include +#include +#include +#include + +namespace boost { +namespace fibers { + +template< typename Signature > +class packaged_task; + +template< typename R, typename ... Args > +class packaged_task< R( Args ... ) > { +private: + typedef typename detail::task_base< R, Args ... >::ptr_t ptr_t; + + bool obtained_; + ptr_t task_; + +public: + packaged_task() noexcept : + obtained_( false), + task_() { + } + + ~packaged_task() { + if ( task_) { + task_->owner_destroyed(); + } + } + + template< typename Fn > + explicit packaged_task( Fn && fn) : + obtained_( false), + task_() { + typedef detail::task_object< + Fn, + std::allocator< packaged_task< R() > >, + R, + Args ... + > object_t; + std::allocator< packaged_task< R() > > alloc; + typename object_t::allocator_t a( alloc); + task_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( a, std::forward< Fn >( fn) ) ); + } + + template< typename Fn, typename Allocator > + explicit packaged_task( std::allocator_arg_t, Allocator const& alloc, Fn && fn) : + obtained_( false), + task_() { + typedef detail::task_object< + Fn, + Allocator, + R + > object_t; + typename object_t::allocator_t a( alloc); + task_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( a, std::forward< Fn >( fn) ) ); + } + + packaged_task( packaged_task const&) = delete; + packaged_task & operator=( packaged_task const&) = delete; + + packaged_task( packaged_task && other) noexcept : + obtained_( other.obtained_), + task_( std::move( other.task_) ) { + other.obtained_ = false; + } + + packaged_task & operator=( packaged_task && other) noexcept { + if ( this != & other) { + obtained_ = other.obtained_; + other.obtained_ = false; + task_ = std::move( other.task_); + } + return * this; + } + + void swap( packaged_task & other) noexcept { + std::swap( obtained_, other.obtained_); + task_.swap( other.task_); + } + + bool valid() const noexcept { + return nullptr != task_.get(); + } + + future< R > get_future() { + if ( obtained_) { + throw future_already_retrieved(); + } + if ( ! valid() ) { + throw packaged_task_uninitialized(); + } + obtained_ = true; + return future< R >( + boost::static_pointer_cast< detail::shared_state< R > >( task_) ); + } + + void operator()( Args && ... args) { + if ( ! valid() ) { + throw packaged_task_uninitialized(); + } + task_->run( std::forward< Args >( args) ... ); + } + + void reset() { + if ( ! valid() ) { + throw packaged_task_uninitialized(); + } + obtained_ = false; + task_->reset(); + } +}; + +template< typename Signature > +void swap( packaged_task< Signature > & l, packaged_task< Signature > & r) { + l.swap( r); +} + +}} + +#endif // BOOST_FIBERS_PACKAGED_TASK_HPP diff --git a/include/boost/fiber/future/promise.hpp b/include/boost/fiber/future/promise.hpp new file mode 100644 index 00000000..db4a5c23 --- /dev/null +++ b/include/boost/fiber/future/promise.hpp @@ -0,0 +1,308 @@ + +// 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_PROMISE_HPP +#define BOOST_FIBERS_PROMISE_HPP + +#include +#include +#include + +#include + +#include +#include +#include +#include + +namespace boost { +namespace fibers { + +template< typename R > +class promise { +private: + typedef typename detail::shared_state< R >::ptr_t ptr_t; + + bool obtained_; + ptr_t future_; + +public: + promise() : + obtained_( false), + future_() { + typedef detail::shared_state_object< + R, std::allocator< promise< R > > + > object_t; + std::allocator< promise< R > > alloc; + typename object_t::allocator_t a( alloc); + future_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( a) ); + } + + template< typename Allocator > + promise( std::allocator_arg_t, Allocator alloc) : + obtained_( false), + future_() { + typedef detail::shared_state_object< R, Allocator > object_t; + typename object_t::allocator_t a( alloc); + future_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( a) ); + } + + ~promise() { + if ( future_) { + future_->owner_destroyed(); + } + } + + promise( promise const&) = delete; + promise & operator=( promise const&) = delete; + + promise( promise && other) noexcept : + obtained_( other.obtained_), + future_( std::move( other.future_) ) { + other.obtained_ = false; + } + + promise & operator=( promise && other) noexcept { + if ( this != & other) { + obtained_ = other.obtained_; + other.obtained_ = false; + future_ = std::move( other.future_); + } + return * this; + } + + void swap( promise & other) noexcept { + std::swap( obtained_, other.obtained_); + future_.swap( other.future_); + } + + future< R > get_future() { + if ( obtained_) { + throw future_already_retrieved(); + } + if ( ! future_) { + throw promise_uninitialized(); + } + obtained_ = true; + return future< R >( future_); + } + + void set_value( R const& value) { + if ( ! future_) { + throw promise_uninitialized(); + } + future_->set_value( value); + } + + void set_value( R && value) { + if ( ! future_) { + throw promise_uninitialized(); + } + future_->set_value( std::move( value) ); + } + + void set_exception( std::exception_ptr p) { + if ( ! future_) { + throw promise_uninitialized(); + } + future_->set_exception( p); + } +}; + +template< typename R > +class promise< R & > { +private: + typedef typename detail::shared_state< R & >::ptr_t ptr_t; + + bool obtained_; + ptr_t future_; + +public: + promise() : + obtained_( false), + future_() { + typedef detail::shared_state_object< + R &, std::allocator< promise< R & > > + > object_t; + std::allocator< promise< R > > alloc; + typename object_t::allocator_t a( alloc); + future_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( a) ); + } + + template< typename Allocator > + promise( std::allocator_arg_t, Allocator alloc) : + obtained_( false), + future_() { + typedef detail::shared_state_object< R &, Allocator > object_t; + typename object_t::allocator_t a( alloc); + future_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( a) ); + } + + ~promise() { + if ( future_) { + future_->owner_destroyed(); + } + } + + promise( promise const&) = delete; + promise & operator=( promise const&) = delete; + + promise( promise && other) noexcept : + obtained_( other.obtained_), + future_( std::move( other.future_) ) { + other.obtained_ = false; + } + + promise & operator=( promise && other) noexcept { + if ( this != & other) { + obtained_ = other.obtained_; + other.obtained_ = false; + future_ = std::move( other.future_); + } + return * this; + } + + void swap( promise & other) noexcept { + std::swap( obtained_, other.obtained_); + future_.swap( other.future_); + } + + future< R & > get_future() { + if ( obtained_) { + throw future_already_retrieved(); + } + if ( ! future_) { + throw promise_uninitialized(); + } + obtained_ = true; + return future< R & >( future_); + } + + void set_value( R & value) { + if ( ! future_) { + throw promise_uninitialized(); + } + future_->set_value( value); + } + + void set_exception( std::exception_ptr p) { + if ( ! future_) { + throw promise_uninitialized(); + } + future_->set_exception( p); + } +}; + +template<> +class promise< void > { +private: + typedef detail::shared_state< void >::ptr_t ptr_t; + + bool obtained_; + ptr_t future_; + +public: + promise() : + obtained_( false), + future_() { + typedef detail::shared_state_object< + void, std::allocator< promise< void > > + > object_t; + std::allocator< promise< void > > alloc; + object_t::allocator_t a( alloc); + future_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( a) ); + } + + template< typename Allocator > + promise( std::allocator_arg_t, Allocator alloc) : + obtained_( false), + future_() { + typedef detail::shared_state_object< void, Allocator > object_t; + typename object_t::allocator_t a( alloc); + future_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( a) ); + } + + ~promise() { + if ( future_) { + future_->owner_destroyed(); + } + } + + promise( promise const&) = delete; + promise & operator=( promise const&) = delete; + + inline + promise( promise && other) noexcept : + obtained_( other.obtained_), + future_( std::move( other.future_) ) { + other.obtained_ = false; + } + + inline + promise & operator=( promise && other) noexcept { + if ( this != & other) { + obtained_ = other.obtained_; + other.obtained_ = false; + future_ = std::move( other.future_); + } + return * this; + } + + inline + void swap( promise & other) noexcept { + std::swap( obtained_, other.obtained_); + future_.swap( other.future_); + } + + inline + future< void > get_future() { + if ( obtained_) { + throw future_already_retrieved(); + } + if ( ! future_) { + throw promise_uninitialized(); + } + obtained_ = true; + return future< void >( future_); + } + + inline + void set_value() { + if ( ! future_) { + throw promise_uninitialized(); + } + future_->set_value(); + } + + inline + void set_exception( std::exception_ptr p) { + if ( ! future_) { + throw promise_uninitialized(); + } + future_->set_exception( p); + } +}; + +template< typename R > +void swap( promise< R > & l, promise< R > & r) { + l.swap( r); +} + +}} + +#endif // BOOST_FIBERS_PROMISE_HPP diff --git a/src/future.cpp b/src/future.cpp new file mode 100644 index 00000000..1fe1afb8 --- /dev/null +++ b/src/future.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/exceptions.hpp" + +namespace boost { +namespace fibers { + +class future_error_category : public std::error_category { +public: + virtual const char* name() const noexcept { + return "future"; + } + + virtual std::error_condition default_error_condition( int ev) const noexcept { + switch ( ev) { + case 1: return std::error_condition( + static_cast< int >( future_errc::broken_promise), + future_category() ); + case 2: return std::error_condition( + static_cast< int >( future_errc::future_already_retrieved), + future_category() ); + case 3: return std::error_condition( + static_cast< int >( future_errc::promise_already_satisfied), + future_category() ); + case 4: return std::error_condition( + static_cast< + int >( future_errc::no_state), + future_category() ); + default: + return std::error_condition( + static_cast< + int >( future_errc::unknown), + future_category() ); + } + } + + 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::unknown: + return std::string("Unknown error"); + 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/test/Jamfile.v2 b/test/Jamfile.v2 index 2898eee8..a3174b11 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -161,3 +161,33 @@ run test_fss.cpp : cxx11_variadic_macros cxx11_variadic_templates cxx14_initialized_lambda_captures ] ; + +run test_future.cpp : +: : +[ requires cxx11_constexpr + cxx11_decltype + cxx11_deleted_functions + cxx11_explicit_conversion_operators + cxx11_hdr_tuple cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_template_aliases + cxx11_rvalue_references + cxx11_variadic_macros + cxx11_variadic_templates + cxx14_initialized_lambda_captures ] ; + +run test_future_mt.cpp : +: : +[ requires cxx11_constexpr + cxx11_decltype + cxx11_deleted_functions + cxx11_explicit_conversion_operators + cxx11_hdr_tuple cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_template_aliases + cxx11_rvalue_references + cxx11_variadic_macros + cxx11_variadic_templates + cxx14_initialized_lambda_captures ] ; diff --git a/test/test_future.cpp b/test/test_future.cpp new file mode 100644 index 00000000..779067e4 --- /dev/null +++ b/test/test_future.cpp @@ -0,0 +1,1152 @@ +// (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 +#include +#include +#include + +#include + +#include + +int gi = 7; + +struct my_exception : public std::runtime_error { + my_exception() : + std::runtime_error("my_exception") { + } +}; + +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( std::bind( 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; +} + +// 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); + + //TODO: promise takes a moveable-only as return type +} + +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); +} + +// 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() ); + + //TODO: future gets a moveable-only as return type + + // 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_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_with_fiber_1() { + boost::fibers::promise< int > p1; + boost::fibers::fiber( + std::bind( 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( 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_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() ); +} + +// 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() ); +} + +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_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_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_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_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); + + //TODO: packaged_task returns a moveable-only as return type +} + +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); + + //TODO: packaged_task returns a moveable-only as return type + + 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_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); +} + +void test_async_1() { + boost::fibers::future< int > f1 = boost::fibers::async( fn3); + BOOST_CHECK( f1.valid() ); + + BOOST_CHECK( 3 == f1.get()); +} + +void test_async_2() { + boost::fibers::future< int > f1 = boost::fibers::async( fn8, 3); + BOOST_CHECK( f1.valid() ); + + BOOST_CHECK( 3 == 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: futures 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_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)); + + 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_ref)); + 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_with_fiber_1)); + test->add(BOOST_TEST_CASE(test_future_wait_with_fiber_2)); + + test->add(BOOST_TEST_CASE(test_shared_future_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_packaged_task_create)); + 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_void)); + test->add(BOOST_TEST_CASE(test_packaged_task_swap)); + 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_void)); + test->add(BOOST_TEST_CASE(test_packaged_task_get_future)); + 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_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_void)); + + test->add(BOOST_TEST_CASE(test_async_1)); + test->add(BOOST_TEST_CASE(test_async_2)); + + return test; +} diff --git a/test/test_future_mt.cpp b/test/test_future_mt.cpp new file mode 100644 index 00000000..68231b8e --- /dev/null +++ b/test/test_future_mt.cpp @@ -0,0 +1,49 @@ +// (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 +#include +#include +#include +#include + +#include +#include + +int fn( int i) { + return i; +} + +boost::fibers::future< int > async( int i) { + boost::fibers::packaged_task< int() > pt( std::bind( fn, i) ); + boost::fibers::future< int > f( pt.get_future() ); + std::thread( [pt=std::move( pt)] () mutable -> decltype( auto) { boost::fibers::fiber( std::move( pt) ).join(); } ).detach(); + return std::move( f); +} + +void test_async() { + int i = 3; + boost::fibers::future< int > f = async( i); + int result = f.get(); + BOOST_CHECK_EQUAL( i, result); +} + +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) + for ( int i = 0; i < 50; ++i) { + test->add(BOOST_TEST_CASE(test_async)); + } +#else + test->add(BOOST_TEST_CASE(test_dummy)); +#endif + + return test; +}