Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Declare all variables at the begining for ANSI C standard compatibility.
[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 Remove a dependency between two tasks
306  *
307  * \param src a task
308  * \param dst a task depending on \a src
309  * \see SD_task_dependency_add()
310  */
311 void SD_task_dependency_remove(SD_task_t src, SD_task_t dst) {
312
313   xbt_dynar_t dynar;
314   int length;
315   int found = 0;
316   int i;
317   SD_dependency_t dependency;
318
319   SD_CHECK_INIT_DONE();
320   xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
321
322   /* remove the dependency from src->tasks_after */
323   dynar = src->tasks_after;
324   length = xbt_dynar_length(dynar);
325
326   for (i = 0; i < length && !found; i++) {
327     xbt_dynar_get_cpy(dynar, i, &dependency);
328     if (dependency->dst == dst) {
329       xbt_dynar_remove_at(dynar, i, NULL);
330       found = 1;
331     }
332   }
333   if (!found)
334     THROW4(arg_error, 0,
335            "No dependency found between task '%s' and '%s': task '%s' is not a successor of task '%s'",
336            SD_task_get_name(src), SD_task_get_name(dst), SD_task_get_name(dst), SD_task_get_name(src));
337
338   /* remove the dependency from dst->tasks_before */
339   dynar = dst->tasks_before;
340   length = xbt_dynar_length(dynar);
341   found = 0;
342   
343   for (i = 0; i < length && !found; i++) {
344     xbt_dynar_get_cpy(dynar, i, &dependency);
345     if (dependency->src == src) {
346       xbt_dynar_remove_at(dynar, i, NULL);
347       __SD_task_dependency_destroy(dependency);
348       found = 1;
349     }
350   }
351   /* should never happen... */
352   xbt_assert4(found, "SimDag error: task '%s' is a successor of '%s' but task '%s' is not a predecessor of task '%s'",
353               SD_task_get_name(dst), SD_task_get_name(src), SD_task_get_name(src), SD_task_get_name(dst));
354
355   /* if the task was scheduled and dst->tasks_before is empty now, we can make it ready */
356   if (xbt_dynar_length(dst->tasks_before) == 0 && __SD_task_is_scheduled(dst))
357     __SD_task_set_state(dst, SD_READY);
358
359   /*  __SD_print_dependencies(src); 
360       __SD_print_dependencies(dst);*/
361 }
362
363 /**
364  * \brief Returns the user data associated with a dependency between two tasks
365  *
366  * \param src a task
367  * \param dst a task depending on \a src
368  * \return the user data associated with this dependency (can be \c NULL)
369  * \see SD_task_dependency_add()
370  */
371 void *SD_task_dependency_get_data(SD_task_t src, SD_task_t dst) {
372
373   xbt_dynar_t dynar;
374   int length;
375   int found = 0;
376   int i;
377   SD_dependency_t dependency;
378
379
380   SD_CHECK_INIT_DONE();
381   xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
382
383   dynar = src->tasks_after;
384   length = xbt_dynar_length(dynar);
385
386   for (i = 0; i < length && !found; i++) {
387     xbt_dynar_get_cpy(dynar, i, &dependency);
388     found = (dependency->dst == dst);
389   }
390   if (!found)
391     THROW2(arg_error, 0, "No dependency found between task '%s' and '%s'", SD_task_get_name(src), SD_task_get_name(dst));
392   return dependency->data;
393 }
394
395 /* temporary function for debugging */
396 static void __SD_print_watch_points(SD_task_t task) {
397   static const int state_masks[] = {SD_SCHEDULED, SD_RUNNING, SD_READY, SD_DONE, SD_FAILED};
398   static const char* state_names[] = {"scheduled", "running", "ready", "done", "failed"};
399   int i;
400
401   INFO2("Task '%s' watch points (%x): ", SD_task_get_name(task), task->watch_points);
402
403
404   for (i = 0; i < 5; i++) {
405     if (task->watch_points & state_masks[i])
406       INFO1("%s ", state_names[i]);
407   }
408 }
409
410 /**
411  * \brief Adds a watch point to a task
412  *
413  * SD_simulate() will stop as soon as the \ref e_SD_task_state_t "state" of this
414  * task becomes the one given in argument. The
415  * watch point is then automatically removed.
416  * 
417  * \param task a task
418  * \param state the \ref e_SD_task_state_t "state" you want to watch
419  * (cannot be #SD_NOT_SCHEDULED)
420  * \see SD_task_unwatch()
421  */
422 void SD_task_watch(SD_task_t task, e_SD_task_state_t state) {
423   SD_CHECK_INIT_DONE();
424   xbt_assert0(task != NULL, "Invalid parameter");
425
426   if (state & SD_NOT_SCHEDULED)
427     THROW0(arg_error, 0, "Cannot add a watch point for state SD_NOT_SCHEDULED");
428
429   task->watch_points = task->watch_points | state;
430   /*  __SD_print_watch_points(task);*/
431 }
432
433 /**
434  * \brief Removes a watch point from a task
435  * 
436  * \param task a task
437  * \param state the \ref e_SD_task_state_t "state" you no longer want to watch
438  * \see SD_task_watch()
439  */
440 void SD_task_unwatch(SD_task_t task, e_SD_task_state_t state) {
441   SD_CHECK_INIT_DONE();
442   xbt_assert0(task != NULL, "Invalid parameter");
443   xbt_assert0(state != SD_NOT_SCHEDULED,
444               "SimDag error: Cannot have a watch point for state SD_NOT_SCHEDULED");
445   
446   task->watch_points = task->watch_points & ~state;
447   /*  __SD_print_watch_points(task);*/
448 }
449
450 /**
451  * \brief Returns an approximative estimation of the execution time of a task.
452  * 
453  * The estimation is very approximative because the value returned is the time
454  * the task would take if it was executed now and if it was the only task.
455  * 
456  * \param task the task to evaluate
457  * \param workstation_nb number of workstations on which the task would be executed
458  * \param workstation_list the workstations on which the task would be executed
459  * \param computation_amount computation amount for each workstation
460  * \param communication_amount communication amount between each pair of workstations
461  * \param rate task execution speed rate
462  * \see SD_schedule()
463  */
464 double SD_task_get_execution_time(SD_task_t task,
465                                   int workstation_nb,
466                                   const SD_workstation_t *workstation_list,
467                                   const double *computation_amount,
468                                   const double *communication_amount,
469                                   double rate) {
470   double time, max_time = 0.0;
471   int i, j;
472   SD_CHECK_INIT_DONE();
473   xbt_assert0(task != NULL && workstation_nb > 0 && workstation_list != NULL &&
474               computation_amount != NULL && communication_amount != NULL,
475               "Invalid parameter");
476
477   /* the task execution time is the maximum execution time of the parallel tasks */
478
479   for (i = 0; i < workstation_nb; i++) {
480     time = SD_workstation_get_computation_time(workstation_list[i], computation_amount[i]);
481     
482     for (j = 0; j < workstation_nb; j++) {
483       time += SD_route_get_communication_time(workstation_list[i], workstation_list[j],
484                                               communication_amount[i * workstation_nb + j]);
485     }
486
487     if (time > max_time) {
488       max_time = time;
489     }
490   }
491   return max_time * SD_task_get_amount(task);
492 }
493
494 /**
495  * \brief Schedules a task
496  *
497  * The task state must be #SD_NOT_SCHEDULED.
498  * Once scheduled, a task will be executed as soon as possible in SD_simulate(),
499  * i.e. when its dependencies are satisfied.
500  * 
501  * \param task the task you want to schedule
502  * \param workstation_nb number of workstations on which the task will be executed
503  * \param workstation_list the workstations on which the task will be executed
504  * \param computation_amount computation amount for each workstation
505  * \param communication_amount communication amount between each pair of workstations
506  * \param rate task execution speed rate
507  * \see SD_task_unschedule()
508  */
509 void SD_task_schedule(SD_task_t task, int workstation_nb,
510                      const SD_workstation_t *workstation_list, const double *computation_amount,
511                      const double *communication_amount, double rate) {
512
513   int communication_nb;
514   
515   SD_CHECK_INIT_DONE();
516   xbt_assert0(task != NULL, "Invalid parameter");
517   xbt_assert0(workstation_nb > 0, "workstation_nb must be positive");
518
519   if (!__SD_task_is_not_scheduled(task))
520     THROW1(arg_error, 0, "Task '%s' has already been scheduled", SD_task_get_name(task));
521
522   task->workstation_nb = workstation_nb;
523   task->rate = rate;
524
525   task->computation_amount = xbt_new(double, workstation_nb);
526   memcpy(task->computation_amount, computation_amount, sizeof(double) * workstation_nb);
527
528   communication_nb = workstation_nb * workstation_nb;
529   task->communication_amount = xbt_new(double, communication_nb);
530   memcpy(task->communication_amount, communication_amount, sizeof(double) * communication_nb);
531
532   task->workstation_list = xbt_new(SD_workstation_t, workstation_nb);
533   memcpy(task->workstation_list, workstation_list, sizeof(SD_workstation_t) * workstation_nb);
534
535   /* update the task state */
536   if (xbt_dynar_length(task->tasks_before) == 0)
537     __SD_task_set_state(task, SD_READY);
538   else
539     __SD_task_set_state(task, SD_SCHEDULED);
540 }
541
542 /**
543  * \brief Unschedules a task
544  *
545  * The task state must be #SD_SCHEDULED, #SD_READY, #SD_RUNNING or #SD_FAILED.
546  * If you call this function, the task state becomes #SD_NOT_SCHEDULED.
547  * Call SD_task_schedule() to schedule it again.
548  *
549  * \param task the task you want to unschedule
550  * \see SD_task_schedule()
551  */
552 void SD_task_unschedule(SD_task_t task) {
553   SD_CHECK_INIT_DONE();
554   xbt_assert0(task != NULL, "Invalid parameter");
555
556   if (task->state_set != sd_global->scheduled_task_set &&
557       task->state_set != sd_global->ready_task_set &&
558       task->state_set != sd_global->running_task_set &&
559       task->state_set != sd_global->failed_task_set)
560     THROW1(arg_error, 0, "Task %s: the state must be SD_SCHEDULED, SD_READY, SD_RUNNING or SD_FAILED",
561            SD_task_get_name(task));
562
563   if (__SD_task_is_scheduled_or_ready(task)) /* if the task is scheduled or ready */
564     __SD_task_destroy_scheduling_data(task);
565
566   if (__SD_task_is_running(task)) /* the task should become SD_FAILED */
567     surf_workstation_resource->common_public->action_cancel(task->surf_action);
568   else
569     __SD_task_set_state(task, SD_NOT_SCHEDULED);
570   task->remains = task->amount;
571   task->start_time = -1.0;
572 }
573
574 /* Destroys the data memorised by SD_task_schedule. Task state must be SD_SCHEDULED or SD_READY.
575  */
576 static void __SD_task_destroy_scheduling_data(SD_task_t task) {
577   SD_CHECK_INIT_DONE();
578   if (!__SD_task_is_scheduled_or_ready(task) && !__SD_task_is_in_fifo(task))
579     THROW1(arg_error, 0, "Task '%s' must be SD_SCHEDULED, SD_READY or SD_IN_FIFO", SD_task_get_name(task));
580
581   xbt_free(task->computation_amount);
582   xbt_free(task->communication_amount);
583 }
584
585 /* Runs a task. This function is directly called by __SD_task_try_to_run if the task
586  * doesn't have to wait in fifos. Otherwise, it is called by __SD_task_just_done when
587  * the task gets out of its fifos.
588  */
589 void __SD_task_really_run(SD_task_t task) {
590
591   int i;
592   void **surf_workstations;
593
594   SD_CHECK_INIT_DONE();
595   xbt_assert0(task != NULL, "Invalid parameter");
596   xbt_assert2(__SD_task_is_ready_or_in_fifo(task), "Task '%s' is not ready or in a fifo! Task state: %d",
597            SD_task_get_name(task), SD_task_get_state(task));
598   xbt_assert1(task->workstation_list != NULL, "Task '%s': workstation_list is NULL!", SD_task_get_name(task));
599
600
601
602   DEBUG1("Really running task '%s'", SD_task_get_name(task));
603
604   /* set this task as current task for the workstations in sequential mode */
605   for (i = 0; i < task->workstation_nb; i++) {
606     if (SD_workstation_get_access_mode(task->workstation_list[i]) == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
607       task->workstation_list[i]->current_task = task;
608       xbt_assert0(__SD_workstation_is_busy(task->workstation_list[i]), "The workstation should be busy now");
609     }
610   }
611   
612   DEBUG1("Task '%s' set as current task for its workstations", SD_task_get_name(task));
613
614   /* start the task */
615
616   /* we have to create a Surf workstation array instead of the SimDag workstation array */
617   surf_workstations = xbt_new(void*, task->workstation_nb);
618
619   for (i = 0; i < task->workstation_nb; i++) {
620     surf_workstations[i] = task->workstation_list[i]->surf_workstation;
621   }
622   
623   task->surf_action = surf_workstation_resource->extension_public->
624     execute_parallel_task(task->workstation_nb,
625                           surf_workstations,
626                           task->computation_amount,
627                           task->communication_amount,
628                           task->amount,
629                           task->rate);
630   surf_workstation_resource->common_public->action_set_data(task->surf_action, task);
631   task->state_changed = 1;
632
633   DEBUG1("surf_action = %p",  task->surf_action);
634
635   xbt_free(surf_workstations);
636   __SD_task_destroy_scheduling_data(task); /* now the scheduling data are not useful anymore */
637   __SD_task_set_state(task, SD_RUNNING);
638   xbt_assert2(__SD_task_is_running(task), "Bad state of task '%s': %d",
639               SD_task_get_name(task), SD_task_get_state(task));
640
641 }
642
643 /* Tries to run a task. This function is called by SD_simulate() when a scheduled task becomes SD_READY
644  * (ie when its dependencies are satisfied).
645  * If one of the workstations where the task is scheduled on is busy (in sequential mode),
646  * the task doesn't start.
647  * Returns whether the task has started.
648  */
649 int __SD_task_try_to_run(SD_task_t task) {
650
651   int can_start = 1;
652   int i;
653   SD_workstation_t workstation;
654
655   SD_CHECK_INIT_DONE();
656   xbt_assert0(task != NULL, "Invalid parameter");
657   xbt_assert2(__SD_task_is_ready(task), "Task '%s' is not ready! Task state: %d",
658            SD_task_get_name(task), SD_task_get_state(task));
659
660
661   for (i = 0; i < task->workstation_nb; i++) {
662     can_start = !__SD_workstation_is_busy(task->workstation_list[i]);
663   }
664
665   DEBUG2("Task '%s' can start: %d", SD_task_get_name(task), can_start);
666   
667   if (!can_start) { /* if the task cannot start and is not in the fifos yet*/
668     for (i = 0; i < task->workstation_nb; i++) {
669       workstation = task->workstation_list[i];
670       if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
671         DEBUG2("Pushing task '%s' in the fifo of workstation '%s'", SD_task_get_name(task),
672                SD_workstation_get_name(workstation));
673         xbt_fifo_push(workstation->task_fifo, task);
674       }
675     }
676     __SD_task_set_state(task, SD_IN_FIFO);
677     xbt_assert2(__SD_task_is_in_fifo(task), "Bad state of task '%s': %d",
678                 SD_task_get_name(task), SD_task_get_state(task));
679     DEBUG1("Task '%s' state is now SD_IN_FIFO", SD_task_get_name(task));
680   }
681   else {
682     __SD_task_really_run(task);
683   }
684
685   return can_start;
686 }
687
688 /* This function is called by SD_simulate when a task is done.
689  * It updates task->state and task->action and executes if necessary the tasks
690  * which were waiting in fifos for the end of `task'
691  */
692 void __SD_task_just_done(SD_task_t task) {
693    int i, j;
694   SD_workstation_t workstation;
695
696   SD_task_t candidate;
697   int candidate_nb = 0;
698   int candidate_capacity = 8;
699   SD_task_t *candidates;
700   int can_start = 1;
701
702   SD_CHECK_INIT_DONE();
703   xbt_assert0(task != NULL, "Invalid parameter");
704   xbt_assert1(__SD_task_is_running(task), "The task must be running! Task state: %d", SD_task_get_state(task));
705   xbt_assert1(task->workstation_list != NULL, "Task '%s': workstation_list is NULL!", SD_task_get_name(task));
706
707
708   candidates = xbt_new(SD_task_t, 8);
709
710   __SD_task_set_state(task, SD_DONE);
711   surf_workstation_resource->common_public->action_free(task->surf_action);
712   task->surf_action = NULL;
713
714   DEBUG0("Looking for candidates");
715
716   /* if the task was executed on sequential workstations,
717      maybe we can execute the next task of the fifo for each workstation */
718   for (i = 0; i < task->workstation_nb; i++) {
719     workstation = task->workstation_list[i];
720     DEBUG2("Workstation '%s': access_mode = %d", SD_workstation_get_name(workstation), workstation->access_mode);
721     if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
722       xbt_assert1(workstation->task_fifo != NULL, "Workstation '%s' has sequential access but no fifo!",
723                   SD_workstation_get_name(workstation));
724       xbt_assert2(workstation->current_task = task, "Workstation '%s': current task should be '%s'",
725                   SD_workstation_get_name(workstation), SD_task_get_name(task));
726
727       /* the task is over so we can release the workstation */
728       workstation->current_task = NULL;
729
730       DEBUG0("Getting candidate in fifo");
731       candidate = xbt_fifo_get_item_content(xbt_fifo_get_first_item(workstation->task_fifo));
732
733       if (candidate != NULL) {
734         DEBUG1("Candidate: '%s'", SD_task_get_name(candidate));
735         xbt_assert2(__SD_task_is_in_fifo(candidate), "Bad state of candidate '%s': %d",
736                     SD_task_get_name(candidate), SD_task_get_state(candidate));
737       }
738
739       DEBUG1("Candidate in fifo: %p", candidate);
740
741       /* if there was a task waiting for my place */
742       if (candidate != NULL) {
743         /* Unfortunately, we are not sure yet that we can execute the task now,
744            because the task can be waiting more deeply in some other workstation's fifos...
745            So we memorize all candidate tasks, and then we will check for each candidate
746            whether or not all its workstations are available. */
747
748         /* realloc if necessary */
749         if (candidate_nb == candidate_capacity) {
750           candidate_capacity *= 2;
751           candidates = xbt_realloc(candidates, sizeof(SD_task_t) * candidate_capacity);
752         }
753
754         /* register the candidate */
755         candidates[candidate_nb++] = candidate;
756         candidate->fifo_checked = 0;
757       }
758     }
759   }
760
761   DEBUG1("Candidates found: %d", candidate_nb);
762
763   /* now we check every candidate task */
764   for (i = 0; i < candidate_nb; i++) {
765     candidate = candidates[i];
766
767     if (candidate->fifo_checked) {
768       continue; /* we have already evaluated that task*/
769     }
770
771     xbt_assert2(__SD_task_is_in_fifo(candidate), "Bad state of candidate '%s': %d",
772                 SD_task_get_name(candidate), SD_task_get_state(candidate));
773
774     for (j = 0; j < candidate->workstation_nb && can_start; j++) {
775       workstation = candidate->workstation_list[j];
776
777       /* I can start on this workstation if the workstation is shared
778          or if I am the first task in the fifo */
779       can_start = workstation->access_mode == SD_WORKSTATION_SHARED_ACCESS ||
780         candidate == xbt_fifo_get_item_content(xbt_fifo_get_first_item(workstation->task_fifo));
781     }
782
783     DEBUG2("Candidate '%s' can start: %d", SD_task_get_name(candidate), can_start);
784
785     /* now we are sure that I can start! */
786     if (can_start) {
787       for (j = 0; j < candidate->workstation_nb && can_start; j++) {
788         workstation = candidate->workstation_list[j];
789
790         /* update the fifo */
791         if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
792           candidate = xbt_fifo_shift(workstation->task_fifo); /* the return value is stored just for debugging */
793           DEBUG1("Head of the fifo: '%s'", (candidate != NULL) ? SD_task_get_name(candidate) : "NULL");
794           xbt_assert0(candidate == candidates[i], "Error in __SD_task_just_done: bad first task in the fifo");
795         }
796       } /* for each workstation */
797       
798       /* finally execute the task */
799       DEBUG2("Task '%s' state: %d", SD_task_get_name(candidate), SD_task_get_state(candidate));
800       __SD_task_really_run(candidate);
801       
802       DEBUG4("Calling __SD_task_is_running: task '%s', state set: %p, running_task_set: %p, is running: %d",
803              SD_task_get_name(candidate), candidate->state_set, sd_global->running_task_set, __SD_task_is_running(candidate));
804       xbt_assert2(__SD_task_is_running(candidate), "Bad state of task '%s': %d",
805                   SD_task_get_name(candidate), SD_task_get_state(candidate));
806       DEBUG0("Okay, the task is running.");
807
808     } /* can start */
809     candidate->fifo_checked = 1;
810   } /* for each candidate */ 
811   
812   xbt_free(candidates);
813 }
814
815 /* Remove all dependencies associated with a task. This function is called when the task is destroyed.
816  */
817 static void __SD_task_remove_dependencies(SD_task_t task) {
818   /* we must destroy the dependencies carefuly (with SD_dependency_remove)
819      because each one is stored twice */
820   SD_dependency_t dependency;
821   while (xbt_dynar_length(task->tasks_before) > 0) {
822     xbt_dynar_get_cpy(task->tasks_before, 0, &dependency);
823     SD_task_dependency_remove(dependency->src, dependency->dst);
824   }
825
826   while (xbt_dynar_length(task->tasks_after) > 0) {
827     xbt_dynar_get_cpy(task->tasks_after, 0, &dependency);
828     SD_task_dependency_remove(dependency->src, dependency->dst);
829   }
830 }
831
832 /**
833  * \brief Returns the start time of a task
834  *
835  * The task state must be SD_RUNNING, SD_DONE or SD_FAILED.
836  *
837  * \param task: a task
838  * \return the start time of this task
839  */
840 double SD_task_get_start_time(SD_task_t task) {
841   SD_CHECK_INIT_DONE();
842   xbt_assert0(task != NULL, "Invalid parameter");
843   if(task->surf_action)
844     return surf_workstation_resource->common_public->action_get_start_time(task->surf_action);
845   else 
846     return task->start_time;
847 }
848
849 /**
850  * \brief Returns the finish time of a task
851  *
852  * The task state must be SD_RUNNING, SD_DONE or SD_FAILED.
853  * If the state is not completed yet, the returned value is an
854  * estimation of the task finish time. This value can fluctuate 
855  * until the task is completed.
856  *
857  * \param task: a task
858  * \return the start time of this task
859  */
860 double SD_task_get_finish_time(SD_task_t task) {
861   SD_CHECK_INIT_DONE();
862   xbt_assert0(task != NULL, "Invalid parameter");
863
864   if(task->surf_action) /* should never happen as actions are destroyed right after their completion */
865     return surf_workstation_resource->common_public->action_get_finish_time(task->surf_action);
866   else 
867     return task->finish_time;
868 }
869
870 /**
871  * \brief Destroys a task.
872  *
873  * The user data (if any) should have been destroyed first.
874  *
875  * \param task the task you want to destroy
876  * \see SD_task_create()
877  */
878 void SD_task_destroy(SD_task_t task) {
879   SD_CHECK_INIT_DONE();
880   xbt_assert0(task != NULL, "Invalid parameter");
881
882   DEBUG1("Destroying task %s...", SD_task_get_name(task));
883
884   __SD_task_remove_dependencies(task);
885
886   /* if the task was scheduled or ready we have to free the scheduling parameters */
887   if (__SD_task_is_scheduled_or_ready(task))
888     __SD_task_destroy_scheduling_data(task);
889
890   if (task->name != NULL)
891     xbt_free(task->name);
892
893   if (task->surf_action != NULL)
894     surf_workstation_resource->common_public->action_free(task->surf_action);
895
896   if (task->workstation_list != NULL)
897     xbt_free(task->workstation_list);
898
899   xbt_dynar_free(&task->tasks_before);
900   xbt_dynar_free(&task->tasks_after);
901   xbt_free(task);
902
903   sd_global->task_number--;
904
905   DEBUG0("Task destroyed.");
906 }