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