Tcl Source Code

Changes On Branch nonmonotonic-obj-alloc
Login

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

Changes In Branch nonmonotonic-obj-alloc Excluding Merge-Ins

This is equivalent to a diff from 208b2e859d to d61626209b

2014-09-08
21:36
Zap outdated comment. Leaf check-in: d61626209b user: ferrieux tags: nonmonotonic-obj-alloc
2014-09-06
19:56
Don't leak our temp sorted chunk table. check-in: a3fa5b8266 user: ferrieux tags: nonmonotonic-obj-alloc
2014-09-05
20:42
merge trunk check-in: dee6e07687 user: ferrieux tags: nonmonotonic-obj-alloc
2014-09-04
22:05
Remove ChannelHandlerEvent struct, which has never been used in all of recorded Tcl history. Still n... check-in: e2338e8828 user: dgp tags: trunk
2014-09-03
21:24
Merging changes from trunk check-in: 6f6342b8fe user: hypnotoad tags: core_zip_vfs
19:44
Distinguish hanging from crashing in exit tests. check-in: 208b2e859d user: ferrieux tags: trunk
15:48
[132fad6fde]. Fixed GetCache to use TclpSysAlloc+memset instead of calloc. Now consistent with tclWi... check-in: da203cbda6 user: ashok tags: trunk

Changes to generic/tclBasic.c.

839
840
841
842
843
844
845


846
847
848
849
850
851
852
     * 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);



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








>
>







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
 * TIP #268.
 * Values for the selection mode, i.e the package require preferences.
 */

enum PkgPreferOptions {
    PKG_PREFER_LATEST, PKG_PREFER_STABLE
};




































/*
 *----------------------------------------------------------------
 * 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







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







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 */
    long free;                   /* temporary counter for gc ; long for word-aligt  */
} 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
			    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_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[]);







>
>
>







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
 * 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;



#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.
 */







>
>







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
 */

#define OBJS_TO_ALLOC_EACH_TIME 100

void
TclAllocateFreeObjects(void)
{
    size_t bytesToAlloc = (OBJS_TO_ALLOC_EACH_TIME * sizeof(Tcl_Obj));
    char *basePtr;

    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);






    prevPtr = NULL;
    objPtr = (Tcl_Obj *) basePtr;
    for (i = 0; i < OBJS_TO_ALLOC_EACH_TIME; i++) {
	objPtr->internalRep.twoPtrValue.ptr1 = prevPtr;
	prevPtr = objPtr;
	objPtr++;







|

>












|
>
>
>
>
>







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 = (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.
     */

    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
                16, "...");
	Tcl_AppendToObj(descObj, "\"", -1);
    } else {
	Tcl_AppendToObj(descObj, ", no string representation", -1);
    }

    Tcl_SetObjResult(interp, descObj);


























































































































































































































































































    return TCL_OK;
}

/*
 * Local Variables:
 * mode: c
 * c-basic-offset: 4
 * fill-column: 78
 * tab-width: 8
 * indent-tabs-mode: nil
 * End:
 */







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>












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
4733
4734
4735
4736
4737
4738
4739
4740
4741
4742
4743
4744
4745
4746
4747
4748
4749
4750
4751
4752
4753
4754
4755
4756
4757
4758
4759
4760
4761
4762
4763
4764
4765
4766
4767
4768
4769
4770
4771
4772
4773
4774
4775
4776
4777
4778
4779
4780
4781
4782
4783
4784
4785
4786
4787
4788
4789
4790
4791
                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 void
BailOut(void)
{
    exit(127);
}

static void
GcLog(
    const char *fmt, ...)
{
    va_list ap;

    va_start(ap, fmt);
    vfprintf(stderr, fmt, ap);
    va_end(ap);
}

static int
ComparePointers(
    const void *a,
    const void *b)
{
    char *aa = *(char **)a;
    char *bb = *(char **)b;

    /*
     * BEWARE: ptr difference (aa-bb) is *not* a proper order
     * (an extra bit is needed for that)
     * Hence we resort to explicit pointer comparison
     * Which stores this bit in the Carry flag.
     */

    return (aa<bb)?-1:((aa>bb)?1:0);
}

