source: mainline/uspace/lib/cpp/include/__bits/thread/promise.hpp@ cf279270

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since cf279270 was cf279270, checked in by Jaroslav Jindrak <dzejrou@…>, 6 years ago

cpp: create promise_base and use it to implement all three versions of the promise template

  • Property mode set to 100644
File size: 9.0 KB
Line 
1/*
2 * Copyright (c) 2019 Jaroslav Jindrak
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#ifndef LIBCPP_BITS_THREAD_PROMISE
30#define LIBCPP_BITS_THREAD_PROMISE
31
32#include <__bits/thread/future.hpp>
33#include <__bits/thread/shared_state.hpp>
34#include <__bits/aux.hpp>
35#include <utility>
36
37namespace std
38{
39 /**
40 * 30.6.5, class template promise:
41 */
42
43 namespace aux
44 {
45 template<class R>
46 class promise_base
47 {
48 public:
49 promise_base()
50 : state_{new aux::shared_state<R>{}}
51 { /* DUMMY BODY */ }
52
53 template<class Allocator>
54 promise_base(allocator_arg_t, const Allocator& a)
55 : promise_base{}
56 {
57 // TODO: Use the allocator.
58 }
59
60 promise_base(promise_base&& rhs) noexcept
61 : state_{}
62 {
63 state_ = rhs.state_;
64 rhs.state_ = nullptr;
65 }
66
67 promise_base(const promise_base&) = delete;
68
69 virtual ~promise_base()
70 {
71 this->abandon_state_();
72 }
73
74 promise_base& operator=(promise_base&& rhs) noexcept
75 {
76 abandon_state_();
77 promise_base{std::move(rhs)}.swap(*this);
78
79 return *this;
80 }
81
82 promise_base& operator=(const promise_base&) = delete;
83
84 void swap(promise_base& other) noexcept
85 {
86 std::swap(state_, other.state_);
87 }
88
89 future<R> get_future()
90 {
91 return future<R>{state_};
92 }
93
94 void set_exception(exception_ptr ptr)
95 {
96 assert(state_);
97
98 state_->set_exception(ptr);
99 }
100
101 void set_exception_at_thread_exit(exception_ptr)
102 {
103 // TODO: No exception handling, no-op at this time.
104 }
105
106 protected:
107 void abandon_state_()
108 {
109 /**
110 * Note: This is the 'abandon' move described in
111 * 30.6.4 (7).
112 * 1) If state is not ready:
113 * a) Store exception of type future_error with
114 * error condition broken_promise.
115 * b) Mark state as ready.
116 * 2) Release the state.
117 */
118 }
119
120 aux::shared_state<R>* state_;
121 };
122 }
123
124 template<class R>
125 class promise: public aux::promise_base<R>
126 {
127 public:
128 promise()
129 : aux::promise_base<R>{}
130 { /* DUMMY BODY */ }
131
132 template<class Allocator>
133 promise(allocator_arg_t, const Allocator& a)
134 : aux::promise_base<R>{}
135 {
136 // TODO: Use the allocator.
137 }
138
139 promise(promise&& rhs) noexcept
140 : aux::promise_base<R>{move(rhs)}
141 { /* DUMMY BODY */ }
142
143 promise(const promise&) = delete;
144
145 ~promise() override = default;
146
147 promise& operator=(promise&& rhs) noexcept = default;
148
149 promise& operator=(const promise&) = delete;
150
151 void set_value(const R& val)
152 {
153 if (!this->state_)
154 throw future_error{make_error_code(future_errc::no_state)};
155 if (this->state_->is_set())
156 {
157 throw future_error{
158 make_error_code(future_errc::promise_already_satisfied)
159 };
160 }
161
162 this->state_->set_value(val, true);
163 }
164
165 void set_value(R&& val)
166 {
167 if (!this->state_)
168 throw future_error{make_error_code(future_errc::no_state)};
169 if (this->state_->is_set())
170 {
171 throw future_error{
172 make_error_code(future_errc::promise_already_satisfied)
173 };
174 }
175
176 this->state_->set_value(std::forward<R>(val), true);
177 }
178
179 void set_value_at_thread_exit(const R& val)
180 {
181 if (!this->state_)
182 throw future_error{make_error_code(future_errc::no_state)};
183 if (this->state_->is_set())
184 {
185 throw future_error{
186 make_error_code(future_errc::promise_already_satisfied)
187 };
188 }
189
190 this->state_->set_value(val, false);
191 // TODO: schedule it to be set as ready when thread exits
192 }
193
194 void set_value_at_thread_exit(R&& val)
195 {
196 if (!this->state_)
197 throw future_error{make_error_code(future_errc::no_state)};
198 if (this->state_->is_set())
199 {
200 throw future_error{
201 make_error_code(future_errc::promise_already_satisfied)
202 };
203 }
204
205 this->state_->set_value(std::forward<R>(val), false);
206 // TODO: schedule it to be set as ready when thread exits
207 }
208 };
209
210 template<class R>
211 class promise<R&>: public aux::promise_base<R>
212 { // TODO: I'm afraid we will need aux::shared_state<R&> specialization for this :/
213 public:
214 promise()
215 : aux::promise_base<R&>{}
216 { /* DUMMY BODY */ }
217
218 template<class Allocator>
219 promise(allocator_arg_t, const Allocator& a)
220 : aux::promise_base<R&>{}
221 {
222 // TODO: Use the allocator.
223 }
224
225 promise(promise&& rhs) noexcept
226 : aux::promise_base<R&>{move(rhs)}
227 { /* DUMMY BODY */ }
228
229 promise(const promise&) = delete;
230
231 ~promise() override = default;
232
233 promise& operator=(promise&& rhs) noexcept = default;
234
235 promise& operator=(const promise&) = delete;
236 };
237
238 template<>
239 class promise<void>: public aux::promise_base<void>
240 {
241 public:
242 promise()
243 : aux::promise_base<void>{}
244 { /* DUMMY BODY */ }
245
246 template<class Allocator>
247 promise(allocator_arg_t, const Allocator& a)
248 : aux::promise_base<void>{}
249 {
250 // TODO: Use the allocator.
251 }
252
253 promise(promise&& rhs) noexcept
254 : aux::promise_base<void>{move(rhs)}
255 { /* DUMMY BODY */ }
256
257 promise(const promise&) = delete;
258
259 ~promise() override = default;
260
261 promise& operator=(promise&& rhs) noexcept = default;
262
263 promise& operator=(const promise&) = delete;
264
265 void set_value()
266 {
267 if (!this->state_)
268 throw future_error{make_error_code(future_errc::no_state)};
269 if (this->state_->is_set())
270 {
271 throw future_error{
272 make_error_code(future_errc::promise_already_satisfied)
273 };
274 }
275
276 this->state_->set_value();
277 }
278 };
279
280 template<class R>
281 void swap(promise<R>& lhs, promise<R>& rhs) noexcept
282 {
283 lhs.swap(rhs);
284 }
285
286 template<class R, class Alloc>
287 struct uses_allocator<promise<R>, Alloc>: true_type
288 { /* DUMMY BODY */ };
289}
290
291#endif
Note: See TracBrowser for help on using the repository browser.