Check for NULL internal context in all functions, as it may
[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 #define SIGNAL_COUNT 32
69
70 typedef struct {
71   SilcUInt32 signal;
72   SilcTaskCallback callback;
73   void *context;
74   bool call;
75 } SilcUnixSignal;
76
77 /* Internal context. */
78 typedef struct {
79   int wakeup_pipe[2];
80   SilcTask wakeup_task;
81   sigset_t signals;
82   sigset_t signals_blocked;
83   SilcUnixSignal signal_call[SIGNAL_COUNT];
84 } *SilcUnixScheduler;
85
86 #ifdef SILC_THREADS
87
88 SILC_TASK_CALLBACK(silc_schedule_wakeup_cb)
89 {
90   SilcUnixScheduler internal = (SilcUnixScheduler)context;
91   unsigned char c;
92
93   read(internal->wakeup_pipe[0], &c, 1);
94 }
95
96 #endif /* SILC_THREADS */
97
98 /* Initializes the platform specific scheduler.  This for example initializes
99    the wakeup mechanism of the scheduler.  In multi-threaded environment
100    the scheduler needs to be wakenup when tasks are added or removed from
101    the task queues.  Returns context to the platform specific scheduler. */
102
103 void *silc_schedule_internal_init(SilcSchedule schedule)
104 {
105   SilcUnixScheduler internal;
106
107   internal = silc_calloc(1, sizeof(*internal));
108   if (!internal)
109     return NULL;
110
111   sigemptyset(&internal->signals);
112
113 #ifdef SILC_THREADS
114   if (pipe(internal->wakeup_pipe)) {
115     SILC_LOG_ERROR(("pipe() fails: %s", strerror(errno)));
116     silc_free(internal);
117     return NULL;
118   }
119
120   internal->wakeup_task = 
121     silc_schedule_task_add(schedule, internal->wakeup_pipe[0],
122                            silc_schedule_wakeup_cb, internal,
123                            0, 0, SILC_TASK_FD, 
124                            SILC_TASK_PRI_NORMAL);
125   if (!internal->wakeup_task) {
126     SILC_LOG_ERROR(("Could not add a wakeup task, threads won't work"));
127     close(internal->wakeup_pipe[0]);
128     close(internal->wakeup_pipe[1]);
129     silc_free(internal);
130     return NULL;
131   }
132 #endif
133
134   return (void *)internal;
135 }
136
137 /* Uninitializes the platform specific scheduler context. */
138
139 void silc_schedule_internal_uninit(void *context)
140 {
141   SilcUnixScheduler internal = (SilcUnixScheduler)context;
142
143   if (!internal)
144     return;
145
146 #ifdef SILC_THREADS
147   close(internal->wakeup_pipe[0]);
148   close(internal->wakeup_pipe[1]);
149 #endif
150
151   silc_free(internal);
152 }
153
154 /* Wakes up the scheduler */
155
156 void silc_schedule_internal_wakeup(void *context)
157 {
158 #ifdef SILC_THREADS
159   SilcUnixScheduler internal = (SilcUnixScheduler)context;
160
161   if (!internal)
162     return;
163
164   write(internal->wakeup_pipe[1], "!", 1);
165 #endif
166 }
167
168 void silc_schedule_internal_signal_register(void *context,
169                                             SilcUInt32 signal,
170                                             SilcTaskCallback callback,
171                                             void *callback_context)
172 {
173   SilcUnixScheduler internal = (SilcUnixScheduler)context;
174   int i;
175
176   if (!internal)
177     return;
178
179   sigprocmask(SIG_BLOCK, &internal->signals, &internal->signals_blocked);
180
181   for (i = 0; i < SIGNAL_COUNT; i++) {
182     if (!internal->signal_call[i].signal) {
183       internal->signal_call[i].signal = signal;
184       internal->signal_call[i].callback = callback;
185       internal->signal_call[i].context = callback_context;
186       internal->signal_call[i].call = FALSE;
187       break;
188     }
189   }
190
191   sigprocmask(SIG_SETMASK, &internal->signals_blocked, NULL);
192   sigaddset(&internal->signals, signal);
193 }
194
195 void silc_schedule_internal_signal_unregister(void *context,
196                                               SilcUInt32 signal,
197                                               SilcTaskCallback callback,
198                                               void *callback_context)
199 {
200   SilcUnixScheduler internal = (SilcUnixScheduler)context;
201   int i;
202
203   if (!internal)
204     return;
205
206   sigprocmask(SIG_BLOCK, &internal->signals, &internal->signals_blocked);
207
208   for (i = 0; i < SIGNAL_COUNT; i++) {
209     if (internal->signal_call[i].signal == signal &&
210         internal->signal_call[i].callback == callback &&
211         internal->signal_call[i].context == callback_context) {
212       internal->signal_call[i].signal = 0;
213       internal->signal_call[i].callback = NULL;
214       internal->signal_call[i].context = NULL;
215       internal->signal_call[i].call = FALSE;
216     }
217   }
218
219   sigprocmask(SIG_SETMASK, &internal->signals_blocked, NULL);
220   sigdelset(&internal->signals, signal);
221 }
222
223 /* Mark signal to be called later. */
224
225 void silc_schedule_internal_signal_call(void *context, SilcUInt32 signal)
226 {
227   SilcUnixScheduler internal = (SilcUnixScheduler)context;
228   int i;
229
230   if (!internal)
231     return;
232
233   sigprocmask(SIG_BLOCK, &internal->signals, &internal->signals_blocked);
234
235   for (i = 0; i < SIGNAL_COUNT; i++) {
236     if (internal->signal_call[i].signal == signal)
237       internal->signal_call[i].call = TRUE;
238   }
239
240   sigprocmask(SIG_SETMASK, &internal->signals_blocked, NULL);
241 }
242                                         
243 /* Call all signals */
244
245 void silc_schedule_internal_signals_call(void *context,
246                                          SilcSchedule schedule)
247 {
248   SilcUnixScheduler internal = (SilcUnixScheduler)context;
249   int i;
250
251   if (!internal)
252     return;
253
254   sigprocmask(SIG_BLOCK, &internal->signals, &internal->signals_blocked);
255
256   for (i = 0; i < SIGNAL_COUNT; i++) {
257     if (internal->signal_call[i].call &&
258         internal->signal_call[i].callback) {
259       internal->signal_call[i].callback(schedule, SILC_TASK_INTERRUPT,
260                                         internal->signal_call[i].signal,
261                                         internal->signal_call[i].context);
262       internal->signal_call[i].call = FALSE;
263     }
264   }
265
266   sigprocmask(SIG_SETMASK, &internal->signals_blocked, NULL);
267 }
268
269 /* Block registered signals in scheduler. */
270
271 void silc_schedule_internal_signals_block(void *context)
272 {
273   SilcUnixScheduler internal = (SilcUnixScheduler)context;
274
275   if (!internal)
276     return;
277
278   sigprocmask(SIG_BLOCK, &internal->signals, &internal->signals_blocked);
279 }
280
281 /* Unblock registered signals in schedule. */
282
283 void silc_schedule_internal_signals_unblock(void *context)
284 {
285   SilcUnixScheduler internal = (SilcUnixScheduler)context;
286
287   if (!internal)
288     return;
289
290   sigprocmask(SIG_SETMASK, &internal->signals_blocked, NULL);
291 }