Created SILC Runtime Toolkit git repository Part II.
[runtime.git] / lib / silcutil / silcschedule.c
1 /*
2
3   silcschedule.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1998 - 2008 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
20 #include "silcruntime.h"
21
22 /************************** Types and definitions ***************************/
23
24 /* Connected event context */
25 typedef struct SilcScheduleEventConnectionStruct {
26   SilcSchedule schedule;
27   SilcTaskEventCallback callback;
28   void *context;
29   struct SilcScheduleEventConnectionStruct *next;
30 } *SilcScheduleEventConnection;
31
32 /* Platform specific implementation */
33 extern const SilcScheduleOps schedule_ops;
34
35 static void silc_schedule_task_remove(SilcSchedule schedule, SilcTask task);
36 static void silc_schedule_dispatch_fd(SilcSchedule schedule);
37 static void silc_schedule_dispatch_timeout(SilcSchedule schedule,
38                                            SilcBool dispatch_all);
39 SILC_TASK_CALLBACK(silc_schedule_event_del_timeout);
40
41 /************************ Static utility functions **************************/
42
43 /* Fd task hash table destructor */
44
45 static void silc_schedule_fd_destructor(void *key, void *context,
46                                         void *user_context)
47 {
48   silc_free(context);
49 }
50
51 /* Executes file descriptor tasks. Invalid tasks are removed here. */
52
53 static void silc_schedule_dispatch_fd(SilcSchedule schedule)
54 {
55   SilcTaskFd task;
56   SilcTask t;
57
58   /* The dispatch list includes only valid tasks, and tasks that have
59      something to dispatch.  Dispatching is atomic; no matter if another
60      thread invalidates a task when we unlock, we dispatch to completion. */
61   SILC_SCHEDULE_UNLOCK(schedule);
62   silc_list_start(schedule->fd_dispatch);
63   while ((task = silc_list_get(schedule->fd_dispatch))) {
64     t = (SilcTask)task;
65
66     /* Is the task ready for reading */
67     if (task->revents & SILC_TASK_READ)
68       t->callback(schedule, schedule->app_context, SILC_TASK_READ,
69                   task->fd, t->context);
70
71     /* Is the task ready for writing */
72     if (t->valid && task->revents & SILC_TASK_WRITE)
73       t->callback(schedule, schedule->app_context, SILC_TASK_WRITE,
74                   task->fd, t->context);
75   }
76   SILC_SCHEDULE_LOCK(schedule);
77
78   /* Remove invalidated tasks */
79   silc_list_start(schedule->fd_dispatch);
80   while ((task = silc_list_get(schedule->fd_dispatch)))
81     if (silc_unlikely(!task->header.valid))
82       silc_schedule_task_remove(schedule, (SilcTask)task);
83 }
84
85 /* Executes all tasks whose timeout has expired. The task is removed from
86    the task queue after the callback function has returned. Also, invalid
87    tasks are removed here. */
88
89 static void silc_schedule_dispatch_timeout(SilcSchedule schedule,
90                                            SilcBool dispatch_all)
91 {
92   SilcTask t;
93   SilcTaskTimeout task;
94   struct timeval curtime;
95   int count = 0;
96
97   SILC_LOG_DEBUG(("Running timeout tasks"));
98
99   silc_gettimeofday(&curtime);
100
101   /* First task in the task queue has always the earliest timeout. */
102   silc_list_start(schedule->timeout_queue);
103   task = silc_list_get(schedule->timeout_queue);
104   if (silc_unlikely(!task))
105     return;
106   do {
107     t = (SilcTask)task;
108
109     /* Remove invalid task */
110     if (silc_unlikely(!t->valid)) {
111       silc_schedule_task_remove(schedule, t);
112       continue;
113     }
114
115     /* Execute the task if the timeout has expired */
116     if (silc_compare_timeval(&task->timeout, &curtime) > 0 && !dispatch_all)
117       break;
118
119     t->valid = FALSE;
120     SILC_SCHEDULE_UNLOCK(schedule);
121     t->callback(schedule, schedule->app_context, SILC_TASK_EXPIRE, 0,
122                 t->context);
123     SILC_SCHEDULE_LOCK(schedule);
124
125     /* Remove the expired task */
126     silc_schedule_task_remove(schedule, t);
127
128     /* Balance when we have lots of small timeouts */
129     if (silc_unlikely((++count) > 40))
130       break;
131   } while (silc_likely((task = silc_list_get(schedule->timeout_queue))));
132 }
133
134 /* Calculates next timeout. This is the timeout value when at earliest some
135    of the timeout tasks expire. If this is in the past, they will be
136    dispatched now. */
137
138 static void silc_schedule_select_timeout(SilcSchedule schedule)
139 {
140   SilcTask t;
141   SilcTaskTimeout task;
142   struct timeval curtime;
143   SilcBool dispatch = TRUE;
144
145   /* Get the current time */
146   silc_gettimeofday(&curtime);
147   schedule->has_timeout = FALSE;
148
149   /* First task in the task queue has always the earliest timeout. */
150   silc_list_start(schedule->timeout_queue);
151   task = silc_list_get(schedule->timeout_queue);
152   if (silc_unlikely(!task))
153     return;
154   do {
155     t = (SilcTask)task;
156
157     /* Remove invalid task */
158     if (silc_unlikely(!t->valid)) {
159       silc_schedule_task_remove(schedule, t);
160       continue;
161     }
162
163     /* If the timeout is in past, we will run the task and all other
164        timeout tasks from the past. */
165     if (silc_compare_timeval(&task->timeout, &curtime) <= 0 && dispatch) {
166       silc_schedule_dispatch_timeout(schedule, FALSE);
167       if (silc_unlikely(!schedule->valid))
168         return;
169
170       /* Start selecting new timeout again after dispatch */
171       silc_list_start(schedule->timeout_queue);
172       dispatch = FALSE;
173       continue;
174     }
175
176     /* Calculate the next timeout */
177     curtime.tv_sec = task->timeout.tv_sec - curtime.tv_sec;
178     curtime.tv_usec = task->timeout.tv_usec - curtime.tv_usec;
179     if (curtime.tv_sec < 0)
180       curtime.tv_sec = 0;
181
182     /* We wouldn't want to go under zero, check for it. */
183     if (curtime.tv_usec < 0) {
184       curtime.tv_sec -= 1;
185       if (curtime.tv_sec < 0)
186         curtime.tv_sec = 0;
187       curtime.tv_usec += 1000000L;
188     }
189     break;
190   } while ((task = silc_list_get(schedule->timeout_queue)));
191
192   /* Save the timeout */
193   if (task) {
194     schedule->timeout = curtime;
195     schedule->has_timeout = TRUE;
196     SILC_LOG_DEBUG(("timeout: sec=%d, usec=%d", schedule->timeout.tv_sec,
197                     schedule->timeout.tv_usec));
198   }
199 }
200
201 /* Removes task from the scheduler.  This must be called with scheduler
202    locked. */
203
204 static void silc_schedule_task_remove(SilcSchedule schedule, SilcTask task)
205 {
206   SilcSchedule parent;
207
208   if (silc_unlikely(task == SILC_ALL_TASKS)) {
209     SilcTask task;
210     SilcEventTask etask;
211     SilcHashTableList htl;
212     void *fd;
213
214     /* Delete from fd queue */
215     silc_hash_table_list(schedule->fd_queue, &htl);
216     while (silc_hash_table_get(&htl, &fd, (void *)&task))
217       silc_hash_table_del(schedule->fd_queue, fd);
218     silc_hash_table_list_reset(&htl);
219
220     /* Delete from timeout queue */
221     silc_list_start(schedule->timeout_queue);
222     while ((task = silc_list_get(schedule->timeout_queue))) {
223       silc_list_del(schedule->timeout_queue, task);
224       silc_free(task);
225     }
226
227     /* Delete even tasks */
228     parent = silc_schedule_get_parent(schedule);
229     if (parent->events) {
230       silc_hash_table_list(parent->events, &htl);
231       while (silc_hash_table_get(&htl, NULL, (void *)&etask)) {
232         silc_hash_table_del_by_context(parent->events, etask->event, etask);
233         silc_free(etask->event);
234         silc_free(etask);
235       }
236       silc_hash_table_list_reset(&htl);
237     }
238     return;
239   }
240
241   switch (task->type) {
242   case SILC_TASK_FD:
243     {
244       /* Delete from fd queue */
245       SilcTaskFd ftask = (SilcTaskFd)task;
246       silc_hash_table_del(schedule->fd_queue, SILC_32_TO_PTR(ftask->fd));
247     }
248     break;
249
250   case SILC_TASK_TIMEOUT:
251     {
252       /* Delete from timeout queue */
253       silc_list_del(schedule->timeout_queue, task);
254
255       /* Put to free list */
256       silc_list_add(schedule->free_tasks, task);
257     }
258     break;
259
260   case SILC_TASK_EVENT:
261     {
262       SilcEventTask etask = (SilcEventTask)task;
263       SilcScheduleEventConnection conn;
264
265       parent = silc_schedule_get_parent(schedule);
266
267       /* Delete event */
268       silc_hash_table_del_by_context(parent->events, etask->event, etask);
269
270       /* Remove all connections */
271       silc_list_start(etask->connections);
272       while ((conn = silc_list_get(etask->connections)))
273         silc_free(conn);
274
275       silc_free(etask->event);
276       silc_free(etask);
277     }
278     break;
279
280   default:
281     break;
282   }
283 }
284
285 /* Timeout freelist garbage collection */
286
287 SILC_TASK_CALLBACK(silc_schedule_timeout_gc)
288 {
289   SilcTaskTimeout t;
290   int c;
291
292   if (!schedule->valid)
293     return;
294
295   SILC_LOG_DEBUG(("Timeout freelist garbage collection"));
296
297   SILC_SCHEDULE_LOCK(schedule);
298
299   if (silc_list_count(schedule->free_tasks) <= 10) {
300     SILC_SCHEDULE_UNLOCK(schedule);
301     silc_schedule_task_add_timeout(schedule, silc_schedule_timeout_gc,
302                                    schedule, 3600, 0);
303     return;
304   }
305   if (silc_list_count(schedule->timeout_queue) >
306       silc_list_count(schedule->free_tasks)) {
307     SILC_SCHEDULE_UNLOCK(schedule);
308     silc_schedule_task_add_timeout(schedule, silc_schedule_timeout_gc,
309                                    schedule, 3600, 0);
310     return;
311   }
312
313   c = silc_list_count(schedule->free_tasks) / 2;
314   if (c > silc_list_count(schedule->timeout_queue))
315     c = (silc_list_count(schedule->free_tasks) -
316          silc_list_count(schedule->timeout_queue));
317   if (silc_list_count(schedule->free_tasks) - c < 10)
318     c -= (10 - (silc_list_count(schedule->free_tasks) - c));
319
320   SILC_LOG_DEBUG(("Freeing %d unused tasks, leaving %d", c,
321                   silc_list_count(schedule->free_tasks) - c));
322
323   silc_list_start(schedule->free_tasks);
324   while ((t = silc_list_get(schedule->free_tasks)) && c-- > 0) {
325     silc_list_del(schedule->free_tasks, t);
326     silc_free(t);
327   }
328   silc_list_start(schedule->free_tasks);
329
330   SILC_SCHEDULE_UNLOCK(schedule);
331
332   silc_schedule_task_add_timeout(schedule, silc_schedule_timeout_gc,
333                                  schedule, 3600, 0);
334 }
335
336 #ifdef SILC_DIST_INPLACE
337 /* Print schedule statistics to stdout */
338
339 void silc_schedule_stats(SilcSchedule schedule)
340 {
341   SilcTaskFd ftask;
342   fprintf(stdout, "Schedule %p statistics:\n\n", schedule);
343   fprintf(stdout, "Num FD tasks         : %d (%lu bytes allocated)\n",
344           silc_hash_table_count(schedule->fd_queue),
345           sizeof(*ftask) * silc_hash_table_count(schedule->fd_queue));
346   fprintf(stdout, "Num Timeout tasks    : %d (%lu bytes allocated)\n",
347           silc_list_count(schedule->timeout_queue),
348           sizeof(struct SilcTaskTimeoutStruct) *
349           silc_list_count(schedule->timeout_queue));
350   fprintf(stdout, "Num Timeout freelist : %d (%lu bytes allocated)\n",
351           silc_list_count(schedule->free_tasks),
352           sizeof(struct SilcTaskTimeoutStruct) *
353           silc_list_count(schedule->free_tasks));
354 }
355 #endif /* SILC_DIST_INPLACE */
356
357 /****************************** Public API **********************************/
358
359 /* Initializes the scheduler. This returns the scheduler context that
360    is given as arugment usually to all silc_schedule_* functions.
361    The `max_tasks' indicates the number of maximum tasks that the
362    scheduler can handle. The `app_context' is application specific
363    context that is delivered to task callbacks. */
364
365 SilcSchedule silc_schedule_init(int max_tasks, void *app_context,
366                                 SilcStack stack, SilcSchedule parent)
367 {
368   SilcSchedule schedule;
369
370   /* Initialize Tls, in case it hasn't been done yet */
371   silc_thread_tls_init();
372
373   stack = silc_stack_alloc(0, stack);
374   if (!stack)
375     return NULL;
376
377   /* Allocate scheduler from the stack */
378   schedule = silc_scalloc(stack, 1, sizeof(*schedule));
379   if (!schedule)
380     return NULL;
381
382   SILC_LOG_DEBUG(("Initializing scheduler %p", schedule));
383
384   /* Allocate Fd task hash table dynamically */
385   schedule->fd_queue =
386     silc_hash_table_alloc(NULL, 0, silc_hash_uint, NULL, NULL, NULL,
387                           silc_schedule_fd_destructor, NULL, TRUE);
388   if (!schedule->fd_queue) {
389     silc_stack_free(stack);
390     return NULL;
391   }
392
393   silc_list_init(schedule->timeout_queue, struct SilcTaskStruct, next);
394   silc_list_init(schedule->free_tasks, struct SilcTaskStruct, next);
395
396   /* Get the parent */
397   if (parent && parent->parent)
398     parent = parent->parent;
399
400   schedule->stack = stack;
401   schedule->app_context = app_context;
402   schedule->valid = TRUE;
403   schedule->max_tasks = max_tasks;
404   schedule->parent = parent;
405
406   /* Allocate scheduler lock */
407   silc_mutex_alloc(&schedule->lock);
408
409   /* Initialize the platform specific scheduler. */
410   schedule->internal = schedule_ops.init(schedule, app_context);
411   if (!schedule->internal) {
412     silc_hash_table_free(schedule->fd_queue);
413     silc_mutex_free(schedule->lock);
414     silc_stack_free(stack);
415     return NULL;
416   }
417
418   /* Timeout freelist garbage collection */
419   silc_schedule_task_add_timeout(schedule, silc_schedule_timeout_gc,
420                                  schedule, 3600, 0);
421
422   return schedule;
423 }
424
425 /* Uninitializes the schedule. This is called when the program is ready
426    to end. This removes all tasks and task queues. Returns FALSE if the
427    scheduler could not be uninitialized. This happens when the scheduler
428    is still valid and silc_schedule_stop has not been called. */
429
430 SilcBool silc_schedule_uninit(SilcSchedule schedule)
431 {
432   SilcTask task;
433
434   SILC_VERIFY(schedule);
435
436   SILC_LOG_DEBUG(("Uninitializing scheduler %p", schedule));
437
438   if (schedule->valid == TRUE)
439     return FALSE;
440
441   /* Dispatch all timeouts before going away */
442   SILC_SCHEDULE_LOCK(schedule);
443   silc_schedule_dispatch_timeout(schedule, TRUE);
444   SILC_SCHEDULE_UNLOCK(schedule);
445
446   /* Deliver signals before going away */
447   if (schedule->signal_tasks) {
448     schedule_ops.signals_call(schedule, schedule->internal);
449     schedule->signal_tasks = FALSE;
450   }
451
452   /* Unregister all tasks */
453   silc_schedule_task_del(schedule, SILC_ALL_TASKS);
454   silc_schedule_task_remove(schedule, SILC_ALL_TASKS);
455
456   /* Delete timeout task freelist */
457   silc_list_start(schedule->free_tasks);
458   while ((task = silc_list_get(schedule->free_tasks)))
459     silc_free(task);
460
461   /* Unregister all task queues */
462   silc_hash_table_free(schedule->fd_queue);
463
464   /* Uninit the platform specific scheduler. */
465   schedule_ops.uninit(schedule, schedule->internal);
466
467   silc_mutex_free(schedule->lock);
468   silc_stack_free(schedule->stack);
469
470   return TRUE;
471 }
472
473 /* Stops the schedule even if it is not supposed to be stopped yet.
474    After calling this, one should call silc_schedule_uninit (after the
475    silc_schedule has returned). */
476
477 void silc_schedule_stop(SilcSchedule schedule)
478 {
479   SILC_LOG_DEBUG(("Stopping scheduler"));
480   SILC_VERIFY(schedule);
481   SILC_SCHEDULE_LOCK(schedule);
482   schedule->valid = FALSE;
483   SILC_SCHEDULE_UNLOCK(schedule);
484 }
485
486 /* Runs the scheduler once and then returns.   Must be called locked. */
487
488 static SilcBool silc_schedule_iterate(SilcSchedule schedule, int timeout_usecs)
489 {
490   struct timeval timeout;
491   int ret;
492
493   do {
494     SILC_LOG_DEBUG(("In scheduler loop"));
495
496     /* Deliver signals if any has been set to be called */
497     if (silc_unlikely(schedule->signal_tasks)) {
498       SILC_SCHEDULE_UNLOCK(schedule);
499       schedule_ops.signals_call(schedule, schedule->internal);
500       schedule->signal_tasks = FALSE;
501       SILC_SCHEDULE_LOCK(schedule);
502     }
503
504     /* Check if scheduler is valid */
505     if (silc_unlikely(schedule->valid == FALSE)) {
506       SILC_LOG_DEBUG(("Scheduler not valid anymore, exiting"));
507       return FALSE;
508     }
509
510     /* Calculate next timeout for silc_select().  This is the timeout value
511        when at earliest some of the timeout tasks expire.  This may dispatch
512        already expired timeouts. */
513     silc_schedule_select_timeout(schedule);
514
515     /* Check if scheduler is valid */
516     if (silc_unlikely(schedule->valid == FALSE)) {
517       SILC_LOG_DEBUG(("Scheduler not valid anymore, exiting"));
518       return FALSE;
519     }
520
521     if (timeout_usecs >= 0) {
522       timeout.tv_sec = 0;
523       timeout.tv_usec = timeout_usecs;
524       schedule->timeout = timeout;
525       schedule->has_timeout = TRUE;
526     }
527
528     /* This is the main silc_select(). The program blocks here until some
529        of the selected file descriptors change status or the selected
530        timeout expires. */
531     SILC_LOG_DEBUG(("Select"));
532     ret = schedule_ops.schedule(schedule, schedule->internal);
533
534     if (silc_likely(ret == 0)) {
535       /* Timeout */
536       SILC_LOG_DEBUG(("Running timeout tasks"));
537       if (silc_likely(silc_list_count(schedule->timeout_queue)))
538         silc_schedule_dispatch_timeout(schedule, FALSE);
539       continue;
540
541     } else if (silc_likely(ret > 0)) {
542       /* There is some data available now */
543       SILC_LOG_DEBUG(("Running fd tasks"));
544       silc_schedule_dispatch_fd(schedule);
545
546       /* If timeout was very short, dispatch also timeout tasks */
547       if (schedule->has_timeout && schedule->timeout.tv_sec == 0 &&
548           schedule->timeout.tv_usec < 50000)
549         silc_schedule_dispatch_timeout(schedule, FALSE);
550       continue;
551
552     } else {
553       /* Error or special case handling */
554       if (errno == EINTR)
555         continue;
556       if (ret == -2)
557         break;
558
559       SILC_LOG_ERROR(("Error in select()/poll(): %s", strerror(errno)));
560       continue;
561     }
562   } while (timeout_usecs == -1);
563
564   return TRUE;
565 }
566
567 /* Runs the scheduler once and then returns. */
568
569 SilcBool silc_schedule_one(SilcSchedule schedule, int timeout_usecs)
570 {
571   SilcBool ret;
572   SILC_SCHEDULE_LOCK(schedule);
573   ret = silc_schedule_iterate(schedule, timeout_usecs);
574   SILC_SCHEDULE_UNLOCK(schedule);
575   return ret;
576 }
577
578 /* Runs the scheduler and blocks here.  When this returns the scheduler
579    has ended. */
580
581 #ifndef SILC_SYMBIAN
582 void silc_schedule(SilcSchedule schedule)
583 {
584   SILC_LOG_DEBUG(("Running scheduler"));
585
586   /* Start the scheduler loop */
587   SILC_SCHEDULE_LOCK(schedule);
588   silc_schedule_iterate(schedule, -1);
589   SILC_SCHEDULE_UNLOCK(schedule);
590 }
591 #endif /* !SILC_SYMBIAN */
592
593 /* Wakes up the scheduler. This is used only in multi-threaded
594    environments where threads may add new tasks or remove old tasks
595    from task queues. This is called to wake up the scheduler in the
596    main thread so that it detects the changes in the task queues.
597    If threads support is not compiled in this function has no effect.
598    Implementation of this function is platform specific. */
599
600 void silc_schedule_wakeup(SilcSchedule schedule)
601 {
602 #ifdef SILC_THREADS
603   SILC_LOG_DEBUG(("Wakeup scheduler"));
604   SILC_SCHEDULE_LOCK(schedule);
605   schedule_ops.wakeup(schedule, schedule->internal);
606   SILC_SCHEDULE_UNLOCK(schedule);
607 #endif
608 }
609
610 /* Returns parent scheduler */
611
612 SilcSchedule silc_schedule_get_parent(SilcSchedule schedule)
613 {
614   return schedule->parent ? schedule->parent : schedule;
615 }
616
617 /* Returns the application specific context that was saved into the
618    scheduler in silc_schedule_init function.  The context is also
619    returned to application in task callback functions, but this function
620    may be used to get it as well if needed. */
621
622 void *silc_schedule_get_context(SilcSchedule schedule)
623 {
624   return schedule->app_context;
625 }
626
627 /* Return the stack of the scheduler */
628
629 SilcStack silc_schedule_get_stack(SilcSchedule schedule)
630 {
631   return schedule->stack;
632 }
633
634 /* Set notify callback */
635
636 void silc_schedule_set_notify(SilcSchedule schedule,
637                               SilcTaskNotifyCb notify, void *context)
638 {
639   schedule->notify = notify;
640   schedule->notify_context = context;
641 }
642
643 /* Set global scheduler */
644
645 void silc_schedule_set_global(SilcSchedule schedule)
646 {
647   SilcTls tls = silc_thread_get_tls();
648
649   if (!tls) {
650     /* Try to initialize Tls */
651     tls = silc_thread_tls_init();
652     SILC_VERIFY(tls);
653     if (!tls)
654       return;
655   }
656
657   SILC_LOG_DEBUG(("Setting global scheduler %p", schedule));
658
659   tls->schedule = schedule;
660 }
661
662 /* Return global scheduler */
663
664 SilcSchedule silc_schedule_get_global(void)
665 {
666   SilcTls tls = silc_thread_get_tls();
667
668   if (!tls)
669     return NULL;
670
671   SILC_LOG_DEBUG(("Return global scheduler %p", tls->schedule));
672
673   return tls->schedule;
674 }
675
676 /* Add new task to the scheduler */
677
678 SilcTask silc_schedule_task_add(SilcSchedule schedule, SilcUInt32 fd,
679                                 SilcTaskCallback callback, void *context,
680                                 long seconds, long useconds,
681                                 SilcTaskType type)
682 {
683   SilcTask task = NULL;
684
685   if (!schedule) {
686     schedule = silc_schedule_get_global();
687     SILC_VERIFY(schedule);
688     if (!schedule) {
689       silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
690       return NULL;
691     }
692   }
693
694   if (silc_unlikely(!schedule->valid)) {
695     silc_set_errno(SILC_ERR_NOT_VALID);
696     return NULL;
697   }
698
699   SILC_SCHEDULE_LOCK(schedule);
700
701   if (silc_likely(type == SILC_TASK_TIMEOUT)) {
702     SilcTaskTimeout tmp, prev, ttask;
703     SilcList list;
704
705     silc_list_start(schedule->free_tasks);
706     ttask = silc_list_get(schedule->free_tasks);
707     if (silc_unlikely(!ttask)) {
708       ttask = silc_calloc(1, sizeof(*ttask));
709       if (silc_unlikely(!ttask))
710         goto out;
711     } else
712       silc_list_del(schedule->free_tasks, ttask);
713
714     ttask->header.type = 1;
715     ttask->header.callback = callback;
716     ttask->header.context = context;
717     ttask->header.valid = TRUE;
718
719     /* Add timeout */
720     silc_gettimeofday(&ttask->timeout);
721     if ((seconds + useconds) > 0) {
722       ttask->timeout.tv_sec += seconds + (useconds / 1000000L);
723       ttask->timeout.tv_usec += (useconds % 1000000L);
724       if (ttask->timeout.tv_usec >= 1000000L) {
725         ttask->timeout.tv_sec += 1;
726         ttask->timeout.tv_usec -= 1000000L;
727       }
728     }
729
730     SILC_LOG_DEBUG(("New timeout task %p: sec=%d, usec=%d", ttask,
731                     seconds, useconds));
732
733     /* Add task to correct spot so that the first task in the list has
734        the earliest timeout. */
735     list = schedule->timeout_queue;
736     silc_list_start(list);
737     prev = NULL;
738     while ((tmp = silc_list_get(list)) != SILC_LIST_END) {
739       /* If we have shorter timeout, we have found our spot */
740       if (silc_compare_timeval(&ttask->timeout, &tmp->timeout) < 0) {
741         silc_list_insert(schedule->timeout_queue, prev, ttask);
742         break;
743       }
744       prev = tmp;
745     }
746     if (!tmp)
747       silc_list_add(schedule->timeout_queue, ttask);
748
749     task = (SilcTask)ttask;
750
751     /* Call notify callback */
752     if (schedule->notify)
753       schedule->notify(schedule, TRUE, task, FALSE, 0, 0, seconds, useconds,
754                        schedule->notify_context);
755
756   } else if (silc_likely(type == SILC_TASK_FD)) {
757     SilcTaskFd ftask;
758
759     /* Check if fd is already added */
760     if (silc_unlikely(silc_hash_table_find(schedule->fd_queue,
761                                            SILC_32_TO_PTR(fd),
762                                            NULL, (void *)&task))) {
763       if (task->valid)
764         goto out;
765
766       /* Remove invalid task.  We must have unique fd key to hash table. */
767       silc_schedule_task_remove(schedule, task);
768     }
769
770     /* Check max tasks */
771     if (silc_unlikely(schedule->max_tasks > 0 &&
772                       silc_hash_table_count(schedule->fd_queue) >=
773                       schedule->max_tasks)) {
774       SILC_LOG_WARNING(("Scheduler task limit reached: cannot add new task"));
775       task = NULL;
776       silc_set_errno(SILC_ERR_LIMIT);
777       goto out;
778     }
779
780     ftask = silc_calloc(1, sizeof(*ftask));
781     if (silc_unlikely(!ftask)) {
782       task = NULL;
783       goto out;
784     }
785
786     SILC_LOG_DEBUG(("New fd task %p fd=%d", ftask, fd));
787
788     ftask->header.type = 0;
789     ftask->header.callback = callback;
790     ftask->header.context = context;
791     ftask->header.valid = TRUE;
792     ftask->events = SILC_TASK_READ;
793     ftask->fd = fd;
794
795     /* Add task and schedule it */
796     if (!silc_hash_table_add(schedule->fd_queue, SILC_32_TO_PTR(fd), ftask)) {
797       silc_free(ftask);
798       task = NULL;
799       goto out;
800     }
801     if (!schedule_ops.schedule_fd(schedule, schedule->internal,
802                                   ftask, ftask->events)) {
803       silc_hash_table_del(schedule->fd_queue, SILC_32_TO_PTR(fd));
804       task = NULL;
805       goto out;
806     }
807
808     task = (SilcTask)ftask;
809
810     /* Call notify callback */
811     if (schedule->notify)
812       schedule->notify(schedule, TRUE, task, TRUE, ftask->fd,
813                        SILC_TASK_READ, 0, 0, schedule->notify_context);
814
815   } else if (silc_unlikely(type == SILC_TASK_SIGNAL)) {
816     SILC_SCHEDULE_UNLOCK(schedule);
817     schedule_ops.signal_register(schedule, schedule->internal, fd,
818                                  callback, context);
819     return NULL;
820   }
821
822  out:
823   SILC_SCHEDULE_UNLOCK(schedule);
824
825 #ifdef SILC_SYMBIAN
826   /* On symbian we wakeup scheduler immediately after adding timeout task
827      in case the task is added outside the scheduler loop (in some active
828      object). */
829   if (task && task->type == 1)
830     silc_schedule_wakeup(schedule);
831 #endif /* SILC_SYMBIAN */
832
833   return task;
834 }
835
836 /* Invalidates task */
837
838 SilcBool silc_schedule_task_del(SilcSchedule schedule, SilcTask task)
839 {
840   SilcSchedule parent;
841
842   if (!schedule) {
843     schedule = silc_schedule_get_global();
844     SILC_VERIFY(schedule);
845     if (!schedule) {
846       silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
847       return FALSE;
848     }
849   }
850
851   if (silc_unlikely(task == SILC_ALL_TASKS)) {
852     SilcHashTableList htl;
853
854     SILC_LOG_DEBUG(("Unregister all tasks"));
855
856     SILC_SCHEDULE_LOCK(schedule);
857
858     /* Delete from fd queue */
859     silc_hash_table_list(schedule->fd_queue, &htl);
860     while (silc_hash_table_get(&htl, NULL, (void *)&task)) {
861       task->valid = FALSE;
862
863       /* Call notify callback */
864       if (schedule->notify)
865         schedule->notify(schedule, FALSE, task, TRUE,
866                          ((SilcTaskFd)task)->fd, 0, 0, 0,
867                          schedule->notify_context);
868     }
869     silc_hash_table_list_reset(&htl);
870
871     /* Delete from timeout queue */
872     silc_list_start(schedule->timeout_queue);
873     while ((task = (SilcTask)silc_list_get(schedule->timeout_queue))) {
874       task->valid = FALSE;
875
876       /* Call notify callback */
877       if (schedule->notify)
878         schedule->notify(schedule, FALSE, task, FALSE, 0, 0, 0, 0,
879                          schedule->notify_context);
880     }
881
882     /* Delete even tasks */
883     parent = silc_schedule_get_parent(schedule);
884     if (parent->events) {
885       silc_hash_table_list(parent->events, &htl);
886       while (silc_hash_table_get(&htl, NULL, (void *)&task))
887         task->valid = FALSE;
888       silc_hash_table_list_reset(&htl);
889     }
890
891     SILC_SCHEDULE_UNLOCK(schedule);
892     return TRUE;
893   }
894
895   SILC_LOG_DEBUG(("Unregistering task %p, type %d", task, task->type));
896   SILC_SCHEDULE_LOCK(schedule);
897   task->valid = FALSE;
898
899   /* Call notify callback */
900   if (schedule->notify && task->type != SILC_TASK_EVENT)
901     schedule->notify(schedule, FALSE, task, task->type == SILC_TASK_FD,
902                      0, 0, 0, 0, schedule->notify_context);
903   SILC_SCHEDULE_UNLOCK(schedule);
904
905   if (task->type == SILC_TASK_EVENT) {
906     /* Schedule removal of deleted event task */
907     parent = silc_schedule_get_parent(schedule);
908     silc_schedule_task_add_timeout(parent, silc_schedule_event_del_timeout,
909                                    task, 0, 1);
910   }
911
912   return TRUE;
913 }
914
915 /* Invalidate task by fd */
916
917 SilcBool silc_schedule_task_del_by_fd(SilcSchedule schedule, SilcUInt32 fd)
918 {
919   SilcTask task = NULL;
920   SilcBool ret = FALSE;
921
922   SILC_LOG_DEBUG(("Unregister task by fd %d", fd));
923
924   if (!schedule) {
925     schedule = silc_schedule_get_global();
926     SILC_VERIFY(schedule);
927     if (!schedule) {
928       silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
929       return FALSE;
930     }
931   }
932
933   SILC_SCHEDULE_LOCK(schedule);
934
935   /* fd is unique, so there is only one task with this fd in the table */
936   if (silc_likely(silc_hash_table_find(schedule->fd_queue,
937                                        SILC_32_TO_PTR(fd), NULL,
938                                        (void *)&task))) {
939     SILC_LOG_DEBUG(("Deleting task %p", task));
940     task->valid = FALSE;
941
942     /* Call notify callback */
943     if (schedule->notify)
944       schedule->notify(schedule, FALSE, task, TRUE, fd, 0, 0, 0,
945                        schedule->notify_context);
946     ret = TRUE;
947   }
948
949   SILC_SCHEDULE_UNLOCK(schedule);
950
951   /* If it is signal, remove it */
952   if (silc_unlikely(!task)) {
953     schedule_ops.signal_unregister(schedule, schedule->internal, fd);
954     ret = TRUE;
955   }
956
957   if (ret == FALSE)
958     silc_set_errno(SILC_ERR_NOT_FOUND);
959
960   return ret;
961 }
962
963 /* Invalidate task by task callback. */
964
965 SilcBool silc_schedule_task_del_by_callback(SilcSchedule schedule,
966                                             SilcTaskCallback callback)
967 {
968   SilcTask task;
969   SilcHashTableList htl;
970   SilcList list;
971   SilcBool ret = FALSE;
972
973   SILC_LOG_DEBUG(("Unregister task by callback"));
974
975   if (!schedule) {
976     schedule = silc_schedule_get_global();
977     SILC_VERIFY(schedule);
978     if (!schedule) {
979       silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
980       return FALSE;
981     }
982   }
983
984   SILC_SCHEDULE_LOCK(schedule);
985
986   /* Delete from fd queue */
987   silc_hash_table_list(schedule->fd_queue, &htl);
988   while (silc_hash_table_get(&htl, NULL, (void *)&task)) {
989     if (task->callback == callback) {
990       task->valid = FALSE;
991
992       /* Call notify callback */
993       if (schedule->notify)
994         schedule->notify(schedule, FALSE, task, TRUE,
995                          ((SilcTaskFd)task)->fd, 0, 0, 0,
996                          schedule->notify_context);
997       ret = TRUE;
998     }
999   }
1000   silc_hash_table_list_reset(&htl);
1001
1002   /* Delete from timeout queue */
1003   list = schedule->timeout_queue;
1004   silc_list_start(list);
1005   while ((task = (SilcTask)silc_list_get(list))) {
1006     if (task->callback == callback) {
1007       task->valid = FALSE;
1008
1009       /* Call notify callback */
1010       if (schedule->notify)
1011         schedule->notify(schedule, FALSE, task, FALSE, 0, 0, 0, 0,
1012                          schedule->notify_context);
1013       ret = TRUE;
1014     }
1015   }
1016
1017   SILC_SCHEDULE_UNLOCK(schedule);
1018
1019   if (ret == FALSE)
1020     silc_set_errno(SILC_ERR_NOT_FOUND);
1021
1022   return ret;
1023 }
1024
1025 /* Invalidate task by context. */
1026
1027 SilcBool silc_schedule_task_del_by_context(SilcSchedule schedule,
1028                                            void *context)
1029 {
1030   SilcTask task;
1031   SilcHashTableList htl;
1032   SilcList list;
1033   SilcBool ret = FALSE;
1034
1035   SILC_LOG_DEBUG(("Unregister task by context"));
1036
1037   if (!schedule) {
1038     schedule = silc_schedule_get_global();
1039     SILC_VERIFY(schedule);
1040     if (!schedule) {
1041       silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1042       return FALSE;
1043     }
1044   }
1045
1046   SILC_SCHEDULE_LOCK(schedule);
1047
1048   /* Delete from fd queue */
1049   silc_hash_table_list(schedule->fd_queue, &htl);
1050   while (silc_hash_table_get(&htl, NULL, (void *)&task)) {
1051     if (task->context == context) {
1052       task->valid = FALSE;
1053
1054       /* Call notify callback */
1055       if (schedule->notify)
1056         schedule->notify(schedule, FALSE, task, TRUE,
1057                          ((SilcTaskFd)task)->fd, 0, 0, 0,
1058                          schedule->notify_context);
1059       ret = TRUE;
1060     }
1061   }
1062   silc_hash_table_list_reset(&htl);
1063
1064   /* Delete from timeout queue */
1065   list = schedule->timeout_queue;
1066   silc_list_start(list);
1067   while ((task = (SilcTask)silc_list_get(list))) {
1068     if (task->context == context) {
1069       task->valid = FALSE;
1070
1071       /* Call notify callback */
1072       if (schedule->notify)
1073         schedule->notify(schedule, FALSE, task, FALSE, 0, 0, 0, 0,
1074                          schedule->notify_context);
1075       ret = TRUE;
1076     }
1077   }
1078
1079   SILC_SCHEDULE_UNLOCK(schedule);
1080
1081   if (ret == FALSE)
1082     silc_set_errno(SILC_ERR_NOT_FOUND);
1083
1084   return ret;
1085 }
1086
1087 /* Invalidate task by all */
1088
1089 SilcBool silc_schedule_task_del_by_all(SilcSchedule schedule, int fd,
1090                                        SilcTaskCallback callback,
1091                                        void *context)
1092 {
1093   SilcTask task;
1094   SilcList list;
1095   SilcBool ret = FALSE;
1096
1097   SILC_LOG_DEBUG(("Unregister task by fd, callback and context"));
1098
1099   /* For fd task, callback and context is irrelevant as fd is unique */
1100   if (fd)
1101     return silc_schedule_task_del_by_fd(schedule, fd);
1102
1103   if (!schedule) {
1104     schedule = silc_schedule_get_global();
1105     SILC_VERIFY(schedule);
1106     if (!schedule) {
1107       silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1108       return FALSE;
1109     }
1110   }
1111
1112   SILC_SCHEDULE_LOCK(schedule);
1113
1114   /* Delete from timeout queue */
1115   list = schedule->timeout_queue;
1116   silc_list_start(list);
1117   while ((task = (SilcTask)silc_list_get(list))) {
1118     if (task->callback == callback && task->context == context) {
1119       task->valid = FALSE;
1120
1121       /* Call notify callback */
1122       if (schedule->notify)
1123         schedule->notify(schedule, FALSE, task, FALSE, 0, 0, 0, 0,
1124                          schedule->notify_context);
1125       ret = TRUE;
1126     }
1127   }
1128
1129   SILC_SCHEDULE_UNLOCK(schedule);
1130
1131   if (ret == FALSE)
1132     silc_set_errno(SILC_ERR_NOT_FOUND);
1133
1134   return TRUE;
1135 }
1136
1137 /* Sets a file descriptor to be listened by scheduler. One can call this
1138    directly if wanted. This can be called multiple times for one file
1139    descriptor to set different iomasks. */
1140
1141 SilcBool silc_schedule_set_listen_fd(SilcSchedule schedule, SilcUInt32 fd,
1142                                      SilcTaskEvent mask, SilcBool send_events)
1143 {
1144   SilcTaskFd task;
1145
1146   if (!schedule) {
1147     schedule = silc_schedule_get_global();
1148     SILC_VERIFY(schedule);
1149     if (!schedule) {
1150       silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1151       return FALSE;
1152     }
1153   }
1154
1155   if (silc_unlikely(!schedule->valid)) {
1156     silc_set_errno(SILC_ERR_NOT_VALID);
1157     return FALSE;
1158   }
1159
1160   SILC_SCHEDULE_LOCK(schedule);
1161
1162   if (silc_hash_table_find(schedule->fd_queue, SILC_32_TO_PTR(fd),
1163                            NULL, (void *)&task)) {
1164     if (!schedule_ops.schedule_fd(schedule, schedule->internal, task, mask)) {
1165       SILC_SCHEDULE_UNLOCK(schedule);
1166       return FALSE;
1167     }
1168     task->events = mask;
1169     if (silc_unlikely(send_events) && mask) {
1170       task->revents = mask;
1171       silc_schedule_dispatch_fd(schedule);
1172     }
1173
1174     /* Call notify callback */
1175     if (schedule->notify)
1176       schedule->notify(schedule, TRUE, (SilcTask)task,
1177                        TRUE, task->fd, mask, 0, 0,
1178                        schedule->notify_context);
1179   }
1180
1181   SILC_SCHEDULE_UNLOCK(schedule);
1182
1183   return TRUE;
1184 }
1185
1186 /* Returns the file descriptor's current requested event mask. */
1187
1188 SilcTaskEvent silc_schedule_get_fd_events(SilcSchedule schedule,
1189                                           SilcUInt32 fd)
1190 {
1191   SilcTaskFd task;
1192   SilcTaskEvent event = 0;
1193
1194   if (!schedule) {
1195     schedule = silc_schedule_get_global();
1196     SILC_VERIFY(schedule);
1197     if (!schedule) {
1198       silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1199       return 0;
1200     }
1201   }
1202
1203   if (silc_unlikely(!schedule->valid)) {
1204     silc_set_errno(SILC_ERR_NOT_VALID);
1205     return 0;
1206   }
1207
1208   SILC_SCHEDULE_LOCK(schedule);
1209   if (silc_hash_table_find(schedule->fd_queue, SILC_32_TO_PTR(fd),
1210                            NULL, (void *)&task))
1211     event = task->events;
1212   SILC_SCHEDULE_UNLOCK(schedule);
1213
1214   return event;
1215 }
1216
1217 /* Removes a file descriptor from listen list. */
1218
1219 void silc_schedule_unset_listen_fd(SilcSchedule schedule, SilcUInt32 fd)
1220 {
1221   silc_schedule_set_listen_fd(schedule, fd, 0, FALSE);
1222 }
1223
1224 /*************************** Asynchronous Events ****************************/
1225
1226 /* Add event */
1227
1228 SilcTask silc_schedule_task_add_event(SilcSchedule schedule,
1229                                       const char *event, ...)
1230 {
1231   SilcEventTask task;
1232   SilcSchedule parent;
1233
1234   if (!schedule) {
1235     schedule = silc_schedule_get_global();
1236     SILC_VERIFY(schedule);
1237     if (!schedule) {
1238       silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1239       return NULL;
1240     }
1241   }
1242
1243   /* Get parent scheduler */
1244   parent = silc_schedule_get_parent(schedule);
1245
1246   SILC_LOG_DEBUG(("Adding event '%s' to scheduler %p", event, parent));
1247
1248   SILC_SCHEDULE_LOCK(parent);
1249
1250   /* Create events hash table if not already done */
1251   if (!parent->events) {
1252     parent->events = silc_hash_table_alloc(NULL, 3,
1253                                            silc_hash_string, NULL,
1254                                            silc_hash_string_compare, NULL,
1255                                            NULL, NULL, FALSE);
1256     if (!parent->events) {
1257       SILC_SCHEDULE_UNLOCK(parent);
1258       return NULL;
1259     }
1260   }
1261
1262   /* Check if this event is added already */
1263   if (silc_hash_table_find(parent->events, (void *)event, NULL, NULL)) {
1264     SILC_SCHEDULE_UNLOCK(parent);
1265     return NULL;
1266   }
1267
1268   /* Add new event */
1269   task = silc_calloc(1, sizeof(*task));
1270   if (!task) {
1271     SILC_SCHEDULE_UNLOCK(parent);
1272     return NULL;
1273   }
1274
1275   task->header.type = SILC_TASK_EVENT;
1276   task->header.valid = TRUE;
1277   task->event = silc_strdup(event);
1278   if (!task->event) {
1279     SILC_SCHEDULE_UNLOCK(parent);
1280     silc_free(task);
1281     return NULL;
1282   }
1283   silc_list_init(task->connections, struct SilcScheduleEventConnectionStruct,
1284                  next);
1285
1286   if (!silc_hash_table_add(parent->events, task->event, task)) {
1287     SILC_SCHEDULE_UNLOCK(parent);
1288     silc_free(task->event);
1289     silc_free(task);
1290     return NULL;
1291   }
1292
1293   SILC_SCHEDULE_UNLOCK(parent);
1294
1295   return (SilcTask)task;
1296 }
1297
1298 /* Connect to event task */
1299
1300 SilcBool silc_schedule_event_connect(SilcSchedule schedule,
1301                                      const char *event, SilcTask task,
1302                                      SilcTaskEventCallback callback,
1303                                      void *context)
1304 {
1305   SilcSchedule parent;
1306   SilcScheduleEventConnection conn;
1307   SilcEventTask etask;
1308
1309   if (!schedule) {
1310     schedule = silc_schedule_get_global();
1311     SILC_VERIFY(schedule);
1312     if (!schedule) {
1313       silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1314       return FALSE;
1315     }
1316   }
1317
1318   if (!event && !task) {
1319     silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1320     return FALSE;
1321   }
1322
1323   if (task && task->type != SILC_TASK_EVENT) {
1324     silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1325     return FALSE;
1326   }
1327
1328   /* Get parent scheduler */
1329   parent = silc_schedule_get_parent(schedule);
1330
1331   SILC_SCHEDULE_LOCK(parent);
1332
1333   if (!task) {
1334     /* Get the event task */
1335     if (!silc_hash_table_find(parent->events, (void *)event, NULL,
1336                               (void *)&task)) {
1337       SILC_SCHEDULE_UNLOCK(parent);
1338       return FALSE;
1339     }
1340   }
1341   etask = (SilcEventTask)task;
1342
1343   /* See if task is deleted */
1344   if (task->valid == FALSE) {
1345     SILC_SCHEDULE_UNLOCK(parent);
1346     silc_set_errno(SILC_ERR_NOT_VALID);
1347     return FALSE;
1348   }
1349
1350   SILC_LOG_DEBUG(("Connect callback %p with context %p to event '%s'",
1351                   callback, context, etask->event));
1352
1353   /* See if already connected */
1354   silc_list_start(etask->connections);
1355   while ((conn = silc_list_get(etask->connections))) {
1356     if (conn->callback == callback && conn->context == context) {
1357       SILC_SCHEDULE_UNLOCK(parent);
1358       silc_set_errno(SILC_ERR_ALREADY_EXISTS);
1359       return FALSE;
1360     }
1361   }
1362
1363   conn = silc_calloc(1, sizeof(*conn));
1364   if (!conn) {
1365     SILC_SCHEDULE_UNLOCK(parent);
1366     return FALSE;
1367   }
1368
1369   /* Connect to the event */
1370   conn->schedule = schedule;
1371   conn->callback = callback;
1372   conn->context = context;
1373   silc_list_add(etask->connections, conn);
1374
1375   SILC_SCHEDULE_UNLOCK(parent);
1376
1377   return TRUE;
1378 }
1379
1380 /* Disconnect from event */
1381
1382 SilcBool silc_schedule_event_disconnect(SilcSchedule schedule,
1383                                         const char *event, SilcTask task,
1384                                         SilcTaskEventCallback callback,
1385                                         void *context)
1386 {
1387   SilcSchedule parent;
1388   SilcScheduleEventConnection conn;
1389   SilcEventTask etask;
1390
1391   if (!schedule) {
1392     schedule = silc_schedule_get_global();
1393     SILC_VERIFY(schedule);
1394     if (!schedule) {
1395       silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1396       return FALSE;
1397     }
1398   }
1399
1400   if (!event && !task) {
1401     silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1402     return FALSE;
1403   }
1404
1405   if (task && task->type != SILC_TASK_EVENT) {
1406     silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1407     return FALSE;
1408   }
1409
1410   /* Get parent scheduler */
1411   parent = silc_schedule_get_parent(schedule);
1412
1413   SILC_SCHEDULE_LOCK(parent);
1414
1415   if (!task) {
1416     /* Get the event task */
1417     if (!silc_hash_table_find(parent->events, (void *)event, NULL,
1418                               (void *)&task)) {
1419       SILC_SCHEDULE_UNLOCK(parent);
1420       return FALSE;
1421     }
1422   }
1423   etask = (SilcEventTask)task;
1424
1425   /* See if task is deleted */
1426   if (task->valid == FALSE) {
1427     SILC_SCHEDULE_UNLOCK(parent);
1428     silc_set_errno(SILC_ERR_NOT_VALID);
1429     return FALSE;
1430   }
1431
1432   SILC_LOG_DEBUG(("Disconnect callback %p with context %p from event '%s'",
1433                   callback, context, etask->event));
1434
1435   /* Disconnect */
1436   silc_list_start(etask->connections);
1437   while ((conn = silc_list_get(etask->connections))) {
1438     if (conn->callback == callback && conn->context == context) {
1439       silc_list_del(etask->connections, conn);
1440       silc_free(conn);
1441       SILC_SCHEDULE_UNLOCK(parent);
1442       return TRUE;
1443     }
1444   }
1445
1446   SILC_SCHEDULE_UNLOCK(parent);
1447   silc_set_errno(SILC_ERR_NOT_FOUND);
1448   return FALSE;
1449 }
1450
1451 /* Signal event */
1452
1453 SilcBool silc_schedule_event_signal(SilcSchedule schedule, const char *event,
1454                                     SilcTask task, ...)
1455 {
1456   SilcSchedule parent;
1457   SilcScheduleEventConnection conn;
1458   SilcEventTask etask;
1459   SilcBool stop;
1460   va_list ap, cp;
1461
1462   if (silc_unlikely(!schedule)) {
1463     schedule = silc_schedule_get_global();
1464     SILC_VERIFY(schedule);
1465     if (!schedule) {
1466       silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1467       return FALSE;
1468     }
1469   }
1470
1471   if (silc_unlikely(!event && !task)) {
1472     silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1473     return FALSE;
1474   }
1475
1476   if (silc_unlikely(task && task->type != SILC_TASK_EVENT)) {
1477     silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1478     return FALSE;
1479   }
1480
1481   /* Get parent scheduler */
1482   parent = silc_schedule_get_parent(schedule);
1483
1484   SILC_SCHEDULE_LOCK(parent);
1485
1486   if (!task) {
1487     /* Get the event task */
1488     if (!silc_hash_table_find(parent->events, (void *)event, NULL,
1489                               (void *)&task)) {
1490       SILC_SCHEDULE_UNLOCK(parent);
1491       return FALSE;
1492     }
1493   }
1494   etask = (SilcEventTask)task;
1495
1496   /* See if task is deleted */
1497   if (task->valid == FALSE) {
1498     SILC_SCHEDULE_UNLOCK(parent);
1499     silc_set_errno(SILC_ERR_NOT_VALID);
1500     return FALSE;
1501   }
1502
1503   SILC_LOG_DEBUG(("Signal event '%s'", etask->event));
1504
1505   va_start(ap, task);
1506
1507   /* Deliver the signal */
1508   silc_list_start(etask->connections);
1509   while ((conn = silc_list_get(etask->connections))) {
1510     SILC_SCHEDULE_UNLOCK(parent);
1511
1512     silc_va_copy(cp, ap);
1513     stop = conn->callback(conn->schedule, conn->schedule->app_context,
1514                           task, conn->context, cp);
1515     va_end(cp);
1516
1517     SILC_SCHEDULE_LOCK(parent);
1518
1519     /* Stop signal if wanted or if the task was deleted */
1520     if (!stop || !task->valid)
1521       break;
1522   }
1523
1524   va_end(ap);
1525
1526   SILC_SCHEDULE_UNLOCK(parent);
1527
1528   return TRUE;
1529 }
1530
1531 /* Delete event */
1532
1533 SilcBool silc_schedule_task_del_event(SilcSchedule schedule, const char *event)
1534 {
1535   SilcSchedule parent;
1536   SilcTask task;
1537
1538   if (!schedule) {
1539     schedule = silc_schedule_get_global();
1540     SILC_VERIFY(schedule);
1541     if (!schedule) {
1542       silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1543       return FALSE;
1544     }
1545   }
1546
1547   if (!event) {
1548     silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1549     return FALSE;
1550   }
1551
1552   /* Get parent scheduler */
1553   parent = silc_schedule_get_parent(schedule);
1554
1555   SILC_SCHEDULE_LOCK(parent);
1556
1557   /* Get the event task */
1558   if (!silc_hash_table_find(parent->events, (void *)event, NULL,
1559                             (void *)&task)) {
1560     SILC_SCHEDULE_UNLOCK(parent);
1561     return FALSE;
1562   }
1563
1564   /* See if already deleted */
1565   if (task->valid == FALSE)
1566     return TRUE;
1567
1568   SILC_LOG_DEBUG(("Delete event '%s'", ((SilcEventTask)task)->event));
1569
1570   SILC_SCHEDULE_UNLOCK(parent);
1571
1572   silc_schedule_task_del(parent, task);
1573
1574   return TRUE;
1575 }
1576
1577 /* Timeout to remove deleted event task */
1578
1579 SILC_TASK_CALLBACK(silc_schedule_event_del_timeout)
1580 {
1581   SILC_SCHEDULE_LOCK(schedule);
1582   silc_schedule_task_remove(schedule, context);
1583   SILC_SCHEDULE_UNLOCK(schedule);
1584 }