#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;
                }
            }
            /* no Panic : avoid dumping core with a huge heap */
            GcLog("### GC internal error: no chunk enclosing obj %p\n",obj);
            BailOut();
        }
        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);
}

#define NEXT_OBJ(objPtr) \
    ((objPtr)->internalRep.twoPtrValue.ptr1)

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();

    GcLog("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;
    }

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

    GcLog("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) {
            GcLog("# GC internal error: chunk at %p counts %ld frees but has "
                    "room for %d only !\n", chunk, info->free, room);
            BailOut();
        }
        if (delta < 0) {
#if 0
            GcLog(" . chunk %p : %d / %d\n", chunk, -delta, room);
#endif
            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*/
#if 0
        GcLog(" PURGE chunk %p : 0 / %d\n", chunk, room);
#endif
    }

    if (!npurge) {
        GcLog(" Sorry - nothing to purge :(\n");
    } else {
        GcLog("GC Phase 4: remove the soon-to-be-purged objs from free "
                "lists\n");
            
        {
            Tcl_Obj **pobj;
            int n, p;

            n = p = 0;
            for (pobj = TclpGetLocalFreeObj(); *pobj != NULL ;) {
                n++;
                info = GC_FindChunkInfo(*pobj, infotab, nch);
                if (info->free != -1) {
                    pobj = (Tcl_Obj **) & NEXT_OBJ(*pobj);
                } else {
                    *pobj = (Tcl_Obj *) NEXT_OBJ(*pobj);
                    p++;
                }
            }
            TclpRecomputeLocalNumObj();
            GcLog(" (local: purge %d / %d\n", p, n);
            n = p = 0;
            for (pobj = TclpGetGlobalFreeObj(); *pobj != NULL ;) {
                n++;
                info = GC_FindChunkInfo(*pobj, infotab, nch);
                if (info->free != -1) {
                    pobj = (Tcl_Obj **) & NEXT_OBJ(*pobj);
                } else {
                    *pobj = (Tcl_Obj *) NEXT_OBJ(*pobj);
                    p++;
                }
            }
            TclpRecomputeGlobalNumObj();
            GcLog(" (global: purge %d / %d\n", p, n);

        }

        GcLog("GC Phase 5: free the located chunks, totalling %d objs\n",
                npurge);

        {
            ObjChunkHeader **pchunk;
            
            for (pchunk = &tclObjChunkList; (chunk = *pchunk) != NULL; ) {
                if (chunk->end) {
                    pchunk = &chunk->next;
                } else {
                    *pchunk = chunk->next;
                    FREE_INTERNAL(chunk);
                }
            }
        }
        free(infotab);
    }
    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.

564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579










580
581
582
583
584
585
586
	    MoveObjs(sharedPtr, cachePtr, numMove);
	}
	Tcl_MutexUnlock(objLockPtr);
	if (cachePtr->numObjects == 0) {
	    Tcl_Obj *newObjsPtr;

	    cachePtr->numObjects = numMove = NOBJALLOC;
	    newObjsPtr = TclpSysAlloc(sizeof(Tcl_Obj) * numMove, 0);
	    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;
	    }










	}
    }

    /*
     * Pop the first object.
     */








|








>
>
>
>
>
>
>
>
>
>







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
595
596
	    MoveObjs(sharedPtr, cachePtr, numMove);
	}
	Tcl_MutexUnlock(objLockPtr);
	if (cachePtr->numObjects == 0) {
	    Tcl_Obj *newObjsPtr;

	    cachePtr->numObjects = numMove = NOBJALLOC;
		newObjsPtr = (Tcl_Obj *)(((ObjChunkHeader *)TclpSysAlloc(sizeof(ObjChunkHeader) + sizeof(Tcl_Obj) * numMove, 0)) + 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.
     */

1046
1047
1048
1049
1050
1051
1052























































1053
1054
1055
1056
1057
1058
1059
TclFinalizeThreadAllocThread(void)
{
    Cache *cachePtr = TclpGetAllocCache();
    if (cachePtr != NULL) {
	TclpFreeAllocCache(cachePtr);
    }
}
























































#else /* !(TCL_THREADS && USE_THREAD_ALLOC) */
/*
 *----------------------------------------------------------------------
 *
 * Tcl_GetMemoryInfo --
 *







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







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
1123
1124
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 --
 *