Tcl Source Code

Changes On Branch tip-458
Login
Bounty program for improvements to Tcl and certain Tcl packages.

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Changes In Branch tip-458 Excluding Merge-Ins

This is equivalent to a diff from a4fd5b790b to 50be45e71e

2017-05-18
12:33
TIP #458 implementation: Add Support for epoll() and kqueue() in the Notifier check-in: 858569fb04 user: jan.nijtmans tags: trunk
2017-05-05
19:11
[6015221f59] Segfault after overflow of [binary] field specifier numeric count. check-in: 5c07ae32cf user: dgp tags: trunk
2017-05-04
22:27
Add [info linkedname] introspection command check-in: 6ed76df29d user: mlafon tags: info-linkedname
14:51
merge trunk Closed-Leaf check-in: 50be45e71e user: dgp tags: tip-458
14:19
merge trunk check-in: ab6adfa911 user: jan.nijtmans tags: win-console-panic
12:39
Merge trunk check-in: 6928ad7045 user: jan.nijtmans tags: novem
12:28
merge core-8-6-branch check-in: a4fd5b790b user: jan.nijtmans tags: trunk
12:26
Use GetModuleHandle() in stead of LoadLibrary() for ntdll, which is already loaded by Cygwin. check-in: a4717aa947 user: jan.nijtmans tags: core-8-6-branch
09:51
merge core-8-6-branch check-in: 790adcb1b9 user: jan.nijtmans tags: trunk
08:55
merge trunk check-in: 9a065b2341 user: jan.nijtmans tags: tip-458

Changes to unix/Makefile.in.

   341    341   	${COMPAT_OBJS}
   342    342   
   343    343   UNIX_OBJS = tclUnixChan.o tclUnixEvent.o tclUnixFCmd.o \
   344    344   	tclUnixFile.o tclUnixPipe.o tclUnixSock.o \
   345    345   	tclUnixTime.o tclUnixInit.o tclUnixThrd.o \
   346    346   	tclUnixCompat.o
   347    347   
   348         -NOTIFY_OBJS = tclUnixNotfy.o
          348  +NOTIFY_OBJS = tclEpollNotfy.o tclKqueueNotfy.o tclSelectNotfy.o
   349    349   
   350    350   MAC_OSX_OBJS = tclMacOSXBundle.o tclMacOSXFCmd.o tclMacOSXNotify.o
   351    351   
   352    352   CYGWIN_OBJS = tclWinError.o
   353    353   
   354    354   DTRACE_OBJ = tclDTrace.o
   355    355   
................................................................................
   561    561   	$(UNIX_DIR)/tclUnixTest.c \
   562    562   	$(UNIX_DIR)/tclUnixThrd.c \
   563    563   	$(UNIX_DIR)/tclUnixTime.c \
   564    564   	$(UNIX_DIR)/tclUnixInit.c \
   565    565   	$(UNIX_DIR)/tclUnixCompat.c
   566    566   
   567    567   NOTIFY_SRCS = \
   568         -	$(UNIX_DIR)/tclUnixNotfy.c
          568  +	$(UNIX_DIR)/tclEpollNotfy.c \
          569  +	$(UNIX_DIR)/tclKqueueNotfy.c \
          570  +	$(UNIX_DIR)/tclSelectNotfy.c
   569    571   
   570    572   DL_SRCS = \
   571    573   	$(UNIX_DIR)/tclLoadAix.c \
   572    574   	$(UNIX_DIR)/tclLoadDl.c \
   573    575   	$(UNIX_DIR)/tclLoadDl2.c \
   574    576   	$(UNIX_DIR)/tclLoadDld.c \
   575    577   	$(UNIX_DIR)/tclLoadDyld.c \
................................................................................
  1555   1557   
  1556   1558   tclUnixFCmd.o: $(UNIX_DIR)/tclUnixFCmd.c
  1557   1559   	$(CC) -c $(CC_SWITCHES) $(UNIX_DIR)/tclUnixFCmd.c
  1558   1560   
  1559   1561   tclUnixFile.o: $(UNIX_DIR)/tclUnixFile.c $(FSHDR)
  1560   1562   	$(CC) -c $(CC_SWITCHES) $(UNIX_DIR)/tclUnixFile.c
  1561   1563   
  1562         -tclUnixNotfy.o: $(UNIX_DIR)/tclUnixNotfy.c
  1563         -	$(CC) -c $(CC_SWITCHES) $(UNIX_DIR)/tclUnixNotfy.c
         1564  +tclEpollNotfy.o: $(UNIX_DIR)/tclEpollNotfy.c
         1565  +	$(CC) -c $(CC_SWITCHES) $(UNIX_DIR)/tclEpollNotfy.c
         1566  +
         1567  +tclKqueueNotfy.o: $(UNIX_DIR)/tclKqueueNotfy.c
         1568  +	$(CC) -c $(CC_SWITCHES) $(UNIX_DIR)/tclKqueueNotfy.c
         1569  +
         1570  +tclSelectNotfy.o: $(UNIX_DIR)/tclSelectNotfy.c
         1571  +	$(CC) -c $(CC_SWITCHES) $(UNIX_DIR)/tclSelectNotfy.c
  1564   1572   
  1565   1573   tclUnixPipe.o: $(UNIX_DIR)/tclUnixPipe.c
  1566   1574   	$(CC) -c $(CC_SWITCHES) $(UNIX_DIR)/tclUnixPipe.c
  1567   1575   
  1568   1576   tclUnixSock.o: $(UNIX_DIR)/tclUnixSock.c
  1569   1577   	$(CC) -c $(CC_SWITCHES) $(UNIX_DIR)/tclUnixSock.c
  1570   1578   

Changes to unix/configure.

  8434   8434       fi
  8435   8435   fi
  8436   8436   if test $tcl_ok = no; then
  8437   8437   
  8438   8438   $as_echo "#define NO_FD_SET 1" >>confdefs.h
  8439   8439   
  8440   8440   fi
         8441  +
         8442  +#------------------------------------------------------------------------
         8443  +#	Options for the notifier. Checks for epoll(7) on Linux, and
         8444  +#	kqueue(2) on {DragonFly,Free,Net,Open}BSD
         8445  +#------------------------------------------------------------------------
         8446  +
         8447  +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for advanced notifier support" >&5
         8448  +$as_echo_n "checking for advanced notifier support... " >&6; }
         8449  +case x`uname -s` in
         8450  +  xLinux)
         8451  +	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: epoll(7)" >&5
         8452  +$as_echo "epoll(7)" >&6; }
         8453  +	for ac_header in sys/epoll.h
         8454  +do :
         8455  +  ac_fn_c_check_header_mongrel "$LINENO" "sys/epoll.h" "ac_cv_header_sys_epoll_h" "$ac_includes_default"
         8456  +if test "x$ac_cv_header_sys_epoll_h" = xyes; then :
         8457  +  cat >>confdefs.h <<_ACEOF
         8458  +#define HAVE_SYS_EPOLL_H 1
         8459  +_ACEOF
         8460  +
         8461  +$as_echo "#define NOTIFIER_EPOLL 1" >>confdefs.h
         8462  +
         8463  +fi
         8464  +
         8465  +done
         8466  +
         8467  +	for ac_header in sys/eventfd.h
         8468  +do :
         8469  +  ac_fn_c_check_header_mongrel "$LINENO" "sys/eventfd.h" "ac_cv_header_sys_eventfd_h" "$ac_includes_default"
         8470  +if test "x$ac_cv_header_sys_eventfd_h" = xyes; then :
         8471  +  cat >>confdefs.h <<_ACEOF
         8472  +#define HAVE_SYS_EVENTFD_H 1
         8473  +_ACEOF
         8474  +
         8475  +$as_echo "#define HAVE_EVENTFD 1" >>confdefs.h
         8476  +
         8477  +fi
         8478  +
         8479  +done
         8480  +;;
         8481  +  xDragonFlyBSD|xFreeBSD|xNetBSD|xOpenBSD)
         8482  +	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: kqueue(2)" >&5
         8483  +$as_echo "kqueue(2)" >&6; }
         8484  +	# Messy because we want to check if *all* the headers are present, and not
         8485  +	# just *any*
         8486  +	tcl_kqueue_headers=x
         8487  +	for ac_header in sys/types.h sys/event.h sys/time.h
         8488  +do :
         8489  +  as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
         8490  +ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
         8491  +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
         8492  +  cat >>confdefs.h <<_ACEOF
         8493  +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
         8494  +_ACEOF
         8495  + tcl_kqueue_headers=${tcl_kqueue_headers}y
         8496  +fi
         8497  +
         8498  +done
         8499  +
         8500  +	if test $tcl_kqueue_headers = xyyy; then :
         8501  +
         8502  +
         8503  +$as_echo "#define NOTIFIER_KQUEUE 1" >>confdefs.h
         8504  +
         8505  +fi;;
         8506  +  xDarwin)
         8507  +	# Assume that we've got CoreFoundation present (checked elsewhere because
         8508  +	# of wider impact).
         8509  +	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: OSX" >&5
         8510  +$as_echo "OSX" >&6; };;
         8511  +  *)
         8512  +	cat >>confdefs.h <<_ACEOF
         8513  +#define NOTIFIER_SELECT 1
         8514  +_ACEOF
         8515  +
         8516  +	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: none" >&5
         8517  +$as_echo "none" >&6; };;
         8518  +esac
  8441   8519   
  8442   8520   #------------------------------------------------------------------------------
  8443   8521   #       Find out all about time handling differences.
  8444   8522   #------------------------------------------------------------------------------
  8445   8523   
  8446   8524   
  8447   8525       for ac_header in sys/time.h

Changes to unix/configure.ac.

   295    295   	AC_DEFINE(HAVE_SYS_SELECT_H, 1, [Should we include <sys/select.h>?])
   296    296   	tcl_ok=yes
   297    297       fi
   298    298   fi
   299    299   if test $tcl_ok = no; then
   300    300       AC_DEFINE(NO_FD_SET, 1, [Do we have fd_set?])
   301    301   fi
          302  +
          303  +#------------------------------------------------------------------------
          304  +#	Options for the notifier. Checks for epoll(7) on Linux, and
          305  +#	kqueue(2) on {DragonFly,Free,Net,Open}BSD
          306  +#------------------------------------------------------------------------
          307  +
          308  +AC_MSG_CHECKING([for advanced notifier support])
          309  +case x`uname -s` in
          310  +  xLinux)
          311  +	AC_MSG_RESULT([epoll(7)])
          312  +	AC_CHECK_HEADERS([sys/epoll.h],
          313  +	    [AC_DEFINE(NOTIFIER_EPOLL, [1], [Is epoll(7) supported?])])
          314  +	AC_CHECK_HEADERS([sys/eventfd.h],
          315  +	    [AC_DEFINE(HAVE_EVENTFD, [1], [Is eventfd(2) supported?])]);;
          316  +  xDragonFlyBSD|xFreeBSD|xNetBSD|xOpenBSD)
          317  +	AC_MSG_RESULT([kqueue(2)])
          318  +	# Messy because we want to check if *all* the headers are present, and not
          319  +	# just *any*
          320  +	tcl_kqueue_headers=x
          321  +	AC_CHECK_HEADERS([sys/types.h sys/event.h sys/time.h],
          322  +	    [tcl_kqueue_headers=${tcl_kqueue_headers}y])
          323  +	AS_IF([test $tcl_kqueue_headers = xyyy], [
          324  +	    AC_DEFINE(NOTIFIER_KQUEUE, [1], [Is kqueue(2) supported?])]);;
          325  +  xDarwin)
          326  +	# Assume that we've got CoreFoundation present (checked elsewhere because
          327  +	# of wider impact).
          328  +	AC_MSG_RESULT([OSX]);;
          329  +  *)
          330  +	AC_DEFINE_UNQUOTED(NOTIFIER_SELECT)
          331  +	AC_MSG_RESULT([none]);;
          332  +esac
   302    333   
   303    334   #------------------------------------------------------------------------------
   304    335   #       Find out all about time handling differences.
   305    336   #------------------------------------------------------------------------------
   306    337   
   307    338   SC_TIME_HANDLER
   308    339   

