source: mainline/uspace/lib/cpp/include/__bits/thread/future.hpp@ 9eea0b7

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

cpp: fix the reference version set_value

  • Property mode set to 100644
File size: 17.0 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_FUTURE
30#define LIBCPP_BITS_THREAD_FUTURE
31
32#include <__bits/refcount_obj.hpp>
33#include <__bits/thread/threading.hpp>
34#include <cassert>
35#include <memory>
36#include <system_error>
37#include <type_traits>
38#include <utility>
39
40namespace std
41{
42 /**
43 * 30.6, futures:
44 */
45
46 enum class future_errc
47 { // The 5001 start is to not collide with system_error's codes.
48 broken_promise = 5001,
49 future_already_retrieved,
50 promise_already_satisfied,
51 no_state
52 };
53
54 enum class launch
55 {
56 async,
57 deferred
58 };
59
60 enum class future_status
61 {
62 ready,
63 timeout,
64 deferred
65 };
66
67 /**
68 * 30.6.2, error handling:
69 */
70
71 template<>
72 struct is_error_code_enum<future_errc>: true_type
73 { /* DUMMY BODY */ };
74
75 error_code make_error_code(future_errc) noexcept;
76 error_condition make_error_condition(future_errc) noexcept;
77
78 const error_category& future_category() noexcept;
79
80 /**
81 * 30.6.3, class future_error:
82 */
83
84 class future_error: public logic_error
85 {
86 public:
87 future_error(error_code ec);
88
89 const error_code& code() const noexcept;
90 const char* what() const noexcept;
91
92 private:
93 error_code code_;
94 };
95
96 /**
97 * 30.6.4, shared state:
98 */
99
100 namespace aux
101 {
102 template<class R>
103 class shared_state: public aux::refcount_obj
104 {
105 public:
106 const bool is_deferred_function;
107
108 shared_state(bool is_deferred = false)
109 : is_deferred_function{is_deferred}, mutex_{},
110 condvar_{}, value_{}, value_set_{false},
111 exception_{}, has_exception_{false}
112 {
113 threading::mutex::init(mutex_);
114 threading::condvar::init(condvar_);
115 }
116
117 void destroy() override
118 {
119 if (this->refs() < 1)
120 {
121 // TODO: what to destroy? just this?
122 }
123 }
124
125 void set_value(const R& val, bool set)
126 {
127 aux::threading::mutex::lock(mutex_);
128 value_ = val;
129 value_set_ = set;
130 aux::threading::mutex::unlock(mutex_);
131
132 aux::threading::condvar::broadcast(condvar_);
133 }
134
135 void set_value(R&& val, bool set)
136 {
137 aux::threading::mutex::lock(mutex_);
138 value_ = std::move(val);
139 value_set_ = set;
140 aux::threading::mutex::unlock(mutex_);
141
142 aux::threading::condvar::broadcast(condvar_);
143 }
144
145 void set_set(bool set = true) noexcept
146 {
147 value_set_ = set;
148 }
149
150 bool is_set() const noexcept
151 {
152 return value_set_;
153 }
154
155 R& get()
156 {
157 return value_;
158 }
159
160 void set_exception(exception_ptr ptr)
161 {
162 exception_ = ptr;
163 has_exception_ = true;
164 }
165
166 bool has_exception() const noexcept
167 {
168 return has_exception_;
169 }
170
171 void throw_stored_exception() const
172 {
173 // TODO: implement
174 }
175
176 /**
177 * TODO: This member function is supposed to be marked
178 * as 'const'. In such a case, however, we cannot
179 * use the underlying fibril API because these
180 * references get converted to pointers and the API
181 * does not accept e.g. 'const fibril_condvar_t*'.
182 *
183 * The same applies to the wait_for and wait_until
184 * functions.
185 */
186 void wait()
187 {
188 aux::threading::mutex::lock(mutex_);
189 while (!value_set_)
190 aux::threading::condvar::wait(condvar_, mutex_);
191 aux::threading::mutex::unlock(mutex_);
192 }
193
194 template<class Rep, class Period>
195 bool wait_for(const chrono::duration<Rep, Period>& rel_time)
196 {
197 aux::threading::mutex::lock(mutex_);
198 aux::threading::condvar::wait_for(
199 condvar_, mutex_,
200 aux::threading::time::convert(rel_time)
201 );
202 aux::threading::mutex::unlock(mutex_);
203
204 return value_set_;
205 }
206
207 template<class Clock, class Duration>
208 bool wait_until(const chrono::time_point<Clock, Duration>& abs_time)
209 {
210 aux::threading::mutex::lock(mutex_);
211 aux::threading::condvar::wait_for(
212 condvar_, mutex_,
213 aux::threading::time::convert(abs_time - Clock::now())
214 );
215 aux::threading::mutex::unlock(mutex_);
216
217 return value_set_;
218 }
219
220 ~shared_state()
221 {
222 // TODO: just destroy?
223 }
224
225 private:
226 aux::mutex_t mutex_;
227 aux::condvar_t condvar_;
228
229 R value_;
230 bool value_set_;
231
232 exception_ptr exception_;
233 bool has_exception_;
234 };
235 }
236
237 template<class R>
238 class future;
239
240 template<class R>
241 class promise
242 {
243 public:
244 promise()
245 : state_{new aux::shared_state<R>{}}
246 { /* DUMMY BODY */ }
247
248 template<class Allocator>
249 promise(allocator_arg_t, const Allocator& a)
250 : promise{}
251 {
252 // TODO: Use the allocator.
253 }
254
255 promise(promise&& rhs) noexcept
256 : state_{}
257 {
258 state_ = rhs.state_;
259 rhs.state_ = nullptr;
260 }
261
262 promise(const promise&) = delete;
263
264 ~promise()
265 {
266 abandon_state_();
267 }
268
269 promise& operator=(promise&& rhs) noexcept
270 {
271 abandon_state_();
272 promise{std::move(rhs)}.swap(*this);
273 }
274
275 promise& operator=(const promise&) = delete;
276
277 void swap(promise& other) noexcept
278 {
279 std::swap(state_, other.state_);
280 }
281
282 future<R> get_future()
283 {
284 return future<R>{state_};
285 }
286
287 void set_value(const R& val)
288 {
289 if (!state_)
290 throw future_error{make_error_code(future_errc::no_state)};
291 if (state_->is_set())
292 {
293 throw future_error{
294 make_error_code(future_errc::promise_already_satisfied)
295 };
296 }
297
298 state_->set_value(val, true);
299 }
300
301 void set_value(R&& val)
302 {
303 if (!state_)
304 throw future_error{make_error_code(future_errc::no_state)};
305 if (state_->is_set())
306 {
307 throw future_error{
308 make_error_code(future_errc::promise_already_satisfied)
309 };
310 }
311
312 state_->set_value(std::forward<R>(val), true);
313 }
314
315 void set_exception(exception_ptr ptr)
316 {
317 assert(state_);
318
319 state_->set_exception(ptr);
320 }
321
322 void set_value_at_thread_exit(const R& val)
323 {
324 if (!state_)
325 throw future_error{make_error_code(future_errc::no_state)};
326 if (state_->is_set())
327 {
328 throw future_error{
329 make_error_code(future_errc::promise_already_satisfied)
330 };
331 }
332
333 state_->set_value(val, false);
334 // TODO: schedule it to be set as ready when thread exits
335 }
336
337 void set_value_at_thread_exit(R&& val)
338 {
339 if (!state_)
340 throw future_error{make_error_code(future_errc::no_state)};
341 if (state_->is_set())
342 {
343 throw future_error{
344 make_error_code(future_errc::promise_already_satisfied)
345 };
346 }
347
348 state_->set_value(std::forward<R>(val), false);
349 // TODO: schedule it to be set as ready when thread exits
350 }
351
352 void set_exception_at_thread_exit(exception_ptr)
353 {
354 // TODO: No exception handling, no-op at this time.
355 }
356
357 private:
358 void abandon_state_()
359 {
360 /**
361 * 1) If state is not ready:
362 * a) Store exception of type future_error with
363 * error condition broken_promise.
364 * b) Mark state as ready.
365 * 2) Rekease the state.
366 */
367 }
368
369 aux::shared_state<R>* state_;
370 };
371
372 template<class R>
373 class promise<R&>
374 {
375 // TODO: Copy & modify once promise is done.
376 };
377
378 template<>
379 class promise<void>
380 {
381 // TODO: Copy & modify once promise is done.
382 };
383
384 template<class R>
385 void swap(promise<R>& lhs, promise<R>& rhs) noexcept
386 {
387 lhs.swap(rhs);
388 }
389
390 template<class R, class Alloc>
391 struct uses_allocator<promise<R>, Alloc>: true_type
392 { /* DUMMY BODY */ };
393
394 template<class R>
395 class shared_future;
396
397 template<class R>
398 class future
399 {
400 public:
401 future() noexcept
402 : state_{nullptr}
403 { /* DUMMY BODY */ }
404
405 future(const future&) = delete;
406
407 future(future&& rhs) noexcept
408 : state_{std::move(rhs.state_)}
409 {
410 rhs.state_ = nullptr;
411 }
412
413 future(aux::shared_state<R>* state)
414 : state_{state}
415 {
416 /**
417 * Note: This is a custom non-standard constructor that allows
418 * us to create a future directly from a shared state. This
419 * should never be a problem as aux::shared_state is a private
420 * type and future has no constructor templates.
421 */
422 }
423
424 ~future()
425 {
426 release_state_();
427 }
428
429 future& operator=(const future) = delete;
430
431 future& operator=(future&& rhs) noexcept
432 {
433 release_state_();
434 state_ = std::move(rhs.state_);
435 rhs.state_ = nullptr;
436 }
437
438 shared_future<R> share()
439 {
440 return shared_future<R>(std::move(*this));
441 }
442
443 R get()
444 {
445 assert(state_);
446
447 wait();
448
449 auto state = state_;
450 state_ = nullptr;
451 if (state->has_exception())
452 state->throw_stored_exception();
453
454 return std::move(state->get());
455 }
456
457 bool valid() const noexcept
458 {
459 return state_ != nullptr;
460 }
461
462 void wait() const noexcept
463 {
464 assert(state_);
465
466 state_->wait();
467 }
468
469 template<class Rep, class Period>
470 future_status wait_for(const chrono::duration<Rep, Period>& rel_time) const
471 {
472 assert(state_);
473 if (state_->is_deffered_function)
474 return future_status::deferred;
475
476 auto res = state_->wait_for(rel_time);
477
478 if (res)
479 return future_status::ready;
480 else
481 return future_status::timeout;
482 }
483
484 template<class Clock, class Duration>
485 future_status wait_until(const chrono::time_point<Clock, Duration>& abs_time) const
486 {
487 assert(state_);
488 if (state_->is_deffered_function)
489 return future_status::deferred;
490
491 auto res = state_->wait_until(abs_time);
492
493 if (res)
494 return future_status::ready;
495 else
496 return future_status::timeout;
497 }
498
499 private:
500 void release_state_()
501 {
502 /**
503 * Last reference to state -> destroy state.
504 * Decrement refcount of state otherwise.
505 * Will not block, unless all following hold:
506 * 1) State was created by call to std::async.
507 * 2) State is not yet ready.
508 * 3) This was the last reference to the shared state.
509 */
510 }
511
512 aux::shared_state<R>* state_;
513 };
514
515 template<class R>
516 class future<R&>
517 {
518 // TODO: Copy & modify once future is done.
519 };
520
521 template<>
522 class future<void>
523 {
524 // TODO: Copy & modify once future is done.
525 };
526
527 // TODO: Make sure the move constructor of shared_future
528 // invalidates the state (i.e. sets to nullptr).
529 template<class R>
530 class shared_future
531 {
532 // TODO: Copy & modify once future is done.
533 };
534
535 template<class R>
536 class shared_future<R&>
537 {
538 // TODO: Copy & modify once future is done.
539 };
540
541 template<>
542 class shared_future<void>
543 {
544 // TODO: Copy & modify once future is done.
545 };
546
547 template<class>
548 class packaged_task; // undefined
549
550 template<class R, class... Args>
551 class packaged_task<R(Args...)>
552 {
553 packaged_task() noexcept
554 {}
555
556 template<class F>
557 explicit packaged_task(F&& f)
558 {}
559
560 template<class F, class Allocator>
561 explicit packaged_task(allocator_arg_t, const Allocator& a, F&& f)
562 {}
563
564 ~packaged_task()
565 {}
566
567 packaged_task(const packaged_task&) = delete;
568 packaged_task& operator=(const packaged_task&) = delete;
569
570 packaged_task(packaged_task&& rhs)
571 {}
572
573 packaged_task& operator=(packaged_task&& rhs)
574 {}
575
576 void swap(packaged_task& other) noexcept
577 {}
578
579 bool valid() const noexcept
580 {}
581
582 future<R> get_future()
583 {}
584
585 void operator()(Args...)
586 {}
587
588 void make_ready_at_thread_exit(Args...)
589 {}
590
591 void reset()
592 {}
593 };
594
595 template<class R, class... Args>
596 void swap(packaged_task<R(Args...)>& lhs, packaged_task<R(Args...)>& rhs) noexcept
597 {
598 lhs.swap(rhs);
599 };
600
601 template<class R, class Alloc>
602 struct uses_allocator<packaged_task<R>, Alloc>: true_type
603 { /* DUMMY BODY */ };
604
605 template<class F, class... Args>
606 future<result_of_t<decay_t<F>(decay_t<Args>...)>>
607 async(F&& f, Args&&... args)
608 {
609 // TODO: implement
610 __unimplemented();
611 }
612
613 template<class F, class... Args>
614 future<result_of_t<decay_t<F>(decay_t<Args>...)>>
615 async(launch, F&& f, Args&&... args)
616 {
617 // TODO: implement
618 __unimplemented();
619 }
620}
621
622#endif
Note: See TracBrowser for help on using the repository browser.