Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Fix uninitialized values
[simgrid.git] / src / simdag / sd_task.c
1 #include "private.h"
2 #include "simdag/simdag.h"
3 #include "xbt/sysdep.h"
4 #include "xbt/dynar.h"
5
6 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(sd_task,sd,
7                                 "Logging specific to SimDag (task)");
8
9 static void __SD_task_remove_dependencies(SD_task_t task);
10 static void __SD_task_destroy_scheduling_data(SD_task_t task);
11
12 /**
13  * \brief Creates a new task.
14  *
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()
20  */
21 SD_task_t SD_task_create(const char *name, void *data, double amount) {
22   SD_CHECK_INIT_DONE();
23
24   SD_task_t task = xbt_new(s_SD_task_t, 1);
25
26   /* general information */
27   task->data = data; /* user data */
28   if (name != NULL)
29     task->name = xbt_strdup(name);
30   else
31     task->name = NULL;
32
33   task->state_hookup.prev = NULL;
34   task->state_hookup.next = NULL;
35   task->state_set = sd_global->not_scheduled_task_set;
36   task->state = SD_NOT_SCHEDULED;
37   xbt_swag_insert(task, task->state_set);
38
39   task->amount = amount;
40   task->remains = amount;
41   task->start_time = -1.0;
42   task->finish_time = -1.0;
43   task->surf_action = NULL;
44   task->watch_points = 0;
45   task->state_changed = 0;
46
47   /* dependencies */
48   task->tasks_before = xbt_dynar_new(sizeof(SD_dependency_t), NULL);
49   task->tasks_after = xbt_dynar_new(sizeof(SD_dependency_t), NULL);
50
51   /* scheduling parameters */
52   task->workstation_nb = 0;
53   task->workstation_list = NULL;
54   task->computation_amount = NULL;
55   task->communication_amount = NULL;
56   task->rate = 0;
57
58   sd_global->task_number++;
59
60   return task;
61 }
62
63 /**
64  * \brief Returns the user data of a task
65  *
66  * \param task a task
67  * \return the user data associated with this task (can be \c NULL)
68  * \see SD_task_set_data()
69  */
70 void* SD_task_get_data(SD_task_t task) {
71   SD_CHECK_INIT_DONE();
72   xbt_assert0(task != NULL, "Invalid parameter");
73   return task->data;
74 }
75
76 /**
77  * \brief Sets the user data of a task
78  *
79  * The new data can be \c NULL. The old data should have been freed first
80  * if it was not \c NULL.
81  *
82  * \param task a task
83  * \param data the new data you want to associate with this task
84  * \see SD_task_get_data()
85  */
86 void SD_task_set_data(SD_task_t task, void *data) {
87   SD_CHECK_INIT_DONE();
88   xbt_assert0(task != NULL, "Invalid parameter");
89   task->data = data;
90 }
91
92 /**
93  * \brief Returns the state of a task
94  *
95  * \param task a task
96  * \return the current \ref e_SD_task_state_t "state" of this task:
97  * #SD_NOT_SCHEDULED, #SD_SCHEDULED, #SD_READY, #SD_RUNNING, #SD_DONE or #SD_FAILED
98  * \see e_SD_task_state_t
99  */
100 e_SD_task_state_t SD_task_get_state(SD_task_t task) {
101   SD_CHECK_INIT_DONE();
102   xbt_assert0(task != NULL, "Invalid parameter");
103   return task->state;
104 }
105
106 /* Changes the state of a task. Updates the swags and the flag sd_global->watch_point_reached.
107  */
108 void __SD_task_set_state(SD_task_t task, e_SD_task_state_t new_state) {
109   xbt_swag_remove(task, task->state_set);
110   switch (new_state) {
111   case SD_NOT_SCHEDULED:
112     task->state_set = sd_global->not_scheduled_task_set;
113     break;
114   case SD_SCHEDULED:
115     task->state_set = sd_global->scheduled_task_set;
116     break;
117   case SD_READY:
118     task->state_set = sd_global->ready_task_set;
119     break;
120   case SD_IN_FIFO:
121     task->state_set = sd_global->in_fifo_task_set;
122     break;
123   case SD_RUNNING:
124     task->state_set = sd_global->running_task_set;
125     task->start_time = surf_workstation_resource->common_public->
126       action_get_start_time(task->surf_action);
127     break;
128   case SD_DONE:
129     task->state_set = sd_global->done_task_set;
130     task->finish_time = surf_workstation_resource->common_public->
131       action_get_finish_time(task->surf_action);
132     task->remains = 0;
133     break;
134   case SD_FAILED:
135     task->state_set = sd_global->failed_task_set;
136     break;
137   default:
138     xbt_assert0(0, "Invalid state");
139   }
140   xbt_swag_insert(task, task->state_set);
141   task->state = new_state;
142
143   if (task->watch_points & new_state) {
144     INFO1("Watch point reached with task '%s'!", SD_task_get_name(task));
145     sd_global->watch_point_reached = 1;
146     SD_task_unwatch(task, new_state); /* remove the watch point */
147   }
148 }
149
150 /**
151  * \brief Returns the name of a task
152  *
153  * \param task a task
154  * \return the name of this task (can be \c NULL)
155  */
156 const char* SD_task_get_name(SD_task_t task) {
157   SD_CHECK_INIT_DONE();
158   xbt_assert0(task != NULL, "Invalid parameter");
159   return task->name;
160 }
161
162 /**
163  * \brief Returns the total amount of a task
164  *
165  * \param task a task
166  * \return the total amount of this task
167  * \see SD_task_get_remaining_amount()
168  */
169 double SD_task_get_amount(SD_task_t task) {
170   SD_CHECK_INIT_DONE();
171   xbt_assert0(task != NULL, "Invalid parameter");
172   return task->amount;
173 }
174
175 /**
176  * \brief Returns the remaining amount of a task
177  *
178  * \param task a task
179  * \return the remaining amount of this task
180  * \see SD_task_get_amount()
181  */
182 double SD_task_get_remaining_amount(SD_task_t task) {
183   SD_CHECK_INIT_DONE();
184   xbt_assert0(task != NULL, "Invalid parameter");
185
186   if (task->surf_action)
187     return task->surf_action->remains;
188   else
189     return task->remains;
190 }
191
192 /* temporary function for debbuging */
193 static void __SD_print_dependencies(SD_task_t task) {
194   INFO1("The following tasks must be executed before %s:", SD_task_get_name(task));
195   xbt_dynar_t dynar = task->tasks_before;
196   int length = xbt_dynar_length(dynar);
197   int i;
198   SD_dependency_t dependency;
199   for (i = 0; i < length; i++) {
200     xbt_dynar_get_cpy(dynar, i, &dependency);
201     INFO1(" %s", SD_task_get_name(dependency->src));
202   }
203
204   INFO1("The following tasks must be executed after %s:", SD_task_get_name(task));
205
206   dynar = task->tasks_after;
207   length = xbt_dynar_length(dynar);
208   for (i = 0; i < length; i++) {
209     xbt_dynar_get_cpy(dynar, i, &dependency);
210     INFO1(" %s", SD_task_get_name(dependency->dst));
211   }
212   INFO0("----------------------------");
213 }
214
215 /* Destroys a dependency between two tasks.
216  */
217 static void __SD_task_dependency_destroy(void *dependency) {
218   if (((SD_dependency_t) dependency)->name != NULL)
219     xbt_free(((SD_dependency_t) dependency)->name);
220   xbt_free(dependency);
221 }
222
223 /**
224  * \brief Adds a dependency between two tasks
225  *
226  * \a dst will depend on \a src, ie \a dst will not start before \a src is finished.
227  * Their \ref e_SD_task_state_t "state" must be #SD_NOT_SCHEDULED, #SD_SCHEDULED or #SD_READY.
228  *
229  * \param name the name of the new dependency (can be \c NULL)
230  * \param data the user data you want to associate with this dependency (can be \c NULL)
231  * \param src the task which must be executed first
232  * \param dst the task you want to make depend on \a src
233  * \see SD_task_dependency_remove()
234  */
235 void SD_task_dependency_add(const char *name, void *data, SD_task_t src, SD_task_t dst) {
236   SD_CHECK_INIT_DONE();
237   xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
238
239   xbt_dynar_t dynar = src->tasks_after;
240   int length = xbt_dynar_length(dynar);
241   int found = 0;
242   int i;
243   SD_dependency_t dependency;
244
245   if (src == dst) 
246     THROW1(arg_error, 0, "Cannot add a dependency between task '%s' and itself",
247            SD_task_get_name(src));
248
249   if (!__SD_task_is_not_scheduled(src) && !__SD_task_is_scheduled_or_ready(src))
250     THROW1(arg_error, 0, "Task '%s' must be SD_NOT_SCHEDULED, SD_SCHEDULED or SD_READY", SD_task_get_name(src));
251
252   if (!__SD_task_is_not_scheduled(dst) && !__SD_task_is_scheduled_or_ready(dst))
253     THROW1(arg_error, 0, "Task '%s' must be SD_NOT_SCHEDULED, SD_SCHEDULED or SD_READY", SD_task_get_name(dst));
254
255   DEBUG2("SD_task_dependency_add: src = %s, dst = %s", SD_task_get_name(src), SD_task_get_name(dst));
256   for (i = 0; i < length && !found; i++) {
257     xbt_dynar_get_cpy(dynar, i, &dependency);
258     found = (dependency->dst == dst);
259     DEBUG2("Dependency %d: dependency->dst = %s", i, SD_task_get_name(dependency->dst));
260   }
261
262   if (found)
263     THROW2(arg_error, 0, "A dependency already exists between task '%s' and task '%s'",
264            SD_task_get_name(src), SD_task_get_name(dst));
265
266   dependency = xbt_new(s_SD_dependency_t, 1);
267
268   if (name != NULL)
269     dependency->name = xbt_strdup(name);
270   else
271     dependency->name = NULL;
272
273   dependency->data = data;
274   dependency->src = src;
275   dependency->dst = dst;
276
277   /* src must be executed before dst */
278   xbt_dynar_push(src->tasks_after, &dependency);
279   xbt_dynar_push(dst->tasks_before, &dependency);
280
281   /* if the task was ready, then dst->tasks_before is not empty anymore,
282      so we must go back to state SD_SCHEDULED */
283   if (__SD_task_is_ready(dst)) {
284     DEBUG1("SD_task_dependency_add: %s was ready and becomes scheduled!", SD_task_get_name(dst));
285     __SD_task_set_state(dst, SD_SCHEDULED);
286   }
287
288   /*  __SD_print_dependencies(src);
289       __SD_print_dependencies(dst); */
290 }
291
292 /**
293  * \brief Remove a dependency between two tasks
294  *
295  * \param src a task
296  * \param dst a task depending on \a src
297  * \see SD_task_dependency_add()
298  */
299 void SD_task_dependency_remove(SD_task_t src, SD_task_t dst) {
300   SD_CHECK_INIT_DONE();
301   xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
302
303   /* remove the dependency from src->tasks_after */
304   xbt_dynar_t dynar = src->tasks_after;
305   int length = xbt_dynar_length(dynar);
306   int found = 0;
307   int i;
308   SD_dependency_t dependency;
309   for (i = 0; i < length && !found; i++) {
310     xbt_dynar_get_cpy(dynar, i, &dependency);
311     if (dependency->dst == dst) {
312       xbt_dynar_remove_at(dynar, i, NULL);
313       found = 1;
314     }
315   }
316   if (!found)
317     THROW4(arg_error, 0,
318            "No dependency found between task '%s' and '%s': task '%s' is not a successor of task '%s'",
319            SD_task_get_name(src), SD_task_get_name(dst), SD_task_get_name(dst), SD_task_get_name(src));
320
321   /* remove the dependency from dst->tasks_before */
322   dynar = dst->tasks_before;
323   length = xbt_dynar_length(dynar);
324   found = 0;
325   
326   for (i = 0; i < length && !found; i++) {
327     xbt_dynar_get_cpy(dynar, i, &dependency);
328     if (dependency->src == src) {
329       xbt_dynar_remove_at(dynar, i, NULL);
330       __SD_task_dependency_destroy(dependency);
331       found = 1;
332     }
333   }
334   /* should never happen... */
335   xbt_assert4(found, "SimDag error: task '%s' is a successor of '%s' but task '%s' is not a predecessor of task '%s'",
336               SD_task_get_name(dst), SD_task_get_name(src), SD_task_get_name(src), SD_task_get_name(dst));
337
338   /* if the task was scheduled and dst->tasks_before is empty now, we can make it ready */
339   if (xbt_dynar_length(dst->tasks_before) == 0 && __SD_task_is_scheduled(dst))
340     __SD_task_set_state(dst, SD_READY);
341
342   /*  __SD_print_dependencies(src); 
343       __SD_print_dependencies(dst);*/
344 }
345
346 /**
347  * \brief Returns the user data associated with a dependency between two tasks
348  *
349  * \param src a task
350  * \param dst a task depending on \a src
351  * \return the user data associated with this dependency (can be \c NULL)
352  * \see SD_task_dependency_add()
353  */
354 void *SD_task_dependency_get_data(SD_task_t src, SD_task_t dst) {
355   SD_CHECK_INIT_DONE();
356   xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
357
358   xbt_dynar_t dynar = src->tasks_after;
359   int length = xbt_dynar_length(dynar);
360   int found = 0;
361   int i;
362   SD_dependency_t dependency;
363   for (i = 0; i < length && !found; i++) {
364     xbt_dynar_get_cpy(dynar, i, &dependency);
365     found = (dependency->dst == dst);
366   }
367   if (!found)
368     THROW2(arg_error, 0, "No dependency found between task '%s' and '%s'", SD_task_get_name(src), SD_task_get_name(dst));
369   return dependency->data;
370 }
371
372 /* temporary function for debugging */
373 static void __SD_print_watch_points(SD_task_t task) {
374   static const int state_masks[] = {SD_SCHEDULED, SD_RUNNING, SD_READY, SD_DONE, SD_FAILED};
375   static const char* state_names[] = {"scheduled", "running", "ready", "done", "failed"};
376
377   INFO2("Task '%s' watch points (%x): ", SD_task_get_name(task), task->watch_points);
378
379   int i;
380   for (i = 0; i < 5; i++) {
381     if (task->watch_points & state_masks[i])
382       INFO1("%s ", state_names[i]);
383   }
384 }
385
386 /**
387  * \brief Adds a watch point to a task
388  *
389  * SD_simulate() will stop as soon as the \ref e_SD_task_state_t "state" of this
390  * task becomes the one given in argument. The
391  * watch point is then automatically removed.
392  * 
393  * \param task a task
394  * \param state the \ref e_SD_task_state_t "state" you want to watch
395  * (cannot be #SD_NOT_SCHEDULED)
396  * \see SD_task_unwatch()
397  */
398 void SD_task_watch(SD_task_t task, e_SD_task_state_t state) {
399   SD_CHECK_INIT_DONE();
400   xbt_assert0(task != NULL, "Invalid parameter");
401
402   if (state & SD_NOT_SCHEDULED)
403     THROW0(arg_error, 0, "Cannot add a watch point for state SD_NOT_SCHEDULED");
404
405   task->watch_points = task->watch_points | state;
406   /*  __SD_print_watch_points(task);*/
407 }
408
409 /**
410  * \brief Removes a watch point from a task
411  * 
412  * \param task a task
413  * \param state the \ref e_SD_task_state_t "state" you no longer want to watch
414  * \see SD_task_watch()
415  */
416 void SD_task_unwatch(SD_task_t task, e_SD_task_state_t state) {
417   SD_CHECK_INIT_DONE();
418   xbt_assert0(task != NULL, "Invalid parameter");
419   xbt_assert0(state != SD_NOT_SCHEDULED,
420               "SimDag error: Cannot have a watch point for state SD_NOT_SCHEDULED");
421   
422   task->watch_points = task->watch_points & ~state;
423   /*  __SD_print_watch_points(task);*/
424 }
425
426 /**
427  * \brief Returns an approximative estimation of the execution time of a task.
428  * 
429  * The estimation is very approximative because the value returned is the time
430  * the task would take if it was executed now and if it was the only task.
431  * 
432  * \param task the task to evaluate
433  * \param workstation_nb number of workstations on which the task would be executed
434  * \param workstation_list the workstations on which the task would be executed
435  * \param computation_amount computation amount for each workstation
436  * \param communication_amount communication amount between each pair of workstations
437  * \param rate task execution speed rate
438  * \see SD_schedule()
439  */
440 double SD_task_get_execution_time(SD_task_t task,
441                                   int workstation_nb,
442                                   const SD_workstation_t *workstation_list,
443                                   const double *computation_amount,
444                                   const double *communication_amount,
445                                   double rate) {
446   SD_CHECK_INIT_DONE();
447   xbt_assert0(task != NULL && workstation_nb > 0 && workstation_list != NULL &&
448               computation_amount != NULL && communication_amount != NULL,
449               "Invalid parameter");
450
451   /* the task execution time is the maximum execution time of the parallel tasks */
452   double time, max_time = 0.0;
453   int i, j;
454   for (i = 0; i < workstation_nb; i++) {
455     time = SD_workstation_get_computation_time(workstation_list[i], computation_amount[i]);
456     
457     for (j = 0; j < workstation_nb; j++) {
458       time += SD_route_get_communication_time(workstation_list[i], workstation_list[j],
459                                               communication_amount[i * workstation_nb + j]);
460     }
461
462     if (time > max_time) {
463       max_time = time;
464     }
465   }
466   return max_time * SD_task_get_amount(task);
467 }
468
469 /**
470  * \brief Schedules a task
471  *
472  * The task state must be #SD_NOT_SCHEDULED.
473  * Once scheduled, a task will be executed as soon as possible in SD_simulate(),
474  * i.e. when its dependencies are satisfied.
475  * 
476  * \param task the task you want to schedule
477  * \param workstation_nb number of workstations on which the task will be executed
478  * \param workstation_list the workstations on which the task will be executed
479  * \param computation_amount computation amount for each workstation
480  * \param communication_amount communication amount between each pair of workstations
481  * \param rate task execution speed rate
482  * \see SD_task_unschedule()
483  */
484 void SD_task_schedule(SD_task_t task, int workstation_nb,
485                      const SD_workstation_t *workstation_list, const double *computation_amount,
486                      const double *communication_amount, double rate) {
487   SD_CHECK_INIT_DONE();
488   xbt_assert0(task != NULL, "Invalid parameter");
489   xbt_assert0(workstation_nb > 0, "workstation_nb must be positive");
490
491   if (!__SD_task_is_not_scheduled(task))
492     THROW1(arg_error, 0, "Task '%s' has already been scheduled", SD_task_get_name(task));
493
494   task->workstation_nb = workstation_nb;
495   task->rate = rate;
496
497   task->computation_amount = xbt_new(double, workstation_nb);
498   memcpy(task->computation_amount, computation_amount, sizeof(double) * workstation_nb);
499
500   int communication_nb = workstation_nb * workstation_nb;
501   task->communication_amount = xbt_new(double, communication_nb);
502   memcpy(task->communication_amount, communication_amount, sizeof(double) * communication_nb);
503
504   task->workstation_list = xbt_new(SD_workstation_t, workstation_nb);
505   memcpy(task->workstation_list, workstation_list, sizeof(SD_workstation_t) * workstation_nb);
506
507   /* update the task state */
508   if (xbt_dynar_length(task->tasks_before) == 0)
509     __SD_task_set_state(task, SD_READY);
510   else
511     __SD_task_set_state(task, SD_SCHEDULED);
512 }
513
514 /**
515  * \brief Unschedules a task
516  *
517  * The task state must be #SD_SCHEDULED, #SD_READY, #SD_RUNNING or #SD_FAILED.
518  * If you call this function, the task state becomes #SD_NOT_SCHEDULED.
519  * Call SD_task_schedule() to schedule it again.
520  *
521  * \param task the task you want to unschedule
522  * \see SD_task_schedule()
523  */
524 void SD_task_unschedule(SD_task_t task) {
525   SD_CHECK_INIT_DONE();
526   xbt_assert0(task != NULL, "Invalid parameter");
527
528   if (task->state_set != sd_global->scheduled_task_set &&
529       task->state_set != sd_global->ready_task_set &&
530       task->state_set != sd_global->running_task_set &&
531       task->state_set != sd_global->failed_task_set)
532     THROW1(arg_error, 0, "Task %s: the state must be SD_SCHEDULED, SD_READY, SD_RUNNING or SD_FAILED",
533            SD_task_get_name(task));
534
535   if (__SD_task_is_scheduled_or_ready(task)) /* if the task is scheduled or ready */
536     __SD_task_destroy_scheduling_data(task);
537
538   if (__SD_task_is_running(task)) /* the task should become SD_FAILED */
539     surf_workstation_resource->common_public->action_cancel(task->surf_action);
540   else
541     __SD_task_set_state(task, SD_NOT_SCHEDULED);
542   task->remains = task->amount;
543   task->start_time = -1.0;
544 }
545
546 /* Destroys the data memorised by SD_task_schedule. Task state must be SD_SCHEDULED or SD_READY.
547  */
548 static void __SD_task_destroy_scheduling_data(SD_task_t task) {
549   SD_CHECK_INIT_DONE();
550   if (!__SD_task_is_scheduled_or_ready(task) && !__SD_task_is_in_fifo(task))
551     THROW1(arg_error, 0, "Task '%s' must be SD_SCHEDULED, SD_READY or SD_IN_FIFO", SD_task_get_name(task));
552
553   xbt_free(task->computation_amount);
554   xbt_free(task->communication_amount);
555 }
556
557 /* Runs a task. This function is directly called by __SD_task_try_to_run if the task
558  * doesn't have to wait in fifos. Otherwise, it is called by __SD_task_just_done when
559  * the task gets out of its fifos.
560  */
561 void __SD_task_really_run(SD_task_t task) {
562   SD_CHECK_INIT_DONE();
563   xbt_assert0(task != NULL, "Invalid parameter");
564   xbt_assert2(__SD_task_is_ready_or_in_fifo(task), "Task '%s' is not ready or in a fifo! Task state: %d",
565            SD_task_get_name(task), SD_task_get_state(task));
566   xbt_assert1(task->workstation_list != NULL, "Task '%s': workstation_list is NULL!", SD_task_get_name(task));
567
568   int i;
569   void **surf_workstations;
570
571   DEBUG1("Really running task '%s'", SD_task_get_name(task));
572
573   /* set this task as current task for the workstations in sequential mode */
574   for (i = 0; i < task->workstation_nb; i++) {
575     if (SD_workstation_get_access_mode(task->workstation_list[i]) == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
576       task->workstation_list[i]->current_task = task;
577       xbt_assert0(__SD_workstation_is_busy(task->workstation_list[i]), "The workstation should be busy now");
578     }
579   }
580   
581   DEBUG1("Task '%s' set as current task for its workstations", SD_task_get_name(task));
582
583   /* start the task */
584
585   /* we have to create a Surf workstation array instead of the SimDag workstation array */
586   surf_workstations = xbt_new(void*, task->workstation_nb);
587
588   for (i = 0; i < task->workstation_nb; i++) {
589     surf_workstations[i] = task->workstation_list[i]->surf_workstation;
590   }
591   
592   task->surf_action = surf_workstation_resource->extension_public->
593     execute_parallel_task(task->workstation_nb,
594                           surf_workstations,
595                           task->computation_amount,
596                           task->communication_amount,
597                           task->amount,
598                           task->rate);
599   surf_workstation_resource->common_public->action_set_data(task->surf_action, task);
600   task->state_changed = 1;
601
602   DEBUG1("surf_action = %p",  task->surf_action);
603
604   xbt_free(surf_workstations);
605   __SD_task_destroy_scheduling_data(task); /* now the scheduling data are not useful anymore */
606   __SD_task_set_state(task, SD_RUNNING);
607   xbt_assert2(__SD_task_is_running(task), "Bad state of task '%s': %d",
608               SD_task_get_name(task), SD_task_get_state(task));
609
610 }
611
612 /* Tries to run a task. This function is called by SD_simulate() when a scheduled task becomes SD_READY
613  * (ie when its dependencies are satisfied).
614  * If one of the workstations where the task is scheduled on is busy (in sequential mode),
615  * the task doesn't start.
616  * Returns whether the task has started.
617  */
618 int __SD_task_try_to_run(SD_task_t task) {
619   SD_CHECK_INIT_DONE();
620   xbt_assert0(task != NULL, "Invalid parameter");
621   xbt_assert2(__SD_task_is_ready(task), "Task '%s' is not ready! Task state: %d",
622            SD_task_get_name(task), SD_task_get_state(task));
623
624   int can_start = 1;
625   int i;
626   SD_workstation_t workstation;
627
628   for (i = 0; i < task->workstation_nb; i++) {
629     can_start = !__SD_workstation_is_busy(task->workstation_list[i]);
630   }
631
632   DEBUG2("Task '%s' can start: %d", SD_task_get_name(task), can_start);
633   
634   if (!can_start) { /* if the task cannot start and is not in the fifos yet*/
635     for (i = 0; i < task->workstation_nb; i++) {
636       workstation = task->workstation_list[i];
637       if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
638         DEBUG2("Pushing task '%s' in the fifo of workstation '%s'", SD_task_get_name(task),
639                SD_workstation_get_name(workstation));
640         xbt_fifo_push(workstation->task_fifo, task);
641       }
642     }
643     __SD_task_set_state(task, SD_IN_FIFO);
644     xbt_assert2(__SD_task_is_in_fifo(task), "Bad state of task '%s': %d",
645                 SD_task_get_name(task), SD_task_get_state(task));
646     DEBUG1("Task '%s' state is now SD_IN_FIFO", SD_task_get_name(task));
647   }
648   else {
649     __SD_task_really_run(task);
650   }
651
652   return can_start;
653 }
654
655 /* This function is called by SD_simulate when a task is done.
656  * It updates task->state and task->action and executes if necessary the tasks
657  * which were waiting in fifos for the end of `task'
658  */
659 void __SD_task_just_done(SD_task_t task) {
660   SD_CHECK_INIT_DONE();
661   xbt_assert0(task != NULL, "Invalid parameter");
662   xbt_assert1(__SD_task_is_running(task), "The task must be running! Task state: %d", SD_task_get_state(task));
663   xbt_assert1(task->workstation_list != NULL, "Task '%s': workstation_list is NULL!", SD_task_get_name(task));
664
665   int i, j;
666   SD_workstation_t workstation;
667
668   SD_task_t candidate;
669   int candidate_nb = 0;
670   int candidate_capacity = 8;
671   SD_task_t *candidates = xbt_new(SD_task_t, 8);
672   int can_start = 1;
673
674   __SD_task_set_state(task, SD_DONE);
675   surf_workstation_resource->common_public->action_free(task->surf_action);
676   task->surf_action = NULL;
677
678   DEBUG0("Looking for candidates");
679
680   /* if the task was executed on sequential workstations,
681      maybe we can execute the next task of the fifo for each workstation */
682   for (i = 0; i < task->workstation_nb; i++) {
683     workstation = task->workstation_list[i];
684     DEBUG2("Workstation '%s': access_mode = %d", SD_workstation_get_name(workstation), workstation->access_mode);
685     if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
686       xbt_assert1(workstation->task_fifo != NULL, "Workstation '%s' has sequential access but no fifo!",
687                   SD_workstation_get_name(workstation));
688       xbt_assert2(workstation->current_task = task, "Workstation '%s': current task should be '%s'",
689                   SD_workstation_get_name(workstation), SD_task_get_name(task));
690
691       /* the task is over so we can release the workstation */
692       workstation->current_task = NULL;
693
694       DEBUG0("Getting candidate in fifo");
695       candidate = xbt_fifo_get_item_content(xbt_fifo_get_first_item(workstation->task_fifo));
696
697       if (candidate != NULL) {
698         DEBUG1("Candidate: '%s'", SD_task_get_name(candidate));
699         xbt_assert2(__SD_task_is_in_fifo(candidate), "Bad state of candidate '%s': %d",
700                     SD_task_get_name(candidate), SD_task_get_state(candidate));
701       }
702
703       DEBUG1("Candidate in fifo: %p", candidate);
704
705       /* if there was a task waiting for my place */
706       if (candidate != NULL) {
707         /* Unfortunately, we are not sure yet that we can execute the task now,
708            because the task can be waiting more deeply in some other workstation's fifos...
709            So we memorize all candidate tasks, and then we will check for each candidate
710            whether or not all its workstations are available. */
711
712         /* realloc if necessary */
713         if (candidate_nb == candidate_capacity) {
714           candidate_capacity *= 2;
715           candidates = xbt_realloc(candidates, sizeof(SD_task_t) * candidate_capacity);
716         }
717
718         /* register the candidate */
719         candidates[candidate_nb++] = candidate;
720         candidate->fifo_checked = 0;
721       }
722     }
723   }
724
725   DEBUG1("Candidates found: %d", candidate_nb);
726
727   /* now we check every candidate task */
728   for (i = 0; i < candidate_nb; i++) {
729     candidate = candidates[i];
730
731     if (candidate->fifo_checked) {
732       continue; /* we have already evaluated that task*/
733     }
734
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));
737
738     for (j = 0; j < candidate->workstation_nb && can_start; j++) {
739       workstation = candidate->workstation_list[j];
740
741       /* I can start on this workstation if the workstation is shared
742          or if I am the first task in the fifo */
743       can_start = workstation->access_mode == SD_WORKSTATION_SHARED_ACCESS ||
744         candidate == xbt_fifo_get_item_content(xbt_fifo_get_first_item(workstation->task_fifo));
745     }
746
747     DEBUG2("Candidate '%s' can start: %d", SD_task_get_name(candidate), can_start);
748
749     /* now we are sure that I can start! */
750     if (can_start) {
751       for (j = 0; j < candidate->workstation_nb && can_start; j++) {
752         workstation = candidate->workstation_list[j];
753
754         /* update the fifo */
755         if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
756           candidate = xbt_fifo_shift(workstation->task_fifo); /* the return value is stored just for debugging */
757           DEBUG1("Head of the fifo: '%s'", (candidate != NULL) ? SD_task_get_name(candidate) : "NULL");
758           xbt_assert0(candidate == candidates[i], "Error in __SD_task_just_done: bad first task in the fifo");
759         }
760       } /* for each workstation */
761       
762       /* finally execute the task */
763       DEBUG2("Task '%s' state: %d", SD_task_get_name(candidate), SD_task_get_state(candidate));
764       __SD_task_really_run(candidate);
765       
766       DEBUG4("Calling __SD_task_is_running: task '%s', state set: %p, running_task_set: %p, is running: %d",
767              SD_task_get_name(candidate), candidate->state_set, sd_global->running_task_set, __SD_task_is_running(candidate));
768       xbt_assert2(__SD_task_is_running(candidate), "Bad state of task '%s': %d",
769                   SD_task_get_name(candidate), SD_task_get_state(candidate));
770       DEBUG0("Okay, the task is running.");
771
772     } /* can start */
773     candidate->fifo_checked = 1;
774   } /* for each candidate */ 
775   
776   xbt_free(candidates);
777 }
778
779 /* Remove all dependencies associated with a task. This function is called when the task is destroyed.
780  */
781 static void __SD_task_remove_dependencies(SD_task_t task) {
782   /* we must destroy the dependencies carefuly (with SD_dependency_remove)
783      because each one is stored twice */
784   SD_dependency_t dependency;
785   while (xbt_dynar_length(task->tasks_before) > 0) {
786     xbt_dynar_get_cpy(task->tasks_before, 0, &dependency);
787     SD_task_dependency_remove(dependency->src, dependency->dst);
788   }
789
790   while (xbt_dynar_length(task->tasks_after) > 0) {
791     xbt_dynar_get_cpy(task->tasks_after, 0, &dependency);
792     SD_task_dependency_remove(dependency->src, dependency->dst);
793   }
794 }
795
796 /**
797  * \brief Returns the start time of a task
798  *
799  * The task state must be SD_RUNNING, SD_DONE or SD_FAILED.
800  *
801  * \param task: a task
802  * \return the start time of this task
803  */
804 double SD_task_get_start_time(SD_task_t task) {
805   SD_CHECK_INIT_DONE();
806   xbt_assert0(task != NULL, "Invalid parameter");
807   if(task->surf_action)
808     return surf_workstation_resource->common_public->action_get_start_time(task->surf_action);
809   else 
810     return task->start_time;
811 }
812
813 /**
814  * \brief Returns the finish time of a task
815  *
816  * The task state must be SD_RUNNING, SD_DONE or SD_FAILED.
817  * If the state is not completed yet, the returned value is an
818  * estimation of the task finish time. This value can fluctuate 
819  * until the task is completed.
820  *
821  * \param task: a task
822  * \return the start time of this task
823  */
824 double SD_task_get_finish_time(SD_task_t task) {
825   SD_CHECK_INIT_DONE();
826   xbt_assert0(task != NULL, "Invalid parameter");
827
828   if(task->surf_action) /* should never happen as actions are destroyed right after their completion */
829     return surf_workstation_resource->common_public->action_get_finish_time(task->surf_action);
830   else 
831     return task->finish_time;
832 }
833
834 /**
835  * \brief Destroys a task.
836  *
837  * The user data (if any) should have been destroyed first.
838  *
839  * \param task the task you want to destroy
840  * \see SD_task_create()
841  */
842 void SD_task_destroy(SD_task_t task) {
843   SD_CHECK_INIT_DONE();
844   xbt_assert0(task != NULL, "Invalid parameter");
845
846   DEBUG1("Destroying task %s...", SD_task_get_name(task));
847
848   __SD_task_remove_dependencies(task);
849
850   /* if the task was scheduled or ready we have to free the scheduling parameters */
851   if (__SD_task_is_scheduled_or_ready(task))
852     __SD_task_destroy_scheduling_data(task);
853
854   if (task->name != NULL)
855     xbt_free(task->name);
856
857   if (task->surf_action != NULL)
858     surf_workstation_resource->common_public->action_free(task->surf_action);
859
860   if (task->workstation_list != NULL)
861     xbt_free(task->workstation_list);
862
863   xbt_dynar_free(&task->tasks_before);
864   xbt_dynar_free(&task->tasks_after);
865   xbt_free(task);
866
867   sd_global->task_number--;
868
869   DEBUG0("Task destroyed.");
870 }