Logo AND Algorithmique Numérique Distribuée

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