Index: generic/tclFileSystem.h ================================================================== --- generic/tclFileSystem.h +++ generic/tclFileSystem.h @@ -50,10 +50,11 @@ int cwdPathEpoch; int filesystemEpoch; Tcl_Obj *cwdPathPtr; ClientData cwdClientData; FilesystemRecord *filesystemList; + int claims; } ThreadSpecificData; /* * The internal TclFS API provides routines for handling and manipulating * paths efficiently, taking direct advantage of the "path" Tcl_Obj type. Index: generic/tclIOUtil.c ================================================================== --- generic/tclIOUtil.c +++ generic/tclIOUtil.c @@ -36,10 +36,12 @@ static Tcl_Obj * FsListMounts(Tcl_Obj *pathPtr, const char *pattern); static void FsAddMountsToGlobResult(Tcl_Obj *resultPtr, Tcl_Obj *pathPtr, const char *pattern, Tcl_GlobTypeData *types); static void FsUpdateCwd(Tcl_Obj *cwdObj, ClientData clientData); +static void Claim(void); +static void Disclaim(void); #ifdef TCL_THREADS static void FsRecacheFilesystemList(void); #endif @@ -592,19 +594,21 @@ /* * Trash the current cache. */ - fsRecPtr = tsdPtr->filesystemList; - while (fsRecPtr != NULL) { + if (tsdPtr->claims <= 0) { + fsRecPtr = tsdPtr->filesystemList; + while (fsRecPtr != NULL) { tmpFsRecPtr = fsRecPtr->nextPtr; - if (--fsRecPtr->fileRefCount <= 0) { - ckfree((char *)fsRecPtr); + if (--fsRecPtr->fileRefCount <= 0) { + ckfree((char *)fsRecPtr); + } + fsRecPtr = tmpFsRecPtr; } - fsRecPtr = tmpFsRecPtr; + tsdPtr->filesystemList = NULL; } - tsdPtr->filesystemList = NULL; /* * Code below operates on shared data. We are already called under mutex * lock so we can safely proceed. * @@ -625,13 +629,10 @@ while (fsRecPtr != NULL) { tmpFsRecPtr = (FilesystemRecord *) ckalloc(sizeof(FilesystemRecord)); *tmpFsRecPtr = *fsRecPtr; tmpFsRecPtr->nextPtr = tsdPtr->filesystemList; tmpFsRecPtr->prevPtr = NULL; - if (tsdPtr->filesystemList) { - tsdPtr->filesystemList->prevPtr = tmpFsRecPtr; - } tsdPtr->filesystemList = tmpFsRecPtr; fsRecPtr = fsRecPtr->prevPtr; } /* @@ -677,10 +678,51 @@ { ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey); (void) FsGetFirstFilesystem(); return (filesystemEpoch == tsdPtr->filesystemEpoch); } + +static void +Claim() +{ +#ifdef TCL_THREADS + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey); + + tsdPtr->claims++; +#endif +} + +static void +Disclaim() +{ +#ifdef TCL_THREADS + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey); + FilesystemRecord *toRelease, *fsRecPtr = tsdPtr->filesystemList; + + if (--tsdPtr->claims > 0) { + return; + } + /* + * No claims held, Release all out of date FilesystemRecords from the + * tsdPtr->filesystemList. First skip the current list. + */ + while (fsRecPtr->fsPtr != &tclNativeFilesystem) { + fsRecPtr = fsRecPtr->nextPtr; + } + + /* Then release everything that comes after. */ + toRelease = fsRecPtr->nextPtr; + while (toRelease != NULL) { + fsRecPtr = toRelease->nextPtr; + + if (--toRelease->fileRefCount <= 0) { + ckfree((char *)toRelease); + } + toRelease = fsRecPtr; + } +#endif +} /* * If non-NULL, clientData is owned by us and must be freed later. */ @@ -1366,10 +1408,13 @@ */ while ((retVal == NULL) && (fsRecPtr != NULL)) { if (fsRecPtr->fsPtr == fsPtr) { retVal = fsRecPtr->clientData; + } + if (fsRecPtr->fsPtr == &tclNativeFilesystem) { + break; } fsRecPtr = fsRecPtr->nextPtr; } return retVal; @@ -1425,10 +1470,11 @@ * native filesystem (i.e. '/' on unix is native). */ firstFsRecPtr = FsGetFirstFilesystem(); + Claim(); fsRecPtr = firstFsRecPtr; while (fsRecPtr != NULL) { if (fsRecPtr->fsPtr == &tclNativeFilesystem) { Tcl_FSNormalizePathProc *proc = fsRecPtr->fsPtr->normalizePathProc; if (proc != NULL) { @@ -1443,11 +1489,13 @@ while (fsRecPtr != NULL) { /* * Skip the native system next time through. */ - if (fsRecPtr->fsPtr != &tclNativeFilesystem) { + if (fsRecPtr->fsPtr == &tclNativeFilesystem) { + break; + } else { Tcl_FSNormalizePathProc *proc = fsRecPtr->fsPtr->normalizePathProc; if (proc != NULL) { startAt = (*proc)(interp, pathPtr, startAt); } @@ -1457,10 +1505,11 @@ * but there's not much benefit. */ } fsRecPtr = fsRecPtr->nextPtr; } + Disclaim(); return startAt; } /* @@ -2651,10 +2700,11 @@ * "Tcl_GetCwd" function in succession. A non-NULL return value * indicates the particular function has succeeded. */ fsRecPtr = FsGetFirstFilesystem(); + Claim(); while ((retVal == NULL) && (fsRecPtr != NULL)) { Tcl_FSGetCwdProc *proc = fsRecPtr->fsPtr->getCwdProc; if (proc != NULL) { if (fsRecPtr->fsPtr->version != TCL_FILESYSTEM_VERSION_1) { ClientData retCd; @@ -2697,13 +2747,17 @@ Tcl_PosixError(interp), NULL); } } else { retVal = (*proc)(interp); } + } + if (fsRecPtr->fsPtr == &tclNativeFilesystem) { + break; } fsRecPtr = fsRecPtr->nextPtr; } + Disclaim(); /* * Now the 'cwd' may NOT be normalized, at least on some platforms. * For the sake of efficiency, we want a completely normalized cwd at * all times. @@ -3646,21 +3700,26 @@ * all the functions registered, since we want a list of all drives from * all filesystems. */ fsRecPtr = FsGetFirstFilesystem(); + Claim(); while (fsRecPtr != NULL) { Tcl_FSListVolumesProc *proc = fsRecPtr->fsPtr->listVolumesProc; if (proc != NULL) { Tcl_Obj *thisFsVolumes = (*proc)(); if (thisFsVolumes != NULL) { Tcl_ListObjAppendList(NULL, resultPtr, thisFsVolumes); Tcl_DecrRefCount(thisFsVolumes); } + } + if (fsRecPtr->fsPtr == &tclNativeFilesystem) { + break; } fsRecPtr = fsRecPtr->nextPtr; } + Disclaim(); return resultPtr; } /* @@ -3696,10 +3755,11 @@ * indicates the particular function has succeeded. We call all the * functions registered, since we want a list from each filesystems. */ fsRecPtr = FsGetFirstFilesystem(); + Claim(); while (fsRecPtr != NULL) { if (fsRecPtr->fsPtr != &tclNativeFilesystem) { Tcl_FSMatchInDirectoryProc *proc = fsRecPtr->fsPtr->matchInDirectoryProc; if (proc != NULL) { @@ -3706,13 +3766,17 @@ if (resultPtr == NULL) { resultPtr = Tcl_NewObj(); } (*proc)(NULL, resultPtr, pathPtr, pattern, &mountsOnly); } + } + if (fsRecPtr->fsPtr == &tclNativeFilesystem) { + break; } fsRecPtr = fsRecPtr->nextPtr; } + Disclaim(); return resultPtr; } /* @@ -3827,17 +3891,23 @@ ClientData clientData, FilesystemRecord **fsRecPtrPtr) { FilesystemRecord *fsRecPtr = FsGetFirstFilesystem(); + Claim(); while (fsRecPtr != NULL) { if (fsRecPtr->fsPtr == fromFilesystem) { *fsRecPtrPtr = fsRecPtr; break; + } + if (fsRecPtr->fsPtr == &tclNativeFilesystem) { + fsRecPtr = NULL; + break; } fsRecPtr = fsRecPtr->nextPtr; } + Disclaim(); if ((fsRecPtr != NULL) && (fromFilesystem->internalToNormalizedProc != NULL)) { return (*fromFilesystem->internalToNormalizedProc)(clientData); } else { @@ -3946,10 +4016,11 @@ * the given path is an absolute path on any of the volumes returned (this * is done by checking whether the path's prefix matches). */ fsRecPtr = FsGetFirstFilesystem(); + Claim(); while (fsRecPtr != NULL) { Tcl_FSListVolumesProc *proc = fsRecPtr->fsPtr->listVolumesProc; /* * We want to skip the native filesystem in this loop because @@ -4021,13 +4092,17 @@ * We don't need to examine any more filesystems. */ break; } } + } + if (fsRecPtr->fsPtr == &tclNativeFilesystem) { + break; } fsRecPtr = fsRecPtr->nextPtr; } + Disclaim(); return type; } /* *--------------------------------------------------------------------------- @@ -4418,10 +4493,11 @@ * have the most up-to-date copy of the master filesystem. This is * accomplished by the FsGetFirstFilesystem() call. */ fsRecPtr = FsGetFirstFilesystem(); + Claim(); if (TclFSEnsureEpochOk(pathPtr, &retVal) != TCL_OK) { return NULL; } @@ -4443,13 +4519,17 @@ */ TclFSSetPathDetails(pathPtr, fsRecPtr, clientData); retVal = fsRecPtr->fsPtr; } + } + if (fsRecPtr->fsPtr == &tclNativeFilesystem) { + break; } fsRecPtr = fsRecPtr->nextPtr; } + Disclaim(); return retVal; } /*