diff options
Diffstat (limited to 'unix/tclKqueueNotfy.c')
| -rw-r--r-- | unix/tclKqueueNotfy.c | 231 |
1 files changed, 121 insertions, 110 deletions
diff --git a/unix/tclKqueueNotfy.c b/unix/tclKqueueNotfy.c index 6d685ad..99d794e 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 = ckalloc(tsdPtr->maxReadyEvents - * sizeof(tsdPtr->readyEvents[0])); + tsdPtr->readyEvents = ckalloc( + 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 = - ckalloc(sizeof(FileHandlerEvent)); + ckalloc(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: |
