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

Last change on this file was 8e24583, checked in by Jaroslav Jindrak <dzejrou@…>, 6 years ago

cpp: write tests for <future> and fix minor bugs they found

  • Property mode set to 100644
File size: 13.6 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/exception.hpp>
33#include <__bits/memory/allocator_traits.hpp>
34#include <__bits/thread/future.hpp>
35#include <__bits/thread/shared_state.hpp>
36#include <__bits/aux.hpp>
37#include <utility>
38
39namespace std
40{
41 /**
42 * 30.6.5, class template promise:
43 */
44
45 namespace aux
46 {
47 template<class R>
48 class promise_base
49 {
50 public:
51 promise_base()
52 : state_{new aux::shared_state<R>{}}
53 { /* DUMMY BODY */ }
54
55 template<class Allocator>
56 promise_base(allocator_arg_t, const Allocator& a)
57 : state_{}
58 {
59 typename allocator_traits<
60 Allocator
61 >::template rebind_alloc<aux::shared_state<R>> rebound{a};
62
63 state_ = rebound.allocate(1);
64 rebound.construct(state_);
65 }
66
67 promise_base(promise_base&& rhs) noexcept
68 : state_{}
69 {
70 state_ = rhs.state_;
71 rhs.state_ = nullptr;
72 }
73
74 promise_base(const promise_base&) = delete;
75
76 virtual ~promise_base()
77 {
78 this->abandon_state_();
79 }
80
81 promise_base& operator=(promise_base&& rhs) noexcept
82 {
83 abandon_state_();
84 promise_base{move(rhs)}.swap(*this);
85
86 return *this;
87 }
88
89 promise_base& operator=(const promise_base&) = delete;
90
91 void swap(promise_base& other) noexcept
92 {
93 std::swap(state_, other.state_);
94 }
95
96 void set_exception(exception_ptr ptr)
97 {
98 assert(state_);
99
100 state_->set_exception(ptr);
101 }
102
103 void set_exception_at_thread_exit(exception_ptr ptr)
104 {
105 assert(state_);
106
107 state_->set_exception_ptr(ptr, false);
108 aux::set_state_exception_at_thread_exit(state_);
109 }
110
111 /**
112 * Useful for testing as we can check some information
113 * otherwise unavailable to us without waiting, e.g.
114 * to check whether the state is ready, its reference
115 * count etc.
116 */
117 aux::shared_state<R>* __state()
118 {
119 return state_;
120 }
121
122 protected:
123 void abandon_state_()
124 {
125 if (!state_)
126 return;
127
128 /**
129 * Note: This is the 'abandon' move described in
130 * 30.6.4 (7).
131 * 1) If state is not ready:
132 * a) Store exception of type future_error with
133 * error condition broken_promise.
134 * b) Mark state as ready.
135 * 2) Release the state.
136 */
137
138 if (!state_->is_set())
139 {
140 state_->set_exception(make_exception_ptr(
141 future_error{make_error_code(future_errc::broken_promise)}
142 ));
143 state_->mark_set(true);
144 }
145
146 if (state_->decrement())
147 {
148 state_->destroy();
149 delete state_;
150 state_ = nullptr;
151 }
152 }
153
154 aux::shared_state<R>* state_;
155 };
156 }
157
158 template<class R>
159 class promise: public aux::promise_base<R>
160 {
161 public:
162 promise()
163 : aux::promise_base<R>{}
164 { /* DUMMY BODY */ }
165
166 template<class Allocator>
167 promise(allocator_arg_t tag, const Allocator& a)
168 : aux::promise_base<R>{tag, a}
169 { /* DUMMY BODY */ }
170
171 promise(promise&& rhs) noexcept
172 : aux::promise_base<R>{move(rhs)}
173 { /* DUMMY BODY */ }
174
175 promise(const promise&) = delete;
176
177 ~promise() override = default;
178
179 promise& operator=(promise&& rhs) noexcept = default;
180
181 promise& operator=(const promise&) = delete;
182
183 future<R> get_future()
184 {
185 assert(this->state_);
186
187 /**
188 * Note: Future constructor that takes a shared
189 * state as its argument does not call increment
190 * because a future can be created as the only
191 * owner of that state (e.g. when created by
192 * std::async), so we have to do it here.
193 */
194 this->state_->increment();
195
196 return future<R>{this->state_};
197 }
198
199 void set_value(const R& val)
200 {
201 if (!this->state_)
202 throw future_error{make_error_code(future_errc::no_state)};
203 if (this->state_->is_set())
204 {
205 throw future_error{
206 make_error_code(future_errc::promise_already_satisfied)
207 };
208 }
209
210 this->state_->set_value(val, true);
211 }
212
213 void set_value(R&& val)
214 {
215 if (!this->state_)
216 throw future_error{make_error_code(future_errc::no_state)};
217 if (this->state_->is_set())
218 {
219 throw future_error{
220 make_error_code(future_errc::promise_already_satisfied)
221 };
222 }
223
224 this->state_->set_value(forward<R>(val), true);
225 }
226
227 void set_value_at_thread_exit(const R& val)
228 {
229 if (!this->state_)
230 throw future_error{make_error_code(future_errc::no_state)};
231 if (this->state_->is_set())
232 {
233 throw future_error{
234 make_error_code(future_errc::promise_already_satisfied)
235 };
236 }
237
238 try
239 {
240 this->state_->set_value(val, false);
241 aux::set_state_value_at_thread_exit(this->state_);
242 }
243 catch(const exception& __exception)
244 {
245 this->state_->set_exception(make_exception_ptr(__exception), false);
246 aux::set_state_exception_at_thread_exit(this->state_);
247 }
248 }
249
250 void set_value_at_thread_exit(R&& val)
251 {
252 if (!this->state_)
253 throw future_error{make_error_code(future_errc::no_state)};
254 if (this->state_->is_set())
255 {
256 throw future_error{
257 make_error_code(future_errc::promise_already_satisfied)
258 };
259 }
260
261 try
262 {
263 this->state_->set_value(forward<R>(val), false);
264 aux::set_state_value_at_thread_exit(this->state_);
265 }
266 catch(const exception& __exception)
267 {
268 this->state_->set_exception(make_exception_ptr(__exception), false);
269 aux::set_state_exception_at_thread_exit(this->state_);
270 }
271 }
272 };
273
274 template<class R>
275 class promise<R&>: public aux::promise_base<R*>
276 {
277 public:
278 promise()
279 : aux::promise_base<R*>{}
280 { /* DUMMY BODY */ }
281
282 template<class Allocator>
283 promise(allocator_arg_t tag, const Allocator& a)
284 : aux::promise_base<R*>{tag, a}
285 { /* DUMMY BODY */ }
286
287 promise(promise&& rhs) noexcept
288 : aux::promise_base<R*>{move(rhs)}
289 { /* DUMMY BODY */ }
290
291 promise(const promise&) = delete;
292
293 ~promise() override = default;
294
295 promise& operator=(promise&& rhs) noexcept = default;
296
297 promise& operator=(const promise&) = delete;
298
299 future<R&> get_future()
300 {
301 assert(this->state_);
302
303 /**
304 * Note: Future constructor that takes a shared
305 * state as its argument does not call increment
306 * because a future can be created as the only
307 * owner of that state (e.g. when created by
308 * std::async), so we have to do it here.
309 */
310 this->state_->increment();
311
312 return future<R&>{this->state_};
313 }
314
315 void set_value(R& val)
316 {
317 if (!this->state_)
318 throw future_error{make_error_code(future_errc::no_state)};
319 if (this->state_->is_set())
320 {
321 throw future_error{
322 make_error_code(future_errc::promise_already_satisfied)
323 };
324 }
325
326 this->state_->set_value(&val, true);
327 }
328
329 void set_value_at_thread_exit(R& val)
330 {
331 if (!this->state_)
332 throw future_error{make_error_code(future_errc::no_state)};
333 if (this->state_->is_set())
334 {
335 throw future_error{
336 make_error_code(future_errc::promise_already_satisfied)
337 };
338 }
339
340 try
341 {
342 this->state_->set_value(&val, false);
343 aux::set_state_value_at_thread_exit(this->state_);
344 }
345 catch(const exception& __exception)
346 {
347 this->state_->set_exception(make_exception_ptr(__exception), false);
348 aux::set_state_exception_at_thread_exit(this->state_);
349 }
350 }
351 };
352
353 template<>
354 class promise<void>: public aux::promise_base<void>
355 {
356 public:
357 promise()
358 : aux::promise_base<void>{}
359 { /* DUMMY BODY */ }
360
361 template<class Allocator>
362 promise(allocator_arg_t tag, const Allocator& a)
363 : aux::promise_base<void>{tag, a}
364 { /* DUMMY BODY */ }
365
366 promise(promise&& rhs) noexcept
367 : aux::promise_base<void>{move(rhs)}
368 { /* DUMMY BODY */ }
369
370 promise(const promise&) = delete;
371
372 ~promise() override = default;
373
374 promise& operator=(promise&& rhs) noexcept = default;
375
376 promise& operator=(const promise&) = delete;
377
378 future<void> get_future()
379 {
380 assert(this->state_);
381
382 /**
383 * Note: Future constructor that takes a shared
384 * state as its argument does not call increment
385 * because a future can be created as the only
386 * owner of that state (e.g. when created by
387 * std::async), so we have to do it here.
388 */
389 this->state_->increment();
390
391 return future<void>{this->state_};
392 }
393
394 void set_value()
395 {
396 if (!this->state_)
397 throw future_error{make_error_code(future_errc::no_state)};
398 if (this->state_->is_set())
399 {
400 throw future_error{
401 make_error_code(future_errc::promise_already_satisfied)
402 };
403 }
404
405 this->state_->set_value();
406 }
407 };
408
409 template<class R>
410 void swap(promise<R>& lhs, promise<R>& rhs) noexcept
411 {
412 lhs.swap(rhs);
413 }
414
415 template<class R, class Alloc>
416 struct uses_allocator<promise<R>, Alloc>: true_type
417 { /* DUMMY BODY */ };
418}
419
420#endif
Note: See TracBrowser for help on using the repository browser.