5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1998 - 2007 Pekka Riikonen
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.
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.
23 /************************** Types and definitions ***************************/
25 /* Connected event context */
26 typedef struct SilcScheduleEventConnectionStruct {
27 SilcSchedule schedule;
28 SilcTaskEventCallback callback;
30 struct SilcScheduleEventConnectionStruct *next;
31 } *SilcScheduleEventConnection;
33 /* Platform specific implementation */
34 extern const SilcScheduleOps schedule_ops;
36 static void silc_schedule_task_remove(SilcSchedule schedule, SilcTask task);
37 static void silc_schedule_dispatch_fd(SilcSchedule schedule);
38 static void silc_schedule_dispatch_timeout(SilcSchedule schedule,
39 SilcBool dispatch_all);
40 SILC_TASK_CALLBACK(silc_schedule_event_del_timeout);
42 /************************ Static utility functions **************************/
44 /* Fd task hash table destructor */
46 static void silc_schedule_fd_destructor(void *key, void *context,
52 /* Executes file descriptor tasks. Invalid tasks are removed here. */
54 static void silc_schedule_dispatch_fd(SilcSchedule schedule)
59 /* The dispatch list includes only valid tasks, and tasks that have
60 something to dispatch. Dispatching is atomic; no matter if another
61 thread invalidates a task when we unlock, we dispatch to completion. */
62 SILC_SCHEDULE_UNLOCK(schedule);
63 silc_list_start(schedule->fd_dispatch);
64 while ((task = silc_list_get(schedule->fd_dispatch))) {
67 /* Is the task ready for reading */
68 if (task->revents & SILC_TASK_READ)
69 t->callback(schedule, schedule->app_context, SILC_TASK_READ,
70 task->fd, t->context);
72 /* Is the task ready for writing */
73 if (t->valid && task->revents & SILC_TASK_WRITE)
74 t->callback(schedule, schedule->app_context, SILC_TASK_WRITE,
75 task->fd, t->context);
77 SILC_SCHEDULE_LOCK(schedule);
79 /* Remove invalidated tasks */
80 silc_list_start(schedule->fd_dispatch);
81 while ((task = silc_list_get(schedule->fd_dispatch)))
82 if (silc_unlikely(!task->header.valid))
83 silc_schedule_task_remove(schedule, (SilcTask)task);
86 /* Executes all tasks whose timeout has expired. The task is removed from
87 the task queue after the callback function has returned. Also, invalid
88 tasks are removed here. */
90 static void silc_schedule_dispatch_timeout(SilcSchedule schedule,
91 SilcBool dispatch_all)
95 struct timeval curtime;
98 SILC_LOG_DEBUG(("Running timeout tasks"));
100 silc_gettimeofday(&curtime);
102 /* First task in the task queue has always the earliest timeout. */
103 silc_list_start(schedule->timeout_queue);
104 task = silc_list_get(schedule->timeout_queue);
105 if (silc_unlikely(!task))
110 /* Remove invalid task */
111 if (silc_unlikely(!t->valid)) {
112 silc_schedule_task_remove(schedule, t);
116 /* Execute the task if the timeout has expired */
117 if (silc_compare_timeval(&task->timeout, &curtime) > 0 && !dispatch_all)
121 SILC_SCHEDULE_UNLOCK(schedule);
122 t->callback(schedule, schedule->app_context, SILC_TASK_EXPIRE, 0,
124 SILC_SCHEDULE_LOCK(schedule);
126 /* Remove the expired task */
127 silc_schedule_task_remove(schedule, t);
129 /* Balance when we have lots of small timeouts */
130 if (silc_unlikely((++count) > 40))
132 } while (silc_likely((task = silc_list_get(schedule->timeout_queue))));
135 /* Calculates next timeout. This is the timeout value when at earliest some
136 of the timeout tasks expire. If this is in the past, they will be
139 static void silc_schedule_select_timeout(SilcSchedule schedule)
142 SilcTaskTimeout task;
143 struct timeval curtime;
144 SilcBool dispatch = TRUE;
146 /* Get the current time */
147 silc_gettimeofday(&curtime);
148 schedule->has_timeout = FALSE;
150 /* First task in the task queue has always the earliest timeout. */
151 silc_list_start(schedule->timeout_queue);
152 task = silc_list_get(schedule->timeout_queue);
153 if (silc_unlikely(!task))
158 /* Remove invalid task */
159 if (silc_unlikely(!t->valid)) {
160 silc_schedule_task_remove(schedule, t);
164 /* If the timeout is in past, we will run the task and all other
165 timeout tasks from the past. */
166 if (silc_compare_timeval(&task->timeout, &curtime) <= 0 && dispatch) {
167 silc_schedule_dispatch_timeout(schedule, FALSE);
168 if (silc_unlikely(!schedule->valid))
171 /* Start selecting new timeout again after dispatch */
172 silc_list_start(schedule->timeout_queue);
177 /* Calculate the next timeout */
178 curtime.tv_sec = task->timeout.tv_sec - curtime.tv_sec;
179 curtime.tv_usec = task->timeout.tv_usec - curtime.tv_usec;
180 if (curtime.tv_sec < 0)
183 /* We wouldn't want to go under zero, check for it. */
184 if (curtime.tv_usec < 0) {
186 if (curtime.tv_sec < 0)
188 curtime.tv_usec += 1000000L;
191 } while ((task = silc_list_get(schedule->timeout_queue)));
193 /* Save the timeout */
195 schedule->timeout = curtime;
196 schedule->has_timeout = TRUE;
197 SILC_LOG_DEBUG(("timeout: sec=%d, usec=%d", schedule->timeout.tv_sec,
198 schedule->timeout.tv_usec));
202 /* Removes task from the scheduler. This must be called with scheduler
205 static void silc_schedule_task_remove(SilcSchedule schedule, SilcTask task)
209 if (silc_unlikely(task == SILC_ALL_TASKS)) {
212 SilcHashTableList htl;
215 /* Delete from fd queue */
216 silc_hash_table_list(schedule->fd_queue, &htl);
217 while (silc_hash_table_get(&htl, &fd, (void *)&task))
218 silc_hash_table_del(schedule->fd_queue, fd);
219 silc_hash_table_list_reset(&htl);
221 /* Delete from timeout queue */
222 silc_list_start(schedule->timeout_queue);
223 while ((task = silc_list_get(schedule->timeout_queue))) {
224 silc_list_del(schedule->timeout_queue, task);
228 /* Delete even tasks */
229 parent = silc_schedule_get_parent(schedule);
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);
236 silc_hash_table_list_reset(&htl);
240 switch (task->type) {
243 /* Delete from fd queue */
244 SilcTaskFd ftask = (SilcTaskFd)task;
245 silc_hash_table_del(schedule->fd_queue, SILC_32_TO_PTR(ftask->fd));
249 case SILC_TASK_TIMEOUT:
251 /* Delete from timeout queue */
252 silc_list_del(schedule->timeout_queue, task);
254 /* Put to free list */
255 silc_list_add(schedule->free_tasks, task);
259 case SILC_TASK_EVENT:
261 SilcEventTask etask = (SilcEventTask)task;
262 SilcScheduleEventConnection conn;
264 parent = silc_schedule_get_parent(schedule);
267 silc_hash_table_del_by_context(parent->events, etask->event, etask);
269 /* Remove all connections */
270 silc_list_start(etask->connections);
271 while ((conn = silc_list_get(etask->connections)))
274 silc_free(etask->event);
284 /* Timeout freelist garbage collection */
286 SILC_TASK_CALLBACK(silc_schedule_timeout_gc)
291 if (!schedule->valid)
294 SILC_LOG_DEBUG(("Timeout freelist garbage collection"));
296 SILC_SCHEDULE_LOCK(schedule);
298 if (silc_list_count(schedule->free_tasks) <= 10) {
299 SILC_SCHEDULE_UNLOCK(schedule);
300 silc_schedule_task_add_timeout(schedule, silc_schedule_timeout_gc,
304 if (silc_list_count(schedule->timeout_queue) >
305 silc_list_count(schedule->free_tasks)) {
306 SILC_SCHEDULE_UNLOCK(schedule);
307 silc_schedule_task_add_timeout(schedule, silc_schedule_timeout_gc,
312 c = silc_list_count(schedule->free_tasks) / 2;
313 if (c > silc_list_count(schedule->timeout_queue))
314 c = (silc_list_count(schedule->free_tasks) -
315 silc_list_count(schedule->timeout_queue));
316 if (silc_list_count(schedule->free_tasks) - c < 10)
317 c -= (10 - (silc_list_count(schedule->free_tasks) - c));
319 SILC_LOG_DEBUG(("Freeing %d unused tasks, leaving %d", c,
320 silc_list_count(schedule->free_tasks) - c));
322 silc_list_start(schedule->free_tasks);
323 while ((t = silc_list_get(schedule->free_tasks)) && c-- > 0) {
324 silc_list_del(schedule->free_tasks, t);
327 silc_list_start(schedule->free_tasks);
329 SILC_SCHEDULE_UNLOCK(schedule);
331 silc_schedule_task_add_timeout(schedule, silc_schedule_timeout_gc,
335 #ifdef SILC_DIST_INPLACE
336 /* Print schedule statistics to stdout */
338 void silc_schedule_stats(SilcSchedule schedule)
341 fprintf(stdout, "Schedule %p statistics:\n\n", schedule);
342 fprintf(stdout, "Num FD tasks : %d (%lu bytes allocated)\n",
343 silc_hash_table_count(schedule->fd_queue),
344 sizeof(*ftask) * silc_hash_table_count(schedule->fd_queue));
345 fprintf(stdout, "Num Timeout tasks : %d (%lu bytes allocated)\n",
346 silc_list_count(schedule->timeout_queue),
347 sizeof(struct SilcTaskTimeoutStruct) *
348 silc_list_count(schedule->timeout_queue));
349 fprintf(stdout, "Num Timeout freelist : %d (%lu bytes allocated)\n",
350 silc_list_count(schedule->free_tasks),
351 sizeof(struct SilcTaskTimeoutStruct) *
352 silc_list_count(schedule->free_tasks));
354 #endif /* SILC_DIST_INPLACE */
356 /****************************** Public API **********************************/
358 /* Initializes the scheduler. This returns the scheduler context that
359 is given as arugment usually to all silc_schedule_* functions.
360 The `max_tasks' indicates the number of maximum tasks that the
361 scheduler can handle. The `app_context' is application specific
362 context that is delivered to task callbacks. */
364 SilcSchedule silc_schedule_init(int max_tasks, void *app_context,
365 SilcStack stack, SilcSchedule parent)
367 SilcSchedule schedule;
369 /* Initialize Tls, in case it hasn't been done yet */
370 silc_thread_tls_init();
372 stack = silc_stack_alloc(0, stack);
376 /* Allocate scheduler from the stack */
377 schedule = silc_scalloc(stack, 1, sizeof(*schedule));
381 SILC_LOG_DEBUG(("Initializing scheduler %p", schedule));
383 /* Allocate Fd task hash table dynamically */
385 silc_hash_table_alloc(NULL, 0, silc_hash_uint, NULL, NULL, NULL,
386 silc_schedule_fd_destructor, NULL, TRUE);
387 if (!schedule->fd_queue) {
388 silc_stack_free(stack);
392 silc_list_init(schedule->timeout_queue, struct SilcTaskStruct, next);
393 silc_list_init(schedule->free_tasks, struct SilcTaskStruct, next);
396 if (parent && parent->parent)
397 parent = parent->parent;
399 schedule->stack = stack;
400 schedule->app_context = app_context;
401 schedule->valid = TRUE;
402 schedule->max_tasks = max_tasks;
403 schedule->parent = parent;
405 /* Allocate scheduler lock */
406 silc_mutex_alloc(&schedule->lock);
408 /* Initialize the platform specific scheduler. */
409 schedule->internal = schedule_ops.init(schedule, app_context);
410 if (!schedule->internal) {
411 silc_hash_table_free(schedule->fd_queue);
412 silc_mutex_free(schedule->lock);
413 silc_stack_free(stack);
417 /* Timeout freelist garbage collection */
418 silc_schedule_task_add_timeout(schedule, silc_schedule_timeout_gc,
424 /* Uninitializes the schedule. This is called when the program is ready
425 to end. This removes all tasks and task queues. Returns FALSE if the
426 scheduler could not be uninitialized. This happens when the scheduler
427 is still valid and silc_schedule_stop has not been called. */
429 SilcBool silc_schedule_uninit(SilcSchedule schedule)
433 SILC_VERIFY(schedule);
435 SILC_LOG_DEBUG(("Uninitializing scheduler %p", schedule));
437 if (schedule->valid == TRUE)
440 /* Dispatch all timeouts before going away */
441 SILC_SCHEDULE_LOCK(schedule);
442 silc_schedule_dispatch_timeout(schedule, TRUE);
443 SILC_SCHEDULE_UNLOCK(schedule);
445 /* Deliver signals before going away */
446 if (schedule->signal_tasks) {
447 schedule_ops.signals_call(schedule, schedule->internal);
448 schedule->signal_tasks = FALSE;
451 /* Unregister all tasks */
452 silc_schedule_task_del(schedule, SILC_ALL_TASKS);
453 silc_schedule_task_remove(schedule, SILC_ALL_TASKS);
455 /* Delete timeout task freelist */
456 silc_list_start(schedule->free_tasks);
457 while ((task = silc_list_get(schedule->free_tasks)))
460 /* Unregister all task queues */
461 silc_hash_table_free(schedule->fd_queue);
463 /* Uninit the platform specific scheduler. */
464 schedule_ops.uninit(schedule, schedule->internal);
466 silc_mutex_free(schedule->lock);
467 silc_stack_free(schedule->stack);
472 /* Stops the schedule even if it is not supposed to be stopped yet.
473 After calling this, one should call silc_schedule_uninit (after the
474 silc_schedule has returned). */
476 void silc_schedule_stop(SilcSchedule schedule)
478 SILC_LOG_DEBUG(("Stopping scheduler"));
479 SILC_VERIFY(schedule);
480 SILC_SCHEDULE_LOCK(schedule);
481 schedule->valid = FALSE;
482 SILC_SCHEDULE_UNLOCK(schedule);
485 /* Runs the scheduler once and then returns. Must be called locked. */
487 static SilcBool silc_schedule_iterate(SilcSchedule schedule, int timeout_usecs)
489 struct timeval timeout;
493 SILC_LOG_DEBUG(("In scheduler loop"));
495 /* Deliver signals if any has been set to be called */
496 if (silc_unlikely(schedule->signal_tasks)) {
497 SILC_SCHEDULE_UNLOCK(schedule);
498 schedule_ops.signals_call(schedule, schedule->internal);
499 schedule->signal_tasks = FALSE;
500 SILC_SCHEDULE_LOCK(schedule);
503 /* Check if scheduler is valid */
504 if (silc_unlikely(schedule->valid == FALSE)) {
505 SILC_LOG_DEBUG(("Scheduler not valid anymore, exiting"));
509 /* Calculate next timeout for silc_select(). This is the timeout value
510 when at earliest some of the timeout tasks expire. This may dispatch
511 already expired timeouts. */
512 silc_schedule_select_timeout(schedule);
514 /* Check if scheduler is valid */
515 if (silc_unlikely(schedule->valid == FALSE)) {
516 SILC_LOG_DEBUG(("Scheduler not valid anymore, exiting"));
520 if (timeout_usecs >= 0) {
522 timeout.tv_usec = timeout_usecs;
523 schedule->timeout = timeout;
524 schedule->has_timeout = TRUE;
527 /* This is the main silc_select(). The program blocks here until some
528 of the selected file descriptors change status or the selected
530 SILC_LOG_DEBUG(("Select"));
531 ret = schedule_ops.schedule(schedule, schedule->internal);
533 if (silc_likely(ret == 0)) {
535 SILC_LOG_DEBUG(("Running timeout tasks"));
536 if (silc_likely(silc_list_count(schedule->timeout_queue)))
537 silc_schedule_dispatch_timeout(schedule, FALSE);
540 } else if (silc_likely(ret > 0)) {
541 /* There is some data available now */
542 SILC_LOG_DEBUG(("Running fd tasks"));
543 silc_schedule_dispatch_fd(schedule);
545 /* If timeout was very short, dispatch also timeout tasks */
546 if (schedule->has_timeout && schedule->timeout.tv_sec == 0 &&
547 schedule->timeout.tv_usec < 50000)
548 silc_schedule_dispatch_timeout(schedule, FALSE);
552 /* Error or special case handling */
558 SILC_LOG_ERROR(("Error in select()/poll(): %s", strerror(errno)));
561 } while (timeout_usecs == -1);
566 /* Runs the scheduler once and then returns. */
568 SilcBool silc_schedule_one(SilcSchedule schedule, int timeout_usecs)
571 SILC_SCHEDULE_LOCK(schedule);
572 ret = silc_schedule_iterate(schedule, timeout_usecs);
573 SILC_SCHEDULE_UNLOCK(schedule);
577 /* Runs the scheduler and blocks here. When this returns the scheduler
581 void silc_schedule(SilcSchedule schedule)
583 SILC_LOG_DEBUG(("Running scheduler"));
585 /* Start the scheduler loop */
586 SILC_SCHEDULE_LOCK(schedule);
587 silc_schedule_iterate(schedule, -1);
588 SILC_SCHEDULE_UNLOCK(schedule);
590 #endif /* !SILC_SYMBIAN */
592 /* Wakes up the scheduler. This is used only in multi-threaded
593 environments where threads may add new tasks or remove old tasks
594 from task queues. This is called to wake up the scheduler in the
595 main thread so that it detects the changes in the task queues.
596 If threads support is not compiled in this function has no effect.
597 Implementation of this function is platform specific. */
599 void silc_schedule_wakeup(SilcSchedule schedule)
602 SILC_LOG_DEBUG(("Wakeup scheduler"));
603 SILC_SCHEDULE_LOCK(schedule);
604 schedule_ops.wakeup(schedule, schedule->internal);
605 SILC_SCHEDULE_UNLOCK(schedule);
609 /* Returns parent scheduler */
611 SilcSchedule silc_schedule_get_parent(SilcSchedule schedule)
613 return schedule->parent ? schedule->parent : schedule;
616 /* Returns the application specific context that was saved into the
617 scheduler in silc_schedule_init function. The context is also
618 returned to application in task callback functions, but this function
619 may be used to get it as well if needed. */
621 void *silc_schedule_get_context(SilcSchedule schedule)
623 return schedule->app_context;
626 /* Return the stack of the scheduler */
628 SilcStack silc_schedule_get_stack(SilcSchedule schedule)
630 return schedule->stack;
633 /* Set notify callback */
635 void silc_schedule_set_notify(SilcSchedule schedule,
636 SilcTaskNotifyCb notify, void *context)
638 schedule->notify = notify;
639 schedule->notify_context = context;
642 /* Set global scheduler */
644 void silc_schedule_set_global(SilcSchedule schedule)
646 SilcTls tls = silc_thread_get_tls();
649 /* Try to initialize Tls */
650 tls = silc_thread_tls_init();
656 SILC_LOG_DEBUG(("Setting global scheduler %p", schedule));
658 tls->schedule = schedule;
661 /* Return global scheduler */
663 SilcSchedule silc_schedule_get_global(void)
665 SilcTls tls = silc_thread_get_tls();
670 SILC_LOG_DEBUG(("Return global scheduler %p", tls->schedule));
672 return tls->schedule;
675 /* Add new task to the scheduler */
677 SilcTask silc_schedule_task_add(SilcSchedule schedule, SilcUInt32 fd,
678 SilcTaskCallback callback, void *context,
679 long seconds, long useconds,
682 SilcTask task = NULL;
685 schedule = silc_schedule_get_global();
686 SILC_VERIFY(schedule);
688 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
693 if (silc_unlikely(!schedule->valid)) {
694 silc_set_errno(SILC_ERR_NOT_VALID);
698 SILC_SCHEDULE_LOCK(schedule);
700 if (silc_likely(type == SILC_TASK_TIMEOUT)) {
701 SilcTaskTimeout tmp, prev, ttask;
704 silc_list_start(schedule->free_tasks);
705 ttask = silc_list_get(schedule->free_tasks);
706 if (silc_unlikely(!ttask)) {
707 ttask = silc_calloc(1, sizeof(*ttask));
708 if (silc_unlikely(!ttask))
711 silc_list_del(schedule->free_tasks, ttask);
713 ttask->header.type = 1;
714 ttask->header.callback = callback;
715 ttask->header.context = context;
716 ttask->header.valid = TRUE;
719 silc_gettimeofday(&ttask->timeout);
720 if ((seconds + useconds) > 0) {
721 ttask->timeout.tv_sec += seconds + (useconds / 1000000L);
722 ttask->timeout.tv_usec += (useconds % 1000000L);
723 if (ttask->timeout.tv_usec >= 1000000L) {
724 ttask->timeout.tv_sec += 1;
725 ttask->timeout.tv_usec -= 1000000L;
729 SILC_LOG_DEBUG(("New timeout task %p: sec=%d, usec=%d", ttask,
732 /* Add task to correct spot so that the first task in the list has
733 the earliest timeout. */
734 list = schedule->timeout_queue;
735 silc_list_start(list);
737 while ((tmp = silc_list_get(list)) != SILC_LIST_END) {
738 /* If we have shorter timeout, we have found our spot */
739 if (silc_compare_timeval(&ttask->timeout, &tmp->timeout) < 0) {
740 silc_list_insert(schedule->timeout_queue, prev, ttask);
746 silc_list_add(schedule->timeout_queue, ttask);
748 task = (SilcTask)ttask;
750 /* Call notify callback */
751 if (schedule->notify)
752 schedule->notify(schedule, TRUE, task, FALSE, 0, 0, seconds, useconds,
753 schedule->notify_context);
755 } else if (silc_likely(type == SILC_TASK_FD)) {
758 /* Check if fd is already added */
759 if (silc_unlikely(silc_hash_table_find(schedule->fd_queue,
761 NULL, (void *)&task))) {
765 /* Remove invalid task. We must have unique fd key to hash table. */
766 silc_schedule_task_remove(schedule, task);
769 /* Check max tasks */
770 if (silc_unlikely(schedule->max_tasks > 0 &&
771 silc_hash_table_count(schedule->fd_queue) >=
772 schedule->max_tasks)) {
773 SILC_LOG_WARNING(("Scheduler task limit reached: cannot add new task"));
775 silc_set_errno(SILC_ERR_LIMIT);
779 ftask = silc_calloc(1, sizeof(*ftask));
780 if (silc_unlikely(!ftask)) {
785 SILC_LOG_DEBUG(("New fd task %p fd=%d", ftask, fd));
787 ftask->header.type = 0;
788 ftask->header.callback = callback;
789 ftask->header.context = context;
790 ftask->header.valid = TRUE;
791 ftask->events = SILC_TASK_READ;
794 /* Add task and schedule it */
795 if (!silc_hash_table_add(schedule->fd_queue, SILC_32_TO_PTR(fd), ftask)) {
800 if (!schedule_ops.schedule_fd(schedule, schedule->internal,
801 ftask, ftask->events)) {
802 silc_hash_table_del(schedule->fd_queue, SILC_32_TO_PTR(fd));
807 task = (SilcTask)ftask;
809 /* Call notify callback */
810 if (schedule->notify)
811 schedule->notify(schedule, TRUE, task, TRUE, ftask->fd,
812 SILC_TASK_READ, 0, 0, schedule->notify_context);
814 } else if (silc_unlikely(type == SILC_TASK_SIGNAL)) {
815 SILC_SCHEDULE_UNLOCK(schedule);
816 schedule_ops.signal_register(schedule, schedule->internal, fd,
822 SILC_SCHEDULE_UNLOCK(schedule);
825 /* On symbian we wakeup scheduler immediately after adding timeout task
826 in case the task is added outside the scheduler loop (in some active
828 if (task && task->type == 1)
829 silc_schedule_wakeup(schedule);
830 #endif /* SILC_SYMBIAN */
835 /* Invalidates task */
837 SilcBool silc_schedule_task_del(SilcSchedule schedule, SilcTask task)
842 schedule = silc_schedule_get_global();
843 SILC_VERIFY(schedule);
845 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
850 if (silc_unlikely(task == SILC_ALL_TASKS)) {
851 SilcHashTableList htl;
853 SILC_LOG_DEBUG(("Unregister all tasks"));
855 SILC_SCHEDULE_LOCK(schedule);
857 /* Delete from fd queue */
858 silc_hash_table_list(schedule->fd_queue, &htl);
859 while (silc_hash_table_get(&htl, NULL, (void *)&task)) {
862 /* Call notify callback */
863 if (schedule->notify)
864 schedule->notify(schedule, FALSE, task, TRUE,
865 ((SilcTaskFd)task)->fd, 0, 0, 0,
866 schedule->notify_context);
868 silc_hash_table_list_reset(&htl);
870 /* Delete from timeout queue */
871 silc_list_start(schedule->timeout_queue);
872 while ((task = (SilcTask)silc_list_get(schedule->timeout_queue))) {
875 /* Call notify callback */
876 if (schedule->notify)
877 schedule->notify(schedule, FALSE, task, FALSE, 0, 0, 0, 0,
878 schedule->notify_context);
881 /* Delete even tasks */
882 parent = silc_schedule_get_parent(schedule);
883 silc_hash_table_list(parent->events, &htl);
884 while (silc_hash_table_get(&htl, NULL, (void *)&task))
886 silc_hash_table_list_reset(&htl);
888 SILC_SCHEDULE_UNLOCK(schedule);
892 SILC_LOG_DEBUG(("Unregistering task %p, type %d", task, task->type));
893 SILC_SCHEDULE_LOCK(schedule);
896 /* Call notify callback */
897 if (schedule->notify && task->type != SILC_TASK_EVENT)
898 schedule->notify(schedule, FALSE, task, task->type == SILC_TASK_FD,
899 0, 0, 0, 0, schedule->notify_context);
900 SILC_SCHEDULE_UNLOCK(schedule);
902 if (task->type == SILC_TASK_EVENT) {
903 /* Schedule removal of deleted event task */
904 parent = silc_schedule_get_parent(schedule);
905 silc_schedule_task_add_timeout(parent, silc_schedule_event_del_timeout,
912 /* Invalidate task by fd */
914 SilcBool silc_schedule_task_del_by_fd(SilcSchedule schedule, SilcUInt32 fd)
916 SilcTask task = NULL;
917 SilcBool ret = FALSE;
919 SILC_LOG_DEBUG(("Unregister task by fd %d", fd));
922 schedule = silc_schedule_get_global();
923 SILC_VERIFY(schedule);
925 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
930 SILC_SCHEDULE_LOCK(schedule);
932 /* fd is unique, so there is only one task with this fd in the table */
933 if (silc_likely(silc_hash_table_find(schedule->fd_queue,
934 SILC_32_TO_PTR(fd), NULL,
936 SILC_LOG_DEBUG(("Deleting task %p", task));
939 /* Call notify callback */
940 if (schedule->notify)
941 schedule->notify(schedule, FALSE, task, TRUE, fd, 0, 0, 0,
942 schedule->notify_context);
946 SILC_SCHEDULE_UNLOCK(schedule);
948 /* If it is signal, remove it */
949 if (silc_unlikely(!task)) {
950 schedule_ops.signal_unregister(schedule, schedule->internal, fd);
955 silc_set_errno(SILC_ERR_NOT_FOUND);
960 /* Invalidate task by task callback. */
962 SilcBool silc_schedule_task_del_by_callback(SilcSchedule schedule,
963 SilcTaskCallback callback)
966 SilcHashTableList htl;
968 SilcBool ret = FALSE;
970 SILC_LOG_DEBUG(("Unregister task by callback"));
973 schedule = silc_schedule_get_global();
974 SILC_VERIFY(schedule);
976 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
981 SILC_SCHEDULE_LOCK(schedule);
983 /* Delete from fd queue */
984 silc_hash_table_list(schedule->fd_queue, &htl);
985 while (silc_hash_table_get(&htl, NULL, (void *)&task)) {
986 if (task->callback == callback) {
989 /* Call notify callback */
990 if (schedule->notify)
991 schedule->notify(schedule, FALSE, task, TRUE,
992 ((SilcTaskFd)task)->fd, 0, 0, 0,
993 schedule->notify_context);
997 silc_hash_table_list_reset(&htl);
999 /* Delete from timeout queue */
1000 list = schedule->timeout_queue;
1001 silc_list_start(list);
1002 while ((task = (SilcTask)silc_list_get(list))) {
1003 if (task->callback == callback) {
1004 task->valid = FALSE;
1006 /* Call notify callback */
1007 if (schedule->notify)
1008 schedule->notify(schedule, FALSE, task, FALSE, 0, 0, 0, 0,
1009 schedule->notify_context);
1014 SILC_SCHEDULE_UNLOCK(schedule);
1017 silc_set_errno(SILC_ERR_NOT_FOUND);
1022 /* Invalidate task by context. */
1024 SilcBool silc_schedule_task_del_by_context(SilcSchedule schedule,
1028 SilcHashTableList htl;
1030 SilcBool ret = FALSE;
1032 SILC_LOG_DEBUG(("Unregister task by context"));
1035 schedule = silc_schedule_get_global();
1036 SILC_VERIFY(schedule);
1038 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1043 SILC_SCHEDULE_LOCK(schedule);
1045 /* Delete from fd queue */
1046 silc_hash_table_list(schedule->fd_queue, &htl);
1047 while (silc_hash_table_get(&htl, NULL, (void *)&task)) {
1048 if (task->context == context) {
1049 task->valid = FALSE;
1051 /* Call notify callback */
1052 if (schedule->notify)
1053 schedule->notify(schedule, FALSE, task, TRUE,
1054 ((SilcTaskFd)task)->fd, 0, 0, 0,
1055 schedule->notify_context);
1059 silc_hash_table_list_reset(&htl);
1061 /* Delete from timeout queue */
1062 list = schedule->timeout_queue;
1063 silc_list_start(list);
1064 while ((task = (SilcTask)silc_list_get(list))) {
1065 if (task->context == context) {
1066 task->valid = FALSE;
1068 /* Call notify callback */
1069 if (schedule->notify)
1070 schedule->notify(schedule, FALSE, task, FALSE, 0, 0, 0, 0,
1071 schedule->notify_context);
1076 SILC_SCHEDULE_UNLOCK(schedule);
1079 silc_set_errno(SILC_ERR_NOT_FOUND);
1084 /* Invalidate task by all */
1086 SilcBool silc_schedule_task_del_by_all(SilcSchedule schedule, int fd,
1087 SilcTaskCallback callback,
1092 SilcBool ret = FALSE;
1094 SILC_LOG_DEBUG(("Unregister task by fd, callback and context"));
1096 /* For fd task, callback and context is irrelevant as fd is unique */
1098 return silc_schedule_task_del_by_fd(schedule, fd);
1101 schedule = silc_schedule_get_global();
1102 SILC_VERIFY(schedule);
1104 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1109 SILC_SCHEDULE_LOCK(schedule);
1111 /* Delete from timeout queue */
1112 list = schedule->timeout_queue;
1113 silc_list_start(list);
1114 while ((task = (SilcTask)silc_list_get(list))) {
1115 if (task->callback == callback && task->context == context) {
1116 task->valid = FALSE;
1118 /* Call notify callback */
1119 if (schedule->notify)
1120 schedule->notify(schedule, FALSE, task, FALSE, 0, 0, 0, 0,
1121 schedule->notify_context);
1126 SILC_SCHEDULE_UNLOCK(schedule);
1129 silc_set_errno(SILC_ERR_NOT_FOUND);
1134 /* Sets a file descriptor to be listened by scheduler. One can call this
1135 directly if wanted. This can be called multiple times for one file
1136 descriptor to set different iomasks. */
1138 SilcBool silc_schedule_set_listen_fd(SilcSchedule schedule, SilcUInt32 fd,
1139 SilcTaskEvent mask, SilcBool send_events)
1144 schedule = silc_schedule_get_global();
1145 SILC_VERIFY(schedule);
1147 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1152 if (silc_unlikely(!schedule->valid)) {
1153 silc_set_errno(SILC_ERR_NOT_VALID);
1157 SILC_SCHEDULE_LOCK(schedule);
1159 if (silc_hash_table_find(schedule->fd_queue, SILC_32_TO_PTR(fd),
1160 NULL, (void *)&task)) {
1161 if (!schedule_ops.schedule_fd(schedule, schedule->internal, task, mask)) {
1162 SILC_SCHEDULE_UNLOCK(schedule);
1165 task->events = mask;
1166 if (silc_unlikely(send_events) && mask) {
1167 task->revents = mask;
1168 silc_schedule_dispatch_fd(schedule);
1171 /* Call notify callback */
1172 if (schedule->notify)
1173 schedule->notify(schedule, TRUE, (SilcTask)task,
1174 TRUE, task->fd, mask, 0, 0,
1175 schedule->notify_context);
1178 SILC_SCHEDULE_UNLOCK(schedule);
1183 /* Returns the file descriptor's current requested event mask. */
1185 SilcTaskEvent silc_schedule_get_fd_events(SilcSchedule schedule,
1189 SilcTaskEvent event = 0;
1192 schedule = silc_schedule_get_global();
1193 SILC_VERIFY(schedule);
1195 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1200 if (silc_unlikely(!schedule->valid)) {
1201 silc_set_errno(SILC_ERR_NOT_VALID);
1205 SILC_SCHEDULE_LOCK(schedule);
1206 if (silc_hash_table_find(schedule->fd_queue, SILC_32_TO_PTR(fd),
1207 NULL, (void *)&task))
1208 event = task->events;
1209 SILC_SCHEDULE_UNLOCK(schedule);
1214 /* Removes a file descriptor from listen list. */
1216 void silc_schedule_unset_listen_fd(SilcSchedule schedule, SilcUInt32 fd)
1218 silc_schedule_set_listen_fd(schedule, fd, 0, FALSE);
1221 /*************************** Asynchronous Events ****************************/
1225 SilcTask silc_schedule_task_add_event(SilcSchedule schedule,
1226 const char *event, ...)
1229 SilcSchedule parent;
1232 schedule = silc_schedule_get_global();
1233 SILC_VERIFY(schedule);
1235 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1240 /* Get parent scheduler */
1241 parent = silc_schedule_get_parent(schedule);
1243 SILC_LOG_DEBUG(("Adding event '%s' to scheduler %p", event, parent));
1245 SILC_SCHEDULE_LOCK(parent);
1247 /* Create events hash table if not already done */
1248 if (!parent->events) {
1249 parent->events = silc_hash_table_alloc(NULL, 3,
1250 silc_hash_string, NULL,
1251 silc_hash_string_compare, NULL,
1253 if (!parent->events) {
1254 SILC_SCHEDULE_UNLOCK(parent);
1259 /* Check if this event is added already */
1260 if (silc_hash_table_find(parent->events, (void *)event, NULL, NULL)) {
1261 SILC_SCHEDULE_UNLOCK(parent);
1266 task = silc_calloc(1, sizeof(*task));
1268 SILC_SCHEDULE_UNLOCK(parent);
1272 task->header.type = SILC_TASK_EVENT;
1273 task->header.valid = TRUE;
1274 task->event = silc_strdup(event);
1276 SILC_SCHEDULE_UNLOCK(parent);
1280 silc_list_init(task->connections, struct SilcScheduleEventConnectionStruct,
1283 if (!silc_hash_table_add(parent->events, task->event, task)) {
1284 SILC_SCHEDULE_UNLOCK(parent);
1285 silc_free(task->event);
1290 SILC_SCHEDULE_UNLOCK(parent);
1292 return (SilcTask)task;
1295 /* Connect to event task */
1297 SilcBool silc_schedule_event_connect(SilcSchedule schedule,
1298 const char *event, SilcTask task,
1299 SilcTaskEventCallback callback,
1302 SilcSchedule parent;
1303 SilcScheduleEventConnection conn;
1304 SilcEventTask etask;
1307 schedule = silc_schedule_get_global();
1308 SILC_VERIFY(schedule);
1310 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1315 if (!event && !task) {
1316 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1320 if (task && task->type != SILC_TASK_EVENT) {
1321 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1325 /* Get parent scheduler */
1326 parent = silc_schedule_get_parent(schedule);
1328 SILC_SCHEDULE_LOCK(parent);
1331 /* Get the event task */
1332 if (!silc_hash_table_find(parent->events, (void *)event, NULL,
1334 SILC_SCHEDULE_UNLOCK(parent);
1338 etask = (SilcEventTask)task;
1340 /* See if task is deleted */
1341 if (task->valid == FALSE) {
1342 SILC_SCHEDULE_UNLOCK(parent);
1343 silc_set_errno(SILC_ERR_NOT_VALID);
1347 SILC_LOG_DEBUG(("Connect callback %p with context %p to event '%s'",
1348 callback, context, etask->event));
1350 /* See if already connected */
1351 silc_list_start(etask->connections);
1352 while ((conn = silc_list_get(etask->connections))) {
1353 if (conn->callback == callback && conn->context == context) {
1354 SILC_SCHEDULE_UNLOCK(parent);
1355 silc_set_errno(SILC_ERR_ALREADY_EXISTS);
1360 conn = silc_calloc(1, sizeof(*conn));
1362 SILC_SCHEDULE_UNLOCK(parent);
1366 /* Connect to the event */
1367 conn->schedule = schedule;
1368 conn->callback = callback;
1369 conn->context = context;
1370 silc_list_add(etask->connections, conn);
1372 SILC_SCHEDULE_UNLOCK(parent);
1377 /* Disconnect from event */
1379 SilcBool silc_schedule_event_disconnect(SilcSchedule schedule,
1380 const char *event, SilcTask task,
1381 SilcTaskEventCallback callback,
1384 SilcSchedule parent;
1385 SilcScheduleEventConnection conn;
1386 SilcEventTask etask;
1389 schedule = silc_schedule_get_global();
1390 SILC_VERIFY(schedule);
1392 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1397 if (!event && !task) {
1398 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1402 if (task && task->type != SILC_TASK_EVENT) {
1403 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1407 /* Get parent scheduler */
1408 parent = silc_schedule_get_parent(schedule);
1410 SILC_SCHEDULE_LOCK(parent);
1413 /* Get the event task */
1414 if (!silc_hash_table_find(parent->events, (void *)event, NULL,
1416 SILC_SCHEDULE_UNLOCK(parent);
1420 etask = (SilcEventTask)task;
1422 /* See if task is deleted */
1423 if (task->valid == FALSE) {
1424 SILC_SCHEDULE_UNLOCK(parent);
1425 silc_set_errno(SILC_ERR_NOT_VALID);
1429 SILC_LOG_DEBUG(("Disconnect callback %p with context %p from event '%s'",
1430 callback, context, etask->event));
1433 silc_list_start(etask->connections);
1434 while ((conn = silc_list_get(etask->connections))) {
1435 if (conn->callback == callback && conn->context == context) {
1436 silc_list_del(etask->connections, conn);
1438 SILC_SCHEDULE_UNLOCK(parent);
1443 SILC_SCHEDULE_UNLOCK(parent);
1444 silc_set_errno(SILC_ERR_NOT_FOUND);
1450 SilcBool silc_schedule_event_signal(SilcSchedule schedule, const char *event,
1453 SilcSchedule parent;
1454 SilcScheduleEventConnection conn;
1455 SilcEventTask etask;
1459 if (silc_unlikely(!schedule)) {
1460 schedule = silc_schedule_get_global();
1461 SILC_VERIFY(schedule);
1463 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1468 if (silc_unlikely(!event && !task)) {
1469 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1473 if (silc_unlikely(task && task->type != SILC_TASK_EVENT)) {
1474 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1478 /* Get parent scheduler */
1479 parent = silc_schedule_get_parent(schedule);
1481 SILC_SCHEDULE_LOCK(parent);
1484 /* Get the event task */
1485 if (!silc_hash_table_find(parent->events, (void *)event, NULL,
1487 SILC_SCHEDULE_UNLOCK(parent);
1491 etask = (SilcEventTask)task;
1493 /* See if task is deleted */
1494 if (task->valid == FALSE) {
1495 SILC_SCHEDULE_UNLOCK(parent);
1496 silc_set_errno(SILC_ERR_NOT_VALID);
1500 SILC_LOG_DEBUG(("Signal event '%s'", etask->event));
1504 /* Deliver the signal */
1505 silc_list_start(etask->connections);
1506 while ((conn = silc_list_get(etask->connections))) {
1507 SILC_SCHEDULE_UNLOCK(parent);
1509 silc_va_copy(cp, ap);
1510 stop = conn->callback(conn->schedule, conn->schedule->app_context,
1511 task, conn->context, cp);
1514 SILC_SCHEDULE_LOCK(parent);
1516 /* Stop signal if wanted or if the task was deleted */
1517 if (!stop || !task->valid)
1523 SILC_SCHEDULE_UNLOCK(parent);
1530 SilcBool silc_schedule_task_del_event(SilcSchedule schedule, const char *event)
1532 SilcSchedule parent;
1536 schedule = silc_schedule_get_global();
1537 SILC_VERIFY(schedule);
1539 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1545 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1549 /* Get parent scheduler */
1550 parent = silc_schedule_get_parent(schedule);
1552 SILC_SCHEDULE_LOCK(parent);
1554 /* Get the event task */
1555 if (!silc_hash_table_find(parent->events, (void *)event, NULL,
1557 SILC_SCHEDULE_UNLOCK(parent);
1561 /* See if already deleted */
1562 if (task->valid == FALSE)
1565 SILC_LOG_DEBUG(("Delete event '%s'", ((SilcEventTask)task)->event));
1567 SILC_SCHEDULE_UNLOCK(parent);
1569 silc_schedule_task_del(parent, task);
1574 /* Timeout to remove deleted event task */
1576 SILC_TASK_CALLBACK(silc_schedule_event_del_timeout)
1578 SILC_SCHEDULE_LOCK(schedule);
1579 silc_schedule_task_remove(schedule, context);
1580 SILC_SCHEDULE_UNLOCK(schedule);