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

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

cpp: make sure exceptions are stored when we set value at thread exit too

  • Property mode set to 100644
File size: 13.2 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 protected:
112 void abandon_state_()
113 {
114 if (!state_)
115 return;
116
117 /**
118 * Note: This is the 'abandon' move described in
119 * 30.6.4 (7).
120 * 1) If state is not ready:
121 * a) Store exception of type future_error with
122 * error condition broken_promise.
123 * b) Mark state as ready.
124 * 2) Release the state.
125 */
126
127 if (!state_->is_set())
128 {
129 state_->set_exception(make_exception_ptr(
130 future_error{make_error_code(future_errc::broken_promise)}
131 ));
132 state_->mark_set(true);
133 }
134
135 if (state_->decrement())
136 {
137 state_->destroy();
138 delete state_;
139 state_ = nullptr;
140 }
141 }
142
143 aux::shared_state<R>* state_;
144 };
145 }
146
147 template<class R>
148 class promise: public aux::promise_base<R>
149 {
150 public:
151 promise()
152 : aux::promise_base<R>{}
153 { /* DUMMY BODY */ }
154
155 template<class Allocator>
156 promise(allocator_arg_t tag, const Allocator& a)
157 : aux::promise_base<R>{tag, a}
158 { /* DUMMY BODY */ }
159
160 promise(promise&& rhs) noexcept
161 : aux::promise_base<R>{move(rhs)}
162 { /* DUMMY BODY */ }
163
164 promise(const promise&) = delete;
165
166 ~promise() override = default;
167
168 promise& operator=(promise&& rhs) noexcept = default;
169
170 promise& operator=(const promise&) = delete;
171
172 future<R> get_future()
173 {
174 assert(this->state_);
175
176 /**
177 * Note: Future constructor that takes a shared
178 * state as its argument does not call increment
179 * because a future can be created as the only
180 * owner of that state (e.g. when created by
181 * std::async), so we have to do it here.
182 */
183 this->state_->increment();
184
185 return future<R>{this->state_};
186 }
187
188 void set_value(const R& val)
189 {
190 if (!this->state_)
191 throw future_error{make_error_code(future_errc::no_state)};
192 if (this->state_->is_set())
193 {
194 throw future_error{
195 make_error_code(future_errc::promise_already_satisfied)
196 };
197 }
198
199 this->state_->set_value(val, true);
200 }
201
202 void set_value(R&& val)
203 {
204 if (!this->state_)
205 throw future_error{make_error_code(future_errc::no_state)};
206 if (this->state_->is_set())
207 {
208 throw future_error{
209 make_error_code(future_errc::promise_already_satisfied)
210 };
211 }
212
213 this->state_->set_value(forward<R>(val), true);
214 }
215
216 void set_value_at_thread_exit(const R& val)
217 {
218 if (!this->state_)
219 throw future_error{make_error_code(future_errc::no_state)};
220 if (this->state_->is_set())
221 {
222 throw future_error{
223 make_error_code(future_errc::promise_already_satisfied)
224 };
225 }
226
227 try
228 {
229 state_->set_value(invoke(func_, args...), false);
230 aux::set_state_value_at_thread_exit(this->state_);
231 }
232 catch(const exception& __exception)
233 {
234 state_->set_exception(make_exception_ptr(__exception), false);
235 aux::set_state_exception_at_thread_exit(this->state_);
236 }
237 }
238
239 void set_value_at_thread_exit(R&& val)
240 {
241 if (!this->state_)
242 throw future_error{make_error_code(future_errc::no_state)};
243 if (this->state_->is_set())
244 {
245 throw future_error{
246 make_error_code(future_errc::promise_already_satisfied)
247 };
248 }
249
250 try
251 {
252 state_->set_value(invoke(func_, args...), false);
253 aux::set_state_value_at_thread_exit(this->state_);
254 }
255 catch(const exception& __exception)
256 {
257 state_->set_exception(make_exception_ptr(__exception), false);
258 aux::set_state_exception_at_thread_exit(this->state_);
259 }
260 }
261 };
262
263 template<class R>
264 class promise<R&>: public aux::promise_base<R*>
265 {
266 public:
267 promise()
268 : aux::promise_base<R*>{}
269 { /* DUMMY BODY */ }
270
271 template<class Allocator>
272 promise(allocator_arg_t tag, const Allocator& a)
273 : aux::promise_base<R*>{tag, a}
274 { /* DUMMY BODY */ }
275
276 promise(promise&& rhs) noexcept
277 : aux::promise_base<R*>{move(rhs)}
278 { /* DUMMY BODY */ }
279
280 promise(const promise&) = delete;
281
282 ~promise() override = default;
283
284 promise& operator=(promise&& rhs) noexcept = default;
285
286 promise& operator=(const promise&) = delete;
287
288 future<R&> get_future()
289 {
290 assert(this->state_);
291
292 /**
293 * Note: Future constructor that takes a shared
294 * state as its argument does not call increment
295 * because a future can be created as the only
296 * owner of that state (e.g. when created by
297 * std::async), so we have to do it here.
298 */
299 this->state_->increment();
300
301 return future<R&>{this->state_};
302 }
303
304 void set_value(R& val)
305 {
306 if (!this->state_)
307 throw future_error{make_error_code(future_errc::no_state)};
308 if (this->state_->is_set())
309 {
310 throw future_error{
311 make_error_code(future_errc::promise_already_satisfied)
312 };
313 }
314
315 this->state_->set_value(&val, true);
316 }
317
318 void set_value_at_thread_exit(R& val)
319 {
320 if (!this->state_)
321 throw future_error{make_error_code(future_errc::no_state)};
322 if (this->state_->is_set())
323 {
324 throw future_error{
325 make_error_code(future_errc::promise_already_satisfied)
326 };
327 }
328
329 try
330 {
331 state_->set_value(invoke(func_, args...), false);
332 aux::set_state_value_at_thread_exit(this->state_);
333 }
334 catch(const exception& __exception)
335 {
336 state_->set_exception(make_exception_ptr(__exception), false);
337 aux::set_state_exception_at_thread_exit(this->state_);
338 }
339 }
340 };
341
342 template<>
343 class promise<void>: public aux::promise_base<void>
344 {
345 public:
346 promise()
347 : aux::promise_base<void>{}
348 { /* DUMMY BODY */ }
349
350 template<class Allocator>
351 promise(allocator_arg_t tag, const Allocator& a)
352 : aux::promise_base<void>{tag, a}
353 { /* DUMMY BODY */ }
354
355 promise(promise&& rhs) noexcept
356 : aux::promise_base<void>{move(rhs)}
357 { /* DUMMY BODY */ }
358
359 promise(const promise&) = delete;
360
361 ~promise() override = default;
362
363 promise& operator=(promise&& rhs) noexcept = default;
364
365 promise& operator=(const promise&) = delete;
366
367 future<void> get_future()
368 {
369 assert(this->state_);
370
371 /**
372 * Note: Future constructor that takes a shared
373 * state as its argument does not call increment
374 * because a future can be created as the only
375 * owner of that state (e.g. when created by
376 * std::async), so we have to do it here.
377 */
378 this->state_->increment();
379
380 return future<void>{this->state_};
381 }
382
383 void set_value()
384 {
385 if (!this->state_)
386 throw future_error{make_error_code(future_errc::no_state)};
387 if (this->state_->is_set())
388 {
389 throw future_error{
390 make_error_code(future_errc::promise_already_satisfied)
391 };
392 }
393
394 this->state_->set_value();
395 }
396 };
397
398 template<class R>
399 void swap(promise<R>& lhs, promise<R>& rhs) noexcept
400 {
401 lhs.swap(rhs);
402 }
403
404 template<class R, class Alloc>
405 struct uses_allocator<promise<R>, Alloc>: true_type
406 { /* DUMMY BODY */ };
407}
408
409#endif
Note: See TracBrowser for help on using the repository browser.