Logo AND Algorithmique Numérique Distribuée

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