singrdk/base/Imported/Bartok/runtime/shared/GCs/ZeroCountTable.cs

233 lines
8.1 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. */
/*******************************************************************/
// #define DEBUG
namespace System.GCs {
using Microsoft.Bartok.Runtime;
using System.Threading;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
/* This class implements the ZeroCountTable for DeferredReferenceCounting
* Collector. The ZCT is an array of 10000 (Consider dynamic sizing later)
* uint entries. Initially, we link all entries together as a free list
* using their array index. When object's RC goes to Zero, we take an
* entry from free list, and put the object's address in it. In that
* object's refState, the first bit(markFlagMask) is set to indicate
* that it is in ZCT. When an object's RC becomes larger than 1, it
* is removed from ZCT, its corresponding entry is cleared and put at the
* front of the freelist. The freelist is a LIFO one.
*
* - table[0] is never used, so that 0 can be used for check, instead of
* using -1
* If ZCT running out of entries, we trigger GC.
*/
internal unsafe class ZeroCountTable {
// Note: 1. in this class, every method should be ManualRefCounted
// 2. all methods/fields are static, no need to allocate in
// BootstrapMemory. But the space for ZCT is dynamically
// allocated in a special pool
// 3. As the pool is marked as NonGC, pointers will not be traced,
// and the pool will not be scanned, therefore keep RC untouched
// Internal variables
private static uint freeHead;
private static UIntPtr[] zeroCountTable;
private static uint maxEntries;
private static ZCTGarbagePicker zctGarbagePicker;
private static UIntPtr tableSize;
[PreInitRefCounts]
public static unsafe void Initialize() {
maxEntries = 1 << 16;
VTable UIntPtrArrayVtable =
((RuntimeType)typeof(UIntPtr[])).classVtable;
tableSize =
ObjectLayout.ArraySize(UIntPtrArrayVtable, maxEntries);
// Allocate a pool for ZCT
BumpAllocator entryPool = new BumpAllocator(PageType.NonGC);
UIntPtr memStart = MemoryManager.AllocateMemory(tableSize);
entryPool.SetZeroedRange(memStart, tableSize);
PageManager.SetStaticDataPages(memStart, tableSize);
// Initialize ZCT
zeroCountTable = (UIntPtr[])
DeferredReferenceCountingCollector.
AllocateArray(ref entryPool,
UIntPtrArrayVtable,
tableSize);
VTable.Assert(zeroCountTable != null,
@"zeroCountTable != null");
*(uint*)(Magic.addressOf(zeroCountTable)+PostHeader.Size) =
maxEntries;
VTable.Assert(zeroCountTable.Length == maxEntries,
@"zeroCountTable.Length == maxEntries");
// Build ZCT freeEntries list
freeHead = 1;
for (uint i = 1; i < maxEntries-1; i++) {
zeroCountTable[i]=(UIntPtr)(((i+1) << 2) | 0x01);
}
zeroCountTable[maxEntries-1] = (UIntPtr)0x01;
zctGarbagePicker =
(ZCTGarbagePicker)BootstrapMemory.
Allocate(typeof(ZCTGarbagePicker));
}
[Inline]
[ManualRefCounts]
[DisableBoundsChecks]
private static uint GetFreeEntry() {
VTable.Assert(freeHead != 0,
@"freeHead != 0");
uint entry = freeHead;
freeHead = ((uint)zeroCountTable[entry]) >> 2;
return entry;
}
[Inline]
[ManualRefCounts]
[DisableBoundsChecks]
private static void PutFreeEntry(uint index) {
zeroCountTable[index] = (UIntPtr)((freeHead << 2) | 0x01);
freeHead = index;
}
[ManualRefCounts]
[DisableBoundsChecks]
public static void Add(Object obj) {
if (freeHead == 0) {
GC.Collect(); // GC may add object into the PLC buffer.
}
uint refState = obj.REF_STATE;
if ((refState &
DeferredReferenceCountingCollector.markFlagMask) != 0) {
return; // GC may already put this object in ZCT
}
uint position = GetFreeEntry();
if ((refState & DeferredReferenceCountingCollector.
acyclicFlagMask) == 0) {
uint index =
DeferredReferenceCountingCollector.GetPLCIndex(obj);
if (index != 0) {
DeferredReferenceCountingCollector.
SetPLCIndex(obj, 0);
DeferredReferenceCountingCollector.
removeFromPLCBuffer(index);
}
}
#if DEBUG
else {
uint index =
DeferredReferenceCountingCollector.GetPLCIndex(obj);
VTable.Assert(index == 0,
@"index == 0");
}
#endif // DEBUG
zeroCountTable[position] = Magic.addressOf(obj);
obj.REF_STATE = position |
DeferredReferenceCountingCollector.markFlagMask |
DeferredReferenceCountingCollector.countingONFlagMask |
(refState &
DeferredReferenceCountingCollector.acyclicFlagMask);
}
[ManualRefCounts]
public static void Remove(Object obj) {
uint refState = obj.REF_STATE;
uint position = refState &
DeferredReferenceCountingCollector.refCountMask;
PutFreeEntry(position);
obj.REF_STATE =
DeferredReferenceCountingCollector.countingONFlagMask |
(refState &
DeferredReferenceCountingCollector.acyclicFlagMask);
}
[ManualRefCounts]
[DisableBoundsChecks]
private static unsafe uint PurgeZCT(NonNullReferenceVisitor entryVisitor)
{
uint purgedSlots = 0;
for (uint i = 1; i < maxEntries; i++) {
UIntPtr content = zeroCountTable[i];
if (((uint)content & 0x01) != 0) {
continue; // Node in free list, skip
}
purgedSlots++;
entryVisitor.Visit(&content);
}
return purgedSlots;
}
[ManualRefCounts]
public static bool OutOfEntries() {
return freeHead == 0;
}
[ManualRefCounts]
public static bool isInZCT(Object obj) {
return (obj.REF_STATE &
DeferredReferenceCountingCollector.
markFlagMask) != 0;
}
[ManualRefCounts]
public static void ProcessZeroCountTable() {
uint purgedSlots;
do {
purgedSlots = PurgeZCT(zctGarbagePicker);
} while (purgedSlots > 0);
}
public static long Size {
get {
return (long)tableSize;
}
}
private class ZCTGarbagePicker : NonNullReferenceVisitor {
[ManualRefCounts]
internal override unsafe void Visit(UIntPtr *loc) {
UIntPtr objAddr = *loc;
Object obj = Magic.fromAddress(objAddr);
// 1. remove from ZCT
Remove(obj);
// 2. decrement RC on objects retained via multiuseword
MultiUseWord muw = MultiUseWord.GetForObject(obj);
if (muw.IsMonitorOrInflatedTag()) {
MultiUseWord.RefCountGCDeadObjHook(muw);
}
// 3. add to deallocation list
DeferredReferenceCountingCollector.deallocateLazily(obj);
}
}
}
}