Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Say goodbye to last global: surf_host_model
[simgrid.git] / doc / doxygen / inside_extending.doc
1 /**
2 @page inside_extending Extending SimGrid
3
4 @tableofcontents
5
6 @section simgrid_dev_guide_model How to add a new model?
7 The figure below shows the architecture of the SURF layer. This layer is composed
8 of different kinds of models representing the different systems we want to
9 model (i.e., cpu, network, storage, workstation, virtual machine).
10
11 A model in SimGrid is composed of three classes: Model, Resource and Action
12 (@ref SURF_interface "surf_interface.hpp").
13
14 @image html surf++.png
15 @image latex surf++.pdf "surf++" width=\textwidth
16
17 Actually there are five kind of models: CpuModel, NetworkModel, WorkstationModel,
18 WorkstationVMModel and StorageModel. For each kind of model, there is an
19 interface (e.g.: @ref SURF_cpu_interface "cpu_interface.hpp") and some implementations (e.g.: cpu_cas01.hpp,
20 cpu_ti.hpp).
21
22 The CPU model Cas01, for instance, is initialized by the function
23     void surf_cpu_model_init_Cas01()
24
25 The different network models that are offered by simgrid are stored in the array
26 that is defined as follows:
27
28 s_surf_model_description_t surf_network_model_description[] = {
29
30 @subsection simgrid_dev_guide_model_implem How to implement a new model?
31
32 If you want to create a new implementation of a kind of model you must extend
33 the classes of the corresponding interfaces.
34
35 For instance, if you want to add a new cup model called `Plop`, create two files
36 cpu_plop.hpp and cpu_plop_cpp which contains classes CpuPlopModel, CpuPlop and
37 CpuPlopAction implementing respectively the interfaces CpuModel, Cpu and
38 CpuAction. You also need to define an initializing function like this:
39 FIXME[donassolo]: update doc
40
41 ~~~~
42 void surf_cpu_model_init_plop()
43 {
44   xbt_assert(!surf_cpu_model_pm);
45
46   surf_cpu_model_pm = new CpuPlopModel();
47
48   simgrid::surf::on_postparse.connect(cpu_add_traces);
49
50   xbt_dynar_push(model_list, &surf_cpu_model_pm);
51 }
52 ~~~~
53
54 and add an entry in the corresponding array in surf_interface.cpp
55
56 ~~~~
57 s_surf_model_description_t surf_cpu_model_description[] = {
58   {"Cas01",
59    "Simplistic CPU model (time=size/speed).",
60    surf_cpu_model_init_Cas01},
61   {"Plop",
62    "The new plop CPU model.",
63    surf_cpu_model_init_plop},
64   {NULL, NULL, NULL}      // this array must be NULL terminated
65 };
66 ~~~~
67
68 @subsection simgrid_dev_guide_model_kind How to add a new kind of model?
69
70 If you want to create a new kind of model, you must create a new interface
71 where you extend the classes Model, Resource and Action, and then create an
72 implementation of this interface.
73
74
75 @section simgrid_dev_guide_surf_callbacks How to use surf callbacks?
76
77 Adding features to surf could also be handle by using surf callbacks (instead
78 of adding new implementation model). The list of available callbacks is
79 accessible there @ref SURF_callbacks. An example of using surf callbacks is the
80 energy plugin. If you want to add a plugin you need to define callback function
81 and to connect them to callbacks handler in an initialization function.
82
83 ~~~~
84 static void MyNetworkLinkCreatedCallback(NetworkLinkPtr cpu){
85   // your code
86 }
87
88 static void MyNetworkLinkDestructedCallback(NetworkLinkPtr cpu){
89   // your code
90 }
91
92 static void MyNetworkCommunicationCallback(NetworkActionPtr cpu,
93                                            RoutingEdgePtr src,
94                                            RoutingEdgePtr dst){
95   // your code
96 }
97
98 void sg_my_network_plugin_init() {
99   networkLinkCreatedCallbacks.connect(MyNetworkLinkCreatedCallback);
100   networkLinkDestructedCallbacks.connect(MyNetworkLinkDestructedCallback);
101   networkCommunicationCallbacks.connect(MyNetworkCommunicationCallback);
102 }
103 ~~~~
104
105 Then you need to add an entry in surf_interface.cpp referring to your
106 initialization function.
107
108 ~~~~
109 s_surf_model_description_t surf_plugin_description[] = {
110                   {"Energy",
111                    "Cpu energy consumption.",
112                    sg_host_energy_plugin_init},
113                   {"MyNetworkPlugin",
114                    "My network plugin.",
115                    sg_my_network_plugin_init},
116                   {NULL, NULL, NULL}      // this array must be NULL terminated
117 };
118 ~~~~
119
120 @section simgrid_dev_guide_simcall How to add a new simcall?
121
122 First of all you might want to avoid defining a new simcall if possible:
123 @ref simgrid_dev_guide_generic_simcall.
124
125 A simcall is used to go from user mode to kernel mode. There is some
126 sort of popping dance involved, as we want to isolate the user
127 contextes from their environment (so that they can run in parallel and
128 so that we can model-check them).
129
130 In short, just add a line to src/simix/simcalls.in and run the
131 src/simix/simcalls.py script. It will guide you about how to implement
132 your simcall. Please keep reading this section (only) if you want to
133 understand how it goes.
134
135
136 The workflow of a simcall is the following:
137
138 - `<ret> simcall_<name>(<args>)`
139  - `simcall_BODY_<name>(<args>)`
140   - Initializes the simcall (store the arguments in position)
141   - If maestro, executes the simcall directly (and return)
142   - If not, call `ActorImpl::yield` to give back the control to maestro
143   - ========== KERNEL MODE ==========
144   - `ActorImpl::simcall_handle` large switch (on simcall) doing for each:
145    - `simcall_HANDLER_<name>(simcall, <args>)` (the manual code handling the simcall)
146    - If the simcall is not marked as "blocking" in its definition,
147      call `ActorImpl::simcall_answer()` that adds back the issuer
148      process to the list of processes to run in the next scheduling round.
149      It is thus the responsibility of the blocking simcalls to call
150      `ActorImpl::simcall_answer()` themselves in their handler.
151
152 Note that empty HANDLERs can be omitted. These functions usually do
153 some parameter checking, or retrieve some information about the
154 simcall issuer, but when there no need for such things, the handler
155 can be omitted. In that case, we directly call the function
156 `simcall_<name>(<args>)`.
157
158 To simplify the simcall creation, a python script generates most of
159 the code and give helpers for the remaining stuff. That script reads
160 the simcall definitions from src/simix/simcalls.in, checks that both
161 `simcall_<name>()` and `simcall_HANDLER()` are defined somewhere, and
162 generates the following files:
163
164 - popping_accessors.hpp:
165   Helper functions to get and set simcall arguments and results
166 - popping_bodies.cpp:
167   The BODY function of each simcall
168 - popping_enum.hpp:
169   Definition of type `enum class Simcall` (one value per existing simcall)
170 - popping_generated.cpp:
171   Definitions of `simcall_names[]` (debug name of each simcall), and
172   ActorImpl::simcall_handle() that deals with the simcall from within the kernel
173
174 The simcall.in file list all the simcalls in sections. A line starting by "##"
175 define a new section which will be replace by a "ifdef" in the generated code.
176
177 @section simgrid_dev_guide_generic_simcall How to avoid adding a new simcall?
178
179 We now have some generic simcalls which can be used to interface with the
180 Maestro without creating new simcalls. You might want to use them instead of
181 the defining additional simcalls.  The long term goal is to replace most of
182 the simcalls with the generic ones.
183
184 For simcalls which never block, `kernelImmediate()` can be used. It takes a
185 C++ callback executes it in maestro. Any value returned by the callback is
186 returned by `kernelImmediate()`. Conversely, if the callback throws an
187 exception, this exception is propagated out of `kernelImmediate()`. Executing
188 the code in maestro enforces mutual exclusion (no other user process is running)
189 and enforce a deterministic order which guarantees the reproducibility of the
190 simulation.  This call is particularly useful for implementing mutable calls:
191
192 ~~~
193 void Host::setProperty(const char*key, const char *value){
194   simgrid::simix::kernelImmediate([&] {
195     simgrid::surf::HostImpl* surf_host = this->extension<simgrid::surf::HostImpl>();
196     surf_host->setProperty(key,value);
197   });
198 }
199 ~~~
200
201 If there is no blocking and no mutation involved (getters), you might consider
202 avoiding switching to Maestro and reading directly the data you're interested
203 in.
204
205 For simcalls which might block, `kernel_sync()` can be used. It takes a
206 C++ callback and executes it immediately in maestro. This C++ callback is
207 expected to return a `simgrid::kernel::Future<T>` reprensenting the operation
208 in the kernel. When the operations completes, the user process is waken up
209 with the result:
210
211 ~~~
212 try {
213   std::vector<char> result = simgrid::simix::kernel_sync([&] {
214     // Fictional example, simgrid::kernel::readFile does not exist.
215     simgrid::kernel::Future<std::vector<char>> result = simgrid::kernel::readFile(file);
216     return result;
217   });
218   XBT_DEBUG("Finished reading file %s: length %zu", file, result.size());
219 }
220 // If the operation failed, kernel_sync() throws an exception:
221 catch (std::runtime_error& e) {
222   XBT_ERROR("Could not read file %s", file);
223 }
224 ~~~
225
226 Asynchronous blocks can be implemented with `kernel_async()`. It works
227 like `kernel_sync()` but does not block. Instead, it returns a
228 `simgrid::simix::Future` representing the operation in the process:
229
230 ~~~
231 simgrid::simix::Future<std:vector<char>> result = simgrid::simix::kernel_sync([&] {
232   // Fictional example, simgrid::kernel::readFile does not exist.
233   simgrid::kernek::Future<std::vector<char>> result = simgrid::kernel::readFile(file);
234   return result;
235 };
236
237 // Do some work while the operation is pending:
238 while (!result.is_ready() && hasWorkToDo())
239   doMoreWork();
240
241 // We don't have anything to do, wait for the operation to complete and
242 // get its value:
243 try {
244   std:vector<char> data = result.get();
245   XBT_DEBUG("Finished reading file %s: length %zu", file, data.size());
246 }
247 // If the operation failed, .get() throws an exception:
248 catch (std::runtime_error& e) {
249   XBT_ERROR("Could not read file %s", file);
250 }
251 ~~~
252
253 <b>Note:</b> `kernel_sync(f)` could be implemented as `kernel_async(f).get()`.
254
255 @section simgrid_dev_guide_tag What is How to add a new tag for xml files?
256
257 You should not do something like that. Please work instead to make XML
258 avoidable, ie to make the C++ interface nice and usable.
259
260 */