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.3 2000/07/18 06:51:58 priikone
24 * Debug version bug fixes.
26 * Revision 1.2 2000/07/05 06:06:35 priikone
27 * Global cosmetic change.
29 * Revision 1.1.1.1 2000/06/27 11:36:55 priikone
30 * Imported from internal CVS/Added Log headers.
35 #include "silcincludes.h"
37 /* The actual schedule object. */
38 static SilcSchedule schedule;
40 /* Initializes the schedule. Sets the non-timeout task queue hook and
41 the timeout task queue hook. This must be called before the schedule
44 void silc_schedule_init(SilcTaskQueue fd_queue,
45 SilcTaskQueue timeout_queue,
46 SilcTaskQueue generic_queue,
51 SILC_LOG_DEBUG(("Initializing scheduler"));
53 /* Initialize the schedule */
54 memset(&schedule, 0, sizeof(schedule));
55 schedule.fd_queue = fd_queue;
56 schedule.timeout_queue = timeout_queue;
57 schedule.generic_queue = generic_queue;
58 schedule.fd_list.fd = silc_calloc(max_fd, sizeof(int));
59 schedule.fd_list.last_fd = 0;
60 schedule.fd_list.max_fd = max_fd;
61 schedule.timeout = NULL;
62 schedule.valid = TRUE;
63 FD_ZERO(&schedule.in);
64 FD_ZERO(&schedule.out);
66 for (i = 0; i < max_fd; i++)
67 schedule.fd_list.fd[i] = -1;
70 /* Uninitializes the schedule. This is called when the program is ready
71 to end. This removes all tasks and task queues. */
73 int silc_schedule_uninit()
76 SILC_LOG_DEBUG(("Uninitializing scheduler"));
78 if (schedule.valid == TRUE)
81 /* Unregister all tasks */
82 if (schedule.fd_queue)
83 silc_task_remove(schedule.fd_queue, SILC_ALL_TASKS);
84 if (schedule.timeout_queue)
85 silc_task_remove(schedule.timeout_queue, SILC_ALL_TASKS);
86 if (schedule.generic_queue)
87 silc_task_remove(schedule.generic_queue, SILC_ALL_TASKS);
89 /* Unregister all task queues */
90 if (schedule.fd_queue)
91 silc_task_queue_free(schedule.fd_queue);
92 if (schedule.timeout_queue)
93 silc_task_queue_free(schedule.timeout_queue);
94 if (schedule.generic_queue)
95 silc_task_queue_free(schedule.generic_queue);
97 /* Clear the fd list */
98 if (schedule.fd_list.fd) {
99 memset(schedule.fd_list.fd, -1, schedule.fd_list.max_fd);
100 silc_free(schedule.fd_list.fd);
103 memset(&schedule, 'F', sizeof(schedule));
107 /* Stops the schedule even if it is not supposed to be stopped yet.
108 After calling this, one should call silc_schedule_uninit (after the
109 silc_schedule has returned). */
111 void silc_schedule_stop()
113 SILC_LOG_DEBUG(("Stopping scheduler"));
115 if (schedule.valid == TRUE)
116 schedule.valid = FALSE;
119 /* Sets a file descriptor to be listened by select() in scheduler. One can
120 call this directly if wanted. This can be called multiple times for
121 one file descriptor to set different iomasks. */
123 void silc_schedule_set_listen_fd(int fd, unsigned int iomask)
125 assert(schedule.valid != FALSE);
126 assert(fd < schedule.fd_list.max_fd);
128 schedule.fd_list.fd[fd] = iomask;
130 if (fd > schedule.fd_list.last_fd)
131 schedule.fd_list.last_fd = fd;
134 /* Removes a file descriptor from listen list. */
136 void silc_schedule_unset_listen_fd(int fd)
138 assert(schedule.valid != FALSE);
139 assert(fd < schedule.fd_list.max_fd);
141 schedule.fd_list.fd[fd] = -1;
143 if (fd == schedule.fd_list.last_fd) {
146 for (i = fd; i >= 0; i--)
147 if (schedule.fd_list.fd[i] != -1)
150 schedule.fd_list.last_fd = i;
154 /* Executes tasks matching the file descriptor set by select(). The task
155 remains on the task queue after execution. Invalid tasks are removed
156 here from the task queue. This macro is used by silc_schedule function.
157 We don't have to care about the tasks priority here because the tasks
158 are sorted in their priority order already at the registration phase. */
160 #define SILC_SCHEDULE_RUN_TASKS \
162 queue = schedule.fd_queue; \
163 if (queue && queue->valid == TRUE && queue->task) { \
164 task = queue->task; \
166 /* Walk thorugh all tasks in the particular task queue and \
167 execute the callback functions of those tasks matching the \
168 fd set by select(). */ \
170 /* Validity of the task is checked always before and after \
171 execution beacuse the task might have been unregistered \
172 in the callback function, ie. it is not valid anymore. */ \
175 /* Task ready for reading */ \
176 if ((FD_ISSET(task->fd, &schedule.in)) && \
177 (task->iomask & (1L << SILC_TASK_READ))) { \
178 task->callback(queue, SILC_TASK_READ, task->context, task->fd); \
184 /* Task ready for writing */ \
185 if ((FD_ISSET(task->fd, &schedule.out)) && \
186 (task->iomask & (1L << SILC_TASK_WRITE))) { \
187 task->callback(queue, SILC_TASK_WRITE, task->context, task->fd); \
192 if (!task->valid) { \
193 /* Invalid (unregistered) tasks are removed from the \
195 if (queue->task == task->next) { \
196 silc_task_remove(queue, task); \
201 silc_task_remove(queue, task->prev); \
205 /* Break if there isn't more tasks in the queue */ \
206 if (queue->task == task->next) \
214 /* Selects tasks to be listened by select(). These are the non-timeout
215 tasks. This checks the scheduler's fd list. This macro is used by
216 silc_schedule function. */
218 #define SILC_SCHEDULE_SELECT_TASKS \
220 for (i = 0; i <= schedule.fd_list.last_fd; i++) { \
221 if (schedule.fd_list.fd[i] != -1) { \
223 /* Set the max fd value for select() to listen */ \
224 if (i > schedule.max_fd) \
225 schedule.max_fd = i; \
227 /* Add tasks for reading */ \
228 if ((schedule.fd_list.fd[i] & (1L << SILC_TASK_READ))) \
229 FD_SET(i, &schedule.in); \
231 /* Add tasks for writing */ \
232 if ((schedule.fd_list.fd[i] & (1L << SILC_TASK_WRITE))) \
233 FD_SET(i, &schedule.out); \
238 /* Executes all tasks whose timeout has expired. The task is removed from
239 the task queue after the callback function has returned. Also, invalid
240 tasks are removed here. The current time must be get before calling this
241 macro. This macro is used by silc_schedule function. We don't have to
242 care about priorities because tasks are already sorted in their priority
243 order at the registration phase. */
245 #define SILC_SCHEDULE_RUN_TIMEOUT_TASKS \
247 queue = schedule.timeout_queue; \
248 if (queue && queue->valid == TRUE && queue->task) { \
249 task = queue->task; \
251 /* Walk thorugh all tasks in the particular task queue \
252 and run all the expired tasks. */ \
254 /* Execute the task if the timeout has expired */ \
255 if (silc_task_timeout_compare(&task->timeout, &curtime)) { \
257 /* Task ready for reading */ \
259 if ((task->iomask & (1L << SILC_TASK_READ))) \
260 task->callback(queue, SILC_TASK_READ, \
261 task->context, task->fd); \
264 /* Task ready for writing */ \
266 if ((task->iomask & (1L << SILC_TASK_WRITE))) \
267 task->callback(queue, SILC_TASK_WRITE, \
268 task->context, task->fd); \
271 /* Break if there isn't more tasks in the queue */ \
272 if (queue->task == task->next) { \
273 /* Remove the task from queue */ \
274 silc_task_remove(queue, task); \
280 /* Remove the task from queue */ \
281 silc_task_remove(queue, task->prev); \
283 /* The timeout hasn't expired, check for next one */ \
285 /* Break if there isn't more tasks in the queue */ \
286 if (queue->task == task->next) \
295 /* Calculates next timeout for select(). This is the timeout value
296 when at earliest some of the timeout tasks expire. If this is in the
297 past, they will be run now. This macro is used by the silc_schedule
300 #define SILC_SCHEDULE_SELECT_TIMEOUT \
302 if (schedule.timeout_queue && schedule.timeout_queue->valid == TRUE) { \
303 queue = schedule.timeout_queue; \
306 /* Get the current time */ \
307 gettimeofday(&curtime, NULL); \
308 schedule.timeout = NULL; \
310 /* First task in the task queue has always the smallest timeout. */ \
311 task = queue->task; \
313 if (task && task->valid == TRUE) { \
315 /* If the timeout is in past, we will run the task and all other \
316 timeout tasks from the past. */ \
317 if (silc_task_timeout_compare(&task->timeout, &curtime)) { \
318 SILC_SCHEDULE_RUN_TIMEOUT_TASKS; \
320 /* The task(s) has expired and doesn't exist on the task queue \
321 anymore. We continue with new timeout. */ \
322 queue = schedule.timeout_queue; \
323 task = queue->task; \
324 if (task == NULL || task->valid == FALSE) \
329 /* Calculate the next timeout for select() */ \
330 queue->timeout.tv_sec = task->timeout.tv_sec - curtime.tv_sec; \
331 queue->timeout.tv_usec = task->timeout.tv_usec - curtime.tv_usec; \
333 /* We wouldn't want to go under zero, check for it. */ \
334 if (queue->timeout.tv_usec < 0) { \
335 queue->timeout.tv_sec -= 1; \
336 queue->timeout.tv_usec += 1000000L; \
339 /* We've got the timeout value */ \
342 /* Task is not valid, remove it and try next one. */ \
343 silc_task_remove(queue, task); \
344 task = queue->task; \
345 if (queue->task == NULL) \
349 /* Save the timeout */ \
351 schedule.timeout = &queue->timeout; \
355 /* Execute generic tasks. These are executed only and only if for the
356 specific fd there wasn't other non-timeout tasks. This checks the earlier
357 set fd list, thus the generic tasks apply to all specified fd's. All the
358 generic tasks are executed at once. */
360 #define SILC_SCHEDULE_RUN_GENERIC_TASKS \
362 if (is_run == FALSE) { \
363 SILC_LOG_DEBUG(("Running generic tasks")); \
364 for (i = 0; i <= schedule.fd_list.last_fd; i++) \
365 if (schedule.fd_list.fd[i] != -1) { \
367 /* Check whether this fd is select()ed. */ \
368 if ((FD_ISSET(i, &schedule.in)) || (FD_ISSET(i, &schedule.out))) { \
370 /* It was selected. Now find the tasks from task queue and execute \
371 all generic tasks. */ \
372 if (schedule.generic_queue && schedule.generic_queue->valid) { \
373 queue = schedule.generic_queue; \
378 task = queue->task; \
381 /* Validity of the task is checked always before and after \
382 execution beacuse the task might have been unregistered \
383 in the callback function, ie. it is not valid anymore. */ \
385 if (task->valid && schedule.fd_list.fd[i] != -1) { \
386 /* Task ready for reading */ \
387 if ((schedule.fd_list.fd[i] & (1L << SILC_TASK_READ))) \
388 task->callback(queue, SILC_TASK_READ, \
392 if (task->valid && schedule.fd_list.fd[i] != -1) { \
393 /* Task ready for writing */ \
394 if ((schedule.fd_list.fd[i] & (1L << SILC_TASK_WRITE))) \
395 task->callback(queue, SILC_TASK_WRITE, \
399 if (!task->valid) { \
400 /* Invalid (unregistered) tasks are removed from the \
402 if (queue->task == task->next) { \
403 silc_task_remove(queue, task); \
408 silc_task_remove(queue, task->prev); \
412 /* Break if there isn't more tasks in the queue */ \
413 if (queue->task == task->next) \
424 /* The SILC scheduler. This is actually the main routine in SILC programs.
425 When this returns the program is to be ended. Before this function can
426 be called, one must call silc_schedule_init function. */
433 struct timeval curtime;
435 SILC_LOG_DEBUG(("Running scheduler"));
437 if (schedule.valid == FALSE) {
438 SILC_LOG_ERROR(("Scheduler is not valid, stopping"));
442 /* Start the scheduler loop */
445 SILC_LOG_DEBUG(("In scheduler loop"));
447 /* If the task queues aren't initialized or we aren't valid anymore
449 if ((!schedule.fd_queue && !schedule.timeout_queue
450 && !schedule.generic_queue) || schedule.valid == FALSE) {
451 SILC_LOG_DEBUG(("Scheduler not valid anymore, exiting"));
455 /* Clear everything */
456 FD_ZERO(&schedule.in);
457 FD_ZERO(&schedule.out);
458 schedule.max_fd = -1;
461 /* Calculate next timeout for select(). This is the timeout value
462 when at earliest some of the timeout tasks expire. */
463 SILC_SCHEDULE_SELECT_TIMEOUT;
465 /* Add the file descriptors to the fd sets. These are the non-timeout
466 tasks. The select() listens to these file descriptors. */
467 SILC_SCHEDULE_SELECT_TASKS;
469 if (schedule.max_fd == -1) {
470 SILC_LOG_ERROR(("Nothing to listen, exiting"));
474 if (schedule.timeout) {
475 SILC_LOG_DEBUG(("timeout: sec=%d, usec=%d", schedule.timeout->tv_sec,
476 schedule.timeout->tv_usec));
479 /* This is the main select(). The program blocks here until some
480 of the selected file descriptors change status or the selected
482 SILC_LOG_DEBUG(("Select"));
483 switch(select(schedule.max_fd + 1, &schedule.in,
484 &schedule.out, 0, schedule.timeout)) {
487 SILC_LOG_ERROR(("Error in select(): %s", strerror(errno)));
491 SILC_LOG_DEBUG(("Running timeout tasks"));
492 gettimeofday(&curtime, NULL);
493 SILC_SCHEDULE_RUN_TIMEOUT_TASKS;
496 /* There is some data available now */
497 SILC_LOG_DEBUG(("Running non-timeout tasks"));
498 SILC_SCHEDULE_RUN_TASKS;
500 SILC_SCHEDULE_RUN_GENERIC_TASKS;