Tcl Source Code

Artifact [147ddc0e3d]
Login

Artifact 147ddc0e3d565b0bc1122e4a09b3a527ae961531:

Attachment "tclFinalize2.diff" to ticket [676271ffff] added by vincentdarley 2003-01-29 17:53:48.
? tclFinalize2.diff
? win/efile
? win/globTest
Index: generic/tclEvent.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclEvent.c,v
retrieving revision 1.26
diff -u -r1.26 tclEvent.c
--- generic/tclEvent.c	25 Jan 2003 03:12:01 -0000	1.26
+++ generic/tclEvent.c	29 Jan 2003 10:47:34 -0000
@@ -813,6 +813,12 @@
 	TclFinalizeEnvironment();
 
 	/* 
+	 * Finalizing the filesystem must come after anything which
+	 * might conceivably interact with the 'Tcl_FS' API. 
+	 */
+	TclFinalizeFilesystem();
+
+	/* 
 	 * We must be sure the encoding finalization doesn't need
 	 * to examine the filesystem in any way.  Since it only
 	 * needs to clean up internal data structures, this is
@@ -843,23 +849,23 @@
 	TclFinalizeSynchronization();
 
 	/*
-	 * FIX FIX FIX:
-	 * There is a conflict here between what apps need when for
-	 * finalization.  There is the encoding note below that
-	 * relates to tclkits, but there is the clear problem in a
-	 * standard threaded build that you must finalize the sync
-	 * objects before the filesystem to handle tsdPtr's in
-	 * extensions (example: dde).  -- hobbs
+	 * We defer unloading of packages until very late 
+	 * to avoid memory access issues.  Both exit callbacks and
+	 * synchronization variables may be stored in packages.
 	 * 
-	 * Finalizing the filesystem must come after anything which
-	 * might conceivably interact with the 'Tcl_FS' API.  This
-	 * will also unload any extensions which have been loaded.
-	 * However, it also needs access to the encoding subsystem
-	 * during finalization, so that system must still be intact
-	 * at this point.
+	 * Note that TclFinalizeLoad unloads packages in the reverse
+	 * of the order they were loaded in (i.e. last to be loaded
+	 * is the first to be unloaded).  This can be important for
+	 * correct unloading when dependencies exist.
+	 * 
+	 * Once load has been finalized, we will have deleted any
+	 * temporary copies of shared libraries and can therefore
+	 * reset the filesystem to its original state.
 	 */
-	TclFinalizeFilesystem();
 
+	TclFinalizeLoad();
+	TclResetFilesystem();
+	
 	/*
 	 * There shouldn't be any malloc'ed memory after this.
 	 */
Index: generic/tclIOUtil.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclIOUtil.c,v
retrieving revision 1.70
diff -u -r1.70 tclIOUtil.c
--- generic/tclIOUtil.c	28 Jan 2003 14:52:47 -0000	1.70
+++ generic/tclIOUtil.c	29 Jan 2003 10:47:35 -0000
@@ -584,18 +584,14 @@
  *	Clean up the filesystem.  After this, calls to all Tcl_FS...
  *	functions will fail.
  *	
- *	Note that, since 'TclFinalizeLoad' may unload extensions
- *	which implement other filesystems, and which may therefore
- *	contain a 'freeProc' for those filesystems, at this stage
- *	we _must_ have freed all objects of "path" type, or we may
- *	end up with segfaults if we try to free them later.
- *
+ *	We will later call TclResetFilesystem to restore the FS
+ *	to a pristine state.
+ *	
  * Results:
  *	None.
  *
  * Side effects:
- *	Frees any memory allocated by the filesystem.  Unloads any
- *	extensions which have been loaded.
+ *	Frees any memory allocated by the filesystem.
  *
  *----------------------------------------------------------------------
  */
@@ -612,56 +608,76 @@
 	cwdPathPtr = NULL;
     }
 
