Tcl Source Code

Check-in [223bd04c8b]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:More work in progress. The problem with release of the elements of a fileSystemList by one routine while some other (caller) routine is still traversing that list is not dependent on threaded operations. An unthreaded build can still encounter the problem. Revised so that threaded/unthreaded operations are much closer to the same (no direct TCL_THREADS dependency). Also simplified the epoch checking which reduces locking to when it's needed. Still have the problem of returning as valid FilesystemRecords that are pulled from an outdated epoch.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | bug-3024359
Files: files | file ages | folders
SHA1: 223bd04c8b4fcdd50ca609acd2a7211d96414718
User & Date: dgp 2012-06-13 17:26:41
Context
2012-06-18
20:36
Next draft fix. This one appears to solve the problem, at least as demo'd by the test attached to T... check-in: dea555ba83 user: dgp tags: bug-3024359
2012-06-13
17:26
More work in progress. The problem with release of the elements of a fileSystemList by one routine w... check-in: 223bd04c8b user: dgp tags: bug-3024359
2012-06-12
13:44
Convert function calls to macros. check-in: eefb9a9fee user: dgp tags: bug-3024359
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to generic/tclIOUtil.c.

35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
static void		FsThrExitProc(ClientData cd);
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);

#ifdef TCL_THREADS
static void		FsRecacheFilesystemList(void);
static void		Purge(FilesystemRecord *fsRecPtr);

#define Claim() (tsdPtr->claims++)
#define Disclaim() if (--tsdPtr->claims <= 0) Purge(tsdPtr->filesystemList);
#else
#define Claim() 
#define Disclaim()
#endif

/*
 * These form part of the native filesystem support. They are needed here
 * because we have a few native filesystem functions (which are the same for
 * win/unix) in this file. There is no need to place them in tclInt.h, because
 * they are not (and should not be) used anywhere else.
 */







<





<
<
<
<







35
36
37
38
39
40
41

42
43
44
45
46




47
48
49
50
51
52
53
static void		FsThrExitProc(ClientData cd);
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		FsRecacheFilesystemList(void);
static void		Purge(FilesystemRecord *fsRecPtr);

#define Claim() (tsdPtr->claims++)
#define Disclaim() if (--tsdPtr->claims <= 0) Purge(tsdPtr->filesystemList);





/*
 * These form part of the native filesystem support. They are needed here
 * because we have a few native filesystem functions (which are the same for
 * win/unix) in this file. There is no need to place them in tclInt.h, because
 * they are not (and should not be) used anywhere else.
 */
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
	    return 1;
	} else {
	    return 0;
	}
    }
}

#ifdef TCL_THREADS
static void
FsRecacheFilesystemList(void)
{
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey);
    FilesystemRecord *fsRecPtr, *tmpFsRecPtr = NULL;

    /*







<







581
582
583
584
585
586
587

588
589
590
591
592
593
594
	    return 1;
	} else {
	    return 0;
	}
    }
}


static void
FsRecacheFilesystemList(void)
{
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey);
    FilesystemRecord *fsRecPtr, *tmpFsRecPtr = NULL;

    /*
610
611
612
613
614
615
616
617
618
619
620
621
622

623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641


642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
	    }
	    fsRecPtr = tmpFsRecPtr;
	}
	tsdPtr->filesystemList = NULL;
    }

    /*
     * Code below operates on shared data. We are already called under mutex
     * lock so we can safely proceed.
     *
     * Locate tail of the global filesystem list.
     */


    fsRecPtr = filesystemList;
    while (fsRecPtr != NULL) {
	tmpFsRecPtr = fsRecPtr;
	fsRecPtr = fsRecPtr->nextPtr;
    }

    /*
     * Refill the cache honouring the order.
     */

    fsRecPtr = tmpFsRecPtr;
    while (fsRecPtr != NULL) {
	tmpFsRecPtr = (FilesystemRecord *) ckalloc(sizeof(FilesystemRecord));
	*tmpFsRecPtr = *fsRecPtr;
	tmpFsRecPtr->nextPtr = tsdPtr->filesystemList;
	tmpFsRecPtr->prevPtr = NULL;
	tsdPtr->filesystemList = tmpFsRecPtr;
	fsRecPtr = fsRecPtr->prevPtr;
    }



    /*
     * Make sure the above gets released on thread exit.
     */

    if (tsdPtr->initialized == 0) {
	Tcl_CreateThreadExitHandler(FsThrExitProc, (ClientData) tsdPtr);
	tsdPtr->initialized = 1;
    }
}
#endif /* TCL_THREADS */