Added unix/tclEpollNotfy.c.

            1  +/*
            2  + * tclEpollNotfy.c --
            3  + *
            4  + *	This file contains the implementation of the epoll()-based
            5  + *	Linux-specific notifier, which is the lowest-level part of the
            6  + *	Tcl event loop. This file works together with generic/tclNotify.c.
            7  + *
            8  + * Copyright (c) 1995-1997 Sun Microsystems, Inc.
            9  + * Copyright (c) 2016 Lucio Andrés Illanes Albornoz <[email protected]>
           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  +
           15  +#ifdef NOTIFIER_EPOLL
           16  +
           17  +#define _GNU_SOURCE		/* For pipe2(2) */
           18  +#include "tclInt.h"
           19  +#ifndef HAVE_COREFOUNDATION	/* Darwin/Mac OS X CoreFoundation notifier is
           20  +				 * in tclMacOSXNotify.c */
           21  +#include <fcntl.h>
           22  +#include <signal.h>
           23  +#include <sys/epoll.h>
           24  +#include <sys/eventfd.h>
           25  +#include <sys/queue.h>
           26  +#include <unistd.h>
           27  +
           28  +/*
           29  + * This structure is used to keep track of the notifier info for a registered
           30  + * file.
           31  + */
           32  +
           33  +struct PlatformEventData;
           34  +typedef 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  +    LIST_ENTRY(FileHandler) readyNode;
           46  +				/* Next/previous in list of FileHandlers asso-
           47  +				 * ciated with regular files (S_IFREG) that are
           48  +				 * ready for I/O. */
           49  +    struct PlatformEventData *pedPtr;
           50  +				/* Pointer to PlatformEventData associating this
           51  +				 * FileHandler with epoll(7) events. */
           52  +} FileHandler;
           53  +
           54  +/*
           55  + * The following structure associates a FileHandler and the thread that owns it
           56  + * with the file descriptors of interest and their event masks passed to epoll_ctl(2)
           57  + * and their corresponding event(s) returned by epoll_wait(2).
           58  + */
           59  +
           60  +struct ThreadSpecificData;
           61  +struct PlatformEventData {
           62  +    FileHandler *filePtr;
           63  +    struct ThreadSpecificData *tsdPtr;
           64  +};
           65  +
           66  +/*
           67  + * The following structure is what is added to the Tcl event queue when file
           68  + * handlers are ready to fire.
           69  + */
           70  +
           71  +typedef struct {
           72  +    Tcl_Event header;		/* Information that is standard for all
           73  +				 * events. */
           74  +    int fd;			/* File descriptor that is ready. Used to find
           75  +				 * the FileHandler structure for the file
           76  +				 * (can't point directly to the FileHandler
           77  +				 * structure because it could go away while
           78  +				 * the event is queued). */
           79  +} FileHandlerEvent;
           80  +
           81  +/*
           82  + * The following static structure contains the state information for the
           83  + * epoll based implementation of the Tcl notifier. One of these structures is
           84  + * created for each thread that is using the notifier.
           85  + */
           86  +
           87  +LIST_HEAD(PlatformReadyFileHandlerList, FileHandler);
           88  +typedef struct ThreadSpecificData {
           89  +    FileHandler *firstFileHandlerPtr;
           90  +				/* Pointer to head of file handler list. */
           91  +    struct PlatformReadyFileHandlerList firstReadyFileHandlerPtr;
           92  +				/* Pointer to head of list of FileHandlers
           93  +				 * associated with regular files (S_IFREG)
           94  +				 * that are ready for I/O. */
           95  +    pthread_mutex_t notifierMutex;
           96  +				/* Mutex protecting notifier termination in
           97  +				 * PlatformEventsFinalize. */
           98  +#ifdef HAVE_EVENTFD
           99  +    int triggerEventFd;		/* eventfd(2) used by other threads to wake
          100  +				 * up this thread for inter-thread IPC. */
          101  +#else
          102  +    int triggerPipe[2];		/* pipe(2) used by other threads to wake
          103  +				 * up this thread for inter-thread IPC. */
          104  +#endif /* HAVE_EVENTFD */
          105  +    int eventsFd;		/* epoll(7) file descriptor used to wait for fds */
          106  +    struct epoll_event *readyEvents;
          107  +				/* Pointer to at most maxReadyEvents events
          108  +				 * returned by epoll_wait(2). */
          109  +    size_t maxReadyEvents;	/* Count of epoll_events in readyEvents. */
          110  +} ThreadSpecificData;
          111  +
          112  +static Tcl_ThreadDataKey dataKey;
          113  +
          114  +void PlatformEventsControl(FileHandler *filePtr, ThreadSpecificData *tsdPtr, int op, int isNew);
          115  +static void PlatformEventsFinalize(void);
          116  +void PlatformEventsInit(void);
          117  +static int PlatformEventsTranslate(struct epoll_event *event);
          118  +static int PlatformEventsWait(struct epoll_event *events, size_t numEvents, struct timeval *timePtr);
          119  +
          120  +#include "tclUnixNotfy.c"
          121  +
          122  +/*
          123  + *----------------------------------------------------------------------
          124  + *
          125  + * Tcl_InitNotifier --
          126  + *
          127  + *	Initializes the platform specific notifier state.
          128  + *
          129  + * Results:
          130  + *	Returns a handle to the notifier state for this thread.
          131  + *
          132  + * Side effects:
          133  + *	If no initNotifierProc notifier hook exists, PlatformEventsInit
          134  + *	is called.
          135  + *
          136  + *----------------------------------------------------------------------
          137  + */
          138  +
          139  +ClientData
          140  +Tcl_InitNotifier(void)
          141  +{
          142  +    if (tclNotifierHooks.initNotifierProc) {
          143  +	return tclNotifierHooks.initNotifierProc();
          144  +    } else {
          145  +	ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
          146  +
          147  +	PlatformEventsInit();
          148  +	return tsdPtr;
          149  +    }
          150  +}
          151  +
          152  +/*
          153  + *----------------------------------------------------------------------
          154  + *
          155  + * Tcl_FinalizeNotifier --
          156  + *
          157  + *	This function is called to cleanup the notifier state before a thread
          158  + *	is terminated.
          159  + *
          160  + * Results:
          161  + *	None.
          162  + *
          163  + * Side effects:
          164  + *	If no finalizeNotifierProc notifier hook exists, PlatformEvents-
          165  + *	Finalize is called.
          166  + *
          167  + *----------------------------------------------------------------------
          168  + */
          169  +
          170  +void
          171  +Tcl_FinalizeNotifier(
          172  +    ClientData clientData)		/* Not used. */
          173  +{
          174  +    if (tclNotifierHooks.finalizeNotifierProc) {
          175  +	tclNotifierHooks.finalizeNotifierProc(clientData);
          176  +	return;
          177  +    } else {
          178  +	PlatformEventsFinalize();
          179  +    }
          180  +}
          181  +
          182  +/*
          183  + *----------------------------------------------------------------------
          184  + *
          185  + * PlatformEventsControl --
          186  + *
          187  + *	This function registers interest for the file descriptor and the mask
          188  + *	of TCL_* bits associated with filePtr on the epoll file descriptor
          189  + *	associated with tsdPtr.
          190  + *	Future calls to epoll_wait will return filePtr and tsdPtr alongside with
          191  + *	the event registered here via the PlatformEventData struct.
          192  + *
          193  + * Results:
          194  + *	None.
          195  + *
          196  + * Side effects:
          197  + *	If adding a new file descriptor, a PlatformEventData struct will be
          198  + *	allocated and associated with filePtr.
          199  + *	fstat is called on the file descriptor; if it is associated with
          200  + *	a regular file (S_IFREG,) filePtr is considered to be ready for I/O
          201  + *	and added to or deleted from the corresponding list in tsdPtr.
          202  + *	If it is not associated with a regular file, the file descriptor is
          203  + *	added, modified concerning its mask of events of interest, or deleted
          204  + *	from the epoll file descriptor of the calling thread.
          205  + *
          206  + *----------------------------------------------------------------------
          207  + */
          208  +
          209  +void
          210  +PlatformEventsControl(
          211  +	FileHandler *filePtr,
          212  +	ThreadSpecificData *tsdPtr,
          213  +	int op,
          214  +	int isNew)
          215  +{
          216  +    struct epoll_event newEvent;
          217  +    struct PlatformEventData *newPedPtr;
          218  +    struct stat fdStat;
          219  +
          220  +    newEvent.events = 0;
          221  +    if (filePtr->mask & (TCL_READABLE | TCL_EXCEPTION)) {
          222  +	newEvent.events |= EPOLLIN;
          223  +    }
          224  +    if (filePtr->mask & TCL_WRITABLE) {
          225  +	newEvent.events |= EPOLLOUT;
          226  +    }
          227  +    if (isNew) {
          228  +        newPedPtr = ckalloc(sizeof(*newPedPtr));
          229  +        newPedPtr->filePtr = filePtr;
          230  +        newPedPtr->tsdPtr = tsdPtr;
          231  +	filePtr->pedPtr = newPedPtr;
          232  +    }
          233  +    newEvent.data.ptr = filePtr->pedPtr;
          234  +
          235  +    /*
          236  +     * N.B.	As discussed in Tcl_WaitForEvent(), epoll(7) does not sup-
          237  +     *		port regular files (S_IFREG.) Therefore, filePtr is in these
          238  +     *		cases simply added or deleted from the list of FileHandlers
          239  +     *		associated with regular files belonging to tsdPtr.
          240  +     */
          241  +
          242  +    if (fstat(filePtr->fd, &fdStat) == -1) {
          243  +	Tcl_Panic("fstat: %s", strerror(errno));
          244  +    } else if ((fdStat.st_mode & S_IFMT) == S_IFREG) {
          245  +	switch (op) {
          246  +	case EPOLL_CTL_ADD:
          247  +	    if (isNew) {
          248  +		LIST_INSERT_HEAD(&tsdPtr->firstReadyFileHandlerPtr, filePtr, readyNode);
          249  +	    }
          250  +	    break;
          251  +	case EPOLL_CTL_DEL:
          252  +	    LIST_REMOVE(filePtr, readyNode);
          253  +	    break;
          254  +	}
          255  +	return;
          256  +   } else if (epoll_ctl(tsdPtr->eventsFd, op, filePtr->fd, &newEvent) == -1) {
          257  +	Tcl_Panic("epoll_ctl: %s", strerror(errno));
          258  +   }
          259  +}
          260  +
          261  +/*
          262  + *----------------------------------------------------------------------
          263  + *
          264  + * PlatformEventsFinalize --
          265  + *
          266  + *	This function closes the eventfd and the epoll file descriptor and
          267  + *	frees the epoll_event structs owned by the thread of the caller.
          268  + *	The above operations are protected by tsdPtr->notifierMutex, which
          269  + *	is destroyed thereafter.
          270  + *
          271  + * Results:
          272  + *	None.
          273  + *
          274  + * Side effects:
          275  + * 	While tsdPtr->notifierMutex is held:
          276  + *	The per-thread eventfd(2) is closed, if non-zero, and set to -1.
          277  + *	The per-thread epoll(7) fd is closed, if non-zero, and set to 0.
          278  + *	The per-thread epoll_event structs are freed, if any, and set to 0.
          279  + *
          280  + *	tsdPtr->notifierMutex is destroyed.
          281  + *
          282  + *----------------------------------------------------------------------
          283  + */
          284  +
          285  +void
          286  +PlatformEventsFinalize(
          287  +	void)
          288  +{
          289  +    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
          290  +
          291  +    pthread_mutex_lock(&tsdPtr->notifierMutex);
          292  +#ifdef HAVE_EVENTFD
          293  +    if (tsdPtr->triggerEventFd) {
          294  +	close(tsdPtr->triggerEventFd);
          295  +	tsdPtr->triggerEventFd = -1;
          296  +    }
          297  +#else
          298  +    if (tsdPtr->triggerPipe[0]) {
          299  +	close(tsdPtr->triggerPipe[0]);
          300  +	tsdPtr->triggerPipe[0] = -1;
          301  +    }
          302  +    if (tsdPtr->triggerPipe[1]) {
          303  +	close(tsdPtr->triggerPipe[1]);
          304  +	tsdPtr->triggerPipe[1] = -1;
          305  +    }
          306  +#endif /* HAVE_EVENTFD */
          307  +    if (tsdPtr->eventsFd > 0) {
          308  +	close(tsdPtr->eventsFd);
          309  +	tsdPtr->eventsFd = 0;
          310  +    }
          311  +    if (tsdPtr->readyEvents) {
          312  +	ckfree(tsdPtr->readyEvents);
          313  +	tsdPtr->maxReadyEvents = 0;
          314  +    }
          315  +    pthread_mutex_unlock(&tsdPtr->notifierMutex);
          316  +    if ((errno = pthread_mutex_destroy(&tsdPtr->notifierMutex))) {
          317  +	Tcl_Panic("pthread_mutex_destroy: %s", strerror(errno));
          318  +    }
          319  +}
          320  +
          321  +/*
          322  + *----------------------------------------------------------------------
          323  + *
          324  + * PlatformEventsInit --
          325  + *
          326  + *	This function abstracts creating a kqueue fd via the epoll_create
          327  + *	system call and allocating memory for the epoll_event structs in
          328  + *	tsdPtr for the thread of the caller.
          329  + *
          330  + * Results:
          331  + *	None.
          332  + *
          333  + * Side effects:
          334  + *	The following per-thread entities are initialised:
          335  + *	notifierMutex is initialised.
          336  + *	The eventfd(2) is created w/ EFD_CLOEXEC and EFD_NONBLOCK.
          337  + *	The epoll(7) fd is created w/ EPOLL_CLOEXEC.
          338  + *	A FileHandler struct is allocated and initialised for the event-
          339  + *	fd(2), registering interest for TCL_READABLE on it via Platform-
          340  + *	EventsControl().
          341  + *	readyEvents and maxReadyEvents are initialised with 512 epoll_events.
          342  + *
          343  + *----------------------------------------------------------------------
          344  + */
          345  +
          346  +void
          347  +PlatformEventsInit(
          348  +	void)
          349  +{
          350  +    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
          351  +    FileHandler *filePtr;
          352  +
          353  +    errno = pthread_mutex_init(&tsdPtr->notifierMutex, NULL);
          354  +    if (errno) {
          355  +	Tcl_Panic("Tcl_InitNotifier: %s", "could not create mutex");
          356  +    }
          357  +    filePtr = ckalloc(sizeof(*filePtr));
          358  +#ifdef HAVE_EVENTFD
          359  +    if ((tsdPtr->triggerEventFd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)) <= 0) {
          360  +	Tcl_Panic("Tcl_InitNotifier: %s", "could not create trigger eventfd");
          361  +    }
          362  +    filePtr->fd = tsdPtr->triggerEventFd;
          363  +#else
          364  +    if (pipe2(tsdPtr->triggerPipe, O_CLOEXEC | O_NONBLOCK) != 0) {
          365  +	Tcl_Panic("Tcl_InitNotifier: %s", "could not create trigger pipe");
          366  +    }
          367  +    filePtr->fd = tsdPtr->triggerPipe[0];
          368  +#endif
          369  +    if ((tsdPtr->eventsFd = epoll_create1(EPOLL_CLOEXEC)) == -1) {
          370  +	Tcl_Panic("epoll_create1: %s", strerror(errno));
          371  +    }
          372  +    filePtr->mask = TCL_READABLE;
          373  +    PlatformEventsControl(filePtr, tsdPtr, EPOLL_CTL_ADD, 1);
          374  +    if (!tsdPtr->readyEvents) {
          375  +        tsdPtr->maxReadyEvents = 512;
          376  +	tsdPtr->readyEvents = ckalloc(tsdPtr->maxReadyEvents
          377  +	    * sizeof(tsdPtr->readyEvents[0]));
          378  +    }
          379  +    LIST_INIT(&tsdPtr->firstReadyFileHandlerPtr);
          380  +}
          381  +
          382  +/*
          383  + *----------------------------------------------------------------------
          384  + *
          385  + * PlatformEventsTranslate --
          386  + *
          387  + *	This function translates the platform-specific mask of returned
          388  + *	events in eventPtr to a mask of TCL_* bits.
          389  + *
          390  + * Results:
          391  + *	Returns the translated mask.
          392  + *
          393  + * Side effects:
          394  + *	None.
          395  + *
          396  + *----------------------------------------------------------------------
          397  + */
          398  +
          399  +int
          400  +PlatformEventsTranslate(
          401  +	struct epoll_event *eventPtr)
          402  +{
          403  +    int mask;
          404  +
          405  +    mask = 0;
          406  +    if (eventPtr->events & (EPOLLIN | EPOLLHUP)) {
          407  +	mask |= TCL_READABLE;
          408  +    }
          409  +    if (eventPtr->events & EPOLLOUT) {
          410  +	mask |= TCL_WRITABLE;
          411  +    }
          412  +    if (eventPtr->events & EPOLLERR) {
          413  +	mask |= TCL_EXCEPTION;
          414  +    }
          415  +    return mask;
          416  +}
          417  +
          418  +/*
          419  + *----------------------------------------------------------------------
          420  + *
          421  + * PlatformEventsWait --
          422  + *
          423  + *	This function abstracts waiting for I/O events via epoll_wait.
          424  + *
          425  + * Results:
          426  + *	Returns -1 if epoll_wait failed. Returns 0 if polling and if no
          427  + *	events became available whilst polling. Returns a pointer to and
          428  + *	the count of all returned events in all other cases.
          429  + *
          430  + * Side effects:
          431  + *	gettimeofday(2), epoll_wait(2), and gettimeofday(2) are called,
          432  + *	in the specified order.
          433  + *	If timePtr specifies a positive value, it is updated to reflect
          434  + *	the amount of time that has passed; if its value would {under,
          435  + *	over}flow, it is set to zero.
          436  + *
          437  + *----------------------------------------------------------------------
          438  + */
          439  +
          440  +int
          441  +PlatformEventsWait(
          442  +    struct epoll_event *events,
          443  +    size_t numEvents,
          444  +    struct timeval *timePtr)
          445  +{
          446  +    int numFound;
          447  +    struct timeval tv0, tv1, tv_delta;
          448  +    int timeout;
          449  +
          450  +    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
          451  +
          452  +    /*
          453  +     * If timePtr is NULL, epoll_wait(2) will wait indefinitely. If it
          454  +     * specifies a timeout of {0,0}, epoll_wait(2) will poll. Otherwise,
          455  +     * the timeout will simply be converted to milliseconds.
          456  +     */
          457  +
          458  +    if (!timePtr) {
          459  +	timeout = -1;
          460  +    } else if (!timePtr->tv_sec && !timePtr->tv_usec) {
          461  +	timeout = 0;
          462  +    } else {
          463  +	timeout = (int)timePtr->tv_sec;
          464  +    }
          465  +
          466  +    /*
          467  +     * Call (and possibly block on) epoll_wait(2) and substract the delta
          468  +     * of gettimeofday(2) before and after the call from timePtr if the
          469  +     * latter is not NULL. Return the number of events returned by epoll_wait(2).
          470  +     */
          471  +
          472  +    gettimeofday(&tv0, NULL);
          473  +    numFound = epoll_wait(tsdPtr->eventsFd, events, (int)numEvents, timeout);
          474  +    gettimeofday(&tv1, NULL);
          475  +    if (timePtr && (timePtr->tv_sec && timePtr->tv_usec)) {
          476  +	timersub(&tv1, &tv0, &tv_delta);
          477  +	if (!timercmp(&tv_delta, timePtr, >)) {
          478  +		timersub(timePtr, &tv_delta, timePtr);
          479  +	} else {
          480  +		timePtr->tv_sec = 0;
          481  +		timePtr->tv_usec = 0;
          482  +	}
          483  +    }
          484  +    return numFound;
          485  +}
          486  +
          487  +/*
          488  + *----------------------------------------------------------------------
          489  + *
          490  + * Tcl_CreateFileHandler --
          491  + *
          492  + *	This function registers a file handler with the epoll notifier
          493  + *	of the thread of the caller.
          494  + *
          495  + * Results:
          496  + *	None.
          497  + *
          498  + * Side effects:
          499  + *	Creates a new file handler structure.
          500  + *	PlatformEventsControl() is called for the new file handler structure.
          501  + *
          502  + *----------------------------------------------------------------------
          503  + */
          504  +
          505  +void
          506  +Tcl_CreateFileHandler(
          507  +    int fd,			/* Handle of stream to watch. */
          508  +    int mask,			/* OR'ed combination of TCL_READABLE,
          509  +				 * TCL_WRITABLE, and TCL_EXCEPTION: indicates
          510  +				 * conditions under which proc should be
          511  +				 * called. */
          512  +    Tcl_FileProc *proc,		/* Function to call for each selected
          513  +				 * event. */
          514  +    ClientData clientData)	/* Arbitrary data to pass to proc. */
          515  +{
          516  +    int isNew;
          517  +
          518  +    if (tclNotifierHooks.createFileHandlerProc) {
          519  +	tclNotifierHooks.createFileHandlerProc(fd, mask, proc, clientData);
          520  +	return;
          521  +    } else {
          522  +	ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
          523  +	FileHandler *filePtr;
          524  +
          525  +	for (filePtr = tsdPtr->firstFileHandlerPtr; filePtr != NULL;
          526  +		filePtr = filePtr->nextPtr) {
          527  +	    if (filePtr->fd == fd) {
          528  +		break;
          529  +	    }
          530  +	}
          531  +	if (filePtr == NULL) {
          532  +	    filePtr = ckalloc(sizeof(FileHandler));
          533  +	    filePtr->fd = fd;
          534  +	    filePtr->readyMask = 0;
          535  +	    filePtr->nextPtr = tsdPtr->firstFileHandlerPtr;
          536  +	    tsdPtr->firstFileHandlerPtr = filePtr;
          537  +	    isNew = 1;
          538  +	} else {
          539  +	    isNew = 0;
          540  +	}
          541  +	filePtr->proc = proc;
          542  +	filePtr->clientData = clientData;
          543  +	filePtr->mask = mask;
          544  +
          545  +	PlatformEventsControl(filePtr, tsdPtr, isNew ?
          546  +	    EPOLL_CTL_ADD : EPOLL_CTL_MOD, isNew);
          547  +    }
          548  +}
          549  +
          550  +/*
          551  + *----------------------------------------------------------------------
          552  + *
          553  + * Tcl_DeleteFileHandler --
          554  + *
          555  + *	Cancel a previously-arranged callback arrangement for a file on
          556  + *	the epoll file descriptor of the thread of the caller.
          557  + *
          558  + * Results:
          559  + *	None.
          560  + *
          561  + * Side effects:
          562  + *	If a callback was previously registered on file, remove it.
          563  + *	PlatformEventsControl() is called for the file handler structure.
          564  + *	The PlatformEventData struct associated with the new file handler
          565  + *	structure is freed.
          566  + *
          567  + *----------------------------------------------------------------------
          568  + */
          569  +
          570  +void
          571  +Tcl_DeleteFileHandler(
          572  +    int fd)			/* Stream id for which to remove callback
          573  +				 * function. */
          574  +{
          575  +    if (tclNotifierHooks.deleteFileHandlerProc) {
          576  +	tclNotifierHooks.deleteFileHandlerProc(fd);
          577  +	return;
          578  +    } else {
          579  +	FileHandler *filePtr, *prevPtr;
          580  +	ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
          581  +
          582  +	/*
          583  +	 * Find the entry for the given file (and return if there isn't one).
          584  +	 */
          585  +
          586  +	for (prevPtr = NULL, filePtr = tsdPtr->firstFileHandlerPtr; ;
          587  +		prevPtr = filePtr, filePtr = filePtr->nextPtr) {
          588  +	    if (filePtr == NULL) {
          589  +		return;
          590  +	    }
          591  +	    if (filePtr->fd == fd) {
          592  +		break;
          593  +	    }
          594  +	}
          595  +
          596  +	/*
          597  +	 * Update the check masks for this file.
          598  +	 */
          599  +
          600  +	PlatformEventsControl(filePtr, tsdPtr, EPOLL_CTL_DEL, 0);
          601  +	if (filePtr->pedPtr) {
          602  +	    ckfree(filePtr->pedPtr);
          603  +	}
          604  +
          605  +	/*
          606  +	 * Clean up information in the callback record.
          607  +	 */
          608  +
          609  +	if (prevPtr == NULL) {
          610  +	    tsdPtr->firstFileHandlerPtr = filePtr->nextPtr;
          611  +	} else {
          612  +	    prevPtr->nextPtr = filePtr->nextPtr;
          613  +	}
          614  +	ckfree(filePtr);
          615  +    }
          616  +}
          617  +
          618  +/*
          619  + *----------------------------------------------------------------------
          620  + *
          621  + * Tcl_WaitForEvent --
          622  + *
          623  + *	This function is called by Tcl_DoOneEvent to wait for new events on
          624  + *	the message queue. If the block time is 0, then Tcl_WaitForEvent just
          625  + *	polls without blocking.
          626  + *	The waiting logic is implemented in PlatformEventsWait.
          627  + *
          628  + * Results:
          629  + *	Returns -1 if PlatformEventsWait() would block forever, otherwise
          630  + *	returns 0.
          631  + *
          632  + * Side effects:
          633  + *	Queues file events that are detected by PlatformEventsWait().
          634  + *
          635  + *----------------------------------------------------------------------
          636  + */
          637  +
          638  +int
          639  +Tcl_WaitForEvent(
          640  +    const Tcl_Time *timePtr)		/* Maximum block time, or NULL. */
          641  +{
          642  +    if (tclNotifierHooks.waitForEventProc) {
          643  +	return tclNotifierHooks.waitForEventProc(timePtr);
          644  +    } else {
          645  +	FileHandler *filePtr;
          646  +	int mask;
          647  +	Tcl_Time vTime;
          648  +	/*
          649  +	 * Impl. notes: timeout & timeoutPtr are used if, and only if threads
          650  +	 * are not enabled. They are the arguments for the regular epoll_wait()
          651  +	 * used when the core is not thread-enabled.
          652  +	 */
          653  +
          654  +	struct timeval timeout, *timeoutPtr;
          655  +	int numFound, numEvent;
          656  +	struct PlatformEventData *pedPtr;
          657  +	ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
          658  +	int numQueued;
          659  +	ssize_t i;
          660  +
          661  +	/*
          662  +	 * Set up the timeout structure. Note that if there are no events to
          663  +	 * check for, we return with a negative result rather than blocking
          664  +	 * forever.
          665  +	 */
          666  +
          667  +	if (timePtr != NULL) {
          668  +	    /*
          669  +	     * TIP #233 (Virtualized Time). Is virtual time in effect? And do
          670  +	     * we actually have something to scale? If yes to both then we
          671  +	     * call the handler to do this scaling.
          672  +	     */
          673  +
          674  +	    if (timePtr->sec != 0 || timePtr->usec != 0) {
          675  +		vTime = *timePtr;
          676  +		tclScaleTimeProcPtr(&vTime, tclTimeClientData);
          677  +		timePtr = &vTime;
          678  +	    }
          679  +	    timeout.tv_sec = timePtr->sec;
          680  +	    timeout.tv_usec = timePtr->usec;
          681  +	    timeoutPtr = &timeout;
          682  +	} else {
          683  +	    timeoutPtr = NULL;
          684  +	}
          685  +
          686  +	/*
          687  +	 * Walk the list of FileHandlers associated with regular files
          688  +	 * (S_IFREG) belonging to tsdPtr, queue Tcl events for them, and
          689  +	 * update their mask of events of interest.
          690  +	 * As epoll(7) does not support regular files, the behaviour of
          691  +	 * {select,poll}(2) is simply simulated here: fds associated with
          692  +	 * regular files are added to this list by PlatformEventsControl()
          693  +	 * and processed here before calling (and possibly blocking) on
          694  +	 * PlatformEventsWait().
          695  +	 */
          696  +
          697  +	numQueued = 0;
          698  +	LIST_FOREACH(filePtr, &tsdPtr->firstReadyFileHandlerPtr, readyNode) {
          699  +	    mask = 0;
          700  +	    if (filePtr->mask & TCL_READABLE) {
          701  +		mask |= TCL_READABLE;
          702  +	    }
          703  +	    if (filePtr->mask & TCL_WRITABLE) {
          704  +		mask |= TCL_WRITABLE;
          705  +	    }
          706  +
          707  +	    /*
          708  +	     * Don't bother to queue an event if the mask was previously
          709  +	     * non-zero since an event must still be on the queue.
          710  +	     */
          711  +
          712  +	    if (filePtr->readyMask == 0) {
          713  +		FileHandlerEvent *fileEvPtr =
          714  +		    ckalloc(sizeof(FileHandlerEvent));
          715  +
          716  +		fileEvPtr->header.proc = FileHandlerEventProc;
          717  +		fileEvPtr->fd = filePtr->fd;
          718  +		Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL);
          719  +		numQueued++;
          720  +	    }
          721  +	    filePtr->readyMask = mask;
          722  +	}
          723  +
          724  +	/*
          725  +	 * If any events were queued in the above loop, force PlatformEvents-
          726  +	 * Wait() to poll as there already are events that need to be processed
          727  +	 * at this point.
          728  +	 */
          729  +
          730  +	if (numQueued) {
          731  +	    timeout.tv_sec = 0;
          732  +	    timeout.tv_usec = 0;
          733  +	    timeoutPtr = &timeout;
          734  +	}
          735  +
          736  +	/*
          737  +	 * Wait or poll for new events, queue Tcl events for the FileHandlers
          738  +	 * corresponding to them, and update the FileHandlers' mask of events
          739  +	 * of interest registered by the last call to Tcl_CreateFileHandler().
          740  +	 *
          741  +	 * Events for the eventfd(2)/trigger pipe are processed here in order
          742  +	 * to facilitate inter-thread IPC. If another thread intends to wake
          743  +	 * up this thread whilst it's blocking on PlatformEventsWait(), it
          744  +	 * write(2)s to the eventfd(2)/trigger pipe (see Tcl_AlertNotifier(),)
          745  +	 * which in turn will cause PlatformEventsWait() to return immediately.
          746  +	 */
          747  +
          748  +	numFound = PlatformEventsWait(tsdPtr->readyEvents, tsdPtr->maxReadyEvents, timeoutPtr);
          749  +	for (numEvent = 0; numEvent < numFound; numEvent++) {
          750  +	    pedPtr = tsdPtr->readyEvents[numEvent].data.ptr;
          751  +	    filePtr = pedPtr->filePtr;
          752  +	    mask = PlatformEventsTranslate(&tsdPtr->readyEvents[numEvent]);
          753  +#ifdef HAVE_EVENTFD
          754  +	    if (filePtr->fd == tsdPtr->triggerEventFd) {
          755  +		uint64_t eventFdVal;
          756  +		i = read(tsdPtr->triggerEventFd, &eventFdVal, sizeof(eventFdVal));
          757  +		if ((i != sizeof(eventFdVal)) && (errno != EAGAIN)) {
          758  +#else
          759  +	    if (filePtr->fd == tsdPtr->triggerPipe[0]) {
          760  +		char triggerPipeVal;
          761  +		i = read(tsdPtr->triggerPipe[0], &triggerPipeVal, sizeof(triggerPipeVal));
          762  +		if ((i != sizeof(triggerPipeVal)) && (errno != EAGAIN)) {
          763  +#endif
          764  +			Tcl_Panic("Tcl_WaitForEvent: "
          765  +				"read from %p->triggerEventFd: %s",
          766  +				(void *)tsdPtr, strerror(errno));
          767  +		} else {
          768  +		    continue;
          769  +		}
          770  +	    }
          771  +	    if (!mask) {
          772  +		continue;
          773  +	    }
          774  +
          775  +	    /*
          776  +	     * Don't bother to queue an event if the mask was previously
          777  +	     * non-zero since an event must still be on the queue.
          778  +	     */
          779  +
          780  +	    if (filePtr->readyMask == 0) {
          781  +		FileHandlerEvent *fileEvPtr =
          782  +			ckalloc(sizeof(FileHandlerEvent));
          783  +
          784  +		fileEvPtr->header.proc = FileHandlerEventProc;
          785  +		fileEvPtr->fd = filePtr->fd;
          786  +		Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL);
          787  +	    }
          788  +	    filePtr->readyMask = mask;
          789  +	}
          790  +	return 0;
          791  +    }
          792  +}
          793  +
          794  +#endif /* !HAVE_COREFOUNDATION */
          795  +
          796  +#endif /* NOTIFIER_EPOLL */
          797  +
          798  +/*
          799  + * Local Variables:
          800  + * mode: c
          801  + * c-basic-offset: 4
          802  + * fill-column: 78
          803  + * End:
          804  + */

