diff options
Diffstat (limited to 'generic/tclThreadAlloc.c')
| -rw-r--r--[-rwxr-xr-x] | generic/tclThreadAlloc.c | 844 | 
1 files changed, 466 insertions, 378 deletions
| diff --git a/generic/tclThreadAlloc.c b/generic/tclThreadAlloc.c index 9ff31db..ddf888a 100755..100644 --- a/generic/tclThreadAlloc.c +++ b/generic/tclThreadAlloc.c @@ -2,33 +2,22 @@   * 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 - *  	fixed size blocks from block caches. - *  + *	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. - * - * RCS: @(#) $Id: tclThreadAlloc.c,v 1.4.2.8 2007/06/29 03:17:33 das Exp $  + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES.   */  #include "tclInt.h" - -#if defined(TCL_THREADS) && defined(USE_THREAD_ALLOC) && !defined(TCL_MEM_DEBUG) - -#ifdef WIN32 -#include "tclWinInt.h" -#else -extern Tcl_Mutex *TclpNewAllocMutex(void); -extern void *TclpGetAllocCache(void); -extern void TclpSetAllocCache(void *); -#endif +#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 @@ -40,33 +29,23 @@ extern void TclpSetAllocCache(void *);  #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 NOBJHIGH	1200 -/* - * Alignment for allocated memory. - */ +#define NOBJALLOC	800 -#if defined(__APPLE__) -#define ALLOCALIGN	16 -#else -#define ALLOCALIGN	8 -#endif +/* Actual definition moved to tclInt.h */ +#define NOBJHIGH	ALLOC_NOBJHIGH  /* - * The following union stores accounting information for - * 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. + * The following union stores accounting information for 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.   */ -  +  typedef union Block {      struct {  	union { @@ -80,93 +59,117 @@ typedef union Block {  	} u;  	size_t reqSize;			/* Requested allocation size. */      } b; -    unsigned char padding[ALLOCALIGN]; +    unsigned char padding[TCL_ALLOCALIGN];  } Block; -#define b_next		b.u.next -#define b_bucket	b.u.s.bucket -#define b_magic1	b.u.s.magic1 -#define b_magic2	b.u.s.magic2 -#define MAGIC		0xef -#define b_reqsize	b.reqSize +#define nextBlock	b.u.next +#define sourceBucket	b.u.s.bucket +#define magicNum1	b.u.s.magic1 +#define magicNum2	b.u.s.magic2 +#define MAGIC		0xEF +#define blockReqSize	b.reqSize  /*   * The following defines the minimum and and maximum block sizes and the number   * of buckets in the bucket cache.   */ -#define MINALLOC	((sizeof(Block) + 8 + (ALLOCALIGN-1)) & ~(ALLOCALIGN-1)) +#define MINALLOC	((sizeof(Block) + 8 + (TCL_ALLOCALIGN-1)) & ~(TCL_ALLOCALIGN-1))  #define NBUCKETS	(11 - (MINALLOC >> 5))  #define MAXALLOC	(MINALLOC << (NBUCKETS - 1))  /* - * 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; -    long nfree; -    long nget; -    long nput; -    long nwait; -    long nlock; -    long nrequest; +    Block *firstPtr;		/* First block available */ +    long numFree;		/* Number of blocks available */ + +    /* All fields below for accounting only */ + +    long numRemoves;		/* Number of removes from bucket */ +    long numInserts;		/* Number of inserts into bucket */ +    long numWaits;		/* Number of waits to acquire a lock */ +    long numLocks;		/* Number of locks acquired */ +    long totalAssigned;		/* Total space assigned to bucket */  } Bucket;  /* - * The following structure defines a cache of buckets and objs. + * The following structure defines a cache of buckets and objs, of which there + * will be (at most) one per thread. Any changes need to be reflected in the + * struct AllocCache defined in tclInt.h, possibly also in the initialisation + * code in Tcl_CreateInterp().   */  typedef struct Cache { -    struct Cache  *nextPtr; -    Tcl_ThreadId   owner; -    Tcl_Obj       *firstObjPtr; -    int            nobjs; -    int	           nsysalloc; -    Bucket         buckets[NBUCKETS]; +    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 repeatedly. + * The following array specifies various per-bucket limits and locks. The + * values are statically initialized to avoid calculating them repeatedly.   */ -struct binfo { -    size_t blocksize;	/* Bucket blocksize. */ -    int maxblocks;	/* Max blocks before move to share. */ -    int nmove;		/* Num blocks to move to share. */ -    Tcl_Mutex *lockPtr; /* Share bucket lock. */ -} binfo[NBUCKETS]; +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. */ +} bucketInfo[NBUCKETS];  /*   * Static functions defined in this file.   */ -static void LockBucket(Cache *cachePtr, int bucket); -static void UnlockBucket(Cache *cachePtr, int bucket); -static void PutBlocks(Cache *cachePtr, int bucket, int nmove); -static int  GetBlocks(Cache *cachePtr, int bucket); -static Block *Ptr2Block(char *ptr); -static char *Block2Ptr(Block *blockPtr, int bucket, unsigned int reqsize); -static void MoveObjs(Cache *fromPtr, Cache *toPtr, int nmove); +static Cache *	GetCache(void); +static void	LockBucket(Cache *cachePtr, int bucket); +static void	UnlockBucket(Cache *cachePtr, int bucket); +static void	PutBlocks(Cache *cachePtr, int bucket, int numMove); +static int	GetBlocks(Cache *cachePtr, int bucket); +static Block *	Ptr2Block(char *ptr); +static char *	Block2Ptr(Block *blockPtr, int bucket, unsigned int reqSize); +static void	MoveObjs(Cache *fromPtr, Cache *toPtr, int numMove);  /* - * Local variables defined in this file and initialized at - * startup. + * Local variables defined in this file and initialized at startup.   */  static Tcl_Mutex *listLockPtr;  static Tcl_Mutex *objLockPtr; -static Cache     sharedCache; -static Cache    *sharedPtr = &sharedCache; -static Cache    *firstCachePtr = &sharedCache; - +static Cache sharedCache; +static Cache *sharedPtr = &sharedCache; +static Cache *firstCachePtr = &sharedCache; + +#if defined(HAVE_FAST_TSD) +static __thread Cache *tcachePtr; + +# define GETCACHE(cachePtr)			\ +    do {					\ +	if (!tcachePtr) {			\ +	    tcachePtr = GetCache();		\ +	}					\ +	(cachePtr) = tcachePtr;			\ +    } while (0) +#else +# define GETCACHE(cachePtr)			\ +    do {					\ +	(cachePtr) = TclpGetAllocCache();	\ +	if ((cachePtr) == NULL) {		\ +	    (cachePtr) = GetCache();		\ +	}					\ +    } while (0) +#endif  /*   *----------------------------------------------------------------------   * - *  GetCache --- + * GetCache ---   *   *	Gets per-thread memory cache, allocating it if necessary.   * @@ -174,7 +177,7 @@ static Cache    *firstCachePtr = &sharedCache;   *	Pointer to cache.   *   * Side effects: - *  	None. + *	None.   *   *----------------------------------------------------------------------   */ @@ -198,10 +201,11 @@ GetCache(void)  	    listLockPtr = TclpNewAllocMutex();  	    objLockPtr = TclpNewAllocMutex();  	    for (i = 0; i < NBUCKETS; ++i) { -		binfo[i].blocksize = MINALLOC << i; -		binfo[i].maxblocks = 1 << (NBUCKETS - 1 - i); -		binfo[i].nmove = i < NBUCKETS-1 ? 1 << (NBUCKETS - 2 - i) : 1; -	        binfo[i].lockPtr = TclpNewAllocMutex(); +		bucketInfo[i].blockSize = MINALLOC << i; +		bucketInfo[i].maxBlocks = 1 << (NBUCKETS - 1 - i); +		bucketInfo[i].numMove = i < NBUCKETS - 1 ? +			1 << (NBUCKETS - 2 - i) : 1; +		bucketInfo[i].lockPtr = TclpNewAllocMutex();  	    }  	}  	Tcl_MutexUnlock(initLockPtr); @@ -213,25 +217,24 @@ GetCache(void)      cachePtr = TclpGetAllocCache();      if (cachePtr == NULL) { -    	cachePtr = calloc(1, sizeof(Cache)); -    	if (cachePtr == NULL) { -	    panic("alloc: could not allocate new cache"); -    	} -    	Tcl_MutexLock(listLockPtr); -    	cachePtr->nextPtr = firstCachePtr; -    	firstCachePtr = cachePtr; -    	Tcl_MutexUnlock(listLockPtr); -    	cachePtr->owner = Tcl_GetCurrentThread(); +	cachePtr = calloc(1, sizeof(Cache)); +	if (cachePtr == NULL) { +	    Tcl_Panic("alloc: could not allocate new cache"); +	} +	Tcl_MutexLock(listLockPtr); +	cachePtr->nextPtr = firstCachePtr; +	firstCachePtr = cachePtr; +	Tcl_MutexUnlock(listLockPtr); +	cachePtr->owner = Tcl_GetCurrentThread();  	TclpSetAllocCache(cachePtr);      }      return cachePtr;  } -  /*   *----------------------------------------------------------------------   * - *  TclFreeAllocCache -- + * TclFreeAllocCache --   *   *	Flush and delete a cache, removing from list of caches.   * @@ -245,7 +248,8 @@ GetCache(void)   */  void -TclFreeAllocCache(void *arg) +TclFreeAllocCache( +    void *arg)  {      Cache *cachePtr = arg;      Cache **nextPtrPtr; @@ -256,8 +260,8 @@ TclFreeAllocCache(void *arg)       */      for (bucket = 0; bucket < NBUCKETS; ++bucket) { -	if (cachePtr->buckets[bucket].nfree > 0) { -	    PutBlocks(cachePtr, bucket, cachePtr->buckets[bucket].nfree); +	if (cachePtr->buckets[bucket].numFree > 0) { +	    PutBlocks(cachePtr, bucket, cachePtr->buckets[bucket].numFree);  	}      } @@ -265,10 +269,10 @@ TclFreeAllocCache(void *arg)       * Flush objs.       */ -    if (cachePtr->nobjs > 0) { -    	Tcl_MutexLock(objLockPtr); -    	MoveObjs(cachePtr, sharedPtr, cachePtr->nobjs); -    	Tcl_MutexUnlock(objLockPtr); +    if (cachePtr->numObjects > 0) { +	Tcl_MutexLock(objLockPtr); +	MoveObjs(cachePtr, sharedPtr, cachePtr->numObjects); +	Tcl_MutexUnlock(objLockPtr);      }      /* @@ -285,12 +289,11 @@ TclFreeAllocCache(void *arg)      Tcl_MutexUnlock(listLockPtr);      free(cachePtr);  } -  /*   *----------------------------------------------------------------------   * - *  TclpAlloc -- + * TclpAlloc --   *   *	Allocate memory.   * @@ -304,60 +307,70 @@ TclFreeAllocCache(void *arg)   */  char * -TclpAlloc(unsigned int reqsize) +TclpAlloc( +    unsigned int reqSize)  { -    Cache         *cachePtr = TclpGetAllocCache(); -    Block         *blockPtr; -    register int   bucket; -    size_t  	   size; +    Cache *cachePtr; +    Block *blockPtr; +    register int bucket; +    size_t size; -    if (cachePtr == NULL) { -	cachePtr = GetCache(); +#ifndef __LP64__ +    if (sizeof(int) >= sizeof(size_t)) { +	/* An unsigned int overflow can also be a size_t overflow */ +	const size_t zero = 0; +	const size_t max = ~zero; + +	if (((size_t) reqSize) > max - sizeof(Block) - RCHECK) { +	    /* Requested allocation exceeds memory */ +	    return NULL; +	}      } -     +#endif + +    GETCACHE(cachePtr); +      /* -     * Increment the requested size to include room for  -     * the Block structure.  Call malloc() directly if the -     * required amount is greater than the largest block, -     * otherwise pop the smallest block large enough, +     * Increment the requested size to include room for the Block structure. +     * 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); +    blockPtr = NULL; +    size = reqSize + sizeof(Block);  #if RCHECK -    ++size; +    size++;  #endif      if (size > MAXALLOC) {  	bucket = NBUCKETS; -    	blockPtr = malloc(size); +	blockPtr = malloc(size);  	if (blockPtr != NULL) { -	    cachePtr->nsysalloc += reqsize; +	    cachePtr->totalAssigned += reqSize;  	}      } else { -    	bucket = 0; -    	while (binfo[bucket].blocksize < size) { -    	    ++bucket; -    	} -    	if (cachePtr->buckets[bucket].nfree || GetBlocks(cachePtr, bucket)) { +	bucket = 0; +	while (bucketInfo[bucket].blockSize < size) { +	    bucket++; +	} +	if (cachePtr->buckets[bucket].numFree || GetBlocks(cachePtr, bucket)) {  	    blockPtr = cachePtr->buckets[bucket].firstPtr; -	    cachePtr->buckets[bucket].firstPtr = blockPtr->b_next; -	    --cachePtr->buckets[bucket].nfree; -    	    ++cachePtr->buckets[bucket].nget; -	    cachePtr->buckets[bucket].nrequest += reqsize; +	    cachePtr->buckets[bucket].firstPtr = blockPtr->nextBlock; +	    cachePtr->buckets[bucket].numFree--; +	    cachePtr->buckets[bucket].numRemoves++; +	    cachePtr->buckets[bucket].totalAssigned += reqSize;  	}      }      if (blockPtr == NULL) { -    	return NULL; +	return NULL;      } -    return Block2Ptr(blockPtr, bucket, reqsize); +    return Block2Ptr(blockPtr, bucket, reqSize);  } -  /*   *----------------------------------------------------------------------   * - *  TclpFree -- + * TclpFree --   *   *	Return blocks to the thread block cache.   * @@ -371,49 +384,49 @@ TclpAlloc(unsigned int reqsize)   */  void -TclpFree(char *ptr) +TclpFree( +    char *ptr)  { -    if (ptr != NULL) { -	Cache  *cachePtr = TclpGetAllocCache(); -	Block *blockPtr; -	int bucket; +    Cache *cachePtr; +    Block *blockPtr; +    int bucket; -	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 -	 * too many free. -	 */ +    if (ptr == NULL) { +	return; +    } -	blockPtr = Ptr2Block(ptr); -	bucket = blockPtr->b_bucket; -	if (bucket == NBUCKETS) { -	    cachePtr->nsysalloc -= blockPtr->b_reqsize; -	    free(blockPtr); -	} else { -	    cachePtr->buckets[bucket].nrequest -= blockPtr->b_reqsize; -	    blockPtr->b_next = cachePtr->buckets[bucket].firstPtr; -	    cachePtr->buckets[bucket].firstPtr = blockPtr; -	    ++cachePtr->buckets[bucket].nfree; -	    ++cachePtr->buckets[bucket].nput; -	    if (cachePtr != sharedPtr && -		    cachePtr->buckets[bucket].nfree > binfo[bucket].maxblocks) { -		PutBlocks(cachePtr, bucket, binfo[bucket].nmove); -	    } -	} +    GETCACHE(cachePtr); + +    /* +     * 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. +     */ + +    blockPtr = Ptr2Block(ptr); +    bucket = blockPtr->sourceBucket; +    if (bucket == NBUCKETS) { +	cachePtr->totalAssigned -= blockPtr->blockReqSize; +	free(blockPtr); +	return;      } -} +    cachePtr->buckets[bucket].totalAssigned -= blockPtr->blockReqSize; +    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); +    } +}  /*   *----------------------------------------------------------------------   * - *  TclpRealloc -- + * TclpRealloc --   *   *	Re-allocate memory to a larger or smaller size.   * @@ -427,71 +440,82 @@ TclpFree(char *ptr)   */  char * -TclpRealloc(char *ptr, unsigned int reqsize) +TclpRealloc( +    char *ptr, +    unsigned int reqSize)  { -    Cache *cachePtr = TclpGetAllocCache(); +    Cache *cachePtr;      Block *blockPtr; -    void *new; +    void *newPtr;      size_t size, min;      int bucket;      if (ptr == NULL) { -	return TclpAlloc(reqsize); +	return TclpAlloc(reqSize);      } -    if (cachePtr == NULL) { -	cachePtr = GetCache(); +#ifndef __LP64__ +    if (sizeof(int) >= sizeof(size_t)) { +	/* An unsigned int overflow can also be a size_t overflow */ +	const size_t zero = 0; +	const size_t max = ~zero; + +	if (((size_t) reqSize) > max - sizeof(Block) - RCHECK) { +	    /* Requested allocation exceeds memory */ +	    return NULL; +	}      } +#endif + +    GETCACHE(cachePtr);      /* -     * If the block is not a system block and fits in place, -     * 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. +     * If the block is not a system block and fits in place, 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.       */      blockPtr = Ptr2Block(ptr); -    size = reqsize + sizeof(Block); +    size = reqSize + sizeof(Block);  #if RCHECK -    ++size; +    size++;  #endif -    bucket = blockPtr->b_bucket; +    bucket = blockPtr->sourceBucket;      if (bucket != NBUCKETS) {  	if (bucket > 0) { -	    min = binfo[bucket-1].blocksize; +	    min = bucketInfo[bucket-1].blockSize;  	} else {  	    min = 0;  	} -	if (size > min && size <= binfo[bucket].blocksize) { -	    cachePtr->buckets[bucket].nrequest -= blockPtr->b_reqsize; -	    cachePtr->buckets[bucket].nrequest += reqsize; -	    return Block2Ptr(blockPtr, bucket, reqsize); +	if (size > min && size <= bucketInfo[bucket].blockSize) { +	    cachePtr->buckets[bucket].totalAssigned -= blockPtr->blockReqSize; +	    cachePtr->buckets[bucket].totalAssigned += reqSize; +	    return Block2Ptr(blockPtr, bucket, reqSize);  	}      } else if (size > MAXALLOC) { -	cachePtr->nsysalloc -= blockPtr->b_reqsize; -	cachePtr->nsysalloc += reqsize; +	cachePtr->totalAssigned -= blockPtr->blockReqSize; +	cachePtr->totalAssigned += reqSize;  	blockPtr = realloc(blockPtr, size);  	if (blockPtr == NULL) {  	    return NULL;  	} -	return Block2Ptr(blockPtr, NBUCKETS, reqsize); +	return Block2Ptr(blockPtr, NBUCKETS, reqSize);      }      /*       * Finally, perform an expensive malloc/copy/free.       */ -    new = TclpAlloc(reqsize); -    if (new != NULL) { -	if (reqsize > blockPtr->b_reqsize) { -	    reqsize = blockPtr->b_reqsize; +    newPtr = TclpAlloc(reqSize); +    if (newPtr != NULL) { +	if (reqSize > blockPtr->blockReqSize) { +	    reqSize = blockPtr->blockReqSize;  	} -    	memcpy(new, ptr, reqsize); -    	TclpFree(ptr); +	memcpy(newPtr, ptr, reqSize); +	TclpFree(ptr);      } -    return new; +    return newPtr;  } -  /*   *---------------------------------------------------------------------- @@ -504,8 +528,12 @@ TclpRealloc(char *ptr, unsigned int reqsize)   *	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. + * + * Note: + *	If this code is updated, the changes need to be reflected in the macro + *	TclAllocObjStorageEx() defined in tclInt.h   *   *----------------------------------------------------------------------   */ @@ -513,39 +541,39 @@ TclpRealloc(char *ptr, unsigned int reqsize)  Tcl_Obj *  TclThreadAllocObj(void)  { -    register Cache *cachePtr = TclpGetAllocCache(); -    register int nmove; +    register Cache *cachePtr;      register Tcl_Obj *objPtr; -    Tcl_Obj *newObjsPtr; -    if (cachePtr == NULL) { -	cachePtr = GetCache(); -    } +    GETCACHE(cachePtr);      /* -     * 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->nobjs == 0) { -    	Tcl_MutexLock(objLockPtr); -	nmove = sharedPtr->nobjs; -	if (nmove > 0) { -	    if (nmove > NOBJALLOC) { -		nmove = NOBJALLOC; + +    if (cachePtr->numObjects == 0) { +	register int numMove; + +	Tcl_MutexLock(objLockPtr); +	numMove = sharedPtr->numObjects; +	if (numMove > 0) { +	    if (numMove > NOBJALLOC) { +		numMove = NOBJALLOC;  	    } -	    MoveObjs(sharedPtr, cachePtr, nmove); +	    MoveObjs(sharedPtr, cachePtr, numMove);  	} -    	Tcl_MutexUnlock(objLockPtr); -	if (cachePtr->nobjs == 0) { -	    cachePtr->nobjs = nmove = NOBJALLOC; -	    newObjsPtr = malloc(sizeof(Tcl_Obj) * nmove); +	Tcl_MutexUnlock(objLockPtr); +	if (cachePtr->numObjects == 0) { +	    Tcl_Obj *newObjsPtr; + +	    cachePtr->numObjects = numMove = NOBJALLOC; +	    newObjsPtr = malloc(sizeof(Tcl_Obj) * numMove);  	    if (newObjsPtr == NULL) { -		panic("alloc: could not allocate %d new objects", nmove); +		Tcl_Panic("alloc: could not allocate %d new objects", numMove);  	    } -	    while (--nmove >= 0) { -		objPtr = &newObjsPtr[nmove]; -		objPtr->internalRep.otherValuePtr = cachePtr->firstObjPtr; +	    while (--numMove >= 0) { +		objPtr = &newObjsPtr[numMove]; +		objPtr->internalRep.twoPtrValue.ptr1 = cachePtr->firstObjPtr;  		cachePtr->firstObjPtr = objPtr;  	    }  	} @@ -556,11 +584,10 @@ TclThreadAllocObj(void)       */      objPtr = cachePtr->firstObjPtr; -    cachePtr->firstObjPtr = objPtr->internalRep.otherValuePtr; -    --cachePtr->nobjs; +    cachePtr->firstObjPtr = objPtr->internalRep.twoPtrValue.ptr1; +    cachePtr->numObjects--;      return objPtr;  } -  /*   *---------------------------------------------------------------------- @@ -573,41 +600,42 @@ TclThreadAllocObj(void)   *	None.   *   * Side effects: - *	May move free Tcl_Obj's to shared list upon hitting high - *  	water mark. + *	May move free Tcl_Obj's to shared list upon hitting high water mark. + * + * Note: + *	If this code is updated, the changes need to be reflected in the macro + *	TclAllocObjStorageEx() defined in tclInt.h   *   *----------------------------------------------------------------------   */  void -TclThreadFreeObj(Tcl_Obj *objPtr) +TclThreadFreeObj( +    Tcl_Obj *objPtr)  { -    Cache *cachePtr = TclpGetAllocCache(); +    Cache *cachePtr; -    if (cachePtr == NULL) { -	cachePtr = GetCache(); -    } +    GETCACHE(cachePtr);      /*       * Get this thread's list and push on the free Tcl_Obj.       */ -      -    objPtr->internalRep.otherValuePtr = cachePtr->firstObjPtr; + +    objPtr->internalRep.twoPtrValue.ptr1 = cachePtr->firstObjPtr;      cachePtr->firstObjPtr = objPtr; -    ++cachePtr->nobjs; -     +    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->nobjs > NOBJHIGH) { + +    if (cachePtr->numObjects > NOBJHIGH) {  	Tcl_MutexLock(objLockPtr);  	MoveObjs(cachePtr, sharedPtr, NOBJALLOC);  	Tcl_MutexUnlock(objLockPtr);      }  } -  /*   *---------------------------------------------------------------------- @@ -620,13 +648,14 @@ TclThreadFreeObj(Tcl_Obj *objPtr)   *	None.   *   * Side effects: - *  	List appended to given dstring. + *	List appended to given dstring.   *   *----------------------------------------------------------------------   */  void -Tcl_GetMemoryInfo(Tcl_DString *dsPtr) +Tcl_GetMemoryInfo( +    Tcl_DString *dsPtr)  {      Cache *cachePtr;      char buf[200]; @@ -637,28 +666,27 @@ Tcl_GetMemoryInfo(Tcl_DString *dsPtr)      while (cachePtr != NULL) {  	Tcl_DStringStartSublist(dsPtr);  	if (cachePtr == sharedPtr) { -    	    Tcl_DStringAppendElement(dsPtr, "shared"); +	    Tcl_DStringAppendElement(dsPtr, "shared");  	} else { -	    sprintf(buf, "thread%d", (int) cachePtr->owner); -    	    Tcl_DStringAppendElement(dsPtr, buf); +	    sprintf(buf, "thread%p", cachePtr->owner); +	    Tcl_DStringAppendElement(dsPtr, buf);  	}  	for (n = 0; n < NBUCKETS; ++n) { -    	    sprintf(buf, "%lu %ld %ld %ld %ld %ld %ld", -		(unsigned long) binfo[n].blocksize, -		cachePtr->buckets[n].nfree, -		cachePtr->buckets[n].nget, -		cachePtr->buckets[n].nput, -		cachePtr->buckets[n].nrequest, -		cachePtr->buckets[n].nlock, -		cachePtr->buckets[n].nwait); +	    sprintf(buf, "%lu %ld %ld %ld %ld %ld %ld", +		    (unsigned long) bucketInfo[n].blockSize, +		    cachePtr->buckets[n].numFree, +		    cachePtr->buckets[n].numRemoves, +		    cachePtr->buckets[n].numInserts, +		    cachePtr->buckets[n].totalAssigned, +		    cachePtr->buckets[n].numLocks, +		    cachePtr->buckets[n].numWaits);  	    Tcl_DStringAppendElement(dsPtr, buf);  	}  	Tcl_DStringEndSublist(dsPtr); -	    cachePtr = cachePtr->nextPtr; +	cachePtr = cachePtr->nextPtr;      }      Tcl_MutexUnlock(listLockPtr);  } -  /*   *---------------------------------------------------------------------- @@ -671,45 +699,46 @@ Tcl_GetMemoryInfo(Tcl_DString *dsPtr)   *	None.   *   * Side effects: - *  	None. + *	None.   *   *----------------------------------------------------------------------   */  static void -MoveObjs(Cache *fromPtr, Cache *toPtr, int nmove) +MoveObjs( +    Cache *fromPtr, +    Cache *toPtr, +    int numMove)  {      register Tcl_Obj *objPtr = fromPtr->firstObjPtr;      Tcl_Obj *fromFirstObjPtr = objPtr; -    toPtr->nobjs += nmove; -    fromPtr->nobjs -= nmove; +    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 -     * in the 'from' cache. +     * 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.       */ -    while (--nmove) { -	objPtr = objPtr->internalRep.otherValuePtr; +    while (--numMove) { +	objPtr = objPtr->internalRep.twoPtrValue.ptr1;      } -    fromPtr->firstObjPtr = objPtr->internalRep.otherValuePtr;     +    fromPtr->firstObjPtr = objPtr->internalRep.twoPtrValue.ptr1;      /* -     * 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; +    objPtr->internalRep.twoPtrValue.ptr1 = toPtr->firstObjPtr;      toPtr->firstObjPtr = fromFirstObjPtr;  } -  /*   *----------------------------------------------------------------------   * - *  Block2Ptr, Ptr2Block -- + * Block2Ptr, Ptr2Block --   *   *	Convert between internal blocks and user pointers.   * @@ -723,84 +752,83 @@ MoveObjs(Cache *fromPtr, Cache *toPtr, int nmove)   */  static char * -Block2Ptr(Block *blockPtr, int bucket, unsigned int reqsize)  +Block2Ptr( +    Block *blockPtr, +    int bucket, +    unsigned int reqSize)  {      register void *ptr; -    blockPtr->b_magic1 = blockPtr->b_magic2 = MAGIC; -    blockPtr->b_bucket = bucket; -    blockPtr->b_reqsize = reqsize; +    blockPtr->magicNum1 = blockPtr->magicNum2 = MAGIC; +    blockPtr->sourceBucket = bucket; +    blockPtr->blockReqSize = reqSize;      ptr = ((void *) (blockPtr + 1));  #if RCHECK -    ((unsigned char *)(ptr))[reqsize] = MAGIC; +    ((unsigned char *)(ptr))[reqSize] = MAGIC;  #endif      return (char *) ptr;  }  static Block * -Ptr2Block(char *ptr) +Ptr2Block( +    char *ptr)  {      register Block *blockPtr;      blockPtr = (((Block *) ptr) - 1); -    if (blockPtr->b_magic1 != MAGIC +    if (blockPtr->magicNum1 != MAGIC || blockPtr->magicNum2 != MAGIC) { +	Tcl_Panic("alloc: invalid block: %p: %x %x", +		blockPtr, blockPtr->magicNum1, blockPtr->magicNum2); +    }  #if RCHECK -	|| ((unsigned char *) ptr)[blockPtr->b_reqsize] != MAGIC -#endif -	|| blockPtr->b_magic2 != MAGIC) { -	panic("alloc: invalid block: %p: %x %x %x\n", -	    blockPtr, blockPtr->b_magic1, blockPtr->b_magic2, -	    ((unsigned char *) ptr)[blockPtr->b_reqsize]); +    if (((unsigned char *) ptr)[blockPtr->blockReqSize] != MAGIC) { +	Tcl_Panic("alloc: invalid block: %p: %x %x %x", +		blockPtr, blockPtr->magicNum1, blockPtr->magicNum2, +		((unsigned char *) ptr)[blockPtr->blockReqSize]);      } +#endif      return blockPtr;  } -  /*   *----------------------------------------------------------------------   * - *  LockBucket, UnlockBucket -- + * LockBucket, UnlockBucket --   *   *	Set/unset the lock to access a bucket in the shared cache.   *   * Results: - *  	None. + *	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(Cache *cachePtr, int bucket) +LockBucket( +    Cache *cachePtr, +    int bucket)  { -#if 0 -    if (Tcl_MutexTryLock(binfo[bucket].lockPtr) != TCL_OK) { -	Tcl_MutexLock(binfo[bucket].lockPtr); -    	++cachePtr->buckets[bucket].nwait; -    	++sharedPtr->buckets[bucket].nwait; -    } -#else -    Tcl_MutexLock(binfo[bucket].lockPtr); -#endif -    ++cachePtr->buckets[bucket].nlock; -    ++sharedPtr->buckets[bucket].nlock; +    Tcl_MutexLock(bucketInfo[bucket].lockPtr); +    cachePtr->buckets[bucket].numLocks++; +    sharedPtr->buckets[bucket].numLocks++;  } -  static void -UnlockBucket(Cache *cachePtr, int bucket) +UnlockBucket( +    Cache *cachePtr, +    int bucket)  { -    Tcl_MutexUnlock(binfo[bucket].lockPtr); +    Tcl_MutexUnlock(bucketInfo[bucket].lockPtr);  } -  /*   *----------------------------------------------------------------------   * - *  PutBlocks -- + * PutBlocks --   *   *	Return unused blocks to the shared cache.   * @@ -814,40 +842,42 @@ UnlockBucket(Cache *cachePtr, int bucket)   */  static void -PutBlocks(Cache *cachePtr, int bucket, int nmove) +PutBlocks( +    Cache *cachePtr, +    int bucket, +    int numMove)  {      register Block *lastPtr, *firstPtr; -    register int n = nmove; +    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->b_next; +	lastPtr = lastPtr->nextBlock;      } -    cachePtr->buckets[bucket].firstPtr = lastPtr->b_next; -    cachePtr->buckets[bucket].nfree -= nmove; +    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->b_next = sharedPtr->buckets[bucket].firstPtr; +    lastPtr->nextBlock = sharedPtr->buckets[bucket].firstPtr;      sharedPtr->buckets[bucket].firstPtr = firstPtr; -    sharedPtr->buckets[bucket].nfree += nmove; +    sharedPtr->buckets[bucket].numFree += numMove;      UnlockBucket(cachePtr, bucket);  } -  /*   *----------------------------------------------------------------------   * - *  GetBlocks -- + * GetBlocks --   *   *	Get more blocks for a bucket.   * @@ -861,67 +891,69 @@ PutBlocks(Cache *cachePtr, int bucket, int nmove)   */  static int -GetBlocks(Cache *cachePtr, int bucket) +GetBlocks( +    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 nfree 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].nfree > 0) { + +    if (cachePtr != sharedPtr && sharedPtr->buckets[bucket].numFree > 0) {  	LockBucket(cachePtr, bucket); -	if (sharedPtr->buckets[bucket].nfree > 0) { +	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 = binfo[bucket].nmove; -	    if (n >= sharedPtr->buckets[bucket].nfree) { +	    n = bucketInfo[bucket].numMove; +	    if (n >= sharedPtr->buckets[bucket].numFree) {  		cachePtr->buckets[bucket].firstPtr = -		    sharedPtr->buckets[bucket].firstPtr; -		cachePtr->buckets[bucket].nfree = -		    sharedPtr->buckets[bucket].nfree; +			sharedPtr->buckets[bucket].firstPtr; +		cachePtr->buckets[bucket].numFree = +			sharedPtr->buckets[bucket].numFree;  		sharedPtr->buckets[bucket].firstPtr = NULL; -		sharedPtr->buckets[bucket].nfree = 0; +		sharedPtr->buckets[bucket].numFree = 0;  	    } else {  		blockPtr = sharedPtr->buckets[bucket].firstPtr;  		cachePtr->buckets[bucket].firstPtr = blockPtr; -		sharedPtr->buckets[bucket].nfree -= n; -		cachePtr->buckets[bucket].nfree = n; +		sharedPtr->buckets[bucket].numFree -= n; +		cachePtr->buckets[bucket].numFree = n;  		while (--n > 0) { -    		    blockPtr = blockPtr->b_next; +		    blockPtr = blockPtr->nextBlock;  		} -		sharedPtr->buckets[bucket].firstPtr = blockPtr->b_next; -		blockPtr->b_next = NULL; +		sharedPtr->buckets[bucket].firstPtr = blockPtr->nextBlock; +		blockPtr->nextBlock = NULL;  	    }  	}  	UnlockBucket(cachePtr, bucket);      } -     -    if (cachePtr->buckets[bucket].nfree == 0) { + +    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; +	blockPtr = NULL;  	n = NBUCKETS;  	size = 0; /* lint */  	while (--n > bucket) { -    	    if (cachePtr->buckets[n].nfree > 0) { -		size = binfo[n].blocksize; +	    if (cachePtr->buckets[n].numFree > 0) { +		size = bucketInfo[n].blockSize;  		blockPtr = cachePtr->buckets[n].firstPtr; -		cachePtr->buckets[n].firstPtr = blockPtr->b_next; -		--cachePtr->buckets[n].nfree; +		cachePtr->buckets[n].firstPtr = blockPtr->nextBlock; +		cachePtr->buckets[n].numFree--;  		break;  	    }  	} @@ -942,26 +974,26 @@ GetBlocks(Cache *cachePtr, int bucket)  	 * Split the larger block into smaller blocks for this bucket.  	 */ -	n = size / binfo[bucket].blocksize; -	cachePtr->buckets[bucket].nfree = n; +	n = size / bucketInfo[bucket].blockSize; +	cachePtr->buckets[bucket].numFree = n;  	cachePtr->buckets[bucket].firstPtr = blockPtr;  	while (--n > 0) { -	    blockPtr->b_next = (Block *)  -		((char *) blockPtr + binfo[bucket].blocksize); -	    blockPtr = blockPtr->b_next; +	    blockPtr->nextBlock = (Block *) +		((char *) blockPtr + bucketInfo[bucket].blockSize); +	    blockPtr = blockPtr->nextBlock;  	} -	blockPtr->b_next = NULL; +	blockPtr->nextBlock = NULL;      }      return 1;  } - +  /*   *----------------------------------------------------------------------   *   * 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. @@ -973,13 +1005,13 @@ GetBlocks(Cache *cachePtr, int bucket)   */  void -TclFinalizeThreadAlloc() +TclFinalizeThreadAlloc(void)  {      unsigned int i;      for (i = 0; i < NBUCKETS; ++i) { -        TclpFreeAllocMutex(binfo[i].lockPtr);  -        binfo[i].lockPtr = NULL; +	TclpFreeAllocMutex(bucketInfo[i].lockPtr); +	bucketInfo[i].lockPtr = NULL;      }      TclpFreeAllocMutex(objLockPtr); @@ -991,15 +1023,64 @@ TclFinalizeThreadAlloc()      TclpFreeAllocCache(NULL);  } -#else /* ! defined(TCL_THREADS) && ! defined(USE_THREAD_ALLOC) */ +/* + *---------------------------------------------------------------------- + * + * TclFinalizeThreadAllocThread -- + * + *	This procedure is used to destroy single thread private resources used + *	in this file.  + * Called in TclpFinalizeThreadData when a thread exits (Tcl_FinalizeThread). + * + * Results: + *	None. + * + * Side effects: + *	None. + * + *---------------------------------------------------------------------- + */ + +void +TclFinalizeThreadAllocThread(void) +{ +    Cache *cachePtr = TclpGetAllocCache(); +    if (cachePtr != NULL) { +	TclpFreeAllocCache(cachePtr); +    } +} + +#else /* !(TCL_THREADS && USE_THREAD_ALLOC) */ +/* + *---------------------------------------------------------------------- + * + * Tcl_GetMemoryInfo -- + * + *	Return a list-of-lists of memory stats. + * + * Results: + *	None. + * + * Side effects: + *	List appended to given dstring. + * + *---------------------------------------------------------------------- + */ +void +Tcl_GetMemoryInfo( +    Tcl_DString *dsPtr) +{ +    Tcl_Panic("Tcl_GetMemoryInfo called when threaded memory allocator not in use"); +} +  /*   *----------------------------------------------------------------------   *   * 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. @@ -1011,9 +1092,16 @@ TclFinalizeThreadAlloc()   */  void -TclFinalizeThreadAlloc() +TclFinalizeThreadAlloc(void)  { -    Tcl_Panic("TclFinalizeThreadAlloc called when threaded memory allocator not in use."); +    Tcl_Panic("TclFinalizeThreadAlloc called when threaded memory allocator not in use");  } - -#endif /* TCL_THREADS */ +#endif /* TCL_THREADS && USE_THREAD_ALLOC */ + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ | 
