Diff
Not logged in

Differences From Artifact [42ef78522d]:

To Artifact [90cac4490e]:


1
2
3
4
5

6
7
8
9
10
11
12


13
14

15
16
17
18
19
20
21
22


23
24
25
26
27
28
29
30
31
32
33
34
35
36



37
38
39

40
41
42
43
44


45
46
47
48


49
50
51

52
53
54
55



56
57
58
59
60

61
62
63
64
65
66
67
68

69
70
71
72
73
74
75
76
77
78


79
80
81
82
83


84
85
86
87
88
89
90
91





92
93
94
95
96


97
98
99
100
101
102
103
104
105






106
107
108
109
110


111
112
113
114
115
116
117
118




119
120
121
122
123
124
125
1
2
3
4

5
6
7
8
9
10


11
12
13

14
15
16
17
18
19
20


21
22
23
24
25
26
27
28
29
30
31
32
33



34
35
36
37
38

39
40
41
42


43
44
45
46


47
48
49
50

51




52
53
54
55
56
57
58

59
60
61
62
63
64
65
66

67
68
69
70
71
72
73
74
75


76
77
78
79
80


81
82
83
84
85





86
87
88
89
90
91
92
93


94
95
96
97
98






99
100
101
102
103
104
105
106
107


108
109

110
111
112




113
114
115
116
117
118
119
120
121
122
123




-
+





-
-
+
+

-
+






-
-
+
+











-
-
-
+
+
+


-
+



-
-
+
+


-
-
+
+


-
+
-
-
-
-
+
+
+




-
+







-
+








-
-
+
+



-
-
+
+



-
-
-
-
-
+
+
+
+
+



-
-
+
+



-
-
-
-
-
-
+
+
+
+
+
+



-
-
+
+
-



-
-
-
-
+
+
+
+







/*
 * tclThreadAlloc.c --
 *
 *	This is a very fast storage allocator for used with threads (designed
 *	avoid lock contention).  The basic strategy is to allocate memory in
 *	avoid lock contention). The basic strategy is to allocate memory in
 *	fixed size blocks from block caches.
 *
 * The Initial Developer of the Original Code is America Online, Inc.
 * Portions created by AOL are Copyright (C) 1999 America Online, Inc.
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 * See the file "license.terms" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * RCS: @(#) $Id: tclThreadAlloc.c,v 1.14.2.1 2005/04/25 21:37:22 kennykb Exp $
 * RCS: @(#) $Id: tclThreadAlloc.c,v 1.14.2.2 2005/08/02 18:16:10 dgp Exp $
 */

#include "tclInt.h"
#if defined(TCL_THREADS) && defined(USE_THREAD_ALLOC)

/*
 * If range checking is enabled, an additional byte will be allocated
 * to store the magic number at the end of the requested memory.
 * If range checking is enabled, an additional byte will be allocated to store
 * the magic number at the end of the requested memory.
 */

#ifndef RCHECK
#ifdef  NDEBUG
#define RCHECK		0
#else
#define RCHECK		1
#endif
#endif

/*
 * The following define the number of Tcl_Obj's to allocate/move
 * at a time and the high water mark to prune a per-thread cache.
 * On a 32 bit system, sizeof(Tcl_Obj) = 24 so 800 * 24 = ~16k.
 * The following define the number of Tcl_Obj's to allocate/move at a time and
 * the high water mark to prune a per-thread cache. On a 32 bit system,
 * sizeof(Tcl_Obj) = 24 so 800 * 24 = ~16k.
 */

#define NOBJALLOC	 800
#define NOBJALLOC	800
#define NOBJHIGH	1200

/*
 * The following defines the number of buckets in the bucket
 * cache and those block sizes from (1<<4) to (1<<(3+NBUCKETS))
 * The following defines the number of buckets in the bucket cache and those
 * block sizes from (1<<4) to (1<<(3+NBUCKETS))
 */

#define NBUCKETS	  11
#define MAXALLOC	  16284
#define NBUCKETS	11
#define MAXALLOC	16284

/*
 * The following union stores accounting information for
 * The following union stores accounting information for each block including
 * each block including two small magic numbers and
 * a bucket number when in use or a next pointer when
 * free.  The original requested size (not including
 * the Block overhead) is also maintained.
 * two small magic numbers and a bucket number when in use or a next pointer
 * when free. The original requested size (not including the Block overhead)
 * is also maintained.
 */

