5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1998 - 2008 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.
20 #include "silcruntime.h"
22 /************************** Types and definitions ***************************/
24 /* Connected event context */
25 typedef struct SilcScheduleEventConnectionStruct {
26 SilcSchedule schedule;
27 SilcTaskEventCallback callback;
29 struct SilcScheduleEventConnectionStruct *next;
30 } *SilcScheduleEventConnection;
32 /* Platform specific implementation */
33 extern const SilcScheduleOps schedule_ops;
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);
41 /************************ Static utility functions **************************/
43 /* Fd task hash table destructor */
45 static void silc_schedule_fd_destructor(void *key, void *context,
51 /* Executes file descriptor tasks. Invalid tasks are removed here. */
53 static void silc_schedule_dispatch_fd(SilcSchedule schedule)
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))) {
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);
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);
76 SILC_SCHEDULE_LOCK(schedule);
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);
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. */
89 static void silc_schedule_dispatch_timeout(SilcSchedule schedule,
90 SilcBool dispatch_all)
94 struct timeval curtime;
97 SILC_LOG_DEBUG(("Running timeout tasks"));
99 silc_gettimeofday(&curtime);
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))
109 /* Remove invalid task */
110 if (silc_unlikely(!t->valid)) {
111 silc_schedule_task_remove(schedule, t);
115 /* Execute the task if the timeout has expired */
116 if (silc_compare_timeval(&task->timeout, &curtime) > 0 && !dispatch_all)
120 SILC_SCHEDULE_UNLOCK(schedule);
121 t->callback(schedule, schedule->app_context, SILC_TASK_EXPIRE, 0,
123 SILC_SCHEDULE_LOCK(schedule);
125 /* Remove the expired task */
126 silc_schedule_task_remove(schedule, t);
128 /* Balance when we have lots of small timeouts */
129 if (silc_unlikely((++count) > 40))
131 } while (silc_likely((task = silc_list_get(schedule->timeout_queue))));
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
138 static void silc_schedule_select_timeout(SilcSchedule schedule)
141 SilcTaskTimeout task;
142 struct timeval curtime;
143 SilcBool dispatch = TRUE;
145 /* Get the current time */
146 silc_gettimeofday(&curtime);
147 schedule->has_timeout = FALSE;
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))
157 /* Remove invalid task */
158 if (silc_unlikely(!t->valid)) {
159 silc_schedule_task_remove(schedule, t);
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))
170 /* Start selecting new timeout again after dispatch */
171 silc_list_start(schedule->timeout_queue);
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)
182 /* We wouldn't want to go under zero, check for it. */
183 if (curtime.tv_usec < 0) {
185 if (curtime.tv_sec < 0)
187 curtime.tv_usec += 1000000L;
190 } while ((task = silc_list_get(schedule->timeout_queue)));
192 /* Save the timeout */
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));
201 /* Removes task from the scheduler. This must be called with scheduler
204 static void silc_schedule_task_remove(SilcSchedule schedule, SilcTask task)
208 if (silc_unlikely(task == SILC_ALL_TASKS)) {
211 SilcHashTableList htl;
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);
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);
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);
236 silc_hash_table_list_reset(&htl);
241 switch (task->type) {
244 /* Delete from fd queue */
245 SilcTaskFd ftask = (SilcTaskFd)task;
246 silc_hash_table_del(schedule->fd_queue, SILC_32_TO_PTR(ftask->fd));
250 case SILC_TASK_TIMEOUT:
252 /* Delete from timeout queue */
253 silc_list_del(schedule->timeout_queue, task);
255 /* Put to free list */
256 silc_list_add(schedule->free_tasks, task);
260 case SILC_TASK_EVENT:
262 SilcEventTask etask = (SilcEventTask)task;
263 SilcScheduleEventConnection conn;
265 parent = silc_schedule_get_parent(schedule);
268 silc_hash_table_del_by_context(parent->events, etask->event, etask);
270 /* Remove all connections */
271 silc_list_start(etask->connections);
272 while ((conn = silc_list_get(etask->connections)))
275 silc_free(etask->event);
285 /* Timeout freelist garbage collection */
287 SILC_TASK_CALLBACK(silc_schedule_timeout_gc)
292 if (!schedule->valid)
295 SILC_LOG_DEBUG(("Timeout freelist garbage collection"));
297 SILC_SCHEDULE_LOCK(schedule);
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,
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,
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));
320 SILC_LOG_DEBUG(("Freeing %d unused tasks, leaving %d", c,
321 silc_list_count(schedule->free_tasks) - c));
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);
328 silc_list_start(schedule->free_tasks);
330 SILC_SCHEDULE_UNLOCK(schedule);
332 silc_schedule_task_add_timeout(schedule, silc_schedule_timeout_gc,
336 #ifdef SILC_DIST_INPLACE
337 /* Print schedule statistics to stdout */
339 void silc_schedule_stats(SilcSchedule schedule)
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));
355 #endif /* SILC_DIST_INPLACE */
357 /****************************** Public API **********************************/
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. */
365 SilcSchedule silc_schedule_init(int max_tasks, void *app_context,
366 SilcStack stack, SilcSchedule parent)
368 SilcSchedule schedule;
370 /* Initialize Tls, in case it hasn't been done yet */
371 silc_thread_tls_init();
373 stack = silc_stack_alloc(0, stack);
377 /* Allocate scheduler from the stack */
378 schedule = silc_scalloc(stack, 1, sizeof(*schedule));
382 SILC_LOG_DEBUG(("Initializing scheduler %p", schedule));
384 /* Allocate Fd task hash table dynamically */
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);
393 silc_list_init(schedule->timeout_queue, struct SilcTaskStruct, next);
394 silc_list_init(schedule->free_tasks, struct SilcTaskStruct, next);
397 if (parent && parent->parent)
398 parent = parent->parent;
400 schedule->stack = stack;
401 schedule->app_context = app_context;
402 schedule->valid = TRUE;
403 schedule->max_tasks = max_tasks;
404 schedule->parent = parent;
406 /* Allocate scheduler lock */
407 silc_mutex_alloc(&schedule->lock);
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);
418 /* Timeout freelist garbage collection */
419 silc_schedule_task_add_timeout(schedule, silc_schedule_timeout_gc,
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. */
430 SilcBool silc_schedule_uninit(SilcSchedule schedule)
434 SILC_VERIFY(schedule);
436 SILC_LOG_DEBUG(("Uninitializing scheduler %p", schedule));
438 if (schedule->valid == TRUE)
441 /* Dispatch all timeouts before going away */
442 SILC_SCHEDULE_LOCK(schedule);
443 silc_schedule_dispatch_timeout(schedule, TRUE);
444 SILC_SCHEDULE_UNLOCK(schedule);
446 /* Deliver signals before going away */
447 if (schedule->signal_tasks) {
448 schedule_ops.signals_call(schedule, schedule->internal);
449 schedule->signal_tasks = FALSE;
452 /* Unregister all tasks */
453 silc_schedule_task_del(schedule, SILC_ALL_TASKS);
454 silc_schedule_task_remove(schedule, SILC_ALL_TASKS);
456 /* Delete timeout task freelist */
457 silc_list_start(schedule->free_tasks);
458 while ((task = silc_list_get(schedule->free_tasks)))
461 /* Unregister all task queues */
462 silc_hash_table_free(schedule->fd_queue);
464 /* Uninit the platform specific scheduler. */
465 schedule_ops.uninit(schedule, schedule->internal);
467 silc_mutex_free(schedule->lock);
468 silc_stack_free(schedule->stack);
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). */
477 void silc_schedule_stop(SilcSchedule schedule)
479 SILC_LOG_DEBUG(("Stopping scheduler"));
480 SILC_VERIFY(schedule);
481 SILC_SCHEDULE_LOCK(schedule);
482 schedule->valid = FALSE;
483 SILC_SCHEDULE_UNLOCK(schedule);
486 /* Runs the scheduler once and then returns. Must be called locked. */
488 static SilcBool silc_schedule_iterate(SilcSchedule schedule, int timeout_usecs)
490 struct timeval timeout;
494 SILC_LOG_DEBUG(("In scheduler loop"));
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);
504 /* Check if scheduler is valid */
505 if (silc_unlikely(schedule->valid == FALSE)) {
506 SILC_LOG_DEBUG(("Scheduler not valid anymore, exiting"));
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);
515 /* Check if scheduler is valid */
516 if (silc_unlikely(schedule->valid == FALSE)) {
517 SILC_LOG_DEBUG(("Scheduler not valid anymore, exiting"));
521 if (timeout_usecs >= 0) {
523 timeout.tv_usec = timeout_usecs;
524 schedule->timeout = timeout;
525 schedule->has_timeout = TRUE;
528 /* This is the main silc_select(). The program blocks here until some
529 of the selected file descriptors change status or the selected
531 SILC_LOG_DEBUG(("Select"));
532 ret = schedule_ops.schedule(schedule, schedule->internal);
534 if (silc_likely(ret == 0)) {
536 SILC_LOG_DEBUG(("Running timeout tasks"));
537 if (silc_likely(silc_list_count(schedule->timeout_queue)))
538 silc_schedule_dispatch_timeout(schedule, FALSE);
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);
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);
553 /* Error or special case handling */
559 SILC_LOG_ERROR(("Error in select()/poll(): %s", strerror(errno)));
562 } while (timeout_usecs == -1);
567 /* Runs the scheduler once and then returns. */
569 SilcBool silc_schedule_one(SilcSchedule schedule, int timeout_usecs)
572 SILC_SCHEDULE_LOCK(schedule);
573 ret = silc_schedule_iterate(schedule, timeout_usecs);
574 SILC_SCHEDULE_UNLOCK(schedule);
578 /* Runs the scheduler and blocks here. When this returns the scheduler
582 void silc_schedule(SilcSchedule schedule)
584 SILC_LOG_DEBUG(("Running scheduler"));
586 /* Start the scheduler loop */
587 SILC_SCHEDULE_LOCK(schedule);
588 silc_schedule_iterate(schedule, -1);
589 SILC_SCHEDULE_UNLOCK(schedule);
591 #endif /* !SILC_SYMBIAN */
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. */
600 void silc_schedule_wakeup(SilcSchedule schedule)
603 SILC_LOG_DEBUG(("Wakeup scheduler"));
604 SILC_SCHEDULE_LOCK(schedule);
605 schedule_ops.wakeup(schedule, schedule->internal);
606 SILC_SCHEDULE_UNLOCK(schedule);
610 /* Returns parent scheduler */
612 SilcSchedule silc_schedule_get_parent(SilcSchedule schedule)
614 return schedule->parent ? schedule->parent : schedule;
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. */
622 void *silc_schedule_get_context(SilcSchedule schedule)
624 return schedule->app_context;
627 /* Return the stack of the scheduler */
629 SilcStack silc_schedule_get_stack(SilcSchedule schedule)
631 return schedule->stack;
634 /* Set notify callback */
636 void silc_schedule_set_notify(SilcSchedule schedule,
637 SilcTaskNotifyCb notify, void *context)
639 schedule->notify = notify;
640 schedule->notify_context = context;
643 /* Set global scheduler */
645 void silc_schedule_set_global(SilcSchedule schedule)
647 SilcTls tls = silc_thread_get_tls();
650 /* Try to initialize Tls */
651 tls = silc_thread_tls_init();
657 SILC_LOG_DEBUG(("Setting global scheduler %p", schedule));
659 tls->schedule = schedule;
662 /* Return global scheduler */
664 SilcSchedule silc_schedule_get_global(void)
666 SilcTls tls = silc_thread_get_tls();
671 SILC_LOG_DEBUG(("Return global scheduler %p", tls->schedule));
673 return tls->schedule;
676 /* Add new task to the scheduler */
678 SilcTask silc_schedule_task_add(SilcSchedule schedule, SilcUInt32 fd,
679 SilcTaskCallback callback, void *context,
680 long seconds, long useconds,
683 SilcTask task = NULL;
686 schedule = silc_schedule_get_global();
687 SILC_VERIFY(schedule);
689 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
694 if (silc_unlikely(!schedule->valid)) {
695 silc_set_errno(SILC_ERR_NOT_VALID);
699 SILC_SCHEDULE_LOCK(schedule);
701 if (silc_likely(type == SILC_TASK_TIMEOUT)) {
702 SilcTaskTimeout tmp, prev, ttask;
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))
712 silc_list_del(schedule->free_tasks, ttask);
714 ttask->header.type = 1;
715 ttask->header.callback = callback;
716 ttask->header.context = context;
717 ttask->header.valid = TRUE;
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;
730 SILC_LOG_DEBUG(("New timeout task %p: sec=%d, usec=%d", ttask,
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);
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);
747 silc_list_add(schedule->timeout_queue, ttask);
749 task = (SilcTask)ttask;
751 /* Call notify callback */
752 if (schedule->notify)
753 schedule->notify(schedule, TRUE, task, FALSE, 0, 0, seconds, useconds,
754 schedule->notify_context);
756 } else if (silc_likely(type == SILC_TASK_FD)) {
759 /* Check if fd is already added */
760 if (silc_unlikely(silc_hash_table_find(schedule->fd_queue,
762 NULL, (void *)&task))) {
766 /* Remove invalid task. We must have unique fd key to hash table. */
767 silc_schedule_task_remove(schedule, task);
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"));
776 silc_set_errno(SILC_ERR_LIMIT);
780 ftask = silc_calloc(1, sizeof(*ftask));
781 if (silc_unlikely(!ftask)) {
786 SILC_LOG_DEBUG(("New fd task %p fd=%d", ftask, fd));
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;
795 /* Add task and schedule it */
796 if (!silc_hash_table_add(schedule->fd_queue, SILC_32_TO_PTR(fd), ftask)) {
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));
808 task = (SilcTask)ftask;
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);
815 } else if (silc_unlikely(type == SILC_TASK_SIGNAL)) {
816 SILC_SCHEDULE_UNLOCK(schedule);
817 schedule_ops.signal_register(schedule, schedule->internal, fd,
823 SILC_SCHEDULE_UNLOCK(schedule);
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
829 if (task && task->type == 1)
830 silc_schedule_wakeup(schedule);
831 #endif /* SILC_SYMBIAN */
836 /* Invalidates task */
838 SilcBool silc_schedule_task_del(SilcSchedule schedule, SilcTask task)
843 schedule = silc_schedule_get_global();
844 SILC_VERIFY(schedule);
846 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
851 if (silc_unlikely(task == SILC_ALL_TASKS)) {
852 SilcHashTableList htl;
854 SILC_LOG_DEBUG(("Unregister all tasks"));
856 SILC_SCHEDULE_LOCK(schedule);
858 /* Delete from fd queue */
859 silc_hash_table_list(schedule->fd_queue, &htl);
860 while (silc_hash_table_get(&htl, NULL, (void *)&task)) {
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);
869 silc_hash_table_list_reset(&htl);
871 /* Delete from timeout queue */
872 silc_list_start(schedule->timeout_queue);
873 while ((task = (SilcTask)silc_list_get(schedule->timeout_queue))) {
876 /* Call notify callback */
877 if (schedule->notify)
878 schedule->notify(schedule, FALSE, task, FALSE, 0, 0, 0, 0,
879 schedule->notify_context);
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))
888 silc_hash_table_list_reset(&htl);
891 SILC_SCHEDULE_UNLOCK(schedule);
895 SILC_LOG_DEBUG(("Unregistering task %p, type %d", task, task->type));
896 SILC_SCHEDULE_LOCK(schedule);
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);
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,
915 /* Invalidate task by fd */
917 SilcBool silc_schedule_task_del_by_fd(SilcSchedule schedule, SilcUInt32 fd)
919 SilcTask task = NULL;
920 SilcBool ret = FALSE;
922 SILC_LOG_DEBUG(("Unregister task by fd %d", fd));
925 schedule = silc_schedule_get_global();
926 SILC_VERIFY(schedule);
928 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
933 SILC_SCHEDULE_LOCK(schedule);
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,
939 SILC_LOG_DEBUG(("Deleting task %p", task));
942 /* Call notify callback */
943 if (schedule->notify)
944 schedule->notify(schedule, FALSE, task, TRUE, fd, 0, 0, 0,
945 schedule->notify_context);
949 SILC_SCHEDULE_UNLOCK(schedule);
951 /* If it is signal, remove it */
952 if (silc_unlikely(!task)) {
953 schedule_ops.signal_unregister(schedule, schedule->internal, fd);
958 silc_set_errno(SILC_ERR_NOT_FOUND);
963 /* Invalidate task by task callback. */
965 SilcBool silc_schedule_task_del_by_callback(SilcSchedule schedule,
966 SilcTaskCallback callback)
969 SilcHashTableList htl;
971 SilcBool ret = FALSE;
973 SILC_LOG_DEBUG(("Unregister task by callback"));
976 schedule = silc_schedule_get_global();
977 SILC_VERIFY(schedule);
979 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
984 SILC_SCHEDULE_LOCK(schedule);
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) {
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);
1000 silc_hash_table_list_reset(&htl);
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;
1009 /* Call notify callback */
1010 if (schedule->notify)
1011 schedule->notify(schedule, FALSE, task, FALSE, 0, 0, 0, 0,
1012 schedule->notify_context);
1017 SILC_SCHEDULE_UNLOCK(schedule);
1020 silc_set_errno(SILC_ERR_NOT_FOUND);
1025 /* Invalidate task by context. */
1027 SilcBool silc_schedule_task_del_by_context(SilcSchedule schedule,
1031 SilcHashTableList htl;
1033 SilcBool ret = FALSE;
1035 SILC_LOG_DEBUG(("Unregister task by context"));
1038 schedule = silc_schedule_get_global();
1039 SILC_VERIFY(schedule);
1041 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1046 SILC_SCHEDULE_LOCK(schedule);
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;
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);
1062 silc_hash_table_list_reset(&htl);
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;
1071 /* Call notify callback */
1072 if (schedule->notify)
1073 schedule->notify(schedule, FALSE, task, FALSE, 0, 0, 0, 0,
1074 schedule->notify_context);
1079 SILC_SCHEDULE_UNLOCK(schedule);
1082 silc_set_errno(SILC_ERR_NOT_FOUND);
1087 /* Invalidate task by all */
1089 SilcBool silc_schedule_task_del_by_all(SilcSchedule schedule, int fd,
1090 SilcTaskCallback callback,
1095 SilcBool ret = FALSE;
1097 SILC_LOG_DEBUG(("Unregister task by fd, callback and context"));
1099 /* For fd task, callback and context is irrelevant as fd is unique */
1101 return silc_schedule_task_del_by_fd(schedule, fd);
1104 schedule = silc_schedule_get_global();
1105 SILC_VERIFY(schedule);
1107 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1112 SILC_SCHEDULE_LOCK(schedule);
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;
1121 /* Call notify callback */
1122 if (schedule->notify)
1123 schedule->notify(schedule, FALSE, task, FALSE, 0, 0, 0, 0,
1124 schedule->notify_context);
1129 SILC_SCHEDULE_UNLOCK(schedule);
1132 silc_set_errno(SILC_ERR_NOT_FOUND);
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. */
1141 SilcBool silc_schedule_set_listen_fd(SilcSchedule schedule, SilcUInt32 fd,
1142 SilcTaskEvent mask, SilcBool send_events)
1147 schedule = silc_schedule_get_global();
1148 SILC_VERIFY(schedule);
1150 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1155 if (silc_unlikely(!schedule->valid)) {
1156 silc_set_errno(SILC_ERR_NOT_VALID);
1160 SILC_SCHEDULE_LOCK(schedule);
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);
1168 task->events = mask;
1169 if (silc_unlikely(send_events) && mask) {
1170 task->revents = mask;
1171 silc_schedule_dispatch_fd(schedule);
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);
1181 SILC_SCHEDULE_UNLOCK(schedule);
1186 /* Returns the file descriptor's current requested event mask. */
1188 SilcTaskEvent silc_schedule_get_fd_events(SilcSchedule schedule,
1192 SilcTaskEvent event = 0;
1195 schedule = silc_schedule_get_global();
1196 SILC_VERIFY(schedule);
1198 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1203 if (silc_unlikely(!schedule->valid)) {
1204 silc_set_errno(SILC_ERR_NOT_VALID);
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);
1217 /* Removes a file descriptor from listen list. */
1219 void silc_schedule_unset_listen_fd(SilcSchedule schedule, SilcUInt32 fd)
1221 silc_schedule_set_listen_fd(schedule, fd, 0, FALSE);
1224 /*************************** Asynchronous Events ****************************/
1228 SilcTask silc_schedule_task_add_event(SilcSchedule schedule,
1229 const char *event, ...)
1232 SilcSchedule parent;
1235 schedule = silc_schedule_get_global();
1236 SILC_VERIFY(schedule);
1238 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1243 /* Get parent scheduler */
1244 parent = silc_schedule_get_parent(schedule);
1246 SILC_LOG_DEBUG(("Adding event '%s' to scheduler %p", event, parent));
1248 SILC_SCHEDULE_LOCK(parent);
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,
1256 if (!parent->events) {
1257 SILC_SCHEDULE_UNLOCK(parent);
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);
1269 task = silc_calloc(1, sizeof(*task));
1271 SILC_SCHEDULE_UNLOCK(parent);
1275 task->header.type = SILC_TASK_EVENT;
1276 task->header.valid = TRUE;
1277 task->event = silc_strdup(event);
1279 SILC_SCHEDULE_UNLOCK(parent);
1283 silc_list_init(task->connections, struct SilcScheduleEventConnectionStruct,
1286 if (!silc_hash_table_add(parent->events, task->event, task)) {
1287 SILC_SCHEDULE_UNLOCK(parent);
1288 silc_free(task->event);
1293 SILC_SCHEDULE_UNLOCK(parent);
1295 return (SilcTask)task;
1298 /* Connect to event task */
1300 SilcBool silc_schedule_event_connect(SilcSchedule schedule,
1301 const char *event, SilcTask task,
1302 SilcTaskEventCallback callback,
1305 SilcSchedule parent;
1306 SilcScheduleEventConnection conn;
1307 SilcEventTask etask;
1310 schedule = silc_schedule_get_global();
1311 SILC_VERIFY(schedule);
1313 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1318 if (!event && !task) {
1319 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1323 if (task && task->type != SILC_TASK_EVENT) {
1324 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1328 /* Get parent scheduler */
1329 parent = silc_schedule_get_parent(schedule);
1331 SILC_SCHEDULE_LOCK(parent);
1334 /* Get the event task */
1335 if (!silc_hash_table_find(parent->events, (void *)event, NULL,
1337 SILC_SCHEDULE_UNLOCK(parent);
1341 etask = (SilcEventTask)task;
1343 /* See if task is deleted */
1344 if (task->valid == FALSE) {
1345 SILC_SCHEDULE_UNLOCK(parent);
1346 silc_set_errno(SILC_ERR_NOT_VALID);
1350 SILC_LOG_DEBUG(("Connect callback %p with context %p to event '%s'",
1351 callback, context, etask->event));
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);
1363 conn = silc_calloc(1, sizeof(*conn));
1365 SILC_SCHEDULE_UNLOCK(parent);
1369 /* Connect to the event */
1370 conn->schedule = schedule;
1371 conn->callback = callback;
1372 conn->context = context;
1373 silc_list_add(etask->connections, conn);
1375 SILC_SCHEDULE_UNLOCK(parent);
1380 /* Disconnect from event */
1382 SilcBool silc_schedule_event_disconnect(SilcSchedule schedule,
1383 const char *event, SilcTask task,
1384 SilcTaskEventCallback callback,
1387 SilcSchedule parent;
1388 SilcScheduleEventConnection conn;
1389 SilcEventTask etask;
1392 schedule = silc_schedule_get_global();
1393 SILC_VERIFY(schedule);
1395 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1400 if (!event && !task) {
1401 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1405 if (task && task->type != SILC_TASK_EVENT) {
1406 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1410 /* Get parent scheduler */
1411 parent = silc_schedule_get_parent(schedule);
1413 SILC_SCHEDULE_LOCK(parent);
1416 /* Get the event task */
1417 if (!silc_hash_table_find(parent->events, (void *)event, NULL,
1419 SILC_SCHEDULE_UNLOCK(parent);
1423 etask = (SilcEventTask)task;
1425 /* See if task is deleted */
1426 if (task->valid == FALSE) {
1427 SILC_SCHEDULE_UNLOCK(parent);
1428 silc_set_errno(SILC_ERR_NOT_VALID);
1432 SILC_LOG_DEBUG(("Disconnect callback %p with context %p from event '%s'",
1433 callback, context, etask->event));
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);
1441 SILC_SCHEDULE_UNLOCK(parent);
1446 SILC_SCHEDULE_UNLOCK(parent);
1447 silc_set_errno(SILC_ERR_NOT_FOUND);
1453 SilcBool silc_schedule_event_signal(SilcSchedule schedule, const char *event,
1456 SilcSchedule parent;
1457 SilcScheduleEventConnection conn;
1458 SilcEventTask etask;
1462 if (silc_unlikely(!schedule)) {
1463 schedule = silc_schedule_get_global();
1464 SILC_VERIFY(schedule);
1466 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1471 if (silc_unlikely(!event && !task)) {
1472 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1476 if (silc_unlikely(task && task->type != SILC_TASK_EVENT)) {
1477 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1481 /* Get parent scheduler */
1482 parent = silc_schedule_get_parent(schedule);
1484 SILC_SCHEDULE_LOCK(parent);
1487 /* Get the event task */
1488 if (!silc_hash_table_find(parent->events, (void *)event, NULL,
1490 SILC_SCHEDULE_UNLOCK(parent);
1494 etask = (SilcEventTask)task;
1496 /* See if task is deleted */
1497 if (task->valid == FALSE) {
1498 SILC_SCHEDULE_UNLOCK(parent);
1499 silc_set_errno(SILC_ERR_NOT_VALID);
1503 SILC_LOG_DEBUG(("Signal event '%s'", etask->event));
1507 /* Deliver the signal */
1508 silc_list_start(etask->connections);
1509 while ((conn = silc_list_get(etask->connections))) {
1510 SILC_SCHEDULE_UNLOCK(parent);
1512 silc_va_copy(cp, ap);
1513 stop = conn->callback(conn->schedule, conn->schedule->app_context,
1514 task, conn->context, cp);
1517 SILC_SCHEDULE_LOCK(parent);
1519 /* Stop signal if wanted or if the task was deleted */
1520 if (!stop || !task->valid)
1526 SILC_SCHEDULE_UNLOCK(parent);
1533 SilcBool silc_schedule_task_del_event(SilcSchedule schedule, const char *event)
1535 SilcSchedule parent;
1539 schedule = silc_schedule_get_global();
1540 SILC_VERIFY(schedule);
1542 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1548 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
1552 /* Get parent scheduler */
1553 parent = silc_schedule_get_parent(schedule);
1555 SILC_SCHEDULE_LOCK(parent);
1557 /* Get the event task */
1558 if (!silc_hash_table_find(parent->events, (void *)event, NULL,
1560 SILC_SCHEDULE_UNLOCK(parent);
1564 /* See if already deleted */
1565 if (task->valid == FALSE)
1568 SILC_LOG_DEBUG(("Delete event '%s'", ((SilcEventTask)task)->event));
1570 SILC_SCHEDULE_UNLOCK(parent);
1572 silc_schedule_task_del(parent, task);
1577 /* Timeout to remove deleted event task */
1579 SILC_TASK_CALLBACK(silc_schedule_event_del_timeout)
1581 SILC_SCHEDULE_LOCK(schedule);
1582 silc_schedule_task_remove(schedule, context);
1583 SILC_SCHEDULE_UNLOCK(schedule);