1 /* Copyright (c) 2007-2009 Da SimGrid Team. All rights reserved. */
3 /* This program is free software; you can redistribute it and/or modify it
4 * under the terms of the license (GNU LGPL) which comes with this package. */
7 #include "simdag/simdag.h"
8 #include "xbt/sysdep.h"
11 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(sd_task, sd,
12 "Logging specific to SimDag (task)");
14 static void __SD_task_remove_dependencies(SD_task_t task);
15 static void __SD_task_destroy_scheduling_data(SD_task_t task);
18 * \brief Creates a new task.
20 * \param name the name of the task (can be \c NULL)
21 * \param data the user data you want to associate with the task (can be \c NULL)
22 * \param amount amount of the task
23 * \return the new task
24 * \see SD_task_destroy()
26 SD_task_t SD_task_create(const char *name, void *data, double amount)
32 task = xbt_new(s_SD_task_t, 1);
34 /* general information */
35 task->data = data; /* user data */
36 task->name = xbt_strdup(name);
38 task->state_hookup.prev = NULL;
39 task->state_hookup.next = NULL;
40 task->state_set = sd_global->not_scheduled_task_set;
41 task->state = SD_NOT_SCHEDULED;
42 xbt_swag_insert(task, task->state_set);
44 task->amount = amount;
45 task->remains = amount;
46 task->start_time = -1.0;
47 task->finish_time = -1.0;
48 task->surf_action = NULL;
49 task->watch_points = 0;
52 task->tasks_before = xbt_dynar_new(sizeof(SD_dependency_t), NULL);
53 task->tasks_after = xbt_dynar_new(sizeof(SD_dependency_t), NULL);
55 /* scheduling parameters */
56 task->workstation_nb = 0;
57 task->workstation_list = NULL;
58 task->computation_amount = NULL;
59 task->communication_amount = NULL;
62 sd_global->task_number++;
68 * \brief Returns the user data of a task
71 * \return the user data associated with this task (can be \c NULL)
72 * \see SD_task_set_data()
74 void *SD_task_get_data(SD_task_t task)
77 xbt_assert0(task != NULL, "Invalid parameter");
82 * \brief Sets the user data of a task
84 * The new data can be \c NULL. The old data should have been freed first
85 * if it was not \c NULL.
88 * \param data the new data you want to associate with this task
89 * \see SD_task_get_data()
91 void SD_task_set_data(SD_task_t task, void *data)
94 xbt_assert0(task != NULL, "Invalid parameter");
99 * \brief Returns the state of a task
102 * \return the current \ref e_SD_task_state_t "state" of this task:
103 * #SD_NOT_SCHEDULED, #SD_SCHEDULED, #SD_READY, #SD_RUNNING, #SD_DONE or #SD_FAILED
104 * \see e_SD_task_state_t
106 e_SD_task_state_t SD_task_get_state(SD_task_t task)
108 SD_CHECK_INIT_DONE();
109 xbt_assert0(task != NULL, "Invalid parameter");
113 /* Changes the state of a task. Updates the swags and the flag sd_global->watch_point_reached.
115 void __SD_task_set_state(SD_task_t task, e_SD_task_state_t new_state)
117 xbt_swag_remove(task, task->state_set);
119 case SD_NOT_SCHEDULED:
120 task->state_set = sd_global->not_scheduled_task_set;
123 task->state_set = sd_global->scheduled_task_set;
126 task->state_set = sd_global->ready_task_set;
129 task->state_set = sd_global->in_fifo_task_set;
132 task->state_set = sd_global->running_task_set;
134 surf_workstation_model->action_get_start_time(task->surf_action);
137 task->state_set = sd_global->done_task_set;
139 surf_workstation_model->action_get_finish_time(task->surf_action);
143 task->state_set = sd_global->failed_task_set;
146 xbt_assert0(0, "Invalid state");
148 xbt_swag_insert(task, task->state_set);
149 task->state = new_state;
151 if (task->watch_points & new_state) {
152 INFO1("Watch point reached with task '%s'!", SD_task_get_name(task));
153 sd_global->watch_point_reached = 1;
154 SD_task_unwatch(task, new_state); /* remove the watch point */
159 * \brief Returns the name of a task
162 * \return the name of this task (can be \c NULL)
164 const char *SD_task_get_name(SD_task_t task)
166 SD_CHECK_INIT_DONE();
167 xbt_assert0(task != NULL, "Invalid parameter");
171 /** @brief Returns the dynar of the parents of a task
174 * \return a newly allocated dynar comprising the parents of this task
177 xbt_dynar_t SD_task_get_parents(SD_task_t task)
182 SD_CHECK_INIT_DONE();
183 xbt_assert0(task != NULL, "Invalid parameter");
185 parents = xbt_dynar_new(sizeof(SD_task_t), NULL);
186 xbt_dynar_foreach(task->tasks_before, i, dep){
187 xbt_dynar_push(parents, &(dep->src));
192 /** @brief Returns the dynar of the parents of a task
195 * \return a newly allocated dynar comprising the parents of this task
197 xbt_dynar_t SD_task_get_children(SD_task_t task)
200 xbt_dynar_t children;
202 SD_CHECK_INIT_DONE();
203 xbt_assert0(task != NULL, "Invalid parameter");
205 children = xbt_dynar_new(sizeof(SD_task_t), NULL);
206 xbt_dynar_foreach(task->tasks_after, i, dep){
207 xbt_dynar_push(children, &(dep->dst));
213 * \brief Returns the amount of workstations involved in a task
215 * Only call this on already scheduled tasks!
218 int SD_task_get_workstation_count(SD_task_t task)
220 SD_CHECK_INIT_DONE();
221 xbt_assert0(task != NULL, "Invalid parameter");
222 // xbt_assert1( task->state_set != sd_global->scheduled_task_set,
223 // "Unscheduled task %s", task->name);
224 return task->workstation_nb;
228 * \brief Returns the list of workstations involved in a task
230 * Only call this on already scheduled tasks!
233 SD_workstation_t* SD_task_get_workstation_list(SD_task_t task)
235 SD_CHECK_INIT_DONE();
236 xbt_assert0(task != NULL, "Invalid parameter");
237 //xbt_assert1( task->state_set != sd_global->scheduled_task_set,
238 // "Unscheduled task %s", task->name);
239 return task->workstation_list;
243 * \brief Returns the total amount of a task
246 * \return the total amount of this task
247 * \see SD_task_get_remaining_amount()
249 double SD_task_get_amount(SD_task_t task)
251 SD_CHECK_INIT_DONE();
252 xbt_assert0(task != NULL, "Invalid parameter");
257 * \brief Returns the remaining amount of a task
260 * \return the remaining amount of this task
261 * \see SD_task_get_amount()
263 double SD_task_get_remaining_amount(SD_task_t task)
265 SD_CHECK_INIT_DONE();
266 xbt_assert0(task != NULL, "Invalid parameter");
268 if (task->surf_action)
269 return surf_workstation_model->get_remains(task->surf_action);
271 return task->remains;
274 int SD_task_get_kind(SD_task_t task) {
278 /** @brief Displays debugging informations about a task */
279 void SD_task_dump(SD_task_t task)
281 unsigned int counter;
282 SD_dependency_t dependency;
285 INFO1("Displaying task %s",SD_task_get_name(task));
286 statename=bprintf("%s %s %s %s %s %s %s",
287 (task->state&SD_NOT_SCHEDULED?"not scheduled":""),
288 (task->state&SD_SCHEDULED?"scheduled":""),
289 (task->state&SD_READY?"ready":"not ready"),
290 (task->state&SD_IN_FIFO?"in fifo":""),
291 (task->state&SD_RUNNING?"running":""),
292 (task->state&SD_DONE?"done":""),
293 (task->state&SD_FAILED?"failed":""));
294 INFO1(" - state: %s",statename);
299 case SD_TASK_COMM_E2E:
300 INFO0(" - kind: end-to-end communication");
302 case SD_TASK_COMP_SEQ:
303 INFO0(" - kind: sequential computation");
306 INFO1(" - (unknown kind %d)",task->kind);
309 INFO1(" - amount: %.0f",SD_task_get_amount(task));
310 if (xbt_dynar_length(task->tasks_before)) {
311 INFO0(" - pre-dependencies:");
312 xbt_dynar_foreach(task->tasks_before,counter,dependency) {
313 INFO1(" %s",SD_task_get_name(dependency->src));
316 if (xbt_dynar_length(task->tasks_after)) {
317 INFO0(" - post-dependencies:");
318 xbt_dynar_foreach(task->tasks_after,counter,dependency) {
319 INFO1(" %s",SD_task_get_name(dependency->dst));
323 /** @brief Dumps the task in dotty formalism into the FILE* passed as second argument */
324 void SD_task_dotty(SD_task_t task,void* out) {
325 unsigned int counter;
326 SD_dependency_t dependency;
327 fprintf(out, " T%ld [label=\"%.20s\"",(unsigned long int)task, task->name);
329 case SD_TASK_COMM_E2E:
330 fprintf(out,", shape=box");
332 case SD_TASK_COMP_SEQ:
333 fprintf(out,", shape=circle");
337 xbt_dynar_foreach(task->tasks_before,counter,dependency) {
338 fprintf(out," T%p -> T%p;\n",dependency->src, dependency->dst);
342 /* Destroys a dependency between two tasks.
344 static void __SD_task_dependency_destroy(void *dependency)
346 if (((SD_dependency_t) dependency)->name != NULL)
347 xbt_free(((SD_dependency_t) dependency)->name);
348 xbt_free(dependency);
352 * \brief Adds a dependency between two tasks
354 * \a dst will depend on \a src, ie \a dst will not start before \a src is finished.
355 * Their \ref e_SD_task_state_t "state" must be #SD_NOT_SCHEDULED, #SD_SCHEDULED or #SD_READY.
357 * \param name the name of the new dependency (can be \c NULL)
358 * \param data the user data you want to associate with this dependency (can be \c NULL)
359 * \param src the task which must be executed first
360 * \param dst the task you want to make depend on \a src
361 * \see SD_task_dependency_remove()
363 void SD_task_dependency_add(const char *name, void *data, SD_task_t src,
370 SD_dependency_t dependency;
372 SD_CHECK_INIT_DONE();
373 xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
375 dynar = src->tasks_after;
376 length = xbt_dynar_length(dynar);
380 "Cannot add a dependency between task '%s' and itself",
381 SD_task_get_name(src));
383 if (!__SD_task_is_not_scheduled(src)
384 && !__SD_task_is_scheduled_or_ready(src))
386 "Task '%s' must be SD_NOT_SCHEDULED, SD_SCHEDULED or SD_READY",
387 SD_task_get_name(src));
389 if (!__SD_task_is_not_scheduled(dst)
390 && !__SD_task_is_scheduled_or_ready(dst))
392 "Task '%s' must be SD_NOT_SCHEDULED, SD_SCHEDULED or SD_READY",
393 SD_task_get_name(dst));
395 DEBUG2("SD_task_dependency_add: src = %s, dst = %s", SD_task_get_name(src),
396 SD_task_get_name(dst));
397 for (i = 0; i < length && !found; i++) {
398 xbt_dynar_get_cpy(dynar, i, &dependency);
399 found = (dependency->dst == dst);
400 DEBUG2("Dependency %d: dependency->dst = %s", i,
401 SD_task_get_name(dependency->dst));
406 "A dependency already exists between task '%s' and task '%s'",
407 SD_task_get_name(src), SD_task_get_name(dst));
409 dependency = xbt_new(s_SD_dependency_t, 1);
411 dependency->name = xbt_strdup(name); /* xbt_strdup is cleaver enough to deal with NULL args itself */
412 dependency->data = data;
413 dependency->src = src;
414 dependency->dst = dst;
416 /* src must be executed before dst */
417 xbt_dynar_push(src->tasks_after, &dependency);
418 xbt_dynar_push(dst->tasks_before, &dependency);
420 /* if the task was ready, then dst->tasks_before is not empty anymore,
421 so we must go back to state SD_SCHEDULED */
422 if (__SD_task_is_ready(dst)) {
423 DEBUG1("SD_task_dependency_add: %s was ready and becomes scheduled!",
424 SD_task_get_name(dst));
425 __SD_task_set_state(dst, SD_SCHEDULED);
428 /* __SD_print_dependencies(src);
429 __SD_print_dependencies(dst); */
433 * \brief Indacates whether there is a dependency between two tasks.
436 * \param dst a task depending on \a src
438 * If src is NULL, checks whether dst has any pre-dependency.
439 * If dst is NULL, checks whether src has any post-dependency.
441 int SD_task_dependency_exists(SD_task_t src, SD_task_t dst)
443 unsigned int counter;
444 SD_dependency_t dependency;
446 SD_CHECK_INIT_DONE();
447 xbt_assert0(src != NULL || dst != NULL, "Invalid parameter: both src and dst are NULL");
451 xbt_dynar_foreach(src->tasks_after,counter,dependency) {
452 if (dependency->dst == dst)
456 return xbt_dynar_length(src->tasks_after);
459 return xbt_dynar_length(dst->tasks_before);
465 * \brief Remove a dependency between two tasks
468 * \param dst a task depending on \a src
469 * \see SD_task_dependency_add()
471 void SD_task_dependency_remove(SD_task_t src, SD_task_t dst)
478 SD_dependency_t dependency;
480 SD_CHECK_INIT_DONE();
481 xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
483 /* remove the dependency from src->tasks_after */
484 dynar = src->tasks_after;
485 length = xbt_dynar_length(dynar);
487 for (i = 0; i < length && !found; i++) {
488 xbt_dynar_get_cpy(dynar, i, &dependency);
489 if (dependency->dst == dst) {
490 xbt_dynar_remove_at(dynar, i, NULL);
496 "No dependency found between task '%s' and '%s': task '%s' is not a successor of task '%s'",
497 SD_task_get_name(src), SD_task_get_name(dst),
498 SD_task_get_name(dst), SD_task_get_name(src));
500 /* remove the dependency from dst->tasks_before */
501 dynar = dst->tasks_before;
502 length = xbt_dynar_length(dynar);
505 for (i = 0; i < length && !found; i++) {
506 xbt_dynar_get_cpy(dynar, i, &dependency);
507 if (dependency->src == src) {
508 xbt_dynar_remove_at(dynar, i, NULL);
509 __SD_task_dependency_destroy(dependency);
513 /* should never happen... */
515 "SimDag error: task '%s' is a successor of '%s' but task '%s' is not a predecessor of task '%s'",
516 SD_task_get_name(dst), SD_task_get_name(src),
517 SD_task_get_name(src), SD_task_get_name(dst));
519 /* if the task was scheduled and dst->tasks_before is empty now, we can make it ready */
520 if (xbt_dynar_length(dst->tasks_before) == 0 && __SD_task_is_scheduled(dst))
521 __SD_task_set_state(dst, SD_READY);
523 /* __SD_print_dependencies(src);
524 __SD_print_dependencies(dst); */
528 * \brief Returns the user data associated with a dependency between two tasks
531 * \param dst a task depending on \a src
532 * \return the user data associated with this dependency (can be \c NULL)
533 * \see SD_task_dependency_add()
535 void *SD_task_dependency_get_data(SD_task_t src, SD_task_t dst)
542 SD_dependency_t dependency;
545 SD_CHECK_INIT_DONE();
546 xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
548 dynar = src->tasks_after;
549 length = xbt_dynar_length(dynar);
551 for (i = 0; i < length && !found; i++) {
552 xbt_dynar_get_cpy(dynar, i, &dependency);
553 found = (dependency->dst == dst);
556 THROW2(arg_error, 0, "No dependency found between task '%s' and '%s'",
557 SD_task_get_name(src), SD_task_get_name(dst));
558 return dependency->data;
561 /* temporary function for debugging */
562 static void __SD_print_watch_points(SD_task_t task)
564 static const int state_masks[] =
565 { SD_SCHEDULED, SD_RUNNING, SD_READY, SD_DONE, SD_FAILED };
566 static const char *state_names[] =
567 { "scheduled", "running", "ready", "done", "failed" };
570 INFO2("Task '%s' watch points (%x): ", SD_task_get_name(task),
574 for (i = 0; i < 5; i++) {
575 if (task->watch_points & state_masks[i])
576 INFO1("%s ", state_names[i]);
581 * \brief Adds a watch point to a task
583 * SD_simulate() will stop as soon as the \ref e_SD_task_state_t "state" of this
584 * task becomes the one given in argument. The
585 * watch point is then automatically removed.
588 * \param state the \ref e_SD_task_state_t "state" you want to watch
589 * (cannot be #SD_NOT_SCHEDULED)
590 * \see SD_task_unwatch()
592 void SD_task_watch(SD_task_t task, e_SD_task_state_t state)
594 SD_CHECK_INIT_DONE();
595 xbt_assert0(task != NULL, "Invalid parameter");
597 if (state & SD_NOT_SCHEDULED)
599 "Cannot add a watch point for state SD_NOT_SCHEDULED");
601 task->watch_points = task->watch_points | state;
602 /* __SD_print_watch_points(task); */
606 * \brief Removes a watch point from a task
609 * \param state the \ref e_SD_task_state_t "state" you no longer want to watch
610 * \see SD_task_watch()
612 void SD_task_unwatch(SD_task_t task, e_SD_task_state_t state)
614 SD_CHECK_INIT_DONE();
615 xbt_assert0(task != NULL, "Invalid parameter");
616 xbt_assert0(state != SD_NOT_SCHEDULED,
617 "SimDag error: Cannot have a watch point for state SD_NOT_SCHEDULED");
619 task->watch_points = task->watch_points & ~state;
620 /* __SD_print_watch_points(task); */
624 * \brief Returns an approximative estimation of the execution time of a task.
626 * The estimation is very approximative because the value returned is the time
627 * the task would take if it was executed now and if it was the only task.
629 * \param task the task to evaluate
630 * \param workstation_nb number of workstations on which the task would be executed
631 * \param workstation_list the workstations on which the task would be executed
632 * \param computation_amount computation amount for each workstation
633 * \param communication_amount communication amount between each pair of workstations
634 * \param rate task execution speed rate
637 double SD_task_get_execution_time(SD_task_t task,
639 const SD_workstation_t * workstation_list,
640 const double *computation_amount,
641 const double *communication_amount,
644 double time, max_time = 0.0;
646 SD_CHECK_INIT_DONE();
647 xbt_assert0(task != NULL && workstation_nb > 0 && workstation_list != NULL
648 && computation_amount != NULL
649 && communication_amount != NULL, "Invalid parameter");
651 /* the task execution time is the maximum execution time of the parallel tasks */
653 for (i = 0; i < workstation_nb; i++) {
655 SD_workstation_get_computation_time(workstation_list[i],
656 computation_amount[i]);
658 for (j = 0; j < workstation_nb; j++) {
660 SD_route_get_communication_time(workstation_list[i],
662 communication_amount[i *
667 if (time > max_time) {
671 return max_time * SD_task_get_amount(task);
673 static inline void SD_task_do_schedule(SD_task_t task) {
674 SD_CHECK_INIT_DONE();
676 if (!__SD_task_is_not_scheduled(task))
677 THROW1(arg_error, 0, "Task '%s' has already been scheduled",
678 SD_task_get_name(task));
680 /* update the task state */
681 if (xbt_dynar_length(task->tasks_before) == 0)
682 __SD_task_set_state(task, SD_READY);
684 __SD_task_set_state(task, SD_SCHEDULED);
688 * \brief Schedules a task
690 * The task state must be #SD_NOT_SCHEDULED.
691 * Once scheduled, a task will be executed as soon as possible in SD_simulate(),
692 * i.e. when its dependencies are satisfied.
694 * \param task the task you want to schedule
695 * \param workstation_nb number of workstations on which the task will be executed
696 * \param workstation_list the workstations on which the task will be executed
697 * \param computation_amount computation amount for each workstation
698 * \param communication_amount communication amount between each pair of workstations
699 * \param rate task execution speed rate
700 * \see SD_task_unschedule()
702 void SD_task_schedule(SD_task_t task, int workstation_count,
703 const SD_workstation_t * workstation_list,
704 const double *computation_amount,
705 const double *communication_amount, double rate)
707 xbt_assert0(workstation_count > 0, "workstation_nb must be positive");
709 int communication_nb;
711 task->workstation_nb = workstation_count;
714 task->computation_amount = xbt_new(double, workstation_count);
715 memcpy(task->computation_amount, computation_amount,
716 sizeof(double) * workstation_count);
718 communication_nb = workstation_count * workstation_count;
719 task->communication_amount = xbt_new(double, communication_nb);
720 memcpy(task->communication_amount, communication_amount,
721 sizeof(double) * communication_nb);
723 task->workstation_list = xbt_new(SD_workstation_t, workstation_count);
724 memcpy(task->workstation_list, workstation_list,
725 sizeof(SD_workstation_t) * workstation_count);
727 SD_task_do_schedule(task);
730 * \brief Unschedules a task
732 * The task state must be #SD_SCHEDULED, #SD_READY, #SD_RUNNING or #SD_FAILED.
733 * If you call this function, the task state becomes #SD_NOT_SCHEDULED.
734 * Call SD_task_schedule() to schedule it again.
736 * \param task the task you want to unschedule
737 * \see SD_task_schedule()
739 void SD_task_unschedule(SD_task_t task)
741 SD_CHECK_INIT_DONE();
742 xbt_assert0(task != NULL, "Invalid parameter");
744 if (task->state_set != sd_global->scheduled_task_set &&
745 task->state_set != sd_global->ready_task_set &&
746 task->state_set != sd_global->running_task_set &&
747 task->state_set != sd_global->failed_task_set)
749 "Task %s: the state must be SD_SCHEDULED, SD_READY, SD_RUNNING or SD_FAILED",
750 SD_task_get_name(task));
752 if (__SD_task_is_scheduled_or_ready(task)) /* if the task is scheduled or ready */
753 __SD_task_destroy_scheduling_data(task);
755 if (__SD_task_is_running(task)) /* the task should become SD_FAILED */
756 surf_workstation_model->action_cancel(task->surf_action);
758 __SD_task_set_state(task, SD_NOT_SCHEDULED);
759 task->remains = task->amount;
760 task->start_time = -1.0;
763 /* Destroys the data memorised by SD_task_schedule. Task state must be SD_SCHEDULED or SD_READY.
765 static void __SD_task_destroy_scheduling_data(SD_task_t task)
767 SD_CHECK_INIT_DONE();
768 if (!__SD_task_is_scheduled_or_ready(task) && !__SD_task_is_in_fifo(task))
770 "Task '%s' must be SD_SCHEDULED, SD_READY or SD_IN_FIFO",
771 SD_task_get_name(task));
773 xbt_free(task->computation_amount);
774 xbt_free(task->communication_amount);
777 /* Runs a task. This function is directly called by __SD_task_try_to_run if the task
778 * doesn't have to wait in fifos. Otherwise, it is called by __SD_task_just_done when
779 * the task gets out of its fifos.
781 void __SD_task_really_run(SD_task_t task)
785 void **surf_workstations;
787 SD_CHECK_INIT_DONE();
788 xbt_assert0(task != NULL, "Invalid parameter");
789 xbt_assert2(__SD_task_is_ready_or_in_fifo(task),
790 "Task '%s' is not ready or in a fifo! Task state: %d",
791 SD_task_get_name(task), SD_task_get_state(task));
792 xbt_assert1(task->workstation_list != NULL,
793 "Task '%s': workstation_list is NULL!", SD_task_get_name(task));
797 DEBUG1("Really running task '%s'", SD_task_get_name(task));
799 /* set this task as current task for the workstations in sequential mode */
800 for (i = 0; i < task->workstation_nb; i++) {
801 if (SD_workstation_get_access_mode(task->workstation_list[i]) ==
802 SD_WORKSTATION_SEQUENTIAL_ACCESS) {
803 task->workstation_list[i]->current_task = task;
804 xbt_assert0(__SD_workstation_is_busy(task->workstation_list[i]),
805 "The workstation should be busy now");
809 DEBUG1("Task '%s' set as current task for its workstations",
810 SD_task_get_name(task));
814 /* we have to create a Surf workstation array instead of the SimDag workstation array */
815 surf_workstations = xbt_new(void *, task->workstation_nb);
817 for (i = 0; i < task->workstation_nb; i++) {
818 surf_workstations[i] = task->workstation_list[i]->surf_workstation;
821 task->surf_action = NULL;
822 if ((task->workstation_nb == 1) && (task->communication_amount[0] == 0.0)) {
824 surf_workstation_model->extension.
825 workstation.execute(surf_workstations[0], task->computation_amount[0]);
826 } else if ((task->workstation_nb == 1)
827 && (task->computation_amount[0] == 0.0)) {
829 surf_workstation_model->extension.
830 workstation.communicate(surf_workstations[0], surf_workstations[0],
831 task->communication_amount[0], task->rate);
832 } else if ((task->workstation_nb == 2)
833 && (task->computation_amount[0] == 0.0)
834 && (task->computation_amount[1] == 0.0)) {
838 for (i = 0; i < task->workstation_nb * task->workstation_nb; i++) {
839 if (task->communication_amount[i] > 0.0) {
841 value = task->communication_amount[i];
846 surf_workstation_model->extension.
847 workstation.communicate(surf_workstations[0], surf_workstations[1],
851 if (!task->surf_action) {
852 double *computation_amount = xbt_new(double, task->workstation_nb);
853 double *communication_amount = xbt_new(double, task->workstation_nb *
854 task->workstation_nb);
856 memcpy(computation_amount, task->computation_amount, sizeof(double) *
857 task->workstation_nb);
858 memcpy(communication_amount, task->communication_amount,
859 sizeof(double) * task->workstation_nb * task->workstation_nb);
862 surf_workstation_model->extension.
863 workstation.execute_parallel_task(task->workstation_nb,
864 surf_workstations, computation_amount,
865 communication_amount, task->amount,
868 xbt_free(surf_workstations);
871 surf_workstation_model->action_data_set(task->surf_action, task);
873 DEBUG1("surf_action = %p", task->surf_action);
875 __SD_task_destroy_scheduling_data(task); /* now the scheduling data are not useful anymore */
876 __SD_task_set_state(task, SD_RUNNING);
877 xbt_assert2(__SD_task_is_running(task), "Bad state of task '%s': %d",
878 SD_task_get_name(task), SD_task_get_state(task));
882 /* Tries to run a task. This function is called by SD_simulate() when a scheduled task becomes SD_READY
883 * (ie when its dependencies are satisfied).
884 * If one of the workstations where the task is scheduled on is busy (in sequential mode),
885 * the task doesn't start.
886 * Returns whether the task has started.
888 int __SD_task_try_to_run(SD_task_t task)
893 SD_workstation_t workstation;
895 SD_CHECK_INIT_DONE();
896 xbt_assert0(task != NULL, "Invalid parameter");
897 xbt_assert2(__SD_task_is_ready(task),
898 "Task '%s' is not ready! Task state: %d",
899 SD_task_get_name(task), SD_task_get_state(task));
902 for (i = 0; i < task->workstation_nb; i++) {
903 can_start = !__SD_workstation_is_busy(task->workstation_list[i]);
906 DEBUG2("Task '%s' can start: %d", SD_task_get_name(task), can_start);
908 if (!can_start) { /* if the task cannot start and is not in the fifos yet */
909 for (i = 0; i < task->workstation_nb; i++) {
910 workstation = task->workstation_list[i];
911 if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
912 DEBUG2("Pushing task '%s' in the fifo of workstation '%s'",
913 SD_task_get_name(task), SD_workstation_get_name(workstation));
914 xbt_fifo_push(workstation->task_fifo, task);
917 __SD_task_set_state(task, SD_IN_FIFO);
918 xbt_assert2(__SD_task_is_in_fifo(task), "Bad state of task '%s': %d",
919 SD_task_get_name(task), SD_task_get_state(task));
920 DEBUG1("Task '%s' state is now SD_IN_FIFO", SD_task_get_name(task));
922 __SD_task_really_run(task);
928 /* This function is called by SD_simulate when a task is done.
929 * It updates task->state and task->action and executes if necessary the tasks
930 * which were waiting in fifos for the end of `task'
932 void __SD_task_just_done(SD_task_t task)
935 SD_workstation_t workstation;
938 int candidate_nb = 0;
939 int candidate_capacity = 8;
940 SD_task_t *candidates;
943 SD_CHECK_INIT_DONE();
944 xbt_assert0(task != NULL, "Invalid parameter");
945 xbt_assert1(__SD_task_is_running(task),
946 "The task must be running! Task state: %d",
947 SD_task_get_state(task));
948 xbt_assert1(task->workstation_list != NULL,
949 "Task '%s': workstation_list is NULL!", SD_task_get_name(task));
952 candidates = xbt_new(SD_task_t, 8);
954 __SD_task_set_state(task, SD_DONE);
955 surf_workstation_model->action_unref(task->surf_action);
956 task->surf_action = NULL;
958 DEBUG0("Looking for candidates");
960 /* if the task was executed on sequential workstations,
961 maybe we can execute the next task of the fifo for each workstation */
962 for (i = 0; i < task->workstation_nb; i++) {
963 workstation = task->workstation_list[i];
964 DEBUG2("Workstation '%s': access_mode = %d",
965 SD_workstation_get_name(workstation), workstation->access_mode);
966 if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
967 xbt_assert1(workstation->task_fifo != NULL,
968 "Workstation '%s' has sequential access but no fifo!",
969 SD_workstation_get_name(workstation));
970 xbt_assert2(workstation->current_task =
971 task, "Workstation '%s': current task should be '%s'",
972 SD_workstation_get_name(workstation),
973 SD_task_get_name(task));
975 /* the task is over so we can release the workstation */
976 workstation->current_task = NULL;
978 DEBUG0("Getting candidate in fifo");
980 xbt_fifo_get_item_content(xbt_fifo_get_first_item
981 (workstation->task_fifo));
983 if (candidate != NULL) {
984 DEBUG1("Candidate: '%s'", SD_task_get_name(candidate));
985 xbt_assert2(__SD_task_is_in_fifo(candidate),
986 "Bad state of candidate '%s': %d",
987 SD_task_get_name(candidate),
988 SD_task_get_state(candidate));
991 DEBUG1("Candidate in fifo: %p", candidate);
993 /* if there was a task waiting for my place */
994 if (candidate != NULL) {
995 /* Unfortunately, we are not sure yet that we can execute the task now,
996 because the task can be waiting more deeply in some other workstation's fifos...
997 So we memorize all candidate tasks, and then we will check for each candidate
998 whether or not all its workstations are available. */
1000 /* realloc if necessary */
1001 if (candidate_nb == candidate_capacity) {
1002 candidate_capacity *= 2;
1004 xbt_realloc(candidates, sizeof(SD_task_t) * candidate_capacity);
1007 /* register the candidate */
1008 candidates[candidate_nb++] = candidate;
1009 candidate->fifo_checked = 0;
1014 DEBUG1("Candidates found: %d", candidate_nb);
1016 /* now we check every candidate task */
1017 for (i = 0; i < candidate_nb; i++) {
1018 candidate = candidates[i];
1020 if (candidate->fifo_checked) {
1021 continue; /* we have already evaluated that task */
1024 xbt_assert2(__SD_task_is_in_fifo(candidate),
1025 "Bad state of candidate '%s': %d",
1026 SD_task_get_name(candidate), SD_task_get_state(candidate));
1028 for (j = 0; j < candidate->workstation_nb && can_start; j++) {
1029 workstation = candidate->workstation_list[j];
1031 /* I can start on this workstation if the workstation is shared
1032 or if I am the first task in the fifo */
1033 can_start = workstation->access_mode == SD_WORKSTATION_SHARED_ACCESS ||
1035 xbt_fifo_get_item_content(xbt_fifo_get_first_item
1036 (workstation->task_fifo));
1039 DEBUG2("Candidate '%s' can start: %d", SD_task_get_name(candidate),
1042 /* now we are sure that I can start! */
1044 for (j = 0; j < candidate->workstation_nb && can_start; j++) {
1045 workstation = candidate->workstation_list[j];
1047 /* update the fifo */
1048 if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
1049 candidate = xbt_fifo_shift(workstation->task_fifo); /* the return value is stored just for debugging */
1050 DEBUG1("Head of the fifo: '%s'",
1051 (candidate != NULL) ? SD_task_get_name(candidate) : "NULL");
1052 xbt_assert0(candidate == candidates[i],
1053 "Error in __SD_task_just_done: bad first task in the fifo");
1055 } /* for each workstation */
1057 /* finally execute the task */
1058 DEBUG2("Task '%s' state: %d", SD_task_get_name(candidate),
1059 SD_task_get_state(candidate));
1060 __SD_task_really_run(candidate);
1063 ("Calling __SD_task_is_running: task '%s', state set: %p, running_task_set: %p, is running: %d",
1064 SD_task_get_name(candidate), candidate->state_set,
1065 sd_global->running_task_set, __SD_task_is_running(candidate));
1066 xbt_assert2(__SD_task_is_running(candidate),
1067 "Bad state of task '%s': %d", SD_task_get_name(candidate),
1068 SD_task_get_state(candidate));
1069 DEBUG0("Okay, the task is running.");
1072 candidate->fifo_checked = 1;
1073 } /* for each candidate */
1075 xbt_free(candidates);
1078 /* Remove all dependencies associated with a task. This function is called when the task is destroyed.
1080 static void __SD_task_remove_dependencies(SD_task_t task)
1082 /* we must destroy the dependencies carefuly (with SD_dependency_remove)
1083 because each one is stored twice */
1084 SD_dependency_t dependency;
1085 while (xbt_dynar_length(task->tasks_before) > 0) {
1086 xbt_dynar_get_cpy(task->tasks_before, 0, &dependency);
1087 SD_task_dependency_remove(dependency->src, dependency->dst);
1090 while (xbt_dynar_length(task->tasks_after) > 0) {
1091 xbt_dynar_get_cpy(task->tasks_after, 0, &dependency);
1092 SD_task_dependency_remove(dependency->src, dependency->dst);
1097 * \brief Returns the start time of a task
1099 * The task state must be SD_RUNNING, SD_DONE or SD_FAILED.
1101 * \param task: a task
1102 * \return the start time of this task
1104 double SD_task_get_start_time(SD_task_t task)
1106 SD_CHECK_INIT_DONE();
1107 xbt_assert0(task != NULL, "Invalid parameter");
1108 if (task->surf_action)
1109 return surf_workstation_model->action_get_start_time(task->surf_action);
1111 return task->start_time;
1115 * \brief Returns the finish time of a task
1117 * The task state must be SD_RUNNING, SD_DONE or SD_FAILED.
1118 * If the state is not completed yet, the returned value is an
1119 * estimation of the task finish time. This value can fluctuate
1120 * until the task is completed.
1122 * \param task: a task
1123 * \return the start time of this task
1125 double SD_task_get_finish_time(SD_task_t task)
1127 SD_CHECK_INIT_DONE();
1128 xbt_assert0(task != NULL, "Invalid parameter");
1130 if (task->surf_action) /* should never happen as actions are destroyed right after their completion */
1131 return surf_workstation_model->action_get_finish_time(task->surf_action);
1133 return task->finish_time;
1137 * \brief Destroys a task.
1139 * The user data (if any) should have been destroyed first.
1141 * \param task the task you want to destroy
1142 * \see SD_task_create()
1144 void SD_task_destroy(SD_task_t task)
1146 SD_CHECK_INIT_DONE();
1147 xbt_assert0(task != NULL, "Invalid parameter");
1149 DEBUG1("Destroying task %s...", SD_task_get_name(task));
1151 __SD_task_remove_dependencies(task);
1152 /* if the task was scheduled or ready we have to free the scheduling parameters */
1153 if (__SD_task_is_scheduled_or_ready(task))
1154 __SD_task_destroy_scheduling_data(task);
1155 xbt_swag_remove(task,task->state_set);
1157 if (task->name != NULL)
1158 xbt_free(task->name);
1160 if (task->surf_action != NULL)
1161 surf_workstation_model->action_unref(task->surf_action);
1163 if (task->workstation_list != NULL)
1164 xbt_free(task->workstation_list);
1166 xbt_dynar_free(&task->tasks_before);
1167 xbt_dynar_free(&task->tasks_after);
1170 sd_global->task_number--;
1172 DEBUG0("Task destroyed.");
1176 static inline SD_task_t SD_task_create_sized(const char*name,void*data,double amount,int ws_count) {
1177 SD_task_t task = SD_task_create(name,data,amount);
1178 task->communication_amount = xbt_new0(double,ws_count*ws_count);
1179 task->computation_amount = xbt_new0(double,ws_count);
1180 task->workstation_nb = ws_count;
1181 task->workstation_list = xbt_new0(SD_workstation_t,ws_count);
1184 /** @brief create a end-to-end communication task that can then be auto-scheduled
1186 * Auto-scheduling mean that the task can be used with SD_task_schedulev(). This
1187 * allows to specify the task costs at creation, and decorelate them from the
1188 * scheduling process where you just specify which resource should deliver the
1191 * A end-to-end communication must be scheduled on 2 hosts, and the amount
1192 * specified at creation is sent from hosts[0] to hosts[1].
1194 SD_task_t SD_task_create_comm_e2e(const char*name, void *data, double amount) {
1195 SD_task_t res = SD_task_create_sized(name,data,amount,2);
1196 res->communication_amount[2] = amount;
1197 res->kind=SD_TASK_COMM_E2E;
1200 /** @brief create a sequential computation task that can then be auto-scheduled
1202 * Auto-scheduling mean that the task can be used with SD_task_schedulev(). This
1203 * allows to specify the task costs at creation, and decorelate them from the
1204 * scheduling process where you just specify which resource should deliver the
1207 * A sequential computation must be scheduled on 1 host, and the amount
1208 * specified at creation to be run on hosts[0].
1210 SD_task_t SD_task_create_comp_seq(const char*name, void *data, double amount) {
1211 SD_task_t res = SD_task_create_sized(name,data,amount,1);
1212 res->computation_amount[0]=amount;
1213 res->kind=SD_TASK_COMP_SEQ;
1217 /** @brief Auto-schedules a task.
1219 * Auto-scheduling mean that the task can be used with SD_task_schedulev(). This
1220 * allows to specify the task costs at creation, and decorelate them from the
1221 * scheduling process where you just specify which resource should deliver the
1224 * To be auto-schedulable, a task must be created with SD_task_create_comm_e2e() or
1225 * SD_task_create_comp_seq(). Check their definitions for the exact semantic of each
1229 * We should create tasks kind for the following categories:
1230 * - Point to point communication (done)
1231 * - Sequential computation (done)
1232 * - group communication (redistribution, several kinds)
1233 * - parallel tasks with no internal communication (one kind per speedup model such as amdal)
1234 * - idem+ internal communication. Task type not enough since we cannot store comm cost alongside to comp one)
1236 void SD_task_schedulev(SD_task_t task, int count, const SD_workstation_t*list) {
1238 xbt_assert1(task->kind != 0,"Task %s is not typed. Cannot automatically schedule it.",SD_task_get_name(task));
1239 switch(task->kind) {
1240 case SD_TASK_COMM_E2E:
1241 case SD_TASK_COMP_SEQ:
1242 xbt_assert(task->workstation_nb==count);
1243 for (i=0;i<count;i++)
1244 task->workstation_list[i]=list[i];
1245 SD_task_do_schedule(task);
1248 xbt_die(bprintf("Kind of task %s not supported by SD_task_schedulev()",
1249 SD_task_get_name(task)));
1251 if (task->kind == SD_TASK_COMM_E2E) {
1252 VERB4("Schedule comm task %s between %s -> %s. It costs %.f bytes",
1253 SD_task_get_name(task),
1254 SD_workstation_get_name(task->workstation_list[0]),SD_workstation_get_name(task->workstation_list[1]),
1255 task->communication_amount[2]);
1258 /* Iterate over all childs and parent being COMM_E2E to say where I am located (and start them if ready) */
1259 if (task->kind == SD_TASK_COMP_SEQ) {
1260 VERB3("Schedule computation task %s on %s. It costs %.f flops",
1261 SD_task_get_name(task),SD_workstation_get_name(task->workstation_list[0]),
1262 task->computation_amount[0]);
1263 SD_dependency_t dep;
1265 xbt_dynar_foreach(task->tasks_before,cpt,dep) {
1266 SD_task_t before = dep->src;
1267 if (before->kind == SD_TASK_COMM_E2E) {
1268 before->workstation_list[1] = task->workstation_list[0];
1269 if (before->workstation_list[0] && __SD_task_is_not_scheduled(before)) {
1270 SD_task_do_schedule(before);
1271 VERB4("Auto-Schedule comm task %s between %s -> %s. It costs %.f bytes",
1272 SD_task_get_name(before),
1273 SD_workstation_get_name(before->workstation_list[0]),SD_workstation_get_name(before->workstation_list[1]),
1274 before->communication_amount[2]);
1278 xbt_dynar_foreach(task->tasks_after,cpt,dep) {
1279 SD_task_t after = dep->dst;
1280 if (after->kind == SD_TASK_COMM_E2E) {
1281 after->workstation_list[0] = task->workstation_list[0];
1282 if (after->workstation_list[1] && __SD_task_is_not_scheduled(after)) {
1283 SD_task_do_schedule(after);
1284 VERB4("Auto-Schedule comm task %s between %s -> %s. It costs %.f bytes",
1285 SD_task_get_name(after),
1286 SD_workstation_get_name(after->workstation_list[0]),SD_workstation_get_name(after->workstation_list[1]),
1287 after->communication_amount[2]);
1294 /** @brief autoschedule a task on a list of workstations
1296 * This function is very similar to SD_task_schedulev(),
1297 * but takes the list of workstations to schedule onto as separate parameters.
1298 * It builds a proper vector of workstations and then call SD_task_schedulev()
1300 void SD_task_schedulel(SD_task_t task, int count, ...) {
1302 SD_workstation_t *list=xbt_new(SD_workstation_t,count);
1305 for (i=0;i<count;i++) {
1306 list[i] = va_arg(ap,SD_workstation_t);
1309 SD_task_schedulev(task,count,list);