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
24 /* Our "select()" for WIN32. This mimics the behaviour of select() system
25    call. It does not call the Winsock's select() though. Its functions
26    are derived from GLib's g_poll() and from some old Xemacs's sys_select().
27
28    This makes following assumptions, which I don't know whether they
29    are correct or not:
30
31    o writefds are ignored, if set this will return immediately.
32    o exceptfds are ignored totally
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    MSDN References:
42
43    o http://msdn.microsoft.com/library/default.asp?
44      url=/library/en-us/winui/hh/winui/messques_77zk.asp 
45
46 */
47
48 int silc_select(int n, fd_set *readfds, fd_set *writefds,
49                 fd_set *exceptfds, struct timeval *timeout)
50 {
51   HANDLE handles[MAXIMUM_WAIT_OBJECTS];
52   DWORD ready, curtime, timeo;
53   int nhandles = 0, i;
54   MSG msg;
55
56   /* Check fd sets (ignoring the exceptfds) */
57   if (readfds) {
58     for (i = 0; i < n - 1; i++)
59       if (FD_ISSET(i, readfds))
60         handles[nhandles++] = (HANDLE)i;
61
62     FD_ZERO(readfds);
63   }
64
65   /* If writefds is set then return immediately */
66   if (writefds) {
67     for (i = 0; i < n - 1; i++)
68       if (FD_ISSET(i, writefds))
69         return 1;
70   }
71
72   timeo = (timeout ? (timeout.tv_sec * 1000) + (timeout.tv_usec / 1000) :
73            INFINITE);
74
75   /* If we have nothing to wait and timeout is set then register a timeout
76      and wait just for windows messages. */
77   if (nhandles == 0 && timeout) {
78     UINT timer = SetTimer(NULL, 0, timeo, NULL);
79     if (timer) {
80       WaitMessage();
81       KillTimer(NULL, timer);
82
83       while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
84         TranslateMessage(&msg); 
85         DispatchMessage(&msg); 
86       }
87
88       return 0;
89     }
90   }
91
92  retry:
93   curtime = GetTickCount();
94   ready = MsgWaitForMultipleObjects(nhandles, handles, FALSE, timeo, 
95                                     QS_ALLINPUT);
96
97   if (ready == WAIT_FAILED) {
98     /* Wait failed with error */
99     SILC_LOG_WARNING(("WaitForMultipleObjects() failed"));
100     return -1;
101
102   } else if (ready >= WAIT_ABANDONED_0 &&
103              ready < WAIT_ABANDONED_0 + nhandles) {
104     /* Signal abandoned */
105     SILC_LOG_WARNING(("WaitForMultipleObjects() failed (ABANDONED)"));
106     return -1;
107   } else if (ready == WAIT_TIMEOUT) {
108     /* Timeout */
109     return 0;
110   } else if (ready == WAIT_OBJECT_0 + nhandles) {
111     /* Windows messages. The MSDN online says that if the application
112        creates a window then its main loop (and we're assuming that
113        it is our SILC Scheduler) must handle the Windows messages, so do
114        it here as the MSDN suggests. */
115     while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
116       TranslateMessage(&msg); 
117       DispatchMessage(&msg); 
118     }
119
120     /* If timeout is set then we must update the timeout since we won't
121        return and we will give the wait another try. */
122     if (timeo != INFINITE) {
123       timeo -= GetTickCount() - curtime;
124       if (timeo < 0)
125         timeo = 0;
126     }
127
128     /* Give the wait another try */
129    goto retry;
130   } else if (ready >= WAIT_OBJECT_0 && ready < WAIT_OBJECT_0 + nhandles &&
131              readfds) {
132     /* Some other event, like SOCKET or something. */
133
134     /* Go through all fds even though only one was set. This is to avoid
135        starvation of high numbered fds. */
136     ready -= WAIT_OBJECT_0;
137     i = 0;
138     do {
139       /* Set the handle to fd set */
140       FD_SET(handle[ready], readfds);
141       i++;
142
143       /* Check the status of the next handle and set it's fd to the fd
144          set if data is available. */
145       while (++ready < n)
146         if (WaitForSingleObject(handle[ready], 0) == WAIT_OBJECT_0)
147           break;
148     } while (ready < n);
149
150     return i;
151   }
152
153   return -1;
154 }