Added SILC Server library.
[silc.git] / lib / silcutil / win32 / silcwin32schedule.c
1 /*
2
3   silcwin32schedule.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2001 - 2005 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 "silc.h"
22
23 /* Our "select()" for WIN32. This mimics the behaviour of select() system
24    call. It does not call the Winsock's select() though. Its functions
25    are derived from GLib's g_poll() and from some old Xemacs's sys_select().
26
27    This makes following assumptions, which I don't know whether they
28    are correct or not:
29
30    o SILC_TASK_WRITE is ignored, if set this will return immediately.
31    o If all arguments except timeout are NULL then this will register
32      a timeout with SetTimer and will wait just for Windows messages
33      with WaitMessage.
34    o MsgWaitForMultipleObjects is used to wait all kind of events, this
35      includes SOCKETs and Windows messages.
36    o All Windows messages are dispatched from this function.
37    o The Operating System has Winsock 2.
38
39    References:
40
41    o http://msdn.microsoft.com/library/default.asp?
42      url=/library/en-us/winui/hh/winui/messques_77zk.asp
43    o http://msdn.microsoft.com/library/default.asp?
44      url=/library/en-us/winsock/hh/winsock/apistart_9g1e.asp
45    o http://msdn.microsoft.com/library/default.asp?
46      url=/library/en-us/dnmgmt/html/msdn_getpeek.asp
47    o http://developer.novell.com/support/winsock/doc/toc.htm
48
49 */
50
51 int silc_select(SilcSchedule schedule, void *context);
52 {
53   SilcHashTableList htl;
54   SilcTaskFd task;
55   HANDLE handles[MAXIMUM_WAIT_OBJECTS];
56   DWORD ready, curtime;
57   LONG timeo;
58   MSG msg;
59   int nhandles = 0, i, fd;
60
61   silc_hash_table_list(schedule->fd_queue, &htl);
62   while (silc_hash_table_get(&htl, (void **)&fd, (void **)&task)) {
63     if (!task->events)
64       continue;
65     if (nhandles >= MAXIMUM_WAIT_OBJECTS)
66       break;
67
68     if (task->events & SILC_TASK_READ)
69       handles[nhandles++] = (HANDLE)fd;
70
71     /* If writing then just set the bit and return */
72     if (task->events & SILC_TASK_WRITE) {
73       task->revents = SILC_TASK_WRITE;
74       return 1;
75     }
76
77     task->revents = 0;
78   }
79   silc_hash_table_list_reset(&htl);
80
81   timeo = (schedule->has_timeout ? ((schedule->timeout.tv_sec * 1000) +
82                                     (schedule->timeout.tv_usec / 1000))
83            : INFINITE);
84
85   /* If we have nothing to wait and timeout is set then register a timeout
86      and wait just for windows messages. */
87   if (nhandles == 0 && schedule->has_timeout) {
88     SILC_SCHEDULE_UNLOCK(schedule);
89     UINT timer = SetTimer(NULL, 0, timeo, NULL);
90     curtime = GetTickCount();
91     while (timer) {
92       WaitMessage();
93
94       while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
95         if (msg.message == WM_TIMER) {
96           KillTimer(NULL, timer);
97           SILC_SCHEDULE_LOCK(schedule);
98           return 0;
99         }
100         TranslateMessage(&msg);
101         DispatchMessage(&msg);
102       }
103
104       KillTimer(NULL, timer);
105       if (timeo != INFINITE) {
106         timeo -= GetTickCount() - curtime;
107         curtime = GetTickCount();
108         if (timeo < 0)
109           timeo = 0;
110       }
111       timer = SetTimer(NULL, 0, timeo, NULL);
112     }
113     SILC_SCHEDULE_LOCK(schedule);
114   }
115
116   SILC_SCHEDULE_UNLOCK(schedule);
117  retry:
118   curtime = GetTickCount();
119   ready = MsgWaitForMultipleObjects(nhandles, handles, FALSE, timeo,
120                                     QS_ALLINPUT);
121   SILC_SCHEDULE_LOCK(schedule);
122
123   if (ready == WAIT_FAILED) {
124     /* Wait failed with error */
125     SILC_LOG_WARNING(("WaitForMultipleObjects() failed"));
126     return -1;
127   } else if (ready >= WAIT_ABANDONED_0 &&
128              ready < WAIT_ABANDONED_0 + nhandles) {
129     /* Signal abandoned */
130     SILC_LOG_WARNING(("WaitForMultipleObjects() failed (ABANDONED)"));
131     return -1;
132   } else if (ready == WAIT_TIMEOUT) {
133     /* Timeout */
134     return 0;
135   } else if (ready == WAIT_OBJECT_0 + nhandles) {
136     /* Windows messages. The MSDN online says that if the application
137        creates a window then its main loop (and we're assuming that
138        it is our SILC Scheduler) must handle the Windows messages, so do
139        it here as the MSDN suggests. */
140     SILC_SCHEDULE_UNLOCK(schedule);
141     while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
142       TranslateMessage(&msg);
143       DispatchMessage(&msg);
144     }
145
146     /* If timeout is set then we must update the timeout since we won't
147        return and we will give the wait another try. */
148     if (timeo != INFINITE) {
149       timeo -= GetTickCount() - curtime;
150       curtime = GetTickCount();
151       if (timeo < 0)
152         timeo = 0;
153     }
154
155     /* Give the wait another try */
156    goto retry;
157   } else if (ready >= WAIT_OBJECT_0 && ready < WAIT_OBJECT_0 + nhandles) {
158     /* Some other event, like SOCKET or something. */
159
160     /* Go through all fds even though only one was set. This is to avoid
161        starvation of high numbered fds. */
162     nhandles = silc_hash_table_count(schedule->fd_queue);
163     ready -= WAIT_OBJECT_0;
164     do {
165       i = 0;
166       silc_hash_table_list(schedule->fd_queue, &htl);
167       while (silc_hash_table_get(&htl, (void **)&fd, (void **)&task)) {
168         if (!task->events)
169           continue;
170
171         if (fd == (int)handles[ready]) {
172           i++;
173           task->revents |= SILC_TASK_READ;
174           break;
175         }
176       }
177       silc_hash_table_list_reset(&htl);
178
179       /* Check the status of the next handle and set its fd to the fd
180          set if data is available. */
181       SILC_SCHEDULE_UNLOCK(schedule);
182       while (++ready < nhandles)
183         if (WaitForSingleObject(handles[ready], 0) == WAIT_OBJECT_0)
184           break;
185       SILC_SCHEDULE_LOCK(schedule);
186     } while (ready < nhandles);
187
188     return i + 1;
189   }
190
191   return -1;
192 }
193
194 #ifdef SILC_THREADS
195
196 /* Internal wakeup context. */
197 typedef struct {
198   HANDLE wakeup_sema;
199   SilcTask wakeup_task;
200 } *SilcWin32Wakeup;
201
202 SILC_TASK_CALLBACK(silc_schedule_wakeup_cb)
203 {
204   /* Nothing */
205 }
206
207 #endif /* SILC_THREADS */
208
209 /* Initializes the platform specific scheduler.  This for example initializes
210    the wakeup mechanism of the scheduler.  In multi-threaded environment
211    the scheduler needs to be wakenup when tasks are added or removed from
212    the task queues.  Returns context to the platform specific scheduler. */
213
214 void *silc_schedule_internal_init(SilcSchedule schedule, void *app_context)
215 {
216 #ifdef SILC_THREADS
217   SilcWin32Wakeup wakeup;
218 #endif
219
220   schedule->max_tasks = MAXIMUM_WAIT_OBJECTS;
221
222 #ifdef SILC_THREADS
223   wakeup = silc_calloc(1, sizeof(*wakeup));
224   if (!wakeup)
225     return NULL;
226
227   wakeup->wakeup_sema = CreateSemaphore(NULL, 0, 100, NULL);
228   if (!wakeup->wakeup_sema) {
229     silc_free(wakeup);
230     return NULL;
231   }
232
233   wakeup->wakeup_task =
234     silc_schedule_task_add(schedule, (int)wakeup->wakeup_sema,
235                            silc_schedule_wakeup_cb, wakeup,
236                            0, 0, SILC_TASK_FD);
237   if (!wakeup->wakeup_task) {
238     CloseHandle(wakeup->wakeup_sema);
239     silc_free(wakeup);
240     return NULL;
241   }
242
243   return (void *)wakeup;
244 #else
245   return NULL;
246 #endif
247 }
248
249 /* Uninitializes the platform specific scheduler context. */
250
251 void silc_schedule_internal_uninit(SilcSchedule schedule, void *context)
252 {
253 #ifdef SILC_THREADS
254   SilcWin32Wakeup wakeup = (SilcWin32Wakeup)context;
255
256   if (!wakeup)
257     return;
258
259   CloseHandle(wakeup->wakeup_sema);
260   silc_free(wakeup);
261 #endif
262 }
263
264 /* Wakes up the scheduler */
265
266 void silc_schedule_internal_wakeup(SilcSchedule schedule, void *context)
267 {
268 #ifdef SILC_THREADS
269   SilcWin32Wakeup wakeup = (SilcWin32Wakeup)context;
270
271   if (!wakeup)
272     return;
273
274   ReleaseSemaphore(wakeup->wakeup_sema, 1, NULL);
275 #endif
276 }
277
278 /* Register signal */
279
280 void silc_schedule_internal_signal_register(SilcSchedule schedule,
281                                             void *context,
282                                             SilcUInt32 signal,
283                                             SilcTaskCallback callback,
284                                             void *callback_context)
285 {
286
287 }
288
289 /* Unregister signal */
290
291 void silc_schedule_internal_signal_unregister(SilcSchedule schedule,
292                                               void *context,
293                                               SilcUInt32 signal,
294                                               SilcTaskCallback callback,
295                                               void *callback_context)
296 {
297
298 }
299
300 /* Mark signal to be called later. */
301
302 void silc_schedule_internal_signal_call(SilcSchedule schedule,
303                                         void *context, SilcUInt32 signal)
304 {
305
306 }
307
308 /* Call all signals */
309
310 void silc_schedule_internal_signals_call(SilcSchedule schedule,
311                                          void *context,
312                                          SilcSchedule schedule)
313 {
314
315 }
316
317 /* Block registered signals in scheduler. */
318
319 void silc_schedule_internal_signals_block(SilcSchedule schedule,
320                                           void *context)
321 {
322
323 }
324
325 /* Unblock registered signals in schedule. */
326
327 void silc_schedule_internal_signals_unblock(SilcSchedule schedule,
328                                             void *context)
329 {
330
331 }
332
333 const SilcScheduleOps schedule_ops =
334 {
335   silc_schedule_internal_init,
336   silc_schedule_internal_uninit,
337   silc_select,
338   silc_schedule_internal_wakeup,
339   silc_schedule_internal_signal_register,
340   silc_schedule_internal_signal_unregister,
341   silc_schedule_internal_signal_call,
342   silc_schedule_internal_signals_call,
343   silc_schedule_internal_signals_block,
344   silc_schedule_internal_signals_unblock,
345 };