Logo AND Algorithmique Numérique Distribuée

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