1 2 // Copyright Oliver Kowalke 2013. 3 // Distributed under the Boost Software License, Version 1.0. 4 // (See accompanying file LICENSE_1_0.txt or copy at 5 // http://www.boost.org/LICENSE_1_0.txt) 6 7 #ifndef BOOST_FIBERS_PROMISE_HPP 8 #define BOOST_FIBERS_PROMISE_HPP 9 10 #include <algorithm> 11 #include <memory> 12 #include <utility> 13 14 #include <boost/config.hpp> 15 #include <boost/core/pointer_traits.hpp> 16 17 #include <boost/fiber/exceptions.hpp> 18 #include <boost/fiber/future/detail/shared_state.hpp> 19 #include <boost/fiber/future/detail/shared_state_object.hpp> 20 #include <boost/fiber/future/future.hpp> 21 22 namespace boost { 23 namespace fibers { 24 namespace detail { 25 26 template< typename R > 27 struct promise_base { 28 typedef typename shared_state< R >::ptr_type ptr_type; 29 30 bool obtained_{ false }; 31 ptr_type future_{}; 32 33 promise_base() : 34 promise_base{ std::allocator_arg, std::allocator< promise_base >{} } { 35 } 36 37 template< typename Allocator > 38 promise_base( std::allocator_arg_t, Allocator alloc) { 39 typedef detail::shared_state_object< R, Allocator > object_type; 40 typedef std::allocator_traits< typename object_type::allocator_type > traits_type; 41 typedef pointer_traits< typename traits_type::pointer > ptrait_type; 42 typename object_type::allocator_type a{ alloc }; 43 typename traits_type::pointer ptr{ traits_type::allocate( a, 1) }; 44 typename ptrait_type::element_type* p = boost::to_address(ptr); 45 46 try { 47 traits_type::construct( a, p, a); 48 } catch (...) { 49 traits_type::deallocate( a, ptr, 1); 50 throw; 51 } 52 future_.reset(p); 53 } 54 55 ~promise_base() { 56 if ( future_ && obtained_) { 57 future_->owner_destroyed(); 58 } 59 } 60 61 promise_base( promise_base const&) = delete; 62 promise_base & operator=( promise_base const&) = delete; 63 64 promise_base( promise_base && other) noexcept : 65 obtained_{ other.obtained_ }, 66 future_{ std::move( other.future_) } { 67 other.obtained_ = false; 68 } 69 70 promise_base & operator=( promise_base && other) noexcept { 71 if ( BOOST_LIKELY( this != & other) ) { 72 promise_base tmp{ std::move( other) }; 73 swap( tmp); 74 } 75 return * this; 76 } 77 78 future< R > get_future() { 79 if ( BOOST_UNLIKELY( obtained_) ) { 80 throw future_already_retrieved{}; 81 } 82 if ( BOOST_UNLIKELY( ! future_) ) { 83 throw promise_uninitialized{}; 84 } 85 obtained_ = true; 86 return future< R >{ future_ }; 87 } 88 89 void swap( promise_base & other) noexcept { 90 std::swap( obtained_, other.obtained_); 91 future_.swap( other.future_); 92 } 93 94 void set_exception( std::exception_ptr p) { 95 if ( BOOST_UNLIKELY( ! future_) ) { 96 throw promise_uninitialized{}; 97 } 98 future_->set_exception( p); 99 } 100 }; 101 102 } 103 104 template< typename R > 105 class promise : private detail::promise_base< R > { 106 private: 107 typedef detail::promise_base< R > base_type; 108 109 public: 110 promise() = default; 111 112 template< typename Allocator > 113 promise( std::allocator_arg_t, Allocator alloc) : 114 base_type{ std::allocator_arg, alloc } { 115 } 116 117 promise( promise const&) = delete; 118 promise & operator=( promise const&) = delete; 119 120 promise( promise && other) noexcept = default; 121 promise & operator=( promise && other) = default; 122 123 void set_value( R const& value) { 124 if ( BOOST_UNLIKELY( ! base_type::future_) ) { 125 throw promise_uninitialized{}; 126 } 127 base_type::future_->set_value( value); 128 } 129 130 void set_value( R && value) { 131 if ( BOOST_UNLIKELY( ! base_type::future_) ) { 132 throw promise_uninitialized{}; 133 } 134 base_type::future_->set_value( std::move( value) ); 135 } 136 137 void swap( promise & other) noexcept { 138 base_type::swap( other); 139 } 140 141 using base_type::get_future; 142 using base_type::set_exception; 143 }; 144 145 template< typename R > 146 class promise< R & > : private detail::promise_base< R & > { 147 private: 148 typedef detail::promise_base< R & > base_type; 149 150 public: 151 promise() = default; 152 153 template< typename Allocator > 154 promise( std::allocator_arg_t, Allocator alloc) : 155 base_type{ std::allocator_arg, alloc } { 156 } 157 158 promise( promise const&) = delete; 159 promise & operator=( promise const&) = delete; 160 161 promise( promise && other) noexcept = default; 162 promise & operator=( promise && other) noexcept = default; 163 164 void set_value( R & value) { 165 if ( BOOST_UNLIKELY( ! base_type::future_) ) { 166 throw promise_uninitialized{}; 167 } 168 base_type::future_->set_value( value); 169 } 170 171 void swap( promise & other) noexcept { 172 base_type::swap( other); 173 } 174 175 using base_type::get_future; 176 using base_type::set_exception; 177 }; 178 179 template<> 180 class promise< void > : private detail::promise_base< void > { 181 private: 182 typedef detail::promise_base< void > base_type; 183 184 public: 185 promise() = default; 186 187 template< typename Allocator > 188 promise( std::allocator_arg_t, Allocator alloc) : 189 base_type{ std::allocator_arg, alloc } { 190 } 191 192 promise( promise const&) = delete; 193 promise & operator=( promise const&) = delete; 194 195 promise( promise && other) noexcept = default; 196 promise & operator=( promise && other) noexcept = default; 197 198 inline 199 void set_value() { 200 if ( BOOST_UNLIKELY( ! base_type::future_) ) { 201 throw promise_uninitialized{}; 202 } 203 base_type::future_->set_value(); 204 } 205 206 inline 207 void swap( promise & other) noexcept { 208 base_type::swap( other); 209 } 210 211 using base_type::get_future; 212 using base_type::set_exception; 213 }; 214 215 template< typename R > 216 void swap( promise< R > & l, promise< R > & r) noexcept { 217 l.swap( r); 218 } 219 220 }} 221 222 #endif // BOOST_FIBERS_PROMISE_HPP 223