From 071288d23e801671b0ef45c73fc70203c6b8c3c4 Mon Sep 17 00:00:00 2001 From: Konstantin Kushnir Date: Wed, 5 Jun 2024 06:42:48 +0000 Subject: [PATCH] The first attempt to make cookfs thread-aware --- generic/vfs.c | 1 + generic/vfs.h | 11 +++ generic/vfsVfs.c | 182 ++++++++++++++++++++++++++++++++++++----------- 3 files changed, 153 insertions(+), 41 deletions(-) diff --git a/generic/vfs.c b/generic/vfs.c index 312ea10..76e7947 100644 --- a/generic/vfs.c +++ b/generic/vfs.c @@ -47,6 +47,7 @@ Cookfs_Vfs *Cookfs_VfsInit(Tcl_Interp* interp, Tcl_Obj* mountPoint, vfs->commandToken = NULL; vfs->interp = interp; vfs->isDead = 0; + vfs->isAlive = 0; #ifdef COOKFS_USETCLCMDS vfs->isRegistered = 0; #endif diff --git a/generic/vfs.h b/generic/vfs.h index e87b4fd..c700c92 100644 --- a/generic/vfs.h +++ b/generic/vfs.h @@ -14,6 +14,7 @@ typedef struct Cookfs_Vfs { Tcl_Command commandToken; int isDead; + int isAlive; #ifdef COOKFS_USETCLCMDS int isRegistered; #endif @@ -26,6 +27,16 @@ typedef struct Cookfs_Vfs { Cookfs_Fsindex *index; Cookfs_Writer *writer; +#ifdef TCL_THREADS + Tcl_Mutex tdLock; + unsigned int tdNumRead; + unsigned int tdNumWrite; + Tcl_Condition tdCondRead; + Tcl_Condition tdCondWrite; + int tdLockCount; + Tcl_ThreadId tdOwner; +#endif + struct Cookfs_Vfs* nextVfs; } Cookfs_Vfs; diff --git a/generic/vfsVfs.c b/generic/vfsVfs.c index 88461cf..0c913ab 100644 --- a/generic/vfsVfs.c +++ b/generic/vfsVfs.c @@ -8,17 +8,31 @@ #include "cookfs.h" -typedef struct ThreadSpecificData { - Cookfs_Vfs *listOfMounts; - Tcl_Obj *cookfsVolumes; -} ThreadSpecificData; +//typedef struct ThreadSpecificData { +// Cookfs_Vfs *listOfMounts; +// Tcl_Obj *cookfsVolumes; +//} ThreadSpecificData; -static Tcl_ThreadDataKey dataKeyCookfs; +//static Tcl_ThreadDataKey dataKeyCookfs; #define COOKFS_ASSOC_KEY "::cookfs::c::inUse" -#define TCL_TSD_INIT(keyPtr) \ - (ThreadSpecificData *)Tcl_GetThreadData((keyPtr), sizeof(ThreadSpecificData)) +/* #define TCL_TSD_INIT(keyPtr) \ + (ThreadSpecificData *)Tcl_GetThreadData((keyPtr), sizeof(ThreadSpecificData)) */ + +static struct { + int isInitialized; + Cookfs_Vfs *listOfMounts; + Tcl_Obj *cookfsVolumes; +#ifdef TCL_THREADS + Tcl_Mutex tdLock; +#endif +} global = { + 0, NULL, NULL +#ifdef TCL_THREADS + , NULL +#endif +}; // Forward declarations static Tcl_InterpDeleteProc Cookfs_CookfsUnregister; @@ -33,6 +47,16 @@ void Cookfs_CookfsRegister(Tcl_Interp *interp) { CookfsLog(printf("Cookfs_CookfsRegister: register in interp [%p]", (void *)interp)); + Tcl_MutexLock(&global.tdLock); + + if (global.isInitialized) { + CookfsLog(printf("Cookfs_CookfsRegister: global data is already" + " initialized")); + } else { + CookfsLog(printf("Cookfs_CookfsRegister: initialize global data...")); + global.isInitialized = 1; + } + // Register Cookfs if it is not already registered CookfsFSData *fsdata = (CookfsFSData *)Tcl_FSData(CookfsFilesystem()); if (fsdata == NULL) { @@ -40,7 +64,7 @@ void Cookfs_CookfsRegister(Tcl_Interp *interp) { fsdata = ckalloc(sizeof(CookfsFSData)); if (fsdata == NULL) { CookfsLog(printf("Cookfs_CookfsRegister: failed to alloc fsdata")); - return; + goto done; } Tcl_Obj *objv[2]; objv[0] = Tcl_NewStringObj("-vfs", -1); @@ -62,6 +86,12 @@ void Cookfs_CookfsRegister(Tcl_Interp *interp) { // Set callback to cleanup mount points for deleted interp Tcl_SetAssocData(interp, COOKFS_ASSOC_KEY, Cookfs_CookfsUnregister, (ClientData) 1); + +done: + + Tcl_MutexUnlock(&global.tdLock); + + return; } static void Cookfs_CookfsUnregister(ClientData clientData, @@ -81,6 +111,7 @@ static void Cookfs_CookfsUnregister(ClientData clientData, break; } CookfsLog(printf("Cookfs_CookfsUnregister: free vfs...")); + Cookfs_VfsFini(interp, vfs, NULL); } @@ -93,6 +124,7 @@ static void Cookfs_CookfsUnregister(ClientData clientData, static void Cookfs_CookfsExitProc(ClientData clientData) { UNUSED(clientData); + CookfsLog(printf("Cookfs_CookfsExitProc: ENTER")); CookfsFSData *fsdata = (CookfsFSData *)Tcl_FSData(CookfsFilesystem()); if (fsdata != NULL) { Tcl_DecrRefCount(fsdata->attrListRoot); @@ -101,14 +133,17 @@ static void Cookfs_CookfsExitProc(ClientData clientData) { ckfree(fsdata); } Tcl_FSUnregister(CookfsFilesystem()); + CookfsLog(printf("Cookfs_CookfsExitProc: ok")); } static void Cookfs_CookfsThreadExitProc(ClientData clientData) { UNUSED(clientData); - ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKeyCookfs); + CookfsLog(printf("Cookfs_CookfsThreadExitProc: ENTER")); + //ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKeyCookfs); // There is nothing here at the moment. Perhaps in the future it will be // used for thread-specific data cleansing. - UNUSED(tsdPtr); + //UNUSED(tsdPtr); + CookfsLog(printf("Cookfs_CookfsThreadExitProc: ok")); } int Cookfs_CookfsAddVfs(Tcl_Interp *interp, Cookfs_Vfs *vfs) { @@ -116,19 +151,28 @@ int Cookfs_CookfsAddVfs(Tcl_Interp *interp, Cookfs_Vfs *vfs) { CookfsLog(printf("Cookfs_CookfsAddVfs: add mount [%s] at [%p]", vfs->mountStr, (void *)vfs)); - ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKeyCookfs); +// ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKeyCookfs); // Check if interp is properly configured to cleanup mount points if (Tcl_GetAssocData(interp, COOKFS_ASSOC_KEY, NULL) == NULL) { return 0; } - vfs->nextVfs = tsdPtr->listOfMounts; - tsdPtr->listOfMounts = vfs; + Tcl_MutexLock(&global.tdLock); + + //vfs->nextVfs = tsdPtr->listOfMounts; + //tsdPtr->listOfMounts = vfs; + vfs->nextVfs = global.listOfMounts; + global.listOfMounts = vfs; + + vfs->isAlive = 1; if (vfs->isVolume) { Cookfs_CookfsAddVolume(vfs->mountObj); } + + Tcl_MutexUnlock(&global.tdLock); + Tcl_FSMountsChanged(CookfsFilesystem()); return 1; @@ -143,10 +187,12 @@ Cookfs_Vfs *Cookfs_CookfsRemoveVfs(Tcl_Interp *interp, Tcl_Obj* mountPoint, " point [%p] ptr [%p] interp [%p]", (void *)mountPoint, (void *)vfsToRemove, (void *)interp)); - ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKeyCookfs); + Tcl_MutexLock(&global.tdLock); + //ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKeyCookfs); Cookfs_Vfs *vfsPrev = NULL; - Cookfs_Vfs *vfs = tsdPtr->listOfMounts; + //Cookfs_Vfs *vfs = tsdPtr->listOfMounts; + Cookfs_Vfs *vfs = global.listOfMounts; while (vfs != NULL) { @@ -176,6 +222,8 @@ Cookfs_Vfs *Cookfs_CookfsRemoveVfs(Tcl_Interp *interp, Tcl_Obj* mountPoint, CookfsLog(printf("Cookfs_CookfsRemoveVfs: the vfs for deletion" " was found")); + vfs->isAlive = 0; + #ifdef COOKFS_USETCLCMDS // Unregister the Cookfs from Tclvfs. Tclvfs will call our // Unmount command. It should be able find this mount point and @@ -186,7 +234,8 @@ Cookfs_Vfs *Cookfs_CookfsRemoveVfs(Tcl_Interp *interp, Tcl_Obj* mountPoint, // Remove the mount from the mount chain if (vfsPrev == NULL) { - tsdPtr->listOfMounts = vfs->nextVfs; + //tsdPtr->listOfMounts = vfs->nextVfs; + global.listOfMounts = vfs->nextVfs; } else { vfsPrev->nextVfs = vfs->nextVfs; } @@ -195,6 +244,8 @@ Cookfs_Vfs *Cookfs_CookfsRemoveVfs(Tcl_Interp *interp, Tcl_Obj* mountPoint, Cookfs_CookfsRemoveVolume(vfs->mountObj); } + Tcl_MutexUnlock(&global.tdLock); + Tcl_FSMountsChanged(CookfsFilesystem()); CookfsLog(printf("Cookfs_CookfsRemoveVfs: return [%p]", @@ -207,6 +258,7 @@ Cookfs_Vfs *Cookfs_CookfsRemoveVfs(Tcl_Interp *interp, Tcl_Obj* mountPoint, } CookfsLog(printf("Cookfs_CookfsRemoveMount: return NULL")); + Tcl_MutexUnlock(&global.tdLock); return NULL; } @@ -217,7 +269,8 @@ void Cookfs_CookfsSearchVfsToListObj(Tcl_Obj *path, const char *pattern, CookfsLog(printf("Cookfs_CookfsSearchVfsToListObj: check mount points")); - ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKeyCookfs); + Tcl_MutexLock(&global.tdLock); + //ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKeyCookfs); Tcl_Size searchLen; const char *searchStr = Tcl_GetStringFromObj(Tcl_FSGetNormalizedPath(NULL, @@ -228,7 +281,8 @@ void Cookfs_CookfsSearchVfsToListObj(Tcl_Obj *path, const char *pattern, searchLen--; } - Cookfs_Vfs *vfs = tsdPtr->listOfMounts; + //Cookfs_Vfs *vfs = tsdPtr->listOfMounts; + Cookfs_Vfs *vfs = global.listOfMounts; while (vfs != NULL) { if (vfs->mountLen <= (searchLen + 1)) { CookfsLog(printf("Cookfs_CookfsSearchVfsToListObj: skip vfs" @@ -261,6 +315,8 @@ void Cookfs_CookfsSearchVfsToListObj(Tcl_Obj *path, const char *pattern, nextVfs: vfs = vfs->nextVfs; } + + Tcl_MutexUnlock(&global.tdLock); } @@ -269,17 +325,22 @@ int Cookfs_CookfsIsVfsExist(Cookfs_Vfs *vfsToSearch) { return 0; } - ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKeyCookfs); + Tcl_MutexLock(&global.tdLock); + //ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKeyCookfs); - Cookfs_Vfs *vfs = tsdPtr->listOfMounts; + int res = 0; + //Cookfs_Vfs *vfs = tsdPtr->listOfMounts; + Cookfs_Vfs *vfs = global.listOfMounts; while (vfs != NULL) { if (vfs == vfsToSearch) { - return 1; + res = 1; + break; } vfs = vfs->nextVfs; } - return 0; + Tcl_MutexUnlock(&global.tdLock); + return res; } Cookfs_Vfs *Cookfs_CookfsFindVfs(Tcl_Obj *path, Tcl_Size len) { @@ -287,7 +348,8 @@ Cookfs_Vfs *Cookfs_CookfsFindVfs(Tcl_Obj *path, Tcl_Size len) { return NULL; } - ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKeyCookfs); + Tcl_MutexLock(&global.tdLock); + //ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKeyCookfs); char *searchStr; if (len == -1) { @@ -296,42 +358,66 @@ Cookfs_Vfs *Cookfs_CookfsFindVfs(Tcl_Obj *path, Tcl_Size len) { searchStr = Tcl_GetString(path); } - Cookfs_Vfs *vfs = tsdPtr->listOfMounts; + //Cookfs_Vfs *vfs = tsdPtr->listOfMounts; + Cookfs_Vfs *vfs = global.listOfMounts; while (vfs != NULL) { if (vfs->mountLen == len && strncmp(vfs->mountStr, searchStr, (size_t)len) == 0) { - return vfs; + break; } vfs = vfs->nextVfs; } - return NULL; + Tcl_MutexUnlock(&global.tdLock); + return vfs; } Tcl_Obj *Cookfs_CookfsGetVolumesList(void) { - ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKeyCookfs); - return tsdPtr->cookfsVolumes; + //ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKeyCookfs); + //return tsdPtr->cookfsVolumes; + return global.cookfsVolumes; } +// This function must be under global.tdLock mutex + static void Cookfs_CookfsAddVolume(Tcl_Obj *volume) { - ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKeyCookfs); + //ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKeyCookfs); + + //if (tsdPtr->cookfsVolumes == NULL) { + // tsdPtr->cookfsVolumes = Tcl_NewListObj(0, NULL); + // Tcl_IncrRefCount(tsdPtr->cookfsVolumes); + //} + + if (global.cookfsVolumes == NULL) { + global.cookfsVolumes = Tcl_NewListObj(0, NULL); + Tcl_IncrRefCount(global.cookfsVolumes); + } else if (Tcl_IsShared(global.cookfsVolumes)) { + // If the volume list is not null, than check if it is shared. + // It is possible, that other thread uses this list. In this case, + // create a duplicate volume list. + Tcl_Obj *tmp = global.cookfsVolumes; + global.cookfsVolumes = Tcl_DuplicateObj(global.cookfsVolumes); + Tcl_DecrRefCount(tmp); + Tcl_IncrRefCount(global.cookfsVolumes); - if (tsdPtr->cookfsVolumes == NULL) { - tsdPtr->cookfsVolumes = Tcl_NewListObj(0, NULL); - Tcl_IncrRefCount(tsdPtr->cookfsVolumes); } - Tcl_ListObjAppendElement(NULL, tsdPtr->cookfsVolumes, volume); - + //Tcl_ListObjAppendElement(NULL, tsdPtr->cookfsVolumes, volume); + Tcl_ListObjAppendElement(NULL, global.cookfsVolumes, volume); } +// This function must be under global.tdLock mutex + static int Cookfs_CookfsRemoveVolume(Tcl_Obj *volume) { - ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKeyCookfs); + //ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKeyCookfs); - if (tsdPtr->cookfsVolumes == NULL || volume == NULL) { + //if (tsdPtr->cookfsVolumes == NULL || volume == NULL) { + // return TCL_OK; + //} + if (global.cookfsVolumes == NULL || volume == NULL) { return TCL_OK; } @@ -339,12 +425,14 @@ static int Cookfs_CookfsRemoveVolume(Tcl_Obj *volume) { char *volumeStr = Tcl_GetStringFromObj(volume, &volumeLen); Tcl_Size volCount; - Tcl_ListObjLength(NULL, tsdPtr->cookfsVolumes, &volCount); + //Tcl_ListObjLength(NULL, tsdPtr->cookfsVolumes, &volCount); + Tcl_ListObjLength(NULL, global.cookfsVolumes, &volCount); for (Tcl_Size i = 0; i < volCount; i++) { Tcl_Obj *obj; - Tcl_ListObjIndex(NULL, tsdPtr->cookfsVolumes, i, &obj); + //Tcl_ListObjIndex(NULL, tsdPtr->cookfsVolumes, i, &obj); + Tcl_ListObjIndex(NULL, global.cookfsVolumes, i, &obj); Tcl_IncrRefCount(obj); @@ -363,10 +451,22 @@ static int Cookfs_CookfsRemoveVolume(Tcl_Obj *volume) { // If we have only one volume in the list, then just release // the volume list if (volCount == 1) { - Tcl_DecrRefCount(tsdPtr->cookfsVolumes); - tsdPtr->cookfsVolumes = NULL; + //Tcl_DecrRefCount(tsdPtr->cookfsVolumes); + //tsdPtr->cookfsVolumes = NULL; + Tcl_DecrRefCount(global.cookfsVolumes); + global.cookfsVolumes = NULL; } else { - Tcl_ListObjReplace(NULL, tsdPtr->cookfsVolumes, i, 1, 0, NULL); + // check if the volume list is shared. It is possible, that other + // thread uses this list. In this case, create a duplicate + // volume list. + if (Tcl_IsShared(global.cookfsVolumes)) { + Tcl_Obj *tmp = global.cookfsVolumes; + global.cookfsVolumes = Tcl_DuplicateObj(global.cookfsVolumes); + Tcl_DecrRefCount(tmp); + Tcl_IncrRefCount(global.cookfsVolumes); + } + //Tcl_ListObjReplace(NULL, tsdPtr->cookfsVolumes, i, 1, 0, NULL); + Tcl_ListObjReplace(NULL, global.cookfsVolumes, i, 1, 0, NULL); } Tcl_DecrRefCount(obj);