static FilesystemRecord *
FsGetFirstFilesystem(void)
{
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey);
    FilesystemRecord *fsRecPtr;
#ifndef TCL_THREADS
    tsdPtr->filesystemEpoch = theFilesystemEpoch;
    fsRecPtr = filesystemList;
#else
    Tcl_MutexLock(&filesystemMutex);
    if (tsdPtr->filesystemList == NULL
	    || (tsdPtr->filesystemEpoch != theFilesystemEpoch)) {
	FsRecacheFilesystemList();
	tsdPtr->filesystemEpoch = theFilesystemEpoch;
    }
    Tcl_MutexUnlock(&filesystemMutex);
    fsRecPtr = tsdPtr->filesystemList;
#endif
    return fsRecPtr;
}

/*
 * The epoch can be changed both by filesystems being added or removed and by
 * env(HOME) changing.
 */

int
TclFSEpochOk(
    int filesystemEpoch)
{
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey);
    (void) FsGetFirstFilesystem();
    return (filesystemEpoch == tsdPtr->filesystemEpoch);
}

#ifdef TCL_THREADS
static void
Purge(
    FilesystemRecord *fsRecPtr)
{
    FilesystemRecord *toRelease;

    /*







<
<
<



>



















>
>










<





<
<
<
<
<
<



<

<
|
<
<











<
<
|


<







604
605
606
607
608
609
610



611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645

646
647
648
649
650






651
652
653

654

655


656
657
658
659
660
661
662
663
664
665
666


667
668
669

670
671
672
673
674
675
676
	    }
	    fsRecPtr = tmpFsRecPtr;
	}
	tsdPtr->filesystemList = NULL;
    }

    /*



     * Locate tail of the global filesystem list.
     */

    Tcl_MutexLock(&filesystemMutex);
    fsRecPtr = filesystemList;
    while (fsRecPtr != NULL) {
	tmpFsRecPtr = fsRecPtr;
	fsRecPtr = fsRecPtr->nextPtr;
    }

    /*
     * Refill the cache honouring the order.
     */

    fsRecPtr = tmpFsRecPtr;
    while (fsRecPtr != NULL) {
	tmpFsRecPtr = (FilesystemRecord *) ckalloc(sizeof(FilesystemRecord));
	*tmpFsRecPtr = *fsRecPtr;
	tmpFsRecPtr->nextPtr = tsdPtr->filesystemList;
	tmpFsRecPtr->prevPtr = NULL;
	tsdPtr->filesystemList = tmpFsRecPtr;
	fsRecPtr = fsRecPtr->prevPtr;
    }
    tsdPtr->filesystemEpoch = theFilesystemEpoch;
    Tcl_MutexUnlock(&filesystemMutex);

    /*
     * Make sure the above gets released on thread exit.
     */

    if (tsdPtr->initialized == 0) {
	Tcl_CreateThreadExitHandler(FsThrExitProc, (ClientData) tsdPtr);
	tsdPtr->initialized = 1;
    }
}


static FilesystemRecord *
FsGetFirstFilesystem(void)
{
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey);






    if (tsdPtr->filesystemList == NULL
	    || (tsdPtr->filesystemEpoch != theFilesystemEpoch)) {
	FsRecacheFilesystemList();

    }

    return tsdPtr->filesystemList;


}

/*
 * The epoch can be changed both by filesystems being added or removed and by
 * env(HOME) changing.
 */

int
TclFSEpochOk(
    int filesystemEpoch)
{


    return (filesystemEpoch == theFilesystemEpoch);
}


static void
Purge(
    FilesystemRecord *fsRecPtr)
{
    FilesystemRecord *toRelease;

    /*
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722

	if (--toRelease->fileRefCount <= 0) {
	    ckfree((char *)toRelease);
	}
	toRelease = fsRecPtr;
    }
}
#endif

/*
 * If non-NULL, clientData is owned by us and must be freed later.
 */

static void
FsUpdateCwd(







<







688
689
690
691
692
693
694

695
696
697
698
699
700
701

	if (--toRelease->fileRefCount <= 0) {
	    ckfree((char *)toRelease);
	}
	toRelease = fsRecPtr;
    }
}


/*
 * If non-NULL, clientData is owned by us and must be freed later.
 */