Added unix/tclKqueueNotfy.c.

            1  +/*
            2  + * tclKqueueNotfy.c --
            3  + *
            4  + *	This file contains the implementation of the kqueue()-based
            5  + *	DragonFly/Free/Net/OpenBSD-specific notifier, which is the lowest-
            6  + *	level part of the Tcl event loop. This file works together with
            7  + *	generic/tclNotify.c.
            8  + *
            9  + * Copyright (c) 1995-1997 Sun Microsystems, Inc.
           10  + * Copyright (c) 2016 Lucio Andrés Illanes Albornoz <[email protected]>
           11  + *
           12  + * See the file "license.terms" for information on usage and redistribution
           13  + * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
           14  + */
           15  +
           16  +#ifdef NOTIFIER_KQUEUE
           17  +
           18  +#include "tclInt.h"
           19  +#ifndef HAVE_COREFOUNDATION	/* Darwin/Mac OS X CoreFoundation notifier is
           20  +				 * in tclMacOSXNotify.c */
           21  +#include <signal.h>
           22  +#include <sys/types.h>
           23  +#include <sys/event.h>
           24  +#include <sys/queue.h>
           25  +#include <sys/time.h>
           26  +
           27  +/*
           28  + * This structure is used to keep track of the notifier info for a registered
           29  + * file.
           30  + */
           31  +
           32  +struct PlatformEventData;
           33  +typedef struct FileHandler {
           34  +    int fd;
           35  +    int mask;			/* Mask of desired events: TCL_READABLE,
           36  +				 * etc. */
           37  +    int readyMask;		/* Mask of events that have been seen since
           38  +				 * the last time file handlers were invoked
           39  +				 * for this file. */
           40  +    Tcl_FileProc *proc;		/* Function to call, in the style of
           41  +				 * Tcl_CreateFileHandler. */
           42  +    ClientData clientData;	/* Argument to pass to proc. */
           43  +    struct FileHandler *nextPtr;/* Next in list of all files we care about. */
           44  +    LIST_ENTRY(FileHandler) readyNode;
           45  +				/* Next/previous in list of FileHandlers asso-
           46  +				 * ciated with regular files (S_IFREG) that are
           47  +				 * ready for I/O. */
           48  +    struct PlatformEventData *pedPtr;
           49  +				/* Pointer to PlatformEventData associating this
           50  +				 * FileHandler with kevent(2) events. */
           51  +} FileHandler;
           52  +
           53  +/*
           54  + * The following structure associates a FileHandler and the thread that owns it
           55  + * with the file descriptors of interest and their event masks passed to kevent(2)
           56  + * and their corresponding event(s) returned by kevent(2).
           57  + */
           58  +
           59  +struct ThreadSpecificData;
           60  +struct PlatformEventData {
           61  +    FileHandler *filePtr;
           62  +    struct ThreadSpecificData *tsdPtr;
           63  +};
           64  +
           65  +/*
           66  + * The following structure is what is added to the Tcl event queue when file
           67  + * handlers are ready to fire.
           68  + */
           69  +
           70  +typedef struct {
           71  +    Tcl_Event header;		/* Information that is standard for all
           72  +				 * events. */
           73  +    int fd;			/* File descriptor that is ready. Used to find
           74  +				 * the FileHandler structure for the file
           75  +				 * (can't point directly to the FileHandler
           76  +				 * structure because it could go away while
           77  +				 * the event is queued). */
           78  +} FileHandlerEvent;
           79  +
           80  +/*
           81  + * The following static structure contains the state information for the
           82  + * kqueue based implementation of the Tcl notifier. One of these structures is
           83  + * created for each thread that is using the notifier.
           84  + */
           85  +
           86  +LIST_HEAD(PlatformReadyFileHandlerList, FileHandler);
           87  +typedef struct ThreadSpecificData {
           88  +    FileHandler *firstFileHandlerPtr;
           89  +				/* Pointer to head of file handler list. */
           90  +    struct PlatformReadyFileHandlerList firstReadyFileHandlerPtr;
           91  +				/* Pointer to head of list of FileHandlers
           92  +				 * associated with regular files (S_IFREG)
           93  +				 * that are ready for I/O. */
           94  +    pthread_mutex_t notifierMutex;
           95  +				/* Mutex protecting notifier termination in
           96  +				 * PlatformEventsFinalize. */
           97  +    int triggerPipe[2];		/* pipe(2) used by other threads to wake
           98  +				 * up this thread for inter-thread IPC. */
           99  +    int eventsFd;		/* kqueue(2) file descriptor used to wait for fds. */
          100  +    struct kevent *readyEvents;	/* Pointer to at most maxReadyEvents events
          101  +				 * returned by kevent(2). */
          102  +    size_t maxReadyEvents;	/* Count of kevents in readyEvents. */
          103  +} ThreadSpecificData;
          104  +
          105  +static Tcl_ThreadDataKey dataKey;
          106  +
          107  +void PlatformEventsControl(FileHandler *filePtr, ThreadSpecificData *tsdPtr, int op, int isNew);
          108  +static void PlatformEventsFinalize(void);
          109  +void PlatformEventsInit(void);
          110  +static int PlatformEventsTranslate(struct kevent *eventPtr);
          111  +static int PlatformEventsWait(struct kevent *events, size_t numEvents, struct timeval *timePtr);
          112  +
          113  +#include "tclUnixNotfy.c"
          114  +
          115  +/*
          116  + *----------------------------------------------------------------------
          117  + *
          118  + * Tcl_InitNotifier --
          119  + *
          120  + *	Initializes the platform specific notifier state.
          121  + *
          122  + * Results:
          123  + *	Returns a handle to the notifier state for this thread.
          124  + *
          125  + * Side effects:
          126  + *	If no initNotifierProc notifier hook exists, PlatformEventsInit
          127  + *	is called.
          128  + *
          129  + *----------------------------------------------------------------------
          130  + */
          131  +
          132  +ClientData
          133  +Tcl_InitNotifier(void)
          134  +{
          135  +    if (tclNotifierHooks.initNotifierProc) {
          136  +	return tclNotifierHooks.initNotifierProc();
          137  +    } else {
          138  +	ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
          139  +
          140  +	PlatformEventsInit();
          141  +	return tsdPtr;
          142  +    }
          143  +}
          144  +
          145  +/*
          146  + *----------------------------------------------------------------------
          147  + *
          148  + * Tcl_FinalizeNotifier --
          149  + *
          150  + *	This function is called to cleanup the notifier state before a thread
          151  + *	is terminated.
          152  + *
          153  + * Results:
          154  + *	None.
          155  + *
          156  + * Side effects:
          157  + *	If no finalizeNotifierProc notifier hook exists, PlatformEvents-
          158  + *	Finalize is called.
          159  + *
          160  + *----------------------------------------------------------------------
          161  + */
          162  +
          163  +void
          164  +Tcl_FinalizeNotifier(
          165  +    ClientData clientData)		/* Not used. */
          166  +{
          167  +    if (tclNotifierHooks.finalizeNotifierProc) {
          168  +	tclNotifierHooks.finalizeNotifierProc(clientData);
          169  +	return;
          170  +    } else {
          171  +	PlatformEventsFinalize();
          172  +    }
          173  +}
          174  +
          175  +/*
          176  + *----------------------------------------------------------------------
          177  + *
          178  + * PlatformEventsControl --
          179  + *
          180  + *	This function registers interest for the file descriptor and the mask
          181  + *	of TCL_* bits associated with filePtr on the kqueue file descriptor
          182  + *	associated with tsdPtr.
          183  + *	Future calls to kevent will return filePtr and tsdPtr alongside with
          184  + *	the event registered here via the PlatformEventData struct.
          185  + *
          186  + * Results:
          187  + *	None.
          188  + *
          189  + * Side effects:
          190  + *	If adding a new file descriptor, a PlatformEventData struct will be
          191  + *	allocated and associated with filePtr.
          192  + *	fstat is called on the file descriptor; if it is associated with
          193  + *	a regular file (S_IFREG,) filePtr is considered to be ready for I/O
          194  + *	and added to or deleted from the corresponding list in tsdPtr.
          195  + *	If it is not associated with a regular file, the file descriptor is
          196  + *	added, modified concerning its mask of events of interest, or deleted
          197  + *	from the epoll file descriptor of the calling thread.
          198  + *	If deleting a file descriptor, kevent(2) is called twice specifying
          199  + *	EVFILT_READ first and then EVFILT_WRITE (see note below.)
          200  + *
          201  + *----------------------------------------------------------------------
          202  + */
          203  +
          204  +void
          205  +PlatformEventsControl(
          206  +	FileHandler *filePtr,
          207  +	ThreadSpecificData *tsdPtr,
          208  +	int op,
          209  +	int isNew)
          210  +{
          211  +    int numChanges;
          212  +    struct kevent changeList[2];
          213  +    struct PlatformEventData *newPedPtr;
          214  +    struct stat fdStat;
          215  +
          216  +    if (isNew) {
          217  +        newPedPtr = ckalloc(sizeof(*newPedPtr));
          218  +        newPedPtr->filePtr = filePtr;
          219  +        newPedPtr->tsdPtr = tsdPtr;
          220  +        filePtr->pedPtr = newPedPtr;
          221  +    }
          222  +
          223  +    /*
          224  +     * N.B.	As discussed in Tcl_WaitForEvent(), kqueue(2) does not repro-
          225  +     *		duce the `always ready' {select,poll}(2) behaviour for regular
          226  +     *		files (S_IFREG) prior to FreeBSD 11.0-RELEASE. Therefore, file-
          227  +     *		Ptr is in these cases simply added or deleted from the list of
          228  +     *		FileHandlers associated with regular files belonging to tsdPtr.
          229  +     */
          230  +
          231  +    if (fstat(filePtr->fd, &fdStat) == -1) {
          232  +	Tcl_Panic("fstat: %s", strerror(errno));
          233  +    } else if ((fdStat.st_mode & S_IFMT) == S_IFREG) {
          234  +	switch (op) {
          235  +	case EV_ADD:
          236  +	    if (isNew) {
          237  +		LIST_INSERT_HEAD(&tsdPtr->firstReadyFileHandlerPtr, filePtr, readyNode);
          238  +	    }
          239  +	    break;
          240  +	case EV_DELETE:
          241  +	    LIST_REMOVE(filePtr, readyNode);
          242  +	    break;
          243  +	}
          244  +	return;
          245  +    }
          246  +
          247  +    numChanges = 0;
          248  +    switch (op) {
          249  +    case EV_ADD:
          250  +	if (filePtr->mask & (TCL_READABLE | TCL_EXCEPTION)) {
          251  +	    EV_SET(&changeList[numChanges], (uintptr_t)filePtr->fd, EVFILT_READ,
          252  +		op, 0, 0, filePtr->pedPtr);
          253  +	    numChanges++;
          254  +	}
          255  +	if (filePtr->mask & TCL_WRITABLE) {
          256  +	    EV_SET(&changeList[numChanges], (uintptr_t)filePtr->fd, EVFILT_WRITE,
          257  +		op, 0, 0, filePtr->pedPtr);
          258  +	    numChanges++;
          259  +	}
          260  +        if (numChanges) {
          261  +	    if (kevent(tsdPtr->eventsFd, changeList, numChanges, NULL, 0, NULL) == -1) {
          262  +		Tcl_Panic("kevent: %s", strerror(errno));
          263  +	    }
          264  +	}
          265  +	break;
          266  +    case EV_DELETE:
          267  +	/*
          268  +	 * N.B.	kqueue(2) has separate filters for readability and writabi-
          269  +	 *		lity fd events. We therefore need to ensure that fds are
          270  +	 *		ompletely removed from the kqueue(2) fd when deleting.
          271  +	 *		This is exacerbated by changes to filePtr->mask w/o calls
          272  +	 *		to PlatforEventsControl() after e.g. an exec(3) in a child
          273  +	 *		process.
          274  +	 *		As one of these calls can fail, two separate kevent(2) calls
          275  +	 *		are made for EVFILT_{READ,WRITE}.
          276  +	 */
          277  +	EV_SET(&changeList[0], (uintptr_t)filePtr->fd, EVFILT_READ, op, 0, 0, NULL);
          278  +	if ((kevent(tsdPtr->eventsFd, changeList, 1, NULL, 0, NULL) == -1)
          279  +		&& (errno != ENOENT)) {
          280  +	    Tcl_Panic("kevent: %s", strerror(errno));
          281  +	}
          282  +	EV_SET(&changeList[0], (uintptr_t)filePtr->fd, EVFILT_WRITE, op, 0, 0, NULL);
          283  +	if ((kevent(tsdPtr->eventsFd, changeList, 1, NULL, 0, NULL) == -1)
          284  +		&& (errno != ENOENT)) {
          285  +	    Tcl_Panic("kevent: %s", strerror(errno));
          286  +	}
          287  +	break;
          288  +    }
          289  +}
          290  +
          291  +/*
          292  + *----------------------------------------------------------------------
          293  + *
          294  + * PlatformEventsFinalize --
          295  + *
          296  + *	This function closes the pipe and the kqueue file descriptors
          297  + *	and frees the kevent structs owned by the thread of the caller.
          298  + *	The above operations are protected by tsdPtr->notifierMutex, which
          299  + *	is destroyed thereafter.
          300  + *
          301  + * Results:
          302  + *	None.
          303  + *
          304  + * Side effects:
          305  + * 	While tsdPtr->notifierMutex is held:
          306  + *	The per-thread pipe(2) fds are closed, if non-zero, and set to -1.
          307  + *	The per-thread kqueue(2) fd is closed, if non-zero, and set to 0.
          308  + *	The per-thread kevent structs are freed, if any, and set to 0.
          309  + *
          310  + *	tsdPtr->notifierMutex is destroyed.
          311  + *
          312  + *----------------------------------------------------------------------
          313  + */
          314  +
          315  +void
          316  +PlatformEventsFinalize(
          317  +	void)
          318  +{
          319  +    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
          320  +
          321  +    pthread_mutex_lock(&tsdPtr->notifierMutex);
          322  +    if (tsdPtr->triggerPipe[0]) {
          323  +	close(tsdPtr->triggerPipe[0]);
          324  +	tsdPtr->triggerPipe[0] = -1;
          325  +    }
          326  +    if (tsdPtr->triggerPipe[1]) {
          327  +	close(tsdPtr->triggerPipe[1]);
          328  +	tsdPtr->triggerPipe[1] = -1;
          329  +    }
          330  +    if (tsdPtr->eventsFd > 0) {
          331  +	close(tsdPtr->eventsFd);
          332  +	tsdPtr->eventsFd = 0;
          333  +    }
          334  +    if (tsdPtr->readyEvents) {
          335  +	ckfree(tsdPtr->readyEvents);
          336  +	tsdPtr->maxReadyEvents = 0;
          337  +    }
          338  +    pthread_mutex_unlock(&tsdPtr->notifierMutex);
          339  +    if ((errno = pthread_mutex_destroy(&tsdPtr->notifierMutex))) {
          340  +	Tcl_Panic("pthread_mutex_destroy: %s", strerror(errno));
          341  +    }
          342  +}
          343  +
          344  +/*
          345  + *----------------------------------------------------------------------
          346  + *
          347  + * PlatformEventsInit --
          348  + *
          349  + *	This function abstracts creating a kqueue fd via the kqueue
          350  + *	system call and allocating memory for the kevents structs in
          351  + *	tsdPtr for the thread of the caller.
          352  + *
          353  + * Results:
          354  + *	None.
          355  + *
          356  + * Side effects:
          357  + *	The following per-thread entities are initialised:
          358  + *	notifierMutex is initialised.
          359  + *	The pipe(2) is created; fcntl(2) is called on both fds to set
          360  + *	FD_CLOEXEC and O_NONBLOCK.
          361  + *	The kqueue(2) fd is created; fcntl(2) is called on it to set
          362  + *	FD_CLOEXEC.
          363  + *	A FileHandler struct is allocated and initialised for the event-
          364  + *	fd(2), registering interest for TCL_READABLE on it via Platform-
          365  + *	EventsControl().
          366  + *	readyEvents and maxReadyEvents are initialised with 512 kevents.
          367  +
          368  + *----------------------------------------------------------------------
          369  + */
          370  +
          371  +void
          372  +PlatformEventsInit(
          373  +	void)
          374  +{
          375  +    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
          376  +    int i, fdFl;
          377  +    FileHandler *filePtr;
          378  +
          379  +    errno = pthread_mutex_init(&tsdPtr->notifierMutex, NULL);
          380  +    if (errno) {
          381  +	Tcl_Panic("Tcl_InitNotifier: %s", "could not create mutex");
          382  +    }
          383  +    if (pipe(tsdPtr->triggerPipe) != 0) {
          384  +	Tcl_Panic("Tcl_InitNotifier: %s", "could not create trigger pipe");
          385  +    } else for (i = 0; i < 2; i++) {
          386  +	if (fcntl(tsdPtr->triggerPipe[i], F_SETFD, FD_CLOEXEC) == -1) {
          387  +	    Tcl_Panic("fcntl: %s", strerror(errno));
          388  +	} else {
          389  +	    fdFl = fcntl(tsdPtr->triggerPipe[i], F_GETFL);
          390  +	    fdFl |= O_NONBLOCK;
          391  +	}
          392  +	if (fcntl(tsdPtr->triggerPipe[i], F_SETFL, fdFl) == -1) {
          393  +	    Tcl_Panic("fcntl: %s", strerror(errno));
          394  +	}
          395  +    }
          396  +    if ((tsdPtr->eventsFd = kqueue()) == -1) {
          397  +	Tcl_Panic("kqueue: %s", strerror(errno));
          398  +    } else if (fcntl(tsdPtr->eventsFd, F_SETFD, FD_CLOEXEC) == -1) {
          399  +	Tcl_Panic("fcntl: %s", strerror(errno));
          400  +    }
          401  +    filePtr = ckalloc(sizeof(*filePtr));
          402  +    filePtr->fd = tsdPtr->triggerPipe[0];
          403  +    filePtr->mask = TCL_READABLE;
          404  +    PlatformEventsControl(filePtr, tsdPtr, EV_ADD, 1);
          405  +    if (!tsdPtr->readyEvents) {
          406  +        tsdPtr->maxReadyEvents = 512;
          407  +	tsdPtr->readyEvents = ckalloc(tsdPtr->maxReadyEvents
          408  +	    * sizeof(tsdPtr->readyEvents[0]));
          409  +    }
          410  +    LIST_INIT(&tsdPtr->firstReadyFileHandlerPtr);
          411  +}
          412  +
          413  +/*
          414  + *----------------------------------------------------------------------
          415  + *
          416  + * PlatformEventsTranslate --
          417  + *
          418  + *	This function translates the platform-specific mask of returned
          419  + *	events in eventPtr to a mask of TCL_* bits.
          420  + *
          421  + * Results:
          422  + *	Returns the translated mask.
          423  + *
          424  + * Side effects:
          425  + *	None.
          426  + *
          427  + *----------------------------------------------------------------------
          428  + */
          429  +
          430  +int
          431  +PlatformEventsTranslate(
          432  +	struct kevent *eventPtr)
          433  +{
          434  +    int mask;
          435  +
          436  +    mask = 0;
          437  +    if (eventPtr->filter == EVFILT_READ) {
          438  +	mask |= TCL_READABLE;
          439  +	if (eventPtr->flags & EV_ERROR) {
          440  +	    mask |= TCL_EXCEPTION;
          441  +	}
          442  +    }
          443  +    if (eventPtr->filter == EVFILT_WRITE) {
          444  +	mask |= TCL_WRITABLE;
          445  +	if (eventPtr->flags & EV_ERROR) {
          446  +	    mask |= TCL_EXCEPTION;
          447  +	}
          448  +    }
          449  +    return mask;
          450  +}
          451  +
          452  +/*
          453  + *----------------------------------------------------------------------
          454  + *
          455  + * PlatformEventsWait --
          456  + *
          457  + *	This function abstracts waiting for I/O events via the kevent
          458  + *	system call.
          459  + *
          460  + * Results:
          461  + *	Returns -1 if kevent failed. Returns 0 if polling and if no events
          462  + *	became available whilst polling. Returns a pointer to and the count
          463  + *	of all returned events in all other cases.
          464  + *
          465  + * Side effects:
          466  + *	gettimeofday(2), kevent(2), and gettimeofday(2) are called,
          467  + *	in the specified order.
          468  + *	If timePtr specifies a positive value, it is updated to reflect
          469  + *	the amount of time that has passed; if its value would {under,
          470  + *	over}flow, it is set to zero.
          471  + *
          472  + *----------------------------------------------------------------------
          473  + */
          474  +
          475  +int
          476  +PlatformEventsWait(
          477  +    struct kevent *events,
          478  +    size_t numEvents,
          479  +    struct timeval *timePtr)
          480  +{
          481  +    int numFound;
          482  +    struct timeval tv0, tv1, tv_delta;
          483  +    struct timespec timeout, *timeoutPtr;
          484  +
          485  +    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
          486  +
          487  +    /*
          488  +     * If timePtr is NULL, kevent(2) will wait indefinitely. If it speci-
          489  +     * fies a timeout of {0,0}, kevent(2) will poll. Otherwise, the time-
          490  +     * out will simply be converted to a timespec.
          491  +     */
          492  +
          493  +    if (!timePtr) {
          494  +	timeoutPtr = NULL;
          495  +    } else if (!timePtr->tv_sec && !timePtr->tv_usec) {
          496  +	timeout.tv_sec = 0;
          497  +	timeout.tv_nsec = 0;
          498  +	timeoutPtr = &timeout;
          499  +    } else {
          500  +	timeout.tv_sec = timePtr->tv_sec;
          501  +	timeout.tv_nsec = timePtr->tv_usec * 1000;
          502  +	timeoutPtr = &timeout;
          503  +    }
          504  +
          505  +    /*
          506  +     * Call (and possibly block on) kevent(2) and substract the delta of
          507  +     * gettimeofday(2) before and after the call from timePtr if the latter
          508  +     * is not NULL. Return the number of events returned by kevent(2).
          509  +     */
          510  +
          511  +    gettimeofday(&tv0, NULL);
          512  +    numFound = kevent(tsdPtr->eventsFd, NULL, 0, events, (int)numEvents, timeoutPtr);
          513  +    gettimeofday(&tv1, NULL);
          514  +    if (timePtr && (timePtr->tv_sec && timePtr->tv_usec)) {
          515  +	timersub(&tv1, &tv0, &tv_delta);
          516  +	if (!timercmp(&tv_delta, timePtr, >)) {
          517  +		timersub(timePtr, &tv_delta, timePtr);
          518  +	} else {
          519  +		timePtr->tv_sec = 0;
          520  +		timePtr->tv_usec = 0;
          521  +	}
          522  +    }
          523  +    return numFound;
          524  +}
          525  +
          526  +/*
          527  + *----------------------------------------------------------------------
          528  + *
          529  + * Tcl_CreateFileHandler --
          530  + *
          531  + *	This function registers a file handler with the kqueue notifier
          532  + *	of the thread of the caller.
          533  + *
          534  + * Results:
          535  + *	None.
          536  + *
          537  + * Side effects:
          538  + *	Creates a new file handler structure.
          539  + *	PlatformEventsControl() is called for the new file handler structure.
          540  + *
          541  + *----------------------------------------------------------------------
          542  + */
          543  +
          544  +void
          545  +Tcl_CreateFileHandler(
          546  +    int fd,			/* Handle of stream to watch. */
          547  +    int mask,			/* OR'ed combination of TCL_READABLE,
          548  +				 * TCL_WRITABLE, and TCL_EXCEPTION: indicates
          549  +				 * conditions under which proc should be
          550  +				 * called. */
          551  +    Tcl_FileProc *proc,		/* Function to call for each selected
          552  +				 * event. */
          553  +    ClientData clientData)	/* Arbitrary data to pass to proc. */
          554  +{
          555  +    int isNew;
          556  +
          557  +    if (tclNotifierHooks.createFileHandlerProc) {
          558  +	tclNotifierHooks.createFileHandlerProc(fd, mask, proc, clientData);
          559  +	return;
          560  +    } else {
          561  +	ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
          562  +	FileHandler *filePtr;
          563  +
          564  +	for (filePtr = tsdPtr->firstFileHandlerPtr; filePtr != NULL;
          565  +		filePtr = filePtr->nextPtr) {
          566  +	    if (filePtr->fd == fd) {
          567  +		break;
          568  +	    }
          569  +	}
          570  +	if (filePtr == NULL) {
          571  +	    filePtr = ckalloc(sizeof(FileHandler));
          572  +	    filePtr->fd = fd;
          573  +	    filePtr->readyMask = 0;
          574  +	    filePtr->nextPtr = tsdPtr->firstFileHandlerPtr;
          575  +	    tsdPtr->firstFileHandlerPtr = filePtr;
          576  +	    isNew = 1;
          577  +	} else {
          578  +	    isNew = 0;
          579  +	}
          580  +	filePtr->proc = proc;
          581  +	filePtr->clientData = clientData;
          582  +	filePtr->mask = mask;
          583  +
          584  +	PlatformEventsControl(filePtr, tsdPtr, EV_ADD, isNew);
          585  +    }
          586  +}
          587  +
          588  +/*
          589  + *----------------------------------------------------------------------
          590  + *
          591  + * Tcl_DeleteFileHandler --
          592  + *
          593  + *	Cancel a previously-arranged callback arrangement for a file on
          594  + *	the kqueue of the thread of the caller.
          595  + *
          596  + * Results:
          597  + *	None.
          598  + *
          599  + * Side effects:
          600  + *	If a callback was previously registered on file, remove it.
          601  + *	PlatformEventsControl() is called for the file handler structure.
          602  + *	The PlatformEventData struct associated with the new file handler
          603  + *	structure is freed.
          604  + *
          605  + *----------------------------------------------------------------------
          606  + */
          607  +
          608  +void
          609  +Tcl_DeleteFileHandler(
          610  +    int fd)			/* Stream id for which to remove callback
          611  +				 * function. */
          612  +{
          613  +    if (tclNotifierHooks.deleteFileHandlerProc) {
          614  +	tclNotifierHooks.deleteFileHandlerProc(fd);
          615  +	return;
          616  +    } else {
          617  +	FileHandler *filePtr, *prevPtr;
          618  +	ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
          619  +
          620  +	/*
          621  +	 * Find the entry for the given file (and return if there isn't one).
          622  +	 */
          623  +
          624  +	for (prevPtr = NULL, filePtr = tsdPtr->firstFileHandlerPtr; ;
          625  +		prevPtr = filePtr, filePtr = filePtr->nextPtr) {
          626  +	    if (filePtr == NULL) {
          627  +		return;
          628  +	    }
          629  +	    if (filePtr->fd == fd) {
          630  +		break;
          631  +	    }
          632  +	}
          633  +
          634  +	/*
          635  +	 * Update the check masks for this file.
          636  +	 */
          637  +
          638  +	PlatformEventsControl(filePtr, tsdPtr, EV_DELETE, 0);
          639  +	if (filePtr->pedPtr) {
          640  +	    ckfree(filePtr->pedPtr);
          641  +	}
          642  +
          643  +	/*
          644  +	 * Clean up information in the callback record.
          645  +	 */
          646  +
          647  +	if (prevPtr == NULL) {
          648  +	    tsdPtr->firstFileHandlerPtr = filePtr->nextPtr;
          649  +	} else {
          650  +	    prevPtr->nextPtr = filePtr->nextPtr;
          651  +	}
          652  +	ckfree(filePtr);
          653  +    }
          654  +}
          655  +
          656  +/*
          657  + *----------------------------------------------------------------------
          658  + *
          659  + * Tcl_WaitForEvent --
          660  + *
          661  + *	This function is called by Tcl_DoOneEvent to wait for new events on
          662  + *	the message queue. If the block time is 0, then Tcl_WaitForEvent just
          663  + *	polls without blocking.
          664  + *	The waiting logic is implemented in PlatformEventsWait.
          665  + *
          666  + * Results:
          667  + *	Returns -1 if PlatformEventsWait() would block forever, otherwise
          668  + *	returns 0.
          669  + *
          670  + * Side effects:
          671  + *	Queues file events that are detected by PlatformEventsWait().
          672  + *
          673  + *----------------------------------------------------------------------
          674  + */
          675  +
          676  +int
          677  +Tcl_WaitForEvent(
          678  +    const Tcl_Time *timePtr)		/* Maximum block time, or NULL. */
          679  +{
          680  +    if (tclNotifierHooks.waitForEventProc) {
          681  +	return tclNotifierHooks.waitForEventProc(timePtr);
          682  +    } else {
          683  +	FileHandler *filePtr;
          684  +	int mask;
          685  +	Tcl_Time vTime;
          686  +	/*
          687  +	 * Impl. notes: timeout & timeoutPtr are used if, and only if threads
          688  +	 * are not enabled. They are the arguments for the regular epoll_wait()
          689  +	 * used when the core is not thread-enabled.
          690  +	 */
          691  +
          692  +	struct timeval timeout, *timeoutPtr;
          693  +	int numFound, numEvent;
          694  +	struct PlatformEventData *pedPtr;
          695  +	ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
          696  +	int numQueued;
          697  +	ssize_t i;
          698  +	char buf[1];
          699  +
          700  +	/*
          701  +	 * Set up the timeout structure. Note that if there are no events to
          702  +	 * check for, we return with a negative result rather than blocking
          703  +	 * forever.
          704  +	 */
          705  +
          706  +	if (timePtr != NULL) {
          707  +	    /*
          708  +	     * TIP #233 (Virtualized Time). Is virtual time in effect? And do
          709  +	     * we actually have something to scale? If yes to both then we
          710  +	     * call the handler to do this scaling.
          711  +	     */
          712  +
          713  +	    if (timePtr->sec != 0 || timePtr->usec != 0) {
          714  +		vTime = *timePtr;
          715  +		tclScaleTimeProcPtr(&vTime, tclTimeClientData);
          716  +		timePtr = &vTime;
          717  +	    }
          718  +	    timeout.tv_sec = timePtr->sec;
          719  +	    timeout.tv_usec = timePtr->usec;
          720  +	    timeoutPtr = &timeout;
          721  +	} else {
          722  +	    timeoutPtr = NULL;
          723  +	}
          724  +
          725  +	/*
          726  +	 * Walk the list of FileHandlers associated with regular files
          727  +	 * (S_IFREG) belonging to tsdPtr, queue Tcl events for them, and
          728  +	 * update their mask of events of interest.
          729  +	 * kqueue(2), unlike epoll(7), does support regular files, but
          730  +	 * EVFILT_READ only `[r]eturns when the file pointer is not at
          731  +	 * the end of file' as opposed to unconditionally. While FreeBSD
          732  +	 * 11.0-RELEASE adds support for this mode (NOTE_FILE_POLL,) this
          733  +	 * is not used for reasons of compatibility.
          734  +	 * Therefore, the behaviour of {select,poll}(2) is simply simulated
          735  +	 * here: fds associated with regular files are added to this list by
          736  +	 * PlatformEventsControl() and processed here before calling (and
          737  +	 * possibly blocking) on PlatformEventsWait().
          738  +	 */
          739  +
          740  +	numQueued = 0;
          741  +	LIST_FOREACH(filePtr, &tsdPtr->firstReadyFileHandlerPtr, readyNode) {
          742  +	    mask = 0;
          743  +	    if (filePtr->mask & TCL_READABLE) {
          744  +		mask |= TCL_READABLE;
          745  +	    }
          746  +	    if (filePtr->mask & TCL_WRITABLE) {
          747  +		mask |= TCL_WRITABLE;
          748  +	    }
          749  +
          750  +	    /*
          751  +	     * Don't bother to queue an event if the mask was previously
          752  +	     * non-zero since an event must still be on the queue.
          753  +	     */
          754  +
          755  +	    if (filePtr->readyMask == 0) {
          756  +		FileHandlerEvent *fileEvPtr =
          757  +		    ckalloc(sizeof(FileHandlerEvent));
          758  +
          759  +		fileEvPtr->header.proc = FileHandlerEventProc;
          760  +		fileEvPtr->fd = filePtr->fd;
          761  +		Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL);
          762  +		numQueued++;
          763  +	    }
          764  +	    filePtr->readyMask = mask;
          765  +	}
          766  +
          767  +	/*
          768  +	 * If any events were queued in the above loop, force PlatformEvents-
          769  +	 * Wait() to poll as there already are events that need to be processed
          770  +	 * at this point.
          771  +	 */
          772  +
          773  +	if (numQueued) {
          774  +	    timeout.tv_sec = 0;
          775  +	    timeout.tv_usec = 0;
          776  +	    timeoutPtr = &timeout;
          777  +	}
          778  +
          779  +	/*
          780  +	 * Wait or poll for new events, queue Tcl events for the FileHandlers
          781  +	 * corresponding to them, and update the FileHandlers' mask of events
          782  +	 * of interest registered by the last call to Tcl_CreateFileHandler().
          783  +	 *
          784  +	 * Events for the trigger pipe are processed here in order to facilitate
          785  +	 * inter-thread IPC. If another thread intends to wake up this thread
          786  +	 * whilst it's blocking on PlatformEventsWait(), it write(2)s to the
          787  +	 * other end of the pipe (see Tcl_AlertNotifier(),) which in turn will
          788  +	 * cause PlatformEventsWait() to return immediately.
          789  +	 */
          790  +
          791  +	numFound = PlatformEventsWait(tsdPtr->readyEvents, tsdPtr->maxReadyEvents, timeoutPtr);
          792  +	for (numEvent = 0; numEvent < numFound; numEvent++) {
          793  +	    pedPtr = (struct PlatformEventData *)tsdPtr->readyEvents[numEvent].udata;
          794  +	    filePtr = pedPtr->filePtr;
          795  +	    mask = PlatformEventsTranslate(&tsdPtr->readyEvents[numEvent]);
          796  +	    if (filePtr->fd == tsdPtr->triggerPipe[0]) {
          797  +		do {
          798  +		    i = read(tsdPtr->triggerPipe[0], buf, 1);
          799  +		    if ((i == -1) && (errno != EAGAIN)) {
          800  +			Tcl_Panic("Tcl_WaitForEvent: "
          801  +				"read from %p->triggerPipe: %s",
          802  +				(void *)tsdPtr, strerror(errno));
          803  +		    } else {
          804  +			break;
          805  +		    }
          806  +		} while (1);
          807  +		continue;
          808  +	    }
          809  +	    if (!mask) {
          810  +		continue;
          811  +	    }
          812  +
          813  +	    /*
          814  +	     * Don't bother to queue an event if the mask was previously
          815  +	     * non-zero since an event must still be on the queue.
          816  +	     */
          817  +
          818  +	    if (filePtr->readyMask == 0) {
          819  +		FileHandlerEvent *fileEvPtr =
          820  +			ckalloc(sizeof(FileHandlerEvent));
          821  +
          822  +		fileEvPtr->header.proc = FileHandlerEventProc;
          823  +		fileEvPtr->fd = filePtr->fd;
          824  +		Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL);
          825  +	    }
          826  +	    filePtr->readyMask |= mask;
          827  +	}
          828  +	return 0;
          829  +    }
          830  +}
          831  +
          832  +#endif /* !HAVE_COREFOUNDATION */
          833  +
          834  +#endif /* NOTIFIER_KQUEUE */
          835  +
          836  +/*
          837  + * Local Variables:
          838  + * mode: c
          839  + * c-basic-offset: 4
          840  + * fill-column: 78
          841  + * End:
          842  + */

