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 /* temporary function for debbuging */
275 void SD_task_dump(SD_task_t task)
277 unsigned int counter;
278 SD_dependency_t dependency;
280 INFO1("Displaying task %s",SD_task_get_name(task));
281 INFO1(" - amount: %.0f",SD_task_get_amount(task));
284 case SD_TASK_COMM_E2E:
285 INFO0(" - kind: end-to-end communication");
287 case SD_TASK_COMP_SEQ:
288 INFO0(" - kind: sequential computation");
291 INFO1(" - (unknown kind %d)",task->kind);
294 if (xbt_dynar_length(task->tasks_before)) {
295 INFO0(" - pre-dependencies:");
296 xbt_dynar_foreach(task->tasks_before,counter,dependency) {
297 INFO1(" %s",SD_task_get_name(dependency->src));
300 if (xbt_dynar_length(task->tasks_after)) {
301 INFO0(" - post-dependencies:");
302 xbt_dynar_foreach(task->tasks_after,counter,dependency) {
303 INFO1(" %s",SD_task_get_name(dependency->dst));
308 /* Destroys a dependency between two tasks.
310 static void __SD_task_dependency_destroy(void *dependency)
312 if (((SD_dependency_t) dependency)->name != NULL)
313 xbt_free(((SD_dependency_t) dependency)->name);
314 xbt_free(dependency);
318 * \brief Adds a dependency between two tasks
320 * \a dst will depend on \a src, ie \a dst will not start before \a src is finished.
321 * Their \ref e_SD_task_state_t "state" must be #SD_NOT_SCHEDULED, #SD_SCHEDULED or #SD_READY.
323 * \param name the name of the new dependency (can be \c NULL)
324 * \param data the user data you want to associate with this dependency (can be \c NULL)
325 * \param src the task which must be executed first
326 * \param dst the task you want to make depend on \a src
327 * \see SD_task_dependency_remove()
329 void SD_task_dependency_add(const char *name, void *data, SD_task_t src,
336 SD_dependency_t dependency;
338 SD_CHECK_INIT_DONE();
339 xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
341 dynar = src->tasks_after;
342 length = xbt_dynar_length(dynar);
346 "Cannot add a dependency between task '%s' and itself",
347 SD_task_get_name(src));
349 if (!__SD_task_is_not_scheduled(src)
350 && !__SD_task_is_scheduled_or_ready(src))
352 "Task '%s' must be SD_NOT_SCHEDULED, SD_SCHEDULED or SD_READY",
353 SD_task_get_name(src));
355 if (!__SD_task_is_not_scheduled(dst)
356 && !__SD_task_is_scheduled_or_ready(dst))
358 "Task '%s' must be SD_NOT_SCHEDULED, SD_SCHEDULED or SD_READY",
359 SD_task_get_name(dst));
361 DEBUG2("SD_task_dependency_add: src = %s, dst = %s", SD_task_get_name(src),
362 SD_task_get_name(dst));
363 for (i = 0; i < length && !found; i++) {
364 xbt_dynar_get_cpy(dynar, i, &dependency);
365 found = (dependency->dst == dst);
366 DEBUG2("Dependency %d: dependency->dst = %s", i,
367 SD_task_get_name(dependency->dst));
372 "A dependency already exists between task '%s' and task '%s'",
373 SD_task_get_name(src), SD_task_get_name(dst));
375 dependency = xbt_new(s_SD_dependency_t, 1);
377 dependency->name = xbt_strdup(name); /* xbt_strdup is cleaver enough to deal with NULL args itself */
378 dependency->data = data;
379 dependency->src = src;
380 dependency->dst = dst;
382 /* src must be executed before dst */
383 xbt_dynar_push(src->tasks_after, &dependency);
384 xbt_dynar_push(dst->tasks_before, &dependency);
386 /* if the task was ready, then dst->tasks_before is not empty anymore,
387 so we must go back to state SD_SCHEDULED */
388 if (__SD_task_is_ready(dst)) {
389 DEBUG1("SD_task_dependency_add: %s was ready and becomes scheduled!",
390 SD_task_get_name(dst));
391 __SD_task_set_state(dst, SD_SCHEDULED);
394 /* __SD_print_dependencies(src);
395 __SD_print_dependencies(dst); */
399 * \brief Indacates whether there is a dependency between two tasks.
402 * \param dst a task depending on \a src
404 * If src is NULL, checks whether dst has any pre-dependency.
405 * If dst is NULL, checks whether src has any post-dependency.
407 int SD_task_dependency_exists(SD_task_t src, SD_task_t dst)
409 unsigned int counter;
410 SD_dependency_t dependency;
412 SD_CHECK_INIT_DONE();
413 xbt_assert0(src != NULL || dst != NULL, "Invalid parameter: both src and dst are NULL");
417 xbt_dynar_foreach(src->tasks_after,counter,dependency) {
418 if (dependency->dst == dst)
422 return xbt_dynar_length(src->tasks_after);
425 return xbt_dynar_length(dst->tasks_before);
431 * \brief Remove a dependency between two tasks
434 * \param dst a task depending on \a src
435 * \see SD_task_dependency_add()
437 void SD_task_dependency_remove(SD_task_t src, SD_task_t dst)
444 SD_dependency_t dependency;
446 SD_CHECK_INIT_DONE();
447 xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
449 /* remove the dependency from src->tasks_after */
450 dynar = src->tasks_after;
451 length = xbt_dynar_length(dynar);
453 for (i = 0; i < length && !found; i++) {
454 xbt_dynar_get_cpy(dynar, i, &dependency);
455 if (dependency->dst == dst) {
456 xbt_dynar_remove_at(dynar, i, NULL);
462 "No dependency found between task '%s' and '%s': task '%s' is not a successor of task '%s'",
463 SD_task_get_name(src), SD_task_get_name(dst),
464 SD_task_get_name(dst), SD_task_get_name(src));
466 /* remove the dependency from dst->tasks_before */
467 dynar = dst->tasks_before;
468 length = xbt_dynar_length(dynar);
471 for (i = 0; i < length && !found; i++) {
472 xbt_dynar_get_cpy(dynar, i, &dependency);
473 if (dependency->src == src) {
474 xbt_dynar_remove_at(dynar, i, NULL);
475 __SD_task_dependency_destroy(dependency);
479 /* should never happen... */
481 "SimDag error: task '%s' is a successor of '%s' but task '%s' is not a predecessor of task '%s'",
482 SD_task_get_name(dst), SD_task_get_name(src),
483 SD_task_get_name(src), SD_task_get_name(dst));
485 /* if the task was scheduled and dst->tasks_before is empty now, we can make it ready */
486 if (xbt_dynar_length(dst->tasks_before) == 0 && __SD_task_is_scheduled(dst))
487 __SD_task_set_state(dst, SD_READY);
489 /* __SD_print_dependencies(src);
490 __SD_print_dependencies(dst); */
494 * \brief Returns the user data associated with a dependency between two tasks
497 * \param dst a task depending on \a src
498 * \return the user data associated with this dependency (can be \c NULL)
499 * \see SD_task_dependency_add()
501 void *SD_task_dependency_get_data(SD_task_t src, SD_task_t dst)
508 SD_dependency_t dependency;
511 SD_CHECK_INIT_DONE();
512 xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
514 dynar = src->tasks_after;
515 length = xbt_dynar_length(dynar);
517 for (i = 0; i < length && !found; i++) {
518 xbt_dynar_get_cpy(dynar, i, &dependency);
519 found = (dependency->dst == dst);
522 THROW2(arg_error, 0, "No dependency found between task '%s' and '%s'",
523 SD_task_get_name(src), SD_task_get_name(dst));
524 return dependency->data;
527 /* temporary function for debugging */
528 static void __SD_print_watch_points(SD_task_t task)
530 static const int state_masks[] =
531 { SD_SCHEDULED, SD_RUNNING, SD_READY, SD_DONE, SD_FAILED };
532 static const char *state_names[] =
533 { "scheduled", "running", "ready", "done", "failed" };
536 INFO2("Task '%s' watch points (%x): ", SD_task_get_name(task),
540 for (i = 0; i < 5; i++) {
541 if (task->watch_points & state_masks[i])
542 INFO1("%s ", state_names[i]);
547 * \brief Adds a watch point to a task
549 * SD_simulate() will stop as soon as the \ref e_SD_task_state_t "state" of this
550 * task becomes the one given in argument. The
551 * watch point is then automatically removed.
554 * \param state the \ref e_SD_task_state_t "state" you want to watch
555 * (cannot be #SD_NOT_SCHEDULED)
556 * \see SD_task_unwatch()
558 void SD_task_watch(SD_task_t task, e_SD_task_state_t state)
560 SD_CHECK_INIT_DONE();
561 xbt_assert0(task != NULL, "Invalid parameter");
563 if (state & SD_NOT_SCHEDULED)
565 "Cannot add a watch point for state SD_NOT_SCHEDULED");
567 task->watch_points = task->watch_points | state;
568 /* __SD_print_watch_points(task); */
572 * \brief Removes a watch point from a task
575 * \param state the \ref e_SD_task_state_t "state" you no longer want to watch
576 * \see SD_task_watch()
578 void SD_task_unwatch(SD_task_t task, e_SD_task_state_t state)
580 SD_CHECK_INIT_DONE();
581 xbt_assert0(task != NULL, "Invalid parameter");
582 xbt_assert0(state != SD_NOT_SCHEDULED,
583 "SimDag error: Cannot have a watch point for state SD_NOT_SCHEDULED");
585 task->watch_points = task->watch_points & ~state;
586 /* __SD_print_watch_points(task); */
590 * \brief Returns an approximative estimation of the execution time of a task.
592 * The estimation is very approximative because the value returned is the time
593 * the task would take if it was executed now and if it was the only task.
595 * \param task the task to evaluate
596 * \param workstation_nb number of workstations on which the task would be executed
597 * \param workstation_list the workstations on which the task would be executed
598 * \param computation_amount computation amount for each workstation
599 * \param communication_amount communication amount between each pair of workstations
600 * \param rate task execution speed rate
603 double SD_task_get_execution_time(SD_task_t task,
605 const SD_workstation_t * workstation_list,
606 const double *computation_amount,
607 const double *communication_amount,
610 double time, max_time = 0.0;
612 SD_CHECK_INIT_DONE();
613 xbt_assert0(task != NULL && workstation_nb > 0 && workstation_list != NULL
614 && computation_amount != NULL
615 && communication_amount != NULL, "Invalid parameter");
617 /* the task execution time is the maximum execution time of the parallel tasks */
619 for (i = 0; i < workstation_nb; i++) {
621 SD_workstation_get_computation_time(workstation_list[i],
622 computation_amount[i]);
624 for (j = 0; j < workstation_nb; j++) {
626 SD_route_get_communication_time(workstation_list[i],
628 communication_amount[i *
633 if (time > max_time) {
637 return max_time * SD_task_get_amount(task);
641 * \brief Schedules a task
643 * The task state must be #SD_NOT_SCHEDULED.
644 * Once scheduled, a task will be executed as soon as possible in SD_simulate(),
645 * i.e. when its dependencies are satisfied.
647 * \param task the task you want to schedule
648 * \param workstation_nb number of workstations on which the task will be executed
649 * \param workstation_list the workstations on which the task will be executed
650 * \param computation_amount computation amount for each workstation
651 * \param communication_amount communication amount between each pair of workstations
652 * \param rate task execution speed rate
653 * \see SD_task_unschedule()
655 void SD_task_schedule(SD_task_t task, int workstation_nb,
656 const SD_workstation_t * workstation_list,
657 const double *computation_amount,
658 const double *communication_amount, double rate)
661 int communication_nb;
663 SD_CHECK_INIT_DONE();
664 xbt_assert0(task != NULL, "Invalid parameter");
665 xbt_assert0(workstation_nb > 0, "workstation_nb must be positive");
667 if (!__SD_task_is_not_scheduled(task))
668 THROW1(arg_error, 0, "Task '%s' has already been scheduled",
669 SD_task_get_name(task));
671 task->workstation_nb = workstation_nb;
674 task->computation_amount = xbt_new(double, workstation_nb);
675 memcpy(task->computation_amount, computation_amount,
676 sizeof(double) * workstation_nb);
678 communication_nb = workstation_nb * workstation_nb;
679 task->communication_amount = xbt_new(double, communication_nb);
680 memcpy(task->communication_amount, communication_amount,
681 sizeof(double) * communication_nb);
683 task->workstation_list = xbt_new(SD_workstation_t, workstation_nb);
684 memcpy(task->workstation_list, workstation_list,
685 sizeof(SD_workstation_t) * workstation_nb);
687 /* update the task state */
688 if (xbt_dynar_length(task->tasks_before) == 0)
689 __SD_task_set_state(task, SD_READY);
691 __SD_task_set_state(task, SD_SCHEDULED);
695 * \brief Unschedules a task
697 * The task state must be #SD_SCHEDULED, #SD_READY, #SD_RUNNING or #SD_FAILED.
698 * If you call this function, the task state becomes #SD_NOT_SCHEDULED.
699 * Call SD_task_schedule() to schedule it again.
701 * \param task the task you want to unschedule
702 * \see SD_task_schedule()
704 void SD_task_unschedule(SD_task_t task)
706 SD_CHECK_INIT_DONE();
707 xbt_assert0(task != NULL, "Invalid parameter");
709 if (task->state_set != sd_global->scheduled_task_set &&
710 task->state_set != sd_global->ready_task_set &&
711 task->state_set != sd_global->running_task_set &&
712 task->state_set != sd_global->failed_task_set)
714 "Task %s: the state must be SD_SCHEDULED, SD_READY, SD_RUNNING or SD_FAILED",
715 SD_task_get_name(task));
717 if (__SD_task_is_scheduled_or_ready(task)) /* if the task is scheduled or ready */
718 __SD_task_destroy_scheduling_data(task);
720 if (__SD_task_is_running(task)) /* the task should become SD_FAILED */
721 surf_workstation_model->action_cancel(task->surf_action);
723 __SD_task_set_state(task, SD_NOT_SCHEDULED);
724 task->remains = task->amount;
725 task->start_time = -1.0;
728 /* Destroys the data memorised by SD_task_schedule. Task state must be SD_SCHEDULED or SD_READY.
730 static void __SD_task_destroy_scheduling_data(SD_task_t task)
732 SD_CHECK_INIT_DONE();
733 if (!__SD_task_is_scheduled_or_ready(task) && !__SD_task_is_in_fifo(task))
735 "Task '%s' must be SD_SCHEDULED, SD_READY or SD_IN_FIFO",
736 SD_task_get_name(task));
738 xbt_free(task->computation_amount);
739 xbt_free(task->communication_amount);
742 /* Runs a task. This function is directly called by __SD_task_try_to_run if the task
743 * doesn't have to wait in fifos. Otherwise, it is called by __SD_task_just_done when
744 * the task gets out of its fifos.
746 void __SD_task_really_run(SD_task_t task)
750 void **surf_workstations;
752 SD_CHECK_INIT_DONE();
753 xbt_assert0(task != NULL, "Invalid parameter");
754 xbt_assert2(__SD_task_is_ready_or_in_fifo(task),
755 "Task '%s' is not ready or in a fifo! Task state: %d",
756 SD_task_get_name(task), SD_task_get_state(task));
757 xbt_assert1(task->workstation_list != NULL,
758 "Task '%s': workstation_list is NULL!", SD_task_get_name(task));
762 DEBUG1("Really running task '%s'", SD_task_get_name(task));
764 /* set this task as current task for the workstations in sequential mode */
765 for (i = 0; i < task->workstation_nb; i++) {
766 if (SD_workstation_get_access_mode(task->workstation_list[i]) ==
767 SD_WORKSTATION_SEQUENTIAL_ACCESS) {
768 task->workstation_list[i]->current_task = task;
769 xbt_assert0(__SD_workstation_is_busy(task->workstation_list[i]),
770 "The workstation should be busy now");
774 DEBUG1("Task '%s' set as current task for its workstations",
775 SD_task_get_name(task));
779 /* we have to create a Surf workstation array instead of the SimDag workstation array */
780 surf_workstations = xbt_new(void *, task->workstation_nb);
782 for (i = 0; i < task->workstation_nb; i++) {
783 surf_workstations[i] = task->workstation_list[i]->surf_workstation;
786 task->surf_action = NULL;
787 if ((task->workstation_nb == 1) && (task->communication_amount[0] == 0.0)) {
789 surf_workstation_model->extension.
790 workstation.execute(surf_workstations[0], task->computation_amount[0]);
791 } else if ((task->workstation_nb == 1)
792 && (task->computation_amount[0] == 0.0)) {
794 surf_workstation_model->extension.
795 workstation.communicate(surf_workstations[0], surf_workstations[0],
796 task->communication_amount[0], task->rate);
797 } else if ((task->workstation_nb == 2)
798 && (task->computation_amount[0] == 0.0)
799 && (task->computation_amount[1] == 0.0)) {
803 for (i = 0; i < task->workstation_nb * task->workstation_nb; i++) {
804 if (task->communication_amount[i] > 0.0) {
806 value = task->communication_amount[i];
811 surf_workstation_model->extension.
812 workstation.communicate(surf_workstations[0], surf_workstations[1],
816 if (!task->surf_action) {
817 double *computation_amount = xbt_new(double, task->workstation_nb);
818 double *communication_amount = xbt_new(double, task->workstation_nb *
819 task->workstation_nb);
821 memcpy(computation_amount, task->computation_amount, sizeof(double) *
822 task->workstation_nb);
823 memcpy(communication_amount, task->communication_amount,
824 sizeof(double) * task->workstation_nb * task->workstation_nb);
827 surf_workstation_model->extension.
828 workstation.execute_parallel_task(task->workstation_nb,
829 surf_workstations, computation_amount,
830 communication_amount, task->amount,
833 xbt_free(surf_workstations);
836 surf_workstation_model->action_data_set(task->surf_action, task);
838 DEBUG1("surf_action = %p", task->surf_action);
840 __SD_task_destroy_scheduling_data(task); /* now the scheduling data are not useful anymore */
841 __SD_task_set_state(task, SD_RUNNING);
842 xbt_assert2(__SD_task_is_running(task), "Bad state of task '%s': %d",
843 SD_task_get_name(task), SD_task_get_state(task));
847 /* Tries to run a task. This function is called by SD_simulate() when a scheduled task becomes SD_READY
848 * (ie when its dependencies are satisfied).
849 * If one of the workstations where the task is scheduled on is busy (in sequential mode),
850 * the task doesn't start.
851 * Returns whether the task has started.
853 int __SD_task_try_to_run(SD_task_t task)
858 SD_workstation_t workstation;
860 SD_CHECK_INIT_DONE();
861 xbt_assert0(task != NULL, "Invalid parameter");
862 xbt_assert2(__SD_task_is_ready(task),
863 "Task '%s' is not ready! Task state: %d",
864 SD_task_get_name(task), SD_task_get_state(task));
867 for (i = 0; i < task->workstation_nb; i++) {
868 can_start = !__SD_workstation_is_busy(task->workstation_list[i]);
871 DEBUG2("Task '%s' can start: %d", SD_task_get_name(task), can_start);
873 if (!can_start) { /* if the task cannot start and is not in the fifos yet */
874 for (i = 0; i < task->workstation_nb; i++) {
875 workstation = task->workstation_list[i];
876 if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
877 DEBUG2("Pushing task '%s' in the fifo of workstation '%s'",
878 SD_task_get_name(task), SD_workstation_get_name(workstation));
879 xbt_fifo_push(workstation->task_fifo, task);
882 __SD_task_set_state(task, SD_IN_FIFO);
883 xbt_assert2(__SD_task_is_in_fifo(task), "Bad state of task '%s': %d",
884 SD_task_get_name(task), SD_task_get_state(task));
885 DEBUG1("Task '%s' state is now SD_IN_FIFO", SD_task_get_name(task));
887 __SD_task_really_run(task);
893 /* This function is called by SD_simulate when a task is done.
894 * It updates task->state and task->action and executes if necessary the tasks
895 * which were waiting in fifos for the end of `task'
897 void __SD_task_just_done(SD_task_t task)
900 SD_workstation_t workstation;
903 int candidate_nb = 0;
904 int candidate_capacity = 8;
905 SD_task_t *candidates;
908 SD_CHECK_INIT_DONE();
909 xbt_assert0(task != NULL, "Invalid parameter");
910 xbt_assert1(__SD_task_is_running(task),
911 "The task must be running! Task state: %d",
912 SD_task_get_state(task));
913 xbt_assert1(task->workstation_list != NULL,
914 "Task '%s': workstation_list is NULL!", SD_task_get_name(task));
917 candidates = xbt_new(SD_task_t, 8);
919 __SD_task_set_state(task, SD_DONE);
920 surf_workstation_model->action_unref(task->surf_action);
921 task->surf_action = NULL;
923 DEBUG0("Looking for candidates");
925 /* if the task was executed on sequential workstations,
926 maybe we can execute the next task of the fifo for each workstation */
927 for (i = 0; i < task->workstation_nb; i++) {
928 workstation = task->workstation_list[i];
929 DEBUG2("Workstation '%s': access_mode = %d",
930 SD_workstation_get_name(workstation), workstation->access_mode);
931 if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
932 xbt_assert1(workstation->task_fifo != NULL,
933 "Workstation '%s' has sequential access but no fifo!",
934 SD_workstation_get_name(workstation));
935 xbt_assert2(workstation->current_task =
936 task, "Workstation '%s': current task should be '%s'",
937 SD_workstation_get_name(workstation),
938 SD_task_get_name(task));
940 /* the task is over so we can release the workstation */
941 workstation->current_task = NULL;
943 DEBUG0("Getting candidate in fifo");
945 xbt_fifo_get_item_content(xbt_fifo_get_first_item
946 (workstation->task_fifo));
948 if (candidate != NULL) {
949 DEBUG1("Candidate: '%s'", SD_task_get_name(candidate));
950 xbt_assert2(__SD_task_is_in_fifo(candidate),
951 "Bad state of candidate '%s': %d",
952 SD_task_get_name(candidate),
953 SD_task_get_state(candidate));
956 DEBUG1("Candidate in fifo: %p", candidate);
958 /* if there was a task waiting for my place */
959 if (candidate != NULL) {
960 /* Unfortunately, we are not sure yet that we can execute the task now,
961 because the task can be waiting more deeply in some other workstation's fifos...
962 So we memorize all candidate tasks, and then we will check for each candidate
963 whether or not all its workstations are available. */
965 /* realloc if necessary */
966 if (candidate_nb == candidate_capacity) {
967 candidate_capacity *= 2;
969 xbt_realloc(candidates, sizeof(SD_task_t) * candidate_capacity);
972 /* register the candidate */
973 candidates[candidate_nb++] = candidate;
974 candidate->fifo_checked = 0;
979 DEBUG1("Candidates found: %d", candidate_nb);
981 /* now we check every candidate task */
982 for (i = 0; i < candidate_nb; i++) {
983 candidate = candidates[i];
985 if (candidate->fifo_checked) {
986 continue; /* we have already evaluated that task */
989 xbt_assert2(__SD_task_is_in_fifo(candidate),
990 "Bad state of candidate '%s': %d",
991 SD_task_get_name(candidate), SD_task_get_state(candidate));
993 for (j = 0; j < candidate->workstation_nb && can_start; j++) {
994 workstation = candidate->workstation_list[j];
996 /* I can start on this workstation if the workstation is shared
997 or if I am the first task in the fifo */
998 can_start = workstation->access_mode == SD_WORKSTATION_SHARED_ACCESS ||
1000 xbt_fifo_get_item_content(xbt_fifo_get_first_item
1001 (workstation->task_fifo));
1004 DEBUG2("Candidate '%s' can start: %d", SD_task_get_name(candidate),
1007 /* now we are sure that I can start! */
1009 for (j = 0; j < candidate->workstation_nb && can_start; j++) {
1010 workstation = candidate->workstation_list[j];
1012 /* update the fifo */
1013 if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
1014 candidate = xbt_fifo_shift(workstation->task_fifo); /* the return value is stored just for debugging */
1015 DEBUG1("Head of the fifo: '%s'",
1016 (candidate != NULL) ? SD_task_get_name(candidate) : "NULL");
1017 xbt_assert0(candidate == candidates[i],
1018 "Error in __SD_task_just_done: bad first task in the fifo");
1020 } /* for each workstation */
1022 /* finally execute the task */
1023 DEBUG2("Task '%s' state: %d", SD_task_get_name(candidate),
1024 SD_task_get_state(candidate));
1025 __SD_task_really_run(candidate);
1028 ("Calling __SD_task_is_running: task '%s', state set: %p, running_task_set: %p, is running: %d",
1029 SD_task_get_name(candidate), candidate->state_set,
1030 sd_global->running_task_set, __SD_task_is_running(candidate));
1031 xbt_assert2(__SD_task_is_running(candidate),
1032 "Bad state of task '%s': %d", SD_task_get_name(candidate),
1033 SD_task_get_state(candidate));
1034 DEBUG0("Okay, the task is running.");
1037 candidate->fifo_checked = 1;
1038 } /* for each candidate */
1040 xbt_free(candidates);
1043 /* Remove all dependencies associated with a task. This function is called when the task is destroyed.
1045 static void __SD_task_remove_dependencies(SD_task_t task)
1047 /* we must destroy the dependencies carefuly (with SD_dependency_remove)
1048 because each one is stored twice */
1049 SD_dependency_t dependency;
1050 while (xbt_dynar_length(task->tasks_before) > 0) {
1051 xbt_dynar_get_cpy(task->tasks_before, 0, &dependency);
1052 SD_task_dependency_remove(dependency->src, dependency->dst);
1055 while (xbt_dynar_length(task->tasks_after) > 0) {
1056 xbt_dynar_get_cpy(task->tasks_after, 0, &dependency);
1057 SD_task_dependency_remove(dependency->src, dependency->dst);
1062 * \brief Returns the start time of a task
1064 * The task state must be SD_RUNNING, SD_DONE or SD_FAILED.
1066 * \param task: a task
1067 * \return the start time of this task
1069 double SD_task_get_start_time(SD_task_t task)
1071 SD_CHECK_INIT_DONE();
1072 xbt_assert0(task != NULL, "Invalid parameter");
1073 if (task->surf_action)
1074 return surf_workstation_model->action_get_start_time(task->surf_action);
1076 return task->start_time;
1080 * \brief Returns the finish time of a task
1082 * The task state must be SD_RUNNING, SD_DONE or SD_FAILED.
1083 * If the state is not completed yet, the returned value is an
1084 * estimation of the task finish time. This value can fluctuate
1085 * until the task is completed.
1087 * \param task: a task
1088 * \return the start time of this task
1090 double SD_task_get_finish_time(SD_task_t task)
1092 SD_CHECK_INIT_DONE();
1093 xbt_assert0(task != NULL, "Invalid parameter");
1095 if (task->surf_action) /* should never happen as actions are destroyed right after their completion */
1096 return surf_workstation_model->action_get_finish_time(task->surf_action);
1098 return task->finish_time;
1102 * \brief Destroys a task.
1104 * The user data (if any) should have been destroyed first.
1106 * \param task the task you want to destroy
1107 * \see SD_task_create()
1109 void SD_task_destroy(SD_task_t task)
1111 SD_CHECK_INIT_DONE();
1112 xbt_assert0(task != NULL, "Invalid parameter");
1114 DEBUG1("Destroying task %s...", SD_task_get_name(task));
1116 __SD_task_remove_dependencies(task);
1118 /* if the task was scheduled or ready we have to free the scheduling parameters */
1119 if (__SD_task_is_scheduled_or_ready(task))
1120 __SD_task_destroy_scheduling_data(task);
1122 if (task->name != NULL)
1123 xbt_free(task->name);
1125 if (task->surf_action != NULL)
1126 surf_workstation_model->action_unref(task->surf_action);
1128 if (task->workstation_list != NULL)
1129 xbt_free(task->workstation_list);
1131 xbt_dynar_free(&task->tasks_before);
1132 xbt_dynar_free(&task->tasks_after);
1135 sd_global->task_number--;
1137 DEBUG0("Task destroyed.");
1141 /** @brief create a end-to-end communication task that can then be auto-scheduled
1143 * Auto-scheduling mean that the task can be used with SD_task_schedulev(). This
1144 * allows to specify the task costs at creation, and decorelate them from the
1145 * scheduling process where you just specify which resource should deliver the
1148 * A end-to-end communication must be scheduled on 2 hosts, and the amount
1149 * specified at creation is sent from hosts[0] to hosts[1].
1151 SD_task_t SD_task_create_comm_e2e(const char*name, void *data, double amount) {
1152 SD_task_t res = SD_task_create(name,data,amount);
1153 res->kind=SD_TASK_COMM_E2E;
1156 /** @brief create a sequential computation task that can then be auto-scheduled
1158 * Auto-scheduling mean that the task can be used with SD_task_schedulev(). This
1159 * allows to specify the task costs at creation, and decorelate them from the
1160 * scheduling process where you just specify which resource should deliver the
1163 * A sequential computation must be scheduled on 1 host, and the amount
1164 * specified at creation to be run on hosts[0].
1166 SD_task_t SD_task_create_comp_seq(const char*name, void *data, double amount) {
1167 SD_task_t res = SD_task_create(name,data,amount);
1168 res->kind=SD_TASK_COMP_SEQ;
1172 /** @brief Auto-schedules a task.
1174 * Auto-scheduling mean that the task can be used with SD_task_schedulev(). This
1175 * allows to specify the task costs at creation, and decorelate them from the
1176 * scheduling process where you just specify which resource should deliver the
1179 * To be auto-schedulable, a task must be created with SD_task_create_comm_e2e() or
1180 * SD_task_create_comp_seq(). Check their definitions for the exact semantic of each
1184 * We should create tasks kind for the following categories:
1185 * - Point to point communication (done)
1186 * - Sequential computation (done)
1187 * - group communication (redistribution, several kinds)
1188 * - parallel tasks with no internal communication (one kind per speedup model such as amdal)
1189 * - idem+ internal communication. Task type not enough since we cannot store comm cost alongside to comp one)
1191 void SD_task_schedulev(SD_task_t task, int count, const SD_workstation_t*list) {
1192 xbt_assert1(task->kind != 0,"Task %s is not typed. Cannot automatically schedule it.",SD_task_get_name(task));
1193 double *comp,*comms;
1194 switch(task->kind) {
1195 case SD_TASK_COMM_E2E:
1196 xbt_assert2(count == 2,
1197 "Task %s is end to end communication, but scheduled with %d hosts",
1198 SD_task_get_name(task),count);
1199 comms=xbt_new(double,count);
1201 comms[1]=SD_task_get_amount(task);
1202 SD_task_schedule(task,count,list,NULL,comms,1);
1204 case SD_TASK_COMP_SEQ:
1205 xbt_assert2(count==1,
1206 "Task %s is sequential computation, but scheduled with %d hosts",
1207 SD_task_get_name(task),count);
1208 comp=xbt_new(double,count);
1209 comp[0]=SD_task_get_amount(task);
1210 SD_task_schedule(task,count,list,comp,NULL,1);
1213 xbt_die(bprintf("Kind of task %s not supported by SD_task_schedulev()",
1214 SD_task_get_name(task)));
1217 /** @brief autoschedule a task on a list of workstations
1219 * This function is very similar to SD_task_schedulev(),
1220 * but takes the list of workstations to schedule onto as separate parameters.
1221 * It builds a proper vector of workstations and then call SD_task_schedulev()
1223 void SD_task_schedulel(SD_task_t task, int count, ...) {
1225 SD_workstation_t *list=xbt_new(SD_workstation_t,count);
1228 for (i=0;i<count;i++) {
1229 list[i] = va_arg(ap,SD_workstation_t);
1232 SD_task_schedulev(task,count,list);