Changes On Branch 86886e44d88a01cf
Not logged in

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

Changes In Branch nonmonotonic-obj-alloc Through [86886e44d8] Excluding Merge-Ins

This is equivalent to a diff from 2c695b9fc8 to 86886e44d8

2014-09-05
20:42
merge trunk check-in: dee6e07687 user: ferrieux tags: nonmonotonic-obj-alloc
2014-08-17
15:23
[7d52e1101b] oo::object should know that oo::class is a subclass. check-in: b3543267de user: dkf tags: trunk
2014-08-14
16:32
Proof-of-concept of a nonmonotonic Tcl_Obj allocator, with zero perf impact and explicit ::tcl::unsu... check-in: 86886e44d8 user: ferrieux tags: nonmonotonic-obj-alloc
06:49
Merge trunk. Remove (old and deprecated) SystemV timezone information. check-in: 2f17676f24 user: jan.nijtmans tags: novem
2014-08-13
09:06
Put back SystemV timezone files, which were removed in previous commit. Don't know if that was by ac... check-in: 2c695b9fc8 user: jan.nijtmans tags: trunk
09:04
Put back SystemV timezone files, which were removed in previous commit. Don't know if that was by ac... check-in: a0fa37f70e user: jan.nijtmans tags: core-8-5-branch
01:45
merge tzdata2014f from ietf.org check-in: d3c5f2eb2f user: kbk tags: trunk

Changes to generic/tclBasic.c.
839
840
841
842
843
844
845


846
847
848
849
850
851
852
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854







+
+







     * Create unsupported commands for debugging bytecode and objects.
     */

    Tcl_CreateObjCommand(interp, "::tcl::unsupported::disassemble",
	    Tcl_DisassembleObjCmd, NULL, NULL);
    Tcl_CreateObjCommand(interp, "::tcl::unsupported::representation",
	    Tcl_RepresentationCmd, NULL, NULL);
    Tcl_CreateObjCommand(interp, "::tcl::unsupported::gc",
	    Tcl_GcCmd, NULL, NULL);

    /* Adding the bytecode assembler command */
    cmdPtr = (Command *) Tcl_NRCreateCommand(interp,
            "::tcl::unsupported::assemble", Tcl_AssembleObjCmd,
            TclNRAssembleObjCmd, NULL, NULL);
    cmdPtr->compileProc = &TclCompileAssembleCmd;

Changes to generic/tclInt.h.
1729
1730
1731
1732
1733
1734
1735



































1736
1737
1738
1739
1740
1741
1742
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







 * TIP #268.
 * Values for the selection mode, i.e the package require preferences.
 */

enum PkgPreferOptions {
    PKG_PREFER_LATEST, PKG_PREFER_STABLE
};

/*
 *----------------------------------------------------------------
 * These structures support gc
 *----------------------------------------------------------------
 */

/*
 * Header starting each chunk of Tcl_Obj, to chain them for use by gc
 */

typedef struct ObjChunkHeader {
    struct ObjChunkHeader *next; /* chaining          */
    Tcl_Obj *end;                /* address of last+1 */
} ObjChunkHeader;

MODULE_SCOPE ObjChunkHeader *tclObjChunkList; /* initialised in tclObj.c */

/*
 * Cell of temporary sorted array of chunk ranges and counters, for
 * dichotomic search in gc
 */

typedef struct ObjChunkInfo {
    Tcl_Obj *beg,*end;           /* [beg,end[ is the chunk's range */
    int free;                    /* temporary counter for gc       */
} ObjChunkInfo;

MODULE_SCOPE void TclpLockAlloc(void);
MODULE_SCOPE void TclpUnlockAlloc(void);
MODULE_SCOPE Tcl_Obj **TclpGetGlobalFreeObj(void);
MODULE_SCOPE Tcl_Obj **TclpGetLocalFreeObj(void);
MODULE_SCOPE void TclpRecomputeGlobalNumObj(void);
MODULE_SCOPE void TclpRecomputeLocalNumObj(void);


