Logo AND Algorithmique Numérique Distribuée

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