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_resource->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_resource->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 Remove a dependency between two tasks
308 * \param dst a task depending on \a src
309 * \see SD_task_dependency_add()
311 void SD_task_dependency_remove(SD_task_t src, SD_task_t dst) {
317 SD_dependency_t dependency;
319 SD_CHECK_INIT_DONE();
320 xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
322 /* remove the dependency from src->tasks_after */
323 dynar = src->tasks_after;
324 length = xbt_dynar_length(dynar);
326 for (i = 0; i < length && !found; i++) {
327 xbt_dynar_get_cpy(dynar, i, &dependency);
328 if (dependency->dst == dst) {
329 xbt_dynar_remove_at(dynar, i, NULL);
335 "No dependency found between task '%s' and '%s': task '%s' is not a successor of task '%s'",
336 SD_task_get_name(src), SD_task_get_name(dst), SD_task_get_name(dst), SD_task_get_name(src));
338 /* remove the dependency from dst->tasks_before */
339 dynar = dst->tasks_before;
340 length = xbt_dynar_length(dynar);
343 for (i = 0; i < length && !found; i++) {
344 xbt_dynar_get_cpy(dynar, i, &dependency);
345 if (dependency->src == src) {
346 xbt_dynar_remove_at(dynar, i, NULL);
347 __SD_task_dependency_destroy(dependency);
351 /* should never happen... */
352 xbt_assert4(found, "SimDag error: task '%s' is a successor of '%s' but task '%s' is not a predecessor of task '%s'",
353 SD_task_get_name(dst), SD_task_get_name(src), SD_task_get_name(src), SD_task_get_name(dst));
355 /* if the task was scheduled and dst->tasks_before is empty now, we can make it ready */
356 if (xbt_dynar_length(dst->tasks_before) == 0 && __SD_task_is_scheduled(dst))
357 __SD_task_set_state(dst, SD_READY);
359 /* __SD_print_dependencies(src);
360 __SD_print_dependencies(dst);*/
364 * \brief Returns the user data associated with a dependency between two tasks
367 * \param dst a task depending on \a src
368 * \return the user data associated with this dependency (can be \c NULL)
369 * \see SD_task_dependency_add()
371 void *SD_task_dependency_get_data(SD_task_t src, SD_task_t dst) {
377 SD_dependency_t dependency;
380 SD_CHECK_INIT_DONE();
381 xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
383 dynar = src->tasks_after;
384 length = xbt_dynar_length(dynar);
386 for (i = 0; i < length && !found; i++) {
387 xbt_dynar_get_cpy(dynar, i, &dependency);
388 found = (dependency->dst == dst);
391 THROW2(arg_error, 0, "No dependency found between task '%s' and '%s'", SD_task_get_name(src), SD_task_get_name(dst));
392 return dependency->data;
395 /* temporary function for debugging */
396 static void __SD_print_watch_points(SD_task_t task) {
397 static const int state_masks[] = {SD_SCHEDULED, SD_RUNNING, SD_READY, SD_DONE, SD_FAILED};
398 static const char* state_names[] = {"scheduled", "running", "ready", "done", "failed"};
401 INFO2("Task '%s' watch points (%x): ", SD_task_get_name(task), task->watch_points);
404 for (i = 0; i < 5; i++) {
405 if (task->watch_points & state_masks[i])
406 INFO1("%s ", state_names[i]);
411 * \brief Adds a watch point to a task
413 * SD_simulate() will stop as soon as the \ref e_SD_task_state_t "state" of this
414 * task becomes the one given in argument. The
415 * watch point is then automatically removed.
418 * \param state the \ref e_SD_task_state_t "state" you want to watch
419 * (cannot be #SD_NOT_SCHEDULED)
420 * \see SD_task_unwatch()
422 void SD_task_watch(SD_task_t task, e_SD_task_state_t state) {
423 SD_CHECK_INIT_DONE();
424 xbt_assert0(task != NULL, "Invalid parameter");
426 if (state & SD_NOT_SCHEDULED)
427 THROW0(arg_error, 0, "Cannot add a watch point for state SD_NOT_SCHEDULED");
429 task->watch_points = task->watch_points | state;
430 /* __SD_print_watch_points(task);*/
434 * \brief Removes a watch point from a task
437 * \param state the \ref e_SD_task_state_t "state" you no longer want to watch
438 * \see SD_task_watch()
440 void SD_task_unwatch(SD_task_t task, e_SD_task_state_t state) {
441 SD_CHECK_INIT_DONE();
442 xbt_assert0(task != NULL, "Invalid parameter");
443 xbt_assert0(state != SD_NOT_SCHEDULED,
444 "SimDag error: Cannot have a watch point for state SD_NOT_SCHEDULED");
446 task->watch_points = task->watch_points & ~state;
447 /* __SD_print_watch_points(task);*/
451 * \brief Returns an approximative estimation of the execution time of a task.
453 * The estimation is very approximative because the value returned is the time
454 * the task would take if it was executed now and if it was the only task.
456 * \param task the task to evaluate
457 * \param workstation_nb number of workstations on which the task would be executed
458 * \param workstation_list the workstations on which the task would be executed
459 * \param computation_amount computation amount for each workstation
460 * \param communication_amount communication amount between each pair of workstations
461 * \param rate task execution speed rate
464 double SD_task_get_execution_time(SD_task_t task,
466 const SD_workstation_t *workstation_list,
467 const double *computation_amount,
468 const double *communication_amount,
470 double time, max_time = 0.0;
472 SD_CHECK_INIT_DONE();
473 xbt_assert0(task != NULL && workstation_nb > 0 && workstation_list != NULL &&
474 computation_amount != NULL && communication_amount != NULL,
475 "Invalid parameter");
477 /* the task execution time is the maximum execution time of the parallel tasks */
479 for (i = 0; i < workstation_nb; i++) {
480 time = SD_workstation_get_computation_time(workstation_list[i], computation_amount[i]);
482 for (j = 0; j < workstation_nb; j++) {
483 time += SD_route_get_communication_time(workstation_list[i], workstation_list[j],
484 communication_amount[i * workstation_nb + j]);
487 if (time > max_time) {
491 return max_time * SD_task_get_amount(task);
495 * \brief Schedules a task
497 * The task state must be #SD_NOT_SCHEDULED.
498 * Once scheduled, a task will be executed as soon as possible in SD_simulate(),
499 * i.e. when its dependencies are satisfied.
501 * \param task the task you want to schedule
502 * \param workstation_nb number of workstations on which the task will be executed
503 * \param workstation_list the workstations on which the task will be executed
504 * \param computation_amount computation amount for each workstation
505 * \param communication_amount communication amount between each pair of workstations
506 * \param rate task execution speed rate
507 * \see SD_task_unschedule()
509 void SD_task_schedule(SD_task_t task, int workstation_nb,
510 const SD_workstation_t *workstation_list, const double *computation_amount,
511 const double *communication_amount, double rate) {
513 int communication_nb;
515 SD_CHECK_INIT_DONE();
516 xbt_assert0(task != NULL, "Invalid parameter");
517 xbt_assert0(workstation_nb > 0, "workstation_nb must be positive");
519 if (!__SD_task_is_not_scheduled(task))
520 THROW1(arg_error, 0, "Task '%s' has already been scheduled", SD_task_get_name(task));
522 task->workstation_nb = workstation_nb;
525 task->computation_amount = xbt_new(double, workstation_nb);
526 memcpy(task->computation_amount, computation_amount, sizeof(double) * workstation_nb);
528 communication_nb = workstation_nb * workstation_nb;
529 task->communication_amount = xbt_new(double, communication_nb);
530 memcpy(task->communication_amount, communication_amount, sizeof(double) * communication_nb);
532 task->workstation_list = xbt_new(SD_workstation_t, workstation_nb);
533 memcpy(task->workstation_list, workstation_list, sizeof(SD_workstation_t) * workstation_nb);
535 /* update the task state */
536 if (xbt_dynar_length(task->tasks_before) == 0)
537 __SD_task_set_state(task, SD_READY);
539 __SD_task_set_state(task, SD_SCHEDULED);
543 * \brief Unschedules a task
545 * The task state must be #SD_SCHEDULED, #SD_READY, #SD_RUNNING or #SD_FAILED.
546 * If you call this function, the task state becomes #SD_NOT_SCHEDULED.
547 * Call SD_task_schedule() to schedule it again.
549 * \param task the task you want to unschedule
550 * \see SD_task_schedule()
552 void SD_task_unschedule(SD_task_t task) {
553 SD_CHECK_INIT_DONE();
554 xbt_assert0(task != NULL, "Invalid parameter");
556 if (task->state_set != sd_global->scheduled_task_set &&
557 task->state_set != sd_global->ready_task_set &&
558 task->state_set != sd_global->running_task_set &&
559 task->state_set != sd_global->failed_task_set)
560 THROW1(arg_error, 0, "Task %s: the state must be SD_SCHEDULED, SD_READY, SD_RUNNING or SD_FAILED",
561 SD_task_get_name(task));
563 if (__SD_task_is_scheduled_or_ready(task)) /* if the task is scheduled or ready */
564 __SD_task_destroy_scheduling_data(task);
566 if (__SD_task_is_running(task)) /* the task should become SD_FAILED */
567 surf_workstation_resource->common_public->action_cancel(task->surf_action);
569 __SD_task_set_state(task, SD_NOT_SCHEDULED);
570 task->remains = task->amount;
571 task->start_time = -1.0;
574 /* Destroys the data memorised by SD_task_schedule. Task state must be SD_SCHEDULED or SD_READY.
576 static void __SD_task_destroy_scheduling_data(SD_task_t task) {
577 SD_CHECK_INIT_DONE();
578 if (!__SD_task_is_scheduled_or_ready(task) && !__SD_task_is_in_fifo(task))
579 THROW1(arg_error, 0, "Task '%s' must be SD_SCHEDULED, SD_READY or SD_IN_FIFO", SD_task_get_name(task));
581 xbt_free(task->computation_amount);
582 xbt_free(task->communication_amount);
585 /* Runs a task. This function is directly called by __SD_task_try_to_run if the task
586 * doesn't have to wait in fifos. Otherwise, it is called by __SD_task_just_done when
587 * the task gets out of its fifos.
589 void __SD_task_really_run(SD_task_t task) {
592 void **surf_workstations;
594 SD_CHECK_INIT_DONE();
595 xbt_assert0(task != NULL, "Invalid parameter");
596 xbt_assert2(__SD_task_is_ready_or_in_fifo(task), "Task '%s' is not ready or in a fifo! Task state: %d",
597 SD_task_get_name(task), SD_task_get_state(task));
598 xbt_assert1(task->workstation_list != NULL, "Task '%s': workstation_list is NULL!", SD_task_get_name(task));
602 DEBUG1("Really running task '%s'", SD_task_get_name(task));
604 /* set this task as current task for the workstations in sequential mode */
605 for (i = 0; i < task->workstation_nb; i++) {
606 if (SD_workstation_get_access_mode(task->workstation_list[i]) == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
607 task->workstation_list[i]->current_task = task;
608 xbt_assert0(__SD_workstation_is_busy(task->workstation_list[i]), "The workstation should be busy now");
612 DEBUG1("Task '%s' set as current task for its workstations", SD_task_get_name(task));
616 /* we have to create a Surf workstation array instead of the SimDag workstation array */
617 surf_workstations = xbt_new(void*, task->workstation_nb);
619 for (i = 0; i < task->workstation_nb; i++) {
620 surf_workstations[i] = task->workstation_list[i]->surf_workstation;
623 task->surf_action = surf_workstation_resource->extension_public->
624 execute_parallel_task(task->workstation_nb,
626 task->computation_amount,
627 task->communication_amount,
630 surf_workstation_resource->common_public->action_set_data(task->surf_action, task);
631 task->state_changed = 1;
633 DEBUG1("surf_action = %p", task->surf_action);
635 xbt_free(surf_workstations);
636 __SD_task_destroy_scheduling_data(task); /* now the scheduling data are not useful anymore */
637 __SD_task_set_state(task, SD_RUNNING);
638 xbt_assert2(__SD_task_is_running(task), "Bad state of task '%s': %d",
639 SD_task_get_name(task), SD_task_get_state(task));
643 /* Tries to run a task. This function is called by SD_simulate() when a scheduled task becomes SD_READY
644 * (ie when its dependencies are satisfied).
645 * If one of the workstations where the task is scheduled on is busy (in sequential mode),
646 * the task doesn't start.
647 * Returns whether the task has started.
649 int __SD_task_try_to_run(SD_task_t task) {
653 SD_workstation_t workstation;
655 SD_CHECK_INIT_DONE();
656 xbt_assert0(task != NULL, "Invalid parameter");
657 xbt_assert2(__SD_task_is_ready(task), "Task '%s' is not ready! Task state: %d",
658 SD_task_get_name(task), SD_task_get_state(task));
661 for (i = 0; i < task->workstation_nb; i++) {
662 can_start = !__SD_workstation_is_busy(task->workstation_list[i]);
665 DEBUG2("Task '%s' can start: %d", SD_task_get_name(task), can_start);
667 if (!can_start) { /* if the task cannot start and is not in the fifos yet*/
668 for (i = 0; i < task->workstation_nb; i++) {
669 workstation = task->workstation_list[i];
670 if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
671 DEBUG2("Pushing task '%s' in the fifo of workstation '%s'", SD_task_get_name(task),
672 SD_workstation_get_name(workstation));
673 xbt_fifo_push(workstation->task_fifo, task);
676 __SD_task_set_state(task, SD_IN_FIFO);
677 xbt_assert2(__SD_task_is_in_fifo(task), "Bad state of task '%s': %d",
678 SD_task_get_name(task), SD_task_get_state(task));
679 DEBUG1("Task '%s' state is now SD_IN_FIFO", SD_task_get_name(task));
682 __SD_task_really_run(task);
688 /* This function is called by SD_simulate when a task is done.
689 * It updates task->state and task->action and executes if necessary the tasks
690 * which were waiting in fifos for the end of `task'
692 void __SD_task_just_done(SD_task_t task) {
694 SD_workstation_t workstation;
697 int candidate_nb = 0;
698 int candidate_capacity = 8;
699 SD_task_t *candidates;
702 SD_CHECK_INIT_DONE();
703 xbt_assert0(task != NULL, "Invalid parameter");
704 xbt_assert1(__SD_task_is_running(task), "The task must be running! Task state: %d", SD_task_get_state(task));
705 xbt_assert1(task->workstation_list != NULL, "Task '%s': workstation_list is NULL!", SD_task_get_name(task));
708 candidates = xbt_new(SD_task_t, 8);
710 __SD_task_set_state(task, SD_DONE);
711 surf_workstation_resource->common_public->action_free(task->surf_action);
712 task->surf_action = NULL;
714 DEBUG0("Looking for candidates");
716 /* if the task was executed on sequential workstations,
717 maybe we can execute the next task of the fifo for each workstation */
718 for (i = 0; i < task->workstation_nb; i++) {
719 workstation = task->workstation_list[i];
720 DEBUG2("Workstation '%s': access_mode = %d", SD_workstation_get_name(workstation), workstation->access_mode);
721 if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
722 xbt_assert1(workstation->task_fifo != NULL, "Workstation '%s' has sequential access but no fifo!",
723 SD_workstation_get_name(workstation));
724 xbt_assert2(workstation->current_task = task, "Workstation '%s': current task should be '%s'",
725 SD_workstation_get_name(workstation), SD_task_get_name(task));
727 /* the task is over so we can release the workstation */
728 workstation->current_task = NULL;
730 DEBUG0("Getting candidate in fifo");
731 candidate = xbt_fifo_get_item_content(xbt_fifo_get_first_item(workstation->task_fifo));
733 if (candidate != NULL) {
734 DEBUG1("Candidate: '%s'", SD_task_get_name(candidate));
735 xbt_assert2(__SD_task_is_in_fifo(candidate), "Bad state of candidate '%s': %d",
736 SD_task_get_name(candidate), SD_task_get_state(candidate));
739 DEBUG1("Candidate in fifo: %p", candidate);
741 /* if there was a task waiting for my place */
742 if (candidate != NULL) {
743 /* Unfortunately, we are not sure yet that we can execute the task now,
744 because the task can be waiting more deeply in some other workstation's fifos...
745 So we memorize all candidate tasks, and then we will check for each candidate
746 whether or not all its workstations are available. */
748 /* realloc if necessary */
749 if (candidate_nb == candidate_capacity) {
750 candidate_capacity *= 2;
751 candidates = xbt_realloc(candidates, sizeof(SD_task_t) * candidate_capacity);
754 /* register the candidate */
755 candidates[candidate_nb++] = candidate;
756 candidate->fifo_checked = 0;
761 DEBUG1("Candidates found: %d", candidate_nb);
763 /* now we check every candidate task */
764 for (i = 0; i < candidate_nb; i++) {
765 candidate = candidates[i];
767 if (candidate->fifo_checked) {
768 continue; /* we have already evaluated that task*/
771 xbt_assert2(__SD_task_is_in_fifo(candidate), "Bad state of candidate '%s': %d",
772 SD_task_get_name(candidate), SD_task_get_state(candidate));
774 for (j = 0; j < candidate->workstation_nb && can_start; j++) {
775 workstation = candidate->workstation_list[j];
777 /* I can start on this workstation if the workstation is shared
778 or if I am the first task in the fifo */
779 can_start = workstation->access_mode == SD_WORKSTATION_SHARED_ACCESS ||
780 candidate == xbt_fifo_get_item_content(xbt_fifo_get_first_item(workstation->task_fifo));
783 DEBUG2("Candidate '%s' can start: %d", SD_task_get_name(candidate), can_start);
785 /* now we are sure that I can start! */
787 for (j = 0; j < candidate->workstation_nb && can_start; j++) {
788 workstation = candidate->workstation_list[j];
790 /* update the fifo */
791 if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
792 candidate = xbt_fifo_shift(workstation->task_fifo); /* the return value is stored just for debugging */
793 DEBUG1("Head of the fifo: '%s'", (candidate != NULL) ? SD_task_get_name(candidate) : "NULL");
794 xbt_assert0(candidate == candidates[i], "Error in __SD_task_just_done: bad first task in the fifo");
796 } /* for each workstation */
798 /* finally execute the task */
799 DEBUG2("Task '%s' state: %d", SD_task_get_name(candidate), SD_task_get_state(candidate));
800 __SD_task_really_run(candidate);
802 DEBUG4("Calling __SD_task_is_running: task '%s', state set: %p, running_task_set: %p, is running: %d",
803 SD_task_get_name(candidate), candidate->state_set, sd_global->running_task_set, __SD_task_is_running(candidate));
804 xbt_assert2(__SD_task_is_running(candidate), "Bad state of task '%s': %d",
805 SD_task_get_name(candidate), SD_task_get_state(candidate));
806 DEBUG0("Okay, the task is running.");
809 candidate->fifo_checked = 1;
810 } /* for each candidate */
812 xbt_free(candidates);
815 /* Remove all dependencies associated with a task. This function is called when the task is destroyed.
817 static void __SD_task_remove_dependencies(SD_task_t task) {
818 /* we must destroy the dependencies carefuly (with SD_dependency_remove)
819 because each one is stored twice */
820 SD_dependency_t dependency;
821 while (xbt_dynar_length(task->tasks_before) > 0) {
822 xbt_dynar_get_cpy(task->tasks_before, 0, &dependency);
823 SD_task_dependency_remove(dependency->src, dependency->dst);
826 while (xbt_dynar_length(task->tasks_after) > 0) {
827 xbt_dynar_get_cpy(task->tasks_after, 0, &dependency);
828 SD_task_dependency_remove(dependency->src, dependency->dst);
833 * \brief Returns the start time of a task
835 * The task state must be SD_RUNNING, SD_DONE or SD_FAILED.
837 * \param task: a task
838 * \return the start time of this task
840 double SD_task_get_start_time(SD_task_t task) {
841 SD_CHECK_INIT_DONE();
842 xbt_assert0(task != NULL, "Invalid parameter");
843 if(task->surf_action)
844 return surf_workstation_resource->common_public->action_get_start_time(task->surf_action);
846 return task->start_time;
850 * \brief Returns the finish time of a task
852 * The task state must be SD_RUNNING, SD_DONE or SD_FAILED.
853 * If the state is not completed yet, the returned value is an
854 * estimation of the task finish time. This value can fluctuate
855 * until the task is completed.
857 * \param task: a task
858 * \return the start time of this task
860 double SD_task_get_finish_time(SD_task_t task) {
861 SD_CHECK_INIT_DONE();
862 xbt_assert0(task != NULL, "Invalid parameter");
864 if(task->surf_action) /* should never happen as actions are destroyed right after their completion */
865 return surf_workstation_resource->common_public->action_get_finish_time(task->surf_action);
867 return task->finish_time;
871 * \brief Destroys a task.
873 * The user data (if any) should have been destroyed first.
875 * \param task the task you want to destroy
876 * \see SD_task_create()
878 void SD_task_destroy(SD_task_t task) {
879 SD_CHECK_INIT_DONE();
880 xbt_assert0(task != NULL, "Invalid parameter");
882 DEBUG1("Destroying task %s...", SD_task_get_name(task));
884 __SD_task_remove_dependencies(task);
886 /* if the task was scheduled or ready we have to free the scheduling parameters */
887 if (__SD_task_is_scheduled_or_ready(task))
888 __SD_task_destroy_scheduling_data(task);
890 if (task->name != NULL)
891 xbt_free(task->name);
893 if (task->surf_action != NULL)
894 surf_workstation_resource->common_public->action_free(task->surf_action);
896 if (task->workstation_list != NULL)
897 xbt_free(task->workstation_list);
899 xbt_dynar_free(&task->tasks_before);
900 xbt_dynar_free(&task->tasks_after);
903 sd_global->task_number--;
905 DEBUG0("Task destroyed.");