/*
 *----------------------------------------------------------------
 * This structure shadows the first few fields of the memory cache for the
 * allocator defined in tclThreadAlloc.c; it has to be kept in sync with the
 * definition there.
 * Some macros require knowledge of some fields in the struct in order to
3280
3281
3282
3283
3284
3285
3286



3287
3288
3289
3290
3291
3292
3293
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331







+
+
+







			    Tcl_Interp *interp, int objc,
			    Tcl_Obj *const objv[]);
MODULE_SCOPE int	Tcl_ForeachObjCmd(ClientData clientData,
			    Tcl_Interp *interp, int objc,
			    Tcl_Obj *const objv[]);
MODULE_SCOPE int	Tcl_FormatObjCmd(ClientData dummy,
			    Tcl_Interp *interp, int objc,
			    Tcl_Obj *const objv[]);
MODULE_SCOPE int	Tcl_GcCmd(ClientData clientData,
			    Tcl_Interp *interp, int objc,
			    Tcl_Obj *const objv[]);
MODULE_SCOPE int	Tcl_GetsObjCmd(ClientData clientData,
			    Tcl_Interp *interp, int objc,
			    Tcl_Obj *const objv[]);
MODULE_SCOPE int	Tcl_GlobalObjCmd(ClientData clientData,
			    Tcl_Interp *interp, int objc,
			    Tcl_Obj *const objv[]);
Changes to generic/tclObj.c.
46
47
48
49
50
51
52


53
54
55
56
57
58
59
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61







+
+







 * Pointer to a heap-allocated string of length zero that the Tcl core uses as
 * the value of an empty string representation for an object. This value is
 * shared by all new objects allocated by Tcl_NewObj.
 */

char tclEmptyString = '\0';
char *tclEmptyStringRep = &tclEmptyString;

ObjChunkHeader *tclObjChunkList = NULL;

#if defined(TCL_MEM_DEBUG) && defined(TCL_THREADS)
/*
 * Structure for tracking the source file and line number where a given
 * Tcl_Obj was allocated.  We also track the pointer to the Tcl_Obj itself,
 * for sanity checking purposes.
 */
1233
1234
1235
1236
1237
1238
1239
1240

1241

1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254






1255
1256
1257
1258
1259
1260
1261
1235
1236
1237
1238
1239
1240
1241

1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256

1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269







-
+

+












-
+
+
+
+
+
+







 */

#define OBJS_TO_ALLOC_EACH_TIME 100

void
TclAllocateFreeObjects(void)
{
    size_t bytesToAlloc = (OBJS_TO_ALLOC_EACH_TIME * sizeof(Tcl_Obj));
    size_t bytesToAlloc = (sizeof(ObjChunkHeader) + OBJS_TO_ALLOC_EACH_TIME * sizeof(Tcl_Obj));
    char *basePtr;
    ObjChunkHeader *header;
    register Tcl_Obj *prevPtr, *objPtr;
    register int i;

    /*
     * This has been noted by Purify to be a potential leak. The problem is
     * that Tcl, when not TCL_MEM_DEBUG compiled, keeps around all allocated
     * Tcl_Obj's, pointed to by tclFreeObjList, when freed instead of actually
     * freeing the memory. TclFinalizeObjects() does not ckfree() this memory,
     * but leaves it to Tcl's memory subsystem finalization to release it.
     * Purify apparently can't figure that out, and fires a false alarm.
     */

    basePtr = ckalloc(bytesToAlloc);
    header = (ObjChunkHeader *) ckalloc(bytesToAlloc);
    header->next = tclObjChunkList;
    header->end = (Tcl_Obj *)(((char *)header) + bytesToAlloc);
    tclObjChunkList = header;

    basePtr = (char *) (header + 1);

    prevPtr = NULL;
    objPtr = (Tcl_Obj *) basePtr;
    for (i = 0; i < OBJS_TO_ALLOC_EACH_TIME; i++) {
	objPtr->internalRep.twoPtrValue.ptr1 = prevPtr;
	prevPtr = objPtr;
	objPtr++;
4483
4484
4485
4486
4487
4488
4489































































































































































































































4490
4491
4492
4493
4494
4495
4496
4497
4498
4499
4500
4501
4491
4492
4493
4494
4495
4496
4497
4498
4499
4500
4501
4502
4503
4504
4505
4506
4507
4508
4509
4510
4511
4512
4513
4514
4515
4516
4517
4518
4519
4520
4521
4522
4523
4524
4525
4526
4527
4528
4529
4530
4531
4532
4533
4534
4535
4536
4537
4538
4539
4540
4541
4542
4543
4544
4545
4546
4547
4548
4549
4550
4551
4552
4553
4554
4555
4556
4557
4558
4559
4560
4561
4562
4563
4564
4565
4566
4567
4568
4569
4570
4571
4572
4573
4574
4575
4576
4577
4578
4579
4580
4581
4582
4583
4584
4585
4586
4587
4588
4589
4590
4591
4592
4593
4594
4595
4596
4597
4598
4599
4600
4601
4602
4603
4604
4605
4606
4607
4608
4609
4610
4611
4612
4613
4614
4615
4616
4617
4618
4619
4620
4621
4622
4623
4624
4625
4626
4627
4628
4629
4630
4631
4632
4633
4634
4635
4636
4637
4638
4639
4640
4641
4642
4643
4644
4645
4646
4647
4648
4649
4650
4651
4652
4653
4654
4655
4656
4657
4658
4659
4660
4661
4662
4663
4664
4665
4666
4667
4668
4669
4670
4671
4672
4673
4674
4675
4676
4677
4678
4679
4680
4681
4682
4683
4684
4685
4686
4687
4688
4689
4690
4691
4692
4693
4694
4695
4696
4697
4698
4699
4700
4701
4702
4703
4704
4705
4706
4707
4708
4709
4710
4711
4712
4713
4714
4715
4716
4717
4718
4719
4720
4721
4722
4723
4724
4725
4726
4727
4728
4729
4730
4731
4732







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+












                16, "...");
	Tcl_AppendToObj(descObj, "\"", -1);
    } else {
	Tcl_AppendToObj(descObj, ", no string representation", -1);
    }

    Tcl_SetObjResult(interp, descObj);
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_GcCmd --
 *
 *	Implementation of the "tcl::unsupported::gc" command.
 *
 * Results:
 *	{purged $nbobj chunks {$start $total $used $start $total $used ...}} 
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static int ComparePointers(const void *a, const void *b)
{
    return (*(char **)a)-(*(char **)b);
}

#define GC_BISECT_MIN_RECURS 4

static ObjChunkInfo *GC_FindChunkInfo(Tcl_Obj *obj, ObjChunkInfo *itab, int len) {
    while (1) {
        int mid;

        if (len <= GC_BISECT_MIN_RECURS) {
            int i;

            for(i = 0; i < len; i++, itab++) {
                if ((obj>=itab->beg)&&(obj<itab->end)) {
                    return itab;
                }
            }
            fprintf(stderr,"# GC internal error: no chunk enclosing obj %p\n",obj);
            return NULL;
        }
        mid = len / 2;
        if (obj >= itab[mid].beg) {
            itab += mid;
            len -= mid;
        } else {
            len = mid;
        }
    }
}

#ifndef USE_THREAD_ALLOC
void TclpLockAlloc(void)
{
    Tcl_MutexLock(&tclObjMutex);
}
void TclpUnlockAlloc(void)
{
    Tcl_MutexUnlock(&tclObjMutex);
}
Tcl_Obj **
TclpGetGlobalFreeObj(void)
{
    return &tclFreeObjList;
}
Tcl_Obj **
TclpGetLocalFreeObj(void)
{
    return NULL;
}
void TclpRecomputeGlobalNumObj(void)
{
}
void TclpRecomputeLocalNumObj(void)
{
}



# define FREE_INTERNAL ckfree
#else
# define FREE_INTERNAL free
#endif

static Tcl_Obj *DerefIf(Tcl_Obj **src)
{
    return (src ? (*src) : NULL);
}

