Logo AND Algorithmique Numérique Distribuée

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