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

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

cpp: moved all the at_exit logic to two functions which are to be implemented later to avoid TODO duplication throughout the code

  • Property mode set to 100644
File size: 12.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 this->state_->set_value(val, false);
228 aux::set_state_value_at_thread_exit(state_);
229 }
230
231 void set_value_at_thread_exit(R&& val)
232 {
233 if (!this->state_)
234 throw future_error{make_error_code(future_errc::no_state)};
235 if (this->state_->is_set())
236 {
237 throw future_error{
238 make_error_code(future_errc::promise_already_satisfied)
239 };
240 }
241
242 this->state_->set_value(forward<R>(val), false);
243 aux::set_state_value_at_thread_exit(state_);
244 }
245 };
246
247 template<class R>
248 class promise<R&>: public aux::promise_base<R*>
249 {
250 public:
251 promise()
252 : aux::promise_base<R*>{}
253 { /* DUMMY BODY */ }
254
255 template<class Allocator>
256 promise(allocator_arg_t tag, const Allocator& a)
257 : aux::promise_base<R*>{tag, a}
258 { /* DUMMY BODY */ }
259
260 promise(promise&& rhs) noexcept
261 : aux::promise_base<R*>{move(rhs)}
262 { /* DUMMY BODY */ }
263
264 promise(const promise&) = delete;
265
266 ~promise() override = default;
267
268 promise& operator=(promise&& rhs) noexcept = default;
269
270 promise& operator=(const promise&) = delete;
271
272 future<R&> get_future()
273 {
274 assert(this->state_);
275
276 /**
277 * Note: Future constructor that takes a shared
278 * state as its argument does not call increment
279 * because a future can be created as the only
280 * owner of that state (e.g. when created by
281 * std::async), so we have to do it here.
282 */
283 this->state_->increment();
284
285 return future<R&>{this->state_};
286 }
287
288 void set_value(R& val)
289 {
290 if (!this->state_)
291 throw future_error{make_error_code(future_errc::no_state)};
292 if (this->state_->is_set())
293 {
294 throw future_error{
295 make_error_code(future_errc::promise_already_satisfied)
296 };
297 }
298
299 this->state_->set_value(&val, true);
300 }
301
302 void set_value_at_thread_exit(R& val)
303 {
304 if (!this->state_)
305 throw future_error{make_error_code(future_errc::no_state)};
306 if (this->state_->is_set())
307 {
308 throw future_error{
309 make_error_code(future_errc::promise_already_satisfied)
310 };
311 }
312
313 this->state_->set_value(&val, false);
314 aux::set_state_value_at_thread_exit(state_);
315 }
316 };
317
318 template<>
319 class promise<void>: public aux::promise_base<void>
320 {
321 public:
322 promise()
323 : aux::promise_base<void>{}
324 { /* DUMMY BODY */ }
325
326 template<class Allocator>
327 promise(allocator_arg_t tag, const Allocator& a)
328 : aux::promise_base<void>{tag, a}
329 { /* DUMMY BODY */ }
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.