typedef struct Block {
    union {
	struct Block *next;		/* Next in free list. */
	struct Block *next;	/* Next in free list. */
	struct {
	    unsigned char magic1;	/* First magic number. */
	    unsigned char bucket;	/* Bucket block allocated from. */
	    unsigned char unused;	/* Padding. */
	    unsigned char magic2;	/* Second magic number. */
	} s;
    } u;
    size_t reqSize;			/* Requested allocation size. */
    size_t reqSize;		/* Requested allocation size. */
} Block;
#define nextBlock	u.next
#define sourceBucket	u.s.bucket
#define magicNum1	u.s.magic1
#define magicNum2	u.s.magic2
#define MAGIC		0xEF

/*
 * The following structure defines a bucket of blocks with
 * various accounting and statistics information.
 * The following structure defines a bucket of blocks with various accounting
 * and statistics information.
 */

typedef struct Bucket {
    Block *firstPtr;			/* First block available */
    int numFree;			/* Number of blocks available */
    Block *firstPtr;		/* First block available */
    int numFree;		/* Number of blocks available */

    /* All fields below for accounting only */

    int numRemoves;			/* Number of removes from bucket */
    int numInserts;			/* Number of inserts into bucket */
    int numWaits;			/* Number of waits to acquire a lock */
    int numLocks;			/* Number of locks acquired */
    int totalAssigned;			/* Total space assigned to bucket */
    int numRemoves;		/* Number of removes from bucket */
    int numInserts;		/* Number of inserts into bucket */
    int numWaits;		/* Number of waits to acquire a lock */
    int numLocks;		/* Number of locks acquired */
    int totalAssigned;		/* Total space assigned to bucket */
} Bucket;

/*
 * The following structure defines a cache of buckets and objs, of
 * which there will be (at most) one per thread.
 * The following structure defines a cache of buckets and objs, of which there
 * will be (at most) one per thread.
 */

typedef struct Cache {
    struct Cache *nextPtr;		/* Linked list of cache entries */
    Tcl_ThreadId owner;			/* Which thread's cache is this? */
    Tcl_Obj *firstObjPtr;		/* List of free objects for thread */
    int numObjects;			/* Number of objects for thread */
    int totalAssigned;			/* Total space assigned to thread */
    Bucket buckets[NBUCKETS];		/* The buckets for this thread */
    struct Cache *nextPtr;	/* Linked list of cache entries */
    Tcl_ThreadId owner;		/* Which thread's cache is this? */
    Tcl_Obj *firstObjPtr;	/* List of free objects for thread */
    int numObjects;		/* Number of objects for thread */
    int totalAssigned;		/* Total space assigned to thread */
    Bucket buckets[NBUCKETS];	/* The buckets for this thread */
} Cache;

/*
 * The following array specifies various per-bucket limits and locks.
 * The values are statically initialized to avoid calculating them
 * The following array specifies various per-bucket limits and locks. The
 * values are statically initialized to avoid calculating them repeatedly.
 * repeatedly.
 */

