Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Move globals to EngineImpl
[simgrid.git] / src / simdag / sd_global.cpp
1 /* Copyright (c) 2006-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 "simdag_private.hpp"
7 #include "simgrid/kernel/resource/Action.hpp"
8 #include "simgrid/kernel/resource/Model.hpp"
9 #include "simgrid/s4u/Engine.hpp"
10 #include "simgrid/sg_config.hpp"
11 #include "src/kernel/EngineImpl.hpp"
12 #include "src/surf/surf_interface.hpp"
13
14 #include <array>
15
16 XBT_LOG_NEW_CATEGORY(sd, "Logging specific to SimDag");
17 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(sd_kernel, sd, "Logging specific to SimDag (kernel)");
18
19 simgrid::sd::Global* sd_global = nullptr;
20
21 namespace simgrid {
22 namespace sd {
23
24 std::set<SD_task_t>* simulate(double how_long)
25 {
26   XBT_VERB("Run simulation for %f seconds", how_long);
27
28   sd_global->watch_point_reached = false;
29   sd_global->return_set.clear();
30
31   /* explore the runnable tasks */
32   while (not sd_global->runnable_tasks.empty())
33     SD_task_run(*(sd_global->runnable_tasks.begin()));
34
35   double elapsed_time = 0.0;
36   double total_time   = 0.0;
37   /* main loop */
38   while (elapsed_time >= 0 && (how_long < 0 || 0.00001 < (how_long - total_time)) &&
39          not sd_global->watch_point_reached) {
40     XBT_DEBUG("Total time: %f", total_time);
41
42     elapsed_time = surf_solve(how_long > 0 ? surf_get_clock() + how_long - total_time : -1.0);
43     XBT_DEBUG("surf_solve() returns %f", elapsed_time);
44     if (elapsed_time > 0.0)
45       total_time += elapsed_time;
46
47     /* let's see which tasks are done */
48     for (auto const& model : simgrid::kernel::EngineImpl::get_instance()->get_all_models()) {
49       const simgrid::kernel::resource::Action* action = model->extract_done_action();
50       while (action != nullptr && action->get_data() != nullptr) {
51         auto* task = static_cast<SD_task_t>(action->get_data());
52         XBT_VERB("Task '%s' done", SD_task_get_name(task));
53         SD_task_set_state(task, SD_DONE);
54
55         /* the state has changed. Add it only if it's the first change */
56         sd_global->return_set.emplace(task);
57
58         /* remove the dependencies after this task */
59         for (auto const& succ : *task->successors) {
60           succ->predecessors->erase(task);
61           succ->inputs->erase(task);
62           XBT_DEBUG("Release dependency on %s: %zu remain(s). Becomes schedulable if %zu=0", SD_task_get_name(succ),
63                     succ->predecessors->size() + succ->inputs->size(), succ->predecessors->size());
64
65           if (SD_task_get_state(succ) == SD_NOT_SCHEDULED && succ->predecessors->empty())
66             SD_task_set_state(succ, SD_SCHEDULABLE);
67
68           if (SD_task_get_state(succ) == SD_SCHEDULED && succ->predecessors->empty() && succ->inputs->empty())
69             SD_task_set_state(succ, SD_RUNNABLE);
70
71           if (SD_task_get_state(succ) == SD_RUNNABLE && not sd_global->watch_point_reached)
72             SD_task_run(succ);
73         }
74         task->successors->clear();
75
76         for (auto const& output : *task->outputs) {
77           output->start_time = task->finish_time;
78           output->predecessors->erase(task);
79           if (SD_task_get_state(output) == SD_SCHEDULED)
80             SD_task_set_state(output, SD_RUNNABLE);
81           else
82             SD_task_set_state(output, SD_SCHEDULABLE);
83
84           SD_task_t comm_dst = *(output->successors->begin());
85           if (SD_task_get_state(comm_dst) == SD_NOT_SCHEDULED && comm_dst->predecessors->empty()) {
86             XBT_DEBUG("%s is a transfer, %s may be ready now if %zu=0", SD_task_get_name(output),
87                       SD_task_get_name(comm_dst), comm_dst->predecessors->size());
88             SD_task_set_state(comm_dst, SD_SCHEDULABLE);
89           }
90           if (SD_task_get_state(output) == SD_RUNNABLE && not sd_global->watch_point_reached)
91             SD_task_run(output);
92         }
93         task->outputs->clear();
94         action = model->extract_done_action();
95       }
96
97       /* let's see which tasks have just failed */
98       action = model->extract_failed_action();
99       while (action != nullptr) {
100         auto* task = static_cast<SD_task_t>(action->get_data());
101         XBT_VERB("Task '%s' failed", SD_task_get_name(task));
102         SD_task_set_state(task, SD_FAILED);
103         sd_global->return_set.insert(task);
104         action = model->extract_failed_action();
105       }
106     }
107   }
108
109   if (not sd_global->watch_point_reached && how_long < 0 && not sd_global->initial_tasks.empty()) {
110     XBT_WARN("Simulation is finished but %zu tasks are still not done", sd_global->initial_tasks.size());
111     for (auto const& t : sd_global->initial_tasks)
112       XBT_WARN("%s is in %s state", SD_task_get_name(t), __get_state_name(SD_task_get_state(t)));
113   }
114
115   XBT_DEBUG("elapsed_time = %f, total_time = %f, watch_point_reached = %d", elapsed_time, total_time,
116             sd_global->watch_point_reached);
117   XBT_DEBUG("current time = %f", surf_get_clock());
118
119   return &sd_global->return_set;
120 }
121 } // namespace sd
122 } // namespace simgrid
123
124 /**
125  * @brief helper for pretty printing of task state
126  * @param state the state of a task
127  * @return the equivalent as a readable string
128  */
129 const char* __get_state_name(e_SD_task_state_t state)
130 {
131   static constexpr std::array<const char*, 7> state_names{
132       {"not scheduled", "schedulable", "scheduled", "runnable", "running", "done", "failed"}};
133   return state_names.at(static_cast<int>(log2(static_cast<double>(state))));
134 }
135
136 /**
137  * @brief Initializes SD internal data
138  *
139  * This function must be called before any other SD function. Then you should call SD_create_environment().
140  *
141  * @param argc argument number
142  * @param argv argument list
143  * @see SD_create_environment(), SD_exit()
144  */
145 void SD_init_nocheck(int* argc, char** argv)
146 {
147   xbt_assert(sd_global == nullptr, "SD_init() already called");
148
149   surf_init(argc, argv);
150
151   sd_global = new simgrid::sd::Global();
152
153   simgrid::config::set_default<std::string>("host/model", "ptask_L07");
154   if (simgrid::config::get_value<bool>("debug/clean-atexit"))
155     atexit(SD_exit);
156 }
157
158 /** @brief set a configuration variable
159  *
160  * Do --help on any simgrid binary to see the list of currently existing configuration variables, and
161  * see Section @ref options.
162  *
163  * Example: SD_config("host/model","default")
164  */
165 void SD_config(const char* key, const char* value)
166 {
167   xbt_assert(sd_global, "ERROR: Please call SD_init() before using SD_config()");
168   simgrid::config::set_as_string(key, value);
169 }
170
171 /**
172  * @brief Creates the environment
173  *
174  * The environment (i.e. the @ref SD_host_api "hosts" and the @ref SD_link_api "links") is created with
175  * the data stored in the given XML platform file.
176  *
177  * @param platform_file name of an XML file describing the environment to create
178  * @see SD_host_api, SD_link_api
179  *
180  * The XML file follows this DTD:
181  *
182  *     @include simgrid.dtd
183  *
184  * Here is a small example of such a platform:
185  *
186  *     @include small_platform.xml
187  */
188 void SD_create_environment(const char* platform_file)
189 {
190   simgrid::s4u::Engine::get_instance()->load_platform(platform_file);
191
192   XBT_DEBUG("Host number: %zu, link number: %d", sg_host_count(), sg_link_count());
193 #if SIMGRID_HAVE_JEDULE
194   jedule_sd_init();
195 #endif
196   XBT_VERB("Starting simulation...");
197   surf_presolve(); /* Takes traces into account */
198 }
199
200 /**
201  * @brief Launches the simulation.
202  *
203  * The function will execute the @ref SD_RUNNABLE runnable tasks.
204  * If @a how_long is positive, then the simulation will be stopped either when time reaches @a how_long or when a watch
205  * point is reached.
206  * A non-positive value for @a how_long means no time limit, in which case the simulation will be stopped either when a
207  * watch point is reached or when no more task can be executed.
208  * Then you can call SD_simulate() again.
209  *
210  * @param how_long maximum duration of the simulation (a negative value means no time limit)
211  * @return a dynar of @ref SD_task_t whose state has changed.
212  * @see SD_task_schedule(), SD_task_watch()
213  */
214 void SD_simulate(double how_long)
215 {
216   simgrid::sd::simulate(how_long);
217 }
218
219 void SD_simulate_with_update(double how_long, xbt_dynar_t changed_tasks_dynar)
220 {
221   const std::set<SD_task_t>* changed_tasks = simgrid::sd::simulate(how_long);
222   for (auto const& task : *changed_tasks)
223     xbt_dynar_push(changed_tasks_dynar, &task);
224 }
225
226 /** @brief Returns the current clock, in seconds */
227 double SD_get_clock()
228 {
229   return surf_get_clock();
230 }
231
232 /**
233  * @brief Destroys all SD internal data
234  * This function should be called when the simulation is over. Don't forget to destroy too.
235  * @see SD_init(), SD_task_destroy()
236  */
237 void SD_exit()
238 {
239 #if SIMGRID_HAVE_JEDULE
240   jedule_sd_exit();
241 #endif
242   delete sd_global;
243 }