Integer type name change.
[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 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 /* 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(SilcScheduleFd fds, SilcUInt32 fds_count, struct timeval *timeout)
54 {
55   HANDLE handles[MAXIMUM_WAIT_OBJECTS];
56   DWORD ready, curtime, timeo;
57   int nhandles = 0, i;
58   MSG msg;
59
60   if (fds_count > MAXIMUM_WAIT_OBJECTS)
61     fds_count = MAXIMUM_WAIT_OBJECTS;
62
63   for (i = 0; i < fds_count; i++) {
64     if (!fds[i].events)
65       continue;
66
67     if (fds[i].events & SILC_TASK_READ)
68       handles[nhandles++] = (HANDLE)fds[i].fd;
69
70     /* If writing then just set the bit and return */
71     if (fds[i].events & SILC_TASK_WRITE) {
72       fds[i].revents = SILC_TASK_WRITE;
73       return 1;
74     }
75
76     fds[i].revents = 0;
77   }
78
79   timeo = (timeout ? (timeout->tv_sec * 1000) + (timeout->tv_usec / 1000) :
80            INFINITE);
81
82   /* If we have nothing to wait and timeout is set then register a timeout
83      and wait just for windows messages. */
84   if (nhandles == 0 && timeout) {
85     UINT timer = SetTimer(NULL, 0, timeo, NULL);
86     curtime = GetTickCount();
87     while (timer) {
88       WaitMessage();
89
90       while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
91         if (msg.message == WM_TIMER) {
92           KillTimer(NULL, timer);
93           return 0;
94         }
95         TranslateMessage(&msg); 
96         DispatchMessage(&msg); 
97       }
98
99       KillTimer(NULL, timer);
100       if (timeo != INFINITE) {
101         timeo -= GetTickCount() - curtime;
102         if (timeo < 0)
103           timeo = 0;
104       }
105       timer = SetTimer(NULL, 0, timeo, NULL);
106     }
107   }
108
109  retry:
110   curtime = GetTickCount();
111   ready = MsgWaitForMultipleObjects(nhandles, handles, FALSE, timeo, 
112                                     QS_ALLINPUT);
113
114   if (ready == WAIT_FAILED) {
115     /* Wait failed with error */
116     SILC_LOG_WARNING(("WaitForMultipleObjects() failed"));
117     return -1;
118   } else if (ready >= WAIT_ABANDONED_0 &&
119              ready < WAIT_ABANDONED_0 + nhandles) {
120     /* Signal abandoned */
121     SILC_LOG_WARNING(("WaitForMultipleObjects() failed (ABANDONED)"));
122     return -1;
123   } else if (ready == WAIT_TIMEOUT) {
124     /* Timeout */
125     return 0;
126   } else if (ready == WAIT_OBJECT_0 + nhandles) {
127     /* Windows messages. The MSDN online says that if the application
128        creates a window then its main loop (and we're assuming that
129        it is our SILC Scheduler) must handle the Windows messages, so do
130        it here as the MSDN suggests. */
131     while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
132       TranslateMessage(&msg); 
133       DispatchMessage(&msg); 
134     }
135
136     /* If timeout is set then we must update the timeout since we won't
137        return and we will give the wait another try. */
138     if (timeo != INFINITE) {
139       timeo -= GetTickCount() - curtime;
140       if (timeo < 0)
141         timeo = 0;
142     }
143
144     /* Give the wait another try */
145    goto retry;
146   } else if (ready >= WAIT_OBJECT_0 && ready < WAIT_OBJECT_0 + nhandles) {
147     /* Some other event, like SOCKET or something. */
148
149     /* Go through all fds even though only one was set. This is to avoid
150        starvation of high numbered fds. */
151     ready -= WAIT_OBJECT_0;
152     do {
153       for (i = 0; i < fds_count; i++) {
154         if (!fds[i].events)
155           continue;
156         
157         if (fds[i].fd == (int)handles[ready]) {
158           fds[i].revents |= SILC_TASK_READ;
159           break;
160         }
161       }
162
163       /* Check the status of the next handle and set its fd to the fd
164          set if data is available. */
165       while (++ready < fds_count)
166         if (WaitForSingleObject(handles[ready], 0) == WAIT_OBJECT_0)
167           break;
168     } while (ready < fds_count);
169
170     return i + 1;
171   }
172
173   return -1;
174 }
175
176 #ifdef SILC_THREADS
177
178 /* Internal wakeup context. */
179 typedef struct {
180   HANDLE wakeup_sema;
181   SilcTask wakeup_task;
182 } *SilcWin32Wakeup;
183
184 SILC_TASK_CALLBACK(silc_schedule_wakeup_cb)
185 {
186   /* Nothing */
187 }
188
189 #endif /* SILC_THREADS */
190
191 /* Initializes the wakeup of the scheduler. In multi-threaded environment
192    the scheduler needs to be wakenup when tasks are added or removed from
193    the task queues. This will initialize the wakeup for the scheduler.
194    Any tasks that needs to be registered must be registered to the `queue'.
195    It is guaranteed that the scheduler will automatically free any
196    registered tasks in this queue. This is system specific routine. */
197
198 void *silc_schedule_wakeup_init(SilcSchedule schedule)
199 {
200 #ifdef SILC_THREADS
201   SilcWin32Wakeup wakeup;
202
203   wakeup = silc_calloc(1, sizeof(*wakeup));
204
205   wakeup->wakeup_sema = CreateSemaphore(NULL, 0, 100, NULL);
206   if (!wakeup->wakeup_sema) {
207     silc_free(wakeup);
208     return NULL;
209   }
210
211   wakeup->wakeup_task = 
212     silc_schedule_task_add(schedule, (int)wakeup->wakeup_sema,
213                            silc_schedule_wakeup_cb, wakeup,
214                            0, 0, SILC_TASK_FD, 
215                            SILC_TASK_PRI_NORMAL);
216   if (!wakeup->wakeup_task) {
217     CloseHandle(wakeup->wakeup_sema);
218     silc_free(wakeup);
219     return NULL;
220   }
221
222   return (void *)wakeup;
223 #else
224   return NULL;
225 #endif
226 }
227
228 /* Uninitializes the system specific wakeup. */
229
230 void silc_schedule_wakeup_uninit(void *context)
231 {
232 #ifdef SILC_THREADS
233   SilcWin32Wakeup wakeup = (SilcWin32Wakeup)context;
234
235   if (!wakeup)
236     return;
237
238   CloseHandle(wakeup->wakeup_sema);
239   silc_free(wakeup);
240 #endif
241 }
242
243 /* Wakes up the scheduler */
244
245 void silc_schedule_wakeup_internal(void *context)
246 {
247 #ifdef SILC_THREADS
248   SilcWin32Wakeup wakeup = (SilcWin32Wakeup)context;
249
250   if (!wakeup)
251     return;
252
253   ReleaseSemaphore(wakeup->wakeup_sema, 1, NULL);
254 #endif
255 }