Added signals suppor to shceduler.
[crypto.git] / lib / silcutil / unix / silcunixschedule.c
1 /*
2
3   silcunixschedule.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1998 - 2001 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 2 of the License, or
12   (at your option) any later version.
13   
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20 /* $Id$ */
21
22 #include "silcincludes.h"
23 #include "silcschedule_i.h"
24
25 /* Calls normal select() system call. */
26
27 int silc_select(SilcScheduleFd fds, SilcUInt32 fds_count, 
28                 struct timeval *timeout)
29 {
30   fd_set in, out;
31   int ret, i, max_fd = 0;
32
33   FD_ZERO(&in);
34   FD_ZERO(&out);
35
36   for (i = 0; i < fds_count; i++) {
37     if (!fds[i].events)
38       continue;
39
40     if (fds[i].fd > max_fd)
41       max_fd = fds[i].fd;
42
43     if (fds[i].events & SILC_TASK_READ)
44       FD_SET(fds[i].fd, &in);
45     if (fds[i].events & SILC_TASK_WRITE)
46       FD_SET(fds[i].fd, &out);
47
48     fds[i].revents = 0;
49   }
50
51   ret = select(max_fd + 1, &in, &out, NULL, timeout);
52   if (ret <= 0)
53     return ret;
54
55   for (i = 0; i < fds_count; i++) {
56     if (!fds[i].events)
57       continue;
58
59     if (FD_ISSET(fds[i].fd, &in))
60       fds[i].revents |= SILC_TASK_READ;
61     if (FD_ISSET(fds[i].fd, &out))
62       fds[i].revents |= SILC_TASK_WRITE;
63   }
64
65   return ret;
66 }
67
68 /* Internal context. */
69 typedef struct {
70   int wakeup_pipe[2];
71   SilcTask wakeup_task;
72   sigset_t signals;
73   sigset_t signals_blocked;
74 } *SilcUnixScheduler;
75
76 #ifdef SILC_THREADS
77
78 SILC_TASK_CALLBACK(silc_schedule_wakeup_cb)
79 {
80   SilcUnixScheduler internal = (SilcUnixScheduler)context;
81   unsigned char c;
82
83   read(internal->wakeup_pipe[0], &c, 1);
84 }
85
86 #endif /* SILC_THREADS */
87
88 /* Initializes the platform specific scheduler.  This for example initializes
89    the wakeup mechanism of the scheduler.  In multi-threaded environment
90    the scheduler needs to be wakenup when tasks are added or removed from
91    the task queues.  Returns context to the platform specific scheduler. */
92
93 void *silc_schedule_internal_init(SilcSchedule schedule)
94 {
95   SilcUnixScheduler internal;
96
97   internal = silc_calloc(1, sizeof(*internal));
98   if (!internal)
99     return NULL;
100
101   sigemptyset(&internal->signals);
102
103 #ifdef SILC_THREADS
104   if (pipe(internal->wakeup_pipe)) {
105     silc_free(internal);
106     return NULL;
107   }
108
109   internal->wakeup_task = 
110     silc_schedule_task_add(schedule, internal->wakeup_pipe[0],
111                            silc_schedule_wakeup_cb, internal,
112                            0, 0, SILC_TASK_FD, 
113                            SILC_TASK_PRI_NORMAL);
114   if (!internal->wakeup_task) {
115     close(internal->wakeup_pipe[0]);
116     close(internal->wakeup_pipe[1]);
117     silc_free(internal);
118     return NULL;
119   }
120 #endif
121
122   return (void *)internal;
123 }
124
125 /* Uninitializes the platform specific scheduler context. */
126
127 void silc_schedule_internal_uninit(void *context)
128 {
129   SilcUnixScheduler internal = (SilcUnixScheduler)context;
130
131   if (!internal)
132     return;
133
134 #ifdef SILC_THREADS
135   close(internal->wakeup_pipe[0]);
136   close(internal->wakeup_pipe[1]);
137 #endif
138
139   silc_free(internal);
140 }
141
142 /* Wakes up the scheduler */
143
144 void silc_schedule_internal_wakeup(void *context)
145 {
146 #ifdef SILC_THREADS
147   SilcUnixScheduler internal = (SilcUnixScheduler)context;
148
149   if (!internal)
150     return;
151
152   write(internal->wakeup_pipe[1], "!", 1);
153 #endif
154 }
155
156 void silc_schedule_internal_signal_register(void *context,
157                                             SilcUInt32 signal)
158 {
159   SilcUnixScheduler internal = (SilcUnixScheduler)context;
160   sigaddset(&internal->signals, signal);
161 }
162
163 void silc_schedule_internal_signal_unregister(void *context,
164                                               SilcUInt32 signal)
165 {
166   SilcUnixScheduler internal = (SilcUnixScheduler)context;
167   sigdelset(&internal->signals, signal);
168 }
169
170 /* Block registered signals in scheduler. */
171
172 void silc_schedule_internal_signals_block(void *context)
173 {
174   SilcUnixScheduler internal = (SilcUnixScheduler)context;
175   sigprocmask(SIG_BLOCK, &internal->signals, &internal->signals_blocked);
176 }
177
178 /* Unblock registered signals in schedule. */
179
180 void silc_schedule_internal_signals_unblock(void *context)
181 {
182   SilcUnixScheduler internal = (SilcUnixScheduler)context;
183   sigprocmask(SIG_SETMASK, &internal->signals_blocked, NULL);
184 }