//
// 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 System.Runtime.CompilerServices;
    using System.Threading;

#if SINGULARITY_KERNEL
    using Microsoft.Singularity;
#endif

    [NoCCtor]
    internal class SlidingCollector: GenerationalCollector {

        internal static SlidingCollector instance;

        // Visitor instances used for marking objects
        private static MarkReferenceVisitor markReferenceVisitor;
        private static RegisterThreadReference registerThreadReferenceVisitor;
        private static RegisterPinnedReference registerPinnedReferenceVisitor;
        private static UpdateThreadReference updateThreadReferenceVisitor;
        private static ForwardReferenceVisitor forwardReferenceVisitor;

        // Internally used data structures
        private UIntPtrQueue skippedPageQueue = new UIntPtrQueue();
        private UIntPtrQueue relocationQueue = new UIntPtrQueue();

      // WARNING: don't initialize any static fields in this class
      // without manually running the class constructor at startup!

        private SlidingCollector() {
        }

        public static new void Initialize() {
            GenerationalCollector.Initialize();
            // SlidingCollector.instance = new SlidingCollector();
            SlidingCollector.instance = (SlidingCollector)
                BootstrapMemory.Allocate(typeof(SlidingCollector));
            // markReferenceVisitor = new MarkReferenceVisitor();
            markReferenceVisitor = (MarkReferenceVisitor)
                BootstrapMemory.Allocate(typeof(MarkReferenceVisitor));
            // registerThreadReferenceVisitor = new RegisterThreadReference();
            registerThreadReferenceVisitor = (RegisterThreadReference)
                BootstrapMemory.Allocate(typeof(RegisterThreadReference));
            // registerPinnedReferenceVisitor = new RegisterPinnedReference();
            registerPinnedReferenceVisitor = (RegisterPinnedReference)
                BootstrapMemory.Allocate(typeof(RegisterPinnedReference));
            // updateThreadReferenceVisitor = new UpdateThreadReference();
            updateThreadReferenceVisitor = (UpdateThreadReference)
                BootstrapMemory.Allocate(typeof(UpdateThreadReference));
            // forwardReferenceVisitor = new ForwardReferenceVisitor();
            forwardReferenceVisitor = (ForwardReferenceVisitor)
                BootstrapMemory.Allocate(typeof(ForwardReferenceVisitor));
        }

        internal override void TruncateOlderAllocationAreas(int generation) {
            // We don't use any allocators for the older generation,
            // so there is nothing to do
        }

        internal override void CollectGeneration(int generation,
                                                 UIntPtr generationPageCount)
        {
            VTable.Assert(IsValidGeneration(generation));
            // 1) Mark the live objects
            CollectorStatistics.Event(GCEvent.TraceStart);
            MultiUseWord.PreGCHook(true /* use shadows */);
            Finalizer.PrepareCollectFinalizers();
            CallStack.ScanStacks(registerThreadReferenceVisitor,
                                 registerPinnedReferenceVisitor);
            registerThreadReferenceVisitor.ForwardReferences();
            if (generation < (int)MAX_GENERATION) {
                // These calls must be done early, as they rely on the
                // contents of Thread.threadTable being intact.
                installedRemSet.Clean();
                installedRemSet.Uniquify();
                this.ScanRemSet((PageType) generation);
            }
            // Process runtime data that is allocated from SystemMemory
            Thread.VisitBootstrapData(markReferenceVisitor);
#if SINGULARITY_KERNEL
            Kernel.VisitSpecialData(markReferenceVisitor);
#endif
            MultiUseWord.VisitStrongRefs(markReferenceVisitor,
                                         true /* use shadows */);
            StaticData.ScanStaticData(markReferenceVisitor);
            CollectorStatistics.Event(GCEvent.TraceSpecial);
            WeakReference.Process(forwardReferenceVisitor, false, true);
            Finalizer.ResurrectCandidates(forwardReferenceVisitor,
                                          markReferenceVisitor,
                                          false);
            markReferenceVisitor.Cleanup();
            // 2) Forward pointers and compact the live objects
            CollectorStatistics.Event(GCEvent.SweepStart, TotalMemory);
            WeakReference.Process(forwardReferenceVisitor, false, false);
            MultiUseWord.VisitWeakRefs(forwardReferenceVisitor,
                                       true /* use shadows */);
            UIntPtr oldAllocPtr;
            UIntPtr newLimit = this.ForwardReferences((PageType)generation,
                                                      out oldAllocPtr);
            this.CompactHeapObjects(oldAllocPtr);
#if SINGULARITY
            Thread.UpdateAfterGC();
#endif
            Thread currentThread = Thread.threadTable[collectorThreadIndex];
#if SINGULARITY_KERNEL
            Kernel.UpdateAfterGC(currentThread);
#endif
            CallStack.ScanStacks(updateThreadReferenceVisitor,
                                 updateThreadReferenceVisitor);
            this.CompactPhaseCleanup(currentThread,
                                     (PageType)generation,
                                     newLimit);
            // Resetting the GC state
            CollectorStatistics.Event(GCEvent.SweepSpecial);
            installedRemSet.Reset();
            MultiUseWord.PostGCHook();
            Finalizer.ReleaseCollectFinalizers();
            CollectorStatistics.Event(GCEvent.CollectionComplete, TotalMemory);
        }

        // Internal workings of the collector

        // Scan is large and will be inlined here.  This is a performance
        // workaround that performs manual specialization of the Scan method.
        [NoInline]
        private void ScanRemSet(PageType generation) {
            installedRemSet.Scan(markReferenceVisitor, generation);
        }


        // Routines for updating pointers to new locations of marked objects
        // No objects can be resurrected using this mechanism
        private class ForwardReferenceVisitor : NonNullReferenceVisitor {

            internal unsafe override void Visit(UIntPtr *loc) {
                UIntPtr addr = *loc;
                UIntPtr page = PageTable.Page(addr);
                PageType pageType = PageTable.Type(page);
                if (!PageTable.IsZombiePage(pageType)) {
                    VTable.Assert(PageTable.IsGcPage(pageType) ||
                                  PageTable.IsNonGcPage(pageType) ||
                                  PageTable.IsStackPage(pageType) ||
                                  PageTable.IsSharedPage(pageType));
                    return;
                }
                UIntPtr vtableAddr = Allocator.GetObjectVTable(addr);
                if ((vtableAddr & 0x1) == 0x1) {
                    // Link this field to be updated
                    *loc = vtableAddr;
                    Allocator.SetObjectVTable(addr, (UIntPtr) loc+1);
                } else {
                    // Zero the reference (not marked)
                    *loc = UIntPtr.Zero;
                }
            }
        }

        // Routines for marking objects and linking object references
        private class MarkReferenceVisitor : NonNullReferenceVisitor {

            internal unsafe override void Visit(UIntPtr *loc) {
                UIntPtr addr = *loc;
                UIntPtr page = PageTable.Page(addr);
                PageType pageType = PageTable.Type(page);
                if (!PageTable.IsZombiePage(pageType)) {
                    VTable.Assert(PageTable.IsGcPage(pageType) ||
                                  PageTable.IsNonGcPage(pageType) ||
                                  PageTable.IsStackPage(pageType) ||
                                  PageTable.IsSharedPage(pageType));
                    return;
                }
                UIntPtr vtableAddr = Allocator.GetObjectVTable(addr);
                // Mark object
                if (vtableAddr == UIntPtr.Zero) {
                    VTable.DebugPrint("Found null vtable in MarkReference (loc = 0x{0:x8}, addr = 0x{1:x8})\n",
                                      __arglist(((UIntPtr)loc), addr));
                    VTable.NotReached();
                }
                *loc = vtableAddr;
                Allocator.SetObjectVTable(addr, (UIntPtr) loc+1);
                // If first visit to the object, schedule visit of fields
                if ((vtableAddr & 0x1) == 0) {
                    MarkVisit(addr, vtableAddr & (UIntPtr) ~2U);
                }
            }

            private void MarkVisit(UIntPtr addr, UIntPtr vtableAddr) {
                // Mark scheduling
                if (fMarkInProgress) {
                    ScheduleMarkedObject(addr, vtableAddr);
                } else {
                    fMarkInProgress = true;
                    VTable vtable =
                        Magic.toVTable(Magic.fromAddress(vtableAddr));
                    this.VisitReferenceFields(addr, vtable);
                    this.ProcessScheduledObjects();
                    fMarkInProgress = false;
                }
            }

            private void ScheduleMarkedObject(UIntPtr addr,
                                              UIntPtr vtableAddr) {
                this.markQueue.Write(addr);
                this.markQueue.Write(vtableAddr);
            }

            private void ProcessScheduledObjects() {
                while (!this.markQueue.IsEmpty) {
                    UIntPtr addr = this.markQueue.Read();
                    UIntPtr vtableAddr = this.markQueue.Read();
                    VTable vtable =
                        Magic.toVTable(Magic.fromAddress(vtableAddr));
                    this.VisitReferenceFields(addr, vtable);
                }
            }

            internal void Cleanup() {
                VTable.Assert(this.markQueue.IsEmpty);
                this.markQueue.Cleanup(true);
            }

            private bool fMarkInProgress;
            private UIntPtrQueue markQueue = new UIntPtrQueue();

        }

        // Routines for linking and updating thread pointers
        private class RegisterThreadReference : NonNullReferenceVisitor {

            internal unsafe override void Visit(UIntPtr *loc) {
                UIntPtr addr = *loc;
                UIntPtr page = PageTable.Page(addr);
                PageType pageType = PageTable.Type(page);
                if (!PageTable.IsZombiePage(pageType)) {
                    VTable.Assert(PageTable.IsGcPage(pageType) ||
                                  PageTable.IsNonGcPage(pageType) ||
                                  PageTable.IsStackPage(pageType) ||
                                  PageTable.IsSharedPage(pageType));
                    return;
                }
                UIntPtr objectAddr = InteriorPtrTable.Find(addr);
                this.threadPtrQueue.Write(objectAddr);
                this.threadPtrQueue.Write(addr - objectAddr);
            }

            internal unsafe void ForwardReferences() {
                UIntPtrQueue.Enumerator queueEnumerator =
                    new UIntPtrQueue.Enumerator(ref this.threadPtrQueue);
                while (queueEnumerator.MoveNext()) {
                    markReferenceVisitor.Visit(queueEnumerator.CurrentAddr);
                    queueEnumerator.MoveNext();
                }
            }

            internal void Cleanup() {
                // Free up the last thread pointer page
                this.threadPtrQueue.Cleanup(true);
            }

            internal UIntPtrQueue threadPtrQueue = new UIntPtrQueue();

        }

        private class RegisterPinnedReference : NonNullReferenceVisitor {

            internal unsafe override void Visit(UIntPtr *loc) {
                UIntPtr addr = *loc;
                UIntPtr page = PageTable.Page(addr);
                PageType pageType = PageTable.Type(page);
                if (!PageTable.IsZombiePage(pageType)) {
                    VTable.Assert(PageTable.IsGcPage(pageType) ||
                                  PageTable.IsNonGcPage(pageType) ||
                                  PageTable.IsStackPage(pageType) ||
                                  PageTable.IsSharedPage(pageType));
                    return;
                }
                UIntPtr objectAddr = InteriorPtrTable.Find(addr);
                registerThreadReferenceVisitor.threadPtrQueue.Write(objectAddr);
                registerThreadReferenceVisitor.threadPtrQueue.Write(addr-objectAddr);
                *Allocator.GetObjectVTableAddress(objectAddr) |= (UIntPtr) 2U;
            }

        }

        private class UpdateThreadReference : NonNullReferenceVisitor {

            internal unsafe override void Visit(UIntPtr *loc) {
                UIntPtr addr = *loc;
                UIntPtr page = PageTable.Page(addr);
                if (!PageTable.IsZombiePage(PageTable.Type(page))) {
                    return;
                }
                UIntPtr objectAddr =
                    registerThreadReferenceVisitor.threadPtrQueue.Read();
                UIntPtr addressDelta =
                    registerThreadReferenceVisitor.threadPtrQueue.Read();
                *loc = objectAddr + addressDelta;
            }

        }

        // Reference updates and object relocation

        private unsafe UIntPtr ForwardReferences(PageType generation,
                                                 out UIntPtr oldAllocPtr)
        {
            VTable.Assert(IsValidGeneration((int)generation));

            UIntPtr destPage = UIntPtr.Zero;
            UIntPtr destCursor;
            UIntPtr destLimit;
            PageType destGeneration;
            if (generation < MAX_GENERATION) {
                destGeneration = generation + 1;
            } else {
                destGeneration = MAX_GENERATION;
            }
            destCursor = UIntPtr.Zero;
            destLimit = UIntPtr.Zero;
            oldAllocPtr = destCursor;
            UIntPtr runLength = UIntPtr.Zero;
            for (UIntPtr i=UIntPtr.Zero; i < PageTable.pageTableCount; i++) {
                if (!IsMyZombiePage(i)) {
                    continue;
                }
                UIntPtr deltaBytes = (UIntPtr) 0x80000000;
                UIntPtr sourceCursor = PageTable.PageAddr(i);
                do {
                    i++;
                } while (i < PageTable.pageTableCount && IsMyZombiePage(i));
                UIntPtr sourceLimit = PageTable.PageAddr(i);
                while (true) {
                    if (sourceCursor >= sourceLimit) {
                        break;
                    }
                    if (Allocator.IsAlignmentMarkerAddr(sourceCursor)) {
                        sourceCursor += UIntPtr.Size;
                        deltaBytes += UIntPtr.Size;
                        continue;
                    }
                    if (BumpAllocator.IsUnusedMarkerAddr(sourceCursor)) {
                        sourceCursor += UIntPtr.Size;
                        sourceCursor = PageTable.PagePad(sourceCursor);
                        deltaBytes = (UIntPtr) 0x80000000;
                        continue;
                    }
                    UIntPtr objectAddr = sourceCursor + PreHeader.Size;
                    UIntPtr vtableOrMarker =
                        Allocator.GetObjectVTable(objectAddr);
                    if (vtableOrMarker == UIntPtr.Zero) {
                        // We found the end of an allocation page
                        sourceCursor = PageTable.PagePad(sourceCursor);
                        deltaBytes = (UIntPtr) 0x80000000;
                        continue;
                    }
                    UIntPtr vtableAddr;
                    if ((vtableOrMarker & 1) != 0) {
                        UIntPtr temp = *(UIntPtr *) (vtableOrMarker - 1);
                        while ((temp & 1) != 0) {
                            temp = *(UIntPtr *) (temp-1);
                        }
                        VTable.Assert(PageTable.IsNonGcPage(PageTable.Type(PageTable.Page(temp))));
                        vtableAddr = temp;
                        if ((temp & 2) != 0) {
                            // Found pinned object
                            SkipDestinationAreas(ref destPage, destCursor,
                                                 ref destLimit,
                                                 sourceCursor);
                            deltaBytes -= (sourceCursor - destCursor);
                            destCursor = sourceCursor;
                            vtableAddr -= 2; // Remove "pinned" bit
                        }
                        Allocator.SetObjectVTable(objectAddr, vtableAddr);
                    } else {
                        vtableAddr = vtableOrMarker;
                    }
                    VTable vtable =
                        Magic.toVTable(Magic.fromAddress(vtableAddr));
                    UIntPtr objectSize =
                        ObjectLayout.ObjectSize(objectAddr, vtable);
                    VTable.Assert(objectSize > 0);
                    if ((vtableOrMarker & 1) != 0) {
                        if (GenerationalCollector.IsLargeObjectSize
                            (objectSize)) {
                            // Don't move large objects
                            SkipDestinationAreas(ref destPage,
                                                 destCursor,
                                                 ref destLimit,
                                                 sourceCursor);
                            UIntPtr localDelta =
                                sourceCursor - destCursor;
                            deltaBytes -= localDelta;
                            if (deltaBytes == UIntPtr.Zero &&
                                runLength != UIntPtr.Zero) {
                                runLength += localDelta;
                            }
                            destCursor = sourceCursor;
                            UIntPtr objLimit = sourceCursor + objectSize;
                            UIntPtr pageEndAddr = PageTable.PagePad(objLimit);
                            objectSize = (pageEndAddr - sourceCursor);
                        } else if (destCursor + objectSize > destLimit) {
                            UIntPtr oldDestCursor = destCursor;
                            FindDestinationArea(ref destPage,
                                                ref destCursor,
                                                ref destLimit,
                                                objectSize,
                                                destGeneration);
                            VTable.Assert(destCursor <= sourceCursor);
                            VTable.Assert(destCursor + objectSize <=
                                          destLimit);
                            deltaBytes -= (destCursor - oldDestCursor);
                        } else if (vtable.baseAlignment > UIntPtr.Size) {
                            uint alignmentMask = vtable.baseAlignment - 1;
                            int offset = PreHeader.Size + UIntPtr.Size;
                            while (((destCursor+offset) & alignmentMask) != 0) {
                                destCursor += UIntPtr.Size;
                                deltaBytes -= UIntPtr.Size;
                                if (deltaBytes == UIntPtr.Zero &&
                                    runLength != UIntPtr.Zero) {
                                    runLength += UIntPtr.Size;
                                }
                            }
                        }
                        if (runLength == UIntPtr.Zero ||
                            deltaBytes != UIntPtr.Zero) {
                            if (runLength != UIntPtr.Zero) {
                                RegisterRelocationEnd(runLength);
                            }
                            RegisterRelocationStart(sourceCursor,
                                                    destCursor);
                            deltaBytes = UIntPtr.Zero;
                            runLength = UIntPtr.Zero;
                        }
                        UIntPtr newObjectAddr = destCursor + PreHeader.Size;
                        do {
                            UIntPtr *ptrAddr = (UIntPtr*) (vtableOrMarker-1);
                            vtableOrMarker = *ptrAddr;
                            *ptrAddr = newObjectAddr;
                        } while ((vtableOrMarker & 1) != 0);
                        destCursor += objectSize;
                        runLength += objectSize;
                    } else {
                        deltaBytes += objectSize;
                        if (runLength != UIntPtr.Zero) {
                            RegisterRelocationEnd(runLength);
                        }
                        runLength = UIntPtr.Zero;
                    }
                    sourceCursor += objectSize;
                }
            }
            if (runLength != UIntPtr.Zero) {
                RegisterRelocationEnd(runLength);
            }
            return destCursor;
        }

        private unsafe void SkipDestinationAreas(ref UIntPtr destPage,
                                                 UIntPtr destCursor,
                                                 ref UIntPtr destLimit,
                                                 UIntPtr sourceCursor)
        {
            UIntPtr cursorPage = PageTable.Page(destCursor);
            UIntPtr sourcePage = PageTable.Page(sourceCursor);
            if (cursorPage != sourcePage) {
                UIntPtr destPageLimit = PageTable.PagePad(destCursor);
                if (destPageLimit != destCursor) {
                    cursorPage++;
                }
                VTable.Assert(PageTable.PageAligned(destLimit));
                UIntPtr limitPage = PageTable.Page(destLimit);
                while (destPage < sourcePage) {
                    if (cursorPage < limitPage) {
                        this.RegisterSkippedPages(cursorPage, limitPage);
                    }
                    do {
                        destPage++;
                    } while (!IsMyZombiePage(destPage));
                    cursorPage = destPage;
                    do {
                        destPage++;
                    } while (IsMyZombiePage(destPage));
                    limitPage = destPage;
                }
                destLimit = PageTable.PageAddr(limitPage);
                VTable.Assert(destPage > sourcePage);
                VTable.Assert(cursorPage <= sourcePage);
                if (cursorPage < sourcePage) {
                    this.RegisterSkippedPages(cursorPage, sourcePage);
                    cursorPage = sourcePage;
                }
                InteriorPtrTable.ClearFirst(cursorPage, destPage);
                InteriorPtrTable.SetFirst(sourceCursor + PreHeader.Size);
                if (GC.remsetType == RemSetType.Cards) {
                     OffsetTable.ClearLast(PageTable.PageAddr(cursorPage),
                           PageTable.PageAddr(destPage)-1);
                }                                
            }
        }

        private unsafe void FindDestinationArea(ref UIntPtr destPage,
                                                ref UIntPtr destCursor,
                                                ref UIntPtr destLimit,
                                                UIntPtr objectSize,
                                                PageType destGeneration)
        {
            VTable.Assert(IsValidGeneration((int)destGeneration));

            UIntPtr cursorPage = PageTable.Page(destCursor);
            UIntPtr limitPage = PageTable.Page(destLimit);
            UIntPtr pageAddr = PageTable.PagePad(destCursor);
            UIntPtr testPage = limitPage;
            UIntPtr endTestPage = PageTable.PageCount(destCursor+objectSize);
            if (destCursor > UIntPtr.Zero &&
                IsMyZombiePage(PageTable.Page(destCursor-1))) {
                VTable.Assert(destPage == limitPage);
                while (IsMyZombiePage(testPage) ||
                       (testPage < endTestPage &&
                        (PageTable.IsUnusedPage(testPage)))) {
                    testPage++;
                }
                if (testPage >= endTestPage) {
                    // We can expand the current region
                    endTestPage = testPage;
                    VTable.Assert(PageTable.PageAligned(destLimit));
                    InteriorPtrTable.ClearFirst(limitPage, testPage);
                    if (GC.remsetType == RemSetType.Cards) {
                        OffsetTable.ClearLast(PageTable.PageAddr(limitPage),
                           PageTable.PageAddr(testPage)-1);
                    }              
                    while (limitPage != endTestPage) {
                        VTable.Assert(PageTable.IsUnusedPage(destPage));
                        do {
                            destPage++;
                        } while (destPage < endTestPage &&
                                 PageTable.IsUnusedPage(destPage));
                        bool fCleanPages = true;
                        bool status =
                            PageManager.TryReserveUnusedPages(null, limitPage,
                                                              destPage-limitPage,
                                                              nurseryGeneration,
                                                              ref fCleanPages);
                        VTable.Assert(status);
                        MakeZombiePages(limitPage, destPage - limitPage,
                                        destGeneration);
                        while (destPage < endTestPage &&
                               IsMyZombiePage(destPage)) {
                            destPage++;
                        }
                        limitPage = destPage;
                    }
                    destLimit = PageTable.PageAddr(limitPage);
                    return;
                }
            }
            if (destCursor != pageAddr) {
                cursorPage++;
            }
            if (cursorPage != limitPage) {
                this.RegisterSkippedPages(cursorPage, limitPage);
            }
            // Find new region big enough to contain object
            UIntPtr neededPages = PageTable.PageCount(objectSize);
            UIntPtr prefixPage;
            while (true) {
                do {
                    destPage++;
                } while (!IsMyZombiePage(destPage));
                cursorPage = destPage;
                prefixPage = cursorPage;
                do {
                    destPage++;
                } while (IsMyZombiePage(destPage));
                limitPage = destPage;
                if (neededPages <= limitPage - cursorPage) {
                    break;
                }
                // Check for following unused pages
                endTestPage = cursorPage + neededPages;
                VTable.Assert(endTestPage <= PageTable.pageTableCount);
                while (destPage < endTestPage &&
                       (PageTable.IsUnusedPage(destPage) ||
                        (IsMyZombiePage(destPage)))) {
                    destPage++;
                }
                if (destPage == endTestPage) {
                    break;
                }
                // Check for preceding unused pages
                if (destPage >= neededPages) {
                    endTestPage = destPage - neededPages;
                    prefixPage = cursorPage - 1;
                    while (prefixPage >= UIntPtr.Zero &&
                           PageTable.IsUnusedPage(prefixPage)) {
                        prefixPage--;
                    }
                    prefixPage++;
                    if (prefixPage == endTestPage) {
                        break;
                    }
                }
                // Register any skipped regions of pages
                this.RegisterSkippedPages(cursorPage, limitPage);
                while (limitPage < destPage) {
                    VTable.Assert(PageTable.IsUnusedPage(limitPage));
                    do {
                        limitPage++;
                    } while (limitPage < destPage &&
                             PageTable.IsUnusedPage(limitPage));
                    cursorPage = limitPage;
                    while (limitPage < destPage && IsMyZombiePage(limitPage)) {
                        limitPage++;
                    }
                    if (cursorPage != limitPage) {
                        this.RegisterSkippedPages(cursorPage, limitPage);
                    }
                }
            }
            // We found an area big enough.  Commit the pre- and
            // postfix areas of unused pages
            if (prefixPage != cursorPage) {
                bool fCleanPages = true;
                bool status =
                    PageManager.TryReserveUnusedPages(null, prefixPage,
                                                      cursorPage-prefixPage,
                                                      nurseryGeneration,
                                                      ref fCleanPages);
                VTable.Assert(status);
                MakeZombiePages(prefixPage, cursorPage - prefixPage,
                                destGeneration);
            }
            while (destPage != limitPage) {
                // Mark the region of unused pages as fromspace
                UIntPtr unusedPage = limitPage;
                VTable.Assert(PageTable.IsUnusedPage(unusedPage));
                do {
                    unusedPage++;
                } while (unusedPage < destPage &&
                         PageTable.IsUnusedPage(unusedPage));
                bool fCleanPages = true;
                bool status =
                    PageManager.TryReserveUnusedPages(null, limitPage,
                                                      unusedPage-limitPage,
                                                      nurseryGeneration,
                                                      ref fCleanPages);
                VTable.Assert(status);
                MakeZombiePages(limitPage, unusedPage - limitPage,
                                destGeneration);
                // Skip any sections of pages already marked as fromspace
                limitPage = unusedPage;
                while (limitPage < destPage && IsMyZombiePage(limitPage)) {
                    limitPage++;
                }
            }
            destCursor = PageTable.PageAddr(prefixPage);
            destLimit = PageTable.PageAddr(limitPage);
            // Take ownership of the new pages
            InteriorPtrTable.ClearFirst(prefixPage, limitPage);
            InteriorPtrTable.SetFirst(destCursor + PreHeader.Size);
            if (GC.remsetType == RemSetType.Cards) {
                  OffsetTable.ClearLast(PageTable.PageAddr(prefixPage),
                        PageTable.PageAddr(limitPage)-1);
             }                            
        }

        private void RegisterSkippedPages(UIntPtr startPage,
                                          UIntPtr limitPage)
        {
            this.skippedPageQueue.Write(startPage);
            this.skippedPageQueue.Write(limitPage);
        }

        private void RegisterRelocationStart(UIntPtr sourceAddress,
                                             UIntPtr destinationAddress)
        {
            this.relocationQueue.Write(sourceAddress);
            this.relocationQueue.Write(destinationAddress);
        }

        private void RegisterRelocationEnd(UIntPtr runLength)
        {
            this.relocationQueue.Write(runLength);
        }

        private unsafe void CompactHeapObjects(UIntPtr previousEnd) {
            while (!this.relocationQueue.IsEmpty) {
                UIntPtr sourceAddress = this.relocationQueue.Read();
                UIntPtr destinationAddress = this.relocationQueue.Read();
                UIntPtr runLength = this.relocationQueue.Read();
                if (previousEnd != destinationAddress) {
                    VTable.Assert(previousEnd < destinationAddress);
                    if (PageTable.Page(destinationAddress) !=
                        PageTable.Page(previousEnd+PreHeader.Size)) {
                        if (!PageTable.PageAligned(previousEnd)) {
                            UIntPtr pageLimit = PageTable.PagePad(previousEnd);
                            BumpAllocator.WriteUnusedMarker(previousEnd);
                            previousEnd += UIntPtr.Size;
                            Util.MemClear(previousEnd,
                                          pageLimit - previousEnd);
                        }
                        if (!PageTable.PageAligned(destinationAddress)) {
                            // This only happens before pinned objects and
                            // large objects
                            UIntPtr start =
                                PageTable.PageAlign(destinationAddress);
                            VTable.Assert(previousEnd <= start);
                            while (start < destinationAddress) {
                                Allocator.WriteAlignment(start);
                                start += UIntPtr.Size;
                            }
                        }
                        UIntPtr objAddr = destinationAddress + PreHeader.Size;
                        InteriorPtrTable.SetFirst(objAddr);
                    } else {
                        VTable.Assert(previousEnd < destinationAddress);
                        UIntPtr start = previousEnd;
                        while (start < destinationAddress) {
                            Allocator.WriteAlignment(start);
                            start += UIntPtr.Size;
                        }
                    }
                }
                Util.MemCopy(destinationAddress, sourceAddress, runLength);
                previousEnd = destinationAddress + runLength;
            }
            // Zero out the end of the allocation page
            if (!PageTable.PageAligned(previousEnd)) {
                UIntPtr pageLimit = PageTable.PagePad(previousEnd);
                Util.MemClear(previousEnd, pageLimit - previousEnd);
            }
            this.relocationQueue.Cleanup(true);
        }

        private void CompactPhaseCleanup(Thread currentThread,
                                         PageType generation,
                                         UIntPtr newLimitPtr)
        {
            VTable.Assert(IsValidGeneration((int)generation));

            registerThreadReferenceVisitor.Cleanup();
            // Free up skipped pages
            while (!this.skippedPageQueue.IsEmpty) {
                UIntPtr start = this.skippedPageQueue.Read();
                UIntPtr finish = this.skippedPageQueue.Read();
                InteriorPtrTable.ClearFirst(start, finish);
                PageManager.FreePageRange(start, finish);
                if (GC.remsetType == RemSetType.Cards) {
                     OffsetTable.ClearLast(PageTable.PageAddr(start),
                           PageTable.PageAddr(finish)-1);
                }                
            }
            this.skippedPageQueue.Cleanup(true);
            // Release the queue standby pages
            UnmanagedPageList.ReleaseStandbyPages();
            // Update the ownership information for the copied data
            PageType destGeneration =
                (generation == MAX_GENERATION) ?
                MAX_GENERATION :
                (PageType) (generation + 1);
            UIntPtr limitPage =
                PageTable.Page(PageTable.PagePad(newLimitPtr));
            for (UIntPtr i = UIntPtr.Zero; i < limitPage; i++) {
                if (IsMyZombiePage(i)) {
                    PageTable.SetType(i, (PageType)destGeneration);
                }
            }
        }

    }

}
