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