1 /*
2 * tclUnixNotify.c --
3 *
4 * This file contains Unix-specific procedures for the notifier,
5 * which is the lowest-level part of the Tcl event loop. This file
6 * works together with ../generic/tclNotify.c.
7 *
8 * Copyright (c) 1995 Sun Microsystems, Inc.
9 *
10 * See the file "license.terms" for information on usage and redistribution
11 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
12 *
13 * SCCS: @(#) tclUnixNotfy.c 1.31 96/07/23 16:17:29
14 */
15
16 #include "tclInt.h"
17 #include "tclPort.h"
18 #include <signal.h>
19
20 #ifdef WIN32
21 #undef _DLL
22 #undef VOID
23 #include <windows.h>
24 #define _DLL 1
25 #endif
26
27 #if HAVE_SYS_SELECT_H && !defined(NFDBITS)
28 #include <sys/select.h>
29 #endif
30
31 /*
32 * The information below is used to provide read, write, and
33 * exception masks to select during calls to Tcl_DoOneEvent.
34 */
35
36 static fd_mask checkMasks[3*MASK_SIZE];
37 /* This array is used to build up the masks
38 * to be used in the next call to select.
39 * Bits are set in response to calls to
40 * Tcl_WatchFile. */
41 static fd_mask readyMasks[3*MASK_SIZE];
42 /* This array reflects the readable/writable
43 * conditions that were found to exist by the
44 * last call to select. */
45 static int numFdBits; /* Number of valid bits in checkMasks
46 * (one more than highest fd for which
47 * Tcl_WatchFile has been called). */
48
49 #if 1
50 static int eventsFound;
51 static int ignoreEvents;
52 #endif
53
54 /*
55 * Static routines in this file:
56 */
57
58 static int MaskEmpty _ANSI_ARGS_((long *maskPtr));
59
TclWinFlushEvents()60 extern void TclWinFlushEvents()
61 {
62 ignoreEvents = 1;
63 while (Tcl_DoOneEvent(TCL_DONT_WAIT|TCL_WINDOW_EVENTS|TCL_IDLE_EVENTS)) {
64 }
65 ignoreEvents = 0;
66 }
67
68 /*
69 *----------------------------------------------------------------------
70 *
71 * Tcl_WatchFile --
72 *
73 * Arrange for Tcl_DoOneEvent to include this file in the masks
74 * for the next call to select. This procedure is invoked by
75 * event sources, which are in turn invoked by Tcl_DoOneEvent
76 * before it invokes select.
77 *
78 * Results:
79 * None.
80 *
81 * Side effects:
82 *
83 * The notifier will generate a file event when the I/O channel
84 * given by fd next becomes ready in the way indicated by mask.
85 * If fd is already registered then the old mask will be replaced
86 * with the new one. Once the event is sent, the notifier will
87 * not send any more events about the fd until the next call to
88 * Tcl_NotifyFile.
89 *
90 *----------------------------------------------------------------------
91 */
92
93 void
Tcl_WatchFile(file,mask)94 Tcl_WatchFile(file, mask)
95 Tcl_File file; /* Generic file handle for a stream. */
96 int mask; /* OR'ed combination of TCL_READABLE,
97 * TCL_WRITABLE, and TCL_EXCEPTION:
98 * indicates conditions to wait for
99 * in select. */
100 {
101 int fd, type, index;
102 fd_mask bit;
103
104 fd = (int) Tcl_GetFileInfo(file, &type);
105
106 if (type != TCL_UNIX_FD) {
107 panic("Tcl_WatchFile: unexpected file type");
108 }
109
110 if (fd >= FD_SETSIZE) {
111 panic("Tcl_WatchFile can't handle file id %d", fd);
112 }
113
114 index = fd/(NBBY*sizeof(fd_mask));
115 bit = 1 << (fd%(NBBY*sizeof(fd_mask)));
116 if (mask & TCL_READABLE) {
117 checkMasks[index] |= bit;
118 }
119 if (mask & TCL_WRITABLE) {
120 (checkMasks+MASK_SIZE)[index] |= bit;
121 }
122 if (mask & TCL_EXCEPTION) {
123 (checkMasks+2*(MASK_SIZE))[index] |= bit;
124 }
125 if (numFdBits <= fd) {
126 numFdBits = fd+1;
127 }
128 }
129
130 #if 1
Tcl_NumEventsFound()131 extern int Tcl_NumEventsFound()
132 {
133 return eventsFound;
134 }
135 #endif
136
137 /*
138 *----------------------------------------------------------------------
139 *
140 * Tcl_FileReady --
141 *
142 * Indicates what conditions (readable, writable, etc.) were
143 * present on a file the last time the notifier invoked select.
144 * This procedure is typically invoked by event sources to see
145 * if they should queue events.
146 *
147 * Results:
148 * The return value is 0 if none of the conditions specified by mask
149 * was true for fd the last time the system checked. If any of the
150 * conditions were true, then the return value is a mask of those
151 * that were true.
152 *
153 * Side effects:
154 * None.
155 *
156 *----------------------------------------------------------------------
157 */
158
159 int
Tcl_FileReady(file,mask)160 Tcl_FileReady(file, mask)
161 Tcl_File file; /* Generic file handle for a stream. */
162 int mask; /* OR'ed combination of TCL_READABLE,
163 * TCL_WRITABLE, and TCL_EXCEPTION:
164 * indicates conditions caller cares about. */
165 {
166 int index, result, type, fd;
167 fd_mask bit;
168
169 fd = (int) Tcl_GetFileInfo(file, &type);
170 if (type != TCL_UNIX_FD) {
171 panic("Tcl_FileReady: unexpected file type");
172 }
173
174 index = fd/(NBBY*sizeof(fd_mask));
175 bit = 1 << (fd%(NBBY*sizeof(fd_mask)));
176 result = 0;
177 if ((mask & TCL_READABLE) && (readyMasks[index] & bit)) {
178 result |= TCL_READABLE;
179 }
180 if ((mask & TCL_WRITABLE) && ((readyMasks+MASK_SIZE)[index] & bit)) {
181 result |= TCL_WRITABLE;
182 }
183 if ((mask & TCL_EXCEPTION) && ((readyMasks+(2*MASK_SIZE))[index] & bit)) {
184 result |= TCL_EXCEPTION;
185 }
186 return result;
187 }
188
189 /*
190 *----------------------------------------------------------------------
191 *
192 * MaskEmpty --
193 *
194 * Returns nonzero if mask is empty (has no bits set).
195 *
196 * Results:
197 * Nonzero if the mask is empty, zero otherwise.
198 *
199 * Side effects:
200 * None
201 *
202 *----------------------------------------------------------------------
203 */
204
205 static int
MaskEmpty(maskPtr)206 MaskEmpty(maskPtr)
207 long *maskPtr;
208 {
209 long *runPtr, *tailPtr;
210 int found, sz;
211
212 sz = 3 * ((MASK_SIZE) / sizeof(long)) * sizeof(fd_mask);
213 for (runPtr = maskPtr, tailPtr = maskPtr + sz, found = 0;
214 runPtr < tailPtr;
215 runPtr++) {
216 if (*runPtr != 0) {
217 found = 1;
218 break;
219 }
220 }
221 return !found;
222 }
223
224 /*
225 *----------------------------------------------------------------------
226 *
227 * Tcl_WaitForEvent --
228 *
229 * This procedure does the lowest level wait for events in a
230 * platform-specific manner. It uses information provided by
231 * previous calls to Tcl_WatchFile, plus the timePtr argument,
232 * to determine what to wait for and how long to wait.
233 *
234 * Results:
235 * The return value is normally TCL_OK. However, if there are
236 * no events to wait for (e.g. no files and no timers) so that
237 * the procedure would block forever, then it returns TCL_ERROR.
238 *
239 * Side effects:
240 * May put the process to sleep for a while, depending on timePtr.
241 * When this procedure returns, an event of interest to the application
242 * has probably, but not necessarily, occurred.
243 *
244 *----------------------------------------------------------------------
245 */
246
247 int
Tcl_WaitForEvent(timePtr)248 Tcl_WaitForEvent(timePtr)
249 Tcl_Time *timePtr; /* Specifies the maximum amount of time
250 * that this procedure should block before
251 * returning. The time is given as an
252 * interval, not an absolute wakeup time.
253 * NULL means block forever. */
254 {
255 struct timeval timeout, *timeoutPtr;
256 int numFound;
257 #ifdef WIN32
258 static int winFd = -1;
259 if (ignoreEvents)
260 return TCL_OK;
261 if (winFd < 0)
262 winFd = open("/dev/windows", 0);
263 Tcl_WatchFile(Tcl_GetFile((ClientData)winFd,TCL_UNIX_FD),TCL_READABLE);
264 #endif
265
266 memcpy((VOID *) readyMasks, (VOID *) checkMasks,
267 3*MASK_SIZE*sizeof(fd_mask));
268 if (timePtr == NULL) {
269 if ((numFdBits == 0) || (MaskEmpty((long *) readyMasks))) {
270 return TCL_ERROR;
271 }
272 timeoutPtr = NULL;
273 } else {
274 timeoutPtr = &timeout;
275 timeout.tv_sec = timePtr->sec;
276 timeout.tv_usec = timePtr->usec;
277 }
278 numFound = select(numFdBits, (SELECT_MASK *) &readyMasks[0],
279 (SELECT_MASK *) &readyMasks[MASK_SIZE],
280 (SELECT_MASK *) &readyMasks[2*MASK_SIZE], timeoutPtr);
281
282 #ifdef WIN32
283 if (FD_ISSET(winFd, ((SELECT_MASK *) &readyMasks[0])))
284 {
285 MSG msg;
286 GetMessage(&msg, NULL, 0, 0);
287 TranslateMessage(&msg);
288 DispatchMessage(&msg);
289 }
290 #endif
291
292 /*
293 * Some systems don't clear the masks after an error, so
294 * we have to do it here.
295 */
296
297 eventsFound = numFound;
298 if (numFound == -1) {
299 memset((VOID *) readyMasks, 0, 3*MASK_SIZE*sizeof(fd_mask));
300 }
301
302 /*
303 * Reset the check masks in preparation for the next call to
304 * select.
305 */
306
307 numFdBits = 0;
308 memset((VOID *) checkMasks, 0, 3*MASK_SIZE*sizeof(fd_mask));
309 return TCL_OK;
310 }
311
312 /*
313 *----------------------------------------------------------------------
314 *
315 * Tcl_Sleep --
316 *
317 * Delay execution for the specified number of milliseconds.
318 *
319 * Results:
320 * None.
321 *
322 * Side effects:
323 * Time passes.
324 *
325 *----------------------------------------------------------------------
326 */
327
328 void
Tcl_Sleep(ms)329 Tcl_Sleep(ms)
330 int ms; /* Number of milliseconds to sleep. */
331 {
332 static struct timeval delay;
333 Tcl_Time before, after;
334
335 /*
336 * The only trick here is that select appears to return early
337 * under some conditions, so we have to check to make sure that
338 * the right amount of time really has elapsed. If it's too
339 * early, go back to sleep again.
340 */
341
342 TclpGetTime(&before);
343 after = before;
344 after.sec += ms/1000;
345 after.usec += (ms%1000)*1000;
346 if (after.usec > 1000000) {
347 after.usec -= 1000000;
348 after.sec += 1;
349 }
350 while (1) {
351 delay.tv_sec = after.sec - before.sec;
352 delay.tv_usec = after.usec - before.usec;
353 if (delay.tv_usec < 0) {
354 delay.tv_usec += 1000000;
355 delay.tv_sec -= 1;
356 }
357
358 /*
359 * Special note: must convert delay.tv_sec to int before comparing
360 * to zero, since delay.tv_usec is unsigned on some platforms.
361 */
362
363 if ((((int) delay.tv_sec) < 0)
364 || ((delay.tv_usec == 0) && (delay.tv_sec == 0))) {
365 break;
366 }
367 (void) select(0, (SELECT_MASK *) 0, (SELECT_MASK *) 0,
368 (SELECT_MASK *) 0, &delay);
369 TclpGetTime(&before);
370 }
371 }
372
373