1 /*
2
3 silcschedule.c
4
5 Author: Pekka Riikonen <priikone@silcnet.org>
6
7 Copyright (C) 1998 - 2007 Pekka Riikonen
8
9 The contents of this file are subject to one of the Licenses specified
10 in the COPYING file; You may not use this file except in compliance
11 with the License.
12
13 The software distributed under the License is distributed on an "AS IS"
14 basis, in the hope that it will be useful, but WITHOUT WARRANTY OF ANY
15 KIND, either expressed or implied. See the COPYING file for more
16 information.
17
18 */
19 /* $Id$ */
20
21 #include "silc.h"
22
23 /************************** Types and definitions ***************************/
24
25 /* Platform specific implementation */
26 extern const SilcScheduleOps schedule_ops;
27
28 static void silc_schedule_task_remove(SilcSchedule schedule, SilcTask task);
29 static void silc_schedule_dispatch_fd(SilcSchedule schedule);
30 static void silc_schedule_dispatch_timeout(SilcSchedule schedule,
31 SilcBool dispatch_all);
32
33
34 /************************ Static utility functions **************************/
35
36 /* Fd task hash table destructor */
37
silc_schedule_fd_destructor(void * key,void * context,void * user_context)38 static void silc_schedule_fd_destructor(void *key, void *context,
39 void *user_context)
40 {
41 silc_free(context);
42 }
43
44 /* Executes file descriptor tasks. Invalid tasks are removed here. */
45
silc_schedule_dispatch_fd(SilcSchedule schedule)46 static void silc_schedule_dispatch_fd(SilcSchedule schedule)
47 {
48 SilcTaskFd task;
49 SilcTask t;
50
51 /* The dispatch list includes only valid tasks, and tasks that have
52 something to dispatch. Dispatching is atomic; no matter if another
53 thread invalidates a task when we unlock, we dispatch to completion. */
54 SILC_SCHEDULE_UNLOCK(schedule);
55 silc_list_start(schedule->fd_dispatch);
56 while ((task = silc_list_get(schedule->fd_dispatch))) {
57 t = (SilcTask)task;
58
59 /* Is the task ready for reading */
60 if (task->revents & SILC_TASK_READ)
61 t->callback(schedule, schedule->app_context, SILC_TASK_READ,
62 task->fd, t->context);
63
64 /* Is the task ready for writing */
65 if (t->valid && task->revents & SILC_TASK_WRITE)
66 t->callback(schedule, schedule->app_context, SILC_TASK_WRITE,
67 task->fd, t->context);
68 }
69 SILC_SCHEDULE_LOCK(schedule);
70
71 /* Remove invalidated tasks */
72 silc_list_start(schedule->fd_dispatch);
73 while ((task = silc_list_get(schedule->fd_dispatch)))
74 if (silc_unlikely(!task->header.valid))
75 silc_schedule_task_remove(schedule, (SilcTask)task);
76 }
77
78 /* Executes all tasks whose timeout has expired. The task is removed from
79 the task queue after the callback function has returned. Also, invalid
80 tasks are removed here. */
81
silc_schedule_dispatch_timeout(SilcSchedule schedule,SilcBool dispatch_all)82 static void silc_schedule_dispatch_timeout(SilcSchedule schedule,
83 SilcBool dispatch_all)
84 {
85 SilcTask t;
86 SilcTaskTimeout task;
87 struct timeval curtime;
88 int count = 0;
89
90 SILC_LOG_DEBUG(("Running timeout tasks"));
91
92 silc_gettimeofday(&curtime);
93
94 /* First task in the task queue has always the earliest timeout. */
95 silc_list_start(schedule->timeout_queue);
96 task = silc_list_get(schedule->timeout_queue);
97 if (silc_unlikely(!task))
98 return;
99 do {
100 t = (SilcTask)task;
101
102 /* Remove invalid task */
103 if (silc_unlikely(!t->valid)) {
104 silc_schedule_task_remove(schedule, t);
105 continue;
106 }
107
108 /* Execute the task if the timeout has expired */
109 if (silc_compare_timeval(&task->timeout, &curtime) > 0 && !dispatch_all)
110 break;
111
112 t->valid = FALSE;
113 SILC_SCHEDULE_UNLOCK(schedule);
114 t->callback(schedule, schedule->app_context, SILC_TASK_EXPIRE, 0,
115 t->context);
116 SILC_SCHEDULE_LOCK(schedule);
117
118 /* Remove the expired task */
119 silc_schedule_task_remove(schedule, t);
120
121 /* Balance when we have lots of small timeouts */
122 if (silc_unlikely((++count) > 40))
123 break;
124 } while (silc_likely((task = silc_list_get(schedule->timeout_queue))));
125 }
126
127 /* Calculates next timeout. This is the timeout value when at earliest some
128 of the timeout tasks expire. If this is in the past, they will be
129 dispatched now. */
130
silc_schedule_select_timeout(SilcSchedule schedule)131 static void silc_schedule_select_timeout(SilcSchedule schedule)
132 {
133 SilcTask t;
134 SilcTaskTimeout task;
135 struct timeval curtime;
136 SilcBool dispatch = TRUE;
137
138 /* Get the current time */
139 silc_gettimeofday(&curtime);
140 schedule->has_timeout = FALSE;
141
142 /* First task in the task queue has always the earliest timeout. */
143 silc_list_start(schedule->timeout_queue);
144 task = silc_list_get(schedule->timeout_queue);
145 if (silc_unlikely(!task))
146 return;
147 do {
148 t = (SilcTask)task;
149
150 /* Remove invalid task */
151 if (silc_unlikely(!t->valid)) {
152 silc_schedule_task_remove(schedule, t);
153 continue;
154 }
155
156 /* If the timeout is in past, we will run the task and all other
157 timeout tasks from the past. */
158 if (silc_compare_timeval(&task->timeout, &curtime) <= 0 && dispatch) {
159 silc_schedule_dispatch_timeout(schedule, FALSE);
160 if (silc_unlikely(!schedule->valid))
161 return;
162
163 /* Start selecting new timeout again after dispatch */
164 silc_list_start(schedule->timeout_queue);
165 dispatch = FALSE;
166 continue;
167 }
168
169 /* Calculate the next timeout */
170 curtime.tv_sec = task->timeout.tv_sec - curtime.tv_sec;
171 curtime.tv_usec = task->timeout.tv_usec - curtime.tv_usec;
172 if (curtime.tv_sec < 0)
173 curtime.tv_sec = 0;
174
175 /* We wouldn't want to go under zero, check for it. */
176 if (curtime.tv_usec < 0) {
177 curtime.tv_sec -= 1;
178 if (curtime.tv_sec < 0)
179 curtime.tv_sec = 0;
180 curtime.tv_usec += 1000000L;
181 }
182 break;
183 } while ((task = silc_list_get(schedule->timeout_queue)));
184
185 /* Save the timeout */
186 if (task) {
187 schedule->timeout = curtime;
188 schedule->has_timeout = TRUE;
189 SILC_LOG_DEBUG(("timeout: sec=%ld, usec=%ld", schedule->timeout.tv_sec,
190 schedule->timeout.tv_usec));
191 }
192 }
193
194 /* Removes task from the scheduler. This must be called with scheduler
195 locked. */
196
silc_schedule_task_remove(SilcSchedule schedule,SilcTask task)197 static void silc_schedule_task_remove(SilcSchedule schedule, SilcTask task)
198 {
199 SilcTaskFd ftask;
200
201 if (silc_unlikely(task == SILC_ALL_TASKS)) {
202 SilcTask task;
203 SilcHashTableList htl;
204 void *fd;
205
206 /* Delete from fd queue */
207 silc_hash_table_list(schedule->fd_queue, &htl);
208 while (silc_hash_table_get(&htl, &fd, (void *)&task))
209 silc_hash_table_del(schedule->fd_queue, fd);
210 silc_hash_table_list_reset(&htl);
211
212 /* Delete from timeout queue */
213 silc_list_start(schedule->timeout_queue);
214 while ((task = silc_list_get(schedule->timeout_queue))) {
215 silc_list_del(schedule->timeout_queue, task);
216 silc_free(task);
217 }
218
219 return;
220 }
221
222 if (silc_likely(task->type == 1)) {
223 /* Delete from timeout queue */
224 silc_list_del(schedule->timeout_queue, task);
225
226 /* Put to free list */
227 silc_list_add(schedule->free_tasks, task);
228 } else {
229 /* Delete from fd queue */
230 ftask = (SilcTaskFd)task;
231 silc_hash_table_del(schedule->fd_queue, SILC_32_TO_PTR(ftask->fd));
232 }
233 }
234
235 /* Timeout freelist garbage collection */
236
SILC_TASK_CALLBACK(silc_schedule_timeout_gc)237 SILC_TASK_CALLBACK(silc_schedule_timeout_gc)
238 {
239 SilcTaskTimeout t;
240 int c;
241
242 if (!schedule->valid)
243 return;
244
245 SILC_LOG_DEBUG(("Timeout freelist garbage collection"));
246
247 SILC_SCHEDULE_LOCK(schedule);
248
249 if (silc_list_count(schedule->free_tasks) <= 10) {
250 SILC_SCHEDULE_UNLOCK(schedule);
251 silc_schedule_task_add_timeout(schedule, silc_schedule_timeout_gc,
252 schedule, 3600, 0);
253 return;
254 }
255 if (silc_list_count(schedule->timeout_queue) >
256 silc_list_count(schedule->free_tasks)) {
257 SILC_SCHEDULE_UNLOCK(schedule);
258 silc_schedule_task_add_timeout(schedule, silc_schedule_timeout_gc,
259 schedule, 3600, 0);
260 return;
261 }
262
263 c = silc_list_count(schedule->free_tasks) / 2;
264 if (c > silc_list_count(schedule->timeout_queue))
265 c = (silc_list_count(schedule->free_tasks) -
266 silc_list_count(schedule->timeout_queue));
267 if (silc_list_count(schedule->free_tasks) - c < 10)
268 c -= (10 - (silc_list_count(schedule->free_tasks) - c));
269
270 SILC_LOG_DEBUG(("Freeing %d unused tasks, leaving %d", c,
271 silc_list_count(schedule->free_tasks) - c));
272
273 silc_list_start(schedule->free_tasks);
274 while ((t = silc_list_get(schedule->free_tasks)) && c-- > 0) {
275 silc_list_del(schedule->free_tasks, t);
276 silc_free(t);
277 }
278 silc_list_start(schedule->free_tasks);
279
280 SILC_SCHEDULE_UNLOCK(schedule);
281
282 silc_schedule_task_add_timeout(schedule, silc_schedule_timeout_gc,
283 schedule, 3600, 0);
284 }
285
286
287 /****************************** Public API **********************************/
288
289 /* Initializes the scheduler. This returns the scheduler context that
290 is given as arugment usually to all silc_schedule_* functions.
291 The `max_tasks' indicates the number of maximum tasks that the
292 scheduler can handle. The `app_context' is application specific
293 context that is delivered to task callbacks. */
294
silc_schedule_init(int max_tasks,void * app_context)295 SilcSchedule silc_schedule_init(int max_tasks, void *app_context)
296 {
297 SilcSchedule schedule;
298
299 SILC_LOG_DEBUG(("Initializing scheduler"));
300
301 schedule = silc_calloc(1, sizeof(*schedule));
302 if (!schedule)
303 return NULL;
304
305 schedule->fd_queue =
306 silc_hash_table_alloc(0, silc_hash_uint, NULL, NULL, NULL,
307 silc_schedule_fd_destructor, NULL, TRUE);
308 if (!schedule->fd_queue) {
309 silc_free(schedule);
310 return NULL;
311 }
312
313 silc_list_init(schedule->timeout_queue, struct SilcTaskStruct, next);
314 silc_list_init(schedule->free_tasks, struct SilcTaskStruct, next);
315
316 schedule->app_context = app_context;
317 schedule->valid = TRUE;
318 schedule->max_tasks = max_tasks;
319
320 /* Allocate scheduler lock */
321 silc_mutex_alloc(&schedule->lock);
322
323 /* Initialize the platform specific scheduler. */
324 schedule->internal = schedule_ops.init(schedule, app_context);
325 if (!schedule->internal) {
326 silc_hash_table_free(schedule->fd_queue);
327 silc_mutex_free(schedule->lock);
328 silc_free(schedule);
329 return NULL;
330 }
331
332 /* Timeout freelist garbage collection */
333 silc_schedule_task_add_timeout(schedule, silc_schedule_timeout_gc,
334 schedule, 3600, 0);
335
336 return schedule;
337 }
338
339 /* Uninitializes the schedule. This is called when the program is ready
340 to end. This removes all tasks and task queues. Returns FALSE if the
341 scheduler could not be uninitialized. This happens when the scheduler
342 is still valid and silc_schedule_stop has not been called. */
343
silc_schedule_uninit(SilcSchedule schedule)344 SilcBool silc_schedule_uninit(SilcSchedule schedule)
345 {
346 SilcTask task;
347
348 SILC_LOG_DEBUG(("Uninitializing scheduler"));
349
350 if (schedule->valid == TRUE)
351 return FALSE;
352
353 /* Dispatch all timeouts before going away */
354 SILC_SCHEDULE_LOCK(schedule);
355 silc_schedule_dispatch_timeout(schedule, TRUE);
356 SILC_SCHEDULE_UNLOCK(schedule);
357
358 /* Deliver signals before going away */
359 if (schedule->signal_tasks) {
360 schedule_ops.signals_call(schedule, schedule->internal);
361 schedule->signal_tasks = FALSE;
362 }
363
364 /* Unregister all tasks */
365 silc_schedule_task_del(schedule, SILC_ALL_TASKS);
366 silc_schedule_task_remove(schedule, SILC_ALL_TASKS);
367
368 /* Delete timeout task freelist */
369 silc_list_start(schedule->free_tasks);
370 while ((task = silc_list_get(schedule->free_tasks)))
371 silc_free(task);
372
373 /* Unregister all task queues */
374 silc_hash_table_free(schedule->fd_queue);
375
376 /* Uninit the platform specific scheduler. */
377 schedule_ops.uninit(schedule, schedule->internal);
378
379 silc_mutex_free(schedule->lock);
380 silc_free(schedule);
381
382 return TRUE;
383 }
384
385 /* Stops the schedule even if it is not supposed to be stopped yet.
386 After calling this, one should call silc_schedule_uninit (after the
387 silc_schedule has returned). */
388
silc_schedule_stop(SilcSchedule schedule)389 void silc_schedule_stop(SilcSchedule schedule)
390 {
391 SILC_LOG_DEBUG(("Stopping scheduler"));
392 SILC_SCHEDULE_LOCK(schedule);
393 schedule->valid = FALSE;
394 SILC_SCHEDULE_UNLOCK(schedule);
395 }
396
397 /* Runs the scheduler once and then returns. Must be called locked. */
398
silc_schedule_iterate(SilcSchedule schedule,int timeout_usecs)399 static SilcBool silc_schedule_iterate(SilcSchedule schedule, int timeout_usecs)
400 {
401 struct timeval timeout;
402 int ret;
403
404 do {
405 SILC_LOG_DEBUG(("In scheduler loop"));
406
407 /* Deliver signals if any has been set to be called */
408 if (silc_unlikely(schedule->signal_tasks)) {
409 SILC_SCHEDULE_UNLOCK(schedule);
410 schedule_ops.signals_call(schedule, schedule->internal);
411 schedule->signal_tasks = FALSE;
412 SILC_SCHEDULE_LOCK(schedule);
413 }
414
415 /* Check if scheduler is valid */
416 if (silc_unlikely(schedule->valid == FALSE)) {
417 SILC_LOG_DEBUG(("Scheduler not valid anymore, exiting"));
418 return FALSE;
419 }
420
421 /* Calculate next timeout for silc_select(). This is the timeout value
422 when at earliest some of the timeout tasks expire. This may dispatch
423 already expired timeouts. */
424 silc_schedule_select_timeout(schedule);
425
426 /* Check if scheduler is valid */
427 if (silc_unlikely(schedule->valid == FALSE)) {
428 SILC_LOG_DEBUG(("Scheduler not valid anymore, exiting"));
429 return FALSE;
430 }
431
432 if (timeout_usecs >= 0) {
433 timeout.tv_sec = 0;
434 timeout.tv_usec = timeout_usecs;
435 schedule->timeout = timeout;
436 schedule->has_timeout = TRUE;
437 }
438
439 /* This is the main silc_select(). The program blocks here until some
440 of the selected file descriptors change status or the selected
441 timeout expires. */
442 SILC_LOG_DEBUG(("Select"));
443 ret = schedule_ops.schedule(schedule, schedule->internal);
444
445 if (silc_likely(ret == 0)) {
446 /* Timeout */
447 SILC_LOG_DEBUG(("Running timeout tasks"));
448 if (silc_likely(silc_list_count(schedule->timeout_queue)))
449 silc_schedule_dispatch_timeout(schedule, FALSE);
450 continue;
451
452 } else if (silc_likely(ret > 0)) {
453 /* There is some data available now */
454 SILC_LOG_DEBUG(("Running fd tasks"));
455 silc_schedule_dispatch_fd(schedule);
456
457 /* If timeout was very short, dispatch also timeout tasks */
458 if (schedule->has_timeout && schedule->timeout.tv_sec == 0 &&
459 schedule->timeout.tv_usec < 50000)
460 silc_schedule_dispatch_timeout(schedule, FALSE);
461 continue;
462
463 } else {
464 /* Error or special case handling */
465 if (errno == EINTR)
466 continue;
467 if (ret == -2)
468 break;
469
470 SILC_LOG_ERROR(("Error in select()/poll(): %s", strerror(errno)));
471 continue;
472 }
473 } while (timeout_usecs == -1);
474
475 return TRUE;
476 }
477
478 /* Runs the scheduler once and then returns. */
479
silc_schedule_one(SilcSchedule schedule,int timeout_usecs)480 SilcBool silc_schedule_one(SilcSchedule schedule, int timeout_usecs)
481 {
482 SilcBool ret;
483 SILC_SCHEDULE_LOCK(schedule);
484 ret = silc_schedule_iterate(schedule, timeout_usecs);
485 SILC_SCHEDULE_UNLOCK(schedule);
486 return ret;
487 }
488
489 /* Runs the scheduler and blocks here. When this returns the scheduler
490 has ended. */
491
492 #ifndef SILC_SYMBIAN
silc_schedule(SilcSchedule schedule)493 void silc_schedule(SilcSchedule schedule)
494 {
495 SILC_LOG_DEBUG(("Running scheduler"));
496
497 /* Start the scheduler loop */
498 SILC_SCHEDULE_LOCK(schedule);
499 silc_schedule_iterate(schedule, -1);
500 SILC_SCHEDULE_UNLOCK(schedule);
501 }
502 #endif /* !SILC_SYMBIAN */
503
504 /* Wakes up the scheduler. This is used only in multi-threaded
505 environments where threads may add new tasks or remove old tasks
506 from task queues. This is called to wake up the scheduler in the
507 main thread so that it detects the changes in the task queues.
508 If threads support is not compiled in this function has no effect.
509 Implementation of this function is platform specific. */
510
silc_schedule_wakeup(SilcSchedule schedule)511 void silc_schedule_wakeup(SilcSchedule schedule)
512 {
513 #ifdef SILC_THREADS
514 SILC_LOG_DEBUG(("Wakeup scheduler"));
515 SILC_SCHEDULE_LOCK(schedule);
516 schedule_ops.wakeup(schedule, schedule->internal);
517 SILC_SCHEDULE_UNLOCK(schedule);
518 #endif
519 }
520
521 /* Returns the application specific context that was saved into the
522 scheduler in silc_schedule_init function. The context is also
523 returned to application in task callback functions, but this function
524 may be used to get it as well if needed. */
525
silc_schedule_get_context(SilcSchedule schedule)526 void *silc_schedule_get_context(SilcSchedule schedule)
527 {
528 return schedule->app_context;
529 }
530
531 /* Set notify callback */
532
silc_schedule_set_notify(SilcSchedule schedule,SilcTaskNotifyCb notify,void * context)533 void silc_schedule_set_notify(SilcSchedule schedule,
534 SilcTaskNotifyCb notify, void *context)
535 {
536 schedule->notify = notify;
537 schedule->notify_context = context;
538 }
539
540 /* Add new task to the scheduler */
541
silc_schedule_task_add(SilcSchedule schedule,SilcUInt32 fd,SilcTaskCallback callback,void * context,long seconds,long useconds,SilcTaskType type)542 SilcTask silc_schedule_task_add(SilcSchedule schedule, SilcUInt32 fd,
543 SilcTaskCallback callback, void *context,
544 long seconds, long useconds,
545 SilcTaskType type)
546 {
547 SilcTask task = NULL;
548
549 if (silc_unlikely(!schedule->valid))
550 return NULL;
551
552 SILC_SCHEDULE_LOCK(schedule);
553
554 if (silc_likely(type == SILC_TASK_TIMEOUT)) {
555 SilcTaskTimeout tmp, prev, ttask;
556 SilcList list;
557
558 silc_list_start(schedule->free_tasks);
559 ttask = silc_list_get(schedule->free_tasks);
560 if (silc_unlikely(!ttask)) {
561 ttask = silc_calloc(1, sizeof(*ttask));
562 if (silc_unlikely(!ttask))
563 goto out;
564 } else
565 silc_list_del(schedule->free_tasks, ttask);
566
567 ttask->header.type = 1;
568 ttask->header.callback = callback;
569 ttask->header.context = context;
570 ttask->header.valid = TRUE;
571
572 /* Add timeout */
573 silc_gettimeofday(&ttask->timeout);
574 if ((seconds + useconds) > 0) {
575 ttask->timeout.tv_sec += seconds + (useconds / 1000000L);
576 ttask->timeout.tv_usec += (useconds % 1000000L);
577 if (ttask->timeout.tv_usec >= 1000000L) {
578 ttask->timeout.tv_sec += 1;
579 ttask->timeout.tv_usec -= 1000000L;
580 }
581 }
582
583 SILC_LOG_DEBUG(("New timeout task %p: sec=%ld, usec=%ld", ttask,
584 seconds, useconds));
585
586 /* Add task to correct spot so that the first task in the list has
587 the earliest timeout. */
588 list = schedule->timeout_queue;
589 silc_list_start(list);
590 prev = NULL;
591 while ((tmp = silc_list_get(list)) != SILC_LIST_END) {
592 /* If we have shorter timeout, we have found our spot */
593 if (silc_compare_timeval(&ttask->timeout, &tmp->timeout) < 0) {
594 silc_list_insert(schedule->timeout_queue, prev, ttask);
595 break;
596 }
597 prev = tmp;
598 }
599 if (!tmp)
600 silc_list_add(schedule->timeout_queue, ttask);
601
602 task = (SilcTask)ttask;
603
604 /* Call notify callback */
605 if (schedule->notify)
606 schedule->notify(schedule, TRUE, task, FALSE, 0, 0, seconds, useconds,
607 schedule->notify_context);
608
609 } else if (silc_likely(type == SILC_TASK_FD)) {
610 SilcTaskFd ftask;
611
612 /* Check if fd is already added */
613 if (silc_unlikely(silc_hash_table_find(schedule->fd_queue,
614 SILC_32_TO_PTR(fd),
615 NULL, (void *)&task))) {
616 if (task->valid)
617 goto out;
618
619 /* Remove invalid task. We must have unique fd key to hash table. */
620 silc_schedule_task_remove(schedule, task);
621 }
622
623 /* Check max tasks */
624 if (silc_unlikely(schedule->max_tasks > 0 &&
625 silc_hash_table_count(schedule->fd_queue) >=
626 schedule->max_tasks)) {
627 SILC_LOG_WARNING(("Scheduler task limit reached: cannot add new task"));
628 task = NULL;
629 goto out;
630 }
631
632 ftask = silc_calloc(1, sizeof(*ftask));
633 if (silc_unlikely(!ftask)) {
634 task = NULL;
635 goto out;
636 }
637
638 SILC_LOG_DEBUG(("New fd task %p fd=%d", ftask, fd));
639
640 ftask->header.type = 0;
641 ftask->header.callback = callback;
642 ftask->header.context = context;
643 ftask->header.valid = TRUE;
644 ftask->events = SILC_TASK_READ;
645 ftask->fd = fd;
646
647 /* Add task and schedule it */
648 if (!silc_hash_table_add(schedule->fd_queue, SILC_32_TO_PTR(fd), ftask)) {
649 silc_free(ftask);
650 task = NULL;
651 goto out;
652 }
653 if (!schedule_ops.schedule_fd(schedule, schedule->internal,
654 ftask, ftask->events)) {
655 silc_hash_table_del(schedule->fd_queue, SILC_32_TO_PTR(fd));
656 task = NULL;
657 goto out;
658 }
659
660 task = (SilcTask)ftask;
661
662 /* Call notify callback */
663 if (schedule->notify)
664 schedule->notify(schedule, TRUE, task, TRUE, ftask->fd,
665 SILC_TASK_READ, 0, 0, schedule->notify_context);
666
667 } else if (silc_unlikely(type == SILC_TASK_SIGNAL)) {
668 SILC_SCHEDULE_UNLOCK(schedule);
669 schedule_ops.signal_register(schedule, schedule->internal, fd,
670 callback, context);
671 return NULL;
672 }
673
674 out:
675 SILC_SCHEDULE_UNLOCK(schedule);
676
677 #ifdef SILC_SYMBIAN
678 /* On symbian we wakeup scheduler immediately after adding timeout task
679 in case the task is added outside the scheduler loop (in some active
680 object). */
681 if (task && task->type == 1)
682 silc_schedule_wakeup(schedule);
683 #endif /* SILC_SYMBIAN */
684
685 return task;
686 }
687
688 /* Invalidates task */
689
silc_schedule_task_del(SilcSchedule schedule,SilcTask task)690 SilcBool silc_schedule_task_del(SilcSchedule schedule, SilcTask task)
691 {
692 if (silc_unlikely(task == SILC_ALL_TASKS)) {
693 SilcHashTableList htl;
694
695 SILC_LOG_DEBUG(("Unregister all tasks"));
696
697 SILC_SCHEDULE_LOCK(schedule);
698
699 /* Delete from fd queue */
700 silc_hash_table_list(schedule->fd_queue, &htl);
701 while (silc_hash_table_get(&htl, NULL, (void *)&task)) {
702 task->valid = FALSE;
703
704 /* Call notify callback */
705 if (schedule->notify)
706 schedule->notify(schedule, FALSE, task, TRUE,
707 ((SilcTaskFd)task)->fd, 0, 0, 0,
708 schedule->notify_context);
709 }
710 silc_hash_table_list_reset(&htl);
711
712 /* Delete from timeout queue */
713 silc_list_start(schedule->timeout_queue);
714 while ((task = (SilcTask)silc_list_get(schedule->timeout_queue))) {
715 task->valid = FALSE;
716
717 /* Call notify callback */
718 if (schedule->notify)
719 schedule->notify(schedule, FALSE, task, FALSE, 0, 0, 0, 0,
720 schedule->notify_context);
721 }
722
723 SILC_SCHEDULE_UNLOCK(schedule);
724 return TRUE;
725 }
726
727 SILC_LOG_DEBUG(("Unregistering task %p", task));
728 SILC_SCHEDULE_LOCK(schedule);
729 task->valid = FALSE;
730
731 /* Call notify callback */
732 if (schedule->notify)
733 schedule->notify(schedule, FALSE, task, !task->type, 0, 0, 0, 0,
734 schedule->notify_context);
735 SILC_SCHEDULE_UNLOCK(schedule);
736
737 return TRUE;
738 }
739
740 /* Invalidate task by fd */
741
silc_schedule_task_del_by_fd(SilcSchedule schedule,SilcUInt32 fd)742 SilcBool silc_schedule_task_del_by_fd(SilcSchedule schedule, SilcUInt32 fd)
743 {
744 SilcTask task = NULL;
745 SilcBool ret = FALSE;
746
747 SILC_LOG_DEBUG(("Unregister task by fd %d", fd));
748
749 SILC_SCHEDULE_LOCK(schedule);
750
751 /* fd is unique, so there is only one task with this fd in the table */
752 if (silc_likely(silc_hash_table_find(schedule->fd_queue,
753 SILC_32_TO_PTR(fd), NULL,
754 (void *)&task))) {
755 SILC_LOG_DEBUG(("Deleting task %p", task));
756 task->valid = FALSE;
757
758 /* Call notify callback */
759 if (schedule->notify)
760 schedule->notify(schedule, FALSE, task, TRUE, fd, 0, 0, 0,
761 schedule->notify_context);
762 ret = TRUE;
763 }
764
765 SILC_SCHEDULE_UNLOCK(schedule);
766
767 /* If it is signal, remove it */
768 if (silc_unlikely(!task)) {
769 schedule_ops.signal_unregister(schedule, schedule->internal, fd);
770 ret = TRUE;
771 }
772
773 return ret;
774 }
775
776 /* Invalidate task by task callback. */
777
silc_schedule_task_del_by_callback(SilcSchedule schedule,SilcTaskCallback callback)778 SilcBool silc_schedule_task_del_by_callback(SilcSchedule schedule,
779 SilcTaskCallback callback)
780 {
781 SilcTask task;
782 SilcHashTableList htl;
783 SilcList list;
784 SilcBool ret = FALSE;
785
786 SILC_LOG_DEBUG(("Unregister task by callback"));
787
788 SILC_SCHEDULE_LOCK(schedule);
789
790 /* Delete from fd queue */
791 silc_hash_table_list(schedule->fd_queue, &htl);
792 while (silc_hash_table_get(&htl, NULL, (void *)&task)) {
793 if (task->callback == callback) {
794 task->valid = FALSE;
795
796 /* Call notify callback */
797 if (schedule->notify)
798 schedule->notify(schedule, FALSE, task, TRUE,
799 ((SilcTaskFd)task)->fd, 0, 0, 0,
800 schedule->notify_context);
801 ret = TRUE;
802 }
803 }
804 silc_hash_table_list_reset(&htl);
805
806 /* Delete from timeout queue */
807 list = schedule->timeout_queue;
808 silc_list_start(list);
809 while ((task = (SilcTask)silc_list_get(list))) {
810 if (task->callback == callback) {
811 task->valid = FALSE;
812
813 /* Call notify callback */
814 if (schedule->notify)
815 schedule->notify(schedule, FALSE, task, FALSE, 0, 0, 0, 0,
816 schedule->notify_context);
817 ret = TRUE;
818 }
819 }
820
821 SILC_SCHEDULE_UNLOCK(schedule);
822
823 return ret;
824 }
825
826 /* Invalidate task by context. */
827
silc_schedule_task_del_by_context(SilcSchedule schedule,void * context)828 SilcBool silc_schedule_task_del_by_context(SilcSchedule schedule,
829 void *context)
830 {
831 SilcTask task;
832 SilcHashTableList htl;
833 SilcList list;
834 SilcBool ret = FALSE;
835
836 SILC_LOG_DEBUG(("Unregister task by context"));
837
838 SILC_SCHEDULE_LOCK(schedule);
839
840 /* Delete from fd queue */
841 silc_hash_table_list(schedule->fd_queue, &htl);
842 while (silc_hash_table_get(&htl, NULL, (void *)&task)) {
843 if (task->context == context) {
844 task->valid = FALSE;
845
846 /* Call notify callback */
847 if (schedule->notify)
848 schedule->notify(schedule, FALSE, task, TRUE,
849 ((SilcTaskFd)task)->fd, 0, 0, 0,
850 schedule->notify_context);
851 ret = TRUE;
852 }
853 }
854 silc_hash_table_list_reset(&htl);
855
856 /* Delete from timeout queue */
857 list = schedule->timeout_queue;
858 silc_list_start(list);
859 while ((task = (SilcTask)silc_list_get(list))) {
860 if (task->context == context) {
861 task->valid = FALSE;
862
863 /* Call notify callback */
864 if (schedule->notify)
865 schedule->notify(schedule, FALSE, task, FALSE, 0, 0, 0, 0,
866 schedule->notify_context);
867 ret = TRUE;
868 }
869 }
870
871 SILC_SCHEDULE_UNLOCK(schedule);
872
873 return ret;
874 }
875
876 /* Invalidate task by all */
877
silc_schedule_task_del_by_all(SilcSchedule schedule,int fd,SilcTaskCallback callback,void * context)878 SilcBool silc_schedule_task_del_by_all(SilcSchedule schedule, int fd,
879 SilcTaskCallback callback,
880 void *context)
881 {
882 SilcTask task;
883 SilcList list;
884 SilcBool ret = FALSE;
885
886 SILC_LOG_DEBUG(("Unregister task by fd, callback and context"));
887
888 /* For fd task, callback and context is irrelevant as fd is unique */
889 if (fd)
890 return silc_schedule_task_del_by_fd(schedule, fd);
891
892 SILC_SCHEDULE_LOCK(schedule);
893
894 /* Delete from timeout queue */
895 list = schedule->timeout_queue;
896 silc_list_start(list);
897 while ((task = (SilcTask)silc_list_get(list))) {
898 if (task->callback == callback && task->context == context) {
899 task->valid = FALSE;
900
901 /* Call notify callback */
902 if (schedule->notify)
903 schedule->notify(schedule, FALSE, task, FALSE, 0, 0, 0, 0,
904 schedule->notify_context);
905 ret = TRUE;
906 }
907 }
908
909 SILC_SCHEDULE_UNLOCK(schedule);
910
911 return ret;
912 }
913
914 /* Sets a file descriptor to be listened by scheduler. One can call this
915 directly if wanted. This can be called multiple times for one file
916 descriptor to set different iomasks. */
917
silc_schedule_set_listen_fd(SilcSchedule schedule,SilcUInt32 fd,SilcTaskEvent mask,SilcBool send_events)918 SilcBool silc_schedule_set_listen_fd(SilcSchedule schedule, SilcUInt32 fd,
919 SilcTaskEvent mask, SilcBool send_events)
920 {
921 SilcTaskFd task;
922
923 if (silc_unlikely(!schedule->valid))
924 return FALSE;
925
926 SILC_SCHEDULE_LOCK(schedule);
927
928 if (silc_hash_table_find(schedule->fd_queue, SILC_32_TO_PTR(fd),
929 NULL, (void *)&task)) {
930 if (!schedule_ops.schedule_fd(schedule, schedule->internal, task, mask)) {
931 SILC_SCHEDULE_UNLOCK(schedule);
932 return FALSE;
933 }
934 task->events = mask;
935 if (silc_unlikely(send_events) && mask) {
936 task->revents = mask;
937 silc_schedule_dispatch_fd(schedule);
938 }
939
940 /* Call notify callback */
941 if (schedule->notify)
942 schedule->notify(schedule, TRUE, (SilcTask)task,
943 TRUE, task->fd, mask, 0, 0,
944 schedule->notify_context);
945 }
946
947 SILC_SCHEDULE_UNLOCK(schedule);
948
949 return TRUE;
950 }
951
952 /* Returns the file descriptor's current requested event mask. */
953
silc_schedule_get_fd_events(SilcSchedule schedule,SilcUInt32 fd)954 SilcTaskEvent silc_schedule_get_fd_events(SilcSchedule schedule,
955 SilcUInt32 fd)
956 {
957 SilcTaskFd task;
958 SilcTaskEvent event = 0;
959
960 if (silc_unlikely(!schedule->valid))
961 return 0;
962
963 SILC_SCHEDULE_LOCK(schedule);
964 if (silc_hash_table_find(schedule->fd_queue, SILC_32_TO_PTR(fd),
965 NULL, (void *)&task))
966 event = task->events;
967 SILC_SCHEDULE_UNLOCK(schedule);
968
969 return event;
970 }
971
972 /* Removes a file descriptor from listen list. */
973
silc_schedule_unset_listen_fd(SilcSchedule schedule,SilcUInt32 fd)974 void silc_schedule_unset_listen_fd(SilcSchedule schedule, SilcUInt32 fd)
975 {
976 silc_schedule_set_listen_fd(schedule, fd, 0, FALSE);
977 }
978