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");
172 * \brief Returns the total amount of a task
175 * \return the total amount of this task
176 * \see SD_task_get_remaining_amount()
178 double SD_task_get_amount(SD_task_t task)
180 SD_CHECK_INIT_DONE();
181 xbt_assert0(task != NULL, "Invalid parameter");
186 * \brief Returns the remaining amount of a task
189 * \return the remaining amount of this task
190 * \see SD_task_get_amount()
192 double SD_task_get_remaining_amount(SD_task_t task)
194 SD_CHECK_INIT_DONE();
195 xbt_assert0(task != NULL, "Invalid parameter");
197 if (task->surf_action)
198 return surf_workstation_model->get_remains(task->surf_action);
200 return task->remains;
203 /* temporary function for debbuging */
204 void SD_task_dump(SD_task_t task)
206 unsigned int counter;
207 SD_dependency_t dependency;
209 INFO1("Displaying task %s",SD_task_get_name(task));
210 INFO1(" - amount: %.0f",SD_task_get_amount(task));
211 if (xbt_dynar_length(task->tasks_before)) {
212 INFO0(" - pre-dependencies:");
213 xbt_dynar_foreach(task->tasks_before,counter,dependency) {
214 INFO1(" %s",SD_task_get_name(dependency->src));
217 if (xbt_dynar_length(task->tasks_after)) {
218 INFO0(" - post-dependencies:");
219 xbt_dynar_foreach(task->tasks_after,counter,dependency) {
220 INFO1(" %s",SD_task_get_name(dependency->dst));
225 /* Destroys a dependency between two tasks.
227 static void __SD_task_dependency_destroy(void *dependency)
229 if (((SD_dependency_t) dependency)->name != NULL)
230 xbt_free(((SD_dependency_t) dependency)->name);
231 xbt_free(dependency);
235 * \brief Adds a dependency between two tasks
237 * \a dst will depend on \a src, ie \a dst will not start before \a src is finished.
238 * Their \ref e_SD_task_state_t "state" must be #SD_NOT_SCHEDULED, #SD_SCHEDULED or #SD_READY.
240 * \param name the name of the new dependency (can be \c NULL)
241 * \param data the user data you want to associate with this dependency (can be \c NULL)
242 * \param src the task which must be executed first
243 * \param dst the task you want to make depend on \a src
244 * \see SD_task_dependency_remove()
246 void SD_task_dependency_add(const char *name, void *data, SD_task_t src,
253 SD_dependency_t dependency;
255 SD_CHECK_INIT_DONE();
256 xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
258 dynar = src->tasks_after;
259 length = xbt_dynar_length(dynar);
263 "Cannot add a dependency between task '%s' and itself",
264 SD_task_get_name(src));
266 if (!__SD_task_is_not_scheduled(src)
267 && !__SD_task_is_scheduled_or_ready(src))
269 "Task '%s' must be SD_NOT_SCHEDULED, SD_SCHEDULED or SD_READY",
270 SD_task_get_name(src));
272 if (!__SD_task_is_not_scheduled(dst)
273 && !__SD_task_is_scheduled_or_ready(dst))
275 "Task '%s' must be SD_NOT_SCHEDULED, SD_SCHEDULED or SD_READY",
276 SD_task_get_name(dst));
278 DEBUG2("SD_task_dependency_add: src = %s, dst = %s", SD_task_get_name(src),
279 SD_task_get_name(dst));
280 for (i = 0; i < length && !found; i++) {
281 xbt_dynar_get_cpy(dynar, i, &dependency);
282 found = (dependency->dst == dst);
283 DEBUG2("Dependency %d: dependency->dst = %s", i,
284 SD_task_get_name(dependency->dst));
289 "A dependency already exists between task '%s' and task '%s'",
290 SD_task_get_name(src), SD_task_get_name(dst));
292 dependency = xbt_new(s_SD_dependency_t, 1);
294 dependency->name = xbt_strdup(name); /* xbt_strdup is cleaver enough to deal with NULL args itself */
295 dependency->data = data;
296 dependency->src = src;
297 dependency->dst = dst;
299 /* src must be executed before dst */
300 xbt_dynar_push(src->tasks_after, &dependency);
301 xbt_dynar_push(dst->tasks_before, &dependency);
303 /* if the task was ready, then dst->tasks_before is not empty anymore,
304 so we must go back to state SD_SCHEDULED */
305 if (__SD_task_is_ready(dst)) {
306 DEBUG1("SD_task_dependency_add: %s was ready and becomes scheduled!",
307 SD_task_get_name(dst));
308 __SD_task_set_state(dst, SD_SCHEDULED);
311 /* __SD_print_dependencies(src);
312 __SD_print_dependencies(dst); */
316 * \brief Indacates whether there is a dependency between two tasks.
319 * \param dst a task depending on \a src
321 * If src is NULL, checks whether dst has any pre-dependency.
322 * If dst is NULL, checks whether src has any post-dependency.
324 int SD_task_dependency_exists(SD_task_t src, SD_task_t dst)
326 unsigned int counter;
327 SD_dependency_t dependency;
329 SD_CHECK_INIT_DONE();
330 xbt_assert0(src != NULL || dst != NULL, "Invalid parameter: both src and dst are NULL");
334 xbt_dynar_foreach(src->tasks_after,counter,dependency) {
335 if (dependency->dst == dst)
339 return xbt_dynar_length(src->tasks_after);
342 return xbt_dynar_length(dst->tasks_before);
348 * \brief Remove a dependency between two tasks
351 * \param dst a task depending on \a src
352 * \see SD_task_dependency_add()
354 void SD_task_dependency_remove(SD_task_t src, SD_task_t dst)
361 SD_dependency_t dependency;
363 SD_CHECK_INIT_DONE();
364 xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
366 /* remove the dependency from src->tasks_after */
367 dynar = src->tasks_after;
368 length = xbt_dynar_length(dynar);
370 for (i = 0; i < length && !found; i++) {
371 xbt_dynar_get_cpy(dynar, i, &dependency);
372 if (dependency->dst == dst) {
373 xbt_dynar_remove_at(dynar, i, NULL);
379 "No dependency found between task '%s' and '%s': task '%s' is not a successor of task '%s'",
380 SD_task_get_name(src), SD_task_get_name(dst),
381 SD_task_get_name(dst), SD_task_get_name(src));
383 /* remove the dependency from dst->tasks_before */
384 dynar = dst->tasks_before;
385 length = xbt_dynar_length(dynar);
388 for (i = 0; i < length && !found; i++) {
389 xbt_dynar_get_cpy(dynar, i, &dependency);
390 if (dependency->src == src) {
391 xbt_dynar_remove_at(dynar, i, NULL);
392 __SD_task_dependency_destroy(dependency);
396 /* should never happen... */
398 "SimDag error: task '%s' is a successor of '%s' but task '%s' is not a predecessor of task '%s'",
399 SD_task_get_name(dst), SD_task_get_name(src),
400 SD_task_get_name(src), SD_task_get_name(dst));
402 /* if the task was scheduled and dst->tasks_before is empty now, we can make it ready */
403 if (xbt_dynar_length(dst->tasks_before) == 0 && __SD_task_is_scheduled(dst))
404 __SD_task_set_state(dst, SD_READY);
406 /* __SD_print_dependencies(src);
407 __SD_print_dependencies(dst); */
411 * \brief Returns the user data associated with a dependency between two tasks
414 * \param dst a task depending on \a src
415 * \return the user data associated with this dependency (can be \c NULL)
416 * \see SD_task_dependency_add()
418 void *SD_task_dependency_get_data(SD_task_t src, SD_task_t dst)
425 SD_dependency_t dependency;
428 SD_CHECK_INIT_DONE();
429 xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
431 dynar = src->tasks_after;
432 length = xbt_dynar_length(dynar);
434 for (i = 0; i < length && !found; i++) {
435 xbt_dynar_get_cpy(dynar, i, &dependency);
436 found = (dependency->dst == dst);
439 THROW2(arg_error, 0, "No dependency found between task '%s' and '%s'",
440 SD_task_get_name(src), SD_task_get_name(dst));
441 return dependency->data;
444 /* temporary function for debugging */
445 static void __SD_print_watch_points(SD_task_t task)
447 static const int state_masks[] =
448 { SD_SCHEDULED, SD_RUNNING, SD_READY, SD_DONE, SD_FAILED };
449 static const char *state_names[] =
450 { "scheduled", "running", "ready", "done", "failed" };
453 INFO2("Task '%s' watch points (%x): ", SD_task_get_name(task),
457 for (i = 0; i < 5; i++) {
458 if (task->watch_points & state_masks[i])
459 INFO1("%s ", state_names[i]);
464 * \brief Adds a watch point to a task
466 * SD_simulate() will stop as soon as the \ref e_SD_task_state_t "state" of this
467 * task becomes the one given in argument. The
468 * watch point is then automatically removed.
471 * \param state the \ref e_SD_task_state_t "state" you want to watch
472 * (cannot be #SD_NOT_SCHEDULED)
473 * \see SD_task_unwatch()
475 void SD_task_watch(SD_task_t task, e_SD_task_state_t state)
477 SD_CHECK_INIT_DONE();
478 xbt_assert0(task != NULL, "Invalid parameter");
480 if (state & SD_NOT_SCHEDULED)
482 "Cannot add a watch point for state SD_NOT_SCHEDULED");
484 task->watch_points = task->watch_points | state;
485 /* __SD_print_watch_points(task); */
489 * \brief Removes a watch point from a task
492 * \param state the \ref e_SD_task_state_t "state" you no longer want to watch
493 * \see SD_task_watch()
495 void SD_task_unwatch(SD_task_t task, e_SD_task_state_t state)
497 SD_CHECK_INIT_DONE();
498 xbt_assert0(task != NULL, "Invalid parameter");
499 xbt_assert0(state != SD_NOT_SCHEDULED,
500 "SimDag error: Cannot have a watch point for state SD_NOT_SCHEDULED");
502 task->watch_points = task->watch_points & ~state;
503 /* __SD_print_watch_points(task); */
507 * \brief Returns an approximative estimation of the execution time of a task.
509 * The estimation is very approximative because the value returned is the time
510 * the task would take if it was executed now and if it was the only task.
512 * \param task the task to evaluate
513 * \param workstation_nb number of workstations on which the task would be executed
514 * \param workstation_list the workstations on which the task would be executed
515 * \param computation_amount computation amount for each workstation
516 * \param communication_amount communication amount between each pair of workstations
517 * \param rate task execution speed rate
520 double SD_task_get_execution_time(SD_task_t task,
522 const SD_workstation_t * workstation_list,
523 const double *computation_amount,
524 const double *communication_amount,
527 double time, max_time = 0.0;
529 SD_CHECK_INIT_DONE();
530 xbt_assert0(task != NULL && workstation_nb > 0 && workstation_list != NULL
531 && computation_amount != NULL
532 && communication_amount != NULL, "Invalid parameter");
534 /* the task execution time is the maximum execution time of the parallel tasks */
536 for (i = 0; i < workstation_nb; i++) {
538 SD_workstation_get_computation_time(workstation_list[i],
539 computation_amount[i]);
541 for (j = 0; j < workstation_nb; j++) {
543 SD_route_get_communication_time(workstation_list[i],
545 communication_amount[i *
550 if (time > max_time) {
554 return max_time * SD_task_get_amount(task);
558 * \brief Schedules a task
560 * The task state must be #SD_NOT_SCHEDULED.
561 * Once scheduled, a task will be executed as soon as possible in SD_simulate(),
562 * i.e. when its dependencies are satisfied.
564 * \param task the task you want to schedule
565 * \param workstation_nb number of workstations on which the task will be executed
566 * \param workstation_list the workstations on which the task will be executed
567 * \param computation_amount computation amount for each workstation
568 * \param communication_amount communication amount between each pair of workstations
569 * \param rate task execution speed rate
570 * \see SD_task_unschedule()
572 void SD_task_schedule(SD_task_t task, int workstation_nb,
573 const SD_workstation_t * workstation_list,
574 const double *computation_amount,
575 const double *communication_amount, double rate)
578 int communication_nb;
580 SD_CHECK_INIT_DONE();
581 xbt_assert0(task != NULL, "Invalid parameter");
582 xbt_assert0(workstation_nb > 0, "workstation_nb must be positive");
584 if (!__SD_task_is_not_scheduled(task))
585 THROW1(arg_error, 0, "Task '%s' has already been scheduled",
586 SD_task_get_name(task));
588 task->workstation_nb = workstation_nb;
591 task->computation_amount = xbt_new(double, workstation_nb);
592 memcpy(task->computation_amount, computation_amount,
593 sizeof(double) * workstation_nb);
595 communication_nb = workstation_nb * workstation_nb;
596 task->communication_amount = xbt_new(double, communication_nb);
597 memcpy(task->communication_amount, communication_amount,
598 sizeof(double) * communication_nb);
600 task->workstation_list = xbt_new(SD_workstation_t, workstation_nb);
601 memcpy(task->workstation_list, workstation_list,
602 sizeof(SD_workstation_t) * workstation_nb);
604 /* update the task state */
605 if (xbt_dynar_length(task->tasks_before) == 0)
606 __SD_task_set_state(task, SD_READY);
608 __SD_task_set_state(task, SD_SCHEDULED);
612 * \brief Unschedules a task
614 * The task state must be #SD_SCHEDULED, #SD_READY, #SD_RUNNING or #SD_FAILED.
615 * If you call this function, the task state becomes #SD_NOT_SCHEDULED.
616 * Call SD_task_schedule() to schedule it again.
618 * \param task the task you want to unschedule
619 * \see SD_task_schedule()
621 void SD_task_unschedule(SD_task_t task)
623 SD_CHECK_INIT_DONE();
624 xbt_assert0(task != NULL, "Invalid parameter");
626 if (task->state_set != sd_global->scheduled_task_set &&
627 task->state_set != sd_global->ready_task_set &&
628 task->state_set != sd_global->running_task_set &&
629 task->state_set != sd_global->failed_task_set)
631 "Task %s: the state must be SD_SCHEDULED, SD_READY, SD_RUNNING or SD_FAILED",
632 SD_task_get_name(task));
634 if (__SD_task_is_scheduled_or_ready(task)) /* if the task is scheduled or ready */
635 __SD_task_destroy_scheduling_data(task);
637 if (__SD_task_is_running(task)) /* the task should become SD_FAILED */
638 surf_workstation_model->action_cancel(task->surf_action);
640 __SD_task_set_state(task, SD_NOT_SCHEDULED);
641 task->remains = task->amount;
642 task->start_time = -1.0;
645 /* Destroys the data memorised by SD_task_schedule. Task state must be SD_SCHEDULED or SD_READY.
647 static void __SD_task_destroy_scheduling_data(SD_task_t task)
649 SD_CHECK_INIT_DONE();
650 if (!__SD_task_is_scheduled_or_ready(task) && !__SD_task_is_in_fifo(task))
652 "Task '%s' must be SD_SCHEDULED, SD_READY or SD_IN_FIFO",
653 SD_task_get_name(task));
655 xbt_free(task->computation_amount);
656 xbt_free(task->communication_amount);
659 /* Runs a task. This function is directly called by __SD_task_try_to_run if the task
660 * doesn't have to wait in fifos. Otherwise, it is called by __SD_task_just_done when
661 * the task gets out of its fifos.
663 void __SD_task_really_run(SD_task_t task)
667 void **surf_workstations;
669 SD_CHECK_INIT_DONE();
670 xbt_assert0(task != NULL, "Invalid parameter");
671 xbt_assert2(__SD_task_is_ready_or_in_fifo(task),
672 "Task '%s' is not ready or in a fifo! Task state: %d",
673 SD_task_get_name(task), SD_task_get_state(task));
674 xbt_assert1(task->workstation_list != NULL,
675 "Task '%s': workstation_list is NULL!", SD_task_get_name(task));
679 DEBUG1("Really running task '%s'", SD_task_get_name(task));
681 /* set this task as current task for the workstations in sequential mode */
682 for (i = 0; i < task->workstation_nb; i++) {
683 if (SD_workstation_get_access_mode(task->workstation_list[i]) ==
684 SD_WORKSTATION_SEQUENTIAL_ACCESS) {
685 task->workstation_list[i]->current_task = task;
686 xbt_assert0(__SD_workstation_is_busy(task->workstation_list[i]),
687 "The workstation should be busy now");
691 DEBUG1("Task '%s' set as current task for its workstations",
692 SD_task_get_name(task));
696 /* we have to create a Surf workstation array instead of the SimDag workstation array */
697 surf_workstations = xbt_new(void *, task->workstation_nb);
699 for (i = 0; i < task->workstation_nb; i++) {
700 surf_workstations[i] = task->workstation_list[i]->surf_workstation;
703 task->surf_action = NULL;
704 if ((task->workstation_nb == 1) && (task->communication_amount[0] == 0.0)) {
706 surf_workstation_model->extension.
707 workstation.execute(surf_workstations[0], task->computation_amount[0]);
708 } else if ((task->workstation_nb == 1)
709 && (task->computation_amount[0] == 0.0)) {
711 surf_workstation_model->extension.
712 workstation.communicate(surf_workstations[0], surf_workstations[0],
713 task->communication_amount[0], task->rate);
714 } else if ((task->workstation_nb == 2)
715 && (task->computation_amount[0] == 0.0)
716 && (task->computation_amount[1] == 0.0)) {
720 for (i = 0; i < task->workstation_nb * task->workstation_nb; i++) {
721 if (task->communication_amount[i] > 0.0) {
723 value = task->communication_amount[i];
728 surf_workstation_model->extension.
729 workstation.communicate(surf_workstations[0], surf_workstations[1],
733 if (!task->surf_action) {
734 double *computation_amount = xbt_new(double, task->workstation_nb);
735 double *communication_amount = xbt_new(double, task->workstation_nb *
736 task->workstation_nb);
738 memcpy(computation_amount, task->computation_amount, sizeof(double) *
739 task->workstation_nb);
740 memcpy(communication_amount, task->communication_amount,
741 sizeof(double) * task->workstation_nb * task->workstation_nb);
744 surf_workstation_model->extension.
745 workstation.execute_parallel_task(task->workstation_nb,
746 surf_workstations, computation_amount,
747 communication_amount, task->amount,
750 xbt_free(surf_workstations);
753 surf_workstation_model->action_data_set(task->surf_action, task);
755 DEBUG1("surf_action = %p", task->surf_action);
757 __SD_task_destroy_scheduling_data(task); /* now the scheduling data are not useful anymore */
758 __SD_task_set_state(task, SD_RUNNING);
759 xbt_assert2(__SD_task_is_running(task), "Bad state of task '%s': %d",
760 SD_task_get_name(task), SD_task_get_state(task));
764 /* Tries to run a task. This function is called by SD_simulate() when a scheduled task becomes SD_READY
765 * (ie when its dependencies are satisfied).
766 * If one of the workstations where the task is scheduled on is busy (in sequential mode),
767 * the task doesn't start.
768 * Returns whether the task has started.
770 int __SD_task_try_to_run(SD_task_t task)
775 SD_workstation_t workstation;
777 SD_CHECK_INIT_DONE();
778 xbt_assert0(task != NULL, "Invalid parameter");
779 xbt_assert2(__SD_task_is_ready(task),
780 "Task '%s' is not ready! Task state: %d",
781 SD_task_get_name(task), SD_task_get_state(task));
784 for (i = 0; i < task->workstation_nb; i++) {
785 can_start = !__SD_workstation_is_busy(task->workstation_list[i]);
788 DEBUG2("Task '%s' can start: %d", SD_task_get_name(task), can_start);
790 if (!can_start) { /* if the task cannot start and is not in the fifos yet */
791 for (i = 0; i < task->workstation_nb; i++) {
792 workstation = task->workstation_list[i];
793 if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
794 DEBUG2("Pushing task '%s' in the fifo of workstation '%s'",
795 SD_task_get_name(task), SD_workstation_get_name(workstation));
796 xbt_fifo_push(workstation->task_fifo, task);
799 __SD_task_set_state(task, SD_IN_FIFO);
800 xbt_assert2(__SD_task_is_in_fifo(task), "Bad state of task '%s': %d",
801 SD_task_get_name(task), SD_task_get_state(task));
802 DEBUG1("Task '%s' state is now SD_IN_FIFO", SD_task_get_name(task));
804 __SD_task_really_run(task);
810 /* This function is called by SD_simulate when a task is done.
811 * It updates task->state and task->action and executes if necessary the tasks
812 * which were waiting in fifos for the end of `task'
814 void __SD_task_just_done(SD_task_t task)
817 SD_workstation_t workstation;
820 int candidate_nb = 0;
821 int candidate_capacity = 8;
822 SD_task_t *candidates;
825 SD_CHECK_INIT_DONE();
826 xbt_assert0(task != NULL, "Invalid parameter");
827 xbt_assert1(__SD_task_is_running(task),
828 "The task must be running! Task state: %d",
829 SD_task_get_state(task));
830 xbt_assert1(task->workstation_list != NULL,
831 "Task '%s': workstation_list is NULL!", SD_task_get_name(task));
834 candidates = xbt_new(SD_task_t, 8);
836 __SD_task_set_state(task, SD_DONE);
837 surf_workstation_model->action_unref(task->surf_action);
838 task->surf_action = NULL;
840 DEBUG0("Looking for candidates");
842 /* if the task was executed on sequential workstations,
843 maybe we can execute the next task of the fifo for each workstation */
844 for (i = 0; i < task->workstation_nb; i++) {
845 workstation = task->workstation_list[i];
846 DEBUG2("Workstation '%s': access_mode = %d",
847 SD_workstation_get_name(workstation), workstation->access_mode);
848 if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
849 xbt_assert1(workstation->task_fifo != NULL,
850 "Workstation '%s' has sequential access but no fifo!",
851 SD_workstation_get_name(workstation));
852 xbt_assert2(workstation->current_task =
853 task, "Workstation '%s': current task should be '%s'",
854 SD_workstation_get_name(workstation),
855 SD_task_get_name(task));
857 /* the task is over so we can release the workstation */
858 workstation->current_task = NULL;
860 DEBUG0("Getting candidate in fifo");
862 xbt_fifo_get_item_content(xbt_fifo_get_first_item
863 (workstation->task_fifo));
865 if (candidate != NULL) {
866 DEBUG1("Candidate: '%s'", SD_task_get_name(candidate));
867 xbt_assert2(__SD_task_is_in_fifo(candidate),
868 "Bad state of candidate '%s': %d",
869 SD_task_get_name(candidate),
870 SD_task_get_state(candidate));
873 DEBUG1("Candidate in fifo: %p", candidate);
875 /* if there was a task waiting for my place */
876 if (candidate != NULL) {
877 /* Unfortunately, we are not sure yet that we can execute the task now,
878 because the task can be waiting more deeply in some other workstation's fifos...
879 So we memorize all candidate tasks, and then we will check for each candidate
880 whether or not all its workstations are available. */
882 /* realloc if necessary */
883 if (candidate_nb == candidate_capacity) {
884 candidate_capacity *= 2;
886 xbt_realloc(candidates, sizeof(SD_task_t) * candidate_capacity);
889 /* register the candidate */
890 candidates[candidate_nb++] = candidate;
891 candidate->fifo_checked = 0;
896 DEBUG1("Candidates found: %d", candidate_nb);
898 /* now we check every candidate task */
899 for (i = 0; i < candidate_nb; i++) {
900 candidate = candidates[i];
902 if (candidate->fifo_checked) {
903 continue; /* we have already evaluated that task */
906 xbt_assert2(__SD_task_is_in_fifo(candidate),
907 "Bad state of candidate '%s': %d",
908 SD_task_get_name(candidate), SD_task_get_state(candidate));
910 for (j = 0; j < candidate->workstation_nb && can_start; j++) {
911 workstation = candidate->workstation_list[j];
913 /* I can start on this workstation if the workstation is shared
914 or if I am the first task in the fifo */
915 can_start = workstation->access_mode == SD_WORKSTATION_SHARED_ACCESS ||
917 xbt_fifo_get_item_content(xbt_fifo_get_first_item
918 (workstation->task_fifo));
921 DEBUG2("Candidate '%s' can start: %d", SD_task_get_name(candidate),
924 /* now we are sure that I can start! */
926 for (j = 0; j < candidate->workstation_nb && can_start; j++) {
927 workstation = candidate->workstation_list[j];
929 /* update the fifo */
930 if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
931 candidate = xbt_fifo_shift(workstation->task_fifo); /* the return value is stored just for debugging */
932 DEBUG1("Head of the fifo: '%s'",
933 (candidate != NULL) ? SD_task_get_name(candidate) : "NULL");
934 xbt_assert0(candidate == candidates[i],
935 "Error in __SD_task_just_done: bad first task in the fifo");
937 } /* for each workstation */
939 /* finally execute the task */
940 DEBUG2("Task '%s' state: %d", SD_task_get_name(candidate),
941 SD_task_get_state(candidate));
942 __SD_task_really_run(candidate);
945 ("Calling __SD_task_is_running: task '%s', state set: %p, running_task_set: %p, is running: %d",
946 SD_task_get_name(candidate), candidate->state_set,
947 sd_global->running_task_set, __SD_task_is_running(candidate));
948 xbt_assert2(__SD_task_is_running(candidate),
949 "Bad state of task '%s': %d", SD_task_get_name(candidate),
950 SD_task_get_state(candidate));
951 DEBUG0("Okay, the task is running.");
954 candidate->fifo_checked = 1;
955 } /* for each candidate */
957 xbt_free(candidates);
960 /* Remove all dependencies associated with a task. This function is called when the task is destroyed.
962 static void __SD_task_remove_dependencies(SD_task_t task)
964 /* we must destroy the dependencies carefuly (with SD_dependency_remove)
965 because each one is stored twice */
966 SD_dependency_t dependency;
967 while (xbt_dynar_length(task->tasks_before) > 0) {
968 xbt_dynar_get_cpy(task->tasks_before, 0, &dependency);
969 SD_task_dependency_remove(dependency->src, dependency->dst);
972 while (xbt_dynar_length(task->tasks_after) > 0) {
973 xbt_dynar_get_cpy(task->tasks_after, 0, &dependency);
974 SD_task_dependency_remove(dependency->src, dependency->dst);
979 * \brief Returns the start time of a task
981 * The task state must be SD_RUNNING, SD_DONE or SD_FAILED.
983 * \param task: a task
984 * \return the start time of this task
986 double SD_task_get_start_time(SD_task_t task)
988 SD_CHECK_INIT_DONE();
989 xbt_assert0(task != NULL, "Invalid parameter");
990 if (task->surf_action)
991 return surf_workstation_model->action_get_start_time(task->surf_action);
993 return task->start_time;
997 * \brief Returns the finish time of a task
999 * The task state must be SD_RUNNING, SD_DONE or SD_FAILED.
1000 * If the state is not completed yet, the returned value is an
1001 * estimation of the task finish time. This value can fluctuate
1002 * until the task is completed.
1004 * \param task: a task
1005 * \return the start time of this task
1007 double SD_task_get_finish_time(SD_task_t task)
1009 SD_CHECK_INIT_DONE();
1010 xbt_assert0(task != NULL, "Invalid parameter");
1012 if (task->surf_action) /* should never happen as actions are destroyed right after their completion */
1013 return surf_workstation_model->action_get_finish_time(task->surf_action);
1015 return task->finish_time;
1019 * \brief Destroys a task.
1021 * The user data (if any) should have been destroyed first.
1023 * \param task the task you want to destroy
1024 * \see SD_task_create()
1026 void SD_task_destroy(SD_task_t task)
1028 SD_CHECK_INIT_DONE();
1029 xbt_assert0(task != NULL, "Invalid parameter");
1031 DEBUG1("Destroying task %s...", SD_task_get_name(task));
1033 __SD_task_remove_dependencies(task);
1035 /* if the task was scheduled or ready we have to free the scheduling parameters */
1036 if (__SD_task_is_scheduled_or_ready(task))
1037 __SD_task_destroy_scheduling_data(task);
1039 if (task->name != NULL)
1040 xbt_free(task->name);
1042 if (task->surf_action != NULL)
1043 surf_workstation_model->action_unref(task->surf_action);
1045 if (task->workstation_list != NULL)
1046 xbt_free(task->workstation_list);
1048 xbt_dynar_free(&task->tasks_before);
1049 xbt_dynar_free(&task->tasks_after);
1052 sd_global->task_number--;
1054 DEBUG0("Task destroyed.");
1058 /** @brief create a end-to-end communication task that can then be auto-scheduled
1060 * Auto-scheduling mean that the task can be used with SD_task_schedulev(). This
1061 * allows to specify the task costs at creation, and decorelate them from the
1062 * scheduling process where you just specify which resource should deliver the
1065 * A end-to-end communication must be scheduled on 2 hosts, and the amount
1066 * specified at creation is sent from hosts[0] to hosts[1].
1068 SD_task_t SD_task_create_comm_e2e(const char*name, void *data, double amount) {
1069 SD_task_t res = SD_task_create(name,data,amount);
1070 res->kind=SD_TASK_COMM_E2E;
1073 /** @brief create a sequential computation task that can then be auto-scheduled
1075 * Auto-scheduling mean that the task can be used with SD_task_schedulev(). This
1076 * allows to specify the task costs at creation, and decorelate them from the
1077 * scheduling process where you just specify which resource should deliver the
1080 * A sequential computation must be scheduled on 1 host, and the amount
1081 * specified at creation to be run on hosts[0].
1083 SD_task_t SD_task_create_comp_seq(const char*name, void *data, double amount) {
1084 SD_task_t res = SD_task_create(name,data,amount);
1085 res->kind=SD_TASK_COMP_SEQ;
1089 /** @brief Auto-schedules a task.
1091 * Auto-scheduling mean that the task can be used with SD_task_schedulev(). This
1092 * allows to specify the task costs at creation, and decorelate them from the
1093 * scheduling process where you just specify which resource should deliver the
1096 * To be auto-schedulable, a task must be created with SD_task_create_comm_e2e() or
1097 * SD_task_create_comp_seq(). Check their definitions for the exact semantic of each
1101 * We should create tasks kind for the following categories:
1102 * - Point to point communication (done)
1103 * - Sequential computation (done)
1104 * - group communication (redistribution, several kinds)
1105 * - parallel tasks with no internal communication (one kind per speedup model such as amdal)
1106 * - idem+ internal communication. Task type not enough since we cannot store comm cost alongside to comp one)
1108 void SD_task_schedulev(SD_task_t task, int count, const SD_workstation_t*list) {
1109 xbt_assert1(task->kind != 0,"Task %s is not typed. Cannot automatically schedule it.",SD_task_get_name(task));
1110 double *comp,*comms;
1111 switch(task->kind) {
1112 case SD_TASK_COMM_E2E:
1113 xbt_assert2(count == 2,
1114 "Task %s is end to end communication, but scheduled with %d hosts",
1115 SD_task_get_name(task),count);
1116 comms=xbt_new(double,count);
1118 comms[1]=SD_task_get_amount(task);
1119 SD_task_schedule(task,count,list,NULL,comms,1);
1121 case SD_TASK_COMP_SEQ:
1122 xbt_assert2(count==1,
1123 "Task %s is sequential computation, but scheduled with %d hosts",
1124 SD_task_get_name(task),count);
1125 comp=xbt_new(double,count);
1126 comp[0]=SD_task_get_amount(task);
1127 SD_task_schedule(task,count,list,comp,NULL,1);
1130 xbt_die(bprintf("Kind of task %s not supported by SD_task_schedulev()",
1131 SD_task_get_name(task)));
1134 /** @brief autoschedule a task on a list of workstations
1136 * This function is very similar to SD_task_schedulev(),
1137 * but takes the list of workstations to schedule onto as separate parameters.
1138 * It builds a proper vector of workstations and then call SD_task_schedulev()
1140 void SD_task_schedulel(SD_task_t task, int count, ...) {
1142 SD_workstation_t *list=xbt_new(SD_workstation_t,count);
1145 for (i=0;i<count;i++) {
1146 list[i] = va_arg(ap,SD_workstation_t);
1149 SD_task_schedulev(task,count,list);