Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Picky gcc.
[simgrid.git] / src / simdag / sd_task.c
1 /* Copyright (c) 2007-2009 Da SimGrid Team.  All rights reserved.           */
2
3 /* This program is free software; you can redistribute it and/or modify it
4  * under the terms of the license (GNU LGPL) which comes with this package. */
5
6 #include "private.h"
7 #include "simdag/simdag.h"
8 #include "xbt/sysdep.h"
9 #include "xbt/dynar.h"
10
11 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(sd_task, sd,
12                                 "Logging specific to SimDag (task)");
13
14 static void __SD_task_remove_dependencies(SD_task_t task);
15 static void __SD_task_destroy_scheduling_data(SD_task_t task);
16
17 /**
18  * \brief Creates a new task.
19  *
20  * \param name the name of the task (can be \c NULL)
21  * \param data the user data you want to associate with the task (can be \c NULL)
22  * \param amount amount of the task
23  * \return the new task
24  * \see SD_task_destroy()
25  */
26 SD_task_t SD_task_create(const char *name, void *data, double amount)
27 {
28
29   SD_task_t task;
30   SD_CHECK_INIT_DONE();
31
32   task = xbt_new(s_SD_task_t, 1);
33
34   /* general information */
35   task->data = data;            /* user data */
36   task->name = xbt_strdup(name);
37   task->kind = SD_TASK_NOT_TYPED;
38   task->state_hookup.prev = NULL;
39   task->state_hookup.next = NULL;
40   task->state_set = sd_global->not_scheduled_task_set;
41   task->state = SD_NOT_SCHEDULED;
42   xbt_swag_insert(task, task->state_set);
43
44   task->amount = amount;
45   task->remains = amount;
46   task->start_time = -1.0;
47   task->finish_time = -1.0;
48   task->surf_action = NULL;
49   task->watch_points = 0;
50
51   /* dependencies */
52   task->tasks_before = xbt_dynar_new(sizeof(SD_dependency_t), NULL);
53   task->tasks_after = xbt_dynar_new(sizeof(SD_dependency_t), NULL);
54
55   /* scheduling parameters */
56   task->workstation_nb = 0;
57   task->workstation_list = NULL;
58   task->computation_amount = NULL;
59   task->communication_amount = NULL;
60   task->rate = 0;
61
62   sd_global->task_number++;
63
64   return task;
65 }
66
67 /**
68  * \brief Returns the user data of a task
69  *
70  * \param task a task
71  * \return the user data associated with this task (can be \c NULL)
72  * \see SD_task_set_data()
73  */
74 void *SD_task_get_data(SD_task_t task)
75 {
76   SD_CHECK_INIT_DONE();
77   xbt_assert0(task != NULL, "Invalid parameter");
78   return task->data;
79 }
80
81 /**
82  * \brief Sets the user data of a task
83  *
84  * The new data can be \c NULL. The old data should have been freed first
85  * if it was not \c NULL.
86  *
87  * \param task a task
88  * \param data the new data you want to associate with this task
89  * \see SD_task_get_data()
90  */
91 void SD_task_set_data(SD_task_t task, void *data)
92 {
93   SD_CHECK_INIT_DONE();
94   xbt_assert0(task != NULL, "Invalid parameter");
95   task->data = data;
96 }
97
98 /**
99  * \brief Returns the state of a task
100  *
101  * \param task a task
102  * \return the current \ref e_SD_task_state_t "state" of this task:
103  * #SD_NOT_SCHEDULED, #SD_SCHEDULED, #SD_READY, #SD_RUNNING, #SD_DONE or #SD_FAILED
104  * \see e_SD_task_state_t
105  */
106 e_SD_task_state_t SD_task_get_state(SD_task_t task)
107 {
108   SD_CHECK_INIT_DONE();
109   xbt_assert0(task != NULL, "Invalid parameter");
110   return task->state;
111 }
112
113 /* Changes the state of a task. Updates the swags and the flag sd_global->watch_point_reached.
114  */
115 void __SD_task_set_state(SD_task_t task, e_SD_task_state_t new_state)
116 {
117   xbt_swag_remove(task, task->state_set);
118   switch (new_state) {
119   case SD_NOT_SCHEDULED:
120     task->state_set = sd_global->not_scheduled_task_set;
121     break;
122   case SD_SCHEDULED:
123     task->state_set = sd_global->scheduled_task_set;
124     break;
125   case SD_READY:
126     task->state_set = sd_global->ready_task_set;
127     break;
128   case SD_IN_FIFO:
129     task->state_set = sd_global->in_fifo_task_set;
130     break;
131   case SD_RUNNING:
132     task->state_set = sd_global->running_task_set;
133     task->start_time =
134       surf_workstation_model->action_get_start_time(task->surf_action);
135     break;
136   case SD_DONE:
137     task->state_set = sd_global->done_task_set;
138     task->finish_time =
139       surf_workstation_model->action_get_finish_time(task->surf_action);
140     task->remains = 0;
141     break;
142   case SD_FAILED:
143     task->state_set = sd_global->failed_task_set;
144     break;
145   default:
146     xbt_assert0(0, "Invalid state");
147   }
148   xbt_swag_insert(task, task->state_set);
149   task->state = new_state;
150
151   if (task->watch_points & new_state) {
152     INFO1("Watch point reached with task '%s'!", SD_task_get_name(task));
153     sd_global->watch_point_reached = 1;
154     SD_task_unwatch(task, new_state);   /* remove the watch point */
155   }
156 }
157
158 /**
159  * \brief Returns the name of a task
160  *
161  * \param task a task
162  * \return the name of this task (can be \c NULL)
163  */
164 const char *SD_task_get_name(SD_task_t task)
165 {
166   SD_CHECK_INIT_DONE();
167   xbt_assert0(task != NULL, "Invalid parameter");
168   return task->name;
169 }
170
171 /** @brief Returns the dynar of the parents of a task
172  *
173  * \param task a task
174  * \return a newly allocated dynar comprising the parents of this task
175  */
176
177 xbt_dynar_t SD_task_get_parents(SD_task_t task)
178 {
179   unsigned int i;
180   xbt_dynar_t parents;
181   SD_dependency_t dep;
182   SD_CHECK_INIT_DONE();
183   xbt_assert0(task != NULL, "Invalid parameter");
184
185   parents = xbt_dynar_new(sizeof(SD_task_t), NULL);
186   xbt_dynar_foreach(task->tasks_before, i, dep){
187     xbt_dynar_push(parents, &(dep->src));
188   }
189   return parents;
190 }
191
192 /** @brief Returns the dynar of the parents of a task
193  *
194  * \param task a task
195  * \return a newly allocated dynar comprising the parents of this task
196  */
197 xbt_dynar_t SD_task_get_children(SD_task_t task)
198 {
199   unsigned int i;
200   xbt_dynar_t children;
201   SD_dependency_t dep;
202   SD_CHECK_INIT_DONE();
203   xbt_assert0(task != NULL, "Invalid parameter");
204
205   children = xbt_dynar_new(sizeof(SD_task_t), NULL);
206   xbt_dynar_foreach(task->tasks_after, i, dep){
207     xbt_dynar_push(children, &(dep->dst));
208   }
209   return children;
210 }
211
212 /**
213  * \brief Returns the amount of workstations involved in a task
214  *
215  * Only call this on already scheduled tasks!
216  * \param task a task
217  */
218 int SD_task_get_workstation_count(SD_task_t task)
219 {
220   SD_CHECK_INIT_DONE();
221   xbt_assert0(task != NULL, "Invalid parameter");
222   //  xbt_assert1( task->state_set != sd_global->scheduled_task_set, 
223   //           "Unscheduled task %s", task->name);
224   return task->workstation_nb;
225 }
226
227 /**
228  * \brief Returns the list of workstations involved in a task
229  *
230  * Only call this on already scheduled tasks!
231  * \param task a task
232  */
233 SD_workstation_t* SD_task_get_workstation_list(SD_task_t task)
234 {
235   SD_CHECK_INIT_DONE();
236   xbt_assert0(task != NULL, "Invalid parameter");
237   //xbt_assert1( task->state_set != sd_global->scheduled_task_set, 
238   //           "Unscheduled task %s", task->name);
239   return task->workstation_list;
240 }
241
242 /**
243  * \brief Returns the total amount of work contained in a task
244  *
245  * \param task a task
246  * \return the total amount of work (computation or data transfer) for this task
247  * \see SD_task_get_remaining_amount()
248  */
249 double SD_task_get_amount(SD_task_t task)
250 {
251   SD_CHECK_INIT_DONE();
252   xbt_assert0(task != NULL, "Invalid parameter");
253   return task->amount;
254 }
255
256 /**
257  * \brief Returns the remaining amount work to do till the completion of a task
258  *
259  * \param task a task
260  * \return the remaining amount of work (computation or data transfer) of this task
261  * \see SD_task_get_amount()
262  */
263 double SD_task_get_remaining_amount(SD_task_t task)
264 {
265   SD_CHECK_INIT_DONE();
266   xbt_assert0(task != NULL, "Invalid parameter");
267
268   if (task->surf_action)
269     return surf_workstation_model->get_remains(task->surf_action);
270   else
271     return task->remains;
272 }
273
274 int SD_task_get_kind(SD_task_t task) {
275   return task->kind;
276 }
277
278 /** @brief Displays debugging informations about a task */
279 void SD_task_dump(SD_task_t task)
280 {
281   unsigned int counter;
282   SD_dependency_t dependency;
283   char *statename;
284
285   INFO1("Displaying task %s",SD_task_get_name(task));
286   statename=bprintf("%s %s %s %s %s %s %s",
287       (task->state&SD_NOT_SCHEDULED?"not scheduled":""),
288       (task->state&SD_SCHEDULED?"scheduled":""),
289       (task->state&SD_READY?"ready":"not ready"),
290       (task->state&SD_IN_FIFO?"in fifo":""),
291       (task->state&SD_RUNNING?"running":""),
292       (task->state&SD_DONE?"done":""),
293       (task->state&SD_FAILED?"failed":""));
294   INFO1("  - state: %s",statename);
295   free(statename);
296
297   if (task->kind!=0) {
298     switch(task->kind){
299     case SD_TASK_COMM_E2E:
300       INFO0("  - kind: end-to-end communication");
301       break;
302     case SD_TASK_COMP_SEQ:
303       INFO0("  - kind: sequential computation");
304       break;
305     default:
306       INFO1("  - (unknown kind %d)",task->kind);
307     }
308   }
309   INFO1("  - amount: %.0f",SD_task_get_amount(task));
310   if (xbt_dynar_length(task->tasks_before)) {
311     INFO0("  - pre-dependencies:");
312     xbt_dynar_foreach(task->tasks_before,counter,dependency) {
313       INFO1("    %s",SD_task_get_name(dependency->src));
314     }
315   }
316   if (xbt_dynar_length(task->tasks_after)) {
317     INFO0("  - post-dependencies:");
318     xbt_dynar_foreach(task->tasks_after,counter,dependency) {
319       INFO1("    %s",SD_task_get_name(dependency->dst));
320     }
321   }
322 }
323 /** @brief Dumps the task in dotty formalism into the FILE* passed as second argument */
324 void SD_task_dotty(SD_task_t task,void* out) {
325   unsigned int counter;
326   SD_dependency_t dependency;
327   fprintf(out, "  T%p [label=\"%.20s\"",task, task->name);
328   switch(task->kind){
329     case SD_TASK_COMM_E2E:
330       fprintf(out,", shape=box");
331       break;
332     case SD_TASK_COMP_SEQ:
333       fprintf(out,", shape=circle");
334       break;
335     default:
336       xbt_die("Unknown task type!");
337   }
338   fprintf(out,"];\n");
339   xbt_dynar_foreach(task->tasks_before,counter,dependency) {
340     fprintf(out," T%p -> T%p;\n",dependency->src, dependency->dst);
341   }
342 }
343
344 /* Destroys a dependency between two tasks.
345  */
346 static void __SD_task_dependency_destroy(void *dependency)
347 {
348   if (((SD_dependency_t) dependency)->name != NULL)
349     xbt_free(((SD_dependency_t) dependency)->name);
350   xbt_free(dependency);
351 }
352
353 /**
354  * \brief Adds a dependency between two tasks
355  *
356  * \a dst will depend on \a src, ie \a dst will not start before \a src is finished.
357  * Their \ref e_SD_task_state_t "state" must be #SD_NOT_SCHEDULED, #SD_SCHEDULED or #SD_READY.
358  *
359  * \param name the name of the new dependency (can be \c NULL)
360  * \param data the user data you want to associate with this dependency (can be \c NULL)
361  * \param src the task which must be executed first
362  * \param dst the task you want to make depend on \a src
363  * \see SD_task_dependency_remove()
364  */
365 void SD_task_dependency_add(const char *name, void *data, SD_task_t src,
366                             SD_task_t dst)
367 {
368   xbt_dynar_t dynar;
369   int length;
370   int found = 0;
371   int i;
372   SD_dependency_t dependency;
373
374   SD_CHECK_INIT_DONE();
375   xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
376
377   dynar = src->tasks_after;
378   length = xbt_dynar_length(dynar);
379
380   if (src == dst)
381     THROW1(arg_error, 0,
382            "Cannot add a dependency between task '%s' and itself",
383            SD_task_get_name(src));
384
385   if (!__SD_task_is_not_scheduled(src)
386       && !__SD_task_is_scheduled_or_ready(src))
387     THROW1(arg_error, 0,
388            "Task '%s' must be SD_NOT_SCHEDULED, SD_SCHEDULED or SD_READY",
389            SD_task_get_name(src));
390
391   if (!__SD_task_is_not_scheduled(dst)
392       && !__SD_task_is_scheduled_or_ready(dst))
393     THROW1(arg_error, 0,
394            "Task '%s' must be SD_NOT_SCHEDULED, SD_SCHEDULED or SD_READY",
395            SD_task_get_name(dst));
396
397   DEBUG2("SD_task_dependency_add: src = %s, dst = %s", SD_task_get_name(src),
398          SD_task_get_name(dst));
399   for (i = 0; i < length && !found; i++) {
400     xbt_dynar_get_cpy(dynar, i, &dependency);
401     found = (dependency->dst == dst);
402     DEBUG2("Dependency %d: dependency->dst = %s", i,
403            SD_task_get_name(dependency->dst));
404   }
405
406   if (found)
407     THROW2(arg_error, 0,
408            "A dependency already exists between task '%s' and task '%s'",
409            SD_task_get_name(src), SD_task_get_name(dst));
410
411   dependency = xbt_new(s_SD_dependency_t, 1);
412
413   dependency->name = xbt_strdup(name); /* xbt_strdup is cleaver enough to deal with NULL args itself */
414   dependency->data = data;
415   dependency->src = src;
416   dependency->dst = dst;
417
418   /* src must be executed before dst */
419   xbt_dynar_push(src->tasks_after, &dependency);
420   xbt_dynar_push(dst->tasks_before, &dependency);
421
422   /* if the task was ready, then dst->tasks_before is not empty anymore,
423      so we must go back to state SD_SCHEDULED */
424   if (__SD_task_is_ready(dst)) {
425     DEBUG1("SD_task_dependency_add: %s was ready and becomes scheduled!",
426            SD_task_get_name(dst));
427     __SD_task_set_state(dst, SD_SCHEDULED);
428   }
429
430   /*  __SD_print_dependencies(src);
431      __SD_print_dependencies(dst); */
432 }
433
434 /**
435  * \brief Indacates whether there is a dependency between two tasks.
436  *
437  * \param src a task
438  * \param dst a task depending on \a src
439  *
440  * If src is NULL, checks whether dst has any pre-dependency.
441  * If dst is NULL, checks whether src has any post-dependency.
442  */
443 int SD_task_dependency_exists(SD_task_t src, SD_task_t dst)
444 {
445   unsigned int counter;
446   SD_dependency_t dependency;
447
448   SD_CHECK_INIT_DONE();
449   xbt_assert0(src != NULL || dst != NULL, "Invalid parameter: both src and dst are NULL");
450
451   if (src) {
452     if (dst) {
453       xbt_dynar_foreach(src->tasks_after,counter,dependency) {
454         if (dependency->dst == dst)
455           return 1;
456       }
457     } else {
458       return xbt_dynar_length(src->tasks_after);
459     }
460   } else {
461     return xbt_dynar_length(dst->tasks_before);
462   }
463   return 0;
464 }
465
466 /**
467  * \brief Remove a dependency between two tasks
468  *
469  * \param src a task
470  * \param dst a task depending on \a src
471  * \see SD_task_dependency_add()
472  */
473 void SD_task_dependency_remove(SD_task_t src, SD_task_t dst)
474 {
475
476   xbt_dynar_t dynar;
477   int length;
478   int found = 0;
479   int i;
480   SD_dependency_t dependency;
481
482   SD_CHECK_INIT_DONE();
483   xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
484
485   /* remove the dependency from src->tasks_after */
486   dynar = src->tasks_after;
487   length = xbt_dynar_length(dynar);
488
489   for (i = 0; i < length && !found; i++) {
490     xbt_dynar_get_cpy(dynar, i, &dependency);
491     if (dependency->dst == dst) {
492       xbt_dynar_remove_at(dynar, i, NULL);
493       found = 1;
494     }
495   }
496   if (!found)
497     THROW4(arg_error, 0,
498            "No dependency found between task '%s' and '%s': task '%s' is not a successor of task '%s'",
499            SD_task_get_name(src), SD_task_get_name(dst),
500            SD_task_get_name(dst), SD_task_get_name(src));
501
502   /* remove the dependency from dst->tasks_before */
503   dynar = dst->tasks_before;
504   length = xbt_dynar_length(dynar);
505   found = 0;
506
507   for (i = 0; i < length && !found; i++) {
508     xbt_dynar_get_cpy(dynar, i, &dependency);
509     if (dependency->src == src) {
510       xbt_dynar_remove_at(dynar, i, NULL);
511       __SD_task_dependency_destroy(dependency);
512       found = 1;
513     }
514   }
515   /* should never happen... */
516   xbt_assert4(found,
517               "SimDag error: task '%s' is a successor of '%s' but task '%s' is not a predecessor of task '%s'",
518               SD_task_get_name(dst), SD_task_get_name(src),
519               SD_task_get_name(src), SD_task_get_name(dst));
520
521   /* if the task was scheduled and dst->tasks_before is empty now, we can make it ready */
522   if (xbt_dynar_length(dst->tasks_before) == 0 && __SD_task_is_scheduled(dst))
523     __SD_task_set_state(dst, SD_READY);
524
525   /*  __SD_print_dependencies(src);
526      __SD_print_dependencies(dst); */
527 }
528
529 /**
530  * \brief Returns the user data associated with a dependency between two tasks
531  *
532  * \param src a task
533  * \param dst a task depending on \a src
534  * \return the user data associated with this dependency (can be \c NULL)
535  * \see SD_task_dependency_add()
536  */
537 void *SD_task_dependency_get_data(SD_task_t src, SD_task_t dst)
538 {
539
540   xbt_dynar_t dynar;
541   int length;
542   int found = 0;
543   int i;
544   SD_dependency_t dependency;
545
546
547   SD_CHECK_INIT_DONE();
548   xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
549
550   dynar = src->tasks_after;
551   length = xbt_dynar_length(dynar);
552
553   for (i = 0; i < length && !found; i++) {
554     xbt_dynar_get_cpy(dynar, i, &dependency);
555     found = (dependency->dst == dst);
556   }
557   if (!found)
558     THROW2(arg_error, 0, "No dependency found between task '%s' and '%s'",
559            SD_task_get_name(src), SD_task_get_name(dst));
560   return dependency->data;
561 }
562
563 /* temporary function for debugging */
564 static void __SD_print_watch_points(SD_task_t task)
565 {
566   static const int state_masks[] =
567     { SD_SCHEDULED, SD_RUNNING, SD_READY, SD_DONE, SD_FAILED };
568   static const char *state_names[] =
569     { "scheduled", "running", "ready", "done", "failed" };
570   int i;
571
572   INFO2("Task '%s' watch points (%x): ", SD_task_get_name(task),
573         task->watch_points);
574
575
576   for (i = 0; i < 5; i++) {
577     if (task->watch_points & state_masks[i])
578       INFO1("%s ", state_names[i]);
579   }
580 }
581
582 /**
583  * \brief Adds a watch point to a task
584  *
585  * SD_simulate() will stop as soon as the \ref e_SD_task_state_t "state" of this
586  * task becomes the one given in argument. The
587  * watch point is then automatically removed.
588  *
589  * \param task a task
590  * \param state the \ref e_SD_task_state_t "state" you want to watch
591  * (cannot be #SD_NOT_SCHEDULED)
592  * \see SD_task_unwatch()
593  */
594 void SD_task_watch(SD_task_t task, e_SD_task_state_t state)
595 {
596   SD_CHECK_INIT_DONE();
597   xbt_assert0(task != NULL, "Invalid parameter");
598
599   if (state & SD_NOT_SCHEDULED)
600     THROW0(arg_error, 0,
601            "Cannot add a watch point for state SD_NOT_SCHEDULED");
602
603   task->watch_points = task->watch_points | state;
604   /*  __SD_print_watch_points(task); */
605 }
606
607 /**
608  * \brief Removes a watch point from a task
609  *
610  * \param task a task
611  * \param state the \ref e_SD_task_state_t "state" you no longer want to watch
612  * \see SD_task_watch()
613  */
614 void SD_task_unwatch(SD_task_t task, e_SD_task_state_t state)
615 {
616   SD_CHECK_INIT_DONE();
617   xbt_assert0(task != NULL, "Invalid parameter");
618   xbt_assert0(state != SD_NOT_SCHEDULED,
619               "SimDag error: Cannot have a watch point for state SD_NOT_SCHEDULED");
620
621   task->watch_points = task->watch_points & ~state;
622   /*  __SD_print_watch_points(task); */
623 }
624
625 /**
626  * \brief Returns an approximative estimation of the execution time of a task.
627  *
628  * The estimation is very approximative because the value returned is the time
629  * the task would take if it was executed now and if it was the only task.
630  *
631  * \param task the task to evaluate
632  * \param workstation_nb number of workstations on which the task would be executed
633  * \param workstation_list the workstations on which the task would be executed
634  * \param computation_amount computation amount for each workstation
635  * \param communication_amount communication amount between each pair of workstations
636  * \see SD_schedule()
637  */
638 double SD_task_get_execution_time(SD_task_t task,
639                                   int workstation_nb,
640                                   const SD_workstation_t * workstation_list,
641                                   const double *computation_amount,
642                                   const double *communication_amount)
643 {
644   double time, max_time = 0.0;
645   int i, j;
646   SD_CHECK_INIT_DONE();
647   xbt_assert0(task != NULL && workstation_nb > 0 && workstation_list != NULL,
648               "Invalid parameter");
649
650   /* the task execution time is the maximum execution time of the parallel tasks */
651
652   for (i = 0; i < workstation_nb; i++) {
653     time = 0.0;
654     if (computation_amount != NULL)
655       time =
656           SD_workstation_get_computation_time(workstation_list[i],
657               computation_amount[i]);
658
659     if (communication_amount != NULL)
660       for (j = 0; j < workstation_nb; j++) {
661         time +=
662             SD_route_get_communication_time(workstation_list[i],
663                 workstation_list[j],
664                 communication_amount[i *
665                                      workstation_nb +
666                                      j]);
667       }
668
669     if (time > max_time) {
670       max_time = time;
671     }
672   }
673   return max_time;
674 }
675 static inline void SD_task_do_schedule(SD_task_t task) {
676   SD_CHECK_INIT_DONE();
677
678    if (!__SD_task_is_not_scheduled(task))
679      THROW1(arg_error, 0, "Task '%s' has already been scheduled",
680             SD_task_get_name(task));
681
682  /* update the task state */
683   if (xbt_dynar_length(task->tasks_before) == 0)
684     __SD_task_set_state(task, SD_READY);
685   else
686     __SD_task_set_state(task, SD_SCHEDULED);
687 }
688
689 /**
690  * \brief Schedules a task
691  *
692  * The task state must be #SD_NOT_SCHEDULED.
693  * Once scheduled, a task will be executed as soon as possible in SD_simulate(),
694  * i.e. when its dependencies are satisfied.
695  *
696  * \param task the task you want to schedule
697  * \param workstation_nb number of workstations on which the task will be executed
698  * \param workstation_list the workstations on which the task will be executed
699  * \param computation_amount computation amount for each workstation
700  * \param communication_amount communication amount between each pair of workstations
701  * \param rate task execution speed rate
702  * \see SD_task_unschedule()
703  */
704 void SD_task_schedule(SD_task_t task, int workstation_count,
705                       const SD_workstation_t * workstation_list,
706                       const double *computation_amount,
707                       const double *communication_amount, double rate)
708 {
709   xbt_assert0(workstation_count > 0, "workstation_nb must be positive");
710
711   int communication_nb;
712
713   task->workstation_nb = workstation_count;
714   task->rate = rate;
715
716   if (computation_amount) {
717     task->computation_amount = xbt_new(double, workstation_count);
718     memcpy(task->computation_amount, computation_amount,
719         sizeof(double) * workstation_count);
720   } else {
721     task->computation_amount = NULL;
722   }
723
724   communication_nb = workstation_count * workstation_count;
725   if (communication_amount) {
726     task->communication_amount = xbt_new(double, communication_nb);
727     memcpy(task->communication_amount, communication_amount,
728         sizeof(double) * communication_nb);
729   } else {
730     task->communication_amount = NULL;
731   }
732
733   task->workstation_list = xbt_new(SD_workstation_t, workstation_count);
734   memcpy(task->workstation_list, workstation_list,
735          sizeof(SD_workstation_t) * workstation_count);
736
737   SD_task_do_schedule(task);
738 }
739 /**
740  * \brief Unschedules a task
741  *
742  * The task state must be #SD_SCHEDULED, #SD_READY, #SD_RUNNING or #SD_FAILED.
743  * If you call this function, the task state becomes #SD_NOT_SCHEDULED.
744  * Call SD_task_schedule() to schedule it again.
745  *
746  * \param task the task you want to unschedule
747  * \see SD_task_schedule()
748  */
749 void SD_task_unschedule(SD_task_t task)
750 {
751   SD_CHECK_INIT_DONE();
752   xbt_assert0(task != NULL, "Invalid parameter");
753
754   if (task->state_set != sd_global->scheduled_task_set &&
755       task->state_set != sd_global->ready_task_set &&
756       task->state_set != sd_global->running_task_set &&
757       task->state_set != sd_global->failed_task_set)
758     THROW1(arg_error, 0,
759            "Task %s: the state must be SD_SCHEDULED, SD_READY, SD_RUNNING or SD_FAILED",
760            SD_task_get_name(task));
761
762   if (__SD_task_is_scheduled_or_ready(task) /* if the task is scheduled or ready */
763       && task->kind == SD_TASK_NOT_TYPED) /* Don't free scheduling data for typed tasks */
764     __SD_task_destroy_scheduling_data(task);
765
766   if (__SD_task_is_running(task))       /* the task should become SD_FAILED */
767     surf_workstation_model->action_cancel(task->surf_action);
768   else
769     __SD_task_set_state(task, SD_NOT_SCHEDULED);
770   task->remains = task->amount;
771   task->start_time = -1.0;
772 }
773
774 /* Destroys the data memorised by SD_task_schedule. Task state must be SD_SCHEDULED or SD_READY.
775  */
776 static void __SD_task_destroy_scheduling_data(SD_task_t task)
777 {
778   SD_CHECK_INIT_DONE();
779   if (!__SD_task_is_scheduled_or_ready(task) && !__SD_task_is_in_fifo(task))
780     THROW1(arg_error, 0,
781            "Task '%s' must be SD_SCHEDULED, SD_READY or SD_IN_FIFO",
782            SD_task_get_name(task));
783
784   xbt_free(task->computation_amount);
785   xbt_free(task->communication_amount);
786   task->computation_amount = task->communication_amount = NULL;
787 }
788
789 /* Runs a task. This function is directly called by __SD_task_try_to_run if the task
790  * doesn't have to wait in fifos. Otherwise, it is called by __SD_task_just_done when
791  * the task gets out of its fifos.
792  */
793 void __SD_task_really_run(SD_task_t task)
794 {
795
796   int i;
797   void **surf_workstations;
798
799   SD_CHECK_INIT_DONE();
800   xbt_assert0(task != NULL, "Invalid parameter");
801   xbt_assert2(__SD_task_is_ready_or_in_fifo(task),
802               "Task '%s' is not ready or in a fifo! Task state: %d",
803               SD_task_get_name(task), SD_task_get_state(task));
804   xbt_assert1(task->workstation_list != NULL,
805               "Task '%s': workstation_list is NULL!", SD_task_get_name(task));
806
807
808
809   DEBUG1("Really running task '%s'", SD_task_get_name(task));
810
811   /* set this task as current task for the workstations in sequential mode */
812   for (i = 0; i < task->workstation_nb; i++) {
813     if (SD_workstation_get_access_mode(task->workstation_list[i]) ==
814         SD_WORKSTATION_SEQUENTIAL_ACCESS) {
815       task->workstation_list[i]->current_task = task;
816       xbt_assert0(__SD_workstation_is_busy(task->workstation_list[i]),
817                   "The workstation should be busy now");
818     }
819   }
820
821   DEBUG1("Task '%s' set as current task for its workstations",
822          SD_task_get_name(task));
823
824   /* start the task */
825
826   /* we have to create a Surf workstation array instead of the SimDag workstation array */
827   surf_workstations = xbt_new(void *, task->workstation_nb);
828
829   for (i = 0; i < task->workstation_nb; i++)
830     surf_workstations[i] = task->workstation_list[i]->surf_workstation;
831
832   /* It's allowed to pass a NULL vector as cost to mean vector of 0.0 (easing user's life). Let's deal with it */
833 #define cost_or_zero(array,pos) ((array)?(array)[pos]:0.0)
834
835   task->surf_action = NULL;
836   if ((task->workstation_nb == 1) && (cost_or_zero(task->communication_amount,0) == 0.0)) {
837     task->surf_action =
838       surf_workstation_model->extension.
839       workstation.execute(surf_workstations[0], cost_or_zero(task->computation_amount,0));
840   } else if ((task->workstation_nb == 1)
841              && (cost_or_zero(task->computation_amount,0) == 0.0)) {
842
843     task->surf_action =
844       surf_workstation_model->extension.
845       workstation.communicate(surf_workstations[0], surf_workstations[0],
846                               cost_or_zero(task->communication_amount,0), task->rate);
847   } else if ((task->workstation_nb == 2)
848              && (cost_or_zero(task->computation_amount,0) == 0.0)
849              && (cost_or_zero(task->computation_amount,1) == 0.0)) {
850     int nb = 0;
851     double value = 0.0;
852
853     for (i = 0; i < task->workstation_nb * task->workstation_nb; i++) {
854       if (cost_or_zero(task->communication_amount,i) > 0.0) {
855         nb++;
856         value = cost_or_zero(task->communication_amount,i);
857       }
858     }
859     if (nb == 1) {
860       task->surf_action =
861         surf_workstation_model->extension.
862         workstation.communicate(surf_workstations[0], surf_workstations[1],
863                                 value, task->rate);
864     }
865   }
866 #undef cost_or_zero
867
868   if (!task->surf_action) {
869     double *computation_amount = xbt_new(double, task->workstation_nb);
870     double *communication_amount = xbt_new(double, task->workstation_nb *
871                                            task->workstation_nb);
872
873     memcpy(computation_amount, task->computation_amount, sizeof(double) *
874            task->workstation_nb);
875     memcpy(communication_amount, task->communication_amount,
876            sizeof(double) * task->workstation_nb * task->workstation_nb);
877
878     task->surf_action =
879       surf_workstation_model->extension.
880       workstation.execute_parallel_task(task->workstation_nb,
881                                         surf_workstations, computation_amount,
882                                         communication_amount, task->amount,
883                                         task->rate);
884   } else {
885     xbt_free(surf_workstations);
886   }
887
888   surf_workstation_model->action_data_set(task->surf_action, task);
889
890   DEBUG1("surf_action = %p", task->surf_action);
891
892   __SD_task_destroy_scheduling_data(task);      /* now the scheduling data are not useful anymore */
893   __SD_task_set_state(task, SD_RUNNING);
894   xbt_assert2(__SD_task_is_running(task), "Bad state of task '%s': %d",
895               SD_task_get_name(task), SD_task_get_state(task));
896
897 }
898
899 /* Tries to run a task. This function is called by SD_simulate() when a scheduled task becomes SD_READY
900  * (ie when its dependencies are satisfied).
901  * If one of the workstations where the task is scheduled on is busy (in sequential mode),
902  * the task doesn't start.
903  * Returns whether the task has started.
904  */
905 int __SD_task_try_to_run(SD_task_t task)
906 {
907
908   int can_start = 1;
909   int i;
910   SD_workstation_t workstation;
911
912   SD_CHECK_INIT_DONE();
913   xbt_assert0(task != NULL, "Invalid parameter");
914   xbt_assert2(__SD_task_is_ready(task),
915               "Task '%s' is not ready! Task state: %d",
916               SD_task_get_name(task), SD_task_get_state(task));
917
918
919   for (i = 0; i < task->workstation_nb; i++) {
920     can_start = !__SD_workstation_is_busy(task->workstation_list[i]);
921   }
922
923   DEBUG2("Task '%s' can start: %d", SD_task_get_name(task), can_start);
924
925   if (!can_start) {             /* if the task cannot start and is not in the fifos yet */
926     for (i = 0; i < task->workstation_nb; i++) {
927       workstation = task->workstation_list[i];
928       if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
929         DEBUG2("Pushing task '%s' in the fifo of workstation '%s'",
930                SD_task_get_name(task), SD_workstation_get_name(workstation));
931         xbt_fifo_push(workstation->task_fifo, task);
932       }
933     }
934     __SD_task_set_state(task, SD_IN_FIFO);
935     xbt_assert2(__SD_task_is_in_fifo(task), "Bad state of task '%s': %d",
936                 SD_task_get_name(task), SD_task_get_state(task));
937     DEBUG1("Task '%s' state is now SD_IN_FIFO", SD_task_get_name(task));
938   } else {
939     __SD_task_really_run(task);
940   }
941
942   return can_start;
943 }
944
945 /* This function is called by SD_simulate when a task is done.
946  * It updates task->state and task->action and executes if necessary the tasks
947  * which were waiting in fifos for the end of `task'
948  */
949 void __SD_task_just_done(SD_task_t task)
950 {
951   int i, j;
952   SD_workstation_t workstation;
953
954   SD_task_t candidate;
955   int candidate_nb = 0;
956   int candidate_capacity = 8;
957   SD_task_t *candidates;
958   int can_start = 1;
959
960   SD_CHECK_INIT_DONE();
961   xbt_assert0(task != NULL, "Invalid parameter");
962   xbt_assert1(__SD_task_is_running(task),
963               "The task must be running! Task state: %d",
964               SD_task_get_state(task));
965   xbt_assert1(task->workstation_list != NULL,
966               "Task '%s': workstation_list is NULL!", SD_task_get_name(task));
967
968
969   candidates = xbt_new(SD_task_t, 8);
970
971   __SD_task_set_state(task, SD_DONE);
972   surf_workstation_model->action_unref(task->surf_action);
973   task->surf_action = NULL;
974
975   DEBUG0("Looking for candidates");
976
977   /* if the task was executed on sequential workstations,
978      maybe we can execute the next task of the fifo for each workstation */
979   for (i = 0; i < task->workstation_nb; i++) {
980     workstation = task->workstation_list[i];
981     DEBUG2("Workstation '%s': access_mode = %d",
982            SD_workstation_get_name(workstation), workstation->access_mode);
983     if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
984       xbt_assert1(workstation->task_fifo != NULL,
985                   "Workstation '%s' has sequential access but no fifo!",
986                   SD_workstation_get_name(workstation));
987       xbt_assert2(workstation->current_task =
988                   task, "Workstation '%s': current task should be '%s'",
989                   SD_workstation_get_name(workstation),
990                   SD_task_get_name(task));
991
992       /* the task is over so we can release the workstation */
993       workstation->current_task = NULL;
994
995       DEBUG0("Getting candidate in fifo");
996       candidate =
997         xbt_fifo_get_item_content(xbt_fifo_get_first_item
998                                   (workstation->task_fifo));
999
1000       if (candidate != NULL) {
1001         DEBUG1("Candidate: '%s'", SD_task_get_name(candidate));
1002         xbt_assert2(__SD_task_is_in_fifo(candidate),
1003                     "Bad state of candidate '%s': %d",
1004                     SD_task_get_name(candidate),
1005                     SD_task_get_state(candidate));
1006       }
1007
1008       DEBUG1("Candidate in fifo: %p", candidate);
1009
1010       /* if there was a task waiting for my place */
1011       if (candidate != NULL) {
1012         /* Unfortunately, we are not sure yet that we can execute the task now,
1013            because the task can be waiting more deeply in some other workstation's fifos...
1014            So we memorize all candidate tasks, and then we will check for each candidate
1015            whether or not all its workstations are available. */
1016
1017         /* realloc if necessary */
1018         if (candidate_nb == candidate_capacity) {
1019           candidate_capacity *= 2;
1020           candidates =
1021             xbt_realloc(candidates, sizeof(SD_task_t) * candidate_capacity);
1022         }
1023
1024         /* register the candidate */
1025         candidates[candidate_nb++] = candidate;
1026         candidate->fifo_checked = 0;
1027       }
1028     }
1029   }
1030
1031   DEBUG1("Candidates found: %d", candidate_nb);
1032
1033   /* now we check every candidate task */
1034   for (i = 0; i < candidate_nb; i++) {
1035     candidate = candidates[i];
1036
1037     if (candidate->fifo_checked) {
1038       continue;                 /* we have already evaluated that task */
1039     }
1040
1041     xbt_assert2(__SD_task_is_in_fifo(candidate),
1042                 "Bad state of candidate '%s': %d",
1043                 SD_task_get_name(candidate), SD_task_get_state(candidate));
1044
1045     for (j = 0; j < candidate->workstation_nb && can_start; j++) {
1046       workstation = candidate->workstation_list[j];
1047
1048       /* I can start on this workstation if the workstation is shared
1049          or if I am the first task in the fifo */
1050       can_start = workstation->access_mode == SD_WORKSTATION_SHARED_ACCESS ||
1051         candidate ==
1052         xbt_fifo_get_item_content(xbt_fifo_get_first_item
1053                                   (workstation->task_fifo));
1054     }
1055
1056     DEBUG2("Candidate '%s' can start: %d", SD_task_get_name(candidate),
1057            can_start);
1058
1059     /* now we are sure that I can start! */
1060     if (can_start) {
1061       for (j = 0; j < candidate->workstation_nb && can_start; j++) {
1062         workstation = candidate->workstation_list[j];
1063
1064         /* update the fifo */
1065         if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
1066           candidate = xbt_fifo_shift(workstation->task_fifo);   /* the return value is stored just for debugging */
1067           DEBUG1("Head of the fifo: '%s'",
1068                  (candidate != NULL) ? SD_task_get_name(candidate) : "NULL");
1069           xbt_assert0(candidate == candidates[i],
1070                       "Error in __SD_task_just_done: bad first task in the fifo");
1071         }
1072       }                         /* for each workstation */
1073
1074       /* finally execute the task */
1075       DEBUG2("Task '%s' state: %d", SD_task_get_name(candidate),
1076              SD_task_get_state(candidate));
1077       __SD_task_really_run(candidate);
1078
1079       DEBUG4
1080         ("Calling __SD_task_is_running: task '%s', state set: %p, running_task_set: %p, is running: %d",
1081          SD_task_get_name(candidate), candidate->state_set,
1082          sd_global->running_task_set, __SD_task_is_running(candidate));
1083       xbt_assert2(__SD_task_is_running(candidate),
1084                   "Bad state of task '%s': %d", SD_task_get_name(candidate),
1085                   SD_task_get_state(candidate));
1086       DEBUG0("Okay, the task is running.");
1087
1088     }                           /* can start */
1089     candidate->fifo_checked = 1;
1090   }                             /* for each candidate */
1091
1092   xbt_free(candidates);
1093 }
1094
1095 /* Remove all dependencies associated with a task. This function is called when the task is destroyed.
1096  */
1097 static void __SD_task_remove_dependencies(SD_task_t task)
1098 {
1099   /* we must destroy the dependencies carefuly (with SD_dependency_remove)
1100      because each one is stored twice */
1101   SD_dependency_t dependency;
1102   while (xbt_dynar_length(task->tasks_before) > 0) {
1103     xbt_dynar_get_cpy(task->tasks_before, 0, &dependency);
1104     SD_task_dependency_remove(dependency->src, dependency->dst);
1105   }
1106
1107   while (xbt_dynar_length(task->tasks_after) > 0) {
1108     xbt_dynar_get_cpy(task->tasks_after, 0, &dependency);
1109     SD_task_dependency_remove(dependency->src, dependency->dst);
1110   }
1111 }
1112
1113 /**
1114  * \brief Returns the start time of a task
1115  *
1116  * The task state must be SD_RUNNING, SD_DONE or SD_FAILED.
1117  *
1118  * \param task: a task
1119  * \return the start time of this task
1120  */
1121 double SD_task_get_start_time(SD_task_t task)
1122 {
1123   SD_CHECK_INIT_DONE();
1124   xbt_assert0(task != NULL, "Invalid parameter");
1125   if (task->surf_action)
1126     return surf_workstation_model->action_get_start_time(task->surf_action);
1127   else
1128     return task->start_time;
1129 }
1130
1131 /**
1132  * \brief Returns the finish time of a task
1133  *
1134  * The task state must be SD_RUNNING, SD_DONE or SD_FAILED.
1135  * If the state is not completed yet, the returned value is an
1136  * estimation of the task finish time. This value can fluctuate
1137  * until the task is completed.
1138  *
1139  * \param task: a task
1140  * \return the start time of this task
1141  */
1142 double SD_task_get_finish_time(SD_task_t task)
1143 {
1144   SD_CHECK_INIT_DONE();
1145   xbt_assert0(task != NULL, "Invalid parameter");
1146
1147   if (task->surf_action)        /* should never happen as actions are destroyed right after their completion */
1148     return surf_workstation_model->action_get_finish_time(task->surf_action);
1149   else
1150     return task->finish_time;
1151 }
1152
1153 /**
1154  * \brief Destroys a task.
1155  *
1156  * The user data (if any) should have been destroyed first.
1157  *
1158  * \param task the task you want to destroy
1159  * \see SD_task_create()
1160  */
1161 void SD_task_destroy(SD_task_t task)
1162 {
1163   SD_CHECK_INIT_DONE();
1164   xbt_assert0(task != NULL, "Invalid parameter");
1165
1166   DEBUG1("Destroying task %s...", SD_task_get_name(task));
1167
1168   __SD_task_remove_dependencies(task);
1169   /* if the task was scheduled or ready we have to free the scheduling parameters */
1170   if (__SD_task_is_scheduled_or_ready(task))
1171     __SD_task_destroy_scheduling_data(task);
1172   xbt_swag_remove(task,task->state_set);
1173
1174   if (task->name != NULL)
1175     xbt_free(task->name);
1176
1177   if (task->surf_action != NULL)
1178     surf_workstation_model->action_unref(task->surf_action);
1179
1180   if (task->workstation_list != NULL)
1181     xbt_free(task->workstation_list);
1182
1183   if (task->communication_amount)
1184     xbt_free(task->communication_amount);
1185
1186   if (task->computation_amount)
1187     xbt_free(task->computation_amount);
1188
1189   xbt_dynar_free(&task->tasks_before);
1190   xbt_dynar_free(&task->tasks_after);
1191   xbt_free(task);
1192
1193   sd_global->task_number--;
1194
1195   DEBUG0("Task destroyed.");
1196 }
1197
1198
1199 static inline SD_task_t SD_task_create_sized(const char*name,void*data,double amount,int ws_count) {
1200   SD_task_t task = SD_task_create(name,data,amount);
1201   task->communication_amount = xbt_new0(double,ws_count*ws_count);
1202   task->computation_amount = xbt_new0(double,ws_count);
1203   task->workstation_nb = ws_count;
1204   task->workstation_list = xbt_new0(SD_workstation_t,ws_count);
1205   return task;
1206 }
1207 /** @brief create a end-to-end communication task that can then be auto-scheduled
1208  *
1209  * Auto-scheduling mean that the task can be used with SD_task_schedulev(). This
1210  * allows to specify the task costs at creation, and decorelate them from the
1211  * scheduling process where you just specify which resource should deliver the
1212  * mandatory power.
1213  *
1214  * A end-to-end communication must be scheduled on 2 hosts, and the amount
1215  * specified at creation is sent from hosts[0] to hosts[1].
1216  */
1217 SD_task_t SD_task_create_comm_e2e(const char*name, void *data, double amount) {
1218   SD_task_t res = SD_task_create_sized(name,data,amount,2);
1219   res->communication_amount[2] = amount;
1220   res->kind=SD_TASK_COMM_E2E;
1221   return res;
1222 }
1223 /** @brief create a sequential computation task that can then be auto-scheduled
1224  *
1225  * Auto-scheduling mean that the task can be used with SD_task_schedulev(). This
1226  * allows to specify the task costs at creation, and decorelate them from the
1227  * scheduling process where you just specify which resource should deliver the
1228  * mandatory power.
1229  *
1230  * A sequential computation must be scheduled on 1 host, and the amount
1231  * specified at creation to be run on hosts[0].
1232  */
1233 SD_task_t SD_task_create_comp_seq(const char*name, void *data, double amount) {
1234   SD_task_t res = SD_task_create_sized(name,data,amount,1);
1235   res->computation_amount[0]=amount;
1236   res->kind=SD_TASK_COMP_SEQ;
1237   return res;
1238 }
1239
1240 /** @brief Auto-schedules a task.
1241  *
1242  * Auto-scheduling mean that the task can be used with SD_task_schedulev(). This
1243  * allows to specify the task costs at creation, and decorelate them from the
1244  * scheduling process where you just specify which resource should deliver the
1245  * mandatory power.
1246  *
1247  * To be auto-schedulable, a task must be created with SD_task_create_comm_e2e() or
1248  * SD_task_create_comp_seq(). Check their definitions for the exact semantic of each
1249  * of them.
1250  *
1251  * @todo
1252  * We should create tasks kind for the following categories:
1253  *  - Point to point communication (done)
1254  *  - Sequential computation       (done)
1255  *  - group communication (redistribution, several kinds)
1256  *  - parallel tasks with no internal communication (one kind per speedup model such as amdal)
1257  *  - idem+ internal communication. Task type not enough since we cannot store comm cost alongside to comp one)
1258  */
1259 void SD_task_schedulev(SD_task_t task, int count, const SD_workstation_t*list) {
1260   int i;
1261   xbt_assert1(task->kind != 0,"Task %s is not typed. Cannot automatically schedule it.",SD_task_get_name(task));
1262   switch(task->kind) {
1263   case SD_TASK_COMM_E2E:
1264   case SD_TASK_COMP_SEQ:
1265     xbt_assert(task->workstation_nb==count);
1266     for (i=0;i<count;i++)
1267       task->workstation_list[i]=list[i];
1268     SD_task_do_schedule(task);
1269     break;
1270   default:
1271     xbt_die(bprintf("Kind of task %s not supported by SD_task_schedulev()",
1272           SD_task_get_name(task)));
1273   }
1274   if (task->kind == SD_TASK_COMM_E2E) {
1275     VERB4("Schedule comm task %s between %s -> %s. It costs %.f bytes",
1276         SD_task_get_name(task),
1277         SD_workstation_get_name(task->workstation_list[0]),SD_workstation_get_name(task->workstation_list[1]),
1278         task->communication_amount[2]);
1279
1280   }
1281   /* Iterate over all childs and parent being COMM_E2E to say where I am located (and start them if ready) */
1282   if (task->kind == SD_TASK_COMP_SEQ) {
1283     VERB3("Schedule computation task %s on %s. It costs %.f flops",
1284         SD_task_get_name(task),SD_workstation_get_name(task->workstation_list[0]),
1285         task->computation_amount[0]);
1286     SD_dependency_t dep;
1287     unsigned int cpt;
1288     xbt_dynar_foreach(task->tasks_before,cpt,dep) {
1289       SD_task_t before = dep->src;
1290       if (before->kind == SD_TASK_COMM_E2E) {
1291         before->workstation_list[1] = task->workstation_list[0];
1292         if (before->workstation_list[0] && __SD_task_is_not_scheduled(before)) {
1293           SD_task_do_schedule(before);
1294           VERB4("Auto-Schedule comm task %s between %s -> %s. It costs %.f bytes",
1295               SD_task_get_name(before),
1296               SD_workstation_get_name(before->workstation_list[0]),SD_workstation_get_name(before->workstation_list[1]),
1297               before->communication_amount[2]);
1298         }
1299       }
1300     }
1301     xbt_dynar_foreach(task->tasks_after,cpt,dep) {
1302       SD_task_t after = dep->dst;
1303       if (after->kind == SD_TASK_COMM_E2E) {
1304         after->workstation_list[0] = task->workstation_list[0];
1305         if (after->workstation_list[1] && __SD_task_is_not_scheduled(after)) {
1306           SD_task_do_schedule(after);
1307           VERB4("Auto-Schedule comm task %s between %s -> %s. It costs %.f bytes",
1308               SD_task_get_name(after),
1309               SD_workstation_get_name(after->workstation_list[0]),SD_workstation_get_name(after->workstation_list[1]),
1310               after->communication_amount[2]);
1311
1312         }
1313       }
1314     }
1315   }
1316 }
1317 /** @brief autoschedule a task on a list of workstations
1318  *
1319  * This function is very similar to SD_task_schedulev(),
1320  * but takes the list of workstations to schedule onto as separate parameters.
1321  * It builds a proper vector of workstations and then call SD_task_schedulev()
1322  */
1323 void SD_task_schedulel(SD_task_t task, int count, ...) {
1324   va_list ap;
1325   SD_workstation_t *list=xbt_new(SD_workstation_t,count);
1326   int i;
1327   va_start(ap,count);
1328   for (i=0;i<count;i++) {
1329       list[i] = va_arg(ap,SD_workstation_t);
1330   }
1331   va_end(ap);
1332   SD_task_schedulev(task,count,list);
1333   free(list);
1334 }