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 
promise_baseboost::fibers::detail::promise_base33     promise_base() :
34         promise_base{ std::allocator_arg, std::allocator< promise_base >{} } {
35     }
36 
37     template< typename Allocator >
promise_baseboost::fibers::detail::promise_base38     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 
~promise_baseboost::fibers::detail::promise_base55     ~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 
promise_baseboost::fibers::detail::promise_base64     promise_base( promise_base && other) noexcept :
65         obtained_{ other.obtained_ },
66         future_{ std::move( other.future_) } {
67         other.obtained_ = false;
68     }
69 
operator =boost::fibers::detail::promise_base70     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 
get_futureboost::fibers::detail::promise_base78     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 
swapboost::fibers::detail::promise_base89     void swap( promise_base & other) noexcept {
90         std::swap( obtained_, other.obtained_);
91         future_.swap( other.future_);
92     }
93 
set_exceptionboost::fibers::detail::promise_base94     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 >
promise(std::allocator_arg_t,Allocator alloc)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 
set_value(R const & value)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 
set_value(R && value)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 
swap(promise & other)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 >
promise(std::allocator_arg_t,Allocator alloc)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 
set_value(R & value)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 
swap(promise & other)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 >
promise(std::allocator_arg_t,Allocator alloc)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
set_value()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
swap(promise & other)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 >
swap(promise<R> & l,promise<R> & 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