5 Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
7 Copyright (C) 1998 - 2000 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
23 * Revision 1.2 2000/07/05 06:06:35 priikone
24 * Global cosmetic change.
26 * Revision 1.1.1.1 2000/06/27 11:36:55 priikone
27 * Imported from internal CVS/Added Log headers.
32 #include "silcincludes.h"
34 /* The actual schedule object. */
35 static SilcSchedule schedule;
37 /* Initializes the schedule. Sets the non-timeout task queue hook and
38 the timeout task queue hook. This must be called before the schedule
41 void silc_schedule_init(SilcTaskQueue fd_queue,
42 SilcTaskQueue timeout_queue,
43 SilcTaskQueue generic_queue,
48 SILC_LOG_DEBUG(("Initializing scheduler"));
50 /* Initialize the schedule */
51 memset(&schedule, 0, sizeof(schedule));
52 schedule.fd_queue = fd_queue;
53 schedule.timeout_queue = timeout_queue;
54 schedule.generic_queue = generic_queue;
55 schedule.fd_list.fd = silc_calloc(max_fd, sizeof(int));
56 schedule.fd_list.last_fd = 0;
57 schedule.fd_list.max_fd = max_fd;
58 schedule.timeout = NULL;
59 schedule.valid = TRUE;
60 FD_ZERO(&schedule.in);
61 FD_ZERO(&schedule.out);
63 for (i = 0; i < max_fd; i++)
64 schedule.fd_list.fd[i] = -1;
67 /* Uninitializes the schedule. This is called when the program is ready
68 to end. This removes all tasks and task queues. */
70 int silc_schedule_uninit()
73 SILC_LOG_DEBUG(("Uninitializing scheduler"));
75 if (schedule.valid == TRUE)
78 /* Unregister all tasks */
79 if (schedule.fd_queue)
80 silc_task_remove(schedule.fd_queue, SILC_ALL_TASKS);
81 if (schedule.timeout_queue)
82 silc_task_remove(schedule.timeout_queue, SILC_ALL_TASKS);
83 if (schedule.generic_queue)
84 silc_task_remove(schedule.generic_queue, SILC_ALL_TASKS);
86 /* Unregister all task queues */
87 if (schedule.fd_queue)
88 silc_task_queue_free(schedule.fd_queue);
89 if (schedule.timeout_queue)
90 silc_task_queue_free(schedule.timeout_queue);
91 if (schedule.generic_queue)
92 silc_task_queue_free(schedule.generic_queue);
94 /* Clear the fd list */
95 if (schedule.fd_list.fd) {
96 memset(schedule.fd_list.fd, -1, schedule.fd_list.max_fd);
97 silc_free(schedule.fd_list.fd);
100 memset(&schedule, 'F', sizeof(schedule));
104 /* Stops the schedule even if it is not supposed to be stopped yet.
105 After calling this, one should call silc_schedule_uninit (after the
106 silc_schedule has returned). */
108 void silc_schedule_stop()
110 SILC_LOG_DEBUG(("Stopping scheduler"));
112 if (schedule.valid == TRUE)
113 schedule.valid = FALSE;
116 /* Sets a file descriptor to be listened by select() in scheduler. One can
117 call this directly if wanted. This can be called multiple times for
118 one file descriptor to set different iomasks. */
120 void silc_schedule_set_listen_fd(int fd, unsigned int iomask)
122 assert(schedule.valid != FALSE);
123 assert(fd < schedule.fd_list.max_fd);
125 schedule.fd_list.fd[fd] = iomask;
127 if (fd > schedule.fd_list.last_fd)
128 schedule.fd_list.last_fd = fd;
131 /* Removes a file descriptor from listen list. */
133 void silc_schedule_unset_listen_fd(int fd)
135 assert(schedule.valid != FALSE);
136 assert(fd < schedule.fd_list.max_fd);
138 schedule.fd_list.fd[fd] = -1;
140 if (fd == schedule.fd_list.last_fd) {
143 for (i = fd; i >= 0; i--)
144 if (schedule.fd_list.fd[i] != -1)
147 schedule.fd_list.last_fd = i;
151 /* Executes tasks matching the file descriptor set by select(). The task
152 remains on the task queue after execution. Invalid tasks are removed
153 here from the task queue. This macro is used by silc_schedule function.
154 We don't have to care about the tasks priority here because the tasks
155 are sorted in their priority order already at the registration phase. */
157 #define SILC_SCHEDULE_RUN_TASKS \
159 queue = schedule.fd_queue; \
160 if (queue && queue->valid == TRUE && queue->task) { \
161 task = queue->task; \
163 /* Walk thorugh all tasks in the particular task queue and \
164 execute the callback functions of those tasks matching the \
165 fd set by select(). */ \
167 /* Validity of the task is checked always before and after \
168 execution beacuse the task might have been unregistered \
169 in the callback function, ie. it is not valid anymore. */ \
172 /* Task ready for reading */ \
173 if ((FD_ISSET(task->fd, &schedule.in)) && \
174 (task->iomask & (1L << SILC_TASK_READ))) { \
175 task->callback(queue, SILC_TASK_READ, task->context, task->fd); \
181 /* Task ready for writing */ \
182 if ((FD_ISSET(task->fd, &schedule.out)) && \
183 (task->iomask & (1L << SILC_TASK_WRITE))) { \
184 task->callback(queue, SILC_TASK_WRITE, task->context, task->fd); \
189 if (!task->valid) { \
190 /* Invalid (unregistered) tasks are removed from the \
192 if (queue->task == task->next) { \
193 silc_task_remove(queue, task); \
198 silc_task_remove(queue, task->prev); \
202 /* Break if there isn't more tasks in the queue */ \
203 if (queue->task == task->next) \
211 /* Selects tasks to be listened by select(). These are the non-timeout
212 tasks. This checks the scheduler's fd list. This macro is used by
213 silc_schedule function. */
215 #define SILC_SCHEDULE_SELECT_TASKS \
217 for (i = 0; i <= schedule.fd_list.last_fd; i++) { \
218 if (schedule.fd_list.fd[i] != -1) { \
220 /* Set the max fd value for select() to listen */ \
221 if (i > schedule.max_fd) \
222 schedule.max_fd = i; \
224 /* Add tasks for reading */ \
225 if ((schedule.fd_list.fd[i] & (1L << SILC_TASK_READ))) \
226 FD_SET(i, &schedule.in); \
228 /* Add tasks for writing */ \
229 if ((schedule.fd_list.fd[i] & (1L << SILC_TASK_WRITE))) \
230 FD_SET(i, &schedule.out); \
235 /* Executes all tasks whose timeout has expired. The task is removed from
236 the task queue after the callback function has returned. Also, invalid
237 tasks are removed here. The current time must be get before calling this
238 macro. This macro is used by silc_schedule function. We don't have to
239 care about priorities because tasks are already sorted in their priority
240 order at the registration phase. */
242 #define SILC_SCHEDULE_RUN_TIMEOUT_TASKS \
244 queue = schedule.timeout_queue; \
245 if (queue && queue->valid == TRUE && queue->task) { \
246 task = queue->task; \
248 /* Walk thorugh all tasks in the particular task queue \
249 and run all the expired tasks. */ \
251 /* Execute the task if the timeout has expired */ \
252 if (silc_task_timeout_compare(&task->timeout, &curtime)) { \
254 /* Task ready for reading */ \
256 if ((task->iomask & (1L << SILC_TASK_READ))) \
257 task->callback(queue, SILC_TASK_READ, \
258 task->context, task->fd); \
261 /* Task ready for writing */ \
263 if ((task->iomask & (1L << SILC_TASK_WRITE))) \
264 task->callback(queue, SILC_TASK_WRITE, \
265 task->context, task->fd); \
268 /* Break if there isn't more tasks in the queue */ \
269 if (queue->task == task->next) { \
270 /* Remove the task from queue */ \
271 silc_task_remove(queue, task); \
277 /* Remove the task from queue */ \
278 silc_task_remove(queue, task->prev); \
280 /* The timeout hasn't expired, check for next one */ \
282 /* Break if there isn't more tasks in the queue */ \
283 if (queue->task == task->next) \
292 /* Calculates next timeout for select(). This is the timeout value
293 when at earliest some of the timeout tasks expire. If this is in the
294 past, they will be run now. This macro is used by the silc_schedule
297 #define SILC_SCHEDULE_SELECT_TIMEOUT \
299 if (schedule.timeout_queue && schedule.timeout_queue->valid == TRUE) { \
300 queue = schedule.timeout_queue; \
303 /* Get the current time */ \
304 gettimeofday(&curtime, NULL); \
305 schedule.timeout = NULL; \
307 /* First task in the task queue has always the smallest timeout. */ \
308 task = queue->task; \
310 if (task && task->valid == TRUE) { \
312 /* If the timeout is in past, we will run the task and all other \
313 timeout tasks from the past. */ \
314 if (silc_task_timeout_compare(&task->timeout, &curtime)) { \
315 SILC_SCHEDULE_RUN_TIMEOUT_TASKS; \
317 /* The task(s) has expired and doesn't exist on the task queue \
318 anymore. We continue with new timeout. */ \
319 queue = schedule.timeout_queue; \
320 task = queue->task; \
321 if (task == NULL || task->valid == FALSE) \
326 /* Calculate the next timeout for select() */ \
327 queue->timeout.tv_sec = task->timeout.tv_sec - curtime.tv_sec; \
328 queue->timeout.tv_usec = task->timeout.tv_usec - curtime.tv_usec; \
330 /* We wouldn't want to go under zero, check for it. */ \
331 if (queue->timeout.tv_usec < 0) { \
332 queue->timeout.tv_sec -= 1; \
333 queue->timeout.tv_usec += 1000000L; \
336 /* We've got the timeout value */ \
339 /* Task is not valid, remove it and try next one. */ \
340 silc_task_remove(queue, task); \
341 task = queue->task; \
342 if (queue->task == NULL) \
346 /* Save the timeout */ \
348 schedule.timeout = &queue->timeout; \
352 /* Execute generic tasks. These are executed only and only if for the
353 specific fd there wasn't other non-timeout tasks. This checks the earlier
354 set fd list, thus the generic tasks apply to all specified fd's. All the
355 generic tasks are executed at once. */
357 #define SILC_SCHEDULE_RUN_GENERIC_TASKS \
359 if (is_run == FALSE) { \
360 SILC_LOG_DEBUG(("Running generic tasks")); \
361 for (i = 0; i <= schedule.fd_list.last_fd; i++) \
362 if (schedule.fd_list.fd[i] != -1) { \
364 /* Check whether this fd is select()ed. */ \
365 if ((FD_ISSET(i, &schedule.in)) || (FD_ISSET(i, &schedule.out))) { \
367 /* It was selected. Now find the tasks from task queue and execute \
368 all generic tasks. */ \
369 if (schedule.generic_queue && schedule.generic_queue->valid) { \
370 queue = schedule.generic_queue; \
375 task = queue->task; \
378 /* Validity of the task is checked always before and after \
379 execution beacuse the task might have been unregistered \
380 in the callback function, ie. it is not valid anymore. */ \
382 if (task->valid && schedule.fd_list.fd[i] != -1) { \
383 /* Task ready for reading */ \
384 if ((schedule.fd_list.fd[i] & (1L << SILC_TASK_READ))) \
385 task->callback(queue, SILC_TASK_READ, \
389 if (task->valid && schedule.fd_list.fd[i] != -1) { \
390 /* Task ready for writing */ \
391 if ((schedule.fd_list.fd[i] & (1L << SILC_TASK_WRITE))) \
392 task->callback(queue, SILC_TASK_WRITE, \
396 if (!task->valid) { \
397 /* Invalid (unregistered) tasks are removed from the \
399 if (queue->task == task->next) { \
400 silc_task_remove(queue, task); \
405 silc_task_remove(queue, task->prev); \
409 /* Break if there isn't more tasks in the queue */ \
410 if (queue->task == task->next) \
421 /* The SILC scheduler. This is actually the main routine in SILC programs.
422 When this returns the program is to be ended. Before this function can
423 be called, one must call silc_schedule_init function. */
430 struct timeval curtime;
432 SILC_LOG_DEBUG(("Running scheduler"));
434 if (schedule.valid == FALSE) {
435 SILC_LOG_ERROR(("Scheduler is not valid, stopping"));
439 /* Start the scheduler loop */
442 SILC_LOG_DEBUG(("In scheduler loop"));
444 /* If the task queues aren't initialized or we aren't valid anymore
446 if ((!schedule.fd_queue && !schedule.timeout_queue
447 && !schedule.generic_queue) || schedule.valid == FALSE) {
448 SILC_LOG_DEBUG(("Scheduler not valid anymore, exiting"));
452 /* Clear everything */
453 FD_ZERO(&schedule.in);
454 FD_ZERO(&schedule.out);
455 schedule.max_fd = -1;
458 /* Calculate next timeout for select(). This is the timeout value
459 when at earliest some of the timeout tasks expire. */
460 SILC_SCHEDULE_SELECT_TIMEOUT;
462 /* Add the file descriptors to the fd sets. These are the non-timeout
463 tasks. The select() listens to these file descriptors. */
464 SILC_SCHEDULE_SELECT_TASKS;
466 if (schedule.max_fd == -1) {
467 SILC_LOG_ERROR(("Nothing to listen, exiting"));
471 if (schedule.timeout)
472 SILC_LOG_DEBUG(("timeout: sec=%d, usec=%d", schedule.timeout->tv_sec,
473 schedule.timeout->tv_usec));
475 /* This is the main select(). The program blocks here until some
476 of the selected file descriptors change status or the selected
478 SILC_LOG_DEBUG(("Select"));
479 switch(select(schedule.max_fd + 1, &schedule.in,
480 &schedule.out, 0, schedule.timeout)) {
483 SILC_LOG_ERROR(("Error in select(): %s", strerror(errno)));
487 SILC_LOG_DEBUG(("Running timeout tasks"));
488 gettimeofday(&curtime, NULL);
489 SILC_SCHEDULE_RUN_TIMEOUT_TASKS;
492 /* There is some data available now */
493 SILC_LOG_DEBUG(("Running non-timeout tasks"));
494 SILC_SCHEDULE_RUN_TASKS;
496 SILC_SCHEDULE_RUN_GENERIC_TASKS;