updates.
[crypto.git] / lib / silcutil / silcschedule.c
1 /*
2
3   silcschedule.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1998 - 2001 Pekka Riikonen
8
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.
13   
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.
18
19 */
20 /* $Id$ */
21 /* XXX on multi-threads the task queue locking is missing here. */
22
23 #include "silcincludes.h"
24
25 /* Routine to remove the task. Implemented in silctask.c. */
26 int silc_task_remove(SilcTaskQueue queue, SilcTask task);
27
28 /* System specific routines. Implemented under unix/ and win32/. */
29
30 /* System specific select(). */
31 int silc_select(int n, fd_set *readfds, fd_set *writefds,
32                 fd_set *exceptfds, struct timeval *timeout);
33
34 /* Initializes the wakeup of the scheduler. In multi-threaded environment
35    the scheduler needs to be wakenup when tasks are added or removed from
36    the task queues. This will initialize the wakeup for the scheduler.
37    Any tasks that needs to be registered must be registered to the `queue'.
38    It is guaranteed that the scheduler will automatically free any
39    registered tasks in this queue. This is system specific routine. */
40 void *silc_schedule_wakeup_init(void *queue);
41
42 /* Uninitializes the system specific wakeup. */
43 void silc_schedule_wakeup_uninit(void *context);
44
45 /* Wakes up the scheduler. This is platform specific routine */
46 void silc_schedule_wakeup_internal(void *context);
47
48 /* Structure holding list of file descriptors, scheduler is supposed to
49    be listenning. The max_fd field is the maximum number of possible file
50    descriptors in the list. This value is set at the initialization
51    of the scheduler and it usually is the maximum number of connections 
52    allowed. */
53 typedef struct {
54   int *fd;
55   uint32 last_fd;
56   uint32 max_fd;
57 } SilcScheduleFdList;
58
59 /* 
60    SILC Scheduler structure.
61
62    This is the actual schedule object in SILC. Both SILC client and server 
63    uses this same scheduler. Actually, this scheduler could be used by any
64    program needing scheduling.
65
66    Following short description of the fields:
67
68    SilcTaskQueue fd_queue
69
70        Task queue hook for non-timeout tasks. Usually this means that these
71        tasks perform different kind of I/O on file descriptors. File 
72        descriptors are usually network sockets but they actually can be
73        any file descriptors. This hook is initialized in silc_schedule_init
74        function. Timeout tasks should not be added to this queue because
75        they will never expire.
76
77    SilcTaskQueue timeout_queue
78
79        Task queue hook for timeout tasks. This hook is reserved specificly
80        for tasks with timeout. Non-timeout tasks should not be added to this
81        queue because they will never get scheduled. This hook is also
82        initialized in silc_schedule_init function.
83
84    SilcTaskQueue generic_queue
85
86        Task queue hook for generic tasks. This hook is reserved specificly
87        for generic tasks, tasks that apply to all file descriptors, except
88        to those that have specificly registered a non-timeout task. This hook
89        is also initialized in silc_schedule_init function.
90
91    SilcScheduleFdList fd_list
92
93        List of file descriptors the scheduler is supposed to be listenning.
94        This is updated internally.
95
96    struct timeval *timeout;
97
98        Pointer to the schedules next timeout. Value of this timeout is
99        automatically updated in the silc_schedule function.
100
101    int valid
102
103        Marks validity of the scheduler. This is a boolean value. When this
104        is false the scheduler is terminated and the program will end. This
105        set to true when the scheduler is initialized with silc_schedule_init
106        function.
107
108    fd_set in
109    fd_set out
110
111        File descriptor sets for select(). These are automatically managed
112        by the scheduler and should not be touched otherwise.
113
114    int max_fd
115
116        Number of maximum file descriptors for select(). This, as well, is
117        managed automatically by the scheduler and should be considered to 
118        be read-only field otherwise.
119
120    void *wakeup
121
122        System specific wakeup context. On multi-threaded environments the
123        scheduler needs to be wakenup (in the thread) when tasks are added
124        or removed. This is initialized by silc_schedule_wakeup_init.
125
126 */
127 struct SilcScheduleStruct {
128   SilcTaskQueue fd_queue;
129   SilcTaskQueue timeout_queue;
130   SilcTaskQueue generic_queue;
131   SilcScheduleFdList fd_list;
132   struct timeval *timeout;
133   bool valid;
134   fd_set in;
135   fd_set out;
136   int max_fd;
137   void *wakeup;
138   SILC_MUTEX_DEFINE(lock);
139 };
140
141 /* Initializes the scheduler. Sets the non-timeout task queue hook and
142    the timeout task queue hook. This must be called before the scheduler
143    is able to work. This will allocate the queue pointers if they are
144    not allocated. Returns the scheduler context that must be freed by
145    the silc_schedule_uninit function. */
146
147 SilcSchedule silc_schedule_init(SilcTaskQueue *fd_queue,
148                                 SilcTaskQueue *timeout_queue,
149                                 SilcTaskQueue *generic_queue,
150                                 int max_fd)
151 {
152   SilcSchedule schedule;
153   int i;
154
155   SILC_LOG_DEBUG(("Initializing scheduler"));
156
157   schedule = silc_calloc(1, sizeof(*schedule));
158
159   /* Register the task queues if they are not registered already. In SILC
160      we have by default three task queues. One task queue for non-timeout
161      tasks which perform different kind of I/O on file descriptors, timeout
162      task queue for timeout tasks, and, generic non-timeout task queue whose
163      tasks apply to all connections. */
164   if (!*fd_queue)
165     silc_task_queue_alloc(schedule, fd_queue, TRUE);
166   if (!*timeout_queue)
167     silc_task_queue_alloc(schedule, timeout_queue, TRUE);
168   if (!*generic_queue)
169     silc_task_queue_alloc(schedule, generic_queue, TRUE);
170
171   /* Initialize the scheduler */
172   schedule->fd_queue = *fd_queue;
173   schedule->timeout_queue = *timeout_queue;
174   schedule->generic_queue = *generic_queue;
175   schedule->fd_list.fd = silc_calloc(max_fd, sizeof(*schedule->fd_list.fd));
176   schedule->fd_list.last_fd = 0;
177   schedule->fd_list.max_fd = max_fd;
178   schedule->timeout = NULL;
179   schedule->valid = TRUE;
180   FD_ZERO(&schedule->in);
181   FD_ZERO(&schedule->out);
182   schedule->max_fd = -1;
183   for (i = 0; i < max_fd; i++)
184     schedule->fd_list.fd[i] = -1;
185
186   silc_mutex_alloc(&schedule->lock);
187
188   /* Initialize the wakeup */
189   schedule->wakeup = silc_schedule_wakeup_init(schedule->fd_queue);
190
191   return schedule;
192 }
193
194 /* Uninitializes the schedule. This is called when the program is ready
195    to end. This removes all tasks and task queues. Returns FALSE if the
196    scheduler could not be uninitialized. This happens when the scheduler
197    is still valid and silc_schedule_stop has not been called. */
198
199 bool silc_schedule_uninit(SilcSchedule schedule)
200 {
201
202   SILC_LOG_DEBUG(("Uninitializing scheduler"));
203
204   if (schedule->valid == TRUE)
205     return FALSE;
206
207   /* Unregister all tasks */
208   if (schedule->fd_queue)
209     silc_task_remove(schedule->fd_queue, SILC_ALL_TASKS);
210   if (schedule->timeout_queue)
211     silc_task_remove(schedule->timeout_queue, SILC_ALL_TASKS);
212   if (schedule->generic_queue)
213     silc_task_remove(schedule->generic_queue, SILC_ALL_TASKS);
214
215   /* Unregister all task queues */
216   if (schedule->fd_queue)
217     silc_task_queue_free(schedule->fd_queue);
218   if (schedule->timeout_queue)
219     silc_task_queue_free(schedule->timeout_queue);
220   if (schedule->generic_queue)
221     silc_task_queue_free(schedule->generic_queue);
222
223   /* Clear the fd list */
224   if (schedule->fd_list.fd) {
225     memset(schedule->fd_list.fd, -1, schedule->fd_list.max_fd);
226     silc_free(schedule->fd_list.fd);
227   }
228
229   /* Uninit the wakeup */
230   silc_schedule_wakeup_uninit(schedule->wakeup);
231
232   silc_mutex_free(schedule->lock);
233
234   return TRUE;
235 }
236
237 /* Stops the schedule even if it is not supposed to be stopped yet. 
238    After calling this, one should call silc_schedule_uninit (after the 
239    silc_schedule has returned). */
240
241 void silc_schedule_stop(SilcSchedule schedule)
242 {
243   SILC_LOG_DEBUG(("Stopping scheduler"));
244   schedule->valid = FALSE;
245 }
246
247 /* Sets a file descriptor to be listened by select() in scheduler. One can
248    call this directly if wanted. This can be called multiple times for
249    one file descriptor to set different iomasks. */
250
251 void silc_schedule_set_listen_fd(SilcSchedule schedule, int fd, uint32 iomask)
252 {
253   silc_mutex_lock(schedule->lock);
254
255   schedule->fd_list.fd[fd] = iomask;
256   
257   if (fd > schedule->fd_list.last_fd)
258     schedule->fd_list.last_fd = fd;
259
260   silc_mutex_unlock(schedule->lock);
261 }
262
263 /* Removes a file descriptor from listen list. */
264
265 void silc_schedule_unset_listen_fd(SilcSchedule schedule, int fd)
266 {
267   silc_mutex_lock(schedule->lock);
268
269   schedule->fd_list.fd[fd] = -1;
270   
271   if (fd == schedule->fd_list.last_fd) {
272     int i;
273
274     for (i = fd; i >= 0; i--)
275       if (schedule->fd_list.fd[i] != -1)
276         break;
277
278     schedule->fd_list.last_fd = i < 0 ? 0 : i;
279   }
280
281   silc_mutex_unlock(schedule->lock);
282 }
283
284 /* Executes tasks matching the file descriptor set by select(). The task
285    remains on the task queue after execution. Invalid tasks are removed 
286    here from the task queue. This macro is used by silc_schedule function. 
287    We don't have to care about the tasks priority here because the tasks
288    are sorted in their priority order already at the registration phase. */
289
290 #define SILC_SCHEDULE_RUN_TASKS                                            \
291 do {                                                                       \
292   queue = schedule->fd_queue;                                              \
293   if (queue && queue->valid == TRUE && queue->task) {                      \
294     task = queue->task;                                                    \
295                                                                            \
296     /* Walk thorugh all tasks in the particular task queue and             \
297        execute the callback functions of those tasks matching the          \
298        fd set by select(). */                                              \
299     while(1) {                                                             \
300       /* Validity of the task is checked always before and after           \
301          execution beacuse the task might have been unregistered           \
302          in the callback function, ie. it is not valid anymore. */         \
303                                                                            \
304       if (task->valid) {                                                   \
305         /* Task ready for reading */                                       \
306         if ((FD_ISSET(task->fd, &schedule->in)) &&                         \
307             (task->iomask & (1L << SILC_TASK_READ))) {                     \
308           task->callback(queue, SILC_TASK_READ, task->context, task->fd);  \
309           is_run = TRUE;                                                   \
310         }                                                                  \
311       }                                                                    \
312                                                                            \
313       if (task->valid) {                                                   \
314         /* Task ready for writing */                                       \
315         if ((FD_ISSET(task->fd, &schedule->out)) &&                        \
316             (task->iomask & (1L << SILC_TASK_WRITE))) {                    \
317           task->callback(queue, SILC_TASK_WRITE, task->context, task->fd); \
318           is_run = TRUE;                                                   \
319         }                                                                  \
320       }                                                                    \
321                                                                            \
322       if (!task->valid) {                                                  \
323         /* Invalid (unregistered) tasks are removed from the               \
324            task queue. */                                                  \
325         if (queue->task == task->next) {                                   \
326           silc_task_remove(queue, task);                                   \
327           break;                                                           \
328         }                                                                  \
329                                                                            \
330         task = task->next;                                                 \
331         silc_task_remove(queue, task->prev);                               \
332         continue;                                                          \
333       }                                                                    \
334                                                                            \
335       /* Break if there isn't more tasks in the queue */                   \
336       if (queue->task == task->next)                                       \
337         break;                                                             \
338                                                                            \
339       task = task->next;                                                   \
340     }                                                                      \
341   }                                                                        \
342 } while(0)
343
344 /* Selects tasks to be listened by select(). These are the non-timeout
345    tasks. This checks the scheduler's fd list. This macro is used by 
346    silc_schedule function. */
347
348 #define SILC_SCHEDULE_SELECT_TASKS                              \
349 do {                                                            \
350   for (i = 0; i <= schedule->fd_list.last_fd; i++) {            \
351     if (schedule->fd_list.fd[i] != -1) {                        \
352                                                                 \
353       /* Set the max fd value for select() to listen */         \
354       if (i > schedule->max_fd)                                 \
355         schedule->max_fd = i;                                   \
356                                                                 \
357       /* Add tasks for reading */                               \
358       if ((schedule->fd_list.fd[i] & (1L << SILC_TASK_READ)))   \
359         FD_SET(i, &schedule->in);                               \
360                                                                 \
361       /* Add tasks for writing */                               \
362       if ((schedule->fd_list.fd[i] & (1L << SILC_TASK_WRITE)))  \
363         FD_SET(i, &schedule->out);                              \
364     }                                                           \
365   }                                                             \
366 } while(0)
367
368 /* Executes all tasks whose timeout has expired. The task is removed from
369    the task queue after the callback function has returned. Also, invalid
370    tasks are removed here. The current time must be get before calling this
371    macro. This macro is used by silc_schedule function. We don't have to
372    care about priorities because tasks are already sorted in their priority
373    order at the registration phase. */
374
375 #define SILC_SCHEDULE_RUN_TIMEOUT_TASKS                                 \
376 do {                                                                    \
377   queue = schedule->timeout_queue;                                      \
378   if (queue && queue->valid == TRUE && queue->task) {                   \
379     task = queue->task;                                                 \
380                                                                         \
381     /* Walk thorugh all tasks in the particular task queue              \
382        and run all the expired tasks. */                                \
383     while(1) {                                                          \
384       /* Execute the task if the timeout has expired */                 \
385       if (silc_task_timeout_compare(&task->timeout, &curtime)) {        \
386                                                                         \
387         /* Task ready for reading */                                    \
388         if (task->valid) {                                              \
389           if ((task->iomask & (1L << SILC_TASK_READ)))                  \
390             task->callback(queue, SILC_TASK_READ,                       \
391                            task->context, task->fd);                    \
392         }                                                               \
393                                                                         \
394         /* Task ready for writing */                                    \
395         if (task->valid) {                                              \
396           if ((task->iomask & (1L << SILC_TASK_WRITE)))                 \
397             task->callback(queue, SILC_TASK_WRITE,                      \
398                            task->context, task->fd);                    \
399         }                                                               \
400                                                                         \
401         /* Break if there isn't more tasks in the queue */              \
402         if (queue->task == task->next) {                                \
403           /* Remove the task from queue */                              \
404           silc_task_remove(queue, task);                                \
405           break;                                                        \
406         }                                                               \
407                                                                         \
408         task = task->next;                                              \
409                                                                         \
410         /* Remove the task from queue */                                \
411         silc_task_remove(queue, task->prev);                            \
412       } else {                                                          \
413         /* The timeout hasn't expired, check for next one */            \
414                                                                         \
415         /* Break if there isn't more tasks in the queue */              \
416         if (queue->task == task->next)                                  \
417           break;                                                        \
418                                                                         \
419         task = task->next;                                              \
420       }                                                                 \
421     }                                                                   \
422   }                                                                     \
423 } while(0)
424
425 /* Calculates next timeout for select(). This is the timeout value
426    when at earliest some of the timeout tasks expire. If this is in the
427    past, they will be run now. This macro is used by the silc_schedule
428    function. */
429
430 #define SILC_SCHEDULE_SELECT_TIMEOUT                                        \
431 do {                                                                        \
432   if (schedule->timeout_queue && schedule->timeout_queue->valid == TRUE) {  \
433     queue = schedule->timeout_queue;                                        \
434     task = NULL;                                                            \
435                                                                             \
436     /* Get the current time */                                              \
437     silc_gettimeofday(&curtime);                                            \
438     schedule->timeout = NULL;                                               \
439                                                                             \
440     /* First task in the task queue has always the smallest timeout. */     \
441     task = queue->task;                                                     \
442     while(1) {                                                              \
443       if (task && task->valid == TRUE) {                                    \
444                                                                             \
445         /* If the timeout is in past, we will run the task and all other    \
446            timeout tasks from the past. */                                  \
447         if (silc_task_timeout_compare(&task->timeout, &curtime)) {          \
448           SILC_SCHEDULE_RUN_TIMEOUT_TASKS;                                  \
449                                                                             \
450           /* The task(s) has expired and doesn't exist on the task queue    \
451              anymore. We continue with new timeout. */                      \
452           queue = schedule->timeout_queue;                                  \
453           task = queue->task;                                               \
454           if (task == NULL || task->valid == FALSE)                         \
455             break;                                                          \
456           goto cont;                                                        \
457         } else {                                                            \
458  cont:                                                                      \
459           /* Calculate the next timeout for select() */                     \
460           queue->timeout.tv_sec = task->timeout.tv_sec - curtime.tv_sec;    \
461           queue->timeout.tv_usec = task->timeout.tv_usec - curtime.tv_usec; \
462           if (queue->timeout.tv_sec < 0)                                    \
463             queue->timeout.tv_sec = 0;                                      \
464                                                                             \
465           /* We wouldn't want to go under zero, check for it. */            \
466           if (queue->timeout.tv_usec < 0) {                                 \
467             queue->timeout.tv_sec -= 1;                                     \
468             if (queue->timeout.tv_sec < 0)                                  \
469               queue->timeout.tv_sec = 0;                                    \
470             queue->timeout.tv_usec += 1000000L;                             \
471           }                                                                 \
472         }                                                                   \
473         /* We've got the timeout value */                                   \
474         break;                                                              \
475       } else {                                                              \
476         /* Task is not valid, remove it and try next one. */                \
477         silc_task_remove(queue, task);                                      \
478         task = queue->task;                                                 \
479         if (queue->task == NULL)                                            \
480           break;                                                            \
481       }                                                                     \
482     }                                                                       \
483     /* Save the timeout */                                                  \
484     if (task)                                                               \
485       schedule->timeout = &queue->timeout;                                  \
486   }                                                                         \
487 } while(0)
488
489 /* Execute generic tasks. These are executed only and only if for the
490    specific fd there wasn't other non-timeout tasks. This checks the earlier
491    set fd list, thus the generic tasks apply to all specified fd's. All the
492    generic tasks are executed at once. */
493
494 #define SILC_SCHEDULE_RUN_GENERIC_TASKS                                      \
495 do {                                                                         \
496   if (is_run == FALSE) {                                                     \
497     SILC_LOG_DEBUG(("Running generic tasks"));                               \
498     silc_mutex_lock(schedule->lock);                                         \
499     for (i = 0; i <= schedule->fd_list.last_fd; i++)                         \
500       if (schedule->fd_list.fd[i] != -1) {                                   \
501                                                                              \
502         /* Check whether this fd is select()ed. */                           \
503         if ((FD_ISSET(i, &schedule->in)) || (FD_ISSET(i, &schedule->out))) { \
504                                                                              \
505           /* It was selected. Now find the tasks from task queue and execute \
506              all generic tasks. */                                           \
507           if (schedule->generic_queue && schedule->generic_queue->valid) {   \
508             queue = schedule->generic_queue;                                 \
509                                                                              \
510             if (!queue->task)                                                \
511               break;                                                         \
512                                                                              \
513             task = queue->task;                                              \
514                                                                              \
515             while(1) {                                                       \
516               /* Validity of the task is checked always before and after     \
517                  execution beacuse the task might have been unregistered     \
518                  in the callback function, ie. it is not valid anymore. */   \
519                                                                              \
520               if (task->valid && schedule->fd_list.fd[i] != -1) {            \
521                 /* Task ready for reading */                                 \
522                 if ((schedule->fd_list.fd[i] & (1L << SILC_TASK_READ))) {    \
523                   silc_mutex_unlock(schedule->lock);                         \
524                   task->callback(queue, SILC_TASK_READ,                      \
525                                  task->context, i);                          \
526                   silc_mutex_lock(schedule->lock);                           \
527                 }                                                            \
528               }                                                              \
529                                                                              \
530               if (task->valid && schedule->fd_list.fd[i] != -1) {            \
531                 /* Task ready for writing */                                 \
532                 if ((schedule->fd_list.fd[i] & (1L << SILC_TASK_WRITE))) {   \
533                   silc_mutex_unlock(schedule->lock);                         \
534                   task->callback(queue, SILC_TASK_WRITE,                     \
535                                  task->context, i);                          \
536                   silc_mutex_lock(schedule->lock);                           \
537                 }                                                            \
538               }                                                              \
539                                                                              \
540               if (!task->valid) {                                            \
541                 /* Invalid (unregistered) tasks are removed from the         \
542                    task queue. */                                            \
543                 if (queue->task == task->next) {                             \
544                   silc_task_remove(queue, task);                             \
545                   break;                                                     \
546                 }                                                            \
547                                                                              \
548                 task = task->next;                                           \
549                 silc_task_remove(queue, task->prev);                         \
550                 continue;                                                    \
551               }                                                              \
552                                                                              \
553               /* Break if there isn't more tasks in the queue */             \
554               if (queue->task == task->next)                                 \
555                 break;                                                       \
556                                                                              \
557               task = task->next;                                             \
558             }                                                                \
559           }                                                                  \
560         }                                                                    \
561       }                                                                      \
562     silc_mutex_unlock(schedule->lock);                                       \
563   }                                                                          \
564 } while(0)
565
566 bool silc_schedule_one(SilcSchedule schedule, int timeout_usecs)
567 {
568   struct timeval timeout;
569   int is_run, i;
570   SilcTask task;
571   SilcTaskQueue queue;
572   struct timeval curtime;
573   int ret;
574
575   SILC_LOG_DEBUG(("In scheduler loop"));
576
577   /* If the task queues aren't initialized or we aren't valid anymore
578      we will return */
579   if ((!schedule->fd_queue && !schedule->timeout_queue 
580        && !schedule->generic_queue) || schedule->valid == FALSE) {
581     SILC_LOG_DEBUG(("Scheduler not valid anymore, exiting"));
582     return FALSE;
583   }
584
585   /* Clear everything */
586   FD_ZERO(&schedule->in);
587   FD_ZERO(&schedule->out);
588   schedule->max_fd = -1;
589   is_run = FALSE;
590
591   /* Calculate next timeout for select(). This is the timeout value
592      when at earliest some of the timeout tasks expire. */
593   SILC_SCHEDULE_SELECT_TIMEOUT;
594
595   silc_mutex_lock(schedule->lock);
596
597   /* Add the file descriptors to the fd sets. These are the non-timeout
598      tasks. The select() listens to these file descriptors. */
599   SILC_SCHEDULE_SELECT_TASKS;
600
601   if (schedule->max_fd == -1 && !schedule->timeout)
602     return FALSE;
603
604   if (schedule->timeout) {
605     SILC_LOG_DEBUG(("timeout: sec=%d, usec=%d", schedule->timeout->tv_sec,
606                     schedule->timeout->tv_usec));
607   }
608
609   if (timeout_usecs >= 0) {
610     timeout.tv_sec = 0;
611     timeout.tv_usec = timeout_usecs;
612     schedule->timeout = &timeout;
613   }
614
615   silc_mutex_unlock(schedule->lock);
616
617   /* This is the main select(). The program blocks here until some
618      of the selected file descriptors change status or the selected
619      timeout expires. */
620   SILC_LOG_DEBUG(("Select"));
621   ret = silc_select(schedule->max_fd + 1, &schedule->in,
622                     &schedule->out, 0, schedule->timeout);
623
624   switch (ret) {
625   case -1:
626     /* Error */
627     if (errno == EINTR)
628       break;
629     SILC_LOG_ERROR(("Error in select(): %s", strerror(errno)));
630     break;
631   case 0:
632     /* Timeout */
633     SILC_LOG_DEBUG(("Running timeout tasks"));
634     silc_gettimeofday(&curtime);
635     SILC_SCHEDULE_RUN_TIMEOUT_TASKS;
636     break;
637   default:
638     /* There is some data available now */
639     SILC_LOG_DEBUG(("Running non-timeout tasks"));
640     SILC_SCHEDULE_RUN_TASKS;
641
642     SILC_SCHEDULE_RUN_GENERIC_TASKS;
643     break;
644   }
645
646   return TRUE;
647 }
648
649 /* The SILC scheduler. This is actually the main routine in SILC programs.
650    When this returns the program is to be ended. Before this function can
651    be called, one must call silc_schedule_init function. */
652
653 void silc_schedule(SilcSchedule schedule)
654 {
655   SILC_LOG_DEBUG(("Running scheduler"));
656
657   if (schedule->valid == FALSE) {
658     SILC_LOG_ERROR(("Scheduler is not valid, stopping"));
659     return;
660   }
661
662   /* Start the scheduler loop */
663   while (silc_schedule_one(schedule, -1)) 
664     ;
665 }
666
667 /* Wakes up the scheduler. This is used only in multi-threaded
668    environments where threads may add new tasks or remove old tasks
669    from task queues. This is called to wake up the scheduler in the
670    main thread so that it detects the changes in the task queues.
671    If threads support is not compiled in this function has no effect.
672    Implementation of this function is platform specific. */
673
674 void silc_schedule_wakeup(SilcSchedule schedule)
675 {
676 #ifdef SILC_THREADS
677   SILC_LOG_DEBUG(("Wakeup scheduler"));
678   silc_mutex_lock(schedule->lock);
679   silc_schedule_wakeup_internal(schedule->wakeup);
680   silc_mutex_unlock(schedule->lock);
681 #endif
682 }