Lely core libraries  2.2.5
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 
36 namespace lely {
37 namespace ev {
38 
45 class future_not_ready : public ::std::runtime_error {
46  using ::std::runtime_error::runtime_error;
47 };
48 
49 template <class, class = ::std::error_code>
50 class Future;
51 
59 template <class T, class E = ::std::error_code>
60 class 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 
134  ~Promise() { ev_promise_release(promise_); }
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 
196 namespace detail {
197 
198 template <class F, class... Args, class R = compat::invoke_result_t<F, Args...>>
199 inline typename ::std::enable_if<!::std::is_void<R>::value,
201 catch_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 
210 template <class F, class... Args, class R = compat::invoke_result_t<F, Args...>>
211 inline typename ::std::enable_if<::std::is_void<R>::value,
213 catch_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 
222 template <class>
223 struct is_future : ::std::false_type {};
224 
225 template <class T, class E>
226 struct is_future<Future<T, E>> : ::std::true_type {};
227 
228 template <class, class = void>
229 class AsyncTask;
230 
231 template <class Invoker>
232 class 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 
260  Executor
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 
276 template <class Invoker>
277 class 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 
335  Executor
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 
358 template <class F, class... Args>
360 
365 template <class F, class... Args>
366 inline AsyncTask<F, Args...>*
367 make_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 
383 template <class T, class E>
384 class 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 
447  ~Future() { ev_future_release(future_); }
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 
573 when_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");
576  return Future<::std::size_t, void>(f);
577 }
578 
580 template <class Allocator>
583  const ::std::vector<ev_future_t*, Allocator>& futures) {
584  return when_all(exec, futures.size(), futures.data());
585 }
586 
588 template <class InputIt>
589 inline typename ::std::enable_if<
590  !::std::is_convertible<InputIt, ev_future_t*>::value,
592 when_all(ev_exec_t* exec, InputIt first, InputIt last) {
593  return when_all(exec, ::std::vector<ev_future_t*>(first, last));
594 }
595 
597 template <class... Futures>
598 inline typename ::std::enable_if<
601 when_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");
605  return Future<::std::size_t, void>(f);
606 }
607 
610 when_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");
613  return Future<::std::size_t, void>(f);
614 }
615 
617 template <class Allocator>
620  const ::std::vector<ev_future_t*, Allocator>& futures) {
621  return when_any(exec, futures.size(), futures.data());
622 }
623 
625 template <class InputIt>
626 inline typename ::std::enable_if<
627  !::std::is_convertible<InputIt, ev_future_t*>::value,
629 when_any(ev_exec_t* exec, InputIt first, InputIt last) {
630  return when_any(exec, ::std::vector<ev_future_t*>(first, last));
631 }
632 
634 template <class... Futures>
635 inline typename ::std::enable_if<
638 when_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");
642  return Future<::std::size_t, void>(f);
643 }
644 
652 template <class E = ::std::error_code>
653 inline Future<void, E>
656  p.set(util::success());
657  return p.get_future();
658 }
659 
668 template <class T, class E = ::std::error_code,
669  class V = typename ::std::decay<T>::type>
670 inline Future<V, E>
671 make_ready_future(T&& value) {
672  Promise<V, E> p;
673  p.set(util::success(::std::forward<T>(value)));
674  return p.get_future();
675 }
676 
685 template <class T, class E, class V = typename ::std::decay<E>::type>
686 inline Future<T, V>
688  Promise<T, V> p;
689  p.set(util::failure(::std::forward<E>(error)));
690  return p.get_future();
691 }
692 
698 template <class F, class... Args>
699 inline typename AsyncTask<F, Args...>::future_type
700 async(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_
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:332
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
const struct ev_exec_vtbl *const ev_exec_t
An abstract task executor.
Definition: ev.h:29
Future<::std::size_t, void > when_any(ev_exec_t *exec, ::std::size_t n, ev_future_t *const *futures)
Definition: future.hpp:610
void * ev_promise_data(const ev_promise_t *promise)
Returns a pointer to the shared state of a promise.
Definition: future.c:233
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
Future(Future &&other) noexcept
Moves the shared state of other to *this.
Definition: future.hpp:407
void ev_promise_release(ev_promise_t *promise)
Releases a reference to a promise.
Definition: future.c:195
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
Promise()
Constructs a promise with (a reference to) an empty shared state.
Definition: future.hpp:70
Future< void, E > make_empty_future()
Creates a shared state of type lely::util::Result<void, E> that is immediately ready, with a successful result, then returns a future associated with that shared state.
Definition: future.hpp:654
#define EV_TASK_INIT(exec, func)
The static initializer for ev_task.
Definition: task.h:53
Future< V, E > make_ready_future(T &&value)
Creates a shared state of type lely::util::Result<V, E> that is immediately ready, with a successul result constructed from std::forward<T>(value), then returns a future associated with that shared state.
Definition: future.hpp:671
Promise(Promise &&other) noexcept
Moves the shared state of other to *this.
Definition: future.hpp:94
void submit(ev_task &task) noexcept
Definition: future.hpp:513
bool is_unique() const noexcept
Checks whether *this contains a unique reference to its shared state.
Definition: future.hpp:462
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:468
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:492
::std::size_t cancel_all() noexcept
Definition: future.hpp:532
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:218
The exception thrown when retrieving the result of a future which is not ready or does not contain a ...
Definition: future.hpp:45
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:151
A future.
Definition: future.c:63
Promise(const Promise &other) noexcept
Constructs a promise with a reference to the shared state of other.
Definition: future.hpp:86
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
This header file is part of the utilities library; it contains a generic type that can represent both...
bool abort(ev_task &task) noexcept
Definition: future.hpp:538
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:386
bool cancel(ev_task &task) noexcept
Definition: future.hpp:526
Future< T, V > make_error_future(E &&error)
Creates a shared state of type lely::util::Result<T, V> that is immediately ready, with a failure result constructed from std::forward<E>(error), then returns a future associated with that shared state.
Definition: future.hpp:687
void ev_future_release(ev_future_t *future)
Releases a reference to a future.
Definition: future.c:325
A promise.
Definition: future.hpp:60
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
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
ev_future_t * ev_future_acquire(ev_future_t *future)
Acquires a reference to a future.
Definition: future.c:317
Future<::std::size_t, void > when_all(ev_exec_t *exec, ::std::size_t n, ev_future_t *const *futures)
Definition: future.hpp:573
::std::size_t abort_all() noexcept
Definition: future.hpp:544
bool is_unique() const noexcept
Checks whether *this contains a unique reference to its shared state.
Definition: future.hpp:149
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:272
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:309
A type capable of representing both the successful and failure result of an operation.
Definition: result.hpp:200
The type of objects thrown as exceptions to report a system error with an associated error code...
Definition: exception.hpp:54
void submit(ev_exec_t *exec, F &&f)
Definition: future.hpp:520
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:412
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:178
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
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
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
This header file is part of the event library; it contains the C++ interface for the abstract task ex...
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:338
This header file is part of the compatibility library; it includes <type_traits> and defines any miss...
An executable task.
Definition: task.h:41
An abstract task executor. This class is a wrapper around ev_exec_t*.
Definition: exec.hpp:38
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
This header file is part of the utilities library; it contains a function object that can be used to ...
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:399
~Future()
Abandons the shared state.
Definition: future.hpp:447
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:248
ev_task_func_t * func
The function to be invoked when the task is run.
Definition: task.h:45
Definition: buf.hpp:32
A promise.
Definition: future.c:92
Future(const Future &other) noexcept
Constructs a future with a reference to the shared state of other.
Definition: future.hpp:399
void * ev_future_get(const ev_future_t *future)
Returns the result of a ready future.
Definition: future.c:351
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:436
~Promise()
Abandons the shared state.
Definition: future.hpp:134
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
Forms the logical conjunction of the type traits B..., effectively performing a logical AND on the se...
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:360
A future.
Definition: future.hpp:50