1 /* Copyright (c) 2006, 2007, 2008, 2009, 2010, 2011. The SimGrid Team.
2 * All rights reserved. */
4 /* This program is free software; you can redistribute it and/or modify it
5 * under the terms of the license (GNU LGPL) which comes with this package. */
8 #include "simdag/simdag.h"
9 #include "xbt/sysdep.h"
10 #include "xbt/dynar.h"
11 #include "instr/instr_private.h"
13 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(sd_task, sd,
14 "Logging specific to SimDag (task)");
16 static void __SD_task_remove_dependencies(SD_task_t task);
17 static void __SD_task_destroy_scheduling_data(SD_task_t task);
19 void* SD_task_new_f(void) {
20 SD_task_t task = xbt_new0(s_SD_task_t, 1);
21 task->tasks_before = xbt_dynar_new(sizeof(SD_dependency_t), NULL);
22 task->tasks_after = xbt_dynar_new(sizeof(SD_dependency_t), NULL);
26 void SD_task_recycle_f(void *t) {
27 SD_task_t task = (SD_task_t)t;
29 __SD_task_remove_dependencies(task);
30 /* if the task was scheduled or runnable we have to free the scheduling parameters */
31 if (__SD_task_is_scheduled_or_runnable(task))
32 __SD_task_destroy_scheduling_data(task);
33 if (task->state_set != NULL) {/* would be null if just created */
34 xbt_swag_remove(task, task->state_set);
36 xbt_swag_remove(task, sd_global->return_set);
38 if (task->name != NULL)
41 if (task->surf_action != NULL)
42 surf_workstation_model->action_unref(task->surf_action);
44 if (task->workstation_list != NULL)
45 xbt_free(task->workstation_list);
47 if (task->communication_amount)
48 xbt_free(task->communication_amount);
50 if (task->computation_amount)
51 xbt_free(task->computation_amount);
53 /* Reset the content */
54 task->kind = SD_TASK_NOT_TYPED;
55 task->state_hookup.prev = NULL;
56 task->state_hookup.next = NULL;
57 task->state_set = sd_global->not_scheduled_task_set;
58 xbt_swag_insert(task, task->state_set);
59 task->state = SD_NOT_SCHEDULED;
60 task->return_hookup.prev = NULL;
61 task->return_hookup.next = NULL;
65 task->start_time = -1.0;
66 task->finish_time = -1.0;
67 task->surf_action = NULL;
68 task->watch_points = 0;
71 xbt_dynar_reset(task->tasks_before); // = new(sizeof(SD_dependency_t), NULL);
72 xbt_dynar_reset(task->tasks_after); // = xbt_dynar_new(sizeof(SD_dependency_t), NULL);
73 task->unsatisfied_dependencies = 0;
74 task->is_not_ready = 0;
76 /* scheduling parameters */
77 task->workstation_nb = 0;
78 task->workstation_list = NULL;
79 task->computation_amount = NULL;
80 task->communication_amount = NULL;
84 TRACE_sd_task_create(task);
87 void SD_task_free_f(void *t) {
88 SD_task_t task = (SD_task_t)t;
90 __SD_task_remove_dependencies(task);
91 /* if the task was scheduled or runnable we have to free the scheduling parameters */
92 if (__SD_task_is_scheduled_or_runnable(task))
93 __SD_task_destroy_scheduling_data(task);
94 if (task->state_set != NULL) /* would be null if just created */
95 xbt_swag_remove(task, task->state_set);
97 xbt_swag_remove(task, sd_global->return_set);
99 if (task->name != NULL)
100 xbt_free(task->name);
102 if (task->surf_action != NULL)
103 surf_workstation_model->action_unref(task->surf_action);
105 if (task->workstation_list != NULL)
106 xbt_free(task->workstation_list);
108 if (task->communication_amount)
109 xbt_free(task->communication_amount);
111 if (task->computation_amount)
112 xbt_free(task->computation_amount);
114 TRACE_sd_task_destroy(task);
117 xbt_dynar_free(&task->tasks_before);
118 xbt_dynar_free(&task->tasks_after);
124 * \brief Creates a new task.
126 * \param name the name of the task (can be \c NULL)
127 * \param data the user data you want to associate with the task (can be \c NULL)
128 * \param amount amount of the task
129 * \return the new task
130 * \see SD_task_destroy()
132 SD_task_t SD_task_create(const char *name, void *data, double amount)
134 SD_task_t task = xbt_mallocator_get(sd_global->task_mallocator);
136 /* general information */
137 task->data = data; /* user data */
138 task->name = xbt_strdup(name);
139 task->amount = amount;
140 task->remains = amount;
142 sd_global->task_number++;
147 * \brief Destroys a task.
149 * The user data (if any) should have been destroyed first.
151 * \param task the task you want to destroy
152 * \see SD_task_create()
154 void SD_task_destroy(SD_task_t task)
156 XBT_DEBUG("Destroying task %s...", SD_task_get_name(task));
158 xbt_mallocator_release(sd_global->task_mallocator,task);
159 sd_global->task_number--;
161 XBT_DEBUG("Task destroyed.");
166 * \brief Returns the user data of a task
169 * \return the user data associated with this task (can be \c NULL)
170 * \see SD_task_set_data()
172 void *SD_task_get_data(SD_task_t task)
178 * \brief Sets the user data of a task
180 * The new data can be \c NULL. The old data should have been freed first
181 * if it was not \c NULL.
184 * \param data the new data you want to associate with this task
185 * \see SD_task_get_data()
187 void SD_task_set_data(SD_task_t task, void *data)
193 * \brief Returns the state of a task
196 * \return the current \ref e_SD_task_state_t "state" of this task:
197 * #SD_NOT_SCHEDULED, #SD_SCHEDULED, #SD_RUNNABLE, #SD_RUNNING, #SD_DONE or #SD_FAILED
198 * \see e_SD_task_state_t
200 e_SD_task_state_t SD_task_get_state(SD_task_t task)
205 /* Changes the state of a task. Updates the swags and the flag sd_global->watch_point_reached.
207 void __SD_task_set_state(SD_task_t task, e_SD_task_state_t new_state)
209 xbt_swag_remove(task, task->state_set);
211 case SD_NOT_SCHEDULED:
212 task->state_set = sd_global->not_scheduled_task_set;
215 task->state_set = sd_global->schedulable_task_set;
218 task->state_set = sd_global->scheduled_task_set;
221 task->state_set = sd_global->runnable_task_set;
224 task->state_set = sd_global->in_fifo_task_set;
227 task->state_set = sd_global->running_task_set;
229 surf_workstation_model->action_get_start_time(task->surf_action);
232 task->state_set = sd_global->done_task_set;
234 surf_workstation_model->action_get_finish_time(task->surf_action);
237 jedule_log_sd_event(task);
241 task->state_set = sd_global->failed_task_set;
244 xbt_die( "Invalid state");
246 xbt_swag_insert(task, task->state_set);
247 task->state = new_state;
249 if (task->watch_points & new_state) {
250 XBT_VERB("Watch point reached with task '%s'!", SD_task_get_name(task));
251 sd_global->watch_point_reached = 1;
252 SD_task_unwatch(task, new_state); /* remove the watch point */
257 * \brief Returns the name of a task
260 * \return the name of this task (can be \c NULL)
262 const char *SD_task_get_name(SD_task_t task)
267 /** @brief Allows to change the name of a task */
268 void SD_task_set_name(SD_task_t task, const char *name)
270 xbt_free(task->name);
271 task->name = xbt_strdup(name);
274 /** @brief Returns the dynar of the parents of a task
277 * \return a newly allocated dynar comprising the parents of this task
280 xbt_dynar_t SD_task_get_parents(SD_task_t task)
286 parents = xbt_dynar_new(sizeof(SD_task_t), NULL);
287 xbt_dynar_foreach(task->tasks_before, i, dep) {
288 xbt_dynar_push(parents, &(dep->src));
293 /** @brief Returns the dynar of the parents of a task
296 * \return a newly allocated dynar comprising the parents of this task
298 xbt_dynar_t SD_task_get_children(SD_task_t task)
301 xbt_dynar_t children;
304 children = xbt_dynar_new(sizeof(SD_task_t), NULL);
305 xbt_dynar_foreach(task->tasks_after, i, dep) {
306 xbt_dynar_push(children, &(dep->dst));
312 * \brief Returns the amount of workstations involved in a task
314 * Only call this on already scheduled tasks!
317 int SD_task_get_workstation_count(SD_task_t task)
319 return task->workstation_nb;
323 * \brief Returns the list of workstations involved in a task
325 * Only call this on already scheduled tasks!
328 SD_workstation_t *SD_task_get_workstation_list(SD_task_t task)
330 return task->workstation_list;
334 * \brief Returns the total amount of work contained in a task
337 * \return the total amount of work (computation or data transfer) for this task
338 * \see SD_task_get_remaining_amount()
340 double SD_task_get_amount(SD_task_t task)
346 * \brief Returns the remaining amount work to do till the completion of a task
349 * \return the remaining amount of work (computation or data transfer) of this task
350 * \see SD_task_get_amount()
352 double SD_task_get_remaining_amount(SD_task_t task)
354 if (task->surf_action)
355 return surf_workstation_model->get_remains(task->surf_action);
357 return task->remains;
360 int SD_task_get_kind(SD_task_t task)
365 /** @brief Displays debugging informations about a task */
366 void SD_task_dump(SD_task_t task)
368 unsigned int counter;
369 SD_dependency_t dependency;
372 XBT_INFO("Displaying task %s", SD_task_get_name(task));
373 statename = bprintf("%s %s %s %s %s %s %s %s",
374 (task->state & SD_NOT_SCHEDULED ? "not scheduled" :
376 (task->state & SD_SCHEDULABLE ? "schedulable" : ""),
377 (task->state & SD_SCHEDULED ? "scheduled" : ""),
378 (task->state & SD_RUNNABLE ? "runnable" :
380 (task->state & SD_IN_FIFO ? "in fifo" : ""),
381 (task->state & SD_RUNNING ? "running" : ""),
382 (task->state & SD_DONE ? "done" : ""),
383 (task->state & SD_FAILED ? "failed" : ""));
384 XBT_INFO(" - state: %s", statename);
387 if (task->kind != 0) {
388 switch (task->kind) {
389 case SD_TASK_COMM_E2E:
390 XBT_INFO(" - kind: end-to-end communication");
392 case SD_TASK_COMP_SEQ:
393 XBT_INFO(" - kind: sequential computation");
396 XBT_INFO(" - (unknown kind %d)", task->kind);
399 XBT_INFO(" - amount: %.0f", SD_task_get_amount(task));
400 XBT_INFO(" - Dependencies to satisfy: %d", task->unsatisfied_dependencies);
401 if (xbt_dynar_length(task->tasks_before)) {
402 XBT_INFO(" - pre-dependencies:");
403 xbt_dynar_foreach(task->tasks_before, counter, dependency) {
404 XBT_INFO(" %s", SD_task_get_name(dependency->src));
407 if (xbt_dynar_length(task->tasks_after)) {
408 XBT_INFO(" - post-dependencies:");
409 xbt_dynar_foreach(task->tasks_after, counter, dependency) {
410 XBT_INFO(" %s", SD_task_get_name(dependency->dst));
415 /** @brief Dumps the task in dotty formalism into the FILE* passed as second argument */
416 void SD_task_dotty(SD_task_t task, void *out)
418 unsigned int counter;
419 SD_dependency_t dependency;
420 fprintf(out, " T%p [label=\"%.20s\"", task, task->name);
421 switch (task->kind) {
422 case SD_TASK_COMM_E2E:
423 fprintf(out, ", shape=box");
425 case SD_TASK_COMP_SEQ:
426 fprintf(out, ", shape=circle");
429 xbt_die("Unknown task type!");
431 fprintf(out, "];\n");
432 xbt_dynar_foreach(task->tasks_before, counter, dependency) {
433 fprintf(out, " T%p -> T%p;\n", dependency->src, dependency->dst);
437 /* Destroys a dependency between two tasks.
439 static void __SD_task_dependency_destroy(void *dependency)
441 if (((SD_dependency_t) dependency)->name != NULL)
442 xbt_free(((SD_dependency_t) dependency)->name);
443 xbt_free(dependency);
447 * \brief Adds a dependency between two tasks
449 * \a dst will depend on \a src, ie \a dst will not start before \a src is finished.
450 * Their \ref e_SD_task_state_t "state" must be #SD_NOT_SCHEDULED, #SD_SCHEDULED or #SD_RUNNABLE.
452 * \param name the name of the new dependency (can be \c NULL)
453 * \param data the user data you want to associate with this dependency (can be \c NULL)
454 * \param src the task which must be executed first
455 * \param dst the task you want to make depend on \a src
456 * \see SD_task_dependency_remove()
458 void SD_task_dependency_add(const char *name, void *data, SD_task_t src,
465 SD_dependency_t dependency;
467 dynar = src->tasks_after;
468 length = xbt_dynar_length(dynar);
472 "Cannot add a dependency between task '%s' and itself",
473 SD_task_get_name(src));
475 if (!__SD_task_is_not_scheduled(src) && !__SD_task_is_schedulable(src)
476 && !__SD_task_is_scheduled_or_runnable(src))
478 "Task '%s' must be SD_NOT_SCHEDULED, SD_SCHEDULABLE, SD_SCHEDULED or SD_RUNNABLE",
479 SD_task_get_name(src));
481 if (!__SD_task_is_not_scheduled(dst) && !__SD_task_is_schedulable(dst)
482 && !__SD_task_is_scheduled_or_runnable(dst))
484 "Task '%s' must be SD_NOT_SCHEDULED, SD_SCHEDULABLE, SD_SCHEDULED or SD_RUNNABLE",
485 SD_task_get_name(dst));
487 XBT_DEBUG("SD_task_dependency_add: src = %s, dst = %s",
488 SD_task_get_name(src), SD_task_get_name(dst));
489 for (i = 0; i < length && !found; i++) {
490 xbt_dynar_get_cpy(dynar, i, &dependency);
491 found = (dependency->dst == dst);
492 XBT_DEBUG("Dependency %d: dependency->dst = %s", i,
493 SD_task_get_name(dependency->dst));
498 "A dependency already exists between task '%s' and task '%s'",
499 SD_task_get_name(src), SD_task_get_name(dst));
501 dependency = xbt_new(s_SD_dependency_t, 1);
503 dependency->name = xbt_strdup(name); /* xbt_strdup is cleaver enough to deal with NULL args itself */
504 dependency->data = data;
505 dependency->src = src;
506 dependency->dst = dst;
508 /* src must be executed before dst */
509 xbt_dynar_push(src->tasks_after, &dependency);
510 xbt_dynar_push(dst->tasks_before, &dependency);
512 dst->unsatisfied_dependencies++;
515 /* if the task was runnable, then dst->tasks_before is not empty anymore,
516 so we must go back to state SD_SCHEDULED */
517 if (__SD_task_is_runnable(dst)) {
519 ("SD_task_dependency_add: %s was runnable and becomes scheduled!",
520 SD_task_get_name(dst));
521 __SD_task_set_state(dst, SD_SCHEDULED);
524 /* __SD_print_dependencies(src);
525 __SD_print_dependencies(dst); */
529 * \brief Indacates whether there is a dependency between two tasks.
532 * \param dst a task depending on \a src
534 * If src is NULL, checks whether dst has any pre-dependency.
535 * If dst is NULL, checks whether src has any post-dependency.
537 int SD_task_dependency_exists(SD_task_t src, SD_task_t dst)
539 unsigned int counter;
540 SD_dependency_t dependency;
542 xbt_assert(src != NULL
544 "Invalid parameter: both src and dst are NULL");
548 xbt_dynar_foreach(src->tasks_after, counter, dependency) {
549 if (dependency->dst == dst)
553 return xbt_dynar_length(src->tasks_after);
556 return xbt_dynar_length(dst->tasks_before);
562 * \brief Remove a dependency between two tasks
565 * \param dst a task depending on \a src
566 * \see SD_task_dependency_add()
568 void SD_task_dependency_remove(SD_task_t src, SD_task_t dst)
575 SD_dependency_t dependency;
577 /* remove the dependency from src->tasks_after */
578 dynar = src->tasks_after;
579 length = xbt_dynar_length(dynar);
581 for (i = 0; i < length && !found; i++) {
582 xbt_dynar_get_cpy(dynar, i, &dependency);
583 if (dependency->dst == dst) {
584 xbt_dynar_remove_at(dynar, i, NULL);
590 "No dependency found between task '%s' and '%s': task '%s' is not a successor of task '%s'",
591 SD_task_get_name(src), SD_task_get_name(dst),
592 SD_task_get_name(dst), SD_task_get_name(src));
594 /* remove the dependency from dst->tasks_before */
595 dynar = dst->tasks_before;
596 length = xbt_dynar_length(dynar);
599 for (i = 0; i < length && !found; i++) {
600 xbt_dynar_get_cpy(dynar, i, &dependency);
601 if (dependency->src == src) {
602 xbt_dynar_remove_at(dynar, i, NULL);
603 __SD_task_dependency_destroy(dependency);
604 dst->unsatisfied_dependencies--;
609 /* should never happen... */
611 "SimDag error: task '%s' is a successor of '%s' but task '%s' is not a predecessor of task '%s'",
612 SD_task_get_name(dst), SD_task_get_name(src),
613 SD_task_get_name(src), SD_task_get_name(dst));
615 /* if the task was scheduled and dst->tasks_before is empty now, we can make it runnable */
617 if (dst->unsatisfied_dependencies == 0) {
618 if (__SD_task_is_scheduled(dst))
619 __SD_task_set_state(dst, SD_RUNNABLE);
621 __SD_task_set_state(dst, SD_SCHEDULABLE);
624 if (dst->is_not_ready == 0)
625 __SD_task_set_state(dst, SD_SCHEDULABLE);
627 /* __SD_print_dependencies(src);
628 __SD_print_dependencies(dst); */
632 * \brief Returns the user data associated with a dependency between two tasks
635 * \param dst a task depending on \a src
636 * \return the user data associated with this dependency (can be \c NULL)
637 * \see SD_task_dependency_add()
639 void *SD_task_dependency_get_data(SD_task_t src, SD_task_t dst)
646 SD_dependency_t dependency;
648 dynar = src->tasks_after;
649 length = xbt_dynar_length(dynar);
651 for (i = 0; i < length && !found; i++) {
652 xbt_dynar_get_cpy(dynar, i, &dependency);
653 found = (dependency->dst == dst);
656 THROWF(arg_error, 0, "No dependency found between task '%s' and '%s'",
657 SD_task_get_name(src), SD_task_get_name(dst));
658 return dependency->data;
661 /* temporary function for debugging */
662 static void __SD_print_watch_points(SD_task_t task)
664 static const int state_masks[] =
665 { SD_SCHEDULABLE, SD_SCHEDULED, SD_RUNNING, SD_RUNNABLE, SD_DONE,
668 static const char *state_names[] =
669 { "schedulable", "scheduled", "running", "runnable", "done",
674 XBT_INFO("Task '%s' watch points (%x): ", SD_task_get_name(task),
678 for (i = 0; i < 5; i++) {
679 if (task->watch_points & state_masks[i])
680 XBT_INFO("%s ", state_names[i]);
685 * \brief Adds a watch point to a task
687 * SD_simulate() will stop as soon as the \ref e_SD_task_state_t "state" of this
688 * task becomes the one given in argument. The
689 * watch point is then automatically removed.
692 * \param state the \ref e_SD_task_state_t "state" you want to watch
693 * (cannot be #SD_NOT_SCHEDULED)
694 * \see SD_task_unwatch()
696 void SD_task_watch(SD_task_t task, e_SD_task_state_t state)
698 if (state & SD_NOT_SCHEDULED)
700 "Cannot add a watch point for state SD_NOT_SCHEDULED");
702 task->watch_points = task->watch_points | state;
703 /* __SD_print_watch_points(task); */
707 * \brief Removes a watch point from a task
710 * \param state the \ref e_SD_task_state_t "state" you no longer want to watch
711 * \see SD_task_watch()
713 void SD_task_unwatch(SD_task_t task, e_SD_task_state_t state)
715 xbt_assert(state != SD_NOT_SCHEDULED,
716 "SimDag error: Cannot have a watch point for state SD_NOT_SCHEDULED");
718 task->watch_points = task->watch_points & ~state;
719 /* __SD_print_watch_points(task); */
723 * \brief Returns an approximative estimation of the execution time of a task.
725 * The estimation is very approximative because the value returned is the time
726 * the task would take if it was executed now and if it was the only task.
728 * \param task the task to evaluate
729 * \param workstation_nb number of workstations on which the task would be executed
730 * \param workstation_list the workstations on which the task would be executed
731 * \param computation_amount computation amount for each workstation
732 * \param communication_amount communication amount between each pair of workstations
735 double SD_task_get_execution_time(SD_task_t task,
737 const SD_workstation_t *
739 const double *computation_amount,
740 const double *communication_amount)
742 double time, max_time = 0.0;
744 xbt_assert(workstation_nb > 0, "Invalid parameter");
746 /* the task execution time is the maximum execution time of the parallel tasks */
748 for (i = 0; i < workstation_nb; i++) {
750 if (computation_amount != NULL)
752 SD_workstation_get_computation_time(workstation_list[i],
753 computation_amount[i]);
755 if (communication_amount != NULL)
756 for (j = 0; j < workstation_nb; j++) {
758 SD_route_get_communication_time(workstation_list[i],
760 communication_amount[i *
765 if (time > max_time) {
772 static XBT_INLINE void SD_task_do_schedule(SD_task_t task)
774 if (!__SD_task_is_not_scheduled(task) && !__SD_task_is_schedulable(task))
775 THROWF(arg_error, 0, "Task '%s' has already been scheduled",
776 SD_task_get_name(task));
778 /* update the task state */
779 if (task->unsatisfied_dependencies == 0)
780 __SD_task_set_state(task, SD_RUNNABLE);
782 __SD_task_set_state(task, SD_SCHEDULED);
786 * \brief Schedules a task
788 * The task state must be #SD_NOT_SCHEDULED.
789 * Once scheduled, a task will be executed as soon as possible in SD_simulate(),
790 * i.e. when its dependencies are satisfied.
792 * \param task the task you want to schedule
793 * \param workstation_count number of workstations on which the task will be executed
794 * \param workstation_list the workstations on which the task will be executed
795 * \param computation_amount computation amount for each workstation
796 * \param communication_amount communication amount between each pair of workstations
797 * \param rate task execution speed rate
798 * \see SD_task_unschedule()
800 void SD_task_schedule(SD_task_t task, int workstation_count,
801 const SD_workstation_t * workstation_list,
802 const double *computation_amount,
803 const double *communication_amount, double rate)
805 int communication_nb;
806 task->workstation_nb = 0;
808 xbt_assert(workstation_count > 0, "workstation_nb must be positive");
810 task->workstation_nb = workstation_count;
813 if (computation_amount) {
814 task->computation_amount = xbt_realloc(task->computation_amount,
815 sizeof(double) * workstation_count);
816 memcpy(task->computation_amount, computation_amount,
817 sizeof(double) * workstation_count);
819 xbt_free(task->computation_amount);
820 task->computation_amount = NULL;
823 communication_nb = workstation_count * workstation_count;
824 if (communication_amount) {
825 task->communication_amount = xbt_realloc(task->communication_amount,
826 sizeof(double) * communication_nb);
827 memcpy(task->communication_amount, communication_amount,
828 sizeof(double) * communication_nb);
830 xbt_free(task->communication_amount);
831 task->communication_amount = NULL;
834 task->workstation_list =
835 xbt_realloc(task->workstation_list,
836 sizeof(SD_workstation_t) * workstation_count);
837 memcpy(task->workstation_list, workstation_list,
838 sizeof(SD_workstation_t) * workstation_count);
840 SD_task_do_schedule(task);
844 * \brief Unschedules a task
846 * The task state must be #SD_SCHEDULED, #SD_RUNNABLE, #SD_RUNNING or #SD_FAILED.
847 * If you call this function, the task state becomes #SD_NOT_SCHEDULED.
848 * Call SD_task_schedule() to schedule it again.
850 * \param task the task you want to unschedule
851 * \see SD_task_schedule()
853 void SD_task_unschedule(SD_task_t task)
855 if (task->state_set != sd_global->scheduled_task_set &&
856 task->state_set != sd_global->runnable_task_set &&
857 task->state_set != sd_global->running_task_set &&
858 task->state_set != sd_global->failed_task_set)
860 "Task %s: the state must be SD_SCHEDULED, SD_RUNNABLE, SD_RUNNING or SD_FAILED",
861 SD_task_get_name(task));
863 if (__SD_task_is_scheduled_or_runnable(task) /* if the task is scheduled or runnable */
864 &&task->kind == SD_TASK_NOT_TYPED) /* Don't free scheduling data for typed tasks */
865 __SD_task_destroy_scheduling_data(task);
867 if (__SD_task_is_running(task)) /* the task should become SD_FAILED */
868 surf_workstation_model->action_cancel(task->surf_action);
870 if (task->unsatisfied_dependencies == 0)
871 __SD_task_set_state(task, SD_SCHEDULABLE);
873 __SD_task_set_state(task, SD_NOT_SCHEDULED);
875 task->remains = task->amount;
876 task->start_time = -1.0;
879 /* Destroys the data memorized by SD_task_schedule. Task state must be SD_SCHEDULED or SD_RUNNABLE.
881 static void __SD_task_destroy_scheduling_data(SD_task_t task)
883 if (!__SD_task_is_scheduled_or_runnable(task)
884 && !__SD_task_is_in_fifo(task))
886 "Task '%s' must be SD_SCHEDULED, SD_RUNNABLE or SD_IN_FIFO",
887 SD_task_get_name(task));
889 xbt_free(task->computation_amount);
890 xbt_free(task->communication_amount);
891 task->computation_amount = task->communication_amount = NULL;
894 /* Runs a task. This function is directly called by __SD_task_try_to_run if the task
895 * doesn't have to wait in fifos. Otherwise, it is called by __SD_task_just_done when
896 * the task gets out of its fifos.
898 void __SD_task_really_run(SD_task_t task)
902 void **surf_workstations;
904 xbt_assert(__SD_task_is_runnable_or_in_fifo(task),
905 "Task '%s' is not runnable or in a fifo! Task state: %d",
906 SD_task_get_name(task), SD_task_get_state(task));
907 xbt_assert(task->workstation_list != NULL,
908 "Task '%s': workstation_list is NULL!",
909 SD_task_get_name(task));
913 XBT_DEBUG("Really running task '%s'", SD_task_get_name(task));
915 /* set this task as current task for the workstations in sequential mode */
916 for (i = 0; i < task->workstation_nb; i++) {
917 if (SD_workstation_get_access_mode(task->workstation_list[i]) ==
918 SD_WORKSTATION_SEQUENTIAL_ACCESS) {
919 task->workstation_list[i]->current_task = task;
920 xbt_assert(__SD_workstation_is_busy(task->workstation_list[i]),
921 "The workstation should be busy now");
925 XBT_DEBUG("Task '%s' set as current task for its workstations",
926 SD_task_get_name(task));
930 /* we have to create a Surf workstation array instead of the SimDag workstation array */
931 surf_workstations = xbt_new(void *, task->workstation_nb);
933 for (i = 0; i < task->workstation_nb; i++)
934 surf_workstations[i] = task->workstation_list[i]->surf_workstation;
936 /* It's allowed to pass a NULL vector as cost to mean vector of 0.0 (easing user's life). Let's deal with it */
937 #define cost_or_zero(array,pos) ((array)?(array)[pos]:0.0)
939 task->surf_action = NULL;
940 if ((task->workstation_nb == 1)
941 && (cost_or_zero(task->communication_amount, 0) == 0.0)) {
943 surf_workstation_model->extension.
944 workstation.execute(surf_workstations[0],
945 cost_or_zero(task->computation_amount, 0));
946 } else if ((task->workstation_nb == 1)
947 && (cost_or_zero(task->computation_amount, 0) == 0.0)) {
950 surf_workstation_model->extension.
951 workstation.communicate(surf_workstations[0], surf_workstations[0],
952 cost_or_zero(task->communication_amount,
954 } else if ((task->workstation_nb == 2)
955 && (cost_or_zero(task->computation_amount, 0) == 0.0)
956 && (cost_or_zero(task->computation_amount, 1) == 0.0)) {
960 for (i = 0; i < task->workstation_nb * task->workstation_nb; i++) {
961 if (cost_or_zero(task->communication_amount, i) > 0.0) {
963 value = cost_or_zero(task->communication_amount, i);
968 surf_workstation_model->extension.
969 workstation.communicate(surf_workstations[0],
970 surf_workstations[1], value, task->rate);
975 if (!task->surf_action) {
976 double *computation_amount = xbt_new(double, task->workstation_nb);
977 double *communication_amount = xbt_new(double, task->workstation_nb *
978 task->workstation_nb);
980 memcpy(computation_amount, task->computation_amount, sizeof(double) *
981 task->workstation_nb);
982 memcpy(communication_amount, task->communication_amount,
983 sizeof(double) * task->workstation_nb * task->workstation_nb);
986 surf_workstation_model->extension.
987 workstation.execute_parallel_task(task->workstation_nb,
990 communication_amount,
991 task->amount, task->rate);
993 xbt_free(surf_workstations);
996 surf_workstation_model->action_data_set(task->surf_action, task);
998 XBT_DEBUG("surf_action = %p", task->surf_action);
1002 TRACE_surf_action(task->surf_action, task->category);
1005 __SD_task_destroy_scheduling_data(task); /* now the scheduling data are not useful anymore */
1006 __SD_task_set_state(task, SD_RUNNING);
1007 xbt_assert(__SD_task_is_running(task), "Bad state of task '%s': %d",
1008 SD_task_get_name(task), SD_task_get_state(task));
1012 /* Tries to run a task. This function is called by SD_simulate() when a scheduled task becomes SD_RUNNABLE
1013 * (ie when its dependencies are satisfied).
1014 * If one of the workstations where the task is scheduled on is busy (in sequential mode),
1015 * the task doesn't start.
1016 * Returns whether the task has started.
1018 int __SD_task_try_to_run(SD_task_t task)
1023 SD_workstation_t workstation;
1025 xbt_assert(__SD_task_is_runnable(task),
1026 "Task '%s' is not runnable! Task state: %d",
1027 SD_task_get_name(task), SD_task_get_state(task));
1030 for (i = 0; i < task->workstation_nb; i++) {
1031 can_start = can_start &&
1032 !__SD_workstation_is_busy(task->workstation_list[i]);
1035 XBT_DEBUG("Task '%s' can start: %d", SD_task_get_name(task), can_start);
1037 if (!can_start) { /* if the task cannot start and is not in the fifos yet */
1038 for (i = 0; i < task->workstation_nb; i++) {
1039 workstation = task->workstation_list[i];
1040 if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
1041 XBT_DEBUG("Pushing task '%s' in the fifo of workstation '%s'",
1042 SD_task_get_name(task),
1043 SD_workstation_get_name(workstation));
1044 xbt_fifo_push(workstation->task_fifo, task);
1047 __SD_task_set_state(task, SD_IN_FIFO);
1048 xbt_assert(__SD_task_is_in_fifo(task), "Bad state of task '%s': %d",
1049 SD_task_get_name(task), SD_task_get_state(task));
1050 XBT_DEBUG("Task '%s' state is now SD_IN_FIFO", SD_task_get_name(task));
1052 __SD_task_really_run(task);
1058 /* This function is called by SD_simulate when a task is done.
1059 * It updates task->state and task->action and executes if necessary the tasks
1060 * which were waiting in fifos for the end of `task'
1062 void __SD_task_just_done(SD_task_t task)
1065 SD_workstation_t workstation;
1067 SD_task_t candidate;
1068 int candidate_nb = 0;
1069 int candidate_capacity = 8;
1070 SD_task_t *candidates;
1073 xbt_assert(__SD_task_is_running(task),
1074 "The task must be running! Task state: %d",
1075 SD_task_get_state(task));
1076 xbt_assert(task->workstation_list != NULL,
1077 "Task '%s': workstation_list is NULL!",
1078 SD_task_get_name(task));
1081 candidates = xbt_new(SD_task_t, 8);
1083 __SD_task_set_state(task, SD_DONE);
1084 surf_workstation_model->action_unref(task->surf_action);
1085 task->surf_action = NULL;
1087 XBT_DEBUG("Looking for candidates");
1089 /* if the task was executed on sequential workstations,
1090 maybe we can execute the next task of the fifo for each workstation */
1091 for (i = 0; i < task->workstation_nb; i++) {
1092 workstation = task->workstation_list[i];
1093 XBT_DEBUG("Workstation '%s': access_mode = %d",
1094 SD_workstation_get_name(workstation), workstation->access_mode);
1095 if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
1096 xbt_assert(workstation->task_fifo != NULL,
1097 "Workstation '%s' has sequential access but no fifo!",
1098 SD_workstation_get_name(workstation));
1099 xbt_assert(workstation->current_task =
1100 task, "Workstation '%s': current task should be '%s'",
1101 SD_workstation_get_name(workstation),
1102 SD_task_get_name(task));
1104 /* the task is over so we can release the workstation */
1105 workstation->current_task = NULL;
1107 XBT_DEBUG("Getting candidate in fifo");
1109 xbt_fifo_get_item_content(xbt_fifo_get_first_item
1110 (workstation->task_fifo));
1112 if (candidate != NULL) {
1113 XBT_DEBUG("Candidate: '%s'", SD_task_get_name(candidate));
1114 xbt_assert(__SD_task_is_in_fifo(candidate),
1115 "Bad state of candidate '%s': %d",
1116 SD_task_get_name(candidate),
1117 SD_task_get_state(candidate));
1120 XBT_DEBUG("Candidate in fifo: %p", candidate);
1122 /* if there was a task waiting for my place */
1123 if (candidate != NULL) {
1124 /* Unfortunately, we are not sure yet that we can execute the task now,
1125 because the task can be waiting more deeply in some other workstation's fifos...
1126 So we memorize all candidate tasks, and then we will check for each candidate
1127 whether or not all its workstations are available. */
1129 /* realloc if necessary */
1130 if (candidate_nb == candidate_capacity) {
1131 candidate_capacity *= 2;
1133 xbt_realloc(candidates,
1134 sizeof(SD_task_t) * candidate_capacity);
1137 /* register the candidate */
1138 candidates[candidate_nb++] = candidate;
1139 candidate->fifo_checked = 0;
1144 XBT_DEBUG("Candidates found: %d", candidate_nb);
1146 /* now we check every candidate task */
1147 for (i = 0; i < candidate_nb; i++) {
1148 candidate = candidates[i];
1150 if (candidate->fifo_checked) {
1151 continue; /* we have already evaluated that task */
1154 xbt_assert(__SD_task_is_in_fifo(candidate),
1155 "Bad state of candidate '%s': %d",
1156 SD_task_get_name(candidate), SD_task_get_state(candidate));
1158 for (j = 0; j < candidate->workstation_nb && can_start; j++) {
1159 workstation = candidate->workstation_list[j];
1161 /* I can start on this workstation if the workstation is shared
1162 or if I am the first task in the fifo */
1163 can_start = workstation->access_mode == SD_WORKSTATION_SHARED_ACCESS
1165 xbt_fifo_get_item_content(xbt_fifo_get_first_item
1166 (workstation->task_fifo));
1169 XBT_DEBUG("Candidate '%s' can start: %d", SD_task_get_name(candidate),
1172 /* now we are sure that I can start! */
1174 for (j = 0; j < candidate->workstation_nb && can_start; j++) {
1175 workstation = candidate->workstation_list[j];
1177 /* update the fifo */
1178 if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
1179 candidate = xbt_fifo_shift(workstation->task_fifo); /* the return value is stored just for debugging */
1180 XBT_DEBUG("Head of the fifo: '%s'",
1182 NULL) ? SD_task_get_name(candidate) : "NULL");
1183 xbt_assert(candidate == candidates[i],
1184 "Error in __SD_task_just_done: bad first task in the fifo");
1186 } /* for each workstation */
1188 /* finally execute the task */
1189 XBT_DEBUG("Task '%s' state: %d", SD_task_get_name(candidate),
1190 SD_task_get_state(candidate));
1191 __SD_task_really_run(candidate);
1194 ("Calling __SD_task_is_running: task '%s', state set: %p, running_task_set: %p, is running: %d",
1195 SD_task_get_name(candidate), candidate->state_set,
1196 sd_global->running_task_set, __SD_task_is_running(candidate));
1197 xbt_assert(__SD_task_is_running(candidate),
1198 "Bad state of task '%s': %d",
1199 SD_task_get_name(candidate),
1200 SD_task_get_state(candidate));
1201 XBT_DEBUG("Okay, the task is running.");
1204 candidate->fifo_checked = 1;
1205 } /* for each candidate */
1207 xbt_free(candidates);
1210 /* Remove all dependencies associated with a task. This function is called when the task is destroyed.
1212 static void __SD_task_remove_dependencies(SD_task_t task)
1214 /* we must destroy the dependencies carefuly (with SD_dependency_remove)
1215 because each one is stored twice */
1216 SD_dependency_t dependency;
1217 while (xbt_dynar_length(task->tasks_before) > 0) {
1218 xbt_dynar_get_cpy(task->tasks_before, 0, &dependency);
1219 SD_task_dependency_remove(dependency->src, dependency->dst);
1222 while (xbt_dynar_length(task->tasks_after) > 0) {
1223 xbt_dynar_get_cpy(task->tasks_after, 0, &dependency);
1224 SD_task_dependency_remove(dependency->src, dependency->dst);
1229 * \brief Returns the start time of a task
1231 * The task state must be SD_RUNNING, SD_DONE or SD_FAILED.
1233 * \param task: a task
1234 * \return the start time of this task
1236 double SD_task_get_start_time(SD_task_t task)
1238 if (task->surf_action)
1239 return surf_workstation_model->
1240 action_get_start_time(task->surf_action);
1242 return task->start_time;
1246 * \brief Returns the finish time of a task
1248 * The task state must be SD_RUNNING, SD_DONE or SD_FAILED.
1249 * If the state is not completed yet, the returned value is an
1250 * estimation of the task finish time. This value can fluctuate
1251 * until the task is completed.
1253 * \param task: a task
1254 * \return the start time of this task
1256 double SD_task_get_finish_time(SD_task_t task)
1258 if (task->surf_action) /* should never happen as actions are destroyed right after their completion */
1259 return surf_workstation_model->
1260 action_get_finish_time(task->surf_action);
1262 return task->finish_time;
1265 static XBT_INLINE SD_task_t SD_task_create_sized(const char *name,
1266 void *data, double amount,
1269 SD_task_t task = SD_task_create(name, data, amount);
1270 task->communication_amount = xbt_new0(double, ws_count * ws_count);
1271 task->computation_amount = xbt_new0(double, ws_count);
1272 task->workstation_nb = ws_count;
1273 task->workstation_list = xbt_new0(SD_workstation_t, ws_count);
1277 /** @brief create a end-to-end communication task that can then be auto-scheduled
1279 * Auto-scheduling mean that the task can be used with SD_task_schedulev(). This
1280 * allows to specify the task costs at creation, and decorelate them from the
1281 * scheduling process where you just specify which resource should deliver the
1284 * A end-to-end communication must be scheduled on 2 hosts, and the amount
1285 * specified at creation is sent from hosts[0] to hosts[1].
1287 SD_task_t SD_task_create_comm_e2e(const char *name, void *data,
1290 SD_task_t res = SD_task_create_sized(name, data, amount, 2);
1291 res->communication_amount[2] = amount;
1292 res->kind = SD_TASK_COMM_E2E;
1296 /** @brief create a sequential computation task that can then be auto-scheduled
1298 * Auto-scheduling mean that the task can be used with SD_task_schedulev(). This
1299 * allows to specify the task costs at creation, and decorelate them from the
1300 * scheduling process where you just specify which resource should deliver the
1303 * A sequential computation must be scheduled on 1 host, and the amount
1304 * specified at creation to be run on hosts[0].
1306 SD_task_t SD_task_create_comp_seq(const char *name, void *data,
1309 SD_task_t res = SD_task_create_sized(name, data, amount, 1);
1310 res->computation_amount[0] = amount;
1311 res->kind = SD_TASK_COMP_SEQ;
1315 /** @brief Auto-schedules a task.
1317 * Auto-scheduling mean that the task can be used with SD_task_schedulev(). This
1318 * allows to specify the task costs at creation, and decorelate them from the
1319 * scheduling process where you just specify which resource should deliver the
1322 * To be auto-schedulable, a task must be created with SD_task_create_comm_e2e() or
1323 * SD_task_create_comp_seq(). Check their definitions for the exact semantic of each
1327 * We should create tasks kind for the following categories:
1328 * - Point to point communication (done)
1329 * - Sequential computation (done)
1330 * - group communication (redistribution, several kinds)
1331 * - parallel tasks with no internal communication (one kind per speedup model such as amdal)
1332 * - idem+ internal communication. Task type not enough since we cannot store comm cost alongside to comp one)
1334 void SD_task_schedulev(SD_task_t task, int count,
1335 const SD_workstation_t * list)
1338 SD_dependency_t dep;
1340 xbt_assert(task->kind != 0,
1341 "Task %s is not typed. Cannot automatically schedule it.",
1342 SD_task_get_name(task));
1343 switch (task->kind) {
1344 case SD_TASK_COMM_E2E:
1345 case SD_TASK_COMP_SEQ:
1346 xbt_assert(task->workstation_nb == count,"Got %d locations, but were expecting %d locations",count,task->workstation_nb);
1347 for (i = 0; i < count; i++)
1348 task->workstation_list[i] = list[i];
1349 SD_task_do_schedule(task);
1352 xbt_die("Kind of task %s not supported by SD_task_schedulev()",
1353 SD_task_get_name(task));
1355 if (task->kind == SD_TASK_COMM_E2E) {
1356 XBT_VERB("Schedule comm task %s between %s -> %s. It costs %.f bytes",
1357 SD_task_get_name(task),
1358 SD_workstation_get_name(task->workstation_list[0]),
1359 SD_workstation_get_name(task->workstation_list[1]),
1360 task->communication_amount[2]);
1363 /* Iterate over all childs and parent being COMM_E2E to say where I am located (and start them if runnable) */
1364 if (task->kind == SD_TASK_COMP_SEQ) {
1365 XBT_VERB("Schedule computation task %s on %s. It costs %.f flops",
1366 SD_task_get_name(task),
1367 SD_workstation_get_name(task->workstation_list[0]),
1368 task->computation_amount[0]);
1370 xbt_dynar_foreach(task->tasks_before, cpt, dep) {
1371 SD_task_t before = dep->src;
1372 if (before->kind == SD_TASK_COMM_E2E) {
1373 before->workstation_list[1] = task->workstation_list[0];
1375 if (before->workstation_list[0] &&
1376 (__SD_task_is_schedulable(before)
1377 || __SD_task_is_not_scheduled(before))) {
1378 SD_task_do_schedule(before);
1380 ("Auto-Schedule comm task %s between %s -> %s. It costs %.f bytes",
1381 SD_task_get_name(before),
1382 SD_workstation_get_name(before->workstation_list[0]),
1383 SD_workstation_get_name(before->workstation_list[1]),
1384 before->communication_amount[2]);
1388 xbt_dynar_foreach(task->tasks_after, cpt, dep) {
1389 SD_task_t after = dep->dst;
1390 if (after->kind == SD_TASK_COMM_E2E) {
1391 after->workstation_list[0] = task->workstation_list[0];
1392 //J-N : Why did you comment on these line (this comment add a bug I think)?
1393 if (after->workstation_list[1]
1394 && (__SD_task_is_not_scheduled(after)
1395 || __SD_task_is_schedulable(after))) {
1396 SD_task_do_schedule(after);
1398 ("Auto-Schedule comm task %s between %s -> %s. It costs %.f bytes",
1399 SD_task_get_name(after),
1400 SD_workstation_get_name(after->workstation_list[0]),
1401 SD_workstation_get_name(after->workstation_list[1]),
1402 after->communication_amount[2]);
1410 /** @brief autoschedule a task on a list of workstations
1412 * This function is very similar to SD_task_schedulev(),
1413 * but takes the list of workstations to schedule onto as separate parameters.
1414 * It builds a proper vector of workstations and then call SD_task_schedulev()
1416 void SD_task_schedulel(SD_task_t task, int count, ...)
1419 SD_workstation_t *list = xbt_new(SD_workstation_t, count);
1421 va_start(ap, count);
1422 for (i = 0; i < count; i++) {
1423 list[i] = va_arg(ap, SD_workstation_t);
1426 SD_task_schedulev(task, count, list);