static struct {
    size_t blockSize;			/* Bucket blocksize. */
    int maxBlocks;			/* Max blocks before move to share. */
    int numMove;			/* Num blocks to move to share. */
    Tcl_Mutex *lockPtr;			/* Share bucket lock. */
    size_t blockSize;		/* Bucket blocksize. */
    int maxBlocks;		/* Max blocks before move to share. */
    int numMove;		/* Num blocks to move to share. */
    Tcl_Mutex *lockPtr;		/* Share bucket lock. */
} bucketInfo[NBUCKETS] = {
    {   16, 1024, 512, NULL},
    {   32,  512, 256, NULL},
    {   64,  256, 128, NULL},
    {  128,  128,  64, NULL},
    {  256,   64,  32, NULL},
    {  512,   32,  16, NULL},
142
143
144
145
146
147
148
149

150
151
152
153
154
155
156
157
140
141
142
143
144
145
146

147

148
149
150
151
152
153
154







-
+
-







static Block *	Ptr2Block _ANSI_ARGS_((char *ptr));
static char *	Block2Ptr _ANSI_ARGS_((Block *blockPtr, int bucket,
		    unsigned int reqSize));
static void	MoveObjs _ANSI_ARGS_((Cache *fromPtr, Cache *toPtr,
		    int numMove));

/*
 * Local variables defined in this file and initialized at
 * Local variables defined in this file and initialized at startup.
 * startup.
 */

static Tcl_Mutex *listLockPtr;
static Tcl_Mutex *objLockPtr;
static Cache sharedCache;
static Cache *sharedPtr = &sharedCache;
static Cache *firstCachePtr = &sharedCache;
302
303
304
305
306
307
308
309

310
311
312


313
314
315
316
317
318
319
299
300
301
302
303
304
305

306



307
308
309
310
311
312
313
314
315







-
+
-
-
-
+
+







    size_t size;

    if (cachePtr == NULL) {
	cachePtr = GetCache();
    }

    /*
     * Increment the requested size to include room for
     * Increment the requested size to include room for the Block structure.
     * the Block structure.  Call malloc() directly if the
     * required amount is greater than the largest block,
     * otherwise pop the smallest block large enough,
     * Call malloc() directly if the required amount is greater than the
     * largest block, otherwise pop the smallest block large enough,
     * allocating more blocks if necessary.
     */

    blockPtr = NULL;
    size = reqSize + sizeof(Block);
#if RCHECK
    ++size;
373
374
375
376
377
378
379
380
381
382



383
384
385
386
387
388
389
390
391
392

393
394
395
396
397

398
399
400
401
402
403
404
369
370
371
372
373
374
375



376
377
378

379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401







-
-
-
+
+
+
-









+





+








    cachePtr = TclpGetAllocCache();
    if (cachePtr == NULL) {
	cachePtr = GetCache();
    }

    /*
     * Get the block back from the user pointer and call system free
     * directly for large blocks.  Otherwise, push the block back on
     * the bucket and move blocks to the shared cache if there are now
     * Get the block back from the user pointer and call system free directly
     * for large blocks. Otherwise, push the block back on the bucket and move
     * blocks to the shared cache if there are now too many free.
     * too many free.
     */

    blockPtr = Ptr2Block(ptr);
    bucket = blockPtr->sourceBucket;
    if (bucket == NBUCKETS) {
	cachePtr->totalAssigned -= blockPtr->reqSize;
	free(blockPtr);
	return;
    }

    cachePtr->buckets[bucket].totalAssigned -= blockPtr->reqSize;
    blockPtr->nextBlock = cachePtr->buckets[bucket].firstPtr;
    cachePtr->buckets[bucket].firstPtr = blockPtr;
    ++cachePtr->buckets[bucket].numFree;
    ++cachePtr->buckets[bucket].numInserts;

    if (cachePtr != sharedPtr &&
	    cachePtr->buckets[bucket].numFree > bucketInfo[bucket].maxBlocks) {
	PutBlocks(cachePtr, bucket, bucketInfo[bucket].numMove);
    }
}

/*
433
434
435
436
437
438
439
440

441
442
443


444
445
446
447
448
449
450
430
431
432
433
434
435
436

437



438
439
440
441
442
443
444
445
446







-
+
-
-
-
+
+







    }

    if (cachePtr == NULL) {
	cachePtr = GetCache();
    }

    /*
     * If the block is not a system block and fits in place,
     * If the block is not a system block and fits in place, simply return the
     * simply return the existing pointer.  Otherwise, if the block
     * is a system block and the new size would also require a system
     * block, call realloc() directly.
     * existing pointer. Otherwise, if the block is a system block and the new
     * size would also require a system block, call realloc() directly.
     */

    blockPtr = Ptr2Block(ptr);
    size = reqSize + sizeof(Block);
#if RCHECK
    ++size;
#endif
492
493
494
495
496
497
498
499
500


501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519


520
521
522


523
524
525
526
527
528
529
530
531
532


533
534
535
536
537
538
539
488
489
490
491
492
493
494


495
496
497
498
499
500
501
502
503
504

505

506
507
508
509
510
511


512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537







-
-
+
+








-

-






-
-
+
+



+
+










+
+







 *
 *	Allocate a Tcl_Obj from the per-thread cache.
 *
 * Results:
 *	Pointer to uninitialized Tcl_Obj.
 *
 * Side effects:
 *	May move Tcl_Obj's from shared cached or allocate new Tcl_Obj's
 *	if list is empty.
 *	May move Tcl_Obj's from shared cached or allocate new Tcl_Obj's if
 *	list is empty.
 *
 *----------------------------------------------------------------------
 */

