// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
/*
* Generational GC handle manager. Internal Implementation Header.
*
* Shared defines and declarations for handle table implementation.
*
*
*/
#include "common.h"
#include "handletable.h"
/*--------------------------------------------------------------------------*/
//@TODO: find a home for this in a project-level header file
#define BITS_PER_BYTE (8)
/*--------------------------------------------------------------------------*/
/****************************************************************************
*
* MAJOR TABLE DEFINITIONS THAT CHANGE DEPENDING ON THE WEATHER
*
****************************************************************************/
// 64k reserved per segment with 4k as header.
#define HANDLE_SEGMENT_SIZE (0x10000) // MUST be a power of 2 (and currently must be 64K due to VirtualAlloc semantics)
#define HANDLE_HEADER_SIZE (0x1000) // SHOULD be <= OS page size
#define HANDLE_SEGMENT_ALIGNMENT HANDLE_SEGMENT_SIZE
#if !BIGENDIAN
// little-endian write barrier mask manipulation
#define GEN_CLUMP_0_MASK (0x000000FF)
#define NEXT_CLUMP_IN_MASK(dw) (dw >> BITS_PER_BYTE)
#else
// big-endian write barrier mask manipulation
#define GEN_CLUMP_0_MASK (0xFF000000)
#define NEXT_CLUMP_IN_MASK(dw) (dw << BITS_PER_BYTE)
#endif
// if the above numbers change than these will likely change as well
#define HANDLE_HANDLES_PER_CLUMP (16) // segment write-barrier granularity
#define HANDLE_HANDLES_PER_BLOCK (64) // segment suballocation granularity
#define HANDLE_OPTIMIZE_FOR_64_HANDLE_BLOCKS // flag for certain optimizations
// number of types allowed for public callers
#define HANDLE_MAX_PUBLIC_TYPES (HANDLE_MAX_INTERNAL_TYPES - 1) // reserve one internal type
// internal block types
#define HNDTYPE_INTERNAL_DATABLOCK (HANDLE_MAX_INTERNAL_TYPES - 1) // reserve last type for data blocks
// max number of generations to support statistics on
#define MAXSTATGEN (5)
/*--------------------------------------------------------------------------*/
/****************************************************************************
*
* MORE DEFINITIONS
*
****************************************************************************/
// fast handle-to-segment mapping
#define HANDLE_SEGMENT_CONTENT_MASK (HANDLE_SEGMENT_SIZE - 1)
#define HANDLE_SEGMENT_ALIGN_MASK (~HANDLE_SEGMENT_CONTENT_MASK)
// table layout metrics
#define HANDLE_SIZE sizeof(_UNCHECKED_OBJECTREF)
#define HANDLE_HANDLES_PER_SEGMENT ((HANDLE_SEGMENT_SIZE - HANDLE_HEADER_SIZE) / HANDLE_SIZE)
#define HANDLE_BLOCKS_PER_SEGMENT (HANDLE_HANDLES_PER_SEGMENT / HANDLE_HANDLES_PER_BLOCK)
#define HANDLE_CLUMPS_PER_SEGMENT (HANDLE_HANDLES_PER_SEGMENT / HANDLE_HANDLES_PER_CLUMP)
#define HANDLE_CLUMPS_PER_BLOCK (HANDLE_HANDLES_PER_BLOCK / HANDLE_HANDLES_PER_CLUMP)
#define HANDLE_BYTES_PER_BLOCK (HANDLE_HANDLES_PER_BLOCK * HANDLE_SIZE)
#define HANDLE_HANDLES_PER_MASK (sizeof(uint32_t) * BITS_PER_BYTE)
#define HANDLE_MASKS_PER_SEGMENT (HANDLE_HANDLES_PER_SEGMENT / HANDLE_HANDLES_PER_MASK)
#define HANDLE_MASKS_PER_BLOCK (HANDLE_HANDLES_PER_BLOCK / HANDLE_HANDLES_PER_MASK)
#define HANDLE_CLUMPS_PER_MASK (HANDLE_HANDLES_PER_MASK / HANDLE_HANDLES_PER_CLUMP)
// We use this relation to check for free mask per block.
C_ASSERT (HANDLE_HANDLES_PER_MASK * 2 == HANDLE_HANDLES_PER_BLOCK);
// cache layout metrics
#define HANDLE_CACHE_TYPE_SIZE 128 // 128 == 63 handles per bank
#define HANDLES_PER_CACHE_BANK ((HANDLE_CACHE_TYPE_SIZE / 2) - 1)
// cache policy defines
#define REBALANCE_TOLERANCE (HANDLES_PER_CACHE_BANK / 3)
#define REBALANCE_LOWATER_MARK (HANDLES_PER_CACHE_BANK - REBALANCE_TOLERANCE)
#define REBALANCE_HIWATER_MARK (HANDLES_PER_CACHE_BANK + REBALANCE_TOLERANCE)
// bulk alloc policy defines
#define SMALL_ALLOC_COUNT (HANDLES_PER_CACHE_BANK / 10)
// misc constants
#define MASK_FULL (0)
#define MASK_EMPTY (0xFFFFFFFF)
#define MASK_LOBYTE (0x000000FF)
#define TYPE_INVALID ((uint8_t)0xFF)
#define BLOCK_INVALID ((uint8_t)0xFF)
/*--------------------------------------------------------------------------*/
/****************************************************************************
*
* CORE TABLE LAYOUT STRUCTURES
*
****************************************************************************/
/*
* we need byte packing for the handle table layout to work
*/
#pragma pack(push,1)
/*
* Table Segment Header
*
* Defines the layout for a segment's header data.
*/
struct _TableSegmentHeader
{
/*
* Write Barrier Generation Numbers
*
* Each slot holds four bytes. Each byte corresponds to a clump of handles.
* The value of the byte corresponds to the lowest possible generation that a
* handle in that clump could point into.
*
* WARNING: Although this array is logically organized as a uint8_t[], it is sometimes
* accessed as uint32_t[] when processing bytes in parallel. Code which treats the
* array as an array of ULONG32s must handle big/little endian issues itself.
*/
uint8_t rgGeneration[HANDLE_BLOCKS_PER_SEGMENT * sizeof(uint32_t) / sizeof(uint8_t)];
/*
* Block Allocation Chains
*
* Each slot indexes the next block in an allocation chain.
*/
uint8_t rgAllocation[HANDLE_BLOCKS_PER_SEGMENT];
/*
* Block Free Masks
*
* Masks - 1 bit for every handle in the segment.
*/
uint32_t rgFreeMask[HANDLE_MASKS_PER_SEGMENT];
/*
* Block Handle Types
*
* Each slot holds the handle type of the associated block.
*/
uint8_t rgBlockType[HANDLE_BLOCKS_PER_SEGMENT];
/*
* Block User Data Map
*
* Each slot holds the index of a user data block (if any) for the associated block.
*/
uint8_t rgUserData[HANDLE_BLOCKS_PER_SEGMENT];
/*
* Block Lock Count
*
* Each slot holds a lock count for its associated block.
* Locked blocks are not freed, even when empty.
*/
uint8_t rgLocks[HANDLE_BLOCKS_PER_SEGMENT];
/*
* Allocation Chain Tails
*
* Each slot holds the tail block index for an allocation chain.
*/
uint8_t rgTail[HANDLE_MAX_INTERNAL_TYPES];
/*
* Allocation Chain Hints
*
* Each slot holds a hint block index for an allocation chain.
*/
uint8_t rgHint[HANDLE_MAX_INTERNAL_TYPES];
/*
* Free Count
*
* Each slot holds the number of free handles in an allocation chain.
*/
uint32_t rgFreeCount[HANDLE_MAX_INTERNAL_TYPES];
/*
* Next Segment
*
* Points to the next segment in the chain (if we ran out of space in this one).
*/
#ifdef DACCESS_COMPILE
TADDR pNextSegment;
#else
struct TableSegment *pNextSegment;
#endif // DACCESS_COMPILE
/*
* Handle Table
*
* Points to owning handle table for this table segment.
*/
PTR_HandleTable pHandleTable;
/*
* Flags
*/
uint8_t fResortChains : 1; // allocation chains need sorting
uint8_t fNeedsScavenging : 1; // free blocks need scavenging
uint8_t _fUnused : 6; // unused
/*
* Free List Head
*
* Index of the first free block in the segment.
*/
uint8_t bFreeList;
/*
* Empty Line
*
* Index of the first KNOWN block of the last group of unused blocks in the segment.
*/
uint8_t bEmptyLine;
/*
* Commit Line
*
* Index of the first uncommited block in the segment.
*/
uint8_t bCommitLine;
/*
* Decommit Line
*
* Index of the first block in the highest committed page of the segment.
*/
uint8_t bDecommitLine;
/*
* Sequence
*
* Indicates the segment sequence number.
*/
uint8_t bSequence;
};
typedef DPTR(struct _TableSegmentHeader) PTR__TableSegmentHeader;
typedef DPTR(uintptr_t) PTR_uintptr_t;
// The handle table is large and may not be entirely mapped. That's one reason for splitting out the table
// segment and the header as two separate classes. In DAC builds, we generally need only a single element from
// the table segment, so we can use the DAC to retrieve just the information we require.
/*
* Table Segment
*
* Defines the layout for a handle table segment.
*/
struct TableSegment : public _TableSegmentHeader
{
/*
* Filler
*/
uint8_t rgUnused[HANDLE_HEADER_SIZE - sizeof(_TableSegmentHeader)];
/*
* Handles
*/
_UNCHECKED_OBJECTREF rgValue[HANDLE_HANDLES_PER_SEGMENT];
#ifdef DACCESS_COMPILE
static uint32_t DacSize(TADDR addr);
#endif
};
typedef SPTR(struct TableSegment) PTR_TableSegment;
/*
* restore default packing
*/
#pragma pack(pop)
/*
* Handle Type Cache
*
* Defines the layout of a per-type handle cache.
*/
struct HandleTypeCache
{
/*
* reserve bank
*/
OBJECTHANDLE rgReserveBank[HANDLES_PER_CACHE_BANK];
/*
* index of next available handle slot in the reserve bank
*/
int32_t lReserveIndex;
/*---------------------------------------------------------------------------------
* N.B. this structure is split up this way so that when HANDLES_PER_CACHE_BANK is
* large enough, lReserveIndex and lFreeIndex will reside in different cache lines
*--------------------------------------------------------------------------------*/
/*
* free bank
*/
OBJECTHANDLE rgFreeBank[HANDLES_PER_CACHE_BANK];
/*
* index of next empty slot in the free bank
*/
int32_t lFreeIndex;
};
/*---------------------------------------------------------------------------*/
/****************************************************************************
*
* SCANNING PROTOTYPES
*
****************************************************************************/
/*
* ScanCallbackInfo
*
* Carries parameters for per-segment and per-block scanning callbacks.
*
*/
struct ScanCallbackInfo
{
PTR_TableSegment pCurrentSegment; // segment we are presently scanning, if any
uint32_t uFlags; // HNDGCF_* flags
BOOL fEnumUserData; // whether user data is being enumerated as well
HANDLESCANPROC pfnScan; // per-handle scan callback
uintptr_t param1; // callback param 1
uintptr_t param2; // callback param 2
uint32_t dwAgeMask; // generation mask for ephemeral GCs
#ifdef _DEBUG
uint32_t DEBUG_BlocksScanned;
uint32_t DEBUG_BlocksScannedNonTrivially;
uint32_t DEBUG_HandleSlotsScanned;
uint32_t DEBUG_HandlesActuallyScanned;
#endif
};
/*
* BLOCKSCANPROC
*
* Prototype for callbacks that implement per-block scanning logic.
*
*/
typedef void (CALLBACK *BLOCKSCANPROC)(PTR_TableSegment pSegment, uint32_t uBlock, uint32_t uCount, ScanCallbackInfo *pInfo);
/*
* SEGMENTITERATOR
*
* Prototype for callbacks that implement per-segment scanning logic.
*
*/
typedef PTR_TableSegment (CALLBACK *SEGMENTITERATOR)(PTR_HandleTable pTable, PTR_TableSegment pPrevSegment, CrstHolderWithState *pCrstHolder);
/*
* TABLESCANPROC
*
* Prototype for TableScanHandles and xxxTableScanHandlesAsync.
*
*/
typedef void (CALLBACK *TABLESCANPROC)(PTR_HandleTable pTable,
const uint32_t *puType, uint32_t uTypeCount,
SEGMENTITERATOR pfnSegmentIterator,
BLOCKSCANPROC pfnBlockHandler,
ScanCallbackInfo *pInfo,
CrstHolderWithState *pCrstHolder);
/*--------------------------------------------------------------------------*/
/****************************************************************************
*
* ADDITIONAL TABLE STRUCTURES
*
****************************************************************************/
/*
* AsyncScanInfo
*
* Tracks the state of an async scan for a handle table.
*
*/
struct AsyncScanInfo
{
/*
* Underlying Callback Info
*
* Specifies callback info for the underlying block handler.
*/
struct ScanCallbackInfo *pCallbackInfo;
/*
* Underlying Segment Iterator
*
* Specifies the segment iterator to be used during async scanning.
*/
SEGMENTITERATOR pfnSegmentIterator;
/*
* Underlying Block Handler
*
* Specifies the block handler to be used during async scanning.
*/
BLOCKSCANPROC pfnBlockHandler;
/*
* Scan Queue
*
* Specifies the nodes to be processed asynchronously.
*/
struct ScanQNode *pScanQueue;
/*
* Queue Tail
*
* Specifies the tail node in the queue, or NULL if the queue is empty.
*/
struct ScanQNode *pQueueTail;
};
/*
* Handle Table
*
* Defines the layout of a handle table object.
*/
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4200 ) // zero-sized array
#endif
struct HandleTable
{
/*
* flags describing handle attributes
*
* N.B. this is at offset 0 due to frequent access by cache free codepath
*/
uint32_t rgTypeFlags[HANDLE_MAX_INTERNAL_TYPES];
/*
* lock for this table
*/
CrstStatic Lock;
/*
* number of types this table supports
*/
uint32_t uTypeCount;
/*
* number of handles owned by this table that are marked as "used"
* (this includes the handles residing in rgMainCache and rgQuickCache)
*/
uint32_t dwCount;
/*
* head of segment list for this table
*/
PTR_TableSegment pSegmentList;
/*
* information on current async scan (if any)
*/
AsyncScanInfo *pAsyncScanInfo;
/*
* per-table user info
*/
uint32_t uTableIndex;
/*
* one-level per-type 'quick' handle cache
*/
OBJECTHANDLE rgQuickCache[HANDLE_MAX_INTERNAL_TYPES]; // interlocked ops used here
/*
* debug-only statistics
*/
#ifdef _DEBUG
int _DEBUG_iMaxGen;
int64_t _DEBUG_TotalBlocksScanned [MAXSTATGEN];
int64_t _DEBUG_TotalBlocksScannedNonTrivially[MAXSTATGEN];
int64_t _DEBUG_TotalHandleSlotsScanned [MAXSTATGEN];
int64_t _DEBUG_TotalHandlesActuallyScanned [MAXSTATGEN];
#endif
/*
* primary per-type handle cache
*/
HandleTypeCache rgMainCache[0]; // interlocked ops used here
};
#ifdef _MSC_VER
#pragma warning(pop)
#endif
/*--------------------------------------------------------------------------*/
/****************************************************************************
*
* HELPERS
*
****************************************************************************/
/*
* A 32/64 comparison callback
*
* @TODO: move/merge into common util file
*
*/
typedef int (*PFNCOMPARE)(uintptr_t p, uintptr_t q);
/*
* A 32/64 neutral quicksort
*
* @TODO: move/merge into common util file
*
*/
void QuickSort(uintptr_t *pData, int left, int right, PFNCOMPARE pfnCompare);
/*
* CompareHandlesByFreeOrder
*
* Returns:
* <0 - handle P should be freed before handle Q
* =0 - handles are eqivalent for free order purposes
* >0 - handle Q should be freed before handle P
*
*/
int CompareHandlesByFreeOrder(uintptr_t p, uintptr_t q);
/*--------------------------------------------------------------------------*/
/****************************************************************************
*
* CORE TABLE MANAGEMENT
*
****************************************************************************/
/*
* TypeHasUserData
*
* Determines whether a given handle type has user data.
*
*/
__inline BOOL TypeHasUserData(HandleTable *pTable, uint32_t uType)
{
LIMITED_METHOD_CONTRACT;
// sanity
_ASSERTE(uType < HANDLE_MAX_INTERNAL_TYPES);
// consult the type flags
return (pTable->rgTypeFlags[uType] & HNDF_EXTRAINFO);
}
/*
* TableCanFreeSegmentNow
*
* Determines if it is OK to free the specified segment at this time.
*
*/
BOOL TableCanFreeSegmentNow(HandleTable *pTable, TableSegment *pSegment);
/*
* BlockIsLocked
*
* Determines if the lock count for the specified block is currently non-zero.
*
*/
__inline BOOL BlockIsLocked(TableSegment *pSegment, uint32_t uBlock)
{
LIMITED_METHOD_CONTRACT;
// sanity
_ASSERTE(uBlock < HANDLE_BLOCKS_PER_SEGMENT);
// fetch the lock count and compare it to zero
return (pSegment->rgLocks[uBlock] != 0);
}
/*
* BlockLock
*
* Increases the lock count for a block.
*
*/
__inline void BlockLock(TableSegment *pSegment, uint32_t uBlock)
{
LIMITED_METHOD_CONTRACT;
// fetch the old lock count
uint8_t bLocks = pSegment->rgLocks[uBlock];
// assert if we are about to trash the count
_ASSERTE(bLocks < 0xFF);
// store the incremented lock count
pSegment->rgLocks[uBlock] = bLocks + 1;
}
/*
* BlockUnlock
*
* Decreases the lock count for a block.
*
*/
__inline void BlockUnlock(TableSegment *pSegment, uint32_t uBlock)
{
LIMITED_METHOD_CONTRACT;
// fetch the old lock count
uint8_t bLocks = pSegment->rgLocks[uBlock];
// assert if we are about to trash the count
_ASSERTE(bLocks > 0);
// store the decremented lock count
pSegment->rgLocks[uBlock] = bLocks - 1;
}
/*
* BlockFetchUserDataPointer
*
* Gets the user data pointer for the first handle in a block.
*
*/
PTR_uintptr_t BlockFetchUserDataPointer(PTR__TableSegmentHeader pSegment, uint32_t uBlock, BOOL fAssertOnError);
/*
* HandleValidateAndFetchUserDataPointer
*
* Gets the user data pointer for a handle.
* ASSERTs and returns NULL if handle is not of the expected type.
*
*/
uintptr_t *HandleValidateAndFetchUserDataPointer(OBJECTHANDLE handle, uint32_t uTypeExpected);
/*
* HandleQuickFetchUserDataPointer
*
* Gets the user data pointer for a handle.
* Less validation is performed.
*
*/
PTR_uintptr_t HandleQuickFetchUserDataPointer(OBJECTHANDLE handle);
/*
* HandleQuickSetUserData
*
* Stores user data with a handle.
* Less validation is performed.
*
*/
void HandleQuickSetUserData(OBJECTHANDLE handle, uintptr_t lUserData);
/*
* HandleFetchType
*
* Computes the type index for a given handle.
*
*/
uint32_t HandleFetchType(OBJECTHANDLE handle);
/*
* HandleFetchHandleTable
*
* Returns the containing handle table of a given handle.
*
*/
PTR_HandleTable HandleFetchHandleTable(OBJECTHANDLE handle);
/*
* SegmentAlloc
*
* Allocates a new segment.
*
*/
TableSegment *SegmentAlloc(HandleTable *pTable);
/*
* SegmentFree
*
* Frees the specified segment.
*
*/
void SegmentFree(TableSegment *pSegment);
/*
* Check if a handle is part of a HandleTable
*/
BOOL TableContainHandle(HandleTable *pTable, OBJECTHANDLE handle);
/*
* SegmentRemoveFreeBlocks
*
* Removes a block from a block list in a segment. The block is returned to
* the segment's free list.
*
*/
void SegmentRemoveFreeBlocks(TableSegment *pSegment, uint32_t uType);
/*
* SegmentResortChains
*
* Sorts the block chains for optimal scanning order.
* Sorts the free list to combat fragmentation.
*
*/
void SegmentResortChains(TableSegment *pSegment);
/*
* DoesSegmentNeedsToTrimExcessPages
*
* Checks to see if any pages can be decommitted from the segment.
*
*/
BOOL DoesSegmentNeedsToTrimExcessPages(TableSegment *pSegment);
/*
* SegmentTrimExcessPages
*
* Checks to see if any pages can be decommitted from the segment.
* In case there any unused pages it goes and decommits them.
*
*/
void SegmentTrimExcessPages(TableSegment *pSegment);
/*
* TableAllocBulkHandles
*
* Attempts to allocate the requested number of handes of the specified type.
*
* Returns the number of handles that were actually allocated. This is always
* the same as the number of handles requested except in out-of-memory conditions,
* in which case it is the number of handles that were successfully allocated.
*
*/
uint32_t TableAllocBulkHandles(HandleTable *pTable, uint32_t uType, OBJECTHANDLE *pHandleBase, uint32_t uCount);
/*
* TableFreeBulkPreparedHandles
*
* Frees an array of handles of the specified type.
*
* This routine is optimized for a sorted array of handles but will accept any order.
*
*/
void TableFreeBulkPreparedHandles(HandleTable *pTable, uint32_t uType, OBJECTHANDLE *pHandleBase, uint32_t uCount);
/*
* TableFreeBulkUnpreparedHandles
*
* Frees an array of handles of the specified type by preparing them and calling TableFreeBulkPreparedHandles.
*
*/
void TableFreeBulkUnpreparedHandles(HandleTable *pTable, uint32_t uType, const OBJECTHANDLE *pHandles, uint32_t uCount);
/*--------------------------------------------------------------------------*/
/****************************************************************************
*
* HANDLE CACHE
*
****************************************************************************/
/*
* TableAllocSingleHandleFromCache
*
* Gets a single handle of the specified type from the handle table by
* trying to fetch it from the reserve cache for that handle type. If the
* reserve cache is empty, this routine calls TableCacheMissOnAlloc.
*
*/
OBJECTHANDLE TableAllocSingleHandleFromCache(HandleTable *pTable, uint32_t uType);
/*
* TableFreeSingleHandleToCache
*
* Returns a single handle of the specified type to the handle table
* by trying to store it in the free cache for that handle type. If the
* free cache is full, this routine calls TableCacheMissOnFree.
*
*/
void TableFreeSingleHandleToCache(HandleTable *pTable, uint32_t uType, OBJECTHANDLE handle);
/*
* TableAllocHandlesFromCache
*
* Allocates multiple handles of the specified type by repeatedly
* calling TableAllocSingleHandleFromCache.
*
*/
uint32_t TableAllocHandlesFromCache(HandleTable *pTable, uint32_t uType, OBJECTHANDLE *pHandleBase, uint32_t uCount);
/*
* TableFreeHandlesToCache
*
* Frees multiple handles of the specified type by repeatedly
* calling TableFreeSingleHandleToCache.
*
*/
void TableFreeHandlesToCache(HandleTable *pTable, uint32_t uType, const OBJECTHANDLE *pHandleBase, uint32_t uCount);
/*--------------------------------------------------------------------------*/
/****************************************************************************
*
* TABLE SCANNING
*
****************************************************************************/
/*
* TableScanHandles
*
* Implements the core handle scanning loop for a table.
*
*/
void CALLBACK TableScanHandles(PTR_HandleTable pTable,
const uint32_t *puType,
uint32_t uTypeCount,
SEGMENTITERATOR pfnSegmentIterator,
BLOCKSCANPROC pfnBlockHandler,
ScanCallbackInfo *pInfo,
CrstHolderWithState *pCrstHolder);
/*
* xxxTableScanHandlesAsync
*
* Implements asynchronous handle scanning for a table.
*
*/
void CALLBACK xxxTableScanHandlesAsync(PTR_HandleTable pTable,
const uint32_t *puType,
uint32_t uTypeCount,
SEGMENTITERATOR pfnSegmentIterator,
BLOCKSCANPROC pfnBlockHandler,
ScanCallbackInfo *pInfo,
CrstHolderWithState *pCrstHolder);
/*
* TypesRequireUserDataScanning
*
* Determines whether the set of types listed should get user data during scans
*
* if ALL types passed have user data then this function will enable user data support
* otherwise it will disable user data support
*
* IN OTHER WORDS, SCANNING WITH A MIX OF USER-DATA AND NON-USER-DATA TYPES IS NOT SUPPORTED
*
*/
BOOL TypesRequireUserDataScanning(HandleTable *pTable, const uint32_t *types, uint32_t typeCount);
/*
* BuildAgeMask
*
* Builds an age mask to be used when examining/updating the write barrier.
*
*/
uint32_t BuildAgeMask(uint32_t uGen, uint32_t uMaxGen);
/*
* QuickSegmentIterator
*
* Returns the next segment to be scanned in a scanning loop.
*
*/
PTR_TableSegment CALLBACK QuickSegmentIterator(PTR_HandleTable pTable, PTR_TableSegment pPrevSegment, CrstHolderWithState *pCrstHolder = 0);
/*
* StandardSegmentIterator
*
* Returns the next segment to be scanned in a scanning loop.
*
* This iterator performs some maintenance on the segments,
* primarily making sure the block chains are sorted so that
* g0 scans are more likely to operate on contiguous blocks.
*
*/
PTR_TableSegment CALLBACK StandardSegmentIterator(PTR_HandleTable pTable, PTR_TableSegment pPrevSegment, CrstHolderWithState *pCrstHolder = 0);
/*
* FullSegmentIterator
*
* Returns the next segment to be scanned in a scanning loop.
*
* This iterator performs full maintenance on the segments,
* including freeing those it notices are empty along the way.
*
*/
PTR_TableSegment CALLBACK FullSegmentIterator(PTR_HandleTable pTable, PTR_TableSegment pPrevSegment, CrstHolderWithState *pCrstHolder = 0);
/*
* BlockScanBlocksWithoutUserData
*
* Calls the specified callback for each handle, optionally aging the corresponding generation clumps.
* NEVER propagates per-handle user data to the callback.
*
*/
void CALLBACK BlockScanBlocksWithoutUserData(PTR_TableSegment pSegment, uint32_t uBlock, uint32_t uCount, ScanCallbackInfo *pInfo);
/*
* BlockScanBlocksWithUserData
*
* Calls the specified callback for each handle, optionally aging the corresponding generation clumps.
* ALWAYS propagates per-handle user data to the callback.
*
*/
void CALLBACK BlockScanBlocksWithUserData(PTR_TableSegment pSegment, uint32_t uBlock, uint32_t uCount, ScanCallbackInfo *pInfo);
/*
* BlockScanBlocksEphemeral
*
* Calls the specified callback for each handle from the specified generation.
* Propagates per-handle user data to the callback if present.
*
*/
void CALLBACK BlockScanBlocksEphemeral(PTR_TableSegment pSegment, uint32_t uBlock, uint32_t uCount, ScanCallbackInfo *pInfo);
/*
* BlockAgeBlocks
*
* Ages all clumps in a range of consecutive blocks.
*
*/
void CALLBACK BlockAgeBlocks(PTR_TableSegment pSegment, uint32_t uBlock, uint32_t uCount, ScanCallbackInfo *pInfo);
/*
* BlockAgeBlocksEphemeral
*
* Ages all clumps within the specified generation.
*
*/
void CALLBACK BlockAgeBlocksEphemeral(PTR_TableSegment pSegment, uint32_t uBlock, uint32_t uCount, ScanCallbackInfo *pInfo);
/*
* BlockResetAgeMapForBlocks
*
* Clears the age maps for a range of blocks.
*
*/
void CALLBACK BlockResetAgeMapForBlocks(PTR_TableSegment pSegment, uint32_t uBlock, uint32_t uCount, ScanCallbackInfo *pInfo);
/*
* BlockVerifyAgeMapForBlocks
*
* Verifies the age maps for a range of blocks, and also validates the objects pointed to.
*
*/
void CALLBACK BlockVerifyAgeMapForBlocks(PTR_TableSegment pSegment, uint32_t uBlock, uint32_t uCount, ScanCallbackInfo *pInfo);
/*
* xxxAsyncSegmentIterator
*
* Implements the core handle scanning loop for a table.
*
*/
PTR_TableSegment CALLBACK xxxAsyncSegmentIterator(PTR_HandleTable pTable, TableSegment *pPrevSegment, CrstHolderWithState *pCrstHolder);
/*--------------------------------------------------------------------------*/