? tclFinalize.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	28 Jan 2003 18:55:54 -0000
@@ -813,6 +813,16 @@
 	TclFinalizeEnvironment();
 
 	/* 
+	 * 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.
+	 */
+	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,22 +853,17 @@
 	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.
 	 */
-	TclFinalizeFilesystem();
+
+	TclFinalizeLoad();
 
 	/*
 	 * 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	28 Jan 2003 18:55:54 -0000
@@ -612,33 +612,24 @@
 	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;
     }
@@ -649,6 +640,13 @@
     accessProcList = NULL;
     openFileChannelProcList = NULL;
     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
@@ -2634,8 +2632,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 +2658,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 +2788,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	28 Jan 2003 18:55:55 -0000
@@ -1716,6 +1716,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	28 Jan 2003 18:55:57 -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	28 Jan 2003 18:55:58 -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/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	28 Jan 2003 18:55:58 -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;
