// // 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; } } }