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 */
37 task->name = xbt_strdup(name);
41 task->state_hookup.prev = NULL;
42 task->state_hookup.next = NULL;
43 task->state_set = sd_global->not_scheduled_task_set;
44 task->state = SD_NOT_SCHEDULED;
45 xbt_swag_insert(task, task->state_set);
47 task->amount = amount;
48 task->remains = amount;
49 task->start_time = -1.0;
50 task->finish_time = -1.0;
51 task->surf_action = NULL;
52 task->watch_points = 0;
55 task->tasks_before = xbt_dynar_new(sizeof(SD_dependency_t), NULL);
56 task->tasks_after = xbt_dynar_new(sizeof(SD_dependency_t), NULL);
58 /* scheduling parameters */
59 task->workstation_nb = 0;
60 task->workstation_list = NULL;
61 task->computation_amount = NULL;
62 task->communication_amount = NULL;
65 sd_global->task_number++;
71 * \brief Returns the user data of a task
74 * \return the user data associated with this task (can be \c NULL)
75 * \see SD_task_set_data()
77 void *SD_task_get_data(SD_task_t task)
80 xbt_assert0(task != NULL, "Invalid parameter");
85 * \brief Sets the user data of a task
87 * The new data can be \c NULL. The old data should have been freed first
88 * if it was not \c NULL.
91 * \param data the new data you want to associate with this task
92 * \see SD_task_get_data()
94 void SD_task_set_data(SD_task_t task, void *data)
97 xbt_assert0(task != NULL, "Invalid parameter");
102 * \brief Returns the state of a task
105 * \return the current \ref e_SD_task_state_t "state" of this task:
106 * #SD_NOT_SCHEDULED, #SD_SCHEDULED, #SD_READY, #SD_RUNNING, #SD_DONE or #SD_FAILED
107 * \see e_SD_task_state_t
109 e_SD_task_state_t SD_task_get_state(SD_task_t task)
111 SD_CHECK_INIT_DONE();
112 xbt_assert0(task != NULL, "Invalid parameter");
116 /* Changes the state of a task. Updates the swags and the flag sd_global->watch_point_reached.
118 void __SD_task_set_state(SD_task_t task, e_SD_task_state_t new_state)
120 xbt_swag_remove(task, task->state_set);
122 case SD_NOT_SCHEDULED:
123 task->state_set = sd_global->not_scheduled_task_set;
126 task->state_set = sd_global->scheduled_task_set;
129 task->state_set = sd_global->ready_task_set;
132 task->state_set = sd_global->in_fifo_task_set;
135 task->state_set = sd_global->running_task_set;
137 surf_workstation_model->action_get_start_time(task->surf_action);
140 task->state_set = sd_global->done_task_set;
142 surf_workstation_model->action_get_finish_time(task->surf_action);
146 task->state_set = sd_global->failed_task_set;
149 xbt_assert0(0, "Invalid state");
151 xbt_swag_insert(task, task->state_set);
152 task->state = new_state;
154 if (task->watch_points & new_state) {
155 INFO1("Watch point reached with task '%s'!", SD_task_get_name(task));
156 sd_global->watch_point_reached = 1;
157 SD_task_unwatch(task, new_state); /* remove the watch point */
162 * \brief Returns the name of a task
165 * \return the name of this task (can be \c NULL)
167 const char *SD_task_get_name(SD_task_t task)
169 SD_CHECK_INIT_DONE();
170 xbt_assert0(task != NULL, "Invalid parameter");
175 * \brief Returns the total amount of a task
178 * \return the total amount of this task
179 * \see SD_task_get_remaining_amount()
181 double SD_task_get_amount(SD_task_t task)
183 SD_CHECK_INIT_DONE();
184 xbt_assert0(task != NULL, "Invalid parameter");
189 * \brief Returns the remaining amount of a task
192 * \return the remaining amount of this task
193 * \see SD_task_get_amount()
195 double SD_task_get_remaining_amount(SD_task_t task)
197 SD_CHECK_INIT_DONE();
198 xbt_assert0(task != NULL, "Invalid parameter");
200 if (task->surf_action)
201 return surf_workstation_model->get_remains(task->surf_action);
203 return task->remains;
206 /* temporary function for debbuging */
207 void SD_task_dump(SD_task_t task)
209 unsigned int counter;
210 SD_dependency_t dependency;
212 INFO1("Displaying task %s",SD_task_get_name(task));
213 INFO1(" - amount: %.0f",SD_task_get_amount(task));
214 if (xbt_dynar_length(task->tasks_before)) {
215 INFO0(" - pre-dependencies:");
216 xbt_dynar_foreach(task->tasks_before,counter,dependency) {
217 INFO1(" %s",SD_task_get_name(dependency->src));
220 if (xbt_dynar_length(task->tasks_after)) {
221 INFO0(" - post-dependencies:");
222 xbt_dynar_foreach(task->tasks_after,counter,dependency) {
223 INFO1(" %s",SD_task_get_name(dependency->dst));
228 /* Destroys a dependency between two tasks.
230 static void __SD_task_dependency_destroy(void *dependency)
232 if (((SD_dependency_t) dependency)->name != NULL)
233 xbt_free(((SD_dependency_t) dependency)->name);
234 xbt_free(dependency);
238 * \brief Adds a dependency between two tasks
240 * \a dst will depend on \a src, ie \a dst will not start before \a src is finished.
241 * Their \ref e_SD_task_state_t "state" must be #SD_NOT_SCHEDULED, #SD_SCHEDULED or #SD_READY.
243 * \param name the name of the new dependency (can be \c NULL)
244 * \param data the user data you want to associate with this dependency (can be \c NULL)
245 * \param src the task which must be executed first
246 * \param dst the task you want to make depend on \a src
247 * \see SD_task_dependency_remove()
249 void SD_task_dependency_add(const char *name, void *data, SD_task_t src,
256 SD_dependency_t dependency;
258 SD_CHECK_INIT_DONE();
259 xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
261 dynar = src->tasks_after;
262 length = xbt_dynar_length(dynar);
266 "Cannot add a dependency between task '%s' and itself",
267 SD_task_get_name(src));
269 if (!__SD_task_is_not_scheduled(src)
270 && !__SD_task_is_scheduled_or_ready(src))
272 "Task '%s' must be SD_NOT_SCHEDULED, SD_SCHEDULED or SD_READY",
273 SD_task_get_name(src));
275 if (!__SD_task_is_not_scheduled(dst)
276 && !__SD_task_is_scheduled_or_ready(dst))
278 "Task '%s' must be SD_NOT_SCHEDULED, SD_SCHEDULED or SD_READY",
279 SD_task_get_name(dst));
281 DEBUG2("SD_task_dependency_add: src = %s, dst = %s", SD_task_get_name(src),
282 SD_task_get_name(dst));
283 for (i = 0; i < length && !found; i++) {
284 xbt_dynar_get_cpy(dynar, i, &dependency);
285 found = (dependency->dst == dst);
286 DEBUG2("Dependency %d: dependency->dst = %s", i,
287 SD_task_get_name(dependency->dst));
292 "A dependency already exists between task '%s' and task '%s'",
293 SD_task_get_name(src), SD_task_get_name(dst));
295 dependency = xbt_new(s_SD_dependency_t, 1);
297 dependency->name = xbt_strdup(name); /* xbt_strdup is cleaver enough to deal with NULL args itself */
298 dependency->data = data;
299 dependency->src = src;
300 dependency->dst = dst;
302 /* src must be executed before dst */
303 xbt_dynar_push(src->tasks_after, &dependency);
304 xbt_dynar_push(dst->tasks_before, &dependency);
306 /* if the task was ready, then dst->tasks_before is not empty anymore,
307 so we must go back to state SD_SCHEDULED */
308 if (__SD_task_is_ready(dst)) {
309 DEBUG1("SD_task_dependency_add: %s was ready and becomes scheduled!",
310 SD_task_get_name(dst));
311 __SD_task_set_state(dst, SD_SCHEDULED);
314 /* __SD_print_dependencies(src);
315 __SD_print_dependencies(dst); */
319 * \brief Indacates whether there is a dependency between two tasks.
322 * \param dst a task depending on \a src
324 * If src is NULL, checks whether dst has any pre-dependency.
325 * If dst is NULL, checks whether src has any post-dependency.
327 int SD_task_dependency_exists(SD_task_t src, SD_task_t dst)
329 unsigned int counter;
330 SD_dependency_t dependency;
332 SD_CHECK_INIT_DONE();
333 xbt_assert0(src != NULL || dst != NULL, "Invalid parameter: both src and dst are NULL");
337 xbt_dynar_foreach(src->tasks_after,counter,dependency) {
338 if (dependency->dst == dst)
342 return xbt_dynar_length(src->tasks_after);
345 return xbt_dynar_length(dst->tasks_before);
351 * \brief Remove a dependency between two tasks
354 * \param dst a task depending on \a src
355 * \see SD_task_dependency_add()
357 void SD_task_dependency_remove(SD_task_t src, SD_task_t dst)
364 SD_dependency_t dependency;
366 SD_CHECK_INIT_DONE();
367 xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
369 /* remove the dependency from src->tasks_after */
370 dynar = src->tasks_after;
371 length = xbt_dynar_length(dynar);
373 for (i = 0; i < length && !found; i++) {
374 xbt_dynar_get_cpy(dynar, i, &dependency);
375 if (dependency->dst == dst) {
376 xbt_dynar_remove_at(dynar, i, NULL);
382 "No dependency found between task '%s' and '%s': task '%s' is not a successor of task '%s'",
383 SD_task_get_name(src), SD_task_get_name(dst),
384 SD_task_get_name(dst), SD_task_get_name(src));
386 /* remove the dependency from dst->tasks_before */
387 dynar = dst->tasks_before;
388 length = xbt_dynar_length(dynar);
391 for (i = 0; i < length && !found; i++) {
392 xbt_dynar_get_cpy(dynar, i, &dependency);
393 if (dependency->src == src) {
394 xbt_dynar_remove_at(dynar, i, NULL);
395 __SD_task_dependency_destroy(dependency);
399 /* should never happen... */
401 "SimDag error: task '%s' is a successor of '%s' but task '%s' is not a predecessor of task '%s'",
402 SD_task_get_name(dst), SD_task_get_name(src),
403 SD_task_get_name(src), SD_task_get_name(dst));
405 /* if the task was scheduled and dst->tasks_before is empty now, we can make it ready */
406 if (xbt_dynar_length(dst->tasks_before) == 0 && __SD_task_is_scheduled(dst))
407 __SD_task_set_state(dst, SD_READY);
409 /* __SD_print_dependencies(src);
410 __SD_print_dependencies(dst); */
414 * \brief Returns the user data associated with a dependency between two tasks
417 * \param dst a task depending on \a src
418 * \return the user data associated with this dependency (can be \c NULL)
419 * \see SD_task_dependency_add()
421 void *SD_task_dependency_get_data(SD_task_t src, SD_task_t dst)
428 SD_dependency_t dependency;
431 SD_CHECK_INIT_DONE();
432 xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
434 dynar = src->tasks_after;
435 length = xbt_dynar_length(dynar);
437 for (i = 0; i < length && !found; i++) {
438 xbt_dynar_get_cpy(dynar, i, &dependency);
439 found = (dependency->dst == dst);
442 THROW2(arg_error, 0, "No dependency found between task '%s' and '%s'",
443 SD_task_get_name(src), SD_task_get_name(dst));
444 return dependency->data;
447 /* temporary function for debugging */
448 static void __SD_print_watch_points(SD_task_t task)
450 static const int state_masks[] =
451 { SD_SCHEDULED, SD_RUNNING, SD_READY, SD_DONE, SD_FAILED };
452 static const char *state_names[] =
453 { "scheduled", "running", "ready", "done", "failed" };
456 INFO2("Task '%s' watch points (%x): ", SD_task_get_name(task),
460 for (i = 0; i < 5; i++) {
461 if (task->watch_points & state_masks[i])
462 INFO1("%s ", state_names[i]);
467 * \brief Adds a watch point to a task
469 * SD_simulate() will stop as soon as the \ref e_SD_task_state_t "state" of this
470 * task becomes the one given in argument. The
471 * watch point is then automatically removed.
474 * \param state the \ref e_SD_task_state_t "state" you want to watch
475 * (cannot be #SD_NOT_SCHEDULED)
476 * \see SD_task_unwatch()
478 void SD_task_watch(SD_task_t task, e_SD_task_state_t state)
480 SD_CHECK_INIT_DONE();
481 xbt_assert0(task != NULL, "Invalid parameter");
483 if (state & SD_NOT_SCHEDULED)
485 "Cannot add a watch point for state SD_NOT_SCHEDULED");
487 task->watch_points = task->watch_points | state;
488 /* __SD_print_watch_points(task); */
492 * \brief Removes a watch point from a task
495 * \param state the \ref e_SD_task_state_t "state" you no longer want to watch
496 * \see SD_task_watch()
498 void SD_task_unwatch(SD_task_t task, e_SD_task_state_t state)
500 SD_CHECK_INIT_DONE();
501 xbt_assert0(task != NULL, "Invalid parameter");
502 xbt_assert0(state != SD_NOT_SCHEDULED,
503 "SimDag error: Cannot have a watch point for state SD_NOT_SCHEDULED");
505 task->watch_points = task->watch_points & ~state;
506 /* __SD_print_watch_points(task); */
510 * \brief Returns an approximative estimation of the execution time of a task.
512 * The estimation is very approximative because the value returned is the time
513 * the task would take if it was executed now and if it was the only task.
515 * \param task the task to evaluate
516 * \param workstation_nb number of workstations on which the task would be executed
517 * \param workstation_list the workstations on which the task would be executed
518 * \param computation_amount computation amount for each workstation
519 * \param communication_amount communication amount between each pair of workstations
520 * \param rate task execution speed rate
523 double SD_task_get_execution_time(SD_task_t task,
525 const SD_workstation_t * workstation_list,
526 const double *computation_amount,
527 const double *communication_amount,
530 double time, max_time = 0.0;
532 SD_CHECK_INIT_DONE();
533 xbt_assert0(task != NULL && workstation_nb > 0 && workstation_list != NULL
534 && computation_amount != NULL
535 && communication_amount != NULL, "Invalid parameter");
537 /* the task execution time is the maximum execution time of the parallel tasks */
539 for (i = 0; i < workstation_nb; i++) {
541 SD_workstation_get_computation_time(workstation_list[i],
542 computation_amount[i]);
544 for (j = 0; j < workstation_nb; j++) {
546 SD_route_get_communication_time(workstation_list[i],
548 communication_amount[i *
553 if (time > max_time) {
557 return max_time * SD_task_get_amount(task);
561 * \brief Schedules a task
563 * The task state must be #SD_NOT_SCHEDULED.
564 * Once scheduled, a task will be executed as soon as possible in SD_simulate(),
565 * i.e. when its dependencies are satisfied.
567 * \param task the task you want to schedule
568 * \param workstation_nb number of workstations on which the task will be executed
569 * \param workstation_list the workstations on which the task will be executed
570 * \param computation_amount computation amount for each workstation
571 * \param communication_amount communication amount between each pair of workstations
572 * \param rate task execution speed rate
573 * \see SD_task_unschedule()
575 void SD_task_schedule(SD_task_t task, int workstation_nb,
576 const SD_workstation_t * workstation_list,
577 const double *computation_amount,
578 const double *communication_amount, double rate)
581 int communication_nb;
583 SD_CHECK_INIT_DONE();
584 xbt_assert0(task != NULL, "Invalid parameter");
585 xbt_assert0(workstation_nb > 0, "workstation_nb must be positive");
587 if (!__SD_task_is_not_scheduled(task))
588 THROW1(arg_error, 0, "Task '%s' has already been scheduled",
589 SD_task_get_name(task));
591 task->workstation_nb = workstation_nb;
594 task->computation_amount = xbt_new(double, workstation_nb);
595 memcpy(task->computation_amount, computation_amount,
596 sizeof(double) * workstation_nb);
598 communication_nb = workstation_nb * workstation_nb;
599 task->communication_amount = xbt_new(double, communication_nb);
600 memcpy(task->communication_amount, communication_amount,
601 sizeof(double) * communication_nb);
603 task->workstation_list = xbt_new(SD_workstation_t, workstation_nb);
604 memcpy(task->workstation_list, workstation_list,
605 sizeof(SD_workstation_t) * workstation_nb);
607 /* update the task state */
608 if (xbt_dynar_length(task->tasks_before) == 0)
609 __SD_task_set_state(task, SD_READY);
611 __SD_task_set_state(task, SD_SCHEDULED);
615 * \brief Unschedules a task
617 * The task state must be #SD_SCHEDULED, #SD_READY, #SD_RUNNING or #SD_FAILED.
618 * If you call this function, the task state becomes #SD_NOT_SCHEDULED.
619 * Call SD_task_schedule() to schedule it again.
621 * \param task the task you want to unschedule
622 * \see SD_task_schedule()
624 void SD_task_unschedule(SD_task_t task)
626 SD_CHECK_INIT_DONE();
627 xbt_assert0(task != NULL, "Invalid parameter");
629 if (task->state_set != sd_global->scheduled_task_set &&
630 task->state_set != sd_global->ready_task_set &&
631 task->state_set != sd_global->running_task_set &&
632 task->state_set != sd_global->failed_task_set)
634 "Task %s: the state must be SD_SCHEDULED, SD_READY, SD_RUNNING or SD_FAILED",
635 SD_task_get_name(task));
637 if (__SD_task_is_scheduled_or_ready(task)) /* if the task is scheduled or ready */
638 __SD_task_destroy_scheduling_data(task);
640 if (__SD_task_is_running(task)) /* the task should become SD_FAILED */
641 surf_workstation_model->action_cancel(task->surf_action);
643 __SD_task_set_state(task, SD_NOT_SCHEDULED);
644 task->remains = task->amount;
645 task->start_time = -1.0;
648 /* Destroys the data memorised by SD_task_schedule. Task state must be SD_SCHEDULED or SD_READY.
650 static void __SD_task_destroy_scheduling_data(SD_task_t task)
652 SD_CHECK_INIT_DONE();
653 if (!__SD_task_is_scheduled_or_ready(task) && !__SD_task_is_in_fifo(task))
655 "Task '%s' must be SD_SCHEDULED, SD_READY or SD_IN_FIFO",
656 SD_task_get_name(task));
658 xbt_free(task->computation_amount);
659 xbt_free(task->communication_amount);
662 /* Runs a task. This function is directly called by __SD_task_try_to_run if the task
663 * doesn't have to wait in fifos. Otherwise, it is called by __SD_task_just_done when
664 * the task gets out of its fifos.
666 void __SD_task_really_run(SD_task_t task)
670 void **surf_workstations;
672 SD_CHECK_INIT_DONE();
673 xbt_assert0(task != NULL, "Invalid parameter");
674 xbt_assert2(__SD_task_is_ready_or_in_fifo(task),
675 "Task '%s' is not ready or in a fifo! Task state: %d",
676 SD_task_get_name(task), SD_task_get_state(task));
677 xbt_assert1(task->workstation_list != NULL,
678 "Task '%s': workstation_list is NULL!", SD_task_get_name(task));
682 DEBUG1("Really running task '%s'", SD_task_get_name(task));
684 /* set this task as current task for the workstations in sequential mode */
685 for (i = 0; i < task->workstation_nb; i++) {
686 if (SD_workstation_get_access_mode(task->workstation_list[i]) ==
687 SD_WORKSTATION_SEQUENTIAL_ACCESS) {
688 task->workstation_list[i]->current_task = task;
689 xbt_assert0(__SD_workstation_is_busy(task->workstation_list[i]),
690 "The workstation should be busy now");
694 DEBUG1("Task '%s' set as current task for its workstations",
695 SD_task_get_name(task));
699 /* we have to create a Surf workstation array instead of the SimDag workstation array */
700 surf_workstations = xbt_new(void *, task->workstation_nb);
702 for (i = 0; i < task->workstation_nb; i++) {
703 surf_workstations[i] = task->workstation_list[i]->surf_workstation;
706 task->surf_action = NULL;
707 if ((task->workstation_nb == 1) && (task->communication_amount[0] == 0.0)) {
709 surf_workstation_model->extension.
710 workstation.execute(surf_workstations[0], task->computation_amount[0]);
711 } else if ((task->workstation_nb == 1)
712 && (task->computation_amount[0] == 0.0)) {
714 surf_workstation_model->extension.
715 workstation.communicate(surf_workstations[0], surf_workstations[0],
716 task->communication_amount[0], task->rate);
717 } else if ((task->workstation_nb == 2)
718 && (task->computation_amount[0] == 0.0)
719 && (task->computation_amount[1] == 0.0)) {
723 for (i = 0; i < task->workstation_nb * task->workstation_nb; i++) {
724 if (task->communication_amount[i] > 0.0) {
726 value = task->communication_amount[i];
731 surf_workstation_model->extension.
732 workstation.communicate(surf_workstations[0], surf_workstations[1],
736 if (!task->surf_action) {
737 double *computation_amount = xbt_new(double, task->workstation_nb);
738 double *communication_amount = xbt_new(double, task->workstation_nb *
739 task->workstation_nb);
741 memcpy(computation_amount, task->computation_amount, sizeof(double) *
742 task->workstation_nb);
743 memcpy(communication_amount, task->communication_amount,
744 sizeof(double) * task->workstation_nb * task->workstation_nb);
747 surf_workstation_model->extension.
748 workstation.execute_parallel_task(task->workstation_nb,
749 surf_workstations, computation_amount,
750 communication_amount, task->amount,
753 xbt_free(surf_workstations);
756 surf_workstation_model->action_data_set(task->surf_action, task);
758 DEBUG1("surf_action = %p", task->surf_action);
760 __SD_task_destroy_scheduling_data(task); /* now the scheduling data are not useful anymore */
761 __SD_task_set_state(task, SD_RUNNING);
762 xbt_assert2(__SD_task_is_running(task), "Bad state of task '%s': %d",
763 SD_task_get_name(task), SD_task_get_state(task));
767 /* Tries to run a task. This function is called by SD_simulate() when a scheduled task becomes SD_READY
768 * (ie when its dependencies are satisfied).
769 * If one of the workstations where the task is scheduled on is busy (in sequential mode),
770 * the task doesn't start.
771 * Returns whether the task has started.
773 int __SD_task_try_to_run(SD_task_t task)
778 SD_workstation_t workstation;
780 SD_CHECK_INIT_DONE();
781 xbt_assert0(task != NULL, "Invalid parameter");
782 xbt_assert2(__SD_task_is_ready(task),
783 "Task '%s' is not ready! Task state: %d",
784 SD_task_get_name(task), SD_task_get_state(task));
787 for (i = 0; i < task->workstation_nb; i++) {
788 can_start = !__SD_workstation_is_busy(task->workstation_list[i]);
791 DEBUG2("Task '%s' can start: %d", SD_task_get_name(task), can_start);
793 if (!can_start) { /* if the task cannot start and is not in the fifos yet */
794 for (i = 0; i < task->workstation_nb; i++) {
795 workstation = task->workstation_list[i];
796 if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
797 DEBUG2("Pushing task '%s' in the fifo of workstation '%s'",
798 SD_task_get_name(task), SD_workstation_get_name(workstation));
799 xbt_fifo_push(workstation->task_fifo, task);
802 __SD_task_set_state(task, SD_IN_FIFO);
803 xbt_assert2(__SD_task_is_in_fifo(task), "Bad state of task '%s': %d",
804 SD_task_get_name(task), SD_task_get_state(task));
805 DEBUG1("Task '%s' state is now SD_IN_FIFO", SD_task_get_name(task));
807 __SD_task_really_run(task);
813 /* This function is called by SD_simulate when a task is done.
814 * It updates task->state and task->action and executes if necessary the tasks
815 * which were waiting in fifos for the end of `task'
817 void __SD_task_just_done(SD_task_t task)
820 SD_workstation_t workstation;
823 int candidate_nb = 0;
824 int candidate_capacity = 8;
825 SD_task_t *candidates;
828 SD_CHECK_INIT_DONE();
829 xbt_assert0(task != NULL, "Invalid parameter");
830 xbt_assert1(__SD_task_is_running(task),
831 "The task must be running! Task state: %d",
832 SD_task_get_state(task));
833 xbt_assert1(task->workstation_list != NULL,
834 "Task '%s': workstation_list is NULL!", SD_task_get_name(task));
837 candidates = xbt_new(SD_task_t, 8);
839 __SD_task_set_state(task, SD_DONE);
840 surf_workstation_model->action_unref(task->surf_action);
841 task->surf_action = NULL;
843 DEBUG0("Looking for candidates");
845 /* if the task was executed on sequential workstations,
846 maybe we can execute the next task of the fifo for each workstation */
847 for (i = 0; i < task->workstation_nb; i++) {
848 workstation = task->workstation_list[i];
849 DEBUG2("Workstation '%s': access_mode = %d",
850 SD_workstation_get_name(workstation), workstation->access_mode);
851 if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
852 xbt_assert1(workstation->task_fifo != NULL,
853 "Workstation '%s' has sequential access but no fifo!",
854 SD_workstation_get_name(workstation));
855 xbt_assert2(workstation->current_task =
856 task, "Workstation '%s': current task should be '%s'",
857 SD_workstation_get_name(workstation),
858 SD_task_get_name(task));
860 /* the task is over so we can release the workstation */
861 workstation->current_task = NULL;
863 DEBUG0("Getting candidate in fifo");
865 xbt_fifo_get_item_content(xbt_fifo_get_first_item
866 (workstation->task_fifo));
868 if (candidate != NULL) {
869 DEBUG1("Candidate: '%s'", SD_task_get_name(candidate));
870 xbt_assert2(__SD_task_is_in_fifo(candidate),
871 "Bad state of candidate '%s': %d",
872 SD_task_get_name(candidate),
873 SD_task_get_state(candidate));
876 DEBUG1("Candidate in fifo: %p", candidate);
878 /* if there was a task waiting for my place */
879 if (candidate != NULL) {
880 /* Unfortunately, we are not sure yet that we can execute the task now,
881 because the task can be waiting more deeply in some other workstation's fifos...
882 So we memorize all candidate tasks, and then we will check for each candidate
883 whether or not all its workstations are available. */
885 /* realloc if necessary */
886 if (candidate_nb == candidate_capacity) {
887 candidate_capacity *= 2;
889 xbt_realloc(candidates, sizeof(SD_task_t) * candidate_capacity);
892 /* register the candidate */
893 candidates[candidate_nb++] = candidate;
894 candidate->fifo_checked = 0;
899 DEBUG1("Candidates found: %d", candidate_nb);
901 /* now we check every candidate task */
902 for (i = 0; i < candidate_nb; i++) {
903 candidate = candidates[i];
905 if (candidate->fifo_checked) {
906 continue; /* we have already evaluated that task */
909 xbt_assert2(__SD_task_is_in_fifo(candidate),
910 "Bad state of candidate '%s': %d",
911 SD_task_get_name(candidate), SD_task_get_state(candidate));
913 for (j = 0; j < candidate->workstation_nb && can_start; j++) {
914 workstation = candidate->workstation_list[j];
916 /* I can start on this workstation if the workstation is shared
917 or if I am the first task in the fifo */
918 can_start = workstation->access_mode == SD_WORKSTATION_SHARED_ACCESS ||
920 xbt_fifo_get_item_content(xbt_fifo_get_first_item
921 (workstation->task_fifo));
924 DEBUG2("Candidate '%s' can start: %d", SD_task_get_name(candidate),
927 /* now we are sure that I can start! */
929 for (j = 0; j < candidate->workstation_nb && can_start; j++) {
930 workstation = candidate->workstation_list[j];
932 /* update the fifo */
933 if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
934 candidate = xbt_fifo_shift(workstation->task_fifo); /* the return value is stored just for debugging */
935 DEBUG1("Head of the fifo: '%s'",
936 (candidate != NULL) ? SD_task_get_name(candidate) : "NULL");
937 xbt_assert0(candidate == candidates[i],
938 "Error in __SD_task_just_done: bad first task in the fifo");
940 } /* for each workstation */
942 /* finally execute the task */
943 DEBUG2("Task '%s' state: %d", SD_task_get_name(candidate),
944 SD_task_get_state(candidate));
945 __SD_task_really_run(candidate);
948 ("Calling __SD_task_is_running: task '%s', state set: %p, running_task_set: %p, is running: %d",
949 SD_task_get_name(candidate), candidate->state_set,
950 sd_global->running_task_set, __SD_task_is_running(candidate));
951 xbt_assert2(__SD_task_is_running(candidate),
952 "Bad state of task '%s': %d", SD_task_get_name(candidate),
953 SD_task_get_state(candidate));
954 DEBUG0("Okay, the task is running.");
957 candidate->fifo_checked = 1;
958 } /* for each candidate */
960 xbt_free(candidates);
963 /* Remove all dependencies associated with a task. This function is called when the task is destroyed.
965 static void __SD_task_remove_dependencies(SD_task_t task)
967 /* we must destroy the dependencies carefuly (with SD_dependency_remove)
968 because each one is stored twice */
969 SD_dependency_t dependency;
970 while (xbt_dynar_length(task->tasks_before) > 0) {
971 xbt_dynar_get_cpy(task->tasks_before, 0, &dependency);
972 SD_task_dependency_remove(dependency->src, dependency->dst);
975 while (xbt_dynar_length(task->tasks_after) > 0) {
976 xbt_dynar_get_cpy(task->tasks_after, 0, &dependency);
977 SD_task_dependency_remove(dependency->src, dependency->dst);
982 * \brief Returns the start time of a task
984 * The task state must be SD_RUNNING, SD_DONE or SD_FAILED.
986 * \param task: a task
987 * \return the start time of this task
989 double SD_task_get_start_time(SD_task_t task)
991 SD_CHECK_INIT_DONE();
992 xbt_assert0(task != NULL, "Invalid parameter");
993 if (task->surf_action)
994 return surf_workstation_model->action_get_start_time(task->surf_action);
996 return task->start_time;
1000 * \brief Returns the finish time of a task
1002 * The task state must be SD_RUNNING, SD_DONE or SD_FAILED.
1003 * If the state is not completed yet, the returned value is an
1004 * estimation of the task finish time. This value can fluctuate
1005 * until the task is completed.
1007 * \param task: a task
1008 * \return the start time of this task
1010 double SD_task_get_finish_time(SD_task_t task)
1012 SD_CHECK_INIT_DONE();
1013 xbt_assert0(task != NULL, "Invalid parameter");
1015 if (task->surf_action) /* should never happen as actions are destroyed right after their completion */
1016 return surf_workstation_model->action_get_finish_time(task->surf_action);
1018 return task->finish_time;
1022 * \brief Destroys a task.
1024 * The user data (if any) should have been destroyed first.
1026 * \param task the task you want to destroy
1027 * \see SD_task_create()
1029 void SD_task_destroy(SD_task_t task)
1031 SD_CHECK_INIT_DONE();
1032 xbt_assert0(task != NULL, "Invalid parameter");
1034 DEBUG1("Destroying task %s...", SD_task_get_name(task));
1036 __SD_task_remove_dependencies(task);
1038 /* if the task was scheduled or ready we have to free the scheduling parameters */
1039 if (__SD_task_is_scheduled_or_ready(task))
1040 __SD_task_destroy_scheduling_data(task);
1042 if (task->name != NULL)
1043 xbt_free(task->name);
1045 if (task->surf_action != NULL)
1046 surf_workstation_model->action_unref(task->surf_action);
1048 if (task->workstation_list != NULL)
1049 xbt_free(task->workstation_list);
1051 xbt_dynar_free(&task->tasks_before);
1052 xbt_dynar_free(&task->tasks_after);
1055 sd_global->task_number--;
1057 DEBUG0("Task destroyed.");