379 lines
14 KiB
C#
379 lines
14 KiB
C#
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
|
|
/*******************************************************************/
|
|
/* WARNING */
|
|
/* This file should be identical in the Bartok and Singularity */
|
|
/* depots. Master copy resides in Bartok Depot. Changes should be */
|
|
/* made to Bartok Depot and propagated to Singularity Depot. */
|
|
/*******************************************************************/
|
|
|
|
namespace System.GCs {
|
|
|
|
using System.Threading;
|
|
using System.Runtime.InteropServices;
|
|
using System.Runtime.CompilerServices;
|
|
|
|
internal class ModifiedFirstFit {
|
|
private const uint minimumBlockSize = 12;
|
|
|
|
private static UIntPtr osCommitSize;
|
|
|
|
private UIntPtr head;
|
|
private UIntPtr tail;
|
|
private UIntPtr last;
|
|
|
|
private UIntPtr totalMemory;
|
|
|
|
[PreInitRefCounts]
|
|
internal static void Initialize() {
|
|
osCommitSize = MemoryManager.OperatingSystemCommitSize;
|
|
UIntPtr heapCommitSize = new UIntPtr(1 << 20);
|
|
VTable.Assert(heapCommitSize > osCommitSize,
|
|
@"heapCommitSize > osCommitSize");
|
|
|
|
PageManager.Initialize(osCommitSize, heapCommitSize);
|
|
}
|
|
|
|
[Inline]
|
|
[ManualRefCounts]
|
|
internal UIntPtr AllocateBlock(UIntPtr blockSize,
|
|
Thread t) {
|
|
UIntPtr prev;
|
|
|
|
if (head == UIntPtr.Zero) { // Free list exhausted
|
|
// or no free list yet.
|
|
prev = growFreeList(blockSize, t);
|
|
} else {
|
|
VTable.Assert(tail >= head,
|
|
@"tail >= head");
|
|
|
|
// Perform a first-fit search from the last
|
|
// free block that served up an allocation
|
|
// request, or the free block after that if
|
|
// it was fully served (next fit).
|
|
|
|
prev = last;
|
|
|
|
UIntPtr desiredBlockSize = blockSize+minimumBlockSize;
|
|
|
|
while (getBlockSize(prev) < desiredBlockSize) {
|
|
prev = getNextBlock(prev);
|
|
|
|
if (prev == last) { // Search has wrapped around.
|
|
prev = growFreeList(blockSize, t);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// At this point, either the block pointed by "prev"
|
|
// has a size that exceeds the requested size by
|
|
// at least "minimumBlockSize", or precedes a block
|
|
// whose size equals the requested size or exceeds
|
|
// it by at least "minimumBlockSize".
|
|
|
|
UIntPtr prevBlockSize = getBlockSize(prev);
|
|
VTable.Assert(prevBlockSize >= blockSize+
|
|
minimumBlockSize ||
|
|
getBlockSize(getNextBlock(prev)) ==
|
|
blockSize ||
|
|
getBlockSize(getNextBlock(prev)) >=
|
|
blockSize+minimumBlockSize,
|
|
@"prevBlockSize >= blockSize+
|
|
minimumBlockSize ||
|
|
getBlockSize(getNextBlock(prev)) ==
|
|
blockSize ||
|
|
getBlockSize(getNextBlock(prev)) >=
|
|
blockSize+minimumBlockSize");
|
|
|
|
UIntPtr resultAddr;
|
|
|
|
if (prevBlockSize >=
|
|
blockSize+minimumBlockSize) { // Break block.
|
|
setBlockSize(prev, prevBlockSize-blockSize);
|
|
resultAddr = prev+(prevBlockSize-blockSize);
|
|
last = prev;
|
|
} else { // Process request from the next block.
|
|
UIntPtr curr = getNextBlock(prev);
|
|
UIntPtr currBlockSize = getBlockSize(curr);
|
|
|
|
if (currBlockSize >= blockSize+minimumBlockSize) {
|
|
setBlockSize(curr, currBlockSize-blockSize);
|
|
resultAddr = curr+(currBlockSize-blockSize);
|
|
last = curr;
|
|
} else { // Return entire block.
|
|
VTable.Assert(currBlockSize == blockSize,
|
|
@"currBlockSize == blockSize");
|
|
|
|
resultAddr = curr;
|
|
if (prev == curr) { // Free list becomes empty.
|
|
head = UIntPtr.Zero;
|
|
tail = UIntPtr.Zero;
|
|
last = UIntPtr.Zero;
|
|
} else { // Skip a block in the free list.
|
|
if (curr == head) {
|
|
curr = getNextBlock(curr);
|
|
setNextBlock(prev, curr);
|
|
head = curr;
|
|
} else if (curr == tail) {
|
|
curr = head;
|
|
setNextBlock(prev, curr);
|
|
tail = prev;
|
|
} else {
|
|
curr = getNextBlock(curr);
|
|
setNextBlock(prev, curr);
|
|
}
|
|
last = curr;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Wipe out old contents.
|
|
Util.MemClear(resultAddr, blockSize);
|
|
|
|
// Adjust memory audit figure.
|
|
totalMemory -= blockSize;
|
|
|
|
return resultAddr;
|
|
}
|
|
|
|
// Free lists used in this implementation have the invariant
|
|
// that their blocks are linked in increasing order of
|
|
// addresses. FreeBlock preserves this invariant. It also
|
|
// coalesces the inserted block with abutting free blocks.
|
|
//
|
|
// It returns the block just before the point of insertion.
|
|
|
|
[Inline]
|
|
[ManualRefCounts]
|
|
internal UIntPtr FreeBlock(UIntPtr block, UIntPtr size) {
|
|
VTable.Assert(block != UIntPtr.Zero,
|
|
@"block != UIntPtr.Zero");
|
|
VTable.Assert(head <= tail,
|
|
@"head <= tail");
|
|
VTable.Assert(size > 0,
|
|
@"size > 0");
|
|
|
|
// Adjust memory audit figure.
|
|
totalMemory += size;
|
|
|
|
UIntPtr below;
|
|
|
|
if (block < head) {
|
|
VTable.Assert(head != UIntPtr.Zero,
|
|
@"head != UIntPtr.Zero");
|
|
VTable.Assert(tail != UIntPtr.Zero,
|
|
@"tail != UIntPtr.Zero");
|
|
VTable.Assert(block+size <= head,
|
|
@"block+size <= head");
|
|
|
|
if (block+size == head) {
|
|
setBlockSize(block, size+getBlockSize(head));
|
|
|
|
if (head == tail) {
|
|
tail = block;
|
|
} else {
|
|
setNextBlock(block, getNextBlock(head));
|
|
}
|
|
if (last == head) {
|
|
last = block;
|
|
}
|
|
} else {
|
|
setBlockSize(block, size);
|
|
setNextBlock(block, head);
|
|
}
|
|
below = tail;
|
|
setNextBlock(below, block);
|
|
head = block;
|
|
} else if (head == UIntPtr.Zero) {
|
|
VTable.Assert(tail == UIntPtr.Zero,
|
|
@"tail == UIntPtr.Zero");
|
|
VTable.Assert(last == UIntPtr.Zero,
|
|
@"last == UIntPtr.Zero");
|
|
|
|
tail = block;
|
|
setBlockSize(block, size);
|
|
setNextBlock(block, block);
|
|
below = block;
|
|
head = block;
|
|
last = block;
|
|
} else if (block > tail) {
|
|
VTable.Assert(tail != UIntPtr.Zero,
|
|
@"tail != UIntPtr.Zero");
|
|
|
|
below = tail;
|
|
UIntPtr lastBlockSize = getBlockSize(below);
|
|
VTable.Assert(below+lastBlockSize <= block,
|
|
@"below+lastBlockSize <= block");
|
|
|
|
if (below+lastBlockSize == block) {
|
|
setBlockSize(below, lastBlockSize+size);
|
|
} else {
|
|
setNextBlock(below, block);
|
|
setBlockSize(block, size);
|
|
setNextBlock(block, head);
|
|
tail = block;
|
|
}
|
|
} else {
|
|
VTable.Assert(UIntPtr.Zero < head,
|
|
@"UIntPtr.Zero < head");
|
|
VTable.Assert(head < block,
|
|
@"head < block");
|
|
VTable.Assert(block < tail,
|
|
@"block < tail");
|
|
|
|
// UIntPtr.Zero < head < block < tail.
|
|
|
|
// The blocks referenced by "below" and "above"
|
|
// ultimately straddle the inserted block.
|
|
|
|
below = head;
|
|
UIntPtr above = getNextBlock(below);
|
|
|
|
while (above < block) {
|
|
below = above;
|
|
above = getNextBlock(below);
|
|
}
|
|
// At this point, below < block < above.
|
|
VTable.Assert(below < block,
|
|
@"below < block");
|
|
VTable.Assert(below+getBlockSize(below) <= block,
|
|
@"below+getBlockSize(below) <= block");
|
|
VTable.Assert(block < above,
|
|
@"block < above");
|
|
VTable.Assert(block+size <= above,
|
|
@"block+size <= above");
|
|
|
|
// Coalesce blocks if possible.
|
|
|
|
if (block+size == above) {
|
|
size += getBlockSize(above);
|
|
setBlockSize(block, size);
|
|
if (tail == above) {
|
|
tail = block;
|
|
}
|
|
if (last == above) {
|
|
last = block;
|
|
}
|
|
above = getNextBlock(above);
|
|
} else {
|
|
setBlockSize(block, size);
|
|
}
|
|
setNextBlock(block, above);
|
|
|
|
UIntPtr belowBlockSize = getBlockSize(below);
|
|
if (below+belowBlockSize == block) {
|
|
setBlockSize(below, belowBlockSize+size);
|
|
setNextBlock(below, above);
|
|
if (tail == block) {
|
|
tail = below;
|
|
}
|
|
if (last == block) {
|
|
last = below;
|
|
}
|
|
} else {
|
|
setNextBlock(below, block);
|
|
}
|
|
}
|
|
|
|
return below;
|
|
}
|
|
|
|
internal UIntPtr TotalMemory {
|
|
[Inline]
|
|
[ManualRefCounts]
|
|
get {
|
|
return totalMemory;
|
|
}
|
|
}
|
|
|
|
[Inline]
|
|
[ManualRefCounts]
|
|
internal unsafe void CheckConsistency() {
|
|
if (head == UIntPtr.Zero) {
|
|
VTable.Assert(tail == UIntPtr.Zero,
|
|
@"tail == UIntPtr.Zero");
|
|
} else if (tail == UIntPtr.Zero) {
|
|
VTable.Assert(head == UIntPtr.Zero,
|
|
@"head == UIntPtr.Zero");
|
|
} else {
|
|
VTable.Assert(head <= tail,
|
|
@"head <= tail");
|
|
VTable.Assert(getNextBlock(tail) == head,
|
|
@"getNextBlock(tail) == head");
|
|
|
|
UIntPtr leastBlockSize = new UIntPtr(2*sizeof(uint));
|
|
|
|
UIntPtr block = head;
|
|
while (block != tail) {
|
|
UIntPtr blockSize = getBlockSize(block);
|
|
UIntPtr nextBlock = getNextBlock(block);
|
|
|
|
VTable.Assert(blockSize >= leastBlockSize,
|
|
@"blockSize >= leastBlockSize");
|
|
VTable.Assert(block+blockSize < nextBlock,
|
|
@"block+blockSize < nextBlock");
|
|
|
|
block = nextBlock;
|
|
}
|
|
|
|
VTable.Assert(getBlockSize(block) >= leastBlockSize,
|
|
@"getBlockSize(block) >= leastBlockSize");
|
|
}
|
|
}
|
|
|
|
// Grow free list by allocating and inserting a block of
|
|
// size at least "blockSize".
|
|
|
|
[Inline]
|
|
[ManualRefCounts]
|
|
private UIntPtr growFreeList(UIntPtr blockSize, Thread t) {
|
|
UIntPtr pageCount = PageTable.PageCount(blockSize);
|
|
bool fCleanPages = true;
|
|
UIntPtr startPage = PageManager.EnsurePages(t, pageCount,
|
|
PageType.Owner0,
|
|
ref fCleanPages);
|
|
UIntPtr newBlockSize = PageTable.RegionSize(pageCount);
|
|
UIntPtr newBlockAddr = PageTable.PageAddr(startPage);
|
|
|
|
return FreeBlock(newBlockAddr, newBlockSize);
|
|
}
|
|
|
|
|
|
// Layout of a free block:
|
|
//
|
|
// Low address ------> High address
|
|
// --------------------------------
|
|
// | | | |
|
|
// | A | B | |
|
|
// | | | |
|
|
// --------------------------------
|
|
// <----------- size ----------->
|
|
//
|
|
// A: UIntPtr field that contains a UIntPtr to the next block
|
|
// on the free list.
|
|
//
|
|
// B: UIntPtr field that contains the size of the free block.
|
|
|
|
private unsafe static UIntPtr getNextBlock(UIntPtr block) {
|
|
return *((UIntPtr*)block+0);
|
|
}
|
|
|
|
private unsafe static void setNextBlock(UIntPtr block,
|
|
UIntPtr nextBlock) {
|
|
*((UIntPtr*)block+0) = nextBlock;
|
|
}
|
|
|
|
private unsafe static UIntPtr getBlockSize(UIntPtr block) {
|
|
return *((UIntPtr*)block+1);
|
|
}
|
|
|
|
private unsafe static void setBlockSize(UIntPtr block,
|
|
UIntPtr size) {
|
|
*((UIntPtr*)block+1) = size;
|
|
}
|
|
}
|
|
}
|