Logo AND Algorithmique Numérique Distribuée

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