Logo AND Algorithmique Numérique Distribuée

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