Lely core libraries 2.3.4
future.hpp
Go to the documentation of this file.
1
24#ifndef LELY_EV_FUTURE_HPP_
25#define LELY_EV_FUTURE_HPP_
26
27#include <lely/ev/exec.hpp>
28#include <lely/ev/future.h>
30#include <lely/util/invoker.hpp>
31#include <lely/util/result.hpp>
32
33#include <utility>
34#include <vector>
35
36namespace lely {
37namespace ev {
38
45class future_not_ready : public ::std::runtime_error {
46 using ::std::runtime_error::runtime_error;
47};
48
49template <class, class = ::std::error_code>
50class Future;
51
59template <class T, class E = ::std::error_code>
60class Promise {
61 public:
64
71 : promise_(ev_promise_create(sizeof(result_type), [](void* ptr) noexcept {
72 static_cast<result_type*>(ptr)->~result_type();
73 })) {
74 if (!promise_) util::throw_errc("Promise");
75 // Since we already registered the destructor for result_type(), we cannot
76 // handle an exception from the default constructor. Wrapping the placement
77 // new in a noexcept lambda prevents undefined behavior.
78 [](void* ptr) noexcept {
79 new (ptr) result_type();
80 }(ev_promise_data(promise_));
81 }
82
83 explicit Promise(ev_promise_t* promise) noexcept : promise_(promise) {}
84
86 Promise(const Promise& other) noexcept
87 : promise_(ev_promise_acquire(other.promise_)) {}
88
94 Promise(Promise&& other) noexcept : promise_(other.promise_) {
95 other.promise_ = nullptr;
96 }
97
104 Promise&
105 operator=(const Promise& other) noexcept {
106 if (promise_ != other.promise_) {
107 ev_promise_release(promise_);
108 promise_ = ev_promise_acquire(other.promise_);
109 }
110 return *this;
111 }
112
121 Promise&
122 operator=(Promise&& other) noexcept {
123 using ::std::swap;
124 swap(promise_, other.promise_);
125 return *this;
126 }
127
135
136 operator ev_promise_t*() const noexcept { return promise_; }
137
139 explicit operator bool() const noexcept { return promise_ != nullptr; }
140
148 bool
149 is_unique() const noexcept {
150 return ev_promise_is_unique(*this) != 0;
151 }
152
164 template <class U>
165 bool
166 set(U&& u) {
167 if (ev_promise_set_acquire(*this)) {
168 auto p = reinterpret_cast<result_type*>(ev_promise_data(*this));
169 try {
170 *p = result_type{::std::forward<U>(u)};
171 ev_promise_set_release(*this, static_cast<void*>(p));
172 } catch (...) {
173 ev_promise_set_release(*this, nullptr);
174 throw;
175 }
176 return true;
177 }
178 return false;
179 }
180
188 get_future() const noexcept {
189 return Future<T, E>(ev_promise_get_future(*this));
190 }
191
192 private:
193 ev_promise_t* promise_{nullptr};
194};
195
196namespace detail {
197
198template <class F, class... Args, class R = compat::invoke_result_t<F, Args...>>
199inline typename ::std::enable_if<!::std::is_void<R>::value,
200 util::Result<R, ::std::exception_ptr>>::type
201catch_result(F&& f, Args&&... args) {
202 try {
203 return util::success(
204 compat::invoke(::std::forward<F>(f), ::std::forward<Args>(args)...));
205 } catch (...) {
206 return util::failure(::std::current_exception());
207 }
208}
209
210template <class F, class... Args, class R = compat::invoke_result_t<F, Args...>>
211inline typename ::std::enable_if<::std::is_void<R>::value,
212 util::Result<R, ::std::exception_ptr>>::type
213catch_result(F&& f, Args&&... args) {
214 try {
215 compat::invoke(::std::forward<F>(f), ::std::forward<Args>(args)...);
216 return util::success();
217 } catch (...) {
218 return util::failure(::std::current_exception());
219 }
220}
221
222template <class>
223struct is_future : ::std::false_type {};
224
225template <class T, class E>
226struct is_future<Future<T, E>> : ::std::true_type {};
227
228template <class, class = void>
230
231template <class Invoker>
232class AsyncTask<Invoker, typename ::std::enable_if<!is_future<
233 compat::invoke_result_t<Invoker>>::value>::type>
234 : public ev_task {
235 public:
236 using value_type = compat::invoke_result_t<Invoker>;
237 using error_type = ::std::exception_ptr;
239
240 template <class F, class... Args>
241 AsyncTask(ev_promise_t* promise, ev_exec_t* exec, F&& f,
242 Args&&... args) noexcept
243 : ev_task EV_TASK_INIT(exec,
244 [](ev_task* task) noexcept {
245 auto self = static_cast<AsyncTask*>(task);
246 auto promise = ::std::move(self->promise_);
247 if (ev_promise_set_acquire(promise)) {
248 self->result_ = catch_result(self->invoker_);
250 promise, ::std::addressof(self->result_));
251 }
252 }),
253 promise_(promise),
254 invoker_(::std::forward<F>(f), ::std::forward<Args>(args)...) {}
255
256 AsyncTask(const AsyncTask&) = delete;
257
258 AsyncTask& operator=(const AsyncTask&) = delete;
259
261 get_executor() const noexcept {
262 return exec;
263 }
264
266 get_future() const noexcept {
267 return promise_.get_future();
268 }
269
270 private:
272 Invoker invoker_;
274};
275
276template <class Invoker>
277class AsyncTask<Invoker, typename ::std::enable_if<is_future<
278 compat::invoke_result_t<Invoker>>::value>::type>
279 : public ev_task {
280 using inner_future_type = compat::invoke_result_t<Invoker>;
281
282 public:
283 using value_type = typename inner_future_type::result_type::value_type;
284 using error_type = ::std::exception_ptr;
286
287 template <class F, class... Args>
288 AsyncTask(ev_promise_t* promise, ev_exec_t* exec, F&& f,
289 Args&&... args) noexcept
290 : ev_task
291 EV_TASK_INIT(exec,
292 [](ev_task* task) noexcept {
293 auto self = static_cast<AsyncTask*>(task);
294 // Prepare the task for the next future.
295 task->func = [](ev_task* task) noexcept {
296 auto self = static_cast<AsyncTask*>(task);
297 auto promise = ::std::move(self->promise_);
298 // Satisfy the promise with the result of the future
299 // (and convert the error value, if any, to an
300 // exception pointer).
301 if (ev_promise_set_acquire(promise)) {
302 self->result_ = catch_result(
303 [](inner_future_type future) {
304 return future.get().value();
305 },
306 ::std::move(self->inner_future_));
308 promise, ::std::addressof(self->result_));
309 }
310 };
311 try {
312 // Store (a reference to) the future returned by the
313 // function.
314 self->inner_future_ = self->invoker_();
315 // Resubmit the task to the new future.
316 self->inner_future_.submit(*self);
317 } catch (...) {
318 // If the function threw an exception, satisfy the
319 // promise immediately.
320 auto promise = ::std::move(self->promise_);
321 if (ev_promise_set_acquire(promise)) {
322 self->result_ = ::std::current_exception();
324 promise, ::std::addressof(self->result_));
325 }
326 }
327 }),
328 promise_(promise),
329 invoker_(::std::forward<F>(f), ::std::forward<Args>(args)...) {}
330
331 AsyncTask(const AsyncTask&) = delete;
332
333 AsyncTask& operator=(const AsyncTask&) = delete;
334
336 get_executor() const noexcept {
337 return exec;
338 }
339
341 get_future() const noexcept {
342 return promise_.get_future();
343 }
344
345 private:
347 Invoker invoker_;
348 inner_future_type inner_future_;
350};
351
352} // namespace detail
353
358template <class F, class... Args>
360
365template <class F, class... Args>
366inline AsyncTask<F, Args...>*
367make_async_task(ev_exec_t* exec, F&& f, Args&&... args) {
368 // Create a promise with enough space to store the task and register the
369 // destructor.
370 auto promise =
371 ev_promise_create(sizeof(AsyncTask<F, Args...>), [](void* ptr) noexcept {
372 static_cast<AsyncTask<F, Args...>*>(ptr)->~AsyncTask();
373 });
374 if (!promise) util::throw_errc("make_async_task");
375 // Construct the task. Since the destructor is already registered, we cannot
376 // handle exceptions thrown by the constructor, so it is defined with the
377 // noexcept attribute (see above).
378 return new (ev_promise_data(promise)) AsyncTask<F, Args...>(
379 promise, exec, ::std::forward<F>(f), ::std::forward<Args>(args)...);
380}
381
383template <class T, class E>
384class Future {
385 public:
388
394 Future() noexcept = default;
395
396 explicit Future(ev_future_t* future) noexcept : future_(future) {}
397
399 Future(const Future& other) noexcept
400 : future_(ev_future_acquire(other.future_)) {}
401
407 Future(Future&& other) noexcept : future_(other.future_) {
408 other.future_ = nullptr;
409 }
410
417 Future&
418 operator=(const Future& other) noexcept {
419 if (future_ != other.future_) {
420 ev_future_release(future_);
421 future_ = ev_future_acquire(other.future_);
422 }
423 return *this;
424 }
425
434 Future&
435 operator=(Future&& other) noexcept {
436 using ::std::swap;
437 swap(future_, other.future_);
438 return *this;
439 }
440
448
449 operator ev_future_t*() const noexcept { return future_; }
450
452 explicit operator bool() const noexcept { return future_ != nullptr; }
453
461 bool
462 is_unique() const noexcept {
463 return ev_future_is_unique(*this) != 0;
464 }
465
474 bool
475 is_ready() const noexcept {
476 return ev_future_is_ready(*this);
477 }
478
489 result_type&
490 get() {
491 if (!*this || !is_ready()) throw future_not_ready("get");
492 return *reinterpret_cast<result_type*>(ev_future_get(*this));
493 }
494
505 const result_type&
506 get() const {
507 if (!*this || !is_ready()) throw future_not_ready("get");
508 return *reinterpret_cast<result_type*>(ev_future_get(*this));
509 }
510
512 void
513 submit(ev_task& task) noexcept {
514 ev_future_submit(*this, &task);
515 }
516
518 template <class F>
519 void
520 submit(ev_exec_t* exec, F&& f) {
521 submit(*make_task_wrapper(exec, ::std::forward<F>(f)));
522 }
523
525 bool
526 cancel(ev_task& task) noexcept {
527 return !!ev_future_cancel(*this, &task);
528 }
529
531 ::std::size_t
532 cancel_all() noexcept {
533 return ev_future_cancel(*this, nullptr);
534 }
535
537 bool
538 abort(ev_task& task) noexcept {
539 return !!ev_future_abort(*this, &task);
540 }
541
543 ::std::size_t
544 abort_all() noexcept {
545 return ev_future_abort(*this, nullptr);
546 }
547
556 template <class F>
558 then(ev_exec_t* exec, F&& f) {
559 auto task = make_async_task(exec, ::std::forward<F>(f), *this);
560 // Obtain a reference to the future before submitting the continuation to
561 // avoid a potential race condition.
562 auto future = task->get_future();
563 submit(*task);
564 return future;
565 }
566
567 private:
568 ev_future_t* future_{nullptr};
569};
570
572inline Future<::std::size_t, void>
573when_all(ev_exec_t* exec, ::std::size_t n, ev_future_t* const* futures) {
574 auto f = ev_future_when_all_n(exec, n, futures);
575 if (!f) util::throw_errc("when_all");
577}
578
580template <class Allocator>
581inline Future<::std::size_t, void>
583 const ::std::vector<ev_future_t*, Allocator>& futures) {
584 return when_all(exec, futures.size(), futures.data());
585}
586
588template <class InputIt>
589inline typename ::std::enable_if<
590 !::std::is_convertible<InputIt, ev_future_t*>::value,
591 Future<::std::size_t, void>>::type
592when_all(ev_exec_t* exec, InputIt first, InputIt last) {
593 return when_all(exec, ::std::vector<ev_future_t*>(first, last));
594}
595
597template <class... Futures>
598inline typename ::std::enable_if<
600 Future<::std::size_t, void>>::type
601when_all(ev_exec_t* exec, Futures&&... futures) {
602 auto f =
603 ev_future_when_all(exec, static_cast<ev_future_t*>(futures)..., nullptr);
604 if (!f) util::throw_errc("when_all");
606}
607
609inline Future<::std::size_t, void>
610when_any(ev_exec_t* exec, ::std::size_t n, ev_future_t* const* futures) {
611 auto f = ev_future_when_any_n(exec, n, futures);
612 if (!f) util::throw_errc("when_any");
614}
615
617template <class Allocator>
618inline Future<::std::size_t, void>
620 const ::std::vector<ev_future_t*, Allocator>& futures) {
621 return when_any(exec, futures.size(), futures.data());
622}
623
625template <class InputIt>
626inline typename ::std::enable_if<
627 !::std::is_convertible<InputIt, ev_future_t*>::value,
628 Future<::std::size_t, void>>::type
629when_any(ev_exec_t* exec, InputIt first, InputIt last) {
630 return when_any(exec, ::std::vector<ev_future_t*>(first, last));
631}
632
634template <class... Futures>
635inline typename ::std::enable_if<
637 Future<::std::size_t, void>>::type
638when_any(ev_exec_t* exec, Futures&&... futures) {
639 auto f =
640 ev_future_when_any(exec, static_cast<ev_future_t*>(futures)..., nullptr);
641 if (!f) util::throw_errc("when_any");
643}
644
652template <class E = ::std::error_code>
653inline Future<void, E>
656 p.set(util::success());
657 return p.get_future();
658}
659
668template <class T, class E = ::std::error_code,
669 class V = typename ::std::decay<T>::type>
670inline Future<V, E>
673 p.set(util::success(::std::forward<T>(value)));
674 return p.get_future();
675}
676
685template <class T, class E, class V = typename ::std::decay<E>::type>
686inline Future<T, V>
689 p.set(util::failure(::std::forward<E>(error)));
690 return p.get_future();
691}
692
698template <class F, class... Args>
699inline typename AsyncTask<F, Args...>::future_type
700async(ev_exec_t* exec, F&& f, Args&&... args) {
701 auto task = make_async_task(exec, ::std::forward<F>(f),
702 ::std::forward<Args>(args)...);
703 // Obtain a reference to the future before submitting the task to avoid a
704 // potential race condition.
705 auto future = task->get_future();
706 task->get_executor().submit(*task);
707 return future;
708}
709
710} // namespace ev
711} // namespace lely
712
713#endif // !LELY_EV_FUTURE_HPP_
The type of objects thrown as exceptions to report a system error with an associated error code.
Definition: exception.hpp:54
An abstract task executor. This class is a wrapper around #ev_exec_t*.
Definition: exec.hpp:38
A future.
Definition: future.hpp:384
bool is_unique() const noexcept
Checks whether *this contains a unique reference to its shared state.
Definition: future.hpp:462
const result_type & get() const
Returns the result of a ready future.
Definition: future.hpp:506
void submit(ev_task &task) noexcept
Definition: future.hpp:513
bool cancel(ev_task &task) noexcept
Definition: future.hpp:526
void submit(ev_exec_t *exec, F &&f)
Definition: future.hpp:520
bool is_ready() const noexcept
Checks whether the future is ready, i.e., its associated promise has been satisfied and a result has ...
Definition: future.hpp:475
Future & operator=(Future &&other) noexcept
Abandons the shared state of *this as if by ~Future(), then moves the shared state of other to *this.
Definition: future.hpp:435
Future() noexcept=default
Constructs future without (a reference to) a shared state.
AsyncTask< F, Future >::future_type then(ev_exec_t *exec, F &&f)
Attaches a continuation function to a future and returns a new future which becomes ready once the co...
Definition: future.hpp:558
::std::size_t abort_all() noexcept
Definition: future.hpp:544
~Future()
Abandons the shared state.
Definition: future.hpp:447
Future(Future &&other) noexcept
Moves the shared state of other to *this.
Definition: future.hpp:407
bool abort(ev_task &task) noexcept
Definition: future.hpp:538
::std::size_t cancel_all() noexcept
Definition: future.hpp:532
Future(const Future &other) noexcept
Constructs a future with a reference to the shared state of other.
Definition: future.hpp:399
Future & operator=(const Future &other) noexcept
Abandons the shared state of *this as if by ~Future(), then creates a reference to the shared state o...
Definition: future.hpp:418
result_type & get()
Returns the result of a ready future.
Definition: future.hpp:490
A promise.
Definition: future.hpp:60
Promise & operator=(Promise &&other) noexcept
Abandons the shared state of *this as if by ~Promise(), then moves the shared state of other to *this...
Definition: future.hpp:122
Promise(Promise &&other) noexcept
Moves the shared state of other to *this.
Definition: future.hpp:94
Promise(const Promise &other) noexcept
Constructs a promise with a reference to the shared state of other.
Definition: future.hpp:86
bool is_unique() const noexcept
Checks whether *this contains a unique reference to its shared state.
Definition: future.hpp:149
Future< T, E > get_future() const noexcept
Returns a lely::ev::Future with (a reference to) the same shared state as *this.
Definition: future.hpp:188
Promise & operator=(const Promise &other) noexcept
Abandons the shared state of *this as if by ~Promise(), then creates a reference to the shared state ...
Definition: future.hpp:105
~Promise()
Abandons the shared state.
Definition: future.hpp:134
Promise()
Constructs a promise with (a reference to) an empty shared state.
Definition: future.hpp:70
bool set(U &&u)
Satisfies a promise, if it was not aready satisfied, and stores the specified value as the result in ...
Definition: future.hpp:166
The exception thrown when retrieving the result of a future which is not ready or does not contain a ...
Definition: future.hpp:45
A type capable of representing both the successful and failure result of an operation.
Definition: result.hpp:200
This header file is part of the event library; it contains the C++ interface for the abstract task ex...
This header file is part of the event library; it contains the futures and promises declarations.
ev_promise_t * ev_promise_acquire(ev_promise_t *promise)
Acquires a reference to a promise.
Definition: future.c:181
int ev_future_is_unique(const ev_future_t *future)
Returns 1 if future is the only reference to the future and no references to its associated promise a...
Definition: future.c:335
void ev_promise_release(ev_promise_t *promise)
Releases a reference to a promise.
Definition: future.c:198
ev_future_t * ev_promise_get_future(ev_promise_t *promise)
Returns (a reference to) a future associated with the specified promise.
Definition: future.c:312
int ev_promise_is_unique(const ev_promise_t *promise)
Returns 1 if promise is the only reference to the promise and no references to its associated future ...
Definition: future.c:221
size_t ev_future_abort(ev_future_t *future, struct ev_task *task)
Aborts the specified task submitted with ev_future_submit(), if it has not yet been scheduled for exe...
Definition: future.c:404
size_t ev_future_cancel(ev_future_t *future, struct ev_task *task)
Cancels the specified task submitted with ev_future_submit(), if it has not yet been scheduled for ex...
Definition: future.c:391
ev_future_t * ev_future_when_all(ev_exec_t *exec, ev_future_t *future,...)
Equivalent to ev_future_when_all_n(), except that it accepts a variable number of arguments instead o...
Definition: future.c:417
void ev_promise_set_release(ev_promise_t *promise, void *value)
Satisfies a promise prepared by ev_promise_set_acquire(), and stores the specified value for retrieva...
Definition: future.c:275
ev_future_t * ev_future_when_any_n(ev_exec_t *exec, size_t n, ev_future_t *const *futures)
Creates a future that becomes ready when at least one of the input futures becomes ready or is abando...
Definition: future.c:500
void * ev_promise_data(const ev_promise_t *promise)
Returns a pointer to the shared state of a promise.
Definition: future.c:236
ev_future_t * ev_future_when_all_n(ev_exec_t *exec, size_t n, ev_future_t *const *futures)
Creates a future that becomes ready when all of the input futures become ready or one of the input fu...
Definition: future.c:441
void * ev_future_get(const ev_future_t *future)
Returns the result of a ready future.
Definition: future.c:356
void ev_future_submit(ev_future_t *future, struct ev_task *task)
Submits a task to be executed once the specified future is ready.
Definition: future.c:364
void ev_future_release(ev_future_t *future)
Releases a reference to a future.
Definition: future.c:328
ev_future_t * ev_future_acquire(ev_future_t *future)
Acquires a reference to a future.
Definition: future.c:320
ev_future_t * ev_future_when_any(ev_exec_t *exec, ev_future_t *future,...)
Equivalent to ev_future_when_any_n(), except that it accepts a variable number of arguments instead o...
Definition: future.c:476
ev_promise_t * ev_promise_create(size_t size, ev_promise_dtor_t *dtor)
Constructs a new promise with an optional empty shared state.
Definition: future.c:154
int ev_future_is_ready(const ev_future_t *future)
Checks if the specified future is ready, i.e., its associated promise has been satisfied.
Definition: future.c:341
int ev_promise_set_acquire(ev_promise_t *promise)
Checks if the specified promise can be satisfied by the caller and, if so, prevents others from satis...
Definition: future.c:251
Future< T, V > make_error_future(E &&error)
Creates a shared state of type #lely::util::Result<T, V> that is immediately ready,...
Definition: future.hpp:687
Future< void, E > make_empty_future()
Creates a shared state of type #lely::util::Result<void, E> that is immediately ready,...
Definition: future.hpp:654
Future<::std::size_t, void > when_all(ev_exec_t *exec, ::std::size_t n, ev_future_t *const *futures)
Definition: future.hpp:573
Future<::std::size_t, void > when_any(ev_exec_t *exec, ::std::size_t n, ev_future_t *const *futures)
Definition: future.hpp:610
Future< V, E > make_ready_future(T &&value)
Creates a shared state of type #lely::util::Result<V, E> that is immediately ready,...
Definition: future.hpp:671
AsyncTask< F, Args... > * make_async_task(ev_exec_t *exec, F &&f, Args &&... args)
Creates a task containing a Callable and its arguments and a future that will eventually hold the res...
Definition: future.hpp:367
AsyncTask< F, Args... >::future_type async(ev_exec_t *exec, F &&f, Args &&... args)
Creates a task containing a Callable and its arguments, submits it for execution to the specified exe...
Definition: future.hpp:700
const struct ev_exec_vtbl *const ev_exec_t
An abstract task executor.
Definition: ev.h:29
This header file is part of the utilities library; it contains a function object that can be used to ...
This header file is part of the compatibility library; it includes <type_traits> and defines any miss...
This header file is part of the utilities library; it contains a generic type that can represent both...
A future.
Definition: future.c:66
A promise.
Definition: future.c:95
An executable task.
Definition: task.h:41
ev_task_func_t * func
The function to be invoked when the task is run.
Definition: task.h:45
Forms the logical conjunction of the type traits B..., effectively performing a logical AND on the se...
#define EV_TASK_INIT(exec, func)
The static initializer for ev_task.
Definition: task.h:53
TaskWrapper< F, Args... > * make_task_wrapper(ev_exec_t *exec, F &&f, Args &&... args)
Creates a temporary task from a callable object with an associated executor (can be nullptr).
Definition: task.hpp:77