summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog15
-rw-r--r--generic/tclInt.h4
-rw-r--r--generic/tclNotify.c41
-rw-r--r--generic/tclStubInit.c25
-rw-r--r--macosx/tclMacOSXNotify.c796
-rw-r--r--unix/tclUnixNotfy.c679
-rw-r--r--win/tclWinNotify.c442
7 files changed, 1014 insertions, 988 deletions
diff --git a/ChangeLog b/ChangeLog
index 57dbc3d..9b2270f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,8 +1,17 @@
+2008-04-16 Daniel Steffen <das@users.sourceforge.net>
+
+ * generic/tclInt.h: revise Tcl_SetNotifier() to use a
+ * generic/tclNotify.c: module-scope hooks table instead of
+ * generic/tclStubInit.c: runtime stubs-table modification;
+ * macosx/tclMacOSXNotify.c: ensure all hookable notifier functions
+ * win/tclWinNotify.c: check for hooks; remove hook checks in
+ * unix/tclUnixNotfy.c: notifier API callers. [Patch 1938497]
+
2008-04-15 Andreas Kupries <andreask@activestate.com>
- * generic/tclIO.c (CopyData): Applied another patch by Alexandre
- * io.test (io-53.8a): Ferrieux <ferrieux@users.sourceforge.net>,
- * chanio.test (chan-io-53.8a): to shift EOF handling to the async
+ * generic/tclIO.c (CopyData): Applied another patch by Alexandre
+ * io.test (io-53.8a): Ferrieux <ferrieux@users.sf.net>,
+ * chanio.test (chan-io-53.8a): to shift EOF handling to the async
part of the command if a callback is specified, should the channel
be at EOF already when fcopy is called. Testcase by myself.
diff --git a/generic/tclInt.h b/generic/tclInt.h
index e513663..7af390d 100644
--- a/generic/tclInt.h
+++ b/generic/tclInt.h
@@ -13,7 +13,7 @@
* See the file "license.terms" for information on usage and redistribution of
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclInt.h,v 1.363 2008/03/30 04:26:16 kennykb Exp $
+ * RCS: @(#) $Id: tclInt.h,v 1.364 2008/04/16 14:29:25 das Exp $
*/
#ifndef _TCLINT
@@ -2357,7 +2357,7 @@ MODULE_SCOPE char * tclNativeExecutableName;
MODULE_SCOPE int tclFindExecutableSearchDone;
MODULE_SCOPE char * tclMemDumpFileName;
MODULE_SCOPE TclPlatformType tclPlatform;
-MODULE_SCOPE Tcl_NotifierProcs tclOriginalNotifier;
+MODULE_SCOPE Tcl_NotifierProcs tclNotifierHooks;
/*
* TIP #233 (Virtualized Time)
diff --git a/generic/tclNotify.c b/generic/tclNotify.c
index 06781d9..805845b 100644
--- a/generic/tclNotify.c
+++ b/generic/tclNotify.c
@@ -14,12 +14,19 @@
* See the file "license.terms" for information on usage and redistribution of
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclNotify.c,v 1.25 2006/09/25 15:02:54 dkf Exp $
+ * RCS: @(#) $Id: tclNotify.c,v 1.26 2008/04/16 14:29:26 das Exp $
*/
#include "tclInt.h"
-extern TclStubs tclStubs;
+/*
+ * Module-scope struct of notifier hooks that are checked in the default
+ * notifier functions (for overriding via Tcl_SetNotifier).
+ */
+
+Tcl_NotifierProcs tclNotifierHooks = {
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+};
/*
* For each event source (created with Tcl_CreateEventSource) there is a
@@ -128,7 +135,7 @@ TclInitNotifier(void)
tsdPtr = TCL_TSD_INIT(&dataKey);
tsdPtr->threadId = threadId;
- tsdPtr->clientData = tclStubs.tcl_InitNotifier();
+ tsdPtr->clientData = Tcl_InitNotifier();
tsdPtr->initialized = 1;
tsdPtr->nextPtr = firstNotifierPtr;
firstNotifierPtr = tsdPtr;
@@ -184,9 +191,7 @@ TclFinalizeNotifier(void)
Tcl_MutexLock(&listLock);
- if (tclStubs.tcl_FinalizeNotifier) {
- tclStubs.tcl_FinalizeNotifier(tsdPtr->clientData);
- }
+ Tcl_FinalizeNotifier(tsdPtr->clientData);
Tcl_MutexFinalize(&(tsdPtr->queueMutex));
for (prevPtrPtr = &firstNotifierPtr; *prevPtrPtr != NULL;
prevPtrPtr = &((*prevPtrPtr)->nextPtr)) {
@@ -213,9 +218,8 @@ TclFinalizeNotifier(void)
* None.
*
* Side effects:
- * Overstomps part of the stub vector. This relies on hooks added to the
- * default functions in case those are called directly (i.e., not through
- * the stub table.)
+ * Set the tclNotifierHooks global, which is checked in the default
+ * notifier functions.
*
*----------------------------------------------------------------------
*/
@@ -224,16 +228,7 @@ void
Tcl_SetNotifier(
Tcl_NotifierProcs *notifierProcPtr)
{
-#if !defined(__WIN32__) /* UNIX */
- tclStubs.tcl_CreateFileHandler = notifierProcPtr->createFileHandlerProc;
- tclStubs.tcl_DeleteFileHandler = notifierProcPtr->deleteFileHandlerProc;
-#endif
- tclStubs.tcl_SetTimer = notifierProcPtr->setTimerProc;
- tclStubs.tcl_WaitForEvent = notifierProcPtr->waitForEventProc;
- tclStubs.tcl_InitNotifier = notifierProcPtr->initNotifierProc;
- tclStubs.tcl_FinalizeNotifier = notifierProcPtr->finalizeNotifierProc;
- tclStubs.tcl_AlertNotifier = notifierProcPtr->alertNotifierProc;
- tclStubs.tcl_ServiceModeHook = notifierProcPtr->serviceModeHookProc;
+ tclNotifierHooks = *notifierProcPtr;
}
/*
@@ -774,9 +769,7 @@ Tcl_SetServiceMode(
oldMode = tsdPtr->serviceMode;
tsdPtr->serviceMode = mode;
- if (tclStubs.tcl_ServiceModeHook) {
- tclStubs.tcl_ServiceModeHook(mode);
- }
+ Tcl_ServiceModeHook(mode);
return oldMode;
}
@@ -1136,9 +1129,7 @@ Tcl_ThreadAlert(
Tcl_MutexLock(&listLock);
for (tsdPtr = firstNotifierPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) {
if (tsdPtr->threadId == threadId) {
- if (tclStubs.tcl_AlertNotifier) {
- tclStubs.tcl_AlertNotifier(tsdPtr->clientData);
- }
+ Tcl_AlertNotifier(tsdPtr->clientData);
break;
}
}
diff --git a/generic/tclStubInit.c b/generic/tclStubInit.c
index b5f31ab..f3b87e1 100644
--- a/generic/tclStubInit.c
+++ b/generic/tclStubInit.c
@@ -8,7 +8,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclStubInit.c,v 1.151 2008/04/02 21:30:04 das Exp $
+ * RCS: @(#) $Id: tclStubInit.c,v 1.152 2008/04/16 14:29:26 das Exp $
*/
#include "tclInt.h"
@@ -34,29 +34,6 @@
#undef Tcl_FindHashEntry
#undef Tcl_CreateHashEntry
-/*
- * Keep a record of the original Notifier procedures, created in the
- * same compilation unit as the stub tables so we can later do reliable,
- * portable comparisons to see whether a Tcl_SetNotifier() call swapped
- * new routines into the stub table.
- */
-
-Tcl_NotifierProcs tclOriginalNotifier = {
- Tcl_SetTimer,
- Tcl_WaitForEvent,
-#if !defined(__WIN32__) /* UNIX */
- Tcl_CreateFileHandler,
- Tcl_DeleteFileHandler,
-#else
- NULL,
- NULL,
-#endif
- NULL,
- NULL,
- NULL,
- NULL
-};
-
MODULE_SCOPE TclIntStubs tclIntStubs;
MODULE_SCOPE TclIntPlatStubs tclIntPlatStubs;
MODULE_SCOPE TclPlatStubs tclPlatStubs;
diff --git a/macosx/tclMacOSXNotify.c b/macosx/tclMacOSXNotify.c
index 0cd8eac..419cc48 100644
--- a/macosx/tclMacOSXNotify.c
+++ b/macosx/tclMacOSXNotify.c
@@ -12,7 +12,7 @@
* See the file "license.terms" for information on usage and redistribution of
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclMacOSXNotify.c,v 1.18 2008/03/11 22:24:17 das Exp $
+ * RCS: @(#) $Id: tclMacOSXNotify.c,v 1.19 2008/04/16 14:29:25 das Exp $
*/
#include "tclInt.h"
@@ -21,9 +21,6 @@
#include <CoreFoundation/CoreFoundation.h>
#include <pthread.h>
-extern TclStubs tclStubs;
-extern Tcl_NotifierProcs tclOriginalNotifier;
-
/*
* This structure is used to keep track of the notifier info for a registered
* file.
@@ -351,105 +348,113 @@ MODULE_SCOPE long tclMacOSXDarwinRelease;
ClientData
Tcl_InitNotifier(void)
{
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+ if (tclNotifierHooks.initNotifierProc) {
+ return tclNotifierHooks.initNotifierProc();
+ } else {
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
- tsdPtr->eventReady = 0;
+ tsdPtr->eventReady = 0;
#ifdef WEAK_IMPORT_SPINLOCKLOCK
- /*
- * Initialize support for weakly imported spinlock API.
- */
- if (pthread_once(&spinLockLockInitControl, SpinLockLockInit)) {
- Tcl_Panic("Tcl_InitNotifier: pthread_once failed");
- }
+ /*
+ * Initialize support for weakly imported spinlock API.
+ */
+ if (pthread_once(&spinLockLockInitControl, SpinLockLockInit)) {
+ Tcl_Panic("Tcl_InitNotifier: pthread_once failed");
+ }
#endif
#ifndef __CONSTANT_CFSTRINGS__
- if (!tclEventsOnlyRunLoopMode) {
- tclEventsOnlyRunLoopMode = CFSTR(TCL_EVENTS_ONLY_RUN_LOOP_MODE);
- }
+ if (!tclEventsOnlyRunLoopMode) {
+ tclEventsOnlyRunLoopMode = CFSTR(TCL_EVENTS_ONLY_RUN_LOOP_MODE);
+ }
#endif
- /*
- * Initialize CFRunLoopSource and add it to CFRunLoop of this thread.
- */
-
- if (!tsdPtr->runLoop) {
- CFRunLoopRef runLoop = CFRunLoopGetCurrent();
- CFRunLoopSourceRef runLoopSource;
- CFRunLoopSourceContext runLoopSourceContext;
+ /*
+ * Initialize CFRunLoopSource and add it to CFRunLoop of this thread.
+ */
- bzero(&runLoopSourceContext, sizeof(CFRunLoopSourceContext));
- runLoopSourceContext.info = tsdPtr;
- runLoopSource = CFRunLoopSourceCreate(NULL, 0, &runLoopSourceContext);
- if (!runLoopSource) {
- Tcl_Panic("Tcl_InitNotifier: could not create CFRunLoopSource");
+ if (!tsdPtr->runLoop) {
+ CFRunLoopRef runLoop = CFRunLoopGetCurrent();
+ CFRunLoopSourceRef runLoopSource;
+ CFRunLoopSourceContext runLoopSourceContext;
+
+ bzero(&runLoopSourceContext, sizeof(CFRunLoopSourceContext));
+ runLoopSourceContext.info = tsdPtr;
+ runLoopSource = CFRunLoopSourceCreate(NULL, 0,
+ &runLoopSourceContext);
+ if (!runLoopSource) {
+ Tcl_Panic("Tcl_InitNotifier: could not create CFRunLoopSource");
+ }
+ CFRunLoopAddSource(runLoop, runLoopSource, kCFRunLoopCommonModes);
+ CFRunLoopAddSource(runLoop, runLoopSource,
+ tclEventsOnlyRunLoopMode);
+ tsdPtr->runLoopSource = runLoopSource;
+ tsdPtr->runLoop = runLoop;
}
- CFRunLoopAddSource(runLoop, runLoopSource, kCFRunLoopCommonModes);
- CFRunLoopAddSource(runLoop, runLoopSource, tclEventsOnlyRunLoopMode);
- tsdPtr->runLoopSource = runLoopSource;
- tsdPtr->runLoop = runLoop;
- }
- LOCK_NOTIFIER_INIT;
+ LOCK_NOTIFIER_INIT;
#ifdef HAVE_PTHREAD_ATFORK
- /*
- * Install pthread_atfork handlers to reinitialize the notifier in the
- * child of a fork.
- */
+ /*
+ * Install pthread_atfork handlers to reinitialize the notifier in the
+ * child of a fork.
+ */
- if (
+ if (
#ifdef WEAK_IMPORT_PTHREAD_ATFORK
- pthread_atfork != NULL &&
+ pthread_atfork != NULL &&
#endif
- !atForkInit) {
- int result = pthread_atfork(AtForkPrepare, AtForkParent, AtForkChild);
- if (result) {
- Tcl_Panic("Tcl_InitNotifier: pthread_atfork failed");
+ !atForkInit) {
+ int result = pthread_atfork(AtForkPrepare, AtForkParent,
+ AtForkChild);
+
+ if (result) {
+ Tcl_Panic("Tcl_InitNotifier: pthread_atfork failed");
+ }
+ atForkInit = 1;
}
- atForkInit = 1;
- }
#endif
- if (notifierCount == 0) {
- int fds[2], status;
+ if (notifierCount == 0) {
+ int fds[2], status;
- /*
- * Initialize trigger pipe.
- */
+ /*
+ * Initialize trigger pipe.
+ */
- if (pipe(fds) != 0) {
- Tcl_Panic("Tcl_InitNotifier: could not create trigger pipe");
- }
+ if (pipe(fds) != 0) {
+ Tcl_Panic("Tcl_InitNotifier: could not create trigger pipe");
+ }
- status = fcntl(fds[0], F_GETFL);
- status |= O_NONBLOCK;
- if (fcntl(fds[0], F_SETFL, status) < 0) {
- Tcl_Panic("Tcl_InitNotifier: could not make receive pipe non blocking");
- }
- status = fcntl(fds[1], F_GETFL);
- status |= O_NONBLOCK;
- if (fcntl(fds[1], F_SETFL, status) < 0) {
- Tcl_Panic("Tcl_InitNotifier: could not make trigger pipe non blocking");
- }
+ status = fcntl(fds[0], F_GETFL);
+ status |= O_NONBLOCK;
+ if (fcntl(fds[0], F_SETFL, status) < 0) {
+ Tcl_Panic("Tcl_InitNotifier: could not make receive pipe non blocking");
+ }
+ status = fcntl(fds[1], F_GETFL);
+ status |= O_NONBLOCK;
+ if (fcntl(fds[1], F_SETFL, status) < 0) {
+ Tcl_Panic("Tcl_InitNotifier: could not make trigger pipe non blocking");
+ }
- receivePipe = fds[0];
- triggerPipe = fds[1];
+ receivePipe = fds[0];
+ triggerPipe = fds[1];
- /*
- * Create notifier thread lazily in Tcl_WaitForEvent() to avoid
- * interfering with fork() followed immediately by execve()
- * (cannot execve() when more than one thread is present).
- */
+ /*
+ * Create notifier thread lazily in Tcl_WaitForEvent() to avoid
+ * interfering with fork() followed immediately by execve()
+ * (cannot execve() when more than one thread is present).
+ */
- notifierThread = 0;
+ notifierThread = 0;
#ifdef TCL_MAC_DEBUG_NOTIFIER
- OPEN_NOTIFIER_LOG;
+ OPEN_NOTIFIER_LOG;
#endif
- }
- notifierCount++;
- UNLOCK_NOTIFIER_INIT;
+ }
+ notifierCount++;
+ UNLOCK_NOTIFIER_INIT;
- return (ClientData) tsdPtr;
+ return (ClientData) tsdPtr;
+ }
}
/*
@@ -474,66 +479,71 @@ void
Tcl_FinalizeNotifier(
ClientData clientData) /* Not used. */
{
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+ if (tclNotifierHooks.finalizeNotifierProc) {
+ tclNotifierHooks.finalizeNotifierProc(clientData);
+ return;
+ } else {
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
- LOCK_NOTIFIER_INIT;
- notifierCount--;
+ LOCK_NOTIFIER_INIT;
+ notifierCount--;
- /*
- * If this is the last thread to use the notifier, close the notifier pipe
- * and wait for the background thread to terminate.
- */
+ /*
+ * If this is the last thread to use the notifier, close the notifier
+ * pipe and wait for the background thread to terminate.
+ */
- if (notifierCount == 0) {
- int result;
+ if (notifierCount == 0) {
+ int result;
- if (triggerPipe < 0) {
- Tcl_Panic("Tcl_FinalizeNotifier: notifier pipe not initialized");
- }
+ if (triggerPipe < 0) {
+ Tcl_Panic("Tcl_FinalizeNotifier: notifier pipe not initialized");
+ }
- /*
- * Send "q" message to the notifier thread so that it will terminate.
- * The notifier will return from its call to select() and notice that
- * a "q" message has arrived, it will then close its side of the pipe
- * and terminate its thread. Note the we can not just close the pipe
- * and check for EOF in the notifier thread because if a background
- * child process was created with exec, select() would not register
- * the EOF on the pipe until the child processes had terminated. [Bug:
- * 4139] [Bug: 1222872]
- */
+ /*
+ * Send "q" message to the notifier thread so that it will
+ * terminate. The notifier will return from its call to select()
+ * and notice that a "q" message has arrived, it will then close
+ * its side of the pipe and terminate its thread. Note the we can
+ * not just close the pipe and check for EOF in the notifier thread
+ * because if a background child process was created with exec,
+ * select() would not register the EOF on the pipe until the child
+ * processes had terminated. [Bug: 4139] [Bug: 1222872]
+ */
- write(triggerPipe, "q", 1);
- close(triggerPipe);
+ write(triggerPipe, "q", 1);
+ close(triggerPipe);
- if (notifierThread) {
- result = pthread_join(notifierThread, NULL);
- if (result) {
- Tcl_Panic("Tcl_FinalizeNotifier: unable to join notifier thread");
+ if (notifierThread) {
+ result = pthread_join(notifierThread, NULL);
+ if (result) {
+ Tcl_Panic("Tcl_FinalizeNotifier: unable to join notifier thread");
+ }
+ notifierThread = 0;
}
- notifierThread = 0;
- }
- close(receivePipe);
- triggerPipe = -1;
+ close(receivePipe);
+ triggerPipe = -1;
#ifdef TCL_MAC_DEBUG_NOTIFIER
- CLOSE_NOTIFIER_LOG;
+ CLOSE_NOTIFIER_LOG;
#endif
- }
- UNLOCK_NOTIFIER_INIT;
+ }
+ UNLOCK_NOTIFIER_INIT;
- LOCK_NOTIFIER; /* for concurrency with Tcl_AlertNotifier */
- if (tsdPtr->runLoop) {
- tsdPtr->runLoop = NULL;
+ LOCK_NOTIFIER; /* for concurrency with Tcl_AlertNotifier */
+ if (tsdPtr->runLoop) {
+ tsdPtr->runLoop = NULL;
- /*
- * Remove runLoopSource from all CFRunLoops and release it.
- */
+ /*
+ * Remove runLoopSource from all CFRunLoops and release it.
+ */
- CFRunLoopSourceInvalidate(tsdPtr->runLoopSource);
- CFRelease(tsdPtr->runLoopSource);
- tsdPtr->runLoopSource = NULL;
+ CFRunLoopSourceInvalidate(tsdPtr->runLoopSource);
+ CFRelease(tsdPtr->runLoopSource);
+ tsdPtr->runLoopSource = NULL;
+ }
+ UNLOCK_NOTIFIER;
}
- UNLOCK_NOTIFIER;
}
/*
@@ -559,15 +569,20 @@ void
Tcl_AlertNotifier(
ClientData clientData)
{
- ThreadSpecificData *tsdPtr = (ThreadSpecificData *) clientData;
+ if (tclNotifierHooks.alertNotifierProc) {
+ tclNotifierHooks.alertNotifierProc(clientData);
+ return;
+ } else {
+ ThreadSpecificData *tsdPtr = (ThreadSpecificData *) clientData;
- LOCK_NOTIFIER;
- if (tsdPtr->runLoop) {
- tsdPtr->eventReady = 1;
- CFRunLoopSourceSignal(tsdPtr->runLoopSource);
- CFRunLoopWakeUp(tsdPtr->runLoop);
+ LOCK_NOTIFIER;
+ if (tsdPtr->runLoop) {
+ tsdPtr->eventReady = 1;
+ CFRunLoopSourceSignal(tsdPtr->runLoopSource);
+ CFRunLoopWakeUp(tsdPtr->runLoop);
+ }
+ UNLOCK_NOTIFIER;
}
- UNLOCK_NOTIFIER;
}
/*
@@ -592,14 +607,15 @@ void
Tcl_SetTimer(
Tcl_Time *timePtr) /* Timeout value, may be NULL. */
{
- /*
- * The interval timer doesn't do anything in this implementation, because
- * the only event loop is via Tcl_DoOneEvent, which passes timeout values
- * to Tcl_WaitForEvent.
- */
-
- if (tclStubs.tcl_SetTimer != tclOriginalNotifier.setTimerProc) {
- tclStubs.tcl_SetTimer(timePtr);
+ if (tclNotifierHooks.setTimerProc) {
+ tclNotifierHooks.setTimerProc(timePtr);
+ return;
+ } else {
+ /*
+ * The interval timer doesn't do anything in this implementation,
+ * because the only event loop is via Tcl_DoOneEvent, which passes
+ * timeout values to Tcl_WaitForEvent.
+ */
}
}
@@ -624,6 +640,12 @@ Tcl_ServiceModeHook(
int mode) /* Either TCL_SERVICE_ALL, or
* TCL_SERVICE_NONE. */
{
+ if (tclNotifierHooks.serviceModeHookProc) {
+ tclNotifierHooks.serviceModeHookProc(mode);
+ return;
+ } else {
+ /* Does nothing in this implementation. */
+ }
}
/*
@@ -653,53 +675,52 @@ Tcl_CreateFileHandler(
* event. */
ClientData clientData) /* Arbitrary data to pass to proc. */
{
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
- FileHandler *filePtr;
-
- if (tclStubs.tcl_CreateFileHandler !=
- tclOriginalNotifier.createFileHandlerProc) {
- tclStubs.tcl_CreateFileHandler(fd, mask, proc, clientData);
+ if (tclNotifierHooks.createFileHandlerProc) {
+ tclNotifierHooks.createFileHandlerProc(fd, mask, proc, clientData);
return;
- }
+ } else {
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+ FileHandler *filePtr;
- for (filePtr = tsdPtr->firstFileHandlerPtr; filePtr != NULL;
- filePtr = filePtr->nextPtr) {
- if (filePtr->fd == fd) {
- break;
+ for (filePtr = tsdPtr->firstFileHandlerPtr; filePtr != NULL;
+ filePtr = filePtr->nextPtr) {
+ if (filePtr->fd == fd) {
+ break;
+ }
}
- }
- if (filePtr == NULL) {
- filePtr = (FileHandler*) ckalloc(sizeof(FileHandler));
- filePtr->fd = fd;
- filePtr->readyMask = 0;
- filePtr->nextPtr = tsdPtr->firstFileHandlerPtr;
- tsdPtr->firstFileHandlerPtr = filePtr;
- }
- filePtr->proc = proc;
- filePtr->clientData = clientData;
- filePtr->mask = mask;
+ if (filePtr == NULL) {
+ filePtr = (FileHandler*) ckalloc(sizeof(FileHandler));
+ filePtr->fd = fd;
+ filePtr->readyMask = 0;
+ filePtr->nextPtr = tsdPtr->firstFileHandlerPtr;
+ tsdPtr->firstFileHandlerPtr = filePtr;
+ }
+ filePtr->proc = proc;
+ filePtr->clientData = clientData;
+ filePtr->mask = mask;
- /*
- * Update the check masks for this file.
- */
+ /*
+ * Update the check masks for this file.
+ */
- if (mask & TCL_READABLE) {
- FD_SET(fd, &(tsdPtr->checkMasks.readable));
- } else {
- FD_CLR(fd, &(tsdPtr->checkMasks.readable));
- }
- if (mask & TCL_WRITABLE) {
- FD_SET(fd, &(tsdPtr->checkMasks.writable));
- } else {
- FD_CLR(fd, &(tsdPtr->checkMasks.writable));
- }
- if (mask & TCL_EXCEPTION) {
- FD_SET(fd, &(tsdPtr->checkMasks.exceptional));
- } else {
- FD_CLR(fd, &(tsdPtr->checkMasks.exceptional));
- }
- if (tsdPtr->numFdBits <= fd) {
- tsdPtr->numFdBits = fd+1;
+ if (mask & TCL_READABLE) {
+ FD_SET(fd, &(tsdPtr->checkMasks.readable));
+ } else {
+ FD_CLR(fd, &(tsdPtr->checkMasks.readable));
+ }
+ if (mask & TCL_WRITABLE) {
+ FD_SET(fd, &(tsdPtr->checkMasks.writable));
+ } else {
+ FD_CLR(fd, &(tsdPtr->checkMasks.writable));
+ }
+ if (mask & TCL_EXCEPTION) {
+ FD_SET(fd, &(tsdPtr->checkMasks.exceptional));
+ } else {
+ FD_CLR(fd, &(tsdPtr->checkMasks.exceptional));
+ }
+ if (tsdPtr->numFdBits <= fd) {
+ tsdPtr->numFdBits = fd+1;
+ }
}
}
@@ -724,70 +745,69 @@ Tcl_DeleteFileHandler(
int fd) /* Stream id for which to remove callback
* function. */
{
- FileHandler *filePtr, *prevPtr;
- int i;
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
-
- if (tclStubs.tcl_DeleteFileHandler !=
- tclOriginalNotifier.deleteFileHandlerProc) {
- tclStubs.tcl_DeleteFileHandler(fd);
+ if (tclNotifierHooks.deleteFileHandlerProc) {
+ tclNotifierHooks.deleteFileHandlerProc(fd);
return;
- }
+ } else {
+ FileHandler *filePtr, *prevPtr;
+ int i;
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
- /*
- * Find the entry for the given file (and return if there isn't one).
- */
+ /*
+ * Find the entry for the given file (and return if there isn't one).
+ */
- for (prevPtr = NULL, filePtr = tsdPtr->firstFileHandlerPtr; ;
- prevPtr = filePtr, filePtr = filePtr->nextPtr) {
- if (filePtr == NULL) {
- return;
- }
- if (filePtr->fd == fd) {
- break;
+ for (prevPtr = NULL, filePtr = tsdPtr->firstFileHandlerPtr; ;
+ prevPtr = filePtr, filePtr = filePtr->nextPtr) {
+ if (filePtr == NULL) {
+ return;
+ }
+ if (filePtr->fd == fd) {
+ break;
+ }
}
- }
- /*
- * Update the check masks for this file.
- */
+ /*
+ * Update the check masks for this file.
+ */
- if (filePtr->mask & TCL_READABLE) {
- FD_CLR(fd, &(tsdPtr->checkMasks.readable));
- }
- if (filePtr->mask & TCL_WRITABLE) {
- FD_CLR(fd, &(tsdPtr->checkMasks.writable));
- }
- if (filePtr->mask & TCL_EXCEPTION) {
- FD_CLR(fd, &(tsdPtr->checkMasks.exceptional));
- }
+ if (filePtr->mask & TCL_READABLE) {
+ FD_CLR(fd, &(tsdPtr->checkMasks.readable));
+ }
+ if (filePtr->mask & TCL_WRITABLE) {
+ FD_CLR(fd, &(tsdPtr->checkMasks.writable));
+ }
+ if (filePtr->mask & TCL_EXCEPTION) {
+ FD_CLR(fd, &(tsdPtr->checkMasks.exceptional));
+ }
- /*
- * Find current max fd.
- */
+ /*
+ * Find current max fd.
+ */
- if (fd+1 == tsdPtr->numFdBits) {
- tsdPtr->numFdBits = 0;
- for (i = fd-1; i >= 0; i--) {
- if (FD_ISSET(i, &(tsdPtr->checkMasks.readable))
- || FD_ISSET(i, &(tsdPtr->checkMasks.writable))
- || FD_ISSET(i, &(tsdPtr->checkMasks.exceptional))) {
- tsdPtr->numFdBits = i+1;
- break;
+ if (fd+1 == tsdPtr->numFdBits) {
+ tsdPtr->numFdBits = 0;
+ for (i = fd-1; i >= 0; i--) {
+ if (FD_ISSET(i, &(tsdPtr->checkMasks.readable))
+ || FD_ISSET(i, &(tsdPtr->checkMasks.writable))
+ || FD_ISSET(i, &(tsdPtr->checkMasks.exceptional))) {
+ tsdPtr->numFdBits = i+1;
+ break;
+ }
}
}
- }
- /*
- * Clean up information in the callback record.
- */
+ /*
+ * Clean up information in the callback record.
+ */
- if (prevPtr == NULL) {
- tsdPtr->firstFileHandlerPtr = filePtr->nextPtr;
- } else {
- prevPtr->nextPtr = filePtr->nextPtr;
+ if (prevPtr == NULL) {
+ tsdPtr->firstFileHandlerPtr = filePtr->nextPtr;
+ } else {
+ prevPtr->nextPtr = filePtr->nextPtr;
+ }
+ ckfree((char *) filePtr);
}
- ckfree((char *) filePtr);
}
/*
@@ -885,195 +905,203 @@ int
Tcl_WaitForEvent(
Tcl_Time *timePtr) /* Maximum block time, or NULL. */
{
- FileHandler *filePtr;
- FileHandlerEvent *fileEvPtr;
- int mask;
- Tcl_Time myTime;
- int waitForFiles;
- Tcl_Time *myTimePtr;
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
-
- if (tclStubs.tcl_WaitForEvent != tclOriginalNotifier.waitForEventProc) {
- return tclStubs.tcl_WaitForEvent(timePtr);
- }
+ if (tclNotifierHooks.waitForEventProc) {
+ return tclNotifierHooks.waitForEventProc(timePtr);
+ } else {
+ FileHandler *filePtr;
+ FileHandlerEvent *fileEvPtr;
+ int mask;
+ Tcl_Time myTime;
+ int waitForFiles;
+ Tcl_Time *myTimePtr;
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
- 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.
+ * 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.
*/
- myTime.sec = timePtr->sec;
- myTime.usec = timePtr->usec;
-
- if (myTime.sec != 0 || myTime.usec != 0) {
- (*tclScaleTimeProcPtr) (&myTime, tclTimeClientData);
- }
+ 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.
+ */
- myTimePtr = &myTime;
- } else {
- myTimePtr = NULL;
- }
+ myTime.sec = timePtr->sec;
+ myTime.usec = timePtr->usec;
- /*
- * Start notifier thread if necessary.
- */
+ if (myTime.sec != 0 || myTime.usec != 0) {
+ (*tclScaleTimeProcPtr) (&myTime, tclTimeClientData);
+ }
- LOCK_NOTIFIER_INIT;
- if (!notifierCount) {
- Tcl_Panic("Tcl_WaitForEvent: notifier not initialized");
- }
- if (!notifierThread) {
- int result;
- pthread_attr_t attr;
-
- pthread_attr_init(&attr);
- pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
- pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
- pthread_attr_setstacksize(&attr, 60 * 1024);
- result = pthread_create(&notifierThread, &attr,
- (void * (*)(void *))NotifierThreadProc, NULL);
- pthread_attr_destroy(&attr);
- if (result || !notifierThread) {
- Tcl_Panic("Tcl_WaitForEvent: unable to start notifier thread");
+ myTimePtr = &myTime;
+ } else {
+ myTimePtr = NULL;
}
- }
- UNLOCK_NOTIFIER_INIT;
- /*
- * Place this thread on the list of interested threads, signal the
- * notifier thread, and wait for a response or a timeout.
- */
-
- LOCK_NOTIFIER;
- if (!tsdPtr->runLoop) {
- Tcl_Panic("Tcl_WaitForEvent: CFRunLoop not initialized");
- }
- waitForFiles = (tsdPtr->numFdBits > 0);
- if (myTimePtr != NULL && myTimePtr->sec == 0 && myTimePtr->usec == 0) {
/*
- * Cannot emulate a polling select with a polling condition variable.
- * Instead, pretend to wait for files and tell the notifier thread
- * what we are doing. The notifier thread makes sure it goes through
- * select with its select mask in the same state as ours currently is.
- * We block until that happens.
+ * Start notifier thread if necessary.
*/
- waitForFiles = 1;
- tsdPtr->pollState = POLL_WANT;
- myTimePtr = NULL;
- } else {
- tsdPtr->pollState = 0;
- }
+ LOCK_NOTIFIER_INIT;
+ if (!notifierCount) {
+ Tcl_Panic("Tcl_WaitForEvent: notifier not initialized");
+ }
+ if (!notifierThread) {
+ int result;
+ pthread_attr_t attr;
+
+ pthread_attr_init(&attr);
+ pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+ pthread_attr_setstacksize(&attr, 60 * 1024);
+ result = pthread_create(&notifierThread, &attr,
+ (void * (*)(void *))NotifierThreadProc, NULL);
+ pthread_attr_destroy(&attr);
+ if (result || !notifierThread) {
+ Tcl_Panic("Tcl_WaitForEvent: unable to start notifier thread");
+ }
+ }
+ UNLOCK_NOTIFIER_INIT;
- if (waitForFiles) {
/*
- * Add the ThreadSpecificData structure of this thread to the list of
- * ThreadSpecificData structures of all threads that are waiting on
- * file events.
+ * Place this thread on the list of interested threads, signal the
+ * notifier thread, and wait for a response or a timeout.
*/
- tsdPtr->nextPtr = waitingListPtr;
- if (waitingListPtr) {
- waitingListPtr->prevPtr = tsdPtr;
+ LOCK_NOTIFIER;
+ if (!tsdPtr->runLoop) {
+ Tcl_Panic("Tcl_WaitForEvent: CFRunLoop not initialized");
}
- tsdPtr->prevPtr = 0;
- waitingListPtr = tsdPtr;
- tsdPtr->onList = 1;
-
- write(triggerPipe, "", 1);
- }
-
- FD_ZERO(&(tsdPtr->readyMasks.readable));
- FD_ZERO(&(tsdPtr->readyMasks.writable));
- FD_ZERO(&(tsdPtr->readyMasks.exceptional));
-
- if (!tsdPtr->eventReady) {
- CFTimeInterval waitTime;
- CFStringRef runLoopMode;
+ waitForFiles = (tsdPtr->numFdBits > 0);
+ if (myTimePtr != NULL && myTimePtr->sec == 0 && myTimePtr->usec == 0) {
+ /*
+ * Cannot emulate a polling select with a polling condition
+ * variable. Instead, pretend to wait for files and tell the
+ * notifier thread what we are doing. The notifier thread makes
+ * sure it goes through select with its select mask in the same
+ * state as ours currently is. We block until that happens.
+ */
- if (myTimePtr == NULL) {
- waitTime = 1.0e10; /* Wait forever, as per CFRunLoop.c */
- } else {
- waitTime = myTimePtr->sec + 1.0e-6 * myTimePtr->usec;
- }
- /*
- * If the run loop is already running (e.g. if Tcl_WaitForEvent was
- * called recursively), re-run it in a custom run loop mode containing
- * only the source for the notifier thread, otherwise wakeups from other
- * sources added to the common run loop modes might get lost.
- */
- if ((runLoopMode = CFRunLoopCopyCurrentMode(tsdPtr->runLoop))) {
- CFRelease(runLoopMode);
- runLoopMode = tclEventsOnlyRunLoopMode;
+ waitForFiles = 1;
+ tsdPtr->pollState = POLL_WANT;
+ myTimePtr = NULL;
} else {
- runLoopMode = kCFRunLoopDefaultMode;
+ tsdPtr->pollState = 0;
}
- UNLOCK_NOTIFIER;
- CFRunLoopRunInMode(runLoopMode, waitTime, TRUE);
- LOCK_NOTIFIER;
- }
- tsdPtr->eventReady = 0;
- if (waitForFiles && tsdPtr->onList) {
- /*
- * Remove the ThreadSpecificData structure of this thread from the
- * waiting list. Alert the notifier thread to recompute its select
- * masks - skipping this caused a hang when trying to close a pipe
- * which the notifier thread was still doing a select on.
- */
+ if (waitForFiles) {
+ /*
+ * Add the ThreadSpecificData structure of this thread to the list
+ * of ThreadSpecificData structures of all threads that are waiting
+ * on file events.
+ */
- if (tsdPtr->prevPtr) {
- tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr;
- } else {
- waitingListPtr = tsdPtr->nextPtr;
- }
- if (tsdPtr->nextPtr) {
- tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr;
+ tsdPtr->nextPtr = waitingListPtr;
+ if (waitingListPtr) {
+ waitingListPtr->prevPtr = tsdPtr;
+ }
+ tsdPtr->prevPtr = 0;
+ waitingListPtr = tsdPtr;
+ tsdPtr->onList = 1;
+
+ write(triggerPipe, "", 1);
}
- tsdPtr->nextPtr = tsdPtr->prevPtr = NULL;
- tsdPtr->onList = 0;
- write(triggerPipe, "", 1);
- }
- /*
- * Queue all detected file events before returning.
- */
+ FD_ZERO(&(tsdPtr->readyMasks.readable));
+ FD_ZERO(&(tsdPtr->readyMasks.writable));
+ FD_ZERO(&(tsdPtr->readyMasks.exceptional));
- for (filePtr = tsdPtr->firstFileHandlerPtr; (filePtr != NULL);
- filePtr = filePtr->nextPtr) {
+ if (!tsdPtr->eventReady) {
+ CFTimeInterval waitTime;
+ CFStringRef runLoopMode;
- mask = 0;
- if (FD_ISSET(filePtr->fd, &(tsdPtr->readyMasks.readable))) {
- mask |= TCL_READABLE;
- }
- if (FD_ISSET(filePtr->fd, &(tsdPtr->readyMasks.writable))) {
- mask |= TCL_WRITABLE;
- }
- if (FD_ISSET(filePtr->fd, &(tsdPtr->readyMasks.exceptional))) {
- mask |= TCL_EXCEPTION;
+ if (myTimePtr == NULL) {
+ waitTime = 1.0e10; /* Wait forever, as per CFRunLoop.c */
+ } else {
+ waitTime = myTimePtr->sec + 1.0e-6 * myTimePtr->usec;
+ }
+ /*
+ * If the run loop is already running (e.g. if Tcl_WaitForEvent was
+ * called recursively), re-run it in a custom run loop mode
+ * containing only the source for the notifier thread, otherwise
+ * wakeups from other sources added to the common run loop modes
+ * might get lost.
+ */
+ if ((runLoopMode = CFRunLoopCopyCurrentMode(tsdPtr->runLoop))) {
+ CFRelease(runLoopMode);
+ runLoopMode = tclEventsOnlyRunLoopMode;
+ } else {
+ runLoopMode = kCFRunLoopDefaultMode;
+ }
+ UNLOCK_NOTIFIER;
+ CFRunLoopRunInMode(runLoopMode, waitTime, TRUE);
+ LOCK_NOTIFIER;
}
+ tsdPtr->eventReady = 0;
- if (!mask) {
- continue;
+ if (waitForFiles && tsdPtr->onList) {
+ /*
+ * Remove the ThreadSpecificData structure of this thread from the
+ * waiting list. Alert the notifier thread to recompute its select
+ * masks - skipping this caused a hang when trying to close a pipe
+ * which the notifier thread was still doing a select on.
+ */
+
+ if (tsdPtr->prevPtr) {
+ tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr;
+ } else {
+ waitingListPtr = tsdPtr->nextPtr;
+ }
+ if (tsdPtr->nextPtr) {
+ tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr;
+ }
+ tsdPtr->nextPtr = tsdPtr->prevPtr = NULL;
+ tsdPtr->onList = 0;
+ write(triggerPipe, "", 1);
}
/*
- * Don't bother to queue an event if the mask was previously non-zero
- * since an event must still be on the queue.
+ * Queue all detected file events before returning.
*/
- if (filePtr->readyMask == 0) {
- fileEvPtr = (FileHandlerEvent *) ckalloc(sizeof(FileHandlerEvent));
- fileEvPtr->header.proc = FileHandlerEventProc;
- fileEvPtr->fd = filePtr->fd;
- Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL);
+ for (filePtr = tsdPtr->firstFileHandlerPtr; (filePtr != NULL);
+ filePtr = filePtr->nextPtr) {
+
+ mask = 0;
+ if (FD_ISSET(filePtr->fd, &(tsdPtr->readyMasks.readable))) {
+ mask |= TCL_READABLE;
+ }
+ if (FD_ISSET(filePtr->fd, &(tsdPtr->readyMasks.writable))) {
+ mask |= TCL_WRITABLE;
+ }
+ if (FD_ISSET(filePtr->fd, &(tsdPtr->readyMasks.exceptional))) {
+ mask |= TCL_EXCEPTION;
+ }
+
+ 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) {
+ fileEvPtr = (FileHandlerEvent *)
+ ckalloc(sizeof(FileHandlerEvent));
+ fileEvPtr->header.proc = FileHandlerEventProc;
+ fileEvPtr->fd = filePtr->fd;
+ Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL);
+ }
+ filePtr->readyMask = mask;
}
- filePtr->readyMask = mask;
+ UNLOCK_NOTIFIER;
+ return 0;
}
- UNLOCK_NOTIFIER;
- return 0;
}
/*
diff --git a/unix/tclUnixNotfy.c b/unix/tclUnixNotfy.c
index a78cabd..a763b1d 100644
--- a/unix/tclUnixNotfy.c
+++ b/unix/tclUnixNotfy.c
@@ -10,7 +10,7 @@
* See the file "license.terms" for information on usage and redistribution of
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclUnixNotfy.c,v 1.34 2008/03/11 22:23:50 das Exp $
+ * RCS: @(#) $Id: tclUnixNotfy.c,v 1.35 2008/04/16 14:29:26 das Exp $
*/
#include "tclInt.h"
@@ -19,14 +19,6 @@
#include <signal.h>
/*
- * This code does deep stub magic to allow replacement of the notifier at
- * runtime.
- */
-
-extern TclStubs tclStubs;
-extern Tcl_NotifierProcs tclOriginalNotifier;
-
-/*
* This structure is used to keep track of the notifier info for a registered
* file.
*/
@@ -199,7 +191,7 @@ static int FileHandlerEventProc(Tcl_Event *evPtr, int flags);
* Initializes the platform specific notifier state.
*
* Results:
- * Returns a handle to the notifier state for this thread..
+ * Returns a handle to the notifier state for this thread.
*
* Side effects:
* None.
@@ -210,35 +202,38 @@ static int FileHandlerEventProc(Tcl_Event *evPtr, int flags);
ClientData
Tcl_InitNotifier(void)
{
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
-
+ if (tclNotifierHooks.initNotifierProc) {
+ return tclNotifierHooks.initNotifierProc();
+ } else {
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
#ifdef TCL_THREADS
- tsdPtr->eventReady = 0;
+ tsdPtr->eventReady = 0;
- /*
- * Start the Notifier thread if necessary.
- */
+ /*
+ * Start the Notifier thread if necessary.
+ */
- Tcl_MutexLock(&notifierMutex);
- if (notifierCount == 0) {
- if (TclpThreadCreate(&notifierThread, NotifierThreadProc, NULL,
- TCL_THREAD_STACK_DEFAULT, TCL_THREAD_JOINABLE) != TCL_OK) {
- Tcl_Panic("Tcl_InitNotifier: unable to start notifier thread");
+ Tcl_MutexLock(&notifierMutex);
+ if (notifierCount == 0) {
+ if (TclpThreadCreate(&notifierThread, NotifierThreadProc, NULL,
+ TCL_THREAD_STACK_DEFAULT, TCL_THREAD_JOINABLE) != TCL_OK) {
+ Tcl_Panic("Tcl_InitNotifier: unable to start notifier thread");
+ }
}
- }
- notifierCount++;
+ notifierCount++;
- /*
- * Wait for the notifier pipe to be created.
- */
+ /*
+ * Wait for the notifier pipe to be created.
+ */
- while (triggerPipe < 0) {
- Tcl_ConditionWait(&notifierCV, &notifierMutex, NULL);
- }
+ while (triggerPipe < 0) {
+ Tcl_ConditionWait(&notifierCV, &notifierMutex, NULL);
+ }
- Tcl_MutexUnlock(&notifierMutex);
+ Tcl_MutexUnlock(&notifierMutex);
#endif
- return (ClientData) tsdPtr;
+ return (ClientData) tsdPtr;
+ }
}
/*
@@ -263,55 +258,60 @@ void
Tcl_FinalizeNotifier(
ClientData clientData) /* Not used. */
{
+ if (tclNotifierHooks.finalizeNotifierProc) {
+ tclNotifierHooks.finalizeNotifierProc(clientData);
+ return;
+ } else {
#ifdef TCL_THREADS
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
- Tcl_MutexLock(&notifierMutex);
- notifierCount--;
+ Tcl_MutexLock(&notifierMutex);
+ notifierCount--;
- /*
- * If this is the last thread to use the notifier, close the notifier pipe
- * and wait for the background thread to terminate.
- */
+ /*
+ * If this is the last thread to use the notifier, close the notifier
+ * pipe and wait for the background thread to terminate.
+ */
- if (notifierCount == 0) {
- int result;
+ if (notifierCount == 0) {
+ int result;
- if (triggerPipe < 0) {
- Tcl_Panic("Tcl_FinalizeNotifier: notifier pipe not initialized");
- }
+ if (triggerPipe < 0) {
+ Tcl_Panic("Tcl_FinalizeNotifier: notifier pipe not initialized");
+ }
- /*
- * Send "q" message to the notifier thread so that it will terminate.
- * The notifier will return from its call to select() and notice that
- * a "q" message has arrived, it will then close its side of the pipe
- * and terminate its thread. Note the we can not just close the pipe
- * and check for EOF in the notifier thread because if a background
- * child process was created with exec, select() would not register
- * the EOF on the pipe until the child processes had terminated. [Bug:
- * 4139] [Bug: 1222872]
- */
+ /*
+ * Send "q" message to the notifier thread so that it will
+ * terminate. The notifier will return from its call to select()
+ * and notice that a "q" message has arrived, it will then close
+ * its side of the pipe and terminate its thread. Note the we can
+ * not just close the pipe and check for EOF in the notifier thread
+ * because if a background child process was created with exec,
+ * select() would not register the EOF on the pipe until the child
+ * processes had terminated. [Bug: 4139] [Bug: 1222872]
+ */
- write(triggerPipe, "q", 1);
- close(triggerPipe);
- while(triggerPipe >= 0) {
- Tcl_ConditionWait(&notifierCV, &notifierMutex, NULL);
- }
+ write(triggerPipe, "q", 1);
+ close(triggerPipe);
+ while(triggerPipe >= 0) {
+ Tcl_ConditionWait(&notifierCV, &notifierMutex, NULL);
+ }
- result = Tcl_JoinThread(notifierThread, NULL);
- if (result) {
- Tcl_Panic("Tcl_FinalizeNotifier: unable to join notifier thread");
+ result = Tcl_JoinThread(notifierThread, NULL);
+ if (result) {
+ Tcl_Panic("Tcl_FinalizeNotifier: unable to join notifier thread");
+ }
}
- }
- /*
- * Clean up any synchronization objects in the thread local storage.
- */
+ /*
+ * Clean up any synchronization objects in the thread local storage.
+ */
- Tcl_ConditionFinalize(&(tsdPtr->waitCV));
+ Tcl_ConditionFinalize(&(tsdPtr->waitCV));
- Tcl_MutexUnlock(&notifierMutex);
+ Tcl_MutexUnlock(&notifierMutex);
#endif
+ }
}
/*
@@ -337,13 +337,18 @@ void
Tcl_AlertNotifier(
ClientData clientData)
{
+ if (tclNotifierHooks.alertNotifierProc) {
+ tclNotifierHooks.alertNotifierProc(clientData);
+ return;
+ } else {
#ifdef TCL_THREADS
- ThreadSpecificData *tsdPtr = (ThreadSpecificData *) clientData;
- Tcl_MutexLock(&notifierMutex);
- tsdPtr->eventReady = 1;
- Tcl_ConditionNotify(&tsdPtr->waitCV);
- Tcl_MutexUnlock(&notifierMutex);
+ ThreadSpecificData *tsdPtr = (ThreadSpecificData *) clientData;
+ Tcl_MutexLock(&notifierMutex);
+ tsdPtr->eventReady = 1;
+ Tcl_ConditionNotify(&tsdPtr->waitCV);
+ Tcl_MutexUnlock(&notifierMutex);
#endif
+ }
}
/*
@@ -368,14 +373,15 @@ void
Tcl_SetTimer(
Tcl_Time *timePtr) /* Timeout value, may be NULL. */
{
- /*
- * The interval timer doesn't do anything in this implementation, because
- * the only event loop is via Tcl_DoOneEvent, which passes timeout values
- * to Tcl_WaitForEvent.
- */
-
- if (tclStubs.tcl_SetTimer != tclOriginalNotifier.setTimerProc) {
- tclStubs.tcl_SetTimer(timePtr);
+ if (tclNotifierHooks.setTimerProc) {
+ tclNotifierHooks.setTimerProc(timePtr);
+ return;
+ } else {
+ /*
+ * The interval timer doesn't do anything in this implementation,
+ * because the only event loop is via Tcl_DoOneEvent, which passes
+ * timeout values to Tcl_WaitForEvent.
+ */
}
}
@@ -400,6 +406,12 @@ Tcl_ServiceModeHook(
int mode) /* Either TCL_SERVICE_ALL, or
* TCL_SERVICE_NONE. */
{
+ if (tclNotifierHooks.serviceModeHookProc) {
+ tclNotifierHooks.serviceModeHookProc(mode);
+ return;
+ } else {
+ /* Does nothing in this implementation. */
+ }
}
/*
@@ -429,53 +441,52 @@ Tcl_CreateFileHandler(
* event. */
ClientData clientData) /* Arbitrary data to pass to proc. */
{
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
- FileHandler *filePtr;
-
- if (tclStubs.tcl_CreateFileHandler !=
- tclOriginalNotifier.createFileHandlerProc) {
- tclStubs.tcl_CreateFileHandler(fd, mask, proc, clientData);
+ if (tclNotifierHooks.createFileHandlerProc) {
+ tclNotifierHooks.createFileHandlerProc(fd, mask, proc, clientData);
return;
- }
+ } else {
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+ FileHandler *filePtr;
- for (filePtr = tsdPtr->firstFileHandlerPtr; filePtr != NULL;
- filePtr = filePtr->nextPtr) {
- if (filePtr->fd == fd) {
- break;
+ for (filePtr = tsdPtr->firstFileHandlerPtr; filePtr != NULL;
+ filePtr = filePtr->nextPtr) {
+ if (filePtr->fd == fd) {
+ break;
+ }
}
- }
- if (filePtr == NULL) {
- filePtr = (FileHandler*) ckalloc(sizeof(FileHandler));
- filePtr->fd = fd;
- filePtr->readyMask = 0;
- filePtr->nextPtr = tsdPtr->firstFileHandlerPtr;
- tsdPtr->firstFileHandlerPtr = filePtr;
- }
- filePtr->proc = proc;
- filePtr->clientData = clientData;
- filePtr->mask = mask;
+ if (filePtr == NULL) {
+ filePtr = (FileHandler*) ckalloc(sizeof(FileHandler));
+ filePtr->fd = fd;
+ filePtr->readyMask = 0;
+ filePtr->nextPtr = tsdPtr->firstFileHandlerPtr;
+ tsdPtr->firstFileHandlerPtr = filePtr;
+ }
+ filePtr->proc = proc;
+ filePtr->clientData = clientData;
+ filePtr->mask = mask;
- /*
- * Update the check masks for this file.
- */
+ /*
+ * Update the check masks for this file.
+ */
- if (mask & TCL_READABLE) {
- FD_SET(fd, &(tsdPtr->checkMasks.readable));
- } else {
- FD_CLR(fd, &(tsdPtr->checkMasks.readable));
- }
- if (mask & TCL_WRITABLE) {
- FD_SET(fd, &(tsdPtr->checkMasks.writable));
- } else {
- FD_CLR(fd, &(tsdPtr->checkMasks.writable));
- }
- if (mask & TCL_EXCEPTION) {
- FD_SET(fd, &(tsdPtr->checkMasks.exceptional));
- } else {
- FD_CLR(fd, &(tsdPtr->checkMasks.exceptional));
- }
- if (tsdPtr->numFdBits <= fd) {
- tsdPtr->numFdBits = fd+1;
+ if (mask & TCL_READABLE) {
+ FD_SET(fd, &(tsdPtr->checkMasks.readable));
+ } else {
+ FD_CLR(fd, &(tsdPtr->checkMasks.readable));
+ }
+ if (mask & TCL_WRITABLE) {
+ FD_SET(fd, &(tsdPtr->checkMasks.writable));
+ } else {
+ FD_CLR(fd, &(tsdPtr->checkMasks.writable));
+ }
+ if (mask & TCL_EXCEPTION) {
+ FD_SET(fd, &(tsdPtr->checkMasks.exceptional));
+ } else {
+ FD_CLR(fd, &(tsdPtr->checkMasks.exceptional));
+ }
+ if (tsdPtr->numFdBits <= fd) {
+ tsdPtr->numFdBits = fd+1;
+ }
}
}
@@ -500,70 +511,69 @@ Tcl_DeleteFileHandler(
int fd) /* Stream id for which to remove callback
* function. */
{
- FileHandler *filePtr, *prevPtr;
- int i;
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
-
- if (tclStubs.tcl_DeleteFileHandler !=
- tclOriginalNotifier.deleteFileHandlerProc) {
- tclStubs.tcl_DeleteFileHandler(fd);
+ if (tclNotifierHooks.deleteFileHandlerProc) {
+ tclNotifierHooks.deleteFileHandlerProc(fd);
return;
- }
+ } else {
+ FileHandler *filePtr, *prevPtr;
+ int i;
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
- /*
- * Find the entry for the given file (and return if there isn't one).
- */
+ /*
+ * Find the entry for the given file (and return if there isn't one).
+ */
- for (prevPtr = NULL, filePtr = tsdPtr->firstFileHandlerPtr; ;
- prevPtr = filePtr, filePtr = filePtr->nextPtr) {
- if (filePtr == NULL) {
- return;
- }
- if (filePtr->fd == fd) {
- break;
+ for (prevPtr = NULL, filePtr = tsdPtr->firstFileHandlerPtr; ;
+ prevPtr = filePtr, filePtr = filePtr->nextPtr) {
+ if (filePtr == NULL) {
+ return;
+ }
+ if (filePtr->fd == fd) {
+ break;
+ }
}
- }
- /*
- * Update the check masks for this file.
- */
+ /*
+ * Update the check masks for this file.
+ */
- if (filePtr->mask & TCL_READABLE) {
- FD_CLR(fd, &(tsdPtr->checkMasks.readable));
- }
- if (filePtr->mask & TCL_WRITABLE) {
- FD_CLR(fd, &(tsdPtr->checkMasks.writable));
- }
- if (filePtr->mask & TCL_EXCEPTION) {
- FD_CLR(fd, &(tsdPtr->checkMasks.exceptional));
- }
+ if (filePtr->mask & TCL_READABLE) {
+ FD_CLR(fd, &(tsdPtr->checkMasks.readable));
+ }
+ if (filePtr->mask & TCL_WRITABLE) {
+ FD_CLR(fd, &(tsdPtr->checkMasks.writable));
+ }
+ if (filePtr->mask & TCL_EXCEPTION) {
+ FD_CLR(fd, &(tsdPtr->checkMasks.exceptional));
+ }
- /*
- * Find current max fd.
- */
+ /*
+ * Find current max fd.
+ */
- if (fd+1 == tsdPtr->numFdBits) {
- tsdPtr->numFdBits = 0;
- for (i = fd-1; i >= 0; i--) {
- if (FD_ISSET(i, &(tsdPtr->checkMasks.readable))
- || FD_ISSET(i, &(tsdPtr->checkMasks.writable))
- || FD_ISSET(i, &(tsdPtr->checkMasks.exceptional))) {
- tsdPtr->numFdBits = i+1;
- break;
+ if (fd+1 == tsdPtr->numFdBits) {
+ tsdPtr->numFdBits = 0;
+ for (i = fd-1; i >= 0; i--) {
+ if (FD_ISSET(i, &(tsdPtr->checkMasks.readable))
+ || FD_ISSET(i, &(tsdPtr->checkMasks.writable))
+ || FD_ISSET(i, &(tsdPtr->checkMasks.exceptional))) {
+ tsdPtr->numFdBits = i+1;
+ break;
+ }
}
}
- }
- /*
- * Clean up information in the callback record.
- */
+ /*
+ * Clean up information in the callback record.
+ */
- if (prevPtr == NULL) {
- tsdPtr->firstFileHandlerPtr = filePtr->nextPtr;
- } else {
- prevPtr->nextPtr = filePtr->nextPtr;
+ if (prevPtr == NULL) {
+ tsdPtr->firstFileHandlerPtr = filePtr->nextPtr;
+ } else {
+ prevPtr->nextPtr = filePtr->nextPtr;
+ }
+ ckfree((char *) filePtr);
}
- ckfree((char *) filePtr);
}
/*
@@ -661,216 +671,219 @@ int
Tcl_WaitForEvent(
Tcl_Time *timePtr) /* Maximum block time, or NULL. */
{
- FileHandler *filePtr;
- FileHandlerEvent *fileEvPtr;
- int mask;
- Tcl_Time myTime;
+ if (tclNotifierHooks.waitForEventProc) {
+ return tclNotifierHooks.waitForEventProc(timePtr);
+ } else {
+ FileHandler *filePtr;
+ FileHandlerEvent *fileEvPtr;
+ int mask;
+ Tcl_Time myTime;
#ifdef TCL_THREADS
- int waitForFiles;
- Tcl_Time *myTimePtr;
+ int waitForFiles;
+ Tcl_Time *myTimePtr;
#else
- /*
- * Impl. notes: timeout & timeoutPtr are used if, and only if threads are
- * not enabled. They are the arguments for the regular select() used when
- * the core is not thread-enabled.
- */
+ /*
+ * Impl. notes: timeout & timeoutPtr are used if, and only if threads
+ * are not enabled. They are the arguments for the regular select()
+ * used when the core is not thread-enabled.
+ */
- struct timeval timeout, *timeoutPtr;
- int numFound;
+ struct timeval timeout, *timeoutPtr;
+ int numFound;
#endif /* TCL_THREADS */
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
-
- if (tclStubs.tcl_WaitForEvent != tclOriginalNotifier.waitForEventProc) {
- return tclStubs.tcl_WaitForEvent(timePtr);
- }
-
- /*
- * 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.
- */
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
- 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.
+ * 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.
*/
- myTime.sec = timePtr->sec;
- myTime.usec = timePtr->usec;
+ 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.
+ */
+
+ myTime.sec = timePtr->sec;
+ myTime.usec = timePtr->usec;
- if (myTime.sec != 0 || myTime.usec != 0) {
- (*tclScaleTimeProcPtr) (&myTime, tclTimeClientData);
- }
+ if (myTime.sec != 0 || myTime.usec != 0) {
+ (*tclScaleTimeProcPtr) (&myTime, tclTimeClientData);
+ }
#ifdef TCL_THREADS
- myTimePtr = &myTime;
+ myTimePtr = &myTime;
#else
- timeout.tv_sec = myTime.sec;
- timeout.tv_usec = myTime.usec;
- timeoutPtr = &timeout;
+ timeout.tv_sec = myTime.sec;
+ timeout.tv_usec = myTime.usec;
+ timeoutPtr = &timeout;
#endif /* TCL_THREADS */
#ifndef TCL_THREADS
- } else if (tsdPtr->numFdBits == 0) {
- /*
- * If there are no threads, no timeout, and no fds registered, then
- * there are no events possible and we must avoid deadlock. Note that
- * this is not entirely correct because there might be a signal that
- * could interrupt the select call, but we don't handle that case if
- * we aren't using threads.
- */
+ } else if (tsdPtr->numFdBits == 0) {
+ /*
+ * If there are no threads, no timeout, and no fds registered, then
+ * there are no events possible and we must avoid deadlock. Note
+ * that this is not entirely correct because there might be a
+ * signal that could interrupt the select call, but we don't handle
+ * that case if we aren't using threads.
+ */
- return -1;
+ return -1;
#endif /* !TCL_THREADS */
- } else {
+ } else {
#ifdef TCL_THREADS
- myTimePtr = NULL;
+ myTimePtr = NULL;
#else
- timeoutPtr = NULL;
+ timeoutPtr = NULL;
#endif /* TCL_THREADS */
- }
+ }
#ifdef TCL_THREADS
- /*
- * Place this thread on the list of interested threads, signal the
- * notifier thread, and wait for a response or a timeout.
- */
+ /*
+ * Place this thread on the list of interested threads, signal the
+ * notifier thread, and wait for a response or a timeout.
+ */
- Tcl_MutexLock(&notifierMutex);
+ Tcl_MutexLock(&notifierMutex);
- waitForFiles = (tsdPtr->numFdBits > 0);
- if (myTimePtr != NULL && myTimePtr->sec == 0 && (myTimePtr->usec == 0
+ waitForFiles = (tsdPtr->numFdBits > 0);
+ if (myTimePtr != NULL && myTimePtr->sec == 0 && (myTimePtr->usec == 0
#if defined(__APPLE__) && defined(__LP64__)
+ /*
+ * On 64-bit Darwin, pthread_cond_timedwait() appears to have a
+ * bug that causes it to wait forever when passed an absolute
+ * time which has already been exceeded by the system time; as
+ * a workaround, when given a very brief timeout, just do a
+ * poll. [Bug 1457797]
+ */
+ || myTimePtr->usec < 10
+#endif
+ )) {
/*
- * On 64-bit Darwin, pthread_cond_timedwait() appears to have a bug
- * that causes it to wait forever when passed an absolute time which
- * has already been exceeded by the system time; as a workaround,
- * when given a very brief timeout, just do a poll. [Bug 1457797]
+ * Cannot emulate a polling select with a polling condition
+ * variable. Instead, pretend to wait for files and tell the
+ * notifier thread what we are doing. The notifier thread makes
+ * sure it goes through select with its select mask in the same
+ * state as ours currently is. We block until that happens.
*/
- || myTimePtr->usec < 10
-#endif
- )) {
- /*
- * Cannot emulate a polling select with a polling condition variable.
- * Instead, pretend to wait for files and tell the notifier thread
- * what we are doing. The notifier thread makes sure it goes through
- * select with its select mask in the same state as ours currently is.
- * We block until that happens.
- */
- waitForFiles = 1;
- tsdPtr->pollState = POLL_WANT;
- myTimePtr = NULL;
- } else {
- tsdPtr->pollState = 0;
- }
-
- if (waitForFiles) {
- /*
- * Add the ThreadSpecificData structure of this thread to the list of
- * ThreadSpecificData structures of all threads that are waiting on
- * file events.
- */
-
- tsdPtr->nextPtr = waitingListPtr;
- if (waitingListPtr) {
- waitingListPtr->prevPtr = tsdPtr;
+ waitForFiles = 1;
+ tsdPtr->pollState = POLL_WANT;
+ myTimePtr = NULL;
+ } else {
+ tsdPtr->pollState = 0;
}
- tsdPtr->prevPtr = 0;
- waitingListPtr = tsdPtr;
- tsdPtr->onList = 1;
-
- write(triggerPipe, "", 1);
- }
-
- FD_ZERO(&(tsdPtr->readyMasks.readable));
- FD_ZERO(&(tsdPtr->readyMasks.writable));
- FD_ZERO(&(tsdPtr->readyMasks.exceptional));
- if (!tsdPtr->eventReady) {
- Tcl_ConditionWait(&tsdPtr->waitCV, &notifierMutex, myTimePtr);
- }
- tsdPtr->eventReady = 0;
+ if (waitForFiles) {
+ /*
+ * Add the ThreadSpecificData structure of this thread to the list
+ * of ThreadSpecificData structures of all threads that are waiting
+ * on file events.
+ */
- if (waitForFiles && tsdPtr->onList) {
- /*
- * Remove the ThreadSpecificData structure of this thread from the
- * waiting list. Alert the notifier thread to recompute its select
- * masks - skipping this caused a hang when trying to close a pipe
- * which the notifier thread was still doing a select on.
- */
+ tsdPtr->nextPtr = waitingListPtr;
+ if (waitingListPtr) {
+ waitingListPtr->prevPtr = tsdPtr;
+ }
+ tsdPtr->prevPtr = 0;
+ waitingListPtr = tsdPtr;
+ tsdPtr->onList = 1;
- if (tsdPtr->prevPtr) {
- tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr;
- } else {
- waitingListPtr = tsdPtr->nextPtr;
+ write(triggerPipe, "", 1);
}
- if (tsdPtr->nextPtr) {
- tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr;
- }
- tsdPtr->nextPtr = tsdPtr->prevPtr = NULL;
- tsdPtr->onList = 0;
- write(triggerPipe, "", 1);
- }
-
-#else
- tsdPtr->readyMasks = tsdPtr->checkMasks;
- numFound = select(tsdPtr->numFdBits, &(tsdPtr->readyMasks.readable),
- &(tsdPtr->readyMasks.writable), &(tsdPtr->readyMasks.exceptional),
- timeoutPtr);
-
- /*
- * Some systems don't clear the masks after an error, so we have to do it
- * here.
- */
- if (numFound == -1) {
FD_ZERO(&(tsdPtr->readyMasks.readable));
FD_ZERO(&(tsdPtr->readyMasks.writable));
FD_ZERO(&(tsdPtr->readyMasks.exceptional));
- }
-#endif /* TCL_THREADS */
- /*
- * Queue all detected file events before returning.
- */
+ if (!tsdPtr->eventReady) {
+ Tcl_ConditionWait(&tsdPtr->waitCV, &notifierMutex, myTimePtr);
+ }
+ tsdPtr->eventReady = 0;
- for (filePtr = tsdPtr->firstFileHandlerPtr; (filePtr != NULL);
- filePtr = filePtr->nextPtr) {
+ if (waitForFiles && tsdPtr->onList) {
+ /*
+ * Remove the ThreadSpecificData structure of this thread from the
+ * waiting list. Alert the notifier thread to recompute its select
+ * masks - skipping this caused a hang when trying to close a pipe
+ * which the notifier thread was still doing a select on.
+ */
- mask = 0;
- if (FD_ISSET(filePtr->fd, &(tsdPtr->readyMasks.readable))) {
- mask |= TCL_READABLE;
- }
- if (FD_ISSET(filePtr->fd, &(tsdPtr->readyMasks.writable))) {
- mask |= TCL_WRITABLE;
- }
- if (FD_ISSET(filePtr->fd, &(tsdPtr->readyMasks.exceptional))) {
- mask |= TCL_EXCEPTION;
+ if (tsdPtr->prevPtr) {
+ tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr;
+ } else {
+ waitingListPtr = tsdPtr->nextPtr;
+ }
+ if (tsdPtr->nextPtr) {
+ tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr;
+ }
+ tsdPtr->nextPtr = tsdPtr->prevPtr = NULL;
+ tsdPtr->onList = 0;
+ write(triggerPipe, "", 1);
}
- if (!mask) {
- continue;
+#else
+ tsdPtr->readyMasks = tsdPtr->checkMasks;
+ numFound = select(tsdPtr->numFdBits, &(tsdPtr->readyMasks.readable),
+ &(tsdPtr->readyMasks.writable),
+ &(tsdPtr->readyMasks.exceptional), timeoutPtr);
+
+ /*
+ * Some systems don't clear the masks after an error, so we have to do
+ * it here.
+ */
+
+ if (numFound == -1) {
+ FD_ZERO(&(tsdPtr->readyMasks.readable));
+ FD_ZERO(&(tsdPtr->readyMasks.writable));
+ FD_ZERO(&(tsdPtr->readyMasks.exceptional));
}
+#endif /* TCL_THREADS */
/*
- * Don't bother to queue an event if the mask was previously non-zero
- * since an event must still be on the queue.
+ * Queue all detected file events before returning.
*/
- if (filePtr->readyMask == 0) {
- fileEvPtr = (FileHandlerEvent *) ckalloc(sizeof(FileHandlerEvent));
- fileEvPtr->header.proc = FileHandlerEventProc;
- fileEvPtr->fd = filePtr->fd;
- Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL);
+ for (filePtr = tsdPtr->firstFileHandlerPtr; (filePtr != NULL);
+ filePtr = filePtr->nextPtr) {
+
+ mask = 0;
+ if (FD_ISSET(filePtr->fd, &(tsdPtr->readyMasks.readable))) {
+ mask |= TCL_READABLE;
+ }
+ if (FD_ISSET(filePtr->fd, &(tsdPtr->readyMasks.writable))) {
+ mask |= TCL_WRITABLE;
+ }
+ if (FD_ISSET(filePtr->fd, &(tsdPtr->readyMasks.exceptional))) {
+ mask |= TCL_EXCEPTION;
+ }
+
+ 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) {
+ fileEvPtr = (FileHandlerEvent *)
+ ckalloc(sizeof(FileHandlerEvent));
+ fileEvPtr->header.proc = FileHandlerEventProc;
+ fileEvPtr->fd = filePtr->fd;
+ Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL);
+ }
+ filePtr->readyMask = mask;
}
- filePtr->readyMask = mask;
- }
#ifdef TCL_THREADS
- Tcl_MutexUnlock(&notifierMutex);
+ Tcl_MutexUnlock(&notifierMutex);
#endif /* TCL_THREADS */
- return 0;
+ return 0;
+ }
}
#ifdef TCL_THREADS
diff --git a/win/tclWinNotify.c b/win/tclWinNotify.c
index d1cbf74..5f2365d 100644
--- a/win/tclWinNotify.c
+++ b/win/tclWinNotify.c
@@ -10,7 +10,7 @@
* See the file "license.terms" for information on usage and redistribution of
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclWinNotify.c,v 1.21 2005/11/04 00:06:50 dkf Exp $
+ * RCS: @(#) $Id: tclWinNotify.c,v 1.22 2008/04/16 14:29:26 das Exp $
*/
#include "tclInt.h"
@@ -44,9 +44,6 @@ typedef struct ThreadSpecificData {
static Tcl_ThreadDataKey dataKey;
-extern TclStubs tclStubs;
-extern Tcl_NotifierProcs tclOriginalNotifier;
-
/*
* The following static indicates the number of threads that have initialized
* notifiers. It controls the lifetime of the TclNotifier window class.
@@ -83,45 +80,49 @@ static LRESULT CALLBACK NotifierProc(HWND hwnd, UINT message,
ClientData
Tcl_InitNotifier(void)
{
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
- WNDCLASS class;
+ if (tclNotifierHooks.initNotifierProc) {
+ return tclNotifierHooks.initNotifierProc();
+ } else {
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+ WNDCLASS class;
- /*
- * Register Notifier window class if this is the first thread to use this
- * module.
- */
+ /*
+ * Register Notifier window class if this is the first thread to use
+ * this module.
+ */
- Tcl_MutexLock(&notifierMutex);
- if (notifierCount == 0) {
- class.style = 0;
- class.cbClsExtra = 0;
- class.cbWndExtra = 0;
- class.hInstance = TclWinGetTclInstance();
- class.hbrBackground = NULL;
- class.lpszMenuName = NULL;
- class.lpszClassName = "TclNotifier";
- class.lpfnWndProc = NotifierProc;
- class.hIcon = NULL;
- class.hCursor = NULL;
-
- if (!RegisterClassA(&class)) {
- Tcl_Panic("Unable to register TclNotifier window class");
+ Tcl_MutexLock(&notifierMutex);
+ if (notifierCount == 0) {
+ class.style = 0;
+ class.cbClsExtra = 0;
+ class.cbWndExtra = 0;
+ class.hInstance = TclWinGetTclInstance();
+ class.hbrBackground = NULL;
+ class.lpszMenuName = NULL;
+ class.lpszClassName = "TclNotifier";
+ class.lpfnWndProc = NotifierProc;
+ class.hIcon = NULL;
+ class.hCursor = NULL;
+
+ if (!RegisterClassA(&class)) {
+ Tcl_Panic("Unable to register TclNotifier window class");
+ }
}
- }
- notifierCount++;
- Tcl_MutexUnlock(&notifierMutex);
+ notifierCount++;
+ Tcl_MutexUnlock(&notifierMutex);
- tsdPtr->pending = 0;
- tsdPtr->timerActive = 0;
+ tsdPtr->pending = 0;
+ tsdPtr->timerActive = 0;
- InitializeCriticalSection(&tsdPtr->crit);
+ InitializeCriticalSection(&tsdPtr->crit);
- tsdPtr->hwnd = NULL;
- tsdPtr->thread = GetCurrentThreadId();
- tsdPtr->event = CreateEvent(NULL, TRUE /* manual */,
- FALSE /* !signaled */, NULL);
+ tsdPtr->hwnd = NULL;
+ tsdPtr->thread = GetCurrentThreadId();
+ tsdPtr->event = CreateEvent(NULL, TRUE /* manual */,
+ FALSE /* !signaled */, NULL);
- return (ClientData) tsdPtr;
+ return (ClientData) tsdPtr;
+ }
}
/*
@@ -145,46 +146,51 @@ void
Tcl_FinalizeNotifier(
ClientData clientData) /* Pointer to notifier data. */
{
- ThreadSpecificData *tsdPtr = (ThreadSpecificData *) clientData;
+ if (tclNotifierHooks.finalizeNotifierProc) {
+ tclNotifierHooks.finalizeNotifierProc(clientData);
+ return;
+ } else {
+ ThreadSpecificData *tsdPtr = (ThreadSpecificData *) clientData;
- /*
- * Only finalize the notifier if a notifier was installed in the current
- * thread; there is a route in which this is not guaranteed to be true
- * (when tclWin32Dll.c:DllMain() is called with the flag
- * DLL_PROCESS_DETACH by the OS, which could be doing so from a thread
- * that's never previously been involved with Tcl, e.g. the task manager)
- * so this check is important.
- *
- * Fixes Bug #217982 reported by Hugh Vu and Gene Leache.
- */
+ /*
+ * Only finalize the notifier if a notifier was installed in the
+ * current thread; there is a route in which this is not guaranteed to
+ * be true (when tclWin32Dll.c:DllMain() is called with the flag
+ * DLL_PROCESS_DETACH by the OS, which could be doing so from a thread
+ * that's never previously been involved with Tcl, e.g. the task
+ * manager) so this check is important.
+ *
+ * Fixes Bug #217982 reported by Hugh Vu and Gene Leache.
+ */
- if (tsdPtr == NULL) {
- return;
- }
+ if (tsdPtr == NULL) {
+ return;
+ }
- DeleteCriticalSection(&tsdPtr->crit);
- CloseHandle(tsdPtr->event);
+ DeleteCriticalSection(&tsdPtr->crit);
+ CloseHandle(tsdPtr->event);
- /*
- * Clean up the timer and messaging window for this thread.
- */
+ /*
+ * Clean up the timer and messaging window for this thread.
+ */
- if (tsdPtr->hwnd) {
- KillTimer(tsdPtr->hwnd, INTERVAL_TIMER);
- DestroyWindow(tsdPtr->hwnd);
- }
+ if (tsdPtr->hwnd) {
+ KillTimer(tsdPtr->hwnd, INTERVAL_TIMER);
+ DestroyWindow(tsdPtr->hwnd);
+ }
- /*
- * If this is the last thread to use the notifier, unregister the notifier
- * window class.
- */
+ /*
+ * If this is the last thread to use the notifier, unregister the
+ * notifier window class.
+ */
- Tcl_MutexLock(&notifierMutex);
- notifierCount--;
- if (notifierCount == 0) {
- UnregisterClassA("TclNotifier", TclWinGetTclInstance());
+ Tcl_MutexLock(&notifierMutex);
+ notifierCount--;
+ if (notifierCount == 0) {
+ UnregisterClassA("TclNotifier", TclWinGetTclInstance());
+ }
+ Tcl_MutexUnlock(&notifierMutex);
}
- Tcl_MutexUnlock(&notifierMutex);
}
/*
@@ -213,27 +219,32 @@ void
Tcl_AlertNotifier(
ClientData clientData) /* Pointer to thread data. */
{
- ThreadSpecificData *tsdPtr = (ThreadSpecificData *) clientData;
-
- /*
- * Note that we do not need to lock around access to the hwnd because the
- * race condition has no effect since any race condition implies that the
- * notifier thread is already awake.
- */
+ if (tclNotifierHooks.alertNotifierProc) {
+ tclNotifierHooks.alertNotifierProc(clientData);
+ return;
+ } else {
+ ThreadSpecificData *tsdPtr = (ThreadSpecificData *) clientData;
- if (tsdPtr->hwnd) {
/*
- * We do need to lock around access to the pending flag.
+ * Note that we do not need to lock around access to the hwnd because
+ * the race condition has no effect since any race condition implies
+ * that the notifier thread is already awake.
*/
- EnterCriticalSection(&tsdPtr->crit);
- if (!tsdPtr->pending) {
- PostMessage(tsdPtr->hwnd, WM_WAKEUP, 0, 0);
+ if (tsdPtr->hwnd) {
+ /*
+ * We do need to lock around access to the pending flag.
+ */
+
+ EnterCriticalSection(&tsdPtr->crit);
+ if (!tsdPtr->pending) {
+ PostMessage(tsdPtr->hwnd, WM_WAKEUP, 0, 0);
+ }
+ tsdPtr->pending = 1;
+ LeaveCriticalSection(&tsdPtr->crit);
+ } else {
+ SetEvent(tsdPtr->event);
}
- tsdPtr->pending = 1;
- LeaveCriticalSection(&tsdPtr->crit);
- } else {
- SetEvent(tsdPtr->event);
}
}
@@ -259,50 +270,45 @@ void
Tcl_SetTimer(
Tcl_Time *timePtr) /* Maximum block time, or NULL. */
{
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
- UINT timeout;
-
- /*
- * Allow the notifier to be hooked. This may not make sense on Windows,
- * but mirrors the UNIX hook.
- */
-
- if (tclStubs.tcl_SetTimer != tclOriginalNotifier.setTimerProc) {
- tclStubs.tcl_SetTimer(timePtr);
- return;
- }
-
- /*
- * We only need to set up an interval timer if we're being called from an
- * external event loop. If we don't have a window handle then we just
- * return immediately and let Tcl_WaitForEvent handle timeouts.
- */
-
- if (!tsdPtr->hwnd) {
+ if (tclNotifierHooks.setTimerProc) {
+ tclNotifierHooks.setTimerProc(timePtr);
return;
- }
-
- if (!timePtr) {
- timeout = 0;
} else {
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+ UINT timeout;
+
/*
- * Make sure we pass a non-zero value into the timeout argument.
- * Windows seems to get confused by zero length timers.
+ * We only need to set up an interval timer if we're being called from
+ * an external event loop. If we don't have a window handle then we
+ * just return immediately and let Tcl_WaitForEvent handle timeouts.
*/
- timeout = timePtr->sec * 1000 + timePtr->usec / 1000;
- if (timeout == 0) {
- timeout = 1;
+ if (!tsdPtr->hwnd) {
+ return;
+ }
+
+ if (!timePtr) {
+ timeout = 0;
+ } else {
+ /*
+ * Make sure we pass a non-zero value into the timeout argument.
+ * Windows seems to get confused by zero length timers.
+ */
+
+ timeout = timePtr->sec * 1000 + timePtr->usec / 1000;
+ if (timeout == 0) {
+ timeout = 1;
+ }
+ }
+ tsdPtr->timeout = timeout;
+ if (timeout != 0) {
+ tsdPtr->timerActive = 1;
+ SetTimer(tsdPtr->hwnd, INTERVAL_TIMER,
+ (unsigned long) tsdPtr->timeout, NULL);
+ } else {
+ tsdPtr->timerActive = 0;
+ KillTimer(tsdPtr->hwnd, INTERVAL_TIMER);
}
- }
- tsdPtr->timeout = timeout;
- if (timeout != 0) {
- tsdPtr->timerActive = 1;
- SetTimer(tsdPtr->hwnd, INTERVAL_TIMER, (unsigned long) tsdPtr->timeout,
- NULL);
- } else {
- tsdPtr->timerActive = 0;
- KillTimer(tsdPtr->hwnd, INTERVAL_TIMER);
}
}
@@ -328,29 +334,36 @@ Tcl_ServiceModeHook(
int mode) /* Either TCL_SERVICE_ALL, or
* TCL_SERVICE_NONE. */
{
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
-
- /*
- * If this is the first time that the notifier has been used from a modal
- * loop, then create a communication window. Note that after this point,
- * the application needs to service events in a timely fashion or Windows
- * will hang waiting for the window to respond to synchronous system
- * messages. At some point, we may want to consider destroying the window
- * if we leave the modal loop, but for now we'll leave it around.
- */
-
- if (mode == TCL_SERVICE_ALL && !tsdPtr->hwnd) {
- tsdPtr->hwnd = CreateWindowA("TclNotifier", "TclNotifier", WS_TILED,
- 0, 0, 0, 0, NULL, NULL, TclWinGetTclInstance(), NULL);
+ if (tclNotifierHooks.serviceModeHookProc) {
+ tclNotifierHooks.serviceModeHookProc(mode);
+ return;
+ } else {
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
/*
- * Send an initial message to the window to ensure that we wake up the
- * notifier once we get into the modal loop. This will force the
- * notifier to recompute the timeout value and schedule a timer if one
- * is needed.
+ * If this is the first time that the notifier has been used from a
+ * modal loop, then create a communication window. Note that after this
+ * point, the application needs to service events in a timely fashion
+ * or Windows will hang waiting for the window to respond to
+ * synchronous system messages. At some point, we may want to consider
+ * destroying the window if we leave the modal loop, but for now we'll
+ * leave it around.
*/
- Tcl_AlertNotifier((ClientData)tsdPtr);
+ if (mode == TCL_SERVICE_ALL && !tsdPtr->hwnd) {
+ tsdPtr->hwnd = CreateWindowA("TclNotifier", "TclNotifier",
+ WS_TILED, 0, 0, 0, 0, NULL, NULL, TclWinGetTclInstance(),
+ NULL);
+
+ /*
+ * Send an initial message to the window to ensure that we wake up
+ * the notifier once we get into the modal loop. This will force
+ * the notifier to recompute the timeout value and schedule a timer
+ * if one is needed.
+ */
+
+ Tcl_AlertNotifier((ClientData)tsdPtr);
+ }
}
}
@@ -420,105 +433,100 @@ int
Tcl_WaitForEvent(
Tcl_Time *timePtr) /* Maximum block time, or NULL. */
{
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
- MSG msg;
- DWORD timeout, result;
- int status;
-
- /*
- * Allow the notifier to be hooked. This may not make sense on windows,
- * but mirrors the UNIX hook.
- */
-
- if (tclStubs.tcl_WaitForEvent != tclOriginalNotifier.waitForEventProc) {
- return tclStubs.tcl_WaitForEvent(timePtr);
- }
-
- /*
- * Compute the timeout in milliseconds.
- */
+ if (tclNotifierHooks.waitForEventProc) {
+ return tclNotifierHooks.waitForEventProc(timePtr);
+ } else {
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+ MSG msg;
+ DWORD timeout, result;
+ int status;
- if (timePtr) {
/*
- * TIP #233 (Virtualized Time). Convert virtual domain delay to
- * real-time.
+ * Compute the timeout in milliseconds.
*/
- Tcl_Time myTime;
+ if (timePtr) {
+ /*
+ * TIP #233 (Virtualized Time). Convert virtual domain delay to
+ * real-time.
+ */
- myTime.sec = timePtr->sec;
- myTime.usec = timePtr->usec;
+ Tcl_Time myTime;
- if (myTime.sec != 0 || myTime.usec != 0) {
- (*tclScaleTimeProcPtr) (&myTime, tclTimeClientData);
- }
+ myTime.sec = timePtr->sec;
+ myTime.usec = timePtr->usec;
- timeout = myTime.sec * 1000 + myTime.usec / 1000;
- } else {
- timeout = INFINITE;
- }
+ if (myTime.sec != 0 || myTime.usec != 0) {
+ (*tclScaleTimeProcPtr) (&myTime, tclTimeClientData);
+ }
- /*
- * Check to see if there are any messages in the queue before waiting
- * because MsgWaitForMultipleObjects will not wake up if there are events
- * currently sitting in the queue.
- */
+ timeout = myTime.sec * 1000 + myTime.usec / 1000;
+ } else {
+ timeout = INFINITE;
+ }
- if (!PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
/*
- * Wait for something to happen (a signal from another thread, a
- * message, or timeout) or loop servicing asynchronous procedure calls
- * queued to this thread.
+ * Check to see if there are any messages in the queue before waiting
+ * because MsgWaitForMultipleObjects will not wake up if there are
+ * events currently sitting in the queue.
*/
- again:
- result = MsgWaitForMultipleObjectsEx(1, &tsdPtr->event, timeout,
- QS_ALLINPUT, MWMO_ALERTABLE);
- if (result == WAIT_IO_COMPLETION) {
- goto again;
- } else if (result == WAIT_FAILED) {
- status = -1;
- goto end;
- }
- }
+ if (!PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
+ /*
+ * Wait for something to happen (a signal from another thread, a
+ * message, or timeout) or loop servicing asynchronous procedure
+ * calls queued to this thread.
+ */
- /*
- * Check to see if there are any messages to process.
- */
+ again:
+ result = MsgWaitForMultipleObjectsEx(1, &tsdPtr->event, timeout,
+ QS_ALLINPUT, MWMO_ALERTABLE);
+ if (result == WAIT_IO_COMPLETION) {
+ goto again;
+ } else if (result == WAIT_FAILED) {
+ status = -1;
+ goto end;
+ }
+ }
- if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
/*
- * Retrieve and dispatch the first message.
+ * Check to see if there are any messages to process.
*/
- result = GetMessage(&msg, NULL, 0, 0);
- if (result == 0) {
- /*
- * We received a request to exit this thread (WM_QUIT), so
- * propagate the quit message and start unwinding.
- */
-
- PostQuitMessage((int) msg.wParam);
- status = -1;
- } else if (result == -1) {
+ if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
/*
- * We got an error from the system. I have no idea why this would
- * happen, so we'll just unwind.
+ * Retrieve and dispatch the first message.
*/
- status = -1;
+ result = GetMessage(&msg, NULL, 0, 0);
+ if (result == 0) {
+ /*
+ * We received a request to exit this thread (WM_QUIT), so
+ * propagate the quit message and start unwinding.
+ */
+
+ PostQuitMessage((int) msg.wParam);
+ status = -1;
+ } else if (result == -1) {
+ /*
+ * We got an error from the system. I have no idea why this
+ * would happen, so we'll just unwind.
+ */
+
+ status = -1;
+ } else {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ status = 1;
+ }
} else {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- status = 1;
+ status = 0;
}
- } else {
- status = 0;
- }
- end:
- ResetEvent(tsdPtr->event);
- return status;
+ end:
+ ResetEvent(tsdPtr->event);
+ return status;
+ }
}
/*