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

824 lines
33 KiB
C#
Raw Normal View History

2008-11-17 18:29:00 -05:00
/*******************************************************************/
/* 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. */
/*******************************************************************/
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
namespace System.GCs {
using Microsoft.Bartok.Runtime;
using System.Runtime.CompilerServices;
using System.Threading;
#if SINGULARITY
using Microsoft.Singularity;
#if SINGULARITY_PROCESS
using Microsoft.Singularity.V1.Services; // Used for timing, only
#elif SINGULARITY_KERNEL
using Microsoft.Singularity.Scheduling;
using Microsoft.Singularity.X86;
#endif
#endif
using Microsoft.Win32;
[NoCCtor]
[RequiredByBartok]
internal class CoCoMSCollector: ConcurrentMSCollector
{
internal static new CoCoMSCollector instance;
internal static Thread cocoThread;
internal static bool didStartTrace;
internal static bool didEndTrace;
internal static bool wantCoCo;
internal static bool doingCoCo;
internal static int markingForCoCo; // actually a boolean
internal static bool inFixUp;
internal static bool die;
internal static ulong numCopied;
internal static int delayCnt;
internal static Object interlock;
internal static int pinTime;
internal static int prepTime;
internal static int copyTime;
internal static int forwardTime;
internal static int timingBefore;
internal static int timingAfterPin;
internal static int timingAfterPrep;
internal static int timingAfterCopy;
internal static int timingAfter;
internal static UIntPtr maxSpaceOverhead;
internal static int sizeFracLim;
internal static int sizeLim;
internal static int pageFragThres;
internal static int pinPenalty;
internal static int cocoDelay;
internal static UIntPtr lastSmallPagesRecycle;
internal static NonNullReferenceVisitor normalStackMarker;
internal static NonNullReferenceVisitor nopStackMarker;
internal static NonNullReferenceVisitor pinStackMarker;
[NoBarriers]
[PreInitRefCounts]
public static new void Initialize()
{
ConcurrentMSCollector.InitializeAllButVisitors();
// instance = new CoCoMSCollector();
ConcurrentMSCollector.instance
= CoCoMSCollector.instance
= (CoCoMSCollector)
BootstrapMemory.Allocate(typeof(CoCoMSCollector));
ConcurrentMSCollector.markReferenceVisitor =
(MarkReferenceVisitor)
BootstrapMemory.Allocate(typeof(MarkAndForwardReferenceVisitor));
CoCoMSCollector.normalStackMarker =
(NonNullReferenceVisitor)
BootstrapMemory.Allocate(typeof(StackForwardReferenceVisitor));
CoCoMSCollector.pinStackMarker =
(NonNullReferenceVisitor)
BootstrapMemory.Allocate(typeof(StackMarkPinnedReferenceVisitor));
CoCoMSCollector.nopStackMarker =
(NonNullReferenceVisitor)
BootstrapMemory.Allocate(typeof(StackNopReferenceVisitor));
ConcurrentMSCollector.stackMarkReferenceVisitor =
CoCoMSCollector.normalStackMarker;
ConcurrentMSCollector.stackMarkPinnedReferenceVisitor =
CoCoMSCollector.normalStackMarker;
ConcurrentMSCollector.updateReferenceVisitor =
(UpdateReferenceVisitor)
BootstrapMemory.Allocate(typeof(UpdateAndForwardReferenceVisitor));
ConcurrentMSCollector.partialFreePageVisitor =
(SegregatedFreeList.PartialFreePageVisitor)
BootstrapMemory.Allocate(typeof(TaggingPartialFreePageVisitor));
// sweepVisitor = new SweepVisitor();
sweepVisitor = (SweepVisitor)
BootstrapMemory.Allocate(typeof(SweepVisitor));
}
internal static int EnvInt(int defVal,
string name)
{
string str=Environment.GetEnvironmentVariable(name);
if (str==null) {
return defVal;
} else {
return Int32.Parse(str);
}
}
internal override void EnableHeap()
{
interlock=new Object();
MultiUseWord.GetMonitor(interlock);
CoCoBarrier.InitLate();
// REVIEW: add some bartok args instead
sizeFracLim = EnvInt( 10 , "COCO_SIZE_FRAC_LIM" );
sizeLim = EnvInt( -1 , "COCO_SIZE_LIM" );
pageFragThres = EnvInt( 2 , "COCO_PAGE_FRAG_THRES" );
pinPenalty = EnvInt( 10 , "COCO_PIN_PENALTY" );
cocoDelay = EnvInt( 8 , "COCO_COPY_DELAY" );
if (EnvInt(0,"COCO_FORCE_SLOW")!=0) {
CoCoBarrier.ForceSlow();
}
if (EnvInt(0,"COCO_FORCE_NOT_IDLE")!=0) {
CoCoBarrier.ForceNotIdle();
}
if (EnvInt(0,"COCO_FORCE_FORWARDING")!=0) {
CoCoBarrier.ForceForwarding();
}
if (EnvInt(0,"COCO_FORCE_PINNING")!=0) {
CoCoBarrier.ForcePinning();
}
base.EnableHeap();
cocoThread=new Thread(new ThreadStart(CoCoLoop));
cocoThread.Start();
}
internal override void Shutdown()
{
base.Shutdown();
lock (interlock) {
die = true;
Monitor.PulseAll(interlock);
}
cocoThread.Join();
if (VTable.enableFinalGCTiming) {
VTable.DebugPrint("CoCo completed ");
VTable.DebugPrint(cycles);
VTable.DebugPrint(" cycles, started ");
VTable.DebugPrint(cyclesStarted);
VTable.DebugPrint(" cycles, and copied ");
VTable.DebugPrint(numCopied);
VTable.DebugPrint(" objects.\n");
VTable.DebugPrint("CoCo took ");
VTable.DebugPrint((ulong)pinTime);
VTable.DebugPrint("+");
VTable.DebugPrint((ulong)prepTime);
VTable.DebugPrint("+");
VTable.DebugPrint((ulong)copyTime);
VTable.DebugPrint("+");
VTable.DebugPrint((ulong)forwardTime);
VTable.DebugPrint("=");
VTable.DebugPrint((ulong)(pinTime+prepTime+copyTime+forwardTime));
VTable.DebugPrint(" ms.\n");
VTable.DebugPrint("max space overhead = ");
VTable.DebugPrint((ulong)maxSpaceOverhead);
VTable.DebugPrint("\n");
CoCoBarrier.PrintStats();
}
}
internal override void ThreadStartNotification(int currentThreadIndex)
{
base.ThreadStartNotification(currentThreadIndex);
CoCoBarrier.ThreadStart(Thread.threadTable[currentThreadIndex]);
}
internal override UIntPtr AllocateObjectMemorySlow(UIntPtr numBytes,
uint alignment,
Thread currentThread)
{
CoCoBarrier.ClientHandshake();
return base.AllocateObjectMemorySlow(numBytes,
alignment,
currentThread);
}
internal static void InterlockWithCopier(ref bool condition)
{
lock (interlock) {
if (fVerbose) {
VTable.DebugPrint(" ~~ acquired lock, setting condition\n");
}
condition = true;
if (fVerbose) {
VTable.DebugPrint(" ~~ pulsing = ");
VTable.DebugPrint((ulong)Magic.addressOf(interlock));
VTable.DebugPrint("\n");
}
Monitor.PulseAll(interlock);
if (fVerbose) {
VTable.DebugPrint(" ~~ waiting\n");
}
while (condition) {
Monitor.Wait(interlock);
}
}
}
internal override void PreTraceHook()
{
if (fVerbose) {
VTable.DebugPrint(" ~~~~~ PreTraceHook ~ waiting\n");
}
InterlockWithCopier(ref didStartTrace);
if (fVerbose) {
VTable.DebugPrint(" ~~~~~ PreTraceHook ~ awoken\n");
}
}
internal override void PostRootScanHook()
{
if (inFixUp) {
timingAfterCopy = Environment.TickCount;
CoCoBarrier.ChangePhase(CoCoBarrier.Phase.Idle,true,true);
}
}
internal override void PostTraceHook()
{
if (fVerbose) {
VTable.DebugPrint(" ~~~~~ PostTraceHook ~ waiting\n");
}
InterlockWithCopier(ref didEndTrace);
if (fVerbose) {
VTable.DebugPrint(" ~~~~~ PostTraceHook ~ awoken\n");
}
}
internal override void PostReclamationHook()
{
}
internal override void PreSweepHook()
{
CoCoBarrier.ExchangeReadyForCoCo(false);
}
internal override void PostSweepHook()
{
CoCoBarrier.ExchangeReadyForCoCo(true);
}
internal static void CoCoLoop()
{
if (fDebug) {
VTable.DebugPrint("coco thread = ");
VTable.DebugPrint((ulong)Win32Native.GetCurrentThreadId());
VTable.DebugPrint("\n");
VTable.DebugPrint("CoCo at ");
VTable.DebugPrint((ulong)Magic.addressOf(Thread.CurrentThread));
VTable.DebugPrint("\n");
}
for (;;) {
lock (interlock) {
doingCoCo=false;
for (;;) {
if (die) {
return;
} else if (didStartTrace) {
didStartTrace=false;
Monitor.PulseAll(interlock);
} else if (didEndTrace) {
didEndTrace=false;
Monitor.PulseAll(interlock);
if (wantCoCo) {
break;
}
}
Monitor.Wait(interlock);
}
wantCoCo=false;
}
// now further tracing is BLOCKED
cyclesStarted++;
timingBefore = Environment.TickCount;
if (fDebug) {
VTable.DebugPrint("+++++ Start Concurrent Copying\n");
}
CoCoBarrier.EnablePinning();
doingCoCo=true;
ConcurrentMSCollector.stackMarkReferenceVisitor =
CoCoMSCollector.nopStackMarker;
ConcurrentMSCollector.stackMarkPinnedReferenceVisitor =
CoCoMSCollector.pinStackMarker;
// Perform a scan of all call stacks, including the call
// stack of the CoCo thread.
ConcurrentMSCollector.TrivialHandshake = false;
ConcurrentMSCollector.IncludeMUWInHandshake = false;
ConcurrentMSCollector.CollectorHandshake(cocoThread);
// In order to scan the call stack of the current thread,
// we need a TransitionRecord for the thread. At this
// point we don't have one, so we have to go through
// CollectBodyTransition to get one.
Transitions.MakeGCRequest(cocoThread.threadIndex);
GC.InvokeCollection(cocoThread);
ConcurrentMSCollector.TrivialHandshake = true;
ConcurrentMSCollector.IncludeMUWInHandshake = true;
ConcurrentMSCollector.stackMarkReferenceVisitor =
CoCoMSCollector.normalStackMarker;
ConcurrentMSCollector.stackMarkPinnedReferenceVisitor =
CoCoMSCollector.normalStackMarker;
timingAfterPin = Environment.TickCount;
if (fDebug) {
VTable.DebugPrint("+++++ Copying\n");
}
if (CoCoBarrier.instance.NeedsPrepPhase) {
CoCoBarrier.ChangePhase(CoCoBarrier.Phase.Prep,false,true);
}
timingAfterPrep = Environment.TickCount;
CoCoBarrier.ChangePhase(CoCoBarrier.Phase.Copy,true,true);
numCopied+=CoCoBarrier.instance.Copy();
CoCoBarrier.ChangePhase(CoCoBarrier.Phase.Fixup,true,true);
AddCollectionRequest();
// wait for a complete collector cycle. This is for fixup.
if (fDebug) {
VTable.DebugPrint("+++++ Fixup: Waiting to start tracing\n");
}
lock (interlock) {
while (!didStartTrace && !die) {
Monitor.Wait(interlock);
}
if (die) {
return;
}
didStartTrace=false;
inFixUp=true;
Monitor.PulseAll(interlock);
}
if (fDebug) {
VTable.DebugPrint("+++++ Fixup: Waiting to end tracing\n");
}
lock (interlock) {
while (!didEndTrace && !die) {
Monitor.Wait(interlock);
}
if (die) {
return;
}
didEndTrace=false;
doingCoCo=false;
inFixUp=false;
Monitor.PulseAll(interlock);
}
timingAfter = Environment.TickCount;
CoCoBarrier.ChangePhase(CoCoBarrier.Phase.Idle,false,false);
if (fDebug) {
VTable.DebugPrint("+++++ Finish Concurrent Copying\n");
}
pinTime+=(timingAfterPin-timingBefore);
prepTime+=(timingAfterPrep-timingAfterPin);
copyTime+=(timingAfterCopy-timingAfterPrep);
forwardTime+=(timingAfter-timingAfterCopy);
cycles++;
}
}
internal class MarkAndForwardReferenceVisitor : MarkReferenceVisitor
{
[NoBarriers]
ulong cnt;
[NoBarriers]
bool amMarkingForCoCo;
[NoBarriers]
UIntPtr spaceOverhead;
internal override void Init()
{
base.Init();
cnt=0;
amMarkingForCoCo=(markingForCoCo!=0);
spaceOverhead=UIntPtr.Zero;
if (fDebug && amMarkingForCoCo) {
VTable.DebugPrint("+++++ Marking For CoCo\n");
}
}
internal override void Cleanup()
{
base.Cleanup();
if (spaceOverhead>maxSpaceOverhead) {
maxSpaceOverhead=spaceOverhead;
}
if (amMarkingForCoCo) {
markingForCoCo=0;
#if !ARM && !ISA_ARM
// HACK: MemoryBarrier is unimplemented in ARM
// and causes compile-time failures when building
// mscorlib in sepcomp mode. This change will break
// CoCo if built with ARM.
Thread.MemoryBarrier();
#endif
}
if (fDebug) {
VTable.DebugPrint(" $$$ tagged ");
VTable.DebugPrint(cnt);
VTable.DebugPrint(" objects\n");
}
}
[NoInline]
internal unsafe void ForwardAndVisit(UIntPtr *loc)
{
UIntPtr addr=*loc;
if (addr==UIntPtr.Zero) {
return;
}
UIntPtr forward=
CoCoBarrier.instance.ToSpaceImplNonNull(Magic.fromAddress(addr));
if (forward != addr) {
CoCoBarrier.CAS(loc,forward,addr);
// if this CAS fails it means that someone stored
// a different pointer into the field, but in that
// case the pointer stored would have already been
// forwarded thanks to the CoCo write barrier.
}
VisitValueNonNull(forward);
}
[Inline]
protected override unsafe
void Filter(UIntPtr *location, ref ObjectDescriptor objDesc)
{
this.Visit(location);
}
/// <summary>
/// Visit an object reference.
/// </summary>
[Inline]
internal unsafe override void Visit(UIntPtr *loc)
{
if (CoCoBarrier.forwarding) {
ForwardAndVisit(loc);
} else {
VisitValueMaybeNull(*loc);
}
}
// This method simply forces the compiler to generate a copy
// of VisitReferenceFieldsTemplate in this class.
[ManualRefCounts]
[Inline]
internal override UIntPtr VisitReferenceFields(Object obj)
{
return this.VisitReferenceFields(Magic.addressOf(obj),
obj.vtable);
}
// This method simply forces the compiler to generate a copy
// of VisitReferenceFieldsTemplate in this class.
[ManualRefCounts]
[Inline]
internal override
UIntPtr VisitReferenceFields(UIntPtr objectBase, VTable vtable)
{
ObjectDescriptor objDesc =
new ObjectDescriptor(vtable, objectBase);
return VisitReferenceFieldsTemplate(ref objDesc);
}
[NoInline]
//[NoBarriers]
void ProcessObjectsSlow(ref ThreadHeaderQueue.LocalList workList)
{
while (!ConcurrentMSCollector.killCollectorThreads
&& !workList.IsEmpty()) {
// Pop the next value
Object obj = workList.Pop(markedColor);
if (fVerbose) {
VTable.DebugPrint("cms popped: ");
VTable.DebugPrint((ulong)Magic.addressOf(obj));
VTable.DebugPrint("\n");
}
// let CoCo do some stuff
ScanHook(obj);
// Visit Fields
this.VisitReferenceFields(obj);
}
}
//[NoBarriers]
internal override
void ProcessMyGrayObjects(ref ThreadHeaderQueue.LocalList workList)
{
if (amMarkingForCoCo) {
ProcessObjectsSlow(ref workList);
} else {
// hand-inlined from ConcurrentMSCollector. needed
// to ensure that the VisitReferenceFields call gets
// inlined.
while (!ConcurrentMSCollector.killCollectorThreads
&& !workList.IsEmpty()) {
// Pop the next value
Object obj = workList.Pop(markedColor);
if (CoCoBarrier.fVerifyToSpaceMark &&
!CoCoBarrier.instance.IsInToSpace(Magic.addressOf(obj))) {
VTable.DebugBreak();
}
// Visit Fields
this.VisitReferenceFields(obj);
}
}
}
internal unsafe void ScanHook(Object obj)
{
UIntPtr page=PageTable.Page(Magic.addressOf(obj));
if (PageTable.Type(page)!=SegregatedFreeList.SMALL_OBJ_PAGE) {
//VTable.DebugPrint(" not tagging because this isn't a small object page");
return;
}
SegregatedFreeList.PageHeader *ph=
(SegregatedFreeList.PageHeader*)PageTable.PageAddr(page);
if (!new CoCoPageUserValue(ph->userValue).Marked) {
//VTable.DebugPrint(" not tagging because the page isn't marked\n");
return;
}
if (obj is EMU ||
obj is Monitor ||
obj is Thread ||
obj is ThreadHeaderQueue) {
CoCoBarrier.NotifyPin(Magic.addressOf(obj));
if (fVerbose) {
VTable.DebugPrint(" $$ not tagging object because it's a monitor or EMU\n");
}
return;
}
if (doingCoCo) {
//VTable.DebugPrint(" not tagging object because doingCoCo\n");
return;
}
if (!CoCoBarrier.instance.ObjectIsNotCopied(obj)) {
if (fVerbose) {
VTable.DebugPrint(" not tagging object because object is already in the process of being copied.\n");
}
return;
}
if (fVerbose && obj.GetType() != typeof(Object)) {
VTable.DebugPrint(" $$ tagging a non-System.Object; type is ");
VTable.DebugPrint(obj.GetType().Name);
VTable.DebugPrint("\n");
}
// REVIEW: I wish that there was an easier way of
// doing this.
Object copy;
if (obj is Array) {
Array a=(Array)obj;
if (a.IsVector) {
copy=GC.AllocateVector(a.vtable,a.Length);
} else {
copy=GC.AllocateArray(a.vtable,a.Rank,a.Length);
}
} else if (obj is String) {
String s=(String)obj;
// REVIEW: this is not nice.
copy=GC.AllocateString(s.ArrayLength-1);
} else {
copy=GC.AllocateObject(obj.vtable);
}
VTable.Assert(ObjectLayout.Sizeof(copy)
== ObjectLayout.Sizeof(obj),
"Copy is not same size as original");
spaceOverhead+=ObjectLayout.Sizeof(copy);
bool first=!CoCoBarrier.instance.AnyTaggedForCopying;
UIntPtr thisSpaceOverhead;
if (CoCoBarrier.instance.TagObjectForCopy(obj,copy,
out thisSpaceOverhead)) {
cnt++;
if (first) {
lock (interlock) {
if (!wantCoCo && !doingCoCo) {
wantCoCo=true;
}
}
}
}
spaceOverhead+=thisSpaceOverhead;
}
}
internal class UpdateAndForwardReferenceVisitor: UpdateReferenceVisitor
{
internal override UIntPtr ForwardIfNecessary(UIntPtr addr)
{
return CoCoBarrier.ToSpaceAsPtr(addr);
}
}
internal class TaggingPartialFreePageVisitor: SegregatedFreeList.PartialFreePageVisitor
{
UIntPtr cnt;
UIntPtr unmarkedCnt;
UIntPtr delayedCnt;
ulong emptyCnt;
bool delay;
internal override void Start()
{
cnt=UIntPtr.Zero;
unmarkedCnt=UIntPtr.Zero;
delayedCnt=UIntPtr.Zero;
emptyCnt=0;
if (fDebug) {
VTable.DebugPrint(" $$$ delayCnt = ");
VTable.DebugPrint((ulong)delayCnt);
VTable.DebugPrint(", markingForCoCo = ");
VTable.DebugPrint((ulong)markingForCoCo);
VTable.DebugPrint("\n");
}
if (delayCnt==0) {
delay=(markingForCoCo!=0); // delay if there are still pages marked
} else {
delayCnt--;
delay=true;
}
if (fDebug) {
VTable.DebugPrint(" $$$ delayCnt = ");
VTable.DebugPrint((ulong)delayCnt);
VTable.DebugPrint(", markingForCoCo = ");
VTable.DebugPrint((ulong)markingForCoCo);
VTable.DebugPrint(", delay = ");
VTable.DebugPrint(delay?"yes":"no");
VTable.DebugPrint("\n");
}
}
internal override void Finish()
{
lastSmallPagesRecycle=SegregatedFreeList.SmallPages;
if (fDebug) {
VTable.DebugPrint(" $$$ marked ");
VTable.DebugPrint((ulong)cnt);
VTable.DebugPrint(", unmarked ");
VTable.DebugPrint((ulong)unmarkedCnt);
VTable.DebugPrint(", delayed ");
VTable.DebugPrint((ulong)delayedCnt);
VTable.DebugPrint(", and freed ");
VTable.DebugPrint(emptyCnt);
VTable.DebugPrint(".\n");
}
if (cnt!=0) {
delayCnt=cocoDelay;
markingForCoCo=1;
#if !ARM && !ISA_ARM
// HACK: MemoryBarrier is unimplemented in ARM
// and causes compile-time failures when building
// mscorlib in sepcomp mode. This change will break
// CoCo if built with ARM.
Thread.MemoryBarrier();
#endif
}
}
internal override void ObserveEmptyPage()
{
emptyCnt++;
}
internal override unsafe SegregatedFreeList.PartialPageAction
VisitPage(UIntPtr page,
SegregatedFreeList.PageHeader *ph,
int cells)
{
if (fDebugPage) {
VTable.DebugPrint(" $$ visiting page.\n");
}
CoCoPageUserValue v=new CoCoPageUserValue(ph->userValue);
if (inFixUp && v.Marked) {
unmarkedCnt++;
v.Marked=false;
if (v.Pinned) {
v.Pinned=false;
v.Version=pinPenalty; /* use the Version to delay
any future evacuation
attempts for this page */
}
} else if (!doingCoCo && !delay) {
if (v.Version>=1) {
// this page has a non-zero Version - this means
// that any attempts to evacuate it should be delayed.
v.Version--;
delayedCnt++;
if (fDebugPage) {
VTable.DebugPrint(" $$ page had non-zero version.\n");
}
} else {
if (fDebugPage) {
VTable.DebugPrint(" $$ cnt = ");
VTable.DebugPrint((ulong)cnt);
VTable.DebugPrint(", lastSmallPagesRecycle = ");
VTable.DebugPrint((ulong)lastSmallPagesRecycle);
VTable.DebugPrint(", sizeFracLim = ");
VTable.DebugPrint((ulong)sizeFracLim);
VTable.DebugPrint(", sizeLim = ");
VTable.DebugPrint((ulong)sizeLim);
VTable.DebugPrint(", PageSize = ");
VTable.DebugPrint((ulong)PageTable.PageSize);
VTable.DebugPrint(", freeCount = ");
VTable.DebugPrint((ulong)ph->freeCount);
VTable.DebugPrint(", pageFragThres = ");
VTable.DebugPrint((ulong)pageFragThres);
VTable.DebugPrint(", cells = ");
VTable.DebugPrint((ulong)cells);
VTable.DebugPrint("\n");
}
if ((long)cnt < ((long)lastSmallPagesRecycle
/(long)sizeFracLim)
&& (sizeLim<0 ||
cnt*PageTable.PageSize < (UIntPtr)sizeLim)
&& ph->freeCount*pageFragThres >= cells) {
cnt++;
v.Marked=true;
}
}
} else {
if (fDebugPage) {
VTable.DebugPrint(" $$ not doing anything about page.\n");
}
}
ph->userValue=v.Bits;
if (v.Marked) {
return SegregatedFreeList.PartialPageAction.CommitFull;
} else {
return SegregatedFreeList.PartialPageAction.CommitFree;
}
}
}
internal class StackForwardReferenceVisitor : ConcurrentMSCollector.StackMarkReferenceVisitor
{
internal override unsafe void ProcessObjectPtr(UIntPtr realPtr,
UIntPtr *loc,
UIntPtr addr)
{
UIntPtr forward=CoCoBarrier.ToSpaceAsPtr(realPtr);
if (forward!=realPtr) {
*loc=addr-realPtr+forward;
}
markReferenceVisitor.VisitValueAnyThreadMaybeNull(forward);
}
}
internal class StackNopReferenceVisitor : NonNullReferenceVisitor
{
[Inline]
internal override unsafe void Visit(UIntPtr *location)
{
// do nothing
}
}
internal class StackMarkPinnedReferenceVisitor : ConcurrentMSCollector.StackMarkReferenceVisitor
{
internal override unsafe void ProcessObjectPtr(UIntPtr realPtr,
UIntPtr *loc,
UIntPtr addr)
{
// FIXME: DoPin() already does FindObjectForInteriorPtr
CoCoBarrier.instance.DoPin(realPtr,CoCoBarrier.Pinner.StackScan);
}
}
private static ulong cyclesStarted;
private static ulong cycles;
private static bool fVerbose { get { return false; } }
private static bool fDebug { get { return false; } }
private static bool fDebugPage { get { return false; } }
}
}