Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
b15a4e65cbb2ee31895310f6867728ea135311a7
[simgrid.git] / src / kernel / EngineImpl.cpp
1 /* Copyright (c) 2016-2021. 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 #include "src/kernel/EngineImpl.hpp"
7 #include "mc/mc.h"
8 #include "simgrid/Exception.hpp"
9 #include "simgrid/kernel/routing/NetPoint.hpp"
10 #include "simgrid/kernel/routing/NetZoneImpl.hpp"
11 #include "simgrid/s4u/Host.hpp"
12 #include "simgrid/sg_config.hpp"
13 #include "src/include/surf/surf.hpp" //get_clock() and surf_solve()
14 #include "src/kernel/resource/DiskImpl.hpp"
15 #include "src/mc/mc_record.hpp"
16 #include "src/mc/mc_replay.hpp"
17 #include "src/simix/smx_private.hpp"
18 #include "src/surf/network_interface.hpp"
19 #include "src/surf/xml/platf.hpp" // FIXME: KILLME. There must be a better way than mimicking XML here
20
21 XBT_LOG_NEW_DEFAULT_CATEGORY(ker_engine, "Logging specific to Engine (kernel)");
22
23 namespace simgrid {
24 namespace kernel {
25
26 config::Flag<double> cfg_breakpoint{"debug/breakpoint",
27                                     "When non-negative, raise a SIGTRAP after given (simulated) time", -1.0};
28 EngineImpl::~EngineImpl()
29 {
30   /* Since hosts_ is a std::map, the hosts are destroyed in the lexicographic order, which ensures that the output is
31    * reproducible.
32    */
33   while (not hosts_.empty())
34     hosts_.begin()->second->destroy();
35
36   /* Also delete the other data */
37   delete netzone_root_;
38   for (auto const& kv : netpoints_)
39     delete kv.second;
40
41   for (auto const& kv : links_)
42     if (kv.second)
43       kv.second->destroy();
44 }
45
46 void EngineImpl::load_deployment(const std::string& file) const
47 {
48   sg_platf_exit();
49   sg_platf_init();
50
51   surf_parse_open(file);
52   surf_parse();
53   surf_parse_close();
54 }
55
56 void EngineImpl::register_function(const std::string& name, const actor::ActorCodeFactory& code)
57 {
58   registered_functions[name] = code;
59 }
60 void EngineImpl::register_default(const actor::ActorCodeFactory& code)
61 {
62   default_function = code;
63 }
64
65 void EngineImpl::add_model(std::shared_ptr<resource::Model> model, const std::vector<resource::Model*>& dependencies)
66 {
67   auto model_name = model->get_name();
68   xbt_assert(models_prio_.find(model_name) == models_prio_.end(),
69              "Model %s already exists, use model.set_name() to change its name", model_name.c_str());
70
71   for (const auto dep : dependencies) {
72     xbt_assert(models_prio_.find(dep->get_name()) != models_prio_.end(),
73                "Model %s doesn't exists. Impossible to use it as dependency.", dep->get_name().c_str());
74   }
75   models_.push_back(model.get());
76   models_prio_[model_name] = std::move(model);
77 }
78
79 /** Wake up all actors waiting for a Surf action to finish */
80 void EngineImpl::wake_all_waiting_actors() const
81 {
82   for (auto const& model : models_) {
83     XBT_DEBUG("Handling the failed actions (if any)");
84     while (auto* action = model->extract_failed_action()) {
85       XBT_DEBUG("   Handling Action %p", action);
86       if (action->get_activity() != nullptr)
87         activity::ActivityImplPtr(action->get_activity())->post();
88     }
89     XBT_DEBUG("Handling the terminated actions (if any)");
90     while (auto* action = model->extract_done_action()) {
91       XBT_DEBUG("   Handling Action %p", action);
92       if (action->get_activity() == nullptr)
93         XBT_DEBUG("probably vcpu's action %p, skip", action);
94       else
95         activity::ActivityImplPtr(action->get_activity())->post();
96     }
97   }
98 }
99
100 /** Execute all the tasks that are queued, e.g. `.then()` callbacks of futures. */
101 bool EngineImpl::execute_tasks()
102 {
103   xbt_assert(tasksTemp.empty());
104
105   if (tasks.empty())
106     return false;
107
108   do {
109     // We don't want the callbacks to modify the vector we are iterating over:
110     tasks.swap(tasksTemp);
111
112     // Execute all the queued tasks:
113     for (auto& task : tasksTemp)
114       task();
115
116     tasksTemp.clear();
117   } while (not tasks.empty());
118
119   return true;
120 }
121
122 void EngineImpl::rm_daemon(actor::ActorImpl* actor)
123 {
124   auto it = daemons_.find(actor);
125   xbt_assert(it != daemons_.end(), "The dying daemon is not a daemon after all. Please report that bug.");
126   daemons_.erase(it);
127 }
128
129 void EngineImpl::run()
130 {
131   if (MC_record_replay_is_active()) {
132     mc::replay(MC_record_path());
133     return;
134   }
135
136   double time = 0;
137
138   do {
139     XBT_DEBUG("New Schedule Round; size(queue)=%zu", simix_global->actors_to_run.size());
140
141     if (cfg_breakpoint >= 0.0 && surf_get_clock() >= cfg_breakpoint) {
142       XBT_DEBUG("Breakpoint reached (%g)", cfg_breakpoint.get());
143       cfg_breakpoint = -1.0;
144 #ifdef SIGTRAP
145       std::raise(SIGTRAP);
146 #else
147       std::raise(SIGABRT);
148 #endif
149     }
150
151     execute_tasks();
152
153     while (not simix_global->actors_to_run.empty()) {
154       XBT_DEBUG("New Sub-Schedule Round; size(queue)=%zu", simix_global->actors_to_run.size());
155
156       /* Run all actors that are ready to run, possibly in parallel */
157       simix_global->run_all_actors();
158
159       /* answer sequentially and in a fixed arbitrary order all the simcalls that were issued during that sub-round */
160
161       /* WARNING, the order *must* be fixed or you'll jeopardize the simulation reproducibility (see RR-7653) */
162
163       /* Here, the order is ok because:
164        *
165        *   Short proof: only maestro adds stuff to the actors_to_run array, so the execution order of user contexts do
166        *   not impact its order.
167        *
168        *   Long proof: actors remain sorted through an arbitrary (implicit, complex but fixed) order in all cases.
169        *
170        *   - if there is no kill during the simulation, actors remain sorted according by their PID.
171        *     Rationale: This can be proved inductively.
172        *        Assume that actors_to_run is sorted at a beginning of one round (it is at round 0: the deployment file
173        *        is parsed linearly).
174        *        Let's show that it is still so at the end of this round.
175        *        - if an actor is added when being created, that's from maestro. It can be either at startup
176        *          time (and then in PID order), or in response to a process_create simcall. Since simcalls are handled
177        *          in arbitrary order (inductive hypothesis), we are fine.
178        *        - If an actor is added because it's getting killed, its subsequent actions shouldn't matter
179        *        - If an actor gets added to actors_to_run because one of their blocking action constituting the meat
180        *          of a simcall terminates, we're still good. Proof:
181        *          - You are added from ActorImpl::simcall_answer() only. When this function is called depends on the
182        *            resource kind (network, cpu, disk, whatever), but the same arguments hold. Let's take communications
183        *            as an example.
184        *          - For communications, this function is called from SIMIX_comm_finish().
185        *            This function itself don't mess with the order since simcalls are handled in FIFO order.
186        *            The function is called:
187        *            - before the comm starts (invalid parameters, or resource already dead or whatever).
188        *              The order then trivial holds since maestro didn't interrupt its handling of the simcall yet
189        *            - because the communication failed or were canceled after startup. In this case, it's called from
190        *              the function we are in, by the chunk:
191        *                       set = model->states.failed_action_set;
192        *                       while ((synchro = extract(set)))
193        *                          SIMIX_simcall_post((smx_synchro_t) synchro->data);
194        *              This order is also fixed because it depends of the order in which the surf actions were
195        *              added to the system, and only maestro can add stuff this way, through simcalls.
196        *              We thus use the inductive hypothesis once again to conclude that the order in which synchros are
197        *              popped out of the set does not depend on the user code's execution order.
198        *            - because the communication terminated. In this case, synchros are served in the order given by
199        *                       set = model->states.done_action_set;
200        *                       while ((synchro = extract(set)))
201        *                          SIMIX_simcall_post((smx_synchro_t) synchro->data);
202        *              and the argument is very similar to the previous one.
203        *            So, in any case, the orders of calls to CommImpl::finish() do not depend on the order in which user
204        *            actors are executed.
205        *          So, in any cases, the orders of actors within actors_to_run do not depend on the order in which
206        *          user actors were executed previously.
207        *     So, if there is no killing in the simulation, the simulation reproducibility is not jeopardized.
208        *   - If there is some actor killings, the order is changed by this decision that comes from user-land
209        *     But this decision may not have been motivated by a situation that were different because the simulation is
210        *     not reproducible.
211        *     So, even the order change induced by the actor killing is perfectly reproducible.
212        *
213        *   So science works, bitches [http://xkcd.com/54/].
214        *
215        *   We could sort the actors_that_ran array completely so that we can describe the order in which simcalls are
216        *   handled (like "according to the PID of issuer"), but it's not mandatory (order is fixed already even if
217        *   unfriendly).
218        *   That would thus be a pure waste of time.
219        */
220
221       for (auto const& actor : simix_global->actors_that_ran) {
222         if (actor->simcall_.call_ != simix::Simcall::NONE) {
223           actor->simcall_handle(0);
224         }
225       }
226
227       execute_tasks();
228       do {
229         wake_all_waiting_actors();
230       } while (execute_tasks());
231
232       /* If only daemon actors remain, cancel their actions, mark them to die and reschedule them */
233       if (simix_global->process_list.size() == daemons_.size())
234         for (auto const& dmon : daemons_) {
235           XBT_DEBUG("Kill %s", dmon->get_cname());
236           simix_global->maestro_->kill(dmon);
237         }
238     }
239
240     time = timer::Timer::next();
241     if (time > -1.0 || not simix_global->process_list.empty()) {
242       XBT_DEBUG("Calling surf_solve");
243       time = surf_solve(time);
244       XBT_DEBUG("Moving time ahead : %g", time);
245     }
246
247     /* Notify all the hosts that have failed */
248     /* FIXME: iterate through the list of failed host and mark each of them */
249     /* as failed. On each host, signal all the running actors with host_fail */
250
251     // Execute timers and tasks until there isn't anything to be done:
252     bool again = false;
253     do {
254       again = timer::Timer::execute_all();
255       if (execute_tasks())
256         again = true;
257       wake_all_waiting_actors();
258     } while (again);
259
260     /* Clean actors to destroy */
261     simix_global->empty_trash();
262
263     XBT_DEBUG("### time %f, #actors %zu, #to_run %zu", time, simix_global->process_list.size(),
264               simix_global->actors_to_run.size());
265
266     if (time < 0. && simix_global->actors_to_run.empty() && not simix_global->process_list.empty()) {
267       if (simix_global->process_list.size() <= daemons_.size()) {
268         XBT_CRITICAL("Oops! Daemon actors cannot do any blocking activity (communications, synchronization, etc) "
269                      "once the simulation is over. Please fix your on_exit() functions.");
270       } else {
271         XBT_CRITICAL("Oops! Deadlock or code not perfectly clean.");
272       }
273       simix_global->display_all_actor_status();
274       simgrid::s4u::Engine::on_deadlock();
275       for (auto const& kv : simix_global->process_list) {
276         XBT_DEBUG("Kill %s", kv.second->get_cname());
277         simix_global->maestro_->kill(kv.second);
278       }
279     }
280   } while (time > -1.0 || not simix_global->actors_to_run.empty());
281
282   if (not simix_global->process_list.empty())
283     THROW_IMPOSSIBLE;
284
285   simgrid::s4u::Engine::on_simulation_end();
286 }
287 } // namespace kernel
288 } // namespace simgrid