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

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

cpp: add stub exception support

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