-    /*
-     * We defer unloading of packages until very late 
-     * to avoid memory access issues.  Both exit callbacks and
-     * synchronization variables may be stored in packages.
-     * 
-     * Note that TclFinalizeLoad unloads packages in the reverse
-     * of the order they were loaded in (i.e. last to be loaded
-     * is the first to be unloaded).  This can be important for
-     * correct unloading when dependencies exist.
+    /* 
+     * Remove all filesystems, freeing any allocated memory
+     * that is no longer needed
      */
-
-    TclFinalizeLoad();
-    
-    /* Remove all filesystems, freeing any allocated memory */
     while (filesystemList != NULL) {
 	FilesystemRecord *tmpFsRecPtr = filesystemList->nextPtr;
-	if (filesystemList->fileRefCount > 1) {
+	if (filesystemList->fileRefCount > 0) {
 	    /* 
-	     * We are freeing a filesystem which actually has
-	     * path objects still around which belong to it.
-	     * This is probably bad, but since we are exiting,
-	     * we don't do anything about it.
+	     * This filesystem must have some path objects still
+	     * around which will be freed later (e.g. when unloading
+	     * any shared libraries).  If not, then someone is
+	     * causing us to leak memory.
 	     */
-	}
-	/* The native filesystem is static, so we don't free it */
-	if (filesystemList != &nativeFilesystemRecord) {
-	    ckfree((char *)filesystemList);
+	} else {
+	    /* The native filesystem is static, so we don't free it */
+	    if (filesystemList != &nativeFilesystemRecord) {
+		ckfree((char *)filesystemList);
+	    }
 	}
 	filesystemList = tmpFsRecPtr;
     }
     /*
-     * Now filesystemList is NULL.  Reset statics to original state.
+     * Now filesystemList is NULL.  This means that any attempt
+     * to use the filesystem is likely to fail.
      */
     statProcList = NULL;
     accessProcList = NULL;
     openFileChannelProcList = NULL;
+#ifdef __WIN32__
+    TclWinEncodingsCleanup();
+#endif
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclResetFilesystem --
+ *
+ *	Restore the filesystem to a pristine state.
+ *	
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TclResetFilesystem() {
     filesystemList = &nativeFilesystemRecord;
+    /* 
+     * Note, at this point, I believe nativeFilesystemRecord ->
+     * fileRefCount should equal 1 and if not, we should try to track
+     * down the cause.
+     */
+    
     filesystemIteratorsInProgress = 0;
     filesystemWantToModify = 0;
 #ifdef TCL_THREADS
     filesystemOkToModify = NULL;
 #endif
+#ifdef __WIN32__
     /* 
-     * Cleans up the win32 API filesystem proc lookup table and
-     * any special encodings which have been loaded.  This must
-     * happen after the filesystem has been closed down, or crashes
-     * can result (especially with vfs).
+     * Cleans up the win32 API filesystem proc lookup table. This must
+     * happen very late in finalization so that deleting of copied
+     * dlls can occur.
      */
-#ifdef __WIN32__
-    TclWinFilesystemAndEncodingsCleanup();
+    TclWinResetInterfaces();
 #endif
 }
 
@@ -2634,8 +2650,16 @@
 		 */
 		if (Tcl_FSDeleteFile(copyToPtr) == TCL_OK) {
 		    Tcl_DecrRefCount(copyToPtr);
-		    (*handlePtr) = NULL;
-		    (*unloadProcPtr) = NULL;
+		    /* 
+		     * We tell our caller about the real shared
+		     * library which was loaded.  Note that this
+		     * does mean that the package list maintained
+		     * by 'load' will store the original (vfs)
+		     * path alongside the temporary load handle
+		     * and unload proc ptr.
+		     */
+		    (*handlePtr) = newLoadHandle;
+		    (*unloadProcPtr) = newUnloadProcPtr;
 		    return TCL_OK;
 		}
 		/* 
@@ -2652,24 +2676,37 @@
 		 */
 		tvdlPtr->loadHandle = newLoadHandle;
 		tvdlPtr->unloadProcPtr = newUnloadProcPtr;
-		/* copyToPtr is already incremented for this reference */
-		tvdlPtr->divertedFile = copyToPtr;
-		/* 
-		 * This is the filesystem we loaded it into.  It is
-		 * almost certainly the tclNativeFilesystem, but we don't
-		 * want to make that assumption.  Since we have a
-		 * reference to 'copyToPtr', we already have a refCount
-		 * on this filesystem, so we don't need to worry about it
-		 * disappearing on us.
-		 */
-		tvdlPtr->divertedFilesystem = copyFsPtr;
-		/* Get the native representation of the file path */
-		tvdlPtr->divertedFileNativeRep = Tcl_FSGetInternalRep(copyToPtr,
-								      copyFsPtr);
+
+		if (copyFsPtr != &tclNativeFilesystem) {
+		    /* copyToPtr is already incremented for this reference */
+		    tvdlPtr->divertedFile = copyToPtr;
+
+		    /* 
+		     * This is the filesystem we loaded it into.  Since
+		     * we have a reference to 'copyToPtr', we already
+		     * have a refCount on this filesystem, so we don't
+		     * need to worry about it disappearing on us.
+		     */
+		    tvdlPtr->divertedFilesystem = copyFsPtr;
+		    tvdlPtr->divertedFileNativeRep = NULL;
+		} else {
+		    /* We need the native rep */
+		    tvdlPtr->divertedFileNativeRep = 
+		      NativeDupInternalRep(Tcl_FSGetInternalRep(copyToPtr, 
+								copyFsPtr));
+		    /* 
+		     * We don't need or want references to the copied
+		     * Tcl_Obj or the filesystem if it is the native
+		     * one.
+		     */
+		    tvdlPtr->divertedFile = NULL;
+		    tvdlPtr->divertedFilesystem = NULL;
+		    Tcl_DecrRefCount(copyToPtr);
+		}
+
 		copyToPtr = NULL;
 		(*handlePtr) = (Tcl_LoadHandle) tvdlPtr;
 		(*unloadProcPtr) = &FSUnloadTempFile;
-		
 		return retVal;
 	    } else {
 		/* Cross-platform copy failed */
@@ -2769,39 +2806,47 @@
 	(*tvdlPtr->unloadProcPtr)(tvdlPtr->loadHandle);
     }
     
