OSDN Git Service

* configure.in: Fix for autoconf 2.5.
[pf3gnuchains/pf3gnuchains3x.git] / tcl / unix / tclUnixNotfy.c
1 /*
2  * tclUnixNotify.c --
3  *
4  *      This file contains the implementation of the select-based
5  *      Unix-specific notifier, which is the lowest-level part of the
6  *      Tcl event loop.  This file works together with
7  *      ../generic/tclNotify.c.
8  *
9  * Copyright (c) 1995-1997 Sun Microsystems, Inc.
10  *
11  * See the file "license.terms" for information on usage and redistribution
12  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13  *
14  * RCS: @(#) $Id$
15  */
16
17 #include "tclInt.h"
18 #include "tclPort.h"
19 #include <signal.h> 
20
21 extern TclStubs tclStubs;
22
23 /*
24  * This structure is used to keep track of the notifier info for a 
25  * a registered file.
26  */
27
28 typedef struct FileHandler {
29     int fd;
30     int mask;                   /* Mask of desired events: TCL_READABLE,
31                                  * etc. */
32     int readyMask;              /* Mask of events that have been seen since the
33                                  * last time file handlers were invoked for
34                                  * this file. */
35     Tcl_FileProc *proc;         /* Procedure to call, in the style of
36                                  * Tcl_CreateFileHandler. */
37     ClientData clientData;      /* Argument to pass to proc. */
38     struct FileHandler *nextPtr;/* Next in list of all files we care about. */
39 } FileHandler;
40
41 /*
42  * The following structure is what is added to the Tcl event queue when
43  * file handlers are ready to fire.
44  */
45
46 typedef struct FileHandlerEvent {
47     Tcl_Event header;           /* Information that is standard for
48                                  * all events. */
49     int fd;                     /* File descriptor that is ready.  Used
50                                  * to find the FileHandler structure for
51                                  * the file (can't point directly to the
52                                  * FileHandler structure because it could
53                                  * go away while the event is queued). */
54 } FileHandlerEvent;
55
56 /*
57  * The following static structure contains the state information for the
58  * select based implementation of the Tcl notifier.  One of these structures
59  * is created for each thread that is using the notifier.  
60  */
61
62 typedef struct ThreadSpecificData {
63     FileHandler *firstFileHandlerPtr;
64                                 /* Pointer to head of file handler list. */
65     fd_mask checkMasks[3*MASK_SIZE];
66                                 /* This array is used to build up the masks
67                                  * to be used in the next call to select.
68                                  * Bits are set in response to calls to
69                                  * Tcl_CreateFileHandler. */
70     fd_mask readyMasks[3*MASK_SIZE];
71                                 /* This array reflects the readable/writable
72                                  * conditions that were found to exist by the
73                                  * last call to select. */
74     int numFdBits;              /* Number of valid bits in checkMasks
75                                  * (one more than highest fd for which
76                                  * Tcl_WatchFile has been called). */
77 #ifdef TCL_THREADS
78     int onList;                 /* True if it is in this list */
79     unsigned int pollState;     /* pollState is used to implement a polling 
80                                  * handshake between each thread and the
81                                  * notifier thread. Bits defined below. */
82     struct ThreadSpecificData *nextPtr, *prevPtr;
83                                 /* All threads that are currently waiting on 
84                                  * an event have their ThreadSpecificData
85                                  * structure on a doubly-linked listed formed
86                                  * from these pointers.  You must hold the
87                                  * notifierMutex lock before accessing these
88                                  * fields. */
89     Tcl_Condition waitCV;     /* Any other thread alerts a notifier
90                                  * that an event is ready to be processed
91                                  * by signaling this condition variable. */
92     int eventReady;           /* True if an event is ready to be processed.
93                                * Used as condition flag together with
94                                * waitCV above. */
95 #endif
96 } ThreadSpecificData;
97
98 static Tcl_ThreadDataKey dataKey;
99
100 #ifdef TCL_THREADS
101 /*
102  * The following static indicates the number of threads that have
103  * initialized notifiers.
104  *
105  * You must hold the notifierMutex lock before accessing this variable.
106  */
107
108 static int notifierCount = 0;
109
110 /*
111  * The following variable points to the head of a doubly-linked list of 
112  * of ThreadSpecificData structures for all threads that are currently
113  * waiting on an event.
114  *
115  * You must hold the notifierMutex lock before accessing this list.
116  */
117
118 static ThreadSpecificData *waitingListPtr = NULL;
119
120 /*
121  * The notifier thread spends all its time in select() waiting for a
122  * file descriptor associated with one of the threads on the waitingListPtr
123  * list to do something interesting.  But if the contents of the
124  * waitingListPtr list ever changes, we need to wake up and restart
125  * the select() system call.  You can wake up the notifier thread by
126  * writing a single byte to the file descriptor defined below.  This
127  * file descriptor is the input-end of a pipe and the notifier thread is
128  * listening for data on the output-end of the same pipe.  Hence writing
129  * to this file descriptor will cause the select() system call to return
130  * and wake up the notifier thread.
131  *
132  * You must hold the notifierMutex lock before accessing this list.
133  */
134
135 static int triggerPipe = -1;
136
137 /*
138  * The notifierMutex locks access to all of the global notifier state. 
139  */
140
141 TCL_DECLARE_MUTEX(notifierMutex)
142
143 /*
144  * The notifier thread signals the notifierCV when it has finished
145  * initializing the triggerPipe and right before the notifier
146  * thread terminates.
147  */
148
149 static Tcl_Condition notifierCV;
150
151 /*
152  * The pollState bits
153  *      POLL_WANT is set by each thread before it waits on its condition
154  *              variable.  It is checked by the notifier before it does
155  *              select.
156  *      POLL_DONE is set by the notifier if it goes into select after
157  *              seeing POLL_WANT.  The idea is to ensure it tries a select
158  *              with the same bits the initial thread had set.
159  */
160 #define POLL_WANT       0x1
161 #define POLL_DONE       0x2
162
163 /*
164  * This is the thread ID of the notifier thread that does select.
165  */
166 static Tcl_ThreadId notifierThread;
167
168 #endif
169
170 /*
171  * Static routines defined in this file.
172  */
173
174 #ifdef TCL_THREADS
175 static void     NotifierThreadProc _ANSI_ARGS_((ClientData clientData));
176 #endif
177 static int      FileHandlerEventProc _ANSI_ARGS_((Tcl_Event *evPtr,
178                     int flags));
179 \f
180 /*
181  *----------------------------------------------------------------------
182  *
183  * Tcl_InitNotifier --
184  *
185  *      Initializes the platform specific notifier state.
186  *
187  * Results:
188  *      Returns a handle to the notifier state for this thread..
189  *
190  * Side effects:
191  *      None.
192  *
193  *----------------------------------------------------------------------
194  */
195
196 ClientData
197 Tcl_InitNotifier()
198 {
199     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
200
201 #ifdef TCL_THREADS
202     tsdPtr->eventReady = 0;
203
204     /*
205      * Start the Notifier thread if necessary.
206      */
207
208     Tcl_MutexLock(&notifierMutex);
209     if (notifierCount == 0) {
210         if (Tcl_CreateThread(&notifierThread, NotifierThreadProc, NULL,
211                      TCL_THREAD_STACK_DEFAULT, TCL_THREAD_NOFLAGS) != TCL_OK) {
212             panic("Tcl_InitNotifier: unable to start notifier thread");
213         }
214     }
215     notifierCount++;
216
217     /*
218      * Wait for the notifier pipe to be created.
219      */
220
221     while (triggerPipe < 0) {
222         Tcl_ConditionWait(&notifierCV, &notifierMutex, NULL);
223     }
224
225     Tcl_MutexUnlock(&notifierMutex);
226 #endif
227     return (ClientData) tsdPtr;
228 }
229 \f
230 /*
231  *----------------------------------------------------------------------
232  *
233  * Tcl_FinalizeNotifier --
234  *
235  *      This function is called to cleanup the notifier state before
236  *      a thread is terminated.
237  *
238  * Results:
239  *      None.
240  *
241  * Side effects:
242  *      May terminate the background notifier thread if this is the
243  *      last notifier instance.
244  *
245  *----------------------------------------------------------------------
246  */
247
248 void
249 Tcl_FinalizeNotifier(clientData)
250     ClientData clientData;              /* Not used. */
251 {
252 #ifdef TCL_THREADS
253     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
254
255     Tcl_MutexLock(&notifierMutex);
256     notifierCount--;
257
258     /*
259      * If this is the last thread to use the notifier, close the notifier
260      * pipe and wait for the background thread to terminate.
261      */
262
263     if (notifierCount == 0) {
264         if (triggerPipe < 0) {
265             panic("Tcl_FinalizeNotifier: notifier pipe not initialized");
266         }
267
268         /*
269          * Send "q" message to the notifier thread so that it will
270          * terminate.  The notifier will return from its call to select()
271          * and notice that a "q" message has arrived, it will then close
272          * its side of the pipe and terminate its thread.  Note the we can
273          * not just close the pipe and check for EOF in the notifier
274          * thread because if a background child process was created with
275          * exec, select() would not register the EOF on the pipe until the
276          * child processes had terminated. [Bug: 4139]
277          */
278         write(triggerPipe, "q", 1);
279         close(triggerPipe);
280
281         Tcl_ConditionWait(&notifierCV, &notifierMutex, NULL);
282     }
283
284     /*
285      * Clean up any synchronization objects in the thread local storage.
286      */
287
288     Tcl_ConditionFinalize(&(tsdPtr->waitCV));
289
290     Tcl_MutexUnlock(&notifierMutex);
291 #endif
292 }
293 \f
294 /*
295  *----------------------------------------------------------------------
296  *
297  * Tcl_AlertNotifier --
298  *
299  *      Wake up the specified notifier from any thread. This routine
300  *      is called by the platform independent notifier code whenever
301  *      the Tcl_ThreadAlert routine is called.  This routine is
302  *      guaranteed not to be called on a given notifier after
303  *      Tcl_FinalizeNotifier is called for that notifier.
304  *
305  * Results:
306  *      None.
307  *
308  * Side effects:
309  *      Signals the notifier condition variable for the specified
310  *      notifier.
311  *
312  *----------------------------------------------------------------------
313  */
314
315 void
316 Tcl_AlertNotifier(clientData)
317     ClientData clientData;
318 {
319 #ifdef TCL_THREADS
320     ThreadSpecificData *tsdPtr = (ThreadSpecificData *) clientData;
321     Tcl_MutexLock(&notifierMutex);
322     tsdPtr->eventReady = 1;
323     Tcl_ConditionNotify(&tsdPtr->waitCV);
324     Tcl_MutexUnlock(&notifierMutex);
325 #endif
326 }
327 \f
328 /*
329  *----------------------------------------------------------------------
330  *
331  * Tcl_SetTimer --
332  *
333  *      This procedure sets the current notifier timer value.  This
334  *      interface is not implemented in this notifier because we are
335  *      always running inside of Tcl_DoOneEvent.
336  *
337  * Results:
338  *      None.
339  *
340  * Side effects:
341  *      None.
342  *
343  *----------------------------------------------------------------------
344  */
345
346 void
347 Tcl_SetTimer(timePtr)
348     Tcl_Time *timePtr;          /* Timeout value, may be NULL. */
349 {
350     /*
351      * The interval timer doesn't do anything in this implementation,
352      * because the only event loop is via Tcl_DoOneEvent, which passes
353      * timeout values to Tcl_WaitForEvent.
354      */
355
356     if (tclStubs.tcl_SetTimer != Tcl_SetTimer) {
357         tclStubs.tcl_SetTimer(timePtr);
358     }
359 }
360 \f
361 /*
362  *----------------------------------------------------------------------
363  *
364  * Tcl_ServiceModeHook --
365  *
366  *      This function is invoked whenever the service mode changes.
367  *
368  * Results:
369  *      None.
370  *
371  * Side effects:
372  *      None.
373  *
374  *----------------------------------------------------------------------
375  */
376
377 void
378 Tcl_ServiceModeHook(mode)
379     int mode;                   /* Either TCL_SERVICE_ALL, or
380                                  * TCL_SERVICE_NONE. */
381 {
382 }
383 \f
384 /*
385  *----------------------------------------------------------------------
386  *
387  * Tcl_CreateFileHandler --
388  *
389  *      This procedure registers a file handler with the select notifier.
390  *
391  * Results:
392  *      None.
393  *
394  * Side effects:
395  *      Creates a new file handler structure.
396  *
397  *----------------------------------------------------------------------
398  */
399
400 void
401 Tcl_CreateFileHandler(fd, mask, proc, clientData)
402     int fd;                     /* Handle of stream to watch. */
403     int mask;                   /* OR'ed combination of TCL_READABLE,
404                                  * TCL_WRITABLE, and TCL_EXCEPTION:
405                                  * indicates conditions under which
406                                  * proc should be called. */
407     Tcl_FileProc *proc;         /* Procedure to call for each
408                                  * selected event. */
409     ClientData clientData;      /* Arbitrary data to pass to proc. */
410 {
411     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
412     FileHandler *filePtr;
413     int index, bit;
414
415     if (tclStubs.tcl_CreateFileHandler != Tcl_CreateFileHandler) {
416         tclStubs.tcl_CreateFileHandler(fd, mask, proc, clientData);
417         return;
418     }
419
420     for (filePtr = tsdPtr->firstFileHandlerPtr; filePtr != NULL;
421          filePtr = filePtr->nextPtr) {
422         if (filePtr->fd == fd) {
423             break;
424         }
425     }
426     if (filePtr == NULL) {
427         filePtr = (FileHandler*) ckalloc(sizeof(FileHandler));
428         filePtr->fd = fd;
429         filePtr->readyMask = 0;
430         filePtr->nextPtr = tsdPtr->firstFileHandlerPtr;
431         tsdPtr->firstFileHandlerPtr = filePtr;
432     }
433     filePtr->proc = proc;
434     filePtr->clientData = clientData;
435     filePtr->mask = mask;
436
437     /*
438      * Update the check masks for this file.
439      */
440
441     index = fd/(NBBY*sizeof(fd_mask));
442     bit = 1 << (fd%(NBBY*sizeof(fd_mask)));
443     if (mask & TCL_READABLE) {
444         tsdPtr->checkMasks[index] |= bit;
445     } else {
446         tsdPtr->checkMasks[index] &= ~bit;
447     } 
448     if (mask & TCL_WRITABLE) {
449         (tsdPtr->checkMasks+MASK_SIZE)[index] |= bit;
450     } else {
451         (tsdPtr->checkMasks+MASK_SIZE)[index] &= ~bit;
452     }
453     if (mask & TCL_EXCEPTION) {
454         (tsdPtr->checkMasks+2*(MASK_SIZE))[index] |= bit;
455     } else {
456         (tsdPtr->checkMasks+2*(MASK_SIZE))[index] &= ~bit;
457     }
458     if (tsdPtr->numFdBits <= fd) {
459         tsdPtr->numFdBits = fd+1;
460     }
461 }
462 \f
463 /*
464  *----------------------------------------------------------------------
465  *
466  * Tcl_DeleteFileHandler --
467  *
468  *      Cancel a previously-arranged callback arrangement for
469  *      a file.
470  *
471  * Results:
472  *      None.
473  *
474  * Side effects:
475  *      If a callback was previously registered on file, remove it.
476  *
477  *----------------------------------------------------------------------
478  */
479
480 void
481 Tcl_DeleteFileHandler(fd)
482     int fd;             /* Stream id for which to remove callback procedure. */
483 {
484     FileHandler *filePtr, *prevPtr;
485     int index, bit, i;
486     unsigned long flags;
487     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
488
489     if (tclStubs.tcl_DeleteFileHandler != Tcl_DeleteFileHandler) {
490         tclStubs.tcl_DeleteFileHandler(fd);
491         return;
492     }
493
494     /*
495      * Find the entry for the given file (and return if there isn't one).
496      */
497
498     for (prevPtr = NULL, filePtr = tsdPtr->firstFileHandlerPtr; ;
499          prevPtr = filePtr, filePtr = filePtr->nextPtr) {
500         if (filePtr == NULL) {
501             return;
502         }
503         if (filePtr->fd == fd) {
504             break;
505         }
506     }
507
508     /*
509      * Update the check masks for this file.
510      */
511
512     index = fd/(NBBY*sizeof(fd_mask));
513     bit = 1 << (fd%(NBBY*sizeof(fd_mask)));
514
515     if (filePtr->mask & TCL_READABLE) {
516         tsdPtr->checkMasks[index] &= ~bit;
517     }
518     if (filePtr->mask & TCL_WRITABLE) {
519         (tsdPtr->checkMasks+MASK_SIZE)[index] &= ~bit;
520     }
521     if (filePtr->mask & TCL_EXCEPTION) {
522         (tsdPtr->checkMasks+2*(MASK_SIZE))[index] &= ~bit;
523     }
524
525     /*
526      * Find current max fd.
527      */
528
529     if (fd+1 == tsdPtr->numFdBits) {
530         for (tsdPtr->numFdBits = 0; index >= 0; index--) {
531             flags = tsdPtr->checkMasks[index]
532                 | (tsdPtr->checkMasks+MASK_SIZE)[index]
533                 | (tsdPtr->checkMasks+2*(MASK_SIZE))[index];
534             if (flags) {
535                 for (i = (NBBY*sizeof(fd_mask)); i > 0; i--) {
536                     if (flags & (((unsigned long)1) << (i-1))) {
537                         break;
538                     }
539                 }
540                 tsdPtr->numFdBits = index * (NBBY*sizeof(fd_mask)) + i;
541                 break;
542             }
543         }
544     }
545
546     /*
547      * Clean up information in the callback record.
548      */
549
550     if (prevPtr == NULL) {
551         tsdPtr->firstFileHandlerPtr = filePtr->nextPtr;
552     } else {
553         prevPtr->nextPtr = filePtr->nextPtr;
554     }
555     ckfree((char *) filePtr);
556 }
557 \f
558 /*
559  *----------------------------------------------------------------------
560  *
561  * FileHandlerEventProc --
562  *
563  *      This procedure is called by Tcl_ServiceEvent when a file event
564  *      reaches the front of the event queue.  This procedure is
565  *      responsible for actually handling the event by invoking the
566  *      callback for the file handler.
567  *
568  * Results:
569  *      Returns 1 if the event was handled, meaning it should be removed
570  *      from the queue.  Returns 0 if the event was not handled, meaning
571  *      it should stay on the queue.  The only time the event isn't
572  *      handled is if the TCL_FILE_EVENTS flag bit isn't set.
573  *
574  * Side effects:
575  *      Whatever the file handler's callback procedure does.
576  *
577  *----------------------------------------------------------------------
578  */
579
580 static int
581 FileHandlerEventProc(evPtr, flags)
582     Tcl_Event *evPtr;           /* Event to service. */
583     int flags;                  /* Flags that indicate what events to
584                                  * handle, such as TCL_FILE_EVENTS. */
585 {
586     int mask;
587     FileHandler *filePtr;
588     FileHandlerEvent *fileEvPtr = (FileHandlerEvent *) evPtr;
589     ThreadSpecificData *tsdPtr;
590
591     if (!(flags & TCL_FILE_EVENTS)) {
592         return 0;
593     }
594
595     /*
596      * Search through the file handlers to find the one whose handle matches
597      * the event.  We do this rather than keeping a pointer to the file
598      * handler directly in the event, so that the handler can be deleted
599      * while the event is queued without leaving a dangling pointer.
600      */
601
602     tsdPtr = TCL_TSD_INIT(&dataKey);
603     for (filePtr = tsdPtr->firstFileHandlerPtr; filePtr != NULL;
604          filePtr = filePtr->nextPtr) {
605         if (filePtr->fd != fileEvPtr->fd) {
606             continue;
607         }
608
609         /*
610          * The code is tricky for two reasons:
611          * 1. The file handler's desired events could have changed
612          *    since the time when the event was queued, so AND the
613          *    ready mask with the desired mask.
614          * 2. The file could have been closed and re-opened since
615          *    the time when the event was queued.  This is why the
616          *    ready mask is stored in the file handler rather than
617          *    the queued event:  it will be zeroed when a new
618          *    file handler is created for the newly opened file.
619          */
620
621         mask = filePtr->readyMask & filePtr->mask;
622         filePtr->readyMask = 0;
623         if (mask != 0) {
624             (*filePtr->proc)(filePtr->clientData, mask);
625         }
626         break;
627     }
628     return 1;
629 }
630 \f
631 /*
632  *----------------------------------------------------------------------
633  *
634  * Tcl_WaitForEvent --
635  *
636  *      This function is called by Tcl_DoOneEvent to wait for new
637  *      events on the message queue.  If the block time is 0, then
638  *      Tcl_WaitForEvent just polls without blocking.
639  *
640  * Results:
641  *      Returns -1 if the select would block forever, otherwise
642  *      returns 0.
643  *
644  * Side effects:
645  *      Queues file events that are detected by the select.
646  *
647  *----------------------------------------------------------------------
648  */
649
650 int
651 Tcl_WaitForEvent(timePtr)
652     Tcl_Time *timePtr;          /* Maximum block time, or NULL. */
653 {
654     FileHandler *filePtr;
655     FileHandlerEvent *fileEvPtr;
656     struct timeval timeout, *timeoutPtr;
657     int bit, index, mask;
658 #ifdef TCL_THREADS
659     int waitForFiles;
660 #else
661     int numFound;
662 #endif
663     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
664
665     if (tclStubs.tcl_WaitForEvent != Tcl_WaitForEvent) {
666         return tclStubs.tcl_WaitForEvent(timePtr);
667     }
668
669     /*
670      * Set up the timeout structure.  Note that if there are no events to
671      * check for, we return with a negative result rather than blocking
672      * forever.
673      */
674
675     if (timePtr) {
676         timeout.tv_sec = timePtr->sec;
677         timeout.tv_usec = timePtr->usec;
678         timeoutPtr = &timeout;
679 #ifndef TCL_THREADS
680     } else if (tsdPtr->numFdBits == 0) {
681         /*
682          * If there are no threads, no timeout, and no fds registered,
683          * then there are no events possible and we must avoid deadlock.
684          * Note that this is not entirely correct because there might
685          * be a signal that could interrupt the select call, but we
686          * don't handle that case if we aren't using threads.
687          */
688
689         return -1;
690 #endif
691     } else {
692         timeoutPtr = NULL;
693     }
694
695 #ifdef TCL_THREADS
696     /*
697      * Place this thread on the list of interested threads, signal the
698      * notifier thread, and wait for a response or a timeout.
699      */
700
701     Tcl_MutexLock(&notifierMutex);
702
703     waitForFiles = (tsdPtr->numFdBits > 0);
704     if (timePtr != NULL && timePtr->sec == 0 && timePtr->usec == 0) {
705         /*
706          * Cannot emulate a polling select with a polling condition variable.
707          * Instead, pretend to wait for files and tell the notifier
708          * thread what we are doing.  The notifier thread makes sure
709          * it goes through select with its select mask in the same state
710          * as ours currently is.  We block until that happens.
711          */
712
713         waitForFiles = 1;
714         tsdPtr->pollState = POLL_WANT;
715         timePtr = NULL;
716     } else {
717         tsdPtr->pollState = 0;
718     }
719
720     if (waitForFiles) {
721         /*
722          * Add the ThreadSpecificData structure of this thread to the list
723          * of ThreadSpecificData structures of all threads that are waiting
724          * on file events.
725          */
726
727
728         tsdPtr->nextPtr = waitingListPtr;
729         if (waitingListPtr) {
730             waitingListPtr->prevPtr = tsdPtr;
731         }
732         tsdPtr->prevPtr = 0;
733         waitingListPtr = tsdPtr;
734         tsdPtr->onList = 1;
735         
736         write(triggerPipe, "", 1);
737     }
738
739     memset((VOID *) tsdPtr->readyMasks, 0, 3*MASK_SIZE*sizeof(fd_mask));
740
741     if (!tsdPtr->eventReady) {
742         Tcl_ConditionWait(&tsdPtr->waitCV, &notifierMutex, timePtr);
743     }
744     tsdPtr->eventReady = 0;
745
746     if (waitForFiles && tsdPtr->onList) {
747         /*
748          * Remove the ThreadSpecificData structure of this thread from the
749          * waiting list.  Alert the notifier thread to recompute its select
750          * masks - skipping this caused a hang when trying to close a pipe
751          * which the notifier thread was still doing a select on.
752          */
753
754         if (tsdPtr->prevPtr) {
755             tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr;
756         } else {
757             waitingListPtr = tsdPtr->nextPtr;
758         }
759         if (tsdPtr->nextPtr) {
760             tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr;
761         }
762         tsdPtr->nextPtr = tsdPtr->prevPtr = NULL;
763         tsdPtr->onList = 0;
764         write(triggerPipe, "", 1);
765     }
766
767     
768 #else
769     memcpy((VOID *) tsdPtr->readyMasks, (VOID *) tsdPtr->checkMasks,
770             3*MASK_SIZE*sizeof(fd_mask));
771     numFound = select(tsdPtr->numFdBits,
772             (SELECT_MASK *) &tsdPtr->readyMasks[0],
773             (SELECT_MASK *) &tsdPtr->readyMasks[MASK_SIZE],
774             (SELECT_MASK *) &tsdPtr->readyMasks[2*MASK_SIZE], timeoutPtr);
775
776     /*
777      * Some systems don't clear the masks after an error, so
778      * we have to do it here.
779      */
780
781     if (numFound == -1) {
782         memset((VOID *) tsdPtr->readyMasks, 0, 3*MASK_SIZE*sizeof(fd_mask));
783     }
784 #endif
785
786     /*
787      * Queue all detected file events before returning.
788      */
789
790     for (filePtr = tsdPtr->firstFileHandlerPtr; (filePtr != NULL);
791          filePtr = filePtr->nextPtr) {
792         index = filePtr->fd / (NBBY*sizeof(fd_mask));
793         bit = 1 << (filePtr->fd % (NBBY*sizeof(fd_mask)));
794         mask = 0;
795
796         if (tsdPtr->readyMasks[index] & bit) {
797             mask |= TCL_READABLE;
798         }
799         if ((tsdPtr->readyMasks+MASK_SIZE)[index] & bit) {
800             mask |= TCL_WRITABLE;
801         }
802         if ((tsdPtr->readyMasks+2*(MASK_SIZE))[index] & bit) {
803             mask |= TCL_EXCEPTION;
804         }
805
806         if (!mask) {
807             continue;
808         }
809
810         /*
811          * Don't bother to queue an event if the mask was previously
812          * non-zero since an event must still be on the queue.
813          */
814
815         if (filePtr->readyMask == 0) {
816             fileEvPtr = (FileHandlerEvent *) ckalloc(
817                 sizeof(FileHandlerEvent));
818             fileEvPtr->header.proc = FileHandlerEventProc;
819             fileEvPtr->fd = filePtr->fd;
820             Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL);
821         }
822         filePtr->readyMask = mask;
823     }
824 #ifdef TCL_THREADS
825     Tcl_MutexUnlock(&notifierMutex);
826 #endif
827     return 0;
828 }
829 \f
830 #ifdef TCL_THREADS
831 /*
832  *----------------------------------------------------------------------
833  *
834  * NotifierThreadProc --
835  *
836  *      This routine is the initial (and only) function executed by the
837  *      special notifier thread.  Its job is to wait for file descriptors
838  *      to become readable or writable or to have an exception condition
839  *      and then to notify other threads who are interested in this
840  *      information by signalling a condition variable.  Other threads
841  *      can signal this notifier thread of a change in their interests
842  *      by writing a single byte to a special pipe that the notifier
843  *      thread is monitoring.
844  *
845  * Result:
846  *      None.  Once started, this routine never exits.  It dies with
847  *      the overall process.
848  *
849  * Side effects:
850  *      The trigger pipe used to signal the notifier thread is created
851  *      when the notifier thread first starts.
852  *
853  *----------------------------------------------------------------------
854  */
855
856 static void
857 NotifierThreadProc(clientData)
858     ClientData clientData;      /* Not used. */
859 {
860     ThreadSpecificData *tsdPtr;
861     fd_mask masks[3*MASK_SIZE];
862     long *maskPtr = (long *)masks;      /* masks[] cast to type long[] */
863     int fds[2];
864     int i, status, index, bit, numFdBits, found, receivePipe, word;
865     struct timeval poll = {0., 0.}, *timePtr;
866     int maskSize = 3 * ((MASK_SIZE) / sizeof(long)) * sizeof(fd_mask);
867     char buf[2];
868
869     if (pipe(fds) != 0) {
870         panic("NotifierThreadProc: could not create trigger pipe.");
871     }
872
873     receivePipe = fds[0];
874
875 #ifndef USE_FIONBIO
876     status = fcntl(receivePipe, F_GETFL);
877     status |= O_NONBLOCK;
878     if (fcntl(receivePipe, F_SETFL, status) < 0) {
879         panic("NotifierThreadProc: could not make receive pipe non blocking.");
880     }
881     status = fcntl(fds[1], F_GETFL);
882     status |= O_NONBLOCK;
883     if (fcntl(fds[1], F_SETFL, status) < 0) {
884         panic("NotifierThreadProc: could not make trigger pipe non blocking.");
885     }
886 #else
887     if (ioctl(receivePipe, (int) FIONBIO, &status) < 0) {
888         panic("NotifierThreadProc: could not make receive pipe non blocking.");
889     }
890     if (ioctl(fds[1], (int) FIONBIO, &status) < 0) {
891         panic("NotifierThreadProc: could not make trigger pipe non blocking.");
892     }
893 #endif
894
895     /*
896      * Install the write end of the pipe into the global variable.
897      */
898
899     Tcl_MutexLock(&notifierMutex);
900     triggerPipe = fds[1];
901
902     /*
903      * Signal any threads that are waiting.
904      */
905
906     Tcl_ConditionNotify(&notifierCV);
907     Tcl_MutexUnlock(&notifierMutex);
908
909     /*
910      * Look for file events and report them to interested threads.
911      */
912
913     while (1) {
914         /*
915          * Set up the select mask to include the receive pipe.
916          */
917
918         memset((VOID *)masks, 0, 3*MASK_SIZE*sizeof(fd_mask));
919         numFdBits = receivePipe + 1;
920         index = receivePipe / (NBBY*sizeof(fd_mask));
921         bit = 1 << (receivePipe % (NBBY*sizeof(fd_mask)));
922         masks[index] |= bit;
923
924         /*
925          * Add in the check masks from all of the waiting notifiers.
926          */
927         
928         Tcl_MutexLock(&notifierMutex);
929         timePtr = NULL;
930         for (tsdPtr = waitingListPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) {
931             for (i = 0; i < maskSize; i++) {
932                 maskPtr[i] |= ((long*)tsdPtr->checkMasks)[i];
933             }
934             if (tsdPtr->numFdBits > numFdBits) {
935                 numFdBits = tsdPtr->numFdBits;
936             }
937             if (tsdPtr->pollState & POLL_WANT) {
938                 /*
939                  * Here we make sure we go through select() with the same
940                  * mask bits that were present when the thread tried to poll.
941                  */
942
943                 tsdPtr->pollState |= POLL_DONE;
944                 timePtr = &poll;
945             }
946         }
947         Tcl_MutexUnlock(&notifierMutex);
948
949         maskSize = 3 * ((MASK_SIZE) / sizeof(long)) * sizeof(fd_mask);
950
951         if (select(numFdBits, (SELECT_MASK *) &masks[0],
952                 (SELECT_MASK *) &masks[MASK_SIZE],
953                 (SELECT_MASK *) &masks[2*MASK_SIZE], timePtr) == -1) {
954             /*
955              * Try again immediately on an error.
956              */
957
958             continue;
959         }
960
961         /*
962          * Alert any threads that are waiting on a ready file descriptor.
963          */
964
965         Tcl_MutexLock(&notifierMutex);
966         for (tsdPtr = waitingListPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) {
967             found = 0;
968
969             for (i = 0; i < maskSize; i++) {
970                 word = maskPtr[i] & ((long*)tsdPtr->checkMasks)[i];
971                 found |= word;
972                 (((long*)(tsdPtr->readyMasks))[i]) = word;
973             }
974             if (found || (tsdPtr->pollState & POLL_DONE)) {
975                 tsdPtr->eventReady = 1;
976                 if (tsdPtr->onList) {
977                     /*
978                      * Remove the ThreadSpecificData structure of this
979                      * thread from the waiting list. This prevents us from
980                      * continuously spining on select until the other
981                      * threads runs and services the file event.
982                      */
983             
984                     if (tsdPtr->prevPtr) {
985                         tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr;
986                     } else {
987                         waitingListPtr = tsdPtr->nextPtr;
988                     }
989                     if (tsdPtr->nextPtr) {
990                         tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr;
991                     }
992                     tsdPtr->nextPtr = tsdPtr->prevPtr = NULL;
993                     tsdPtr->onList = 0;
994                     tsdPtr->pollState = 0;
995                 }
996                 Tcl_ConditionNotify(&tsdPtr->waitCV);
997             }
998         }
999         Tcl_MutexUnlock(&notifierMutex);
1000         
1001         /*
1002          * Consume the next byte from the notifier pipe if the pipe was
1003          * readable.  Note that there may be multiple bytes pending, but
1004          * to avoid a race condition we only read one at a time.
1005          */
1006
1007         if (masks[index] & bit) {
1008             i = read(receivePipe, buf, 1);
1009
1010             if ((i == 0) || ((i == 1) && (buf[0] == 'q'))) {
1011                 /*
1012                  * Someone closed the write end of the pipe or sent us a
1013                  * Quit message [Bug: 4139] and then closed the write end
1014                  * of the pipe so we need to shut down the notifier thread.
1015                  */
1016
1017                 break;
1018             }
1019         }
1020     }
1021
1022     /*
1023      * Clean up the read end of the pipe and signal any threads waiting on
1024      * termination of the notifier thread.
1025      */
1026
1027     close(receivePipe);
1028     Tcl_MutexLock(&notifierMutex);
1029     triggerPipe = -1;
1030     Tcl_ConditionNotify(&notifierCV);
1031     Tcl_MutexUnlock(&notifierMutex);
1032 }
1033 #endif