Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
c9db873fdf386271359d0c19ca6610347e64bcdc
[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     double *computation_amount = xbt_new(double, task->workstation_nb);
678     double *communication_amount = xbt_new(double, task->workstation_nb * 
679                                            task->workstation_nb);
680
681     memcpy(computation_amount, task->computation_amount, sizeof(double) * 
682            task->workstation_nb);
683     memcpy(communication_amount, task->communication_amount, 
684            sizeof(double) * task->workstation_nb * task->workstation_nb);
685
686     task->surf_action = surf_workstation_model->extension_public->
687       execute_parallel_task(task->workstation_nb,
688                             surf_workstations,
689                             computation_amount,
690                             communication_amount,
691                             task->amount,
692                             task->rate);
693   } else {
694     xbt_free(surf_workstations);
695   }
696
697   surf_workstation_model->common_public->action_set_data(task->surf_action, task);
698   task->state_changed = 1;
699
700   DEBUG1("surf_action = %p",  task->surf_action);
701
702   __SD_task_destroy_scheduling_data(task); /* now the scheduling data are not useful anymore */
703   __SD_task_set_state(task, SD_RUNNING);
704   xbt_assert2(__SD_task_is_running(task), "Bad state of task '%s': %d",
705               SD_task_get_name(task), SD_task_get_state(task));
706
707 }
708
709 /* Tries to run a task. This function is called by SD_simulate() when a scheduled task becomes SD_READY
710  * (ie when its dependencies are satisfied).
711  * If one of the workstations where the task is scheduled on is busy (in sequential mode),
712  * the task doesn't start.
713  * Returns whether the task has started.
714  */
715 int __SD_task_try_to_run(SD_task_t task) {
716
717   int can_start = 1;
718   int i;
719   SD_workstation_t workstation;
720
721   SD_CHECK_INIT_DONE();
722   xbt_assert0(task != NULL, "Invalid parameter");
723   xbt_assert2(__SD_task_is_ready(task), "Task '%s' is not ready! Task state: %d",
724            SD_task_get_name(task), SD_task_get_state(task));
725
726
727   for (i = 0; i < task->workstation_nb; i++) {
728     can_start = !__SD_workstation_is_busy(task->workstation_list[i]);
729   }
730
731   DEBUG2("Task '%s' can start: %d", SD_task_get_name(task), can_start);
732   
733   if (!can_start) { /* if the task cannot start and is not in the fifos yet*/
734     for (i = 0; i < task->workstation_nb; i++) {
735       workstation = task->workstation_list[i];
736       if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
737         DEBUG2("Pushing task '%s' in the fifo of workstation '%s'", SD_task_get_name(task),
738                SD_workstation_get_name(workstation));
739         xbt_fifo_push(workstation->task_fifo, task);
740       }
741     }
742     __SD_task_set_state(task, SD_IN_FIFO);
743     xbt_assert2(__SD_task_is_in_fifo(task), "Bad state of task '%s': %d",
744                 SD_task_get_name(task), SD_task_get_state(task));
745     DEBUG1("Task '%s' state is now SD_IN_FIFO", SD_task_get_name(task));
746   }
747   else {
748     __SD_task_really_run(task);
749   }
750
751   return can_start;
752 }
753
754 /* This function is called by SD_simulate when a task is done.
755  * It updates task->state and task->action and executes if necessary the tasks
756  * which were waiting in fifos for the end of `task'
757  */
758 void __SD_task_just_done(SD_task_t task) {
759    int i, j;
760   SD_workstation_t workstation;
761
762   SD_task_t candidate;
763   int candidate_nb = 0;
764   int candidate_capacity = 8;
765   SD_task_t *candidates;
766   int can_start = 1;
767
768   SD_CHECK_INIT_DONE();
769   xbt_assert0(task != NULL, "Invalid parameter");
770   xbt_assert1(__SD_task_is_running(task), "The task must be running! Task state: %d", SD_task_get_state(task));
771   xbt_assert1(task->workstation_list != NULL, "Task '%s': workstation_list is NULL!", SD_task_get_name(task));
772
773
774   candidates = xbt_new(SD_task_t, 8);
775
776   __SD_task_set_state(task, SD_DONE);
777   surf_workstation_model->common_public->action_free(task->surf_action);
778   task->surf_action = NULL;
779
780   DEBUG0("Looking for candidates");
781
782   /* if the task was executed on sequential workstations,
783      maybe we can execute the next task of the fifo for each workstation */
784   for (i = 0; i < task->workstation_nb; i++) {
785     workstation = task->workstation_list[i];
786     DEBUG2("Workstation '%s': access_mode = %d", SD_workstation_get_name(workstation), workstation->access_mode);
787     if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
788       xbt_assert1(workstation->task_fifo != NULL, "Workstation '%s' has sequential access but no fifo!",
789                   SD_workstation_get_name(workstation));
790       xbt_assert2(workstation->current_task = task, "Workstation '%s': current task should be '%s'",
791                   SD_workstation_get_name(workstation), SD_task_get_name(task));
792
793       /* the task is over so we can release the workstation */
794       workstation->current_task = NULL;
795
796       DEBUG0("Getting candidate in fifo");
797       candidate = xbt_fifo_get_item_content(xbt_fifo_get_first_item(workstation->task_fifo));
798
799       if (candidate != NULL) {
800         DEBUG1("Candidate: '%s'", SD_task_get_name(candidate));
801         xbt_assert2(__SD_task_is_in_fifo(candidate), "Bad state of candidate '%s': %d",
802                     SD_task_get_name(candidate), SD_task_get_state(candidate));
803       }
804
805       DEBUG1("Candidate in fifo: %p", candidate);
806
807       /* if there was a task waiting for my place */
808       if (candidate != NULL) {
809         /* Unfortunately, we are not sure yet that we can execute the task now,
810            because the task can be waiting more deeply in some other workstation's fifos...
811            So we memorize all candidate tasks, and then we will check for each candidate
812            whether or not all its workstations are available. */
813
814         /* realloc if necessary */
815         if (candidate_nb == candidate_capacity) {
816           candidate_capacity *= 2;
817           candidates = xbt_realloc(candidates, sizeof(SD_task_t) * candidate_capacity);
818         }
819
820         /* register the candidate */
821         candidates[candidate_nb++] = candidate;
822         candidate->fifo_checked = 0;
823       }
824     }
825   }
826
827   DEBUG1("Candidates found: %d", candidate_nb);
828
829   /* now we check every candidate task */
830   for (i = 0; i < candidate_nb; i++) {
831     candidate = candidates[i];
832
833     if (candidate->fifo_checked) {
834       continue; /* we have already evaluated that task*/
835     }
836
837     xbt_assert2(__SD_task_is_in_fifo(candidate), "Bad state of candidate '%s': %d",
838                 SD_task_get_name(candidate), SD_task_get_state(candidate));
839
840     for (j = 0; j < candidate->workstation_nb && can_start; j++) {
841       workstation = candidate->workstation_list[j];
842
843       /* I can start on this workstation if the workstation is shared
844          or if I am the first task in the fifo */
845       can_start = workstation->access_mode == SD_WORKSTATION_SHARED_ACCESS ||
846         candidate == xbt_fifo_get_item_content(xbt_fifo_get_first_item(workstation->task_fifo));
847     }
848
849     DEBUG2("Candidate '%s' can start: %d", SD_task_get_name(candidate), can_start);
850
851     /* now we are sure that I can start! */
852     if (can_start) {
853       for (j = 0; j < candidate->workstation_nb && can_start; j++) {
854         workstation = candidate->workstation_list[j];
855
856         /* update the fifo */
857         if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
858           candidate = xbt_fifo_shift(workstation->task_fifo); /* the return value is stored just for debugging */
859           DEBUG1("Head of the fifo: '%s'", (candidate != NULL) ? SD_task_get_name(candidate) : "NULL");
860           xbt_assert0(candidate == candidates[i], "Error in __SD_task_just_done: bad first task in the fifo");
861         }
862       } /* for each workstation */
863       
864       /* finally execute the task */
865       DEBUG2("Task '%s' state: %d", SD_task_get_name(candidate), SD_task_get_state(candidate));
866       __SD_task_really_run(candidate);
867       
868       DEBUG4("Calling __SD_task_is_running: task '%s', state set: %p, running_task_set: %p, is running: %d",
869              SD_task_get_name(candidate), candidate->state_set, sd_global->running_task_set, __SD_task_is_running(candidate));
870       xbt_assert2(__SD_task_is_running(candidate), "Bad state of task '%s': %d",
871                   SD_task_get_name(candidate), SD_task_get_state(candidate));
872       DEBUG0("Okay, the task is running.");
873
874     } /* can start */
875     candidate->fifo_checked = 1;
876   } /* for each candidate */ 
877   
878   xbt_free(candidates);
879 }
880
881 /* Remove all dependencies associated with a task. This function is called when the task is destroyed.
882  */
883 static void __SD_task_remove_dependencies(SD_task_t task) {
884   /* we must destroy the dependencies carefuly (with SD_dependency_remove)
885      because each one is stored twice */
886   SD_dependency_t dependency;
887   while (xbt_dynar_length(task->tasks_before) > 0) {
888     xbt_dynar_get_cpy(task->tasks_before, 0, &dependency);
889     SD_task_dependency_remove(dependency->src, dependency->dst);
890   }
891
892   while (xbt_dynar_length(task->tasks_after) > 0) {
893     xbt_dynar_get_cpy(task->tasks_after, 0, &dependency);
894     SD_task_dependency_remove(dependency->src, dependency->dst);
895   }
896 }
897
898 /**
899  * \brief Returns the start time of a task
900  *
901  * The task state must be SD_RUNNING, SD_DONE or SD_FAILED.
902  *
903  * \param task: a task
904  * \return the start time of this task
905  */
906 double SD_task_get_start_time(SD_task_t task) {
907   SD_CHECK_INIT_DONE();
908   xbt_assert0(task != NULL, "Invalid parameter");
909   if(task->surf_action)
910     return surf_workstation_model->common_public->action_get_start_time(task->surf_action);
911   else 
912     return task->start_time;
913 }
914
915 /**
916  * \brief Returns the finish time of a task
917  *
918  * The task state must be SD_RUNNING, SD_DONE or SD_FAILED.
919  * If the state is not completed yet, the returned value is an
920  * estimation of the task finish time. This value can fluctuate 
921  * until the task is completed.
922  *
923  * \param task: a task
924  * \return the start time of this task
925  */
926 double SD_task_get_finish_time(SD_task_t task) {
927   SD_CHECK_INIT_DONE();
928   xbt_assert0(task != NULL, "Invalid parameter");
929
930   if(task->surf_action) /* should never happen as actions are destroyed right after their completion */
931     return surf_workstation_model->common_public->action_get_finish_time(task->surf_action);
932   else 
933     return task->finish_time;
934 }
935
936 /**
937  * \brief Destroys a task.
938  *
939  * The user data (if any) should have been destroyed first.
940  *
941  * \param task the task you want to destroy
942  * \see SD_task_create()
943  */
944 void SD_task_destroy(SD_task_t task) {
945   SD_CHECK_INIT_DONE();
946   xbt_assert0(task != NULL, "Invalid parameter");
947
948   DEBUG1("Destroying task %s...", SD_task_get_name(task));
949
950   __SD_task_remove_dependencies(task);
951
952   /* if the task was scheduled or ready we have to free the scheduling parameters */
953   if (__SD_task_is_scheduled_or_ready(task))
954     __SD_task_destroy_scheduling_data(task);
955
956   if (task->name != NULL)
957     xbt_free(task->name);
958
959   if (task->surf_action != NULL)
960     surf_workstation_model->common_public->action_free(task->surf_action);
961
962   if (task->workstation_list != NULL)
963     xbt_free(task->workstation_list);
964
965   xbt_dynar_free(&task->tasks_before);
966   xbt_dynar_free(&task->tasks_after);
967   xbt_free(task);
968
969   sd_global->task_number--;
970
971   DEBUG0("Task destroyed.");
972 }