-    /* Remove the temporary file we created. */
-    if (Tcl_FSDeleteFile(tvdlPtr->divertedFile) != TCL_OK) {
+    if (tvdlPtr->divertedFilesystem == NULL) {
 	/* 
-	 * The above may have failed because the filesystem, or something
-	 * it depends upon (e.g. encodings) are being taken down because
-	 * Tcl is exiting.
-	 * 
-	 * Therefore we try to call the filesystem's 'delete file proc' 
-	 * directly.  Note that this call may still cause problems, because
-	 * it will ask for the native representation of the divertedFile,
-	 * and that may need to be _recalculated_, in which case this
-	 * call isn't very different to the above.  What we could do
-	 * instead is generate a new Tcl_Obj (pure native) by calling:
-	 * 
-	 * Tcl_Obj *tmp = Tcl_FSNewNativePath(tvdlPtr->divertedFile, 
-	 *                     tvdlPtr->divertedFileNativeRep);
-	 * Tcl_IncrRefCount(tmp);                   
-	 * tvdlPtr->divertedFilesystem->deleteFileProc(tmp);
-	 * Tcl_DecrRefCount(tmp);
-	 *                     
-	 * and then use that in this call.  This approach would potentially
-	 * work even if the encodings and everything else have been 
-	 * deconstructed.  For the moment, however, we simply assume
-	 * Tcl_FSDeleteFile has worked correctly.
+	 * It was the native filesystem, and we have a special
+	 * function available just for this purpose, which we 
+	 * know works even at this late stage.
+	 */
+	TclpDeleteFile(tvdlPtr->divertedFileNativeRep);
+	NativeFreeInternalRep(tvdlPtr->divertedFileNativeRep);
+    } else {
+	/* 
+	 * Remove the temporary file we created.  Note, we may crash
+	 * here because encodings have been taken down already.
+	 */
+	if (tvdlPtr->divertedFilesystem->deleteFileProc(tvdlPtr->divertedFile)
+	    != TCL_OK) {
+	    /* 
+	     * The above may have failed because the filesystem, or something
+	     * it depends upon (e.g. encodings) have been taken down because
+	     * Tcl is exiting.
+	     * 
+	     * We may need to work out how to delete this file more
+	     * robustly (or give the filesystem the information it needs
+	     * to delete the file more robustly).
+	     * 
+	     * In particular, one problem might be that the filesystem
+	     * cannot extract the information it needs from the above
+	     * path object because Tcl's entire filesystem apparatus
+	     * (the code in this file) has been finalized, and it
+	     * refuses to pass the internal representation to the
+	     * filesystem.
+	     */
+	}
+	
+	/* 
+	 * And free up the allocations.  This will also of course remove
+	 * a refCount from the Tcl_Filesystem to which this file belongs,
+	 * which could then free up the filesystem if we are exiting.
 	 */
+	Tcl_DecrRefCount(tvdlPtr->divertedFile);
     }
-    
-    /* 
-     * And free up the allocations.  This will also of course remove
-     * a refCount from the Tcl_Filesystem to which this file belongs,
-     * which could then free up the filesystem if we are exiting.
-     */
-    Tcl_DecrRefCount(tvdlPtr->divertedFile);
+
     ckfree((char*)tvdlPtr);
 }
 
