From 0d6bc7fc83128e570ec80f6849bec9b144714d73 Mon Sep 17 00:00:00 2001 From: pooryorick Date: Sat, 15 May 2021 07:55:55 +0000 Subject: Separate library unloading routine from [unload] command processing. --- generic/tclLoad.c | 50 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/generic/tclLoad.c b/generic/tclLoad.c index c9d1b31..e3a7b26 100644 --- a/generic/tclLoad.c +++ b/generic/tclLoad.c @@ -12,6 +12,7 @@ #include "tclInt.h" + /* * The following structure describes a library that has been loaded either * dynamically (with the "load" command) or statically (as indicated by a call @@ -93,8 +94,12 @@ typedef struct InterpLibrary { * Prototypes for functions that are private to this file: */ -static void LoadCleanupProc(ClientData clientData, - Tcl_Interp *interp); +static void LoadCleanupProc(ClientData clientData, + Tcl_Interp *interp); +static int UnloadLibrary(Tcl_Interp *interp, Tcl_Interp *target, + LoadedLibrary *library, int keepLibrary, + const char *fullFileName); + /* *---------------------------------------------------------------------- @@ -549,10 +554,8 @@ Tcl_UnloadObjCmd( Tcl_Interp *target; /* Which interpreter to unload from. */ LoadedLibrary *libraryPtr, *defaultPtr; Tcl_DString pfx, tmp; - Tcl_LibraryUnloadProc *unloadProc; InterpLibrary *ipFirstPtr, *ipPtr; int i, index, code, complain = 1, keepLibrary = 0; - int trustedRefCount = -1, safeRefCount = -1; const char *fullFileName = ""; const char *prefix; static const char *const options[] = { @@ -741,6 +744,33 @@ Tcl_UnloadObjCmd( goto done; } + code = UnloadLibrary(interp, target, libraryPtr, keepLibrary, fullFileName); + + done: + Tcl_DStringFree(&pfx); + Tcl_DStringFree(&tmp); + if (!complain && (code != TCL_OK)) { + code = TCL_OK; + Tcl_ResetResult(interp); + } + return code; +} + +static int +UnloadLibrary( + Tcl_Interp *interp, + Tcl_Interp *target, + LoadedLibrary *libraryPtr, + int keepLibrary, + const char *fullFileName +) +{ + int code; + InterpLibrary *ipFirstPtr, *ipPtr; + LoadedLibrary *defaultPtr; + int trustedRefCount, safeRefCount; + Tcl_LibraryUnloadProc *unloadProc; + /* * Ensure that the DLL can be unloaded. If it is a trusted interpreter, * libraryPtr->unloadProc must not be NULL for the DLL to be unloadable. If @@ -771,6 +801,9 @@ Tcl_UnloadObjCmd( unloadProc = libraryPtr->unloadProc; } + + + /* * We are ready to unload the library. First, evaluate the unload * function. If this fails, we cannot proceed with unload. Also, we must @@ -889,8 +922,7 @@ Tcl_UnloadObjCmd( } } } - Tcl_SetAssocData(target, "tclLoad", LoadCleanupProc, - ipFirstPtr); + Tcl_SetAssocData(target, "tclLoad", LoadCleanupProc, ipFirstPtr); ckfree(defaultPtr->fileName); ckfree(defaultPtr->prefix); ckfree(defaultPtr); @@ -911,12 +943,6 @@ Tcl_UnloadObjCmd( } done: - Tcl_DStringFree(&pfx); - Tcl_DStringFree(&tmp); - if (!complain && (code != TCL_OK)) { - code = TCL_OK; - Tcl_ResetResult(interp); - } return code; } -- cgit v0.12 From 0f818acbb72a2b4792acb1d82f051507c76e59a2 Mon Sep 17 00:00:00 2001 From: pooryorick Date: Sat, 15 May 2021 18:24:59 +0000 Subject: Add valgrind option --keep-deguginfo=yes --- unix/Makefile.in | 1 + 1 file changed, 1 insertion(+) diff --git a/unix/Makefile.in b/unix/Makefile.in index 8bf9def..17e70d9 100644 --- a/unix/Makefile.in +++ b/unix/Makefile.in @@ -268,6 +268,7 @@ TRACE_OPTS = VALGRIND = valgrind VALGRINDARGS = --tool=memcheck --num-callers=24 \ --leak-resolution=high --leak-check=yes --show-reachable=yes -v \ + --keep-debuginfo=yes \ --suppressions=$(TOOL_DIR)/valgrind_suppress #-------------------------------------------------------------------------- -- cgit v0.12 From 1134511a980250dfb27153be57b4e64e8455cdfe Mon Sep 17 00:00:00 2001 From: pooryorick Date: Sat, 15 May 2021 18:42:40 +0000 Subject: When deleting an interp, delete associated data after running the corresponding Tcl_InterpDeleteProc instead of before to allow the Tcl_InterpDeleteProc to make use of the data. In TcltestObj.c/VarPtrDeleteProc, remove call to Tcl_DeleteAssocData that is redundant and cylic. --- generic/tclBasic.c | 14 +++++++------- generic/tclTestObj.c | 1 - 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/generic/tclBasic.c b/generic/tclBasic.c index 5ca70d4..2d10812 100644 --- a/generic/tclBasic.c +++ b/generic/tclBasic.c @@ -1826,28 +1826,28 @@ DeleteInterpProc( ckfree(hTablePtr); } - /* - * Invoke deletion callbacks; note that a callback can create new - * callbacks, so we iterate. - */ - while (iPtr->assocData != NULL) { + if (iPtr->assocData != NULL) { AssocData *dPtr; hTablePtr = iPtr->assocData; - iPtr->assocData = NULL; + /* + * Invoke deletion callbacks; note that a callback can create new + * callbacks, so we iterate. + */ for (hPtr = Tcl_FirstHashEntry(hTablePtr, &search); hPtr != NULL; hPtr = Tcl_FirstHashEntry(hTablePtr, &search)) { dPtr = (AssocData *)Tcl_GetHashValue(hPtr); - Tcl_DeleteHashEntry(hPtr); if (dPtr->proc != NULL) { dPtr->proc(dPtr->clientData, interp); } + Tcl_DeleteHashEntry(hPtr); ckfree(dPtr); } Tcl_DeleteHashTable(hTablePtr); ckfree(hTablePtr); + iPtr->assocData = NULL; } /* diff --git a/generic/tclTestObj.c b/generic/tclTestObj.c index 17546a4..4e7cec9 100644 --- a/generic/tclTestObj.c +++ b/generic/tclTestObj.c @@ -61,7 +61,6 @@ static void VarPtrDeleteProc(void *clientData, Tcl_Interp *interp) for (i = 0; i < NUMBER_OF_OBJECT_VARS; i++) { if (varPtr[i]) Tcl_DecrRefCount(varPtr[i]); } - Tcl_DeleteAssocData(interp, VARPTR_KEY); ckfree(varPtr); } -- cgit v0.12 From 7ee82af4aa11b02822159875e976d1469492e937 Mon Sep 17 00:00:00 2001 From: pooryorick Date: Sat, 15 May 2021 21:56:03 +0000 Subject: Fix [28027d8bb7745fb0], memory leaks in tclUnload.c, --- generic/tclLoad.c | 120 ++++++++++++++++++++++++++++++-------------------- tests/pkgMkIndex.test | 4 +- unix/dltest/pkgua.c | 20 ++++++--- 3 files changed, 91 insertions(+), 53 deletions(-) diff --git a/generic/tclLoad.c b/generic/tclLoad.c index 514d3c8..3d64edb 100644 --- a/generic/tclLoad.c +++ b/generic/tclLoad.c @@ -96,11 +96,19 @@ typedef struct InterpLibrary { static void LoadCleanupProc(ClientData clientData, Tcl_Interp *interp); +static int IsStatic (LoadedLibrary *libraryPtr); static int UnloadLibrary(Tcl_Interp *interp, Tcl_Interp *target, LoadedLibrary *library, int keepLibrary, - const char *fullFileName); + const char *fullFileName, int interpExiting); +static int +IsStatic (LoadedLibrary *libraryPtr) { + int res; + res = (libraryPtr->fileName[0] == '\0'); + return res; +} + /* *---------------------------------------------------------------------- * @@ -649,7 +657,7 @@ Tcl_UnloadObjCmd( * - Its prefix and file match the once we're looking for. * - Its file matches, and we weren't given a prefix. * - Its prefix matches, the file name was specified as empty, and there is - * only no statically loaded library with the same prefix. + * no statically loaded library with the same prefix. */ Tcl_MutexLock(&libraryMutex); @@ -744,7 +752,7 @@ Tcl_UnloadObjCmd( goto done; } - code = UnloadLibrary(interp, target, libraryPtr, keepLibrary, fullFileName); + code = UnloadLibrary(interp, target, libraryPtr, keepLibrary, fullFileName, 0); done: Tcl_DStringFree(&pfx); @@ -762,14 +770,15 @@ UnloadLibrary( Tcl_Interp *target, LoadedLibrary *libraryPtr, int keepLibrary, - const char *fullFileName + const char *fullFileName, + int interpExiting ) { int code; InterpLibrary *ipFirstPtr, *ipPtr; LoadedLibrary *defaultPtr; - int trustedRefCount, safeRefCount; - Tcl_LibraryUnloadProc *unloadProc; + int trustedRefCount = -1, safeRefCount = -1; + Tcl_LibraryUnloadProc *unloadProc = NULL; /* * Ensure that the DLL can be unloaded. If it is a trusted interpreter, @@ -779,31 +788,34 @@ UnloadLibrary( if (Tcl_IsSafe(target)) { if (libraryPtr->safeUnloadProc == NULL) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "file \"%s\" cannot be unloaded under a safe interpreter", - fullFileName)); - Tcl_SetErrorCode(interp, "TCL", "OPERATION", "UNLOAD", "CANNOT", - NULL); - code = TCL_ERROR; - goto done; + if (!interpExiting) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "file \"%s\" cannot be unloaded under a safe interpreter", + fullFileName)); + Tcl_SetErrorCode(interp, "TCL", "OPERATION", "UNLOAD", "CANNOT", + NULL); + code = TCL_ERROR; + goto done; + } } unloadProc = libraryPtr->safeUnloadProc; } else { if (libraryPtr->unloadProc == NULL) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "file \"%s\" cannot be unloaded under a trusted interpreter", - fullFileName)); - Tcl_SetErrorCode(interp, "TCL", "OPERATION", "UNLOAD", "CANNOT", - NULL); - code = TCL_ERROR; - goto done; + if (!interpExiting) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "file \"%s\" cannot be unloaded under a trusted interpreter", + fullFileName)); + Tcl_SetErrorCode(interp, "TCL", "OPERATION", "UNLOAD", "CANNOT", + NULL); + code = TCL_ERROR; + goto done; + } } unloadProc = libraryPtr->unloadProc; } - /* * We are ready to unload the library. First, evaluate the unload * function. If this fails, we cannot proceed with unload. Also, we must @@ -814,24 +826,30 @@ UnloadLibrary( * after the callback returns, TCL_UNLOAD_DETACH_FROM_PROCESS is passed. */ - code = TCL_UNLOAD_DETACH_FROM_INTERPRETER; - if (!keepLibrary) { - Tcl_MutexLock(&libraryMutex); - trustedRefCount = libraryPtr->interpRefCount; - safeRefCount = libraryPtr->safeInterpRefCount; - Tcl_MutexUnlock(&libraryMutex); + if (unloadProc == NULL) { + code = TCL_OK; + } else { + code = TCL_UNLOAD_DETACH_FROM_INTERPRETER; + if (!keepLibrary) { + Tcl_MutexLock(&libraryMutex); + trustedRefCount = libraryPtr->interpRefCount; + safeRefCount = libraryPtr->safeInterpRefCount; + Tcl_MutexUnlock(&libraryMutex); - if (Tcl_IsSafe(target)) { - safeRefCount--; - } else { - trustedRefCount--; - } + if (Tcl_IsSafe(target)) { + safeRefCount--; + } else { + trustedRefCount--; + } - if (safeRefCount <= 0 && trustedRefCount <= 0) { - code = TCL_UNLOAD_DETACH_FROM_PROCESS; + if (safeRefCount <= 0 && trustedRefCount <= 0) { + code = TCL_UNLOAD_DETACH_FROM_PROCESS; + } } + code = unloadProc(target, code); } - code = unloadProc(target, code); + + if (code != TCL_OK) { Tcl_TransferResult(target, code, interp); goto done; @@ -857,16 +875,20 @@ UnloadLibrary( } } } - Tcl_SetAssocData(target, "tclLoad", LoadCleanupProc, - ipFirstPtr); + ckfree(ipPtr); + Tcl_SetAssocData(target, "tclLoad", LoadCleanupProc, ipFirstPtr); + if (IsStatic(libraryPtr)) { + goto done; + } /* * The unload function executed fine. Examine the reference count to see * if we unload the DLL. */ + Tcl_MutexLock(&libraryMutex); if (Tcl_IsSafe(target)) { libraryPtr->safeInterpRefCount--; @@ -908,7 +930,7 @@ UnloadLibrary( * it's been unloaded. */ - if (libraryPtr->fileName[0] != '\0') { + if (!IsStatic(libraryPtr)) { Tcl_MutexLock(&libraryMutex); if (Tcl_FSUnloadFile(interp, libraryPtr->loadHandle) == TCL_OK) { /* @@ -931,7 +953,6 @@ UnloadLibrary( ckfree(defaultPtr->fileName); ckfree(defaultPtr->prefix); ckfree(defaultPtr); - ckfree(ipPtr); Tcl_MutexUnlock(&libraryMutex); } else { code = TCL_ERROR; @@ -1020,6 +1041,8 @@ Tcl_StaticLibrary( libraryPtr->loadHandle = NULL; libraryPtr->initProc = initProc; libraryPtr->safeInitProc = safeInitProc; + libraryPtr->unloadProc = NULL; + libraryPtr->safeUnloadProc = NULL; Tcl_MutexLock(&libraryMutex); libraryPtr->nextPtr = firstLibraryPtr; firstLibraryPtr = libraryPtr; @@ -1170,15 +1193,18 @@ static void LoadCleanupProc( ClientData clientData, /* Pointer to first InterpLibrary structure * for interp. */ - TCL_UNUSED(Tcl_Interp *)) + Tcl_Interp *interp) { - InterpLibrary *ipPtr, *nextPtr; + InterpLibrary *ipPtr; + LoadedLibrary *libraryPtr; - ipPtr = (InterpLibrary *)clientData; - while (ipPtr != NULL) { - nextPtr = ipPtr->nextPtr; - ckfree(ipPtr); - ipPtr = nextPtr; + while (1) { + ipPtr = (InterpLibrary *)Tcl_GetAssocData(interp, "tclLoad", NULL); + if (ipPtr == NULL) { + break; + } + libraryPtr = ipPtr->libraryPtr; + UnloadLibrary(interp, interp, libraryPtr, 0 ,"", 1); } } @@ -1223,7 +1249,7 @@ TclFinalizeLoad(void) * it has been unloaded. */ - if (libraryPtr->fileName[0] != '\0') { + if (!IsStatic(libraryPtr)) { Tcl_FSUnloadFile(NULL, libraryPtr->loadHandle); } #endif diff --git a/tests/pkgMkIndex.test b/tests/pkgMkIndex.test index df49c32..002efcc 100644 --- a/tests/pkgMkIndex.test +++ b/tests/pkgMkIndex.test @@ -577,19 +577,21 @@ test pkgMkIndex-10.1 {package in DLL and script} [list exec $dll] { exec [interpreter] << $cmd pkgtest::runCreatedIndex {0 {}} -lazy $fullPkgPath pkga[info sharedlibextension] pkga.tcl } "0 {{pkga:1.0 {tclPkgSetup {pkga[info sharedlibextension] load {pkga_eq pkga_quote}} {pkga.tcl source pkga_neq}}}}" + test pkgMkIndex-10.2 {package in DLL hidden by -load} [list exec $dll] { # Do all [load]ing of shared libraries in another process, so we can # delete the file and not get stuck because we're holding a reference to # it. # # This test depends on context from prior test, so repeat it. + set script \ "[list pkg_mkIndex -lazy $fullPkgPath [file tail $x] pkga.tcl]" append script \n \ "[list pkg_mkIndex -lazy -load Pkg* $fullPkgPath [file tail $x]]" exec [interpreter] << $script pkgtest::runCreatedIndex {0 {}} -lazy -load Pkg* -- $fullPkgPath pkga[info sharedlibextension] -} {0 {}} +} {0 {{pkga:1.0 {tclPkgSetup {pkga.so load {pkga_eq pkga_quote}}}}}} if {[testConstraint $dll]} { file delete -force [file join $fullPkgPath [file tail $x]] diff --git a/unix/dltest/pkgua.c b/unix/dltest/pkgua.c index 0ab3e23..7082b36 100644 --- a/unix/dltest/pkgua.c +++ b/unix/dltest/pkgua.c @@ -21,6 +21,7 @@ static int PkguaEqObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); static int PkguaQuoteObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); +static void CommandDeleted(ClientData clientData); /* * In the following hash table we are going to store a struct that holds all @@ -40,6 +41,13 @@ static int interpTokenMapInitialised = 0; static void +CommandDeleted(ClientData clientData) +{ + Tcl_Command *cmdToken = clientData; + *cmdToken = NULL; +} + +static void PkguaInitTokensHashTable(void) { if (interpTokenMapInitialised) { @@ -221,12 +229,14 @@ Pkgua_Init( Tcl_SetVar2(interp, "::pkgua_loaded", NULL, ".", TCL_APPEND_VALUE); cmdTokens = PkguaInterpToTokens(interp); - cmdTokens[cmdIndex++] = - Tcl_CreateObjCommand(interp, "pkgua_eq", PkguaEqObjCmd, NULL, - NULL); - cmdTokens[cmdIndex++] = + cmdTokens[cmdIndex] = + Tcl_CreateObjCommand(interp, "pkgua_eq", PkguaEqObjCmd, &cmdTokens[cmdIndex], + CommandDeleted); + cmdIndex++; + cmdTokens[cmdIndex] = Tcl_CreateObjCommand(interp, "pkgua_quote", PkguaQuoteObjCmd, - NULL, NULL); + &cmdTokens[cmdIndex], CommandDeleted); + cmdIndex++; return TCL_OK; } -- cgit v0.12 From 3634214bbb26d84ee5eebc626e98c33decf178f8 Mon Sep 17 00:00:00 2001 From: pooryorick Date: Sun, 16 May 2021 10:03:55 +0000 Subject: Eliminate compiler warnings about unused parameters. --- generic/tclLoad.c | 24 ++++++++++-------------- generic/tclTestObj.c | 2 +- generic/tclZipfs.c | 2 +- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/generic/tclLoad.c b/generic/tclLoad.c index 3d64edb..ed2be03 100644 --- a/generic/tclLoad.c +++ b/generic/tclLoad.c @@ -560,7 +560,7 @@ Tcl_UnloadObjCmd( Tcl_Obj *const objv[]) /* Argument objects. */ { Tcl_Interp *target; /* Which interpreter to unload from. */ - LoadedLibrary *libraryPtr, *defaultPtr; + LoadedLibrary *libraryPtr; Tcl_DString pfx, tmp; InterpLibrary *ipFirstPtr, *ipPtr; int i, index, code, complain = 1, keepLibrary = 0; @@ -662,7 +662,6 @@ Tcl_UnloadObjCmd( Tcl_MutexLock(&libraryMutex); - defaultPtr = NULL; for (libraryPtr = firstLibraryPtr; libraryPtr != NULL; libraryPtr = libraryPtr->nextPtr) { int namesMatch, filesMatch; @@ -688,9 +687,6 @@ Tcl_UnloadObjCmd( if (filesMatch && (namesMatch || (prefix == NULL))) { break; } - if (namesMatch && (fullFileName[0] == 0)) { - defaultPtr = libraryPtr; - } if (filesMatch && !namesMatch && (fullFileName[0] != 0)) { break; } @@ -776,7 +772,7 @@ UnloadLibrary( { int code; InterpLibrary *ipFirstPtr, *ipPtr; - LoadedLibrary *defaultPtr; + LoadedLibrary *iterLibraryPtr; int trustedRefCount = -1, safeRefCount = -1; Tcl_LibraryUnloadProc *unloadProc = NULL; @@ -937,22 +933,22 @@ UnloadLibrary( * Remove this library from the loaded library cache. */ - defaultPtr = libraryPtr; - if (defaultPtr == firstLibraryPtr) { + iterLibraryPtr = libraryPtr; + if (iterLibraryPtr == firstLibraryPtr) { firstLibraryPtr = libraryPtr->nextPtr; } else { for (libraryPtr = firstLibraryPtr; libraryPtr != NULL; libraryPtr = libraryPtr->nextPtr) { - if (libraryPtr->nextPtr == defaultPtr) { - libraryPtr->nextPtr = defaultPtr->nextPtr; + if (libraryPtr->nextPtr == iterLibraryPtr) { + libraryPtr->nextPtr = iterLibraryPtr->nextPtr; break; } } } - ckfree(defaultPtr->fileName); - ckfree(defaultPtr->prefix); - ckfree(defaultPtr); + ckfree(iterLibraryPtr->fileName); + ckfree(iterLibraryPtr->prefix); + ckfree(iterLibraryPtr); Tcl_MutexUnlock(&libraryMutex); } else { code = TCL_ERROR; @@ -1191,7 +1187,7 @@ TclGetLoadedLibraries( static void LoadCleanupProc( - ClientData clientData, /* Pointer to first InterpLibrary structure + TCL_UNUSED(ClientData), /* Pointer to first InterpLibrary structure * for interp. */ Tcl_Interp *interp) { diff --git a/generic/tclTestObj.c b/generic/tclTestObj.c index 4e7cec9..f766030 100644 --- a/generic/tclTestObj.c +++ b/generic/tclTestObj.c @@ -54,7 +54,7 @@ static Tcl_ObjCmdProc TeststringobjCmd; #define VARPTR_KEY "TCLOBJTEST_VARPTR" #define NUMBER_OF_OBJECT_VARS 20 -static void VarPtrDeleteProc(void *clientData, Tcl_Interp *interp) +static void VarPtrDeleteProc(void *clientData, TCL_UNUSED(Tcl_Interp *)) { int i; Tcl_Obj **varPtr = (Tcl_Obj **) clientData; diff --git a/generic/tclZipfs.c b/generic/tclZipfs.c index bad4cb9..399aa65 100644 --- a/generic/tclZipfs.c +++ b/generic/tclZipfs.c @@ -5733,7 +5733,7 @@ ZipfsAppHookFindTclInit( static void ZipfsExitHandler( - ClientData clientData) + TCL_UNUSED(ClientData)) { Tcl_HashEntry *hPtr; Tcl_HashSearch search; -- cgit v0.12 From e4e1451d4ab61e253188270e234b00bd31d4e33d Mon Sep 17 00:00:00 2001 From: pooryorick Date: Sun, 16 May 2021 19:09:47 +0000 Subject: Break TclDeleteNamespaceChildren out of TclTeardownNamespace. --- generic/tclInt.h | 1 + generic/tclNamesp.c | 134 ++++++++++++++++++++++++++++++---------------------- 2 files changed, 79 insertions(+), 56 deletions(-) diff --git a/generic/tclInt.h b/generic/tclInt.h index b8ed3c1..d0c8173 100644 --- a/generic/tclInt.h +++ b/generic/tclInt.h @@ -2958,6 +2958,7 @@ MODULE_SCOPE Tcl_Command TclCreateEnsembleInNs(Tcl_Interp *interp, const char *name, Tcl_Namespace *nameNamespacePtr, Tcl_Namespace *ensembleNamespacePtr, int flags); MODULE_SCOPE void TclDeleteNamespaceVars(Namespace *nsPtr); +MODULE_SCOPE void TclDeleteNamespaceChildren(Namespace *nsPtr); MODULE_SCOPE int TclFindDictElement(Tcl_Interp *interp, const char *dict, int dictLength, const char **elementPtr, const char **nextPtr, diff --git a/generic/tclNamesp.c b/generic/tclNamesp.c index f57b7e1..99a777e 100644 --- a/generic/tclNamesp.c +++ b/generic/tclNamesp.c @@ -1073,6 +1073,83 @@ TclNamespaceDeleted( return (nsPtr->flags & NS_DYING) ? 1 : 0; } +void +TclDeleteNamespaceChildren( + Namespace *nsPtr /* Namespace whose children to delete */ +) +{ + Interp *iPtr = (Interp *) nsPtr->interp; + Tcl_HashEntry *entryPtr; + int i, unchecked; + Tcl_HashSearch search; + /* + * Delete all the child namespaces. + * + * BE CAREFUL: When each child is deleted, it divorces itself from its + * parent. The hash table can't be proplery traversed if its elements are + * being deleted. Because of traces (and the desire to avoid the + * quadratic problems of just using Tcl_FirstHashEntry over and over, [Bug + * f97d4ee020]) copy to a temporary array and then delete all those + * namespaces. + * + * Important: leave the hash table itself still live. + */ + +#ifndef BREAK_NAMESPACE_COMPAT + unchecked = (nsPtr->childTable.numEntries > 0); + while (nsPtr->childTable.numEntries > 0 && unchecked) { + int length = nsPtr->childTable.numEntries; + Namespace **children = (Namespace **)TclStackAlloc((Tcl_Interp *) iPtr, + sizeof(Namespace *) * length); + + i = 0; + for (entryPtr = Tcl_FirstHashEntry(&nsPtr->childTable, &search); + entryPtr != NULL; + entryPtr = Tcl_NextHashEntry(&search)) { + children[i] = (Namespace *)Tcl_GetHashValue(entryPtr); + children[i]->refCount++; + i++; + } + unchecked = 0; + for (i = 0 ; i < length ; i++) { + if (!(children[i]->flags & NS_DYING)) { + unchecked = 1; + Tcl_DeleteNamespace((Tcl_Namespace *) children[i]); + TclNsDecrRefCount(children[i]); + } + } + TclStackFree((Tcl_Interp *) iPtr, children); + } +#else + if (nsPtr->childTablePtr != NULL) { + unchecked = (nsPtr->childTable.numEntries > 0); + while (nsPtr->childTable.numEntries > 0 && unchecked) { + int length = nsPtr->childTablePtr->numEntries; + Namespace **children = (Namespace **)TclStackAlloc((Tcl_Interp *) iPtr, + sizeof(Namespace *) * length); + + i = 0; + for (entryPtr = Tcl_FirstHashEntry(nsPtr->childTablePtr, &search); + entryPtr != NULL; + entryPtr = Tcl_NextHashEntry(&search)) { + children[i] = (Namespace *)Tcl_GetHashValue(entryPtr); + children[i]->refCount++; + i++; + } + unchecked = 0; + for (i = 0 ; i < length ; i++) { + if (!(children[i]->flags & NS_DYING)) { + unchecked = 1; + Tcl_DeleteNamespace((Tcl_Namespace *) children[i]); + TclNsDecrRefCount(children[i]); + } + } + TclStackFree((Tcl_Interp *) iPtr, children); + } + } +#endif +} + /* *---------------------------------------------------------------------- * @@ -1181,62 +1258,7 @@ TclTeardownNamespace( nsPtr->commandPathSourceList = NULL; } - /* - * Delete all the child namespaces. - * - * BE CAREFUL: When each child is deleted, it will divorce itself from its - * parent. You can't traverse a hash table properly if its elements are - * being deleted. Because of traces (and the desire to avoid the - * quadratic problems of just using Tcl_FirstHashEntry over and over, [Bug - * f97d4ee020]) we copy to a temporary array and then delete all those - * namespaces. - * - * Important: leave the hash table itself still live. - */ - -#ifndef BREAK_NAMESPACE_COMPAT - while (nsPtr->childTable.numEntries > 0) { - int length = nsPtr->childTable.numEntries; - Namespace **children = (Namespace **)TclStackAlloc((Tcl_Interp *) iPtr, - sizeof(Namespace *) * length); - - i = 0; - for (entryPtr = Tcl_FirstHashEntry(&nsPtr->childTable, &search); - entryPtr != NULL; - entryPtr = Tcl_NextHashEntry(&search)) { - children[i] = (Namespace *)Tcl_GetHashValue(entryPtr); - children[i]->refCount++; - i++; - } - for (i = 0 ; i < length ; i++) { - Tcl_DeleteNamespace((Tcl_Namespace *) children[i]); - TclNsDecrRefCount(children[i]); - } - TclStackFree((Tcl_Interp *) iPtr, children); - } -#else - if (nsPtr->childTablePtr != NULL) { - while (nsPtr->childTablePtr->numEntries > 0) { - int length = nsPtr->childTablePtr->numEntries; - Namespace **children = (Namespace **)TclStackAlloc((Tcl_Interp *) iPtr, - sizeof(Namespace *) * length); - - i = 0; - for (entryPtr = Tcl_FirstHashEntry(nsPtr->childTablePtr, &search); - entryPtr != NULL; - entryPtr = Tcl_NextHashEntry(&search)) { - children[i] = Tcl_GetHashValue(entryPtr); - children[i]->refCount++; - i++; - } - for (i = 0 ; i < length ; i++) { - Tcl_DeleteNamespace((Tcl_Namespace *) children[i]); - TclNsDecrRefCount(children[i]); - } - TclStackFree((Tcl_Interp *) iPtr, children); - } - } -#endif + TclDeleteNamespaceChildren(nsPtr); /* * Free the namespace's export pattern array. -- cgit v0.12 From d4d8fe187b4352bb54fced2e2ba5c6ebbb1f62d8 Mon Sep 17 00:00:00 2001 From: pooryorick Date: Mon, 17 May 2021 07:39:20 +0000 Subject: use [info sharedlibextension] for instead of ".so" --- tests/pkgMkIndex.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/pkgMkIndex.test b/tests/pkgMkIndex.test index 002efcc..f01f497 100644 --- a/tests/pkgMkIndex.test +++ b/tests/pkgMkIndex.test @@ -591,7 +591,7 @@ test pkgMkIndex-10.2 {package in DLL hidden by -load} [list exec $dll] { "[list pkg_mkIndex -lazy -load Pkg* $fullPkgPath [file tail $x]]" exec [interpreter] << $script pkgtest::runCreatedIndex {0 {}} -lazy -load Pkg* -- $fullPkgPath pkga[info sharedlibextension] -} {0 {{pkga:1.0 {tclPkgSetup {pkga.so load {pkga_eq pkga_quote}}}}}} +} "0 {{pkga:1.0 {tclPkgSetup {pkga[info sharedlibextension] load {pkga_eq pkga_quote}}}}}" if {[testConstraint $dll]} { file delete -force [file join $fullPkgPath [file tail $x]] -- cgit v0.12 From 11b101cd271056ab23a8d18601bde009eb50e549 Mon Sep 17 00:00:00 2001 From: pooryorick Date: Tue, 18 May 2021 06:03:40 +0000 Subject: Additional test for [688fcc7082fa99a4], imported command trace memory error. --- tests/namespace.test | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/namespace.test b/tests/namespace.test index efd00a8..ebc00ab 100644 --- a/tests/namespace.test +++ b/tests/namespace.test @@ -3340,6 +3340,38 @@ test namespace-56.5 {Bug 8b9854c3d8} -setup { } -result 1 +test namespace-56.6 { + Namespace deletion traces on both the original routine and the imported + routine should run without any memory error under a debug build. +} -body { + variable res 0 + + proc ondelete {old new op} { + $old + } + + namespace eval ns1 {} { + namespace export * + proc p1 {} { + namespace upvar [namespace parent] res res + incr res + } + trace add command p1 delete ondelete + } + + namespace eval ns2 {} { + namespace import ::ns1::p1 + trace add command p1 delete ondelete + } + + namespace delete ns1 + namespace delete ns2 + return $res +} -cleanup { + unset res + rename ondelete {} +} -result 2 + test namespace-57.0 { an imported alias should be usable in the deletion trace for the alias -- cgit v0.12 From 1c9f5c87416a863ae166c4ec0938d0b72a238636 Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Tue, 18 May 2021 12:23:32 +0000 Subject: Make pkgua package thread-safe --- tests/namespace.test | 2 +- tests/pkgMkIndex.test | 2 +- unix/dltest/pkgua.c | 51 +++++++++++++++++++++++++++------------------------ 3 files changed, 29 insertions(+), 26 deletions(-) diff --git a/tests/namespace.test b/tests/namespace.test index ebc00ab..1a18096 100644 --- a/tests/namespace.test +++ b/tests/namespace.test @@ -3344,7 +3344,7 @@ test namespace-56.6 { Namespace deletion traces on both the original routine and the imported routine should run without any memory error under a debug build. } -body { - variable res 0 + variable res 0 proc ondelete {old new op} { $old diff --git a/tests/pkgMkIndex.test b/tests/pkgMkIndex.test index f01f497..62bd3d4 100644 --- a/tests/pkgMkIndex.test +++ b/tests/pkgMkIndex.test @@ -584,7 +584,7 @@ test pkgMkIndex-10.2 {package in DLL hidden by -load} [list exec $dll] { # it. # # This test depends on context from prior test, so repeat it. - + set script \ "[list pkg_mkIndex -lazy $fullPkgPath [file tail $x] pkga.tcl]" append script \n \ diff --git a/unix/dltest/pkgua.c b/unix/dltest/pkgua.c index 7082b36..a822541 100644 --- a/unix/dltest/pkgua.c +++ b/unix/dltest/pkgua.c @@ -21,7 +21,7 @@ static int PkguaEqObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); static int PkguaQuoteObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); -static void CommandDeleted(ClientData clientData); +static void CommandDeleted(ClientData clientData); /* * In the following hash table we are going to store a struct that holds all @@ -31,30 +31,32 @@ static void CommandDeleted(ClientData clientData); * need to keep the various command tokens we have registered, as they are the * only safe way to unregister our registered commands, even if they have been * renamed. - * - * Note that this code is utterly single-threaded. */ -static Tcl_HashTable interpTokenMap; -static int interpTokenMapInitialised = 0; +typedef struct ThreadSpecificData { + int interpTokenMapInitialised; + Tcl_HashTable interpTokenMap; +} ThreadSpecificData; +static Tcl_ThreadDataKey dataKey; #define MAX_REGISTERED_COMMANDS 2 - static void CommandDeleted(ClientData clientData) { - Tcl_Command *cmdToken = clientData; + Tcl_Command *cmdToken = (Tcl_Command *)clientData; *cmdToken = NULL; } static void PkguaInitTokensHashTable(void) { - if (interpTokenMapInitialised) { + ThreadSpecificData *tsdPtr = (ThreadSpecificData *)Tcl_GetThreadData((&dataKey), sizeof(ThreadSpecificData)); + + if (tsdPtr->interpTokenMapInitialised) { return; } - Tcl_InitHashTable(&interpTokenMap, TCL_ONE_WORD_KEYS); - interpTokenMapInitialised = 1; + Tcl_InitHashTable(&tsdPtr->interpTokenMap, TCL_ONE_WORD_KEYS); + tsdPtr->interpTokenMapInitialised = 1; } static void @@ -62,12 +64,13 @@ PkguaFreeTokensHashTable(void) { Tcl_HashSearch search; Tcl_HashEntry *entryPtr; + ThreadSpecificData *tsdPtr = (ThreadSpecificData *)Tcl_GetThreadData((&dataKey), sizeof(ThreadSpecificData)); - for (entryPtr = Tcl_FirstHashEntry(&interpTokenMap, &search); + for (entryPtr = Tcl_FirstHashEntry(&tsdPtr->interpTokenMap, &search); entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&search)) { Tcl_Free((char *) Tcl_GetHashValue(entryPtr)); } - interpTokenMapInitialised = 0; + tsdPtr->interpTokenMapInitialised = 0; } static Tcl_Command * @@ -76,13 +79,14 @@ PkguaInterpToTokens( { int newEntry; Tcl_Command *cmdTokens; + ThreadSpecificData *tsdPtr = (ThreadSpecificData *)Tcl_GetThreadData((&dataKey), sizeof(ThreadSpecificData)); Tcl_HashEntry *entryPtr = - Tcl_CreateHashEntry(&interpTokenMap, interp, &newEntry); + Tcl_CreateHashEntry(&tsdPtr->interpTokenMap, (char *) interp, &newEntry); if (newEntry) { cmdTokens = (Tcl_Command *) - Tcl_Alloc(sizeof(Tcl_Command) * (MAX_REGISTERED_COMMANDS+1)); - for (newEntry=0 ; newEntryinterpTokenMap, (char *) interp); if (entryPtr) { Tcl_Free((char *) Tcl_GetHashValue(entryPtr)); @@ -207,7 +212,7 @@ Pkgua_Init( Tcl_Interp *interp) /* Interpreter in which the package is to be * made available. */ { - int code, cmdIndex = 0; + int code; Tcl_Command *cmdTokens; if (Tcl_InitStubs(interp, "8.5-", 0) == NULL) { @@ -215,7 +220,7 @@ Pkgua_Init( } /* - * Initialise our Hash table, where we store the registered command tokens + * Initialize our Hash table, where we store the registered command tokens * for each interpreter. */ @@ -229,14 +234,12 @@ Pkgua_Init( Tcl_SetVar2(interp, "::pkgua_loaded", NULL, ".", TCL_APPEND_VALUE); cmdTokens = PkguaInterpToTokens(interp); - cmdTokens[cmdIndex] = - Tcl_CreateObjCommand(interp, "pkgua_eq", PkguaEqObjCmd, &cmdTokens[cmdIndex], + cmdTokens[0] = + Tcl_CreateObjCommand(interp, "pkgua_eq", PkguaEqObjCmd, &cmdTokens[0], CommandDeleted); - cmdIndex++; - cmdTokens[cmdIndex] = + cmdTokens[1] = Tcl_CreateObjCommand(interp, "pkgua_quote", PkguaQuoteObjCmd, - &cmdTokens[cmdIndex], CommandDeleted); - cmdIndex++; + &cmdTokens[1], CommandDeleted); return TCL_OK; } -- cgit v0.12 From a201f0f3838bd1574ea59075e9d4db242d2e6a61 Mon Sep 17 00:00:00 2001 From: pooryorick Date: Tue, 18 May 2021 22:09:41 +0000 Subject: Fix for issue [e39cb3f462631a99], namespace is removed from other namespace paths before deletion is complete --- generic/tclBasic.c | 5 ++--- generic/tclEnsemble.c | 4 ++-- generic/tclNamesp.c | 8 ++++---- tests/namespace.test | 28 ++++++++++++++++++---------- 4 files changed, 26 insertions(+), 19 deletions(-) diff --git a/generic/tclBasic.c b/generic/tclBasic.c index 2d10812..86d7960 100644 --- a/generic/tclBasic.c +++ b/generic/tclBasic.c @@ -3505,15 +3505,14 @@ Tcl_DeleteCommandFromToken( cmdPtr->flags |= CMD_DYING; /* - * Call trace functions for the command being deleted. Then delete its - * traces. + * Call each functions and then delete the trace. */ cmdPtr->nsPtr->refCount++; if (cmdPtr->tracePtr != NULL) { CommandTrace *tracePtr; - /* Note that CallCommandTraces() never frees cmdPtr, that's + /* CallCommandTraces() does not cmdPtr, that's * done just before Tcl_DeleteCommandFromToken() returns */ CallCommandTraces(iPtr,cmdPtr,NULL,NULL,TCL_TRACE_DELETE); diff --git a/generic/tclEnsemble.c b/generic/tclEnsemble.c index 929f3ef..ccd43b9 100644 --- a/generic/tclEnsemble.c +++ b/generic/tclEnsemble.c @@ -165,7 +165,7 @@ TclNamespaceEnsembleCmd( const char *simpleName; int index, done; - if (nsPtr == NULL || nsPtr->flags & NS_DYING) { + if (nsPtr == NULL || nsPtr->flags & NS_DEAD) { if (!Tcl_InterpDeleted(interp)) { Tcl_SetObjResult(interp, Tcl_NewStringObj( "tried to manipulate ensemble of deleted namespace", @@ -1730,7 +1730,7 @@ NsEnsembleImplementationCmdNR( return TCL_ERROR; } - if (ensemblePtr->nsPtr->flags & NS_DYING) { + if (ensemblePtr->nsPtr->flags & NS_DEAD) { /* * Don't know how we got here, but make things give up quickly. */ diff --git a/generic/tclNamesp.c b/generic/tclNamesp.c index 99a777e..64fcda4 100644 --- a/generic/tclNamesp.c +++ b/generic/tclNamesp.c @@ -2640,7 +2640,7 @@ Tcl_FindCommand( &simpleName); if ((realNsPtr != NULL) && (simpleName != NULL)) { if ((cxtNsPtr == realNsPtr) - || !(realNsPtr->flags & NS_DYING)) { + || !(realNsPtr->flags & NS_DEAD)) { entryPtr = Tcl_FindHashEntry(&realNsPtr->cmdTable, simpleName); if (entryPtr != NULL) { cmdPtr = (Command *)Tcl_GetHashValue(entryPtr); @@ -2652,7 +2652,7 @@ Tcl_FindCommand( * Next, check along the path. */ - for (i=0 ; icommandPathLength && cmdPtr==NULL ; i++) { + for (i=0 ; (cmdPtr == NULL) && icommandPathLength ; i++) { pathNsPtr = cxtNsPtr->commandPathArray[i].nsPtr; if (pathNsPtr == NULL) { continue; @@ -2661,7 +2661,7 @@ Tcl_FindCommand( TCL_NAMESPACE_ONLY, &realNsPtr, &dummyNsPtr, &dummyNsPtr, &simpleName); if ((realNsPtr != NULL) && (simpleName != NULL) - && !(realNsPtr->flags & NS_DYING)) { + && !(realNsPtr->flags & NS_DEAD)) { entryPtr = Tcl_FindHashEntry(&realNsPtr->cmdTable, simpleName); if (entryPtr != NULL) { cmdPtr = (Command *)Tcl_GetHashValue(entryPtr); @@ -2679,7 +2679,7 @@ Tcl_FindCommand( TCL_GLOBAL_ONLY, &realNsPtr, &dummyNsPtr, &dummyNsPtr, &simpleName); if ((realNsPtr != NULL) && (simpleName != NULL) - && !(realNsPtr->flags & NS_DYING)) { + && !(realNsPtr->flags & NS_DEAD)) { entryPtr = Tcl_FindHashEntry(&realNsPtr->cmdTable, simpleName); if (entryPtr != NULL) { cmdPtr = (Command *)Tcl_GetHashValue(entryPtr); diff --git a/tests/namespace.test b/tests/namespace.test index 1a18096..f826599 100644 --- a/tests/namespace.test +++ b/tests/namespace.test @@ -182,8 +182,8 @@ test namespace-7.6 {recursive Tcl_DeleteNamespace, no active call frames in ns} } {} test namespace-7.7 {Bug 1655305} -setup { interp create child - # Can't invoke through the ensemble, since deleting the global namespace - # (indirectly, via deleting ::tcl) deletes the ensemble. + # Can't invoke through the ensemble, since deleting ::tcl + # (indirectly, via deleting the global namespace) deletes the ensemble. child eval {rename ::tcl::info::commands ::infocommands} child hide infocommands child eval { @@ -207,7 +207,7 @@ test namespace-7.8 {Bug ba1419303b4c} -setup { namespace delete ns1 } } -body { - # No segmentation fault given --enable-symbols=mem. + # No segmentation fault given --enable-symbols. namespace delete ns1 } -result {} @@ -2723,7 +2723,11 @@ test namespace-51.12 {name resolution path control} -body { catch {namespace delete ::test_ns_3} catch {namespace delete ::test_ns_4} } -test namespace-51.13 {name resolution path control} -body { +test namespace-51.13 { + name resolution path control + when the trace fires, ns_2 is being deleted but isn't gone yet, and is + still visible for the trace +} -body { set ::result {} namespace eval ::test_ns_1 { proc foo {} {lappend ::result 1} @@ -2746,8 +2750,7 @@ test namespace-51.13 {name resolution path control} -body { } bar } - # Should the result be "2 {} {2 3 2 1}" instead? -} -result {2 {} {2 3 1 1}} -cleanup { +} -result {2 {} {2 3 2 1}} -cleanup { catch {namespace delete ::test_ns_1} catch {namespace delete ::test_ns_2} catch {namespace delete ::test_ns_3} @@ -3344,11 +3347,15 @@ test namespace-56.6 { Namespace deletion traces on both the original routine and the imported routine should run without any memory error under a debug build. } -body { - variable res 0 + variable res {} proc ondelete {old new op} { - $old + variable res + set tail [namespace tail $old] + set up [namespace tail [namespace qualifiers $old]] + lappend res [list $up $tail] } + namespace eval ns1 {} { namespace export * @@ -3360,17 +3367,18 @@ test namespace-56.6 { } namespace eval ns2 {} { - namespace import ::ns1::p1 + namespace import [namespace parent]::ns1::p1 trace add command p1 delete ondelete } namespace delete ns1 namespace delete ns2 + after 1 return $res } -cleanup { unset res rename ondelete {} -} -result 2 +} -result {{ns1 p1} {ns2 p1}} test namespace-57.0 { -- cgit v0.12 From 93e207f19d5bf8e298a86aa95c881aab05036144 Mon Sep 17 00:00:00 2001 From: pooryorick Date: Wed, 19 May 2021 07:15:10 +0000 Subject: new test for issue [e39cb3f462631a99], namespace is removed from other namespace paths before deletion is complete --- tests/namespace.test | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/tests/namespace.test b/tests/namespace.test index f826599..64f237d 100644 --- a/tests/namespace.test +++ b/tests/namespace.test @@ -211,6 +211,68 @@ test namespace-7.8 {Bug ba1419303b4c} -setup { namespace delete ns1 } -result {} + +test namespace-7.9 { + Bug e39cb3f462631a99 + + A namespace being deleted should not be removed from other namespace paths + until the contents of the namespace are entirely removed. +} -setup { + + + + +} -body { + + variable res {} + + + namespace eval ns1 { + proc p1 caller { + lappend [namespace parent]::res $caller + } + } + + + namespace eval ns1a { + namespace path [namespace parent]::ns1 + + proc t1 {old new op} { + $old t1 + } + } + + namespace eval ns2 { + proc p1 caller { + lappend [namespace parent]::res $caller + } + } + + namespace eval ns2a { + namespace path [namespace parent]::ns2 + + proc t1 {old new op} { + [namespace tail $old] t2 + } + } + + + trace add command ns1::p1 delete ns1a::t1 + namespace delete ns1 + + trace add command ns2::p1 delete ns2a::t1 + namespace delete ns2 + + return $res + +} -cleanup { + namespace delete ns1a + namespace delete ns2a + unset res +} -result {t1 t2} + + + test namespace-8.1 {TclTeardownNamespace, delete global namespace} { catch {interp delete test_interp} interp create test_interp -- cgit v0.12 From 0f48a8bc9251c7c3d0bde6c0f8ce0ee19b9c35c9 Mon Sep 17 00:00:00 2001 From: pooryorick Date: Wed, 19 May 2021 10:44:18 +0000 Subject: update documentation and macro names --- generic/tclInt.h | 28 +++++++++++++--------------- generic/tclNamesp.c | 41 +++++++++++++++++++++-------------------- 2 files changed, 34 insertions(+), 35 deletions(-) diff --git a/generic/tclInt.h b/generic/tclInt.h index d0c8173..ad9a5c1 100644 --- a/generic/tclInt.h +++ b/generic/tclInt.h @@ -415,29 +415,27 @@ struct NamespacePathEntry { * Flags used to represent the status of a namespace: * * NS_DYING - 1 means Tcl_DeleteNamespace has been called to delete the - * namespace but there are still active call frames on the Tcl + * namespace. There may still be active call frames on the Tcl * stack that refer to the namespace. When the last call frame - * referring to it has been popped, it's variables and command - * will be destroyed and it will be marked "dead" (NS_DEAD). The - * namespace can no longer be looked up by name. + * referring to it has been popped, its remaining variables and + * commands are destroyed and it is marked "dead" (NS_DEAD). + * NS_TEARDOWN -1 means that TclTeardownNamespace has already been called on + * this namespace and it should not be called again [Bug 1355942]. * NS_DEAD - 1 means Tcl_DeleteNamespace has been called to delete the - * namespace and no call frames still refer to it. Its variables - * and command have already been destroyed. This bit allows the - * namespace resolution code to recognize that the namespace is - * "deleted". When the last namespaceName object in any byte code - * unit that refers to the namespace has been freed (i.e., when - * the namespace's refCount is 0), the namespace's storage will - * be freed. - * NS_KILLED - 1 means that TclTeardownNamespace has already been called on - * this namespace and it should not be called again [Bug 1355942] + * namespace and no call frames still refer to it. It is no longer + * accessible by name. Its variables and commands have already + * been destroyed. When the last namespaceName object in any byte + * code unit that refers to the namespace has been freed (i.e., + * when the namespace's refCount is 0), the namespace's storage + * will be freed. * NS_SUPPRESS_COMPILATION - * Marks the commands in this namespace for not being compiled, * forcing them to be looked up every time. */ #define NS_DYING 0x01 -#define NS_DEAD 0x02 -#define NS_KILLED 0x04 +#define NS_TEARDOWN 0x02 +#define NS_DEAD 0x04 #define NS_SUPPRESS_COMPILATION 0x08 /* diff --git a/generic/tclNamesp.c b/generic/tclNamesp.c index 64fcda4..5dc9659 100644 --- a/generic/tclNamesp.c +++ b/generic/tclNamesp.c @@ -306,7 +306,7 @@ Tcl_PushCallFrame( /* * TODO: Examine whether it would be better to guard based on NS_DYING - * or NS_KILLED. It appears that these are not tested because they can + * or NS_TEARDOWN. It appears that these are not tested because they can * be set in a global interp that has been [namespace delete]d, but * which never really completely goes away because of lingering global * things like ::errorInfo and [::unknown] and hidden commands. @@ -986,20 +986,21 @@ Tcl_DeleteNamespace( } /* - * If the namespace is on the call frame stack, it is marked as "dying" - * (NS_DYING is OR'd into its flags): the namespace can't be looked up by - * name but its commands and variables are still usable by those active - * call frames. When all active call frames referring to the namespace - * have been popped from the Tcl stack, Tcl_PopCallFrame will call this - * function again to delete everything in the namespace. If no nsName - * objects refer to the namespace (i.e., if its refCount is zero), its - * commands and variables are deleted and the storage for its namespace - * structure is freed. Otherwise, if its refCount is nonzero, the - * namespace's commands and variables are deleted but the structure isn't - * freed. Instead, NS_DEAD is OR'd into the structure's flags to allow the - * namespace resolution code to recognize that the namespace is "deleted". - * The structure's storage is freed by FreeNsNameInternalRep when its - * refCount reaches 0. + * If the namespace is on the call frame stack, it is marked as "dying" + * (NS_DYING is OR'd into its flags): Contents of the namespace are + * still available and visible until the namespace is later marked as + * NS_DEAD, and its commands and variables are still usable by any + * active call frames referring to th namespace. When all active call + * frames referring to the namespace have been popped from the Tcl + * stack, Tcl_PopCallFrame calls Tcl_DeleteNamespace again. If no + * nsName objects refer to the namespace (i.e., if its refCount is + * zero), its commands and variables are deleted and the storage for + * its namespace structure is freed. Otherwise, if its refCount is + * nonzero, the namespace's commands and variables are deleted but the + * structure isn't freed. Instead, NS_DEAD is OR'd into the structure's + * flags to allow the namespace resolution code to recognize that the + * namespace is "deleted". The structure's storage is freed by + * FreeNsNameInternalRep when its refCount reaches 0. */ if (nsPtr->activationCount - (nsPtr == globalNsPtr) > 0) { @@ -1013,16 +1014,16 @@ Tcl_DeleteNamespace( } } nsPtr->parentPtr = NULL; - } else if (!(nsPtr->flags & NS_KILLED)) { + } else if (!(nsPtr->flags & NS_TEARDOWN)) { /* * Delete the namespace and everything in it. If this is the global * namespace, then clear it but don't free its storage unless the - * interpreter is being torn down. Set the NS_KILLED flag to avoid + * interpreter is being torn down. Set the NS_TEARDOWN flag to avoid * recursive calls here - if the namespace is really in the process of * being deleted, ignore any second call. */ - nsPtr->flags |= (NS_DYING|NS_KILLED); + nsPtr->flags |= (NS_DYING|NS_TEARDOWN); TclTeardownNamespace(nsPtr); @@ -1060,7 +1061,7 @@ Tcl_DeleteNamespace( * get killed later, avoiding mem leaks. */ - nsPtr->flags &= ~(NS_DYING|NS_KILLED); + nsPtr->flags &= ~(NS_DYING|NS_TEARDOWN); } } TclNsDecrRefCount(nsPtr); @@ -3299,7 +3300,7 @@ NamespaceDeleteCmd( name = TclGetString(objv[i]); namespacePtr = Tcl_FindNamespace(interp, name, NULL, /*flags*/ 0); if ((namespacePtr == NULL) - || (((Namespace *) namespacePtr)->flags & NS_KILLED)) { + || (((Namespace *) namespacePtr)->flags & NS_TEARDOWN)) { Tcl_SetObjResult(interp, Tcl_ObjPrintf( "unknown namespace \"%s\" in namespace delete command", TclGetString(objv[i]))); -- cgit v0.12