Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Remove features marked with XBT_ATTRIB_DEPRECATED_v325.
[simgrid.git] / include / simgrid / simix / blocking_simcall.hpp
1 /* Copyright (c) 2016-2019. The SimGrid Team. All rights reserved.          */
2
3 /* This program is free software; you can redistribute it and/or modify it
4  * under the terms of the license (GNU LGPL) which comes with this package. */
5
6 #ifndef SIMGRID_SIMIX_BLOCKING_SIMCALL_HPP
7 #define SIMGRID_SIMIX_BLOCKING_SIMCALL_HPP
8
9 #include <exception>
10 #include <functional>
11 #include <future>
12 #include <utility>
13
14 #include <xbt/sysdep.h>
15
16 #include <xbt/future.hpp>
17 #include <simgrid/kernel/future.hpp>
18 #include <simgrid/simix.h>
19 #include <simgrid/simix.hpp>
20
21 namespace simgrid {
22 namespace simix {
23
24 XBT_PUBLIC void unblock(smx_actor_t process);
25
26 /** Execute some code in kernel mode and wakes up the actor when
27  *  the result is available.
28  *
29  * It is given a callback which is executed in the SimGrid kernel and
30  * returns a `simgrid::kernel::Future<T>`. The kernel blocks the actor
31  * until the Future is ready and:
32  *
33  *  - either returns the value wrapped in the future to the actor
34  *
35  *  - or raises the exception stored in the future in the actor.
36  *
37  * This can be used to implement blocking calls without adding new simcalls.
38  * One downside of this approach is that we don't have any semantic on what
39  * the actor is waiting. This might be a problem for the model-checker and
40  * we'll have to devise a way to make it work.
41  *
42  * @param     code Kernel code returning a `simgrid::kernel::Future<T>`
43  * @return         Value of the kernel future
44  * @exception      Exception from the kernel future
45  */
46 template <class F> auto kernel_sync(F code) -> decltype(code().get())
47 {
48   typedef decltype(code().get()) T;
49   if (SIMIX_is_maestro())
50     xbt_die("Can't execute blocking call in kernel mode");
51
52   smx_actor_t self = SIMIX_process_self();
53   simgrid::xbt::Result<T> result;
54
55   simcall_run_blocking(
56       [&result, self, &code] {
57         try {
58           auto future = code();
59           future.then_([&result, self](std::shared_ptr<simgrid::kernel::FutureState<T>>&& value) {
60             simgrid::xbt::set_promise(result, simgrid::kernel::Future<T>(value));
61             simgrid::simix::unblock(self);
62           });
63         } catch (...) {
64           result.set_exception(std::current_exception());
65           simgrid::simix::unblock(self);
66         }
67       },
68       nullptr);
69   return result.get();
70 }
71
72 /** A blocking (`wait()`-based) future for SIMIX processes */
73 // TODO, .wait_for
74 // TODO, .wait_until
75 // TODO, SharedFuture
76 // TODO, simgrid::simix::when_all - wait for all future to be ready (this one is simple!)
77 // TODO, simgrid::simix::when_any - wait for any future to be ready
78 template <class T>
79 class Future {
80 public:
81   Future() { /* Nothing to do*/}
82   explicit Future(simgrid::kernel::Future<T> future) : future_(std::move(future)) {}
83
84   bool valid() const { return future_.valid(); }
85   T get()
86   {
87     if (not valid())
88       throw std::future_error(std::future_errc::no_state);
89     smx_actor_t self = SIMIX_process_self();
90     simgrid::xbt::Result<T> result;
91     simcall_run_blocking(
92         [this, &result, self] {
93           try {
94             // When the kernel future is ready...
95             this->future_.then_([&result, self](std::shared_ptr<simgrid::kernel::FutureState<T>>&& value) {
96               // ... wake up the process with the result of the kernel future.
97               simgrid::xbt::set_promise(result, simgrid::kernel::Future<T>(value));
98               simgrid::simix::unblock(self);
99             });
100           } catch (...) {
101             result.set_exception(std::current_exception());
102             simgrid::simix::unblock(self);
103           }
104         },
105         nullptr);
106     return result.get();
107   }
108   bool is_ready() const
109   {
110     if (not valid())
111       throw std::future_error(std::future_errc::no_state);
112     return future_.is_ready();
113   }
114   void wait()
115   {
116     // The future is ready! We don't have to wait:
117     if (this->is_ready())
118       return;
119     // The future is not ready. We have to delegate to the SimGrid kernel:
120     std::exception_ptr exception;
121     smx_actor_t self = SIMIX_process_self();
122     simcall_run_blocking(
123         [this, &exception, self] {
124           try {
125             // When the kernel future is ready...
126             this->future_.then_([this, self](std::shared_ptr<simgrid::kernel::FutureState<T>>&& value) {
127               // ...store it the simix kernel and wake up.
128               this->future_ = std::move(simgrid::kernel::Future<T>(value));
129               simgrid::simix::unblock(self);
130             });
131           } catch (...) {
132             exception = std::current_exception();
133             simgrid::simix::unblock(self);
134           }
135         },
136         nullptr);
137   }
138 private:
139   // We wrap an event-based kernel future:
140   simgrid::kernel::Future<T> future_;
141 };
142
143 /** Start some asynchronous work
144  *
145  *  @param code SimGrid kernel code which returns a simgrid::kernel::Future
146  *  @return     Actor future
147  */
148 template <class F> auto kernel_async(F code) -> Future<decltype(code().get())>
149 {
150   typedef decltype(code().get()) T;
151
152   // Execute the code in the kernel and get the kernel future:
153   simgrid::kernel::Future<T> future = simgrid::kernel::actor::simcall(std::move(code));
154
155   // Wrap the kernel future in a actor future:
156   return simgrid::simix::Future<T>(std::move(future));
157 }
158 }
159 }
160
161 #endif