diff options
author | jan.nijtmans <nijtmans@users.sourceforge.net> | 2021-08-17 16:16:29 (GMT) |
---|---|---|
committer | jan.nijtmans <nijtmans@users.sourceforge.net> | 2021-08-17 16:16:29 (GMT) |
commit | 5f83d31eceac5d0a48f75293f02725a59168beaf (patch) | |
tree | 088ba5712739eee4f6fd1ff277de958420af7021 /unix | |
parent | 58502bda0b655dcb2b8e619c1cce4ba99f294aad (diff) | |
download | tcl-5f83d31eceac5d0a48f75293f02725a59168beaf.zip tcl-5f83d31eceac5d0a48f75293f02725a59168beaf.tar.gz tcl-5f83d31eceac5d0a48f75293f02725a59168beaf.tar.bz2 |
tip#511 proposed implementation
Diffstat (limited to 'unix')
-rwxr-xr-x | unix/configure | 35 | ||||
-rw-r--r-- | unix/configure.ac | 7 | ||||
-rw-r--r-- | unix/tclConfig.h.in | 3 | ||||
-rw-r--r-- | unix/tclEpollNotfy.c | 59 | ||||
-rw-r--r-- | unix/tclKqueueNotfy.c | 71 | ||||
-rw-r--r-- | unix/tclSelectNotfy.c | 137 | ||||
-rw-r--r-- | unix/tclUnixNotfy.c | 76 |
7 files changed, 361 insertions, 27 deletions
diff --git a/unix/configure b/unix/configure index 98d3a50..701377b 100755 --- a/unix/configure +++ b/unix/configure @@ -9205,6 +9205,41 @@ printf "%s\n" "#define NO_FD_SET 1" >>confdefs.h fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for pselect" >&5 +printf %s "checking for pselect... " >&6; } +if test ${tcl_cv_func_pselect+y} +then : + printf %s "(cached) " >&6 +else $as_nop + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <sys/types.h> +int +main (void) +{ +void *func = pselect; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + tcl_cv_func_pselect=yes +else $as_nop + tcl_cv_func_pselect=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $tcl_cv_func_pselect" >&5 +printf "%s\n" "$tcl_cv_func_pselect" >&6; } +tcl_ok=$tcl_cv_func_pselect +if test $tcl_ok = yes; then + +printf "%s\n" "#define HAVE_PSELECT 1" >>confdefs.h + +fi + #------------------------------------------------------------------------ # Options for the notifier. Checks for epoll(7) on Linux, and # kqueue(2) on {DragonFly,Free,Net,Open}BSD diff --git a/unix/configure.ac b/unix/configure.ac index 485f13d..b824ede 100644 --- a/unix/configure.ac +++ b/unix/configure.ac @@ -318,6 +318,13 @@ if test $tcl_ok = no; then AC_DEFINE(NO_FD_SET, 1, [Do we have fd_set?]) fi +AC_CACHE_CHECK([for pselect], tcl_cv_func_pselect, [ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <sys/types.h>]], [[void *func = pselect;]])],[tcl_cv_func_pselect=yes],[tcl_cv_func_pselect=no])]) +tcl_ok=$tcl_cv_func_pselect +if test $tcl_ok = yes; then + AC_DEFINE(HAVE_PSELECT, 1, [Should we use pselect()?]) +fi + #------------------------------------------------------------------------ # Options for the notifier. Checks for epoll(7) on Linux, and # kqueue(2) on {DragonFly,Free,Net,Open}BSD diff --git a/unix/tclConfig.h.in b/unix/tclConfig.h.in index 0bf3c3d..d7f51bf 100644 --- a/unix/tclConfig.h.in +++ b/unix/tclConfig.h.in @@ -190,6 +190,9 @@ /* Define to 1 if you have the `pthread_attr_setstacksize' function. */ #undef HAVE_PTHREAD_ATTR_SETSTACKSIZE +/* Define to 1 if you have the `pselect' function */ +#undef HAVE_PSELECT + /* Does putenv() copy strings or incorporate them by reference? */ #undef HAVE_PUTENV_THAT_COPIES diff --git a/unix/tclEpollNotfy.c b/unix/tclEpollNotfy.c index 4be1439..ce7d6b1 100644 --- a/unix/tclEpollNotfy.c +++ b/unix/tclEpollNotfy.c @@ -111,6 +111,7 @@ typedef struct ThreadSpecificData { /* Pointer to at most maxReadyEvents events * returned by epoll_wait(2). */ size_t maxReadyEvents; /* Count of epoll_events in readyEvents. */ + int asyncPending; /* True when signal triggered thread. */ } ThreadSpecificData; static Tcl_ThreadDataKey dataKey; @@ -478,6 +479,10 @@ PlatformEventsWait( timePtr->tv_usec = 0; } } + if (tsdPtr->asyncPending) { + tsdPtr->asyncPending = 0; + TclAsyncMarkFromNotifier(); + } return numFound; } @@ -765,6 +770,60 @@ TclpWaitForEvent( return 0; } +/* + *---------------------------------------------------------------------- + * + * TclAsyncNotifier -- + * + * This procedure sets the async mark of an async handler to a + * given value, if it is called from the target thread. + * + * Result: + * True, when the handler will be marked, false otherwise. + * + * Side effects: + * The signal may be resent to the target thread. + * + *---------------------------------------------------------------------- + */ + +int +TclAsyncNotifier( + int sigNumber, /* Signal number. */ + Tcl_ThreadId threadId, /* Target thread. */ + ClientData clientData, /* Notifier data. */ + int *flagPtr, /* Flag to mark. */ + int value) /* Value of mark. */ +{ +#ifdef TCL_THREADS + /* + * WARNING: + * This code most likely runs in a signal handler. Thus, + * only few async-signal-safe system calls are allowed, + * e.g. pthread_self(), sem_post(), write(). + */ + + if (pthread_equal(pthread_self(), (pthread_t) threadId)) { + ThreadSpecificData *tsdPtr = (ThreadSpecificData *) clientData; + + *flagPtr = value; + if (tsdPtr != NULL && !tsdPtr->asyncPending) { + tsdPtr->asyncPending = 1; + TclpAlertNotifier(tsdPtr); + return 1; + } + return 0; + } + + /* + * Re-send the signal to the proper target thread. + */ + + pthread_kill((pthread_t) threadId, sigNumber); +#endif + return 0; +} + #endif /* NOTIFIER_EPOLL && TCL_THREADS */ #else TCL_MAC_EMPTY_FILE(unix_tclEpollNotfy_c) diff --git a/unix/tclKqueueNotfy.c b/unix/tclKqueueNotfy.c index c0b9f6f..872f71c 100644 --- a/unix/tclKqueueNotfy.c +++ b/unix/tclKqueueNotfy.c @@ -102,6 +102,7 @@ typedef struct ThreadSpecificData { struct kevent *readyEvents; /* Pointer to at most maxReadyEvents events * returned by kevent(2). */ size_t maxReadyEvents; /* Count of kevents in readyEvents. */ + int asyncPending; /* True when signal triggered thread. */ } ThreadSpecificData; static Tcl_ThreadDataKey dataKey; @@ -165,11 +166,11 @@ PlatformEventsControl( Tcl_StatBuf fdStat; if (isNew) { - newPedPtr = (struct PlatformEventData *) + newPedPtr = (struct PlatformEventData *) ckalloc(sizeof(struct PlatformEventData)); - newPedPtr->filePtr = filePtr; - newPedPtr->tsdPtr = tsdPtr; - filePtr->pedPtr = newPedPtr; + newPedPtr->filePtr = filePtr; + newPedPtr->tsdPtr = tsdPtr; + filePtr->pedPtr = newPedPtr; } /* @@ -213,7 +214,7 @@ PlatformEventsControl( EVFILT_WRITE, op, 0, 0, filePtr->pedPtr); numChanges++; } - if (numChanges) { + if (numChanges) { if (kevent(tsdPtr->eventsFd, changeList, numChanges, NULL, 0, NULL) == -1) { Tcl_Panic("kevent: %s", strerror(errno)); @@ -363,7 +364,7 @@ TclpInitNotifier(void) filePtr->mask = TCL_READABLE; PlatformEventsControl(filePtr, tsdPtr, EV_ADD, 1); if (!tsdPtr->readyEvents) { - tsdPtr->maxReadyEvents = 512; + tsdPtr->maxReadyEvents = 512; tsdPtr->readyEvents = (struct kevent *) ckalloc( tsdPtr->maxReadyEvents * sizeof(tsdPtr->readyEvents[0])); } @@ -483,6 +484,10 @@ PlatformEventsWait( timePtr->tv_usec = 0; } } + if (tsdPtr->asyncPending) { + tsdPtr->asyncPending = 0; + TclAsyncMarkFromNotifier(); + } return numFound; } @@ -761,6 +766,60 @@ TclpWaitForEvent( return 0; } +/* + *---------------------------------------------------------------------- + * + * TclAsyncNotifier -- + * + * This procedure sets the async mark of an async handler to a + * given value, if it is called from the target thread. + * + * Result: + * True, when the handler will be marked, false otherwise. + * + * Side effects: + * The signal may be resent to the target thread. + * + *---------------------------------------------------------------------- + */ + +int +TclAsyncNotifier( + int sigNumber, /* Signal number. */ + Tcl_ThreadId threadId, /* Target thread. */ + ClientData clientData, /* Notifier data. */ + int *flagPtr, /* Flag to mark. */ + int value) /* Value of mark. */ +{ +#ifdef TCL_THREADS + /* + * WARNING: + * This code most likely runs in a signal handler. Thus, + * only few async-signal-safe system calls are allowed, + * e.g. pthread_self(), sem_post(), write(). + */ + + if (pthread_equal(pthread_self(), (pthread_t) threadId)) { + ThreadSpecificData *tsdPtr = (ThreadSpecificData *) clientData; + + *flagPtr = value; + if (tsdPtr != NULL && !tsdPtr->asyncPending) { + tsdPtr->asyncPending = 1; + TclpAlertNotifier(tsdPtr); + return 1; + } + return 0; + } + + /* + * Re-send the signal to the proper target thread. + */ + + pthread_kill((pthread_t) threadId, sigNumber); +#endif + return 0; +} + #endif /* NOTIFIER_KQUEUE && TCL_THREADS */ #else TCL_MAC_EMPTY_FILE(unix_tclKqueueNotfy_c) diff --git a/unix/tclSelectNotfy.c b/unix/tclSelectNotfy.c index 82f2ef7..e69ffd8 100644 --- a/unix/tclSelectNotfy.c +++ b/unix/tclSelectNotfy.c @@ -148,6 +148,7 @@ static ThreadSpecificData *waitingListPtr = NULL; */ static int triggerPipe = -1; +static int otherPipe = -1; /* * The notifierMutex locks access to all of the global notifier state. @@ -164,9 +165,15 @@ static pthread_mutex_t notifierMutex = PTHREAD_MUTEX_INITIALIZER; static int notifierThreadRunning = 0; /* + * The following static flag indicates that async handlers are pending. + */ + +static int asyncPending = 0; + +/* * The notifier thread signals the notifierCV when it has finished * initializing the triggerPipe and right before the notifier thread - * terminates. + * terminates. This condition is used to deal with the signal mask, too. */ static pthread_cond_t notifierCV = PTHREAD_COND_INITIALIZER; @@ -190,6 +197,14 @@ static pthread_cond_t notifierCV = PTHREAD_COND_INITIALIZER; */ static Tcl_ThreadId notifierThread; + +/* + * Signal mask information for notifier thread. + */ + +static sigset_t notifierSigMask; +static sigset_t allSigMask; + #endif /* TCL_THREADS */ /* @@ -409,6 +424,14 @@ TclpFinalizeNotifier( "unable to join notifier thread"); } notifierThreadRunning = 0; + + /* + * If async marks are outstanding, perform actions now. + */ + if (asyncPending) { + asyncPending = 0; + TclAsyncMarkFromNotifier(); + } } } @@ -875,6 +898,61 @@ TclpWaitForEvent( /* *---------------------------------------------------------------------- * + * TclAsyncNotifier -- + * + * This procedure sets the async mark of an async handler to a + * given value, if it is called from the notifier thread. + * + * Result: + * True, when the handler will be marked, false otherwise. + * + * Side effetcs: + * The trigger pipe is written when called from the notifier + * thread. + * + *---------------------------------------------------------------------- + */ + +int +TclAsyncNotifier( + int sigNumber, /* Signal number. */ + Tcl_ThreadId threadId, /* Target thread. */ + TCL_UNUSED(ClientData), /* Notifier data. */ + int *flagPtr, /* Flag to mark. */ + int value) /* Value of mark. */ +{ +#ifdef TCL_THREADS + /* + * WARNING: + * This code most likely runs in a signal handler. Thus, + * only few async-signal-safe system calls are allowed, + * e.g. pthread_self(), sem_post(), write(). + */ + + if (pthread_equal(pthread_self(), (pthread_t) notifierThread)) { + if (notifierThreadRunning) { + *flagPtr = value; + if (!asyncPending) { + asyncPending = 1; + write(triggerPipe, "S", 1); + } + return 1; + } + return 0; + } + + /* + * Re-send the signal to the notifier thread. + */ + + pthread_kill((pthread_t) notifierThread, sigNumber); +#endif + return 0; +} + +/* + *---------------------------------------------------------------------- + * * NotifierThreadProc -- * * This routine is the initial (and only) function executed by the @@ -906,8 +984,7 @@ NotifierThreadProc( fd_set readableMask; fd_set writableMask; fd_set exceptionMask; - int i; - int fds[2], receivePipe; + int i, fds[2], receivePipe, ret; long found; struct timeval poll = {0, 0}, *timePtr; char buf[2]; @@ -917,6 +994,14 @@ NotifierThreadProc( Tcl_Panic("NotifierThreadProc: %s", "could not create trigger pipe"); } + /* + * Ticket [c6897e6e6a]. + */ + + if (fds[0] >= FD_SETSIZE || fds[1] >= FD_SETSIZE) { + Tcl_Panic("NotifierThreadProc: %s", "too many open files"); + } + receivePipe = fds[0]; if (TclUnixSetBlockingMode(receivePipe, TCL_MODE_NONBLOCKING) < 0) { @@ -942,6 +1027,7 @@ NotifierThreadProc( pthread_mutex_lock(¬ifierMutex); triggerPipe = fds[1]; + otherPipe = fds[0]; /* * Signal any threads that are waiting. @@ -1002,12 +1088,44 @@ NotifierThreadProc( } FD_SET(receivePipe, &readableMask); - if (select(numFdBits, &readableMask, &writableMask, &exceptionMask, - timePtr) == -1) { + /* + * Signals are unblocked only during select(). + */ + +#ifdef HAVE_PSELECT + { + struct timespec tspec, *tspecPtr; + + if (timePtr == NULL) { + tspecPtr = NULL; + } else { + tspecPtr = &tspec; + tspecPtr->tv_sec = timePtr->tv_sec; + tspecPtr->tv_nsec = timePtr->tv_usec * 1000; + } + ret = pselect(numFdBits, &readableMask, &writableMask, + &exceptionMask, tspecPtr, ¬ifierSigMask); + } +#else + pthread_sigmask(SIG_SETMASK, ¬ifierSigMask, NULL); + ret = select(numFdBits, &readableMask, &writableMask, &exceptionMask, + timePtr); + pthread_sigmask(SIG_BLOCK, &allSigMask, NULL); +#endif + + if (ret == -1) { /* - * Try again immediately on an error. + * In case a signal was caught during select(), + * perform work on async handlers now. */ + if (errno == EINTR && asyncPending) { + asyncPending = 0; + TclAsyncMarkFromNotifier(); + } + /* + * Try again immediately on select() error. + */ continue; } @@ -1063,6 +1181,12 @@ NotifierThreadProc( break; } } while (1); + + if (asyncPending) { + asyncPending = 0; + TclAsyncMarkFromNotifier(); + } + if ((i == 0) || (buf[0] == 'q')) { break; } @@ -1076,6 +1200,7 @@ NotifierThreadProc( close(receivePipe); pthread_mutex_lock(¬ifierMutex); triggerPipe = -1; + otherPipe = -1; pthread_cond_broadcast(¬ifierCV); pthread_mutex_unlock(¬ifierMutex); diff --git a/unix/tclUnixNotfy.c b/unix/tclUnixNotfy.c index c1f00d5..e7e70ef 100644 --- a/unix/tclUnixNotfy.c +++ b/unix/tclUnixNotfy.c @@ -350,24 +350,24 @@ AlertSingleThread( { tsdPtr->eventReady = 1; if (tsdPtr->onList) { - /* - * Remove the ThreadSpecificData structure of this thread from the - * waiting list. This prevents us from continuously spinning on - * epoll_wait until the other threads runs and services the file - * event. - */ - - if (tsdPtr->prevPtr) { + /* + * Remove the ThreadSpecificData structure of this thread from the + * waiting list. This prevents us from continuously spinning on + * epoll_wait until the other threads runs and services the file + * event. + */ + + if (tsdPtr->prevPtr) { tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr; - } else { + } else { waitingListPtr = tsdPtr->nextPtr; - } - if (tsdPtr->nextPtr) { + } + if (tsdPtr->nextPtr) { tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr; - } - tsdPtr->nextPtr = tsdPtr->prevPtr = NULL; - tsdPtr->onList = 0; - tsdPtr->pollState = 0; + } + tsdPtr->nextPtr = tsdPtr->prevPtr = NULL; + tsdPtr->onList = 0; + tsdPtr->pollState = 0; } #ifdef __CYGWIN__ PostMessageW(tsdPtr->hwnd, 1024, 0, 0); @@ -403,6 +403,10 @@ AtForkChild(void) pthread_mutex_init(¬ifierMutex, NULL); pthread_cond_init(¬ifierCV, NULL); +#ifdef NOTIFIER_SELECT + asyncPending = 0; +#endif + /* * notifierThreadRunning == 1: thread is running, (there might be data in * notifier lists) @@ -420,6 +424,10 @@ AtForkChild(void) close(triggerPipe); triggerPipe = -1; +#ifdef NOTIFIER_SELECT + close(otherPipe); + otherPipe = -1; +#endif /* * The waitingListPtr might contain event info from multiple * threads, which are invalid here, so setting it to NULL is not @@ -456,6 +464,14 @@ AtForkChild(void) } Tcl_InitNotifier(); + +#ifdef NOTIFIER_SELECT + /* + * Restart the notifier thread for signal handling. + */ + + StartNotifierThread("AtForkChild"); +#endif } #endif /* HAVE_PTHREAD_ATFORK */ #endif /* TCL_THREADS */ @@ -464,6 +480,36 @@ AtForkChild(void) /* *---------------------------------------------------------------------- * + * TclpNotifierData -- + * + * This function returns a ClientData pointer to be associated + * with a Tcl_AsyncHandler. + * + * Results: + * For the epoll and kqueue notifiers, this function returns the + * thread specific data. Otherwise NULL. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +ClientData +TclpNotifierData(void) +{ +#if defined(NOTIFIER_EPOLL) || defined(NOTIFIER_KQUEUE) + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + + return (ClientData) tsdPtr; +#else + return NULL; +#endif +} + +/* + *---------------------------------------------------------------------- + * * TclUnixWaitForFile -- * * This function waits synchronously for a file to become readable or |