Added unix/tclSelectNotfy.c.

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

Changes to unix/tclUnixChan.c.

  1721   1721       Tcl_SetObjResult(interp, Tcl_ObjPrintf(
  1722   1722   	    "\"%s\" cannot be used to get a FILE *", chanID));
  1723   1723       Tcl_SetErrorCode(interp, "TCL", "VALUE", "CHANNEL", "NO_DESCRIPTOR",
  1724   1724   	    NULL);
  1725   1725       return TCL_ERROR;
  1726   1726   }
  1727   1727   
  1728         -#ifndef HAVE_COREFOUNDATION	/* Darwin/Mac OS X CoreFoundation notifier is
  1729         -				 * in tclMacOSXNotify.c */
  1730         -/*
  1731         - *----------------------------------------------------------------------
  1732         - *
  1733         - * TclUnixWaitForFile --
  1734         - *
  1735         - *	This function waits synchronously for a file to become readable or
  1736         - *	writable, with an optional timeout.
  1737         - *
  1738         - * Results:
  1739         - *	The return value is an OR'ed combination of TCL_READABLE,
  1740         - *	TCL_WRITABLE, and TCL_EXCEPTION, indicating the conditions that are
  1741         - *	present on file at the time of the return. This function will not
  1742         - *	return until either "timeout" milliseconds have elapsed or at least
  1743         - *	one of the conditions given by mask has occurred for file (a return
  1744         - *	value of 0 means that a timeout occurred). No normal events will be
  1745         - *	serviced during the execution of this function.
  1746         - *
  1747         - * Side effects:
  1748         - *	Time passes.
  1749         - *
  1750         - *----------------------------------------------------------------------
  1751         - */
  1752         -
  1753         -int
  1754         -TclUnixWaitForFile(
  1755         -    int fd,			/* Handle for file on which to wait. */
  1756         -    int mask,			/* What to wait for: OR'ed combination of
  1757         -				 * TCL_READABLE, TCL_WRITABLE, and
  1758         -				 * TCL_EXCEPTION. */
  1759         -    int timeout)		/* Maximum amount of time to wait for one of
  1760         -				 * the conditions in mask to occur, in
  1761         -				 * milliseconds. A value of 0 means don't wait
  1762         -				 * at all, and a value of -1 means wait
  1763         -				 * forever. */
  1764         -{
  1765         -    Tcl_Time abortTime = {0, 0}, now; /* silence gcc 4 warning */
  1766         -    struct timeval blockTime, *timeoutPtr;
  1767         -    int numFound, result = 0;
  1768         -    fd_set readableMask;
  1769         -    fd_set writableMask;
  1770         -    fd_set exceptionMask;
  1771         -
  1772         -#ifndef _DARWIN_C_SOURCE
  1773         -    /*
  1774         -     * Sanity check fd.
  1775         -     */
  1776         -
  1777         -    if (fd >= FD_SETSIZE) {
  1778         -	Tcl_Panic("TclUnixWaitForFile can't handle file id %d", fd);
  1779         -	/* must never get here, or select masks overrun will occur below */
  1780         -    }
  1781         -#endif
  1782         -
  1783         -    /*
  1784         -     * If there is a non-zero finite timeout, compute the time when we give
  1785         -     * up.
  1786         -     */
  1787         -
  1788         -    if (timeout > 0) {
  1789         -	Tcl_GetTime(&now);
  1790         -	abortTime.sec = now.sec + timeout/1000;
  1791         -	abortTime.usec = now.usec + (timeout%1000)*1000;
  1792         -	if (abortTime.usec >= 1000000) {
  1793         -	    abortTime.usec -= 1000000;
  1794         -	    abortTime.sec += 1;
  1795         -	}
  1796         -	timeoutPtr = &blockTime;
  1797         -    } else if (timeout == 0) {
  1798         -	timeoutPtr = &blockTime;
  1799         -	blockTime.tv_sec = 0;
  1800         -	blockTime.tv_usec = 0;
  1801         -    } else {
  1802         -	timeoutPtr = NULL;
  1803         -    }
  1804         -
  1805         -    /*
  1806         -     * Initialize the select masks.
  1807         -     */
  1808         -
  1809         -    FD_ZERO(&readableMask);
  1810         -    FD_ZERO(&writableMask);
  1811         -    FD_ZERO(&exceptionMask);
  1812         -
  1813         -    /*
  1814         -     * Loop in a mini-event loop of our own, waiting for either the file to
  1815         -     * become ready or a timeout to occur.
  1816         -     */
  1817         -
  1818         -    while (1) {
  1819         -	if (timeout > 0) {
  1820         -	    blockTime.tv_sec = abortTime.sec - now.sec;
  1821         -	    blockTime.tv_usec = abortTime.usec - now.usec;
  1822         -	    if (blockTime.tv_usec < 0) {
  1823         -		blockTime.tv_sec -= 1;
  1824         -		blockTime.tv_usec += 1000000;
  1825         -	    }
  1826         -	    if (blockTime.tv_sec < 0) {
  1827         -		blockTime.tv_sec = 0;
  1828         -		blockTime.tv_usec = 0;
  1829         -	    }
  1830         -	}
  1831         -
  1832         -	/*
  1833         -	 * Setup the select masks for the fd.
  1834         -	 */
  1835         -
  1836         -	if (mask & TCL_READABLE) {
  1837         -	    FD_SET(fd, &readableMask);
  1838         -	}
  1839         -	if (mask & TCL_WRITABLE) {
  1840         -	    FD_SET(fd, &writableMask);
  1841         -	}
  1842         -	if (mask & TCL_EXCEPTION) {
  1843         -	    FD_SET(fd, &exceptionMask);
  1844         -	}
  1845         -
  1846         -	/*
  1847         -	 * Wait for the event or a timeout.
  1848         -	 */
  1849         -
  1850         -	numFound = select(fd + 1, &readableMask, &writableMask,
  1851         -		&exceptionMask, timeoutPtr);
  1852         -	if (numFound == 1) {
  1853         -	    if (FD_ISSET(fd, &readableMask)) {
  1854         -		SET_BITS(result, TCL_READABLE);
  1855         -	    }
  1856         -	    if (FD_ISSET(fd, &writableMask)) {
  1857         -		SET_BITS(result, TCL_WRITABLE);
  1858         -	    }
  1859         -	    if (FD_ISSET(fd, &exceptionMask)) {
  1860         -		SET_BITS(result, TCL_EXCEPTION);
  1861         -	    }
  1862         -	    result &= mask;
  1863         -	    if (result) {
  1864         -		break;
  1865         -	    }
  1866         -	}
  1867         -	if (timeout == 0) {
  1868         -	    break;
  1869         -	}
  1870         -	if (timeout < 0) {
  1871         -	    continue;
  1872         -	}
  1873         -
  1874         -	/*
  1875         -	 * The select returned early, so we need to recompute the timeout.
  1876         -	 */
  1877         -
  1878         -	Tcl_GetTime(&now);
  1879         -	if ((abortTime.sec < now.sec)
  1880         -		|| (abortTime.sec==now.sec && abortTime.usec<=now.usec)) {
  1881         -	    break;
  1882         -	}
  1883         -    }
  1884         -    return result;
  1885         -}
  1886         -#endif /* HAVE_COREFOUNDATION */
  1887         -
  1888   1728   /*
  1889   1729    *----------------------------------------------------------------------
  1890   1730    *
  1891   1731    * FileTruncateProc --
  1892   1732    *
  1893   1733    *	Truncates a file to a given length.
  1894   1734    *

Changes to unix/tclUnixNotfy.c.

     1      1   /*
     2      2    * tclUnixNotfy.c --
     3      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.
            4  + *	This file contains subroutines shared by all notifier backend
            5  + *	implementations on *nix platforms.
     7      6    *
     8      7    * Copyright (c) 1995-1997 Sun Microsystems, Inc.
            8  + * Copyright (c) 2016 Lucio Andrés Illanes Albornoz <[email protected]>
     9      9    *
    10     10    * See the file "license.terms" for information on usage and redistribution
    11     11    * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
    12     12    */
    13     13   
    14         -#include "tclInt.h"
    15         -#ifndef HAVE_COREFOUNDATION	/* Darwin/Mac OS X CoreFoundation notifier is
    16         -				 * in tclMacOSXNotify.c */
    17         -#include <signal.h>
    18         -
    19         -/*
    20         - * This structure is used to keep track of the notifier info for a registered
    21         - * file.
    22         - */
    23         -
    24         -typedef struct FileHandler {
    25         -    int fd;
    26         -    int mask;			/* Mask of desired events: TCL_READABLE,
    27         -				 * etc. */
    28         -    int readyMask;		/* Mask of events that have been seen since
    29         -				 * the last time file handlers were invoked
    30         -				 * for this file. */
    31         -    Tcl_FileProc *proc;		/* Function to call, in the style of
    32         -				 * Tcl_CreateFileHandler. */
    33         -    ClientData clientData;	/* Argument to pass to proc. */
    34         -    struct FileHandler *nextPtr;/* Next in list of all files we care about. */
    35         -} FileHandler;
    36         -
    37         -/*
    38         - * The following structure is what is added to the Tcl event queue when file
    39         - * handlers are ready to fire.
    40         - */
    41         -
    42         -typedef struct {
    43         -    Tcl_Event header;		/* Information that is standard for all
    44         -				 * events. */
    45         -    int fd;			/* File descriptor that is ready. Used to find
    46         -				 * the FileHandler structure for the file
    47         -				 * (can't point directly to the FileHandler
    48         -				 * structure because it could go away while
    49         -				 * the event is queued). */
    50         -} FileHandlerEvent;
    51         -
    52         -/*
    53         - * The following structure contains a set of select() masks to track readable,
    54         - * writable, and exception conditions.
    55         - */
    56         -
    57         -typedef struct {
    58         -    fd_set readable;
    59         -    fd_set writable;
    60         -    fd_set exception;
    61         -} SelectMasks;
    62         -
    63         -/*
    64         - * The following static structure contains the state information for the
    65         - * select based implementation of the Tcl notifier. One of these structures is
    66         - * created for each thread that is using the notifier.
    67         - */
    68         -
    69         -typedef struct ThreadSpecificData {
    70         -    FileHandler *firstFileHandlerPtr;
    71         -				/* Pointer to head of file handler list. */
    72         -    SelectMasks checkMasks;	/* This structure is used to build up the
    73         -				 * masks to be used in the next call to
    74         -				 * select. Bits are set in response to calls
    75         -				 * to Tcl_CreateFileHandler. */
    76         -    SelectMasks readyMasks;	/* This array reflects the readable/writable
    77         -				 * conditions that were found to exist by the
    78         -				 * last call to select. */
    79         -    int numFdBits;		/* Number of valid bits in checkMasks (one
    80         -				 * more than highest fd for which
    81         -				 * Tcl_WatchFile has been called). */
    82         -#ifdef TCL_THREADS
    83         -    int onList;			/* True if it is in this list */
    84         -    unsigned int pollState;	/* pollState is used to implement a polling
    85         -				 * handshake between each thread and the
    86         -				 * notifier thread. Bits defined below. */
    87         -    struct ThreadSpecificData *nextPtr, *prevPtr;
    88         -				/* All threads that are currently waiting on
    89         -				 * an event have their ThreadSpecificData
    90         -				 * structure on a doubly-linked listed formed
    91         -				 * from these pointers. You must hold the
    92         -				 * notifierMutex lock before accessing these
    93         -				 * fields. */
    94         -#ifdef __CYGWIN__
    95         -    void *event;     /* Any other thread alerts a notifier
    96         -	 * that an event is ready to be processed
    97         -	 * by sending this event. */
    98         -    void *hwnd;			/* Messaging window. */
    99         -#else /* !__CYGWIN__ */
   100         -    pthread_cond_t waitCV;	/* Any other thread alerts a notifier that an
   101         -				 * event is ready to be processed by signaling
   102         -				 * this condition variable. */
   103         -#endif /* __CYGWIN__ */
   104         -    int waitCVinitialized;	/* Variable to flag initialization of the structure */
   105         -    int eventReady;		/* True if an event is ready to be processed.
   106         -				 * Used as condition flag together with waitCV
   107         -				 * above. */
   108         -#endif /* TCL_THREADS */
   109         -} ThreadSpecificData;
   110         -
   111         -static Tcl_ThreadDataKey dataKey;
   112         -
   113         -#ifdef TCL_THREADS
   114         -/*
   115         - * The following static indicates the number of threads that have initialized
   116         - * notifiers.
   117         - *
   118         - * You must hold the notifierMutex lock before accessing this variable.
   119         - */
   120         -
   121         -static int notifierCount = 0;
   122         -
   123         -/*
   124         - * The following variable points to the head of a doubly-linked list of
   125         - * ThreadSpecificData structures for all threads that are currently waiting on
   126         - * an event.
   127         - *
   128         - * You must hold the notifierMutex lock before accessing this list.
   129         - */
   130         -
   131         -static ThreadSpecificData *waitingListPtr = NULL;
   132         -
   133         -/*
   134         - * The notifier thread spends all its time in select() waiting for a file
   135         - * descriptor associated with one of the threads on the waitingListPtr list to
   136         - * do something interesting. But if the contents of the waitingListPtr list
   137         - * ever changes, we need to wake up and restart the select() system call. You
   138         - * can wake up the notifier thread by writing a single byte to the file
   139         - * descriptor defined below. This file descriptor is the input-end of a pipe
   140         - * and the notifier thread is listening for data on the output-end of the same
   141         - * pipe. Hence writing to this file descriptor will cause the select() system
   142         - * call to return and wake up the notifier thread.
   143         - *
   144         - * You must hold the notifierMutex lock before writing to the pipe.
   145         - */
   146         -
   147         -static int triggerPipe = -1;
   148         -
   149         -/*
   150         - * The notifierMutex locks access to all of the global notifier state.
   151         - */
   152         -
   153         -static pthread_mutex_t notifierInitMutex = PTHREAD_MUTEX_INITIALIZER;
   154         -static pthread_mutex_t notifierMutex     = PTHREAD_MUTEX_INITIALIZER;
   155         -/*
   156         - * The following static indicates if the notifier thread is running.
   157         - *
   158         - * You must hold the notifierInitMutex before accessing this variable.
   159         - */
   160         -
   161         -static int notifierThreadRunning = 0;
   162         -
   163         -/*
   164         - * The notifier thread signals the notifierCV when it has finished
   165         - * initializing the triggerPipe and right before the notifier thread
   166         - * terminates.
   167         - */
   168         -
   169         -static pthread_cond_t notifierCV = PTHREAD_COND_INITIALIZER;
   170         -
   171         -/*
   172         - * The pollState bits
   173         - *	POLL_WANT is set by each thread before it waits on its condition
   174         - *		variable. It is checked by the notifier before it does select.
   175         - *	POLL_DONE is set by the notifier if it goes into select after seeing
   176         - *		POLL_WANT. The idea is to ensure it tries a select with the
   177         - *		same bits the initial thread had set.
   178         - */
   179         -
   180         -#define POLL_WANT	0x1
   181         -#define POLL_DONE	0x2
   182         -
   183         -/*
   184         - * This is the thread ID of the notifier thread that does select.
   185         - */
   186         -
   187         -static Tcl_ThreadId notifierThread;
   188         -
   189         -#endif /* TCL_THREADS */
           14  +#include <poll.h>
   190     15   
   191     16   /*
   192     17    * Static routines defined in this file.
   193     18    */
   194     19   
           20  +#ifdef NOTIFIER_SELECT
   195     21   #ifdef TCL_THREADS
   196     22   static TCL_NORETURN void NotifierThreadProc(ClientData clientData);
   197     23   #if defined(HAVE_PTHREAD_ATFORK)
   198         -static int	atForkInit = 0;
   199     24   static void	AtForkChild(void);
   200     25   #endif /* HAVE_PTHREAD_ATFORK */
   201     26   #endif /* TCL_THREADS */
           27  +#endif /* NOTIFIER_SELECT */
   202     28   static int	FileHandlerEventProc(Tcl_Event *evPtr, int flags);
   203     29   
   204         -/*
   205         - * Import of Windows API when building threaded with Cygwin.
   206         - */
   207         -
   208         -#if defined(TCL_THREADS) && defined(__CYGWIN__)
   209         -typedef struct {
   210         -    void *hwnd;
   211         -    unsigned int *message;
   212         -    int wParam;
   213         -    int lParam;
   214         -    int time;
   215         -    int x;
   216         -    int y;
   217         -} MSG;
   218         -
   219         -typedef struct {
   220         -    unsigned int style;
   221         -    void *lpfnWndProc;
   222         -    int cbClsExtra;
   223         -    int cbWndExtra;
   224         -    void *hInstance;
   225         -    void *hIcon;
   226         -    void *hCursor;
   227         -    void *hbrBackground;
   228         -    void *lpszMenuName;
   229         -    const void *lpszClassName;
   230         -} WNDCLASS;
   231         -
   232         -extern void __stdcall	CloseHandle(void *);
   233         -extern void *__stdcall	CreateEventW(void *, unsigned char, unsigned char,
   234         -			    void *);
   235         -extern void * __stdcall	CreateWindowExW(void *, const void *, const void *,
   236         -			    DWORD, int, int, int, int, void *, void *, void *, void *);
   237         -extern DWORD __stdcall	DefWindowProcW(void *, int, void *, void *);
   238         -extern unsigned char __stdcall	DestroyWindow(void *);
   239         -extern int __stdcall	DispatchMessageW(const MSG *);
   240         -extern unsigned char __stdcall	GetMessageW(MSG *, void *, int, int);
   241         -extern void __stdcall	MsgWaitForMultipleObjects(DWORD, void *,
   242         -			    unsigned char, DWORD, DWORD);
   243         -extern unsigned char __stdcall	PeekMessageW(MSG *, void *, int, int, int);
   244         -extern unsigned char __stdcall	PostMessageW(void *, unsigned int, void *,
   245         -				    void *);
   246         -extern void __stdcall	PostQuitMessage(int);
   247         -extern void *__stdcall	RegisterClassW(const WNDCLASS *);
   248         -extern unsigned char __stdcall	ResetEvent(void *);
   249         -extern unsigned char __stdcall	TranslateMessage(const MSG *);
   250         -
   251         -/*
   252         - * Threaded-cygwin specific constants and functions in this file:
   253         - */
   254         -
   255         -static const WCHAR className[] = L"TclNotifier";
   256         -static DWORD __stdcall	NotifierProc(void *hwnd, unsigned int message,
   257         -			    void *wParam, void *lParam);
   258         -#endif /* TCL_THREADS && __CYGWIN__ */
   259         -
           30  +#ifdef NOTIFIER_SELECT
   260     31   #if TCL_THREADS
   261     32   /*
   262     33    *----------------------------------------------------------------------
   263     34    *
   264     35    * StartNotifierThread --
   265     36    *
   266     37    *	Start a notfier thread and wait for the notifier pipe to be created.
................................................................................
   296     67   
   297     68   	    notifierThreadRunning = 1;
   298     69   	}
   299     70   	pthread_mutex_unlock(&notifierInitMutex);
   300     71       }
   301     72   }
   302     73   #endif /* TCL_THREADS */
   303         -
   304         -/*
   305         - *----------------------------------------------------------------------
   306         - *
   307         - * Tcl_InitNotifier --
   308         - *
   309         - *	Initializes the platform specific notifier state.
   310         - *
   311         - * Results:
   312         - *	Returns a handle to the notifier state for this thread.
   313         - *
   314         - * Side effects:
   315         - *	None.
   316         - *
   317         - *----------------------------------------------------------------------
   318         - */
   319         -
   320         -ClientData
   321         -Tcl_InitNotifier(void)
   322         -{
   323         -    if (tclNotifierHooks.initNotifierProc) {
   324         -	return tclNotifierHooks.initNotifierProc();
   325         -    } else {
   326         -	ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
   327         -
   328         -#ifdef TCL_THREADS
   329         -	tsdPtr->eventReady = 0;
   330         -
   331         -	/*
   332         -	 * Initialize thread specific condition variable for this thread.
   333         -	 */
   334         -	if (tsdPtr->waitCVinitialized == 0) {
   335         -#ifdef __CYGWIN__
   336         -	    WNDCLASS class;
   337         -
   338         -	    class.style = 0;
   339         -	    class.cbClsExtra = 0;
   340         -	    class.cbWndExtra = 0;
   341         -	    class.hInstance = TclWinGetTclInstance();
   342         -	    class.hbrBackground = NULL;
   343         -	    class.lpszMenuName = NULL;
   344         -	    class.lpszClassName = className;
   345         -	    class.lpfnWndProc = NotifierProc;
   346         -	    class.hIcon = NULL;
   347         -	    class.hCursor = NULL;
   348         -
   349         -	    RegisterClassW(&class);
   350         -	    tsdPtr->hwnd = CreateWindowExW(NULL, class.lpszClassName,
   351         -		    class.lpszClassName, 0, 0, 0, 0, 0, NULL, NULL,
   352         -		    TclWinGetTclInstance(), NULL);
   353         -	    tsdPtr->event = CreateEventW(NULL, 1 /* manual */,
   354         -		    0 /* !signaled */, NULL);
   355         -#else
   356         -	    pthread_cond_init(&tsdPtr->waitCV, NULL);
   357         -#endif /* __CYGWIN__ */
   358         -	    tsdPtr->waitCVinitialized = 1;
   359         -	}
   360         -
   361         -	pthread_mutex_lock(&notifierInitMutex);
   362         -#if defined(HAVE_PTHREAD_ATFORK)
   363         -	/*
   364         -	 * Install pthread_atfork handlers to clean up the notifier in the
   365         -	 * child of a fork.
   366         -	 */
   367         -
   368         -	if (!atForkInit) {
   369         -	    int result = pthread_atfork(NULL, NULL, AtForkChild);
   370         -
   371         -	    if (result) {
   372         -		Tcl_Panic("Tcl_InitNotifier: pthread_atfork failed");
   373         -	    }
   374         -	    atForkInit = 1;
   375         -	}
   376         -#endif /* HAVE_PTHREAD_ATFORK */
   377         -
   378         -	notifierCount++;
   379         -
   380         -	pthread_mutex_unlock(&notifierInitMutex);
   381         -
   382         -#endif /* TCL_THREADS */
   383         -	return tsdPtr;
   384         -    }
   385         -}
   386         -
   387         -/*
   388         - *----------------------------------------------------------------------
   389         - *
   390         - * Tcl_FinalizeNotifier --
   391         - *
   392         - *	This function is called to cleanup the notifier state before a thread
   393         - *	is terminated.
   394         - *
   395         - * Results:
   396         - *	None.
   397         - *
   398         - * Side effects:
   399         - *	May terminate the background notifier thread if this is the last
   400         - *	notifier instance.
   401         - *
   402         - *----------------------------------------------------------------------
   403         - */
   404         -
   405         -void
   406         -Tcl_FinalizeNotifier(
   407         -    ClientData clientData)		/* Not used. */
   408         -{
   409         -    if (tclNotifierHooks.finalizeNotifierProc) {
   410         -	tclNotifierHooks.finalizeNotifierProc(clientData);
   411         -	return;
   412         -    } else {
   413         -#ifdef TCL_THREADS
   414         -	ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
   415         -
   416         -	pthread_mutex_lock(&notifierInitMutex);
   417         -	notifierCount--;
   418         -
   419         -	/*
   420         -	 * If this is the last thread to use the notifier, close the notifier
   421         -	 * pipe and wait for the background thread to terminate.
   422         -	 */
   423         -
   424         -	if (notifierCount == 0) {
   425         -
   426         -	    if (triggerPipe != -1) {
   427         -		if (write(triggerPipe, "q", 1) != 1) {
   428         -		    Tcl_Panic("Tcl_FinalizeNotifier: %s",
   429         -			    "unable to write q to triggerPipe");
   430         -		}
   431         -		close(triggerPipe);
   432         -		pthread_mutex_lock(&notifierMutex);
   433         -		while(triggerPipe != -1) {
   434         -		    pthread_cond_wait(&notifierCV, &notifierMutex);
   435         -		}
   436         -		pthread_mutex_unlock(&notifierMutex);
   437         -		if (notifierThreadRunning) {
   438         -		    int result = pthread_join((pthread_t) notifierThread, NULL);
   439         -
   440         -		    if (result) {
   441         -			Tcl_Panic("Tcl_FinalizeNotifier: unable to join notifier "
   442         -				"thread");
   443         -		    }
   444         -		    notifierThreadRunning = 0;
   445         -		}
   446         -	    }
   447         -	}
   448         -
   449         -	/*
   450         -	 * Clean up any synchronization objects in the thread local storage.
   451         -	 */
   452         -
   453         -#ifdef __CYGWIN__
   454         -	DestroyWindow(tsdPtr->hwnd);
   455         -	CloseHandle(tsdPtr->event);
   456         -#else /* __CYGWIN__ */
   457         -	pthread_cond_destroy(&tsdPtr->waitCV);
   458         -#endif /* __CYGWIN__ */
   459         -	tsdPtr->waitCVinitialized = 0;
   460         -
   461         -	pthread_mutex_unlock(&notifierInitMutex);
   462         -#endif /* TCL_THREADS */
   463         -    }
   464         -}
           74  +#endif /* NOTIFIER_SELECT */
   465     75   
   466     76   /*
   467     77    *----------------------------------------------------------------------
   468     78    *
   469     79    * Tcl_AlertNotifier --
   470     80    *
   471     81    *	Wake up the specified notifier from any thread. This routine is called
................................................................................
   473     83    *	routine is called. This routine is guaranteed not to be called on a
   474     84    *	given notifier after Tcl_FinalizeNotifier is called for that notifier.
   475     85    *
   476     86    * Results:
   477     87    *	None.
   478     88    *
   479     89    * Side effects:
   480         - *	Signals the notifier condition variable for the specified notifier.
           90  + *	select(2) notifier:
           91  + *		signals the notifier condition variable for the specified
           92  + *		notifier.
           93  + *	epoll(7) notifier:
           94  + *		write(2)s to the eventfd(2) of the specified thread.
           95  + *	kqueue(2) notifier:
           96  + *		write(2)s to the trigger pipe(2) of the specified thread.
   481     97    *
   482     98    *----------------------------------------------------------------------
   483     99    */
   484    100   
   485    101   void
   486    102   Tcl_AlertNotifier(
   487    103       ClientData clientData)
   488    104   {
   489    105       if (tclNotifierHooks.alertNotifierProc) {
   490    106   	tclNotifierHooks.alertNotifierProc(clientData);
   491    107   	return;
   492    108       } else {
          109  +#ifdef NOTIFIER_SELECT
   493    110   #ifdef TCL_THREADS
   494    111   	ThreadSpecificData *tsdPtr = clientData;
   495    112   
   496    113   	pthread_mutex_lock(&notifierMutex);
   497    114   	tsdPtr->eventReady = 1;
   498    115   
   499    116   #   ifdef __CYGWIN__
   500    117   	PostMessageW(tsdPtr->hwnd, 1024, 0, 0);
   501    118   #   else
   502    119   	pthread_cond_broadcast(&tsdPtr->waitCV);
   503    120   #   endif /* __CYGWIN__ */
   504    121   	pthread_mutex_unlock(&notifierMutex);
   505    122   #endif /* TCL_THREADS */
          123  +#else
          124  +	ThreadSpecificData *tsdPtr = clientData;
          125  +#if defined(NOTIFIER_EPOLL) && defined(HAVE_EVENTFD)
          126  +	uint64_t eventFdVal = 1;
          127  +	if (write(tsdPtr->triggerEventFd, &eventFdVal,
          128  +		sizeof(eventFdVal)) != sizeof(eventFdVal)) {
          129  +	    Tcl_Panic("Tcl_AlertNotifier: unable to write to %p->triggerEventFd",
          130  +		(void *)tsdPtr);
          131  +#else
          132  +	if (write(tsdPtr->triggerPipe[1], "", 1) != 1) {
          133  +	    Tcl_Panic("Tcl_AlertNotifier: unable to write to %p->triggerPipe",
          134  +		(void *)tsdPtr);
          135  +#endif /* NOTIFIER_EPOLL && HAVE_EVENTFD */
          136  +	}
          137  +#endif /* NOTIFIER_SELECT */
   506    138       }
   507    139   }
   508    140   
   509    141   /*
   510    142    *----------------------------------------------------------------------
   511    143    *
   512    144    * Tcl_SetTimer --
................................................................................
   561    193       int mode)			/* Either TCL_SERVICE_ALL, or
   562    194   				 * TCL_SERVICE_NONE. */
   563    195   {
   564    196       if (tclNotifierHooks.serviceModeHookProc) {
   565    197   	tclNotifierHooks.serviceModeHookProc(mode);
   566    198   	return;
   567    199       } else if (mode == TCL_SERVICE_ALL) {
          200  +#ifdef NOTIFIER_SELECT
   568    201   #if TCL_THREADS
   569    202   	StartNotifierThread("Tcl_ServiceModeHook");
   570    203   #endif
   571         -    }
   572         -}
   573         -
   574         -/*
   575         - *----------------------------------------------------------------------
   576         - *
   577         - * Tcl_CreateFileHandler --
   578         - *
   579         - *	This function registers a file handler with the select notifier.
   580         - *
   581         - * Results:
   582         - *	None.
   583         - *
   584         - * Side effects:
   585         - *	Creates a new file handler structure.
   586         - *
   587         - *----------------------------------------------------------------------
   588         - */
   589         -
   590         -void
   591         -Tcl_CreateFileHandler(
   592         -    int fd,			/* Handle of stream to watch. */
   593         -    int mask,			/* OR'ed combination of TCL_READABLE,
   594         -				 * TCL_WRITABLE, and TCL_EXCEPTION: indicates
   595         -				 * conditions under which proc should be
   596         -				 * called. */
   597         -    Tcl_FileProc *proc,		/* Function to call for each selected
   598         -				 * event. */
   599         -    ClientData clientData)	/* Arbitrary data to pass to proc. */
   600         -{
   601         -    if (tclNotifierHooks.createFileHandlerProc) {
   602         -	tclNotifierHooks.createFileHandlerProc(fd, mask, proc, clientData);
   603         -	return;
   604         -    } else {
   605         -	ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
   606         -	FileHandler *filePtr;
   607         -
   608         -	for (filePtr = tsdPtr->firstFileHandlerPtr; filePtr != NULL;
   609         -		filePtr = filePtr->nextPtr) {
   610         -	    if (filePtr->fd == fd) {
   611         -		break;
   612         -	    }
   613         -	}
   614         -	if (filePtr == NULL) {
   615         -	    filePtr = ckalloc(sizeof(FileHandler));
   616         -	    filePtr->fd = fd;
   617         -	    filePtr->readyMask = 0;
   618         -	    filePtr->nextPtr = tsdPtr->firstFileHandlerPtr;
   619         -	    tsdPtr->firstFileHandlerPtr = filePtr;
   620         -	}
   621         -	filePtr->proc = proc;
   622         -	filePtr->clientData = clientData;
   623         -	filePtr->mask = mask;
   624         -
   625         -	/*
   626         -	 * Update the check masks for this file.
   627         -	 */
   628         -
   629         -	if (mask & TCL_READABLE) {
   630         -	    FD_SET(fd, &tsdPtr->checkMasks.readable);
   631         -	} else {
   632         -	    FD_CLR(fd, &tsdPtr->checkMasks.readable);
   633         -	}
   634         -	if (mask & TCL_WRITABLE) {
   635         -	    FD_SET(fd, &tsdPtr->checkMasks.writable);
   636         -	} else {
   637         -	    FD_CLR(fd, &tsdPtr->checkMasks.writable);
   638         -	}
   639         -	if (mask & TCL_EXCEPTION) {
   640         -	    FD_SET(fd, &tsdPtr->checkMasks.exception);
   641         -	} else {
   642         -	    FD_CLR(fd, &tsdPtr->checkMasks.exception);
   643         -	}
   644         -	if (tsdPtr->numFdBits <= fd) {
   645         -	    tsdPtr->numFdBits = fd+1;
   646         -	}
   647         -    }
   648         -}
   649         -
   650         -/*
   651         - *----------------------------------------------------------------------
   652         - *
   653         - * Tcl_DeleteFileHandler --
   654         - *
   655         - *	Cancel a previously-arranged callback arrangement for a file.
   656         - *
   657         - * Results:
   658         - *	None.
   659         - *
   660         - * Side effects:
   661         - *	If a callback was previously registered on file, remove it.
   662         - *
   663         - *----------------------------------------------------------------------
   664         - */
   665         -
   666         -void
   667         -Tcl_DeleteFileHandler(
   668         -    int fd)			/* Stream id for which to remove callback
   669         -				 * function. */
   670         -{
   671         -    if (tclNotifierHooks.deleteFileHandlerProc) {
   672         -	tclNotifierHooks.deleteFileHandlerProc(fd);
   673         -	return;
   674         -    } else {
   675         -	FileHandler *filePtr, *prevPtr;
   676         -	int i;
   677         -	ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
   678         -
   679         -	/*
   680         -	 * Find the entry for the given file (and return if there isn't one).
   681         -	 */
   682         -
   683         -	for (prevPtr = NULL, filePtr = tsdPtr->firstFileHandlerPtr; ;
   684         -		prevPtr = filePtr, filePtr = filePtr->nextPtr) {
   685         -	    if (filePtr == NULL) {
   686         -		return;
   687         -	    }
   688         -	    if (filePtr->fd == fd) {
   689         -		break;
   690         -	    }
   691         -	}
   692         -
   693         -	/*
   694         -	 * Update the check masks for this file.
   695         -	 */
   696         -
   697         -	if (filePtr->mask & TCL_READABLE) {
   698         -	    FD_CLR(fd, &tsdPtr->checkMasks.readable);
   699         -	}
   700         -	if (filePtr->mask & TCL_WRITABLE) {
   701         -	    FD_CLR(fd, &tsdPtr->checkMasks.writable);
   702         -	}
   703         -	if (filePtr->mask & TCL_EXCEPTION) {
   704         -	    FD_CLR(fd, &tsdPtr->checkMasks.exception);
   705         -	}
   706         -
   707         -	/*
   708         -	 * Find current max fd.
   709         -	 */
   710         -
   711         -	if (fd+1 == tsdPtr->numFdBits) {
   712         -	    int numFdBits = 0;
   713         -
   714         -	    for (i = fd-1; i >= 0; i--) {
   715         -		if (FD_ISSET(i, &tsdPtr->checkMasks.readable)
   716         -			|| FD_ISSET(i, &tsdPtr->checkMasks.writable)
   717         -			|| FD_ISSET(i, &tsdPtr->checkMasks.exception)) {
   718         -		    numFdBits = i+1;
   719         -		    break;
   720         -		}
   721         -	    }
   722         -	    tsdPtr->numFdBits = numFdBits;
   723         -	}
   724         -
   725         -	/*
   726         -	 * Clean up information in the callback record.
   727         -	 */
   728         -
   729         -	if (prevPtr == NULL) {
   730         -	    tsdPtr->firstFileHandlerPtr = filePtr->nextPtr;
   731         -	} else {
   732         -	    prevPtr->nextPtr = filePtr->nextPtr;
   733         -	}
   734         -	ckfree(filePtr);
          204  +#endif /* NOTIFIER_SELECT */
   735    205       }
   736    206   }
   737    207   
   738    208   /*
   739    209    *----------------------------------------------------------------------
   740    210    *
   741    211    * FileHandlerEventProc --
................................................................................
   805    275   	    filePtr->proc(filePtr->clientData, mask);
   806    276   	}
   807    277   	break;
   808    278       }
   809    279       return 1;
   810    280   }
   811    281   
   812         -#if defined(TCL_THREADS) && defined(__CYGWIN__)
   813         -
   814         -static DWORD __stdcall
   815         -NotifierProc(
   816         -    void *hwnd,
   817         -    unsigned int message,
   818         -    void *wParam,
   819         -    void *lParam)
   820         -{
   821         -    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
   822         -
   823         -    if (message != 1024) {
   824         -	return DefWindowProcW(hwnd, message, wParam, lParam);
   825         -    }
   826         -
   827         -    /*
   828         -     * Process all of the runnable events.
   829         -     */
   830         -
   831         -    tsdPtr->eventReady = 1;
   832         -    Tcl_ServiceAll();
   833         -    return 0;
   834         -}
   835         -#endif /* TCL_THREADS && __CYGWIN__ */
   836         -
          282  +#ifdef NOTIFIER_SELECT
          283  +#ifdef TCL_THREADS
   837    284   /*
   838    285    *----------------------------------------------------------------------
   839    286    *
   840         - * Tcl_WaitForEvent --
          287  + * AlertSingleThread --
   841    288    *
   842         - *	This function is called by Tcl_DoOneEvent to wait for new events on
   843         - *	the message queue. If the block time is 0, then Tcl_WaitForEvent just
   844         - *	polls without blocking.
          289  + *	Notify a single thread that is waiting on a file descriptor to become
          290  + *	readable or writable or to have an exception condition.
          291  + *	notifierMutex must be held.
   845    292    *
   846         - * Results:
   847         - *	Returns -1 if the select would block forever, otherwise returns 0.
          293  + * Result:
          294  + *	None.
   848    295    *
   849    296    * Side effects:
   850         - *	Queues file events that are detected by the select.
          297  + *	The condition variable associated with the thread is broadcasted.
   851    298    *
   852    299    *----------------------------------------------------------------------
   853    300    */
   854    301   
   855         -int
   856         -Tcl_WaitForEvent(
   857         -    const Tcl_Time *timePtr)		/* Maximum block time, or NULL. */
          302  +static void
          303  +AlertSingleThread(
          304  +	ThreadSpecificData *tsdPtr)
   858    305   {
   859         -    if (tclNotifierHooks.waitForEventProc) {
   860         -	return tclNotifierHooks.waitForEventProc(timePtr);
   861         -    } else {
   862         -	FileHandler *filePtr;
   863         -	int mask;
   864         -	Tcl_Time vTime;
   865         -#ifdef TCL_THREADS
   866         -	int waitForFiles;
   867         -#   ifdef __CYGWIN__
   868         -	MSG msg;
   869         -#   endif /* __CYGWIN__ */
   870         -#else
   871         -	/*
   872         -	 * Impl. notes: timeout & timeoutPtr are used if, and only if threads
   873         -	 * are not enabled. They are the arguments for the regular select()
   874         -	 * used when the core is not thread-enabled.
   875         -	 */
   876         -
   877         -	struct timeval timeout, *timeoutPtr;
   878         -	int numFound;
   879         -#endif /* TCL_THREADS */
   880         -	ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
   881         -
   882         -	/*
   883         -	 * Set up the timeout structure. Note that if there are no events to
   884         -	 * check for, we return with a negative result rather than blocking
   885         -	 * forever.
   886         -	 */
   887         -
   888         -	if (timePtr != NULL) {
   889         -	    /*
   890         -	     * TIP #233 (Virtualized Time). Is virtual time in effect? And do
   891         -	     * we actually have something to scale? If yes to both then we
   892         -	     * call the handler to do this scaling.
   893         -	     */
   894         -
   895         -	    if (timePtr->sec != 0 || timePtr->usec != 0) {
   896         -		vTime = *timePtr;
   897         -		tclScaleTimeProcPtr(&vTime, tclTimeClientData);
   898         -		timePtr = &vTime;
   899         -	    }
   900         -#ifndef TCL_THREADS
   901         -	    timeout.tv_sec = timePtr->sec;
   902         -	    timeout.tv_usec = timePtr->usec;
   903         -	    timeoutPtr = &timeout;
   904         -	} else if (tsdPtr->numFdBits == 0) {
   905         -	    /*
   906         -	     * If there are no threads, no timeout, and no fds registered,
   907         -	     * then there are no events possible and we must avoid deadlock.
   908         -	     * Note that this is not entirely correct because there might be a
   909         -	     * signal that could interrupt the select call, but we don't
   910         -	     * handle that case if we aren't using threads.
   911         -	     */
   912         -
   913         -	    return -1;
   914         -	} else {
   915         -	    timeoutPtr = NULL;
   916         -#endif /* !TCL_THREADS */
   917         -	}
   918         -
   919         -#ifdef TCL_THREADS
   920         -	/*
   921         -	 * Start notifier thread and place this thread on the list of
   922         -	 * interested threads, signal the notifier thread, and wait for a
   923         -	 * response or a timeout.
   924         -	 */
   925         -	StartNotifierThread("Tcl_WaitForEvent");
   926         -
   927         -	pthread_mutex_lock(&notifierMutex);
   928         -
   929         -	if (timePtr != NULL && timePtr->sec == 0 && (timePtr->usec == 0
   930         -#if defined(__APPLE__) && defined(__LP64__)
   931         -		/*
   932         -		 * On 64-bit Darwin, pthread_cond_timedwait() appears to have
   933         -		 * a bug that causes it to wait forever when passed an
   934         -		 * absolute time which has already been exceeded by the system
   935         -		 * time; as a workaround, when given a very brief timeout,
   936         -		 * just do a poll. [Bug 1457797]
   937         -		 */
   938         -		|| timePtr->usec < 10
   939         -#endif /* __APPLE__ && __LP64__ */
   940         -		)) {
   941         -	    /*
   942         -	     * Cannot emulate a polling select with a polling condition
   943         -	     * variable. Instead, pretend to wait for files and tell the
   944         -	     * notifier thread what we are doing. The notifier thread makes
   945         -	     * sure it goes through select with its select mask in the same
   946         -	     * state as ours currently is. We block until that happens.
   947         -	     */
   948         -
   949         -	    waitForFiles = 1;
   950         -	    tsdPtr->pollState = POLL_WANT;
   951         -	    timePtr = NULL;
   952         -	} else {
   953         -	    waitForFiles = (tsdPtr->numFdBits > 0);
   954         -	    tsdPtr->pollState = 0;
   955         -	}
   956         -
   957         -	if (waitForFiles) {
   958         -	    /*
   959         -	     * Add the ThreadSpecificData structure of this thread to the list
   960         -	     * of ThreadSpecificData structures of all threads that are
   961         -	     * waiting on file events.
   962         -	     */
   963         -
   964         -	    tsdPtr->nextPtr = waitingListPtr;
   965         -	    if (waitingListPtr) {
   966         -		waitingListPtr->prevPtr = tsdPtr;
   967         -	    }
   968         -	    tsdPtr->prevPtr = 0;
   969         -	    waitingListPtr = tsdPtr;
   970         -	    tsdPtr->onList = 1;
   971         -
   972         -	    if ((write(triggerPipe, "", 1) == -1) && (errno != EAGAIN)) {
   973         -		Tcl_Panic("Tcl_WaitForEvent: %s",
   974         -			"unable to write to triggerPipe");
   975         -	    }
   976         -	}
   977         -
   978         -	FD_ZERO(&tsdPtr->readyMasks.readable);
   979         -	FD_ZERO(&tsdPtr->readyMasks.writable);
   980         -	FD_ZERO(&tsdPtr->readyMasks.exception);
   981         -
   982         -	if (!tsdPtr->eventReady) {
          306  +    tsdPtr->eventReady = 1;
          307  +    if (tsdPtr->onList) {
          308  +        /*
          309  +         * Remove the ThreadSpecificData structure of this thread
          310  +         * from the waiting list. This prevents us from
          311  +         * continuously spinning on epoll_wait until the other
          312  +         * threads runs and services the file event.
          313  +         */
          314  +
          315  +        if (tsdPtr->prevPtr) {
          316  +    	    tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr;
          317  +        } else {
          318  +    	    waitingListPtr = tsdPtr->nextPtr;
          319  +        }
          320  +        if (tsdPtr->nextPtr) {
          321  +    	    tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr;
          322  +        }
          323  +        tsdPtr->nextPtr = tsdPtr->prevPtr = NULL;
          324  +        tsdPtr->onList = 0;
          325  +        tsdPtr->pollState = 0;
          326  +    }
   983    327   #ifdef __CYGWIN__
   984         -	    if (!PeekMessageW(&msg, NULL, 0, 0, 0)) {
   985         -		DWORD timeout;
   986         -
   987         -		if (timePtr) {
   988         -		    timeout = timePtr->sec * 1000 + timePtr->usec / 1000;
   989         -		} else {
   990         -		    timeout = 0xFFFFFFFF;
   991         -		}
   992         -		pthread_mutex_unlock(&notifierMutex);
   993         -		MsgWaitForMultipleObjects(1, &tsdPtr->event, 0, timeout, 1279);
   994         -		pthread_mutex_lock(&notifierMutex);
   995         -	    }
   996         -#else
   997         -	    if (timePtr != NULL) {
   998         -	       Tcl_Time now;
   999         -	       struct timespec ptime;
  1000         -
  1001         -	       Tcl_GetTime(&now);
  1002         -	       ptime.tv_sec = timePtr->sec + now.sec + (timePtr->usec + now.usec) / 1000000;
  1003         -	       ptime.tv_nsec = 1000 * ((timePtr->usec + now.usec) % 1000000);
  1004         -
  1005         -	       pthread_cond_timedwait(&tsdPtr->waitCV, &notifierMutex, &ptime);
  1006         -	    } else {
  1007         -	       pthread_cond_wait(&tsdPtr->waitCV, &notifierMutex);
  1008         -	    }
  1009         -#endif /* __CYGWIN__ */
  1010         -	}
  1011         -	tsdPtr->eventReady = 0;
  1012         -
  1013         -#ifdef __CYGWIN__
  1014         -	while (PeekMessageW(&msg, NULL, 0, 0, 0)) {
  1015         -	    /*
  1016         -	     * Retrieve and dispatch the message.
  1017         -	     */
  1018         -
  1019         -	    DWORD result = GetMessageW(&msg, NULL, 0, 0);
  1020         -
  1021         -	    if (result == 0) {
  1022         -		PostQuitMessage(msg.wParam);
  1023         -		/* What to do here? */
  1024         -	    } else if (result != (DWORD) -1) {
  1025         -		TranslateMessage(&msg);
  1026         -		DispatchMessageW(&msg);
  1027         -	    }
  1028         -	}
  1029         -	ResetEvent(tsdPtr->event);
  1030         -#endif /* __CYGWIN__ */
  1031         -
  1032         -	if (waitForFiles && tsdPtr->onList) {
  1033         -	    /*
  1034         -	     * Remove the ThreadSpecificData structure of this thread from the
  1035         -	     * waiting list. Alert the notifier thread to recompute its select
  1036         -	     * masks - skipping this caused a hang when trying to close a pipe
  1037         -	     * which the notifier thread was still doing a select on.
  1038         -	     */
  1039         -
  1040         -	    if (tsdPtr->prevPtr) {
  1041         -		tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr;
  1042         -	    } else {
  1043         -		waitingListPtr = tsdPtr->nextPtr;
  1044         -	    }
  1045         -	    if (tsdPtr->nextPtr) {
  1046         -		tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr;
  1047         -	    }
  1048         -	    tsdPtr->nextPtr = tsdPtr->prevPtr = NULL;
  1049         -	    tsdPtr->onList = 0;
  1050         -	    if ((write(triggerPipe, "", 1) == -1) && (errno != EAGAIN)) {
  1051         -		Tcl_Panic("Tcl_WaitForEvent: %s",
  1052         -			"unable to write to triggerPipe");
  1053         -	    }
  1054         -	}
  1055         -
  1056         -#else
  1057         -	tsdPtr->readyMasks = tsdPtr->checkMasks;
  1058         -	numFound = select(tsdPtr->numFdBits, &tsdPtr->readyMasks.readable,
  1059         -		&tsdPtr->readyMasks.writable, &tsdPtr->readyMasks.exception,
  1060         -		timeoutPtr);
  1061         -
  1062         -	/*
  1063         -	 * Some systems don't clear the masks after an error, so we have to do
  1064         -	 * it here.
  1065         -	 */
  1066         -
  1067         -	if (numFound == -1) {
  1068         -	    FD_ZERO(&tsdPtr->readyMasks.readable);
  1069         -	    FD_ZERO(&tsdPtr->readyMasks.writable);
  1070         -	    FD_ZERO(&tsdPtr->readyMasks.exception);
  1071         -	}
  1072         -#endif /* TCL_THREADS */
  1073         -
  1074         -	/*
  1075         -	 * Queue all detected file events before returning.
  1076         -	 */
  1077         -
  1078         -	for (filePtr = tsdPtr->firstFileHandlerPtr; (filePtr != NULL);
  1079         -		filePtr = filePtr->nextPtr) {
  1080         -	    mask = 0;
  1081         -	    if (FD_ISSET(filePtr->fd, &tsdPtr->readyMasks.readable)) {
  1082         -		mask |= TCL_READABLE;
  1083         -	    }
  1084         -	    if (FD_ISSET(filePtr->fd, &tsdPtr->readyMasks.writable)) {
  1085         -		mask |= TCL_WRITABLE;
  1086         -	    }
  1087         -	    if (FD_ISSET(filePtr->fd, &tsdPtr->readyMasks.exception)) {
  1088         -		mask |= TCL_EXCEPTION;
  1089         -	    }
  1090         -
  1091         -	    if (!mask) {
  1092         -		continue;
  1093         -	    }
  1094         -
  1095         -	    /*
  1096         -	     * Don't bother to queue an event if the mask was previously
  1097         -	     * non-zero since an event must still be on the queue.
  1098         -	     */
  1099         -
  1100         -	    if (filePtr->readyMask == 0) {
  1101         -		FileHandlerEvent *fileEvPtr =
  1102         -			ckalloc(sizeof(FileHandlerEvent));
  1103         -
  1104         -		fileEvPtr->header.proc = FileHandlerEventProc;
  1105         -		fileEvPtr->fd = filePtr->fd;
  1106         -		Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL);
  1107         -	    }
  1108         -	    filePtr->readyMask = mask;
  1109         -	}
  1110         -#ifdef TCL_THREADS
  1111         -	pthread_mutex_unlock(&notifierMutex);
  1112         -#endif /* TCL_THREADS */
  1113         -	return 0;
  1114         -    }
  1115         -}
  1116         -
  1117         -#ifdef TCL_THREADS
  1118         -/*
  1119         - *----------------------------------------------------------------------
  1120         - *
  1121         - * NotifierThreadProc --
  1122         - *
  1123         - *	This routine is the initial (and only) function executed by the
  1124         - *	special notifier thread. Its job is to wait for file descriptors to
  1125         - *	become readable or writable or to have an exception condition and then
  1126         - *	to notify other threads who are interested in this information by
  1127         - *	signalling a condition variable. Other threads can signal this
  1128         - *	notifier thread of a change in their interests by writing a single
  1129         - *	byte to a special pipe that the notifier thread is monitoring.
  1130         - *
  1131         - * Result:
  1132         - *	None. Once started, this routine never exits. It dies with the overall
  1133         - *	process.
  1134         - *
  1135         - * Side effects:
  1136         - *	The trigger pipe used to signal the notifier thread is created when
  1137         - *	the notifier thread first starts.
  1138         - *
  1139         - *----------------------------------------------------------------------
  1140         - */
  1141         -
  1142         -static TCL_NORETURN void
  1143         -NotifierThreadProc(
  1144         -    ClientData clientData)	/* Not used. */
  1145         -{
  1146         -    ThreadSpecificData *tsdPtr;
  1147         -    fd_set readableMask;
  1148         -    fd_set writableMask;
  1149         -    fd_set exceptionMask;
  1150         -    int fds[2];
  1151         -    int i, numFdBits = 0, receivePipe;
  1152         -    long found;
  1153         -    struct timeval poll = {0., 0.}, *timePtr;
  1154         -    char buf[2];
  1155         -
  1156         -    if (pipe(fds) != 0) {
  1157         -	Tcl_Panic("NotifierThreadProc: %s", "could not create trigger pipe");
  1158         -    }
  1159         -
  1160         -    receivePipe = fds[0];
  1161         -
  1162         -    if (TclUnixSetBlockingMode(receivePipe, TCL_MODE_NONBLOCKING) < 0) {
  1163         -	Tcl_Panic("NotifierThreadProc: %s",
  1164         -		"could not make receive pipe non blocking");
  1165         -    }
  1166         -    if (TclUnixSetBlockingMode(fds[1], TCL_MODE_NONBLOCKING) < 0) {
  1167         -	Tcl_Panic("NotifierThreadProc: %s",
  1168         -		"could not make trigger pipe non blocking");
  1169         -    }
  1170         -    if (fcntl(receivePipe, F_SETFD, FD_CLOEXEC) < 0) {
  1171         -	Tcl_Panic("NotifierThreadProc: %s",
  1172         -		"could not make receive pipe close-on-exec");
  1173         -    }
  1174         -    if (fcntl(fds[1], F_SETFD, FD_CLOEXEC) < 0) {
  1175         -	Tcl_Panic("NotifierThreadProc: %s",
  1176         -		"could not make trigger pipe close-on-exec");
  1177         -    }
  1178         -
  1179         -    /*
  1180         -     * Install the write end of the pipe into the global variable.
  1181         -     */
  1182         -
  1183         -    pthread_mutex_lock(&notifierMutex);
  1184         -    triggerPipe = fds[1];
  1185         -
  1186         -    /*
  1187         -     * Signal any threads that are waiting.
  1188         -     */
  1189         -
  1190         -    pthread_cond_broadcast(&notifierCV);
  1191         -    pthread_mutex_unlock(&notifierMutex);
  1192         -
  1193         -    /*
  1194         -     * Look for file events and report them to interested threads.
  1195         -     */
  1196         -
  1197         -    while (1) {
  1198         -	FD_ZERO(&readableMask);
  1199         -	FD_ZERO(&writableMask);
  1200         -	FD_ZERO(&exceptionMask);
  1201         -
  1202         -	/*
  1203         -	 * Compute the logical OR of the select masks from all the waiting
  1204         -	 * notifiers.
  1205         -	 */
  1206         -
  1207         -	pthread_mutex_lock(&notifierMutex);
  1208         -	timePtr = NULL;
  1209         -	for (tsdPtr = waitingListPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) {
  1210         -	    for (i = tsdPtr->numFdBits-1; i >= 0; --i) {
  1211         -		if (FD_ISSET(i, &tsdPtr->checkMasks.readable)) {
  1212         -		    FD_SET(i, &readableMask);
  1213         -		}
  1214         -		if (FD_ISSET(i, &tsdPtr->checkMasks.writable)) {
  1215         -		    FD_SET(i, &writableMask);
  1216         -		}
  1217         -		if (FD_ISSET(i, &tsdPtr->checkMasks.exception)) {
  1218         -		    FD_SET(i, &exceptionMask);
  1219         -		}
  1220         -	    }
  1221         -	    if (tsdPtr->numFdBits > numFdBits) {
  1222         -		numFdBits = tsdPtr->numFdBits;
  1223         -	    }
  1224         -	    if (tsdPtr->pollState & POLL_WANT) {
  1225         -		/*
  1226         -		 * Here we make sure we go through select() with the same mask
  1227         -		 * bits that were present when the thread tried to poll.
  1228         -		 */
  1229         -
  1230         -		tsdPtr->pollState |= POLL_DONE;
  1231         -		timePtr = &poll;
  1232         -	    }
  1233         -	}
  1234         -	pthread_mutex_unlock(&notifierMutex);
  1235         -
  1236         -	/*
  1237         -	 * Set up the select mask to include the receive pipe.
  1238         -	 */
  1239         -
  1240         -	if (receivePipe >= numFdBits) {
  1241         -	    numFdBits = receivePipe + 1;
  1242         -	}
  1243         -	FD_SET(receivePipe, &readableMask);
  1244         -
  1245         -	if (select(numFdBits, &readableMask, &writableMask, &exceptionMask,
  1246         -		timePtr) == -1) {
  1247         -	    /*
  1248         -	     * Try again immediately on an error.
  1249         -	     */
  1250         -
  1251         -	    continue;
  1252         -	}
  1253         -
  1254         -	/*
  1255         -	 * Alert any threads that are waiting on a ready file descriptor.
  1256         -	 */
  1257         -
  1258         -	pthread_mutex_lock(&notifierMutex);
  1259         -	for (tsdPtr = waitingListPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) {
  1260         -	    found = 0;
  1261         -
  1262         -	    for (i = tsdPtr->numFdBits-1; i >= 0; --i) {
  1263         -		if (FD_ISSET(i, &tsdPtr->checkMasks.readable)
  1264         -			&& FD_ISSET(i, &readableMask)) {
  1265         -		    FD_SET(i, &tsdPtr->readyMasks.readable);
  1266         -		    found = 1;
  1267         -		}
  1268         -		if (FD_ISSET(i, &tsdPtr->checkMasks.writable)
  1269         -			&& FD_ISSET(i, &writableMask)) {
  1270         -		    FD_SET(i, &tsdPtr->readyMasks.writable);
  1271         -		    found = 1;
  1272         -		}
  1273         -		if (FD_ISSET(i, &tsdPtr->checkMasks.exception)
  1274         -			&& FD_ISSET(i, &exceptionMask)) {
  1275         -		    FD_SET(i, &tsdPtr->readyMasks.exception);
  1276         -		    found = 1;
  1277         -		}
  1278         -	    }
  1279         -
  1280         -	    if (found || (tsdPtr->pollState & POLL_DONE)) {
  1281         -		tsdPtr->eventReady = 1;
  1282         -		if (tsdPtr->onList) {
  1283         -		    /*
  1284         -		     * Remove the ThreadSpecificData structure of this thread
  1285         -		     * from the waiting list. This prevents us from
  1286         -		     * continuously spining on select until the other threads
  1287         -		     * runs and services the file event.
  1288         -		     */
  1289         -
  1290         -		    if (tsdPtr->prevPtr) {
  1291         -			tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr;
  1292         -		    } else {
  1293         -			waitingListPtr = tsdPtr->nextPtr;
  1294         -		    }
  1295         -		    if (tsdPtr->nextPtr) {
  1296         -			tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr;
  1297         -		    }
  1298         -		    tsdPtr->nextPtr = tsdPtr->prevPtr = NULL;
  1299         -		    tsdPtr->onList = 0;
  1300         -		    tsdPtr->pollState = 0;
  1301         -		}
  1302         -#ifdef __CYGWIN__
  1303         -		PostMessageW(tsdPtr->hwnd, 1024, 0, 0);
          328  +    PostMessageW(tsdPtr->hwnd, 1024, 0, 0);
  1304    329   #else /* __CYGWIN__ */
  1305         -		pthread_cond_broadcast(&tsdPtr->waitCV);
          330  +    pthread_cond_broadcast(&tsdPtr->waitCV);
  1306    331   #endif /* __CYGWIN__ */
  1307         -	    }
  1308         -	}
  1309         -	pthread_mutex_unlock(&notifierMutex);
  1310         -
  1311         -	/*
  1312         -	 * Consume the next byte from the notifier pipe if the pipe was
  1313         -	 * readable. Note that there may be multiple bytes pending, but to
  1314         -	 * avoid a race condition we only read one at a time.
  1315         -	 */
  1316         -
  1317         -	if (FD_ISSET(receivePipe, &readableMask)) {
  1318         -	    i = read(receivePipe, buf, 1);
  1319         -
  1320         -	    if ((i == 0) || ((i == 1) && (buf[0] == 'q'))) {
  1321         -		/*
  1322         -		 * Someone closed the write end of the pipe or sent us a Quit
  1323         -		 * message [Bug: 4139] and then closed the write end of the
  1324         -		 * pipe so we need to shut down the notifier thread.
  1325         -		 */
  1326         -
  1327         -		break;
  1328         -	    }
  1329         -	}
  1330         -    }
  1331         -
  1332         -    /*
  1333         -     * Clean up the read end of the pipe and signal any threads waiting on
  1334         -     * termination of the notifier thread.
  1335         -     */
  1336         -
  1337         -    close(receivePipe);
  1338         -    pthread_mutex_lock(&notifierMutex);
  1339         -    triggerPipe = -1;
  1340         -    pthread_cond_broadcast(&notifierCV);
  1341         -    pthread_mutex_unlock(&notifierMutex);
  1342         -
  1343         -    TclpThreadExit(0);
  1344    332   }
  1345    333   
  1346    334   #if defined(HAVE_PTHREAD_ATFORK)
  1347    335   /*
  1348    336    *----------------------------------------------------------------------
  1349    337    *
  1350    338    * AtForkChild --
................................................................................
  1403    391   		    className, 0, 0, 0, 0, 0, NULL, NULL,
  1404    392   		    TclWinGetTclInstance(), NULL);
  1405    393   	    ResetEvent(tsdPtr->event);
  1406    394   #else
  1407    395   	    pthread_cond_destroy(&tsdPtr->waitCV);
  1408    396   	    pthread_cond_init(&tsdPtr->waitCV, NULL);
  1409    397   #endif
          398  +
  1410    399   	    /*
  1411    400   	     * In case, we had multiple threads running before the fork,
  1412    401   	     * make sure, we don't try to reach out to their thread local data.
  1413    402   	     */
  1414    403   	    tsdPtr->nextPtr = tsdPtr->prevPtr = NULL;
  1415    404   
  1416    405   	    /*
................................................................................
  1422    411   
  1423    412       Tcl_InitNotifier();
  1424    413   }
  1425    414   #endif /* HAVE_PTHREAD_ATFORK */
  1426    415   
  1427    416   #endif /* TCL_THREADS */
  1428    417   
          418  +#endif /* NOTIFIER_SELECT */
          419  +#ifndef HAVE_COREFOUNDATION	/* Darwin/Mac OS X CoreFoundation notifier is
          420  +				 * in tclMacOSXNotify.c */
          421  +/*
          422  + *----------------------------------------------------------------------
          423  + *
          424  + * TclUnixWaitForFile --
          425  + *
          426  + *	This function waits synchronously for a file to become readable or
          427  + *	writable, with an optional timeout.
          428  + *
          429  + * Results:
          430  + *	The return value is an OR'ed combination of TCL_READABLE,
          431  + *	TCL_WRITABLE, and TCL_EXCEPTION, indicating the conditions that are
          432  + *	present on file at the time of the return. This function will not
          433  + *	return until either "timeout" milliseconds have elapsed or at least
          434  + *	one of the conditions given by mask has occurred for file (a return
          435  + *	value of 0 means that a timeout occurred). No normal events will be
          436  + *	serviced during the execution of this function.
          437  + *
          438  + * Side effects:
          439  + *	Time passes.
          440  + *
          441  + *----------------------------------------------------------------------
          442  + */
          443  +
          444  +int
          445  +TclUnixWaitForFile(
          446  +    int fd,			/* Handle for file on which to wait. */
          447  +    int mask,			/* What to wait for: OR'ed combination of
          448  +				 * TCL_READABLE, TCL_WRITABLE, and
          449  +				 * TCL_EXCEPTION. */
          450  +    int timeout)		/* Maximum amount of time to wait for one of
          451  +				 * the conditions in mask to occur, in
          452  +				 * milliseconds. A value of 0 means don't wait
          453  +				 * at all, and a value of -1 means wait
          454  +				 * forever. */
          455  +{
          456  +    Tcl_Time abortTime = {0, 0}, now; /* silence gcc 4 warning */
          457  +    struct timeval blockTime, *timeoutPtr;
          458  +    struct pollfd pollFds[1];
          459  +    int numFound, result = 0, pollTimeout;
          460  +
          461  +    /*
          462  +     * If there is a non-zero finite timeout, compute the time when we give
          463  +     * up.
          464  +     */
          465  +
          466  +    if (timeout > 0) {
          467  +	Tcl_GetTime(&now);
          468  +	abortTime.sec = now.sec + timeout/1000;
          469  +	abortTime.usec = now.usec + (timeout%1000)*1000;
          470  +	if (abortTime.usec >= 1000000) {
          471  +	    abortTime.usec -= 1000000;
          472  +	    abortTime.sec += 1;
          473  +	}
          474  +	timeoutPtr = &blockTime;
          475  +    } else if (timeout == 0) {
          476  +	timeoutPtr = &blockTime;
          477  +	blockTime.tv_sec = 0;
          478  +	blockTime.tv_usec = 0;
          479  +    } else {
          480  +	timeoutPtr = NULL;
          481  +    }
          482  +
          483  +    /*
          484  +     * Setup the pollfd structure for the fd.
          485  +     */
          486  +
          487  +    pollFds[0].fd = fd;
          488  +    pollFds[0].events = pollFds[0].revents = 0;
          489  +    if (mask & TCL_READABLE) {
          490  +	pollFds[0].events |= (POLLIN | POLLHUP);
          491  +    }
          492  +    if (mask & TCL_WRITABLE) {
          493  +	pollFds[0].events |= POLLOUT;
          494  +    }
          495  +    if (mask & TCL_EXCEPTION) {
          496  +	pollFds[0].events |= POLLERR;
          497  +    }
          498  +
          499  +    /*
          500  +     * Loop in a mini-event loop of our own, waiting for either the file to
          501  +     * become ready or a timeout to occur.
          502  +     */
          503  +
          504  +    while (1) {
          505  +	if (timeout > 0) {
          506  +	    blockTime.tv_sec = abortTime.sec - now.sec;
          507  +	    blockTime.tv_usec = abortTime.usec - now.usec;
          508  +	    if (blockTime.tv_usec < 0) {
          509  +		blockTime.tv_sec -= 1;
          510  +		blockTime.tv_usec += 1000000;
          511  +	    }
          512  +	    if (blockTime.tv_sec < 0) {
          513  +		blockTime.tv_sec = 0;
          514  +		blockTime.tv_usec = 0;
          515  +	    }
          516  +	}
          517  +
          518  +	/*
          519  +	 * Wait for the event or a timeout.
          520  +	 */
          521  +
          522  +	if (!timeoutPtr) {
          523  +	    pollTimeout = -1;
          524  +	} else if (!timeoutPtr->tv_sec && !timeoutPtr->tv_usec) {
          525  +	    pollTimeout = 0;
          526  +	} else {
          527  +	    pollTimeout = (int)timeoutPtr->tv_sec * 1000;
          528  +	    if (timeoutPtr->tv_usec) {
          529  +		pollTimeout += ((int)timeoutPtr->tv_usec / 1000);
          530  +	    }
          531  +	}
          532  +	numFound = poll(pollFds, 1, pollTimeout);
          533  +	if (numFound == 1) {
          534  +	    result = 0;
          535  +	    if (pollFds[0].events & (POLLIN | POLLHUP)) {
          536  +		result |= TCL_READABLE;
          537  +	    }
          538  +	    if (pollFds[0].events & POLLOUT) {
          539  +		result |= TCL_WRITABLE;
          540  +	    }
          541  +	    if (pollFds[0].events & POLLERR) {
          542  +		result |= TCL_EXCEPTION;
          543  +	    }
          544  +	    if (result) {
          545  +		break;
          546  +	    }
          547  +	}
          548  +	if (timeout == 0) {
          549  +	    break;
          550  +	}
          551  +	if (timeout < 0) {
          552  +	    continue;
          553  +	}
          554  +
          555  +	/*
          556  +	 * The select returned early, so we need to recompute the timeout.
          557  +	 */
          558  +
          559  +	Tcl_GetTime(&now);
          560  +	if ((abortTime.sec < now.sec)
          561  +		|| (abortTime.sec==now.sec && abortTime.usec<=now.usec)) {
          562  +	    break;
          563  +	}
          564  +    }
          565  +    return result;
          566  +}
  1429    567   #endif /* !HAVE_COREFOUNDATION */
  1430         -
          568  +
  1431    569   /*
  1432    570    * Local Variables:
  1433    571    * mode: c
  1434    572    * c-basic-offset: 4
  1435    573    * fill-column: 78
  1436    574    * End:
  1437    575    */