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.1 2000/06/27 11:36:55 priikone
29 #include "silcincludes.h"
31 /* The actual schedule object. */
32 static SilcSchedule schedule;
34 /* Initializes the schedule. Sets the non-timeout task queue hook and
35 the timeout task queue hook. This must be called before the schedule
38 void silc_schedule_init(SilcTaskQueue fd_queue,
39 SilcTaskQueue timeout_queue,
40 SilcTaskQueue generic_queue,
45 SILC_LOG_DEBUG(("Initializing scheduler"));
47 /* Initialize the schedule */
48 memset(&schedule, 0, sizeof(schedule));
49 schedule.fd_queue = fd_queue;
50 schedule.timeout_queue = timeout_queue;
51 schedule.generic_queue = generic_queue;
52 schedule.fd_list.fd = silc_calloc(max_fd, sizeof(int));
53 schedule.fd_list.last_fd = 0;
54 schedule.fd_list.max_fd = max_fd;
55 schedule.timeout = NULL;
56 schedule.valid = TRUE;
57 FD_ZERO(&schedule.in);
58 FD_ZERO(&schedule.out);
60 for (i = 0; i < max_fd; i++)
61 schedule.fd_list.fd[i] = -1;
64 /* Uninitializes the schedule. This is called when the program is ready
65 to end. This removes all tasks and task queues. */
67 int silc_schedule_uninit()
70 SILC_LOG_DEBUG(("Uninitializing scheduler"));
72 if (schedule.valid == TRUE)
75 /* Unregister all tasks */
76 if (schedule.fd_queue)
77 silc_task_remove(schedule.fd_queue, SILC_ALL_TASKS);
78 if (schedule.timeout_queue)
79 silc_task_remove(schedule.timeout_queue, SILC_ALL_TASKS);
80 if (schedule.generic_queue)
81 silc_task_remove(schedule.generic_queue, SILC_ALL_TASKS);
83 /* Unregister all task queues */
84 if (schedule.fd_queue)
85 silc_task_queue_free(schedule.fd_queue);
86 if (schedule.timeout_queue)
87 silc_task_queue_free(schedule.timeout_queue);
88 if (schedule.generic_queue)
89 silc_task_queue_free(schedule.generic_queue);
91 /* Clear the fd list */
92 if (schedule.fd_list.fd) {
93 memset(schedule.fd_list.fd, -1, schedule.fd_list.max_fd);
94 silc_free(schedule.fd_list.fd);
97 memset(&schedule, 'F', sizeof(schedule));
101 /* Stops the schedule even if it is not supposed to be stopped yet.
102 After calling this, one should call silc_schedule_uninit (after the
103 silc_schedule has returned). */
105 void silc_schedule_stop()
107 SILC_LOG_DEBUG(("Stopping scheduler"));
109 if (schedule.valid == TRUE)
110 schedule.valid = FALSE;
113 /* Sets a file descriptor to be listened by select() in scheduler. One can
114 call this directly if wanted. This can be called multiple times for
115 one file descriptor to set different iomasks. */
117 void silc_schedule_set_listen_fd(int fd, unsigned int iomask)
119 assert(schedule.valid != FALSE);
120 assert(fd < schedule.fd_list.max_fd);
122 schedule.fd_list.fd[fd] = iomask;
124 if (fd > schedule.fd_list.last_fd)
125 schedule.fd_list.last_fd = fd;
128 /* Removes a file descriptor from listen list. */
130 void silc_schedule_unset_listen_fd(int fd)
132 assert(schedule.valid != FALSE);
133 assert(fd < schedule.fd_list.max_fd);
135 schedule.fd_list.fd[fd] = -1;
137 if (fd == schedule.fd_list.last_fd) {
140 for (i = fd; i >= 0; i--)
141 if (schedule.fd_list.fd[i] != -1)
144 schedule.fd_list.last_fd = i;
148 /* Executes tasks matching the file descriptor set by select(). The task
149 remains on the task queue after execution. Invalid tasks are removed
150 here from the task queue. This macro is used by silc_schedule function.
151 We don't have to care about the tasks priority here because the tasks
152 are sorted in their priority order already at the registration phase. */
154 #define SILC_SCHEDULE_RUN_TASKS \
156 queue = schedule.fd_queue; \
157 if (queue && queue->valid == TRUE && queue->task) { \
158 task = queue->task; \
160 /* Walk thorugh all tasks in the particular task queue and \
161 execute the callback functions of those tasks matching the \
162 fd set by select(). */ \
164 /* Validity of the task is checked always before and after \
165 execution beacuse the task might have been unregistered \
166 in the callback function, ie. it is not valid anymore. */ \
169 /* Task ready for reading */ \
170 if ((FD_ISSET(task->fd, &schedule.in)) && \
171 (task->iomask & (1L << SILC_TASK_READ))) { \
172 task->callback(queue, SILC_TASK_READ, task->context, task->fd); \
178 /* Task ready for writing */ \
179 if ((FD_ISSET(task->fd, &schedule.out)) && \
180 (task->iomask & (1L << SILC_TASK_WRITE))) { \
181 task->callback(queue, SILC_TASK_WRITE, task->context, task->fd); \
186 if (!task->valid) { \
187 /* Invalid (unregistered) tasks are removed from the \
189 if (queue->task == task->next) { \
190 silc_task_remove(queue, task); \
195 silc_task_remove(queue, task->prev); \
199 /* Break if there isn't more tasks in the queue */ \
200 if (queue->task == task->next) \
208 /* Selects tasks to be listened by select(). These are the non-timeout
209 tasks. This checks the scheduler's fd list. This macro is used by
210 silc_schedule function. */
212 #define SILC_SCHEDULE_SELECT_TASKS \
214 for (i = 0; i <= schedule.fd_list.last_fd; i++) { \
215 if (schedule.fd_list.fd[i] != -1) { \
217 /* Set the max fd value for select() to listen */ \
218 if (i > schedule.max_fd) \
219 schedule.max_fd = i; \
221 /* Add tasks for reading */ \
222 if ((schedule.fd_list.fd[i] & (1L << SILC_TASK_READ))) \
223 FD_SET(i, &schedule.in); \
225 /* Add tasks for writing */ \
226 if ((schedule.fd_list.fd[i] & (1L << SILC_TASK_WRITE))) \
227 FD_SET(i, &schedule.out); \
232 /* Executes all tasks whose timeout has expired. The task is removed from
233 the task queue after the callback function has returned. Also, invalid
234 tasks are removed here. The current time must be get before calling this
235 macro. This macro is used by silc_schedule function. We don't have to
236 care about priorities because tasks are already sorted in their priority
237 order at the registration phase. */
239 #define SILC_SCHEDULE_RUN_TIMEOUT_TASKS \
241 queue = schedule.timeout_queue; \
242 if (queue && queue->valid == TRUE && queue->task) { \
243 task = queue->task; \
245 /* Walk thorugh all tasks in the particular task queue \
246 and run all the expired tasks. */ \
248 /* Execute the task if the timeout has expired */ \
249 if (silc_task_timeout_compare(&task->timeout, &curtime)) { \
251 /* Task ready for reading */ \
253 if ((task->iomask & (1L << SILC_TASK_READ))) \
254 task->callback(queue, SILC_TASK_READ, \
255 task->context, task->fd); \
258 /* Task ready for writing */ \
260 if ((task->iomask & (1L << SILC_TASK_WRITE))) \
261 task->callback(queue, SILC_TASK_WRITE, \
262 task->context, task->fd); \
265 /* Break if there isn't more tasks in the queue */ \
266 if (queue->task == task->next) { \
267 /* Remove the task from queue */ \
268 silc_task_remove(queue, task); \
274 /* Remove the task from queue */ \
275 silc_task_remove(queue, task->prev); \
277 /* The timeout hasn't expired, check for next one */ \
279 /* Break if there isn't more tasks in the queue */ \
280 if (queue->task == task->next) \
289 /* Calculates next timeout for select(). This is the timeout value
290 when at earliest some of the timeout tasks expire. If this is in the
291 past, they will be run now. This macro is used by the silc_schedule
294 #define SILC_SCHEDULE_SELECT_TIMEOUT \
296 if (schedule.timeout_queue && schedule.timeout_queue->valid == TRUE) { \
297 queue = schedule.timeout_queue; \
300 /* Get the current time */ \
301 gettimeofday(&curtime, NULL); \
302 schedule.timeout = NULL; \
304 /* First task in the task queue has always the smallest timeout. */ \
305 task = queue->task; \
307 if (task && task->valid == TRUE) { \
309 /* If the timeout is in past, we will run the task and all other \
310 timeout tasks from the past. */ \
311 if (silc_task_timeout_compare(&task->timeout, &curtime)) { \
312 SILC_SCHEDULE_RUN_TIMEOUT_TASKS; \
314 /* The task(s) has expired and doesn't exist on the task queue \
315 anymore. We continue with new timeout. */ \
316 queue = schedule.timeout_queue; \
317 task = queue->task; \
318 if (task == NULL || task->valid == FALSE) \
323 /* Calculate the next timeout for select() */ \
324 queue->timeout.tv_sec = task->timeout.tv_sec - curtime.tv_sec; \
325 queue->timeout.tv_usec = task->timeout.tv_usec - curtime.tv_usec; \
327 /* We wouldn't want to go under zero, check for it. */ \
328 if (queue->timeout.tv_usec < 0) { \
329 queue->timeout.tv_sec -= 1; \
330 queue->timeout.tv_usec += 1000000L; \
333 /* We've got the timeout value */ \
336 /* Task is not valid, remove it and try next one. */ \
337 silc_task_remove(queue, task); \
338 task = queue->task; \
339 if (queue->task == NULL) \
343 /* Save the timeout */ \
345 schedule.timeout = &queue->timeout; \
349 /* Execute generic tasks. These are executed only and only if for the
350 specific fd there wasn't other non-timeout tasks. This checks the earlier
351 set fd list, thus the generic tasks apply to all specified fd's. All the
352 generic tasks are executed at once. */
354 #define SILC_SCHEDULE_RUN_GENERIC_TASKS \
356 if (is_run == FALSE) { \
357 SILC_LOG_DEBUG(("Running generic tasks")); \
358 for (i = 0; i <= schedule.fd_list.last_fd; i++) \
359 if (schedule.fd_list.fd[i] != -1) { \
361 /* Check whether this fd is select()ed. */ \
362 if ((FD_ISSET(i, &schedule.in)) || (FD_ISSET(i, &schedule.out))) { \
364 /* It was selected. Now find the tasks from task queue and execute \
365 all generic tasks. */ \
366 if (schedule.generic_queue && schedule.generic_queue->valid) { \
367 queue = schedule.generic_queue; \
372 task = queue->task; \
375 /* Validity of the task is checked always before and after \
376 execution beacuse the task might have been unregistered \
377 in the callback function, ie. it is not valid anymore. */ \
379 if (task->valid && schedule.fd_list.fd[i] != -1) { \
380 /* Task ready for reading */ \
381 if ((schedule.fd_list.fd[i] & (1L << SILC_TASK_READ))) \
382 task->callback(queue, SILC_TASK_READ, \
386 if (task->valid && schedule.fd_list.fd[i] != -1) { \
387 /* Task ready for writing */ \
388 if ((schedule.fd_list.fd[i] & (1L << SILC_TASK_WRITE))) \
389 task->callback(queue, SILC_TASK_WRITE, \
393 if (!task->valid) { \
394 /* Invalid (unregistered) tasks are removed from the \
396 if (queue->task == task->next) { \
397 silc_task_remove(queue, task); \
402 silc_task_remove(queue, task->prev); \
406 /* Break if there isn't more tasks in the queue */ \
407 if (queue->task == task->next) \
418 /* The SILC scheduler. This is actually the main routine in SILC programs.
419 When this returns the program is to be ended. Before this function can
420 be called, one must call silc_schedule_init function. */
427 struct timeval curtime;
429 SILC_LOG_DEBUG(("Running scheduler"));
431 if (schedule.valid == FALSE) {
432 SILC_LOG_ERROR(("Scheduler is not valid, stopping"));
436 /* Start the scheduler loop */
439 SILC_LOG_DEBUG(("In scheduler loop"));
441 /* If the task queues aren't initialized or we aren't valid anymore
443 if ((!schedule.fd_queue && !schedule.timeout_queue
444 && !schedule.generic_queue) || schedule.valid == FALSE) {
445 SILC_LOG_DEBUG(("Scheduler not valid anymore, exiting"));
449 /* Clear everything */
450 FD_ZERO(&schedule.in);
451 FD_ZERO(&schedule.out);
452 schedule.max_fd = -1;
455 /* Calculate next timeout for select(). This is the timeout value
456 when at earliest some of the timeout tasks expire. */
457 SILC_SCHEDULE_SELECT_TIMEOUT;
459 /* Add the file descriptors to the fd sets. These are the non-timeout
460 tasks. The select() listens to these file descriptors. */
461 SILC_SCHEDULE_SELECT_TASKS;
463 if (schedule.max_fd == -1) {
464 SILC_LOG_ERROR(("Nothing to listen, exiting"));
468 if (schedule.timeout)
469 SILC_LOG_DEBUG(("timeout: sec=%d, usec=%d", schedule.timeout->tv_sec,
470 schedule.timeout->tv_usec));
472 /* This is the main select(). The program blocks here until some
473 of the selected file descriptors change status or the selected
475 SILC_LOG_DEBUG(("Select"));
476 switch(select(schedule.max_fd + 1, &schedule.in,
477 &schedule.out, 0, schedule.timeout)) {
480 SILC_LOG_ERROR(("Error in select(): %s", strerror(errno)));
484 SILC_LOG_DEBUG(("Running timeout tasks"));
485 gettimeofday(&curtime, NULL);
486 SILC_SCHEDULE_RUN_TIMEOUT_TASKS;
489 /* There is some data available now */
490 SILC_LOG_DEBUG(("Running non-timeout tasks"));
491 SILC_SCHEDULE_RUN_TASKS;
493 SILC_SCHEDULE_RUN_GENERIC_TASKS;