Logo AND Algorithmique Numérique Distribuée

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