tclThreadAlloc.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:23k
源码类别:

通讯编程

开发平台:

Visual C++

  1. /*
  2.  * tclThreadAlloc.c --
  3.  *
  4.  * This is a very fast storage allocator for used with threads (designed
  5.  * avoid lock contention).  The basic strategy is to allocate memory in
  6.  *   fixed size blocks from block caches.
  7.  * 
  8.  * The Initial Developer of the Original Code is America Online, Inc.
  9.  * Portions created by AOL are Copyright (C) 1999 America Online, Inc.
  10.  *
  11.  * See the file "license.terms" for information on usage and redistribution
  12.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  13.  *
  14.  * RCS: @(#) $Id: tclThreadAlloc.c,v 1.4.2.8 2007/06/29 03:17:33 das Exp $ 
  15.  */
  16. #include "tclInt.h"
  17. #if defined(TCL_THREADS) && defined(USE_THREAD_ALLOC) && !defined(TCL_MEM_DEBUG)
  18. #ifdef WIN32
  19. #include "tclWinInt.h"
  20. #else
  21. extern Tcl_Mutex *TclpNewAllocMutex(void);
  22. extern void *TclpGetAllocCache(void);
  23. extern void TclpSetAllocCache(void *);
  24. #endif
  25. /*
  26.  * If range checking is enabled, an additional byte will be allocated
  27.  * to store the magic number at the end of the requested memory.
  28.  */
  29. #ifndef RCHECK
  30. #ifdef  NDEBUG
  31. #define RCHECK 0
  32. #else
  33. #define RCHECK 1
  34. #endif
  35. #endif
  36. /*
  37.  * The following define the number of Tcl_Obj's to allocate/move
  38.  * at a time and the high water mark to prune a per-thread cache.
  39.  * On a 32 bit system, sizeof(Tcl_Obj) = 24 so 800 * 24 = ~16k.
  40.  *
  41.  */
  42.  
  43. #define NOBJALLOC  800
  44. #define NOBJHIGH 1200
  45. /*
  46.  * Alignment for allocated memory.
  47.  */
  48. #if defined(__APPLE__)
  49. #define ALLOCALIGN 16
  50. #else
  51. #define ALLOCALIGN 8
  52. #endif
  53. /*
  54.  * The following union stores accounting information for
  55.  * each block including two small magic numbers and
  56.  * a bucket number when in use or a next pointer when
  57.  * free.  The original requested size (not including
  58.  * the Block overhead) is also maintained.
  59.  */
  60.  
  61. typedef union Block {
  62.     struct {
  63. union {
  64.     union Block *next; /* Next in free list. */
  65.     struct {
  66. unsigned char magic1; /* First magic number. */
  67. unsigned char bucket; /* Bucket block allocated from. */
  68. unsigned char unused; /* Padding. */
  69. unsigned char magic2; /* Second magic number. */
  70.     } s;
  71. } u;
  72. size_t reqSize; /* Requested allocation size. */
  73.     } b;
  74.     unsigned char padding[ALLOCALIGN];
  75. } Block;
  76. #define b_next b.u.next
  77. #define b_bucket b.u.s.bucket
  78. #define b_magic1 b.u.s.magic1
  79. #define b_magic2 b.u.s.magic2
  80. #define MAGIC 0xef
  81. #define b_reqsize b.reqSize
  82. /*
  83.  * The following defines the minimum and and maximum block sizes and the number
  84.  * of buckets in the bucket cache.
  85.  */
  86. #define MINALLOC ((sizeof(Block) + 8 + (ALLOCALIGN-1)) & ~(ALLOCALIGN-1))
  87. #define NBUCKETS (11 - (MINALLOC >> 5))
  88. #define MAXALLOC (MINALLOC << (NBUCKETS - 1))
  89. /*
  90.  * The following structure defines a bucket of blocks with
  91.  * various accounting and statistics information.
  92.  */
  93. typedef struct Bucket {
  94.     Block *firstPtr;
  95.     long nfree;
  96.     long nget;
  97.     long nput;
  98.     long nwait;
  99.     long nlock;
  100.     long nrequest;
  101. } Bucket;
  102. /*
  103.  * The following structure defines a cache of buckets and objs.
  104.  */
  105. typedef struct Cache {
  106.     struct Cache  *nextPtr;
  107.     Tcl_ThreadId   owner;
  108.     Tcl_Obj       *firstObjPtr;
  109.     int            nobjs;
  110.     int            nsysalloc;
  111.     Bucket         buckets[NBUCKETS];
  112. } Cache;
  113. /*
  114.  * The following array specifies various per-bucket 
  115.  * limits and locks.  The values are statically initialized
  116.  * to avoid calculating them repeatedly.
  117.  */
  118. struct binfo {
  119.     size_t blocksize; /* Bucket blocksize. */
  120.     int maxblocks; /* Max blocks before move to share. */
  121.     int nmove; /* Num blocks to move to share. */
  122.     Tcl_Mutex *lockPtr; /* Share bucket lock. */
  123. } binfo[NBUCKETS];
  124. /*
  125.  * Static functions defined in this file.
  126.  */
  127. static void LockBucket(Cache *cachePtr, int bucket);
  128. static void UnlockBucket(Cache *cachePtr, int bucket);
  129. static void PutBlocks(Cache *cachePtr, int bucket, int nmove);
  130. static int  GetBlocks(Cache *cachePtr, int bucket);
  131. static Block *Ptr2Block(char *ptr);
  132. static char *Block2Ptr(Block *blockPtr, int bucket, unsigned int reqsize);
  133. static void MoveObjs(Cache *fromPtr, Cache *toPtr, int nmove);
  134. /*
  135.  * Local variables defined in this file and initialized at
  136.  * startup.
  137.  */
  138. static Tcl_Mutex *listLockPtr;
  139. static Tcl_Mutex *objLockPtr;
  140. static Cache     sharedCache;
  141. static Cache    *sharedPtr = &sharedCache;
  142. static Cache    *firstCachePtr = &sharedCache;
  143. /*
  144.  *----------------------------------------------------------------------
  145.  *
  146.  *  GetCache ---
  147.  *
  148.  * Gets per-thread memory cache, allocating it if necessary.
  149.  *
  150.  * Results:
  151.  * Pointer to cache.
  152.  *
  153.  * Side effects:
  154.  *   None.
  155.  *
  156.  *----------------------------------------------------------------------
  157.  */
  158. static Cache *
  159. GetCache(void)
  160. {
  161.     Cache *cachePtr;
  162.     /*
  163.      * Check for first-time initialization.
  164.      */
  165.     if (listLockPtr == NULL) {
  166. Tcl_Mutex *initLockPtr;
  167. unsigned int i;
  168. initLockPtr = Tcl_GetAllocMutex();
  169. Tcl_MutexLock(initLockPtr);
  170. if (listLockPtr == NULL) {
  171.     listLockPtr = TclpNewAllocMutex();
  172.     objLockPtr = TclpNewAllocMutex();
  173.     for (i = 0; i < NBUCKETS; ++i) {
  174. binfo[i].blocksize = MINALLOC << i;
  175. binfo[i].maxblocks = 1 << (NBUCKETS - 1 - i);
  176. binfo[i].nmove = i < NBUCKETS-1 ? 1 << (NBUCKETS - 2 - i) : 1;
  177.         binfo[i].lockPtr = TclpNewAllocMutex();
  178.     }
  179. }
  180. Tcl_MutexUnlock(initLockPtr);
  181.     }
  182.     /*
  183.      * Get this thread's cache, allocating if necessary.
  184.      */
  185.     cachePtr = TclpGetAllocCache();
  186.     if (cachePtr == NULL) {
  187.      cachePtr = calloc(1, sizeof(Cache));
  188.      if (cachePtr == NULL) {
  189.     panic("alloc: could not allocate new cache");
  190.      }
  191.      Tcl_MutexLock(listLockPtr);
  192.      cachePtr->nextPtr = firstCachePtr;
  193.      firstCachePtr = cachePtr;
  194.      Tcl_MutexUnlock(listLockPtr);
  195.      cachePtr->owner = Tcl_GetCurrentThread();
  196. TclpSetAllocCache(cachePtr);
  197.     }
  198.     return cachePtr;
  199. }
  200. /*
  201.  *----------------------------------------------------------------------
  202.  *
  203.  *  TclFreeAllocCache --
  204.  *
  205.  * Flush and delete a cache, removing from list of caches.
  206.  *
  207.  * Results:
  208.  * None.
  209.  *
  210.  * Side effects:
  211.  * None.
  212.  *
  213.  *----------------------------------------------------------------------
  214.  */
  215. void
  216. TclFreeAllocCache(void *arg)
  217. {
  218.     Cache *cachePtr = arg;
  219.     Cache **nextPtrPtr;
  220.     register unsigned int bucket;
  221.     /*
  222.      * Flush blocks.
  223.      */
  224.     for (bucket = 0; bucket < NBUCKETS; ++bucket) {
  225. if (cachePtr->buckets[bucket].nfree > 0) {
  226.     PutBlocks(cachePtr, bucket, cachePtr->buckets[bucket].nfree);
  227. }
  228.     }
  229.     /*
  230.      * Flush objs.
  231.      */
  232.     if (cachePtr->nobjs > 0) {
  233.      Tcl_MutexLock(objLockPtr);
  234.      MoveObjs(cachePtr, sharedPtr, cachePtr->nobjs);
  235.      Tcl_MutexUnlock(objLockPtr);
  236.     }
  237.     /*
  238.      * Remove from pool list.
  239.      */
  240.     Tcl_MutexLock(listLockPtr);
  241.     nextPtrPtr = &firstCachePtr;
  242.     while (*nextPtrPtr != cachePtr) {
  243. nextPtrPtr = &(*nextPtrPtr)->nextPtr;
  244.     }
  245.     *nextPtrPtr = cachePtr->nextPtr;
  246.     cachePtr->nextPtr = NULL;
  247.     Tcl_MutexUnlock(listLockPtr);
  248.     free(cachePtr);
  249. }
  250. /*
  251.  *----------------------------------------------------------------------
  252.  *
  253.  *  TclpAlloc --
  254.  *
  255.  * Allocate memory.
  256.  *
  257.  * Results:
  258.  * Pointer to memory just beyond Block pointer.
  259.  *
  260.  * Side effects:
  261.  * May allocate more blocks for a bucket.
  262.  *
  263.  *----------------------------------------------------------------------
  264.  */
  265. char *
  266. TclpAlloc(unsigned int reqsize)
  267. {
  268.     Cache         *cachePtr = TclpGetAllocCache();
  269.     Block         *blockPtr;
  270.     register int   bucket;
  271.     size_t      size;
  272.     if (cachePtr == NULL) {
  273. cachePtr = GetCache();
  274.     }
  275.     
  276.     /*
  277.      * Increment the requested size to include room for 
  278.      * the Block structure.  Call malloc() directly if the
  279.      * required amount is greater than the largest block,
  280.      * otherwise pop the smallest block large enough,
  281.      * allocating more blocks if necessary.
  282.      */
  283.     blockPtr = NULL;     
  284.     size = reqsize + sizeof(Block);
  285. #if RCHECK
  286.     ++size;
  287. #endif
  288.     if (size > MAXALLOC) {
  289. bucket = NBUCKETS;
  290.      blockPtr = malloc(size);
  291. if (blockPtr != NULL) {
  292.     cachePtr->nsysalloc += reqsize;
  293. }
  294.     } else {
  295.      bucket = 0;
  296.      while (binfo[bucket].blocksize < size) {
  297.          ++bucket;
  298.      }
  299.      if (cachePtr->buckets[bucket].nfree || GetBlocks(cachePtr, bucket)) {
  300.     blockPtr = cachePtr->buckets[bucket].firstPtr;
  301.     cachePtr->buckets[bucket].firstPtr = blockPtr->b_next;
  302.     --cachePtr->buckets[bucket].nfree;
  303.          ++cachePtr->buckets[bucket].nget;
  304.     cachePtr->buckets[bucket].nrequest += reqsize;
  305. }
  306.     }
  307.     if (blockPtr == NULL) {
  308.      return NULL;
  309.     }
  310.     return Block2Ptr(blockPtr, bucket, reqsize);
  311. }
  312. /*
  313.  *----------------------------------------------------------------------
  314.  *
  315.  *  TclpFree --
  316.  *
  317.  * Return blocks to the thread block cache.
  318.  *
  319.  * Results:
  320.  * None.
  321.  *
  322.  * Side effects:
  323.  * May move blocks to shared cache.
  324.  *
  325.  *----------------------------------------------------------------------
  326.  */
  327. void
  328. TclpFree(char *ptr)
  329. {
  330.     if (ptr != NULL) {
  331. Cache  *cachePtr = TclpGetAllocCache();
  332. Block *blockPtr;
  333. int bucket;
  334. if (cachePtr == NULL) {
  335.     cachePtr = GetCache();
  336. }
  337.  
  338. /*
  339.  * Get the block back from the user pointer and
  340.  * call system free directly for large blocks.
  341.  * Otherwise, push the block back on the bucket and
  342.  * move blocks to the shared cache if there are now
  343.  * too many free.
  344.  */
  345. blockPtr = Ptr2Block(ptr);
  346. bucket = blockPtr->b_bucket;
  347. if (bucket == NBUCKETS) {
  348.     cachePtr->nsysalloc -= blockPtr->b_reqsize;
  349.     free(blockPtr);
  350. } else {
  351.     cachePtr->buckets[bucket].nrequest -= blockPtr->b_reqsize;
  352.     blockPtr->b_next = cachePtr->buckets[bucket].firstPtr;
  353.     cachePtr->buckets[bucket].firstPtr = blockPtr;
  354.     ++cachePtr->buckets[bucket].nfree;
  355.     ++cachePtr->buckets[bucket].nput;
  356.     if (cachePtr != sharedPtr &&
  357.     cachePtr->buckets[bucket].nfree > binfo[bucket].maxblocks) {
  358. PutBlocks(cachePtr, bucket, binfo[bucket].nmove);
  359.     }
  360. }
  361.     }
  362. }
  363. /*
  364.  *----------------------------------------------------------------------
  365.  *
  366.  *  TclpRealloc --
  367.  *
  368.  * Re-allocate memory to a larger or smaller size.
  369.  *
  370.  * Results:
  371.  * Pointer to memory just beyond Block pointer.
  372.  *
  373.  * Side effects:
  374.  * Previous memory, if any, may be freed.
  375.  *
  376.  *----------------------------------------------------------------------
  377.  */
  378. char *
  379. TclpRealloc(char *ptr, unsigned int reqsize)
  380. {
  381.     Cache *cachePtr = TclpGetAllocCache();
  382.     Block *blockPtr;
  383.     void *new;
  384.     size_t size, min;
  385.     int bucket;
  386.     if (ptr == NULL) {
  387. return TclpAlloc(reqsize);
  388.     }
  389.     if (cachePtr == NULL) {
  390. cachePtr = GetCache();
  391.     }
  392.     /*
  393.      * If the block is not a system block and fits in place,
  394.      * simply return the existing pointer.  Otherwise, if the block
  395.      * is a system block and the new size would also require a system
  396.      * block, call realloc() directly.
  397.      */
  398.     blockPtr = Ptr2Block(ptr);
  399.     size = reqsize + sizeof(Block);
  400. #if RCHECK
  401.     ++size;
  402. #endif
  403.     bucket = blockPtr->b_bucket;
  404.     if (bucket != NBUCKETS) {
  405. if (bucket > 0) {
  406.     min = binfo[bucket-1].blocksize;
  407. } else {
  408.     min = 0;
  409. }
  410. if (size > min && size <= binfo[bucket].blocksize) {
  411.     cachePtr->buckets[bucket].nrequest -= blockPtr->b_reqsize;
  412.     cachePtr->buckets[bucket].nrequest += reqsize;
  413.     return Block2Ptr(blockPtr, bucket, reqsize);
  414. }
  415.     } else if (size > MAXALLOC) {
  416. cachePtr->nsysalloc -= blockPtr->b_reqsize;
  417. cachePtr->nsysalloc += reqsize;
  418. blockPtr = realloc(blockPtr, size);
  419. if (blockPtr == NULL) {
  420.     return NULL;
  421. }
  422. return Block2Ptr(blockPtr, NBUCKETS, reqsize);
  423.     }
  424.     /*
  425.      * Finally, perform an expensive malloc/copy/free.
  426.      */
  427.     new = TclpAlloc(reqsize);
  428.     if (new != NULL) {
  429. if (reqsize > blockPtr->b_reqsize) {
  430.     reqsize = blockPtr->b_reqsize;
  431. }
  432.      memcpy(new, ptr, reqsize);
  433.      TclpFree(ptr);
  434.     }
  435.     return new;
  436. }
  437. /*
  438.  *----------------------------------------------------------------------
  439.  *
  440.  * TclThreadAllocObj --
  441.  *
  442.  * Allocate a Tcl_Obj from the per-thread cache.
  443.  *
  444.  * Results:
  445.  * Pointer to uninitialized Tcl_Obj.
  446.  *
  447.  * Side effects:
  448.  * May move Tcl_Obj's from shared cached or allocate new Tcl_Obj's
  449.  *   if list is empty.
  450.  *
  451.  *----------------------------------------------------------------------
  452.  */
  453. Tcl_Obj *
  454. TclThreadAllocObj(void)
  455. {
  456.     register Cache *cachePtr = TclpGetAllocCache();
  457.     register int nmove;
  458.     register Tcl_Obj *objPtr;
  459.     Tcl_Obj *newObjsPtr;
  460.     if (cachePtr == NULL) {
  461. cachePtr = GetCache();
  462.     }
  463.     /*
  464.      * Get this thread's obj list structure and move
  465.      * or allocate new objs if necessary.
  466.      */
  467.      
  468.     if (cachePtr->nobjs == 0) {
  469.      Tcl_MutexLock(objLockPtr);
  470. nmove = sharedPtr->nobjs;
  471. if (nmove > 0) {
  472.     if (nmove > NOBJALLOC) {
  473. nmove = NOBJALLOC;
  474.     }
  475.     MoveObjs(sharedPtr, cachePtr, nmove);
  476. }
  477.      Tcl_MutexUnlock(objLockPtr);
  478. if (cachePtr->nobjs == 0) {
  479.     cachePtr->nobjs = nmove = NOBJALLOC;
  480.     newObjsPtr = malloc(sizeof(Tcl_Obj) * nmove);
  481.     if (newObjsPtr == NULL) {
  482. panic("alloc: could not allocate %d new objects", nmove);
  483.     }
  484.     while (--nmove >= 0) {
  485. objPtr = &newObjsPtr[nmove];
  486. objPtr->internalRep.otherValuePtr = cachePtr->firstObjPtr;
  487. cachePtr->firstObjPtr = objPtr;
  488.     }
  489. }
  490.     }
  491.     /*
  492.      * Pop the first object.
  493.      */
  494.     objPtr = cachePtr->firstObjPtr;
  495.     cachePtr->firstObjPtr = objPtr->internalRep.otherValuePtr;
  496.     --cachePtr->nobjs;
  497.     return objPtr;
  498. }
  499. /*
  500.  *----------------------------------------------------------------------
  501.  *
  502.  * TclThreadFreeObj --
  503.  *
  504.  * Return a free Tcl_Obj to the per-thread cache.
  505.  *
  506.  * Results:
  507.  * None.
  508.  *
  509.  * Side effects:
  510.  * May move free Tcl_Obj's to shared list upon hitting high
  511.  *   water mark.
  512.  *
  513.  *----------------------------------------------------------------------
  514.  */
  515. void
  516. TclThreadFreeObj(Tcl_Obj *objPtr)
  517. {
  518.     Cache *cachePtr = TclpGetAllocCache();
  519.     if (cachePtr == NULL) {
  520. cachePtr = GetCache();
  521.     }
  522.     /*
  523.      * Get this thread's list and push on the free Tcl_Obj.
  524.      */
  525.      
  526.     objPtr->internalRep.otherValuePtr = cachePtr->firstObjPtr;
  527.     cachePtr->firstObjPtr = objPtr;
  528.     ++cachePtr->nobjs;
  529.     
  530.     /*
  531.      * If the number of free objects has exceeded the high
  532.      * water mark, move some blocks to the shared list.
  533.      */
  534.      
  535.     if (cachePtr->nobjs > NOBJHIGH) {
  536. Tcl_MutexLock(objLockPtr);
  537. MoveObjs(cachePtr, sharedPtr, NOBJALLOC);
  538. Tcl_MutexUnlock(objLockPtr);
  539.     }
  540. }
  541. /*
  542.  *----------------------------------------------------------------------
  543.  *
  544.  * Tcl_GetMemoryInfo --
  545.  *
  546.  * Return a list-of-lists of memory stats.
  547.  *
  548.  * Results:
  549.  * None.
  550.  *
  551.  * Side effects:
  552.  *   List appended to given dstring.
  553.  *
  554.  *----------------------------------------------------------------------
  555.  */
  556. void
  557. Tcl_GetMemoryInfo(Tcl_DString *dsPtr)
  558. {
  559.     Cache *cachePtr;
  560.     char buf[200];
  561.     unsigned int n;
  562.     Tcl_MutexLock(listLockPtr);
  563.     cachePtr = firstCachePtr;
  564.     while (cachePtr != NULL) {
  565. Tcl_DStringStartSublist(dsPtr);
  566. if (cachePtr == sharedPtr) {
  567.          Tcl_DStringAppendElement(dsPtr, "shared");
  568. } else {
  569.     sprintf(buf, "thread%d", (int) cachePtr->owner);
  570.          Tcl_DStringAppendElement(dsPtr, buf);
  571. }
  572. for (n = 0; n < NBUCKETS; ++n) {
  573.          sprintf(buf, "%lu %ld %ld %ld %ld %ld %ld",
  574. (unsigned long) binfo[n].blocksize,
  575. cachePtr->buckets[n].nfree,
  576. cachePtr->buckets[n].nget,
  577. cachePtr->buckets[n].nput,
  578. cachePtr->buckets[n].nrequest,
  579. cachePtr->buckets[n].nlock,
  580. cachePtr->buckets[n].nwait);
  581.     Tcl_DStringAppendElement(dsPtr, buf);
  582. }
  583. Tcl_DStringEndSublist(dsPtr);
  584.     cachePtr = cachePtr->nextPtr;
  585.     }
  586.     Tcl_MutexUnlock(listLockPtr);
  587. }
  588. /*
  589.  *----------------------------------------------------------------------
  590.  *
  591.  * MoveObjs --
  592.  *
  593.  * Move Tcl_Obj's between caches.
  594.  *
  595.  * Results:
  596.  * None.
  597.  *
  598.  * Side effects:
  599.  *   None.
  600.  *
  601.  *----------------------------------------------------------------------
  602.  */
  603. static void
  604. MoveObjs(Cache *fromPtr, Cache *toPtr, int nmove)
  605. {
  606.     register Tcl_Obj *objPtr = fromPtr->firstObjPtr;
  607.     Tcl_Obj *fromFirstObjPtr = objPtr;
  608.     toPtr->nobjs += nmove;
  609.     fromPtr->nobjs -= nmove;
  610.     /*
  611.      * Find the last object to be moved; set the next one
  612.      * (the first one not to be moved) as the first object
  613.      * in the 'from' cache.
  614.      */
  615.     while (--nmove) {
  616. objPtr = objPtr->internalRep.otherValuePtr;
  617.     }
  618.     fromPtr->firstObjPtr = objPtr->internalRep.otherValuePtr;    
  619.     /*
  620.      * Move all objects as a block - they are already linked to
  621.      * each other, we just have to update the first and last.
  622.      */
  623.     objPtr->internalRep.otherValuePtr = toPtr->firstObjPtr;
  624.     toPtr->firstObjPtr = fromFirstObjPtr;
  625. }
  626. /*
  627.  *----------------------------------------------------------------------
  628.  *
  629.  *  Block2Ptr, Ptr2Block --
  630.  *
  631.  * Convert between internal blocks and user pointers.
  632.  *
  633.  * Results:
  634.  * User pointer or internal block.
  635.  *
  636.  * Side effects:
  637.  * Invalid blocks will abort the server.
  638.  *
  639.  *----------------------------------------------------------------------
  640.  */
  641. static char *
  642. Block2Ptr(Block *blockPtr, int bucket, unsigned int reqsize) 
  643. {
  644.     register void *ptr;
  645.     blockPtr->b_magic1 = blockPtr->b_magic2 = MAGIC;
  646.     blockPtr->b_bucket = bucket;
  647.     blockPtr->b_reqsize = reqsize;
  648.     ptr = ((void *) (blockPtr + 1));
  649. #if RCHECK
  650.     ((unsigned char *)(ptr))[reqsize] = MAGIC;
  651. #endif
  652.     return (char *) ptr;
  653. }
  654. static Block *
  655. Ptr2Block(char *ptr)
  656. {
  657.     register Block *blockPtr;
  658.     blockPtr = (((Block *) ptr) - 1);
  659.     if (blockPtr->b_magic1 != MAGIC
  660. #if RCHECK
  661. || ((unsigned char *) ptr)[blockPtr->b_reqsize] != MAGIC
  662. #endif
  663. || blockPtr->b_magic2 != MAGIC) {
  664. panic("alloc: invalid block: %p: %x %x %xn",
  665.     blockPtr, blockPtr->b_magic1, blockPtr->b_magic2,
  666.     ((unsigned char *) ptr)[blockPtr->b_reqsize]);
  667.     }
  668.     return blockPtr;
  669. }
  670. /*
  671.  *----------------------------------------------------------------------
  672.  *
  673.  *  LockBucket, UnlockBucket --
  674.  *
  675.  * Set/unset the lock to access a bucket in the shared cache.
  676.  *
  677.  * Results:
  678.  *   None.
  679.  *
  680.  * Side effects:
  681.  * Lock activity and contention are monitored globally and on
  682.  *   a per-cache basis.
  683.  *
  684.  *----------------------------------------------------------------------
  685.  */
  686. static void
  687. LockBucket(Cache *cachePtr, int bucket)
  688. {
  689. #if 0
  690.     if (Tcl_MutexTryLock(binfo[bucket].lockPtr) != TCL_OK) {
  691. Tcl_MutexLock(binfo[bucket].lockPtr);
  692.      ++cachePtr->buckets[bucket].nwait;
  693.      ++sharedPtr->buckets[bucket].nwait;
  694.     }
  695. #else
  696.     Tcl_MutexLock(binfo[bucket].lockPtr);
  697. #endif
  698.     ++cachePtr->buckets[bucket].nlock;
  699.     ++sharedPtr->buckets[bucket].nlock;
  700. }
  701. static void
  702. UnlockBucket(Cache *cachePtr, int bucket)
  703. {
  704.     Tcl_MutexUnlock(binfo[bucket].lockPtr);
  705. }
  706. /*
  707.  *----------------------------------------------------------------------
  708.  *
  709.  *  PutBlocks --
  710.  *
  711.  * Return unused blocks to the shared cache.
  712.  *
  713.  * Results:
  714.  * None.
  715.  *
  716.  * Side effects:
  717.  * None.
  718.  *
  719.  *----------------------------------------------------------------------
  720.  */
  721. static void
  722. PutBlocks(Cache *cachePtr, int bucket, int nmove)
  723. {
  724.     register Block *lastPtr, *firstPtr;
  725.     register int n = nmove;
  726.     /*
  727.      * Before acquiring the lock, walk the block list to find
  728.      * the last block to be moved.
  729.      */
  730.     firstPtr = lastPtr = cachePtr->buckets[bucket].firstPtr;
  731.     while (--n > 0) {
  732. lastPtr = lastPtr->b_next;
  733.     }
  734.     cachePtr->buckets[bucket].firstPtr = lastPtr->b_next;
  735.     cachePtr->buckets[bucket].nfree -= nmove;
  736.     /*
  737.      * Aquire the lock and place the list of blocks at the front
  738.      * of the shared cache bucket.
  739.      */
  740.     LockBucket(cachePtr, bucket);
  741.     lastPtr->b_next = sharedPtr->buckets[bucket].firstPtr;
  742.     sharedPtr->buckets[bucket].firstPtr = firstPtr;
  743.     sharedPtr->buckets[bucket].nfree += nmove;
  744.     UnlockBucket(cachePtr, bucket);
  745. }
  746. /*
  747.  *----------------------------------------------------------------------
  748.  *
  749.  *  GetBlocks --
  750.  *
  751.  * Get more blocks for a bucket.
  752.  *
  753.  * Results:
  754.  * 1 if blocks where allocated, 0 otherwise.
  755.  *
  756.  * Side effects:
  757.  * Cache may be filled with available blocks.
  758.  *
  759.  *----------------------------------------------------------------------
  760.  */
  761. static int
  762. GetBlocks(Cache *cachePtr, int bucket)
  763. {
  764.     register Block *blockPtr;
  765.     register int n;
  766.     register size_t size;
  767.     /*
  768.      * First, atttempt to move blocks from the shared cache.  Note
  769.      * the potentially dirty read of nfree before acquiring the lock
  770.      * which is a slight performance enhancement.  The value is
  771.      * verified after the lock is actually acquired.
  772.      */
  773.      
  774.     if (cachePtr != sharedPtr && sharedPtr->buckets[bucket].nfree > 0) {
  775. LockBucket(cachePtr, bucket);
  776. if (sharedPtr->buckets[bucket].nfree > 0) {
  777.     /*
  778.      * Either move the entire list or walk the list to find
  779.      * the last block to move.
  780.      */
  781.     n = binfo[bucket].nmove;
  782.     if (n >= sharedPtr->buckets[bucket].nfree) {
  783. cachePtr->buckets[bucket].firstPtr =
  784.     sharedPtr->buckets[bucket].firstPtr;
  785. cachePtr->buckets[bucket].nfree =
  786.     sharedPtr->buckets[bucket].nfree;
  787. sharedPtr->buckets[bucket].firstPtr = NULL;
  788. sharedPtr->buckets[bucket].nfree = 0;
  789.     } else {
  790. blockPtr = sharedPtr->buckets[bucket].firstPtr;
  791. cachePtr->buckets[bucket].firstPtr = blockPtr;
  792. sharedPtr->buckets[bucket].nfree -= n;
  793. cachePtr->buckets[bucket].nfree = n;
  794. while (--n > 0) {
  795.          blockPtr = blockPtr->b_next;
  796. }
  797. sharedPtr->buckets[bucket].firstPtr = blockPtr->b_next;
  798. blockPtr->b_next = NULL;
  799.     }
  800. }
  801. UnlockBucket(cachePtr, bucket);
  802.     }
  803.     
  804.     if (cachePtr->buckets[bucket].nfree == 0) {
  805. /*
  806.  * If no blocks could be moved from shared, first look for a
  807.  * larger block in this cache to split up.
  808.  */
  809.      blockPtr = NULL;
  810. n = NBUCKETS;
  811. size = 0; /* lint */
  812. while (--n > bucket) {
  813.          if (cachePtr->buckets[n].nfree > 0) {
  814. size = binfo[n].blocksize;
  815. blockPtr = cachePtr->buckets[n].firstPtr;
  816. cachePtr->buckets[n].firstPtr = blockPtr->b_next;
  817. --cachePtr->buckets[n].nfree;
  818. break;
  819.     }
  820. }
  821. /*
  822.  * Otherwise, allocate a big new block directly.
  823.  */
  824. if (blockPtr == NULL) {
  825.     size = MAXALLOC;
  826.     blockPtr = malloc(size);
  827.     if (blockPtr == NULL) {
  828. return 0;
  829.     }
  830. }
  831. /*
  832.  * Split the larger block into smaller blocks for this bucket.
  833.  */
  834. n = size / binfo[bucket].blocksize;
  835. cachePtr->buckets[bucket].nfree = n;
  836. cachePtr->buckets[bucket].firstPtr = blockPtr;
  837. while (--n > 0) {
  838.     blockPtr->b_next = (Block *) 
  839. ((char *) blockPtr + binfo[bucket].blocksize);
  840.     blockPtr = blockPtr->b_next;
  841. }
  842. blockPtr->b_next = NULL;
  843.     }
  844.     return 1;
  845. }
  846. /*
  847.  *----------------------------------------------------------------------
  848.  *
  849.  * TclFinalizeThreadAlloc --
  850.  *
  851.  * This procedure is used to destroy all private resources used in
  852.  * this file.
  853.  *
  854.  * Results:
  855.  * None.
  856.  *
  857.  * Side effects:
  858.  * None.
  859.  *
  860.  *----------------------------------------------------------------------
  861.  */
  862. void
  863. TclFinalizeThreadAlloc()
  864. {
  865.     unsigned int i;
  866.     for (i = 0; i < NBUCKETS; ++i) {
  867.         TclpFreeAllocMutex(binfo[i].lockPtr); 
  868.         binfo[i].lockPtr = NULL;
  869.     }
  870.     TclpFreeAllocMutex(objLockPtr);
  871.     objLockPtr = NULL;
  872.     TclpFreeAllocMutex(listLockPtr);
  873.     listLockPtr = NULL;
  874.     TclpFreeAllocCache(NULL);
  875. }
  876. #else /* ! defined(TCL_THREADS) && ! defined(USE_THREAD_ALLOC) */
  877. /*
  878.  *----------------------------------------------------------------------
  879.  *
  880.  * TclFinalizeThreadAlloc --
  881.  *
  882.  * This procedure is used to destroy all private resources used in
  883.  * this file.
  884.  *
  885.  * Results:
  886.  * None.
  887.  *
  888.  * Side effects:
  889.  * None.
  890.  *
  891.  *----------------------------------------------------------------------
  892.  */
  893. void
  894. TclFinalizeThreadAlloc()
  895. {
  896.     Tcl_Panic("TclFinalizeThreadAlloc called when threaded memory allocator not in use.");
  897. }
  898. #endif /* TCL_THREADS */