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 
16 #include <boost/fiber/detail/convert.hpp>
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_t   ptr_t;
29 
30     bool            obtained_{ false };
31     ptr_t           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_t;
40         typedef std::allocator_traits< typename object_t::allocator_t > traits_t;
41         typename object_t::allocator_t a{ alloc };
42         typename traits_t::pointer ptr{ traits_t::allocate( a, 1) };
43 
44         try {
45             traits_t::construct( a, ptr, a);
46         } catch (...) {
47             traits_t::deallocate( a, ptr, 1);
48             throw;
49         }
50         future_.reset( convert( ptr) );
51     }
52 
~promise_baseboost::fibers::detail::promise_base53     ~promise_base() {
54         if ( future_) {
55             future_->owner_destroyed();
56         }
57     }
58 
59     promise_base( promise_base const&) = delete;
60     promise_base & operator=( promise_base const&) = delete;
61 
promise_baseboost::fibers::detail::promise_base62     promise_base( promise_base && other) noexcept :
63         obtained_{ other.obtained_ },
64         future_{ std::move( other.future_) } {
65         other.obtained_ = false;
66     }
67 
operator =boost::fibers::detail::promise_base68     promise_base & operator=( promise_base && other) noexcept {
69         if ( this == & other) return * this;
70         promise_base tmp{ std::move( other) };
71         swap( tmp);
72         return * this;
73     }
74 
get_futureboost::fibers::detail::promise_base75     future< R > get_future() {
76         if ( obtained_) {
77             throw future_already_retrieved{};
78         }
79         if ( ! future_) {
80             throw promise_uninitialized{};
81         }
82         obtained_ = true;
83         return future< R >{ future_ };
84     }
85 
swapboost::fibers::detail::promise_base86     void swap( promise_base & other) noexcept {
87         std::swap( obtained_, other.obtained_);
88         future_.swap( other.future_);
89     }
90 
set_exceptionboost::fibers::detail::promise_base91     void set_exception( std::exception_ptr p) {
92         if ( ! future_) {
93             throw promise_uninitialized{};
94         }
95         future_->set_exception( p);
96     }
97 };
98 
99 }
100 
101 template< typename R >
102 class promise : private detail::promise_base< R > {
103 private:
104     typedef detail::promise_base< R >  base_t;
105 
106 public:
107     promise() = default;
108 
109     template< typename Allocator >
promise(std::allocator_arg_t,Allocator alloc)110     promise( std::allocator_arg_t, Allocator alloc) :
111         base_t{ std::allocator_arg, alloc } {
112     }
113 
114     promise( promise const&) = delete;
115     promise & operator=( promise const&) = delete;
116 
117     promise( promise && other) noexcept = default;
118     promise & operator=( promise && other) = default;
119 
set_value(R const & value)120     void set_value( R const& value) {
121         if ( ! base_t::future_) {
122             throw promise_uninitialized{};
123         }
124         base_t::future_->set_value( value);
125     }
126 
set_value(R && value)127     void set_value( R && value) {
128         if ( ! base_t::future_) {
129             throw promise_uninitialized{};
130         }
131         base_t::future_->set_value( std::move( value) );
132     }
133 
swap(promise & other)134     void swap( promise & other) noexcept {
135         base_t::swap( other);
136     }
137 
138     using base_t::get_future;
139     using base_t::set_exception;
140 };
141 
142 template< typename R >
143 class promise< R & > : private detail::promise_base< R & > {
144 private:
145     typedef detail::promise_base< R & >  base_t;
146 
147 public:
148     promise() = default;
149 
150     template< typename Allocator >
promise(std::allocator_arg_t,Allocator alloc)151     promise( std::allocator_arg_t, Allocator alloc) :
152         base_t{ std::allocator_arg, alloc } {
153     }
154 
155     promise( promise const&) = delete;
156     promise & operator=( promise const&) = delete;
157 
158     promise( promise && other) noexcept = default;
159     promise & operator=( promise && other) noexcept = default;
160 
set_value(R & value)161     void set_value( R & value) {
162         if ( ! base_t::future_) {
163             throw promise_uninitialized{};
164         }
165         base_t::future_->set_value( value);
166     }
167 
swap(promise & other)168     void swap( promise & other) noexcept {
169         base_t::swap( other);
170     }
171 
172     using base_t::get_future;
173     using base_t::set_exception;
174 };
175 
176 template<>
177 class promise< void > : private detail::promise_base< void > {
178 private:
179     typedef detail::promise_base< void >  base_t;
180 
181 public:
182     promise() = default;
183 
184     template< typename Allocator >
promise(std::allocator_arg_t,Allocator alloc)185     promise( std::allocator_arg_t, Allocator alloc) :
186         base_t{ std::allocator_arg, alloc } {
187     }
188 
189     promise( promise const&) = delete;
190     promise & operator=( promise const&) = delete;
191 
192     promise( promise && other) noexcept = default;
193     promise & operator=( promise && other) noexcept = default;
194 
195     inline
set_value()196     void set_value() {
197         if ( ! base_t::future_) {
198             throw promise_uninitialized{};
199         }
200         base_t::future_->set_value();
201     }
202 
203     inline
swap(promise & other)204     void swap( promise & other) noexcept {
205         base_t::swap( other);
206     }
207 
208     using base_t::get_future;
209     using base_t::set_exception;
210 };
211 
212 template< typename R >
swap(promise<R> & l,promise<R> & r)213 void swap( promise< R > & l, promise< R > & r) noexcept {
214     l.swap( r);
215 }
216 
217 }}
218 
219 #endif // BOOST_FIBERS_PROMISE_HPP
220