Logo AND Algorithmique Numérique Distribuée

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