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 task->surf_action = surf_workstation_model->extension_public->
678 execute_parallel_task(task->workstation_nb,
680 task->computation_amount,
681 task->communication_amount,
685 surf_workstation_model->common_public->action_set_data(task->surf_action, task);
686 task->state_changed = 1;
688 DEBUG1("surf_action = %p", task->surf_action);
690 xbt_free(surf_workstations);
691 __SD_task_destroy_scheduling_data(task); /* now the scheduling data are not useful anymore */
692 __SD_task_set_state(task, SD_RUNNING);
693 xbt_assert2(__SD_task_is_running(task), "Bad state of task '%s': %d",
694 SD_task_get_name(task), SD_task_get_state(task));
698 /* Tries to run a task. This function is called by SD_simulate() when a scheduled task becomes SD_READY
699 * (ie when its dependencies are satisfied).
700 * If one of the workstations where the task is scheduled on is busy (in sequential mode),
701 * the task doesn't start.
702 * Returns whether the task has started.
704 int __SD_task_try_to_run(SD_task_t task) {
708 SD_workstation_t workstation;
710 SD_CHECK_INIT_DONE();
711 xbt_assert0(task != NULL, "Invalid parameter");
712 xbt_assert2(__SD_task_is_ready(task), "Task '%s' is not ready! Task state: %d",
713 SD_task_get_name(task), SD_task_get_state(task));
716 for (i = 0; i < task->workstation_nb; i++) {
717 can_start = !__SD_workstation_is_busy(task->workstation_list[i]);
720 DEBUG2("Task '%s' can start: %d", SD_task_get_name(task), can_start);
722 if (!can_start) { /* if the task cannot start and is not in the fifos yet*/
723 for (i = 0; i < task->workstation_nb; i++) {
724 workstation = task->workstation_list[i];
725 if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
726 DEBUG2("Pushing task '%s' in the fifo of workstation '%s'", SD_task_get_name(task),
727 SD_workstation_get_name(workstation));
728 xbt_fifo_push(workstation->task_fifo, task);
731 __SD_task_set_state(task, SD_IN_FIFO);
732 xbt_assert2(__SD_task_is_in_fifo(task), "Bad state of task '%s': %d",
733 SD_task_get_name(task), SD_task_get_state(task));
734 DEBUG1("Task '%s' state is now SD_IN_FIFO", SD_task_get_name(task));
737 __SD_task_really_run(task);
743 /* This function is called by SD_simulate when a task is done.
744 * It updates task->state and task->action and executes if necessary the tasks
745 * which were waiting in fifos for the end of `task'
747 void __SD_task_just_done(SD_task_t task) {
749 SD_workstation_t workstation;
752 int candidate_nb = 0;
753 int candidate_capacity = 8;
754 SD_task_t *candidates;
757 SD_CHECK_INIT_DONE();
758 xbt_assert0(task != NULL, "Invalid parameter");
759 xbt_assert1(__SD_task_is_running(task), "The task must be running! Task state: %d", SD_task_get_state(task));
760 xbt_assert1(task->workstation_list != NULL, "Task '%s': workstation_list is NULL!", SD_task_get_name(task));
763 candidates = xbt_new(SD_task_t, 8);
765 __SD_task_set_state(task, SD_DONE);
766 surf_workstation_model->common_public->action_free(task->surf_action);
767 task->surf_action = NULL;
769 DEBUG0("Looking for candidates");
771 /* if the task was executed on sequential workstations,
772 maybe we can execute the next task of the fifo for each workstation */
773 for (i = 0; i < task->workstation_nb; i++) {
774 workstation = task->workstation_list[i];
775 DEBUG2("Workstation '%s': access_mode = %d", SD_workstation_get_name(workstation), workstation->access_mode);
776 if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
777 xbt_assert1(workstation->task_fifo != NULL, "Workstation '%s' has sequential access but no fifo!",
778 SD_workstation_get_name(workstation));
779 xbt_assert2(workstation->current_task = task, "Workstation '%s': current task should be '%s'",
780 SD_workstation_get_name(workstation), SD_task_get_name(task));
782 /* the task is over so we can release the workstation */
783 workstation->current_task = NULL;
785 DEBUG0("Getting candidate in fifo");
786 candidate = xbt_fifo_get_item_content(xbt_fifo_get_first_item(workstation->task_fifo));
788 if (candidate != NULL) {
789 DEBUG1("Candidate: '%s'", SD_task_get_name(candidate));
790 xbt_assert2(__SD_task_is_in_fifo(candidate), "Bad state of candidate '%s': %d",
791 SD_task_get_name(candidate), SD_task_get_state(candidate));
794 DEBUG1("Candidate in fifo: %p", candidate);
796 /* if there was a task waiting for my place */
797 if (candidate != NULL) {
798 /* Unfortunately, we are not sure yet that we can execute the task now,
799 because the task can be waiting more deeply in some other workstation's fifos...
800 So we memorize all candidate tasks, and then we will check for each candidate
801 whether or not all its workstations are available. */
803 /* realloc if necessary */
804 if (candidate_nb == candidate_capacity) {
805 candidate_capacity *= 2;
806 candidates = xbt_realloc(candidates, sizeof(SD_task_t) * candidate_capacity);
809 /* register the candidate */
810 candidates[candidate_nb++] = candidate;
811 candidate->fifo_checked = 0;
816 DEBUG1("Candidates found: %d", candidate_nb);
818 /* now we check every candidate task */
819 for (i = 0; i < candidate_nb; i++) {
820 candidate = candidates[i];
822 if (candidate->fifo_checked) {
823 continue; /* we have already evaluated that task*/
826 xbt_assert2(__SD_task_is_in_fifo(candidate), "Bad state of candidate '%s': %d",
827 SD_task_get_name(candidate), SD_task_get_state(candidate));
829 for (j = 0; j < candidate->workstation_nb && can_start; j++) {
830 workstation = candidate->workstation_list[j];
832 /* I can start on this workstation if the workstation is shared
833 or if I am the first task in the fifo */
834 can_start = workstation->access_mode == SD_WORKSTATION_SHARED_ACCESS ||
835 candidate == xbt_fifo_get_item_content(xbt_fifo_get_first_item(workstation->task_fifo));
838 DEBUG2("Candidate '%s' can start: %d", SD_task_get_name(candidate), can_start);
840 /* now we are sure that I can start! */
842 for (j = 0; j < candidate->workstation_nb && can_start; j++) {
843 workstation = candidate->workstation_list[j];
845 /* update the fifo */
846 if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
847 candidate = xbt_fifo_shift(workstation->task_fifo); /* the return value is stored just for debugging */
848 DEBUG1("Head of the fifo: '%s'", (candidate != NULL) ? SD_task_get_name(candidate) : "NULL");
849 xbt_assert0(candidate == candidates[i], "Error in __SD_task_just_done: bad first task in the fifo");
851 } /* for each workstation */
853 /* finally execute the task */
854 DEBUG2("Task '%s' state: %d", SD_task_get_name(candidate), SD_task_get_state(candidate));
855 __SD_task_really_run(candidate);
857 DEBUG4("Calling __SD_task_is_running: task '%s', state set: %p, running_task_set: %p, is running: %d",
858 SD_task_get_name(candidate), candidate->state_set, sd_global->running_task_set, __SD_task_is_running(candidate));
859 xbt_assert2(__SD_task_is_running(candidate), "Bad state of task '%s': %d",
860 SD_task_get_name(candidate), SD_task_get_state(candidate));
861 DEBUG0("Okay, the task is running.");
864 candidate->fifo_checked = 1;
865 } /* for each candidate */
867 xbt_free(candidates);
870 /* Remove all dependencies associated with a task. This function is called when the task is destroyed.
872 static void __SD_task_remove_dependencies(SD_task_t task) {
873 /* we must destroy the dependencies carefuly (with SD_dependency_remove)
874 because each one is stored twice */
875 SD_dependency_t dependency;
876 while (xbt_dynar_length(task->tasks_before) > 0) {
877 xbt_dynar_get_cpy(task->tasks_before, 0, &dependency);
878 SD_task_dependency_remove(dependency->src, dependency->dst);
881 while (xbt_dynar_length(task->tasks_after) > 0) {
882 xbt_dynar_get_cpy(task->tasks_after, 0, &dependency);
883 SD_task_dependency_remove(dependency->src, dependency->dst);
888 * \brief Returns the start time of a task
890 * The task state must be SD_RUNNING, SD_DONE or SD_FAILED.
892 * \param task: a task
893 * \return the start time of this task
895 double SD_task_get_start_time(SD_task_t task) {
896 SD_CHECK_INIT_DONE();
897 xbt_assert0(task != NULL, "Invalid parameter");
898 if(task->surf_action)
899 return surf_workstation_model->common_public->action_get_start_time(task->surf_action);
901 return task->start_time;
905 * \brief Returns the finish time of a task
907 * The task state must be SD_RUNNING, SD_DONE or SD_FAILED.
908 * If the state is not completed yet, the returned value is an
909 * estimation of the task finish time. This value can fluctuate
910 * until the task is completed.
912 * \param task: a task
913 * \return the start time of this task
915 double SD_task_get_finish_time(SD_task_t task) {
916 SD_CHECK_INIT_DONE();
917 xbt_assert0(task != NULL, "Invalid parameter");
919 if(task->surf_action) /* should never happen as actions are destroyed right after their completion */
920 return surf_workstation_model->common_public->action_get_finish_time(task->surf_action);
922 return task->finish_time;
926 * \brief Destroys a task.
928 * The user data (if any) should have been destroyed first.
930 * \param task the task you want to destroy
931 * \see SD_task_create()
933 void SD_task_destroy(SD_task_t task) {
934 SD_CHECK_INIT_DONE();
935 xbt_assert0(task != NULL, "Invalid parameter");
937 DEBUG1("Destroying task %s...", SD_task_get_name(task));
939 __SD_task_remove_dependencies(task);
941 /* if the task was scheduled or ready we have to free the scheduling parameters */
942 if (__SD_task_is_scheduled_or_ready(task))
943 __SD_task_destroy_scheduling_data(task);
945 if (task->name != NULL)
946 xbt_free(task->name);
948 if (task->surf_action != NULL)
949 surf_workstation_model->common_public->action_free(task->surf_action);
951 if (task->workstation_list != NULL)
952 xbt_free(task->workstation_list);
954 xbt_dynar_free(&task->tasks_before);
955 xbt_dynar_free(&task->tasks_after);
958 sd_global->task_number--;
960 DEBUG0("Task destroyed.");