Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
361f544db2094bc7beb6ced865e68765fa4e3f45
[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
23   SD_task_t task;
24   SD_CHECK_INIT_DONE();
25
26   task = xbt_new(s_SD_task_t, 1);
27
28   /* general information */
29   task->data = data; /* user data */
30   if (name != NULL)
31     task->name = xbt_strdup(name);
32   else
33     task->name = NULL;
34
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);
40
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;
48
49   /* dependencies */
50   task->tasks_before = xbt_dynar_new(sizeof(SD_dependency_t), NULL);
51   task->tasks_after = xbt_dynar_new(sizeof(SD_dependency_t), NULL);
52
53   /* scheduling parameters */
54   task->workstation_nb = 0;
55   task->workstation_list = NULL;
56   task->computation_amount = NULL;
57   task->communication_amount = NULL;
58   task->rate = 0;
59
60   sd_global->task_number++;
61
62   return task;
63 }
64
65 /**
66  * \brief Returns the user data of a task
67  *
68  * \param task a task
69  * \return the user data associated with this task (can be \c NULL)
70  * \see SD_task_set_data()
71  */
72 void* SD_task_get_data(SD_task_t task) {
73   SD_CHECK_INIT_DONE();
74   xbt_assert0(task != NULL, "Invalid parameter");
75   return task->data;
76 }
77
78 /**
79  * \brief Sets the user data of a task
80  *
81  * The new data can be \c NULL. The old data should have been freed first
82  * if it was not \c NULL.
83  *
84  * \param task a task
85  * \param data the new data you want to associate with this task
86  * \see SD_task_get_data()
87  */
88 void SD_task_set_data(SD_task_t task, void *data) {
89   SD_CHECK_INIT_DONE();
90   xbt_assert0(task != NULL, "Invalid parameter");
91   task->data = data;
92 }
93
94 /**
95  * \brief Returns the state of a task
96  *
97  * \param task 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
101  */
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");
105   return task->state;
106 }
107
108 /* Changes the state of a task. Updates the swags and the flag sd_global->watch_point_reached.
109  */
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);
112   switch (new_state) {
113   case SD_NOT_SCHEDULED:
114     task->state_set = sd_global->not_scheduled_task_set;
115     break;
116   case SD_SCHEDULED:
117     task->state_set = sd_global->scheduled_task_set;
118     break;
119   case SD_READY:
120     task->state_set = sd_global->ready_task_set;
121     break;
122   case SD_IN_FIFO:
123     task->state_set = sd_global->in_fifo_task_set;
124     break;
125   case SD_RUNNING:
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);
129     break;
130   case SD_DONE:
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);
134     task->remains = 0;
135     break;
136   case SD_FAILED:
137     task->state_set = sd_global->failed_task_set;
138     break;
139   default:
140     xbt_assert0(0, "Invalid state");
141   }
142   xbt_swag_insert(task, task->state_set);
143   task->state = new_state;
144
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 */
149   }
150 }
151
152 /**
153  * \brief Returns the name of a task
154  *
155  * \param task a task
156  * \return the name of this task (can be \c NULL)
157  */
158 const char* SD_task_get_name(SD_task_t task) {
159   SD_CHECK_INIT_DONE();
160   xbt_assert0(task != NULL, "Invalid parameter");
161   return task->name;
162 }
163
164 /**
165  * \brief Returns the total amount of a task
166  *
167  * \param task a task
168  * \return the total amount of this task
169  * \see SD_task_get_remaining_amount()
170  */
171 double SD_task_get_amount(SD_task_t task) {
172   SD_CHECK_INIT_DONE();
173   xbt_assert0(task != NULL, "Invalid parameter");
174   return task->amount;
175 }
176
177 /**
178  * \brief Returns the remaining amount of a task
179  *
180  * \param task a task
181  * \return the remaining amount of this task
182  * \see SD_task_get_amount()
183  */
184 double SD_task_get_remaining_amount(SD_task_t task) {
185   SD_CHECK_INIT_DONE();
186   xbt_assert0(task != NULL, "Invalid parameter");
187
188   if (task->surf_action)
189     return task->surf_action->remains;
190   else
191     return task->remains;
192 }
193
194 /* temporary function for debbuging */
195 static void __SD_print_dependencies(SD_task_t task) {
196   xbt_dynar_t dynar;
197   int length;
198   int i;
199   SD_dependency_t dependency;
200
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);
204
205
206   for (i = 0; i < length; i++) {
207     xbt_dynar_get_cpy(dynar, i, &dependency);
208     INFO1(" %s", SD_task_get_name(dependency->src));
209   }
210
211   INFO1("The following tasks must be executed after %s:", SD_task_get_name(task));
212
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));
218   }
219   INFO0("----------------------------");
220 }
221
222 /* Destroys a dependency between two tasks.
223  */
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);
228 }
229
230 /**
231  * \brief Adds a dependency between two tasks
232  *
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.
235  *
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()
241  */
242 void SD_task_dependency_add(const char *name, void *data, SD_task_t src, SD_task_t dst) {
243   xbt_dynar_t dynar;
244   int length;
245   int found = 0;
246   int i;
247   SD_dependency_t dependency;
248
249   SD_CHECK_INIT_DONE();
250   xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
251
252   dynar = src->tasks_after;
253   length = xbt_dynar_length(dynar);
254
255
256
257   if (src == dst) 
258     THROW1(arg_error, 0, "Cannot add a dependency between task '%s' and itself",
259            SD_task_get_name(src));
260
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));
263
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));
266
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));
272   }
273
274   if (found)
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));
277
278   dependency = xbt_new(s_SD_dependency_t, 1);
279
280   if (name != NULL)
281     dependency->name = xbt_strdup(name);
282   else
283     dependency->name = NULL;
284
285   dependency->data = data;
286   dependency->src = src;
287   dependency->dst = dst;
288
289   /* src must be executed before dst */
290   xbt_dynar_push(src->tasks_after, &dependency);
291   xbt_dynar_push(dst->tasks_before, &dependency);
292
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);
298   }
299
300   /*  __SD_print_dependencies(src);
301       __SD_print_dependencies(dst); */
302 }
303
304 /**
305  * \brief Indacates whether there is a dependency between two tasks.
306  *
307  * \param src a task
308  * \param dst a task depending on \a src
309  */
310 int SD_task_dependency_exists(SD_task_t src, SD_task_t dst) {
311   xbt_dynar_t dynar;
312   int length;
313   int i;
314   SD_dependency_t dependency;
315
316   SD_CHECK_INIT_DONE();
317   xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
318
319   dynar = src->tasks_after;
320   length = xbt_dynar_length(dynar);
321
322   for (i = 0; i < length; i++) {
323     xbt_dynar_get_cpy(dynar, i, &dependency);
324     if (dependency->dst == dst) return 1;
325   }
326   return 0;
327 }
328
329 /**
330  * \brief Remove a dependency between two tasks
331  *
332  * \param src a task
333  * \param dst a task depending on \a src
334  * \see SD_task_dependency_add()
335  */
336 void SD_task_dependency_remove(SD_task_t src, SD_task_t dst) {
337
338   xbt_dynar_t dynar;
339   int length;
340   int found = 0;
341   int i;
342   SD_dependency_t dependency;
343
344   SD_CHECK_INIT_DONE();
345   xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
346
347   /* remove the dependency from src->tasks_after */
348   dynar = src->tasks_after;
349   length = xbt_dynar_length(dynar);
350
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);
355       found = 1;
356     }
357   }
358   if (!found)
359     THROW4(arg_error, 0,
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));
362
363   /* remove the dependency from dst->tasks_before */
364   dynar = dst->tasks_before;
365   length = xbt_dynar_length(dynar);
366   found = 0;
367   
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);
373       found = 1;
374     }
375   }
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));
379
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);
383
384   /*  __SD_print_dependencies(src); 
385       __SD_print_dependencies(dst);*/
386 }
387
388 /**
389  * \brief Returns the user data associated with a dependency between two tasks
390  *
391  * \param src a task
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()
395  */
396 void *SD_task_dependency_get_data(SD_task_t src, SD_task_t dst) {
397
398   xbt_dynar_t dynar;
399   int length;
400   int found = 0;
401   int i;
402   SD_dependency_t dependency;
403
404
405   SD_CHECK_INIT_DONE();
406   xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
407
408   dynar = src->tasks_after;
409   length = xbt_dynar_length(dynar);
410
411   for (i = 0; i < length && !found; i++) {
412     xbt_dynar_get_cpy(dynar, i, &dependency);
413     found = (dependency->dst == dst);
414   }
415   if (!found)
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;
418 }
419
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"};
424   int i;
425
426   INFO2("Task '%s' watch points (%x): ", SD_task_get_name(task), task->watch_points);
427
428
429   for (i = 0; i < 5; i++) {
430     if (task->watch_points & state_masks[i])
431       INFO1("%s ", state_names[i]);
432   }
433 }
434
435 /**
436  * \brief Adds a watch point to a task
437  *
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.
441  * 
442  * \param task a task
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()
446  */
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");
450
451   if (state & SD_NOT_SCHEDULED)
452     THROW0(arg_error, 0, "Cannot add a watch point for state SD_NOT_SCHEDULED");
453
454   task->watch_points = task->watch_points | state;
455   /*  __SD_print_watch_points(task);*/
456 }
457
458 /**
459  * \brief Removes a watch point from a task
460  * 
461  * \param task a task
462  * \param state the \ref e_SD_task_state_t "state" you no longer want to watch
463  * \see SD_task_watch()
464  */
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");
470   
471   task->watch_points = task->watch_points & ~state;
472   /*  __SD_print_watch_points(task);*/
473 }
474
475 /**
476  * \brief Returns an approximative estimation of the execution time of a task.
477  * 
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.
480  * 
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
487  * \see SD_schedule()
488  */
489 double SD_task_get_execution_time(SD_task_t task,
490                                   int workstation_nb,
491                                   const SD_workstation_t *workstation_list,
492                                   const double *computation_amount,
493                                   const double *communication_amount,
494                                   double rate) {
495   double time, max_time = 0.0;
496   int i, j;
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");
501
502   /* the task execution time is the maximum execution time of the parallel tasks */
503
504   for (i = 0; i < workstation_nb; i++) {
505     time = SD_workstation_get_computation_time(workstation_list[i], computation_amount[i]);
506     
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]);
510     }
511
512     if (time > max_time) {
513       max_time = time;
514     }
515   }
516   return max_time * SD_task_get_amount(task);
517 }
518
519 /**
520  * \brief Schedules a task
521  *
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.
525  * 
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()
533  */
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) {
537
538   int communication_nb;
539   
540   SD_CHECK_INIT_DONE();
541   xbt_assert0(task != NULL, "Invalid parameter");
542   xbt_assert0(workstation_nb > 0, "workstation_nb must be positive");
543
544   if (!__SD_task_is_not_scheduled(task))
545     THROW1(arg_error, 0, "Task '%s' has already been scheduled", SD_task_get_name(task));
546
547   task->workstation_nb = workstation_nb;
548   task->rate = rate;
549
550   task->computation_amount = xbt_new(double, workstation_nb);
551   memcpy(task->computation_amount, computation_amount, sizeof(double) * workstation_nb);
552
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);
556
557   task->workstation_list = xbt_new(SD_workstation_t, workstation_nb);
558   memcpy(task->workstation_list, workstation_list, sizeof(SD_workstation_t) * workstation_nb);
559
560   /* update the task state */
561   if (xbt_dynar_length(task->tasks_before) == 0)
562     __SD_task_set_state(task, SD_READY);
563   else
564     __SD_task_set_state(task, SD_SCHEDULED);
565 }
566
567 /**
568  * \brief Unschedules a task
569  *
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.
573  *
574  * \param task the task you want to unschedule
575  * \see SD_task_schedule()
576  */
577 void SD_task_unschedule(SD_task_t task) {
578   SD_CHECK_INIT_DONE();
579   xbt_assert0(task != NULL, "Invalid parameter");
580
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));
587
588   if (__SD_task_is_scheduled_or_ready(task)) /* if the task is scheduled or ready */
589     __SD_task_destroy_scheduling_data(task);
590
591   if (__SD_task_is_running(task)) /* the task should become SD_FAILED */
592     surf_workstation_model->common_public->action_cancel(task->surf_action);
593   else
594     __SD_task_set_state(task, SD_NOT_SCHEDULED);
595   task->remains = task->amount;
596   task->start_time = -1.0;
597 }
598
599 /* Destroys the data memorised by SD_task_schedule. Task state must be SD_SCHEDULED or SD_READY.
600  */
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));
605
606   xbt_free(task->computation_amount);
607   xbt_free(task->communication_amount);
608 }
609
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.
613  */
614 void __SD_task_really_run(SD_task_t task) {
615
616   int i;
617   void **surf_workstations;
618
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));
624
625
626
627   DEBUG1("Really running task '%s'", SD_task_get_name(task));
628
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");
634     }
635   }
636   
637   DEBUG1("Task '%s' set as current task for its workstations", SD_task_get_name(task));
638
639   /* start the task */
640
641   /* we have to create a Surf workstation array instead of the SimDag workstation array */
642   surf_workstations = xbt_new(void*, task->workstation_nb);
643
644   for (i = 0; i < task->workstation_nb; i++) {
645     surf_workstations[i] = task->workstation_list[i]->surf_workstation;
646   }
647   
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)) {
661     int nb=0;
662     double value=0.0;
663     
664     for (i = 0; i < task->workstation_nb*task->workstation_nb; i++) {
665       if(task->communication_amount[i]>0.0) {
666         nb++;
667         value = task->communication_amount[i];
668       }
669     }
670     if(nb==1) {
671       task->surf_action = surf_workstation_model->extension_public->
672         communicate(surf_workstations[0], surf_workstations[1],
673                     value, task->rate);
674     }
675   }
676   if(!task->surf_action) 
677     task->surf_action = surf_workstation_model->extension_public->
678       execute_parallel_task(task->workstation_nb,
679                             surf_workstations,
680                             task->computation_amount,
681                             task->communication_amount,
682                             task->amount,
683                             task->rate);
684
685   surf_workstation_model->common_public->action_set_data(task->surf_action, task);
686   task->state_changed = 1;
687
688   DEBUG1("surf_action = %p",  task->surf_action);
689
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));
695
696 }
697
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.
703  */
704 int __SD_task_try_to_run(SD_task_t task) {
705
706   int can_start = 1;
707   int i;
708   SD_workstation_t workstation;
709
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));
714
715
716   for (i = 0; i < task->workstation_nb; i++) {
717     can_start = !__SD_workstation_is_busy(task->workstation_list[i]);
718   }
719
720   DEBUG2("Task '%s' can start: %d", SD_task_get_name(task), can_start);
721   
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);
729       }
730     }
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));
735   }
736   else {
737     __SD_task_really_run(task);
738   }
739
740   return can_start;
741 }
742
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'
746  */
747 void __SD_task_just_done(SD_task_t task) {
748    int i, j;
749   SD_workstation_t workstation;
750
751   SD_task_t candidate;
752   int candidate_nb = 0;
753   int candidate_capacity = 8;
754   SD_task_t *candidates;
755   int can_start = 1;
756
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));
761
762
763   candidates = xbt_new(SD_task_t, 8);
764
765   __SD_task_set_state(task, SD_DONE);
766   surf_workstation_model->common_public->action_free(task->surf_action);
767   task->surf_action = NULL;
768
769   DEBUG0("Looking for candidates");
770
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));
781
782       /* the task is over so we can release the workstation */
783       workstation->current_task = NULL;
784
785       DEBUG0("Getting candidate in fifo");
786       candidate = xbt_fifo_get_item_content(xbt_fifo_get_first_item(workstation->task_fifo));
787
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));
792       }
793
794       DEBUG1("Candidate in fifo: %p", candidate);
795
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. */
802
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);
807         }
808
809         /* register the candidate */
810         candidates[candidate_nb++] = candidate;
811         candidate->fifo_checked = 0;
812       }
813     }
814   }
815
816   DEBUG1("Candidates found: %d", candidate_nb);
817
818   /* now we check every candidate task */
819   for (i = 0; i < candidate_nb; i++) {
820     candidate = candidates[i];
821
822     if (candidate->fifo_checked) {
823       continue; /* we have already evaluated that task*/
824     }
825
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));
828
829     for (j = 0; j < candidate->workstation_nb && can_start; j++) {
830       workstation = candidate->workstation_list[j];
831
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));
836     }
837
838     DEBUG2("Candidate '%s' can start: %d", SD_task_get_name(candidate), can_start);
839
840     /* now we are sure that I can start! */
841     if (can_start) {
842       for (j = 0; j < candidate->workstation_nb && can_start; j++) {
843         workstation = candidate->workstation_list[j];
844
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");
850         }
851       } /* for each workstation */
852       
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);
856       
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.");
862
863     } /* can start */
864     candidate->fifo_checked = 1;
865   } /* for each candidate */ 
866   
867   xbt_free(candidates);
868 }
869
870 /* Remove all dependencies associated with a task. This function is called when the task is destroyed.
871  */
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);
879   }
880
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);
884   }
885 }
886
887 /**
888  * \brief Returns the start time of a task
889  *
890  * The task state must be SD_RUNNING, SD_DONE or SD_FAILED.
891  *
892  * \param task: a task
893  * \return the start time of this task
894  */
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);
900   else 
901     return task->start_time;
902 }
903
904 /**
905  * \brief Returns the finish time of a task
906  *
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.
911  *
912  * \param task: a task
913  * \return the start time of this task
914  */
915 double SD_task_get_finish_time(SD_task_t task) {
916   SD_CHECK_INIT_DONE();
917   xbt_assert0(task != NULL, "Invalid parameter");
918
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);
921   else 
922     return task->finish_time;
923 }
924
925 /**
926  * \brief Destroys a task.
927  *
928  * The user data (if any) should have been destroyed first.
929  *
930  * \param task the task you want to destroy
931  * \see SD_task_create()
932  */
933 void SD_task_destroy(SD_task_t task) {
934   SD_CHECK_INIT_DONE();
935   xbt_assert0(task != NULL, "Invalid parameter");
936
937   DEBUG1("Destroying task %s...", SD_task_get_name(task));
938
939   __SD_task_remove_dependencies(task);
940
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);
944
945   if (task->name != NULL)
946     xbt_free(task->name);
947
948   if (task->surf_action != NULL)
949     surf_workstation_model->common_public->action_free(task->surf_action);
950
951   if (task->workstation_list != NULL)
952     xbt_free(task->workstation_list);
953
954   xbt_dynar_free(&task->tasks_before);
955   xbt_dynar_free(&task->tasks_after);
956   xbt_free(task);
957
958   sd_global->task_number--;
959
960   DEBUG0("Task destroyed.");
961 }