int
Tcl_GcCmd(
    ClientData clientData,
    Tcl_Interp *interp,
    int objc,
    Tcl_Obj *const objv[])
{
    int nch, i, npurge;
    ObjChunkHeader *chunk, **tmp;
    ObjChunkInfo *info, *infotab;
    Tcl_Obj *obj;

    if (objc != 1) {
	Tcl_WrongNumArgs(interp, 1, objv, "");
	return TCL_ERROR;
    }

    TclpLockAlloc();

    fprintf(stderr, "GC Phase 1: prepare sorted list of chunk info\n");
    nch = 0;
    for (chunk = tclObjChunkList; chunk; chunk = chunk->next) {
        nch++;
    }
    infotab = (ObjChunkInfo *) malloc(nch * sizeof(ObjChunkInfo));
    tmp = (ObjChunkHeader **) infotab; /* pointers are smaller, so they fit */
    for (chunk = tclObjChunkList; chunk; chunk = chunk->next) {
        *(tmp++) = chunk;
    }
    qsort(infotab, nch, sizeof(ObjChunkHeader *), ComparePointers);

    /* in-place cacheing of chunk headers into chunk infos */
    for(i = nch - 1; i >= 0; i--) {
        chunk = ((ObjChunkHeader **)infotab)[i];
        info = infotab + i;
        info->beg = (Tcl_Obj *)(chunk + 1);
        info->end = chunk->end;
        info->free = 0;
    }

    fprintf(stderr, "GC Phase 2: scan free lists, locating each obj's chunk and updating its free count\n");
    for (obj = DerefIf(TclpGetLocalFreeObj()); obj; obj = (Tcl_Obj *)obj->internalRep.twoPtrValue.ptr1) {
        info = GC_FindChunkInfo(obj, infotab, nch);
        if (info) info->free++;
    }
    for (obj = DerefIf(TclpGetGlobalFreeObj()); obj; obj = (Tcl_Obj *)obj->internalRep.twoPtrValue.ptr1) {
        info = GC_FindChunkInfo(obj, infotab, nch);
        if (info) info->free++;
    }

    fprintf(stderr, "GC Phase 3: locate chunks entirely made of free objs and mark them with chunk->end=NULL and info->free=-1 \n");
    npurge = 0;
    for (i = 0, info = infotab; i < nch; i++, info++) {
        int room, delta;

        room = info->end - info->beg;
        delta = info->free - room;
        chunk = ((ObjChunkHeader *)info->beg) - 1;
        if (delta > 0) {
            fprintf(stderr,"# GC internal error: chunk at %p counts %d frees but has room for %d only !\n",
                    chunk,
                    info->free,
                    room);
            break;
        }
        if (delta < 0) {
            fprintf(stderr," . chunk %p : %d / %d\n",
                    chunk,
                    -delta,
                    room);
            continue;
        }
        /* here we have a purgeable chunk */
        npurge += room;
        chunk->end = NULL ; /* mark it for final sweep of chunks */
        info->free = -1 ; /* mark it for final sweep of objs*/
        fprintf(stderr," PURGE chunk %p : 0 / %d\n",
                chunk,
                room);
    }

    if (!npurge) {
        fprintf(stderr," Sorry - nothing to purge :(\n");
    } else {
        {
            Tcl_Obj **pobj;
            int n,p;

            fprintf(stderr, "GC Phase 4: remove the soon-to-be-purged objs from free lists\n");
            
            n = p = 0;
            for (pobj = TclpGetLocalFreeObj(); (*pobj);) {
                n++;
                info = GC_FindChunkInfo(*pobj, infotab, nch);
                if (info->free != -1) {
                    pobj = (Tcl_Obj **)&(**pobj).internalRep.twoPtrValue.ptr1;
                } else {
                    *pobj = (Tcl_Obj *)(**pobj).internalRep.twoPtrValue.ptr1;
                    p++;
                }
            }
            TclpRecomputeLocalNumObj();
            fprintf(stderr," (local: purge %d / %d\n",p,n);
            n = p = 0;
            for (pobj = TclpGetGlobalFreeObj(); (*pobj);) {
                n++;
                info = GC_FindChunkInfo(*pobj, infotab, nch);
                if (info->free != -1) {
                    pobj = (Tcl_Obj **)&(**pobj).internalRep.twoPtrValue.ptr1;
                } else {
                    *pobj = (Tcl_Obj *)(**pobj).internalRep.twoPtrValue.ptr1;
                    p++;
                }
            }
            TclpRecomputeGlobalNumObj();
            fprintf(stderr," (global: purge %d / %d\n",p,n);

        }
        {
            ObjChunkHeader **pchunk;
            
            fprintf(stderr, "GC Phase 5: free the located chunks, totalling %d objs\n", npurge);
            for (pchunk = &tclObjChunkList; (chunk = *pchunk); ) {
                if (chunk->end) {
                    pchunk=&chunk->next;
                } else {
                    *pchunk = chunk->next;
                    FREE_INTERNAL(chunk);
                }
            }
        }
    }
    TclpUnlockAlloc();

    return TCL_OK;
}