Index: generic/tclInt.h
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclInt.h,v
retrieving revision 1.116
diff -u -r1.116 tclInt.h
--- generic/tclInt.h	26 Jan 2003 05:59:37 -0000	1.116
+++ generic/tclInt.h	29 Jan 2003 10:47:36 -0000
@@ -1661,6 +1661,7 @@
 EXTERN void		TclFinalizeExecution _ANSI_ARGS_((void));
 EXTERN void		TclFinalizeIOSubsystem _ANSI_ARGS_((void));
 EXTERN void		TclFinalizeFilesystem _ANSI_ARGS_((void));
+EXTERN void		TclResetFilesystem _ANSI_ARGS_((void));
 EXTERN void		TclFinalizeLoad _ANSI_ARGS_((void));
 EXTERN void		TclFinalizeMemorySubsystem _ANSI_ARGS_((void));
 EXTERN void		TclFinalizeNotifier _ANSI_ARGS_((void));
@@ -1716,6 +1717,7 @@
 			    Tcl_StatBuf *buf));
 EXTERN int		TclpCheckStackSpace _ANSI_ARGS_((void));
 EXTERN Tcl_Obj*         TclpTempFileName _ANSI_ARGS_((void));
+EXTERN int              TclpDeleteFile _ANSI_ARGS_((CONST char *path));
 EXTERN void		TclpFinalizeCondition _ANSI_ARGS_((
 			    Tcl_Condition *condPtr));
 EXTERN void		TclpFinalizeMutex _ANSI_ARGS_((Tcl_Mutex *mutexPtr));
Index: mac/tclMacFCmd.c
===================================================================
RCS file: /cvsroot/tcl/tcl/mac/tclMacFCmd.c,v
retrieving revision 1.18
diff -u -r1.18 tclMacFCmd.c
--- mac/tclMacFCmd.c	9 Oct 2002 11:54:20 -0000	1.18
+++ mac/tclMacFCmd.c	29 Jan 2003 10:47:38 -0000
@@ -85,7 +85,6 @@
 static int		DoCopyFile _ANSI_ARGS_((CONST char *src, 
 			    CONST char *dst));
 static int		DoCreateDirectory _ANSI_ARGS_((CONST char *path));
