1 /* Copyright (c) 2006, 2007, 2008, 2009, 2010. 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);
20 * \brief Creates a new task.
22 * \param name the name of the task (can be \c NULL)
23 * \param data the user data you want to associate with the task (can be \c NULL)
24 * \param amount amount of the task
25 * \return the new task
26 * \see SD_task_destroy()
28 SD_task_t SD_task_create(const char *name, void *data, double amount)
34 task = xbt_new(s_SD_task_t, 1);
36 /* general information */
37 task->data = data; /* user data */
38 task->name = xbt_strdup(name);
39 task->kind = SD_TASK_NOT_TYPED;
40 task->state_hookup.prev = NULL;
41 task->state_hookup.next = NULL;
42 task->state_set = sd_global->not_scheduled_task_set;
43 task->state = SD_NOT_SCHEDULED;
44 task->return_hookup.prev = NULL;
45 task->return_hookup.next = NULL;
48 xbt_swag_insert(task, task->state_set);
50 task->amount = amount;
51 task->remains = amount;
52 task->start_time = -1.0;
53 task->finish_time = -1.0;
54 task->surf_action = NULL;
55 task->watch_points = 0;
58 task->tasks_before = xbt_dynar_new(sizeof(SD_dependency_t), NULL);
59 task->tasks_after = xbt_dynar_new(sizeof(SD_dependency_t), NULL);
60 task->unsatisfied_dependencies = 0;
61 task->is_not_ready = 0;
63 /* scheduling parameters */
64 task->workstation_nb = 0;
65 task->workstation_list = NULL;
66 task->computation_amount = NULL;
67 task->communication_amount = NULL;
70 sd_global->task_number++;
73 TRACE_sd_task_create(task);
80 * \brief Returns the user data of a task
83 * \return the user data associated with this task (can be \c NULL)
84 * \see SD_task_set_data()
86 void *SD_task_get_data(SD_task_t task)
89 xbt_assert(task != NULL, "Invalid parameter");
94 * \brief Sets the user data of a task
96 * The new data can be \c NULL. The old data should have been freed first
97 * if it was not \c NULL.
100 * \param data the new data you want to associate with this task
101 * \see SD_task_get_data()
103 void SD_task_set_data(SD_task_t task, void *data)
105 SD_CHECK_INIT_DONE();
106 xbt_assert(task != NULL, "Invalid parameter");
111 * \brief Returns the state of a task
114 * \return the current \ref e_SD_task_state_t "state" of this task:
115 * #SD_NOT_SCHEDULED, #SD_SCHEDULED, #SD_RUNNABLE, #SD_RUNNING, #SD_DONE or #SD_FAILED
116 * \see e_SD_task_state_t
118 e_SD_task_state_t SD_task_get_state(SD_task_t task)
120 SD_CHECK_INIT_DONE();
121 xbt_assert(task != NULL, "Invalid parameter");
125 /* Changes the state of a task. Updates the swags and the flag sd_global->watch_point_reached.
127 void __SD_task_set_state(SD_task_t task, e_SD_task_state_t new_state)
129 xbt_swag_remove(task, task->state_set);
131 case SD_NOT_SCHEDULED:
132 task->state_set = sd_global->not_scheduled_task_set;
135 task->state_set = sd_global->schedulable_task_set;
138 task->state_set = sd_global->scheduled_task_set;
141 task->state_set = sd_global->runnable_task_set;
144 task->state_set = sd_global->in_fifo_task_set;
147 task->state_set = sd_global->running_task_set;
149 surf_workstation_model->action_get_start_time(task->surf_action);
152 task->state_set = sd_global->done_task_set;
154 surf_workstation_model->action_get_finish_time(task->surf_action);
157 jedule_log_sd_event(task);
161 task->state_set = sd_global->failed_task_set;
164 xbt_die( "Invalid state");
166 xbt_swag_insert(task, task->state_set);
167 task->state = new_state;
169 if (task->watch_points & new_state) {
170 XBT_VERB("Watch point reached with task '%s'!", SD_task_get_name(task));
171 sd_global->watch_point_reached = 1;
172 SD_task_unwatch(task, new_state); /* remove the watch point */
177 * \brief Returns the name of a task
180 * \return the name of this task (can be \c NULL)
182 const char *SD_task_get_name(SD_task_t task)
184 SD_CHECK_INIT_DONE();
185 xbt_assert(task != NULL, "Invalid parameter");
189 /** @brief Allows to change the name of a task */
190 void SD_task_set_name(SD_task_t task, const char *name)
192 xbt_free(task->name);
193 task->name = xbt_strdup(name);
196 /** @brief Returns the dynar of the parents of a task
199 * \return a newly allocated dynar comprising the parents of this task
202 xbt_dynar_t SD_task_get_parents(SD_task_t task)
207 SD_CHECK_INIT_DONE();
208 xbt_assert(task != NULL, "Invalid parameter");
210 parents = xbt_dynar_new(sizeof(SD_task_t), NULL);
211 xbt_dynar_foreach(task->tasks_before, i, dep) {
212 xbt_dynar_push(parents, &(dep->src));
217 /** @brief Returns the dynar of the parents of a task
220 * \return a newly allocated dynar comprising the parents of this task
222 xbt_dynar_t SD_task_get_children(SD_task_t task)
225 xbt_dynar_t children;
227 SD_CHECK_INIT_DONE();
228 xbt_assert(task != NULL, "Invalid parameter");
230 children = xbt_dynar_new(sizeof(SD_task_t), NULL);
231 xbt_dynar_foreach(task->tasks_after, i, dep) {
232 xbt_dynar_push(children, &(dep->dst));
238 * \brief Returns the amount of workstations involved in a task
240 * Only call this on already scheduled tasks!
243 int SD_task_get_workstation_count(SD_task_t task)
245 SD_CHECK_INIT_DONE();
246 xbt_assert(task != NULL, "Invalid parameter");
247 //xbt_assert(task->state_set != sd_global->scheduled_task_set,
248 // "Unscheduled task %s", task->name);
249 return task->workstation_nb;
253 * \brief Returns the list of workstations involved in a task
255 * Only call this on already scheduled tasks!
258 SD_workstation_t *SD_task_get_workstation_list(SD_task_t task)
260 SD_CHECK_INIT_DONE();
261 xbt_assert(task != NULL, "Invalid parameter");
262 //xbt_assert(task->state_set != sd_global->scheduled_task_set,
263 // "Unscheduled task %s", task->name);
264 return task->workstation_list;
268 * \brief Returns the total amount of work contained in a task
271 * \return the total amount of work (computation or data transfer) for this task
272 * \see SD_task_get_remaining_amount()
274 double SD_task_get_amount(SD_task_t task)
276 SD_CHECK_INIT_DONE();
277 xbt_assert(task != NULL, "Invalid parameter");
282 * \brief Returns the remaining amount work to do till the completion of a task
285 * \return the remaining amount of work (computation or data transfer) of this task
286 * \see SD_task_get_amount()
288 double SD_task_get_remaining_amount(SD_task_t task)
290 SD_CHECK_INIT_DONE();
291 xbt_assert(task != NULL, "Invalid parameter");
293 if (task->surf_action)
294 return surf_workstation_model->get_remains(task->surf_action);
296 return task->remains;
299 int SD_task_get_kind(SD_task_t task)
304 /** @brief Displays debugging informations about a task */
305 void SD_task_dump(SD_task_t task)
307 unsigned int counter;
308 SD_dependency_t dependency;
311 XBT_INFO("Displaying task %s", SD_task_get_name(task));
312 statename = bprintf("%s %s %s %s %s %s %s %s",
313 (task->state & SD_NOT_SCHEDULED ? "not scheduled" :
315 (task->state & SD_SCHEDULABLE ? "schedulable" : ""),
316 (task->state & SD_SCHEDULED ? "scheduled" : ""),
317 (task->state & SD_RUNNABLE ? "runnable" :
319 (task->state & SD_IN_FIFO ? "in fifo" : ""),
320 (task->state & SD_RUNNING ? "running" : ""),
321 (task->state & SD_DONE ? "done" : ""),
322 (task->state & SD_FAILED ? "failed" : ""));
323 XBT_INFO(" - state: %s", statename);
326 if (task->kind != 0) {
327 switch (task->kind) {
328 case SD_TASK_COMM_E2E:
329 XBT_INFO(" - kind: end-to-end communication");
331 case SD_TASK_COMP_SEQ:
332 XBT_INFO(" - kind: sequential computation");
335 XBT_INFO(" - (unknown kind %d)", task->kind);
338 XBT_INFO(" - amount: %.0f", SD_task_get_amount(task));
339 XBT_INFO(" - Dependencies to satisfy: %d", task->unsatisfied_dependencies);
340 if (xbt_dynar_length(task->tasks_before)) {
341 XBT_INFO(" - pre-dependencies:");
342 xbt_dynar_foreach(task->tasks_before, counter, dependency) {
343 XBT_INFO(" %s", SD_task_get_name(dependency->src));
346 if (xbt_dynar_length(task->tasks_after)) {
347 XBT_INFO(" - post-dependencies:");
348 xbt_dynar_foreach(task->tasks_after, counter, dependency) {
349 XBT_INFO(" %s", SD_task_get_name(dependency->dst));
354 /** @brief Dumps the task in dotty formalism into the FILE* passed as second argument */
355 void SD_task_dotty(SD_task_t task, void *out)
357 unsigned int counter;
358 SD_dependency_t dependency;
359 fprintf(out, " T%p [label=\"%.20s\"", task, task->name);
360 switch (task->kind) {
361 case SD_TASK_COMM_E2E:
362 fprintf(out, ", shape=box");
364 case SD_TASK_COMP_SEQ:
365 fprintf(out, ", shape=circle");
368 xbt_die("Unknown task type!");
370 fprintf(out, "];\n");
371 xbt_dynar_foreach(task->tasks_before, counter, dependency) {
372 fprintf(out, " T%p -> T%p;\n", dependency->src, dependency->dst);
376 /* Destroys a dependency between two tasks.
378 static void __SD_task_dependency_destroy(void *dependency)
380 if (((SD_dependency_t) dependency)->name != NULL)
381 xbt_free(((SD_dependency_t) dependency)->name);
382 xbt_free(dependency);
386 * \brief Adds a dependency between two tasks
388 * \a dst will depend on \a src, ie \a dst will not start before \a src is finished.
389 * Their \ref e_SD_task_state_t "state" must be #SD_NOT_SCHEDULED, #SD_SCHEDULED or #SD_RUNNABLE.
391 * \param name the name of the new dependency (can be \c NULL)
392 * \param data the user data you want to associate with this dependency (can be \c NULL)
393 * \param src the task which must be executed first
394 * \param dst the task you want to make depend on \a src
395 * \see SD_task_dependency_remove()
397 void SD_task_dependency_add(const char *name, void *data, SD_task_t src,
404 SD_dependency_t dependency;
406 SD_CHECK_INIT_DONE();
407 xbt_assert(src != NULL && dst != NULL, "Invalid parameter");
409 dynar = src->tasks_after;
410 length = xbt_dynar_length(dynar);
414 "Cannot add a dependency between task '%s' and itself",
415 SD_task_get_name(src));
417 if (!__SD_task_is_not_scheduled(src) && !__SD_task_is_schedulable(src)
418 && !__SD_task_is_scheduled_or_runnable(src))
420 "Task '%s' must be SD_NOT_SCHEDULED, SD_SCHEDULABLE, SD_SCHEDULED or SD_RUNNABLE",
421 SD_task_get_name(src));
423 if (!__SD_task_is_not_scheduled(dst) && !__SD_task_is_schedulable(dst)
424 && !__SD_task_is_scheduled_or_runnable(dst))
426 "Task '%s' must be SD_NOT_SCHEDULED, SD_SCHEDULABLE, SD_SCHEDULED or SD_RUNNABLE",
427 SD_task_get_name(dst));
429 XBT_DEBUG("SD_task_dependency_add: src = %s, dst = %s",
430 SD_task_get_name(src), SD_task_get_name(dst));
431 for (i = 0; i < length && !found; i++) {
432 xbt_dynar_get_cpy(dynar, i, &dependency);
433 found = (dependency->dst == dst);
434 XBT_DEBUG("Dependency %d: dependency->dst = %s", i,
435 SD_task_get_name(dependency->dst));
440 "A dependency already exists between task '%s' and task '%s'",
441 SD_task_get_name(src), SD_task_get_name(dst));
443 dependency = xbt_new(s_SD_dependency_t, 1);
445 dependency->name = xbt_strdup(name); /* xbt_strdup is cleaver enough to deal with NULL args itself */
446 dependency->data = data;
447 dependency->src = src;
448 dependency->dst = dst;
450 /* src must be executed before dst */
451 xbt_dynar_push(src->tasks_after, &dependency);
452 xbt_dynar_push(dst->tasks_before, &dependency);
454 dst->unsatisfied_dependencies++;
457 /* if the task was runnable, then dst->tasks_before is not empty anymore,
458 so we must go back to state SD_SCHEDULED */
459 if (__SD_task_is_runnable(dst)) {
461 ("SD_task_dependency_add: %s was runnable and becomes scheduled!",
462 SD_task_get_name(dst));
463 __SD_task_set_state(dst, SD_SCHEDULED);
466 /* __SD_print_dependencies(src);
467 __SD_print_dependencies(dst); */
471 * \brief Indacates whether there is a dependency between two tasks.
474 * \param dst a task depending on \a src
476 * If src is NULL, checks whether dst has any pre-dependency.
477 * If dst is NULL, checks whether src has any post-dependency.
479 int SD_task_dependency_exists(SD_task_t src, SD_task_t dst)
481 unsigned int counter;
482 SD_dependency_t dependency;
484 SD_CHECK_INIT_DONE();
485 xbt_assert(src != NULL
487 "Invalid parameter: both src and dst are NULL");
491 xbt_dynar_foreach(src->tasks_after, counter, dependency) {
492 if (dependency->dst == dst)
496 return xbt_dynar_length(src->tasks_after);
499 return xbt_dynar_length(dst->tasks_before);
505 * \brief Remove a dependency between two tasks
508 * \param dst a task depending on \a src
509 * \see SD_task_dependency_add()
511 void SD_task_dependency_remove(SD_task_t src, SD_task_t dst)
518 SD_dependency_t dependency;
520 SD_CHECK_INIT_DONE();
521 xbt_assert(src != NULL && dst != NULL, "Invalid parameter");
523 /* remove the dependency from src->tasks_after */
524 dynar = src->tasks_after;
525 length = xbt_dynar_length(dynar);
527 for (i = 0; i < length && !found; i++) {
528 xbt_dynar_get_cpy(dynar, i, &dependency);
529 if (dependency->dst == dst) {
530 xbt_dynar_remove_at(dynar, i, NULL);
536 "No dependency found between task '%s' and '%s': task '%s' is not a successor of task '%s'",
537 SD_task_get_name(src), SD_task_get_name(dst),
538 SD_task_get_name(dst), SD_task_get_name(src));
540 /* remove the dependency from dst->tasks_before */
541 dynar = dst->tasks_before;
542 length = xbt_dynar_length(dynar);
545 for (i = 0; i < length && !found; i++) {
546 xbt_dynar_get_cpy(dynar, i, &dependency);
547 if (dependency->src == src) {
548 xbt_dynar_remove_at(dynar, i, NULL);
549 __SD_task_dependency_destroy(dependency);
550 dst->unsatisfied_dependencies--;
555 /* should never happen... */
557 "SimDag error: task '%s' is a successor of '%s' but task '%s' is not a predecessor of task '%s'",
558 SD_task_get_name(dst), SD_task_get_name(src),
559 SD_task_get_name(src), SD_task_get_name(dst));
561 /* if the task was scheduled and dst->tasks_before is empty now, we can make it runnable */
563 if (dst->unsatisfied_dependencies == 0) {
564 if (__SD_task_is_scheduled(dst))
565 __SD_task_set_state(dst, SD_RUNNABLE);
567 __SD_task_set_state(dst, SD_SCHEDULABLE);
570 if (dst->is_not_ready == 0)
571 __SD_task_set_state(dst, SD_SCHEDULABLE);
573 /* __SD_print_dependencies(src);
574 __SD_print_dependencies(dst); */
578 * \brief Returns the user data associated with a dependency between two tasks
581 * \param dst a task depending on \a src
582 * \return the user data associated with this dependency (can be \c NULL)
583 * \see SD_task_dependency_add()
585 void *SD_task_dependency_get_data(SD_task_t src, SD_task_t dst)
592 SD_dependency_t dependency;
595 SD_CHECK_INIT_DONE();
596 xbt_assert(src != NULL && dst != NULL, "Invalid parameter");
598 dynar = src->tasks_after;
599 length = xbt_dynar_length(dynar);
601 for (i = 0; i < length && !found; i++) {
602 xbt_dynar_get_cpy(dynar, i, &dependency);
603 found = (dependency->dst == dst);
606 THROWF(arg_error, 0, "No dependency found between task '%s' and '%s'",
607 SD_task_get_name(src), SD_task_get_name(dst));
608 return dependency->data;
611 /* temporary function for debugging */
612 static void __SD_print_watch_points(SD_task_t task)
614 static const int state_masks[] =
615 { SD_SCHEDULABLE, SD_SCHEDULED, SD_RUNNING, SD_RUNNABLE, SD_DONE,
618 static const char *state_names[] =
619 { "schedulable", "scheduled", "running", "runnable", "done",
624 XBT_INFO("Task '%s' watch points (%x): ", SD_task_get_name(task),
628 for (i = 0; i < 5; i++) {
629 if (task->watch_points & state_masks[i])
630 XBT_INFO("%s ", state_names[i]);
635 * \brief Adds a watch point to a task
637 * SD_simulate() will stop as soon as the \ref e_SD_task_state_t "state" of this
638 * task becomes the one given in argument. The
639 * watch point is then automatically removed.
642 * \param state the \ref e_SD_task_state_t "state" you want to watch
643 * (cannot be #SD_NOT_SCHEDULED)
644 * \see SD_task_unwatch()
646 void SD_task_watch(SD_task_t task, e_SD_task_state_t state)
648 SD_CHECK_INIT_DONE();
649 xbt_assert(task != NULL, "Invalid parameter");
651 if (state & SD_NOT_SCHEDULED)
653 "Cannot add a watch point for state SD_NOT_SCHEDULED");
655 task->watch_points = task->watch_points | state;
656 /* __SD_print_watch_points(task); */
660 * \brief Removes a watch point from a task
663 * \param state the \ref e_SD_task_state_t "state" you no longer want to watch
664 * \see SD_task_watch()
666 void SD_task_unwatch(SD_task_t task, e_SD_task_state_t state)
668 SD_CHECK_INIT_DONE();
669 xbt_assert(task != NULL, "Invalid parameter");
670 xbt_assert(state != SD_NOT_SCHEDULED,
671 "SimDag error: Cannot have a watch point for state SD_NOT_SCHEDULED");
673 task->watch_points = task->watch_points & ~state;
674 /* __SD_print_watch_points(task); */
678 * \brief Returns an approximative estimation of the execution time of a task.
680 * The estimation is very approximative because the value returned is the time
681 * the task would take if it was executed now and if it was the only task.
683 * \param task the task to evaluate
684 * \param workstation_nb number of workstations on which the task would be executed
685 * \param workstation_list the workstations on which the task would be executed
686 * \param computation_amount computation amount for each workstation
687 * \param communication_amount communication amount between each pair of workstations
690 double SD_task_get_execution_time(SD_task_t task,
692 const SD_workstation_t *
694 const double *computation_amount,
695 const double *communication_amount)
697 double time, max_time = 0.0;
699 SD_CHECK_INIT_DONE();
700 xbt_assert(task != NULL && workstation_nb > 0
701 && workstation_list != NULL, "Invalid parameter");
703 /* the task execution time is the maximum execution time of the parallel tasks */
705 for (i = 0; i < workstation_nb; i++) {
707 if (computation_amount != NULL)
709 SD_workstation_get_computation_time(workstation_list[i],
710 computation_amount[i]);
712 if (communication_amount != NULL)
713 for (j = 0; j < workstation_nb; j++) {
715 SD_route_get_communication_time(workstation_list[i],
717 communication_amount[i *
722 if (time > max_time) {
729 static XBT_INLINE void SD_task_do_schedule(SD_task_t task)
731 SD_CHECK_INIT_DONE();
733 if (!__SD_task_is_not_scheduled(task) && !__SD_task_is_schedulable(task))
734 THROWF(arg_error, 0, "Task '%s' has already been scheduled",
735 SD_task_get_name(task));
737 /* update the task state */
738 if (task->unsatisfied_dependencies == 0)
739 __SD_task_set_state(task, SD_RUNNABLE);
741 __SD_task_set_state(task, SD_SCHEDULED);
745 * \brief Schedules a task
747 * The task state must be #SD_NOT_SCHEDULED.
748 * Once scheduled, a task will be executed as soon as possible in SD_simulate(),
749 * i.e. when its dependencies are satisfied.
751 * \param task the task you want to schedule
752 * \param workstation_count number of workstations on which the task will be executed
753 * \param workstation_list the workstations on which the task will be executed
754 * \param computation_amount computation amount for each workstation
755 * \param communication_amount communication amount between each pair of workstations
756 * \param rate task execution speed rate
757 * \see SD_task_unschedule()
759 void SD_task_schedule(SD_task_t task, int workstation_count,
760 const SD_workstation_t * workstation_list,
761 const double *computation_amount,
762 const double *communication_amount, double rate)
764 int communication_nb;
765 task->workstation_nb = 0;
767 xbt_assert(workstation_count > 0, "workstation_nb must be positive");
769 task->workstation_nb = workstation_count;
772 if (computation_amount) {
773 task->computation_amount = xbt_realloc(task->computation_amount,
774 sizeof(double) * workstation_count);
775 memcpy(task->computation_amount, computation_amount,
776 sizeof(double) * workstation_count);
778 xbt_free(task->computation_amount);
779 task->computation_amount = NULL;
782 communication_nb = workstation_count * workstation_count;
783 if (communication_amount) {
784 task->communication_amount = xbt_realloc(task->communication_amount,
785 sizeof(double) * communication_nb);
786 memcpy(task->communication_amount, communication_amount,
787 sizeof(double) * communication_nb);
789 xbt_free(task->communication_amount);
790 task->communication_amount = NULL;
793 task->workstation_list =
794 xbt_realloc(task->workstation_list,
795 sizeof(SD_workstation_t) * workstation_count);
796 memcpy(task->workstation_list, workstation_list,
797 sizeof(SD_workstation_t) * workstation_count);
799 SD_task_do_schedule(task);
803 * \brief Unschedules a task
805 * The task state must be #SD_SCHEDULED, #SD_RUNNABLE, #SD_RUNNING or #SD_FAILED.
806 * If you call this function, the task state becomes #SD_NOT_SCHEDULED.
807 * Call SD_task_schedule() to schedule it again.
809 * \param task the task you want to unschedule
810 * \see SD_task_schedule()
812 void SD_task_unschedule(SD_task_t task)
814 SD_CHECK_INIT_DONE();
815 xbt_assert(task != NULL, "Invalid parameter");
817 if (task->state_set != sd_global->scheduled_task_set &&
818 task->state_set != sd_global->runnable_task_set &&
819 task->state_set != sd_global->running_task_set &&
820 task->state_set != sd_global->failed_task_set)
822 "Task %s: the state must be SD_SCHEDULED, SD_RUNNABLE, SD_RUNNING or SD_FAILED",
823 SD_task_get_name(task));
825 if (__SD_task_is_scheduled_or_runnable(task) /* if the task is scheduled or runnable */
826 &&task->kind == SD_TASK_NOT_TYPED) /* Don't free scheduling data for typed tasks */
827 __SD_task_destroy_scheduling_data(task);
829 if (__SD_task_is_running(task)) /* the task should become SD_FAILED */
830 surf_workstation_model->action_cancel(task->surf_action);
832 if (task->unsatisfied_dependencies == 0)
833 __SD_task_set_state(task, SD_SCHEDULABLE);
835 __SD_task_set_state(task, SD_NOT_SCHEDULED);
837 task->remains = task->amount;
838 task->start_time = -1.0;
841 /* Destroys the data memorized by SD_task_schedule. Task state must be SD_SCHEDULED or SD_RUNNABLE.
843 static void __SD_task_destroy_scheduling_data(SD_task_t task)
845 SD_CHECK_INIT_DONE();
846 if (!__SD_task_is_scheduled_or_runnable(task)
847 && !__SD_task_is_in_fifo(task))
849 "Task '%s' must be SD_SCHEDULED, SD_RUNNABLE or SD_IN_FIFO",
850 SD_task_get_name(task));
852 xbt_free(task->computation_amount);
853 xbt_free(task->communication_amount);
854 task->computation_amount = task->communication_amount = NULL;
857 /* Runs a task. This function is directly called by __SD_task_try_to_run if the task
858 * doesn't have to wait in fifos. Otherwise, it is called by __SD_task_just_done when
859 * the task gets out of its fifos.
861 void __SD_task_really_run(SD_task_t task)
865 void **surf_workstations;
867 SD_CHECK_INIT_DONE();
868 xbt_assert(task != NULL, "Invalid parameter");
869 xbt_assert(__SD_task_is_runnable_or_in_fifo(task),
870 "Task '%s' is not runnable or in a fifo! Task state: %d",
871 SD_task_get_name(task), SD_task_get_state(task));
872 xbt_assert(task->workstation_list != NULL,
873 "Task '%s': workstation_list is NULL!",
874 SD_task_get_name(task));
878 XBT_DEBUG("Really running task '%s'", SD_task_get_name(task));
880 /* set this task as current task for the workstations in sequential mode */
881 for (i = 0; i < task->workstation_nb; i++) {
882 if (SD_workstation_get_access_mode(task->workstation_list[i]) ==
883 SD_WORKSTATION_SEQUENTIAL_ACCESS) {
884 task->workstation_list[i]->current_task = task;
885 xbt_assert(__SD_workstation_is_busy(task->workstation_list[i]),
886 "The workstation should be busy now");
890 XBT_DEBUG("Task '%s' set as current task for its workstations",
891 SD_task_get_name(task));
895 /* we have to create a Surf workstation array instead of the SimDag workstation array */
896 surf_workstations = xbt_new(void *, task->workstation_nb);
898 for (i = 0; i < task->workstation_nb; i++)
899 surf_workstations[i] = task->workstation_list[i]->surf_workstation;
901 /* 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 */
902 #define cost_or_zero(array,pos) ((array)?(array)[pos]:0.0)
904 task->surf_action = NULL;
905 if ((task->workstation_nb == 1)
906 && (cost_or_zero(task->communication_amount, 0) == 0.0)) {
908 surf_workstation_model->extension.
909 workstation.execute(surf_workstations[0],
910 cost_or_zero(task->computation_amount, 0));
911 } else if ((task->workstation_nb == 1)
912 && (cost_or_zero(task->computation_amount, 0) == 0.0)) {
915 surf_workstation_model->extension.
916 workstation.communicate(surf_workstations[0], surf_workstations[0],
917 cost_or_zero(task->communication_amount,
919 } else if ((task->workstation_nb == 2)
920 && (cost_or_zero(task->computation_amount, 0) == 0.0)
921 && (cost_or_zero(task->computation_amount, 1) == 0.0)) {
925 for (i = 0; i < task->workstation_nb * task->workstation_nb; i++) {
926 if (cost_or_zero(task->communication_amount, i) > 0.0) {
928 value = cost_or_zero(task->communication_amount, i);
933 surf_workstation_model->extension.
934 workstation.communicate(surf_workstations[0],
935 surf_workstations[1], value, task->rate);
940 if (!task->surf_action) {
941 double *computation_amount = xbt_new(double, task->workstation_nb);
942 double *communication_amount = xbt_new(double, task->workstation_nb *
943 task->workstation_nb);
945 memcpy(computation_amount, task->computation_amount, sizeof(double) *
946 task->workstation_nb);
947 memcpy(communication_amount, task->communication_amount,
948 sizeof(double) * task->workstation_nb * task->workstation_nb);
951 surf_workstation_model->extension.
952 workstation.execute_parallel_task(task->workstation_nb,
955 communication_amount,
956 task->amount, task->rate);
958 xbt_free(surf_workstations);
961 surf_workstation_model->action_data_set(task->surf_action, task);
963 XBT_DEBUG("surf_action = %p", task->surf_action);
967 TRACE_surf_action(task->surf_action, task->category);
970 __SD_task_destroy_scheduling_data(task); /* now the scheduling data are not useful anymore */
971 __SD_task_set_state(task, SD_RUNNING);
972 xbt_assert(__SD_task_is_running(task), "Bad state of task '%s': %d",
973 SD_task_get_name(task), SD_task_get_state(task));
977 /* Tries to run a task. This function is called by SD_simulate() when a scheduled task becomes SD_RUNNABLE
978 * (ie when its dependencies are satisfied).
979 * If one of the workstations where the task is scheduled on is busy (in sequential mode),
980 * the task doesn't start.
981 * Returns whether the task has started.
983 int __SD_task_try_to_run(SD_task_t task)
988 SD_workstation_t workstation;
990 SD_CHECK_INIT_DONE();
991 xbt_assert(task != NULL, "Invalid parameter");
992 xbt_assert(__SD_task_is_runnable(task),
993 "Task '%s' is not runnable! Task state: %d",
994 SD_task_get_name(task), SD_task_get_state(task));
997 for (i = 0; i < task->workstation_nb; i++) {
998 can_start = can_start &&
999 !__SD_workstation_is_busy(task->workstation_list[i]);
1002 XBT_DEBUG("Task '%s' can start: %d", SD_task_get_name(task), can_start);
1004 if (!can_start) { /* if the task cannot start and is not in the fifos yet */
1005 for (i = 0; i < task->workstation_nb; i++) {
1006 workstation = task->workstation_list[i];
1007 if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
1008 XBT_DEBUG("Pushing task '%s' in the fifo of workstation '%s'",
1009 SD_task_get_name(task),
1010 SD_workstation_get_name(workstation));
1011 xbt_fifo_push(workstation->task_fifo, task);
1014 __SD_task_set_state(task, SD_IN_FIFO);
1015 xbt_assert(__SD_task_is_in_fifo(task), "Bad state of task '%s': %d",
1016 SD_task_get_name(task), SD_task_get_state(task));
1017 XBT_DEBUG("Task '%s' state is now SD_IN_FIFO", SD_task_get_name(task));
1019 __SD_task_really_run(task);
1025 /* This function is called by SD_simulate when a task is done.
1026 * It updates task->state and task->action and executes if necessary the tasks
1027 * which were waiting in fifos for the end of `task'
1029 void __SD_task_just_done(SD_task_t task)
1032 SD_workstation_t workstation;
1034 SD_task_t candidate;
1035 int candidate_nb = 0;
1036 int candidate_capacity = 8;
1037 SD_task_t *candidates;
1040 SD_CHECK_INIT_DONE();
1041 xbt_assert(task != NULL, "Invalid parameter");
1042 xbt_assert(__SD_task_is_running(task),
1043 "The task must be running! Task state: %d",
1044 SD_task_get_state(task));
1045 xbt_assert(task->workstation_list != NULL,
1046 "Task '%s': workstation_list is NULL!",
1047 SD_task_get_name(task));
1050 candidates = xbt_new(SD_task_t, 8);
1052 __SD_task_set_state(task, SD_DONE);
1053 surf_workstation_model->action_unref(task->surf_action);
1054 task->surf_action = NULL;
1056 XBT_DEBUG("Looking for candidates");
1058 /* if the task was executed on sequential workstations,
1059 maybe we can execute the next task of the fifo for each workstation */
1060 for (i = 0; i < task->workstation_nb; i++) {
1061 workstation = task->workstation_list[i];
1062 XBT_DEBUG("Workstation '%s': access_mode = %d",
1063 SD_workstation_get_name(workstation), workstation->access_mode);
1064 if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
1065 xbt_assert(workstation->task_fifo != NULL,
1066 "Workstation '%s' has sequential access but no fifo!",
1067 SD_workstation_get_name(workstation));
1068 xbt_assert(workstation->current_task =
1069 task, "Workstation '%s': current task should be '%s'",
1070 SD_workstation_get_name(workstation),
1071 SD_task_get_name(task));
1073 /* the task is over so we can release the workstation */
1074 workstation->current_task = NULL;
1076 XBT_DEBUG("Getting candidate in fifo");
1078 xbt_fifo_get_item_content(xbt_fifo_get_first_item
1079 (workstation->task_fifo));
1081 if (candidate != NULL) {
1082 XBT_DEBUG("Candidate: '%s'", SD_task_get_name(candidate));
1083 xbt_assert(__SD_task_is_in_fifo(candidate),
1084 "Bad state of candidate '%s': %d",
1085 SD_task_get_name(candidate),
1086 SD_task_get_state(candidate));
1089 XBT_DEBUG("Candidate in fifo: %p", candidate);
1091 /* if there was a task waiting for my place */
1092 if (candidate != NULL) {
1093 /* Unfortunately, we are not sure yet that we can execute the task now,
1094 because the task can be waiting more deeply in some other workstation's fifos...
1095 So we memorize all candidate tasks, and then we will check for each candidate
1096 whether or not all its workstations are available. */
1098 /* realloc if necessary */
1099 if (candidate_nb == candidate_capacity) {
1100 candidate_capacity *= 2;
1102 xbt_realloc(candidates,
1103 sizeof(SD_task_t) * candidate_capacity);
1106 /* register the candidate */
1107 candidates[candidate_nb++] = candidate;
1108 candidate->fifo_checked = 0;
1113 XBT_DEBUG("Candidates found: %d", candidate_nb);
1115 /* now we check every candidate task */
1116 for (i = 0; i < candidate_nb; i++) {
1117 candidate = candidates[i];
1119 if (candidate->fifo_checked) {
1120 continue; /* we have already evaluated that task */
1123 xbt_assert(__SD_task_is_in_fifo(candidate),
1124 "Bad state of candidate '%s': %d",
1125 SD_task_get_name(candidate), SD_task_get_state(candidate));
1127 for (j = 0; j < candidate->workstation_nb && can_start; j++) {
1128 workstation = candidate->workstation_list[j];
1130 /* I can start on this workstation if the workstation is shared
1131 or if I am the first task in the fifo */
1132 can_start = workstation->access_mode == SD_WORKSTATION_SHARED_ACCESS
1134 xbt_fifo_get_item_content(xbt_fifo_get_first_item
1135 (workstation->task_fifo));
1138 XBT_DEBUG("Candidate '%s' can start: %d", SD_task_get_name(candidate),
1141 /* now we are sure that I can start! */
1143 for (j = 0; j < candidate->workstation_nb && can_start; j++) {
1144 workstation = candidate->workstation_list[j];
1146 /* update the fifo */
1147 if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
1148 candidate = xbt_fifo_shift(workstation->task_fifo); /* the return value is stored just for debugging */
1149 XBT_DEBUG("Head of the fifo: '%s'",
1151 NULL) ? SD_task_get_name(candidate) : "NULL");
1152 xbt_assert(candidate == candidates[i],
1153 "Error in __SD_task_just_done: bad first task in the fifo");
1155 } /* for each workstation */
1157 /* finally execute the task */
1158 XBT_DEBUG("Task '%s' state: %d", SD_task_get_name(candidate),
1159 SD_task_get_state(candidate));
1160 __SD_task_really_run(candidate);
1163 ("Calling __SD_task_is_running: task '%s', state set: %p, running_task_set: %p, is running: %d",
1164 SD_task_get_name(candidate), candidate->state_set,
1165 sd_global->running_task_set, __SD_task_is_running(candidate));
1166 xbt_assert(__SD_task_is_running(candidate),
1167 "Bad state of task '%s': %d",
1168 SD_task_get_name(candidate),
1169 SD_task_get_state(candidate));
1170 XBT_DEBUG("Okay, the task is running.");
1173 candidate->fifo_checked = 1;
1174 } /* for each candidate */
1176 xbt_free(candidates);
1179 /* Remove all dependencies associated with a task. This function is called when the task is destroyed.
1181 static void __SD_task_remove_dependencies(SD_task_t task)
1183 /* we must destroy the dependencies carefuly (with SD_dependency_remove)
1184 because each one is stored twice */
1185 SD_dependency_t dependency;
1186 while (xbt_dynar_length(task->tasks_before) > 0) {
1187 xbt_dynar_get_cpy(task->tasks_before, 0, &dependency);
1188 SD_task_dependency_remove(dependency->src, dependency->dst);
1191 while (xbt_dynar_length(task->tasks_after) > 0) {
1192 xbt_dynar_get_cpy(task->tasks_after, 0, &dependency);
1193 SD_task_dependency_remove(dependency->src, dependency->dst);
1198 * \brief Returns the start time of a task
1200 * The task state must be SD_RUNNING, SD_DONE or SD_FAILED.
1202 * \param task: a task
1203 * \return the start time of this task
1205 double SD_task_get_start_time(SD_task_t task)
1207 SD_CHECK_INIT_DONE();
1208 xbt_assert(task != NULL, "Invalid parameter");
1209 if (task->surf_action)
1210 return surf_workstation_model->
1211 action_get_start_time(task->surf_action);
1213 return task->start_time;
1217 * \brief Returns the finish time of a task
1219 * The task state must be SD_RUNNING, SD_DONE or SD_FAILED.
1220 * If the state is not completed yet, the returned value is an
1221 * estimation of the task finish time. This value can fluctuate
1222 * until the task is completed.
1224 * \param task: a task
1225 * \return the start time of this task
1227 double SD_task_get_finish_time(SD_task_t task)
1229 SD_CHECK_INIT_DONE();
1230 xbt_assert(task != NULL, "Invalid parameter");
1232 if (task->surf_action) /* should never happen as actions are destroyed right after their completion */
1233 return surf_workstation_model->
1234 action_get_finish_time(task->surf_action);
1236 return task->finish_time;
1240 * \brief Destroys a task.
1242 * The user data (if any) should have been destroyed first.
1244 * \param task the task you want to destroy
1245 * \see SD_task_create()
1247 void SD_task_destroy(SD_task_t task)
1249 SD_CHECK_INIT_DONE();
1250 xbt_assert(task != NULL, "Invalid parameter");
1252 XBT_DEBUG("Destroying task %s...", SD_task_get_name(task));
1254 __SD_task_remove_dependencies(task);
1255 /* if the task was scheduled or runnable we have to free the scheduling parameters */
1256 if (__SD_task_is_scheduled_or_runnable(task))
1257 __SD_task_destroy_scheduling_data(task);
1258 xbt_swag_remove(task, task->state_set);
1259 xbt_swag_remove(task, sd_global->return_set);
1261 if (task->name != NULL)
1262 xbt_free(task->name);
1264 if (task->surf_action != NULL)
1265 surf_workstation_model->action_unref(task->surf_action);
1267 if (task->workstation_list != NULL)
1268 xbt_free(task->workstation_list);
1270 if (task->communication_amount)
1271 xbt_free(task->communication_amount);
1273 if (task->computation_amount)
1274 xbt_free(task->computation_amount);
1277 TRACE_sd_task_destroy(task);
1280 xbt_dynar_free(&task->tasks_before);
1281 xbt_dynar_free(&task->tasks_after);
1284 sd_global->task_number--;
1286 XBT_DEBUG("Task destroyed.");
1290 static XBT_INLINE SD_task_t SD_task_create_sized(const char *name,
1291 void *data, double amount,
1294 SD_task_t task = SD_task_create(name, data, amount);
1295 task->communication_amount = xbt_new0(double, ws_count * ws_count);
1296 task->computation_amount = xbt_new0(double, ws_count);
1297 task->workstation_nb = ws_count;
1298 task->workstation_list = xbt_new0(SD_workstation_t, ws_count);
1302 /** @brief create a end-to-end communication task that can then be auto-scheduled
1304 * Auto-scheduling mean that the task can be used with SD_task_schedulev(). This
1305 * allows to specify the task costs at creation, and decorelate them from the
1306 * scheduling process where you just specify which resource should deliver the
1309 * A end-to-end communication must be scheduled on 2 hosts, and the amount
1310 * specified at creation is sent from hosts[0] to hosts[1].
1312 SD_task_t SD_task_create_comm_e2e(const char *name, void *data,
1315 SD_task_t res = SD_task_create_sized(name, data, amount, 2);
1316 res->communication_amount[2] = amount;
1317 res->kind = SD_TASK_COMM_E2E;
1321 /** @brief create a sequential computation task that can then be auto-scheduled
1323 * Auto-scheduling mean that the task can be used with SD_task_schedulev(). This
1324 * allows to specify the task costs at creation, and decorelate them from the
1325 * scheduling process where you just specify which resource should deliver the
1328 * A sequential computation must be scheduled on 1 host, and the amount
1329 * specified at creation to be run on hosts[0].
1331 SD_task_t SD_task_create_comp_seq(const char *name, void *data,
1334 SD_task_t res = SD_task_create_sized(name, data, amount, 1);
1335 res->computation_amount[0] = amount;
1336 res->kind = SD_TASK_COMP_SEQ;
1340 /** @brief Auto-schedules a task.
1342 * Auto-scheduling mean that the task can be used with SD_task_schedulev(). This
1343 * allows to specify the task costs at creation, and decorelate them from the
1344 * scheduling process where you just specify which resource should deliver the
1347 * To be auto-schedulable, a task must be created with SD_task_create_comm_e2e() or
1348 * SD_task_create_comp_seq(). Check their definitions for the exact semantic of each
1352 * We should create tasks kind for the following categories:
1353 * - Point to point communication (done)
1354 * - Sequential computation (done)
1355 * - group communication (redistribution, several kinds)
1356 * - parallel tasks with no internal communication (one kind per speedup model such as amdal)
1357 * - idem+ internal communication. Task type not enough since we cannot store comm cost alongside to comp one)
1359 void SD_task_schedulev(SD_task_t task, int count,
1360 const SD_workstation_t * list)
1363 SD_dependency_t dep;
1365 xbt_assert(task->kind != 0,
1366 "Task %s is not typed. Cannot automatically schedule it.",
1367 SD_task_get_name(task));
1368 switch (task->kind) {
1369 case SD_TASK_COMM_E2E:
1370 case SD_TASK_COMP_SEQ:
1371 xbt_assert(task->workstation_nb == count,"Got %d locations, but were expecting %d locations",count,task->workstation_nb);
1372 for (i = 0; i < count; i++)
1373 task->workstation_list[i] = list[i];
1374 SD_task_do_schedule(task);
1377 xbt_die("Kind of task %s not supported by SD_task_schedulev()",
1378 SD_task_get_name(task));
1380 if (task->kind == SD_TASK_COMM_E2E) {
1381 XBT_VERB("Schedule comm task %s between %s -> %s. It costs %.f bytes",
1382 SD_task_get_name(task),
1383 SD_workstation_get_name(task->workstation_list[0]),
1384 SD_workstation_get_name(task->workstation_list[1]),
1385 task->communication_amount[2]);
1388 /* Iterate over all childs and parent being COMM_E2E to say where I am located (and start them if runnable) */
1389 if (task->kind == SD_TASK_COMP_SEQ) {
1390 XBT_VERB("Schedule computation task %s on %s. It costs %.f flops",
1391 SD_task_get_name(task),
1392 SD_workstation_get_name(task->workstation_list[0]),
1393 task->computation_amount[0]);
1395 xbt_dynar_foreach(task->tasks_before, cpt, dep) {
1396 SD_task_t before = dep->src;
1397 if (before->kind == SD_TASK_COMM_E2E) {
1398 before->workstation_list[1] = task->workstation_list[0];
1400 if (before->workstation_list[0] &&
1401 (__SD_task_is_schedulable(before)
1402 || __SD_task_is_not_scheduled(before))) {
1403 SD_task_do_schedule(before);
1405 ("Auto-Schedule comm task %s between %s -> %s. It costs %.f bytes",
1406 SD_task_get_name(before),
1407 SD_workstation_get_name(before->workstation_list[0]),
1408 SD_workstation_get_name(before->workstation_list[1]),
1409 before->communication_amount[2]);
1413 xbt_dynar_foreach(task->tasks_after, cpt, dep) {
1414 SD_task_t after = dep->dst;
1415 if (after->kind == SD_TASK_COMM_E2E) {
1416 after->workstation_list[0] = task->workstation_list[0];
1417 //J-N : Why did you comment on these line (this comment add a bug I think)?
1418 if (after->workstation_list[1]
1419 && (__SD_task_is_not_scheduled(after)
1420 || __SD_task_is_schedulable(after))) {
1421 SD_task_do_schedule(after);
1423 ("Auto-Schedule comm task %s between %s -> %s. It costs %.f bytes",
1424 SD_task_get_name(after),
1425 SD_workstation_get_name(after->workstation_list[0]),
1426 SD_workstation_get_name(after->workstation_list[1]),
1427 after->communication_amount[2]);
1435 /** @brief autoschedule a task on a list of workstations
1437 * This function is very similar to SD_task_schedulev(),
1438 * but takes the list of workstations to schedule onto as separate parameters.
1439 * It builds a proper vector of workstations and then call SD_task_schedulev()
1441 void SD_task_schedulel(SD_task_t task, int count, ...)
1444 SD_workstation_t *list = xbt_new(SD_workstation_t, count);
1446 va_start(ap, count);
1447 for (i = 0; i < count; i++) {
1448 list[i] = va_arg(ap, SD_workstation_t);
1451 SD_task_schedulev(task, count, list);