Logo AND Algorithmique Numérique Distribuée

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