static void
FsUpdateCwd(
828
829
830
831
832
833
834

835
836
837
838
839
840
841

	    if (fsRecPtr->fsPtr != &tclNativeFilesystem) {
		ckfree((char *)fsRecPtr);
	    }
	}
	fsRecPtr = tmpFsRecPtr;
    }

    filesystemList = NULL;

    /*
     * Now filesystemList is NULL. This means that any attempt to use the
     * filesystem is likely to fail.
     */








>







807
808
809
810
811
812
813
814
815
816
817
818
819
820
821

	    if (fsRecPtr->fsPtr != &tclNativeFilesystem) {
		ckfree((char *)fsRecPtr);
	    }
	}
	fsRecPtr = tmpFsRecPtr;
    }
    theFilesystemEpoch++;
    filesystemList = NULL;

    /*
     * Now filesystemList is NULL. This means that any attempt to use the
     * filesystem is likely to fail.
     */

865
866
867
868
869
870
871

872
873
874
875
876
877
878
 *----------------------------------------------------------------------
 */

void
TclResetFilesystem(void)
{
    filesystemList = &nativeFilesystemRecord;


    /*
     * Note, at this point, I believe nativeFilesystemRecord -> fileRefCount
     * should equal 1 and if not, we should try to track down the cause.
     */

#ifdef __WIN32__







>







845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
 *----------------------------------------------------------------------
 */

void
TclResetFilesystem(void)
{
    filesystemList = &nativeFilesystemRecord;
    theFilesystemEpoch++;

    /*
     * Note, at this point, I believe nativeFilesystemRecord -> fileRefCount
     * should equal 1 and if not, we should try to track down the cause.
     */

#ifdef __WIN32__
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
    Tcl_Interp *interp,		/* Used for error messages. */
    Tcl_Obj *pathPtr,		/* The path to normalize in place */
    int startAt,		/* Start at this char-offset */
    ClientData *clientDataPtr)	/* If we generated a complete normalized path
				 * for a given filesystem, we can optionally
				 * return an fs-specific clientdata here. */
{
#ifdef TCL_THREADS
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey);
#endif
    FilesystemRecord *fsRecPtr, *firstFsRecPtr;
    /* Ignore this variable */
    (void) clientDataPtr;

    /*
     * Call each of the "normalise path" functions in succession. This is a
     * special case, in which if we have a native filesystem handler, we call







<

<







1428
1429
1430
1431
1432
1433
1434

1435

1436
1437
1438
1439
1440
1441
1442
    Tcl_Interp *interp,		/* Used for error messages. */
    Tcl_Obj *pathPtr,		/* The path to normalize in place */
    int startAt,		/* Start at this char-offset */
    ClientData *clientDataPtr)	/* If we generated a complete normalized path
				 * for a given filesystem, we can optionally
				 * return an fs-specific clientdata here. */
{

    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey);

    FilesystemRecord *fsRecPtr, *firstFsRecPtr;
    /* Ignore this variable */
    (void) clientDataPtr;

    /*
     * Call each of the "normalise path" functions in succession. This is a
     * special case, in which if we have a native filesystem handler, we call
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
 *
 *---------------------------------------------------------------------------
 */

Tcl_Obj*
Tcl_FSListVolumes(void)
{
#ifdef TCL_THREADS
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey);
#endif
    FilesystemRecord *fsRecPtr;
    Tcl_Obj *resultPtr = Tcl_NewObj();

    /*
     * Call each of the "listVolumes" function in succession. A non-NULL
     * return value indicates the particular function has succeeded. We call
     * all the functions registered, since we want a list of all drives from







<

<







3661
3662
3663
3664
3665
3666
3667

3668

3669
3670
3671
3672
3673
3674
3675
 *
 *---------------------------------------------------------------------------
 */

Tcl_Obj*
Tcl_FSListVolumes(void)
{

    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey);

    FilesystemRecord *fsRecPtr;
    Tcl_Obj *resultPtr = Tcl_NewObj();

    /*
     * Call each of the "listVolumes" function in succession. A non-NULL
     * return value indicates the particular function has succeeded. We call
     * all the functions registered, since we want a list of all drives from
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
 */

static Tcl_Obj *
FsListMounts(
    Tcl_Obj *pathPtr,		/* Contains path to directory to search. */
    const char *pattern)	/* Pattern to match against. */
{
#ifdef TCL_THREADS
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey);
#endif
    FilesystemRecord *fsRecPtr;
    Tcl_GlobTypeData mountsOnly = { TCL_GLOB_TYPE_MOUNT, 0, NULL, NULL };
    Tcl_Obj *resultPtr = NULL;

    /*
     * Call each of the "matchInDirectory" functions in succession, with the
     * specific type information 'mountsOnly'. A non-NULL return value







<

<







3716
3717
3718
3719
3720
3721
3722

3723

3724
3725
3726
3727
3728
3729
3730
 */

