Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/tcl8.5.2/unix/tclUnixNotfy.c @ 25

Last change on this file since 25 was 25, checked in by landauf, 16 years ago

added tcl to libs

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