/*
 * Local Variables:
 * mode: c
 * c-basic-offset: 4
 * fill-column: 78
 * tab-width: 8
 * indent-tabs-mode: nil
 * End:
 */
Changes to generic/tclThreadAlloc.c.
563
564
565
566
567
568
569
570

571
572
573
574
575
576
577
578









579
580
581
582
583
584
585
563
564
565
566
567
568
569

570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594







-
+








+
+
+
+
+
+
+
+
+







	    MoveObjs(sharedPtr, cachePtr, numMove);
	}
	Tcl_MutexUnlock(objLockPtr);
	if (cachePtr->numObjects == 0) {
	    Tcl_Obj *newObjsPtr;

	    cachePtr->numObjects = numMove = NOBJALLOC;
	    newObjsPtr = malloc(sizeof(Tcl_Obj) * numMove);
	    newObjsPtr = (Tcl_Obj *)(((ObjChunkHeader *)malloc(sizeof(ObjChunkHeader) + sizeof(Tcl_Obj) * numMove)) + 1);
	    if (newObjsPtr == NULL) {
		Tcl_Panic("alloc: could not allocate %d new objects", numMove);
	    }
	    while (--numMove >= 0) {
		objPtr = &newObjsPtr[numMove];
		objPtr->internalRep.twoPtrValue.ptr1 = cachePtr->firstObjPtr;
		cachePtr->firstObjPtr = objPtr;
	    }
	    {
		ObjChunkHeader *chunk = ((ObjChunkHeader *)newObjsPtr) - 1;

		chunk->end = newObjsPtr + NOBJALLOC;
		Tcl_MutexLock(objLockPtr);
		chunk->next = tclObjChunkList;
		tclObjChunkList = chunk;
		Tcl_MutexUnlock(objLockPtr);
	    }
	}
    }

    /*
     * Pop the first object.
     */

1045
1046
1047
1048
1049
1050
1051























































1052
1053
1054
1055
1056
1057
1058
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







TclFinalizeThreadAllocThread(void)
{
    Cache *cachePtr = TclpGetAllocCache();
    if (cachePtr != NULL) {
	TclpFreeAllocCache(cachePtr);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * TclpLockAlloc, TclpUnlockAlloc, TclpGetGlobalFreeObj, TclpGetLocalFreeObj --
 *     These functions allow outside callers to reach safely into our internal
 *     state for inspection or gc.
 *----------------------------------------------------------------------
 */

void
TclpLockAlloc(void)
{
    Tcl_MutexLock(objLockPtr);
}

void
TclpUnlockAlloc(void)
{
    Tcl_MutexUnlock(objLockPtr);
}

Tcl_Obj **
TclpGetGlobalFreeObj(void)
{
    return &sharedPtr->firstObjPtr;
}

Tcl_Obj **
TclpGetLocalFreeObj(void)
{
    Cache *cachePtr;

    GETCACHE(cachePtr);
    return &cachePtr->firstObjPtr;
}

void TclpRecomputeGlobalNumObj(void)
{
    int n;
    Tcl_Obj *obj;

    for(n=0,obj=sharedPtr->firstObjPtr;obj;obj=(Tcl_Obj *)obj->internalRep.twoPtrValue.ptr1,n++);
    sharedPtr->numObjects = n;
}
void TclpRecomputeLocalNumObj(void)
{
    int n;
    Tcl_Obj *obj;
    Cache *cachePtr;

    GETCACHE(cachePtr);
    for(n=0,obj=cachePtr->firstObjPtr;obj;obj=(Tcl_Obj *)obj->internalRep.twoPtrValue.ptr1,n++);
    cachePtr->numObjects = n;
}

#else /* !(TCL_THREADS && USE_THREAD_ALLOC) */
/*
 *----------------------------------------------------------------------
 *
 * Tcl_GetMemoryInfo --
 *