mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-12 14:37:50 +00:00
325 lines
14 KiB
C
325 lines
14 KiB
C
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; -*-
|
|
*
|
|
* The contents of this file are subject to the Netscape Public
|
|
* License Version 1.1 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS
|
|
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
* implied. See the License for the specific language governing
|
|
* rights and limitations under the License.
|
|
*
|
|
* The Original Code is mozilla.org code.
|
|
*
|
|
* The Initial Developer of the Original Code is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All
|
|
* Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*/
|
|
|
|
/*******************************************************************************
|
|
S P O R T M O D E L
|
|
_____
|
|
____/_____\____
|
|
/__o____o____o__\ __
|
|
\_______________/ (@@)/
|
|
/\_____|_____/\ x~[]~
|
|
~~~~~~~~~~~/~~~~~~~|~~~~~~~\~~~~~~~~/\~~~~~~~~~
|
|
|
|
Advanced Technology Garbage Collector
|
|
Copyright (c) 1997 Netscape Communications, Inc. All rights reserved.
|
|
Recovered by: Warren Harris
|
|
*******************************************************************************/
|
|
|
|
#ifndef __SMPOOL__
|
|
#define __SMPOOL__
|
|
|
|
#include "sm.h"
|
|
#include "smpage.h"
|
|
#include "prmon.h"
|
|
|
|
SM_BEGIN_EXTERN_C
|
|
|
|
/*******************************************************************************
|
|
* Allocation
|
|
******************************************************************************/
|
|
|
|
#define SM_MIN_SMALLOBJECT_BITS 3 /* 8 bytes */
|
|
#define SM_MIN_SMALLOBJECT_SIZE (1 << SM_MIN_SMALLOBJECT_BITS)
|
|
#define SM_MAX_SMALLOBJECT_DENOM_BITS 2 /* one forth of a page */
|
|
#define SM_MAX_SMALLOBJECT_BITS (SM_PAGE_BITS - SM_MAX_SMALLOBJECT_DENOM_BITS)
|
|
#define SM_MAX_SMALLOBJECT_SIZE (1 << SM_MAX_SMALLOBJECT_BITS)
|
|
|
|
/* Number of allocation buckets needed for SM_MIN_SMALLOBJECT_SIZE <=
|
|
size <= SM_MAX_SMALLOBJECT_SIZE. The value is really:
|
|
ln(SM_MAX_SMALLOBJECT_SIZE) - ln(SM_MIN_SMALLOBJECT_SIZE) + 1
|
|
== SM_MAX_SMALLOBJECT_BITS - SM_MIN_SMALLOBJECT_BITS + 1
|
|
Be careful to keep these all in sync. */
|
|
#define SM_POWER_OF_TWO_BUCKETS \
|
|
(SM_MAX_SMALLOBJECT_BITS - SM_MIN_SMALLOBJECT_BITS + 1)
|
|
|
|
#define SM_MIDSIZE_BUCKETS (SM_POWER_OF_TWO_BUCKETS - 1)
|
|
#define SM_FIRST_MIDSIZE_BUCKET SM_POWER_OF_TWO_BUCKETS
|
|
#define SM_LARGE_OBJECT_BUCKET (SM_POWER_OF_TWO_BUCKETS + SM_MIDSIZE_BUCKETS)
|
|
#define SM_ALLOC_BUCKETS (SM_LARGE_OBJECT_BUCKET + 1)
|
|
|
|
typedef PRUint8 SMBucket;
|
|
|
|
#ifdef IS_64
|
|
#error "fix me"
|
|
#else
|
|
|
|
/* Find the bin number for a given size (in bytes). This rounds
|
|
up as values from (2^n)+1 to (2^(n+1)) share the same bin. */
|
|
#define SM_SIZE_BIN(bin, size) \
|
|
{ \
|
|
PRUword _t, _n = (PRUword)(size); \
|
|
bin = 0; \
|
|
if ((_t = (_n >> 16)) != 0) { bin += 16; _n = _t; } \
|
|
if ((_t = (_n >> 8)) != 0) { bin += 8; _n = _t; } \
|
|
if ((_t = (_n >> 4)) != 0) { bin += 4; _n = _t; } \
|
|
if ((_t = (_n >> 2)) != 0) { bin += 2; _n = _t; } \
|
|
if ((_t = (_n >> 1)) != 0) { bin += 1; _n = _t; } \
|
|
if (_n != 0) bin++; \
|
|
} \
|
|
|
|
/* Returns the bucket for a given size. */
|
|
#define SM_GET_ALLOC_BUCKET(bucket, size) \
|
|
{ \
|
|
PRUword _mid, _size = (PRUword)(size); \
|
|
if (_size <= SM_MIN_SMALLOBJECT_SIZE) \
|
|
bucket = 0; \
|
|
else if (_size > SM_MAX_SMALLOBJECT_SIZE) \
|
|
bucket = SM_LARGE_OBJECT_BUCKET; \
|
|
else { \
|
|
SM_SIZE_BIN(bucket, _size - 1); \
|
|
_mid = (PRUword)(3 << (bucket - 2)); \
|
|
if (_size <= _mid) { \
|
|
bucket += SM_FIRST_MIDSIZE_BUCKET - 1; \
|
|
} \
|
|
bucket -= SM_MIN_SMALLOBJECT_BITS; \
|
|
} \
|
|
} \
|
|
|
|
#endif
|
|
|
|
/*******************************************************************************
|
|
* Object Sizes and Counts
|
|
******************************************************************************/
|
|
|
|
/* Smallest type able to hold the size of a small object. Must be kept in
|
|
sync with SM_MAX_SMALLOBJECT_SIZE, above. */
|
|
#if SM_MAX_SMALLOBJECT_SIZE <= 256
|
|
typedef PRUint8 SMSmallObjSize;
|
|
#elif SM_MAX_SMALLOBJECT_SIZE <= 65536
|
|
typedef PRUint16 SMSmallObjSize;
|
|
#else
|
|
typedef PRUint32 SMSmallObjSize;
|
|
#endif
|
|
|
|
extern SMSmallObjSize sm_ObjectSize[];
|
|
extern SMSmallObjCount sm_ObjectsPerPage[];
|
|
|
|
#define SM_OBJECT_GROSS_SIZE(sizeResult, obj) \
|
|
{ \
|
|
SMPageCount _pageNum = SM_PAGE_NUMBER(obj); \
|
|
SMPageDesc* _pd = &sm_PageMgr.pageTable[_pageNum]; \
|
|
SMBucket _bucket = SM_PAGEDESC_BUCKET(_pd); \
|
|
if (_bucket == SM_LARGE_OBJECT_BUCKET) { \
|
|
SM_ASSERT(SM_PAGEDESC_IS_LARGE_OBJECT_START(_pd)); \
|
|
*(sizeResult) = SM_PAGE_WIDTH(_pd->allocCount); \
|
|
} \
|
|
else { \
|
|
SMBucket _bucket = SM_PAGEDESC_BUCKET(_pd); \
|
|
*(sizeResult) = sm_ObjectSize[_bucket]; \
|
|
} \
|
|
} \
|
|
|
|
/*******************************************************************************
|
|
* Fast Division
|
|
******************************************************************************/
|
|
|
|
#ifndef SM_NO_TABLE_DIVISION
|
|
|
|
/* Without SM_NO_TABLE_DIVISION defined, division is done by table lookup
|
|
which looks the fastest. However it takes up the space of the table, and
|
|
could contribute to higher level-two cache miss rates for large working
|
|
sets (we need to take some real world measurements). */
|
|
|
|
#define SM_DIVTABLE_ENTRY_COUNT (SM_MIDSIZE_BUCKETS * SM_PAGE_REFS + 1)
|
|
|
|
extern SMSmallObjCount* sm_ObjectSizeDivTable[SM_ALLOC_BUCKETS];
|
|
|
|
#define SM_DIV(offset, bucket) \
|
|
(((bucket) == SM_LARGE_OBJECT_BUCKET) \
|
|
? 0 \
|
|
: (((bucket) < SM_FIRST_MIDSIZE_BUCKET) \
|
|
? ((offset) >> ((bucket) + SM_MIN_SMALLOBJECT_BITS)) \
|
|
: sm_ObjectSizeDivTable[bucket][(offset) >> SM_REF_BITS])) \
|
|
|
|
#else /* SM_NO_TABLE_DIVISION */
|
|
|
|
/* With SM_NO_TABLE_DIVISION defined, division is done by a fancy fixed-point
|
|
algorithm that's hard-coded to divide by 3/2 times some power of 2. (Ask
|
|
Waldemar if you want to know how it works.) It looks like it may be a little
|
|
slower (although we have to measure it in the real-world) but it takes
|
|
less space because there's no division table. */
|
|
|
|
#define SM_DIV_3_2(offset, bucket) \
|
|
((((offset) + 1) * 21845) \
|
|
>> ((bucket) - SM_FIRST_MIDSIZE_BUCKET+ 18)) \
|
|
|
|
#define SM_DIV(offset, bucket) \
|
|
(((bucket) == SM_LARGE_OBJECT_BUCKET) \
|
|
? 0 \
|
|
: (((bucket) < SM_FIRST_MIDSIZE_BUCKET) \
|
|
? ((offset) >> ((bucket) + SM_MIN_SMALLOBJECT_BITS)) \
|
|
: SM_DIV_3_2(offset, bucket))) \
|
|
|
|
#endif /* SM_NO_TABLE_DIVISION */
|
|
|
|
/*******************************************************************************
|
|
* Pools
|
|
******************************************************************************/
|
|
|
|
typedef struct SMSweepList {
|
|
SMPageDesc* sweepPage;
|
|
SMSmallObjCount sweepIndex;
|
|
PRMonitor* monitor;
|
|
} SMSweepList;
|
|
|
|
struct SMPool {
|
|
SMSweepList sweepList[SM_ALLOC_BUCKETS];
|
|
PRUword allocAmount;
|
|
#ifdef SM_STATS
|
|
PRUword totalAllocated;
|
|
PRUword totalRequested;
|
|
#endif
|
|
};
|
|
|
|
#ifdef SM_STATS
|
|
|
|
#define SM_POOL_SET_ALLOC_STATS(pool, allocated, requested) \
|
|
{ \
|
|
PRUword _alloc = (pool)->totalAllocated; \
|
|
(pool)->totalAllocated += (allocated); \
|
|
(pool)->totalRequested += (requested); \
|
|
SM_ASSERT(_alloc <= (pool)->totalAllocated); \
|
|
} \
|
|
|
|
#else
|
|
|
|
#define SM_POOL_SET_ALLOC_STATS(pool, allocated, requested)
|
|
|
|
#endif
|
|
|
|
#define SM_POOL_DESTROY_PAGE(pool, pd) \
|
|
{ \
|
|
SMPageDesc* _pd = (pd); \
|
|
SMPageCount _pageNum = _pd - sm_PageMgr.pageTable; \
|
|
SMPage* _page = SM_PAGE_ADDR(_pageNum); \
|
|
SMObjDesc* _objTable = _pd->objTable; \
|
|
if (_objTable) \
|
|
SM_Free(_objTable); \
|
|
SM_DESTROY_PAGE(&sm_PageMgr, _page); \
|
|
} \
|
|
|
|
/******************************************************************************/
|
|
|
|
extern void
|
|
sm_InitAntiBuckets(void);
|
|
|
|
extern void
|
|
sm_InitFastDivision(void);
|
|
|
|
extern void*
|
|
sm_PoolInitPage(SMPool* pool, SMGenNum genNum, SMBucket bucket,
|
|
SMPage* page, SMObjDesc* objTable, SMSmallObjCount allocIndex);
|
|
|
|
extern PRStatus
|
|
sm_InitPool(SMPool* pool);
|
|
|
|
extern void
|
|
sm_FiniPool(SMPool* pool);
|
|
|
|
extern void*
|
|
sm_PoolAllocObj(SMPool* pool, SMBucket bucket);
|
|
|
|
#define SM_POOL_FREE_OBJ(pool, obj) \
|
|
{ \
|
|
SMPool* _pool = (pool); \
|
|
void* _obj = (obj); \
|
|
SMPageCount _pageNum = SM_PAGE_NUMBER(_obj); \
|
|
SMSmallObjCount _objOffset = (SMSmallObjCount)SM_PAGE_OFFSET(_obj); \
|
|
SMPageDesc* _pd = &sm_PageMgr.pageTable[_pageNum]; \
|
|
SMBucket _bucket = SM_PAGEDESC_BUCKET(_pd); \
|
|
SMSmallObjSize _objIndex = SM_DIV(_objOffset, _bucket); \
|
|
SMObjDesc* _od = &_pd->objTable[_objIndex]; \
|
|
SMSmallObjCount _objsPerPage = sm_ObjectsPerPage[_bucket]; \
|
|
SMSweepList* _sweepList = &_pool->sweepList[bucket]; \
|
|
SM_ASSERT(!SM_OBJDESC_IS_FREE(_od)); \
|
|
SM_OBJDESC_SET_FREE(_od); \
|
|
if (_pd->allocCount-- == _objsPerPage) { \
|
|
/* page was full -- add it to the front of the sweep list */ \
|
|
SM_ASSERT(_pd->next == NULL); \
|
|
_pd->next = _sweepList->sweepPage; \
|
|
_sweepList->sweepPage = _pd; \
|
|
_sweepList->sweepIndex = _objIndex; \
|
|
} \
|
|
else if (_pd == _sweepList->sweepPage \
|
|
&& _objIndex < _sweepList->sweepIndex) { \
|
|
/* if the object was on the first sweep page, and before */ \
|
|
/* the sweep index, move the sweep index back */ \
|
|
_sweepList->sweepIndex = _objIndex; \
|
|
} \
|
|
else if (_pd->allocCount == 0) { \
|
|
sm_PoolRemovePage(_pool, _bucket, _pd); \
|
|
} \
|
|
SM_POOL_VERIFY_PAGE(_pool, _bucket, _pd); \
|
|
} \
|
|
|
|
/* used by malloc, not the gc */
|
|
extern void
|
|
sm_PoolRemovePage(SMPool* pool, SMBucket bucket, SMPageDesc* freePd);
|
|
|
|
extern void*
|
|
sm_PoolAllocLargeObj(SMPool* pool, SMGenNum genNum, PRUword size);
|
|
|
|
#define SM_POOL_FREE_LARGE_OBJ(pool, obj) \
|
|
{ \
|
|
SMPage* _page = (SMPage*)(obj); \
|
|
SMPageDesc* _pd = SM_OBJECT_PAGEDESC(_page); \
|
|
SM_ASSERT(SM_PAGEDESC_IS_LARGE_OBJECT_START(_pd)); \
|
|
sm_DestroyCluster(&sm_PageMgr, _page, _pd->allocCount); \
|
|
} \
|
|
|
|
typedef PRBool
|
|
(PR_CALLBACK* SMCompactProc)(PRUword spaceNeeded);
|
|
|
|
extern SMCompactProc sm_CompactHook;
|
|
|
|
/******************************************************************************/
|
|
|
|
#ifdef DEBUG
|
|
|
|
extern void
|
|
sm_PoolVerifyPage(SMPool* pool, SMBucket bucket, SMPageDesc* pd);
|
|
|
|
#define SM_POOL_VERIFY_PAGE(pool, bucket, pd) sm_PoolVerifyPage(pool, bucket, pd)
|
|
|
|
#else /* !DEBUG */
|
|
|
|
#define SM_POOL_VERIFY_PAGE(pool, bucket, pd) /* no-op */
|
|
|
|
#endif /* !DEBUG */
|
|
|
|
/******************************************************************************/
|
|
|
|
SM_END_EXTERN_C
|
|
|
|
#endif /* __SMPOOL__ */
|
|
/******************************************************************************/
|