Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Adding convenient function for Fred Suter
[simgrid.git] / src / simdag / sd_task.c
1 #include "private.h"
2 #include "simdag/simdag.h"
3 #include "xbt/sysdep.h"
4 #include "xbt/dynar.h"
5
6 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(sd_task,sd,
7                                 "Logging specific to SimDag (task)");
8
9 static void __SD_task_remove_dependencies(SD_task_t task);
10 static void __SD_task_destroy_scheduling_data(SD_task_t task);
11
12 /**
13  * \brief Creates a new task.
14  *
15  * \param name the name of the task (can be \c NULL)
16  * \param data the user data you want to associate with the task (can be \c NULL)
17  * \param amount amount of the task
18  * \return the new task
19  * \see SD_task_destroy()
20  */
21 SD_task_t SD_task_create(const char *name, void *data, double amount) {
22
23   SD_task_t task;
24   SD_CHECK_INIT_DONE();
25
26   task = xbt_new(s_SD_task_t, 1);
27
28   /* general information */
29   task->data = data; /* user data */
30   if (name != NULL)
31     task->name = xbt_strdup(name);
32   else
33     task->name = NULL;
34
35   task->state_hookup.prev = NULL;
36   task->state_hookup.next = NULL;
37   task->state_set = sd_global->not_scheduled_task_set;
38   task->state = SD_NOT_SCHEDULED;
39   xbt_swag_insert(task, task->state_set);
40
41   task->amount = amount;
42   task->remains = amount;
43   task->start_time = -1.0;
44   task->finish_time = -1.0;
45   task->surf_action = NULL;
46   task->watch_points = 0;
47   task->state_changed = 0;
48
49   /* dependencies */
50   task->tasks_before = xbt_dynar_new(sizeof(SD_dependency_t), NULL);
51   task->tasks_after = xbt_dynar_new(sizeof(SD_dependency_t), NULL);
52
53   /* scheduling parameters */
54   task->workstation_nb = 0;
55   task->workstation_list = NULL;
56   task->computation_amount = NULL;
57   task->communication_amount = NULL;
58   task->rate = 0;
59
60   sd_global->task_number++;
61
62   return task;
63 }
64
65 /**
66  * \brief Returns the user data of a task
67  *
68  * \param task a task
69  * \return the user data associated with this task (can be \c NULL)
70  * \see SD_task_set_data()
71  */
72 void* SD_task_get_data(SD_task_t task) {
73   SD_CHECK_INIT_DONE();
74   xbt_assert0(task != NULL, "Invalid parameter");
75   return task->data;
76 }
77
78 /**
79  * \brief Sets the user data of a task
80  *
81  * The new data can be \c NULL. The old data should have been freed first
82  * if it was not \c NULL.
83  *
84  * \param task a task
85  * \param data the new data you want to associate with this task
86  * \see SD_task_get_data()
87  */
88 void SD_task_set_data(SD_task_t task, void *data) {
89   SD_CHECK_INIT_DONE();
90   xbt_assert0(task != NULL, "Invalid parameter");
91   task->data = data;
92 }
93
94 /**
95  * \brief Returns the state of a task
96  *
97  * \param task a task
98  * \return the current \ref e_SD_task_state_t "state" of this task:
99  * #SD_NOT_SCHEDULED, #SD_SCHEDULED, #SD_READY, #SD_RUNNING, #SD_DONE or #SD_FAILED
100  * \see e_SD_task_state_t
101  */
102 e_SD_task_state_t SD_task_get_state(SD_task_t task) {
103   SD_CHECK_INIT_DONE();
104   xbt_assert0(task != NULL, "Invalid parameter");
105   return task->state;
106 }
107
108 /* Changes the state of a task. Updates the swags and the flag sd_global->watch_point_reached.
109  */
110 void __SD_task_set_state(SD_task_t task, e_SD_task_state_t new_state) {
111   xbt_swag_remove(task, task->state_set);
112   switch (new_state) {
113   case SD_NOT_SCHEDULED:
114     task->state_set = sd_global->not_scheduled_task_set;
115     break;
116   case SD_SCHEDULED:
117     task->state_set = sd_global->scheduled_task_set;
118     break;
119   case SD_READY:
120     task->state_set = sd_global->ready_task_set;
121     break;
122   case SD_IN_FIFO:
123     task->state_set = sd_global->in_fifo_task_set;
124     break;
125   case SD_RUNNING:
126     task->state_set = sd_global->running_task_set;
127     task->start_time = surf_workstation_resource->common_public->
128       action_get_start_time(task->surf_action);
129     break;
130   case SD_DONE:
131     task->state_set = sd_global->done_task_set;
132     task->finish_time = surf_workstation_resource->common_public->
133       action_get_finish_time(task->surf_action);
134     task->remains = 0;
135     break;
136   case SD_FAILED:
137     task->state_set = sd_global->failed_task_set;
138     break;
139   default:
140     xbt_assert0(0, "Invalid state");
141   }
142   xbt_swag_insert(task, task->state_set);
143   task->state = new_state;
144
145   if (task->watch_points & new_state) {
146     INFO1("Watch point reached with task '%s'!", SD_task_get_name(task));
147     sd_global->watch_point_reached = 1;
148     SD_task_unwatch(task, new_state); /* remove the watch point */
149   }
150 }
151
152 /**
153  * \brief Returns the name of a task
154  *
155  * \param task a task
156  * \return the name of this task (can be \c NULL)
157  */
158 const char* SD_task_get_name(SD_task_t task) {
159   SD_CHECK_INIT_DONE();
160   xbt_assert0(task != NULL, "Invalid parameter");
161   return task->name;
162 }
163
164 /**
165  * \brief Returns the total amount of a task
166  *
167  * \param task a task
168  * \return the total amount of this task
169  * \see SD_task_get_remaining_amount()
170  */
171 double SD_task_get_amount(SD_task_t task) {
172   SD_CHECK_INIT_DONE();
173   xbt_assert0(task != NULL, "Invalid parameter");
174   return task->amount;
175 }
176
177 /**
178  * \brief Returns the remaining amount of a task
179  *
180  * \param task a task
181  * \return the remaining amount of this task
182  * \see SD_task_get_amount()
183  */
184 double SD_task_get_remaining_amount(SD_task_t task) {
185   SD_CHECK_INIT_DONE();
186   xbt_assert0(task != NULL, "Invalid parameter");
187
188   if (task->surf_action)
189     return task->surf_action->remains;
190   else
191     return task->remains;
192 }
193
194 /* temporary function for debbuging */
195 static void __SD_print_dependencies(SD_task_t task) {
196   xbt_dynar_t dynar;
197   int length;
198   int i;
199   SD_dependency_t dependency;
200
201   INFO1("The following tasks must be executed before %s:", SD_task_get_name(task));
202   dynar = task->tasks_before;
203   length = xbt_dynar_length(dynar);
204
205
206   for (i = 0; i < length; i++) {
207     xbt_dynar_get_cpy(dynar, i, &dependency);
208     INFO1(" %s", SD_task_get_name(dependency->src));
209   }
210
211   INFO1("The following tasks must be executed after %s:", SD_task_get_name(task));
212
213   dynar = task->tasks_after;
214   length = xbt_dynar_length(dynar);
215   for (i = 0; i < length; i++) {
216     xbt_dynar_get_cpy(dynar, i, &dependency);
217     INFO1(" %s", SD_task_get_name(dependency->dst));
218   }
219   INFO0("----------------------------");
220 }
221
222 /* Destroys a dependency between two tasks.
223  */
224 static void __SD_task_dependency_destroy(void *dependency) {
225   if (((SD_dependency_t) dependency)->name != NULL)
226     xbt_free(((SD_dependency_t) dependency)->name);
227   xbt_free(dependency);
228 }
229
230 /**
231  * \brief Adds a dependency between two tasks
232  *
233  * \a dst will depend on \a src, ie \a dst will not start before \a src is finished.
234  * Their \ref e_SD_task_state_t "state" must be #SD_NOT_SCHEDULED, #SD_SCHEDULED or #SD_READY.
235  *
236  * \param name the name of the new dependency (can be \c NULL)
237  * \param data the user data you want to associate with this dependency (can be \c NULL)
238  * \param src the task which must be executed first
239  * \param dst the task you want to make depend on \a src
240  * \see SD_task_dependency_remove()
241  */
242 void SD_task_dependency_add(const char *name, void *data, SD_task_t src, SD_task_t dst) {
243   xbt_dynar_t dynar;
244   int length;
245   int found = 0;
246   int i;
247   SD_dependency_t dependency;
248
249   SD_CHECK_INIT_DONE();
250   xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
251
252   dynar = src->tasks_after;
253   length = xbt_dynar_length(dynar);
254
255
256
257   if (src == dst) 
258     THROW1(arg_error, 0, "Cannot add a dependency between task '%s' and itself",
259            SD_task_get_name(src));
260
261   if (!__SD_task_is_not_scheduled(src) && !__SD_task_is_scheduled_or_ready(src))
262     THROW1(arg_error, 0, "Task '%s' must be SD_NOT_SCHEDULED, SD_SCHEDULED or SD_READY", SD_task_get_name(src));
263
264   if (!__SD_task_is_not_scheduled(dst) && !__SD_task_is_scheduled_or_ready(dst))
265     THROW1(arg_error, 0, "Task '%s' must be SD_NOT_SCHEDULED, SD_SCHEDULED or SD_READY", SD_task_get_name(dst));
266
267   DEBUG2("SD_task_dependency_add: src = %s, dst = %s", SD_task_get_name(src), SD_task_get_name(dst));
268   for (i = 0; i < length && !found; i++) {
269     xbt_dynar_get_cpy(dynar, i, &dependency);
270     found = (dependency->dst == dst);
271     DEBUG2("Dependency %d: dependency->dst = %s", i, SD_task_get_name(dependency->dst));
272   }
273
274   if (found)
275     THROW2(arg_error, 0, "A dependency already exists between task '%s' and task '%s'",
276            SD_task_get_name(src), SD_task_get_name(dst));
277
278   dependency = xbt_new(s_SD_dependency_t, 1);
279
280   if (name != NULL)
281     dependency->name = xbt_strdup(name);
282   else
283     dependency->name = NULL;
284
285   dependency->data = data;
286   dependency->src = src;
287   dependency->dst = dst;
288
289   /* src must be executed before dst */
290   xbt_dynar_push(src->tasks_after, &dependency);
291   xbt_dynar_push(dst->tasks_before, &dependency);
292
293   /* if the task was ready, then dst->tasks_before is not empty anymore,
294      so we must go back to state SD_SCHEDULED */
295   if (__SD_task_is_ready(dst)) {
296     DEBUG1("SD_task_dependency_add: %s was ready and becomes scheduled!", SD_task_get_name(dst));
297     __SD_task_set_state(dst, SD_SCHEDULED);
298   }
299
300   /*  __SD_print_dependencies(src);
301       __SD_print_dependencies(dst); */
302 }
303
304 /**
305  * \brief Indacates whether there is a dependency between two tasks.
306  *
307  * \param src a task
308  * \param dst a task depending on \a src
309  */
310 int SD_task_dependency_exists(SD_task_t src, SD_task_t dst) {
311   xbt_dynar_t dynar;
312   int length;
313   int i;
314   SD_dependency_t dependency;
315
316   SD_CHECK_INIT_DONE();
317   xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
318
319   dynar = src->tasks_after;
320   length = xbt_dynar_length(dynar);
321
322   for (i = 0; i < length; i++) {
323     xbt_dynar_get_cpy(dynar, i, &dependency);
324     if (dependency->dst == dst) return 1;
325   }
326   return 0;
327 }
328
329 /**
330  * \brief Remove a dependency between two tasks
331  *
332  * \param src a task
333  * \param dst a task depending on \a src
334  * \see SD_task_dependency_add()
335  */
336 void SD_task_dependency_remove(SD_task_t src, SD_task_t dst) {
337
338   xbt_dynar_t dynar;
339   int length;
340   int found = 0;
341   int i;
342   SD_dependency_t dependency;
343
344   SD_CHECK_INIT_DONE();
345   xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
346
347   /* remove the dependency from src->tasks_after */
348   dynar = src->tasks_after;
349   length = xbt_dynar_length(dynar);
350
351   for (i = 0; i < length && !found; i++) {
352     xbt_dynar_get_cpy(dynar, i, &dependency);
353     if (dependency->dst == dst) {
354       xbt_dynar_remove_at(dynar, i, NULL);
355       found = 1;
356     }
357   }
358   if (!found)
359     THROW4(arg_error, 0,
360            "No dependency found between task '%s' and '%s': task '%s' is not a successor of task '%s'",
361            SD_task_get_name(src), SD_task_get_name(dst), SD_task_get_name(dst), SD_task_get_name(src));
362
363   /* remove the dependency from dst->tasks_before */
364   dynar = dst->tasks_before;
365   length = xbt_dynar_length(dynar);
366   found = 0;
367   
368   for (i = 0; i < length && !found; i++) {
369     xbt_dynar_get_cpy(dynar, i, &dependency);
370     if (dependency->src == src) {
371       xbt_dynar_remove_at(dynar, i, NULL);
372       __SD_task_dependency_destroy(dependency);
373       found = 1;
374     }
375   }
376   /* should never happen... */
377   xbt_assert4(found, "SimDag error: task '%s' is a successor of '%s' but task '%s' is not a predecessor of task '%s'",
378               SD_task_get_name(dst), SD_task_get_name(src), SD_task_get_name(src), SD_task_get_name(dst));
379
380   /* if the task was scheduled and dst->tasks_before is empty now, we can make it ready */
381   if (xbt_dynar_length(dst->tasks_before) == 0 && __SD_task_is_scheduled(dst))
382     __SD_task_set_state(dst, SD_READY);
383
384   /*  __SD_print_dependencies(src); 
385       __SD_print_dependencies(dst);*/
386 }
387
388 /**
389  * \brief Returns the user data associated with a dependency between two tasks
390  *
391  * \param src a task
392  * \param dst a task depending on \a src
393  * \return the user data associated with this dependency (can be \c NULL)
394  * \see SD_task_dependency_add()
395  */
396 void *SD_task_dependency_get_data(SD_task_t src, SD_task_t dst) {
397
398   xbt_dynar_t dynar;
399   int length;
400   int found = 0;
401   int i;
402   SD_dependency_t dependency;
403
404
405   SD_CHECK_INIT_DONE();
406   xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
407
408   dynar = src->tasks_after;
409   length = xbt_dynar_length(dynar);
410
411   for (i = 0; i < length && !found; i++) {
412     xbt_dynar_get_cpy(dynar, i, &dependency);
413     found = (dependency->dst == dst);
414   }
415   if (!found)
416     THROW2(arg_error, 0, "No dependency found between task '%s' and '%s'", SD_task_get_name(src), SD_task_get_name(dst));
417   return dependency->data;
418 }
419
420 /* temporary function for debugging */
421 static void __SD_print_watch_points(SD_task_t task) {
422   static const int state_masks[] = {SD_SCHEDULED, SD_RUNNING, SD_READY, SD_DONE, SD_FAILED};
423   static const char* state_names[] = {"scheduled", "running", "ready", "done", "failed"};
424   int i;
425
426   INFO2("Task '%s' watch points (%x): ", SD_task_get_name(task), task->watch_points);
427
428
429   for (i = 0; i < 5; i++) {
430     if (task->watch_points & state_masks[i])
431       INFO1("%s ", state_names[i]);
432   }
433 }
434
435 /**
436  * \brief Adds a watch point to a task
437  *
438  * SD_simulate() will stop as soon as the \ref e_SD_task_state_t "state" of this
439  * task becomes the one given in argument. The
440  * watch point is then automatically removed.
441  * 
442  * \param task a task
443  * \param state the \ref e_SD_task_state_t "state" you want to watch
444  * (cannot be #SD_NOT_SCHEDULED)
445  * \see SD_task_unwatch()
446  */
447 void SD_task_watch(SD_task_t task, e_SD_task_state_t state) {
448   SD_CHECK_INIT_DONE();
449   xbt_assert0(task != NULL, "Invalid parameter");
450
451   if (state & SD_NOT_SCHEDULED)
452     THROW0(arg_error, 0, "Cannot add a watch point for state SD_NOT_SCHEDULED");
453
454   task->watch_points = task->watch_points | state;
455   /*  __SD_print_watch_points(task);*/
456 }
457
458 /**
459  * \brief Removes a watch point from a task
460  * 
461  * \param task a task
462  * \param state the \ref e_SD_task_state_t "state" you no longer want to watch
463  * \see SD_task_watch()
464  */
465 void SD_task_unwatch(SD_task_t task, e_SD_task_state_t state) {
466   SD_CHECK_INIT_DONE();
467   xbt_assert0(task != NULL, "Invalid parameter");
468   xbt_assert0(state != SD_NOT_SCHEDULED,
469               "SimDag error: Cannot have a watch point for state SD_NOT_SCHEDULED");
470   
471   task->watch_points = task->watch_points & ~state;
472   /*  __SD_print_watch_points(task);*/
473 }
474
475 /**
476  * \brief Returns an approximative estimation of the execution time of a task.
477  * 
478  * The estimation is very approximative because the value returned is the time
479  * the task would take if it was executed now and if it was the only task.
480  * 
481  * \param task the task to evaluate
482  * \param workstation_nb number of workstations on which the task would be executed
483  * \param workstation_list the workstations on which the task would be executed
484  * \param computation_amount computation amount for each workstation
485  * \param communication_amount communication amount between each pair of workstations
486  * \param rate task execution speed rate
487  * \see SD_schedule()
488  */
489 double SD_task_get_execution_time(SD_task_t task,
490                                   int workstation_nb,
491                                   const SD_workstation_t *workstation_list,
492                                   const double *computation_amount,
493                                   const double *communication_amount,
494                                   double rate) {
495   double time, max_time = 0.0;
496   int i, j;
497   SD_CHECK_INIT_DONE();
498   xbt_assert0(task != NULL && workstation_nb > 0 && workstation_list != NULL &&
499               computation_amount != NULL && communication_amount != NULL,
500               "Invalid parameter");
501
502   /* the task execution time is the maximum execution time of the parallel tasks */
503
504   for (i = 0; i < workstation_nb; i++) {
505     time = SD_workstation_get_computation_time(workstation_list[i], computation_amount[i]);
506     
507     for (j = 0; j < workstation_nb; j++) {
508       time += SD_route_get_communication_time(workstation_list[i], workstation_list[j],
509                                               communication_amount[i * workstation_nb + j]);
510     }
511
512     if (time > max_time) {
513       max_time = time;
514     }
515   }
516   return max_time * SD_task_get_amount(task);
517 }
518
519 /**
520  * \brief Schedules a task
521  *
522  * The task state must be #SD_NOT_SCHEDULED.
523  * Once scheduled, a task will be executed as soon as possible in SD_simulate(),
524  * i.e. when its dependencies are satisfied.
525  * 
526  * \param task the task you want to schedule
527  * \param workstation_nb number of workstations on which the task will be executed
528  * \param workstation_list the workstations on which the task will be executed
529  * \param computation_amount computation amount for each workstation
530  * \param communication_amount communication amount between each pair of workstations
531  * \param rate task execution speed rate
532  * \see SD_task_unschedule()
533  */
534 void SD_task_schedule(SD_task_t task, int workstation_nb,
535                      const SD_workstation_t *workstation_list, const double *computation_amount,
536                      const double *communication_amount, double rate) {
537
538   int communication_nb;
539   
540   SD_CHECK_INIT_DONE();
541   xbt_assert0(task != NULL, "Invalid parameter");
542   xbt_assert0(workstation_nb > 0, "workstation_nb must be positive");
543
544   if (!__SD_task_is_not_scheduled(task))
545     THROW1(arg_error, 0, "Task '%s' has already been scheduled", SD_task_get_name(task));
546
547   task->workstation_nb = workstation_nb;
548   task->rate = rate;
549
550   task->computation_amount = xbt_new(double, workstation_nb);
551   memcpy(task->computation_amount, computation_amount, sizeof(double) * workstation_nb);
552
553   communication_nb = workstation_nb * workstation_nb;
554   task->communication_amount = xbt_new(double, communication_nb);
555   memcpy(task->communication_amount, communication_amount, sizeof(double) * communication_nb);
556
557   task->workstation_list = xbt_new(SD_workstation_t, workstation_nb);
558   memcpy(task->workstation_list, workstation_list, sizeof(SD_workstation_t) * workstation_nb);
559
560   /* update the task state */
561   if (xbt_dynar_length(task->tasks_before) == 0)
562     __SD_task_set_state(task, SD_READY);
563   else
564     __SD_task_set_state(task, SD_SCHEDULED);
565 }
566
567 /**
568  * \brief Unschedules a task
569  *
570  * The task state must be #SD_SCHEDULED, #SD_READY, #SD_RUNNING or #SD_FAILED.
571  * If you call this function, the task state becomes #SD_NOT_SCHEDULED.
572  * Call SD_task_schedule() to schedule it again.
573  *
574  * \param task the task you want to unschedule
575  * \see SD_task_schedule()
576  */
577 void SD_task_unschedule(SD_task_t task) {
578   SD_CHECK_INIT_DONE();
579   xbt_assert0(task != NULL, "Invalid parameter");
580
581   if (task->state_set != sd_global->scheduled_task_set &&
582       task->state_set != sd_global->ready_task_set &&
583       task->state_set != sd_global->running_task_set &&
584       task->state_set != sd_global->failed_task_set)
585     THROW1(arg_error, 0, "Task %s: the state must be SD_SCHEDULED, SD_READY, SD_RUNNING or SD_FAILED",
586            SD_task_get_name(task));
587
588   if (__SD_task_is_scheduled_or_ready(task)) /* if the task is scheduled or ready */
589     __SD_task_destroy_scheduling_data(task);
590
591   if (__SD_task_is_running(task)) /* the task should become SD_FAILED */
592     surf_workstation_resource->common_public->action_cancel(task->surf_action);
593   else
594     __SD_task_set_state(task, SD_NOT_SCHEDULED);
595   task->remains = task->amount;
596   task->start_time = -1.0;
597 }
598
599 /* Destroys the data memorised by SD_task_schedule. Task state must be SD_SCHEDULED or SD_READY.
600  */
601 static void __SD_task_destroy_scheduling_data(SD_task_t task) {
602   SD_CHECK_INIT_DONE();
603   if (!__SD_task_is_scheduled_or_ready(task) && !__SD_task_is_in_fifo(task))
604     THROW1(arg_error, 0, "Task '%s' must be SD_SCHEDULED, SD_READY or SD_IN_FIFO", SD_task_get_name(task));
605
606   xbt_free(task->computation_amount);
607   xbt_free(task->communication_amount);
608 }
609
610 /* Runs a task. This function is directly called by __SD_task_try_to_run if the task
611  * doesn't have to wait in fifos. Otherwise, it is called by __SD_task_just_done when
612  * the task gets out of its fifos.
613  */
614 void __SD_task_really_run(SD_task_t task) {
615
616   int i;
617   void **surf_workstations;
618
619   SD_CHECK_INIT_DONE();
620   xbt_assert0(task != NULL, "Invalid parameter");
621   xbt_assert2(__SD_task_is_ready_or_in_fifo(task), "Task '%s' is not ready or in a fifo! Task state: %d",
622            SD_task_get_name(task), SD_task_get_state(task));
623   xbt_assert1(task->workstation_list != NULL, "Task '%s': workstation_list is NULL!", SD_task_get_name(task));
624
625
626
627   DEBUG1("Really running task '%s'", SD_task_get_name(task));
628
629   /* set this task as current task for the workstations in sequential mode */
630   for (i = 0; i < task->workstation_nb; i++) {
631     if (SD_workstation_get_access_mode(task->workstation_list[i]) == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
632       task->workstation_list[i]->current_task = task;
633       xbt_assert0(__SD_workstation_is_busy(task->workstation_list[i]), "The workstation should be busy now");
634     }
635   }
636   
637   DEBUG1("Task '%s' set as current task for its workstations", SD_task_get_name(task));
638
639   /* start the task */
640
641   /* we have to create a Surf workstation array instead of the SimDag workstation array */
642   surf_workstations = xbt_new(void*, task->workstation_nb);
643
644   for (i = 0; i < task->workstation_nb; i++) {
645     surf_workstations[i] = task->workstation_list[i]->surf_workstation;
646   }
647   
648   task->surf_action = surf_workstation_resource->extension_public->
649     execute_parallel_task(task->workstation_nb,
650                           surf_workstations,
651                           task->computation_amount,
652                           task->communication_amount,
653                           task->amount,
654                           task->rate);
655   surf_workstation_resource->common_public->action_set_data(task->surf_action, task);
656   task->state_changed = 1;
657
658   DEBUG1("surf_action = %p",  task->surf_action);
659
660   xbt_free(surf_workstations);
661   __SD_task_destroy_scheduling_data(task); /* now the scheduling data are not useful anymore */
662   __SD_task_set_state(task, SD_RUNNING);
663   xbt_assert2(__SD_task_is_running(task), "Bad state of task '%s': %d",
664               SD_task_get_name(task), SD_task_get_state(task));
665
666 }
667
668 /* Tries to run a task. This function is called by SD_simulate() when a scheduled task becomes SD_READY
669  * (ie when its dependencies are satisfied).
670  * If one of the workstations where the task is scheduled on is busy (in sequential mode),
671  * the task doesn't start.
672  * Returns whether the task has started.
673  */
674 int __SD_task_try_to_run(SD_task_t task) {
675
676   int can_start = 1;
677   int i;
678   SD_workstation_t workstation;
679
680   SD_CHECK_INIT_DONE();
681   xbt_assert0(task != NULL, "Invalid parameter");
682   xbt_assert2(__SD_task_is_ready(task), "Task '%s' is not ready! Task state: %d",
683            SD_task_get_name(task), SD_task_get_state(task));
684
685
686   for (i = 0; i < task->workstation_nb; i++) {
687     can_start = !__SD_workstation_is_busy(task->workstation_list[i]);
688   }
689
690   DEBUG2("Task '%s' can start: %d", SD_task_get_name(task), can_start);
691   
692   if (!can_start) { /* if the task cannot start and is not in the fifos yet*/
693     for (i = 0; i < task->workstation_nb; i++) {
694       workstation = task->workstation_list[i];
695       if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
696         DEBUG2("Pushing task '%s' in the fifo of workstation '%s'", SD_task_get_name(task),
697                SD_workstation_get_name(workstation));
698         xbt_fifo_push(workstation->task_fifo, task);
699       }
700     }
701     __SD_task_set_state(task, SD_IN_FIFO);
702     xbt_assert2(__SD_task_is_in_fifo(task), "Bad state of task '%s': %d",
703                 SD_task_get_name(task), SD_task_get_state(task));
704     DEBUG1("Task '%s' state is now SD_IN_FIFO", SD_task_get_name(task));
705   }
706   else {
707     __SD_task_really_run(task);
708   }
709
710   return can_start;
711 }
712
713 /* This function is called by SD_simulate when a task is done.
714  * It updates task->state and task->action and executes if necessary the tasks
715  * which were waiting in fifos for the end of `task'
716  */
717 void __SD_task_just_done(SD_task_t task) {
718    int i, j;
719   SD_workstation_t workstation;
720
721   SD_task_t candidate;
722   int candidate_nb = 0;
723   int candidate_capacity = 8;
724   SD_task_t *candidates;
725   int can_start = 1;
726
727   SD_CHECK_INIT_DONE();
728   xbt_assert0(task != NULL, "Invalid parameter");
729   xbt_assert1(__SD_task_is_running(task), "The task must be running! Task state: %d", SD_task_get_state(task));
730   xbt_assert1(task->workstation_list != NULL, "Task '%s': workstation_list is NULL!", SD_task_get_name(task));
731
732
733   candidates = xbt_new(SD_task_t, 8);
734
735   __SD_task_set_state(task, SD_DONE);
736   surf_workstation_resource->common_public->action_free(task->surf_action);
737   task->surf_action = NULL;
738
739   DEBUG0("Looking for candidates");
740
741   /* if the task was executed on sequential workstations,
742      maybe we can execute the next task of the fifo for each workstation */
743   for (i = 0; i < task->workstation_nb; i++) {
744     workstation = task->workstation_list[i];
745     DEBUG2("Workstation '%s': access_mode = %d", SD_workstation_get_name(workstation), workstation->access_mode);
746     if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
747       xbt_assert1(workstation->task_fifo != NULL, "Workstation '%s' has sequential access but no fifo!",
748                   SD_workstation_get_name(workstation));
749       xbt_assert2(workstation->current_task = task, "Workstation '%s': current task should be '%s'",
750                   SD_workstation_get_name(workstation), SD_task_get_name(task));
751
752       /* the task is over so we can release the workstation */
753       workstation->current_task = NULL;
754
755       DEBUG0("Getting candidate in fifo");
756       candidate = xbt_fifo_get_item_content(xbt_fifo_get_first_item(workstation->task_fifo));
757
758       if (candidate != NULL) {
759         DEBUG1("Candidate: '%s'", SD_task_get_name(candidate));
760         xbt_assert2(__SD_task_is_in_fifo(candidate), "Bad state of candidate '%s': %d",
761                     SD_task_get_name(candidate), SD_task_get_state(candidate));
762       }
763
764       DEBUG1("Candidate in fifo: %p", candidate);
765
766       /* if there was a task waiting for my place */
767       if (candidate != NULL) {
768         /* Unfortunately, we are not sure yet that we can execute the task now,
769            because the task can be waiting more deeply in some other workstation's fifos...
770            So we memorize all candidate tasks, and then we will check for each candidate
771            whether or not all its workstations are available. */
772
773         /* realloc if necessary */
774         if (candidate_nb == candidate_capacity) {
775           candidate_capacity *= 2;
776           candidates = xbt_realloc(candidates, sizeof(SD_task_t) * candidate_capacity);
777         }
778
779         /* register the candidate */
780         candidates[candidate_nb++] = candidate;
781         candidate->fifo_checked = 0;
782       }
783     }
784   }
785
786   DEBUG1("Candidates found: %d", candidate_nb);
787
788   /* now we check every candidate task */
789   for (i = 0; i < candidate_nb; i++) {
790     candidate = candidates[i];
791
792     if (candidate->fifo_checked) {
793       continue; /* we have already evaluated that task*/
794     }
795
796     xbt_assert2(__SD_task_is_in_fifo(candidate), "Bad state of candidate '%s': %d",
797                 SD_task_get_name(candidate), SD_task_get_state(candidate));
798
799     for (j = 0; j < candidate->workstation_nb && can_start; j++) {
800       workstation = candidate->workstation_list[j];
801
802       /* I can start on this workstation if the workstation is shared
803          or if I am the first task in the fifo */
804       can_start = workstation->access_mode == SD_WORKSTATION_SHARED_ACCESS ||
805         candidate == xbt_fifo_get_item_content(xbt_fifo_get_first_item(workstation->task_fifo));
806     }
807
808     DEBUG2("Candidate '%s' can start: %d", SD_task_get_name(candidate), can_start);
809
810     /* now we are sure that I can start! */
811     if (can_start) {
812       for (j = 0; j < candidate->workstation_nb && can_start; j++) {
813         workstation = candidate->workstation_list[j];
814
815         /* update the fifo */
816         if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
817           candidate = xbt_fifo_shift(workstation->task_fifo); /* the return value is stored just for debugging */
818           DEBUG1("Head of the fifo: '%s'", (candidate != NULL) ? SD_task_get_name(candidate) : "NULL");
819           xbt_assert0(candidate == candidates[i], "Error in __SD_task_just_done: bad first task in the fifo");
820         }
821       } /* for each workstation */
822       
823       /* finally execute the task */
824       DEBUG2("Task '%s' state: %d", SD_task_get_name(candidate), SD_task_get_state(candidate));
825       __SD_task_really_run(candidate);
826       
827       DEBUG4("Calling __SD_task_is_running: task '%s', state set: %p, running_task_set: %p, is running: %d",
828              SD_task_get_name(candidate), candidate->state_set, sd_global->running_task_set, __SD_task_is_running(candidate));
829       xbt_assert2(__SD_task_is_running(candidate), "Bad state of task '%s': %d",
830                   SD_task_get_name(candidate), SD_task_get_state(candidate));
831       DEBUG0("Okay, the task is running.");
832
833     } /* can start */
834     candidate->fifo_checked = 1;
835   } /* for each candidate */ 
836   
837   xbt_free(candidates);
838 }
839
840 /* Remove all dependencies associated with a task. This function is called when the task is destroyed.
841  */
842 static void __SD_task_remove_dependencies(SD_task_t task) {
843   /* we must destroy the dependencies carefuly (with SD_dependency_remove)
844      because each one is stored twice */
845   SD_dependency_t dependency;
846   while (xbt_dynar_length(task->tasks_before) > 0) {
847     xbt_dynar_get_cpy(task->tasks_before, 0, &dependency);
848     SD_task_dependency_remove(dependency->src, dependency->dst);
849   }
850
851   while (xbt_dynar_length(task->tasks_after) > 0) {
852     xbt_dynar_get_cpy(task->tasks_after, 0, &dependency);
853     SD_task_dependency_remove(dependency->src, dependency->dst);
854   }
855 }
856
857 /**
858  * \brief Returns the start time of a task
859  *
860  * The task state must be SD_RUNNING, SD_DONE or SD_FAILED.
861  *
862  * \param task: a task
863  * \return the start time of this task
864  */
865 double SD_task_get_start_time(SD_task_t task) {
866   SD_CHECK_INIT_DONE();
867   xbt_assert0(task != NULL, "Invalid parameter");
868   if(task->surf_action)
869     return surf_workstation_resource->common_public->action_get_start_time(task->surf_action);
870   else 
871     return task->start_time;
872 }
873
874 /**
875  * \brief Returns the finish time of a task
876  *
877  * The task state must be SD_RUNNING, SD_DONE or SD_FAILED.
878  * If the state is not completed yet, the returned value is an
879  * estimation of the task finish time. This value can fluctuate 
880  * until the task is completed.
881  *
882  * \param task: a task
883  * \return the start time of this task
884  */
885 double SD_task_get_finish_time(SD_task_t task) {
886   SD_CHECK_INIT_DONE();
887   xbt_assert0(task != NULL, "Invalid parameter");
888
889   if(task->surf_action) /* should never happen as actions are destroyed right after their completion */
890     return surf_workstation_resource->common_public->action_get_finish_time(task->surf_action);
891   else 
892     return task->finish_time;
893 }
894
895 /**
896  * \brief Destroys a task.
897  *
898  * The user data (if any) should have been destroyed first.
899  *
900  * \param task the task you want to destroy
901  * \see SD_task_create()
902  */
903 void SD_task_destroy(SD_task_t task) {
904   SD_CHECK_INIT_DONE();
905   xbt_assert0(task != NULL, "Invalid parameter");
906
907   DEBUG1("Destroying task %s...", SD_task_get_name(task));
908
909   __SD_task_remove_dependencies(task);
910
911   /* if the task was scheduled or ready we have to free the scheduling parameters */
912   if (__SD_task_is_scheduled_or_ready(task))
913     __SD_task_destroy_scheduling_data(task);
914
915   if (task->name != NULL)
916     xbt_free(task->name);
917
918   if (task->surf_action != NULL)
919     surf_workstation_resource->common_public->action_free(task->surf_action);
920
921   if (task->workstation_list != NULL)
922     xbt_free(task->workstation_list);
923
924   xbt_dynar_free(&task->tasks_before);
925   xbt_dynar_free(&task->tasks_after);
926   xbt_free(task);
927
928   sd_global->task_number--;
929
930   DEBUG0("Task destroyed.");
931 }