diff --git a/include/boost/fiber/bounded_channel.hpp b/include/boost/fiber/bounded_channel.hpp index 60eed966..6c5523dd 100644 --- a/include/boost/fiber/bounded_channel.hpp +++ b/include/boost/fiber/bounded_channel.hpp @@ -37,22 +37,25 @@ public: private: struct node { - typedef intrusive_ptr< node > ptr; - typedef typename std::allocator_traits< Allocator >::template rebind_alloc< node > allocator_type; + typedef intrusive_ptr< node > ptr_t; + typedef typename std::allocator_traits< Allocator >::template rebind_alloc< + node + > allocator_t; + typedef std::allocator_traits< allocator_t > allocator_traits_t; - std::size_t use_count{ 0 }; - allocator_type & alloc; - T va; - ptr nxt{}; + std::size_t use_count{ 0 }; + allocator_t alloc; + T va; + ptr_t nxt{}; - node( T const& t, allocator_type & alloc_) noexcept : + node( T const& t, allocator_t const& alloc_) noexcept : alloc{ alloc_ }, va{ t } { } - node( T && t, allocator_type & alloc_) noexcept : + node( T && t, allocator_t & alloc_) noexcept : alloc{ alloc_ }, - va{ std::forward< T >( t) } { + va{ std::move( t) } { } friend @@ -63,30 +66,32 @@ private: friend void intrusive_ptr_release( node * p) noexcept { if ( 0 == --p->use_count) { - allocator_type & alloc( p->alloc); - std::allocator_traits< allocator_type >::destroy( alloc, p); - std::allocator_traits< allocator_type >::deallocate( alloc, p, 1); + allocator_t alloc( p->alloc); + allocator_traits_t::destroy( alloc, p); + allocator_traits_t::deallocate( alloc, p, 1); } } }; - typedef typename std::allocator_traits< Allocator >::template rebind_alloc< node > allocator_type; + using ptr_t = typename node::ptr_t; + using allocator_t = typename node::allocator_t; + using allocator_traits_t = typename node::allocator_traits_t; enum class queue_status { open = 0, closed }; - allocator_type alloc_; - queue_status state_{ queue_status::open }; - std::size_t count_{ 0 }; - typename node::ptr head_{}; - typename node::ptr * tail_; - mutable mutex mtx_{}; - condition not_empty_cond_{}; - condition not_full_cond_{}; - std::size_t hwm_; - std::size_t lwm_; + allocator_t alloc_; + queue_status state_{ queue_status::open }; + std::size_t count_{ 0 }; + ptr_t head_{}; + ptr_t * tail_; + mutable mutex mtx_{}; + condition not_empty_cond_{}; + condition not_full_cond_{}; + std::size_t hwm_; + std::size_t lwm_; bool is_closed_() const noexcept { return queue_status::closed == state_; @@ -111,7 +116,7 @@ private: return count_ >= hwm_; } - channel_op_status push_( typename node::ptr const& new_node, + channel_op_status push_( ptr_t new_node, std::unique_lock< boost::fibers::mutex > & lk) { if ( is_closed_() ) { return channel_op_status::closed; @@ -122,7 +127,7 @@ private: return push_and_notify_( new_node, lk); } - channel_op_status try_push_( typename node::ptr const& new_node, + channel_op_status try_push_( ptr_t new_node, std::unique_lock< boost::fibers::mutex > & lk) noexcept { if ( is_closed_() ) { return channel_op_status::closed; @@ -134,7 +139,7 @@ private: } template< typename Clock, typename Duration > - channel_op_status push_wait_until_( typename node::ptr const& new_node, + channel_op_status push_wait_until_( ptr_t new_node, std::chrono::time_point< Clock, Duration > const& timeout_time, std::unique_lock< boost::fibers::mutex > & lk) { if ( is_closed_() ) { @@ -148,7 +153,7 @@ private: return push_and_notify_( new_node, lk); } - channel_op_status push_and_notify_( typename node::ptr const& new_node, + channel_op_status push_and_notify_( ptr_t new_node, std::unique_lock< boost::fibers::mutex > & lk) noexcept { push_tail_( new_node); lk.unlock(); @@ -156,7 +161,7 @@ private: return channel_op_status::success; } - void push_tail_( typename node::ptr new_node) noexcept { + void push_tail_( ptr_t new_node) noexcept { * tail_ = new_node; tail_ = & new_node->nxt; ++count_; @@ -179,7 +184,7 @@ private: return std::move( old_head->va); } - typename node::ptr pop_head_() noexcept { + ptr_t pop_head_() noexcept { auto old_head = head_; head_ = old_head->nxt; if ( ! head_) { @@ -236,17 +241,30 @@ public: } channel_op_status push( value_type const& va) { - typename node::ptr new_node( - new ( alloc_.allocate( 1) ) node( va, alloc_) ); + typename allocator_traits_t::pointer ptr{ + allocator_traits_t::allocate( alloc_, 1) }; + try { + allocator_traits_t::construct( alloc_, ptr, va, alloc_); + } catch (...) { + allocator_traits_t::deallocate( alloc_, ptr, 1); + throw; + } std::unique_lock< mutex > lk( mtx_); - return push_( new_node, lk); + return push_( { detail::convert( ptr) }, lk); } channel_op_status push( value_type && va) { - typename node::ptr new_node( - new ( alloc_.allocate( 1) ) node( std::forward< value_type >( va), alloc_) ); + typename allocator_traits_t::pointer ptr{ + allocator_traits_t::allocate( alloc_, 1) }; + try { + allocator_traits_t::construct( + alloc_, ptr, std::move( va), alloc_); + } catch (...) { + allocator_traits_t::deallocate( alloc_, ptr, 1); + throw; + } std::unique_lock< mutex > lk( mtx_); - return push_( new_node, lk); + return push_( { detail::convert( ptr) }, lk); } template< typename Rep, typename Period > @@ -266,33 +284,59 @@ public: template< typename Clock, typename Duration > channel_op_status push_wait_until( value_type const& va, std::chrono::time_point< Clock, Duration > const& timeout_time) { - typename node::ptr new_node( - new ( alloc_.allocate( 1) ) node( va, alloc_) ); + typename allocator_traits_t::pointer ptr{ + allocator_traits_t::allocate( alloc_, 1) }; + try { + allocator_traits_t::construct( alloc_, ptr, va, alloc_); + } catch (...) { + allocator_traits_t::deallocate( alloc_, ptr, 1); + throw; + } std::unique_lock< mutex > lk( mtx_); - return push_wait_until_( new_node, timeout_time, lk); + return push_wait_until_( { detail::convert( ptr) }, timeout_time, lk); } template< typename Clock, typename Duration > channel_op_status push_wait_until( value_type && va, std::chrono::time_point< Clock, Duration > const& timeout_time) { - typename node::ptr new_node( - new ( alloc_.allocate( 1) ) node( std::forward< value_type >( va), alloc_) ); + typename allocator_traits_t::pointer ptr{ + allocator_traits_t::allocate( alloc_, 1) }; + try { + allocator_traits_t::construct( + alloc_, ptr, std::move( va), alloc_); + } catch (...) { + allocator_traits_t::deallocate( alloc_, ptr, 1); + throw; + } std::unique_lock< mutex > lk( mtx_); - return push_wait_until_( new_node, timeout_time, lk); + return push_wait_until_( { detail::convert( ptr) }, timeout_time, lk); } channel_op_status try_push( value_type const& va) noexcept { - typename node::ptr new_node( - new ( alloc_.allocate( 1) ) node( va, alloc_) ); + typename allocator_traits_t::pointer ptr{ + allocator_traits_t::allocate( alloc_, 1) }; + try { + allocator_traits_t::construct( alloc_, ptr, va, alloc_); + } catch (...) { + allocator_traits_t::deallocate( alloc_, ptr, 1); + throw; + } std::unique_lock< mutex > lk( mtx_); - return try_push_( new_node, lk); + return try_push_( { detail::convert( ptr) }, lk); } channel_op_status try_push( value_type && va) noexcept { - typename node::ptr new_node( - new ( alloc_.allocate( 1) ) node( std::forward< value_type >( va), alloc_) ); + typename allocator_traits_t::pointer ptr{ + allocator_traits_t::allocate( alloc_, 1) }; + try { + allocator_traits_t::construct( + alloc_, ptr, std::move( va), alloc_); + } catch (...) { + allocator_traits_t::deallocate( alloc_, ptr, 1); + throw; + } std::unique_lock< mutex > lk( mtx_); - return try_push_( new_node, lk); + return try_push_( { detail::convert( ptr) }, lk); } channel_op_status pop( value_type & va) { diff --git a/include/boost/fiber/detail/convert.hpp b/include/boost/fiber/detail/convert.hpp index 1620bba7..ac190d85 100644 --- a/include/boost/fiber/detail/convert.hpp +++ b/include/boost/fiber/detail/convert.hpp @@ -8,6 +8,7 @@ #define BOOST_FIBERS_DETAIL_CONVERT_H #include +#include #include @@ -33,6 +34,22 @@ std::chrono::steady_clock::time_point convert( return std::chrono::steady_clock::now() + ( timeout_time - Clock::now() ); } +// suggested by Howard Hinnant +template< typename T > +inline +T * convert( T * p) noexcept { + return p; +} + +template< typename Pointer > +inline +typename std::pointer_traits< Pointer >::element_type * +convert( Pointer p) noexcept { + return nullptr != p + ? to_raw_pointer( p.operator->() ) + : nullptr; +} + }}} #ifdef BOOST_HAS_ABI_HEADERS diff --git a/include/boost/fiber/future/detail/shared_state_object.hpp b/include/boost/fiber/future/detail/shared_state_object.hpp index f869bbd8..081efc40 100644 --- a/include/boost/fiber/future/detail/shared_state_object.hpp +++ b/include/boost/fiber/future/detail/shared_state_object.hpp @@ -7,6 +7,8 @@ #ifndef BOOST_FIBERS_DETAIL_SHARED_STATE_OBJECT_H #define BOOST_FIBERS_DETAIL_SHARED_STATE_OBJECT_H +#include + #include #include @@ -23,9 +25,9 @@ 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; + typedef typename std::allocator_traits< Allocator >::template rebind_alloc< + shared_state_object + > allocator_t; shared_state_object( allocator_t const& alloc) : shared_state< R >(), alloc_( alloc) { diff --git a/include/boost/fiber/future/detail/task_object.hpp b/include/boost/fiber/future/detail/task_object.hpp index 0e906e87..bef91dfc 100644 --- a/include/boost/fiber/future/detail/task_object.hpp +++ b/include/boost/fiber/future/detail/task_object.hpp @@ -8,7 +8,8 @@ #define BOOST_FIBERS_DETAIL_TASK_OBJECT_H #include -#include // std::forward() +#include +#include #include #include @@ -27,13 +28,13 @@ 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; + typedef typename std::allocator_traits< Allocator >::template rebind_alloc< + task_object + > allocator_t; - explicit task_object( allocator_t const& alloc, Fn && fn) : + task_object( allocator_t const& alloc, Fn && fn) : task_base< R, Args ... >(), - fn_( std::forward< Fn >( fn) ), + fn_( std::move( fn) ), alloc_( alloc) { } @@ -69,9 +70,9 @@ public: task_object< Fn, Allocator, void, Args ... > >::other allocator_t; - explicit task_object( allocator_t const& alloc, Fn && fn) : + task_object( allocator_t const& alloc, Fn && fn) : task_base< void, Args ... >(), - fn_( std::forward< Fn >( fn) ), + fn_( std::move( fn) ), alloc_( alloc) { } diff --git a/include/boost/fiber/future/packaged_task.hpp b/include/boost/fiber/future/packaged_task.hpp index 92425329..218057af 100644 --- a/include/boost/fiber/future/packaged_task.hpp +++ b/include/boost/fiber/future/packaged_task.hpp @@ -13,6 +13,7 @@ #include +#include #include #include #include @@ -42,31 +43,30 @@ public: } template< typename Fn > - explicit packaged_task( Fn && fn) { - 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) } }; + explicit packaged_task( Fn && fn) : + packaged_task{ std::allocator_arg, + std::allocator< packaged_task >{}, + std::forward< Fn >( fn) } { } template< typename Fn, typename Allocator > - explicit packaged_task( std::allocator_arg_t, Allocator const& alloc, Fn && fn) { + packaged_task( std::allocator_arg_t, Allocator const& alloc, Fn && fn) { typedef detail::task_object< - Fn, - Allocator, - R + Fn, Allocator, R, Args ... > object_t; + typedef std::allocator_traits< + typename object_t::allocator_t + > traits_t; typename object_t::allocator_t a{ alloc }; - task_ = ptr_t{ - // placement new - ::new( a.allocate( 1) ) object_t{ a, std::forward< Fn >( fn) } }; + typename traits_t::pointer ptr{ traits_t::allocate( a, 1) }; + + try { + traits_t::construct( a, ptr, a, std::forward< Fn >( fn) ); + } catch (...) { + traits_t::deallocate( a, ptr, 1); + throw; + } + task_.reset( convert( ptr) ); } packaged_task( packaged_task const&) = delete; diff --git a/include/boost/fiber/future/promise.hpp b/include/boost/fiber/future/promise.hpp index 4b4b3c99..e0a4ccc4 100644 --- a/include/boost/fiber/future/promise.hpp +++ b/include/boost/fiber/future/promise.hpp @@ -13,6 +13,7 @@ #include +#include #include #include #include @@ -36,10 +37,17 @@ struct promise_base { template< typename Allocator > promise_base( std::allocator_arg_t, Allocator alloc) { typedef detail::shared_state_object< R, Allocator > object_t; + typedef std::allocator_traits< typename object_t::allocator_t > traits_t; typename object_t::allocator_t a{ alloc }; - future_ = ptr_t{ - // placement new - ::new( a.allocate( 1) ) object_t{ a } }; + typename traits_t::pointer ptr{ traits_t::allocate( a, 1) }; + + try { + traits_t::construct( a, ptr, a); + } catch (...) { + traits_t::deallocate( a, ptr, 1); + throw; + } + future_.reset( convert( ptr) ); } ~promise_base() { diff --git a/include/boost/fiber/unbounded_channel.hpp b/include/boost/fiber/unbounded_channel.hpp index 37afae81..f80a70c5 100644 --- a/include/boost/fiber/unbounded_channel.hpp +++ b/include/boost/fiber/unbounded_channel.hpp @@ -8,18 +8,19 @@ #ifndef BOOST_FIBERS_UNBOUNDED_CHANNEL_H #define BOOST_FIBERS_UNBOUNDED_CHANNEL_H -#include // std::move() +#include #include #include -#include // std::allocator -#include // std::unique_lock -#include // std::forward() +#include +#include +#include #include #include #include #include +#include #include #include #include @@ -38,22 +39,25 @@ public: private: struct node { - typedef intrusive_ptr< node > ptr; - typedef typename std::allocator_traits< Allocator >::template rebind_alloc< node > allocator_type; + typedef intrusive_ptr< node > ptr_t; + typedef typename std::allocator_traits< Allocator >::template rebind_alloc< + node + > allocator_t; + typedef std::allocator_traits< allocator_t > allocator_traits_t; - std::size_t use_count{ 0 }; - allocator_type & alloc; - T va; - ptr nxt{}; + std::size_t use_count{ 0 }; + allocator_t alloc; + T va; + ptr_t nxt{}; - node( T const& t, allocator_type & alloc_) noexcept : + node( T const& t, allocator_t const& alloc_) noexcept : alloc{ alloc_ }, va{ t } { } - node( T && t, allocator_type & alloc_) noexcept : + node( T && t, allocator_t const& alloc_) noexcept : alloc{ alloc_ }, - va{ std::forward< T >( t) } { + va{ std::move( t) } { } friend @@ -64,26 +68,28 @@ private: friend void intrusive_ptr_release( node * p) noexcept { if ( 0 == --p->use_count) { - allocator_type & alloc( p->alloc); - std::allocator_traits< allocator_type >::destroy( alloc, p); - std::allocator_traits< allocator_type >::deallocate( alloc, p, 1); + allocator_t alloc( p->alloc); + allocator_traits_t::destroy( alloc, p); + allocator_traits_t::deallocate( alloc, p, 1); } } }; - typedef typename std::allocator_traits< Allocator >::template rebind_alloc< node > allocator_type; + using ptr_t = typename node::ptr_t; + using allocator_t = typename node::allocator_t; + using allocator_traits_t = typename node::allocator_traits_t; enum class queue_status { open = 0, closed }; - allocator_type alloc_; - queue_status state_{ queue_status::open }; - typename node::ptr head_{}; - typename node::ptr * tail_; - mutable mutex mtx_{}; - condition not_empty_cond_{}; + allocator_t alloc_; + queue_status state_{ queue_status::open }; + ptr_t head_{}; + ptr_t * tail_; + mutable mutex mtx_{}; + condition not_empty_cond_{}; bool is_closed_() const noexcept { return queue_status::closed == state_; @@ -99,7 +105,7 @@ private: return ! head_; } - channel_op_status push_( typename node::ptr const& new_node, + channel_op_status push_( ptr_t new_node, std::unique_lock< mutex > & lk) noexcept { if ( is_closed_() ) { return channel_op_status::closed; @@ -107,7 +113,7 @@ private: return push_and_notify_( new_node, lk); } - channel_op_status push_and_notify_( typename node::ptr const& new_node, + channel_op_status push_and_notify_( ptr_t new_node, std::unique_lock< mutex > & lk) noexcept { push_tail_( new_node); lk.unlock(); @@ -115,7 +121,7 @@ private: return channel_op_status::success; } - void push_tail_( typename node::ptr new_node) noexcept { + void push_tail_( ptr_t new_node) noexcept { * tail_ = new_node; tail_ = & new_node->nxt; } @@ -126,7 +132,7 @@ private: return std::move( old_head->va); } - typename node::ptr pop_head_() noexcept { + ptr_t pop_head_() noexcept { auto old_head = head_; head_ = old_head->nxt; if ( ! head_) { @@ -150,18 +156,31 @@ public: close_( lk); } - channel_op_status push( value_type const& va) noexcept { - typename node::ptr new_node( - new ( alloc_.allocate( 1) ) node( va, alloc_) ); + channel_op_status push( value_type const& va) { + typename allocator_traits_t::pointer ptr{ + allocator_traits_t::allocate( alloc_, 1) }; + try { + allocator_traits_t::construct( alloc_, ptr, va, alloc_); + } catch (...) { + allocator_traits_t::deallocate( alloc_, ptr, 1); + throw; + } std::unique_lock< mutex > lk( mtx_); - return push_( new_node, lk); + return push_( { detail::convert( ptr) }, lk); } - channel_op_status push( value_type && va) noexcept { - typename node::ptr new_node( - new ( alloc_.allocate( 1) ) node( std::forward< value_type >( va), alloc_) ); + channel_op_status push( value_type && va) { + typename allocator_traits_t::pointer ptr{ + allocator_traits_t::allocate( alloc_, 1) }; + try { + allocator_traits_t::construct( + alloc_, ptr, std::move( va), alloc_); + } catch (...) { + allocator_traits_t::deallocate( alloc_, ptr, 1); + throw; + } std::unique_lock< mutex > lk( mtx_); - return push_( new_node, lk); + return push_( { detail::convert( ptr) }, lk); } channel_op_status pop( value_type & va) { diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 518cb397..4bac255b 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -27,6 +27,22 @@ project boost/fiber/test multi ; +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_fiber.cpp : : : [ requires cxx11_constexpr @@ -139,22 +155,6 @@ run test_fss.cpp : 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_mutex_mt.cpp : : : [ requires cxx11_constexpr