Logo AND Algorithmique Numérique Distribuée

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