]> git.tuebingen.mpg.de Git - paraslash.git/blob - sched.c
90df5933689c377ad2c570e36bb340eda74e9dae
[paraslash.git] / sched.c
1 /*
2  * Copyright (C) 2006-2008 Andre Noll <maan@systemlinux.org>
3  *
4  * Licensed under the GPL v2. For licencing details see COPYING.
5  */
6
7 /** \file sched.c paraslash's scheduling functions */
8
9 #include <dirent.h> /* readdir() */
10 #include <assert.h>
11 #include <sys/time.h>
12
13 #include "para.h"
14 #include "ipc.h"
15 #include "fd.h"
16 #include "list.h"
17 #include "sched.h"
18 #include "string.h"
19 #include "error.h"
20
21 static struct list_head pre_select_list, post_select_list;
22 static int initialized;
23
24 static struct timeval now_struct;
25 struct timeval *now = &now_struct;
26
27 /**
28  * Remove a task from the scheduler.
29  *
30  * \param t the task to remove
31  *
32  * If the pre_select pointer of \a t is not \p NULL, it is removed from
33  * the pre_select list of the scheduler. Same goes for \a post_select.
34  */
35 void unregister_task(struct task *t)
36 {
37         if (!initialized)
38                 return;
39         PARA_INFO_LOG("unregistering %s (%p)\n", t->status, t);
40         if (t->pre_select)
41                 list_del(&t->pre_select_node);
42         if (t->post_select)
43                 list_del(&t->post_select_node);
44         if (t->error >= 0)
45                 t->error = -E_TASK_KILLED;
46 };
47
48
49 static void sched_preselect(struct sched *s)
50 {
51         struct task *t, *tmp;
52         list_for_each_entry_safe(t, tmp, &pre_select_list, pre_select_node) {
53                 t->pre_select(s, t);
54 //              PARA_INFO_LOG("%s \n", t->status);
55                 if (t->error >= 0)
56                         continue;
57                 unregister_task(t);
58         }
59 }
60
61 static void sched_post_select(struct sched *s)
62 {
63         struct task *t, *tmp;
64
65         list_for_each_entry_safe(t, tmp, &post_select_list, post_select_node) {
66                 t->post_select(s, t);
67 //              PARA_INFO_LOG("%s: %d\n", t->status, t->ret);
68                 if (t->error >= 0)
69                         continue;
70                 unregister_task(t);
71         }
72 }
73
74 /**
75  * the core function for all paraslash programs
76  *
77  * \param s pointer to the scheduler struct
78  *
79  * This function updates the global \a now pointer, calls all registered
80  * pre_select hooks which may set the timeout and add any file descriptors to
81  * the fd sets of \a s.  Next, it calls para_select() and makes the result available
82  * to the registered tasks by calling their post_select hook.
83  *
84  * \return Zero if no more tasks are left in either of the two lists, negative
85  * if para_select returned an error.
86  *
87  * \sa task, now.
88  */
89 int schedule(struct sched *s)
90 {
91         int ret;
92
93         if (!initialized)
94                 return -E_NOT_INITIALIZED;
95         gettimeofday(now, NULL);
96 again:
97         FD_ZERO(&s->rfds);
98         FD_ZERO(&s->wfds);
99         s->timeout = s->default_timeout;
100         s->max_fileno = -1;
101         sched_preselect(s);
102         ret = para_select(s->max_fileno + 1, &s->rfds, &s->wfds, &s->timeout);
103         if (ret < 0)
104                 return ret;
105         gettimeofday(now, NULL);
106         sched_post_select(s);
107         if (list_empty(&pre_select_list) && list_empty(&post_select_list))
108                 return 0;
109         goto again;
110 }
111
112 /**
113  * initialize the paraslash scheduler
114  */
115 static void init_sched(void)
116 {
117         PARA_INFO_LOG("initializing scheduler\n");
118         INIT_LIST_HEAD(&pre_select_list);
119         INIT_LIST_HEAD(&post_select_list);
120         initialized = 1;
121 };
122
123 /**
124  * add a task to the scheduler
125  *
126  * \param t the task to add
127  *
128  * If the pre_select pointer of \a t is not \p NULL, it is added to
129  * the pre_select list of the scheduler. Same goes for post_select.
130  *
131  * \sa task::pre_select, task::post_select
132  */
133 void register_task(struct task *t)
134 {
135         if (!initialized)
136                 init_sched();
137         PARA_INFO_LOG("registering %s (%p)\n", t->status, t);
138         if (t->pre_select) {
139                 PARA_DEBUG_LOG("pre_select: %p\n", &t->pre_select);
140                 list_add_tail(&t->pre_select_node, &pre_select_list);
141         }
142         if (t->post_select) {
143                 PARA_DEBUG_LOG("post_select: %p\n", &t->pre_select);
144                 list_add_tail(&t->post_select_node, &post_select_list);
145         }
146 }
147
148 /**
149  * unregister all tasks
150  *
151  * This will cause \a schedule() to return immediately because both the
152  * \a pre_select_list and the \a post_select_list are empty.
153  */
154 void sched_shutdown(void)
155 {
156         struct task *t, *tmp;
157
158         if (!initialized)
159                 return;
160         list_for_each_entry_safe(t, tmp, &pre_select_list, pre_select_node)
161                 unregister_task(t);
162         /* remove tasks which do not have a pre_select hook */
163         list_for_each_entry_safe(t, tmp, &post_select_list, post_select_node)
164                 unregister_task(t);
165         initialized = 0;
166 };
167
168 /**
169  * get the list of all registered tasks.
170  *
171  * \return the task list
172  *
173  * Each entry of the list contains an identifier which is simply a hex number
174  * that may be used in \a kill_task() to terminate the task.
175  * The result ist dynamically allocated and must be freed by the caller.
176  */
177 char *get_task_list(void)
178 {
179         struct task *t, *tmp;
180         char *msg = NULL;
181
182         if (!initialized)
183                 return NULL;
184         list_for_each_entry_safe(t, tmp, &pre_select_list, pre_select_node) {
185                 char *tmp_msg;
186                 tmp_msg = make_message("%s%p\tpre\t%s\n", msg? msg : "", t, t->status);
187                 free(msg);
188                 msg = tmp_msg;
189         }
190         list_for_each_entry_safe(t, tmp, &post_select_list, post_select_node) {
191                 char *tmp_msg;
192 //              if (t->pre_select)
193 //                      continue;
194                 tmp_msg = make_message("%s%p\tpost\t%s\n", msg? msg : "", t, t->status);
195                 free(msg);
196                 msg = tmp_msg;
197         }
198         //PARA_DEBUG_LOG("task list:\n%s", msg);
199         return msg;
200 }
201
202 /**
203  * simulate an error for the given task
204  *
205  * \param id the task identifier
206  *
207  * Find the task identified by \a id, set the tasks' error value to
208  * \p -E_TASK_KILLED and unregister the task.
209  *
210  * \return Positive on success, negative on errors (e.g. if \a id does not
211  * correspond to a registered task).
212  */
213 int kill_task(char *id)
214 {
215         struct task *t, *tmp;
216         char buf[20];
217
218         if (!initialized)
219                 return -E_NOT_INITIALIZED;
220         list_for_each_entry_safe(t, tmp, &pre_select_list, pre_select_node) {
221                 sprintf(buf, "%p", t);
222                 if (strcmp(id, buf))
223                         continue;
224                 t->error = -E_TASK_KILLED;
225                 unregister_task(t);
226                 return 1;
227         }
228         list_for_each_entry_safe(t, tmp, &post_select_list, post_select_node) {
229                 sprintf(buf, "%p", t);
230                 if (strcmp(id, buf))
231                         continue;
232                 t->error = -E_TASK_KILLED;
233                 unregister_task(t);
234                 return 1;
235         }
236         return -E_NO_SUCH_TASK;
237 }