Logo AND Algorithmique Numérique Distribuée

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