Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
[simix] C++-based ownership of process name and arguments
[simgrid.git] / include / simgrid / simix.hpp
1 /* Copyright (c) 2007-2010, 2012-2015. 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_HPP
8 #define SIMGRID_SIMIX_HPP
9
10 #include <cstddef>
11
12 #include <exception>
13 #include <string>
14 #include <utility>
15 #include <memory>
16 #include <functional>
17 #include <future>
18 #include <type_traits>
19
20 #include <xbt/function_types.h>
21 #include <simgrid/simix.h>
22
23 XBT_PUBLIC(void) simcall_run_kernel(std::function<void()> const& code);
24
25 namespace simgrid {
26 namespace simix {
27
28 /** Fulfill a promise by executing a given code */
29 template<class R, class F>
30 void fulfill_promise(std::promise<R>& promise, F&& code)
31 {
32   try {
33     promise.set_value(std::forward<F>(code)());
34   }
35   catch(...) {
36     promise.set_exception(std::current_exception());
37   }
38 }
39
40 /** Fulfill a promise by executing a given code
41  *
42  *  This is a special version for `std::promise<void>` because the default
43  *  version does not compile in this case.
44  */
45 template<class F>
46 void fulfill_promise(std::promise<void>& promise, F&& code)
47 {
48   try {
49     std::forward<F>(code)();
50     promise.set_value();
51   }
52   catch(...) {
53     promise.set_exception(std::current_exception());
54   }
55 }
56
57 /** Execute some code in the kernel/maestro
58  *
59  *  This can be used to enforce mutual exclusion with other simcall.
60  *  More importantly, this enforces a deterministic/reproducible ordering
61  *  of the operation with respect to other simcalls.
62  */
63 template<class F>
64 typename std::result_of<F()>::type kernel(F&& code)
65 {
66   // If we are in the maestro, we take the fast path and execute the
67   // code directly without simcall mashalling/unmarshalling/dispatch:
68   if (SIMIX_is_maestro())
69     return std::forward<F>(code)();
70
71   // If we are in the application, pass the code to the maestro which is
72   // executes it for us and reports the result. We use a std::future which
73   // conveniently handles the success/failure value for us.
74   typedef typename std::result_of<F()>::type R;
75   std::promise<R> promise;
76   simcall_run_kernel([&]{
77     xbt_assert(SIMIX_is_maestro(), "Not in maestro");
78     fulfill_promise(promise, std::forward<F>(code));
79   });
80   return promise.get_future().get();
81 }
82
83 class args {
84 private:
85   int argc_ = 0;
86   char** argv_ = nullptr;
87 public:
88
89   // Main constructors
90   args() {}
91
92   void assign(int argc, const char*const* argv)
93   {
94     clear();
95     char** new_argv = xbt_new(char*,argc + 1);
96     for (int i = 0; i < argc; i++)
97       new_argv[i] = xbt_strdup(argv[i]);
98     new_argv[argc] = nullptr;
99     this->argc_ = argc;
100     this->argv_ = new_argv;
101   }
102   args(int argc, const char*const* argv)
103   {
104     this->assign(argc, argv);
105   }
106
107   char** to_argv() const
108   {
109     const int argc = argc_;
110     char** argv = xbt_new(char*, argc + 1);
111     for (int i=0; i< argc; i++)
112       argv[i] = xbt_strdup(argv_[i]);
113     argv[argc] = nullptr;
114     return argv;
115   }
116
117   // Free
118   void clear()
119   {
120     for (int i = 0; i < this->argc_; i++)
121       free(this->argv_[i]);
122     free(this->argv_);
123     this->argc_ = 0;
124     this->argv_ = nullptr;
125   }
126   ~args() { clear(); }
127
128   // Copy
129   args(args const& that)
130   {
131     this->assign(that.argc(), that.argv());
132   }
133   args& operator=(args const& that)
134   {
135     this->assign(that.argc(), that.argv());
136     return *this;
137   }
138
139   // Move:
140   args(args&& that) : argc_(that.argc_), argv_(that.argv_)
141   {
142     that.argc_ = 0;
143     that.argv_ = nullptr;
144   }
145   args& operator=(args&& that)
146   {
147     this->argc_ = that.argc_;
148     this->argv_ = that.argv_;
149     that.argc_ = 0;
150     that.argv_ = nullptr;
151     return *this;
152   }
153
154   int    argc()            const { return argc_; }
155   char** argv()                  { return argv_; }
156   const char*const* argv() const { return argv_; }
157   char* operator[](std::size_t i) { return argv_[i]; }
158 };
159
160 inline
161 std::function<void()> wrap_main(xbt_main_func_t code, int argc, const char*const* argv)
162 {
163   if (code) {
164     auto arg = std::make_shared<simgrid::simix::args>(argc, argv);
165     return [=]() {
166       code(arg->argc(), arg->argv());
167     };
168   }
169   // TODO, we should free argv
170   else return std::function<void()>();
171 }
172
173 class Context;
174 class ContextFactory;
175
176 XBT_PUBLIC_CLASS ContextFactory {
177 private:
178   std::string name_;
179 public:
180
181   ContextFactory(std::string name) : name_(std::move(name)) {}
182   virtual ~ContextFactory();
183   virtual Context* create_context(std::function<void()> code,
184     void_pfn_smxprocess_t cleanup, smx_process_t process) = 0;
185
186   // Optional methods for attaching main() as a context:
187
188   /** Creates a context from the current context of execution
189    *
190    *  This will not work on all implementation of `ContextFactory`.
191    */
192   virtual Context* attach(void_pfn_smxprocess_t cleanup_func, smx_process_t process);
193   virtual Context* create_maestro(std::function<void()> code, smx_process_t process);
194
195   virtual void run_all() = 0;
196   virtual Context* self();
197   std::string const& name() const
198   {
199     return name_;
200   }
201 private:
202   void declare_context(void* T, std::size_t size);
203 protected:
204   template<class T, class... Args>
205   T* new_context(Args&&... args)
206   {
207     T* context = new T(std::forward<Args>(args)...);
208     this->declare_context(context, sizeof(T));
209     return context;
210   }
211 };
212
213 XBT_PUBLIC_CLASS Context {
214 private:
215   std::function<void()> code_;
216   void_pfn_smxprocess_t cleanup_func_ = nullptr;
217   smx_process_t process_ = nullptr;
218 public:
219   bool iwannadie;
220 public:
221   Context(std::function<void()> code,
222           void_pfn_smxprocess_t cleanup_func,
223           smx_process_t process);
224   void operator()()
225   {
226     code_();
227   }
228   bool has_code() const
229   {
230     return (bool) code_;
231   }
232   smx_process_t process()
233   {
234     return this->process_;
235   }
236   void set_cleanup(void_pfn_smxprocess_t cleanup)
237   {
238     cleanup_func_ = cleanup;
239   }
240
241   // Virtual methods
242   virtual ~Context();
243   virtual void stop();
244   virtual void suspend() = 0;
245 };
246
247 XBT_PUBLIC_CLASS AttachContext : public Context {
248 public:
249
250   AttachContext(std::function<void()> code,
251           void_pfn_smxprocess_t cleanup_func,
252           smx_process_t process)
253     : Context(std::move(code), cleanup_func, process)
254   {}
255
256   ~AttachContext();
257
258   /** Called by the context when it is ready to give control
259    *  to the maestro.
260    */
261   virtual void attach_start() = 0;
262
263   /** Called by the context when it has finished its job */
264   virtual void attach_stop() = 0;
265 };
266
267 XBT_PUBLIC(void) set_maestro(std::function<void()> code);
268 XBT_PUBLIC(void) create_maestro(std::function<void()> code);
269
270 }
271 }
272
273 #endif