Added debugging.
[silc.git] / lib / silcutil / unix / silcunixschedule.c
1 /*
2
3   silcunixschedule.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1998 - 2004 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; version 2 of the License.
12
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.
17
18 */
19 /* $Id$ */
20
21 #include "silcincludes.h"
22 #include "silcschedule_i.h"
23
24 /* Calls normal select() system call. */
25
26 int silc_select(SilcScheduleFd fds, SilcUInt32 fds_count,
27                 struct timeval *timeout)
28 {
29   fd_set in, out;
30   int ret, i, max_fd = 0;
31
32   FD_ZERO(&in);
33   FD_ZERO(&out);
34
35   for (i = 0; i < fds_count; i++) {
36     if (!fds[i].events)
37       continue;
38
39     if (fds[i].fd > max_fd)
40       max_fd = fds[i].fd;
41
42     if (fds[i].events & SILC_TASK_READ)
43       FD_SET(fds[i].fd, &in);
44     if (fds[i].events & SILC_TASK_WRITE)
45       FD_SET(fds[i].fd, &out);
46
47     fds[i].revents = 0;
48   }
49
50   ret = select(max_fd + 1, &in, &out, NULL, timeout);
51   if (ret <= 0)
52     return ret;
53
54   for (i = 0; i < fds_count; i++) {
55     if (!fds[i].events)
56       continue;
57
58     if (FD_ISSET(fds[i].fd, &in))
59       fds[i].revents |= SILC_TASK_READ;
60     if (FD_ISSET(fds[i].fd, &out))
61       fds[i].revents |= SILC_TASK_WRITE;
62   }
63
64   return ret;
65 }
66
67 #define SIGNAL_COUNT 32
68
69 typedef struct {
70   SilcUInt32 signal;
71   SilcTaskCallback callback;
72   void *context;
73   bool call;
74 } SilcUnixSignal;
75
76 /* Internal context. */
77 typedef struct {
78   void *app_context;
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                                   void *app_context)
105 {
106   SilcUnixScheduler internal;
107
108   internal = silc_calloc(1, sizeof(*internal));
109   if (!internal)
110     return NULL;
111
112   sigemptyset(&internal->signals);
113
114 #ifdef SILC_THREADS
115   if (pipe(internal->wakeup_pipe)) {
116     SILC_LOG_ERROR(("pipe() fails: %s", strerror(errno)));
117     silc_free(internal);
118     return NULL;
119   }
120
121   internal->wakeup_task =
122     silc_schedule_task_add(schedule, internal->wakeup_pipe[0],
123                            silc_schedule_wakeup_cb, internal,
124                            0, 0, SILC_TASK_FD,
125                            SILC_TASK_PRI_NORMAL);
126   if (!internal->wakeup_task) {
127     SILC_LOG_ERROR(("Could not add a wakeup task, threads won't work"));
128     close(internal->wakeup_pipe[0]);
129     close(internal->wakeup_pipe[1]);
130     silc_free(internal);
131     return NULL;
132   }
133 #endif
134
135   internal->app_context = app_context;
136
137   return (void *)internal;
138 }
139
140 /* Uninitializes the platform specific scheduler context. */
141
142 void silc_schedule_internal_uninit(void *context)
143 {
144   SilcUnixScheduler internal = (SilcUnixScheduler)context;
145
146   if (!internal)
147     return;
148
149 #ifdef SILC_THREADS
150   close(internal->wakeup_pipe[0]);
151   close(internal->wakeup_pipe[1]);
152 #endif
153
154   silc_free(internal);
155 }
156
157 /* Wakes up the scheduler */
158
159 void silc_schedule_internal_wakeup(void *context)
160 {
161 #ifdef SILC_THREADS
162   SilcUnixScheduler internal = (SilcUnixScheduler)context;
163
164   if (!internal)
165     return;
166
167   write(internal->wakeup_pipe[1], "!", 1);
168 #endif
169 }
170
171 void silc_schedule_internal_signal_register(void *context,
172                                             SilcUInt32 signal,
173                                             SilcTaskCallback callback,
174                                             void *callback_context)
175 {
176   SilcUnixScheduler internal = (SilcUnixScheduler)context;
177   int i;
178
179   if (!internal)
180     return;
181
182   SILC_LOG_DEBUG(("Registering signal %d", signal));
183
184   silc_schedule_internal_signals_block(context);
185
186   for (i = 0; i < SIGNAL_COUNT; i++) {
187     if (!internal->signal_call[i].signal) {
188       internal->signal_call[i].signal = signal;
189       internal->signal_call[i].callback = callback;
190       internal->signal_call[i].context = callback_context;
191       internal->signal_call[i].call = FALSE;
192       break;
193     }
194   }
195
196   silc_schedule_internal_signals_unblock(context);
197   sigaddset(&internal->signals, signal);
198 }
199
200 void silc_schedule_internal_signal_unregister(void *context,
201                                               SilcUInt32 signal,
202                                               SilcTaskCallback callback,
203                                               void *callback_context)
204 {
205   SilcUnixScheduler internal = (SilcUnixScheduler)context;
206   int i;
207
208   if (!internal)
209     return;
210
211   SILC_LOG_DEBUG(("Unregistering signal %d", signal));
212
213   silc_schedule_internal_signals_block(context);
214
215   for (i = 0; i < SIGNAL_COUNT; i++) {
216     if (internal->signal_call[i].signal == signal &&
217         internal->signal_call[i].callback == callback &&
218         internal->signal_call[i].context == callback_context) {
219       internal->signal_call[i].signal = 0;
220       internal->signal_call[i].callback = NULL;
221       internal->signal_call[i].context = NULL;
222       internal->signal_call[i].call = FALSE;
223     }
224   }
225
226   silc_schedule_internal_signals_unblock(context);
227   sigdelset(&internal->signals, signal);
228 }
229
230 /* Mark signal to be called later. */
231
232 void silc_schedule_internal_signal_call(void *context, SilcUInt32 signal)
233 {
234   SilcUnixScheduler internal = (SilcUnixScheduler)context;
235   int i;
236
237   if (!internal)
238     return;
239
240   silc_schedule_internal_signals_block(context);
241
242   for (i = 0; i < SIGNAL_COUNT; i++) {
243     if (internal->signal_call[i].signal == signal) {
244       internal->signal_call[i].call = TRUE;
245       SILC_LOG_DEBUG(("Scheduling signal %d to be called",
246                       internal->signal_call[i].signal));
247     }
248   }
249
250   silc_schedule_internal_signals_unblock(context);
251 }
252
253 /* Call all signals */
254
255 void silc_schedule_internal_signals_call(void *context,
256                                          SilcSchedule schedule)
257 {
258   SilcUnixScheduler internal = (SilcUnixScheduler)context;
259   int i;
260
261   SILC_LOG_DEBUG(("Start"));
262
263   if (!internal)
264     return;
265
266   silc_schedule_internal_signals_block(context);
267
268   for (i = 0; i < SIGNAL_COUNT; i++) {
269     if (internal->signal_call[i].call &&
270         internal->signal_call[i].callback) {
271       SILC_LOG_DEBUG(("Calling signal %d callback",
272                       internal->signal_call[i].signal));
273       internal->signal_call[i].callback(schedule, internal->app_context,
274                                         SILC_TASK_INTERRUPT,
275                                         internal->signal_call[i].signal,
276                                         internal->signal_call[i].context);
277       internal->signal_call[i].call = FALSE;
278     }
279   }
280
281   silc_schedule_internal_signals_unblock(context);
282 }
283
284 /* Block registered signals in scheduler. */
285
286 void silc_schedule_internal_signals_block(void *context)
287 {
288   SilcUnixScheduler internal = (SilcUnixScheduler)context;
289
290   if (!internal)
291     return;
292
293   sigprocmask(SIG_BLOCK, &internal->signals, &internal->signals_blocked);
294 }
295
296 /* Unblock registered signals in schedule. */
297
298 void silc_schedule_internal_signals_unblock(void *context)
299 {
300   SilcUnixScheduler internal = (SilcUnixScheduler)context;
301
302   if (!internal)
303     return;
304
305   sigprocmask(SIG_SETMASK, &internal->signals_blocked, NULL);
306 }