Logo AND Algorithmique Numérique Distribuée

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