summaryrefslogtreecommitdiffstats
path: root/unix
diff options
context:
space:
mode:
authorjan.nijtmans <nijtmans@users.sourceforge.net>2021-08-17 16:16:29 (GMT)
committerjan.nijtmans <nijtmans@users.sourceforge.net>2021-08-17 16:16:29 (GMT)
commit5f83d31eceac5d0a48f75293f02725a59168beaf (patch)
tree088ba5712739eee4f6fd1ff277de958420af7021 /unix
parent58502bda0b655dcb2b8e619c1cce4ba99f294aad (diff)
downloadtcl-5f83d31eceac5d0a48f75293f02725a59168beaf.zip
tcl-5f83d31eceac5d0a48f75293f02725a59168beaf.tar.gz
tcl-5f83d31eceac5d0a48f75293f02725a59168beaf.tar.bz2
tip#511 proposed implementation
Diffstat (limited to 'unix')
-rwxr-xr-xunix/configure35
-rw-r--r--unix/configure.ac7
-rw-r--r--unix/tclConfig.h.in3
-rw-r--r--unix/tclEpollNotfy.c59
-rw-r--r--unix/tclKqueueNotfy.c71
-rw-r--r--unix/tclSelectNotfy.c137
-rw-r--r--unix/tclUnixNotfy.c76
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(&notifierMutex);
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, &notifierSigMask);
+ }
+#else
+ pthread_sigmask(SIG_SETMASK, &notifierSigMask, 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(&notifierMutex);
triggerPipe = -1;
+ otherPipe = -1;
pthread_cond_broadcast(&notifierCV);
pthread_mutex_unlock(&notifierMutex);
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(&notifierMutex, NULL);
pthread_cond_init(&notifierCV, 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