Logo AND Algorithmique Numérique Distribuée

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