6b893d33c9ccb2b07c65fcb8fd02ac168d7bc6f8
[paraslash.git] / sched.c
1 /*
2 * Copyright (C) 2006 Andre Noll <maan@systemlinux.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
17 */
18
19 /** \file sched.c paraslash's scheduling functions */
20
21 #include <sys/time.h>
22 #include "para.h"
23 #include "ipc.h"
24 #include "fd.h"
25 #include "list.h"
26 #include "sched.h"
27 #include "string.h"
28 #include "error.h"
29
30 /**
31 * The scheduler manages two lists of tasks. The pre_select list contains
32 * pointers to functions that are called before calling select() from the main
33 * loop. Similarly, \a post_select_list is a list of function pointers each of
34 * which is called after the select call.
35 */
36 struct list_head pre_select_list, post_select_list;
37
38 static struct timeval now_struct;
39
40 struct timeval *now = &now_struct;
41
42 static void sched_preselect(struct sched *s)
43 {
44 struct task *t, *tmp;
45 again:
46 list_for_each_entry_safe(t, tmp, &pre_select_list, pre_select_node) {
47 t->pre_select(s, t);
48 // PARA_INFO_LOG("%s \n", t->status);
49 if (t->ret > 0)
50 continue;
51 if (!t->event_handler)
52 continue;
53 t->event_handler(t);
54 goto again;
55 }
56 }
57
58 static void sched_post_select(struct sched *s)
59 {
60 struct task *t, *tmp;
61
62 list_for_each_entry_safe(t, tmp, &post_select_list, post_select_node) {
63 t->post_select(s, t);
64 // PARA_INFO_LOG("%s: %d\n", t->status, t->ret);
65 if (t->ret > 0 || !t->event_handler)
66 continue;
67 t->event_handler(t);
68 }
69 }
70
71 /**
72 * the core function for all paraslash programs
73 *
74 * Short and sweet. It updates the global \a now pointer, calls all registered
75 * pre_select hooks which may set the timeout and add any file descriptors to
76 * the fd sets of \a s. Next, it calls para_select() and makes the result available
77 * to the registered tasks by calling their post_select hook.
78 *
79 * \return Zero if no more tasks are left in either of the two lists, negative
80 * if para_select returned an error.
81 *
82 * \sa task, now.
83 */
84 int sched(struct sched *s)
85 {
86
87 gettimeofday(now, NULL);
88 again:
89 FD_ZERO(&s->rfds);
90 FD_ZERO(&s->wfds);
91 s->timeout = s->default_timeout;
92 s->max_fileno = -1;
93 sched_preselect(s);
94 s->select_ret = para_select(s->max_fileno + 1, &s->rfds,
95 &s->wfds, &s->timeout);
96 if (s->select_ret < 0)
97 return s->select_ret;
98 gettimeofday(now, NULL);
99 sched_post_select(s);
100 if (list_empty(&pre_select_list) && list_empty(&post_select_list))
101 return 0;
102 goto again;
103 }
104
105 /**
106 * add a task to the scheduler
107 *
108 * \param t the task to add
109 *
110 * If the pre_select pointer of \a t is not \p NULL, it is added to
111 * the pre_select list of the scheduler. Same goes for post_select.
112 *
113 * \sa task::pre_select, task::post_select
114 */
115 void register_task(struct task *t)
116 {
117 PARA_INFO_LOG("registering %s (%p)\n", t->status, t);
118 if (t->pre_select) {
119 PARA_DEBUG_LOG("pre_select: %p\n", &t->pre_select);
120 list_add(&t->pre_select_node, &pre_select_list);
121 }
122 if (t->post_select) {
123 PARA_DEBUG_LOG("post_select: %p\n", &t->pre_select);
124 list_add(&t->post_select_node, &post_select_list);
125 }
126 }
127
128 /**
129 * remove a task from the scheduler
130 *
131 * \param t the task to remove
132 *
133 * If the pre_select pointer of \a t is not \p NULL, it is removed from
134 * the pre_select list of the scheduler. Same goes for \a post_select.
135 */
136 void unregister_task(struct task *t)
137 {
138 PARA_INFO_LOG("unregistering %s (%p)\n", t->status, t);
139 if (t->pre_select)
140 list_del(&t->pre_select_node);
141 if (t->post_select)
142 list_del(&t->post_select_node);
143 };
144
145 /**
146 * initialize the paraslash scheduler
147 */
148 void init_sched(void)
149 {
150 INIT_LIST_HEAD(&pre_select_list);
151 INIT_LIST_HEAD(&post_select_list);
152 };
153
154 /**
155 * unregister all tasks
156 *
157 * This will cause \a sched() to return immediately because both the
158 * \a pre_select_list and the \a post_select_list are empty.
159 */
160 void sched_shutdown(void)
161 {
162 struct task *t, *tmp;
163
164 list_for_each_entry_safe(t, tmp, &pre_select_list, pre_select_node)
165 unregister_task(t);
166 /* remove tasks which do not have a pre_select hook */
167 list_for_each_entry_safe(t, tmp, &post_select_list, post_select_node)
168 unregister_task(t);
169 };
170
171 /**
172 * get the list of all registered tasks.
173 *
174 * \return the task list
175 *
176 * Each entry of the list contains an identifier which is simply a hex number
177 * that may be used in \a kill_task() to terminate the task.
178 * The result ist dynamically allocated and must be freed by the caller.
179 */
180 char *get_task_list(void)
181 {
182 struct task *t, *tmp;
183 char *msg = 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 sucess, negative if \a id does not correspond to a
211 * registered task.
212 */
213 int kill_task(char *id)
214 {
215 struct task *t, *tmp;
216 char buf[20];
217 list_for_each_entry_safe(t, tmp, &pre_select_list, pre_select_node) {
218 sprintf(buf, "%p", t);
219 if (strcmp(id, buf))
220 continue;
221 t->ret = -E_TASK_KILLED;
222 if (t->event_handler)
223 t->event_handler(t);
224 return 1;
225 }
226 list_for_each_entry_safe(t, tmp, &post_select_list, post_select_node) {
227 sprintf(buf, "%p", t);
228 if (strcmp(id, buf))
229 continue;
230 t->ret = -E_TASK_KILLED;
231 if (t->event_handler)
232 t->event_handler(t);
233 return 1;
234 }
235 return -E_NO_SUCH_TASK;
236 }