2 #include "simdag/simdag.h"
3 #include "xbt/sysdep.h"
6 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(sd_task,sd,
7 "Logging specific to SimDag (task)");
9 static void __SD_task_remove_dependencies(SD_task_t task);
10 static void __SD_task_destroy_scheduling_data(SD_task_t task);
13 * \brief Creates a new task.
15 * \param name the name of the task (can be \c NULL)
16 * \param data the user data you want to associate with the task (can be \c NULL)
17 * \param amount amount of the task
18 * \return the new task
19 * \see SD_task_destroy()
21 SD_task_t SD_task_create(const char *name, void *data, double amount) {
24 SD_task_t task = xbt_new0(s_SD_task_t, 1);
26 /* general information */
27 task->data = data; /* user data */
29 task->name = xbt_strdup(name);
33 task->state_set = sd_global->not_scheduled_task_set;
34 xbt_swag_insert(task,task->state_set);
36 task->amount = amount;
37 task->surf_action = NULL;
38 task->watch_points = 0;
39 task->state_changed = 0;
42 task->tasks_before = xbt_dynar_new(sizeof(SD_dependency_t), NULL);
43 task->tasks_after = xbt_dynar_new(sizeof(SD_dependency_t), NULL);
45 /* scheduling parameters */
46 task->workstation_nb = 0;
47 task->workstation_list = NULL;
48 task->computation_amount = NULL;
49 task->communication_amount = NULL;
56 * \brief Returns the user data of a task
59 * \return the user data associated with this task (can be \c NULL)
60 * \see SD_task_set_data()
62 void* SD_task_get_data(SD_task_t task) {
64 xbt_assert0(task != NULL, "Invalid parameter");
69 * \brief Sets the user data of a task
71 * The new data can be \c NULL. The old data should have been freed first
72 * if it was not \c NULL.
75 * \param data the new data you want to associate with this task
76 * \see SD_task_get_data()
78 void SD_task_set_data(SD_task_t task, void *data) {
80 xbt_assert0(task != NULL, "Invalid parameter");
85 * \brief Returns the state of a task
88 * \return the current \ref e_SD_task_state_t "state" of this task:
89 * #SD_NOT_SCHEDULED, #SD_SCHEDULED, #SD_READY, #SD_RUNNING, #SD_DONE or #SD_FAILED
90 * \see e_SD_task_state_t
92 e_SD_task_state_t SD_task_get_state(SD_task_t task) {
94 xbt_assert0(task != NULL, "Invalid parameter");
96 if (task->state_set == sd_global->scheduled_task_set)
98 if (task->state_set == sd_global->done_task_set)
100 if (task->state_set == sd_global->running_task_set)
102 if (task->state_set == sd_global->ready_task_set)
104 if (task->state_set == sd_global->not_scheduled_task_set)
105 return SD_NOT_SCHEDULED;
109 /* Changes the state of a task. Updates the swags and the flag sd_global->watch_point_reached.
111 void __SD_task_set_state(SD_task_t task, e_SD_task_state_t new_state) {
112 xbt_swag_remove(task, task->state_set);
114 case SD_NOT_SCHEDULED:
115 task->state_set = sd_global->not_scheduled_task_set;
118 task->state_set = sd_global->scheduled_task_set;
121 task->state_set = sd_global->ready_task_set;
124 task->state_set = sd_global->running_task_set;
127 task->state_set = sd_global->done_task_set;
130 task->state_set = sd_global->failed_task_set;
133 xbt_assert0(0, "Invalid state");
135 xbt_swag_insert(task, task->state_set);
137 if (task->watch_points & new_state) {
138 INFO1("Watch point reached with task '%s'!", SD_task_get_name(task));
139 sd_global->watch_point_reached = 1;
140 SD_task_unwatch(task, new_state); /* remove the watch point */
145 * \brief Returns the name of a task
148 * \return the name of this task (can be \c NULL)
150 const char* SD_task_get_name(SD_task_t task) {
151 SD_CHECK_INIT_DONE();
152 xbt_assert0(task != NULL, "Invalid parameter");
157 * \brief Returns the total amount of a task
160 * \return the total amount of this task
161 * \see SD_task_get_remaining_amount()
163 double SD_task_get_amount(SD_task_t task) {
164 SD_CHECK_INIT_DONE();
165 xbt_assert0(task != NULL, "Invalid parameter");
170 * \brief Returns the remaining amount of a task
173 * \return the remaining amount of this task
174 * \see SD_task_get_amount()
176 double SD_task_get_remaining_amount(SD_task_t task) {
177 SD_CHECK_INIT_DONE();
178 xbt_assert0(task != NULL, "Invalid parameter");
180 if (task->surf_action)
181 return task->surf_action->remains;
186 /* temporary function for debbuging */
187 static void __SD_print_dependencies(SD_task_t task) {
188 INFO1("The following tasks must be executed before %s:", SD_task_get_name(task));
189 xbt_dynar_t dynar = task->tasks_before;
190 int length = xbt_dynar_length(dynar);
192 SD_dependency_t dependency;
193 for (i = 0; i < length; i++) {
194 xbt_dynar_get_cpy(dynar, i, &dependency);
195 INFO1(" %s", SD_task_get_name(dependency->src));
198 INFO1("The following tasks must be executed after %s:", SD_task_get_name(task));
200 dynar = task->tasks_after;
201 length = xbt_dynar_length(dynar);
202 for (i = 0; i < length; i++) {
203 xbt_dynar_get_cpy(dynar, i, &dependency);
204 INFO1(" %s", SD_task_get_name(dependency->dst));
206 INFO0("----------------------------");
209 /* Destroys a dependency between two tasks.
211 static void __SD_task_dependency_destroy(void *dependency) {
212 if (((SD_dependency_t) dependency)->name != NULL)
213 xbt_free(((SD_dependency_t) dependency)->name);
214 xbt_free(dependency);
218 * \brief Adds a dependency between two tasks
220 * \a dst will depend on \a src, ie \a dst will not start before \a src is finished.
221 * Their \ref e_SD_task_state_t "state" must be #SD_NOT_SCHEDULED, #SD_SCHEDULED or #SD_READY.
223 * \param name the name of the new dependency (can be \c NULL)
224 * \param data the user data you want to associate with this dependency (can be \c NULL)
225 * \param src the task which must be executed first
226 * \param dst the task you want to make depend on \a src
227 * \see SD_task_dependency_remove()
229 void SD_task_dependency_add(const char *name, void *data, SD_task_t src, SD_task_t dst) {
230 SD_CHECK_INIT_DONE();
231 xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
232 xbt_assert1(src != dst, "Cannot add a dependency between task '%s' and itself", SD_task_get_name(src));
233 xbt_assert1(__SD_task_is_not_scheduled(src) || __SD_task_is_scheduled_or_ready(src),
234 "Task '%s' must be SD_NOT_SCHEDULED, SD_SCHEDULED or SD_READY", SD_task_get_name(src));
235 xbt_assert1(__SD_task_is_not_scheduled(dst) || __SD_task_is_scheduled_or_ready(dst),
236 "Task '%s' must be SD_NOT_SCHEDULED, SD_SCHEDULED or SD_READY", SD_task_get_name(dst));
238 xbt_dynar_t dynar = src->tasks_after;
239 int length = xbt_dynar_length(dynar);
242 SD_dependency_t dependency;
243 for (i = 0; i < length && !found; i++) {
244 xbt_dynar_get_cpy(dynar, i, &dependency);
245 found = (dependency->dst == dst);
247 xbt_assert2(!found, "A dependency already exists between task '%s' and task '%s'",
248 SD_task_get_name(src), SD_task_get_name(dst));
250 dependency = xbt_new0(s_SD_dependency_t, 1);
253 dependency->name = xbt_strdup(name);
255 dependency->name = NULL;
257 dependency->data = data;
258 dependency->src = src;
259 dependency->dst = dst;
261 /* src must be executed before dst */
262 xbt_dynar_push(src->tasks_after, &dependency);
263 xbt_dynar_push(dst->tasks_before, &dependency);
265 /* if the task was ready, then dst->tasks_before is not empty anymore,
266 so we must go back to state SD_SCHEDULED */
267 if (__SD_task_is_ready(dst)) {
268 DEBUG1("SD_task_dependency_add: %s was ready and becomes scheduled!", SD_task_get_name(dst));
269 __SD_task_set_state(dst, SD_SCHEDULED);
272 /* __SD_print_dependencies(src);
273 __SD_print_dependencies(dst); */
277 * \brief Remove a dependency between two tasks
280 * \param dst a task depending on \a src
281 * \see SD_task_dependency_add()
283 void SD_task_dependency_remove(SD_task_t src, SD_task_t dst) {
284 SD_CHECK_INIT_DONE();
285 xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
287 /* remove the dependency from src->tasks_after */
288 xbt_dynar_t dynar = src->tasks_after;
289 int length = xbt_dynar_length(dynar);
292 SD_dependency_t dependency;
293 for (i = 0; i < length && !found; i++) {
294 xbt_dynar_get_cpy(dynar, i, &dependency);
295 if (dependency->dst == dst) {
296 xbt_dynar_remove_at(dynar, i, NULL);
300 xbt_assert4(found, "No dependency found between task '%s' and '%s': task '%s' is not a successor of task '%s'",
301 SD_task_get_name(src), SD_task_get_name(dst), SD_task_get_name(dst), SD_task_get_name(src));
303 /* remove the dependency from dst->tasks_before */
304 dynar = dst->tasks_before;
305 length = xbt_dynar_length(dynar);
308 for (i = 0; i < length && !found; i++) {
309 xbt_dynar_get_cpy(dynar, i, &dependency);
310 if (dependency->src == src) {
311 xbt_dynar_remove_at(dynar, i, NULL);
312 __SD_task_dependency_destroy(dependency);
316 xbt_assert4(found, "SimDag error: task '%s' is a successor of '%s' but task '%s' is not a predecessor of task '%s'",
317 SD_task_get_name(dst), SD_task_get_name(src), SD_task_get_name(src), SD_task_get_name(dst)); /* should never happen... */
319 /* if the task was scheduled and dst->tasks_before is empty now, we can make it ready */
320 if (xbt_dynar_length(dst->tasks_before) == 0 && __SD_task_is_scheduled(dst))
321 __SD_task_set_state(dst, SD_READY);
323 /* __SD_print_dependencies(src);
324 __SD_print_dependencies(dst);*/
328 * \brief Returns the user data associated with a dependency between two tasks
331 * \param dst a task depending on \a src
332 * \return the user data associated with this dependency (can be \c NULL)
333 * \see SD_task_dependency_add()
335 void *SD_task_dependency_get_data(SD_task_t src, SD_task_t dst) {
336 SD_CHECK_INIT_DONE();
337 xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
339 xbt_dynar_t dynar = src->tasks_after;
340 int length = xbt_dynar_length(dynar);
343 SD_dependency_t dependency;
344 for (i = 0; i < length && !found; i++) {
345 xbt_dynar_get_cpy(dynar, i, &dependency);
346 found = (dependency->dst == dst);
348 xbt_assert2(found, "No dependency found between task '%s' and '%s'", SD_task_get_name(src), SD_task_get_name(dst));
349 return dependency->data;
352 /* temporary function for debugging */
353 static void __SD_print_watch_points(SD_task_t task) {
354 static const int state_masks[] = {SD_SCHEDULED, SD_RUNNING, SD_READY, SD_DONE, SD_FAILED};
355 static const char* state_names[] = {"scheduled", "running", "ready", "done", "failed"};
357 INFO2("Task '%s' watch points (%x): ", SD_task_get_name(task), task->watch_points);
360 for (i = 0; i < 5; i++) {
361 if (task->watch_points & state_masks[i])
362 INFO1("%s ", state_names[i]);
367 * \brief Adds a watch point to a task
369 * SD_simulate() will stop as soon as the \ref e_SD_task_state_t "state" of this task becomes the one given in argument. The
370 * watch point is then automatically removed.
373 * \param state the \ref e_SD_task_state_t "state" you want to watch (cannot be #SD_NOT_SCHEDULED)
374 * \see SD_task_unwatch()
376 void SD_task_watch(SD_task_t task, e_SD_task_state_t state) {
377 SD_CHECK_INIT_DONE();
378 xbt_assert0(task != NULL, "Invalid parameter");
379 xbt_assert0(state != SD_NOT_SCHEDULED, "Cannot add a watch point for state SD_NOT_SCHEDULED");
381 task->watch_points = task->watch_points | state;
382 /* __SD_print_watch_points(task);*/
386 * \brief Removes a watch point from a task
389 * \param state the \ref e_SD_task_state_t "state" you no longer want to watch
390 * \see SD_task_watch()
392 void SD_task_unwatch(SD_task_t task, e_SD_task_state_t state) {
393 SD_CHECK_INIT_DONE();
394 xbt_assert0(task != NULL, "Invalid parameter");
395 xbt_assert0(state != SD_NOT_SCHEDULED, "Cannot have a watch point for state SD_NOT_SCHEDULED");
397 task->watch_points = task->watch_points & ~state;
398 /* __SD_print_watch_points(task);*/
402 * \brief Returns an approximative estimation of the execution time of a task.
404 * The estimation is very approximative because the value returned is the time
405 * the task would take if it was executed now and if it was the only task.
407 * \param task the task to evaluate
408 * \param workstation_nb number of workstations on which the task would be executed
409 * \param workstation_list the workstations on which the task would be executed
410 * \param computation_amount computation amount for each workstation
411 * \param communication_amount communication amount between each pair of workstations
412 * \param rate task execution speed rate
415 double SD_task_get_execution_time(SD_task_t task,
417 const SD_workstation_t *workstation_list,
418 const double *computation_amount,
419 const double *communication_amount,
421 /* the task execution time is the maximum execution time of the parallel tasks */
422 double time, max_time = 0.0;
424 for (i = 0; i < workstation_nb; i++) {
425 time = SD_workstation_get_computation_time(workstation_list[i], computation_amount[i]);
427 for (j = 0; j < workstation_nb; j++) {
428 time += SD_route_get_communication_time(workstation_list[i], workstation_list[j],
429 communication_amount[i * workstation_nb + j]);
432 if (time > max_time) {
436 return max_time * SD_task_get_amount(task);
440 * \brief Schedules a task
442 * The task state must be #SD_NOT_SCHEDULED.
443 * Once scheduled, a task will be executed as soon as possible in SD_simulate(),
444 * i.e. when its dependencies are satisfied.
446 * \param task the task you want to schedule
447 * \param workstation_nb number of workstations on which the task will be executed
448 * \param workstation_list the workstations on which the task will be executed
449 * \param computation_amount computation amount for each workstation
450 * \param communication_amount communication amount between each pair of workstations
451 * \param rate task execution speed rate
452 * \see SD_task_unschedule()
454 void SD_task_schedule(SD_task_t task, int workstation_nb,
455 const SD_workstation_t *workstation_list, const double *computation_amount,
456 const double *communication_amount, double rate) {
457 SD_CHECK_INIT_DONE();
458 xbt_assert0(task, "Invalid parameter");
459 xbt_assert1(__SD_task_is_not_scheduled(task), "Task '%s' has already been scheduled.", SD_task_get_name(task));
460 xbt_assert0(workstation_nb > 0, "workstation_nb must be positive");
462 task->workstation_nb = workstation_nb;
465 task->computation_amount = xbt_new0(double, workstation_nb);
466 memcpy(task->computation_amount, computation_amount, sizeof(double) * workstation_nb);
468 int communication_nb = workstation_nb * workstation_nb;
469 task->communication_amount = xbt_new0(double, communication_nb);
470 memcpy(task->communication_amount, communication_amount, sizeof(double) * communication_nb);
472 /* we have to create a Surf workstation array instead of the SimDag workstation array */
473 task->workstation_list = xbt_new0(void*, workstation_nb);
475 for (i = 0; i < workstation_nb; i++) {
476 task->workstation_list[i] = workstation_list[i]->surf_workstation;
479 /* update the task state */
480 if (xbt_dynar_length(task->tasks_before) == 0)
481 __SD_task_set_state(task, SD_READY);
483 __SD_task_set_state(task, SD_SCHEDULED);
487 * \brief Unschedules a task
489 * The task state must be #SD_SCHEDULED, #SD_READY, #SD_RUNNING or #SD_FAILED.
490 * If you call this function, the task state becomes #SD_NOT_SCHEDULED.
491 * Call SD_task_schedule() to schedule it again.
493 * \param task the task you want to unschedule
494 * \see SD_task_schedule()
496 void SD_task_unschedule(SD_task_t task) {
497 SD_CHECK_INIT_DONE();
498 xbt_assert0(task != NULL, "Invalid parameter");
499 xbt_assert1(task->state_set == sd_global->scheduled_task_set ||
500 task->state_set == sd_global->ready_task_set ||
501 task->state_set == sd_global->running_task_set ||
502 task->state_set == sd_global->failed_task_set,
503 "Task %s: the state must be SD_SCHEDULED, SD_READY, SD_RUNNING or SD_FAILED",
504 SD_task_get_name(task));
506 if (__SD_task_is_scheduled_or_ready(task)) /* if the task is scheduled or ready */
507 __SD_task_destroy_scheduling_data(task);
509 if (__SD_task_is_running(task)) /* the task should become SD_FAILED */
510 surf_workstation_resource->common_public->action_cancel(task->surf_action);
512 __SD_task_set_state(task, SD_NOT_SCHEDULED);
515 /* Destroys the data memorised by SD_task_schedule. Task state must be SD_SCHEDULED or SD_READY.
517 static void __SD_task_destroy_scheduling_data(SD_task_t task) {
518 SD_CHECK_INIT_DONE();
519 xbt_assert1(__SD_task_is_scheduled_or_ready(task),
520 "Task '%s' must be SD_SCHEDULED or SD_READY", SD_task_get_name(task));
521 xbt_free(task->workstation_list);
522 xbt_free(task->computation_amount);
523 xbt_free(task->communication_amount);
526 /* Runs a task. This function is called by SD_simulate() when a scheduled task can start
527 * (ie when its dependencies are satisfied).
529 surf_action_t __SD_task_run(SD_task_t task) {
530 SD_CHECK_INIT_DONE();
531 xbt_assert0(task != NULL, "Invalid parameter");
532 xbt_assert2(__SD_task_is_ready(task), "Task '%s' is not ready! Task state: %d",
533 SD_task_get_name(task), SD_task_get_state(task));
535 surf_action_t surf_action = surf_workstation_resource->extension_public->
536 execute_parallel_task(task->workstation_nb,
537 task->workstation_list,
538 task->computation_amount,
539 task->communication_amount,
543 DEBUG1("surf_action = %p", surf_action);
545 __SD_task_destroy_scheduling_data(task); /* now the scheduling data are not useful anymore */
546 __SD_task_set_state(task, SD_RUNNING);
550 /* Remove all dependencies associated with a task. This function is called when the task is destroyed.
552 static void __SD_task_remove_dependencies(SD_task_t task) {
553 /* we must destroy the dependencies carefuly (with SD_dependency_remove)
554 because each one is stored twice */
555 SD_dependency_t dependency;
556 while (xbt_dynar_length(task->tasks_before) > 0) {
557 xbt_dynar_get_cpy(task->tasks_before, 0, &dependency);
558 SD_task_dependency_remove(dependency->src, dependency->dst);
561 while (xbt_dynar_length(task->tasks_after) > 0) {
562 xbt_dynar_get_cpy(task->tasks_after, 0, &dependency);
563 SD_task_dependency_remove(dependency->src, dependency->dst);
568 * \brief Destroys a task.
570 * The user data (if any) should have been destroyed first.
572 * \param task the task you want to destroy
573 * \see SD_task_create()
575 void SD_task_destroy(SD_task_t task) {
576 SD_CHECK_INIT_DONE();
577 xbt_assert0(task != NULL, "Invalid parameter");
579 DEBUG1("Destroying task %s...", SD_task_get_name(task));
581 __SD_task_remove_dependencies(task);
583 /* if the task was scheduled or ready we have to free the scheduling parameters */
584 if (__SD_task_is_scheduled_or_ready(task))
585 __SD_task_destroy_scheduling_data(task);
587 if (task->name != NULL)
588 xbt_free(task->name);
590 if (task->surf_action != NULL)
591 surf_workstation_resource->common_public->action_free(task->surf_action);
593 xbt_dynar_free(&task->tasks_before);
594 xbt_dynar_free(&task->tasks_after);
597 DEBUG0("Task destroyed.");