Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Remove attribute amount from structure task
[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 static void __SD_task_remove_dependencies(SD_task_t task);
7
8 /**
9  * \brief Creates a new task.
10  *
11  * \param name the name of the task (can be \c NULL)
12  * \param data the user data you want to associate with the task (can be \c NULL)
13  * \return the new task
14  * \see SD_task_destroy()
15  */
16 SD_task_t SD_task_create(const char *name, void *data) {
17   SD_CHECK_INIT_DONE();
18
19   SD_task_t task = xbt_new0(s_SD_task_t, 1);
20
21   /* general information */
22   task->data = data; /* user data */
23   if (name != NULL)
24     task->name = xbt_strdup(name);
25   else
26     task->name = NULL;
27
28   task->state_set = sd_global->not_scheduled_task_set;
29   xbt_swag_insert(task,task->state_set);
30
31   task->surf_action = NULL;
32   task->watch_points = 0;
33   task->state_changed = 0;
34
35   /* dependencies */
36   task->tasks_before = xbt_dynar_new(sizeof(SD_dependency_t), NULL);
37   task->tasks_after = xbt_dynar_new(sizeof(SD_dependency_t), NULL);
38
39   /* scheduling parameters */
40   task->workstation_nb = 0;
41   task->workstation_list = NULL;
42   task->computation_amount = NULL;
43   task->communication_amount = NULL;
44   task->rate = 0;
45
46   return task;
47 }
48
49 /**
50  * \brief Returns the user data of a task
51  *
52  * \param task a task
53  * \return the user data associated with this task (can be \c NULL)
54  * \see SD_task_set_data()
55  */
56 void* SD_task_get_data(SD_task_t task) {
57   SD_CHECK_INIT_DONE();
58   xbt_assert0(task != NULL, "Invalid parameter");
59   return task->data;
60 }
61
62 /**
63  * \brief Sets the user data of a task
64  *
65  * The new data can be \c NULL. The old data should have been freed first
66  * if it was not \c NULL.
67  *
68  * \param task a task
69  * \param data the new data you want to associate with this task
70  * \see SD_task_get_data()
71  */
72 void SD_task_set_data(SD_task_t task, void *data) {
73   SD_CHECK_INIT_DONE();
74   xbt_assert0(task != NULL, "Invalid parameter");
75   task->data = data;
76 }
77
78 /**
79  * \brief Returns the state of a task
80  *
81  * \param task a task
82  * \return the current \ref e_SD_task_state_t "state" of this task:
83  * #SD_NOT_SCHEDULED, #SD_SCHEDULED, #SD_READY, #SD_RUNNING, #SD_DONE or #SD_FAILED
84  * \see e_SD_task_state_t
85  */
86 e_SD_task_state_t SD_task_get_state(SD_task_t task) {
87   SD_CHECK_INIT_DONE();
88   xbt_assert0(task != NULL, "Invalid parameter");
89
90   if (task->state_set == sd_global->scheduled_task_set)
91     return SD_SCHEDULED;
92   if (task->state_set == sd_global->done_task_set)
93     return SD_DONE;
94   if (task->state_set == sd_global->running_task_set)
95     return SD_RUNNING;
96   if (task->state_set == sd_global->ready_task_set)
97     return SD_READY;
98   if (task->state_set == sd_global->not_scheduled_task_set)
99     return SD_NOT_SCHEDULED;
100   return SD_FAILED;
101 }
102
103 /* Changes the state of a task. Updates the swags and the flag sd_global->watch_point_reached.
104  */
105 void __SD_task_set_state(SD_task_t task, e_SD_task_state_t new_state) {
106   xbt_swag_remove(task, task->state_set);
107   switch (new_state) {
108   case SD_NOT_SCHEDULED:
109     task->state_set = sd_global->not_scheduled_task_set;
110     break;
111   case SD_SCHEDULED:
112     task->state_set = sd_global->scheduled_task_set;
113     break;
114   case SD_READY:
115     task->state_set = sd_global->ready_task_set;
116     break;
117   case SD_RUNNING:
118     task->state_set = sd_global->running_task_set;
119     break;
120   case SD_DONE:
121     task->state_set = sd_global->done_task_set;
122     break;
123   case SD_FAILED:
124     task->state_set = sd_global->failed_task_set;
125     break;
126   default:
127     xbt_assert0(0, "Invalid state");
128   }
129   xbt_swag_insert(task, task->state_set);
130
131   if (task->watch_points & new_state) {
132     printf("Watch point reached with task '%s'!\n", SD_task_get_name(task));
133     sd_global->watch_point_reached = 1;
134     SD_task_unwatch(task, new_state); /* remove the watch point */
135   }
136 }
137
138 /**
139  * \brief Returns the name of a task
140  *
141  * \param task a task
142  * \return the name of this task (can be \c NULL)
143  */
144 const char* SD_task_get_name(SD_task_t task) {
145   SD_CHECK_INIT_DONE();
146   xbt_assert0(task != NULL, "Invalid parameter");
147   return task->name;
148 }
149
150 /**
151  * \brief Returns the total amount of a task
152  *
153  * \param task a task
154  * \return the total amount of this task
155  * \see SD_task_get_remaining_amount()
156  */
157 double SD_task_get_amount(SD_task_t task) {
158   SD_CHECK_INIT_DONE();
159   xbt_assert0(task != NULL, "Invalid parameter");
160   xbt_assert1(task->surf_action != NULL, "The task '%s' is not running", SD_task_get_name(task));
161   return task->surf_action->cost;
162 }
163
164 /**
165  * \brief Returns the remaining amount of a task
166  *
167  * \param task a task
168  * \return the remaining amount of this task
169  * \see SD_task_get_amount()
170  */
171 double SD_task_get_remaining_amount(SD_task_t task) {
172   SD_CHECK_INIT_DONE();
173   xbt_assert0(task != NULL, "Invalid parameter");
174   xbt_assert1(task->surf_action != NULL, "The task '%s' is not running", SD_task_get_name(task));
175   return task->surf_action->remains / task->surf_action->cost;
176 }
177
178 /* temporary function for debbuging */
179 static void __SD_print_dependencies(SD_task_t task) {
180   printf("The following tasks must be executed before %s:", SD_task_get_name(task));
181   xbt_dynar_t dynar = task->tasks_before;
182   int length = xbt_dynar_length(dynar);
183   int i;
184   SD_dependency_t dependency;
185   for (i = 0; i < length; i++) {
186     xbt_dynar_get_cpy(dynar, i, &dependency);
187     printf(" %s", SD_task_get_name(dependency->src));
188   }
189
190   printf("\nThe following tasks must be executed after %s:", SD_task_get_name(task));
191
192   dynar = task->tasks_after;
193   length = xbt_dynar_length(dynar);
194   for (i = 0; i < length; i++) {
195     xbt_dynar_get_cpy(dynar, i, &dependency);
196     printf(" %s", SD_task_get_name(dependency->dst));
197   }
198   printf("\n----------------------------\n");
199 }
200
201 /* Destroys a dependency between two tasks.
202  */
203 static void __SD_task_dependency_destroy(void *dependency) {
204   if (((SD_dependency_t) dependency)->name != NULL)
205     xbt_free(((SD_dependency_t) dependency)->name);
206   xbt_free(dependency);
207 }
208
209 /**
210  * \brief Adds a dependency between two tasks
211  *
212  * \a dst will depend on \a src, ie \a dst will not start before \a src is finished.
213  * Their \ref e_SD_task_state_t "state" must be #SD_NOT_SCHEDULED, #SD_SCHEDULED or #SD_READY.
214  *
215  * \param name the name of the new dependency (can be \c NULL)
216  * \param data the user data you want to associate with this dependency (can be \c NULL)
217  * \param src the task which must be executed first
218  * \param dst the task you want to make depend on \a src
219  * \see SD_task_dependency_remove()
220  */
221 void SD_task_dependency_add(const char *name, void *data, SD_task_t src, SD_task_t dst) {
222   SD_CHECK_INIT_DONE();
223   xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
224   xbt_assert1(src != dst, "Cannot add a dependency between task '%s' and itself", SD_task_get_name(src));
225   xbt_assert1(__SD_task_is_not_scheduled(src) || __SD_task_is_scheduled_or_ready(src),
226               "Task '%s' must be SD_NOT_SCHEDULED, SD_SCHEDULED or SD_READY", SD_task_get_name(src));
227   xbt_assert1(__SD_task_is_not_scheduled(dst) || __SD_task_is_scheduled_or_ready(dst),
228               "Task '%s' must be SD_NOT_SCHEDULED, SD_SCHEDULED or SD_READY", SD_task_get_name(dst));
229
230   xbt_dynar_t dynar = src->tasks_after;
231   int length = xbt_dynar_length(dynar);
232   int found = 0;
233   int i;
234   SD_dependency_t dependency;
235   for (i = 0; i < length && !found; i++) {
236     xbt_dynar_get_cpy(dynar, i, &dependency);
237     found = (dependency->dst == dst);
238   }
239   xbt_assert2(!found, "A dependency already exists between task '%s' and task '%s'",
240               SD_task_get_name(src), SD_task_get_name(dst));
241
242   dependency = xbt_new0(s_SD_dependency_t, 1);
243
244   if (name != NULL)
245     dependency->name = xbt_strdup(name);
246   dependency->data = data;
247   dependency->src = src;
248   dependency->dst = dst;
249
250   /* src must be executed before dst */
251   xbt_dynar_push(src->tasks_after, &dependency);
252   xbt_dynar_push(dst->tasks_before, &dependency);
253
254   /* if the task was ready, then dst->tasks_before is not empty anymore,
255      so we must go back to state SD_SCHEDULED */
256   if (__SD_task_is_ready(dst)) {
257     printf("SD_task_dependency_add: %s was ready and becomes scheduled!\n", SD_task_get_name(dst));
258     __SD_task_set_state(dst, SD_SCHEDULED);
259   }
260
261   /*  __SD_print_dependencies(src);
262       __SD_print_dependencies(dst); */
263 }
264
265 /**
266  * \brief Remove a dependency between two tasks
267  *
268  * \param src a task
269  * \param dst a task depending on \a src
270  * \see SD_task_dependency_add()
271  */
272 void SD_task_dependency_remove(SD_task_t src, SD_task_t dst) {
273   SD_CHECK_INIT_DONE();
274   xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
275
276   /* remove the dependency from src->tasks_after */
277   xbt_dynar_t dynar = src->tasks_after;
278   int length = xbt_dynar_length(dynar);
279   int found = 0;
280   int i;
281   SD_dependency_t dependency;
282   for (i = 0; i < length && !found; i++) {
283     xbt_dynar_get_cpy(dynar, i, &dependency);
284     if (dependency->dst == dst) {
285       xbt_dynar_remove_at(dynar, i, NULL);
286       found = 1;
287     }
288   }
289   xbt_assert4(found, "No dependency found between task '%s' and '%s': task '%s' is not a successor of task '%s'",
290               SD_task_get_name(src), SD_task_get_name(dst), SD_task_get_name(dst), SD_task_get_name(src));
291
292   /* remove the dependency from dst->tasks_before */
293   dynar = dst->tasks_before;
294   length = xbt_dynar_length(dynar);
295   found = 0;
296   
297   for (i = 0; i < length && !found; i++) {
298     xbt_dynar_get_cpy(dynar, i, &dependency);
299     if (dependency->src == src) {
300       xbt_dynar_remove_at(dynar, i, NULL);
301       __SD_task_dependency_destroy(dependency);
302       found = 1;
303     }
304   }
305   xbt_assert4(found, "SimDag error: task '%s' is a successor of '%s' but task '%s' is not a predecessor of task '%s'",
306               SD_task_get_name(dst), SD_task_get_name(src), SD_task_get_name(src), SD_task_get_name(dst)); /* should never happen... */
307
308   /* if the task was scheduled and dst->tasks_before is empty now, we can make it ready */
309   if (xbt_dynar_length(dst->tasks_before) == 0 && __SD_task_is_scheduled(dst))
310     __SD_task_set_state(dst, SD_READY);
311
312   /*  __SD_print_dependencies(src); 
313       __SD_print_dependencies(dst);*/
314 }
315
316 /**
317  * \brief Returns the user data associated with a dependency between two tasks
318  *
319  * \param src a task
320  * \param dst a task depending on \a src
321  * \return the user data associated with this dependency (can be \c NULL)
322  * \see SD_task_dependency_add()
323  */
324 void *SD_task_dependency_get_data(SD_task_t src, SD_task_t dst) {
325   SD_CHECK_INIT_DONE();
326   xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
327
328   xbt_dynar_t dynar = src->tasks_after;
329   int length = xbt_dynar_length(dynar);
330   int found = 0;
331   int i;
332   SD_dependency_t dependency;
333   for (i = 0; i < length && !found; i++) {
334     xbt_dynar_get_cpy(dynar, i, &dependency);
335     found = (dependency->dst == dst);
336   }
337   xbt_assert2(found, "No dependency found between task '%s' and '%s'", SD_task_get_name(src), SD_task_get_name(dst));
338   return dependency->data;
339 }
340
341 /* temporary function for debugging */
342 static void __SD_print_watch_points(SD_task_t task) {
343   static const int state_masks[] = {SD_SCHEDULED, SD_RUNNING, SD_READY, SD_DONE, SD_FAILED};
344   static const char* state_names[] = {"scheduled", "running", "ready", "done", "failed"};
345
346   printf("Task '%s' watch points (%x): ", SD_task_get_name(task), task->watch_points);
347
348   int i;
349   for (i = 0; i < 5; i++) {
350     if (task->watch_points & state_masks[i])
351       printf("%s ", state_names[i]);
352   }
353   printf("\n");
354 }
355
356 /**
357  * \brief Adds a watch point to a task
358  *
359  * SD_simulate() will stop as soon as the \ref e_SD_task_state_t "state" of this task becomes the one given in argument. The
360  * watch point is then automatically removed.
361  * 
362  * \param task a task
363  * \param state the \ref e_SD_task_state_t "state" you want to watch (cannot be #SD_NOT_SCHEDULED)
364  * \see SD_task_unwatch()
365  */
366 void SD_task_watch(SD_task_t task, e_SD_task_state_t state) {
367   SD_CHECK_INIT_DONE();
368   xbt_assert0(task != NULL, "Invalid parameter");
369   xbt_assert0(state != SD_NOT_SCHEDULED, "Cannot add a watch point for state SD_NOT_SCHEDULED");
370
371   task->watch_points = task->watch_points | state;
372   /*  __SD_print_watch_points(task);*/
373 }
374
375 /**
376  * \brief Removes a watch point from a task
377  * 
378  * \param task a task
379  * \param state the \ref e_SD_task_state_t "state" you no longer want to watch
380  * \see SD_task_watch()
381  */
382 void SD_task_unwatch(SD_task_t task, e_SD_task_state_t state) {
383   SD_CHECK_INIT_DONE();
384   xbt_assert0(task != NULL, "Invalid parameter");
385   xbt_assert0(state != SD_NOT_SCHEDULED, "Cannot have a watch point for state SD_NOT_SCHEDULED");
386   
387   task->watch_points = task->watch_points & ~state;
388   /*  __SD_print_watch_points(task);*/
389 }
390
391 /* Destroys the data memorised by SD_task_schedule. Task state must be SD_SCHEDULED or SD_READY.
392  */
393 static void __SD_task_destroy_scheduling_data(SD_task_t task) {
394   SD_CHECK_INIT_DONE();
395   xbt_assert1(__SD_task_is_scheduled_or_ready(task),
396               "Task '%s' must be SD_SCHEDULED or SD_READY", SD_task_get_name(task));
397   xbt_free(task->workstation_list);
398   xbt_free(task->computation_amount);
399   xbt_free(task->communication_amount);
400 }
401
402 /**
403  * \brief Schedules a task
404  *
405  * The task state must be #SD_NOT_SCHEDULED.
406  * Once scheduled, a task will be executed as soon as possible in SD_simulate(),
407  * i.e. when its dependencies are satisfied.
408  * 
409  * \param task the task you want to schedule
410  * \param workstation_nb number of workstations on which the task will be executed
411  * \param workstation_list the workstations on which the task will be executed
412  * \param computation_amount computation amount for each workstation
413  * \param communication_amount communication amount between each pair of workstations
414  * \param rate task execution speed rate
415  * \see SD_task_unschedule()
416  */
417 void SD_task_schedule(SD_task_t task, int workstation_nb,
418                      const SD_workstation_t *workstation_list, const double *computation_amount,
419                      const double *communication_amount, double rate) {
420   SD_CHECK_INIT_DONE();
421   xbt_assert0(task, "Invalid parameter");
422   xbt_assert1(__SD_task_is_not_scheduled(task), "Task '%s' has already been scheduled.", SD_task_get_name(task));
423   xbt_assert0(workstation_nb > 0, "workstation_nb must be positive");
424
425   task->workstation_nb = workstation_nb;
426   task->rate = rate;
427
428   task->computation_amount = xbt_new0(double, workstation_nb);
429   memcpy(task->computation_amount, computation_amount, sizeof(double) * workstation_nb);
430
431   int communication_nb = workstation_nb * workstation_nb;
432   task->communication_amount = xbt_new0(double, communication_nb);
433   memcpy(task->communication_amount, communication_amount, sizeof(double) * communication_nb);
434
435   /* we have to create a Surf workstation array instead of the SimDag workstation array */
436   task->workstation_list = xbt_new0(void*, workstation_nb);
437   int i;
438   for (i = 0; i < workstation_nb; i++) {
439     task->workstation_list[i] = workstation_list[i]->surf_workstation;
440   }
441
442   /* update the task state */
443   if (xbt_dynar_length(task->tasks_before) == 0)
444     __SD_task_set_state(task, SD_READY);
445   else
446     __SD_task_set_state(task, SD_SCHEDULED);
447 }
448
449 /**
450  * \brief Unschedules a task
451  *
452  * The task state must be #SD_SCHEDULED, #SD_READY, #SD_RUNNING or #SD_FAILED.
453  * If you call this function, the task state becomes #SD_NOT_SCHEDULED.
454  * Call SD_task_schedule() to schedule it again.
455  *
456  * \param task the task you want to unschedule
457  * \see SD_task_schedule()
458  */
459 void SD_task_unschedule(SD_task_t task) {
460   SD_CHECK_INIT_DONE();
461   xbt_assert0(task != NULL, "Invalid parameter");
462   xbt_assert1(task->state_set == sd_global->scheduled_task_set ||
463               task->state_set == sd_global->ready_task_set ||
464               task->state_set == sd_global->running_task_set ||
465               task->state_set == sd_global->failed_task_set,
466               "Task %s: the state must be SD_SCHEDULED, SD_READY, SD_RUNNING or SD_FAILED",
467               SD_task_get_name(task));
468
469   if (__SD_task_is_scheduled_or_ready(task)) /* if the task is scheduled or ready */
470     __SD_task_destroy_scheduling_data(task);
471
472   if (__SD_task_is_running(task)) /* the task should become SD_FAILED */
473     surf_workstation_resource->common_public->action_cancel(task->surf_action);
474   else
475     __SD_task_set_state(task, SD_NOT_SCHEDULED);
476 }
477
478 /* Runs a task. This function is called by SD_simulate when a scheduled task can start
479  * (ie when its dependencies are satisfied).
480  */
481 surf_action_t __SD_task_run(SD_task_t task) {
482   SD_CHECK_INIT_DONE();
483   xbt_assert0(task != NULL, "Invalid parameter");
484   xbt_assert2(__SD_task_is_ready(task), "Task '%s' is not ready! Task state: %d",
485               SD_task_get_name(task), SD_task_get_state(task));
486
487   surf_action_t surf_action = surf_workstation_resource->extension_public->
488     execute_parallel_task(task->workstation_nb,
489                           task->workstation_list,
490                           task->computation_amount,
491                           task->communication_amount,
492                           1.0,
493                           task->rate);
494
495   __SD_task_destroy_scheduling_data(task); /* now the scheduling data are not useful anymore */
496   __SD_task_set_state(task, SD_RUNNING);
497
498   return surf_action;
499 }
500 /* Remove all dependencies associated with a task. This function is called when the task is destroyed.
501  */
502 static void __SD_task_remove_dependencies(SD_task_t task) {
503   /* we must destroy the dependencies carefuly (with SD_dependency_remove)
504      because each one is stored twice */
505   SD_dependency_t dependency;
506   while (xbt_dynar_length(task->tasks_before) > 0) {
507     xbt_dynar_get_cpy(task->tasks_before, 0, &dependency);
508     SD_task_dependency_remove(dependency->src, dependency->dst);
509   }
510
511   while (xbt_dynar_length(task->tasks_after) > 0) {
512     xbt_dynar_get_cpy(task->tasks_after, 0, &dependency);
513     SD_task_dependency_remove(dependency->src, dependency->dst);
514   }
515 }
516
517 /**
518  * \brief Destroys a task.
519  *
520  * The user data (if any) should have been destroyed first.
521  *
522  * \param task the task you want to destroy
523  * \see SD_task_create()
524  */
525 void SD_task_destroy(SD_task_t task) {
526   SD_CHECK_INIT_DONE();
527   xbt_assert0(task != NULL, "Invalid parameter");
528
529   /*printf("Destroying task %s...\n", SD_task_get_name(task));*/
530
531   __SD_task_remove_dependencies(task);
532
533   /* if the task was scheduled or ready we have to free the scheduling parameters */
534   if (__SD_task_is_scheduled_or_ready(task))
535     __SD_task_destroy_scheduling_data(task);
536
537   if (task->name != NULL)
538     xbt_free(task->name);
539
540   xbt_dynar_free(&task->tasks_before);
541   xbt_dynar_free(&task->tasks_after);
542   xbt_free(task);
543
544   /*printf("Task destroyed.\n");*/
545 }