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

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

cpp: make sure that promise::get_future increments the refcount since the state is shared between the future and the promise

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