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);
37 task->kind = SD_TASK_NOT_TYPED;
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 Allows to change the name of a task */
172 void SD_task_set_name(SD_task_t task, const char *name) {
173 xbt_free(task->name);
174 task->name = xbt_strdup(name);
177 /** @brief Returns the dynar of the parents of a task
180 * \return a newly allocated dynar comprising the parents of this task
183 xbt_dynar_t SD_task_get_parents(SD_task_t task)
188 SD_CHECK_INIT_DONE();
189 xbt_assert0(task != NULL, "Invalid parameter");
191 parents = xbt_dynar_new(sizeof(SD_task_t), NULL);
192 xbt_dynar_foreach(task->tasks_before, i, dep){
193 xbt_dynar_push(parents, &(dep->src));
198 /** @brief Returns the dynar of the parents of a task
201 * \return a newly allocated dynar comprising the parents of this task
203 xbt_dynar_t SD_task_get_children(SD_task_t task)
206 xbt_dynar_t children;
208 SD_CHECK_INIT_DONE();
209 xbt_assert0(task != NULL, "Invalid parameter");
211 children = xbt_dynar_new(sizeof(SD_task_t), NULL);
212 xbt_dynar_foreach(task->tasks_after, i, dep){
213 xbt_dynar_push(children, &(dep->dst));
219 * \brief Returns the amount of workstations involved in a task
221 * Only call this on already scheduled tasks!
224 int SD_task_get_workstation_count(SD_task_t task)
226 SD_CHECK_INIT_DONE();
227 xbt_assert0(task != NULL, "Invalid parameter");
228 // xbt_assert1( task->state_set != sd_global->scheduled_task_set,
229 // "Unscheduled task %s", task->name);
230 return task->workstation_nb;
234 * \brief Returns the list of workstations involved in a task
236 * Only call this on already scheduled tasks!
239 SD_workstation_t* SD_task_get_workstation_list(SD_task_t task)
241 SD_CHECK_INIT_DONE();
242 xbt_assert0(task != NULL, "Invalid parameter");
243 //xbt_assert1( task->state_set != sd_global->scheduled_task_set,
244 // "Unscheduled task %s", task->name);
245 return task->workstation_list;
249 * \brief Returns the total amount of work contained in a task
252 * \return the total amount of work (computation or data transfer) for this task
253 * \see SD_task_get_remaining_amount()
255 double SD_task_get_amount(SD_task_t task)
257 SD_CHECK_INIT_DONE();
258 xbt_assert0(task != NULL, "Invalid parameter");
263 * \brief Returns the remaining amount work to do till the completion of a task
266 * \return the remaining amount of work (computation or data transfer) of this task
267 * \see SD_task_get_amount()
269 double SD_task_get_remaining_amount(SD_task_t task)
271 SD_CHECK_INIT_DONE();
272 xbt_assert0(task != NULL, "Invalid parameter");
274 if (task->surf_action)
275 return surf_workstation_model->get_remains(task->surf_action);
277 return task->remains;
280 int SD_task_get_kind(SD_task_t task) {
284 /** @brief Displays debugging informations about a task */
285 void SD_task_dump(SD_task_t task)
287 unsigned int counter;
288 SD_dependency_t dependency;
291 INFO1("Displaying task %s",SD_task_get_name(task));
292 statename=bprintf("%s %s %s %s %s %s %s",
293 (task->state&SD_NOT_SCHEDULED?"not scheduled":""),
294 (task->state&SD_SCHEDULED?"scheduled":""),
295 (task->state&SD_READY?"ready":"not ready"),
296 (task->state&SD_IN_FIFO?"in fifo":""),
297 (task->state&SD_RUNNING?"running":""),
298 (task->state&SD_DONE?"done":""),
299 (task->state&SD_FAILED?"failed":""));
300 INFO1(" - state: %s",statename);
305 case SD_TASK_COMM_E2E:
306 INFO0(" - kind: end-to-end communication");
308 case SD_TASK_COMP_SEQ:
309 INFO0(" - kind: sequential computation");
312 INFO1(" - (unknown kind %d)",task->kind);
315 INFO1(" - amount: %.0f",SD_task_get_amount(task));
316 if (xbt_dynar_length(task->tasks_before)) {
317 INFO0(" - pre-dependencies:");
318 xbt_dynar_foreach(task->tasks_before,counter,dependency) {
319 INFO1(" %s",SD_task_get_name(dependency->src));
322 if (xbt_dynar_length(task->tasks_after)) {
323 INFO0(" - post-dependencies:");
324 xbt_dynar_foreach(task->tasks_after,counter,dependency) {
325 INFO1(" %s",SD_task_get_name(dependency->dst));
329 /** @brief Dumps the task in dotty formalism into the FILE* passed as second argument */
330 void SD_task_dotty(SD_task_t task,void* out) {
331 unsigned int counter;
332 SD_dependency_t dependency;
333 fprintf(out, " T%p [label=\"%.20s\"",task, task->name);
335 case SD_TASK_COMM_E2E:
336 fprintf(out,", shape=box");
338 case SD_TASK_COMP_SEQ:
339 fprintf(out,", shape=circle");
342 xbt_die("Unknown task type!");
345 xbt_dynar_foreach(task->tasks_before,counter,dependency) {
346 fprintf(out," T%p -> T%p;\n",dependency->src, dependency->dst);
350 /* Destroys a dependency between two tasks.
352 static void __SD_task_dependency_destroy(void *dependency)
354 if (((SD_dependency_t) dependency)->name != NULL)
355 xbt_free(((SD_dependency_t) dependency)->name);
356 xbt_free(dependency);
360 * \brief Adds a dependency between two tasks
362 * \a dst will depend on \a src, ie \a dst will not start before \a src is finished.
363 * Their \ref e_SD_task_state_t "state" must be #SD_NOT_SCHEDULED, #SD_SCHEDULED or #SD_READY.
365 * \param name the name of the new dependency (can be \c NULL)
366 * \param data the user data you want to associate with this dependency (can be \c NULL)
367 * \param src the task which must be executed first
368 * \param dst the task you want to make depend on \a src
369 * \see SD_task_dependency_remove()
371 void SD_task_dependency_add(const char *name, void *data, SD_task_t src,
378 SD_dependency_t dependency;
380 SD_CHECK_INIT_DONE();
381 xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
383 dynar = src->tasks_after;
384 length = xbt_dynar_length(dynar);
388 "Cannot add a dependency between task '%s' and itself",
389 SD_task_get_name(src));
391 if (!__SD_task_is_not_scheduled(src)
392 && !__SD_task_is_scheduled_or_ready(src))
394 "Task '%s' must be SD_NOT_SCHEDULED, SD_SCHEDULED or SD_READY",
395 SD_task_get_name(src));
397 if (!__SD_task_is_not_scheduled(dst)
398 && !__SD_task_is_scheduled_or_ready(dst))
400 "Task '%s' must be SD_NOT_SCHEDULED, SD_SCHEDULED or SD_READY",
401 SD_task_get_name(dst));
403 DEBUG2("SD_task_dependency_add: src = %s, dst = %s", SD_task_get_name(src),
404 SD_task_get_name(dst));
405 for (i = 0; i < length && !found; i++) {
406 xbt_dynar_get_cpy(dynar, i, &dependency);
407 found = (dependency->dst == dst);
408 DEBUG2("Dependency %d: dependency->dst = %s", i,
409 SD_task_get_name(dependency->dst));
414 "A dependency already exists between task '%s' and task '%s'",
415 SD_task_get_name(src), SD_task_get_name(dst));
417 dependency = xbt_new(s_SD_dependency_t, 1);
419 dependency->name = xbt_strdup(name); /* xbt_strdup is cleaver enough to deal with NULL args itself */
420 dependency->data = data;
421 dependency->src = src;
422 dependency->dst = dst;
424 /* src must be executed before dst */
425 xbt_dynar_push(src->tasks_after, &dependency);
426 xbt_dynar_push(dst->tasks_before, &dependency);
428 /* if the task was ready, then dst->tasks_before is not empty anymore,
429 so we must go back to state SD_SCHEDULED */
430 if (__SD_task_is_ready(dst)) {
431 DEBUG1("SD_task_dependency_add: %s was ready and becomes scheduled!",
432 SD_task_get_name(dst));
433 __SD_task_set_state(dst, SD_SCHEDULED);
436 /* __SD_print_dependencies(src);
437 __SD_print_dependencies(dst); */
441 * \brief Indacates whether there is a dependency between two tasks.
444 * \param dst a task depending on \a src
446 * If src is NULL, checks whether dst has any pre-dependency.
447 * If dst is NULL, checks whether src has any post-dependency.
449 int SD_task_dependency_exists(SD_task_t src, SD_task_t dst)
451 unsigned int counter;
452 SD_dependency_t dependency;
454 SD_CHECK_INIT_DONE();
455 xbt_assert0(src != NULL || dst != NULL, "Invalid parameter: both src and dst are NULL");
459 xbt_dynar_foreach(src->tasks_after,counter,dependency) {
460 if (dependency->dst == dst)
464 return xbt_dynar_length(src->tasks_after);
467 return xbt_dynar_length(dst->tasks_before);
473 * \brief Remove a dependency between two tasks
476 * \param dst a task depending on \a src
477 * \see SD_task_dependency_add()
479 void SD_task_dependency_remove(SD_task_t src, SD_task_t dst)
486 SD_dependency_t dependency;
488 SD_CHECK_INIT_DONE();
489 xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
491 /* remove the dependency from src->tasks_after */
492 dynar = src->tasks_after;
493 length = xbt_dynar_length(dynar);
495 for (i = 0; i < length && !found; i++) {
496 xbt_dynar_get_cpy(dynar, i, &dependency);
497 if (dependency->dst == dst) {
498 xbt_dynar_remove_at(dynar, i, NULL);
504 "No dependency found between task '%s' and '%s': task '%s' is not a successor of task '%s'",
505 SD_task_get_name(src), SD_task_get_name(dst),
506 SD_task_get_name(dst), SD_task_get_name(src));
508 /* remove the dependency from dst->tasks_before */
509 dynar = dst->tasks_before;
510 length = xbt_dynar_length(dynar);
513 for (i = 0; i < length && !found; i++) {
514 xbt_dynar_get_cpy(dynar, i, &dependency);
515 if (dependency->src == src) {
516 xbt_dynar_remove_at(dynar, i, NULL);
517 __SD_task_dependency_destroy(dependency);
521 /* should never happen... */
523 "SimDag error: task '%s' is a successor of '%s' but task '%s' is not a predecessor of task '%s'",
524 SD_task_get_name(dst), SD_task_get_name(src),
525 SD_task_get_name(src), SD_task_get_name(dst));
527 /* if the task was scheduled and dst->tasks_before is empty now, we can make it ready */
528 if (xbt_dynar_length(dst->tasks_before) == 0 && __SD_task_is_scheduled(dst))
529 __SD_task_set_state(dst, SD_READY);
531 /* __SD_print_dependencies(src);
532 __SD_print_dependencies(dst); */
536 * \brief Returns the user data associated with a dependency between two tasks
539 * \param dst a task depending on \a src
540 * \return the user data associated with this dependency (can be \c NULL)
541 * \see SD_task_dependency_add()
543 void *SD_task_dependency_get_data(SD_task_t src, SD_task_t dst)
550 SD_dependency_t dependency;
553 SD_CHECK_INIT_DONE();
554 xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
556 dynar = src->tasks_after;
557 length = xbt_dynar_length(dynar);
559 for (i = 0; i < length && !found; i++) {
560 xbt_dynar_get_cpy(dynar, i, &dependency);
561 found = (dependency->dst == dst);
564 THROW2(arg_error, 0, "No dependency found between task '%s' and '%s'",
565 SD_task_get_name(src), SD_task_get_name(dst));
566 return dependency->data;
569 /* temporary function for debugging */
570 static void __SD_print_watch_points(SD_task_t task)
572 static const int state_masks[] =
573 { SD_SCHEDULED, SD_RUNNING, SD_READY, SD_DONE, SD_FAILED };
574 static const char *state_names[] =
575 { "scheduled", "running", "ready", "done", "failed" };
578 INFO2("Task '%s' watch points (%x): ", SD_task_get_name(task),
582 for (i = 0; i < 5; i++) {
583 if (task->watch_points & state_masks[i])
584 INFO1("%s ", state_names[i]);
589 * \brief Adds a watch point to a task
591 * SD_simulate() will stop as soon as the \ref e_SD_task_state_t "state" of this
592 * task becomes the one given in argument. The
593 * watch point is then automatically removed.
596 * \param state the \ref e_SD_task_state_t "state" you want to watch
597 * (cannot be #SD_NOT_SCHEDULED)
598 * \see SD_task_unwatch()
600 void SD_task_watch(SD_task_t task, e_SD_task_state_t state)
602 SD_CHECK_INIT_DONE();
603 xbt_assert0(task != NULL, "Invalid parameter");
605 if (state & SD_NOT_SCHEDULED)
607 "Cannot add a watch point for state SD_NOT_SCHEDULED");
609 task->watch_points = task->watch_points | state;
610 /* __SD_print_watch_points(task); */
614 * \brief Removes a watch point from a task
617 * \param state the \ref e_SD_task_state_t "state" you no longer want to watch
618 * \see SD_task_watch()
620 void SD_task_unwatch(SD_task_t task, e_SD_task_state_t state)
622 SD_CHECK_INIT_DONE();
623 xbt_assert0(task != NULL, "Invalid parameter");
624 xbt_assert0(state != SD_NOT_SCHEDULED,
625 "SimDag error: Cannot have a watch point for state SD_NOT_SCHEDULED");
627 task->watch_points = task->watch_points & ~state;
628 /* __SD_print_watch_points(task); */
632 * \brief Returns an approximative estimation of the execution time of a task.
634 * The estimation is very approximative because the value returned is the time
635 * the task would take if it was executed now and if it was the only task.
637 * \param task the task to evaluate
638 * \param workstation_nb number of workstations on which the task would be executed
639 * \param workstation_list the workstations on which the task would be executed
640 * \param computation_amount computation amount for each workstation
641 * \param communication_amount communication amount between each pair of workstations
644 double SD_task_get_execution_time(SD_task_t task,
646 const SD_workstation_t * workstation_list,
647 const double *computation_amount,
648 const double *communication_amount)
650 double time, max_time = 0.0;
652 SD_CHECK_INIT_DONE();
653 xbt_assert0(task != NULL && workstation_nb > 0 && workstation_list != NULL,
654 "Invalid parameter");
656 /* the task execution time is the maximum execution time of the parallel tasks */
658 for (i = 0; i < workstation_nb; i++) {
660 if (computation_amount != NULL)
662 SD_workstation_get_computation_time(workstation_list[i],
663 computation_amount[i]);
665 if (communication_amount != NULL)
666 for (j = 0; j < workstation_nb; j++) {
668 SD_route_get_communication_time(workstation_list[i],
670 communication_amount[i *
675 if (time > max_time) {
681 static inline void SD_task_do_schedule(SD_task_t task) {
682 SD_CHECK_INIT_DONE();
684 if (!__SD_task_is_not_scheduled(task))
685 THROW1(arg_error, 0, "Task '%s' has already been scheduled",
686 SD_task_get_name(task));
688 /* update the task state */
689 if (xbt_dynar_length(task->tasks_before) == 0)
690 __SD_task_set_state(task, SD_READY);
692 __SD_task_set_state(task, SD_SCHEDULED);
696 * \brief Schedules a task
698 * The task state must be #SD_NOT_SCHEDULED.
699 * Once scheduled, a task will be executed as soon as possible in SD_simulate(),
700 * i.e. when its dependencies are satisfied.
702 * \param task the task you want to schedule
703 * \param workstation_nb number of workstations on which the task will be executed
704 * \param workstation_list the workstations on which the task will be executed
705 * \param computation_amount computation amount for each workstation
706 * \param communication_amount communication amount between each pair of workstations
707 * \param rate task execution speed rate
708 * \see SD_task_unschedule()
710 void SD_task_schedule(SD_task_t task, int workstation_count,
711 const SD_workstation_t * workstation_list,
712 const double *computation_amount,
713 const double *communication_amount, double rate)
715 xbt_assert0(workstation_count > 0, "workstation_nb must be positive");
717 int communication_nb;
719 task->workstation_nb = workstation_count;
722 if (computation_amount) {
723 task->computation_amount = xbt_new(double, workstation_count);
724 memcpy(task->computation_amount, computation_amount,
725 sizeof(double) * workstation_count);
727 task->computation_amount = NULL;
730 communication_nb = workstation_count * workstation_count;
731 if (communication_amount) {
732 task->communication_amount = xbt_new(double, communication_nb);
733 memcpy(task->communication_amount, communication_amount,
734 sizeof(double) * communication_nb);
736 task->communication_amount = NULL;
739 task->workstation_list = xbt_new(SD_workstation_t, workstation_count);
740 memcpy(task->workstation_list, workstation_list,
741 sizeof(SD_workstation_t) * workstation_count);
743 SD_task_do_schedule(task);
746 * \brief Unschedules a task
748 * The task state must be #SD_SCHEDULED, #SD_READY, #SD_RUNNING or #SD_FAILED.
749 * If you call this function, the task state becomes #SD_NOT_SCHEDULED.
750 * Call SD_task_schedule() to schedule it again.
752 * \param task the task you want to unschedule
753 * \see SD_task_schedule()
755 void SD_task_unschedule(SD_task_t task)
757 SD_CHECK_INIT_DONE();
758 xbt_assert0(task != NULL, "Invalid parameter");
760 if (task->state_set != sd_global->scheduled_task_set &&
761 task->state_set != sd_global->ready_task_set &&
762 task->state_set != sd_global->running_task_set &&
763 task->state_set != sd_global->failed_task_set)
765 "Task %s: the state must be SD_SCHEDULED, SD_READY, SD_RUNNING or SD_FAILED",
766 SD_task_get_name(task));
768 if (__SD_task_is_scheduled_or_ready(task) /* if the task is scheduled or ready */
769 && task->kind == SD_TASK_NOT_TYPED) /* Don't free scheduling data for typed tasks */
770 __SD_task_destroy_scheduling_data(task);
772 if (__SD_task_is_running(task)) /* the task should become SD_FAILED */
773 surf_workstation_model->action_cancel(task->surf_action);
775 __SD_task_set_state(task, SD_NOT_SCHEDULED);
776 task->remains = task->amount;
777 task->start_time = -1.0;
780 /* Destroys the data memorised by SD_task_schedule. Task state must be SD_SCHEDULED or SD_READY.
782 static void __SD_task_destroy_scheduling_data(SD_task_t task)
784 SD_CHECK_INIT_DONE();
785 if (!__SD_task_is_scheduled_or_ready(task) && !__SD_task_is_in_fifo(task))
787 "Task '%s' must be SD_SCHEDULED, SD_READY or SD_IN_FIFO",
788 SD_task_get_name(task));
790 xbt_free(task->computation_amount);
791 xbt_free(task->communication_amount);
792 task->computation_amount = task->communication_amount = NULL;
795 /* Runs a task. This function is directly called by __SD_task_try_to_run if the task
796 * doesn't have to wait in fifos. Otherwise, it is called by __SD_task_just_done when
797 * the task gets out of its fifos.
799 void __SD_task_really_run(SD_task_t task)
803 void **surf_workstations;
805 SD_CHECK_INIT_DONE();
806 xbt_assert0(task != NULL, "Invalid parameter");
807 xbt_assert2(__SD_task_is_ready_or_in_fifo(task),
808 "Task '%s' is not ready or in a fifo! Task state: %d",
809 SD_task_get_name(task), SD_task_get_state(task));
810 xbt_assert1(task->workstation_list != NULL,
811 "Task '%s': workstation_list is NULL!", SD_task_get_name(task));
815 DEBUG1("Really running task '%s'", SD_task_get_name(task));
817 /* set this task as current task for the workstations in sequential mode */
818 for (i = 0; i < task->workstation_nb; i++) {
819 if (SD_workstation_get_access_mode(task->workstation_list[i]) ==
820 SD_WORKSTATION_SEQUENTIAL_ACCESS) {
821 task->workstation_list[i]->current_task = task;
822 xbt_assert0(__SD_workstation_is_busy(task->workstation_list[i]),
823 "The workstation should be busy now");
827 DEBUG1("Task '%s' set as current task for its workstations",
828 SD_task_get_name(task));
832 /* we have to create a Surf workstation array instead of the SimDag workstation array */
833 surf_workstations = xbt_new(void *, task->workstation_nb);
835 for (i = 0; i < task->workstation_nb; i++)
836 surf_workstations[i] = task->workstation_list[i]->surf_workstation;
838 /* 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 */
839 #define cost_or_zero(array,pos) ((array)?(array)[pos]:0.0)
841 task->surf_action = NULL;
842 if ((task->workstation_nb == 1) && (cost_or_zero(task->communication_amount,0) == 0.0)) {
844 surf_workstation_model->extension.
845 workstation.execute(surf_workstations[0], cost_or_zero(task->computation_amount,0));
846 } else if ((task->workstation_nb == 1)
847 && (cost_or_zero(task->computation_amount,0) == 0.0)) {
850 surf_workstation_model->extension.
851 workstation.communicate(surf_workstations[0], surf_workstations[0],
852 cost_or_zero(task->communication_amount,0), task->rate);
853 } else if ((task->workstation_nb == 2)
854 && (cost_or_zero(task->computation_amount,0) == 0.0)
855 && (cost_or_zero(task->computation_amount,1) == 0.0)) {
859 for (i = 0; i < task->workstation_nb * task->workstation_nb; i++) {
860 if (cost_or_zero(task->communication_amount,i) > 0.0) {
862 value = cost_or_zero(task->communication_amount,i);
867 surf_workstation_model->extension.
868 workstation.communicate(surf_workstations[0], surf_workstations[1],
874 if (!task->surf_action) {
875 double *computation_amount = xbt_new(double, task->workstation_nb);
876 double *communication_amount = xbt_new(double, task->workstation_nb *
877 task->workstation_nb);
879 memcpy(computation_amount, task->computation_amount, sizeof(double) *
880 task->workstation_nb);
881 memcpy(communication_amount, task->communication_amount,
882 sizeof(double) * task->workstation_nb * task->workstation_nb);
885 surf_workstation_model->extension.
886 workstation.execute_parallel_task(task->workstation_nb,
887 surf_workstations, computation_amount,
888 communication_amount, task->amount,
891 xbt_free(surf_workstations);
894 surf_workstation_model->action_data_set(task->surf_action, task);
896 DEBUG1("surf_action = %p", task->surf_action);
898 __SD_task_destroy_scheduling_data(task); /* now the scheduling data are not useful anymore */
899 __SD_task_set_state(task, SD_RUNNING);
900 xbt_assert2(__SD_task_is_running(task), "Bad state of task '%s': %d",
901 SD_task_get_name(task), SD_task_get_state(task));
905 /* Tries to run a task. This function is called by SD_simulate() when a scheduled task becomes SD_READY
906 * (ie when its dependencies are satisfied).
907 * If one of the workstations where the task is scheduled on is busy (in sequential mode),
908 * the task doesn't start.
909 * Returns whether the task has started.
911 int __SD_task_try_to_run(SD_task_t task)
916 SD_workstation_t workstation;
918 SD_CHECK_INIT_DONE();
919 xbt_assert0(task != NULL, "Invalid parameter");
920 xbt_assert2(__SD_task_is_ready(task),
921 "Task '%s' is not ready! Task state: %d",
922 SD_task_get_name(task), SD_task_get_state(task));
925 for (i = 0; i < task->workstation_nb; i++) {
926 can_start = !__SD_workstation_is_busy(task->workstation_list[i]);
929 DEBUG2("Task '%s' can start: %d", SD_task_get_name(task), can_start);
931 if (!can_start) { /* if the task cannot start and is not in the fifos yet */
932 for (i = 0; i < task->workstation_nb; i++) {
933 workstation = task->workstation_list[i];
934 if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
935 DEBUG2("Pushing task '%s' in the fifo of workstation '%s'",
936 SD_task_get_name(task), SD_workstation_get_name(workstation));
937 xbt_fifo_push(workstation->task_fifo, task);
940 __SD_task_set_state(task, SD_IN_FIFO);
941 xbt_assert2(__SD_task_is_in_fifo(task), "Bad state of task '%s': %d",
942 SD_task_get_name(task), SD_task_get_state(task));
943 DEBUG1("Task '%s' state is now SD_IN_FIFO", SD_task_get_name(task));
945 __SD_task_really_run(task);
951 /* This function is called by SD_simulate when a task is done.
952 * It updates task->state and task->action and executes if necessary the tasks
953 * which were waiting in fifos for the end of `task'
955 void __SD_task_just_done(SD_task_t task)
958 SD_workstation_t workstation;
961 int candidate_nb = 0;
962 int candidate_capacity = 8;
963 SD_task_t *candidates;
966 SD_CHECK_INIT_DONE();
967 xbt_assert0(task != NULL, "Invalid parameter");
968 xbt_assert1(__SD_task_is_running(task),
969 "The task must be running! Task state: %d",
970 SD_task_get_state(task));
971 xbt_assert1(task->workstation_list != NULL,
972 "Task '%s': workstation_list is NULL!", SD_task_get_name(task));
975 candidates = xbt_new(SD_task_t, 8);
977 __SD_task_set_state(task, SD_DONE);
978 surf_workstation_model->action_unref(task->surf_action);
979 task->surf_action = NULL;
981 DEBUG0("Looking for candidates");
983 /* if the task was executed on sequential workstations,
984 maybe we can execute the next task of the fifo for each workstation */
985 for (i = 0; i < task->workstation_nb; i++) {
986 workstation = task->workstation_list[i];
987 DEBUG2("Workstation '%s': access_mode = %d",
988 SD_workstation_get_name(workstation), workstation->access_mode);
989 if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
990 xbt_assert1(workstation->task_fifo != NULL,
991 "Workstation '%s' has sequential access but no fifo!",
992 SD_workstation_get_name(workstation));
993 xbt_assert2(workstation->current_task =
994 task, "Workstation '%s': current task should be '%s'",
995 SD_workstation_get_name(workstation),
996 SD_task_get_name(task));
998 /* the task is over so we can release the workstation */
999 workstation->current_task = NULL;
1001 DEBUG0("Getting candidate in fifo");
1003 xbt_fifo_get_item_content(xbt_fifo_get_first_item
1004 (workstation->task_fifo));
1006 if (candidate != NULL) {
1007 DEBUG1("Candidate: '%s'", SD_task_get_name(candidate));
1008 xbt_assert2(__SD_task_is_in_fifo(candidate),
1009 "Bad state of candidate '%s': %d",
1010 SD_task_get_name(candidate),
1011 SD_task_get_state(candidate));
1014 DEBUG1("Candidate in fifo: %p", candidate);
1016 /* if there was a task waiting for my place */
1017 if (candidate != NULL) {
1018 /* Unfortunately, we are not sure yet that we can execute the task now,
1019 because the task can be waiting more deeply in some other workstation's fifos...
1020 So we memorize all candidate tasks, and then we will check for each candidate
1021 whether or not all its workstations are available. */
1023 /* realloc if necessary */
1024 if (candidate_nb == candidate_capacity) {
1025 candidate_capacity *= 2;
1027 xbt_realloc(candidates, sizeof(SD_task_t) * candidate_capacity);
1030 /* register the candidate */
1031 candidates[candidate_nb++] = candidate;
1032 candidate->fifo_checked = 0;
1037 DEBUG1("Candidates found: %d", candidate_nb);
1039 /* now we check every candidate task */
1040 for (i = 0; i < candidate_nb; i++) {
1041 candidate = candidates[i];
1043 if (candidate->fifo_checked) {
1044 continue; /* we have already evaluated that task */
1047 xbt_assert2(__SD_task_is_in_fifo(candidate),
1048 "Bad state of candidate '%s': %d",
1049 SD_task_get_name(candidate), SD_task_get_state(candidate));
1051 for (j = 0; j < candidate->workstation_nb && can_start; j++) {
1052 workstation = candidate->workstation_list[j];
1054 /* I can start on this workstation if the workstation is shared
1055 or if I am the first task in the fifo */
1056 can_start = workstation->access_mode == SD_WORKSTATION_SHARED_ACCESS ||
1058 xbt_fifo_get_item_content(xbt_fifo_get_first_item
1059 (workstation->task_fifo));
1062 DEBUG2("Candidate '%s' can start: %d", SD_task_get_name(candidate),
1065 /* now we are sure that I can start! */
1067 for (j = 0; j < candidate->workstation_nb && can_start; j++) {
1068 workstation = candidate->workstation_list[j];
1070 /* update the fifo */
1071 if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
1072 candidate = xbt_fifo_shift(workstation->task_fifo); /* the return value is stored just for debugging */
1073 DEBUG1("Head of the fifo: '%s'",
1074 (candidate != NULL) ? SD_task_get_name(candidate) : "NULL");
1075 xbt_assert0(candidate == candidates[i],
1076 "Error in __SD_task_just_done: bad first task in the fifo");
1078 } /* for each workstation */
1080 /* finally execute the task */
1081 DEBUG2("Task '%s' state: %d", SD_task_get_name(candidate),
1082 SD_task_get_state(candidate));
1083 __SD_task_really_run(candidate);
1086 ("Calling __SD_task_is_running: task '%s', state set: %p, running_task_set: %p, is running: %d",
1087 SD_task_get_name(candidate), candidate->state_set,
1088 sd_global->running_task_set, __SD_task_is_running(candidate));
1089 xbt_assert2(__SD_task_is_running(candidate),
1090 "Bad state of task '%s': %d", SD_task_get_name(candidate),
1091 SD_task_get_state(candidate));
1092 DEBUG0("Okay, the task is running.");
1095 candidate->fifo_checked = 1;
1096 } /* for each candidate */
1098 xbt_free(candidates);
1101 /* Remove all dependencies associated with a task. This function is called when the task is destroyed.
1103 static void __SD_task_remove_dependencies(SD_task_t task)
1105 /* we must destroy the dependencies carefuly (with SD_dependency_remove)
1106 because each one is stored twice */
1107 SD_dependency_t dependency;
1108 while (xbt_dynar_length(task->tasks_before) > 0) {
1109 xbt_dynar_get_cpy(task->tasks_before, 0, &dependency);
1110 SD_task_dependency_remove(dependency->src, dependency->dst);
1113 while (xbt_dynar_length(task->tasks_after) > 0) {
1114 xbt_dynar_get_cpy(task->tasks_after, 0, &dependency);
1115 SD_task_dependency_remove(dependency->src, dependency->dst);
1120 * \brief Returns the start time of a task
1122 * The task state must be SD_RUNNING, SD_DONE or SD_FAILED.
1124 * \param task: a task
1125 * \return the start time of this task
1127 double SD_task_get_start_time(SD_task_t task)
1129 SD_CHECK_INIT_DONE();
1130 xbt_assert0(task != NULL, "Invalid parameter");
1131 if (task->surf_action)
1132 return surf_workstation_model->action_get_start_time(task->surf_action);
1134 return task->start_time;
1138 * \brief Returns the finish time of a task
1140 * The task state must be SD_RUNNING, SD_DONE or SD_FAILED.
1141 * If the state is not completed yet, the returned value is an
1142 * estimation of the task finish time. This value can fluctuate
1143 * until the task is completed.
1145 * \param task: a task
1146 * \return the start time of this task
1148 double SD_task_get_finish_time(SD_task_t task)
1150 SD_CHECK_INIT_DONE();
1151 xbt_assert0(task != NULL, "Invalid parameter");
1153 if (task->surf_action) /* should never happen as actions are destroyed right after their completion */
1154 return surf_workstation_model->action_get_finish_time(task->surf_action);
1156 return task->finish_time;
1160 * \brief Destroys a task.
1162 * The user data (if any) should have been destroyed first.
1164 * \param task the task you want to destroy
1165 * \see SD_task_create()
1167 void SD_task_destroy(SD_task_t task)
1169 SD_CHECK_INIT_DONE();
1170 xbt_assert0(task != NULL, "Invalid parameter");
1172 DEBUG1("Destroying task %s...", SD_task_get_name(task));
1174 __SD_task_remove_dependencies(task);
1175 /* if the task was scheduled or ready we have to free the scheduling parameters */
1176 if (__SD_task_is_scheduled_or_ready(task))
1177 __SD_task_destroy_scheduling_data(task);
1178 xbt_swag_remove(task,task->state_set);
1180 if (task->name != NULL)
1181 xbt_free(task->name);
1183 if (task->surf_action != NULL)
1184 surf_workstation_model->action_unref(task->surf_action);
1186 if (task->workstation_list != NULL)
1187 xbt_free(task->workstation_list);
1189 if (task->communication_amount)
1190 xbt_free(task->communication_amount);
1192 if (task->computation_amount)
1193 xbt_free(task->computation_amount);
1195 xbt_dynar_free(&task->tasks_before);
1196 xbt_dynar_free(&task->tasks_after);
1199 sd_global->task_number--;
1201 DEBUG0("Task destroyed.");
1205 static inline SD_task_t SD_task_create_sized(const char*name,void*data,double amount,int ws_count) {
1206 SD_task_t task = SD_task_create(name,data,amount);
1207 task->communication_amount = xbt_new0(double,ws_count*ws_count);
1208 task->computation_amount = xbt_new0(double,ws_count);
1209 task->workstation_nb = ws_count;
1210 task->workstation_list = xbt_new0(SD_workstation_t,ws_count);
1213 /** @brief create a end-to-end communication task that can then be auto-scheduled
1215 * Auto-scheduling mean that the task can be used with SD_task_schedulev(). This
1216 * allows to specify the task costs at creation, and decorelate them from the
1217 * scheduling process where you just specify which resource should deliver the
1220 * A end-to-end communication must be scheduled on 2 hosts, and the amount
1221 * specified at creation is sent from hosts[0] to hosts[1].
1223 SD_task_t SD_task_create_comm_e2e(const char*name, void *data, double amount) {
1224 SD_task_t res = SD_task_create_sized(name,data,amount,2);
1225 res->communication_amount[2] = amount;
1226 res->kind=SD_TASK_COMM_E2E;
1229 /** @brief create a sequential computation task that can then be auto-scheduled
1231 * Auto-scheduling mean that the task can be used with SD_task_schedulev(). This
1232 * allows to specify the task costs at creation, and decorelate them from the
1233 * scheduling process where you just specify which resource should deliver the
1236 * A sequential computation must be scheduled on 1 host, and the amount
1237 * specified at creation to be run on hosts[0].
1239 SD_task_t SD_task_create_comp_seq(const char*name, void *data, double amount) {
1240 SD_task_t res = SD_task_create_sized(name,data,amount,1);
1241 res->computation_amount[0]=amount;
1242 res->kind=SD_TASK_COMP_SEQ;
1246 /** @brief Auto-schedules a task.
1248 * Auto-scheduling mean that the task can be used with SD_task_schedulev(). This
1249 * allows to specify the task costs at creation, and decorelate them from the
1250 * scheduling process where you just specify which resource should deliver the
1253 * To be auto-schedulable, a task must be created with SD_task_create_comm_e2e() or
1254 * SD_task_create_comp_seq(). Check their definitions for the exact semantic of each
1258 * We should create tasks kind for the following categories:
1259 * - Point to point communication (done)
1260 * - Sequential computation (done)
1261 * - group communication (redistribution, several kinds)
1262 * - parallel tasks with no internal communication (one kind per speedup model such as amdal)
1263 * - idem+ internal communication. Task type not enough since we cannot store comm cost alongside to comp one)
1265 void SD_task_schedulev(SD_task_t task, int count, const SD_workstation_t*list) {
1267 xbt_assert1(task->kind != 0,"Task %s is not typed. Cannot automatically schedule it.",SD_task_get_name(task));
1268 switch(task->kind) {
1269 case SD_TASK_COMM_E2E:
1270 case SD_TASK_COMP_SEQ:
1271 xbt_assert(task->workstation_nb==count);
1272 for (i=0;i<count;i++)
1273 task->workstation_list[i]=list[i];
1274 SD_task_do_schedule(task);
1277 xbt_die(bprintf("Kind of task %s not supported by SD_task_schedulev()",
1278 SD_task_get_name(task)));
1280 if (task->kind == SD_TASK_COMM_E2E) {
1281 VERB4("Schedule comm task %s between %s -> %s. It costs %.f bytes",
1282 SD_task_get_name(task),
1283 SD_workstation_get_name(task->workstation_list[0]),SD_workstation_get_name(task->workstation_list[1]),
1284 task->communication_amount[2]);
1287 /* Iterate over all childs and parent being COMM_E2E to say where I am located (and start them if ready) */
1288 if (task->kind == SD_TASK_COMP_SEQ) {
1289 VERB3("Schedule computation task %s on %s. It costs %.f flops",
1290 SD_task_get_name(task),SD_workstation_get_name(task->workstation_list[0]),
1291 task->computation_amount[0]);
1292 SD_dependency_t dep;
1294 xbt_dynar_foreach(task->tasks_before,cpt,dep) {
1295 SD_task_t before = dep->src;
1296 if (before->kind == SD_TASK_COMM_E2E) {
1297 before->workstation_list[1] = task->workstation_list[0];
1298 if (before->workstation_list[0] && __SD_task_is_not_scheduled(before)) {
1299 SD_task_do_schedule(before);
1300 VERB4("Auto-Schedule comm task %s between %s -> %s. It costs %.f bytes",
1301 SD_task_get_name(before),
1302 SD_workstation_get_name(before->workstation_list[0]),SD_workstation_get_name(before->workstation_list[1]),
1303 before->communication_amount[2]);
1307 xbt_dynar_foreach(task->tasks_after,cpt,dep) {
1308 SD_task_t after = dep->dst;
1309 if (after->kind == SD_TASK_COMM_E2E) {
1310 after->workstation_list[0] = task->workstation_list[0];
1311 if (after->workstation_list[1] && __SD_task_is_not_scheduled(after)) {
1312 SD_task_do_schedule(after);
1313 VERB4("Auto-Schedule comm task %s between %s -> %s. It costs %.f bytes",
1314 SD_task_get_name(after),
1315 SD_workstation_get_name(after->workstation_list[0]),SD_workstation_get_name(after->workstation_list[1]),
1316 after->communication_amount[2]);
1323 /** @brief autoschedule a task on a list of workstations
1325 * This function is very similar to SD_task_schedulev(),
1326 * but takes the list of workstations to schedule onto as separate parameters.
1327 * It builds a proper vector of workstations and then call SD_task_schedulev()
1329 void SD_task_schedulel(SD_task_t task, int count, ...) {
1331 SD_workstation_t *list=xbt_new(SD_workstation_t,count);
1334 for (i=0;i<count;i++) {
1335 list[i] = va_arg(ap,SD_workstation_t);
1338 SD_task_schedulev(task,count,list);