Tcl_Obj *
TclThreadAllocObj(void)
{
    register Cache *cachePtr = TclpGetAllocCache();
    register int numMove;
    register Tcl_Obj *objPtr;
    Tcl_Obj *newObjsPtr;

    if (cachePtr == NULL) {
	cachePtr = GetCache();
    }

    /*
     * Get this thread's obj list structure and move
     * or allocate new objs if necessary.
     * Get this thread's obj list structure and move or allocate new objs if
     * necessary.
     */

    if (cachePtr->numObjects == 0) {
	register int numMove;

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

	    cachePtr->numObjects = numMove = NOBJALLOC;
	    newObjsPtr = malloc(sizeof(Tcl_Obj) * numMove);
	    if (newObjsPtr == NULL) {
		Tcl_Panic("alloc: could not allocate %d new objects", numMove);
	    }
	    while (--numMove >= 0) {
		objPtr = &newObjsPtr[numMove];
560
561
562
563
564
565
566
567

568
569
570
571
572
573
574
575
558
559
560
561
562
563
564

565

566
567
568
569
570
571
572







-
+
-







 *
 *	Return a free Tcl_Obj to the per-thread cache.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	May move free Tcl_Obj's to shared list upon hitting high
 *	May move free Tcl_Obj's to shared list upon hitting high water mark.
 *	water mark.
 *
 *----------------------------------------------------------------------
 */

void
TclThreadFreeObj(objPtr)
    Tcl_Obj *objPtr;
585
586
587
588
589
590
591
592
593


594
595
596
597
598
599
600
582
583
584
585
586
587
588


589
590
591
592
593
594
595
596
597







-
-
+
+







     */

    objPtr->internalRep.otherValuePtr = cachePtr->firstObjPtr;
    cachePtr->firstObjPtr = objPtr;
    ++cachePtr->numObjects;

    /*
     * If the number of free objects has exceeded the high
     * water mark, move some blocks to the shared list.
     * If the number of free objects has exceeded the high water mark, move
     * some blocks to the shared list.
     */

    if (cachePtr->numObjects > NOBJHIGH) {
	Tcl_MutexLock(objLockPtr);
	MoveObjs(cachePtr, sharedPtr, NOBJALLOC);
	Tcl_MutexUnlock(objLockPtr);
    }
675
676
677
678
679
680
681
682
683


684
685
686
687
688
689
690
691
692
693
694


695
696
697
698
699
700
701
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
697







-
-
+
+
-








-
-
+
+







    register Tcl_Obj *objPtr = fromPtr->firstObjPtr;
    Tcl_Obj *fromFirstObjPtr = objPtr;

    toPtr->numObjects += numMove;
    fromPtr->numObjects -= numMove;

    /*
     * Find the last object to be moved; set the next one
     * (the first one not to be moved) as the first object
     * Find the last object to be moved; set the next one (the first one not
     * to be moved) as the first object in the 'from' cache.
     * in the 'from' cache.
     */

    while (--numMove) {
	objPtr = objPtr->internalRep.otherValuePtr;
    }
    fromPtr->firstObjPtr = objPtr->internalRep.otherValuePtr;

    /*
     * Move all objects as a block - they are already linked to
     * each other, we just have to update the first and last.
     * Move all objects as a block - they are already linked to each other, we
     * just have to update the first and last.
     */

    objPtr->internalRep.otherValuePtr = toPtr->firstObjPtr;
    toPtr->firstObjPtr = fromFirstObjPtr;
}

/*
760
761
762
763
764
765
766
767
768


769
770
771
772
773
774
775
756
757
758
759
760
761
762


763
764
765
766
767
768
769
770
771







-
-
+
+







 *
 *	Set/unset the lock to access a bucket in the shared cache.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Lock activity and contention are monitored globally and on
 *	a per-cache basis.
 *	Lock activity and contention are monitored globally and on a per-cache
 *	basis.
 *
 *----------------------------------------------------------------------
 */

static void
LockBucket(cachePtr, bucket)
    Cache *cachePtr;
817
818
819
820
821
822
823
824
825


826
827
828
829
830
831
832
833
834
835
836
837


838
839
840
841
842
843
844
813
814
815
816
817
818
819


820
821
822
823
824
825
826
827
828
829
830
831


832
833
834
835
836
837
838
839
840







-
-
+
+










-
-
+
+







    Cache *cachePtr;
    int bucket, numMove;
{
    register Block *lastPtr, *firstPtr;
    register int n = numMove;

    /*
     * Before acquiring the lock, walk the block list to find
     * the last block to be moved.
     * Before acquiring the lock, walk the block list to find the last block
     * to be moved.
     */

    firstPtr = lastPtr = cachePtr->buckets[bucket].firstPtr;
    while (--n > 0) {
	lastPtr = lastPtr->nextBlock;
    }
    cachePtr->buckets[bucket].firstPtr = lastPtr->nextBlock;
    cachePtr->buckets[bucket].numFree -= numMove;

    /*
     * Aquire the lock and place the list of blocks at the front
     * of the shared cache bucket.
     * Aquire the lock and place the list of blocks at the front of the shared
     * cache bucket.
     */

    LockBucket(cachePtr, bucket);
    lastPtr->nextBlock = sharedPtr->buckets[bucket].firstPtr;
    sharedPtr->buckets[bucket].firstPtr = firstPtr;
    sharedPtr->buckets[bucket].numFree += numMove;
    UnlockBucket(cachePtr, bucket);
863
864
865
866
867
868
869
870
871
872
873
874
875
876




877
878
879
880
881
882
883
884
885


886
887
888
889
890
891
892
859
860
861
862
863
864
865

866
867




868
869
870
871
872
873
874
875
876
877
878


879
880
881
882
883
884
885
886
887







-


-
-
-
-
+
+
+
+







-
-
+
+







static int
GetBlocks(cachePtr, bucket)
    Cache *cachePtr;
    int bucket;
{
    register Block *blockPtr;
    register int n;
    register size_t size;

    /*
     * First, atttempt to move blocks from the shared cache.  Note
     * the potentially dirty read of numFree before acquiring the lock
     * which is a slight performance enhancement.  The value is
     * verified after the lock is actually acquired.
     * First, atttempt to move blocks from the shared cache. Note the
     * potentially dirty read of numFree before acquiring the lock which is a
     * slight performance enhancement. The value is verified after the lock is
     * actually acquired.
     */

    if (cachePtr != sharedPtr && sharedPtr->buckets[bucket].numFree > 0) {
	LockBucket(cachePtr, bucket);
	if (sharedPtr->buckets[bucket].numFree > 0) {

	    /*
	     * Either move the entire list or walk the list to find
	     * the last block to move.
	     * Either move the entire list or walk the list to find the last
	     * block to move.
	     */

	    n = bucketInfo[bucket].numMove;
	    if (n >= sharedPtr->buckets[bucket].numFree) {
		cachePtr->buckets[bucket].firstPtr =
			sharedPtr->buckets[bucket].firstPtr;
		cachePtr->buckets[bucket].numFree =
905
906
907
908
909
910
911

912
913
914
915


916
917
918
919
920
921
922
900
901
902
903
904
905
906
907
908
909


910
911
912
913
914
915
916
917
918







+


-
-
+
+







		blockPtr->nextBlock = NULL;
	    }
	}
	UnlockBucket(cachePtr, bucket);
    }

    if (cachePtr->buckets[bucket].numFree == 0) {
	register size_t size;

	/*
	 * If no blocks could be moved from shared, first look for a
	 * larger block in this cache to split up.
	 * If no blocks could be moved from shared, first look for a larger
	 * block in this cache to split up.
	 */

	blockPtr = NULL;
	n = NBUCKETS;
	size = 0; /* lint */
	while (--n > bucket) {
	    if (cachePtr->buckets[n].numFree > 0) {
958
959
960
961
962
963
964
965
966


967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982

983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003


1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018

1019
1020








954
955
956
957
958
959
960


961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977

978
979
980
981
982
983
984
985
986
987
988
989
990
991

992
993
994
995
996


997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014


1015
1016
1017
1018
1019
1020
1021
1022







-
-
+
+















-
+













-





-
-
+
+















+
-
-
+
+
+
+
+
+
+
+
}

/*
 *----------------------------------------------------------------------
 *
 * TclFinalizeThreadAlloc --
 *
 *	This procedure is used to destroy all private resources used in
 *	this file.
 *	This procedure is used to destroy all private resources used in this
 *	file.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

void
TclFinalizeThreadAlloc()
{
    int i;
    for (i = 0; i < NBUCKETS; ++i) {
        TclpFreeAllocMutex(bucketInfo[i].lockPtr); 
        TclpFreeAllocMutex(bucketInfo[i].lockPtr);
        bucketInfo[i].lockPtr = NULL;
    }

    TclpFreeAllocMutex(objLockPtr);
    objLockPtr = NULL;

    TclpFreeAllocMutex(listLockPtr);
    listLockPtr = NULL;

    TclpFreeAllocCache(NULL);
}

#else

/*
 *----------------------------------------------------------------------
 *
 * TclFinalizeThreadAlloc --
 *
 *	This procedure is used to destroy all private resources used in
 *	this file.
 *	This procedure is used to destroy all private resources used in this
 *	file.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

void
TclFinalizeThreadAlloc()
{
    Tcl_Panic("TclFinalizeThreadAlloc called when threaded memory allocator not in use.");
}
#endif /* TCL_THREADS */

#endif /* TCL_THREADS */

/*
 * Local Variables:
 * mode: c
 * c-basic-offset: 4
 * fill-column: 78
 * End:
 */