Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Merge remote-tracking branch 'upstream/master' into issue95
[simgrid.git] / doc / doxygen / inside_extending.doc
1 /**
2 @page inside_extending Extending SimGrid
3
4 @tableofcontents
5
6 @section simgrid_dev_guide_simcall How to add a new simcall?
7
8 First of all you might want to avoid defining a new simcall if possible:
9 @ref simgrid_dev_guide_generic_simcall.
10
11 A simcall is used to go from user mode to kernel mode. There is some
12 sort of popping dance involved, as we want to isolate the user
13 contextes from their environment (so that they can run in parallel and
14 so that we can model-check them).
15
16 In short, just add a line to src/simix/simcalls.in and run the
17 src/simix/simcalls.py script. It will guide you about how to implement
18 your simcall. Please keep reading this section (only) if you want to
19 understand how it goes.
20
21
22 The workflow of a simcall is the following:
23
24 - `<ret> simcall_<name>(<args>)`
25  - `simcall_BODY_<name>(<args>)`
26   - Initializes the simcall (store the arguments in position)
27   - If maestro, executes the simcall directly (and return)
28   - If not, call `ActorImpl::yield` to give back the control to maestro
29   - ========== KERNEL MODE ==========
30   - `ActorImpl::simcall_handle` large switch (on simcall) doing for each:
31    - `simcall_HANDLER_<name>(simcall, <args>)` (the manual code handling the simcall)
32    - If the simcall is not marked as "blocking" in its definition,
33      call `ActorImpl::simcall_answer()` that adds back the issuer
34      process to the list of processes to run in the next scheduling round.
35      It is thus the responsibility of the blocking simcalls to call
36      `ActorImpl::simcall_answer()` themselves in their handler.
37
38 Note that empty HANDLERs can be omitted. These functions usually do
39 some parameter checking, or retrieve some information about the
40 simcall issuer, but when there no need for such things, the handler
41 can be omitted. In that case, we directly call the function
42 `simcall_<name>(<args>)`.
43
44 To simplify the simcall creation, a python script generates most of
45 the code and give helpers for the remaining stuff. That script reads
46 the simcall definitions from src/simix/simcalls.in, checks that both
47 `simcall_<name>()` and `simcall_HANDLER()` are defined somewhere, and
48 generates the following files:
49
50 - popping_accessors.hpp:
51   Helper functions to get and set simcall arguments and results
52 - popping_bodies.cpp:
53   The BODY function of each simcall
54 - popping_enum.hpp:
55   Definition of type `enum class Simcall` (one value per existing simcall)
56 - popping_generated.cpp:
57   Definitions of `simcall_names[]` (debug name of each simcall), and
58   ActorImpl::simcall_handle() that deals with the simcall from within the kernel
59
60 The simcall.in file list all the simcalls in sections. A line starting by "##"
61 define a new section which will be replace by a "ifdef" in the generated code.
62
63 @section simgrid_dev_guide_generic_simcall How to avoid adding a new simcall?
64
65 We now have some generic simcalls which can be used to interface with the
66 Maestro without creating new simcalls. You might want to use them instead of
67 the defining additional simcalls.  The long term goal is to replace most of
68 the simcalls with the generic ones.
69
70 For simcalls which never block, `kernelImmediate()` can be used. It takes a
71 C++ callback executes it in maestro. Any value returned by the callback is
72 returned by `kernelImmediate()`. Conversely, if the callback throws an
73 exception, this exception is propagated out of `kernelImmediate()`. Executing
74 the code in maestro enforces mutual exclusion (no other user process is running)
75 and enforce a deterministic order which guarantees the reproducibility of the
76 simulation.  This call is particularly useful for implementing mutable calls:
77
78 ~~~
79 void Host::setProperty(const char*key, const char *value){
80   simgrid::simix::kernelImmediate([&] {
81     simgrid::kernel::resource::HostImpl* host =
82        this->extension<simgrid::kernel::resource::HostImpl>();
83     host->setProperty(key,value);
84   });
85 }
86 ~~~
87
88 If there is no blocking and no mutation involved (getters), you might consider
89 avoiding switching to Maestro and reading directly the data you're interested
90 in.
91
92 For simcalls which might block, `kernel_sync()` can be used. It takes a
93 C++ callback and executes it immediately in maestro. This C++ callback is
94 expected to return a `simgrid::kernel::Future<T>` reprensenting the operation
95 in the kernel. When the operations completes, the user process is waken up
96 with the result:
97
98 ~~~
99 try {
100   std::vector<char> result = simgrid::simix::kernel_sync([&] {
101     // Fictional example, simgrid::kernel::readFile does not exist.
102     simgrid::kernel::Future<std::vector<char>> result = simgrid::kernel::readFile(file);
103     return result;
104   });
105   XBT_DEBUG("Finished reading file %s: length %zu", file, result.size());
106 }
107 // If the operation failed, kernel_sync() throws an exception:
108 catch (std::runtime_error& e) {
109   XBT_ERROR("Could not read file %s", file);
110 }
111 ~~~
112
113 Asynchronous blocks can be implemented with `kernel_async()`. It works
114 like `kernel_sync()` but does not block. Instead, it returns a
115 `simgrid::simix::Future` representing the operation in the process:
116
117 ~~~
118 simgrid::simix::Future<std:vector<char>> result = simgrid::simix::kernel_sync([&] {
119   // Fictional example, simgrid::kernel::readFile does not exist.
120   simgrid::kernek::Future<std::vector<char>> result = simgrid::kernel::readFile(file);
121   return result;
122 };
123
124 // Do some work while the operation is pending:
125 while (!result.is_ready() && hasWorkToDo())
126   doMoreWork();
127
128 // We don't have anything to do, wait for the operation to complete and
129 // get its value:
130 try {
131   std:vector<char> data = result.get();
132   XBT_DEBUG("Finished reading file %s: length %zu", file, data.size());
133 }
134 // If the operation failed, .get() throws an exception:
135 catch (std::runtime_error& e) {
136   XBT_ERROR("Could not read file %s", file);
137 }
138 ~~~
139
140 <b>Note:</b> `kernel_sync(f)` could be implemented as `kernel_async(f).get()`.
141
142 @section simgrid_dev_guide_tag What is How to add a new tag for xml files?
143
144 You should not do something like that. Please work instead to make XML
145 avoidable, ie to make the C++ interface nice and usable.
146
147 */