📄 tclthreadalloc.c
字号:
/* * 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. * * 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 2002/08/26 13:05:56 msofer Exp $ */#if defined(TCL_THREADS) && defined(USE_THREAD_ALLOC)#include "tclInt.h"#ifdef WIN32#include "tclWinInt.h"#elseextern Tcl_Mutex *TclpNewAllocMutex(void);extern void *TclpGetAllocCache(void);extern void TclpSetAllocCache(void *);#endif/* * 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. * */ #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)) */#define NBUCKETS 11#define MAXALLOC 16284/* * 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 struct Block { union { 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. */ } b_s; } b_u; size_t b_reqsize; /* Requested allocation size. */} Block;#define b_next b_u.next#define b_bucket b_u.b_s.bucket#define b_magic1 b_u.b_s.magic1#define b_magic2 b_u.b_s.magic2#define MAGIC 0xef/* * The following structure defines a bucket of blocks with * various accounting and statistics information. */typedef struct Bucket { Block *firstPtr; int nfree; int nget; int nput; int nwait; int nlock; int nrequest;} Bucket;/* * The following structure defines a cache of buckets and objs. */typedef struct Cache { struct Cache *nextPtr; Tcl_ThreadId owner; Tcl_Obj *firstObjPtr; int nobjs; int nsysalloc; Bucket buckets[NBUCKETS];} Cache;/* * 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] = { { 16, 1024, 512, NULL}, { 32, 512, 256, NULL}, { 64, 256, 128, NULL}, { 128, 128, 64, NULL}, { 256, 64, 32, NULL}, { 512, 32, 16, NULL}, { 1024, 16, 8, NULL}, { 2048, 8, 4, NULL}, { 4096, 4, 2, NULL}, { 8192, 2, 1, NULL}, {16284, 1, 1, NULL},};/* * 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);/* * 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;/* *---------------------------------------------------------------------- * * GetCache --- * * Gets per-thread memory cache, allocating it if necessary. * * Results: * Pointer to cache. * * Side effects: * None. * *---------------------------------------------------------------------- */static Cache *GetCache(void){ Cache *cachePtr; /* * Check for first-time initialization. */ if (listLockPtr == NULL) { Tcl_Mutex *initLockPtr; int i; initLockPtr = Tcl_GetAllocMutex(); Tcl_MutexLock(initLockPtr); if (listLockPtr == NULL) { listLockPtr = TclpNewAllocMutex(); objLockPtr = TclpNewAllocMutex(); for (i = 0; i < NBUCKETS; ++i) { binfo[i].lockPtr = TclpNewAllocMutex(); } } Tcl_MutexUnlock(initLockPtr); } /* * Get this thread's cache, allocating if necessary. */ 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(); TclpSetAllocCache(cachePtr); } return cachePtr;}/* *---------------------------------------------------------------------- * * TclFreeAllocCache -- * * Flush and delete a cache, removing from list of caches. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */voidTclFreeAllocCache(void *arg){ Cache *cachePtr = arg; Cache **nextPtrPtr; register int bucket; /* * Flush blocks. */ for (bucket = 0; bucket < NBUCKETS; ++bucket) { if (cachePtr->buckets[bucket].nfree > 0) { PutBlocks(cachePtr, bucket, cachePtr->buckets[bucket].nfree); } } /* * Flush objs. */ if (cachePtr->nobjs > 0) { Tcl_MutexLock(objLockPtr); MoveObjs(cachePtr, sharedPtr, cachePtr->nobjs); Tcl_MutexUnlock(objLockPtr); } /* * Remove from pool list. */ Tcl_MutexLock(listLockPtr); nextPtrPtr = &firstCachePtr; while (*nextPtrPtr != cachePtr) { nextPtrPtr = &(*nextPtrPtr)->nextPtr; } *nextPtrPtr = cachePtr->nextPtr; cachePtr->nextPtr = NULL; Tcl_MutexUnlock(listLockPtr);#ifdef WIN32 TlsFree((DWORD) cachePtr);#else free(cachePtr);#endif}/* *---------------------------------------------------------------------- * * TclpAlloc -- * * Allocate memory. * * Results: * Pointer to memory just beyond Block pointer. * * Side effects: * May allocate more blocks for a bucket. * *---------------------------------------------------------------------- */char *TclpAlloc(unsigned int reqsize){ Cache *cachePtr = TclpGetAllocCache(); Block *blockPtr; register int bucket; size_t size; if (cachePtr == NULL) { cachePtr = GetCache(); } /* * 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);#if RCHECK ++size;#endif if (size > MAXALLOC) { bucket = NBUCKETS; blockPtr = malloc(size); if (blockPtr != NULL) { cachePtr->nsysalloc += reqsize; } } else { bucket = 0; while (binfo[bucket].blocksize < size) { ++bucket; } if (cachePtr->buckets[bucket].nfree || 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; } } if (blockPtr == NULL) { return NULL; } return Block2Ptr(blockPtr, bucket, reqsize);}/* *---------------------------------------------------------------------- * * TclpFree -- * * Return blocks to the thread block cache. * * Results: * None. * * Side effects: * May move blocks to shared cache. * *---------------------------------------------------------------------- */voidTclpFree(char *ptr){ if (ptr != NULL) { Cache *cachePtr = TclpGetAllocCache(); 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. */ 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); } } }}/* *---------------------------------------------------------------------- * * TclpRealloc -- * * Re-allocate memory to a larger or smaller size. * * Results: * Pointer to memory just beyond Block pointer. * * Side effects: * Previous memory, if any, may be freed. * *---------------------------------------------------------------------- */char *TclpRealloc(char *ptr, unsigned int reqsize){ Cache *cachePtr = TclpGetAllocCache(); Block *blockPtr; void *new; size_t size, min; int bucket; if (ptr == NULL) { return TclpAlloc(reqsize); } if (cachePtr == NULL) { cachePtr = GetCache(); } /* * 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);#if RCHECK ++size;#endif bucket = blockPtr->b_bucket; if (bucket != NBUCKETS) { if (bucket > 0) { min = binfo[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); } } else if (size > MAXALLOC) { cachePtr->nsysalloc -= blockPtr->b_reqsize; cachePtr->nsysalloc += reqsize; blockPtr = realloc(blockPtr, size); if (blockPtr == NULL) { return NULL; } return Block2Ptr(blockPtr, NBUCKETS, reqsize); } /* * Finally, perform an expensive malloc/copy/free.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -