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)
174 SD_CHECK_INIT_DONE();
175 xbt_assert(task != NULL, "Invalid parameter");
180 * \brief Sets the user data of a task
182 * The new data can be \c NULL. The old data should have been freed first
183 * if it was not \c NULL.
186 * \param data the new data you want to associate with this task
187 * \see SD_task_get_data()
189 void SD_task_set_data(SD_task_t task, void *data)
191 SD_CHECK_INIT_DONE();
192 xbt_assert(task != NULL, "Invalid parameter");
197 * \brief Returns the state of a task
200 * \return the current \ref e_SD_task_state_t "state" of this task:
201 * #SD_NOT_SCHEDULED, #SD_SCHEDULED, #SD_RUNNABLE, #SD_RUNNING, #SD_DONE or #SD_FAILED
202 * \see e_SD_task_state_t
204 e_SD_task_state_t SD_task_get_state(SD_task_t task)
206 SD_CHECK_INIT_DONE();
207 xbt_assert(task != NULL, "Invalid parameter");
211 /* Changes the state of a task. Updates the swags and the flag sd_global->watch_point_reached.
213 void __SD_task_set_state(SD_task_t task, e_SD_task_state_t new_state)
215 xbt_swag_remove(task, task->state_set);
217 case SD_NOT_SCHEDULED:
218 task->state_set = sd_global->not_scheduled_task_set;
221 task->state_set = sd_global->schedulable_task_set;
224 task->state_set = sd_global->scheduled_task_set;
227 task->state_set = sd_global->runnable_task_set;
230 task->state_set = sd_global->in_fifo_task_set;
233 task->state_set = sd_global->running_task_set;
235 surf_workstation_model->action_get_start_time(task->surf_action);
238 task->state_set = sd_global->done_task_set;
240 surf_workstation_model->action_get_finish_time(task->surf_action);
243 jedule_log_sd_event(task);
247 task->state_set = sd_global->failed_task_set;
250 xbt_die( "Invalid state");
252 xbt_swag_insert(task, task->state_set);
253 task->state = new_state;
255 if (task->watch_points & new_state) {
256 XBT_VERB("Watch point reached with task '%s'!", SD_task_get_name(task));
257 sd_global->watch_point_reached = 1;
258 SD_task_unwatch(task, new_state); /* remove the watch point */
263 * \brief Returns the name of a task
266 * \return the name of this task (can be \c NULL)
268 const char *SD_task_get_name(SD_task_t task)
270 SD_CHECK_INIT_DONE();
271 xbt_assert(task != NULL, "Invalid parameter");
275 /** @brief Allows to change the name of a task */
276 void SD_task_set_name(SD_task_t task, const char *name)
278 xbt_free(task->name);
279 task->name = xbt_strdup(name);
282 /** @brief Returns the dynar of the parents of a task
285 * \return a newly allocated dynar comprising the parents of this task
288 xbt_dynar_t SD_task_get_parents(SD_task_t task)
293 SD_CHECK_INIT_DONE();
294 xbt_assert(task != NULL, "Invalid parameter");
296 parents = xbt_dynar_new(sizeof(SD_task_t), NULL);
297 xbt_dynar_foreach(task->tasks_before, i, dep) {
298 xbt_dynar_push(parents, &(dep->src));
303 /** @brief Returns the dynar of the parents of a task
306 * \return a newly allocated dynar comprising the parents of this task
308 xbt_dynar_t SD_task_get_children(SD_task_t task)
311 xbt_dynar_t children;
313 SD_CHECK_INIT_DONE();
314 xbt_assert(task != NULL, "Invalid parameter");
316 children = xbt_dynar_new(sizeof(SD_task_t), NULL);
317 xbt_dynar_foreach(task->tasks_after, i, dep) {
318 xbt_dynar_push(children, &(dep->dst));
324 * \brief Returns the amount of workstations involved in a task
326 * Only call this on already scheduled tasks!
329 int SD_task_get_workstation_count(SD_task_t task)
331 SD_CHECK_INIT_DONE();
332 xbt_assert(task != NULL, "Invalid parameter");
333 //xbt_assert(task->state_set != sd_global->scheduled_task_set,
334 // "Unscheduled task %s", task->name);
335 return task->workstation_nb;
339 * \brief Returns the list of workstations involved in a task
341 * Only call this on already scheduled tasks!
344 SD_workstation_t *SD_task_get_workstation_list(SD_task_t task)
346 SD_CHECK_INIT_DONE();
347 xbt_assert(task != NULL, "Invalid parameter");
348 //xbt_assert(task->state_set != sd_global->scheduled_task_set,
349 // "Unscheduled task %s", task->name);
350 return task->workstation_list;
354 * \brief Returns the total amount of work contained in a task
357 * \return the total amount of work (computation or data transfer) for this task
358 * \see SD_task_get_remaining_amount()
360 double SD_task_get_amount(SD_task_t task)
362 SD_CHECK_INIT_DONE();
363 xbt_assert(task != NULL, "Invalid parameter");
368 * \brief Returns the remaining amount work to do till the completion of a task
371 * \return the remaining amount of work (computation or data transfer) of this task
372 * \see SD_task_get_amount()
374 double SD_task_get_remaining_amount(SD_task_t task)
376 SD_CHECK_INIT_DONE();
377 xbt_assert(task != NULL, "Invalid parameter");
379 if (task->surf_action)
380 return surf_workstation_model->get_remains(task->surf_action);
382 return task->remains;
385 int SD_task_get_kind(SD_task_t task)
390 /** @brief Displays debugging informations about a task */
391 void SD_task_dump(SD_task_t task)
393 unsigned int counter;
394 SD_dependency_t dependency;
397 XBT_INFO("Displaying task %s", SD_task_get_name(task));
398 statename = bprintf("%s %s %s %s %s %s %s %s",
399 (task->state & SD_NOT_SCHEDULED ? "not scheduled" :
401 (task->state & SD_SCHEDULABLE ? "schedulable" : ""),
402 (task->state & SD_SCHEDULED ? "scheduled" : ""),
403 (task->state & SD_RUNNABLE ? "runnable" :
405 (task->state & SD_IN_FIFO ? "in fifo" : ""),
406 (task->state & SD_RUNNING ? "running" : ""),
407 (task->state & SD_DONE ? "done" : ""),
408 (task->state & SD_FAILED ? "failed" : ""));
409 XBT_INFO(" - state: %s", statename);
412 if (task->kind != 0) {
413 switch (task->kind) {
414 case SD_TASK_COMM_E2E:
415 XBT_INFO(" - kind: end-to-end communication");
417 case SD_TASK_COMP_SEQ:
418 XBT_INFO(" - kind: sequential computation");
421 XBT_INFO(" - (unknown kind %d)", task->kind);
424 XBT_INFO(" - amount: %.0f", SD_task_get_amount(task));
425 XBT_INFO(" - Dependencies to satisfy: %d", task->unsatisfied_dependencies);
426 if (xbt_dynar_length(task->tasks_before)) {
427 XBT_INFO(" - pre-dependencies:");
428 xbt_dynar_foreach(task->tasks_before, counter, dependency) {
429 XBT_INFO(" %s", SD_task_get_name(dependency->src));
432 if (xbt_dynar_length(task->tasks_after)) {
433 XBT_INFO(" - post-dependencies:");
434 xbt_dynar_foreach(task->tasks_after, counter, dependency) {
435 XBT_INFO(" %s", SD_task_get_name(dependency->dst));
440 /** @brief Dumps the task in dotty formalism into the FILE* passed as second argument */
441 void SD_task_dotty(SD_task_t task, void *out)
443 unsigned int counter;
444 SD_dependency_t dependency;
445 fprintf(out, " T%p [label=\"%.20s\"", task, task->name);
446 switch (task->kind) {
447 case SD_TASK_COMM_E2E:
448 fprintf(out, ", shape=box");
450 case SD_TASK_COMP_SEQ:
451 fprintf(out, ", shape=circle");
454 xbt_die("Unknown task type!");
456 fprintf(out, "];\n");
457 xbt_dynar_foreach(task->tasks_before, counter, dependency) {
458 fprintf(out, " T%p -> T%p;\n", dependency->src, dependency->dst);
462 /* Destroys a dependency between two tasks.
464 static void __SD_task_dependency_destroy(void *dependency)
466 if (((SD_dependency_t) dependency)->name != NULL)
467 xbt_free(((SD_dependency_t) dependency)->name);
468 xbt_free(dependency);
472 * \brief Adds a dependency between two tasks
474 * \a dst will depend on \a src, ie \a dst will not start before \a src is finished.
475 * Their \ref e_SD_task_state_t "state" must be #SD_NOT_SCHEDULED, #SD_SCHEDULED or #SD_RUNNABLE.
477 * \param name the name of the new dependency (can be \c NULL)
478 * \param data the user data you want to associate with this dependency (can be \c NULL)
479 * \param src the task which must be executed first
480 * \param dst the task you want to make depend on \a src
481 * \see SD_task_dependency_remove()
483 void SD_task_dependency_add(const char *name, void *data, SD_task_t src,
490 SD_dependency_t dependency;
492 SD_CHECK_INIT_DONE();
493 xbt_assert(src != NULL && dst != NULL, "Invalid parameter");
495 dynar = src->tasks_after;
496 length = xbt_dynar_length(dynar);
500 "Cannot add a dependency between task '%s' and itself",
501 SD_task_get_name(src));
503 if (!__SD_task_is_not_scheduled(src) && !__SD_task_is_schedulable(src)
504 && !__SD_task_is_scheduled_or_runnable(src))
506 "Task '%s' must be SD_NOT_SCHEDULED, SD_SCHEDULABLE, SD_SCHEDULED or SD_RUNNABLE",
507 SD_task_get_name(src));
509 if (!__SD_task_is_not_scheduled(dst) && !__SD_task_is_schedulable(dst)
510 && !__SD_task_is_scheduled_or_runnable(dst))
512 "Task '%s' must be SD_NOT_SCHEDULED, SD_SCHEDULABLE, SD_SCHEDULED or SD_RUNNABLE",
513 SD_task_get_name(dst));
515 XBT_DEBUG("SD_task_dependency_add: src = %s, dst = %s",
516 SD_task_get_name(src), SD_task_get_name(dst));
517 for (i = 0; i < length && !found; i++) {
518 xbt_dynar_get_cpy(dynar, i, &dependency);
519 found = (dependency->dst == dst);
520 XBT_DEBUG("Dependency %d: dependency->dst = %s", i,
521 SD_task_get_name(dependency->dst));
526 "A dependency already exists between task '%s' and task '%s'",
527 SD_task_get_name(src), SD_task_get_name(dst));
529 dependency = xbt_new(s_SD_dependency_t, 1);
531 dependency->name = xbt_strdup(name); /* xbt_strdup is cleaver enough to deal with NULL args itself */
532 dependency->data = data;
533 dependency->src = src;
534 dependency->dst = dst;
536 /* src must be executed before dst */
537 xbt_dynar_push(src->tasks_after, &dependency);
538 xbt_dynar_push(dst->tasks_before, &dependency);
540 dst->unsatisfied_dependencies++;
543 /* if the task was runnable, then dst->tasks_before is not empty anymore,
544 so we must go back to state SD_SCHEDULED */
545 if (__SD_task_is_runnable(dst)) {
547 ("SD_task_dependency_add: %s was runnable and becomes scheduled!",
548 SD_task_get_name(dst));
549 __SD_task_set_state(dst, SD_SCHEDULED);
552 /* __SD_print_dependencies(src);
553 __SD_print_dependencies(dst); */
557 * \brief Indacates whether there is a dependency between two tasks.
560 * \param dst a task depending on \a src
562 * If src is NULL, checks whether dst has any pre-dependency.
563 * If dst is NULL, checks whether src has any post-dependency.
565 int SD_task_dependency_exists(SD_task_t src, SD_task_t dst)
567 unsigned int counter;
568 SD_dependency_t dependency;
570 SD_CHECK_INIT_DONE();
571 xbt_assert(src != NULL
573 "Invalid parameter: both src and dst are NULL");
577 xbt_dynar_foreach(src->tasks_after, counter, dependency) {
578 if (dependency->dst == dst)
582 return xbt_dynar_length(src->tasks_after);
585 return xbt_dynar_length(dst->tasks_before);
591 * \brief Remove a dependency between two tasks
594 * \param dst a task depending on \a src
595 * \see SD_task_dependency_add()
597 void SD_task_dependency_remove(SD_task_t src, SD_task_t dst)
604 SD_dependency_t dependency;
606 SD_CHECK_INIT_DONE();
607 xbt_assert(src != NULL && dst != NULL, "Invalid parameter");
609 /* remove the dependency from src->tasks_after */
610 dynar = src->tasks_after;
611 length = xbt_dynar_length(dynar);
613 for (i = 0; i < length && !found; i++) {
614 xbt_dynar_get_cpy(dynar, i, &dependency);
615 if (dependency->dst == dst) {
616 xbt_dynar_remove_at(dynar, i, NULL);
622 "No dependency found between task '%s' and '%s': task '%s' is not a successor of task '%s'",
623 SD_task_get_name(src), SD_task_get_name(dst),
624 SD_task_get_name(dst), SD_task_get_name(src));
626 /* remove the dependency from dst->tasks_before */
627 dynar = dst->tasks_before;
628 length = xbt_dynar_length(dynar);
631 for (i = 0; i < length && !found; i++) {
632 xbt_dynar_get_cpy(dynar, i, &dependency);
633 if (dependency->src == src) {
634 xbt_dynar_remove_at(dynar, i, NULL);
635 __SD_task_dependency_destroy(dependency);
636 dst->unsatisfied_dependencies--;
641 /* should never happen... */
643 "SimDag error: task '%s' is a successor of '%s' but task '%s' is not a predecessor of task '%s'",
644 SD_task_get_name(dst), SD_task_get_name(src),
645 SD_task_get_name(src), SD_task_get_name(dst));
647 /* if the task was scheduled and dst->tasks_before is empty now, we can make it runnable */
649 if (dst->unsatisfied_dependencies == 0) {
650 if (__SD_task_is_scheduled(dst))
651 __SD_task_set_state(dst, SD_RUNNABLE);
653 __SD_task_set_state(dst, SD_SCHEDULABLE);
656 if (dst->is_not_ready == 0)
657 __SD_task_set_state(dst, SD_SCHEDULABLE);
659 /* __SD_print_dependencies(src);
660 __SD_print_dependencies(dst); */
664 * \brief Returns the user data associated with a dependency between two tasks
667 * \param dst a task depending on \a src
668 * \return the user data associated with this dependency (can be \c NULL)
669 * \see SD_task_dependency_add()
671 void *SD_task_dependency_get_data(SD_task_t src, SD_task_t dst)
678 SD_dependency_t dependency;
681 SD_CHECK_INIT_DONE();
682 xbt_assert(src != NULL && dst != NULL, "Invalid parameter");
684 dynar = src->tasks_after;
685 length = xbt_dynar_length(dynar);
687 for (i = 0; i < length && !found; i++) {
688 xbt_dynar_get_cpy(dynar, i, &dependency);
689 found = (dependency->dst == dst);
692 THROWF(arg_error, 0, "No dependency found between task '%s' and '%s'",
693 SD_task_get_name(src), SD_task_get_name(dst));
694 return dependency->data;
697 /* temporary function for debugging */
698 static void __SD_print_watch_points(SD_task_t task)
700 static const int state_masks[] =
701 { SD_SCHEDULABLE, SD_SCHEDULED, SD_RUNNING, SD_RUNNABLE, SD_DONE,
704 static const char *state_names[] =
705 { "schedulable", "scheduled", "running", "runnable", "done",
710 XBT_INFO("Task '%s' watch points (%x): ", SD_task_get_name(task),
714 for (i = 0; i < 5; i++) {
715 if (task->watch_points & state_masks[i])
716 XBT_INFO("%s ", state_names[i]);
721 * \brief Adds a watch point to a task
723 * SD_simulate() will stop as soon as the \ref e_SD_task_state_t "state" of this
724 * task becomes the one given in argument. The
725 * watch point is then automatically removed.
728 * \param state the \ref e_SD_task_state_t "state" you want to watch
729 * (cannot be #SD_NOT_SCHEDULED)
730 * \see SD_task_unwatch()
732 void SD_task_watch(SD_task_t task, e_SD_task_state_t state)
734 SD_CHECK_INIT_DONE();
735 xbt_assert(task != NULL, "Invalid parameter");
737 if (state & SD_NOT_SCHEDULED)
739 "Cannot add a watch point for state SD_NOT_SCHEDULED");
741 task->watch_points = task->watch_points | state;
742 /* __SD_print_watch_points(task); */
746 * \brief Removes a watch point from a task
749 * \param state the \ref e_SD_task_state_t "state" you no longer want to watch
750 * \see SD_task_watch()
752 void SD_task_unwatch(SD_task_t task, e_SD_task_state_t state)
754 SD_CHECK_INIT_DONE();
755 xbt_assert(task != NULL, "Invalid parameter");
756 xbt_assert(state != SD_NOT_SCHEDULED,
757 "SimDag error: Cannot have a watch point for state SD_NOT_SCHEDULED");
759 task->watch_points = task->watch_points & ~state;
760 /* __SD_print_watch_points(task); */
764 * \brief Returns an approximative estimation of the execution time of a task.
766 * The estimation is very approximative because the value returned is the time
767 * the task would take if it was executed now and if it was the only task.
769 * \param task the task to evaluate
770 * \param workstation_nb number of workstations on which the task would be executed
771 * \param workstation_list the workstations on which the task would be executed
772 * \param computation_amount computation amount for each workstation
773 * \param communication_amount communication amount between each pair of workstations
776 double SD_task_get_execution_time(SD_task_t task,
778 const SD_workstation_t *
780 const double *computation_amount,
781 const double *communication_amount)
783 double time, max_time = 0.0;
785 SD_CHECK_INIT_DONE();
786 xbt_assert(task != NULL && workstation_nb > 0
787 && workstation_list != NULL, "Invalid parameter");
789 /* the task execution time is the maximum execution time of the parallel tasks */
791 for (i = 0; i < workstation_nb; i++) {
793 if (computation_amount != NULL)
795 SD_workstation_get_computation_time(workstation_list[i],
796 computation_amount[i]);
798 if (communication_amount != NULL)
799 for (j = 0; j < workstation_nb; j++) {
801 SD_route_get_communication_time(workstation_list[i],
803 communication_amount[i *
808 if (time > max_time) {
815 static XBT_INLINE void SD_task_do_schedule(SD_task_t task)
817 SD_CHECK_INIT_DONE();
819 if (!__SD_task_is_not_scheduled(task) && !__SD_task_is_schedulable(task))
820 THROWF(arg_error, 0, "Task '%s' has already been scheduled",
821 SD_task_get_name(task));
823 /* update the task state */
824 if (task->unsatisfied_dependencies == 0)
825 __SD_task_set_state(task, SD_RUNNABLE);
827 __SD_task_set_state(task, SD_SCHEDULED);
831 * \brief Schedules a task
833 * The task state must be #SD_NOT_SCHEDULED.
834 * Once scheduled, a task will be executed as soon as possible in SD_simulate(),
835 * i.e. when its dependencies are satisfied.
837 * \param task the task you want to schedule
838 * \param workstation_count number of workstations on which the task will be executed
839 * \param workstation_list the workstations on which the task will be executed
840 * \param computation_amount computation amount for each workstation
841 * \param communication_amount communication amount between each pair of workstations
842 * \param rate task execution speed rate
843 * \see SD_task_unschedule()
845 void SD_task_schedule(SD_task_t task, int workstation_count,
846 const SD_workstation_t * workstation_list,
847 const double *computation_amount,
848 const double *communication_amount, double rate)
850 int communication_nb;
851 task->workstation_nb = 0;
853 xbt_assert(workstation_count > 0, "workstation_nb must be positive");
855 task->workstation_nb = workstation_count;
858 if (computation_amount) {
859 task->computation_amount = xbt_realloc(task->computation_amount,
860 sizeof(double) * workstation_count);
861 memcpy(task->computation_amount, computation_amount,
862 sizeof(double) * workstation_count);
864 xbt_free(task->computation_amount);
865 task->computation_amount = NULL;
868 communication_nb = workstation_count * workstation_count;
869 if (communication_amount) {
870 task->communication_amount = xbt_realloc(task->communication_amount,
871 sizeof(double) * communication_nb);
872 memcpy(task->communication_amount, communication_amount,
873 sizeof(double) * communication_nb);
875 xbt_free(task->communication_amount);
876 task->communication_amount = NULL;
879 task->workstation_list =
880 xbt_realloc(task->workstation_list,
881 sizeof(SD_workstation_t) * workstation_count);
882 memcpy(task->workstation_list, workstation_list,
883 sizeof(SD_workstation_t) * workstation_count);
885 SD_task_do_schedule(task);
889 * \brief Unschedules a task
891 * The task state must be #SD_SCHEDULED, #SD_RUNNABLE, #SD_RUNNING or #SD_FAILED.
892 * If you call this function, the task state becomes #SD_NOT_SCHEDULED.
893 * Call SD_task_schedule() to schedule it again.
895 * \param task the task you want to unschedule
896 * \see SD_task_schedule()
898 void SD_task_unschedule(SD_task_t task)
900 SD_CHECK_INIT_DONE();
901 xbt_assert(task != NULL, "Invalid parameter");
903 if (task->state_set != sd_global->scheduled_task_set &&
904 task->state_set != sd_global->runnable_task_set &&
905 task->state_set != sd_global->running_task_set &&
906 task->state_set != sd_global->failed_task_set)
908 "Task %s: the state must be SD_SCHEDULED, SD_RUNNABLE, SD_RUNNING or SD_FAILED",
909 SD_task_get_name(task));
911 if (__SD_task_is_scheduled_or_runnable(task) /* if the task is scheduled or runnable */
912 &&task->kind == SD_TASK_NOT_TYPED) /* Don't free scheduling data for typed tasks */
913 __SD_task_destroy_scheduling_data(task);
915 if (__SD_task_is_running(task)) /* the task should become SD_FAILED */
916 surf_workstation_model->action_cancel(task->surf_action);
918 if (task->unsatisfied_dependencies == 0)
919 __SD_task_set_state(task, SD_SCHEDULABLE);
921 __SD_task_set_state(task, SD_NOT_SCHEDULED);
923 task->remains = task->amount;
924 task->start_time = -1.0;
927 /* Destroys the data memorized by SD_task_schedule. Task state must be SD_SCHEDULED or SD_RUNNABLE.
929 static void __SD_task_destroy_scheduling_data(SD_task_t task)
931 SD_CHECK_INIT_DONE();
932 if (!__SD_task_is_scheduled_or_runnable(task)
933 && !__SD_task_is_in_fifo(task))
935 "Task '%s' must be SD_SCHEDULED, SD_RUNNABLE or SD_IN_FIFO",
936 SD_task_get_name(task));
938 xbt_free(task->computation_amount);
939 xbt_free(task->communication_amount);
940 task->computation_amount = task->communication_amount = NULL;
943 /* Runs a task. This function is directly called by __SD_task_try_to_run if the task
944 * doesn't have to wait in fifos. Otherwise, it is called by __SD_task_just_done when
945 * the task gets out of its fifos.
947 void __SD_task_really_run(SD_task_t task)
951 void **surf_workstations;
953 SD_CHECK_INIT_DONE();
954 xbt_assert(task != NULL, "Invalid parameter");
955 xbt_assert(__SD_task_is_runnable_or_in_fifo(task),
956 "Task '%s' is not runnable or in a fifo! Task state: %d",
957 SD_task_get_name(task), SD_task_get_state(task));
958 xbt_assert(task->workstation_list != NULL,
959 "Task '%s': workstation_list is NULL!",
960 SD_task_get_name(task));
964 XBT_DEBUG("Really running task '%s'", SD_task_get_name(task));
966 /* set this task as current task for the workstations in sequential mode */
967 for (i = 0; i < task->workstation_nb; i++) {
968 if (SD_workstation_get_access_mode(task->workstation_list[i]) ==
969 SD_WORKSTATION_SEQUENTIAL_ACCESS) {
970 task->workstation_list[i]->current_task = task;
971 xbt_assert(__SD_workstation_is_busy(task->workstation_list[i]),
972 "The workstation should be busy now");
976 XBT_DEBUG("Task '%s' set as current task for its workstations",
977 SD_task_get_name(task));
981 /* we have to create a Surf workstation array instead of the SimDag workstation array */
982 surf_workstations = xbt_new(void *, task->workstation_nb);
984 for (i = 0; i < task->workstation_nb; i++)
985 surf_workstations[i] = task->workstation_list[i]->surf_workstation;
987 /* 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 */
988 #define cost_or_zero(array,pos) ((array)?(array)[pos]:0.0)
990 task->surf_action = NULL;
991 if ((task->workstation_nb == 1)
992 && (cost_or_zero(task->communication_amount, 0) == 0.0)) {
994 surf_workstation_model->extension.
995 workstation.execute(surf_workstations[0],
996 cost_or_zero(task->computation_amount, 0));
997 } else if ((task->workstation_nb == 1)
998 && (cost_or_zero(task->computation_amount, 0) == 0.0)) {
1001 surf_workstation_model->extension.
1002 workstation.communicate(surf_workstations[0], surf_workstations[0],
1003 cost_or_zero(task->communication_amount,
1005 } else if ((task->workstation_nb == 2)
1006 && (cost_or_zero(task->computation_amount, 0) == 0.0)
1007 && (cost_or_zero(task->computation_amount, 1) == 0.0)) {
1011 for (i = 0; i < task->workstation_nb * task->workstation_nb; i++) {
1012 if (cost_or_zero(task->communication_amount, i) > 0.0) {
1014 value = cost_or_zero(task->communication_amount, i);
1019 surf_workstation_model->extension.
1020 workstation.communicate(surf_workstations[0],
1021 surf_workstations[1], value, task->rate);
1026 if (!task->surf_action) {
1027 double *computation_amount = xbt_new(double, task->workstation_nb);
1028 double *communication_amount = xbt_new(double, task->workstation_nb *
1029 task->workstation_nb);
1031 memcpy(computation_amount, task->computation_amount, sizeof(double) *
1032 task->workstation_nb);
1033 memcpy(communication_amount, task->communication_amount,
1034 sizeof(double) * task->workstation_nb * task->workstation_nb);
1037 surf_workstation_model->extension.
1038 workstation.execute_parallel_task(task->workstation_nb,
1041 communication_amount,
1042 task->amount, task->rate);
1044 xbt_free(surf_workstations);
1047 surf_workstation_model->action_data_set(task->surf_action, task);
1049 XBT_DEBUG("surf_action = %p", task->surf_action);
1053 TRACE_surf_action(task->surf_action, task->category);
1056 __SD_task_destroy_scheduling_data(task); /* now the scheduling data are not useful anymore */
1057 __SD_task_set_state(task, SD_RUNNING);
1058 xbt_assert(__SD_task_is_running(task), "Bad state of task '%s': %d",
1059 SD_task_get_name(task), SD_task_get_state(task));
1063 /* Tries to run a task. This function is called by SD_simulate() when a scheduled task becomes SD_RUNNABLE
1064 * (ie when its dependencies are satisfied).
1065 * If one of the workstations where the task is scheduled on is busy (in sequential mode),
1066 * the task doesn't start.
1067 * Returns whether the task has started.
1069 int __SD_task_try_to_run(SD_task_t task)
1074 SD_workstation_t workstation;
1076 SD_CHECK_INIT_DONE();
1077 xbt_assert(task != NULL, "Invalid parameter");
1078 xbt_assert(__SD_task_is_runnable(task),
1079 "Task '%s' is not runnable! Task state: %d",
1080 SD_task_get_name(task), SD_task_get_state(task));
1083 for (i = 0; i < task->workstation_nb; i++) {
1084 can_start = can_start &&
1085 !__SD_workstation_is_busy(task->workstation_list[i]);
1088 XBT_DEBUG("Task '%s' can start: %d", SD_task_get_name(task), can_start);
1090 if (!can_start) { /* if the task cannot start and is not in the fifos yet */
1091 for (i = 0; i < task->workstation_nb; i++) {
1092 workstation = task->workstation_list[i];
1093 if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
1094 XBT_DEBUG("Pushing task '%s' in the fifo of workstation '%s'",
1095 SD_task_get_name(task),
1096 SD_workstation_get_name(workstation));
1097 xbt_fifo_push(workstation->task_fifo, task);
1100 __SD_task_set_state(task, SD_IN_FIFO);
1101 xbt_assert(__SD_task_is_in_fifo(task), "Bad state of task '%s': %d",
1102 SD_task_get_name(task), SD_task_get_state(task));
1103 XBT_DEBUG("Task '%s' state is now SD_IN_FIFO", SD_task_get_name(task));
1105 __SD_task_really_run(task);
1111 /* This function is called by SD_simulate when a task is done.
1112 * It updates task->state and task->action and executes if necessary the tasks
1113 * which were waiting in fifos for the end of `task'
1115 void __SD_task_just_done(SD_task_t task)
1118 SD_workstation_t workstation;
1120 SD_task_t candidate;
1121 int candidate_nb = 0;
1122 int candidate_capacity = 8;
1123 SD_task_t *candidates;
1126 SD_CHECK_INIT_DONE();
1127 xbt_assert(task != NULL, "Invalid parameter");
1128 xbt_assert(__SD_task_is_running(task),
1129 "The task must be running! Task state: %d",
1130 SD_task_get_state(task));
1131 xbt_assert(task->workstation_list != NULL,
1132 "Task '%s': workstation_list is NULL!",
1133 SD_task_get_name(task));
1136 candidates = xbt_new(SD_task_t, 8);
1138 __SD_task_set_state(task, SD_DONE);
1139 surf_workstation_model->action_unref(task->surf_action);
1140 task->surf_action = NULL;
1142 XBT_DEBUG("Looking for candidates");
1144 /* if the task was executed on sequential workstations,
1145 maybe we can execute the next task of the fifo for each workstation */
1146 for (i = 0; i < task->workstation_nb; i++) {
1147 workstation = task->workstation_list[i];
1148 XBT_DEBUG("Workstation '%s': access_mode = %d",
1149 SD_workstation_get_name(workstation), workstation->access_mode);
1150 if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
1151 xbt_assert(workstation->task_fifo != NULL,
1152 "Workstation '%s' has sequential access but no fifo!",
1153 SD_workstation_get_name(workstation));
1154 xbt_assert(workstation->current_task =
1155 task, "Workstation '%s': current task should be '%s'",
1156 SD_workstation_get_name(workstation),
1157 SD_task_get_name(task));
1159 /* the task is over so we can release the workstation */
1160 workstation->current_task = NULL;
1162 XBT_DEBUG("Getting candidate in fifo");
1164 xbt_fifo_get_item_content(xbt_fifo_get_first_item
1165 (workstation->task_fifo));
1167 if (candidate != NULL) {
1168 XBT_DEBUG("Candidate: '%s'", SD_task_get_name(candidate));
1169 xbt_assert(__SD_task_is_in_fifo(candidate),
1170 "Bad state of candidate '%s': %d",
1171 SD_task_get_name(candidate),
1172 SD_task_get_state(candidate));
1175 XBT_DEBUG("Candidate in fifo: %p", candidate);
1177 /* if there was a task waiting for my place */
1178 if (candidate != NULL) {
1179 /* Unfortunately, we are not sure yet that we can execute the task now,
1180 because the task can be waiting more deeply in some other workstation's fifos...
1181 So we memorize all candidate tasks, and then we will check for each candidate
1182 whether or not all its workstations are available. */
1184 /* realloc if necessary */
1185 if (candidate_nb == candidate_capacity) {
1186 candidate_capacity *= 2;
1188 xbt_realloc(candidates,
1189 sizeof(SD_task_t) * candidate_capacity);
1192 /* register the candidate */
1193 candidates[candidate_nb++] = candidate;
1194 candidate->fifo_checked = 0;
1199 XBT_DEBUG("Candidates found: %d", candidate_nb);
1201 /* now we check every candidate task */
1202 for (i = 0; i < candidate_nb; i++) {
1203 candidate = candidates[i];
1205 if (candidate->fifo_checked) {
1206 continue; /* we have already evaluated that task */
1209 xbt_assert(__SD_task_is_in_fifo(candidate),
1210 "Bad state of candidate '%s': %d",
1211 SD_task_get_name(candidate), SD_task_get_state(candidate));
1213 for (j = 0; j < candidate->workstation_nb && can_start; j++) {
1214 workstation = candidate->workstation_list[j];
1216 /* I can start on this workstation if the workstation is shared
1217 or if I am the first task in the fifo */
1218 can_start = workstation->access_mode == SD_WORKSTATION_SHARED_ACCESS
1220 xbt_fifo_get_item_content(xbt_fifo_get_first_item
1221 (workstation->task_fifo));
1224 XBT_DEBUG("Candidate '%s' can start: %d", SD_task_get_name(candidate),
1227 /* now we are sure that I can start! */
1229 for (j = 0; j < candidate->workstation_nb && can_start; j++) {
1230 workstation = candidate->workstation_list[j];
1232 /* update the fifo */
1233 if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
1234 candidate = xbt_fifo_shift(workstation->task_fifo); /* the return value is stored just for debugging */
1235 XBT_DEBUG("Head of the fifo: '%s'",
1237 NULL) ? SD_task_get_name(candidate) : "NULL");
1238 xbt_assert(candidate == candidates[i],
1239 "Error in __SD_task_just_done: bad first task in the fifo");
1241 } /* for each workstation */
1243 /* finally execute the task */
1244 XBT_DEBUG("Task '%s' state: %d", SD_task_get_name(candidate),
1245 SD_task_get_state(candidate));
1246 __SD_task_really_run(candidate);
1249 ("Calling __SD_task_is_running: task '%s', state set: %p, running_task_set: %p, is running: %d",
1250 SD_task_get_name(candidate), candidate->state_set,
1251 sd_global->running_task_set, __SD_task_is_running(candidate));
1252 xbt_assert(__SD_task_is_running(candidate),
1253 "Bad state of task '%s': %d",
1254 SD_task_get_name(candidate),
1255 SD_task_get_state(candidate));
1256 XBT_DEBUG("Okay, the task is running.");
1259 candidate->fifo_checked = 1;
1260 } /* for each candidate */
1262 xbt_free(candidates);
1265 /* Remove all dependencies associated with a task. This function is called when the task is destroyed.
1267 static void __SD_task_remove_dependencies(SD_task_t task)
1269 /* we must destroy the dependencies carefuly (with SD_dependency_remove)
1270 because each one is stored twice */
1271 SD_dependency_t dependency;
1272 while (xbt_dynar_length(task->tasks_before) > 0) {
1273 xbt_dynar_get_cpy(task->tasks_before, 0, &dependency);
1274 SD_task_dependency_remove(dependency->src, dependency->dst);
1277 while (xbt_dynar_length(task->tasks_after) > 0) {
1278 xbt_dynar_get_cpy(task->tasks_after, 0, &dependency);
1279 SD_task_dependency_remove(dependency->src, dependency->dst);
1284 * \brief Returns the start time of a task
1286 * The task state must be SD_RUNNING, SD_DONE or SD_FAILED.
1288 * \param task: a task
1289 * \return the start time of this task
1291 double SD_task_get_start_time(SD_task_t task)
1293 SD_CHECK_INIT_DONE();
1294 xbt_assert(task != NULL, "Invalid parameter");
1295 if (task->surf_action)
1296 return surf_workstation_model->
1297 action_get_start_time(task->surf_action);
1299 return task->start_time;
1303 * \brief Returns the finish time of a task
1305 * The task state must be SD_RUNNING, SD_DONE or SD_FAILED.
1306 * If the state is not completed yet, the returned value is an
1307 * estimation of the task finish time. This value can fluctuate
1308 * until the task is completed.
1310 * \param task: a task
1311 * \return the start time of this task
1313 double SD_task_get_finish_time(SD_task_t task)
1315 SD_CHECK_INIT_DONE();
1316 xbt_assert(task != NULL, "Invalid parameter");
1318 if (task->surf_action) /* should never happen as actions are destroyed right after their completion */
1319 return surf_workstation_model->
1320 action_get_finish_time(task->surf_action);
1322 return task->finish_time;
1325 static XBT_INLINE SD_task_t SD_task_create_sized(const char *name,
1326 void *data, double amount,
1329 SD_task_t task = SD_task_create(name, data, amount);
1330 task->communication_amount = xbt_new0(double, ws_count * ws_count);
1331 task->computation_amount = xbt_new0(double, ws_count);
1332 task->workstation_nb = ws_count;
1333 task->workstation_list = xbt_new0(SD_workstation_t, ws_count);
1337 /** @brief create a end-to-end communication task that can then be auto-scheduled
1339 * Auto-scheduling mean that the task can be used with SD_task_schedulev(). This
1340 * allows to specify the task costs at creation, and decorelate them from the
1341 * scheduling process where you just specify which resource should deliver the
1344 * A end-to-end communication must be scheduled on 2 hosts, and the amount
1345 * specified at creation is sent from hosts[0] to hosts[1].
1347 SD_task_t SD_task_create_comm_e2e(const char *name, void *data,
1350 SD_task_t res = SD_task_create_sized(name, data, amount, 2);
1351 res->communication_amount[2] = amount;
1352 res->kind = SD_TASK_COMM_E2E;
1356 /** @brief create a sequential computation task that can then be auto-scheduled
1358 * Auto-scheduling mean that the task can be used with SD_task_schedulev(). This
1359 * allows to specify the task costs at creation, and decorelate them from the
1360 * scheduling process where you just specify which resource should deliver the
1363 * A sequential computation must be scheduled on 1 host, and the amount
1364 * specified at creation to be run on hosts[0].
1366 SD_task_t SD_task_create_comp_seq(const char *name, void *data,
1369 SD_task_t res = SD_task_create_sized(name, data, amount, 1);
1370 res->computation_amount[0] = amount;
1371 res->kind = SD_TASK_COMP_SEQ;
1375 /** @brief Auto-schedules a task.
1377 * Auto-scheduling mean that the task can be used with SD_task_schedulev(). This
1378 * allows to specify the task costs at creation, and decorelate them from the
1379 * scheduling process where you just specify which resource should deliver the
1382 * To be auto-schedulable, a task must be created with SD_task_create_comm_e2e() or
1383 * SD_task_create_comp_seq(). Check their definitions for the exact semantic of each
1387 * We should create tasks kind for the following categories:
1388 * - Point to point communication (done)
1389 * - Sequential computation (done)
1390 * - group communication (redistribution, several kinds)
1391 * - parallel tasks with no internal communication (one kind per speedup model such as amdal)
1392 * - idem+ internal communication. Task type not enough since we cannot store comm cost alongside to comp one)
1394 void SD_task_schedulev(SD_task_t task, int count,
1395 const SD_workstation_t * list)
1398 SD_dependency_t dep;
1400 xbt_assert(task->kind != 0,
1401 "Task %s is not typed. Cannot automatically schedule it.",
1402 SD_task_get_name(task));
1403 switch (task->kind) {
1404 case SD_TASK_COMM_E2E:
1405 case SD_TASK_COMP_SEQ:
1406 xbt_assert(task->workstation_nb == count,"Got %d locations, but were expecting %d locations",count,task->workstation_nb);
1407 for (i = 0; i < count; i++)
1408 task->workstation_list[i] = list[i];
1409 SD_task_do_schedule(task);
1412 xbt_die("Kind of task %s not supported by SD_task_schedulev()",
1413 SD_task_get_name(task));
1415 if (task->kind == SD_TASK_COMM_E2E) {
1416 XBT_VERB("Schedule comm task %s between %s -> %s. It costs %.f bytes",
1417 SD_task_get_name(task),
1418 SD_workstation_get_name(task->workstation_list[0]),
1419 SD_workstation_get_name(task->workstation_list[1]),
1420 task->communication_amount[2]);
1423 /* Iterate over all childs and parent being COMM_E2E to say where I am located (and start them if runnable) */
1424 if (task->kind == SD_TASK_COMP_SEQ) {
1425 XBT_VERB("Schedule computation task %s on %s. It costs %.f flops",
1426 SD_task_get_name(task),
1427 SD_workstation_get_name(task->workstation_list[0]),
1428 task->computation_amount[0]);
1430 xbt_dynar_foreach(task->tasks_before, cpt, dep) {
1431 SD_task_t before = dep->src;
1432 if (before->kind == SD_TASK_COMM_E2E) {
1433 before->workstation_list[1] = task->workstation_list[0];
1435 if (before->workstation_list[0] &&
1436 (__SD_task_is_schedulable(before)
1437 || __SD_task_is_not_scheduled(before))) {
1438 SD_task_do_schedule(before);
1440 ("Auto-Schedule comm task %s between %s -> %s. It costs %.f bytes",
1441 SD_task_get_name(before),
1442 SD_workstation_get_name(before->workstation_list[0]),
1443 SD_workstation_get_name(before->workstation_list[1]),
1444 before->communication_amount[2]);
1448 xbt_dynar_foreach(task->tasks_after, cpt, dep) {
1449 SD_task_t after = dep->dst;
1450 if (after->kind == SD_TASK_COMM_E2E) {
1451 after->workstation_list[0] = task->workstation_list[0];
1452 //J-N : Why did you comment on these line (this comment add a bug I think)?
1453 if (after->workstation_list[1]
1454 && (__SD_task_is_not_scheduled(after)
1455 || __SD_task_is_schedulable(after))) {
1456 SD_task_do_schedule(after);
1458 ("Auto-Schedule comm task %s between %s -> %s. It costs %.f bytes",
1459 SD_task_get_name(after),
1460 SD_workstation_get_name(after->workstation_list[0]),
1461 SD_workstation_get_name(after->workstation_list[1]),
1462 after->communication_amount[2]);
1470 /** @brief autoschedule a task on a list of workstations
1472 * This function is very similar to SD_task_schedulev(),
1473 * but takes the list of workstations to schedule onto as separate parameters.
1474 * It builds a proper vector of workstations and then call SD_task_schedulev()
1476 void SD_task_schedulel(SD_task_t task, int count, ...)
1479 SD_workstation_t *list = xbt_new(SD_workstation_t, count);
1481 va_start(ap, count);
1482 for (i = 0; i < count; i++) {
1483 list[i] = va_arg(ap, SD_workstation_t);
1486 SD_task_schedulev(task, count, list);