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/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 xbt_swag_insert(task, task->state_set);
46 task->amount = amount;
47 task->remains = amount;
48 task->start_time = -1.0;
49 task->finish_time = -1.0;
50 task->surf_action = NULL;
51 task->watch_points = 0;
54 task->tasks_before = xbt_dynar_new(sizeof(SD_dependency_t), NULL);
55 task->tasks_after = xbt_dynar_new(sizeof(SD_dependency_t), NULL);
56 task->unsatisfied_dependencies = 0;
57 task->is_not_ready = 0;
59 /* scheduling parameters */
60 task->workstation_nb = 0;
61 task->workstation_list = NULL;
62 task->computation_amount = NULL;
63 task->communication_amount = NULL;
66 sd_global->task_number++;
69 TRACE_sd_task_create(task);
76 * \brief Returns the user data of a task
79 * \return the user data associated with this task (can be \c NULL)
80 * \see SD_task_set_data()
82 void *SD_task_get_data(SD_task_t task)
85 xbt_assert0(task != NULL, "Invalid parameter");
90 * \brief Sets the user data of a task
92 * The new data can be \c NULL. The old data should have been freed first
93 * if it was not \c NULL.
96 * \param data the new data you want to associate with this task
97 * \see SD_task_get_data()
99 void SD_task_set_data(SD_task_t task, void *data)
101 SD_CHECK_INIT_DONE();
102 xbt_assert0(task != NULL, "Invalid parameter");
107 * \brief Returns the state of a task
110 * \return the current \ref e_SD_task_state_t "state" of this task:
111 * #SD_NOT_SCHEDULED, #SD_SCHEDULED, #SD_RUNNABLE, #SD_RUNNING, #SD_DONE or #SD_FAILED
112 * \see e_SD_task_state_t
114 e_SD_task_state_t SD_task_get_state(SD_task_t task)
116 SD_CHECK_INIT_DONE();
117 xbt_assert0(task != NULL, "Invalid parameter");
121 /* Changes the state of a task. Updates the swags and the flag sd_global->watch_point_reached.
123 void __SD_task_set_state(SD_task_t task, e_SD_task_state_t new_state)
125 xbt_swag_remove(task, task->state_set);
127 case SD_NOT_SCHEDULED:
128 task->state_set = sd_global->not_scheduled_task_set;
131 task->state_set = sd_global->schedulable_task_set;
134 task->state_set = sd_global->scheduled_task_set;
137 task->state_set = sd_global->runnable_task_set;
140 task->state_set = sd_global->in_fifo_task_set;
143 task->state_set = sd_global->running_task_set;
145 surf_workstation_model->action_get_start_time(task->surf_action);
148 task->state_set = sd_global->done_task_set;
150 surf_workstation_model->action_get_finish_time(task->surf_action);
154 task->state_set = sd_global->failed_task_set;
157 xbt_assert0(0, "Invalid state");
159 xbt_swag_insert(task, task->state_set);
160 task->state = new_state;
162 if (task->watch_points & new_state) {
163 VERB1("Watch point reached with task '%s'!", SD_task_get_name(task));
164 sd_global->watch_point_reached = 1;
165 SD_task_unwatch(task, new_state); /* remove the watch point */
170 * \brief Returns the name of a task
173 * \return the name of this task (can be \c NULL)
175 const char *SD_task_get_name(SD_task_t task)
177 SD_CHECK_INIT_DONE();
178 xbt_assert0(task != NULL, "Invalid parameter");
182 /** @brief Allows to change the name of a task */
183 void SD_task_set_name(SD_task_t task, const char *name)
185 xbt_free(task->name);
186 task->name = xbt_strdup(name);
189 /** @brief Returns the dynar of the parents of a task
192 * \return a newly allocated dynar comprising the parents of this task
195 xbt_dynar_t SD_task_get_parents(SD_task_t task)
200 SD_CHECK_INIT_DONE();
201 xbt_assert0(task != NULL, "Invalid parameter");
203 parents = xbt_dynar_new(sizeof(SD_task_t), NULL);
204 xbt_dynar_foreach(task->tasks_before, i, dep) {
205 xbt_dynar_push(parents, &(dep->src));
210 /** @brief Returns the dynar of the parents of a task
213 * \return a newly allocated dynar comprising the parents of this task
215 xbt_dynar_t SD_task_get_children(SD_task_t task)
218 xbt_dynar_t children;
220 SD_CHECK_INIT_DONE();
221 xbt_assert0(task != NULL, "Invalid parameter");
223 children = xbt_dynar_new(sizeof(SD_task_t), NULL);
224 xbt_dynar_foreach(task->tasks_after, i, dep) {
225 xbt_dynar_push(children, &(dep->dst));
231 * \brief Returns the amount of workstations involved in a task
233 * Only call this on already scheduled tasks!
236 int SD_task_get_workstation_count(SD_task_t task)
238 SD_CHECK_INIT_DONE();
239 xbt_assert0(task != NULL, "Invalid parameter");
240 // xbt_assert1( task->state_set != sd_global->scheduled_task_set,
241 // "Unscheduled task %s", task->name);
242 return task->workstation_nb;
246 * \brief Returns the list of workstations involved in a task
248 * Only call this on already scheduled tasks!
251 SD_workstation_t *SD_task_get_workstation_list(SD_task_t task)
253 SD_CHECK_INIT_DONE();
254 xbt_assert0(task != NULL, "Invalid parameter");
255 //xbt_assert1( task->state_set != sd_global->scheduled_task_set,
256 // "Unscheduled task %s", task->name);
257 return task->workstation_list;
261 * \brief Returns the total amount of work contained in a task
264 * \return the total amount of work (computation or data transfer) for this task
265 * \see SD_task_get_remaining_amount()
267 double SD_task_get_amount(SD_task_t task)
269 SD_CHECK_INIT_DONE();
270 xbt_assert0(task != NULL, "Invalid parameter");
275 * \brief Returns the remaining amount work to do till the completion of a task
278 * \return the remaining amount of work (computation or data transfer) of this task
279 * \see SD_task_get_amount()
281 double SD_task_get_remaining_amount(SD_task_t task)
283 SD_CHECK_INIT_DONE();
284 xbt_assert0(task != NULL, "Invalid parameter");
286 if (task->surf_action)
287 return surf_workstation_model->get_remains(task->surf_action);
289 return task->remains;
292 int SD_task_get_kind(SD_task_t task)
297 /** @brief Displays debugging informations about a task */
298 void SD_task_dump(SD_task_t task)
300 unsigned int counter;
301 SD_dependency_t dependency;
304 INFO1("Displaying task %s", SD_task_get_name(task));
305 statename = bprintf("%s %s %s %s %s %s %s %s",
306 (task->state & SD_NOT_SCHEDULED ? "not scheduled" :
308 (task->state & SD_SCHEDULABLE ? "schedulable" : ""),
309 (task->state & SD_SCHEDULED ? "scheduled" : ""),
310 (task->state & SD_RUNNABLE ? "runnable" :
312 (task->state & SD_IN_FIFO ? "in fifo" : ""),
313 (task->state & SD_RUNNING ? "running" : ""),
314 (task->state & SD_DONE ? "done" : ""),
315 (task->state & SD_FAILED ? "failed" : ""));
316 INFO1(" - state: %s", statename);
319 if (task->kind != 0) {
320 switch (task->kind) {
321 case SD_TASK_COMM_E2E:
322 INFO0(" - kind: end-to-end communication");
324 case SD_TASK_COMP_SEQ:
325 INFO0(" - kind: sequential computation");
328 INFO1(" - (unknown kind %d)", task->kind);
331 INFO1(" - amount: %.0f", SD_task_get_amount(task));
332 INFO1(" - Dependencies to satisfy: %d", task->unsatisfied_dependencies);
333 if (xbt_dynar_length(task->tasks_before)) {
334 INFO0(" - pre-dependencies:");
335 xbt_dynar_foreach(task->tasks_before, counter, dependency) {
336 INFO1(" %s", SD_task_get_name(dependency->src));
339 if (xbt_dynar_length(task->tasks_after)) {
340 INFO0(" - post-dependencies:");
341 xbt_dynar_foreach(task->tasks_after, counter, dependency) {
342 INFO1(" %s", SD_task_get_name(dependency->dst));
347 /** @brief Dumps the task in dotty formalism into the FILE* passed as second argument */
348 void SD_task_dotty(SD_task_t task, void *out)
350 unsigned int counter;
351 SD_dependency_t dependency;
352 fprintf(out, " T%p [label=\"%.20s\"", task, task->name);
353 switch (task->kind) {
354 case SD_TASK_COMM_E2E:
355 fprintf(out, ", shape=box");
357 case SD_TASK_COMP_SEQ:
358 fprintf(out, ", shape=circle");
361 xbt_die("Unknown task type!");
363 fprintf(out, "];\n");
364 xbt_dynar_foreach(task->tasks_before, counter, dependency) {
365 fprintf(out, " T%p -> T%p;\n", dependency->src, dependency->dst);
369 /* Destroys a dependency between two tasks.
371 static void __SD_task_dependency_destroy(void *dependency)
373 if (((SD_dependency_t) dependency)->name != NULL)
374 xbt_free(((SD_dependency_t) dependency)->name);
375 xbt_free(dependency);
379 * \brief Adds a dependency between two tasks
381 * \a dst will depend on \a src, ie \a dst will not start before \a src is finished.
382 * Their \ref e_SD_task_state_t "state" must be #SD_NOT_SCHEDULED, #SD_SCHEDULED or #SD_RUNNABLE.
384 * \param name the name of the new dependency (can be \c NULL)
385 * \param data the user data you want to associate with this dependency (can be \c NULL)
386 * \param src the task which must be executed first
387 * \param dst the task you want to make depend on \a src
388 * \see SD_task_dependency_remove()
390 void SD_task_dependency_add(const char *name, void *data, SD_task_t src,
397 SD_dependency_t dependency;
399 SD_CHECK_INIT_DONE();
400 xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
402 dynar = src->tasks_after;
403 length = xbt_dynar_length(dynar);
407 "Cannot add a dependency between task '%s' and itself",
408 SD_task_get_name(src));
410 if (!__SD_task_is_not_scheduled(src) && !__SD_task_is_schedulable(src)
411 && !__SD_task_is_scheduled_or_runnable(src))
413 "Task '%s' must be SD_NOT_SCHEDULED, SD_SCHEDULABLE, SD_SCHEDULED or SD_RUNNABLE",
414 SD_task_get_name(src));
416 if (!__SD_task_is_not_scheduled(dst) && !__SD_task_is_schedulable(dst)
417 && !__SD_task_is_scheduled_or_runnable(dst))
419 "Task '%s' must be SD_NOT_SCHEDULED, SD_SCHEDULABLE, SD_SCHEDULED or SD_RUNNABLE",
420 SD_task_get_name(dst));
422 DEBUG2("SD_task_dependency_add: src = %s, dst = %s",
423 SD_task_get_name(src), SD_task_get_name(dst));
424 for (i = 0; i < length && !found; i++) {
425 xbt_dynar_get_cpy(dynar, i, &dependency);
426 found = (dependency->dst == dst);
427 DEBUG2("Dependency %d: dependency->dst = %s", i,
428 SD_task_get_name(dependency->dst));
433 "A dependency already exists between task '%s' and task '%s'",
434 SD_task_get_name(src), SD_task_get_name(dst));
436 dependency = xbt_new(s_SD_dependency_t, 1);
438 dependency->name = xbt_strdup(name); /* xbt_strdup is cleaver enough to deal with NULL args itself */
439 dependency->data = data;
440 dependency->src = src;
441 dependency->dst = dst;
443 /* src must be executed before dst */
444 xbt_dynar_push(src->tasks_after, &dependency);
445 xbt_dynar_push(dst->tasks_before, &dependency);
447 dst->unsatisfied_dependencies++;
450 /* if the task was runnable, then dst->tasks_before is not empty anymore,
451 so we must go back to state SD_SCHEDULED */
452 if (__SD_task_is_runnable(dst)) {
454 ("SD_task_dependency_add: %s was runnable and becomes scheduled!",
455 SD_task_get_name(dst));
456 __SD_task_set_state(dst, SD_SCHEDULED);
459 /* __SD_print_dependencies(src);
460 __SD_print_dependencies(dst); */
464 * \brief Indacates whether there is a dependency between two tasks.
467 * \param dst a task depending on \a src
469 * If src is NULL, checks whether dst has any pre-dependency.
470 * If dst is NULL, checks whether src has any post-dependency.
472 int SD_task_dependency_exists(SD_task_t src, SD_task_t dst)
474 unsigned int counter;
475 SD_dependency_t dependency;
477 SD_CHECK_INIT_DONE();
478 xbt_assert0(src != NULL
480 "Invalid parameter: both src and dst are NULL");
484 xbt_dynar_foreach(src->tasks_after, counter, dependency) {
485 if (dependency->dst == dst)
489 return xbt_dynar_length(src->tasks_after);
492 return xbt_dynar_length(dst->tasks_before);
498 * \brief Remove a dependency between two tasks
501 * \param dst a task depending on \a src
502 * \see SD_task_dependency_add()
504 void SD_task_dependency_remove(SD_task_t src, SD_task_t dst)
511 SD_dependency_t dependency;
513 SD_CHECK_INIT_DONE();
514 xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
516 /* remove the dependency from src->tasks_after */
517 dynar = src->tasks_after;
518 length = xbt_dynar_length(dynar);
520 for (i = 0; i < length && !found; i++) {
521 xbt_dynar_get_cpy(dynar, i, &dependency);
522 if (dependency->dst == dst) {
523 xbt_dynar_remove_at(dynar, i, NULL);
529 "No dependency found between task '%s' and '%s': task '%s' is not a successor of task '%s'",
530 SD_task_get_name(src), SD_task_get_name(dst),
531 SD_task_get_name(dst), SD_task_get_name(src));
533 /* remove the dependency from dst->tasks_before */
534 dynar = dst->tasks_before;
535 length = xbt_dynar_length(dynar);
538 for (i = 0; i < length && !found; i++) {
539 xbt_dynar_get_cpy(dynar, i, &dependency);
540 if (dependency->src == src) {
541 xbt_dynar_remove_at(dynar, i, NULL);
542 __SD_task_dependency_destroy(dependency);
543 dst->unsatisfied_dependencies--;
548 /* should never happen... */
550 "SimDag error: task '%s' is a successor of '%s' but task '%s' is not a predecessor of task '%s'",
551 SD_task_get_name(dst), SD_task_get_name(src),
552 SD_task_get_name(src), SD_task_get_name(dst));
554 /* if the task was scheduled and dst->tasks_before is empty now, we can make it runnable */
556 if (dst->unsatisfied_dependencies == 0) {
557 if (__SD_task_is_scheduled(dst))
558 __SD_task_set_state(dst, SD_RUNNABLE);
560 __SD_task_set_state(dst, SD_SCHEDULABLE);
563 if (dst->is_not_ready == 0)
564 __SD_task_set_state(dst, SD_SCHEDULABLE);
566 /* __SD_print_dependencies(src);
567 __SD_print_dependencies(dst); */
571 * \brief Returns the user data associated with a dependency between two tasks
574 * \param dst a task depending on \a src
575 * \return the user data associated with this dependency (can be \c NULL)
576 * \see SD_task_dependency_add()
578 void *SD_task_dependency_get_data(SD_task_t src, SD_task_t dst)
585 SD_dependency_t dependency;
588 SD_CHECK_INIT_DONE();
589 xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
591 dynar = src->tasks_after;
592 length = xbt_dynar_length(dynar);
594 for (i = 0; i < length && !found; i++) {
595 xbt_dynar_get_cpy(dynar, i, &dependency);
596 found = (dependency->dst == dst);
599 THROW2(arg_error, 0, "No dependency found between task '%s' and '%s'",
600 SD_task_get_name(src), SD_task_get_name(dst));
601 return dependency->data;
604 /* temporary function for debugging */
605 static void __SD_print_watch_points(SD_task_t task)
607 static const int state_masks[] =
608 { SD_SCHEDULABLE, SD_SCHEDULED, SD_RUNNING, SD_RUNNABLE, SD_DONE,
611 static const char *state_names[] =
612 { "schedulable", "scheduled", "running", "runnable", "done",
617 INFO2("Task '%s' watch points (%x): ", SD_task_get_name(task),
621 for (i = 0; i < 5; i++) {
622 if (task->watch_points & state_masks[i])
623 INFO1("%s ", state_names[i]);
628 * \brief Adds a watch point to a task
630 * SD_simulate() will stop as soon as the \ref e_SD_task_state_t "state" of this
631 * task becomes the one given in argument. The
632 * watch point is then automatically removed.
635 * \param state the \ref e_SD_task_state_t "state" you want to watch
636 * (cannot be #SD_NOT_SCHEDULED)
637 * \see SD_task_unwatch()
639 void SD_task_watch(SD_task_t task, e_SD_task_state_t state)
641 SD_CHECK_INIT_DONE();
642 xbt_assert0(task != NULL, "Invalid parameter");
644 if (state & SD_NOT_SCHEDULED)
646 "Cannot add a watch point for state SD_NOT_SCHEDULED");
648 task->watch_points = task->watch_points | state;
649 /* __SD_print_watch_points(task); */
653 * \brief Removes a watch point from a task
656 * \param state the \ref e_SD_task_state_t "state" you no longer want to watch
657 * \see SD_task_watch()
659 void SD_task_unwatch(SD_task_t task, e_SD_task_state_t state)
661 SD_CHECK_INIT_DONE();
662 xbt_assert0(task != NULL, "Invalid parameter");
663 xbt_assert0(state != SD_NOT_SCHEDULED,
664 "SimDag error: Cannot have a watch point for state SD_NOT_SCHEDULED");
666 task->watch_points = task->watch_points & ~state;
667 /* __SD_print_watch_points(task); */
671 * \brief Returns an approximative estimation of the execution time of a task.
673 * The estimation is very approximative because the value returned is the time
674 * the task would take if it was executed now and if it was the only task.
676 * \param task the task to evaluate
677 * \param workstation_nb number of workstations on which the task would be executed
678 * \param workstation_list the workstations on which the task would be executed
679 * \param computation_amount computation amount for each workstation
680 * \param communication_amount communication amount between each pair of workstations
683 double SD_task_get_execution_time(SD_task_t task,
685 const SD_workstation_t *
687 const double *computation_amount,
688 const double *communication_amount)
690 double time, max_time = 0.0;
692 SD_CHECK_INIT_DONE();
693 xbt_assert0(task != NULL && workstation_nb > 0
694 && workstation_list != NULL, "Invalid parameter");
696 /* the task execution time is the maximum execution time of the parallel tasks */
698 for (i = 0; i < workstation_nb; i++) {
700 if (computation_amount != NULL)
702 SD_workstation_get_computation_time(workstation_list[i],
703 computation_amount[i]);
705 if (communication_amount != NULL)
706 for (j = 0; j < workstation_nb; j++) {
708 SD_route_get_communication_time(workstation_list[i],
710 communication_amount[i *
715 if (time > max_time) {
722 static XBT_INLINE void SD_task_do_schedule(SD_task_t task)
724 SD_CHECK_INIT_DONE();
726 if (!__SD_task_is_not_scheduled(task) && !__SD_task_is_schedulable(task))
727 THROW1(arg_error, 0, "Task '%s' has already been scheduled",
728 SD_task_get_name(task));
730 /* update the task state */
731 if (task->unsatisfied_dependencies == 0)
732 __SD_task_set_state(task, SD_RUNNABLE);
734 __SD_task_set_state(task, SD_SCHEDULED);
738 * \brief Schedules a task
740 * The task state must be #SD_NOT_SCHEDULED.
741 * Once scheduled, a task will be executed as soon as possible in SD_simulate(),
742 * i.e. when its dependencies are satisfied.
744 * \param task the task you want to schedule
745 * \param workstation_nb number of workstations on which the task will be executed
746 * \param workstation_list the workstations on which the task will be executed
747 * \param computation_amount computation amount for each workstation
748 * \param communication_amount communication amount between each pair of workstations
749 * \param rate task execution speed rate
750 * \see SD_task_unschedule()
752 void SD_task_schedule(SD_task_t task, int workstation_count,
753 const SD_workstation_t * workstation_list,
754 const double *computation_amount,
755 const double *communication_amount, double rate)
757 int communication_nb;
758 task->workstation_nb = 0;
760 xbt_assert0(workstation_count > 0, "workstation_nb must be positive");
762 task->workstation_nb = workstation_count;
765 if (computation_amount) {
766 task->computation_amount = xbt_new(double, workstation_count);
767 memcpy(task->computation_amount, computation_amount,
768 sizeof(double) * workstation_count);
770 task->computation_amount = NULL;
773 communication_nb = workstation_count * workstation_count;
774 if (communication_amount) {
775 task->communication_amount = xbt_new(double, communication_nb);
776 memcpy(task->communication_amount, communication_amount,
777 sizeof(double) * communication_nb);
779 task->communication_amount = NULL;
782 task->workstation_list = xbt_new(SD_workstation_t, workstation_count);
783 memcpy(task->workstation_list, workstation_list,
784 sizeof(SD_workstation_t) * workstation_count);
786 SD_task_do_schedule(task);
790 * \brief Unschedules a task
792 * The task state must be #SD_SCHEDULED, #SD_RUNNABLE, #SD_RUNNING or #SD_FAILED.
793 * If you call this function, the task state becomes #SD_NOT_SCHEDULED.
794 * Call SD_task_schedule() to schedule it again.
796 * \param task the task you want to unschedule
797 * \see SD_task_schedule()
799 void SD_task_unschedule(SD_task_t task)
801 SD_CHECK_INIT_DONE();
802 xbt_assert0(task != NULL, "Invalid parameter");
804 if (task->state_set != sd_global->scheduled_task_set &&
805 task->state_set != sd_global->runnable_task_set &&
806 task->state_set != sd_global->running_task_set &&
807 task->state_set != sd_global->failed_task_set)
809 "Task %s: the state must be SD_SCHEDULED, SD_RUNNABLE, SD_RUNNING or SD_FAILED",
810 SD_task_get_name(task));
812 if (__SD_task_is_scheduled_or_runnable(task) /* if the task is scheduled or runnable */
813 &&task->kind == SD_TASK_NOT_TYPED) /* Don't free scheduling data for typed tasks */
814 __SD_task_destroy_scheduling_data(task);
816 if (__SD_task_is_running(task)) /* the task should become SD_FAILED */
817 surf_workstation_model->action_cancel(task->surf_action);
819 if (task->unsatisfied_dependencies == 0)
820 __SD_task_set_state(task, SD_SCHEDULABLE);
822 __SD_task_set_state(task, SD_NOT_SCHEDULED);
824 task->remains = task->amount;
825 task->start_time = -1.0;
828 /* Destroys the data memorized by SD_task_schedule. Task state must be SD_SCHEDULED or SD_RUNNABLE.
830 static void __SD_task_destroy_scheduling_data(SD_task_t task)
832 SD_CHECK_INIT_DONE();
833 if (!__SD_task_is_scheduled_or_runnable(task)
834 && !__SD_task_is_in_fifo(task))
836 "Task '%s' must be SD_SCHEDULED, SD_RUNNABLE or SD_IN_FIFO",
837 SD_task_get_name(task));
839 xbt_free(task->computation_amount);
840 xbt_free(task->communication_amount);
841 task->computation_amount = task->communication_amount = NULL;
844 /* Runs a task. This function is directly called by __SD_task_try_to_run if the task
845 * doesn't have to wait in fifos. Otherwise, it is called by __SD_task_just_done when
846 * the task gets out of its fifos.
848 void __SD_task_really_run(SD_task_t task)
852 void **surf_workstations;
854 SD_CHECK_INIT_DONE();
855 xbt_assert0(task != NULL, "Invalid parameter");
856 xbt_assert2(__SD_task_is_runnable_or_in_fifo(task),
857 "Task '%s' is not runnable or in a fifo! Task state: %d",
858 SD_task_get_name(task), SD_task_get_state(task));
859 xbt_assert1(task->workstation_list != NULL,
860 "Task '%s': workstation_list is NULL!",
861 SD_task_get_name(task));
865 DEBUG1("Really running task '%s'", SD_task_get_name(task));
867 /* set this task as current task for the workstations in sequential mode */
868 for (i = 0; i < task->workstation_nb; i++) {
869 if (SD_workstation_get_access_mode(task->workstation_list[i]) ==
870 SD_WORKSTATION_SEQUENTIAL_ACCESS) {
871 task->workstation_list[i]->current_task = task;
872 xbt_assert0(__SD_workstation_is_busy(task->workstation_list[i]),
873 "The workstation should be busy now");
877 DEBUG1("Task '%s' set as current task for its workstations",
878 SD_task_get_name(task));
882 /* we have to create a Surf workstation array instead of the SimDag workstation array */
883 surf_workstations = xbt_new(void *, task->workstation_nb);
885 for (i = 0; i < task->workstation_nb; i++)
886 surf_workstations[i] = task->workstation_list[i]->surf_workstation;
888 /* 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 */
889 #define cost_or_zero(array,pos) ((array)?(array)[pos]:0.0)
891 task->surf_action = NULL;
892 if ((task->workstation_nb == 1)
893 && (cost_or_zero(task->communication_amount, 0) == 0.0)) {
895 surf_workstation_model->extension.
896 workstation.execute(surf_workstations[0],
897 cost_or_zero(task->computation_amount, 0));
898 } else if ((task->workstation_nb == 1)
899 && (cost_or_zero(task->computation_amount, 0) == 0.0)) {
902 surf_workstation_model->extension.
903 workstation.communicate(surf_workstations[0], surf_workstations[0],
904 cost_or_zero(task->communication_amount,
906 } else if ((task->workstation_nb == 2)
907 && (cost_or_zero(task->computation_amount, 0) == 0.0)
908 && (cost_or_zero(task->computation_amount, 1) == 0.0)) {
912 for (i = 0; i < task->workstation_nb * task->workstation_nb; i++) {
913 if (cost_or_zero(task->communication_amount, i) > 0.0) {
915 value = cost_or_zero(task->communication_amount, i);
920 surf_workstation_model->extension.
921 workstation.communicate(surf_workstations[0],
922 surf_workstations[1], value, task->rate);
927 if (!task->surf_action) {
928 double *computation_amount = xbt_new(double, task->workstation_nb);
929 double *communication_amount = xbt_new(double, task->workstation_nb *
930 task->workstation_nb);
932 memcpy(computation_amount, task->computation_amount, sizeof(double) *
933 task->workstation_nb);
934 memcpy(communication_amount, task->communication_amount,
935 sizeof(double) * task->workstation_nb * task->workstation_nb);
938 surf_workstation_model->extension.
939 workstation.execute_parallel_task(task->workstation_nb,
942 communication_amount,
943 task->amount, task->rate);
945 xbt_free(surf_workstations);
948 surf_workstation_model->action_data_set(task->surf_action, task);
950 DEBUG1("surf_action = %p", task->surf_action);
954 TRACE_surf_action(task->surf_action, task->category);
957 __SD_task_destroy_scheduling_data(task); /* now the scheduling data are not useful anymore */
958 __SD_task_set_state(task, SD_RUNNING);
959 xbt_assert2(__SD_task_is_running(task), "Bad state of task '%s': %d",
960 SD_task_get_name(task), SD_task_get_state(task));
964 /* Tries to run a task. This function is called by SD_simulate() when a scheduled task becomes SD_RUNNABLE
965 * (ie when its dependencies are satisfied).
966 * If one of the workstations where the task is scheduled on is busy (in sequential mode),
967 * the task doesn't start.
968 * Returns whether the task has started.
970 int __SD_task_try_to_run(SD_task_t task)
975 SD_workstation_t workstation;
977 SD_CHECK_INIT_DONE();
978 xbt_assert0(task != NULL, "Invalid parameter");
979 xbt_assert2(__SD_task_is_runnable(task),
980 "Task '%s' is not runnable! Task state: %d",
981 SD_task_get_name(task), SD_task_get_state(task));
984 for (i = 0; i < task->workstation_nb; i++) {
985 can_start = can_start &&
986 !__SD_workstation_is_busy(task->workstation_list[i]);
989 DEBUG2("Task '%s' can start: %d", SD_task_get_name(task), can_start);
991 if (!can_start) { /* if the task cannot start and is not in the fifos yet */
992 for (i = 0; i < task->workstation_nb; i++) {
993 workstation = task->workstation_list[i];
994 if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
995 DEBUG2("Pushing task '%s' in the fifo of workstation '%s'",
996 SD_task_get_name(task),
997 SD_workstation_get_name(workstation));
998 xbt_fifo_push(workstation->task_fifo, task);
1001 __SD_task_set_state(task, SD_IN_FIFO);
1002 xbt_assert2(__SD_task_is_in_fifo(task), "Bad state of task '%s': %d",
1003 SD_task_get_name(task), SD_task_get_state(task));
1004 DEBUG1("Task '%s' state is now SD_IN_FIFO", SD_task_get_name(task));
1006 __SD_task_really_run(task);
1012 /* This function is called by SD_simulate when a task is done.
1013 * It updates task->state and task->action and executes if necessary the tasks
1014 * which were waiting in fifos for the end of `task'
1016 void __SD_task_just_done(SD_task_t task)
1019 SD_workstation_t workstation;
1021 SD_task_t candidate;
1022 int candidate_nb = 0;
1023 int candidate_capacity = 8;
1024 SD_task_t *candidates;
1027 SD_CHECK_INIT_DONE();
1028 xbt_assert0(task != NULL, "Invalid parameter");
1029 xbt_assert1(__SD_task_is_running(task),
1030 "The task must be running! Task state: %d",
1031 SD_task_get_state(task));
1032 xbt_assert1(task->workstation_list != NULL,
1033 "Task '%s': workstation_list is NULL!",
1034 SD_task_get_name(task));
1037 candidates = xbt_new(SD_task_t, 8);
1039 __SD_task_set_state(task, SD_DONE);
1040 surf_workstation_model->action_unref(task->surf_action);
1041 task->surf_action = NULL;
1043 DEBUG0("Looking for candidates");
1045 /* if the task was executed on sequential workstations,
1046 maybe we can execute the next task of the fifo for each workstation */
1047 for (i = 0; i < task->workstation_nb; i++) {
1048 workstation = task->workstation_list[i];
1049 DEBUG2("Workstation '%s': access_mode = %d",
1050 SD_workstation_get_name(workstation), workstation->access_mode);
1051 if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
1052 xbt_assert1(workstation->task_fifo != NULL,
1053 "Workstation '%s' has sequential access but no fifo!",
1054 SD_workstation_get_name(workstation));
1055 xbt_assert2(workstation->current_task =
1056 task, "Workstation '%s': current task should be '%s'",
1057 SD_workstation_get_name(workstation),
1058 SD_task_get_name(task));
1060 /* the task is over so we can release the workstation */
1061 workstation->current_task = NULL;
1063 DEBUG0("Getting candidate in fifo");
1065 xbt_fifo_get_item_content(xbt_fifo_get_first_item
1066 (workstation->task_fifo));
1068 if (candidate != NULL) {
1069 DEBUG1("Candidate: '%s'", SD_task_get_name(candidate));
1070 xbt_assert2(__SD_task_is_in_fifo(candidate),
1071 "Bad state of candidate '%s': %d",
1072 SD_task_get_name(candidate),
1073 SD_task_get_state(candidate));
1076 DEBUG1("Candidate in fifo: %p", candidate);
1078 /* if there was a task waiting for my place */
1079 if (candidate != NULL) {
1080 /* Unfortunately, we are not sure yet that we can execute the task now,
1081 because the task can be waiting more deeply in some other workstation's fifos...
1082 So we memorize all candidate tasks, and then we will check for each candidate
1083 whether or not all its workstations are available. */
1085 /* realloc if necessary */
1086 if (candidate_nb == candidate_capacity) {
1087 candidate_capacity *= 2;
1089 xbt_realloc(candidates,
1090 sizeof(SD_task_t) * candidate_capacity);
1093 /* register the candidate */
1094 candidates[candidate_nb++] = candidate;
1095 candidate->fifo_checked = 0;
1100 DEBUG1("Candidates found: %d", candidate_nb);
1102 /* now we check every candidate task */
1103 for (i = 0; i < candidate_nb; i++) {
1104 candidate = candidates[i];
1106 if (candidate->fifo_checked) {
1107 continue; /* we have already evaluated that task */
1110 xbt_assert2(__SD_task_is_in_fifo(candidate),
1111 "Bad state of candidate '%s': %d",
1112 SD_task_get_name(candidate), SD_task_get_state(candidate));
1114 for (j = 0; j < candidate->workstation_nb && can_start; j++) {
1115 workstation = candidate->workstation_list[j];
1117 /* I can start on this workstation if the workstation is shared
1118 or if I am the first task in the fifo */
1119 can_start = workstation->access_mode == SD_WORKSTATION_SHARED_ACCESS
1121 xbt_fifo_get_item_content(xbt_fifo_get_first_item
1122 (workstation->task_fifo));
1125 DEBUG2("Candidate '%s' can start: %d", SD_task_get_name(candidate),
1128 /* now we are sure that I can start! */
1130 for (j = 0; j < candidate->workstation_nb && can_start; j++) {
1131 workstation = candidate->workstation_list[j];
1133 /* update the fifo */
1134 if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
1135 candidate = xbt_fifo_shift(workstation->task_fifo); /* the return value is stored just for debugging */
1136 DEBUG1("Head of the fifo: '%s'",
1138 NULL) ? SD_task_get_name(candidate) : "NULL");
1139 xbt_assert0(candidate == candidates[i],
1140 "Error in __SD_task_just_done: bad first task in the fifo");
1142 } /* for each workstation */
1144 /* finally execute the task */
1145 DEBUG2("Task '%s' state: %d", SD_task_get_name(candidate),
1146 SD_task_get_state(candidate));
1147 __SD_task_really_run(candidate);
1150 ("Calling __SD_task_is_running: task '%s', state set: %p, running_task_set: %p, is running: %d",
1151 SD_task_get_name(candidate), candidate->state_set,
1152 sd_global->running_task_set, __SD_task_is_running(candidate));
1153 xbt_assert2(__SD_task_is_running(candidate),
1154 "Bad state of task '%s': %d",
1155 SD_task_get_name(candidate),
1156 SD_task_get_state(candidate));
1157 DEBUG0("Okay, the task is running.");
1160 candidate->fifo_checked = 1;
1161 } /* for each candidate */
1163 xbt_free(candidates);
1166 /* Remove all dependencies associated with a task. This function is called when the task is destroyed.
1168 static void __SD_task_remove_dependencies(SD_task_t task)
1170 /* we must destroy the dependencies carefuly (with SD_dependency_remove)
1171 because each one is stored twice */
1172 SD_dependency_t dependency;
1173 while (xbt_dynar_length(task->tasks_before) > 0) {
1174 xbt_dynar_get_cpy(task->tasks_before, 0, &dependency);
1175 SD_task_dependency_remove(dependency->src, dependency->dst);
1178 while (xbt_dynar_length(task->tasks_after) > 0) {
1179 xbt_dynar_get_cpy(task->tasks_after, 0, &dependency);
1180 SD_task_dependency_remove(dependency->src, dependency->dst);
1185 * \brief Returns the start time of a task
1187 * The task state must be SD_RUNNING, SD_DONE or SD_FAILED.
1189 * \param task: a task
1190 * \return the start time of this task
1192 double SD_task_get_start_time(SD_task_t task)
1194 SD_CHECK_INIT_DONE();
1195 xbt_assert0(task != NULL, "Invalid parameter");
1196 if (task->surf_action)
1197 return surf_workstation_model->
1198 action_get_start_time(task->surf_action);
1200 return task->start_time;
1204 * \brief Returns the finish time of a task
1206 * The task state must be SD_RUNNING, SD_DONE or SD_FAILED.
1207 * If the state is not completed yet, the returned value is an
1208 * estimation of the task finish time. This value can fluctuate
1209 * until the task is completed.
1211 * \param task: a task
1212 * \return the start time of this task
1214 double SD_task_get_finish_time(SD_task_t task)
1216 SD_CHECK_INIT_DONE();
1217 xbt_assert0(task != NULL, "Invalid parameter");
1219 if (task->surf_action) /* should never happen as actions are destroyed right after their completion */
1220 return surf_workstation_model->
1221 action_get_finish_time(task->surf_action);
1223 return task->finish_time;
1227 * \brief Destroys a task.
1229 * The user data (if any) should have been destroyed first.
1231 * \param task the task you want to destroy
1232 * \see SD_task_create()
1234 void SD_task_destroy(SD_task_t task)
1236 SD_CHECK_INIT_DONE();
1237 xbt_assert0(task != NULL, "Invalid parameter");
1239 DEBUG1("Destroying task %s...", SD_task_get_name(task));
1241 __SD_task_remove_dependencies(task);
1242 /* if the task was scheduled or runnable we have to free the scheduling parameters */
1243 if (__SD_task_is_scheduled_or_runnable(task))
1244 __SD_task_destroy_scheduling_data(task);
1245 xbt_swag_remove(task, task->state_set);
1247 if (task->name != NULL)
1248 xbt_free(task->name);
1250 if (task->surf_action != NULL)
1251 surf_workstation_model->action_unref(task->surf_action);
1253 if (task->workstation_list != NULL)
1254 xbt_free(task->workstation_list);
1256 if (task->communication_amount)
1257 xbt_free(task->communication_amount);
1259 if (task->computation_amount)
1260 xbt_free(task->computation_amount);
1263 TRACE_sd_task_destroy(task);
1266 xbt_dynar_free(&task->tasks_before);
1267 xbt_dynar_free(&task->tasks_after);
1270 sd_global->task_number--;
1272 DEBUG0("Task destroyed.");
1276 static XBT_INLINE SD_task_t SD_task_create_sized(const char *name,
1277 void *data, double amount,
1280 SD_task_t task = SD_task_create(name, data, amount);
1281 task->communication_amount = xbt_new0(double, ws_count * ws_count);
1282 task->computation_amount = xbt_new0(double, ws_count);
1283 task->workstation_nb = ws_count;
1284 task->workstation_list = xbt_new0(SD_workstation_t, ws_count);
1288 /** @brief create a end-to-end communication task that can then be auto-scheduled
1290 * Auto-scheduling mean that the task can be used with SD_task_schedulev(). This
1291 * allows to specify the task costs at creation, and decorelate them from the
1292 * scheduling process where you just specify which resource should deliver the
1295 * A end-to-end communication must be scheduled on 2 hosts, and the amount
1296 * specified at creation is sent from hosts[0] to hosts[1].
1298 SD_task_t SD_task_create_comm_e2e(const char *name, void *data,
1301 SD_task_t res = SD_task_create_sized(name, data, amount, 2);
1302 res->communication_amount[2] = amount;
1303 res->kind = SD_TASK_COMM_E2E;
1307 /** @brief create a sequential computation task that can then be auto-scheduled
1309 * Auto-scheduling mean that the task can be used with SD_task_schedulev(). This
1310 * allows to specify the task costs at creation, and decorelate them from the
1311 * scheduling process where you just specify which resource should deliver the
1314 * A sequential computation must be scheduled on 1 host, and the amount
1315 * specified at creation to be run on hosts[0].
1317 SD_task_t SD_task_create_comp_seq(const char *name, void *data,
1320 SD_task_t res = SD_task_create_sized(name, data, amount, 1);
1321 res->computation_amount[0] = amount;
1322 res->kind = SD_TASK_COMP_SEQ;
1326 /** @brief Auto-schedules a task.
1328 * Auto-scheduling mean that the task can be used with SD_task_schedulev(). This
1329 * allows to specify the task costs at creation, and decorelate them from the
1330 * scheduling process where you just specify which resource should deliver the
1333 * To be auto-schedulable, a task must be created with SD_task_create_comm_e2e() or
1334 * SD_task_create_comp_seq(). Check their definitions for the exact semantic of each
1338 * We should create tasks kind for the following categories:
1339 * - Point to point communication (done)
1340 * - Sequential computation (done)
1341 * - group communication (redistribution, several kinds)
1342 * - parallel tasks with no internal communication (one kind per speedup model such as amdal)
1343 * - idem+ internal communication. Task type not enough since we cannot store comm cost alongside to comp one)
1345 void SD_task_schedulev(SD_task_t task, int count,
1346 const SD_workstation_t * list)
1349 SD_dependency_t dep;
1351 xbt_assert1(task->kind != 0,
1352 "Task %s is not typed. Cannot automatically schedule it.",
1353 SD_task_get_name(task));
1354 switch (task->kind) {
1355 case SD_TASK_COMM_E2E:
1356 case SD_TASK_COMP_SEQ:
1357 xbt_assert(task->workstation_nb == count);
1358 for (i = 0; i < count; i++)
1359 task->workstation_list[i] = list[i];
1360 SD_task_do_schedule(task);
1363 xbt_die(bprintf("Kind of task %s not supported by SD_task_schedulev()",
1364 SD_task_get_name(task)));
1366 if (task->kind == SD_TASK_COMM_E2E) {
1367 VERB4("Schedule comm task %s between %s -> %s. It costs %.f bytes",
1368 SD_task_get_name(task),
1369 SD_workstation_get_name(task->workstation_list[0]),
1370 SD_workstation_get_name(task->workstation_list[1]),
1371 task->communication_amount[2]);
1374 /* Iterate over all childs and parent being COMM_E2E to say where I am located (and start them if runnable) */
1375 if (task->kind == SD_TASK_COMP_SEQ) {
1376 VERB3("Schedule computation task %s on %s. It costs %.f flops",
1377 SD_task_get_name(task),
1378 SD_workstation_get_name(task->workstation_list[0]),
1379 task->computation_amount[0]);
1381 xbt_dynar_foreach(task->tasks_before, cpt, dep) {
1382 SD_task_t before = dep->src;
1383 if (before->kind == SD_TASK_COMM_E2E) {
1384 before->workstation_list[1] = task->workstation_list[0];
1386 if (before->workstation_list[0] &&
1387 (__SD_task_is_schedulable(before)
1388 || __SD_task_is_not_scheduled(before))) {
1389 SD_task_do_schedule(before);
1391 ("Auto-Schedule comm task %s between %s -> %s. It costs %.f bytes",
1392 SD_task_get_name(before),
1393 SD_workstation_get_name(before->workstation_list[0]),
1394 SD_workstation_get_name(before->workstation_list[1]),
1395 before->communication_amount[2]);
1399 xbt_dynar_foreach(task->tasks_after, cpt, dep) {
1400 SD_task_t after = dep->dst;
1401 if (after->kind == SD_TASK_COMM_E2E) {
1402 after->workstation_list[0] = task->workstation_list[0];
1403 //J-N : Why did you comment on these line (this comment add a bug I think)?
1404 if (after->workstation_list[1]
1405 && (__SD_task_is_not_scheduled(after)
1406 || __SD_task_is_schedulable(after))) {
1407 SD_task_do_schedule(after);
1409 ("Auto-Schedule comm task %s between %s -> %s. It costs %.f bytes",
1410 SD_task_get_name(after),
1411 SD_workstation_get_name(after->workstation_list[0]),
1412 SD_workstation_get_name(after->workstation_list[1]),
1413 after->communication_amount[2]);
1421 /** @brief autoschedule a task on a list of workstations
1423 * This function is very similar to SD_task_schedulev(),
1424 * but takes the list of workstations to schedule onto as separate parameters.
1425 * It builds a proper vector of workstations and then call SD_task_schedulev()
1427 void SD_task_schedulel(SD_task_t task, int count, ...)
1430 SD_workstation_t *list = xbt_new(SD_workstation_t, count);
1432 va_start(ap, count);
1433 for (i = 0; i < count; i++) {
1434 list[i] = va_arg(ap, SD_workstation_t);
1437 SD_task_schedulev(task, count, list);