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.2.2 2010/01/29 09:38:47 nijtmans 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 writing to the pipe.
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	if (write(triggerPipe, "q", 1) != 1) {
296		    Tcl_Panic("Tcl_FinalizeNotifier: unable to write q to triggerPipe");
297	}
298	close(triggerPipe);
299	while(triggerPipe >= 0) {
300	    Tcl_ConditionWait(&notifierCV, &notifierMutex, NULL);
301	}
302
303	result = Tcl_JoinThread(notifierThread, NULL);
304	if (result) {
305	    Tcl_Panic("Tcl_FinalizeNotifier: unable to join notifier thread");
306	}
307    }
308
309    /*
310     * Clean up any synchronization objects in the thread local storage.
311     */
312
313    Tcl_ConditionFinalize(&(tsdPtr->waitCV));
314
315    Tcl_MutexUnlock(&notifierMutex);
316#endif
317}
318
319/*
320 *----------------------------------------------------------------------
321 *
322 * Tcl_AlertNotifier --
323 *
324 *	Wake up the specified notifier from any thread. This routine is called
325 *	by the platform independent notifier code whenever the Tcl_ThreadAlert
326 *	routine is called. This routine is guaranteed not to be called on a
327 *	given notifier after Tcl_FinalizeNotifier is called for that notifier.
328 *
329 * Results:
330 *	None.
331 *
332 * Side effects:
333 *	Signals the notifier condition variable for the specified notifier.
334 *
335 *----------------------------------------------------------------------
336 */
337
338void
339Tcl_AlertNotifier(
340    ClientData clientData)
341{
342#ifdef TCL_THREADS
343    ThreadSpecificData *tsdPtr = (ThreadSpecificData *) clientData;
344    Tcl_MutexLock(&notifierMutex);
345    tsdPtr->eventReady = 1;
346    Tcl_ConditionNotify(&tsdPtr->waitCV);
347    Tcl_MutexUnlock(&notifierMutex);
348#endif
349}
350
351/*
352 *----------------------------------------------------------------------
353 *
354 * Tcl_SetTimer --
355 *
356 *	This function sets the current notifier timer value. This interface is
357 *	not implemented in this notifier because we are always running inside
358 *	of Tcl_DoOneEvent.
359 *
360 * Results:
361 *	None.
362 *
363 * Side effects:
364 *	None.
365 *
366 *----------------------------------------------------------------------
367 */
368
369void
370Tcl_SetTimer(
371    Tcl_Time *timePtr)		/* Timeout value, may be NULL. */
372{
373    /*
374     * The interval timer doesn't do anything in this implementation, because
375     * the only event loop is via Tcl_DoOneEvent, which passes timeout values
376     * to Tcl_WaitForEvent.
377     */
378
379    if (tclStubs.tcl_SetTimer != tclOriginalNotifier.setTimerProc) {
380	tclStubs.tcl_SetTimer(timePtr);
381    }
382}
383
384/*
385 *----------------------------------------------------------------------
386 *
387 * Tcl_ServiceModeHook --
388 *
389 *	This function is invoked whenever the service mode changes.
390 *
391 * Results:
392 *	None.
393 *
394 * Side effects:
395 *	None.
396 *
397 *----------------------------------------------------------------------
398 */
399
400void
401Tcl_ServiceModeHook(
402    int mode)			/* Either TCL_SERVICE_ALL, or
403				 * TCL_SERVICE_NONE. */
404{
405}
406
407/*
408 *----------------------------------------------------------------------
409 *
410 * Tcl_CreateFileHandler --
411 *
412 *	This function registers a file handler with the select notifier.
413 *
414 * Results:
415 *	None.
416 *
417 * Side effects:
418 *	Creates a new file handler structure.
419 *
420 *----------------------------------------------------------------------
421 */
422
423void
424Tcl_CreateFileHandler(
425    int fd,			/* Handle of stream to watch. */
426    int mask,			/* OR'ed combination of TCL_READABLE,
427				 * TCL_WRITABLE, and TCL_EXCEPTION: indicates
428				 * conditions under which proc should be
429				 * called. */
430    Tcl_FileProc *proc,		/* Function to call for each selected
431				 * event. */
432    ClientData clientData)	/* Arbitrary data to pass to proc. */
433{
434    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
435    FileHandler *filePtr;
436
437    if (tclStubs.tcl_CreateFileHandler !=
438	    tclOriginalNotifier.createFileHandlerProc) {
439	tclStubs.tcl_CreateFileHandler(fd, mask, proc, clientData);
440	return;
441    }
442
443    for (filePtr = tsdPtr->firstFileHandlerPtr; filePtr != NULL;
444	    filePtr = filePtr->nextPtr) {
445	if (filePtr->fd == fd) {
446	    break;
447	}
448    }
449    if (filePtr == NULL) {
450	filePtr = (FileHandler*) ckalloc(sizeof(FileHandler));
451	filePtr->fd = fd;
452	filePtr->readyMask = 0;
453	filePtr->nextPtr = tsdPtr->firstFileHandlerPtr;
454	tsdPtr->firstFileHandlerPtr = filePtr;
455    }
456    filePtr->proc = proc;
457    filePtr->clientData = clientData;
458    filePtr->mask = mask;
459
460    /*
461     * Update the check masks for this file.
462     */
463
464    if (mask & TCL_READABLE) {
465	FD_SET(fd, &(tsdPtr->checkMasks.readable));
466    } else {
467	FD_CLR(fd, &(tsdPtr->checkMasks.readable));
468    }
469    if (mask & TCL_WRITABLE) {
470	FD_SET(fd, &(tsdPtr->checkMasks.writable));
471    } else {
472	FD_CLR(fd, &(tsdPtr->checkMasks.writable));
473    }
474    if (mask & TCL_EXCEPTION) {
475	FD_SET(fd, &(tsdPtr->checkMasks.exceptional));
476    } else {
477	FD_CLR(fd, &(tsdPtr->checkMasks.exceptional));
478    }
479    if (tsdPtr->numFdBits <= fd) {
480	tsdPtr->numFdBits = fd+1;
481    }
482}
483
484/*
485 *----------------------------------------------------------------------
486 *
487 * Tcl_DeleteFileHandler --
488 *
489 *	Cancel a previously-arranged callback arrangement for a file.
490 *
491 * Results:
492 *	None.
493 *
494 * Side effects:
495 *	If a callback was previously registered on file, remove it.
496 *
497 *----------------------------------------------------------------------
498 */
499
500void
501Tcl_DeleteFileHandler(
502    int fd)			/* Stream id for which to remove callback
503				 * function. */
504{
505    FileHandler *filePtr, *prevPtr;
506    int i;
507    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
508
509    if (tclStubs.tcl_DeleteFileHandler !=
510	    tclOriginalNotifier.deleteFileHandlerProc) {
511	tclStubs.tcl_DeleteFileHandler(fd);
512	return;
513    }
514
515    /*
516     * Find the entry for the given file (and return if there isn't one).
517     */
518
519    for (prevPtr = NULL, filePtr = tsdPtr->firstFileHandlerPtr; ;
520	 prevPtr = filePtr, filePtr = filePtr->nextPtr) {
521	if (filePtr == NULL) {
522	    return;
523	}
524	if (filePtr->fd == fd) {
525	    break;
526	}
527    }
528
529    /*
530     * Update the check masks for this file.
531     */
532
533    if (filePtr->mask & TCL_READABLE) {
534	FD_CLR(fd, &(tsdPtr->checkMasks.readable));
535    }
536    if (filePtr->mask & TCL_WRITABLE) {
537	FD_CLR(fd, &(tsdPtr->checkMasks.writable));
538    }
539    if (filePtr->mask & TCL_EXCEPTION) {
540	FD_CLR(fd, &(tsdPtr->checkMasks.exceptional));
541    }
542
543    /*
544     * Find current max fd.
545     */
546
547    if (fd+1 == tsdPtr->numFdBits) {
548	int numFdBits = 0;
549
550	for (i = fd-1; i >= 0; i--) {
551	    if (FD_ISSET(i, &(tsdPtr->checkMasks.readable))
552		    || FD_ISSET(i, &(tsdPtr->checkMasks.writable))
553		    || FD_ISSET(i, &(tsdPtr->checkMasks.exceptional))) {
554		numFdBits = i+1;
555		break;
556	    }
557	}
558	tsdPtr->numFdBits = numFdBits;
559    }
560
561    /*
562     * Clean up information in the callback record.
563     */
564
565    if (prevPtr == NULL) {
566	tsdPtr->firstFileHandlerPtr = filePtr->nextPtr;
567    } else {
568	prevPtr->nextPtr = filePtr->nextPtr;
569    }
570    ckfree((char *) filePtr);
571}
572
573/*
574 *----------------------------------------------------------------------
575 *
576 * FileHandlerEventProc --
577 *
578 *	This function is called by Tcl_ServiceEvent when a file event reaches
579 *	the front of the event queue. This function is responsible for
580 *	actually handling the event by invoking the callback for the file
581 *	handler.
582 *
583 * Results:
584 *	Returns 1 if the event was handled, meaning it should be removed from
585 *	the queue. Returns 0 if the event was not handled, meaning it should
586 *	stay on the queue. The only time the event isn't handled is if the
587 *	TCL_FILE_EVENTS flag bit isn't set.
588 *
589 * Side effects:
590 *	Whatever the file handler's callback function does.
591 *
592 *----------------------------------------------------------------------
593 */
594
595static int
596FileHandlerEventProc(
597    Tcl_Event *evPtr,		/* Event to service. */
598    int flags)			/* Flags that indicate what events to handle,
599				 * such as TCL_FILE_EVENTS. */
600{
601    int mask;
602    FileHandler *filePtr;
603    FileHandlerEvent *fileEvPtr = (FileHandlerEvent *) evPtr;
604    ThreadSpecificData *tsdPtr;
605
606    if (!(flags & TCL_FILE_EVENTS)) {
607	return 0;
608    }
609
610    /*
611     * Search through the file handlers to find the one whose handle matches
612     * the event. We do this rather than keeping a pointer to the file handler
613     * directly in the event, so that the handler can be deleted while the
614     * event is queued without leaving a dangling pointer.
615     */
616
617    tsdPtr = TCL_TSD_INIT(&dataKey);
618    for (filePtr = tsdPtr->firstFileHandlerPtr; filePtr != NULL;
619	    filePtr = filePtr->nextPtr) {
620	if (filePtr->fd != fileEvPtr->fd) {
621	    continue;
622	}
623
624	/*
625	 * The code is tricky for two reasons:
626	 * 1. The file handler's desired events could have changed since the
627	 *    time when the event was queued, so AND the ready mask with the
628	 *    desired mask.
629	 * 2. The file could have been closed and re-opened since the time
630	 *    when the event was queued. This is why the ready mask is stored
631	 *    in the file handler rather than the queued event: it will be
632	 *    zeroed when a new file handler is created for the newly opened
633	 *    file.
634	 */
635
636	mask = filePtr->readyMask & filePtr->mask;
637	filePtr->readyMask = 0;
638	if (mask != 0) {
639	    (*filePtr->proc)(filePtr->clientData, mask);
640	}
641	break;
642    }
643    return 1;
644}
645
646/*
647 *----------------------------------------------------------------------
648 *
649 * Tcl_WaitForEvent --
650 *
651 *	This function is called by Tcl_DoOneEvent to wait for new events on
652 *	the message queue. If the block time is 0, then Tcl_WaitForEvent just
653 *	polls without blocking.
654 *
655 * Results:
656 *	Returns -1 if the select would block forever, otherwise returns 0.
657 *
658 * Side effects:
659 *	Queues file events that are detected by the select.
660 *
661 *----------------------------------------------------------------------
662 */
663
664int
665Tcl_WaitForEvent(
666    Tcl_Time *timePtr)		/* Maximum block time, or NULL. */
667{
668    FileHandler *filePtr;
669    FileHandlerEvent *fileEvPtr;
670    int mask;
671    Tcl_Time vTime;
672#ifdef TCL_THREADS
673    int waitForFiles;
674#else
675    /*
676     * Impl. notes: timeout & timeoutPtr are used if, and only if threads are
677     * not enabled. They are the arguments for the regular select() used when
678     * the core is not thread-enabled.
679     */
680
681    struct timeval timeout, *timeoutPtr;
682    int numFound;
683#endif /* TCL_THREADS */
684    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
685
686    if (tclStubs.tcl_WaitForEvent != tclOriginalNotifier.waitForEventProc) {
687	return tclStubs.tcl_WaitForEvent(timePtr);
688    }
689
690    /*
691     * Set up the timeout structure. Note that if there are no events to check
692     * for, we return with a negative result rather than blocking forever.
693     */
694
695    if (timePtr != NULL) {
696	/*
697	 * TIP #233 (Virtualized Time). Is virtual time in effect? And do we
698	 * actually have something to scale? If yes to both then we call the
699	 * handler to do this scaling.
700	 */
701
702	if (timePtr->sec != 0 || timePtr->usec != 0) {
703	    vTime = *timePtr;
704	    (*tclScaleTimeProcPtr) (&vTime, tclTimeClientData);
705	    timePtr = &vTime;
706	}
707#ifndef TCL_THREADS
708	timeout.tv_sec = timePtr->sec;
709	timeout.tv_usec = timePtr->usec;
710	timeoutPtr = &timeout;
711    } else if (tsdPtr->numFdBits == 0) {
712	/*
713	 * If there are no threads, no timeout, and no fds registered, then
714	 * there are no events possible and we must avoid deadlock. Note that
715	 * this is not entirely correct because there might be a signal that
716	 * could interrupt the select call, but we don't handle that case if
717	 * we aren't using threads.
718	 */
719
720	return -1;
721    } else {
722	timeoutPtr = NULL;
723#endif /* TCL_THREADS */
724    }
725
726#ifdef TCL_THREADS
727    /*
728     * Place this thread on the list of interested threads, signal the
729     * notifier thread, and wait for a response or a timeout.
730     */
731
732    Tcl_MutexLock(&notifierMutex);
733
734    if (timePtr != NULL && timePtr->sec == 0 && (timePtr->usec == 0
735#if defined(__APPLE__) && defined(__LP64__)
736	    /*
737	     * On 64-bit Darwin, pthread_cond_timedwait() appears to have a bug
738	     * that causes it to wait forever when passed an absolute time which
739	     * has already been exceeded by the system time; as a workaround,
740	     * when given a very brief timeout, just do a poll. [Bug 1457797]
741	     */
742	    || timePtr->usec < 10
743#endif
744	    )) {
745	/*
746	 * Cannot emulate a polling select with a polling condition variable.
747	 * Instead, pretend to wait for files and tell the notifier thread
748	 * what we are doing. The notifier thread makes sure it goes through
749	 * select with its select mask in the same state as ours currently is.
750	 * We block until that happens.
751	 */
752
753	waitForFiles = 1;
754	tsdPtr->pollState = POLL_WANT;
755	timePtr = NULL;
756    } else {
757	waitForFiles = (tsdPtr->numFdBits > 0);
758	tsdPtr->pollState = 0;
759    }
760
761    if (waitForFiles) {
762	/*
763	 * Add the ThreadSpecificData structure of this thread to the list of
764	 * ThreadSpecificData structures of all threads that are waiting on
765	 * file events.
766	 */
767
768	tsdPtr->nextPtr = waitingListPtr;
769	if (waitingListPtr) {
770	    waitingListPtr->prevPtr = tsdPtr;
771	}
772	tsdPtr->prevPtr = 0;
773	waitingListPtr = tsdPtr;
774	tsdPtr->onList = 1;
775
776	if (write(triggerPipe, "", 1) != 1) {
777		Tcl_Panic("Tcl_WaitForEvent: unable to write to triggerPipe");
778	}
779    }
780
781    FD_ZERO(&(tsdPtr->readyMasks.readable));
782    FD_ZERO(&(tsdPtr->readyMasks.writable));
783    FD_ZERO(&(tsdPtr->readyMasks.exceptional));
784
785    if (!tsdPtr->eventReady) {
786	Tcl_ConditionWait(&tsdPtr->waitCV, &notifierMutex, timePtr);
787    }
788    tsdPtr->eventReady = 0;
789
790    if (waitForFiles && tsdPtr->onList) {
791	/*
792	 * Remove the ThreadSpecificData structure of this thread from the
793	 * waiting list. Alert the notifier thread to recompute its select
794	 * masks - skipping this caused a hang when trying to close a pipe
795	 * which the notifier thread was still doing a select on.
796	 */
797
798	if (tsdPtr->prevPtr) {
799	    tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr;
800	} else {
801	    waitingListPtr = tsdPtr->nextPtr;
802	}
803	if (tsdPtr->nextPtr) {
804	    tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr;
805	}
806	tsdPtr->nextPtr = tsdPtr->prevPtr = NULL;
807	tsdPtr->onList = 0;
808	if (write(triggerPipe, "", 1) != 1) {
809		Tcl_Panic("Tcl_WaitForEvent: unable to write to triggerPipe");
810	}
811    }
812
813#else
814    tsdPtr->readyMasks = tsdPtr->checkMasks;
815    numFound = select(tsdPtr->numFdBits, &(tsdPtr->readyMasks.readable),
816	    &(tsdPtr->readyMasks.writable), &(tsdPtr->readyMasks.exceptional),
817	    timeoutPtr);
818
819    /*
820     * Some systems don't clear the masks after an error, so we have to do it
821     * here.
822     */
823
824    if (numFound == -1) {
825	FD_ZERO(&(tsdPtr->readyMasks.readable));
826	FD_ZERO(&(tsdPtr->readyMasks.writable));
827	FD_ZERO(&(tsdPtr->readyMasks.exceptional));
828    }
829#endif /* TCL_THREADS */
830
831    /*
832     * Queue all detected file events before returning.
833     */
834
835    for (filePtr = tsdPtr->firstFileHandlerPtr; (filePtr != NULL);
836	    filePtr = filePtr->nextPtr) {
837
838	mask = 0;
839	if (FD_ISSET(filePtr->fd, &(tsdPtr->readyMasks.readable))) {
840	    mask |= TCL_READABLE;
841	}
842	if (FD_ISSET(filePtr->fd, &(tsdPtr->readyMasks.writable))) {
843	    mask |= TCL_WRITABLE;
844	}
845	if (FD_ISSET(filePtr->fd, &(tsdPtr->readyMasks.exceptional))) {
846	    mask |= TCL_EXCEPTION;
847	}
848
849	if (!mask) {
850	    continue;
851	}
852
853	/*
854	 * Don't bother to queue an event if the mask was previously non-zero
855	 * since an event must still be on the queue.
856	 */
857
858	if (filePtr->readyMask == 0) {
859	    fileEvPtr = (FileHandlerEvent *) ckalloc(sizeof(FileHandlerEvent));
860	    fileEvPtr->header.proc = FileHandlerEventProc;
861	    fileEvPtr->fd = filePtr->fd;
862	    Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL);
863	}
864	filePtr->readyMask = mask;
865    }
866#ifdef TCL_THREADS
867    Tcl_MutexUnlock(&notifierMutex);
868#endif /* TCL_THREADS */
869    return 0;
870}
871
872#ifdef TCL_THREADS
873/*
874 *----------------------------------------------------------------------
875 *
876 * NotifierThreadProc --
877 *
878 *	This routine is the initial (and only) function executed by the
879 *	special notifier thread. Its job is to wait for file descriptors to
880 *	become readable or writable or to have an exception condition and then
881 *	to notify other threads who are interested in this information by
882 *	signalling a condition variable. Other threads can signal this
883 *	notifier thread of a change in their interests by writing a single
884 *	byte to a special pipe that the notifier thread is monitoring.
885 *
886 * Result:
887 *	None. Once started, this routine never exits. It dies with the overall
888 *	process.
889 *
890 * Side effects:
891 *	The trigger pipe used to signal the notifier thread is created when
892 *	the notifier thread first starts.
893 *
894 *----------------------------------------------------------------------
895 */
896
897static void
898NotifierThreadProc(
899    ClientData clientData)	/* Not used. */
900{
901    ThreadSpecificData *tsdPtr;
902    fd_set readableMask;
903    fd_set writableMask;
904    fd_set exceptionalMask;
905    int fds[2];
906    int i, numFdBits = 0, receivePipe;
907    long found;
908    struct timeval poll = {0., 0.}, *timePtr;
909    char buf[2];
910
911    if (pipe(fds) != 0) {
912	Tcl_Panic("NotifierThreadProc: could not create trigger pipe");
913    }
914
915    receivePipe = fds[0];
916
917    if (TclUnixSetBlockingMode(receivePipe, TCL_MODE_NONBLOCKING) < 0) {
918	Tcl_Panic("NotifierThreadProc: could not make receive pipe non blocking");
919    }
920    if (TclUnixSetBlockingMode(fds[1], TCL_MODE_NONBLOCKING) < 0) {
921	Tcl_Panic("NotifierThreadProc: could not make trigger pipe non blocking");
922    }
923
924    /*
925     * Install the write end of the pipe into the global variable.
926     */
927
928    Tcl_MutexLock(&notifierMutex);
929    triggerPipe = fds[1];
930
931    /*
932     * Signal any threads that are waiting.
933     */
934
935    Tcl_ConditionNotify(&notifierCV);
936    Tcl_MutexUnlock(&notifierMutex);
937
938    /*
939     * Look for file events and report them to interested threads.
940     */
941
942    while (1) {
943	FD_ZERO(&readableMask);
944	FD_ZERO(&writableMask);
945	FD_ZERO(&exceptionalMask);
946
947	/*
948	 * Compute the logical OR of the select masks from all the waiting
949	 * notifiers.
950	 */
951
952	Tcl_MutexLock(&notifierMutex);
953	timePtr = NULL;
954	for (tsdPtr = waitingListPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) {
955	    for (i = tsdPtr->numFdBits-1; i >= 0; --i) {
956		if (FD_ISSET(i, &(tsdPtr->checkMasks.readable))) {
957		    FD_SET(i, &readableMask);
958		}
959		if (FD_ISSET(i, &(tsdPtr->checkMasks.writable))) {
960		    FD_SET(i, &writableMask);
961		}
962		if (FD_ISSET(i, &(tsdPtr->checkMasks.exceptional))) {
963		    FD_SET(i, &exceptionalMask);
964		}
965	    }
966	    if (tsdPtr->numFdBits > numFdBits) {
967		numFdBits = tsdPtr->numFdBits;
968	    }
969	    if (tsdPtr->pollState & POLL_WANT) {
970		/*
971		 * Here we make sure we go through select() with the same mask
972		 * bits that were present when the thread tried to poll.
973		 */
974
975		tsdPtr->pollState |= POLL_DONE;
976		timePtr = &poll;
977	    }
978	}
979	Tcl_MutexUnlock(&notifierMutex);
980
981	/*
982	 * Set up the select mask to include the receive pipe.
983	 */
984
985	if (receivePipe >= numFdBits) {
986	    numFdBits = receivePipe + 1;
987	}
988	FD_SET(receivePipe, &readableMask);
989
990	if (select(numFdBits, &readableMask, &writableMask, &exceptionalMask,
991		timePtr) == -1) {
992	    /*
993	     * Try again immediately on an error.
994	     */
995
996	    continue;
997	}
998
999	/*
1000	 * Alert any threads that are waiting on a ready file descriptor.
1001	 */
1002
1003	Tcl_MutexLock(&notifierMutex);
1004	for (tsdPtr = waitingListPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) {
1005	    found = 0;
1006
1007	    for (i = tsdPtr->numFdBits-1; i >= 0; --i) {
1008		if (FD_ISSET(i, &(tsdPtr->checkMasks.readable))
1009			&& FD_ISSET(i, &readableMask)) {
1010		    FD_SET(i, &(tsdPtr->readyMasks.readable));
1011		    found = 1;
1012		}
1013		if (FD_ISSET(i, &(tsdPtr->checkMasks.writable))
1014			&& FD_ISSET(i, &writableMask)) {
1015		    FD_SET(i, &(tsdPtr->readyMasks.writable));
1016		    found = 1;
1017		}
1018		if (FD_ISSET(i, &(tsdPtr->checkMasks.exceptional))
1019			&& FD_ISSET(i, &exceptionalMask)) {
1020		    FD_SET(i, &(tsdPtr->readyMasks.exceptional));
1021		    found = 1;
1022		}
1023	    }
1024
1025	    if (found || (tsdPtr->pollState & POLL_DONE)) {
1026		tsdPtr->eventReady = 1;
1027		if (tsdPtr->onList) {
1028		    /*
1029		     * Remove the ThreadSpecificData structure of this thread
1030		     * from the waiting list. This prevents us from
1031		     * continuously spining on select until the other threads
1032		     * runs and services the file event.
1033		     */
1034
1035		    if (tsdPtr->prevPtr) {
1036			tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr;
1037		    } else {
1038			waitingListPtr = tsdPtr->nextPtr;
1039		    }
1040		    if (tsdPtr->nextPtr) {
1041			tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr;
1042		    }
1043		    tsdPtr->nextPtr = tsdPtr->prevPtr = NULL;
1044		    tsdPtr->onList = 0;
1045		    tsdPtr->pollState = 0;
1046		}
1047		Tcl_ConditionNotify(&tsdPtr->waitCV);
1048	    }
1049	}
1050	Tcl_MutexUnlock(&notifierMutex);
1051
1052	/*
1053	 * Consume the next byte from the notifier pipe if the pipe was
1054	 * readable. Note that there may be multiple bytes pending, but to
1055	 * avoid a race condition we only read one at a time.
1056	 */
1057
1058	if (FD_ISSET(receivePipe, &readableMask)) {
1059	    i = read(receivePipe, buf, 1);
1060
1061	    if ((i == 0) || ((i == 1) && (buf[0] == 'q'))) {
1062		/*
1063		 * Someone closed the write end of the pipe or sent us a Quit
1064		 * message [Bug: 4139] and then closed the write end of the
1065		 * pipe so we need to shut down the notifier thread.
1066		 */
1067
1068		break;
1069	    }
1070	}
1071    }
1072
1073    /*
1074     * Clean up the read end of the pipe and signal any threads waiting on
1075     * termination of the notifier thread.
1076     */
1077
1078    close(receivePipe);
1079    Tcl_MutexLock(&notifierMutex);
1080    triggerPipe = -1;
1081    Tcl_ConditionNotify(&notifierCV);
1082    Tcl_MutexUnlock(&notifierMutex);
1083
1084    TclpThreadExit (0);
1085}
1086#endif /* TCL_THREADS */
1087
1088#endif /* HAVE_COREFOUNDATION */
1089
1090/*
1091 * Local Variables:
1092 * mode: c
1093 * c-basic-offset: 4
1094 * fill-column: 78
1095 * End:
1096 */
1097