WIN32 compilation error fixes.
[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 - 2007 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 const SilcScheduleOps schedule_ops;
24
25 /* Our "select()" for WIN32. This mimics the behaviour of select() system
26    call. It does not call the Winsock's select() though. Its functions
27    are derived from GLib's g_poll() and from some old Xemacs's sys_select().
28
29    This makes following assumptions, which I don't know whether they
30    are correct or not:
31
32    o SILC_TASK_WRITE is ignored, if set this will return immediately.
33    o If all arguments except timeout are NULL then this will register
34      a timeout with SetTimer and will wait just for Windows messages
35      with WaitMessage.
36    o MsgWaitForMultipleObjects is used to wait all kind of events, this
37      includes SOCKETs and Windows messages.
38    o All Windows messages are dispatched from this function.
39    o The Operating System has Winsock 2.
40
41    References:
42
43    o http://msdn.microsoft.com/library/default.asp?
44      url=/library/en-us/winui/hh/winui/messques_77zk.asp
45    o http://msdn.microsoft.com/library/default.asp?
46      url=/library/en-us/winsock/hh/winsock/apistart_9g1e.asp
47    o http://msdn.microsoft.com/library/default.asp?
48      url=/library/en-us/dnmgmt/html/msdn_getpeek.asp
49    o http://developer.novell.com/support/winsock/doc/toc.htm
50
51 */
52
53 int silc_select(SilcSchedule schedule, void *context)
54 {
55   SilcHashTableList htl;
56   SilcTaskFd task;
57   HANDLE handles[MAXIMUM_WAIT_OBJECTS];
58   DWORD ready, curtime;
59   LONG timeo;
60   UINT timer;
61   MSG msg;
62   int nhandles = 0, fd;
63
64   silc_hash_table_list(schedule->fd_queue, &htl);
65   while (silc_hash_table_get(&htl, (void **)&fd, (void **)&task)) {
66     if (!task->events)
67       continue;
68     if (nhandles >= MAXIMUM_WAIT_OBJECTS)
69       break;
70
71     if (task->events & SILC_TASK_READ)
72       handles[nhandles++] = (HANDLE)fd;
73
74     /* If writing then just set the bit and return */
75     if (task->events & SILC_TASK_WRITE) {
76       task->revents = SILC_TASK_WRITE;
77       return 1;
78     }
79
80     task->revents = 0;
81   }
82   silc_hash_table_list_reset(&htl);
83   silc_list_init(schedule->fd_dispatch, struct SilcTaskStruct, next);
84
85   timeo = (schedule->has_timeout ? ((schedule->timeout.tv_sec * 1000) +
86                                     (schedule->timeout.tv_usec / 1000))
87            : INFINITE);
88
89   /* If we have nothing to wait and timeout is set then register a timeout
90      and wait just for windows messages. */
91   if (nhandles == 0 && schedule->has_timeout) {
92     SILC_SCHEDULE_UNLOCK(schedule);
93     timer = SetTimer(NULL, 0, timeo, NULL);
94     curtime = GetTickCount();
95     while (timer) {
96       WaitMessage();
97
98       while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
99         if (msg.message == WM_TIMER) {
100           KillTimer(NULL, timer);
101           SILC_SCHEDULE_LOCK(schedule);
102           return 0;
103         }
104         TranslateMessage(&msg);
105         DispatchMessage(&msg);
106       }
107
108       KillTimer(NULL, timer);
109       if (timeo != INFINITE) {
110         timeo -= GetTickCount() - curtime;
111         curtime = GetTickCount();
112         if (timeo < 0)
113           timeo = 0;
114       }
115       timer = SetTimer(NULL, 0, timeo, NULL);
116     }
117     SILC_SCHEDULE_LOCK(schedule);
118   }
119
120   SILC_SCHEDULE_UNLOCK(schedule);
121  retry:
122   curtime = GetTickCount();
123   ready = MsgWaitForMultipleObjects(nhandles, handles, FALSE, timeo,
124                                     QS_ALLINPUT);
125   SILC_SCHEDULE_LOCK(schedule);
126
127   if (ready == WAIT_FAILED) {
128     /* Wait failed with error */
129     SILC_LOG_WARNING(("WaitForMultipleObjects() failed"));
130     return -1;
131   } else if (ready >= WAIT_ABANDONED_0 &&
132              ready < WAIT_ABANDONED_0 + nhandles) {
133     /* Signal abandoned */
134     SILC_LOG_WARNING(("WaitForMultipleObjects() failed (ABANDONED)"));
135     return -1;
136   } else if (ready == WAIT_TIMEOUT) {
137     /* Timeout */
138     return 0;
139   } else if (ready == WAIT_OBJECT_0 + nhandles) {
140     /* Windows messages. The MSDN online says that if the application
141        creates a window then its main loop (and we're assuming that
142        it is our SILC Scheduler) must handle the Windows messages, so do
143        it here as the MSDN suggests. */
144     SILC_SCHEDULE_UNLOCK(schedule);
145     while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
146       TranslateMessage(&msg);
147       DispatchMessage(&msg);
148     }
149
150     /* If timeout is set then we must update the timeout since we won't
151        return and we will give the wait another try. */
152     if (timeo != INFINITE) {
153       timeo -= GetTickCount() - curtime;
154       curtime = GetTickCount();
155       if (timeo < 0)
156         timeo = 0;
157     }
158
159     /* Give the wait another try */
160     goto retry;
161   } else if (ready >= WAIT_OBJECT_0 && ready < WAIT_OBJECT_0 + nhandles) {
162     /* Some other event, like SOCKET or something. */
163
164     /* Go through all fds even though only one was set. This is to avoid
165        starvation of high numbered fds. */
166     nhandles = silc_hash_table_count(schedule->fd_queue);
167     ready -= WAIT_OBJECT_0;
168     do {
169       if (!silc_hash_table_find(schedule->fd_queue,
170                                 SILC_32_TO_PTR((SilcUInt32)handles[ready]),
171                                 NULL, (void *)&task))
172         break;
173
174       if (task->header.valid && task->events) {
175         task->revents |= SILC_TASK_READ;
176         silc_list_add(schedule->fd_dispatch, task);
177       }
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 silc_list_count(schedule->fd_dispatch);
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 (void *)1;
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 /* Schedule `task' with events `event_mask'. Zero `event_mask' unschedules. */
265
266 SilcBool silc_schedule_internal_schedule_fd(SilcSchedule schedule,
267                                             void *context,
268                                             SilcTaskFd task,
269                                             SilcTaskEvent event_mask)
270 {
271   return TRUE;
272 }
273
274 /* Wakes up the scheduler */
275
276 void silc_schedule_internal_wakeup(SilcSchedule schedule, void *context)
277 {
278 #ifdef SILC_THREADS
279   SilcWin32Wakeup wakeup = (SilcWin32Wakeup)context;
280
281   if (!wakeup)
282     return;
283
284   ReleaseSemaphore(wakeup->wakeup_sema, 1, NULL);
285 #endif
286 }
287
288 /* Register signal */
289
290 void silc_schedule_internal_signal_register(SilcSchedule schedule,
291                                             void *context,
292                                             SilcUInt32 signal,
293                                             SilcTaskCallback callback,
294                                             void *callback_context)
295 {
296
297 }
298
299 /* Unregister signal */
300
301 void silc_schedule_internal_signal_unregister(SilcSchedule schedule,
302                                               void *context,
303                                               SilcUInt32 signal)
304 {
305
306 }
307
308 /* Call all signals */
309
310 void silc_schedule_internal_signals_call(SilcSchedule schedule,
311                                          void *context)
312 {
313
314 }
315
316 /* Block registered signals in scheduler. */
317
318 void silc_schedule_internal_signals_block(SilcSchedule schedule,
319                                           void *context)
320 {
321
322 }
323
324 /* Unblock registered signals in schedule. */
325
326 void silc_schedule_internal_signals_unblock(SilcSchedule schedule,
327                                             void *context)
328 {
329
330 }
331
332 const SilcScheduleOps schedule_ops =
333 {
334   silc_schedule_internal_init,
335   silc_schedule_internal_uninit,
336   silc_select,
337   silc_schedule_internal_schedule_fd,
338   silc_schedule_internal_wakeup,
339   silc_schedule_internal_signal_register,
340   silc_schedule_internal_signal_unregister,
341   silc_schedule_internal_signals_call,
342   silc_schedule_internal_signals_block,
343   silc_schedule_internal_signals_unblock,
344 };