* Added Unix specific utility functions from the
lib/silcutil/silcutil.c to lib/silcutil/unix/silcunixutil.c.
+ * Added WIN32 SILC Scheduler to the file
+ lib/silcutil/win32/silcwin32schedule.c. The code is of course
+ untested.
+
Fri Jun 22 10:44:14 EEST 2001 Pekka Riikonen <priikone@silcnet.org>
* Do not handle JOIN notify in the server if the target client
silcmemory.c \
silcnet.c \
silctask.c \
+ silcschedule.c \
silcutil.c \
silchashtable.c
--- /dev/null
+/*
+
+ silcschedule.c
+
+ Author: Pekka Riikonen <priikone@silcnet.org>
+
+ Copyright (C) 1998 - 2001 Pekka Riikonen
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+*/
+/* $Id$ */
+
+#include "silcincludes.h"
+
+/* System specific implementation of the select. */
+int silc_select(int n, fd_set *readfds, fd_set *writefds,
+ fd_set *exceptfds, struct timeval *timeout);
+
+/* Structure holding list of file descriptors, scheduler is supposed to
+ be listenning. The max_fd field is the maximum number of possible file
+ descriptors in the list. This value is set at the initialization
+ of the scheduler and it usually is the maximum number of connections
+ allowed. */
+typedef struct {
+ int *fd;
+ uint32 last_fd;
+ uint32 max_fd;
+} SilcScheduleFdList;
+
+/*
+ SILC Scheduler structure.
+
+ This is the actual schedule object in SILC. Both SILC client and server
+ uses this same scheduler. Actually, this scheduler could be used by any
+ program needing scheduling.
+
+ Following short description of the fields:
+
+ SilcTaskQueue fd_queue
+
+ Task queue hook for non-timeout tasks. Usually this means that these
+ tasks perform different kind of I/O on file descriptors. File
+ descriptors are usually network sockets but they actually can be
+ any file descriptors. This hook is initialized in silc_schedule_init
+ function. Timeout tasks should not be added to this queue because
+ they will never expire.
+
+ SilcTaskQueue timeout_queue
+
+ Task queue hook for timeout tasks. This hook is reserved specificly
+ for tasks with timeout. Non-timeout tasks should not be added to this
+ queue because they will never get scheduled. This hook is also
+ initialized in silc_schedule_init function.
+
+ SilcTaskQueue generic_queue
+
+ Task queue hook for generic tasks. This hook is reserved specificly
+ for generic tasks, tasks that apply to all file descriptors, except
+ to those that have specificly registered a non-timeout task. This hook
+ is also initialized in silc_schedule_init function.
+
+ SilcScheduleFdList fd_list
+
+ List of file descriptors the scheduler is supposed to be listenning.
+ This is updated internally.
+
+ struct timeval *timeout;
+
+ Pointer to the schedules next timeout. Value of this timeout is
+ automatically updated in the silc_schedule function.
+
+ int valid
+
+ Marks validity of the scheduler. This is a boolean value. When this
+ is false the scheduler is terminated and the program will end. This
+ set to true when the scheduler is initialized with silc_schedule_init
+ function.
+
+ fd_set in
+ fd_set out
+
+ File descriptor sets for select(). These are automatically managed
+ by the scheduler and should not be touched otherwise.
+
+ int max_fd
+
+ Number of maximum file descriptors for select(). This, as well, is
+ managed automatically by the scheduler and should be considered to
+ be read-only field otherwise.
+
+*/
+struct SilcScheduleStruct {
+ SilcTaskQueue fd_queue;
+ SilcTaskQueue timeout_queue;
+ SilcTaskQueue generic_queue;
+ SilcScheduleFdList fd_list;
+ struct timeval *timeout;
+ bool valid;
+ fd_set in;
+ fd_set out;
+ int max_fd;
+};
+
+/* Initializes the scheduler. Sets the non-timeout task queue hook and
+ the timeout task queue hook. This must be called before the scheduler
+ is able to work. This will allocate the queue pointers if they are
+ not allocated. Returns the scheduler context that must be freed by
+ the silc_schedule_uninit function. */
+
+SilcSchedule silc_schedule_init(SilcTaskQueue *fd_queue,
+ SilcTaskQueue *timeout_queue,
+ SilcTaskQueue *generic_queue,
+ int max_fd)
+{
+ SilcSchedule schedule;
+ int i;
+
+ SILC_LOG_DEBUG(("Initializing scheduler"));
+
+ schedule = silc_calloc(1, sizeof(*schedule));
+
+ /* Register the task queues if they are not registered already. In SILC
+ we have by default three task queues. One task queue for non-timeout
+ tasks which perform different kind of I/O on file descriptors, timeout
+ task queue for timeout tasks, and, generic non-timeout task queue whose
+ tasks apply to all connections. */
+ if (!*fd_queue)
+ silc_task_queue_alloc(schedule, fd_queue, TRUE);
+ if (!*timeout_queue)
+ silc_task_queue_alloc(schedule, timeout_queue, TRUE);
+ if (!*generic_queue)
+ silc_task_queue_alloc(schedule, generic_queue, TRUE);
+
+ /* Initialize the scheduler */
+ schedule->fd_queue = *fd_queue;
+ schedule->timeout_queue = *timeout_queue;
+ schedule->generic_queue = *generic_queue;
+ schedule->fd_list.fd = silc_calloc(max_fd, sizeof(*schedule->fd_list.fd));
+ schedule->fd_list.last_fd = 0;
+ schedule->fd_list.max_fd = max_fd;
+ schedule->timeout = NULL;
+ schedule->valid = TRUE;
+ FD_ZERO(&schedule->in);
+ FD_ZERO(&schedule->out);
+ schedule->max_fd = -1;
+ for (i = 0; i < max_fd; i++)
+ schedule->fd_list.fd[i] = -1;
+
+ return schedule;
+}
+
+/* Uninitializes the schedule. This is called when the program is ready
+ to end. This removes all tasks and task queues. Returns FALSE if the
+ scheduler could not be uninitialized. This happens when the scheduler
+ is still valid and silc_schedule_stop has not been called. */
+
+bool silc_schedule_uninit(SilcSchedule schedule)
+{
+
+ SILC_LOG_DEBUG(("Uninitializing scheduler"));
+
+ if (schedule->valid == TRUE)
+ return FALSE;
+
+ /* Unregister all tasks */
+ if (schedule->fd_queue)
+ silc_task_remove(schedule->fd_queue, SILC_ALL_TASKS);
+ if (schedule->timeout_queue)
+ silc_task_remove(schedule->timeout_queue, SILC_ALL_TASKS);
+ if (schedule->generic_queue)
+ silc_task_remove(schedule->generic_queue, SILC_ALL_TASKS);
+
+ /* Unregister all task queues */
+ if (schedule->fd_queue)
+ silc_task_queue_free(schedule->fd_queue);
+ if (schedule->timeout_queue)
+ silc_task_queue_free(schedule->timeout_queue);
+ if (schedule->generic_queue)
+ silc_task_queue_free(schedule->generic_queue);
+
+ /* Clear the fd list */
+ if (schedule->fd_list.fd) {
+ memset(schedule->fd_list.fd, -1, schedule->fd_list.max_fd);
+ silc_free(schedule->fd_list.fd);
+ }
+
+ return TRUE;
+}
+
+/* Stops the schedule even if it is not supposed to be stopped yet.
+ After calling this, one should call silc_schedule_uninit (after the
+ silc_schedule has returned). */
+
+void silc_schedule_stop(SilcSchedule schedule)
+{
+ SILC_LOG_DEBUG(("Stopping scheduler"));
+
+ if (schedule->valid == TRUE)
+ schedule->valid = FALSE;
+}
+
+/* Sets a file descriptor to be listened by select() in scheduler. One can
+ call this directly if wanted. This can be called multiple times for
+ one file descriptor to set different iomasks. */
+
+void silc_schedule_set_listen_fd(SilcSchedule schedule, int fd, uint32 iomask)
+{
+ schedule->fd_list.fd[fd] = iomask;
+
+ if (fd > schedule->fd_list.last_fd)
+ schedule->fd_list.last_fd = fd;
+}
+
+/* Removes a file descriptor from listen list. */
+
+void silc_schedule_unset_listen_fd(SilcSchedule schedule, int fd)
+{
+ schedule->fd_list.fd[fd] = -1;
+
+ if (fd == schedule->fd_list.last_fd) {
+ int i;
+
+ for (i = fd; i >= 0; i--)
+ if (schedule->fd_list.fd[i] != -1)
+ break;
+
+ schedule->fd_list.last_fd = i < 0 ? 0 : i;
+ }
+}
+
+/* Executes tasks matching the file descriptor set by select(). The task
+ remains on the task queue after execution. Invalid tasks are removed
+ here from the task queue. This macro is used by silc_schedule function.
+ We don't have to care about the tasks priority here because the tasks
+ are sorted in their priority order already at the registration phase. */
+
+#define SILC_SCHEDULE_RUN_TASKS \
+do { \
+ queue = schedule->fd_queue; \
+ if (queue && queue->valid == TRUE && queue->task) { \
+ task = queue->task; \
+ \
+ /* Walk thorugh all tasks in the particular task queue and \
+ execute the callback functions of those tasks matching the \
+ fd set by select(). */ \
+ while(1) { \
+ /* Validity of the task is checked always before and after \
+ execution beacuse the task might have been unregistered \
+ in the callback function, ie. it is not valid anymore. */ \
+ \
+ if (task->valid) { \
+ /* Task ready for reading */ \
+ if ((FD_ISSET(task->fd, &schedule->in)) && \
+ (task->iomask & (1L << SILC_TASK_READ))) { \
+ task->callback(queue, SILC_TASK_READ, task->context, task->fd); \
+ is_run = TRUE; \
+ } \
+ } \
+ \
+ if (task->valid) { \
+ /* Task ready for writing */ \
+ if ((FD_ISSET(task->fd, &schedule->out)) && \
+ (task->iomask & (1L << SILC_TASK_WRITE))) { \
+ task->callback(queue, SILC_TASK_WRITE, task->context, task->fd); \
+ is_run = TRUE; \
+ } \
+ } \
+ \
+ if (!task->valid) { \
+ /* Invalid (unregistered) tasks are removed from the \
+ task queue. */ \
+ if (queue->task == task->next) { \
+ silc_task_remove(queue, task); \
+ break; \
+ } \
+ \
+ task = task->next; \
+ silc_task_remove(queue, task->prev); \
+ continue; \
+ } \
+ \
+ /* Break if there isn't more tasks in the queue */ \
+ if (queue->task == task->next) \
+ break; \
+ \
+ task = task->next; \
+ } \
+ } \
+} while(0)
+
+/* Selects tasks to be listened by select(). These are the non-timeout
+ tasks. This checks the scheduler's fd list. This macro is used by
+ silc_schedule function. */
+
+#define SILC_SCHEDULE_SELECT_TASKS \
+do { \
+ for (i = 0; i <= schedule->fd_list.last_fd; i++) { \
+ if (schedule->fd_list.fd[i] != -1) { \
+ \
+ /* Set the max fd value for select() to listen */ \
+ if (i > schedule->max_fd) \
+ schedule->max_fd = i; \
+ \
+ /* Add tasks for reading */ \
+ if ((schedule->fd_list.fd[i] & (1L << SILC_TASK_READ))) \
+ FD_SET(i, &schedule->in); \
+ \
+ /* Add tasks for writing */ \
+ if ((schedule->fd_list.fd[i] & (1L << SILC_TASK_WRITE))) \
+ FD_SET(i, &schedule->out); \
+ } \
+ } \
+} while(0)
+
+/* Executes all tasks whose timeout has expired. The task is removed from
+ the task queue after the callback function has returned. Also, invalid
+ tasks are removed here. The current time must be get before calling this
+ macro. This macro is used by silc_schedule function. We don't have to
+ care about priorities because tasks are already sorted in their priority
+ order at the registration phase. */
+
+#define SILC_SCHEDULE_RUN_TIMEOUT_TASKS \
+do { \
+ queue = schedule->timeout_queue; \
+ if (queue && queue->valid == TRUE && queue->task) { \
+ task = queue->task; \
+ \
+ /* Walk thorugh all tasks in the particular task queue \
+ and run all the expired tasks. */ \
+ while(1) { \
+ /* Execute the task if the timeout has expired */ \
+ if (silc_task_timeout_compare(&task->timeout, &curtime)) { \
+ \
+ /* Task ready for reading */ \
+ if (task->valid) { \
+ if ((task->iomask & (1L << SILC_TASK_READ))) \
+ task->callback(queue, SILC_TASK_READ, \
+ task->context, task->fd); \
+ } \
+ \
+ /* Task ready for writing */ \
+ if (task->valid) { \
+ if ((task->iomask & (1L << SILC_TASK_WRITE))) \
+ task->callback(queue, SILC_TASK_WRITE, \
+ task->context, task->fd); \
+ } \
+ \
+ /* Break if there isn't more tasks in the queue */ \
+ if (queue->task == task->next) { \
+ /* Remove the task from queue */ \
+ silc_task_remove(queue, task); \
+ break; \
+ } \
+ \
+ task = task->next; \
+ \
+ /* Remove the task from queue */ \
+ silc_task_remove(queue, task->prev); \
+ } else { \
+ /* The timeout hasn't expired, check for next one */ \
+ \
+ /* Break if there isn't more tasks in the queue */ \
+ if (queue->task == task->next) \
+ break; \
+ \
+ task = task->next; \
+ } \
+ } \
+ } \
+} while(0)
+
+/* Calculates next timeout for select(). This is the timeout value
+ when at earliest some of the timeout tasks expire. If this is in the
+ past, they will be run now. This macro is used by the silc_schedule
+ function. */
+
+#define SILC_SCHEDULE_SELECT_TIMEOUT \
+do { \
+ if (schedule->timeout_queue && schedule->timeout_queue->valid == TRUE) { \
+ queue = schedule->timeout_queue; \
+ task = NULL; \
+ \
+ /* Get the current time */ \
+ gettimeofday(&curtime, NULL); \
+ schedule->timeout = NULL; \
+ \
+ /* First task in the task queue has always the smallest timeout. */ \
+ task = queue->task; \
+ while(1) { \
+ if (task && task->valid == TRUE) { \
+ \
+ /* If the timeout is in past, we will run the task and all other \
+ timeout tasks from the past. */ \
+ if (silc_task_timeout_compare(&task->timeout, &curtime)) { \
+ SILC_SCHEDULE_RUN_TIMEOUT_TASKS; \
+ \
+ /* The task(s) has expired and doesn't exist on the task queue \
+ anymore. We continue with new timeout. */ \
+ queue = schedule->timeout_queue; \
+ task = queue->task; \
+ if (task == NULL || task->valid == FALSE) \
+ break; \
+ goto cont; \
+ } else { \
+ cont: \
+ /* Calculate the next timeout for select() */ \
+ queue->timeout.tv_sec = task->timeout.tv_sec - curtime.tv_sec; \
+ queue->timeout.tv_usec = task->timeout.tv_usec - curtime.tv_usec; \
+ if (queue->timeout.tv_sec < 0) \
+ queue->timeout.tv_sec = 0; \
+ \
+ /* We wouldn't want to go under zero, check for it. */ \
+ if (queue->timeout.tv_usec < 0) { \
+ queue->timeout.tv_sec -= 1; \
+ if (queue->timeout.tv_sec < 0) \
+ queue->timeout.tv_sec = 0; \
+ queue->timeout.tv_usec += 1000000L; \
+ } \
+ } \
+ /* We've got the timeout value */ \
+ break; \
+ } else { \
+ /* Task is not valid, remove it and try next one. */ \
+ silc_task_remove(queue, task); \
+ task = queue->task; \
+ if (queue->task == NULL) \
+ break; \
+ } \
+ } \
+ /* Save the timeout */ \
+ if (task) \
+ schedule->timeout = &queue->timeout; \
+ } \
+} while(0)
+
+/* Execute generic tasks. These are executed only and only if for the
+ specific fd there wasn't other non-timeout tasks. This checks the earlier
+ set fd list, thus the generic tasks apply to all specified fd's. All the
+ generic tasks are executed at once. */
+
+#define SILC_SCHEDULE_RUN_GENERIC_TASKS \
+do { \
+ if (is_run == FALSE) { \
+ SILC_LOG_DEBUG(("Running generic tasks")); \
+ for (i = 0; i <= schedule->fd_list.last_fd; i++) \
+ if (schedule->fd_list.fd[i] != -1) { \
+ \
+ /* Check whether this fd is select()ed. */ \
+ if ((FD_ISSET(i, &schedule->in)) || (FD_ISSET(i, &schedule->out))) { \
+ \
+ /* It was selected. Now find the tasks from task queue and execute \
+ all generic tasks. */ \
+ if (schedule->generic_queue && schedule->generic_queue->valid) { \
+ queue = schedule->generic_queue; \
+ \
+ if (!queue->task) \
+ break; \
+ \
+ task = queue->task; \
+ \
+ while(1) { \
+ /* Validity of the task is checked always before and after \
+ execution beacuse the task might have been unregistered \
+ in the callback function, ie. it is not valid anymore. */ \
+ \
+ if (task->valid && schedule->fd_list.fd[i] != -1) { \
+ /* Task ready for reading */ \
+ if ((schedule->fd_list.fd[i] & (1L << SILC_TASK_READ))) \
+ task->callback(queue, SILC_TASK_READ, \
+ task->context, i); \
+ } \
+ \
+ if (task->valid && schedule->fd_list.fd[i] != -1) { \
+ /* Task ready for writing */ \
+ if ((schedule->fd_list.fd[i] & (1L << SILC_TASK_WRITE))) \
+ task->callback(queue, SILC_TASK_WRITE, \
+ task->context, i); \
+ } \
+ \
+ if (!task->valid) { \
+ /* Invalid (unregistered) tasks are removed from the \
+ task queue. */ \
+ if (queue->task == task->next) { \
+ silc_task_remove(queue, task); \
+ break; \
+ } \
+ \
+ task = task->next; \
+ silc_task_remove(queue, task->prev); \
+ continue; \
+ } \
+ \
+ /* Break if there isn't more tasks in the queue */ \
+ if (queue->task == task->next) \
+ break; \
+ \
+ task = task->next; \
+ } \
+ } \
+ } \
+ } \
+ } \
+} while(0)
+
+bool silc_schedule_one(SilcSchedule schedule, int timeout_usecs)
+{
+ struct timeval timeout;
+ int is_run, i;
+ SilcTask task;
+ SilcTaskQueue queue;
+ struct timeval curtime;
+
+ SILC_LOG_DEBUG(("In scheduler loop"));
+
+ /* If the task queues aren't initialized or we aren't valid anymore
+ we will return */
+ if ((!schedule->fd_queue && !schedule->timeout_queue
+ && !schedule->generic_queue) || schedule->valid == FALSE) {
+ SILC_LOG_DEBUG(("Scheduler not valid anymore, exiting"));
+ return FALSE;
+ }
+
+ /* Clear everything */
+ FD_ZERO(&schedule->in);
+ FD_ZERO(&schedule->out);
+ schedule->max_fd = -1;
+ is_run = FALSE;
+
+ /* Calculate next timeout for select(). This is the timeout value
+ when at earliest some of the timeout tasks expire. */
+ SILC_SCHEDULE_SELECT_TIMEOUT;
+
+ /* Add the file descriptors to the fd sets. These are the non-timeout
+ tasks. The select() listens to these file descriptors. */
+ SILC_SCHEDULE_SELECT_TASKS;
+
+ if (schedule->max_fd == -1 && !schedule->timeout)
+ return FALSE;
+
+ if (schedule->timeout) {
+ SILC_LOG_DEBUG(("timeout: sec=%d, usec=%d", schedule->timeout->tv_sec,
+ schedule->timeout->tv_usec));
+ }
+
+ if (timeout_usecs >= 0) {
+ timeout.tv_sec = 0;
+ timeout.tv_usec = timeout_usecs;
+ schedule->timeout = &timeout;
+ }
+
+ /* This is the main select(). The program blocks here until some
+ of the selected file descriptors change status or the selected
+ timeout expires. */
+ SILC_LOG_DEBUG(("Select"));
+ switch (silc_select(schedule->max_fd + 1, &schedule->in,
+ &schedule->out, 0, schedule->timeout)) {
+ case -1:
+ /* Error */
+ if (errno == EINTR)
+ break;
+ SILC_LOG_ERROR(("Error in select(): %s", strerror(errno)));
+ break;
+ case 0:
+ /* Timeout */
+ SILC_LOG_DEBUG(("Running timeout tasks"));
+ gettimeofday(&curtime, NULL);
+ SILC_SCHEDULE_RUN_TIMEOUT_TASKS;
+ break;
+ default:
+ /* There is some data available now */
+ SILC_LOG_DEBUG(("Running non-timeout tasks"));
+ SILC_SCHEDULE_RUN_TASKS;
+
+ SILC_SCHEDULE_RUN_GENERIC_TASKS;
+ break;
+ }
+
+ return TRUE;
+}
+
+/* The SILC scheduler. This is actually the main routine in SILC programs.
+ When this returns the program is to be ended. Before this function can
+ be called, one must call silc_schedule_init function. */
+
+void silc_schedule(SilcSchedule schedule)
+{
+ SILC_LOG_DEBUG(("Running scheduler"));
+
+ if (schedule->valid == FALSE) {
+ SILC_LOG_ERROR(("Scheduler is not valid, stopping"));
+ return;
+ }
+
+ /* Start the scheduler loop */
+ while (silc_schedule_one(schedule, -1))
+ ;
+}
#include "silcincludes.h"
-/* Structure holding list of file descriptors, scheduler is supposed to
- be listenning. The max_fd field is the maximum number of possible file
- descriptors in the list. This value is set at the initialization
- of the scheduler and it usually is the maximum number of connections
- allowed. */
-typedef struct {
- int *fd;
- uint32 last_fd;
- uint32 max_fd;
-} SilcScheduleFdList;
+/* Calls normal select() system call. */
-/*
- SILC Unix Scheduler structure.
-
- This is the actual schedule object in SILC. Both SILC client and server
- uses this same scheduler. Actually, this scheduler could be used by any
- program needing scheduling.
-
- Following short description of the fields:
-
- SilcTaskQueue fd_queue
-
- Task queue hook for non-timeout tasks. Usually this means that these
- tasks perform different kind of I/O on file descriptors. File
- descriptors are usually network sockets but they actually can be
- any file descriptors. This hook is initialized in silc_schedule_init
- function. Timeout tasks should not be added to this queue because
- they will never expire.
-
- SilcTaskQueue timeout_queue
-
- Task queue hook for timeout tasks. This hook is reserved specificly
- for tasks with timeout. Non-timeout tasks should not be added to this
- queue because they will never get scheduled. This hook is also
- initialized in silc_schedule_init function.
-
- SilcTaskQueue generic_queue
-
- Task queue hook for generic tasks. This hook is reserved specificly
- for generic tasks, tasks that apply to all file descriptors, except
- to those that have specificly registered a non-timeout task. This hook
- is also initialized in silc_schedule_init function.
-
- SilcScheduleFdList fd_list
-
- List of file descriptors the scheduler is supposed to be listenning.
- This is updated internally.
-
- struct timeval *timeout;
-
- Pointer to the schedules next timeout. Value of this timeout is
- automatically updated in the silc_schedule function.
-
- int valid
-
- Marks validity of the scheduler. This is a boolean value. When this
- is false the scheduler is terminated and the program will end. This
- set to true when the scheduler is initialized with silc_schedule_init
- function.
-
- fd_set in
- fd_set out
-
- File descriptor sets for select(). These are automatically managed
- by the scheduler and should not be touched otherwise.
-
- int max_fd
-
- Number of maximum file descriptors for select(). This, as well, is
- managed automatically by the scheduler and should be considered to
- be read-only field otherwise.
-
-*/
-struct SilcScheduleStruct {
- SilcTaskQueue fd_queue;
- SilcTaskQueue timeout_queue;
- SilcTaskQueue generic_queue;
- SilcScheduleFdList fd_list;
- struct timeval *timeout;
- int valid;
- fd_set in;
- fd_set out;
- int max_fd;
-};
-
-/* Initializes the scheduler. Sets the non-timeout task queue hook and
- the timeout task queue hook. This must be called before the scheduler
- is able to work. This will allocate the queue pointers if they are
- not allocated. Returns the scheduler context that must be freed by
- the silc_schedule_uninit function. */
-
-SilcSchedule silc_schedule_init(SilcTaskQueue *fd_queue,
- SilcTaskQueue *timeout_queue,
- SilcTaskQueue *generic_queue,
- int max_fd)
-{
- SilcSchedule schedule;
- int i;
-
- SILC_LOG_DEBUG(("Initializing scheduler"));
-
- schedule = silc_calloc(1, sizeof(*schedule));
-
- /* Register the task queues if they are not registered already. In SILC
- we have by default three task queues. One task queue for non-timeout
- tasks which perform different kind of I/O on file descriptors, timeout
- task queue for timeout tasks, and, generic non-timeout task queue whose
- tasks apply to all connections. */
- if (!*fd_queue)
- silc_task_queue_alloc(schedule, fd_queue, TRUE);
- if (!*timeout_queue)
- silc_task_queue_alloc(schedule, timeout_queue, TRUE);
- if (!*generic_queue)
- silc_task_queue_alloc(schedule, generic_queue, TRUE);
-
- /* Initialize the scheduler */
- schedule->fd_queue = *fd_queue;
- schedule->timeout_queue = *timeout_queue;
- schedule->generic_queue = *generic_queue;
- schedule->fd_list.fd = silc_calloc(max_fd, sizeof(int));
- schedule->fd_list.last_fd = 0;
- schedule->fd_list.max_fd = max_fd;
- schedule->timeout = NULL;
- schedule->valid = TRUE;
- FD_ZERO(&schedule->in);
- FD_ZERO(&schedule->out);
- schedule->max_fd = -1;
- for (i = 0; i < max_fd; i++)
- schedule->fd_list.fd[i] = -1;
-
- return schedule;
-}
-
-/* Uninitializes the schedule. This is called when the program is ready
- to end. This removes all tasks and task queues. Returns FALSE if the
- scheduler could not be uninitialized. This happens when the scheduler
- is still valid and silc_schedule_stop has not been called. */
-
-bool silc_schedule_uninit(SilcSchedule schedule)
-{
-
- SILC_LOG_DEBUG(("Uninitializing scheduler"));
-
- if (schedule->valid == TRUE)
- return FALSE;
-
- /* Unregister all tasks */
- if (schedule->fd_queue)
- silc_task_remove(schedule->fd_queue, SILC_ALL_TASKS);
- if (schedule->timeout_queue)
- silc_task_remove(schedule->timeout_queue, SILC_ALL_TASKS);
- if (schedule->generic_queue)
- silc_task_remove(schedule->generic_queue, SILC_ALL_TASKS);
-
- /* Unregister all task queues */
- if (schedule->fd_queue)
- silc_task_queue_free(schedule->fd_queue);
- if (schedule->timeout_queue)
- silc_task_queue_free(schedule->timeout_queue);
- if (schedule->generic_queue)
- silc_task_queue_free(schedule->generic_queue);
-
- /* Clear the fd list */
- if (schedule->fd_list.fd) {
- memset(schedule->fd_list.fd, -1, schedule->fd_list.max_fd);
- silc_free(schedule->fd_list.fd);
- }
-
- memset(&schedule, 'F', sizeof(schedule));
- return TRUE;
-}
-
-/* Stops the schedule even if it is not supposed to be stopped yet.
- After calling this, one should call silc_schedule_uninit (after the
- silc_schedule has returned). */
-
-void silc_schedule_stop(SilcSchedule schedule)
-{
- SILC_LOG_DEBUG(("Stopping scheduler"));
-
- if (schedule->valid == TRUE)
- schedule->valid = FALSE;
-}
-
-/* Sets a file descriptor to be listened by select() in scheduler. One can
- call this directly if wanted. This can be called multiple times for
- one file descriptor to set different iomasks. */
-
-void silc_schedule_set_listen_fd(SilcSchedule schedule, int fd, uint32 iomask)
-{
- schedule->fd_list.fd[fd] = iomask;
-
- if (fd > schedule->fd_list.last_fd)
- schedule->fd_list.last_fd = fd;
-}
-
-/* Removes a file descriptor from listen list. */
-
-void silc_schedule_unset_listen_fd(SilcSchedule schedule, int fd)
-{
- schedule->fd_list.fd[fd] = -1;
-
- if (fd == schedule->fd_list.last_fd) {
- int i;
-
- for (i = fd; i >= 0; i--)
- if (schedule->fd_list.fd[i] != -1)
- break;
-
- schedule->fd_list.last_fd = i < 0 ? 0 : i;
- }
-}
-
-/* Executes tasks matching the file descriptor set by select(). The task
- remains on the task queue after execution. Invalid tasks are removed
- here from the task queue. This macro is used by silc_schedule function.
- We don't have to care about the tasks priority here because the tasks
- are sorted in their priority order already at the registration phase. */
-
-#define SILC_SCHEDULE_RUN_TASKS \
-do { \
- queue = schedule->fd_queue; \
- if (queue && queue->valid == TRUE && queue->task) { \
- task = queue->task; \
- \
- /* Walk thorugh all tasks in the particular task queue and \
- execute the callback functions of those tasks matching the \
- fd set by select(). */ \
- while(1) { \
- /* Validity of the task is checked always before and after \
- execution beacuse the task might have been unregistered \
- in the callback function, ie. it is not valid anymore. */ \
- \
- if (task->valid) { \
- /* Task ready for reading */ \
- if ((FD_ISSET(task->fd, &schedule->in)) && \
- (task->iomask & (1L << SILC_TASK_READ))) { \
- task->callback(queue, SILC_TASK_READ, task->context, task->fd); \
- is_run = TRUE; \
- } \
- } \
- \
- if (task->valid) { \
- /* Task ready for writing */ \
- if ((FD_ISSET(task->fd, &schedule->out)) && \
- (task->iomask & (1L << SILC_TASK_WRITE))) { \
- task->callback(queue, SILC_TASK_WRITE, task->context, task->fd); \
- is_run = TRUE; \
- } \
- } \
- \
- if (!task->valid) { \
- /* Invalid (unregistered) tasks are removed from the \
- task queue. */ \
- if (queue->task == task->next) { \
- silc_task_remove(queue, task); \
- break; \
- } \
- \
- task = task->next; \
- silc_task_remove(queue, task->prev); \
- continue; \
- } \
- \
- /* Break if there isn't more tasks in the queue */ \
- if (queue->task == task->next) \
- break; \
- \
- task = task->next; \
- } \
- } \
-} while(0)
-
-/* Selects tasks to be listened by select(). These are the non-timeout
- tasks. This checks the scheduler's fd list. This macro is used by
- silc_schedule function. */
-
-#define SILC_SCHEDULE_SELECT_TASKS \
-do { \
- for (i = 0; i <= schedule->fd_list.last_fd; i++) { \
- if (schedule->fd_list.fd[i] != -1) { \
- \
- /* Set the max fd value for select() to listen */ \
- if (i > schedule->max_fd) \
- schedule->max_fd = i; \
- \
- /* Add tasks for reading */ \
- if ((schedule->fd_list.fd[i] & (1L << SILC_TASK_READ))) \
- FD_SET(i, &schedule->in); \
- \
- /* Add tasks for writing */ \
- if ((schedule->fd_list.fd[i] & (1L << SILC_TASK_WRITE))) \
- FD_SET(i, &schedule->out); \
- } \
- } \
-} while(0)
-
-/* Executes all tasks whose timeout has expired. The task is removed from
- the task queue after the callback function has returned. Also, invalid
- tasks are removed here. The current time must be get before calling this
- macro. This macro is used by silc_schedule function. We don't have to
- care about priorities because tasks are already sorted in their priority
- order at the registration phase. */
-
-#define SILC_SCHEDULE_RUN_TIMEOUT_TASKS \
-do { \
- queue = schedule->timeout_queue; \
- if (queue && queue->valid == TRUE && queue->task) { \
- task = queue->task; \
- \
- /* Walk thorugh all tasks in the particular task queue \
- and run all the expired tasks. */ \
- while(1) { \
- /* Execute the task if the timeout has expired */ \
- if (silc_task_timeout_compare(&task->timeout, &curtime)) { \
- \
- /* Task ready for reading */ \
- if (task->valid) { \
- if ((task->iomask & (1L << SILC_TASK_READ))) \
- task->callback(queue, SILC_TASK_READ, \
- task->context, task->fd); \
- } \
- \
- /* Task ready for writing */ \
- if (task->valid) { \
- if ((task->iomask & (1L << SILC_TASK_WRITE))) \
- task->callback(queue, SILC_TASK_WRITE, \
- task->context, task->fd); \
- } \
- \
- /* Break if there isn't more tasks in the queue */ \
- if (queue->task == task->next) { \
- /* Remove the task from queue */ \
- silc_task_remove(queue, task); \
- break; \
- } \
- \
- task = task->next; \
- \
- /* Remove the task from queue */ \
- silc_task_remove(queue, task->prev); \
- } else { \
- /* The timeout hasn't expired, check for next one */ \
- \
- /* Break if there isn't more tasks in the queue */ \
- if (queue->task == task->next) \
- break; \
- \
- task = task->next; \
- } \
- } \
- } \
-} while(0)
-
-/* Calculates next timeout for select(). This is the timeout value
- when at earliest some of the timeout tasks expire. If this is in the
- past, they will be run now. This macro is used by the silc_schedule
- function. */
-
-#define SILC_SCHEDULE_SELECT_TIMEOUT \
-do { \
- if (schedule->timeout_queue && schedule->timeout_queue->valid == TRUE) { \
- queue = schedule->timeout_queue; \
- task = NULL; \
- \
- /* Get the current time */ \
- gettimeofday(&curtime, NULL); \
- schedule->timeout = NULL; \
- \
- /* First task in the task queue has always the smallest timeout. */ \
- task = queue->task; \
- while(1) { \
- if (task && task->valid == TRUE) { \
- \
- /* If the timeout is in past, we will run the task and all other \
- timeout tasks from the past. */ \
- if (silc_task_timeout_compare(&task->timeout, &curtime)) { \
- SILC_SCHEDULE_RUN_TIMEOUT_TASKS; \
- \
- /* The task(s) has expired and doesn't exist on the task queue \
- anymore. We continue with new timeout. */ \
- queue = schedule->timeout_queue; \
- task = queue->task; \
- if (task == NULL || task->valid == FALSE) \
- break; \
- goto cont; \
- } else { \
- cont: \
- /* Calculate the next timeout for select() */ \
- queue->timeout.tv_sec = task->timeout.tv_sec - curtime.tv_sec; \
- queue->timeout.tv_usec = task->timeout.tv_usec - curtime.tv_usec; \
- if (queue->timeout.tv_sec < 0) \
- queue->timeout.tv_sec = 0; \
- \
- /* We wouldn't want to go under zero, check for it. */ \
- if (queue->timeout.tv_usec < 0) { \
- queue->timeout.tv_sec -= 1; \
- if (queue->timeout.tv_sec < 0) \
- queue->timeout.tv_sec = 0; \
- queue->timeout.tv_usec += 1000000L; \
- } \
- } \
- /* We've got the timeout value */ \
- break; \
- } else { \
- /* Task is not valid, remove it and try next one. */ \
- silc_task_remove(queue, task); \
- task = queue->task; \
- if (queue->task == NULL) \
- break; \
- } \
- } \
- /* Save the timeout */ \
- if (task) \
- schedule->timeout = &queue->timeout; \
- } \
-} while(0)
-
-/* Execute generic tasks. These are executed only and only if for the
- specific fd there wasn't other non-timeout tasks. This checks the earlier
- set fd list, thus the generic tasks apply to all specified fd's. All the
- generic tasks are executed at once. */
-
-#define SILC_SCHEDULE_RUN_GENERIC_TASKS \
-do { \
- if (is_run == FALSE) { \
- SILC_LOG_DEBUG(("Running generic tasks")); \
- for (i = 0; i <= schedule->fd_list.last_fd; i++) \
- if (schedule->fd_list.fd[i] != -1) { \
- \
- /* Check whether this fd is select()ed. */ \
- if ((FD_ISSET(i, &schedule->in)) || (FD_ISSET(i, &schedule->out))) { \
- \
- /* It was selected. Now find the tasks from task queue and execute \
- all generic tasks. */ \
- if (schedule->generic_queue && schedule->generic_queue->valid) { \
- queue = schedule->generic_queue; \
- \
- if (!queue->task) \
- break; \
- \
- task = queue->task; \
- \
- while(1) { \
- /* Validity of the task is checked always before and after \
- execution beacuse the task might have been unregistered \
- in the callback function, ie. it is not valid anymore. */ \
- \
- if (task->valid && schedule->fd_list.fd[i] != -1) { \
- /* Task ready for reading */ \
- if ((schedule->fd_list.fd[i] & (1L << SILC_TASK_READ))) \
- task->callback(queue, SILC_TASK_READ, \
- task->context, i); \
- } \
- \
- if (task->valid && schedule->fd_list.fd[i] != -1) { \
- /* Task ready for writing */ \
- if ((schedule->fd_list.fd[i] & (1L << SILC_TASK_WRITE))) \
- task->callback(queue, SILC_TASK_WRITE, \
- task->context, i); \
- } \
- \
- if (!task->valid) { \
- /* Invalid (unregistered) tasks are removed from the \
- task queue. */ \
- if (queue->task == task->next) { \
- silc_task_remove(queue, task); \
- break; \
- } \
- \
- task = task->next; \
- silc_task_remove(queue, task->prev); \
- continue; \
- } \
- \
- /* Break if there isn't more tasks in the queue */ \
- if (queue->task == task->next) \
- break; \
- \
- task = task->next; \
- } \
- } \
- } \
- } \
- } \
-} while(0)
-
-bool silc_schedule_one(SilcSchedule schedule, int timeout_usecs)
+int silc_select(int n, fd_set *readfds, fd_set *writefds,
+ fd_set *exceptfds, struct timeval *timeout)
{
- struct timeval timeout;
- int is_run, i;
- SilcTask task;
- SilcTaskQueue queue;
- struct timeval curtime;
-
- SILC_LOG_DEBUG(("In scheduler loop"));
-
- /* If the task queues aren't initialized or we aren't valid anymore
- we will return */
- if ((!schedule->fd_queue && !schedule->timeout_queue
- && !schedule->generic_queue) || schedule->valid == FALSE) {
- SILC_LOG_DEBUG(("Scheduler not valid anymore, exiting"));
- return FALSE;
- }
-
- /* Clear everything */
- FD_ZERO(&schedule->in);
- FD_ZERO(&schedule->out);
- schedule->max_fd = -1;
- is_run = FALSE;
-
- /* Calculate next timeout for select(). This is the timeout value
- when at earliest some of the timeout tasks expire. */
- SILC_SCHEDULE_SELECT_TIMEOUT;
-
- /* Add the file descriptors to the fd sets. These are the non-timeout
- tasks. The select() listens to these file descriptors. */
- SILC_SCHEDULE_SELECT_TASKS;
-
- if (schedule->max_fd == -1 && !schedule->timeout)
- return FALSE;
-
- if (schedule->timeout) {
- SILC_LOG_DEBUG(("timeout: sec=%d, usec=%d", schedule->timeout->tv_sec,
- schedule->timeout->tv_usec));
- }
-
- if (timeout_usecs >= 0) {
- timeout.tv_sec = 0;
- timeout.tv_usec = timeout_usecs;
- schedule->timeout = &timeout;
- }
-
- /* This is the main select(). The program blocks here until some
- of the selected file descriptors change status or the selected
- timeout expires. */
- SILC_LOG_DEBUG(("Select"));
- switch (select(schedule->max_fd + 1, &schedule->in,
- &schedule->out, 0, schedule->timeout)) {
- case -1:
- /* Error */
- if (errno == EINTR)
- break;
- SILC_LOG_ERROR(("Error in select(): %s", strerror(errno)));
- break;
- case 0:
- /* Timeout */
- SILC_LOG_DEBUG(("Running timeout tasks"));
- gettimeofday(&curtime, NULL);
- SILC_SCHEDULE_RUN_TIMEOUT_TASKS;
- break;
- default:
- /* There is some data available now */
- SILC_LOG_DEBUG(("Running non-timeout tasks"));
- SILC_SCHEDULE_RUN_TASKS;
-
- SILC_SCHEDULE_RUN_GENERIC_TASKS;
- break;
- }
-
- return TRUE;
-}
-
-/* The SILC scheduler. This is actually the main routine in SILC programs.
- When this returns the program is to be ended. Before this function can
- be called, one must call silc_schedule_init function. */
-
-void silc_schedule(SilcSchedule schedule)
-{
- SILC_LOG_DEBUG(("Running scheduler"));
-
- if (schedule->valid == FALSE) {
- SILC_LOG_ERROR(("Scheduler is not valid, stopping"));
- return;
- }
-
- /* Start the scheduler loop */
- while (silc_schedule_one(schedule, -1))
- ;
+ return select(n, readfds, writefds, exceptfds, timeout);
}
noinst_LIBRARIES = libsilcwin32util.a
-libsilcwin32util_a_SOURCES = \
- silcwin32net.c
+libsilcwin32util_a_SOURCES = \
+ silcwin32net.c \
+ silcwin32schedule.c
EXTRA_DIST = *.h
#include "silcincludes.h"
+/* XXX Untested! */
+
/* Our "select()" for WIN32. This actually is not the select() and does
not call Winsock's select() (since it cannot be used for our purposes)
but mimics the functions of select().