Fix three PyChecker-detected gotchas.
[python/dscho.git] / Mac / tclmods / tclSelectNotify.c
bloba728442fc1561c1ddbc3d661f3788ca7be8af17f
1 /*
2 * tclSelectNotify.c --
4 * Partial even handling, select only. This file is adapted from TclUnixNotify.c, and
5 * meant as an add-in for Mac (and possibly Windows) environments where select *is* available.
6 * TclMacNotify.c works together with this file.
8 * Copyright (c) 1995-1997 Sun Microsystems, Inc.
10 * See the file "license.terms" for information on usage and redistribution
11 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13 * SCCS: @(#) tclUnixNotfy.c 1.42 97/07/02 20:55:44
16 #if defined(__CFM68K__) && !defined(__USING_STATIC_LIBS__)
17 #pragma import on
18 #endif
20 #include <unistd.h>
22 #if defined(__CFM68K__) && !defined(__USING_STATIC_LIBS__)
23 #pragma import reset
24 #endif
26 #ifdef USE_GUSI
27 /* Move this include up otherwise tclPort.h tried to redefine signals */
28 #include <sys/signal.h>
29 #endif
30 #include "tclInt.h"
31 #include "tclPort.h"
32 #include <signal.h>
34 #ifndef MASK_SIZE
35 #define MASK_SIZE howmany(FD_SETSIZE, NFDBITS)
36 #endif
37 #ifndef SELECT_MASK
38 #define SELECT_MASK fd_set
39 #endif
41 /* Prototype (too lazy to create new .h) */
42 int Tcl_PollSelectEvent(void);
45 * This structure is used to keep track of the notifier info for a
46 * a registered file.
49 typedef struct FileHandler {
50 int fd;
51 int mask; /* Mask of desired events: TCL_READABLE,
52 * etc. */
53 int readyMask; /* Mask of events that have been seen since the
54 * last time file handlers were invoked for
55 * this file. */
56 Tcl_FileProc *proc; /* Procedure to call, in the style of
57 * Tcl_CreateFileHandler. */
58 ClientData clientData; /* Argument to pass to proc. */
59 struct FileHandler *nextPtr;/* Next in list of all files we care about. */
60 } FileHandler;
63 * The following structure is what is added to the Tcl event queue when
64 * file handlers are ready to fire.
67 typedef struct FileHandlerEvent {
68 Tcl_Event header; /* Information that is standard for
69 * all events. */
70 int fd; /* File descriptor that is ready. Used
71 * to find the FileHandler structure for
72 * the file (can't point directly to the
73 * FileHandler structure because it could
74 * go away while the event is queued). */
75 } FileHandlerEvent;
78 * The following static structure contains the state information for the
79 * select based implementation of the Tcl notifier.
82 static struct {
83 FileHandler *firstFileHandlerPtr;
84 /* Pointer to head of file handler list. */
85 fd_mask checkMasks[3*MASK_SIZE];
86 /* This array is used to build up the masks
87 * to be used in the next call to select.
88 * Bits are set in response to calls to
89 * Tcl_CreateFileHandler. */
90 fd_mask readyMasks[3*MASK_SIZE];
91 /* This array reflects the readable/writable
92 * conditions that were found to exist by the
93 * last call to select. */
94 int numFdBits; /* Number of valid bits in checkMasks
95 * (one more than highest fd for which
96 * Tcl_WatchFile has been called). */
97 } notifier;
100 * The following static indicates whether this module has been initialized.
103 static int initialized = 0;
106 * Static routines defined in this file.
109 static void InitNotifier _ANSI_ARGS_((void));
110 static void NotifierExitHandler _ANSI_ARGS_((
111 ClientData clientData));
112 static int FileHandlerEventProc _ANSI_ARGS_((Tcl_Event *evPtr,
113 int flags));
116 *----------------------------------------------------------------------
118 * InitNotifier --
120 * Initializes the notifier state.
122 * Results:
123 * None.
125 * Side effects:
126 * Creates a new exit handler.
128 *----------------------------------------------------------------------
131 static void
132 InitNotifier()
134 initialized = 1;
135 memset(&notifier, 0, sizeof(notifier));
136 Tcl_CreateExitHandler(NotifierExitHandler, NULL);
140 *----------------------------------------------------------------------
142 * NotifierExitHandler --
144 * This function is called to cleanup the notifier state before
145 * Tcl is unloaded.
147 * Results:
148 * None.
150 * Side effects:
151 * Destroys the notifier window.
153 *----------------------------------------------------------------------
156 static void
157 NotifierExitHandler(clientData)
158 ClientData clientData; /* Not used. */
160 initialized = 0;
164 *----------------------------------------------------------------------
166 * Tcl_CreateFileHandler --
168 * This procedure registers a file handler with the Xt notifier.
170 * Results:
171 * None.
173 * Side effects:
174 * Creates a new file handler structure and registers one or more
175 * input procedures with Xt.
177 *----------------------------------------------------------------------
180 void
181 Tcl_CreateFileHandler(fd, mask, proc, clientData)
182 int fd; /* Handle of stream to watch. */
183 int mask; /* OR'ed combination of TCL_READABLE,
184 * TCL_WRITABLE, and TCL_EXCEPTION:
185 * indicates conditions under which
186 * proc should be called. */
187 Tcl_FileProc *proc; /* Procedure to call for each
188 * selected event. */
189 ClientData clientData; /* Arbitrary data to pass to proc. */
191 FileHandler *filePtr;
192 int index, bit;
194 if (!initialized) {
195 InitNotifier();
198 for (filePtr = notifier.firstFileHandlerPtr; filePtr != NULL;
199 filePtr = filePtr->nextPtr) {
200 if (filePtr->fd == fd) {
201 break;
204 if (filePtr == NULL) {
205 filePtr = (FileHandler*) ckalloc(sizeof(FileHandler)); /* MLK */
206 filePtr->fd = fd;
207 filePtr->readyMask = 0;
208 filePtr->nextPtr = notifier.firstFileHandlerPtr;
209 notifier.firstFileHandlerPtr = filePtr;
211 filePtr->proc = proc;
212 filePtr->clientData = clientData;
213 filePtr->mask = mask;
216 * Update the check masks for this file.
219 index = fd/(NBBY*sizeof(fd_mask));
220 bit = 1 << (fd%(NBBY*sizeof(fd_mask)));
221 if (mask & TCL_READABLE) {
222 notifier.checkMasks[index] |= bit;
223 } else {
224 notifier.checkMasks[index] &= ~bit;
226 if (mask & TCL_WRITABLE) {
227 (notifier.checkMasks+MASK_SIZE)[index] |= bit;
228 } else {
229 (notifier.checkMasks+MASK_SIZE)[index] &= ~bit;
231 if (mask & TCL_EXCEPTION) {
232 (notifier.checkMasks+2*(MASK_SIZE))[index] |= bit;
233 } else {
234 (notifier.checkMasks+2*(MASK_SIZE))[index] &= ~bit;
236 if (notifier.numFdBits <= fd) {
237 notifier.numFdBits = fd+1;
242 *----------------------------------------------------------------------
244 * Tcl_DeleteFileHandler --
246 * Cancel a previously-arranged callback arrangement for
247 * a file.
249 * Results:
250 * None.
252 * Side effects:
253 * If a callback was previously registered on file, remove it.
255 *----------------------------------------------------------------------
258 void
259 Tcl_DeleteFileHandler(fd)
260 int fd; /* Stream id for which to remove callback procedure. */
262 FileHandler *filePtr, *prevPtr;
263 int index, bit, mask, i;
265 if (!initialized) {
266 InitNotifier();
270 * Find the entry for the given file (and return if there
271 * isn't one).
274 for (prevPtr = NULL, filePtr = notifier.firstFileHandlerPtr; ;
275 prevPtr = filePtr, filePtr = filePtr->nextPtr) {
276 if (filePtr == NULL) {
277 return;
279 if (filePtr->fd == fd) {
280 break;
285 * Update the check masks for this file.
288 index = fd/(NBBY*sizeof(fd_mask));
289 bit = 1 << (fd%(NBBY*sizeof(fd_mask)));
291 if (filePtr->mask & TCL_READABLE) {
292 notifier.checkMasks[index] &= ~bit;
294 if (filePtr->mask & TCL_WRITABLE) {
295 (notifier.checkMasks+MASK_SIZE)[index] &= ~bit;
297 if (filePtr->mask & TCL_EXCEPTION) {
298 (notifier.checkMasks+2*(MASK_SIZE))[index] &= ~bit;
302 * Find current max fd.
305 if (fd+1 == notifier.numFdBits) {
306 for (notifier.numFdBits = 0; index >= 0; index--) {
307 mask = notifier.checkMasks[index]
308 | (notifier.checkMasks+MASK_SIZE)[index]
309 | (notifier.checkMasks+2*(MASK_SIZE))[index];
310 if (mask) {
311 for (i = (NBBY*sizeof(fd_mask)); i > 0; i--) {
312 if (mask & (1 << (i-1))) {
313 break;
316 notifier.numFdBits = index * (NBBY*sizeof(fd_mask)) + i;
317 break;
323 * Clean up information in the callback record.
326 if (prevPtr == NULL) {
327 notifier.firstFileHandlerPtr = filePtr->nextPtr;
328 } else {
329 prevPtr->nextPtr = filePtr->nextPtr;
331 ckfree((char *) filePtr);
335 *----------------------------------------------------------------------
337 * FileHandlerEventProc --
339 * This procedure is called by Tcl_ServiceEvent when a file event
340 * reaches the front of the event queue. This procedure is
341 * responsible for actually handling the event by invoking the
342 * callback for the file handler.
344 * Results:
345 * Returns 1 if the event was handled, meaning it should be removed
346 * from the queue. Returns 0 if the event was not handled, meaning
347 * it should stay on the queue. The only time the event isn't
348 * handled is if the TCL_FILE_EVENTS flag bit isn't set.
350 * Side effects:
351 * Whatever the file handler's callback procedure does.
353 *----------------------------------------------------------------------
356 static int
357 FileHandlerEventProc(evPtr, flags)
358 Tcl_Event *evPtr; /* Event to service. */
359 int flags; /* Flags that indicate what events to
360 * handle, such as TCL_FILE_EVENTS. */
362 FileHandler *filePtr;
363 FileHandlerEvent *fileEvPtr = (FileHandlerEvent *) evPtr;
364 int mask;
366 if (!(flags & TCL_FILE_EVENTS)) {
367 return 0;
371 * Search through the file handlers to find the one whose handle matches
372 * the event. We do this rather than keeping a pointer to the file
373 * handler directly in the event, so that the handler can be deleted
374 * while the event is queued without leaving a dangling pointer.
377 for (filePtr = notifier.firstFileHandlerPtr; filePtr != NULL;
378 filePtr = filePtr->nextPtr) {
379 if (filePtr->fd != fileEvPtr->fd) {
380 continue;
384 * The code is tricky for two reasons:
385 * 1. The file handler's desired events could have changed
386 * since the time when the event was queued, so AND the
387 * ready mask with the desired mask.
388 * 2. The file could have been closed and re-opened since
389 * the time when the event was queued. This is why the
390 * ready mask is stored in the file handler rather than
391 * the queued event: it will be zeroed when a new
392 * file handler is created for the newly opened file.
395 mask = filePtr->readyMask & filePtr->mask;
396 filePtr->readyMask = 0;
397 if (mask != 0) {
398 (*filePtr->proc)(filePtr->clientData, mask);
400 break;
402 return 1;
406 *----------------------------------------------------------------------
408 * Tcl_PollSelectEvent --
410 * This function is called by Tcl_WaitForEvent to wait for new
411 * events on the message queue. If the block time is 0, then
412 * Tcl_WaitForEvent just polls without blocking.
414 * Results:
415 * Returns 1 if any event handled, 0 otherwise.
417 * Side effects:
418 * Queues file events that are detected by the select.
420 *----------------------------------------------------------------------
424 Tcl_PollSelectEvent(void)
426 FileHandler *filePtr;
427 FileHandlerEvent *fileEvPtr;
428 struct timeval timeout, *timeoutPtr;
429 int bit, index, mask, numFound;
431 if (!initialized) {
432 InitNotifier();
436 * Set up the timeout structure.
439 timeout.tv_sec = 0;
440 timeout.tv_usec = 0;
441 timeoutPtr = &timeout;
443 memcpy((VOID *) notifier.readyMasks, (VOID *) notifier.checkMasks,
444 3*MASK_SIZE*sizeof(fd_mask));
445 numFound = select(notifier.numFdBits,
446 (SELECT_MASK *) &notifier.readyMasks[0],
447 (SELECT_MASK *) &notifier.readyMasks[MASK_SIZE],
448 (SELECT_MASK *) &notifier.readyMasks[2*MASK_SIZE], timeoutPtr);
451 * Some systems don't clear the masks after an error, so
452 * we have to do it here.
455 if (numFound == -1) {
456 memset((VOID *) notifier.readyMasks, 0, 3*MASK_SIZE*sizeof(fd_mask));
460 * Return if nothing to do.
462 if ( numFound == 0 )
463 return 0;
466 * Queue all detected file events before returning.
469 for (filePtr = notifier.firstFileHandlerPtr;
470 (filePtr != NULL) && (numFound > 0);
471 filePtr = filePtr->nextPtr) {
472 index = filePtr->fd / (NBBY*sizeof(fd_mask));
473 bit = 1 << (filePtr->fd % (NBBY*sizeof(fd_mask)));
474 mask = 0;
476 if (notifier.readyMasks[index] & bit) {
477 mask |= TCL_READABLE;
479 if ((notifier.readyMasks+MASK_SIZE)[index] & bit) {
480 mask |= TCL_WRITABLE;
482 if ((notifier.readyMasks+2*(MASK_SIZE))[index] & bit) {
483 mask |= TCL_EXCEPTION;
486 if (!mask) {
487 continue;
488 } else {
489 numFound--;
493 * Don't bother to queue an event if the mask was previously
494 * non-zero since an event must still be on the queue.
497 if (filePtr->readyMask == 0) {
498 fileEvPtr = (FileHandlerEvent *) ckalloc(
499 sizeof(FileHandlerEvent));
500 fileEvPtr->header.proc = FileHandlerEventProc;
501 fileEvPtr->fd = filePtr->fd;
502 Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL);
504 filePtr->readyMask = mask;
506 return 1;