static Tcl_Obj *
FsListMounts(
    Tcl_Obj *pathPtr,		/* Contains path to directory to search. */
    const char *pattern)	/* Pattern to match against. */
{

    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey);

    FilesystemRecord *fsRecPtr;
    Tcl_GlobTypeData mountsOnly = { TCL_GLOB_TYPE_MOUNT, 0, NULL, NULL };
    Tcl_Obj *resultPtr = NULL;

    /*
     * Call each of the "matchInDirectory" functions in succession, with the
     * specific type information 'mountsOnly'. A non-NULL return value
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
/* Simple helper function */
Tcl_Obj *
TclFSInternalToNormalized(
    Tcl_Filesystem *fromFilesystem,
    ClientData clientData,
    FilesystemRecord **fsRecPtrPtr)
{
#ifdef TCL_THREADS
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey);
#endif
    FilesystemRecord *fsRecPtr = FsGetFirstFilesystem();

    Claim();
    while (fsRecPtr != NULL) {
	if (fsRecPtr->fsPtr == fromFilesystem) {
	    *fsRecPtrPtr = fsRecPtr;
	    break;







<

<







3863
3864
3865
3866
3867
3868
3869

3870

3871
3872
3873
3874
3875
3876
3877
/* Simple helper function */
Tcl_Obj *
TclFSInternalToNormalized(
    Tcl_Filesystem *fromFilesystem,
    ClientData clientData,
    FilesystemRecord **fsRecPtrPtr)
{

    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey);

    FilesystemRecord *fsRecPtr = FsGetFirstFilesystem();

    Claim();
    while (fsRecPtr != NULL) {
	if (fsRecPtr->fsPtr == fromFilesystem) {
	    *fsRecPtrPtr = fsRecPtr;
	    break;
4008
4009
4010
4011
4012
4013
4014
4015
4016
4017
4018
4019
4020
4021
4022
4023
4024
				 * driveName. */
    Tcl_Obj **driveNameRef)	/* If the path is absolute, and this is
				 * non-NULL, then set to the name of the
				 * drive, network-volume which contains the
				 * path, already with a refCount for the
				 * caller. */
{
#ifdef TCL_THREADS
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey);
#endif
    FilesystemRecord *fsRecPtr;
    Tcl_PathType type = TCL_PATH_RELATIVE;

    /*
     * Call each of the "listVolumes" function in succession, checking whether
     * the given path is an absolute path on any of the volumes returned (this
     * is done by checking whether the path's prefix matches).







<

<







3981
3982
3983
3984
3985
3986
3987

3988

3989
3990
3991
3992
3993
3994
3995
				 * driveName. */
    Tcl_Obj **driveNameRef)	/* If the path is absolute, and this is
				 * non-NULL, then set to the name of the
				 * drive, network-volume which contains the
				 * path, already with a refCount for the
				 * caller. */
{

    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey);

    FilesystemRecord *fsRecPtr;
    Tcl_PathType type = TCL_PATH_RELATIVE;

    /*
     * Call each of the "listVolumes" function in succession, checking whether
     * the given path is an absolute path on any of the volumes returned (this
     * is done by checking whether the path's prefix matches).
4471
4472
4473
4474
4475
4476
4477
4478
4479
4480
4481
4482
4483
4484
4485
4486
4487
 *---------------------------------------------------------------------------
 */

Tcl_Filesystem *
Tcl_FSGetFileSystemForPath(
    Tcl_Obj* pathPtr)
{
#ifdef TCL_THREADS
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey);
#endif
    FilesystemRecord *fsRecPtr;
    Tcl_Filesystem* retVal = NULL;

    if (pathPtr == NULL) {
	Tcl_Panic("Tcl_FSGetFileSystemForPath called with NULL object");
	return NULL;
    }







<

<







4442
4443
4444
4445
4446
4447
4448

4449

4450
4451
4452
4453
4454
4455
4456
 *---------------------------------------------------------------------------
 */

Tcl_Filesystem *
Tcl_FSGetFileSystemForPath(
    Tcl_Obj* pathPtr)
{

    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey);

    FilesystemRecord *fsRecPtr;
    Tcl_Filesystem* retVal = NULL;

    if (pathPtr == NULL) {
	Tcl_Panic("Tcl_FSGetFileSystemForPath called with NULL object");
	return NULL;
    }