summaryrefslogtreecommitdiffstats
path: root/generic
diff options
context:
space:
mode:
Diffstat (limited to 'generic')
-rw-r--r--generic/tclAlloc.c2
-rw-r--r--generic/tclInt.h75
-rw-r--r--generic/tclNotify.c298
-rw-r--r--generic/tclThreadJoin.c2
4 files changed, 366 insertions, 11 deletions
diff --git a/generic/tclAlloc.c b/generic/tclAlloc.c
index 2043248..03655b9 100644
--- a/generic/tclAlloc.c
+++ b/generic/tclAlloc.c
@@ -748,6 +748,8 @@ TclpRealloc(
}
#endif /* !USE_TCLALLOC */
+#else
+TCL_MAC_EMPTY_FILE(generic_tclAlloc_c)
#endif /* !TCL_THREADS */
/*
diff --git a/generic/tclInt.h b/generic/tclInt.h
index 75167df..fa661d6 100644
--- a/generic/tclInt.h
+++ b/generic/tclInt.h
@@ -2739,7 +2739,6 @@ MODULE_SCOPE char *tclNativeExecutableName;
MODULE_SCOPE int tclFindExecutableSearchDone;
MODULE_SCOPE char *tclMemDumpFileName;
MODULE_SCOPE TclPlatformType tclPlatform;
-MODULE_SCOPE Tcl_NotifierProcs tclNotifierHooks;
MODULE_SCOPE Tcl_Encoding tclIdentityEncoding;
@@ -3133,12 +3132,21 @@ MODULE_SCOPE int TclProcessReturn(Tcl_Interp *interp,
int code, int level, Tcl_Obj *returnOpts);
MODULE_SCOPE int TclpObjLstat(Tcl_Obj *pathPtr, Tcl_StatBuf *buf);
MODULE_SCOPE Tcl_Obj * TclpTempFileName(void);
-MODULE_SCOPE Tcl_Obj * TclpTempFileNameForLibrary(Tcl_Interp *interp, Tcl_Obj* pathPtr);
+MODULE_SCOPE Tcl_Obj * TclpTempFileNameForLibrary(Tcl_Interp *interp,
+ Tcl_Obj* pathPtr);
MODULE_SCOPE Tcl_Obj * TclNewFSPathObj(Tcl_Obj *dirPtr, const char *addStrRep,
int len);
+MODULE_SCOPE void TclpAlertNotifier(ClientData clientData);
+MODULE_SCOPE void TclpServiceModeHook(int mode);
+MODULE_SCOPE void TclpSetTimer(const Tcl_Time *timePtr);
+MODULE_SCOPE int TclpWaitForEvent(const Tcl_Time *timePtr);
+MODULE_SCOPE void TclpCreateFileHandler(int fd, int mask,
+ Tcl_FileProc *proc, ClientData clientData);
MODULE_SCOPE int TclpDeleteFile(const void *path);
+MODULE_SCOPE void TclpDeleteFileHandler(int fd);
MODULE_SCOPE void TclpFinalizeCondition(Tcl_Condition *condPtr);
MODULE_SCOPE void TclpFinalizeMutex(Tcl_Mutex *mutexPtr);
+MODULE_SCOPE void TclpFinalizeNotifier(ClientData clientData);
MODULE_SCOPE void TclpFinalizePipes(void);
MODULE_SCOPE void TclpFinalizeSockets(void);
MODULE_SCOPE int TclCreateSocketAddress(Tcl_Interp *interp,
@@ -3152,6 +3160,7 @@ MODULE_SCOPE int TclpFindVariable(const char *name, int *lengthPtr);
MODULE_SCOPE void TclpInitLibraryPath(char **valuePtr,
unsigned int *lengthPtr, Tcl_Encoding *encodingPtr);
MODULE_SCOPE void TclpInitLock(void);
+MODULE_SCOPE ClientData TclpInitNotifier(void);
MODULE_SCOPE void TclpInitPlatform(void);
MODULE_SCOPE void TclpInitUnlock(void);
MODULE_SCOPE Tcl_Obj * TclpObjListVolumes(void);
@@ -3178,8 +3187,9 @@ MODULE_SCOPE int TclpObjChdir(Tcl_Obj *pathPtr);
MODULE_SCOPE Tcl_Channel TclpOpenTemporaryFile(Tcl_Obj *dirObj,
Tcl_Obj *basenameObj, Tcl_Obj *extensionObj,
Tcl_Obj *resultingNameObj);
-MODULE_SCOPE void TclPkgFileSeen(Tcl_Interp *interp, const char *fileName);
-MODULE_SCOPE void *TclInitPkgFiles(Tcl_Interp *interp);
+MODULE_SCOPE void TclPkgFileSeen(Tcl_Interp *interp,
+ const char *fileName);
+MODULE_SCOPE void * TclInitPkgFiles(Tcl_Interp *interp);
MODULE_SCOPE Tcl_Obj * TclPathPart(Tcl_Interp *interp, Tcl_Obj *pathPtr,
Tcl_PathPart portion);
MODULE_SCOPE char * TclpReadlink(const char *fileName,
@@ -4212,6 +4222,37 @@ MODULE_SCOPE int TclIndexDecode(int encoded, int endValue);
#define TCL_INDEX_START (0)
/*
+ *----------------------------------------------------------------------
+ *
+ * TclScaleTime --
+ *
+ * TIP #233 (Virtualized Time): Wrapper around the time virutalisation
+ * rescale function to hide the binding of the clientData.
+ *
+ * This is static inline code; it's like a macro, but a function. It's
+ * used because this is a piece of code that ends up in places that are a
+ * bit performance sensitive.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * Updates the time structure (given as an argument) with what the time
+ * should be after virtualisation.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static inline void
+TclScaleTime(
+ Tcl_Time *timePtr)
+{
+ if (timePtr != NULL) {
+ tclScaleTimeProcPtr(timePtr, tclTimeClientData);
+ }
+}
+
+/*
*----------------------------------------------------------------
* Macros used by the Tcl core to create and release Tcl objects.
* TclNewObj(objPtr) creates a new object denoting an empty string.
@@ -5145,11 +5186,33 @@ typedef struct NRE_callback {
#endif
/*
+ * Special hack for macOS, where the static linker (technically the 'ar'
+ * command) hates empty object files, and accepts no flags to make it shut up.
+ *
+ * These symbols are otherwise completely useless.
+ *
+ * They can't be written to or written through. They can't be seen by any
+ * other code. They use a separate attribute (supported by all macOS
+ * compilers, which are derivatives of clang or gcc) to stop the compilation
+ * from moaning. They will be excluded during the final linking stage.
+ *
+ * Other platforms get nothing at all. That's good.
+ */
+
+#ifdef MAC_OSX_TCL
+#define TCL_MAC_EMPTY_FILE(name) \
+ static __attribute__((used)) const void *const TclUnusedFile_ ## name; \
+ static const void *const TclUnusedFile_ ## name = NULL;
+#else
+#define TCL_MAC_EMPTY_FILE(name)
+#endif /* MAC_OSX_TCL */
+
+/*
* Other externals.
*/
-MODULE_SCOPE size_t TclEnvEpoch; /* Epoch of the tcl environment
- * (if changed with tcl-env). */
+MODULE_SCOPE size_t TclEnvEpoch; /* Epoch of the tcl environment
+ * (if changed with tcl-env). */
#endif /* _TCLINT */
diff --git a/generic/tclNotify.c b/generic/tclNotify.c
index b43413d..12b40b1 100644
--- a/generic/tclNotify.c
+++ b/generic/tclNotify.c
@@ -10,6 +10,7 @@
* Copyright © 1995-1997 Sun Microsystems, Inc.
* Copyright © 1998 Scriptics Corporation.
* Copyright © 2003 Kevin B. Kenny. All rights reserved.
+ * Copyright © 2021 Donal K. Fellows
*
* See the file "license.terms" for information on usage and redistribution of
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -18,11 +19,11 @@
#include "tclInt.h"
/*
- * Module-scope struct of notifier hooks that are checked in the default
+ * Notifier hooks that are checked in the public wrappers for the default
* notifier functions (for overriding via Tcl_SetNotifier).
*/
-Tcl_NotifierProcs tclNotifierHooks = {
+static Tcl_NotifierProcs tclNotifierHooks = {
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
};
@@ -174,7 +175,8 @@ TclFinalizeNotifier(void)
Tcl_Event *evPtr, *hold;
if (!tsdPtr->initialized) {
- return; /* Notifier not initialized for the current thread */
+ return; /* Notifier not initialized for the current
+ * thread. */
}
Tcl_MutexLock(&(tsdPtr->queueMutex));
@@ -227,6 +229,38 @@ Tcl_SetNotifier(
Tcl_NotifierProcs *notifierProcPtr)
{
tclNotifierHooks = *notifierProcPtr;
+
+ /*
+ * Don't allow hooks to refer to the hook point functions; avoids infinite
+ * loop.
+ */
+
+ if (tclNotifierHooks.setTimerProc == Tcl_SetTimer) {
+ tclNotifierHooks.setTimerProc = NULL;
+ }
+ if (tclNotifierHooks.waitForEventProc == Tcl_WaitForEvent) {
+ tclNotifierHooks.waitForEventProc = NULL;
+ }
+ if (tclNotifierHooks.initNotifierProc == Tcl_InitNotifier) {
+ tclNotifierHooks.initNotifierProc = NULL;
+ }
+ if (tclNotifierHooks.finalizeNotifierProc == Tcl_FinalizeNotifier) {
+ tclNotifierHooks.finalizeNotifierProc = NULL;
+ }
+ if (tclNotifierHooks.alertNotifierProc == Tcl_AlertNotifier) {
+ tclNotifierHooks.alertNotifierProc = NULL;
+ }
+ if (tclNotifierHooks.serviceModeHookProc == Tcl_ServiceModeHook) {
+ tclNotifierHooks.serviceModeHookProc = NULL;
+ }
+#ifndef _WIN32
+ if (tclNotifierHooks.createFileHandlerProc == Tcl_CreateFileHandler) {
+ tclNotifierHooks.createFileHandlerProc = NULL;
+ }
+ if (tclNotifierHooks.deleteFileHandlerProc == Tcl_DeleteFileHandler) {
+ tclNotifierHooks.deleteFileHandlerProc = NULL;
+ }
+#endif /* !_WIN32 */
}
/*
@@ -276,7 +310,7 @@ Tcl_CreateEventSource(
* checkProc. */
{
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
- EventSource *sourcePtr = (EventSource *)ckalloc(sizeof(EventSource));
+ EventSource *sourcePtr = (EventSource *) ckalloc(sizeof(EventSource));
sourcePtr->setupProc = setupProc;
sourcePtr->checkProc = checkProc;
@@ -794,7 +828,7 @@ Tcl_SetServiceMode(
void
Tcl_SetMaxBlockTime(
- const Tcl_Time *timePtr) /* Specifies a maximum elapsed time for the
+ const Tcl_Time *timePtr) /* Specifies a maximum elapsed time for the
* next blocking operation in the event
* tsdPtr-> */
{
@@ -1133,6 +1167,260 @@ Tcl_ThreadAlert(
}
/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_InitNotifier --
+ *
+ * Initializes the platform specific notifier state. Forwards to the
+ * platform implementation when the hook is not enabled.
+ *
+ * Results:
+ * Returns a handle to the notifier state for this thread..
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+ClientData
+Tcl_InitNotifier(void)
+{
+ if (tclNotifierHooks.initNotifierProc) {
+ return tclNotifierHooks.initNotifierProc();
+ } else {
+ return TclpInitNotifier();
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_FinalizeNotifier --
+ *
+ * This function is called to cleanup the notifier state before a thread
+ * is terminated. Forwards to the platform implementation when the hook
+ * is not enabled.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * If no finalizeNotifierProc notifier hook exists, TclpFinalizeNotifier
+ * is called.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tcl_FinalizeNotifier(
+ ClientData clientData)
+{
+ if (tclNotifierHooks.finalizeNotifierProc) {
+ tclNotifierHooks.finalizeNotifierProc(clientData);
+ } else {
+ TclpFinalizeNotifier(clientData);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_AlertNotifier --
+ *
+ * Wake up the specified notifier from any thread. This routine is called
+ * by the platform independent notifier code whenever the Tcl_ThreadAlert
+ * routine is called. This routine is guaranteed not to be called by Tcl
+ * on a given notifier after Tcl_FinalizeNotifier is called for that
+ * notifier. This routine is typically called from a thread other than
+ * the notifier's thread. Forwards to the platform implementation when
+ * the hook is not enabled.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * See the platform-specific implementations.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tcl_AlertNotifier(
+ ClientData clientData) /* Pointer to thread data. */
+{
+ if (tclNotifierHooks.alertNotifierProc) {
+ tclNotifierHooks.alertNotifierProc(clientData);
+ } else {
+ TclpAlertNotifier(clientData);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_ServiceModeHook --
+ *
+ * This function is invoked whenever the service mode changes. Forwards
+ * to the platform implementation when the hook is not enabled.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * See the platform-specific implementations.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tcl_ServiceModeHook(
+ int mode) /* Either TCL_SERVICE_ALL, or
+ * TCL_SERVICE_NONE. */
+{
+ if (tclNotifierHooks.serviceModeHookProc) {
+ tclNotifierHooks.serviceModeHookProc(mode);
+ } else {
+ TclpServiceModeHook(mode);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_SetTimer --
+ *
+ * This function sets the current notifier timer value. Forwards to the
+ * platform implementation when the hook is not enabled.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * See the platform-specific implementations.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tcl_SetTimer(
+ const Tcl_Time *timePtr) /* Timeout value, may be NULL. */
+{
+ if (tclNotifierHooks.setTimerProc) {
+ tclNotifierHooks.setTimerProc(timePtr);
+ } else {
+ TclpSetTimer(timePtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_WaitForEvent --
+ *
+ * This function is called by Tcl_DoOneEvent to wait for new events on
+ * the notifier's message queue. If the block time is 0, then
+ * Tcl_WaitForEvent just polls without blocking. Forwards to the
+ * platform implementation when the hook is not enabled.
+ *
+ * Results:
+ * Returns -1 if the wait would block forever, 1 if an out-of-loop source
+ * was processed (see platform-specific notes) and otherwise returns 0.
+ *
+ * Side effects:
+ * Queues file events that are detected by the notifier.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tcl_WaitForEvent(
+ const Tcl_Time *timePtr) /* Maximum block time, or NULL. */
+{
+ if (tclNotifierHooks.waitForEventProc) {
+ return tclNotifierHooks.waitForEventProc(timePtr);
+ } else {
+ return TclpWaitForEvent(timePtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_CreateFileHandler --
+ *
+ * This function registers a file descriptor handler with the notifier.
+ * Forwards to the platform implementation when the hook is not enabled.
+ *
+ * This function is not defined on Windows. The OS API there is too
+ * different.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Creates a new file handler structure.
+ *
+ *----------------------------------------------------------------------
+ */
+
+#ifndef _WIN32
+void
+Tcl_CreateFileHandler(
+ 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. */
+ ClientData clientData) /* Arbitrary data to pass to proc. */
+{
+ if (tclNotifierHooks.createFileHandlerProc) {
+ tclNotifierHooks.createFileHandlerProc(fd, mask, proc, clientData);
+ } else {
+ TclpCreateFileHandler(fd, mask, proc, clientData);
+ }
+}
+#endif /* !_WIN32 */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_DeleteFileHandler --
+ *
+ * Cancel a previously-arranged callback arrangement for a file
+ * descriptor. Forwards to the platform implementation when the hook is
+ * not enabled.
+ *
+ * This function is not defined on Windows. The OS API there is too
+ * different.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * If a callback was previously registered on the file descriptor, remove
+ * it.
+ *
+ *----------------------------------------------------------------------
+ */
+
+#ifndef _WIN32
+void
+Tcl_DeleteFileHandler(
+ int fd) /* Stream id for which to remove callback
+ * function. */
+{
+ if (tclNotifierHooks.deleteFileHandlerProc) {
+ tclNotifierHooks.deleteFileHandlerProc(fd);
+ } else {
+ TclpDeleteFileHandler(fd);
+ }
+}
+#endif /* !_WIN32 */
+
+/*
* Local Variables:
* mode: c
* c-basic-offset: 4
diff --git a/generic/tclThreadJoin.c b/generic/tclThreadJoin.c
index ba789d3..4d2aca5 100644
--- a/generic/tclThreadJoin.c
+++ b/generic/tclThreadJoin.c
@@ -305,6 +305,8 @@ TclSignalExitThread(
Tcl_MutexUnlock(&threadPtr->threadMutex);
}
+#else
+TCL_MAC_EMPTY_FILE(generic_tclThreadJoin_c)
#endif /* _WIN32 */
/*