2 #include "simdag/simdag.h"
3 #include "xbt/sysdep.h"
6 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(sd_task,sd,
7 "Logging specific to SimDag (task)");
9 static void __SD_task_remove_dependencies(SD_task_t task);
10 static void __SD_task_destroy_scheduling_data(SD_task_t task);
13 * \brief Creates a new task.
15 * \param name the name of the task (can be \c NULL)
16 * \param data the user data you want to associate with the task (can be \c NULL)
17 * \param amount amount of the task
18 * \return the new task
19 * \see SD_task_destroy()
21 SD_task_t SD_task_create(const char *name, void *data, double amount) {
26 task = xbt_new(s_SD_task_t, 1);
28 /* general information */
29 task->data = data; /* user data */
31 task->name = xbt_strdup(name);
35 task->state_hookup.prev = NULL;
36 task->state_hookup.next = NULL;
37 task->state_set = sd_global->not_scheduled_task_set;
38 task->state = SD_NOT_SCHEDULED;
39 xbt_swag_insert(task, task->state_set);
41 task->amount = amount;
42 task->remains = amount;
43 task->start_time = -1.0;
44 task->finish_time = -1.0;
45 task->surf_action = NULL;
46 task->watch_points = 0;
47 task->state_changed = 0;
50 task->tasks_before = xbt_dynar_new(sizeof(SD_dependency_t), NULL);
51 task->tasks_after = xbt_dynar_new(sizeof(SD_dependency_t), NULL);
53 /* scheduling parameters */
54 task->workstation_nb = 0;
55 task->workstation_list = NULL;
56 task->computation_amount = NULL;
57 task->communication_amount = NULL;
60 sd_global->task_number++;
66 * \brief Returns the user data of a task
69 * \return the user data associated with this task (can be \c NULL)
70 * \see SD_task_set_data()
72 void* SD_task_get_data(SD_task_t task) {
74 xbt_assert0(task != NULL, "Invalid parameter");
79 * \brief Sets the user data of a task
81 * The new data can be \c NULL. The old data should have been freed first
82 * if it was not \c NULL.
85 * \param data the new data you want to associate with this task
86 * \see SD_task_get_data()
88 void SD_task_set_data(SD_task_t task, void *data) {
90 xbt_assert0(task != NULL, "Invalid parameter");
95 * \brief Returns the state of a task
98 * \return the current \ref e_SD_task_state_t "state" of this task:
99 * #SD_NOT_SCHEDULED, #SD_SCHEDULED, #SD_READY, #SD_RUNNING, #SD_DONE or #SD_FAILED
100 * \see e_SD_task_state_t
102 e_SD_task_state_t SD_task_get_state(SD_task_t task) {
103 SD_CHECK_INIT_DONE();
104 xbt_assert0(task != NULL, "Invalid parameter");
108 /* Changes the state of a task. Updates the swags and the flag sd_global->watch_point_reached.
110 void __SD_task_set_state(SD_task_t task, e_SD_task_state_t new_state) {
111 xbt_swag_remove(task, task->state_set);
113 case SD_NOT_SCHEDULED:
114 task->state_set = sd_global->not_scheduled_task_set;
117 task->state_set = sd_global->scheduled_task_set;
120 task->state_set = sd_global->ready_task_set;
123 task->state_set = sd_global->in_fifo_task_set;
126 task->state_set = sd_global->running_task_set;
127 task->start_time = surf_workstation_model->common_public->
128 action_get_start_time(task->surf_action);
131 task->state_set = sd_global->done_task_set;
132 task->finish_time = surf_workstation_model->common_public->
133 action_get_finish_time(task->surf_action);
137 task->state_set = sd_global->failed_task_set;
140 xbt_assert0(0, "Invalid state");
142 xbt_swag_insert(task, task->state_set);
143 task->state = new_state;
145 if (task->watch_points & new_state) {
146 INFO1("Watch point reached with task '%s'!", SD_task_get_name(task));
147 sd_global->watch_point_reached = 1;
148 SD_task_unwatch(task, new_state); /* remove the watch point */
153 * \brief Returns the name of a task
156 * \return the name of this task (can be \c NULL)
158 const char* SD_task_get_name(SD_task_t task) {
159 SD_CHECK_INIT_DONE();
160 xbt_assert0(task != NULL, "Invalid parameter");
165 * \brief Returns the total amount of a task
168 * \return the total amount of this task
169 * \see SD_task_get_remaining_amount()
171 double SD_task_get_amount(SD_task_t task) {
172 SD_CHECK_INIT_DONE();
173 xbt_assert0(task != NULL, "Invalid parameter");
178 * \brief Returns the remaining amount of a task
181 * \return the remaining amount of this task
182 * \see SD_task_get_amount()
184 double SD_task_get_remaining_amount(SD_task_t task) {
185 SD_CHECK_INIT_DONE();
186 xbt_assert0(task != NULL, "Invalid parameter");
188 if (task->surf_action)
189 return task->surf_action->remains;
191 return task->remains;
194 /* temporary function for debbuging */
195 static void __SD_print_dependencies(SD_task_t task) {
199 SD_dependency_t dependency;
201 INFO1("The following tasks must be executed before %s:", SD_task_get_name(task));
202 dynar = task->tasks_before;
203 length = xbt_dynar_length(dynar);
206 for (i = 0; i < length; i++) {
207 xbt_dynar_get_cpy(dynar, i, &dependency);
208 INFO1(" %s", SD_task_get_name(dependency->src));
211 INFO1("The following tasks must be executed after %s:", SD_task_get_name(task));
213 dynar = task->tasks_after;
214 length = xbt_dynar_length(dynar);
215 for (i = 0; i < length; i++) {
216 xbt_dynar_get_cpy(dynar, i, &dependency);
217 INFO1(" %s", SD_task_get_name(dependency->dst));
219 INFO0("----------------------------");
222 /* Destroys a dependency between two tasks.
224 static void __SD_task_dependency_destroy(void *dependency) {
225 if (((SD_dependency_t) dependency)->name != NULL)
226 xbt_free(((SD_dependency_t) dependency)->name);
227 xbt_free(dependency);
231 * \brief Adds a dependency between two tasks
233 * \a dst will depend on \a src, ie \a dst will not start before \a src is finished.
234 * Their \ref e_SD_task_state_t "state" must be #SD_NOT_SCHEDULED, #SD_SCHEDULED or #SD_READY.
236 * \param name the name of the new dependency (can be \c NULL)
237 * \param data the user data you want to associate with this dependency (can be \c NULL)
238 * \param src the task which must be executed first
239 * \param dst the task you want to make depend on \a src
240 * \see SD_task_dependency_remove()
242 void SD_task_dependency_add(const char *name, void *data, SD_task_t src, SD_task_t dst) {
247 SD_dependency_t dependency;
249 SD_CHECK_INIT_DONE();
250 xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
252 dynar = src->tasks_after;
253 length = xbt_dynar_length(dynar);
258 THROW1(arg_error, 0, "Cannot add a dependency between task '%s' and itself",
259 SD_task_get_name(src));
261 if (!__SD_task_is_not_scheduled(src) && !__SD_task_is_scheduled_or_ready(src))
262 THROW1(arg_error, 0, "Task '%s' must be SD_NOT_SCHEDULED, SD_SCHEDULED or SD_READY", SD_task_get_name(src));
264 if (!__SD_task_is_not_scheduled(dst) && !__SD_task_is_scheduled_or_ready(dst))
265 THROW1(arg_error, 0, "Task '%s' must be SD_NOT_SCHEDULED, SD_SCHEDULED or SD_READY", SD_task_get_name(dst));
267 DEBUG2("SD_task_dependency_add: src = %s, dst = %s", SD_task_get_name(src), SD_task_get_name(dst));
268 for (i = 0; i < length && !found; i++) {
269 xbt_dynar_get_cpy(dynar, i, &dependency);
270 found = (dependency->dst == dst);
271 DEBUG2("Dependency %d: dependency->dst = %s", i, SD_task_get_name(dependency->dst));
275 THROW2(arg_error, 0, "A dependency already exists between task '%s' and task '%s'",
276 SD_task_get_name(src), SD_task_get_name(dst));
278 dependency = xbt_new(s_SD_dependency_t, 1);
281 dependency->name = xbt_strdup(name);
283 dependency->name = NULL;
285 dependency->data = data;
286 dependency->src = src;
287 dependency->dst = dst;
289 /* src must be executed before dst */
290 xbt_dynar_push(src->tasks_after, &dependency);
291 xbt_dynar_push(dst->tasks_before, &dependency);
293 /* if the task was ready, then dst->tasks_before is not empty anymore,
294 so we must go back to state SD_SCHEDULED */
295 if (__SD_task_is_ready(dst)) {
296 DEBUG1("SD_task_dependency_add: %s was ready and becomes scheduled!", SD_task_get_name(dst));
297 __SD_task_set_state(dst, SD_SCHEDULED);
300 /* __SD_print_dependencies(src);
301 __SD_print_dependencies(dst); */
305 * \brief Indacates whether there is a dependency between two tasks.
308 * \param dst a task depending on \a src
310 int SD_task_dependency_exists(SD_task_t src, SD_task_t dst) {
314 SD_dependency_t dependency;
316 SD_CHECK_INIT_DONE();
317 xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
319 dynar = src->tasks_after;
320 length = xbt_dynar_length(dynar);
322 for (i = 0; i < length; i++) {
323 xbt_dynar_get_cpy(dynar, i, &dependency);
324 if (dependency->dst == dst) return 1;
330 * \brief Remove a dependency between two tasks
333 * \param dst a task depending on \a src
334 * \see SD_task_dependency_add()
336 void SD_task_dependency_remove(SD_task_t src, SD_task_t dst) {
342 SD_dependency_t dependency;
344 SD_CHECK_INIT_DONE();
345 xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
347 /* remove the dependency from src->tasks_after */
348 dynar = src->tasks_after;
349 length = xbt_dynar_length(dynar);
351 for (i = 0; i < length && !found; i++) {
352 xbt_dynar_get_cpy(dynar, i, &dependency);
353 if (dependency->dst == dst) {
354 xbt_dynar_remove_at(dynar, i, NULL);
360 "No dependency found between task '%s' and '%s': task '%s' is not a successor of task '%s'",
361 SD_task_get_name(src), SD_task_get_name(dst), SD_task_get_name(dst), SD_task_get_name(src));
363 /* remove the dependency from dst->tasks_before */
364 dynar = dst->tasks_before;
365 length = xbt_dynar_length(dynar);
368 for (i = 0; i < length && !found; i++) {
369 xbt_dynar_get_cpy(dynar, i, &dependency);
370 if (dependency->src == src) {
371 xbt_dynar_remove_at(dynar, i, NULL);
372 __SD_task_dependency_destroy(dependency);
376 /* should never happen... */
377 xbt_assert4(found, "SimDag error: task '%s' is a successor of '%s' but task '%s' is not a predecessor of task '%s'",
378 SD_task_get_name(dst), SD_task_get_name(src), SD_task_get_name(src), SD_task_get_name(dst));
380 /* if the task was scheduled and dst->tasks_before is empty now, we can make it ready */
381 if (xbt_dynar_length(dst->tasks_before) == 0 && __SD_task_is_scheduled(dst))
382 __SD_task_set_state(dst, SD_READY);
384 /* __SD_print_dependencies(src);
385 __SD_print_dependencies(dst);*/
389 * \brief Returns the user data associated with a dependency between two tasks
392 * \param dst a task depending on \a src
393 * \return the user data associated with this dependency (can be \c NULL)
394 * \see SD_task_dependency_add()
396 void *SD_task_dependency_get_data(SD_task_t src, SD_task_t dst) {
402 SD_dependency_t dependency;
405 SD_CHECK_INIT_DONE();
406 xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
408 dynar = src->tasks_after;
409 length = xbt_dynar_length(dynar);
411 for (i = 0; i < length && !found; i++) {
412 xbt_dynar_get_cpy(dynar, i, &dependency);
413 found = (dependency->dst == dst);
416 THROW2(arg_error, 0, "No dependency found between task '%s' and '%s'", SD_task_get_name(src), SD_task_get_name(dst));
417 return dependency->data;
420 /* temporary function for debugging */
421 static void __SD_print_watch_points(SD_task_t task) {
422 static const int state_masks[] = {SD_SCHEDULED, SD_RUNNING, SD_READY, SD_DONE, SD_FAILED};
423 static const char* state_names[] = {"scheduled", "running", "ready", "done", "failed"};
426 INFO2("Task '%s' watch points (%x): ", SD_task_get_name(task), task->watch_points);
429 for (i = 0; i < 5; i++) {
430 if (task->watch_points & state_masks[i])
431 INFO1("%s ", state_names[i]);
436 * \brief Adds a watch point to a task
438 * SD_simulate() will stop as soon as the \ref e_SD_task_state_t "state" of this
439 * task becomes the one given in argument. The
440 * watch point is then automatically removed.
443 * \param state the \ref e_SD_task_state_t "state" you want to watch
444 * (cannot be #SD_NOT_SCHEDULED)
445 * \see SD_task_unwatch()
447 void SD_task_watch(SD_task_t task, e_SD_task_state_t state) {
448 SD_CHECK_INIT_DONE();
449 xbt_assert0(task != NULL, "Invalid parameter");
451 if (state & SD_NOT_SCHEDULED)
452 THROW0(arg_error, 0, "Cannot add a watch point for state SD_NOT_SCHEDULED");
454 task->watch_points = task->watch_points | state;
455 /* __SD_print_watch_points(task);*/
459 * \brief Removes a watch point from a task
462 * \param state the \ref e_SD_task_state_t "state" you no longer want to watch
463 * \see SD_task_watch()
465 void SD_task_unwatch(SD_task_t task, e_SD_task_state_t state) {
466 SD_CHECK_INIT_DONE();
467 xbt_assert0(task != NULL, "Invalid parameter");
468 xbt_assert0(state != SD_NOT_SCHEDULED,
469 "SimDag error: Cannot have a watch point for state SD_NOT_SCHEDULED");
471 task->watch_points = task->watch_points & ~state;
472 /* __SD_print_watch_points(task);*/
476 * \brief Returns an approximative estimation of the execution time of a task.
478 * The estimation is very approximative because the value returned is the time
479 * the task would take if it was executed now and if it was the only task.
481 * \param task the task to evaluate
482 * \param workstation_nb number of workstations on which the task would be executed
483 * \param workstation_list the workstations on which the task would be executed
484 * \param computation_amount computation amount for each workstation
485 * \param communication_amount communication amount between each pair of workstations
486 * \param rate task execution speed rate
489 double SD_task_get_execution_time(SD_task_t task,
491 const SD_workstation_t *workstation_list,
492 const double *computation_amount,
493 const double *communication_amount,
495 double time, max_time = 0.0;
497 SD_CHECK_INIT_DONE();
498 xbt_assert0(task != NULL && workstation_nb > 0 && workstation_list != NULL &&
499 computation_amount != NULL && communication_amount != NULL,
500 "Invalid parameter");
502 /* the task execution time is the maximum execution time of the parallel tasks */
504 for (i = 0; i < workstation_nb; i++) {
505 time = SD_workstation_get_computation_time(workstation_list[i], computation_amount[i]);
507 for (j = 0; j < workstation_nb; j++) {
508 time += SD_route_get_communication_time(workstation_list[i], workstation_list[j],
509 communication_amount[i * workstation_nb + j]);
512 if (time > max_time) {
516 return max_time * SD_task_get_amount(task);
520 * \brief Schedules a task
522 * The task state must be #SD_NOT_SCHEDULED.
523 * Once scheduled, a task will be executed as soon as possible in SD_simulate(),
524 * i.e. when its dependencies are satisfied.
526 * \param task the task you want to schedule
527 * \param workstation_nb number of workstations on which the task will be executed
528 * \param workstation_list the workstations on which the task will be executed
529 * \param computation_amount computation amount for each workstation
530 * \param communication_amount communication amount between each pair of workstations
531 * \param rate task execution speed rate
532 * \see SD_task_unschedule()
534 void SD_task_schedule(SD_task_t task, int workstation_nb,
535 const SD_workstation_t *workstation_list, const double *computation_amount,
536 const double *communication_amount, double rate) {
538 int communication_nb;
540 SD_CHECK_INIT_DONE();
541 xbt_assert0(task != NULL, "Invalid parameter");
542 xbt_assert0(workstation_nb > 0, "workstation_nb must be positive");
544 if (!__SD_task_is_not_scheduled(task))
545 THROW1(arg_error, 0, "Task '%s' has already been scheduled", SD_task_get_name(task));
547 task->workstation_nb = workstation_nb;
550 task->computation_amount = xbt_new(double, workstation_nb);
551 memcpy(task->computation_amount, computation_amount, sizeof(double) * workstation_nb);
553 communication_nb = workstation_nb * workstation_nb;
554 task->communication_amount = xbt_new(double, communication_nb);
555 memcpy(task->communication_amount, communication_amount, sizeof(double) * communication_nb);
557 task->workstation_list = xbt_new(SD_workstation_t, workstation_nb);
558 memcpy(task->workstation_list, workstation_list, sizeof(SD_workstation_t) * workstation_nb);
560 /* update the task state */
561 if (xbt_dynar_length(task->tasks_before) == 0)
562 __SD_task_set_state(task, SD_READY);
564 __SD_task_set_state(task, SD_SCHEDULED);
568 * \brief Unschedules a task
570 * The task state must be #SD_SCHEDULED, #SD_READY, #SD_RUNNING or #SD_FAILED.
571 * If you call this function, the task state becomes #SD_NOT_SCHEDULED.
572 * Call SD_task_schedule() to schedule it again.
574 * \param task the task you want to unschedule
575 * \see SD_task_schedule()
577 void SD_task_unschedule(SD_task_t task) {
578 SD_CHECK_INIT_DONE();
579 xbt_assert0(task != NULL, "Invalid parameter");
581 if (task->state_set != sd_global->scheduled_task_set &&
582 task->state_set != sd_global->ready_task_set &&
583 task->state_set != sd_global->running_task_set &&
584 task->state_set != sd_global->failed_task_set)
585 THROW1(arg_error, 0, "Task %s: the state must be SD_SCHEDULED, SD_READY, SD_RUNNING or SD_FAILED",
586 SD_task_get_name(task));
588 if (__SD_task_is_scheduled_or_ready(task)) /* if the task is scheduled or ready */
589 __SD_task_destroy_scheduling_data(task);
591 if (__SD_task_is_running(task)) /* the task should become SD_FAILED */
592 surf_workstation_model->common_public->action_cancel(task->surf_action);
594 __SD_task_set_state(task, SD_NOT_SCHEDULED);
595 task->remains = task->amount;
596 task->start_time = -1.0;
599 /* Destroys the data memorised by SD_task_schedule. Task state must be SD_SCHEDULED or SD_READY.
601 static void __SD_task_destroy_scheduling_data(SD_task_t task) {
602 SD_CHECK_INIT_DONE();
603 if (!__SD_task_is_scheduled_or_ready(task) && !__SD_task_is_in_fifo(task))
604 THROW1(arg_error, 0, "Task '%s' must be SD_SCHEDULED, SD_READY or SD_IN_FIFO", SD_task_get_name(task));
606 xbt_free(task->computation_amount);
607 xbt_free(task->communication_amount);
610 /* Runs a task. This function is directly called by __SD_task_try_to_run if the task
611 * doesn't have to wait in fifos. Otherwise, it is called by __SD_task_just_done when
612 * the task gets out of its fifos.
614 void __SD_task_really_run(SD_task_t task) {
617 void **surf_workstations;
619 SD_CHECK_INIT_DONE();
620 xbt_assert0(task != NULL, "Invalid parameter");
621 xbt_assert2(__SD_task_is_ready_or_in_fifo(task), "Task '%s' is not ready or in a fifo! Task state: %d",
622 SD_task_get_name(task), SD_task_get_state(task));
623 xbt_assert1(task->workstation_list != NULL, "Task '%s': workstation_list is NULL!", SD_task_get_name(task));
627 DEBUG1("Really running task '%s'", SD_task_get_name(task));
629 /* set this task as current task for the workstations in sequential mode */
630 for (i = 0; i < task->workstation_nb; i++) {
631 if (SD_workstation_get_access_mode(task->workstation_list[i]) == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
632 task->workstation_list[i]->current_task = task;
633 xbt_assert0(__SD_workstation_is_busy(task->workstation_list[i]), "The workstation should be busy now");
637 DEBUG1("Task '%s' set as current task for its workstations", SD_task_get_name(task));
641 /* we have to create a Surf workstation array instead of the SimDag workstation array */
642 surf_workstations = xbt_new(void*, task->workstation_nb);
644 for (i = 0; i < task->workstation_nb; i++) {
645 surf_workstations[i] = task->workstation_list[i]->surf_workstation;
648 task->surf_action = NULL;
649 if((task->workstation_nb==1) &&
650 (task->communication_amount[0]==0.0)) {
651 task->surf_action = surf_workstation_model->extension_public->
652 execute(surf_workstations[0], task->computation_amount[0]);
653 } else if((task->workstation_nb==1) &&
654 (task->computation_amount[0]==0.0)) {
655 task->surf_action = surf_workstation_model->extension_public->
656 communicate(surf_workstations[0], surf_workstations[0],
657 task->communication_amount[0],task->rate);
658 } else if((task->workstation_nb==2) &&
659 (task->computation_amount[0]==0.0)&&
660 (task->computation_amount[1]==0.0)) {
664 for (i = 0; i < task->workstation_nb*task->workstation_nb; i++) {
665 if(task->communication_amount[i]>0.0) {
667 value = task->communication_amount[i];
671 task->surf_action = surf_workstation_model->extension_public->
672 communicate(surf_workstations[0], surf_workstations[1],
676 if(!task->surf_action) {
677 double *computation_amount = xbt_new(double, task->workstation_nb);
678 double *communication_amount = xbt_new(double, task->workstation_nb *
679 task->workstation_nb);
681 memcpy(computation_amount, task->computation_amount, sizeof(double) *
682 task->workstation_nb);
683 memcpy(communication_amount, task->communication_amount,
684 sizeof(double) * task->workstation_nb * task->workstation_nb);
686 task->surf_action = surf_workstation_model->extension_public->
687 execute_parallel_task(task->workstation_nb,
690 communication_amount,
694 xbt_free(surf_workstations);
697 surf_workstation_model->common_public->action_set_data(task->surf_action, task);
698 task->state_changed = 1;
700 DEBUG1("surf_action = %p", task->surf_action);
702 __SD_task_destroy_scheduling_data(task); /* now the scheduling data are not useful anymore */
703 __SD_task_set_state(task, SD_RUNNING);
704 xbt_assert2(__SD_task_is_running(task), "Bad state of task '%s': %d",
705 SD_task_get_name(task), SD_task_get_state(task));
709 /* Tries to run a task. This function is called by SD_simulate() when a scheduled task becomes SD_READY
710 * (ie when its dependencies are satisfied).
711 * If one of the workstations where the task is scheduled on is busy (in sequential mode),
712 * the task doesn't start.
713 * Returns whether the task has started.
715 int __SD_task_try_to_run(SD_task_t task) {
719 SD_workstation_t workstation;
721 SD_CHECK_INIT_DONE();
722 xbt_assert0(task != NULL, "Invalid parameter");
723 xbt_assert2(__SD_task_is_ready(task), "Task '%s' is not ready! Task state: %d",
724 SD_task_get_name(task), SD_task_get_state(task));
727 for (i = 0; i < task->workstation_nb; i++) {
728 can_start = !__SD_workstation_is_busy(task->workstation_list[i]);
731 DEBUG2("Task '%s' can start: %d", SD_task_get_name(task), can_start);
733 if (!can_start) { /* if the task cannot start and is not in the fifos yet*/
734 for (i = 0; i < task->workstation_nb; i++) {
735 workstation = task->workstation_list[i];
736 if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
737 DEBUG2("Pushing task '%s' in the fifo of workstation '%s'", SD_task_get_name(task),
738 SD_workstation_get_name(workstation));
739 xbt_fifo_push(workstation->task_fifo, task);
742 __SD_task_set_state(task, SD_IN_FIFO);
743 xbt_assert2(__SD_task_is_in_fifo(task), "Bad state of task '%s': %d",
744 SD_task_get_name(task), SD_task_get_state(task));
745 DEBUG1("Task '%s' state is now SD_IN_FIFO", SD_task_get_name(task));
748 __SD_task_really_run(task);
754 /* This function is called by SD_simulate when a task is done.
755 * It updates task->state and task->action and executes if necessary the tasks
756 * which were waiting in fifos for the end of `task'
758 void __SD_task_just_done(SD_task_t task) {
760 SD_workstation_t workstation;
763 int candidate_nb = 0;
764 int candidate_capacity = 8;
765 SD_task_t *candidates;
768 SD_CHECK_INIT_DONE();
769 xbt_assert0(task != NULL, "Invalid parameter");
770 xbt_assert1(__SD_task_is_running(task), "The task must be running! Task state: %d", SD_task_get_state(task));
771 xbt_assert1(task->workstation_list != NULL, "Task '%s': workstation_list is NULL!", SD_task_get_name(task));
774 candidates = xbt_new(SD_task_t, 8);
776 __SD_task_set_state(task, SD_DONE);
777 surf_workstation_model->common_public->action_free(task->surf_action);
778 task->surf_action = NULL;
780 DEBUG0("Looking for candidates");
782 /* if the task was executed on sequential workstations,
783 maybe we can execute the next task of the fifo for each workstation */
784 for (i = 0; i < task->workstation_nb; i++) {
785 workstation = task->workstation_list[i];
786 DEBUG2("Workstation '%s': access_mode = %d", SD_workstation_get_name(workstation), workstation->access_mode);
787 if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
788 xbt_assert1(workstation->task_fifo != NULL, "Workstation '%s' has sequential access but no fifo!",
789 SD_workstation_get_name(workstation));
790 xbt_assert2(workstation->current_task = task, "Workstation '%s': current task should be '%s'",
791 SD_workstation_get_name(workstation), SD_task_get_name(task));
793 /* the task is over so we can release the workstation */
794 workstation->current_task = NULL;
796 DEBUG0("Getting candidate in fifo");
797 candidate = xbt_fifo_get_item_content(xbt_fifo_get_first_item(workstation->task_fifo));
799 if (candidate != NULL) {
800 DEBUG1("Candidate: '%s'", SD_task_get_name(candidate));
801 xbt_assert2(__SD_task_is_in_fifo(candidate), "Bad state of candidate '%s': %d",
802 SD_task_get_name(candidate), SD_task_get_state(candidate));
805 DEBUG1("Candidate in fifo: %p", candidate);
807 /* if there was a task waiting for my place */
808 if (candidate != NULL) {
809 /* Unfortunately, we are not sure yet that we can execute the task now,
810 because the task can be waiting more deeply in some other workstation's fifos...
811 So we memorize all candidate tasks, and then we will check for each candidate
812 whether or not all its workstations are available. */
814 /* realloc if necessary */
815 if (candidate_nb == candidate_capacity) {
816 candidate_capacity *= 2;
817 candidates = xbt_realloc(candidates, sizeof(SD_task_t) * candidate_capacity);
820 /* register the candidate */
821 candidates[candidate_nb++] = candidate;
822 candidate->fifo_checked = 0;
827 DEBUG1("Candidates found: %d", candidate_nb);
829 /* now we check every candidate task */
830 for (i = 0; i < candidate_nb; i++) {
831 candidate = candidates[i];
833 if (candidate->fifo_checked) {
834 continue; /* we have already evaluated that task*/
837 xbt_assert2(__SD_task_is_in_fifo(candidate), "Bad state of candidate '%s': %d",
838 SD_task_get_name(candidate), SD_task_get_state(candidate));
840 for (j = 0; j < candidate->workstation_nb && can_start; j++) {
841 workstation = candidate->workstation_list[j];
843 /* I can start on this workstation if the workstation is shared
844 or if I am the first task in the fifo */
845 can_start = workstation->access_mode == SD_WORKSTATION_SHARED_ACCESS ||
846 candidate == xbt_fifo_get_item_content(xbt_fifo_get_first_item(workstation->task_fifo));
849 DEBUG2("Candidate '%s' can start: %d", SD_task_get_name(candidate), can_start);
851 /* now we are sure that I can start! */
853 for (j = 0; j < candidate->workstation_nb && can_start; j++) {
854 workstation = candidate->workstation_list[j];
856 /* update the fifo */
857 if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
858 candidate = xbt_fifo_shift(workstation->task_fifo); /* the return value is stored just for debugging */
859 DEBUG1("Head of the fifo: '%s'", (candidate != NULL) ? SD_task_get_name(candidate) : "NULL");
860 xbt_assert0(candidate == candidates[i], "Error in __SD_task_just_done: bad first task in the fifo");
862 } /* for each workstation */
864 /* finally execute the task */
865 DEBUG2("Task '%s' state: %d", SD_task_get_name(candidate), SD_task_get_state(candidate));
866 __SD_task_really_run(candidate);
868 DEBUG4("Calling __SD_task_is_running: task '%s', state set: %p, running_task_set: %p, is running: %d",
869 SD_task_get_name(candidate), candidate->state_set, sd_global->running_task_set, __SD_task_is_running(candidate));
870 xbt_assert2(__SD_task_is_running(candidate), "Bad state of task '%s': %d",
871 SD_task_get_name(candidate), SD_task_get_state(candidate));
872 DEBUG0("Okay, the task is running.");
875 candidate->fifo_checked = 1;
876 } /* for each candidate */
878 xbt_free(candidates);
881 /* Remove all dependencies associated with a task. This function is called when the task is destroyed.
883 static void __SD_task_remove_dependencies(SD_task_t task) {
884 /* we must destroy the dependencies carefuly (with SD_dependency_remove)
885 because each one is stored twice */
886 SD_dependency_t dependency;
887 while (xbt_dynar_length(task->tasks_before) > 0) {
888 xbt_dynar_get_cpy(task->tasks_before, 0, &dependency);
889 SD_task_dependency_remove(dependency->src, dependency->dst);
892 while (xbt_dynar_length(task->tasks_after) > 0) {
893 xbt_dynar_get_cpy(task->tasks_after, 0, &dependency);
894 SD_task_dependency_remove(dependency->src, dependency->dst);
899 * \brief Returns the start time of a task
901 * The task state must be SD_RUNNING, SD_DONE or SD_FAILED.
903 * \param task: a task
904 * \return the start time of this task
906 double SD_task_get_start_time(SD_task_t task) {
907 SD_CHECK_INIT_DONE();
908 xbt_assert0(task != NULL, "Invalid parameter");
909 if(task->surf_action)
910 return surf_workstation_model->common_public->action_get_start_time(task->surf_action);
912 return task->start_time;
916 * \brief Returns the finish time of a task
918 * The task state must be SD_RUNNING, SD_DONE or SD_FAILED.
919 * If the state is not completed yet, the returned value is an
920 * estimation of the task finish time. This value can fluctuate
921 * until the task is completed.
923 * \param task: a task
924 * \return the start time of this task
926 double SD_task_get_finish_time(SD_task_t task) {
927 SD_CHECK_INIT_DONE();
928 xbt_assert0(task != NULL, "Invalid parameter");
930 if(task->surf_action) /* should never happen as actions are destroyed right after their completion */
931 return surf_workstation_model->common_public->action_get_finish_time(task->surf_action);
933 return task->finish_time;
937 * \brief Destroys a task.
939 * The user data (if any) should have been destroyed first.
941 * \param task the task you want to destroy
942 * \see SD_task_create()
944 void SD_task_destroy(SD_task_t task) {
945 SD_CHECK_INIT_DONE();
946 xbt_assert0(task != NULL, "Invalid parameter");
948 DEBUG1("Destroying task %s...", SD_task_get_name(task));
950 __SD_task_remove_dependencies(task);
952 /* if the task was scheduled or ready we have to free the scheduling parameters */
953 if (__SD_task_is_scheduled_or_ready(task))
954 __SD_task_destroy_scheduling_data(task);
956 if (task->name != NULL)
957 xbt_free(task->name);
959 if (task->surf_action != NULL)
960 surf_workstation_model->common_public->action_free(task->surf_action);
962 if (task->workstation_list != NULL)
963 xbt_free(task->workstation_list);
965 xbt_dynar_free(&task->tasks_before);
966 xbt_dynar_free(&task->tasks_after);
969 sd_global->task_number--;
971 DEBUG0("Task destroyed.");