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