Fix tv_divide().
[paraslash.git] / sched.c
1 /*
2  * Copyright (C) 2006-2007 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 static void sched_preselect(struct sched *s)
28 {
29         struct task *t, *tmp;
30 again:
31         list_for_each_entry_safe(t, tmp, &pre_select_list, pre_select_node) {
32                 t->pre_select(s, t);
33 //              PARA_INFO_LOG("%s \n", t->status);
34                 if (t->ret > 0)
35                         continue;
36                 if (!t->event_handler)
37                         continue;
38                 t->event_handler(t);
39                 goto again;
40         }
41 }
42
43 static void sched_post_select(struct sched *s)
44 {
45         struct task *t, *tmp;
46
47         list_for_each_entry_safe(t, tmp, &post_select_list, post_select_node) {
48                 t->post_select(s, t);
49 //              PARA_INFO_LOG("%s: %d\n", t->status, t->ret);
50                 if (t->ret > 0 || !t->event_handler)
51                         continue;
52                 t->event_handler(t);
53         }
54 }
55
56 /**
57  * the core function for all paraslash programs
58  *
59  * \param s pointer to the scheduler struct
60  *
61  * This function updates the global \a now pointer, calls all registered
62  * pre_select hooks which may set the timeout and add any file descriptors to
63  * the fd sets of \a s.  Next, it calls para_select() and makes the result available
64  * to the registered tasks by calling their post_select hook.
65  *
66  * \return Zero if no more tasks are left in either of the two lists, negative
67  * if para_select returned an error.
68  *
69  * \sa task, now.
70  */
71 int sched(struct sched *s)
72 {
73         if (!initialized)
74                 return -E_NOT_INITIALIZED;
75         gettimeofday(now, NULL);
76 again:
77         FD_ZERO(&s->rfds);
78         FD_ZERO(&s->wfds);
79         s->timeout = s->default_timeout;
80         s->max_fileno = -1;
81         sched_preselect(s);
82         s->select_ret = para_select(s->max_fileno + 1, &s->rfds,
83                 &s->wfds, &s->timeout);
84         if (s->select_ret < 0)
85                 return s->select_ret;
86         gettimeofday(now, NULL);
87         sched_post_select(s);
88         if (list_empty(&pre_select_list) && list_empty(&post_select_list))
89                 return 0;
90         goto again;
91 }
92
93 /**
94  * initialize the paraslash scheduler
95  */
96 static void init_sched(void)
97 {
98         PARA_INFO_LOG("%s", "initializing scheduler\n");
99         INIT_LIST_HEAD(&pre_select_list);
100         INIT_LIST_HEAD(&post_select_list);
101         initialized = 1;
102 };
103
104 /**
105  * add a task to the scheduler
106  *
107  * \param t the task to add
108  *
109  * If the pre_select pointer of \a t is not \p NULL, it is added to
110  * the pre_select list of the scheduler. Same goes for post_select.
111  *
112  * \sa task::pre_select, task::post_select
113  */
114 void register_task(struct task *t)
115 {
116         if (!initialized)
117                 init_sched();
118         PARA_INFO_LOG("registering %s (%p)\n", t->status, t);
119         if (t->pre_select) {
120                 PARA_DEBUG_LOG("pre_select: %p\n", &t->pre_select);
121                 para_list_add(&t->pre_select_node, &pre_select_list);
122         }
123         if (t->post_select) {
124                 PARA_DEBUG_LOG("post_select: %p\n", &t->pre_select);
125                 para_list_add(&t->post_select_node, &post_select_list);
126         }
127 }
128
129 /**
130  * remove a task from the scheduler
131  *
132  * \param t the task to remove
133  *
134  * If the pre_select pointer of \a t is not \p NULL, it is removed from
135  * the pre_select list of the scheduler. Same goes for \a post_select.
136  */
137 void unregister_task(struct task *t)
138 {
139         if (!initialized)
140                 return;
141         PARA_INFO_LOG("unregistering %s (%p)\n", t->status, t);
142         if (t->pre_select)
143                 list_del(&t->pre_select_node);
144         if (t->post_select)
145                 list_del(&t->post_select_node);
146 };
147
148 /**
149  * unregister all tasks
150  *
151  * This will cause \a sched() 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' return value to
208  * \p -E_TASK_KILLED and call the event handler of 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->ret = -E_TASK_KILLED;
225                 if (t->event_handler)
226                         t->event_handler(t);
227                 return 1;
228         }
229         list_for_each_entry_safe(t, tmp, &post_select_list, post_select_node) {
230                 sprintf(buf, "%p", t);
231                 if (strcmp(id, buf))
232                         continue;
233                 t->ret = -E_TASK_KILLED;
234                 if (t->event_handler)
235                         t->event_handler(t);
236                 return 1;
237         }
238         return -E_NO_SUCH_TASK;
239 }