Optimized function calls during scheduler.
[crypto.git] / lib / silcutil / silcschedule.c
1 /*
2
3   silcschedule.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1998 - 2006 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; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19 /* $Id$ */
20
21 #include "silc.h"
22
23 /************************** Types and definitions ***************************/
24
25 /* Platform specific implementation */
26 extern const SilcScheduleOps schedule_ops;
27
28 static void silc_schedule_task_remove(SilcSchedule schedule, SilcTask task);
29 static void silc_schedule_dispatch_fd(SilcSchedule schedule);
30 static void silc_schedule_dispatch_timeout(SilcSchedule schedule,
31                                            SilcBool dispatch_all);
32
33
34 /************************ Static utility functions **************************/
35
36 /* Fd task hash table destructor */
37
38 static void silc_schedule_fd_destructor(void *key, void *context,
39                                         void *user_context)
40 {
41   silc_free(context);
42 }
43
44 /* Executes file descriptor tasks. Invalid tasks are removed here. */
45
46 static void silc_schedule_dispatch_fd(SilcSchedule schedule)
47 {
48   SilcHashTableList htl;
49   SilcTask t;
50   SilcTaskFd task;
51   SilcUInt32 fd;
52
53   silc_hash_table_list(schedule->fd_queue, &htl);
54   while (silc_hash_table_get(&htl, (void **)&fd, (void **)&task)) {
55     t = (SilcTask)task;
56
57     if (!t->valid) {
58       silc_schedule_task_remove(schedule, t);
59       continue;
60     }
61     if (!task->events || !task->revents)
62       continue;
63
64     /* Is the task ready for reading */
65     if (task->revents & SILC_TASK_READ) {
66       SILC_SCHEDULE_UNLOCK(schedule);
67       t->callback(schedule, schedule->app_context, SILC_TASK_READ,
68                   task->fd, t->context);
69       SILC_SCHEDULE_LOCK(schedule);
70     }
71
72     /* Is the task ready for writing */
73     if (t->valid && task->revents & SILC_TASK_WRITE) {
74       SILC_SCHEDULE_UNLOCK(schedule);
75       t->callback(schedule, schedule->app_context, SILC_TASK_WRITE,
76                   task->fd, t->context);
77       SILC_SCHEDULE_LOCK(schedule);
78     }
79
80     /* Remove if task was invalidated in the task callback */
81     if (!t->valid)
82       silc_schedule_task_remove(schedule, t);
83   }
84   silc_hash_table_list_reset(&htl);
85 }
86
87 /* Executes all tasks whose timeout has expired. The task is removed from
88    the task queue after the callback function has returned. Also, invalid
89    tasks are removed here. */
90
91 static void silc_schedule_dispatch_timeout(SilcSchedule schedule,
92                                            SilcBool dispatch_all)
93 {
94   SilcTask t;
95   SilcTaskTimeout task;
96   struct timeval curtime;
97   int count = 0;
98
99   SILC_LOG_DEBUG(("Running timeout tasks"));
100
101   silc_gettimeofday(&curtime);
102
103   /* First task in the task queue has always the earliest timeout. */
104   silc_list_start(schedule->timeout_queue);
105   while ((task = silc_list_get(schedule->timeout_queue)) != SILC_LIST_END) {
106     t = (SilcTask)task;
107
108     /* Remove invalid task */
109     if (!t->valid) {
110       silc_schedule_task_remove(schedule, t);
111       continue;
112     }
113
114     /* Execute the task if the timeout has expired */
115     if (dispatch_all || silc_compare_timeval(&task->timeout, &curtime)) {
116       t->valid = FALSE;
117       SILC_SCHEDULE_UNLOCK(schedule);
118       t->callback(schedule, schedule->app_context, SILC_TASK_EXPIRE, 0,
119                   t->context);
120       SILC_SCHEDULE_LOCK(schedule);
121
122       /* Remove the expired task */
123       silc_schedule_task_remove(schedule, t);
124
125       /* Balance when we have lots of small timeouts */
126       if ((++count) > 40)
127         break;
128     }
129   }
130 }
131
132 /* Calculates next timeout. This is the timeout value when at earliest some
133    of the timeout tasks expire. If this is in the past, they will be
134    dispatched now. */
135
136 static void silc_schedule_select_timeout(SilcSchedule schedule)
137 {
138   SilcTask t;
139   SilcTaskTimeout task;
140   struct timeval curtime;
141   SilcBool dispatch = TRUE;
142
143   /* Get the current time */
144   silc_gettimeofday(&curtime);
145   schedule->has_timeout = FALSE;
146
147   /* First task in the task queue has always the earliest timeout. */
148   silc_list_start(schedule->timeout_queue);
149   while ((task = silc_list_get(schedule->timeout_queue)) != SILC_LIST_END) {
150     t = (SilcTask)task;
151
152     /* Remove invalid task */
153     if (!t->valid) {
154       silc_schedule_task_remove(schedule, t);
155       continue;
156     }
157
158     /* If the timeout is in past, we will run the task and all other
159        timeout tasks from the past. */
160     if (silc_compare_timeval(&task->timeout, &curtime) && dispatch) {
161       silc_schedule_dispatch_timeout(schedule, FALSE);
162       if (!schedule->valid)
163         return;
164
165       /* Start selecting new timeout again after dispatch */
166       silc_list_start(schedule->timeout_queue);
167       dispatch = FALSE;
168       continue;
169     }
170
171     /* Calculate the next timeout */
172     curtime.tv_sec = task->timeout.tv_sec - curtime.tv_sec;
173     curtime.tv_usec = task->timeout.tv_usec - curtime.tv_usec;
174     if (curtime.tv_sec < 0)
175       curtime.tv_sec = 0;
176
177     /* We wouldn't want to go under zero, check for it. */
178     if (curtime.tv_usec < 0) {
179       curtime.tv_sec -= 1;
180       if (curtime.tv_sec < 0)
181         curtime.tv_sec = 0;
182       curtime.tv_usec += 1000000L;
183     }
184
185     break;
186   }
187
188   /* Save the timeout */
189   if (task) {
190     schedule->timeout = curtime;
191     schedule->has_timeout = TRUE;
192     SILC_LOG_DEBUG(("timeout: sec=%d, usec=%d", schedule->timeout.tv_sec,
193                     schedule->timeout.tv_usec));
194   }
195 }
196
197 /* Removes task from the scheduler.  This must be called with scheduler
198    locked. */
199
200 static void silc_schedule_task_remove(SilcSchedule schedule, SilcTask task)
201 {
202   SilcTaskFd ftask;
203   SilcTaskTimeout ttask;
204
205   if (task == SILC_ALL_TASKS) {
206     SilcTask task;
207     SilcHashTableList htl;
208     SilcUInt32 fd;
209
210     /* Delete from fd queue */
211     silc_hash_table_list(schedule->fd_queue, &htl);
212     while (silc_hash_table_get(&htl, (void **)&fd, (void **)&task))
213       silc_hash_table_del(schedule->fd_queue, SILC_32_TO_PTR(fd));
214     silc_hash_table_list_reset(&htl);
215
216     /* Delete from timeout queue */
217     silc_list_start(schedule->timeout_queue);
218     while ((task = silc_list_get(schedule->timeout_queue))) {
219       silc_list_del(schedule->timeout_queue, task);
220       silc_free(task);
221     }
222
223     return;
224   }
225
226   /* Delete from timeout queue */
227   if (task->type == 1) {
228     silc_list_start(schedule->timeout_queue);
229     while ((ttask = silc_list_get(schedule->timeout_queue)) != SILC_LIST_END) {
230       if (ttask == (SilcTaskTimeout)task) {
231         silc_list_del(schedule->timeout_queue, ttask);
232
233         /* Put to free list */
234         silc_list_add(schedule->free_tasks, ttask);
235         if (silc_list_count(schedule->free_tasks) == 1)
236           silc_list_start(schedule->free_tasks);
237         break;
238       }
239     }
240
241     return;
242   }
243
244   /* Delete from fd queue */
245   ftask = (SilcTaskFd)task;
246   silc_hash_table_del(schedule->fd_queue, SILC_32_TO_PTR(ftask->fd));
247 }
248
249 /* Timeout freelist garbage collection */
250
251 SILC_TASK_CALLBACK(silc_schedule_timeout_gc)
252 {
253   SilcTaskTimeout t;
254   int c;
255
256   if (!schedule->valid)
257     return;
258
259   SILC_LOG_DEBUG(("Timeout freelist garbage collection"));
260
261   SILC_SCHEDULE_LOCK(schedule);
262
263   if (silc_list_count(schedule->free_tasks) <= 10) {
264     SILC_SCHEDULE_UNLOCK(schedule);
265     silc_schedule_task_add_timeout(schedule, silc_schedule_timeout_gc,
266                                    schedule, 3600, 0);
267     return;
268   }
269   if (silc_list_count(schedule->timeout_queue) >
270       silc_list_count(schedule->free_tasks)) {
271     SILC_SCHEDULE_UNLOCK(schedule);
272     silc_schedule_task_add_timeout(schedule, silc_schedule_timeout_gc,
273                                    schedule, 3600, 0);
274     return;
275   }
276
277   c = silc_list_count(schedule->free_tasks) / 2;
278   if (c > silc_list_count(schedule->timeout_queue))
279     c = (silc_list_count(schedule->free_tasks) -
280          silc_list_count(schedule->timeout_queue));
281   if (silc_list_count(schedule->free_tasks) - c < 10)
282     c -= (10 - (silc_list_count(schedule->free_tasks) - c));
283
284   SILC_LOG_DEBUG(("Freeing %d unused tasks, leaving %d", c,
285                   silc_list_count(schedule->free_tasks) - c));
286
287   silc_list_start(schedule->free_tasks);
288   while ((t = silc_list_get(schedule->free_tasks)) && c-- > 0) {
289     silc_list_del(schedule->free_tasks, t);
290     silc_free(t);
291   }
292   silc_list_start(schedule->free_tasks);
293
294   SILC_SCHEDULE_UNLOCK(schedule);
295
296   silc_schedule_task_add_timeout(schedule, silc_schedule_timeout_gc,
297                                  schedule, 3600, 0);
298 }
299
300 #ifdef SILC_DIST_INPLACE
301 /* Print schedule statistics to stdout */
302
303 void silc_schedule_stats(SilcSchedule schedule)
304 {
305   SilcTaskFd ftask;
306   fprintf(stdout, "Schedule %p statistics:\n\n", schedule);
307   fprintf(stdout, "Num FD tasks         : %lu (%lu bytes allocated)\n",
308           silc_hash_table_count(schedule->fd_queue),
309           sizeof(*ftask) * silc_hash_table_count(schedule->fd_queue));
310   fprintf(stdout, "Num Timeout tasks    : %d (%d bytes allocated)\n",
311           silc_list_count(schedule->timeout_queue),
312           sizeof(struct SilcTaskTimeoutStruct) *
313           silc_list_count(schedule->timeout_queue));
314   fprintf(stdout, "Num Timeout freelist : %d (%d bytes allocated)\n",
315           silc_list_count(schedule->free_tasks),
316           sizeof(struct SilcTaskTimeoutStruct) *
317           silc_list_count(schedule->free_tasks));
318 }
319 #endif /* SILC_DIST_INPLACE */
320
321 /****************************** Public API **********************************/
322
323 /* Initializes the scheduler. This returns the scheduler context that
324    is given as arugment usually to all silc_schedule_* functions.
325    The `max_tasks' indicates the number of maximum tasks that the
326    scheduler can handle. The `app_context' is application specific
327    context that is delivered to task callbacks. */
328
329 SilcSchedule silc_schedule_init(int max_tasks, void *app_context)
330 {
331   SilcSchedule schedule;
332
333   SILC_LOG_DEBUG(("Initializing scheduler"));
334
335   schedule = silc_calloc(1, sizeof(*schedule));
336   if (!schedule)
337     return NULL;
338
339   schedule->fd_queue =
340     silc_hash_table_alloc(0, silc_hash_uint, NULL, NULL, NULL,
341                           silc_schedule_fd_destructor, NULL, TRUE);
342   if (!schedule->fd_queue)
343     return NULL;
344
345   silc_list_init(schedule->timeout_queue, struct SilcTaskTimeoutStruct, next);
346   silc_list_init(schedule->free_tasks, struct SilcTaskTimeoutStruct, next);
347
348   schedule->app_context = app_context;
349   schedule->valid = TRUE;
350   schedule->max_tasks = max_tasks;
351
352   /* Allocate scheduler lock */
353   silc_mutex_alloc(&schedule->lock);
354
355   /* Initialize the platform specific scheduler. */
356   schedule->internal = schedule_ops.init(schedule, app_context);
357
358   /* Timeout freelist garbage collection */
359   silc_schedule_task_add_timeout(schedule, silc_schedule_timeout_gc,
360                                  schedule, 3600, 0);
361
362   return schedule;
363 }
364
365 /* Uninitializes the schedule. This is called when the program is ready
366    to end. This removes all tasks and task queues. Returns FALSE if the
367    scheduler could not be uninitialized. This happens when the scheduler
368    is still valid and silc_schedule_stop has not been called. */
369
370 SilcBool silc_schedule_uninit(SilcSchedule schedule)
371 {
372   SilcTask task;
373
374   SILC_LOG_DEBUG(("Uninitializing scheduler"));
375
376   if (schedule->valid == TRUE)
377     return FALSE;
378
379   /* Dispatch all timeouts before going away */
380   SILC_SCHEDULE_LOCK(schedule);
381   silc_schedule_dispatch_timeout(schedule, TRUE);
382   SILC_SCHEDULE_UNLOCK(schedule);
383
384   /* Deliver signals before going away */
385   if (schedule->signal_tasks) {
386     schedule_ops.signals_call(schedule, schedule->internal);
387     schedule->signal_tasks = FALSE;
388   }
389
390   /* Unregister all tasks */
391   silc_schedule_task_del(schedule, SILC_ALL_TASKS);
392   silc_schedule_task_remove(schedule, SILC_ALL_TASKS);
393
394   /* Delete timeout task freelist */
395   silc_list_start(schedule->free_tasks);
396   while ((task = silc_list_get(schedule->free_tasks)))
397     silc_free(task);
398
399   /* Unregister all task queues */
400   silc_hash_table_free(schedule->fd_queue);
401
402   /* Uninit the platform specific scheduler. */
403   schedule_ops.uninit(schedule, schedule->internal);
404
405   silc_mutex_free(schedule->lock);
406   silc_free(schedule);
407
408   return TRUE;
409 }
410
411 /* Stops the schedule even if it is not supposed to be stopped yet.
412    After calling this, one should call silc_schedule_uninit (after the
413    silc_schedule has returned). */
414
415 void silc_schedule_stop(SilcSchedule schedule)
416 {
417   SILC_LOG_DEBUG(("Stopping scheduler"));
418   SILC_SCHEDULE_LOCK(schedule);
419   schedule->valid = FALSE;
420   SILC_SCHEDULE_UNLOCK(schedule);
421 }
422
423 /* Runs the scheduler once and then returns. */
424
425 SilcBool silc_schedule_one(SilcSchedule schedule, int timeout_usecs)
426 {
427   struct timeval timeout;
428   int ret;
429
430   if (!schedule->is_locked)
431     SILC_SCHEDULE_LOCK(schedule);
432
433   while (1) {
434     SILC_LOG_DEBUG(("In scheduler loop"));
435
436     /* Deliver signals if any has been set to be called */
437     if (schedule->signal_tasks) {
438       SILC_SCHEDULE_UNLOCK(schedule);
439       schedule_ops.signals_call(schedule, schedule->internal);
440       schedule->signal_tasks = FALSE;
441       SILC_SCHEDULE_LOCK(schedule);
442     }
443
444     /* Check if scheduler is valid */
445     if (schedule->valid == FALSE) {
446       SILC_LOG_DEBUG(("Scheduler not valid anymore, exiting"));
447       if (!schedule->is_locked)
448         SILC_SCHEDULE_UNLOCK(schedule);
449       return FALSE;
450     }
451
452     /* Calculate next timeout for silc_select().  This is the timeout value
453        when at earliest some of the timeout tasks expire.  This may dispatch
454        already expired timeouts. */
455     silc_schedule_select_timeout(schedule);
456
457     /* Check if scheduler is valid */
458     if (schedule->valid == FALSE) {
459       SILC_LOG_DEBUG(("Scheduler not valid anymore, exiting"));
460       if (!schedule->is_locked)
461         SILC_SCHEDULE_UNLOCK(schedule);
462       return FALSE;
463     }
464
465     if (timeout_usecs >= 0) {
466       timeout.tv_sec = 0;
467       timeout.tv_usec = timeout_usecs;
468       schedule->timeout = timeout;
469       schedule->has_timeout = TRUE;
470     }
471
472     /* This is the main silc_select(). The program blocks here until some
473        of the selected file descriptors change status or the selected
474        timeout expires. */
475     SILC_LOG_DEBUG(("Select"));
476     ret = schedule_ops.select(schedule, schedule->internal);
477
478     switch (ret) {
479     case 0:
480       /* Timeout */
481       SILC_LOG_DEBUG(("Running timeout tasks"));
482       if (silc_list_count(schedule->timeout_queue))
483         silc_schedule_dispatch_timeout(schedule, FALSE);
484       break;
485     case -1:
486       /* Error */
487       if (errno == EINTR)
488         break;
489       SILC_LOG_ERROR(("Error in select(): %s", strerror(errno)));
490       break;
491     default:
492       /* There is some data available now */
493       SILC_LOG_DEBUG(("Running fd tasks"));
494       silc_schedule_dispatch_fd(schedule);
495       break;
496     }
497
498     if (timeout_usecs >= 0)
499       break;
500   }
501
502   if (!schedule->is_locked)
503     SILC_SCHEDULE_UNLOCK(schedule);
504
505   return TRUE;
506 }
507
508 /* The SILC scheduler. This is actually the main routine in SILC programs.
509    When this returns the program is to be ended. Before this function can
510    be called, one must call silc_schedule_init function. */
511
512 void silc_schedule(SilcSchedule schedule)
513 {
514   SILC_LOG_DEBUG(("Running scheduler"));
515
516   if (schedule->valid == FALSE) {
517     SILC_LOG_ERROR(("Scheduler is not valid, stopping"));
518     return;
519   }
520
521   /* Start the scheduler loop */
522   SILC_SCHEDULE_LOCK(schedule);
523   schedule->is_locked = TRUE;
524   silc_schedule_one(schedule, -1);
525   SILC_SCHEDULE_UNLOCK(schedule);
526 }
527
528 /* Wakes up the scheduler. This is used only in multi-threaded
529    environments where threads may add new tasks or remove old tasks
530    from task queues. This is called to wake up the scheduler in the
531    main thread so that it detects the changes in the task queues.
532    If threads support is not compiled in this function has no effect.
533    Implementation of this function is platform specific. */
534
535 void silc_schedule_wakeup(SilcSchedule schedule)
536 {
537 #ifdef SILC_THREADS
538   SILC_LOG_DEBUG(("Wakeup scheduler"));
539   SILC_SCHEDULE_LOCK(schedule);
540   schedule_ops.wakeup(schedule, schedule->internal);
541   SILC_SCHEDULE_UNLOCK(schedule);
542 #endif
543 }
544
545 /* Returns the application specific context that was saved into the
546    scheduler in silc_schedule_init function.  The context is also
547    returned to application in task callback functions, but this function
548    may be used to get it as well if needed. */
549
550 void *silc_schedule_get_context(SilcSchedule schedule)
551 {
552   return schedule->app_context;
553 }
554
555 /* Add new task to the scheduler */
556
557 SilcTask silc_schedule_task_add(SilcSchedule schedule, SilcUInt32 fd,
558                                 SilcTaskCallback callback, void *context,
559                                 long seconds, long useconds,
560                                 SilcTaskType type)
561 {
562   SilcTask task = NULL;
563
564   if (!schedule->valid)
565     return NULL;
566
567   SILC_SCHEDULE_LOCK(schedule);
568
569   if (type == SILC_TASK_TIMEOUT) {
570     SilcTaskTimeout tmp, prev, ttask;
571
572     ttask = silc_list_get(schedule->free_tasks);
573     if (!ttask) {
574       ttask = silc_calloc(1, sizeof(*ttask));
575       if (!ttask)
576         goto out;
577     }
578     silc_list_del(schedule->free_tasks, ttask);
579
580     ttask->header.type = 1;
581     ttask->header.callback = callback;
582     ttask->header.context = context;
583     ttask->header.valid = TRUE;
584
585     /* Add timeout */
586     silc_gettimeofday(&ttask->timeout);
587     if ((seconds + useconds) > 0) {
588       ttask->timeout.tv_sec += seconds + (useconds / 1000000L);
589       ttask->timeout.tv_usec += (useconds % 1000000L);
590       if (ttask->timeout.tv_usec >= 1000000L) {
591         ttask->timeout.tv_sec += 1;
592         ttask->timeout.tv_usec -= 1000000L;
593       }
594     }
595
596     SILC_LOG_DEBUG(("New timeout task %p: sec=%d, usec=%d", ttask,
597                     seconds, useconds));
598
599     /* Add task to correct spot so that the first task in the list has
600        the earliest timeout. */
601     silc_list_start(schedule->timeout_queue);
602     prev = NULL;
603     while ((tmp = silc_list_get(schedule->timeout_queue)) != SILC_LIST_END) {
604       /* If we have shorter timeout, we have found our spot */
605       if (silc_compare_timeval(&ttask->timeout, &tmp->timeout)) {
606         silc_list_insert(schedule->timeout_queue, prev, ttask);
607         break;
608       }
609       prev = tmp;
610     }
611     if (!tmp)
612       silc_list_add(schedule->timeout_queue, ttask);
613
614     task = (SilcTask)ttask;
615
616   } else if (type == SILC_TASK_FD) {
617     /* Check if fd is already added */
618     if (silc_hash_table_find(schedule->fd_queue, SILC_32_TO_PTR(fd),
619                              NULL, (void **)&task))
620       goto out;
621
622     /* Check max tasks */
623     if (schedule->max_tasks > 0 &&
624         silc_hash_table_count(schedule->fd_queue) >= schedule->max_tasks) {
625       SILC_LOG_WARNING(("Scheduler task limit reached: cannot add new task"));
626       goto out;
627     }
628
629     SilcTaskFd ftask = silc_calloc(1, sizeof(*ftask));
630     if (!ftask)
631       goto out;
632
633     SILC_LOG_DEBUG(("New fd task %p fd=%d", ftask, fd));
634
635     ftask->header.type = 0;
636     ftask->header.callback = callback;
637     ftask->header.context = context;
638     ftask->header.valid = TRUE;
639     ftask->events = SILC_TASK_READ;
640     ftask->fd = fd;
641
642     /* Add task */
643     silc_hash_table_add(schedule->fd_queue, SILC_32_TO_PTR(fd), ftask);
644
645     task = (SilcTask)ftask;
646
647   } else if (type == SILC_TASK_SIGNAL) {
648     SILC_SCHEDULE_UNLOCK(schedule);
649     schedule_ops.signal_register(schedule, schedule->internal, (int)fd,
650                                  callback, context);
651     return NULL;
652   }
653
654
655  out:
656   SILC_SCHEDULE_UNLOCK(schedule);
657   return task;
658 }
659
660 /* Invalidates task */
661
662 void silc_schedule_task_del(SilcSchedule schedule, SilcTask task)
663 {
664   if (task == SILC_ALL_TASKS) {
665     SilcHashTableList htl;
666
667     SILC_LOG_DEBUG(("Unregister all tasks"));
668
669     SILC_SCHEDULE_LOCK(schedule);
670
671     /* Delete from fd queue */
672     silc_hash_table_list(schedule->fd_queue, &htl);
673     while (silc_hash_table_get(&htl, NULL, (void **)&task))
674       task->valid = FALSE;
675     silc_hash_table_list_reset(&htl);
676
677     /* Delete from timeout queue */
678     silc_list_start(schedule->timeout_queue);
679     while ((task = (SilcTask)silc_list_get(schedule->timeout_queue))
680            != SILC_LIST_END)
681       task->valid = FALSE;
682
683     SILC_SCHEDULE_UNLOCK(schedule);
684     return;
685   }
686
687   SILC_LOG_DEBUG(("Unregistering task %p", task));
688   SILC_SCHEDULE_LOCK(schedule);
689   task->valid = FALSE;
690   SILC_SCHEDULE_UNLOCK(schedule);
691 }
692
693 /* Invalidate task by fd */
694
695 void silc_schedule_task_del_by_fd(SilcSchedule schedule, SilcUInt32 fd)
696 {
697   SilcTask task = NULL;
698
699   SILC_LOG_DEBUG(("Unregister task by fd %d", fd));
700
701   SILC_SCHEDULE_LOCK(schedule);
702
703   /* fd is unique, so there is only one task with this fd in the table */
704   if (silc_hash_table_find(schedule->fd_queue, SILC_32_TO_PTR(fd), NULL,
705                            (void **)&task))
706     task->valid = FALSE;
707
708   SILC_SCHEDULE_UNLOCK(schedule);
709
710   /* If it is signal, remove it */
711   if (!task)
712     schedule_ops.signal_unregister(schedule, schedule->internal, fd);
713 }
714
715 /* Invalidate task by task callback. */
716
717 void silc_schedule_task_del_by_callback(SilcSchedule schedule,
718                                         SilcTaskCallback callback)
719 {
720   SilcTask task;
721   SilcHashTableList htl;
722
723   SILC_LOG_DEBUG(("Unregister task by callback"));
724
725   SILC_SCHEDULE_LOCK(schedule);
726
727   /* Delete from fd queue */
728   silc_hash_table_list(schedule->fd_queue, &htl);
729   while (silc_hash_table_get(&htl, NULL, (void **)&task)) {
730     if (task->callback == callback)
731       task->valid = FALSE;
732   }
733   silc_hash_table_list_reset(&htl);
734
735   /* Delete from timeout queue */
736   silc_list_start(schedule->timeout_queue);
737   while ((task = (SilcTask)silc_list_get(schedule->timeout_queue))
738          != SILC_LIST_END) {
739     if (task->callback == callback)
740       task->valid = FALSE;
741   }
742
743   SILC_SCHEDULE_UNLOCK(schedule);
744 }
745
746 /* Invalidate task by context. */
747
748 void silc_schedule_task_del_by_context(SilcSchedule schedule, void *context)
749 {
750   SilcTask task;
751   SilcHashTableList htl;
752
753   SILC_LOG_DEBUG(("Unregister task by context"));
754
755   SILC_SCHEDULE_LOCK(schedule);
756
757   /* Delete from fd queue */
758   silc_hash_table_list(schedule->fd_queue, &htl);
759   while (silc_hash_table_get(&htl, NULL, (void **)&task)) {
760     if (task->context == context)
761       task->valid = FALSE;
762   }
763   silc_hash_table_list_reset(&htl);
764
765   /* Delete from timeout queue */
766   silc_list_start(schedule->timeout_queue);
767   while ((task = (SilcTask)silc_list_get(schedule->timeout_queue))
768          != SILC_LIST_END) {
769     if (task->context == context)
770       task->valid = FALSE;
771   }
772
773   SILC_SCHEDULE_UNLOCK(schedule);
774 }
775
776 /* Invalidate task by all */
777
778 void silc_schedule_task_del_by_all(SilcSchedule schedule, int fd,
779                                    SilcTaskCallback callback, void *context)
780 {
781   SilcTask task;
782
783   SILC_LOG_DEBUG(("Unregister task by fd, callback and context"));
784
785   /* For fd task, callback and context is irrelevant as fd is unique */
786   if (fd)
787     silc_schedule_task_del_by_fd(schedule, fd);
788
789   SILC_SCHEDULE_LOCK(schedule);
790
791   /* Delete from timeout queue */
792   silc_list_start(schedule->timeout_queue);
793   while ((task = (SilcTask)silc_list_get(schedule->timeout_queue))
794          != SILC_LIST_END) {
795     if (task->callback == callback && task->context == context)
796       task->valid = FALSE;
797   }
798
799   SILC_SCHEDULE_UNLOCK(schedule);
800 }
801
802 /* Sets a file descriptor to be listened by scheduler. One can call this
803    directly if wanted. This can be called multiple times for one file
804    descriptor to set different iomasks. */
805
806 void silc_schedule_set_listen_fd(SilcSchedule schedule, SilcUInt32 fd,
807                                  SilcTaskEvent mask, SilcBool send_events)
808 {
809   SilcTaskFd task;
810
811   if (!schedule->valid)
812     return;
813
814   SILC_SCHEDULE_LOCK(schedule);
815
816   if (silc_hash_table_find(schedule->fd_queue, SILC_32_TO_PTR(fd),
817                            NULL, (void **)&task)) {
818     task->events = mask;
819     if (send_events) {
820       task->revents = mask;
821       silc_schedule_dispatch_fd(schedule);
822     }
823   }
824
825   SILC_SCHEDULE_UNLOCK(schedule);
826 }
827
828 /* Removes a file descriptor from listen list. */
829
830 void silc_schedule_unset_listen_fd(SilcSchedule schedule, SilcUInt32 fd)
831 {
832   silc_schedule_set_listen_fd(schedule, fd, 0, FALSE);
833 }