Logo AND Algorithmique Numérique Distribuée

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