Symbian compilation fixes.
[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     SilcTaskFd ftask;
599
600     /* Check if fd is already added */
601     if (silc_unlikely(silc_hash_table_find(schedule->fd_queue,
602                                            SILC_32_TO_PTR(fd),
603                                            NULL, (void **)&task)))
604       goto out;
605
606     /* Check max tasks */
607     if (silc_unlikely(schedule->max_tasks > 0 &&
608                       silc_hash_table_count(schedule->fd_queue) >=
609                       schedule->max_tasks)) {
610       SILC_LOG_WARNING(("Scheduler task limit reached: cannot add new task"));
611       goto out;
612     }
613
614     ftask = silc_calloc(1, sizeof(*ftask));
615     if (silc_unlikely(!ftask))
616       goto out;
617
618     SILC_LOG_DEBUG(("New fd task %p fd=%d", ftask, fd));
619
620     ftask->header.type = 0;
621     ftask->header.callback = callback;
622     ftask->header.context = context;
623     ftask->header.valid = TRUE;
624     ftask->events = SILC_TASK_READ;
625     ftask->fd = fd;
626
627     /* Add task */
628     silc_hash_table_add(schedule->fd_queue, SILC_32_TO_PTR(fd), ftask);
629
630     task = (SilcTask)ftask;
631
632   } else if (silc_unlikely(type == SILC_TASK_SIGNAL)) {
633     SILC_SCHEDULE_UNLOCK(schedule);
634     schedule_ops.signal_register(schedule, schedule->internal, fd,
635                                  callback, context);
636     return NULL;
637   }
638
639  out:
640   SILC_SCHEDULE_UNLOCK(schedule);
641   return task;
642 }
643
644 /* Invalidates task */
645
646 void silc_schedule_task_del(SilcSchedule schedule, SilcTask task)
647 {
648   if (silc_unlikely(task == SILC_ALL_TASKS)) {
649     SilcHashTableList htl;
650
651     SILC_LOG_DEBUG(("Unregister all tasks"));
652
653     SILC_SCHEDULE_LOCK(schedule);
654
655     /* Delete from fd queue */
656     silc_hash_table_list(schedule->fd_queue, &htl);
657     while (silc_hash_table_get(&htl, NULL, (void **)&task))
658       task->valid = FALSE;
659     silc_hash_table_list_reset(&htl);
660
661     /* Delete from timeout queue */
662     silc_list_start(schedule->timeout_queue);
663     while ((task = (SilcTask)silc_list_get(schedule->timeout_queue))
664            != SILC_LIST_END)
665       task->valid = FALSE;
666
667     SILC_SCHEDULE_UNLOCK(schedule);
668     return;
669   }
670
671   SILC_LOG_DEBUG(("Unregistering task %p", task));
672   SILC_SCHEDULE_LOCK(schedule);
673   task->valid = FALSE;
674   SILC_SCHEDULE_UNLOCK(schedule);
675 }
676
677 /* Invalidate task by fd */
678
679 void silc_schedule_task_del_by_fd(SilcSchedule schedule, SilcUInt32 fd)
680 {
681   SilcTask task = NULL;
682
683   SILC_LOG_DEBUG(("Unregister task by fd %d", fd));
684
685   SILC_SCHEDULE_LOCK(schedule);
686
687   /* fd is unique, so there is only one task with this fd in the table */
688   if (silc_likely(silc_hash_table_find(schedule->fd_queue,
689                                        SILC_32_TO_PTR(fd), NULL,
690                                        (void **)&task)))
691     task->valid = FALSE;
692
693   SILC_SCHEDULE_UNLOCK(schedule);
694
695   /* If it is signal, remove it */
696   if (silc_unlikely(!task))
697     schedule_ops.signal_unregister(schedule, schedule->internal, fd);
698 }
699
700 /* Invalidate task by task callback. */
701
702 void silc_schedule_task_del_by_callback(SilcSchedule schedule,
703                                         SilcTaskCallback callback)
704 {
705   SilcTask task;
706   SilcHashTableList htl;
707   SilcList list;
708
709   SILC_LOG_DEBUG(("Unregister task by callback"));
710
711   SILC_SCHEDULE_LOCK(schedule);
712
713   /* Delete from fd queue */
714   silc_hash_table_list(schedule->fd_queue, &htl);
715   while (silc_hash_table_get(&htl, NULL, (void **)&task)) {
716     if (task->callback == callback)
717       task->valid = FALSE;
718   }
719   silc_hash_table_list_reset(&htl);
720
721   /* Delete from timeout queue */
722   list = schedule->timeout_queue;
723   silc_list_start(list);
724   while ((task = (SilcTask)silc_list_get(list))) {
725     if (task->callback == callback)
726       task->valid = FALSE;
727   }
728
729   SILC_SCHEDULE_UNLOCK(schedule);
730 }
731
732 /* Invalidate task by context. */
733
734 void silc_schedule_task_del_by_context(SilcSchedule schedule, void *context)
735 {
736   SilcTask task;
737   SilcHashTableList htl;
738   SilcList list;
739
740   SILC_LOG_DEBUG(("Unregister task by context"));
741
742   SILC_SCHEDULE_LOCK(schedule);
743
744   /* Delete from fd queue */
745   silc_hash_table_list(schedule->fd_queue, &htl);
746   while (silc_hash_table_get(&htl, NULL, (void **)&task)) {
747     if (task->context == context)
748       task->valid = FALSE;
749   }
750   silc_hash_table_list_reset(&htl);
751
752   /* Delete from timeout queue */
753   list = schedule->timeout_queue;
754   silc_list_start(list);
755   while ((task = (SilcTask)silc_list_get(list))) {
756     if (task->context == context)
757       task->valid = FALSE;
758   }
759
760   SILC_SCHEDULE_UNLOCK(schedule);
761 }
762
763 /* Invalidate task by all */
764
765 void silc_schedule_task_del_by_all(SilcSchedule schedule, int fd,
766                                    SilcTaskCallback callback, void *context)
767 {
768   SilcTask task;
769   SilcList list;
770
771   SILC_LOG_DEBUG(("Unregister task by fd, callback and context"));
772
773   /* For fd task, callback and context is irrelevant as fd is unique */
774   if (fd)
775     silc_schedule_task_del_by_fd(schedule, fd);
776
777   SILC_SCHEDULE_LOCK(schedule);
778
779   /* Delete from timeout queue */
780   list = schedule->timeout_queue;
781   silc_list_start(list);
782   while ((task = (SilcTask)silc_list_get(list))) {
783     if (task->callback == callback && task->context == context)
784       task->valid = FALSE;
785   }
786
787   SILC_SCHEDULE_UNLOCK(schedule);
788 }
789
790 /* Sets a file descriptor to be listened by scheduler. One can call this
791    directly if wanted. This can be called multiple times for one file
792    descriptor to set different iomasks. */
793
794 void silc_schedule_set_listen_fd(SilcSchedule schedule, SilcUInt32 fd,
795                                  SilcTaskEvent mask, SilcBool send_events)
796 {
797   SilcTaskFd task;
798
799   if (silc_unlikely(!schedule->valid))
800     return;
801
802   SILC_SCHEDULE_LOCK(schedule);
803
804   if (silc_hash_table_find(schedule->fd_queue, SILC_32_TO_PTR(fd),
805                            NULL, (void **)&task)) {
806     task->events = mask;
807     schedule_ops.schedule_fd(schedule, schedule->internal, task, mask);
808     if (silc_unlikely(send_events) && mask) {
809       task->revents = mask;
810       silc_schedule_dispatch_fd(schedule);
811     }
812   }
813
814   SILC_SCHEDULE_UNLOCK(schedule);
815 }
816
817 /* Returns the file descriptors current requested event mask. */
818
819 SilcTaskEvent silc_schedule_get_fd_events(SilcSchedule schedule,
820                                           SilcUInt32 fd)
821 {
822   SilcTaskFd task;
823   SilcTaskEvent event = 0;
824
825   if (silc_unlikely(!schedule->valid))
826     return 0;
827
828   SILC_SCHEDULE_LOCK(schedule);
829   if (silc_hash_table_find(schedule->fd_queue, SILC_32_TO_PTR(fd),
830                            NULL, (void **)&task))
831     event = task->events;
832   SILC_SCHEDULE_UNLOCK(schedule);
833
834   return event;
835 }
836
837 /* Removes a file descriptor from listen list. */
838
839 void silc_schedule_unset_listen_fd(SilcSchedule schedule, SilcUInt32 fd)
840 {
841   silc_schedule_set_listen_fd(schedule, fd, 0, FALSE);
842 }