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