// // 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 Microsoft.Bartok.Runtime; using Microsoft.Bartok.Options; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; using MarkingPhase = CMSMarking.MarkingPhase; internal unsafe abstract class CoCoBarrier: Barrier { // hook-ins to the system. maybe this could be redone so that // it is more orthogonal. internal bool BigEndian { get { return false; /* BUGBUG */ } } [NoBarriers] internal static new void Initialize() { // preWriteFieldsVisitor = new PreWriteFieldsVisitor(); LowAbortCoCoBarrier.preWriteFieldsVisitor = (PreWriteFieldsVisitor) BootstrapMemory.Allocate(typeof(PreWriteFieldsVisitor)); } internal static UIntPtr GCFieldOffset; internal static UIntPtr vtableFieldOffset; private static MarkingPhase CurrentMarkingPhase { [NoStackLinkCheck] get { return CMSMarking.CurrentMarkingPhase; } } // wrappers - used for profiling internal static UIntPtr CAS(UIntPtr *loc, UIntPtr newValue, UIntPtr comparand) { UIntPtr result= Interlocked.CompareExchange(loc, newValue, comparand); if (fProfileCAS && result != comparand) { numCASFailed++; } return result; } [NoBarriers] internal static UIntPtr CAS(ref UIntPtr loc, UIntPtr newValue, UIntPtr comparand) { UIntPtr result= Interlocked.CompareExchange(ref loc, newValue, comparand); if (fProfileCAS && result != comparand) { numCASFailed++; } return result; } [NoBarriers] internal static int CAS(ref int loc, int newValue, int comparand) { int result= Interlocked.CompareExchange(ref loc, newValue, comparand); if (fProfileCAS && result != comparand) { numCASFailed++; } return result; } internal static int CAS(int *loc, int newValue, int comparand) { int result= Interlocked.CompareExchange(loc, newValue, comparand); if (fProfileCAS && result != comparand) { numCASFailed++; } return result; } internal static long CAS(long *loc, long newValue, long comparand) { long result=Interlocked.CompareExchange(loc, newValue, comparand); if (fProfileCAS && result != comparand) { numCASFailed++; } return result; } [NoBarriers] internal static Object CAS(ref Object loc, Object newValue, Object comparand) { Object result = Interlocked.CompareExchange(ref loc, newValue, comparand); if (fProfileCAS && result != comparand) { numCASFailed++; } return result; } // hacks [NoInline] internal static float IntToFloatBits(uint x) { return *(float*)Magic.toPointer(ref x); } [NoInline] internal static double LongToDoubleBits(ulong x) { return *(double*)Magic.toPointer(ref x); } [NoInline] internal static uint FloatToIntBits(float x) { return *(uint*)Magic.toPointer(ref x); } [NoInline] internal static ulong DoubleToLongBits(double x) { return *(ulong*)Magic.toPointer(ref x); } internal enum Phase { Idle = 0, Prep = 1, Copy = 2, Fixup = 3 } internal static Phase phase; internal static bool forwarding; internal static bool pinning; // these force thingies are for profiling internal static bool forceSlow; internal static bool forceNotIdle; internal static bool forceForwarding; internal static bool forcePinning; internal static bool isNotIdle; // The value of this variable is // !((phase == Phase.Idle && !forwarding && !pinning) && !forceSlow) // The purposes of this variable is to take advantage of the default // initialization of boolean variables to false. In this case, this // allows us to use the fast-path version upon startup. private static bool dontAllowFastPath; internal static bool allowFastPath { get { return !dontAllowFastPath; } set { dontAllowFastPath = !value; } } [MixinConditional("CoCo")] [MixinConditional("AllThreadMixins")] [Mixin(typeof(Thread))] public sealed class CoCoThread : Object { internal bool readyForCoCo; internal int acknowledgedPhase; internal bool acknowledgedForwarding; internal bool acknowledgedPinning; internal bool pinnedOut; internal ulong phaseVersion; // useful for detecting transitions } internal static CoCoThread MixinThread(Thread t) { return (CoCoThread) (Object) t; } internal static ulong CurThreadPhaseVersion { get { if (Thread.CanGetCurThread) { return MixinThread(Thread.CurrentThread).phaseVersion; } else { return 0; } } } internal static bool inited; internal static Object interlock; // NOTE: this code uses ForceInline instead of Inline to indicate that // inlining should occur even if the caller is huge. In general, this // attribute should be used with great care. DO NOT USE IT ELSEWHERE IN // THE RUNTIME UNLESS YOU ARE WILLING TO DOCUMENT YOUR USE IN // IrSimpleInliner.cs AND Attributes.cs! AND NEVER USE IT IN // APPLICATION OR OS CODE! internal static bool NeedsTargetBarrier { [ForceInline] get { return CurrentMarkingPhase >= MarkingPhase.ComputingRoots; } } internal static bool NeedsSourceBarrier { [ForceInline] get { return CurrentMarkingPhase == MarkingPhase.ComputingRoots; } } internal static bool NeedsTargetOrSourceBarrier { [ForceInline] get { return CurrentMarkingPhase >= MarkingPhase.ComputingRoots; } } [AssertDevirtualize] internal abstract bool IsInToSpace(UIntPtr ptr); internal static void Mark(UIntPtr ptr) { if (CMSMarking.MarkIfNecessary(ptr) && fVerifyToSpaceMark && ptr!=UIntPtr.Zero && !instance.IsInToSpace(ptr)) { VTable.DebugBreak(); } } internal static void TargetBarrierWithForward(UIntPtr value) { if (NeedsTargetBarrier) { Mark(ToSpaceAsPtr(value)); } } internal static void TargetBarrierNoForward(UIntPtr value) { if (NeedsTargetBarrier) { Mark(value); } } // source barrier needs forwarding during the fixup phase, where // the stacks will have from-space references but we want the collector // to mark to-space references. internal static void SourceBarrierWithForward(UIntPtr value) { if (NeedsSourceBarrier) { Mark(ToSpaceAsPtr(value)); } } internal static void SourceBarrierNoForward(UIntPtr value) { if (NeedsSourceBarrier) { Mark(value); } } [NoInline] [CalledRarely] internal static void TargetAndSourceBarrierNoForwardSlow(UIntPtr *target, UIntPtr source) { Mark(*target); if (NeedsSourceBarrier) { Mark(source); } } [ForceInline] internal static void TargetAndSourceBarrierNoForward(UIntPtr *target, UIntPtr source) { if (NeedsTargetOrSourceBarrier) { TargetAndSourceBarrierNoForwardSlow(target, source); } } [CalledRarely] internal static void TargetWithForwardAndSourceNoForwardBarrierSlow(UIntPtr *target, UIntPtr source) { Mark(ToSpaceAsPtr(*target)); if (NeedsSourceBarrier) { Mark(source); } } [ForceInline] internal static void TargetWithForwardAndSourceNoForwardBarrier(UIntPtr *target, UIntPtr source) { if (NeedsTargetOrSourceBarrier) { TargetWithForwardAndSourceNoForwardBarrierSlow(target, source); } } [CalledRarely] [NoBarriers] internal static void TargetWithForwardAndSourceNoForwardBarrierSlow(ref Object target, Object source) { Mark(ToSpaceAsPtr(target)); if (NeedsSourceBarrier) { Mark(Magic.addressOf(source)); } } [ForceInline] internal static void TargetWithForwardAndSourceNoForwardBarrier(ref Object target, Object source) { if (NeedsTargetOrSourceBarrier) { TargetWithForwardAndSourceNoForwardBarrierSlow(ref target, source); } } internal static UIntPtr CoCoWordOffset; internal static bool IgnoreOffset(UIntPtr offset) { return !inited || offset == GCFieldOffset || offset == vtableFieldOffset || offset == CoCoWordOffset; } // for this object, does this offset represent an internally used, directly // accessed (without barriers) immutable entity? internal static bool InternalImmutableOffset(Object o, UIntPtr offset) { if (o is Array) { return (offset - vtableFieldOffset) < (UIntPtr)(o.vtable.baseLength - PreHeader.Size); } else { return offset == vtableFieldOffset; } } internal abstract void InitLateStub(); // call when the heap is inited internal static void InitLate() { if (fDebug) { VTable.DebugPrint("CoCo: in InitLate\n"); } interlock=new Object(); MultiUseWord.GetMonitor(interlock); MixinThread(Thread.CurrentThread).readyForCoCo=true; // REVIEW: this is just offensive GCFieldOffset = (UIntPtr)Magic.toPointer(ref ThreadHeaderQueue.MixinObject(instance).preHeader.link) - Magic.addressOf(instance); vtableFieldOffset = (UIntPtr)instance.VTableFieldAddr - Magic.addressOf(instance); instance.InitLateStub(); inited=true; } internal static void ForceSlow() { VTable.DebugPrint("using FORCE SLOW\n"); allowFastPath = false; forceSlow = true; } internal static void ForceNotIdle() { VTable.DebugPrint("using FORCE NOT IDLE\n"); isNotIdle = true; forceNotIdle = true; } internal static void ForceForwarding() { VTable.DebugPrint("using FORCE FORWARDING\n"); forwarding = true; forceForwarding = true; } internal static void ForcePinning() { VTable.DebugPrint("using FORCE PINNING\n"); pinning = true; forcePinning = true; } internal static Thread debugThread; internal static bool DebugThread { get { if (Thread.CanGetCurThread) { if (debugThread==null) { debugThread=Thread.CurrentThread; } return debugThread==Thread.CurrentThread; } else { return true; } } } private static UIntPtr interlockAddr, forwardedInterlockAddr; internal static void ClientHandshake() { if (inited) { if (fVerbose) { VTable.DebugPrint(" !! ClientHandshake: interlock at "); VTable.DebugPrint((ulong)Magic.addressOf(interlock)); VTable.DebugPrint("\n"); } if (fVerbose) { if (Magic.addressOf(interlock)!=interlockAddr) { VTable.DebugPrint(" !! ClientHandshake seeing interlock at new address: "); VTable.DebugPrint((ulong)Magic.addressOf(interlock)); VTable.DebugPrint("\n"); } if (ToSpaceAsPtr(interlock)!=forwardedInterlockAddr) { VTable.DebugPrint(" !! ClientHandshake seeing interlock at new FORWARDED address: "); VTable.DebugPrint((ulong)ToSpaceAsPtr(interlock)); VTable.DebugPrint("\n"); } } lock (interlock) { if (fVerbose) { interlockAddr=Magic.addressOf(interlock); forwardedInterlockAddr=ToSpaceAsPtr(interlock); } CoCoThread t=MixinThread(Thread.CurrentThread); if (phase!=(Phase)t.acknowledgedPhase || forwarding!=t.acknowledgedForwarding || pinning!=t.acknowledgedPinning) { if (fDebug) { VTable.DebugPrint(" !! thread "); VTable.DebugPrint((ulong)Magic.addressOf(t)); VTable.DebugPrint(" doing ack\n"); } t.acknowledgedPhase=(int)phase; t.acknowledgedForwarding=forwarding; t.acknowledgedPinning=pinning; t.phaseVersion++; Monitor.PulseAll(interlock); } } } } internal static bool ExchangeReadyForCoCo(bool value) { CoCoThread t=MixinThread(Thread.CurrentThread); bool result=t.readyForCoCo; t.readyForCoCo=value; return result; } internal static void ThreadStart(Thread t_) { if (inited) { lock (interlock) { CoCoThread t = MixinThread(t_); t.readyForCoCo=true; t.acknowledgedPhase=(int)phase; t.acknowledgedForwarding=forwarding; t.acknowledgedPinning=pinning; } } } // must have some manner of handshake after this internal static void EnablePinning() { if (fDebug) { VTable.DebugPrint(" --> CoCo enabling pinning "); } pinning = true; SetAllowFastPath(); } internal static void SetAllowFastPath() { allowFastPath = (phase == Phase.Idle && !forwarding && !pinning) && !forceSlow; } internal static void ChangePhase(Phase phase_, bool forwarding_, bool pinning_) { if (fDebug) { VTable.DebugPrint(" --> CoCo going to "); switch (phase_) { case Phase.Idle: VTable.DebugPrint("Idle"); break; case Phase.Prep: VTable.DebugPrint("Prep"); break; case Phase.Copy: VTable.DebugPrint("Copy"); break; case Phase.Fixup: VTable.DebugPrint("Fixup"); break; default: VTable.NotReached(); break; } VTable.DebugPrint(" (with"); if (!forwarding_) { VTable.DebugPrint("out"); } VTable.DebugPrint(" forwarding, with"); if (!pinning_) { VTable.DebugPrint("out"); } VTable.DebugPrint(" pinning)\n"); } lock (interlock) { phase = phase_; forwarding = forwarding_ || forceForwarding; pinning = pinning_ || forcePinning; SetAllowFastPath(); isNotIdle = phase != Phase.Idle || forceNotIdle; CoCoThread t = MixinThread(Thread.CurrentThread); t.acknowledgedPhase=(int)phase; t.acknowledgedForwarding=forwarding; t.acknowledgedPinning=pinning; t.phaseVersion++; Monitor.PulseAll(interlock); for (;;) { bool needToWait=false; bool doPulseAll=false; for (int i = 0; i < Thread.threadTable.Length; ++i) { Thread t_=Thread.threadTable[i]; if (t_==null) { continue; } t = MixinThread(t_); if (Transitions.InDormantState(i) || !t.readyForCoCo || t.pinnedOut) { t.acknowledgedPhase = (int)phase; t.acknowledgedForwarding = forwarding; t.acknowledgedPinning = pinning; } if (t.pinnedOut && phase==Phase.Idle) { t.pinnedOut=false; doPulseAll=true; } if ((Phase)t.acknowledgedPhase != phase || t.acknowledgedForwarding != forwarding || t.acknowledgedPinning != pinning) { if (fDebug) { VTable.DebugPrint(" !! thread "); VTable.DebugPrint((ulong)Magic.addressOf(t)); VTable.DebugPrint(" not ack\n"); } needToWait = true; } } if (doPulseAll) { Monitor.PulseAll(interlock); } if (!needToWait) { break; } // REVIEW: make the timeout less than 500 ms Monitor.Wait(interlock, 500); } } } internal static bool IsIdle { [ForceInline] get { return !isNotIdle; } } // Given a pointer to an object or into the PostHeader or payload // parts of an object, return the address of the object. internal static UIntPtr FindObjectForInteriorPtr(UIntPtr addr) { UIntPtr result; if (SegregatedFreeList.IsGcPtr(addr)) { result = GC.installedGC.FindObjectAddr(addr); } else { result = UIntPtr.Zero; } return result; } // Given a pointer to an object or into the PreHeader, PostHeader, // or payload parts, return the address of the object. A pointer // past the last element of an object or array, which is considered // a valid "user-level" interior pointer into the object, may be // considered an interior pointer into the subsequent object in // memory, so this method is only to be used to translate from // real field addresses to object addresses. internal static UIntPtr FindObjectForPreInteriorPtr(UIntPtr addr) { return FindObjectForInteriorPtr(addr + PreHeader.Size); } [ForceInline] internal static bool StrictlyAllowFastPath(int mask) { return (mask & BarrierMask.PathSpec.AllowFast)!=0; } [ForceInline] internal static bool AllowIdleFastPath(int mask) { if ((mask & BarrierMask.PathSpec.UseMask)!=0) { return (mask & BarrierMask.PathSpec.AllowFast)!=0; } else { return IsIdle; } } [ForceInline] internal static bool AllowFastPath(int mask) { if ((mask & BarrierMask.PathSpec.UseMask)!=0) { return (mask & BarrierMask.PathSpec.AllowFast)!=0; } else { return allowFastPath; } } [ForceInline] internal static bool AllowPinFastPath(int mask) { if ((mask & BarrierMask.PathSpec.UseMask)!=0) { return (mask & BarrierMask.PathSpec.AllowFast)!=0; } else { return !pinning; } } [NoBarriers] [TrustedNonNull] internal static CoCoBarrier instance; internal CoCoBarrier() { } [ForceInline] protected override bool AllowFastPathImpl() { return allowFastPath; } internal abstract bool ObjectIsNotCopied(Object o); internal abstract UIntPtr ToSpaceImplBeforeReadyNonNull(Object o); internal enum Pinner { StackScan, Barrier } [AssertDevirtualize] internal abstract UIntPtr DoPin(UIntPtr address, Pinner pinner); [Inline] [NoBarriers] internal static UIntPtr ToSpaceBeforeReadyImpl(Object o) { if (o==null) { return UIntPtr.Zero; } else { return instance.ToSpaceImplBeforeReadyNonNull(o); } } [AssertDevirtualize] internal abstract UIntPtr ToSpaceImplNonNull(Object o); [Inline] [NoBarriers] internal static UIntPtr ToSpaceImpl(Object o) { if (o==null) { return UIntPtr.Zero; } else { return instance.ToSpaceImplNonNull(o); } } [NoInline] [NoBarriers] [CalledRarely] internal static UIntPtr ToSpaceImplNoInline(Object o) { return ToSpaceImpl(o); } [Inline] internal static UIntPtr ToSpaceAsPtr(Object o) { return ToSpaceImpl(o); } [Inline] internal static UIntPtr ToSpaceAsPtr(UIntPtr addr) { return ToSpaceImpl(Magic.fromAddress(addr)); } [Inline] internal static Object ToSpaceAsObj(Object o) { return Magic.fromAddress(ToSpaceImpl(o)); } [Inline] internal static Object ToSpaceAsObj(UIntPtr addr) { return Magic.fromAddress(ToSpaceImpl(Magic.fromAddress(addr))); } [Inline] protected override Object WeakRefReadImpl(UIntPtr addr, int mask) { addr=ToSpaceAsPtr(addr); if (NeedsTargetBarrier) { Mark(addr); } return Magic.fromAddress(addr); } [Inline] protected override UIntPtr WeakRefWriteImpl(Object obj, int mask) { return ToSpaceAsPtr(obj); } // returns true if any objects have been tagged for copying. internal abstract bool AnyTaggedForCopying { get; } // collector calls this method to indicate the intent to copy a given // object. the to-space copy must already be allocated. no calls to // this method are allowed after the pin stack scan. return false // if tagging failed. spaceOverhead will hold the amount of space // overhead induced by tagging (or failing to tag), not counting the // to-space. internal abstract bool TagObjectForCopy(Object from, Object to, out UIntPtr spaceOverhead); internal abstract void PinningEnabledHook(); // returns true if the prep phase is needed. (if false then go directly // to copy.) internal abstract bool NeedsPrepPhase { get; } // returns true if any offset pointers (i.e. managed pointers into // objects) should result in pinning of the base object. internal abstract bool PinOffsetPointers { get; } // collector calls this method to request copying. can only be called // from the copy phase. returns number of objects actually copied. internal abstract ulong Copy(); // this is _just_ a notification - it doesn't pin the object, it's just // what we do if an object ends up being pinned. internal static void NotifyPin(UIntPtr objAddr) { UIntPtr page=PageTable.Page(objAddr); if (PageTable.Type(page)!=SegregatedFreeList.SMALL_OBJ_PAGE) { return; } SegregatedFreeList.PageHeader *ph= (SegregatedFreeList.PageHeader*)PageTable.PageAddr(page); CoCoPageUserValue v=new CoCoPageUserValue(ph->userValue); if (v.Marked) { v.Pinned=true; } ph->userValue=v.Bits; } internal static bool ShouldPin(UIntPtr objAddr) { UIntPtr page=PageTable.Page(objAddr); if (PageTable.Type(page)!=SegregatedFreeList.SMALL_OBJ_PAGE) { // in practice this won't be reached return true; } SegregatedFreeList.PageHeader *ph= (SegregatedFreeList.PageHeader*)PageTable.PageAddr(page); return new CoCoPageUserValue(ph->userValue).Pinned; } //////////////////// Pre write barriers //////////////////// // REVIEW: this isn't general enough. Currently it is // only used by CoCo, but it is probably of use to other // collectors. // // value = the old value at a location being overwritten [AssertDevirtualize] [ForceInline] protected virtual void TargetBarrierImpl(UIntPtr value) { Mark(value); } [TrustedNonNull] private static PreWriteFieldsVisitor preWriteFieldsVisitor; [Inline] [AssertDevirtualize] protected void PreWriteStruct(VTable vtable, UIntPtr dstPtr) { preWriteFieldsVisitor.VisitReferenceFields(vtable, dstPtr); } [Inline] [AssertDevirtualize] protected void PreWriteObject(Object dstObject) { preWriteFieldsVisitor.VisitReferenceFields(dstObject); } [Inline] [AssertDevirtualize] protected void PreWriteArray(Array array, int offset, int length) { preWriteFieldsVisitor .VisitReferenceFields(array.vtable, Magic.addressOf(array) + IndexedElementOffset(array, offset), length); } private class PreWriteFieldsVisitor : OffsetReferenceVisitor { // Pre struct copy internal void VisitReferenceFields(VTable vtable, UIntPtr dstPtr) { int postHeaderSize = PostHeader.Size; ObjectDescriptor objDesc = new ObjectDescriptor(vtable, dstPtr - postHeaderSize); VisitReferenceFieldsTemplate(ref objDesc); } // Pre object copy (already provided) //internal void VisitReferenceFields(Object dstObject) // Partial array copy or array zero internal void VisitReferenceFields(VTable vtable, UIntPtr dstElementPtr, int length) { ObjectDescriptor objDesc = new ObjectDescriptor(vtable, dstElementPtr); VisitReferenceFieldsTemplate(ref objDesc, length); } internal override void FieldOffset(UIntPtr offset, ref ObjectDescriptor objDesc) { UIntPtr *dstAddr = (UIntPtr *) (objDesc.objectBase + offset); ((LowAbortCoCoBarrier) Barrier.installedBarrier).TargetBarrierImpl(*dstAddr); } } internal static bool fCount { get { return true; } } internal static ulong numPins; internal static ulong numWaitPins; internal static ulong numPinWaits; internal static ulong numAtomics; internal static ulong numSlowCopyStructs; internal static ulong numSlowClones; internal static ulong numSlowArrayZeroes; internal static ulong numSlowArrayCopies; internal static ulong numTaintPins; internal static bool fProfileCAS { get { return true; } } internal static ulong numCASFailed; internal static void PrintStat(string name, ulong n) { VTable.DebugPrint(name); VTable.DebugPrint(" = "); VTable.DebugPrint(n); VTable.DebugPrint("\n"); } internal static void PrintStats() { if (fCount) { PrintStat("numPins", numPins); PrintStat("numWaitPins", numWaitPins); PrintStat("numPinWaits", numPinWaits); PrintStat("numAtomics", numAtomics); PrintStat("numSlowCopyStructs", numSlowCopyStructs); PrintStat("numSlowClones", numSlowClones); PrintStat("numSlowArrayZeroes", numSlowArrayZeroes); PrintStat("numSlowArrayCopies", numSlowArrayCopies); } if (fProfileCAS) { PrintStat("numCASFailed", numCASFailed); } } internal static bool fDebug { get { return false; } } internal static bool fVerboseNull { get { return false; } } internal static bool fVerboseCopy { get { return false; } } internal static bool fGorierCopy { get { return false; } } internal static bool fVerboseRead { get { return false; } } internal static bool fVerbose { get { return false; } } internal static bool fDebugFindObj { get { return false; } } internal static bool fVerifyToSpaceMark { get { return true; } } } }