-static int		DoDeleteFile _ANSI_ARGS_((CONST char *path));
 static int		DoRemoveDirectory _ANSI_ARGS_((CONST char *path, 
 			    int recursive, Tcl_DString *errorPtr));
 static int		DoRenameFile _ANSI_ARGS_((CONST char *src,
@@ -482,7 +481,7 @@
 /*
  *---------------------------------------------------------------------------
  *
- * TclpObjDeleteFile, DoDeleteFile --
+ * TclpObjDeleteFile, TclpDeleteFile --
  *
  *      Removes a single file (not a directory).
  *
@@ -505,11 +504,11 @@
 TclpObjDeleteFile(pathPtr)
     Tcl_Obj *pathPtr;
 {
-    return DoDeleteFile(Tcl_FSGetNativePath(pathPtr));
+    return TclpDeleteFile(Tcl_FSGetNativePath(pathPtr));
 }
 
-static int
-DoDeleteFile(
+int
+TclpDeleteFile(
     CONST char *path)		/* Pathname of file to be removed (native). */
 {
     OSErr err;
Index: unix/tclUnixFCmd.c
===================================================================
RCS file: /cvsroot/tcl/tcl/unix/tclUnixFCmd.c,v
retrieving revision 1.25
diff -u -r1.25 tclUnixFCmd.c
--- unix/tclUnixFCmd.c	28 Jun 2002 09:56:54 -0000	1.25
+++ unix/tclUnixFCmd.c	29 Jan 2003 10:47:39 -0000
@@ -134,7 +134,6 @@
 static int		DoCopyFile _ANSI_ARGS_((CONST char *srcPtr,
 			    CONST char *dstPtr));
 static int		DoCreateDirectory _ANSI_ARGS_((CONST char *pathPtr));
-static int		DoDeleteFile _ANSI_ARGS_((CONST char *path));
 static int		DoRemoveDirectory _ANSI_ARGS_((Tcl_DString *pathPtr,
 			    int recursive, Tcl_DString *errorPtr));
 static int		DoRenameFile _ANSI_ARGS_((CONST char *src,
@@ -500,7 +499,7 @@
 /*
  *---------------------------------------------------------------------------
  *
- * TclpObjDeleteFile, DoDeleteFile --
+ * TclpObjDeleteFile, TclpDeleteFile --
  *
  *      Removes a single file (not a directory).
  *
@@ -523,11 +522,11 @@
 TclpObjDeleteFile(pathPtr)
     Tcl_Obj *pathPtr;
 {
-    return DoDeleteFile(Tcl_FSGetNativePath(pathPtr));
+    return TclpDeleteFile(Tcl_FSGetNativePath(pathPtr));
 }
 
-static int
-DoDeleteFile(path)
+int
+TclpDeleteFile(path)
     CONST char *path;	/* Pathname of file to be removed (native). */
 {
     if (unlink(path) != 0) {				/* INTL: Native. */
@@ -995,7 +994,7 @@
 {
     switch (type) {
         case DOTREE_F: {
-	    if (DoDeleteFile(Tcl_DStringValue(srcPtr)) == 0) {
+	    if (TclpDeleteFile(Tcl_DStringValue(srcPtr)) == 0) {
 		return TCL_OK;
 	    }
 	    break;
Index: win/tclWin32Dll.c
===================================================================
RCS file: /cvsroot/tcl/tcl/win/tclWin32Dll.c,v
retrieving revision 1.23
diff -u -r1.23 tclWin32Dll.c
--- win/tclWin32Dll.c	25 Jan 2003 14:11:32 -0000	1.23
+++ win/tclWin32Dll.c	29 Jan 2003 10:47:39 -0000
@@ -556,9 +556,37 @@
 /*
  *---------------------------------------------------------------------------
  *
+ * TclWinResetInterfaceEncodings --
+ *
+ *	Called during finalization to free up any encodings we use.
+ *	The tclWinProcs-> look up table is still ok to use after
+ *	this call, provided no encoding conversion is required.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	None.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+TclWinResetInterfaceEncodings()
+{
+    if (tclWinTCharEncoding != NULL) {
+	Tcl_FreeEncoding(tclWinTCharEncoding);
+	tclWinTCharEncoding = NULL;
+    }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
  * TclWinResetInterfaces --
  *
  *	Called during finalization to reset us to a safe state for reuse.
+ *	After this call, it is best not to use the tclWinProcs-> look
+ *	up table since it is likely to be different to what is expected.
  *
  * Results:
  *	None.
@@ -568,12 +596,9 @@
  *
  *---------------------------------------------------------------------------
  */
-
 void
 TclWinResetInterfaces()
 {
-    Tcl_FreeEncoding(tclWinTCharEncoding);
-    tclWinTCharEncoding = NULL;
     tclWinProcs = &asciiProcs;
 }
 
Index: win/tclWinFCmd.c
===================================================================
RCS file: /cvsroot/tcl/tcl/win/tclWinFCmd.c,v
retrieving revision 1.33
diff -u -r1.33 tclWinFCmd.c
--- win/tclWinFCmd.c	25 Jan 2003 14:11:33 -0000	1.33
+++ win/tclWinFCmd.c	29 Jan 2003 10:47:39 -0000
@@ -99,7 +99,6 @@
 			    Tcl_Obj **attributePtrPtr);
 static int		DoCopyFile(CONST TCHAR *srcPtr, CONST TCHAR *dstPtr);
 static int		DoCreateDirectory(CONST TCHAR *pathPtr);
-static int		DoDeleteFile(CONST TCHAR *pathPtr);
 static int		DoRemoveJustDirectory(CONST TCHAR *nativeSrc, 
 			    int ignoreError, Tcl_DString *errorPtr);
 static int		DoRemoveDirectory(Tcl_DString *pathPtr, int recursive, 
@@ -670,7 +669,7 @@
 /*
  *---------------------------------------------------------------------------
  *
- * TclpObjDeleteFile, DoDeleteFile --
+ * TclpObjDeleteFile, TclpDeleteFile --
  *
  *      Removes a single file (not a directory).
  *
@@ -696,11 +695,11 @@
 TclpObjDeleteFile(pathPtr)
     Tcl_Obj *pathPtr;
 {
-    return DoDeleteFile(Tcl_FSGetNativePath(pathPtr));
+    return TclpDeleteFile(Tcl_FSGetNativePath(pathPtr));
 }
 
-static int
-DoDeleteFile(
+int
+TclpDeleteFile(
     CONST TCHAR *nativePath)	/* Pathname of file to be removed (native). */
 {
     DWORD attr;
@@ -1380,7 +1379,7 @@
 {
     switch (type) {
 	case DOTREE_F: {
-	    if (DoDeleteFile(nativeSrc) == TCL_OK) {
+	    if (TclpDeleteFile(nativeSrc) == TCL_OK) {
 		return TCL_OK;
 	    }
 	    break;
Index: win/tclWinInit.c
===================================================================
RCS file: /cvsroot/tcl/tcl/win/tclWinInit.c,v
retrieving revision 1.37
diff -u -r1.37 tclWinInit.c
--- win/tclWinInit.c	10 Jan 2003 15:03:55 -0000	1.37
+++ win/tclWinInit.c	29 Jan 2003 10:47:39 -0000
@@ -465,7 +465,7 @@
 /*
  *---------------------------------------------------------------------------
  *
- * TclWinFilesystemAndEncodingsCleanup --
+ * TclWinEncodingsCleanup --
  *
  *	Reset information to its original state in finalization to
  *	allow for reinitialization to be possible.  This must not
@@ -482,9 +482,9 @@
  */
 
 void
-TclWinFilesystemAndEncodingsCleanup()
+TclWinEncodingsCleanup()
 {
-    TclWinResetInterfaces();
+    TclWinResetInterfaceEncodings();
     libraryPathEncodingFixed = 0;
     if (binaryEncoding != NULL) {
 	Tcl_FreeEncoding(binaryEncoding);
Index: win/tclWinInt.h
===================================================================
RCS file: /cvsroot/tcl/tcl/win/tclWinInt.h,v
retrieving revision 1.19
diff -u -r1.19 tclWinInt.h
--- win/tclWinInt.h	10 Jan 2003 15:03:55 -0000	1.19
+++ win/tclWinInt.h	29 Jan 2003 10:47:39 -0000
@@ -112,7 +112,8 @@
  * stubs table.
  */
 
-EXTERN void		TclWinFilesystemAndEncodingsCleanup();
+EXTERN void		TclWinEncodingsCleanup();
+EXTERN void		TclWinResetInterfaceEncodings();
 EXTERN void		TclWinInit(HINSTANCE hInst);
 EXTERN int              TclWinSymLinkCopyDirectory(CONST TCHAR* LinkOriginal,
 						   CONST TCHAR* LinkCopy);