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_DETAIL_SHARED_STATE_H 8 #define BOOST_FIBERS_DETAIL_SHARED_STATE_H 9 10 #include <algorithm> 11 #include <atomic> 12 #include <chrono> 13 #include <cstddef> 14 #include <exception> 15 #include <memory> 16 #include <mutex> 17 #include <type_traits> 18 19 #include <boost/assert.hpp> 20 #include <boost/config.hpp> 21 #include <boost/intrusive_ptr.hpp> 22 23 #include <boost/fiber/detail/config.hpp> 24 #include <boost/fiber/future/future_status.hpp> 25 #include <boost/fiber/condition_variable.hpp> 26 #include <boost/fiber/exceptions.hpp> 27 #include <boost/fiber/mutex.hpp> 28 29 #ifdef BOOST_HAS_ABI_HEADERS 30 # include BOOST_ABI_PREFIX 31 #endif 32 33 namespace boost { 34 namespace fibers { 35 namespace detail { 36 37 class shared_state_base { 38 private: 39 std::atomic< std::size_t > use_count_{ 0 }; 40 mutable condition_variable waiters_{}; 41 42 protected: 43 mutable mutex mtx_{}; 44 bool ready_{ false }; 45 std::exception_ptr except_{}; 46 mark_ready_and_notify_(std::unique_lock<mutex> & lk)47 void mark_ready_and_notify_( std::unique_lock< mutex > & lk) noexcept { 48 BOOST_ASSERT( lk.owns_lock() ); 49 ready_ = true; 50 lk.unlock(); 51 waiters_.notify_all(); 52 } 53 owner_destroyed_(std::unique_lock<mutex> & lk)54 void owner_destroyed_( std::unique_lock< mutex > & lk) { 55 BOOST_ASSERT( lk.owns_lock() ); 56 if ( ! ready_) { 57 set_exception_( 58 std::make_exception_ptr( broken_promise() ), 59 lk); 60 } 61 } 62 set_exception_(std::exception_ptr except,std::unique_lock<mutex> & lk)63 void set_exception_( std::exception_ptr except, std::unique_lock< mutex > & lk) { 64 BOOST_ASSERT( lk.owns_lock() ); 65 if ( BOOST_UNLIKELY( ready_) ) { 66 throw promise_already_satisfied(); 67 } 68 except_ = except; 69 mark_ready_and_notify_( lk); 70 } 71 get_exception_ptr_(std::unique_lock<mutex> & lk)72 std::exception_ptr get_exception_ptr_( std::unique_lock< mutex > & lk) { 73 BOOST_ASSERT( lk.owns_lock() ); 74 wait_( lk); 75 return except_; 76 } 77 wait_(std::unique_lock<mutex> & lk) const78 void wait_( std::unique_lock< mutex > & lk) const { 79 BOOST_ASSERT( lk.owns_lock() ); 80 waiters_.wait( lk, [this](){ return ready_; }); 81 } 82 83 template< typename Rep, typename Period > wait_for_(std::unique_lock<mutex> & lk,std::chrono::duration<Rep,Period> const & timeout_duration) const84 future_status wait_for_( std::unique_lock< mutex > & lk, 85 std::chrono::duration< Rep, Period > const& timeout_duration) const { 86 BOOST_ASSERT( lk.owns_lock() ); 87 return waiters_.wait_for( lk, timeout_duration, [this](){ return ready_; }) 88 ? future_status::ready 89 : future_status::timeout; 90 } 91 92 template< typename Clock, typename Duration > wait_until_(std::unique_lock<mutex> & lk,std::chrono::time_point<Clock,Duration> const & timeout_time) const93 future_status wait_until_( std::unique_lock< mutex > & lk, 94 std::chrono::time_point< Clock, Duration > const& timeout_time) const { 95 BOOST_ASSERT( lk.owns_lock() ); 96 return waiters_.wait_until( lk, timeout_time, [this](){ return ready_; }) 97 ? future_status::ready 98 : future_status::timeout; 99 } 100 101 virtual void deallocate_future() noexcept = 0; 102 103 public: 104 shared_state_base() = default; 105 106 virtual ~shared_state_base() = default; 107 108 shared_state_base( shared_state_base const&) = delete; 109 shared_state_base & operator=( shared_state_base const&) = delete; 110 owner_destroyed()111 void owner_destroyed() { 112 std::unique_lock< mutex > lk{ mtx_ }; 113 owner_destroyed_( lk); 114 } 115 set_exception(std::exception_ptr except)116 void set_exception( std::exception_ptr except) { 117 std::unique_lock< mutex > lk{ mtx_ }; 118 set_exception_( except, lk); 119 } 120 get_exception_ptr()121 std::exception_ptr get_exception_ptr() { 122 std::unique_lock< mutex > lk{ mtx_ }; 123 return get_exception_ptr_( lk); 124 } 125 wait() const126 void wait() const { 127 std::unique_lock< mutex > lk{ mtx_ }; 128 wait_( lk); 129 } 130 131 template< typename Rep, typename Period > wait_for(std::chrono::duration<Rep,Period> const & timeout_duration) const132 future_status wait_for( std::chrono::duration< Rep, Period > const& timeout_duration) const { 133 std::unique_lock< mutex > lk{ mtx_ }; 134 return wait_for_( lk, timeout_duration); 135 } 136 137 template< typename Clock, typename Duration > wait_until(std::chrono::time_point<Clock,Duration> const & timeout_time) const138 future_status wait_until( std::chrono::time_point< Clock, Duration > const& timeout_time) const { 139 std::unique_lock< mutex > lk{ mtx_ }; 140 return wait_until_( lk, timeout_time); 141 } 142 143 friend inline intrusive_ptr_add_ref(shared_state_base * p)144 void intrusive_ptr_add_ref( shared_state_base * p) noexcept { 145 p->use_count_.fetch_add( 1, std::memory_order_relaxed); 146 } 147 148 friend inline intrusive_ptr_release(shared_state_base * p)149 void intrusive_ptr_release( shared_state_base * p) noexcept { 150 if ( 1 == p->use_count_.fetch_sub( 1, std::memory_order_release) ) { 151 std::atomic_thread_fence( std::memory_order_acquire); 152 p->deallocate_future(); 153 } 154 } 155 }; 156 157 template< typename R > 158 class shared_state : public shared_state_base { 159 private: 160 typename std::aligned_storage< sizeof( R), alignof( R) >::type storage_{}; 161 set_value_(R const & value,std::unique_lock<mutex> & lk)162 void set_value_( R const& value, std::unique_lock< mutex > & lk) { 163 BOOST_ASSERT( lk.owns_lock() ); 164 if ( BOOST_UNLIKELY( ready_) ) { 165 throw promise_already_satisfied{}; 166 } 167 ::new ( static_cast< void * >( std::addressof( storage_) ) ) R( value ); 168 mark_ready_and_notify_( lk); 169 } 170 set_value_(R && value,std::unique_lock<mutex> & lk)171 void set_value_( R && value, std::unique_lock< mutex > & lk) { 172 BOOST_ASSERT( lk.owns_lock() ); 173 if ( BOOST_UNLIKELY( ready_) ) { 174 throw promise_already_satisfied{}; 175 } 176 ::new ( static_cast< void * >( std::addressof( storage_) ) ) R( std::move( value) ); 177 mark_ready_and_notify_( lk); 178 } 179 get_(std::unique_lock<mutex> & lk)180 R & get_( std::unique_lock< mutex > & lk) { 181 BOOST_ASSERT( lk.owns_lock() ); 182 wait_( lk); 183 if ( except_) { 184 std::rethrow_exception( except_); 185 } 186 return * reinterpret_cast< R * >( std::addressof( storage_) ); 187 } 188 189 public: 190 typedef intrusive_ptr< shared_state > ptr_type; 191 192 shared_state() = default; 193 ~shared_state()194 virtual ~shared_state() { 195 if ( ready_ && ! except_) { 196 reinterpret_cast< R * >( std::addressof( storage_) )->~R(); 197 } 198 } 199 200 shared_state( shared_state const&) = delete; 201 shared_state & operator=( shared_state const&) = delete; 202 set_value(R const & value)203 void set_value( R const& value) { 204 std::unique_lock< mutex > lk{ mtx_ }; 205 set_value_( value, lk); 206 } 207 set_value(R && value)208 void set_value( R && value) { 209 std::unique_lock< mutex > lk{ mtx_ }; 210 set_value_( std::move( value), lk); 211 } 212 get()213 R & get() { 214 std::unique_lock< mutex > lk{ mtx_ }; 215 return get_( lk); 216 } 217 }; 218 219 template< typename R > 220 class shared_state< R & > : public shared_state_base { 221 private: 222 R * value_{ nullptr }; 223 set_value_(R & value,std::unique_lock<mutex> & lk)224 void set_value_( R & value, std::unique_lock< mutex > & lk) { 225 BOOST_ASSERT( lk.owns_lock() ); 226 if ( BOOST_UNLIKELY( ready_) ) { 227 throw promise_already_satisfied(); 228 } 229 value_ = std::addressof( value); 230 mark_ready_and_notify_( lk); 231 } 232 get_(std::unique_lock<mutex> & lk)233 R & get_( std::unique_lock< mutex > & lk) { 234 BOOST_ASSERT( lk.owns_lock() ); 235 wait_( lk); 236 if ( except_) { 237 std::rethrow_exception( except_); 238 } 239 return * value_; 240 } 241 242 public: 243 typedef intrusive_ptr< shared_state > ptr_type; 244 245 shared_state() = default; 246 247 virtual ~shared_state() = default; 248 249 shared_state( shared_state const&) = delete; 250 shared_state & operator=( shared_state const&) = delete; 251 set_value(R & value)252 void set_value( R & value) { 253 std::unique_lock< mutex > lk{ mtx_ }; 254 set_value_( value, lk); 255 } 256 get()257 R & get() { 258 std::unique_lock< mutex > lk{ mtx_ }; 259 return get_( lk); 260 } 261 }; 262 263 template<> 264 class shared_state< void > : public shared_state_base { 265 private: 266 inline set_value_(std::unique_lock<mutex> & lk)267 void set_value_( std::unique_lock< mutex > & lk) { 268 BOOST_ASSERT( lk.owns_lock() ); 269 if ( BOOST_UNLIKELY( ready_) ) { 270 throw promise_already_satisfied(); 271 } 272 mark_ready_and_notify_( lk); 273 } 274 275 inline get_(std::unique_lock<mutex> & lk)276 void get_( std::unique_lock< mutex > & lk) { 277 BOOST_ASSERT( lk.owns_lock() ); 278 wait_( lk); 279 if ( except_) { 280 std::rethrow_exception( except_); 281 } 282 } 283 284 public: 285 typedef intrusive_ptr< shared_state > ptr_type; 286 287 shared_state() = default; 288 289 virtual ~shared_state() = default; 290 291 shared_state( shared_state const&) = delete; 292 shared_state & operator=( shared_state const&) = delete; 293 294 inline set_value()295 void set_value() { 296 std::unique_lock< mutex > lk{ mtx_ }; 297 set_value_( lk); 298 } 299 300 inline get()301 void get() { 302 std::unique_lock< mutex > lk{ mtx_ }; 303 get_( lk); 304 } 305 }; 306 307 }}} 308 309 #ifdef BOOST_HAS_ABI_HEADERS 310 # include BOOST_ABI_SUFFIX 311 #endif 312 313 #endif // BOOST_FIBERS_DETAIL_SHARED_STATE_H 314