summaryrefslogtreecommitdiffstats
path: root/unix/tclKqueueNotfy.c
diff options
context:
space:
mode:
authorjan.nijtmans <nijtmans@users.sourceforge.net>2018-10-22 18:04:44 (GMT)
committerjan.nijtmans <nijtmans@users.sourceforge.net>2018-10-22 18:04:44 (GMT)
commitcd4cb2999b77e8378e1198c6dd6aa65327d76d7a (patch)
tree418241b005c396ed6105cf8ca8be15cb7025a1aa /unix/tclKqueueNotfy.c
parent817a97238eb4d10f5fbb213987776efb3f86adc9 (diff)
parent251798717672709f93ba749470e08c8541ea9bcf (diff)
downloadtcl-cd4cb2999b77e8378e1198c6dd6aa65327d76d7a.zip
tcl-cd4cb2999b77e8378e1198c6dd6aa65327d76d7a.tar.gz
tcl-cd4cb2999b77e8378e1198c6dd6aa65327d76d7a.tar.bz2
Merge 8.7
Diffstat (limited to 'unix/tclKqueueNotfy.c')
-rw-r--r--unix/tclKqueueNotfy.c231
1 files changed, 121 insertions, 110 deletions
diff --git a/unix/tclKqueueNotfy.c b/unix/tclKqueueNotfy.c
index bea1b30..bbb8061 100644
--- a/unix/tclKqueueNotfy.c
+++ b/unix/tclKqueueNotfy.c
@@ -14,10 +14,10 @@
*/
#include "tclInt.h"
-#if defined(NOTIFIER_KQUEUE) && TCL_THREADS
-
#ifndef HAVE_COREFOUNDATION /* Darwin/Mac OS X CoreFoundation notifier is
* in tclMacOSXNotify.c */
+#if defined(NOTIFIER_KQUEUE) && TCL_THREADS
+
#include <signal.h>
#include <sys/types.h>
#include <sys/event.h>
@@ -51,9 +51,9 @@ typedef struct FileHandler {
} FileHandler;
/*
- * The following structure associates a FileHandler and the thread that owns it
- * with the file descriptors of interest and their event masks passed to kevent(2)
- * and their corresponding event(s) returned by kevent(2).
+ * The following structure associates a FileHandler and the thread that owns
+ * it with the file descriptors of interest and their event masks passed to
+ * kevent(2) and their corresponding event(s) returned by kevent(2).
*/
struct ThreadSpecificData;
@@ -96,20 +96,27 @@ typedef struct ThreadSpecificData {
* PlatformEventsFinalize. */
int triggerPipe[2]; /* pipe(2) used by other threads to wake
* up this thread for inter-thread IPC. */
- int eventsFd; /* kqueue(2) file descriptor used to wait for fds. */
+ int eventsFd; /* kqueue(2) file descriptor used to wait for
+ * fds. */
struct kevent *readyEvents; /* Pointer to at most maxReadyEvents events
* returned by kevent(2). */
size_t maxReadyEvents; /* Count of kevents in readyEvents. */
} ThreadSpecificData;
static Tcl_ThreadDataKey dataKey;
-
-static void PlatformEventsControl(FileHandler *filePtr, ThreadSpecificData *tsdPtr, int op, int isNew);
-static void PlatformEventsFinalize(void);
-static void PlatformEventsInit(void);
-static int PlatformEventsTranslate(struct kevent *eventPtr);
-static int PlatformEventsWait(struct kevent *events, size_t numEvents, struct timeval *timePtr);
-
+
+/*
+ * Forward declarations of internal functions.
+ */
+
+static void PlatformEventsControl(FileHandler *filePtr,
+ ThreadSpecificData *tsdPtr, int op, int isNew);
+static void PlatformEventsFinalize(void);
+static void PlatformEventsInit(void);
+static int PlatformEventsTranslate(struct kevent *eventPtr);
+static int PlatformEventsWait(struct kevent *events,
+ size_t numEvents, struct timeval *timePtr);
+
#include "tclUnixNotfy.c"
/*
@@ -180,6 +187,7 @@ Tcl_FinalizeNotifier(
* This function registers interest for the file descriptor and the mask
* of TCL_* bits associated with filePtr on the kqueue file descriptor
* associated with tsdPtr.
+ *
* Future calls to kevent will return filePtr and tsdPtr alongside with
* the event registered here via the PlatformEventData struct.
*
@@ -187,26 +195,26 @@ Tcl_FinalizeNotifier(
* None.
*
* Side effects:
- * If adding a new file descriptor, a PlatformEventData struct will be
- * allocated and associated with filePtr.
- * fstat is called on the file descriptor; if it is associated with
- * a regular file (S_IFREG,) filePtr is considered to be ready for I/O
- * and added to or deleted from the corresponding list in tsdPtr.
- * If it is not associated with a regular file, the file descriptor is
- * added, modified concerning its mask of events of interest, or deleted
- * from the epoll file descriptor of the calling thread.
- * If deleting a file descriptor, kevent(2) is called twice specifying
- * EVFILT_READ first and then EVFILT_WRITE (see note below.)
+ * - If adding a new file descriptor, a PlatformEventData struct will be
+ * allocated and associated with filePtr.
+ * - fstat is called on the file descriptor; if it is associated with
+ * a regular file (S_IFREG,) filePtr is considered to be ready for I/O
+ * and added to or deleted from the corresponding list in tsdPtr.
+ * - If it is not associated with a regular file, the file descriptor is
+ * added, modified concerning its mask of events of interest, or
+ * deleted from the epoll file descriptor of the calling thread.
+ * - If deleting a file descriptor, kevent(2) is called twice specifying
+ * EVFILT_READ first and then EVFILT_WRITE (see note below.)
*
*----------------------------------------------------------------------
*/
void
PlatformEventsControl(
- FileHandler *filePtr,
- ThreadSpecificData *tsdPtr,
- int op,
- int isNew)
+ FileHandler *filePtr,
+ ThreadSpecificData *tsdPtr,
+ int op,
+ int isNew)
{
int numChanges;
struct kevent changeList[2];
@@ -221,11 +229,11 @@ PlatformEventsControl(
}
/*
- * N.B. As discussed in Tcl_WaitForEvent(), kqueue(2) does not repro-
- * duce the `always ready' {select,poll}(2) behaviour for regular
- * files (S_IFREG) prior to FreeBSD 11.0-RELEASE. Therefore, file-
- * Ptr is in these cases simply added or deleted from the list of
- * FileHandlers associated with regular files belonging to tsdPtr.
+ * N.B. As discussed in Tcl_WaitForEvent(), kqueue(2) does not reproduce
+ * the `always ready' {select,poll}(2) behaviour for regular files
+ * (S_IFREG) prior to FreeBSD 11.0-RELEASE. Therefore, filePtr is in these
+ * cases simply added or deleted from the list of FileHandlers associated
+ * with regular files belonging to tsdPtr.
*/
if (fstat(filePtr->fd, &fdStat) == -1) {
@@ -234,7 +242,8 @@ PlatformEventsControl(
switch (op) {
case EV_ADD:
if (isNew) {
- LIST_INSERT_HEAD(&tsdPtr->firstReadyFileHandlerPtr, filePtr, readyNode);
+ LIST_INSERT_HEAD(&tsdPtr->firstReadyFileHandlerPtr, filePtr,
+ readyNode);
}
break;
case EV_DELETE:
@@ -248,38 +257,41 @@ PlatformEventsControl(
switch (op) {
case EV_ADD:
if (filePtr->mask & (TCL_READABLE | TCL_EXCEPTION)) {
- EV_SET(&changeList[numChanges], (uintptr_t)filePtr->fd, EVFILT_READ,
- op, 0, 0, filePtr->pedPtr);
+ EV_SET(&changeList[numChanges], (uintptr_t)filePtr->fd,
+ EVFILT_READ, op, 0, 0, filePtr->pedPtr);
numChanges++;
}
if (filePtr->mask & TCL_WRITABLE) {
- EV_SET(&changeList[numChanges], (uintptr_t)filePtr->fd, EVFILT_WRITE,
- op, 0, 0, filePtr->pedPtr);
+ EV_SET(&changeList[numChanges], (uintptr_t)filePtr->fd,
+ EVFILT_WRITE, op, 0, 0, filePtr->pedPtr);
numChanges++;
}
if (numChanges) {
- if (kevent(tsdPtr->eventsFd, changeList, numChanges, NULL, 0, NULL) == -1) {
+ if (kevent(tsdPtr->eventsFd, changeList, numChanges, NULL, 0,
+ NULL) == -1) {
Tcl_Panic("kevent: %s", strerror(errno));
}
}
break;
case EV_DELETE:
/*
- * N.B. kqueue(2) has separate filters for readability and writabi-
- * lity fd events. We therefore need to ensure that fds are
- * ompletely removed from the kqueue(2) fd when deleting.
- * This is exacerbated by changes to filePtr->mask w/o calls
- * to PlatforEventsControl() after e.g. an exec(3) in a child
- * process.
- * As one of these calls can fail, two separate kevent(2) calls
- * are made for EVFILT_{READ,WRITE}.
+ * N.B. kqueue(2) has separate filters for readability and writability
+ * fd events. We therefore need to ensure that fds are ompletely
+ * removed from the kqueue(2) fd when deleting. This is exacerbated
+ * by changes to filePtr->mask w/o calls to PlatforEventsControl()
+ * after e.g. an exec(3) in a child process.
+ *
+ * As one of these calls can fail, two separate kevent(2) calls are
+ * made for EVFILT_{READ,WRITE}.
*/
- EV_SET(&changeList[0], (uintptr_t)filePtr->fd, EVFILT_READ, op, 0, 0, NULL);
+ EV_SET(&changeList[0], (uintptr_t)filePtr->fd, EVFILT_READ, op, 0, 0,
+ NULL);
if ((kevent(tsdPtr->eventsFd, changeList, 1, NULL, 0, NULL) == -1)
&& (errno != ENOENT)) {
Tcl_Panic("kevent: %s", strerror(errno));
}
- EV_SET(&changeList[0], (uintptr_t)filePtr->fd, EVFILT_WRITE, op, 0, 0, NULL);
+ EV_SET(&changeList[0], (uintptr_t)filePtr->fd, EVFILT_WRITE, op, 0, 0,
+ NULL);
if ((kevent(tsdPtr->eventsFd, changeList, 1, NULL, 0, NULL) == -1)
&& (errno != ENOENT)) {
Tcl_Panic("kevent: %s", strerror(errno));
@@ -293,10 +305,10 @@ PlatformEventsControl(
*
* PlatformEventsFinalize --
*
- * This function closes the pipe and the kqueue file descriptors
- * and frees the kevent structs owned by the thread of the caller.
- * The above operations are protected by tsdPtr->notifierMutex, which
- * is destroyed thereafter.
+ * This function closes the pipe and the kqueue file descriptors and
+ * frees the kevent structs owned by the thread of the caller. The above
+ * operations are protected by tsdPtr->notifierMutex, which is destroyed
+ * thereafter.
*
* Results:
* None.
@@ -314,7 +326,7 @@ PlatformEventsControl(
void
PlatformEventsFinalize(
- void)
+ void)
{
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
@@ -346,31 +358,30 @@ PlatformEventsFinalize(
*
* PlatformEventsInit --
*
- * This function abstracts creating a kqueue fd via the kqueue
- * system call and allocating memory for the kevents structs in
- * tsdPtr for the thread of the caller.
+ * This function abstracts creating a kqueue fd via the kqueue system
+ * call and allocating memory for the kevents structs in tsdPtr for the
+ * thread of the caller.
*
* Results:
* None.
*
* Side effects:
* The following per-thread entities are initialised:
- * notifierMutex is initialised.
- * The pipe(2) is created; fcntl(2) is called on both fds to set
- * FD_CLOEXEC and O_NONBLOCK.
- * The kqueue(2) fd is created; fcntl(2) is called on it to set
- * FD_CLOEXEC.
- * A FileHandler struct is allocated and initialised for the event-
- * fd(2), registering interest for TCL_READABLE on it via Platform-
- * EventsControl().
- * readyEvents and maxReadyEvents are initialised with 512 kevents.
-
+ * - notifierMutex is initialised.
+ * - The pipe(2) is created; fcntl(2) is called on both fds to set
+ * FD_CLOEXEC and O_NONBLOCK.
+ * - The kqueue(2) fd is created; fcntl(2) is called on it to set
+ * FD_CLOEXEC.
+ * - A FileHandler struct is allocated and initialised for the event-
+ * fd(2), registering interest for TCL_READABLE on it via Platform-
+ * EventsControl().
+ * - readyEvents and maxReadyEvents are initialised with 512 kevents.
+ *
*----------------------------------------------------------------------
*/
void
-PlatformEventsInit(
- void)
+PlatformEventsInit(void)
{
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
int i, fdFl;
@@ -404,8 +415,8 @@ PlatformEventsInit(
PlatformEventsControl(filePtr, tsdPtr, EV_ADD, 1);
if (!tsdPtr->readyEvents) {
tsdPtr->maxReadyEvents = 512;
- tsdPtr->readyEvents = Tcl_Alloc(tsdPtr->maxReadyEvents
- * sizeof(tsdPtr->readyEvents[0]));
+ tsdPtr->readyEvents = Tcl_Alloc(
+ tsdPtr->maxReadyEvents * sizeof(tsdPtr->readyEvents[0]));
}
LIST_INIT(&tsdPtr->firstReadyFileHandlerPtr);
}
@@ -429,7 +440,7 @@ PlatformEventsInit(
int
PlatformEventsTranslate(
- struct kevent *eventPtr)
+ struct kevent *eventPtr)
{
int mask;
@@ -454,20 +465,20 @@ PlatformEventsTranslate(
*
* PlatformEventsWait --
*
- * This function abstracts waiting for I/O events via the kevent
- * system call.
+ * This function abstracts waiting for I/O events via the kevent system
+ * call.
*
* Results:
* Returns -1 if kevent failed. Returns 0 if polling and if no events
- * became available whilst polling. Returns a pointer to and the count
- * of all returned events in all other cases.
+ * became available whilst polling. Returns a pointer to and the count of
+ * all returned events in all other cases.
*
* Side effects:
- * gettimeofday(2), kevent(2), and gettimeofday(2) are called,
- * in the specified order.
- * If timePtr specifies a positive value, it is updated to reflect
- * the amount of time that has passed; if its value would {under,
- * over}flow, it is set to zero.
+ * gettimeofday(2), kevent(2), and gettimeofday(2) are called, in the
+ * specified order.
+ * If timePtr specifies a positive value, it is updated to reflect the
+ * amount of time that has passed; if its value would {under, over}flow,
+ * it is set to zero.
*
*----------------------------------------------------------------------
*/
@@ -485,9 +496,9 @@ PlatformEventsWait(
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
/*
- * If timePtr is NULL, kevent(2) will wait indefinitely. If it speci-
- * fies a timeout of {0,0}, kevent(2) will poll. Otherwise, the time-
- * out will simply be converted to a timespec.
+ * If timePtr is NULL, kevent(2) will wait indefinitely. If it specifies a
+ * timeout of {0,0}, kevent(2) will poll. Otherwise, the timeout will
+ * simply be converted to a timespec.
*/
if (!timePtr) {
@@ -504,20 +515,21 @@ PlatformEventsWait(
/*
* Call (and possibly block on) kevent(2) and substract the delta of
- * gettimeofday(2) before and after the call from timePtr if the latter
- * is not NULL. Return the number of events returned by kevent(2).
+ * gettimeofday(2) before and after the call from timePtr if the latter is
+ * not NULL. Return the number of events returned by kevent(2).
*/
gettimeofday(&tv0, NULL);
- numFound = kevent(tsdPtr->eventsFd, NULL, 0, events, (int)numEvents, timeoutPtr);
+ numFound = kevent(tsdPtr->eventsFd, NULL, 0, events, (int) numEvents,
+ timeoutPtr);
gettimeofday(&tv1, NULL);
if (timePtr && (timePtr->tv_sec && timePtr->tv_usec)) {
timersub(&tv1, &tv0, &tv_delta);
if (!timercmp(&tv_delta, timePtr, >)) {
- timersub(timePtr, &tv_delta, timePtr);
+ timersub(timePtr, &tv_delta, timePtr);
} else {
- timePtr->tv_sec = 0;
- timePtr->tv_usec = 0;
+ timePtr->tv_sec = 0;
+ timePtr->tv_usec = 0;
}
}
return numFound;
@@ -590,8 +602,8 @@ Tcl_CreateFileHandler(
*
* Tcl_DeleteFileHandler --
*
- * Cancel a previously-arranged callback arrangement for a file on
- * the kqueue of the thread of the caller.
+ * Cancel a previously-arranged callback arrangement for a file on the
+ * kqueue of the thread of the caller.
*
* Results:
* None.
@@ -661,6 +673,7 @@ Tcl_DeleteFileHandler(
* This function is called by Tcl_DoOneEvent to wait for new events on
* the message queue. If the block time is 0, then Tcl_WaitForEvent just
* polls without blocking.
+ *
* The waiting logic is implemented in PlatformEventsWait.
*
* Results:
@@ -726,11 +739,13 @@ Tcl_WaitForEvent(
* Walk the list of FileHandlers associated with regular files
* (S_IFREG) belonging to tsdPtr, queue Tcl events for them, and
* update their mask of events of interest.
+ *
* kqueue(2), unlike epoll(7), does support regular files, but
- * EVFILT_READ only `[r]eturns when the file pointer is not at
- * the end of file' as opposed to unconditionally. While FreeBSD
- * 11.0-RELEASE adds support for this mode (NOTE_FILE_POLL,) this
- * is not used for reasons of compatibility.
+ * EVFILT_READ only `[r]eturns when the file pointer is not at the end
+ * of file' as opposed to unconditionally. While FreeBSD 11.0-RELEASE
+ * adds support for this mode (NOTE_FILE_POLL,) this is not used for
+ * reasons of compatibility.
+ *
* Therefore, the behaviour of {select,poll}(2) is simply simulated
* here: fds associated with regular files are added to this list by
* PlatformEventsControl() and processed here before calling (and
@@ -754,7 +769,7 @@ Tcl_WaitForEvent(
if (filePtr->readyMask == 0) {
FileHandlerEvent *fileEvPtr =
- Tcl_Alloc(sizeof(FileHandlerEvent));
+ Tcl_Alloc(sizeof(FileHandlerEvent));
fileEvPtr->header.proc = FileHandlerEventProc;
fileEvPtr->fd = filePtr->fd;
@@ -788,22 +803,19 @@ Tcl_WaitForEvent(
* cause PlatformEventsWait() to return immediately.
*/
- numFound = PlatformEventsWait(tsdPtr->readyEvents, tsdPtr->maxReadyEvents, timeoutPtr);
+ numFound = PlatformEventsWait(tsdPtr->readyEvents,
+ tsdPtr->maxReadyEvents, timeoutPtr);
for (numEvent = 0; numEvent < numFound; numEvent++) {
- pedPtr = (struct PlatformEventData *)tsdPtr->readyEvents[numEvent].udata;
+ pedPtr = (struct PlatformEventData *)
+ tsdPtr->readyEvents[numEvent].udata;
filePtr = pedPtr->filePtr;
mask = PlatformEventsTranslate(&tsdPtr->readyEvents[numEvent]);
if (filePtr->fd == tsdPtr->triggerPipe[0]) {
- do {
- i = read(tsdPtr->triggerPipe[0], buf, 1);
- if ((i == -1) && (errno != EAGAIN)) {
- Tcl_Panic("Tcl_WaitForEvent: "
- "read from %p->triggerPipe: %s",
- (void *)tsdPtr, strerror(errno));
- } else {
- break;
- }
- } while (1);
+ i = read(tsdPtr->triggerPipe[0], buf, 1);
+ if ((i == -1) && (errno != EAGAIN)) {
+ Tcl_Panic("Tcl_WaitForEvent: read from %p->triggerPipe: %s",
+ (void *) tsdPtr, strerror(errno));
+ }
continue;
}
if (!mask) {
@@ -829,9 +841,8 @@ Tcl_WaitForEvent(
}
}
+#endif /* NOTIFIER_KQUEUE && TCL_THREADS */
#endif /* !HAVE_COREFOUNDATION */
-
-#endif /* NOTIFIER_KQUEUE */
/*
* Local Variables: