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