Static analyzer bug fixes
[silc.git] / lib / silcutil / silcschedule.c
1 /*
2
3   silcschedule.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1998 - 2007 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) > 0 && !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) <= 0 && 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=%ld, usec=%ld", 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     void *fd;
205
206     /* Delete from fd queue */
207     silc_hash_table_list(schedule->fd_queue, &htl);
208     while (silc_hash_table_get(&htl, &fd, (void *)&task))
209       silc_hash_table_del(schedule->fd_queue, 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         : %u (%u bytes allocated)\n",
294           silc_hash_table_count(schedule->fd_queue),
295           (unsigned int)sizeof(*ftask) *
296           silc_hash_table_count(schedule->fd_queue));
297   fprintf(stdout, "Num Timeout tasks    : %d (%d bytes allocated)\n",
298           silc_list_count(schedule->timeout_queue),
299           (unsigned int)sizeof(struct SilcTaskTimeoutStruct) *
300           silc_list_count(schedule->timeout_queue));
301   fprintf(stdout, "Num Timeout freelist : %d (%d bytes allocated)\n",
302           silc_list_count(schedule->free_tasks),
303           (unsigned int)sizeof(struct SilcTaskTimeoutStruct) *
304           silc_list_count(schedule->free_tasks));
305 }
306 #endif /* SILC_DIST_INPLACE */
307
308 /****************************** Public API **********************************/
309
310 /* Initializes the scheduler. This returns the scheduler context that
311    is given as arugment usually to all silc_schedule_* functions.
312    The `max_tasks' indicates the number of maximum tasks that the
313    scheduler can handle. The `app_context' is application specific
314    context that is delivered to task callbacks. */
315
316 SilcSchedule silc_schedule_init(int max_tasks, void *app_context)
317 {
318   SilcSchedule schedule;
319
320   SILC_LOG_DEBUG(("Initializing scheduler"));
321
322   schedule = silc_calloc(1, sizeof(*schedule));
323   if (!schedule)
324     return NULL;
325
326   schedule->fd_queue =
327     silc_hash_table_alloc(0, silc_hash_uint, NULL, NULL, NULL,
328                           silc_schedule_fd_destructor, NULL, TRUE);
329   if (!schedule->fd_queue) {
330     silc_free(schedule);
331     return NULL;
332   }
333
334   silc_list_init(schedule->timeout_queue, struct SilcTaskStruct, next);
335   silc_list_init(schedule->free_tasks, struct SilcTaskStruct, next);
336
337   schedule->app_context = app_context;
338   schedule->valid = TRUE;
339   schedule->max_tasks = max_tasks;
340
341   /* Allocate scheduler lock */
342   silc_mutex_alloc(&schedule->lock);
343
344   /* Initialize the platform specific scheduler. */
345   schedule->internal = schedule_ops.init(schedule, app_context);
346   if (!schedule->internal) {
347     silc_hash_table_free(schedule->fd_queue);
348     silc_mutex_free(schedule->lock);
349     silc_free(schedule);
350     return NULL;
351   }
352
353   /* Timeout freelist garbage collection */
354   silc_schedule_task_add_timeout(schedule, silc_schedule_timeout_gc,
355                                  schedule, 3600, 0);
356
357   return schedule;
358 }
359
360 /* Uninitializes the schedule. This is called when the program is ready
361    to end. This removes all tasks and task queues. Returns FALSE if the
362    scheduler could not be uninitialized. This happens when the scheduler
363    is still valid and silc_schedule_stop has not been called. */
364
365 SilcBool silc_schedule_uninit(SilcSchedule schedule)
366 {
367   SilcTask task;
368
369   SILC_LOG_DEBUG(("Uninitializing scheduler"));
370
371   if (schedule->valid == TRUE)
372     return FALSE;
373
374   /* Dispatch all timeouts before going away */
375   SILC_SCHEDULE_LOCK(schedule);
376   silc_schedule_dispatch_timeout(schedule, TRUE);
377   SILC_SCHEDULE_UNLOCK(schedule);
378
379   /* Deliver signals before going away */
380   if (schedule->signal_tasks) {
381     schedule_ops.signals_call(schedule, schedule->internal);
382     schedule->signal_tasks = FALSE;
383   }
384
385   /* Unregister all tasks */
386   silc_schedule_task_del(schedule, SILC_ALL_TASKS);
387   silc_schedule_task_remove(schedule, SILC_ALL_TASKS);
388
389   /* Delete timeout task freelist */
390   silc_list_start(schedule->free_tasks);
391   while ((task = silc_list_get(schedule->free_tasks)))
392     silc_free(task);
393
394   /* Unregister all task queues */
395   silc_hash_table_free(schedule->fd_queue);
396
397   /* Uninit the platform specific scheduler. */
398   schedule_ops.uninit(schedule, schedule->internal);
399
400   silc_mutex_free(schedule->lock);
401   silc_free(schedule);
402
403   return TRUE;
404 }
405
406 /* Stops the schedule even if it is not supposed to be stopped yet.
407    After calling this, one should call silc_schedule_uninit (after the
408    silc_schedule has returned). */
409
410 void silc_schedule_stop(SilcSchedule schedule)
411 {
412   SILC_LOG_DEBUG(("Stopping scheduler"));
413   SILC_SCHEDULE_LOCK(schedule);
414   schedule->valid = FALSE;
415   SILC_SCHEDULE_UNLOCK(schedule);
416 }
417
418 /* Runs the scheduler once and then returns.   Must be called locked. */
419
420 static SilcBool silc_schedule_iterate(SilcSchedule schedule, int timeout_usecs)
421 {
422   struct timeval timeout;
423   int ret;
424
425   do {
426     SILC_LOG_DEBUG(("In scheduler loop"));
427
428     /* Deliver signals if any has been set to be called */
429     if (silc_unlikely(schedule->signal_tasks)) {
430       SILC_SCHEDULE_UNLOCK(schedule);
431       schedule_ops.signals_call(schedule, schedule->internal);
432       schedule->signal_tasks = FALSE;
433       SILC_SCHEDULE_LOCK(schedule);
434     }
435
436     /* Check if scheduler is valid */
437     if (silc_unlikely(schedule->valid == FALSE)) {
438       SILC_LOG_DEBUG(("Scheduler not valid anymore, exiting"));
439       return FALSE;
440     }
441
442     /* Calculate next timeout for silc_select().  This is the timeout value
443        when at earliest some of the timeout tasks expire.  This may dispatch
444        already expired timeouts. */
445     silc_schedule_select_timeout(schedule);
446
447     /* Check if scheduler is valid */
448     if (silc_unlikely(schedule->valid == FALSE)) {
449       SILC_LOG_DEBUG(("Scheduler not valid anymore, exiting"));
450       return FALSE;
451     }
452
453     if (timeout_usecs >= 0) {
454       timeout.tv_sec = 0;
455       timeout.tv_usec = timeout_usecs;
456       schedule->timeout = timeout;
457       schedule->has_timeout = TRUE;
458     }
459
460     /* This is the main silc_select(). The program blocks here until some
461        of the selected file descriptors change status or the selected
462        timeout expires. */
463     SILC_LOG_DEBUG(("Select"));
464     ret = schedule_ops.schedule(schedule, schedule->internal);
465
466     if (silc_likely(ret == 0)) {
467       /* Timeout */
468       SILC_LOG_DEBUG(("Running timeout tasks"));
469       if (silc_likely(silc_list_count(schedule->timeout_queue)))
470         silc_schedule_dispatch_timeout(schedule, FALSE);
471       continue;
472
473     } else if (silc_likely(ret > 0)) {
474       /* There is some data available now */
475       SILC_LOG_DEBUG(("Running fd tasks"));
476       silc_schedule_dispatch_fd(schedule);
477
478       /* If timeout was very short, dispatch also timeout tasks */
479       if (schedule->has_timeout && schedule->timeout.tv_sec == 0 &&
480           schedule->timeout.tv_usec < 50000)
481         silc_schedule_dispatch_timeout(schedule, FALSE);
482       continue;
483
484     } else {
485       /* Error or special case handling */
486       if (errno == EINTR)
487         continue;
488       if (ret == -2)
489         break;
490
491       SILC_LOG_ERROR(("Error in select()/poll(): %s", strerror(errno)));
492       continue;
493     }
494   } while (timeout_usecs == -1);
495
496   return TRUE;
497 }
498
499 /* Runs the scheduler once and then returns. */
500
501 SilcBool silc_schedule_one(SilcSchedule schedule, int timeout_usecs)
502 {
503   SilcBool ret;
504   SILC_SCHEDULE_LOCK(schedule);
505   ret = silc_schedule_iterate(schedule, timeout_usecs);
506   SILC_SCHEDULE_UNLOCK(schedule);
507   return ret;
508 }
509
510 /* Runs the scheduler and blocks here.  When this returns the scheduler
511    has ended. */
512
513 #ifndef SILC_SYMBIAN
514 void silc_schedule(SilcSchedule schedule)
515 {
516   SILC_LOG_DEBUG(("Running scheduler"));
517
518   /* Start the scheduler loop */
519   SILC_SCHEDULE_LOCK(schedule);
520   silc_schedule_iterate(schedule, -1);
521   SILC_SCHEDULE_UNLOCK(schedule);
522 }
523 #endif /* !SILC_SYMBIAN */
524
525 /* Wakes up the scheduler. This is used only in multi-threaded
526    environments where threads may add new tasks or remove old tasks
527    from task queues. This is called to wake up the scheduler in the
528    main thread so that it detects the changes in the task queues.
529    If threads support is not compiled in this function has no effect.
530    Implementation of this function is platform specific. */
531
532 void silc_schedule_wakeup(SilcSchedule schedule)
533 {
534 #ifdef SILC_THREADS
535   SILC_LOG_DEBUG(("Wakeup scheduler"));
536   SILC_SCHEDULE_LOCK(schedule);
537   schedule_ops.wakeup(schedule, schedule->internal);
538   SILC_SCHEDULE_UNLOCK(schedule);
539 #endif
540 }
541
542 /* Returns the application specific context that was saved into the
543    scheduler in silc_schedule_init function.  The context is also
544    returned to application in task callback functions, but this function
545    may be used to get it as well if needed. */
546
547 void *silc_schedule_get_context(SilcSchedule schedule)
548 {
549   return schedule->app_context;
550 }
551
552 /* Set notify callback */
553
554 void silc_schedule_set_notify(SilcSchedule schedule,
555                               SilcTaskNotifyCb notify, void *context)
556 {
557   schedule->notify = notify;
558   schedule->notify_context = context;
559 }
560
561 /* Add new task to the scheduler */
562
563 SilcTask silc_schedule_task_add(SilcSchedule schedule, SilcUInt32 fd,
564                                 SilcTaskCallback callback, void *context,
565                                 long seconds, long useconds,
566                                 SilcTaskType type)
567 {
568   SilcTask task = NULL;
569
570   if (silc_unlikely(!schedule->valid))
571     return NULL;
572
573   SILC_SCHEDULE_LOCK(schedule);
574
575   if (silc_likely(type == SILC_TASK_TIMEOUT)) {
576     SilcTaskTimeout tmp, prev, ttask;
577     SilcList list;
578
579     silc_list_start(schedule->free_tasks);
580     ttask = silc_list_get(schedule->free_tasks);
581     if (silc_unlikely(!ttask)) {
582       ttask = silc_calloc(1, sizeof(*ttask));
583       if (silc_unlikely(!ttask))
584         goto out;
585     } else
586       silc_list_del(schedule->free_tasks, ttask);
587
588     ttask->header.type = 1;
589     ttask->header.callback = callback;
590     ttask->header.context = context;
591     ttask->header.valid = TRUE;
592
593     /* Add timeout */
594     silc_gettimeofday(&ttask->timeout);
595     if ((seconds + useconds) > 0) {
596       ttask->timeout.tv_sec += seconds + (useconds / 1000000L);
597       ttask->timeout.tv_usec += (useconds % 1000000L);
598       if (ttask->timeout.tv_usec >= 1000000L) {
599         ttask->timeout.tv_sec += 1;
600         ttask->timeout.tv_usec -= 1000000L;
601       }
602     }
603
604     SILC_LOG_DEBUG(("New timeout task %p: sec=%ld, usec=%ld", ttask,
605                     seconds, useconds));
606
607     /* Add task to correct spot so that the first task in the list has
608        the earliest timeout. */
609     list = schedule->timeout_queue;
610     silc_list_start(list);
611     prev = NULL;
612     while ((tmp = silc_list_get(list)) != SILC_LIST_END) {
613       /* If we have shorter timeout, we have found our spot */
614       if (silc_compare_timeval(&ttask->timeout, &tmp->timeout) < 0) {
615         silc_list_insert(schedule->timeout_queue, prev, ttask);
616         break;
617       }
618       prev = tmp;
619     }
620     if (!tmp)
621       silc_list_add(schedule->timeout_queue, ttask);
622
623     task = (SilcTask)ttask;
624
625     /* Call notify callback */
626     if (schedule->notify)
627       schedule->notify(schedule, TRUE, task, FALSE, 0, 0, seconds, useconds,
628                        schedule->notify_context);
629
630   } else if (silc_likely(type == SILC_TASK_FD)) {
631     SilcTaskFd ftask;
632
633     /* Check if fd is already added */
634     if (silc_unlikely(silc_hash_table_find(schedule->fd_queue,
635                                            SILC_32_TO_PTR(fd),
636                                            NULL, (void *)&task))) {
637       if (task->valid)
638         goto out;
639
640       /* Remove invalid task.  We must have unique fd key to hash table. */
641       silc_schedule_task_remove(schedule, task);
642     }
643
644     /* Check max tasks */
645     if (silc_unlikely(schedule->max_tasks > 0 &&
646                       silc_hash_table_count(schedule->fd_queue) >=
647                       schedule->max_tasks)) {
648       SILC_LOG_WARNING(("Scheduler task limit reached: cannot add new task"));
649       task = NULL;
650       goto out;
651     }
652
653     ftask = silc_calloc(1, sizeof(*ftask));
654     if (silc_unlikely(!ftask)) {
655       task = NULL;
656       goto out;
657     }
658
659     SILC_LOG_DEBUG(("New fd task %p fd=%d", ftask, fd));
660
661     ftask->header.type = 0;
662     ftask->header.callback = callback;
663     ftask->header.context = context;
664     ftask->header.valid = TRUE;
665     ftask->events = SILC_TASK_READ;
666     ftask->fd = fd;
667
668     /* Add task and schedule it */
669     if (!silc_hash_table_add(schedule->fd_queue, SILC_32_TO_PTR(fd), ftask)) {
670       silc_free(ftask);
671       task = NULL;
672       goto out;
673     }
674     if (!schedule_ops.schedule_fd(schedule, schedule->internal,
675                                   ftask, ftask->events)) {
676       silc_hash_table_del(schedule->fd_queue, SILC_32_TO_PTR(fd));
677       task = NULL;
678       goto out;
679     }
680
681     task = (SilcTask)ftask;
682
683     /* Call notify callback */
684     if (schedule->notify)
685       schedule->notify(schedule, TRUE, task, TRUE, ftask->fd,
686                        SILC_TASK_READ, 0, 0, schedule->notify_context);
687
688   } else if (silc_unlikely(type == SILC_TASK_SIGNAL)) {
689     SILC_SCHEDULE_UNLOCK(schedule);
690     schedule_ops.signal_register(schedule, schedule->internal, fd,
691                                  callback, context);
692     return NULL;
693   }
694
695  out:
696   SILC_SCHEDULE_UNLOCK(schedule);
697
698 #ifdef SILC_SYMBIAN
699   /* On symbian we wakeup scheduler immediately after adding timeout task
700      in case the task is added outside the scheduler loop (in some active
701      object). */
702   if (task && task->type == 1)
703     silc_schedule_wakeup(schedule);
704 #endif /* SILC_SYMBIAN */
705
706   return task;
707 }
708
709 /* Invalidates task */
710
711 SilcBool silc_schedule_task_del(SilcSchedule schedule, SilcTask task)
712 {
713   if (silc_unlikely(task == SILC_ALL_TASKS)) {
714     SilcHashTableList htl;
715
716     SILC_LOG_DEBUG(("Unregister all tasks"));
717
718     SILC_SCHEDULE_LOCK(schedule);
719
720     /* Delete from fd queue */
721     silc_hash_table_list(schedule->fd_queue, &htl);
722     while (silc_hash_table_get(&htl, NULL, (void *)&task)) {
723       task->valid = FALSE;
724
725       /* Call notify callback */
726       if (schedule->notify)
727         schedule->notify(schedule, FALSE, task, TRUE,
728                          ((SilcTaskFd)task)->fd, 0, 0, 0,
729                          schedule->notify_context);
730     }
731     silc_hash_table_list_reset(&htl);
732
733     /* Delete from timeout queue */
734     silc_list_start(schedule->timeout_queue);
735     while ((task = (SilcTask)silc_list_get(schedule->timeout_queue))) {
736       task->valid = FALSE;
737
738       /* Call notify callback */
739       if (schedule->notify)
740         schedule->notify(schedule, FALSE, task, FALSE, 0, 0, 0, 0,
741                          schedule->notify_context);
742     }
743
744     SILC_SCHEDULE_UNLOCK(schedule);
745     return TRUE;
746   }
747
748   SILC_LOG_DEBUG(("Unregistering task %p", task));
749   SILC_SCHEDULE_LOCK(schedule);
750   task->valid = FALSE;
751
752   /* Call notify callback */
753   if (schedule->notify)
754     schedule->notify(schedule, FALSE, task, !task->type, 0, 0, 0, 0,
755                      schedule->notify_context);
756   SILC_SCHEDULE_UNLOCK(schedule);
757
758   return TRUE;
759 }
760
761 /* Invalidate task by fd */
762
763 SilcBool silc_schedule_task_del_by_fd(SilcSchedule schedule, SilcUInt32 fd)
764 {
765   SilcTask task = NULL;
766   SilcBool ret = FALSE;
767
768   SILC_LOG_DEBUG(("Unregister task by fd %d", fd));
769
770   SILC_SCHEDULE_LOCK(schedule);
771
772   /* fd is unique, so there is only one task with this fd in the table */
773   if (silc_likely(silc_hash_table_find(schedule->fd_queue,
774                                        SILC_32_TO_PTR(fd), NULL,
775                                        (void *)&task))) {
776     SILC_LOG_DEBUG(("Deleting task %p", task));
777     task->valid = FALSE;
778
779     /* Call notify callback */
780     if (schedule->notify)
781       schedule->notify(schedule, FALSE, task, TRUE, fd, 0, 0, 0,
782                        schedule->notify_context);
783     ret = TRUE;
784   }
785
786   SILC_SCHEDULE_UNLOCK(schedule);
787
788   /* If it is signal, remove it */
789   if (silc_unlikely(!task)) {
790     schedule_ops.signal_unregister(schedule, schedule->internal, fd);
791     ret = TRUE;
792   }
793
794   return ret;
795 }
796
797 /* Invalidate task by task callback. */
798
799 SilcBool silc_schedule_task_del_by_callback(SilcSchedule schedule,
800                                             SilcTaskCallback callback)
801 {
802   SilcTask task;
803   SilcHashTableList htl;
804   SilcList list;
805   SilcBool ret = FALSE;
806
807   SILC_LOG_DEBUG(("Unregister task by callback"));
808
809   SILC_SCHEDULE_LOCK(schedule);
810
811   /* Delete from fd queue */
812   silc_hash_table_list(schedule->fd_queue, &htl);
813   while (silc_hash_table_get(&htl, NULL, (void *)&task)) {
814     if (task->callback == callback) {
815       task->valid = FALSE;
816
817       /* Call notify callback */
818       if (schedule->notify)
819         schedule->notify(schedule, FALSE, task, TRUE,
820                          ((SilcTaskFd)task)->fd, 0, 0, 0,
821                          schedule->notify_context);
822       ret = TRUE;
823     }
824   }
825   silc_hash_table_list_reset(&htl);
826
827   /* Delete from timeout queue */
828   list = schedule->timeout_queue;
829   silc_list_start(list);
830   while ((task = (SilcTask)silc_list_get(list))) {
831     if (task->callback == callback) {
832       task->valid = FALSE;
833
834       /* Call notify callback */
835       if (schedule->notify)
836         schedule->notify(schedule, FALSE, task, FALSE, 0, 0, 0, 0,
837                          schedule->notify_context);
838       ret = TRUE;
839     }
840   }
841
842   SILC_SCHEDULE_UNLOCK(schedule);
843
844   return ret;
845 }
846
847 /* Invalidate task by context. */
848
849 SilcBool silc_schedule_task_del_by_context(SilcSchedule schedule,
850                                            void *context)
851 {
852   SilcTask task;
853   SilcHashTableList htl;
854   SilcList list;
855   SilcBool ret = FALSE;
856
857   SILC_LOG_DEBUG(("Unregister task by context"));
858
859   SILC_SCHEDULE_LOCK(schedule);
860
861   /* Delete from fd queue */
862   silc_hash_table_list(schedule->fd_queue, &htl);
863   while (silc_hash_table_get(&htl, NULL, (void *)&task)) {
864     if (task->context == context) {
865       task->valid = FALSE;
866
867       /* Call notify callback */
868       if (schedule->notify)
869         schedule->notify(schedule, FALSE, task, TRUE,
870                          ((SilcTaskFd)task)->fd, 0, 0, 0,
871                          schedule->notify_context);
872       ret = TRUE;
873     }
874   }
875   silc_hash_table_list_reset(&htl);
876
877   /* Delete from timeout queue */
878   list = schedule->timeout_queue;
879   silc_list_start(list);
880   while ((task = (SilcTask)silc_list_get(list))) {
881     if (task->context == context) {
882       task->valid = FALSE;
883
884       /* Call notify callback */
885       if (schedule->notify)
886         schedule->notify(schedule, FALSE, task, FALSE, 0, 0, 0, 0,
887                          schedule->notify_context);
888       ret = TRUE;
889     }
890   }
891
892   SILC_SCHEDULE_UNLOCK(schedule);
893
894   return ret;
895 }
896
897 /* Invalidate task by all */
898
899 SilcBool silc_schedule_task_del_by_all(SilcSchedule schedule, int fd,
900                                        SilcTaskCallback callback,
901                                        void *context)
902 {
903   SilcTask task;
904   SilcList list;
905   SilcBool ret = FALSE;
906
907   SILC_LOG_DEBUG(("Unregister task by fd, callback and context"));
908
909   /* For fd task, callback and context is irrelevant as fd is unique */
910   if (fd)
911     return silc_schedule_task_del_by_fd(schedule, fd);
912
913   SILC_SCHEDULE_LOCK(schedule);
914
915   /* Delete from timeout queue */
916   list = schedule->timeout_queue;
917   silc_list_start(list);
918   while ((task = (SilcTask)silc_list_get(list))) {
919     if (task->callback == callback && task->context == context) {
920       task->valid = FALSE;
921
922       /* Call notify callback */
923       if (schedule->notify)
924         schedule->notify(schedule, FALSE, task, FALSE, 0, 0, 0, 0,
925                          schedule->notify_context);
926       ret = TRUE;
927     }
928   }
929
930   SILC_SCHEDULE_UNLOCK(schedule);
931
932   return ret;
933 }
934
935 /* Sets a file descriptor to be listened by scheduler. One can call this
936    directly if wanted. This can be called multiple times for one file
937    descriptor to set different iomasks. */
938
939 SilcBool silc_schedule_set_listen_fd(SilcSchedule schedule, SilcUInt32 fd,
940                                      SilcTaskEvent mask, SilcBool send_events)
941 {
942   SilcTaskFd task;
943
944   if (silc_unlikely(!schedule->valid))
945     return FALSE;
946
947   SILC_SCHEDULE_LOCK(schedule);
948
949   if (silc_hash_table_find(schedule->fd_queue, SILC_32_TO_PTR(fd),
950                            NULL, (void *)&task)) {
951     if (!schedule_ops.schedule_fd(schedule, schedule->internal, task, mask)) {
952       SILC_SCHEDULE_UNLOCK(schedule);
953       return FALSE;
954     }
955     task->events = mask;
956     if (silc_unlikely(send_events) && mask) {
957       task->revents = mask;
958       silc_schedule_dispatch_fd(schedule);
959     }
960
961     /* Call notify callback */
962     if (schedule->notify)
963       schedule->notify(schedule, TRUE, (SilcTask)task,
964                        TRUE, task->fd, mask, 0, 0,
965                        schedule->notify_context);
966   }
967
968   SILC_SCHEDULE_UNLOCK(schedule);
969
970   return TRUE;
971 }
972
973 /* Returns the file descriptor's current requested event mask. */
974
975 SilcTaskEvent silc_schedule_get_fd_events(SilcSchedule schedule,
976                                           SilcUInt32 fd)
977 {
978   SilcTaskFd task;
979   SilcTaskEvent event = 0;
980
981   if (silc_unlikely(!schedule->valid))
982     return 0;
983
984   SILC_SCHEDULE_LOCK(schedule);
985   if (silc_hash_table_find(schedule->fd_queue, SILC_32_TO_PTR(fd),
986                            NULL, (void *)&task))
987     event = task->events;
988   SILC_SCHEDULE_UNLOCK(schedule);
989
990   return event;
991 }
992
993 /* Removes a file descriptor from listen list. */
994
995 void silc_schedule_unset_listen_fd(SilcSchedule schedule, SilcUInt32 fd)
996 {
997   silc_schedule_set_listen_fd(schedule, fd, 0, FALSE);
998 }