calog/vendor/tcl/unix/tclKqueueNotfy.c
2026-07-04 23:22:22 -05:00

839 lines
24 KiB
C
Vendored
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* tclKqueueNotfy.c --
*
* This file contains the implementation of the kqueue()-based
* DragonFly/Free/Net/OpenBSD-specific notifier, which is the lowest-
* level part of the Tcl event loop. This file works together with
* generic/tclNotify.c.
*
* Copyright © 1995-1997 Sun Microsystems, Inc.
* Copyright © 2016 Lucio Andrés Illanes Albornoz <l.illanes@gmx.de>
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*/
#include "tclInt.h"
#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>
#include <sys/queue.h>
#include <sys/time.h>
/*
* This structure is used to keep track of the notifier info for a registered
* file.
*/
struct PlatformEventData;
typedef struct FileHandler {
int fd; /* File descriptor that this is describing a
* handler for. */
int mask; /* Mask of desired events: TCL_READABLE,
* etc. */
int readyMask; /* Mask of events that have been seen since
* the last time file handlers were invoked
* for this file. */
Tcl_FileProc *proc; /* Function to call, in the style of
* Tcl_CreateFileHandler. */
void *clientData; /* Argument to pass to proc. */
struct FileHandler *nextPtr;/* Next in list of all files we care about. */
LIST_ENTRY(FileHandler) readyNode;
/* Next/previous in list of FileHandlers asso-
* ciated with regular files (S_IFREG) that are
* ready for I/O. */
struct PlatformEventData *pedPtr;
/* Pointer to PlatformEventData associating this
* FileHandler with kevent(2) events. */
} 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).
*/
struct ThreadSpecificData;
struct PlatformEventData {
FileHandler *filePtr;
struct ThreadSpecificData *tsdPtr;
};
/*
* The following structure is what is added to the Tcl event queue when file
* handlers are ready to fire.
*/
typedef struct {
Tcl_Event header; /* Information that is standard for all
* events. */
int fd; /* File descriptor that is ready. Used to find
* the FileHandler structure for the file
* (can't point directly to the FileHandler
* structure because it could go away while
* the event is queued). */
} FileHandlerEvent;
/*
* The following static structure contains the state information for the
* kqueue based implementation of the Tcl notifier. One of these structures is
* created for each thread that is using the notifier.
*/
LIST_HEAD(PlatformReadyFileHandlerList, FileHandler);
typedef struct ThreadSpecificData {
FileHandler *firstFileHandlerPtr;
/* Pointer to head of file handler list. */
struct PlatformReadyFileHandlerList firstReadyFileHandlerPtr;
/* Pointer to head of list of FileHandlers
* associated with regular files (S_IFREG)
* that are ready for I/O. */
pthread_mutex_t notifierMutex;
/* Mutex protecting notifier termination in
* TclpFinalizeNotifier. */
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. */
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;
/*
* Forward declarations of internal functions.
*/
static void PlatformEventsControl(FileHandler *filePtr,
ThreadSpecificData *tsdPtr, int op, int isNew);
static int PlatformEventsTranslate(struct kevent *eventPtr);
static int PlatformEventsWait(struct kevent *events,
size_t numEvents, struct timeval *timePtr);
/*
* Incorporate the base notifier implementation.
*/
#include "tclUnixNotfy.c"
/*
*----------------------------------------------------------------------
*
* PlatformEventsControl --
*
* 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.
*
* Results:
* 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.)
*
*----------------------------------------------------------------------
*/
static void
PlatformEventsControl(
FileHandler *filePtr,
ThreadSpecificData *tsdPtr,
int op,
int isNew)
{
int numChanges;
struct kevent changeList[2];
struct PlatformEventData *newPedPtr;
Tcl_StatBuf fdStat;
if (isNew) {
newPedPtr = (struct PlatformEventData *)
Tcl_Alloc(sizeof(struct PlatformEventData));
newPedPtr->filePtr = filePtr;
newPedPtr->tsdPtr = tsdPtr;
filePtr->pedPtr = newPedPtr;
}
/*
* 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 (TclOSfstat(filePtr->fd, &fdStat) == -1) {
Tcl_Panic("fstat: %s", strerror(errno));
} else if ((fdStat.st_mode & S_IFMT) == S_IFREG
|| (fdStat.st_mode & S_IFMT) == S_IFDIR
|| (fdStat.st_mode & S_IFMT) == S_IFLNK) {
switch (op) {
case EV_ADD:
if (isNew) {
LIST_INSERT_HEAD(&tsdPtr->firstReadyFileHandlerPtr, filePtr,
readyNode);
}
break;
case EV_DELETE:
LIST_REMOVE(filePtr, readyNode);
break;
}
return;
}
numChanges = 0;
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);
numChanges++;
}
if (filePtr->mask & TCL_WRITABLE) {
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) {
Tcl_Panic("kevent: %s", strerror(errno));
}
}
break;
case EV_DELETE:
/*
* 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);
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);
if ((kevent(tsdPtr->eventsFd, changeList, 1, NULL, 0, NULL) == -1)
&& (errno != ENOENT)) {
Tcl_Panic("kevent: %s", strerror(errno));
}
break;
}
}
/*
*----------------------------------------------------------------------
*
* TclpFinalizeNotifier --
*
* 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.
*
* Side effects:
* While tsdPtr->notifierMutex is held:
* The per-thread pipe(2) fds are closed, if non-zero, and set to -1.
* The per-thread kqueue(2) fd is closed, if non-zero, and set to 0.
* The per-thread kevent structs are freed, if any, and set to 0.
*
* tsdPtr->notifierMutex is destroyed.
*
*----------------------------------------------------------------------
*/
void
TclpFinalizeNotifier(
TCL_UNUSED(void *))
{
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
pthread_mutex_lock(&tsdPtr->notifierMutex);
if (tsdPtr->triggerPipe[0]) {
close(tsdPtr->triggerPipe[0]);
tsdPtr->triggerPipe[0] = -1;
}
if (tsdPtr->triggerPipe[1]) {
close(tsdPtr->triggerPipe[1]);
tsdPtr->triggerPipe[1] = -1;
}
if (tsdPtr->eventsFd > 0) {
close(tsdPtr->eventsFd);
tsdPtr->eventsFd = 0;
}
if (tsdPtr->readyEvents) {
Tcl_Free(tsdPtr->readyEvents);
tsdPtr->maxReadyEvents = 0;
}
pthread_mutex_unlock(&tsdPtr->notifierMutex);
if ((errno = pthread_mutex_destroy(&tsdPtr->notifierMutex))) {
Tcl_Panic("pthread_mutex_destroy: %s", strerror(errno));
}
}
/*
*----------------------------------------------------------------------
*
* TclpInitNotifier --
*
* Initializes the platform specific notifier state.
*
* 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:
* Returns a handle to the notifier state for this thread.
*
* 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.
*
*----------------------------------------------------------------------
*/
void *
TclpInitNotifier(void)
{
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
int i, fdFl;
FileHandler *filePtr;
errno = pthread_mutex_init(&tsdPtr->notifierMutex, NULL);
if (errno) {
Tcl_Panic("Tcl_InitNotifier: %s", "could not create mutex");
}
if (pipe(tsdPtr->triggerPipe) != 0) {
Tcl_Panic("Tcl_InitNotifier: %s", "could not create trigger pipe");
} else for (i = 0; i < 2; i++) {
if (fcntl(tsdPtr->triggerPipe[i], F_SETFD, FD_CLOEXEC) == -1) {
Tcl_Panic("fcntl: %s", strerror(errno));
} else {
fdFl = fcntl(tsdPtr->triggerPipe[i], F_GETFL);
fdFl |= O_NONBLOCK;
}
if (fcntl(tsdPtr->triggerPipe[i], F_SETFL, fdFl) == -1) {
Tcl_Panic("fcntl: %s", strerror(errno));
}
}
if ((tsdPtr->eventsFd = kqueue()) == -1) {
Tcl_Panic("kqueue: %s", strerror(errno));
} else if (fcntl(tsdPtr->eventsFd, F_SETFD, FD_CLOEXEC) == -1) {
Tcl_Panic("fcntl: %s", strerror(errno));
}
filePtr = (FileHandler *) Tcl_Alloc(sizeof(FileHandler));
filePtr->fd = tsdPtr->triggerPipe[0];
filePtr->mask = TCL_READABLE;
PlatformEventsControl(filePtr, tsdPtr, EV_ADD, 1);
if (!tsdPtr->readyEvents) {
tsdPtr->maxReadyEvents = 512;
tsdPtr->readyEvents = (struct kevent *) Tcl_Alloc(
tsdPtr->maxReadyEvents * sizeof(tsdPtr->readyEvents[0]));
}
LIST_INIT(&tsdPtr->firstReadyFileHandlerPtr);
return tsdPtr;
}
/*
*----------------------------------------------------------------------
*
* PlatformEventsTranslate --
*
* This function translates the platform-specific mask of returned
* events in eventPtr to a mask of TCL_* bits.
*
* Results:
* Returns the translated mask.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static int
PlatformEventsTranslate(
struct kevent *eventPtr)
{
int mask;
mask = 0;
if (eventPtr->filter == EVFILT_READ) {
mask |= TCL_READABLE;
if (eventPtr->flags & EV_ERROR) {
mask |= TCL_EXCEPTION;
}
}
if (eventPtr->filter == EVFILT_WRITE) {
mask |= TCL_WRITABLE;
if (eventPtr->flags & EV_ERROR) {
mask |= TCL_EXCEPTION;
}
}
return mask;
}
/*
*----------------------------------------------------------------------
*
* PlatformEventsWait --
*
* 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.
*
* 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.
*
*----------------------------------------------------------------------
*/
static int
PlatformEventsWait(
struct kevent *events,
size_t numEvents,
struct timeval *timePtr)
{
int numFound;
struct timeval tv0, tv1, tv_delta;
struct timespec timeout, *timeoutPtr;
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
/*
* 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) {
timeoutPtr = NULL;
} else if (!timePtr->tv_sec && !timePtr->tv_usec) {
timeout.tv_sec = 0;
timeout.tv_nsec = 0;
timeoutPtr = &timeout;
} else {
timeout.tv_sec = timePtr->tv_sec;
timeout.tv_nsec = timePtr->tv_usec * 1000;
timeoutPtr = &timeout;
}
/*
* 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(&tv0, NULL);
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);
} else {
timePtr->tv_sec = 0;
timePtr->tv_usec = 0;
}
}
if (tsdPtr->asyncPending) {
tsdPtr->asyncPending = 0;
TclAsyncMarkFromNotifier();
}
return numFound;
}
/*
*----------------------------------------------------------------------
*
* TclpCreateFileHandler --
*
* This function registers a file handler with the kqueue notifier
* of the thread of the caller.
*
* Results:
* None.
*
* Side effects:
* Creates a new file handler structure.
* PlatformEventsControl() is called for the new file handler structure.
*
*----------------------------------------------------------------------
*/
void
TclpCreateFileHandler(
int fd, /* Handle of stream to watch. */
int mask, /* OR'ed combination of TCL_READABLE,
* TCL_WRITABLE, and TCL_EXCEPTION: indicates
* conditions under which proc should be
* called. */
Tcl_FileProc *proc, /* Function to call for each selected
* event. */
void *clientData) /* Arbitrary data to pass to proc. */
{
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
FileHandler *filePtr = LookUpFileHandler(tsdPtr, fd, NULL);
int isNew = (filePtr == NULL);
if (isNew) {
filePtr = (FileHandler *) Tcl_Alloc(sizeof(FileHandler));
filePtr->fd = fd;
filePtr->readyMask = 0;
filePtr->nextPtr = tsdPtr->firstFileHandlerPtr;
tsdPtr->firstFileHandlerPtr = filePtr;
}
filePtr->proc = proc;
filePtr->clientData = clientData;
filePtr->mask = mask;
PlatformEventsControl(filePtr, tsdPtr, EV_ADD, isNew);
}
/*
*----------------------------------------------------------------------
*
* TclpDeleteFileHandler --
*
* Cancel a previously-arranged callback arrangement for a file on the
* kqueue of the thread of the caller.
*
* Results:
* None.
*
* Side effects:
* If a callback was previously registered on file, remove it.
* PlatformEventsControl() is called for the file handler structure.
* The PlatformEventData struct associated with the new file handler
* structure is freed.
*
*----------------------------------------------------------------------
*/
void
TclpDeleteFileHandler(
int fd) /* Stream id for which to remove callback
* function. */
{
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
FileHandler *filePtr, *prevPtr;
/*
* Find the entry for the given file (and return if there isn't one).
*/
filePtr = LookUpFileHandler(tsdPtr, fd, &prevPtr);
if (filePtr == NULL) {
return;
}
/*
* Update the check masks for this file.
*/
PlatformEventsControl(filePtr, tsdPtr, EV_DELETE, 0);
if (filePtr->pedPtr) {
Tcl_Free(filePtr->pedPtr);
}
/*
* Clean up information in the callback record.
*/
if (prevPtr == NULL) {
tsdPtr->firstFileHandlerPtr = filePtr->nextPtr;
} else {
prevPtr->nextPtr = filePtr->nextPtr;
}
Tcl_Free(filePtr);
}
/*
*----------------------------------------------------------------------
*
* TclpWaitForEvent --
*
* This function is called by Tcl_DoOneEvent to wait for new events on
* the message queue. If the block time is 0, then TclpWaitForEvent just
* polls without blocking.
*
* The waiting logic is implemented in PlatformEventsWait.
*
* Results:
* Returns -1 if PlatformEventsWait() would block forever, otherwise
* returns 0.
*
* Side effects:
* Queues file events that are detected by PlatformEventsWait().
*
*----------------------------------------------------------------------
*/
int
TclpWaitForEvent(
const Tcl_Time *timePtr) /* Maximum block time, or NULL. */
{
FileHandler *filePtr;
int mask;
Tcl_Time vTime;
struct timeval timeout, *timeoutPtr;
/* Impl. notes: timeout & timeoutPtr are used
* if, and only if threads are not enabled.
* They are the arguments for the regular
* epoll_wait() used when the core is not
* thread-enabled. */
int numFound, numEvent;
struct PlatformEventData *pedPtr;
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
int numQueued;
ssize_t i;
char buf[1];
/*
* Set up the timeout structure. Note that if there are no events to check
* for, we return with a negative result rather than blocking forever.
*/
if (timePtr != NULL) {
/*
* TIP #233 (Virtualized Time). Is virtual time in effect? And do we
* actually have something to scale? If yes to both then we call the
* handler to do this scaling.
*/
if (timePtr->sec != 0 || timePtr->usec != 0) {
vTime = *timePtr;
TclScaleTime(&vTime);
timePtr = &vTime;
}
timeout.tv_sec = timePtr->sec;
timeout.tv_usec = timePtr->usec;
timeoutPtr = &timeout;
} else {
timeoutPtr = NULL;
}
/*
* 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.
*
* 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 possibly
* blocking) on PlatformEventsWait().
*/
numQueued = 0;
LIST_FOREACH(filePtr, &tsdPtr->firstReadyFileHandlerPtr, readyNode) {
mask = 0;
if (filePtr->mask & TCL_READABLE) {
mask |= TCL_READABLE;
}
if (filePtr->mask & TCL_WRITABLE) {
mask |= TCL_WRITABLE;
}
/*
* Don't bother to queue an event if the mask was previously non-zero
* since an event must still be on the queue.
*/
if (filePtr->readyMask == 0) {
FileHandlerEvent *fileEvPtr = (FileHandlerEvent *)
Tcl_Alloc(sizeof(FileHandlerEvent));
fileEvPtr->header.proc = FileHandlerEventProc;
fileEvPtr->fd = filePtr->fd;
Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL);
numQueued++;
}
filePtr->readyMask = mask;
}
/*
* If any events were queued in the above loop, force PlatformEventsWait()
* to poll as there already are events that need to be processed at this
* point.
*/
if (numQueued) {
timeout.tv_sec = 0;
timeout.tv_usec = 0;
timeoutPtr = &timeout;
}
/*
* Wait or poll for new events, queue Tcl events for the FileHandlers
* corresponding to them, and update the FileHandlers' mask of events of
* interest registered by the last call to Tcl_CreateFileHandler().
*
* Events for the trigger pipe are processed here in order to facilitate
* inter-thread IPC. If another thread intends to wake up this thread
* whilst it's blocking on PlatformEventsWait(), it write(2)s to the other
* end of the pipe (see Tcl_AlertNotifier(),) which in turn will cause
* PlatformEventsWait() to return immediately.
*/
numFound = PlatformEventsWait(tsdPtr->readyEvents,
tsdPtr->maxReadyEvents, timeoutPtr);
for (numEvent = 0; numEvent < numFound; numEvent++) {
pedPtr = (struct PlatformEventData *)
tsdPtr->readyEvents[numEvent].udata;
filePtr = pedPtr->filePtr;
mask = PlatformEventsTranslate(&tsdPtr->readyEvents[numEvent]);
if (filePtr->fd == tsdPtr->triggerPipe[0]) {
i = read(tsdPtr->triggerPipe[0], buf, 1);
if ((i == -1) && (errno != EAGAIN)) {
Tcl_Panic("Tcl_WaitForEvent: read from %p->triggerPipe: %s",
tsdPtr, strerror(errno));
}
continue;
}
if (!mask) {
continue;
}
/*
* Don't bother to queue an event if the mask was previously non-zero
* since an event must still be on the queue.
*/
if (filePtr->readyMask == 0) {
FileHandlerEvent *fileEvPtr = (FileHandlerEvent *)
Tcl_Alloc(sizeof(FileHandlerEvent));
fileEvPtr->header.proc = FileHandlerEventProc;
fileEvPtr->fd = filePtr->fd;
Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL);
}
filePtr->readyMask |= mask;
}
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. */
void *clientData, /* Notifier data. */
int *flagPtr, /* Flag to mark. */
int value) /* Value of mark. */
{
#if 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);
#else
(void)sigNumber;
(void)threadId;
(void)clientData;
(void)flagPtr;
(void)value;
#endif
return 0;
}
#endif /* NOTIFIER_KQUEUE && TCL_THREADS */
#else
TCL_MAC_EMPTY_FILE(unix_tclKqueueNotfy_c)
#endif /* !HAVE_COREFOUNDATION */
/*
* Local Variables:
* mode: c
* c-basic-offset: 4
* fill-column: 78
* End:
*/