/* * This file is part of the Jikes RVM project (http://jikesrvm.org). * * This file is licensed to You under the Common Public License (CPL); * You may not use this file except in compliance with the License. You * may obtain a copy of the License at * * http://www.opensource.org/licenses/cpl1.0.php * * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. */ package org.mmtk.plan.refcount.cd; import org.mmtk.plan.refcount.RCBase; import org.mmtk.plan.refcount.RCBaseCollector; import org.mmtk.plan.refcount.RCHeader; import org.mmtk.utility.Constants; import org.mmtk.utility.deque.ObjectReferenceDeque; import org.mmtk.vm.VM; import org.vmmagic.pragma.*; import org.vmmagic.unboxed.ObjectReference; /** * This class implements <i>per-collector thread</i> behavior * and state for a trial deletion cycle detector. */ @Uninterruptible public final class TrialDeletionCollector extends CDCollector implements Constants { /**************************************************************************** * * Class variables */ /**************************************************************************** * * Instance variables */ public final ObjectReferenceDeque workQueue; public final ObjectReferenceDeque blackQueue; public final ObjectReferenceDeque unfilteredPurpleBuffer; public final ObjectReferenceDeque maturePurpleBuffer; public final ObjectReferenceDeque filteredPurpleBuffer; public final ObjectReferenceDeque cycleBufferA; public final ObjectReferenceDeque cycleBufferB; public final ObjectReferenceDeque freeBuffer; public final TrialDeletionCollectStep collectStep; public final TrialDeletionGreyStep greyStep; public final TrialDeletionScanStep scanStep; public final TrialDeletionScanBlackStep scanBlackStep; /**************************************************************************** * * Initialization */ public TrialDeletionCollector() { workQueue = new ObjectReferenceDeque("cycle workqueue", global().workPool); blackQueue = new ObjectReferenceDeque("cycle black workqueue", global().blackPool); unfilteredPurpleBuffer = new ObjectReferenceDeque("unfiltered purple buf", global().unfilteredPurplePool); maturePurpleBuffer = new ObjectReferenceDeque("mature purple buf", global().maturePurplePool); filteredPurpleBuffer = new ObjectReferenceDeque("filtered purple buf", global().filteredPurplePool); cycleBufferA = new ObjectReferenceDeque("cycle buf A", global().cyclePoolA); cycleBufferB = new ObjectReferenceDeque("cycle buf B", global().cyclePoolB); freeBuffer = new ObjectReferenceDeque("free buffer", global().freePool); collectStep = new TrialDeletionCollectStep(); greyStep = new TrialDeletionGreyStep(); scanStep = new TrialDeletionScanStep(); scanBlackStep = new TrialDeletionScanBlackStep(); } /** * Perform a collection phase. * * @param phaseId Collection phase to execute. * @param primary Use this thread to execute any single-threaded collector * context actions. */ public boolean collectionPhase(int phaseId, boolean primary) { boolean filter = global().cdMode >= TrialDeletion.FILTER_PURPLE; boolean collect = global().cdMode >= TrialDeletion.FULL_COLLECTION; // Phases that occur when we are filtering or collecting if (phaseId == TrialDeletion.CD_FILTER_PURPLE) { if (filter) filterPurpleBufs(); return true; } if (phaseId == TrialDeletion.CD_FREE_FILTERED) { if (filter) processFreeBufs(); return true; } // Phases that occur when we are doing a full collection if (phaseId == TrialDeletion.CD_FILTER_MATURE) { if (collect) { filterMaturePurpleBufs(); } return true; } if (phaseId == TrialDeletion.CD_MARK_GREY) { if (collect && primary) { doMarkGreyPhase(); } return true; } if (phaseId == TrialDeletion.CD_SCAN) { if (collect && primary) { doScanPhase(); } return true; } if (phaseId == TrialDeletion.CD_COLLECT) { if (collect && primary) { doCollectPhase(); } return true; } if (phaseId == TrialDeletion.CD_FREE) { if (collect) processFreeBufs(); return true; } if (phaseId == TrialDeletion.CD_FLUSH_FILTERED) { if (collect) flushFilteredPurpleBufs(); return true; } if (phaseId == TrialDeletion.CD_PROCESS_DECS) { if (collect) ((RCBaseCollector)VM.activePlan.collector()).processDecBuffer(); return true; } return false; } private void filterPurpleBufs() { filterPurpleBufs(unfilteredPurpleBuffer, maturePurpleBuffer); maturePurpleBuffer.flushLocal(); } private void filterMaturePurpleBufs() { if (filteredPurpleBuffer.isEmpty()) { filterPurpleBufs(maturePurpleBuffer, filteredPurpleBuffer); } } private void filterPurpleBufs(ObjectReferenceDeque src, ObjectReferenceDeque tgt) { ObjectReference object = ObjectReference.nullReference(); src.flushLocal(); while (!(object = src.pop()).isNull()) { filter(object, tgt); } tgt.flushLocal(); } private void flushFilteredPurpleBufs() { ObjectReference object = ObjectReference.nullReference(); while (!(object = filteredPurpleBuffer.pop()).isNull()) { maturePurpleBuffer.push(object); } maturePurpleBuffer.flushLocal(); } private void filter(ObjectReference object, ObjectReferenceDeque tgt) { if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(!RCHeader.isGreen(object)); if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(RCHeader.isBuffered(object)); if (RCHeader.isLiveRC(object)) { if (RCHeader.isPurple(object)) { tgt.insert(object); } else { RCHeader.clearBufferedBit(object); } } else { RCHeader.clearBufferedBit(object); freeBuffer.push(object); } } private void processFreeBufs() { ObjectReference object; while (!(object = freeBuffer.pop()).isNull()) { if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(!RCHeader.isBuffered(object)); RCBase.free(object); } } /**************************************************************************** * * Mark grey * * Trace from purple "roots", marking grey. Try to work within a * time cap. This will work <b>only</b> if the purple objects are * maintained as a <b>queue</b> rather than a <b>stack</b> * (otherwise objects at the bottom of the stack, which may be key, * may never get processed). It is therefore important that the * "insert" operation is used when adding to the purple queue, * rather than "push". */ /** * Vist as many purple objects as time allows and transitively mark * grey. This means "pretending" that the initial object is dead, * and thus applying temporary decrements to each of the object's * decendents. */ private void doMarkGreyPhase() { ObjectReference object = ObjectReference.nullReference(); while (!(object = filteredPurpleBuffer.pop()).isNull()) { processGreyObject(object, cycleBufferA); } } @Inline private boolean processGreyObject(ObjectReference object, ObjectReferenceDeque tgt) { // Log.write("pg[");Log.write(object);RefCountSpace.print(object);Log.writeln("]"); if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(!RCHeader.isGreen(object)); if (RCHeader.isPurpleNotGrey(object)) { if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(RCHeader.isLiveRC(object)); markGrey(object); tgt.push(object); } else { if (VM.VERIFY_ASSERTIONS) { if (!(RCHeader.isGrey(object) || RCHeader.isBlack(object))) { RCHeader.print(object); } VM.assertions._assert(RCHeader.isGrey(object) || RCHeader.isBlack(object)); } RCHeader.clearBufferedBit(object); } return true; } @Inline private void markGrey(ObjectReference object) { if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(workQueue.pop().isNull()); while (!object.isNull()) { if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(!RCHeader.isGreen(object)); if (!RCHeader.isGrey(object)) { RCHeader.makeGrey(object); VM.scanning.scanObject(greyStep, object); } object = workQueue.pop(); } } @Inline public void enumerateGrey(ObjectReference object) { if (RCBase.isRCObject(object) && !RCHeader.isGreen(object)) { if (VM.VERIFY_ASSERTIONS) { // TODO VM.assertions._assert(RCHeader.isLiveRC(object)); } RCHeader.unsyncDecRC(object); workQueue.push(object); } } private void doScanPhase() { ObjectReference object; ObjectReferenceDeque src = cycleBufferA; ObjectReferenceDeque tgt = cycleBufferB; while (!(object = src.pop()).isNull()) { if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(!RCHeader.isGreen(object)); scan(object); tgt.push(object); } } @Inline private void scan(ObjectReference object) { if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(workQueue.pop().isNull()); while (!object.isNull()) { if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(!RCHeader.isGreen(object)); if (RCHeader.isGrey(object)) { if (RCHeader.isLiveRC(object)) { scanBlack(object); } else { RCHeader.makeWhite(object); VM.scanning.scanObject(scanStep, object); } } object = workQueue.pop(); } } @Inline public void enumerateScan(ObjectReference object) { if (RCBase.isRCObject(object) && !RCHeader.isGreen(object)) workQueue.push(object); } @Inline private void scanBlack(ObjectReference object) { if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(blackQueue.pop().isNull()); while (!object.isNull()) { if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(!RCHeader.isGreen(object)); if (!RCHeader.isBlack(object)) { // FIXME can't this just be if (isGrey(object)) ?? RCHeader.makeBlack(object); VM.scanning.scanObject(scanBlackStep, object); } object = blackQueue.pop(); } } @Inline public void enumerateScanBlack(ObjectReference object) { if (RCBase.isRCObject(object) && !RCHeader.isGreen(object)) { RCHeader.unsyncIncRC(object); if (!RCHeader.isBlack(object)) blackQueue.push(object); } } private void doCollectPhase() { ObjectReference object; ObjectReferenceDeque src = cycleBufferB; while (!(object = src.pop()).isNull()) { if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(!RCHeader.isGreen(object)); RCHeader.clearBufferedBit(object); collectWhite(object); } } @Inline private void collectWhite(ObjectReference object) { if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(workQueue.pop().isNull()); while (!object.isNull()) { if (RCHeader.isWhite(object) && !RCHeader.isBuffered(object)) { RCHeader.makeBlack(object); VM.scanning.scanObject(collectStep, object); freeBuffer.push(object); } object = workQueue.pop(); } } @Inline public void enumerateCollect(ObjectReference object) { if (RCBase.isRCObject(object)) { if (RCHeader.isGreen(object)) { ((RCBaseCollector)VM.activePlan.collector()).decBuffer.push(object); } else { workQueue.push(object); } } } /** * Buffer an object after a successful update when shouldBufferOnDecRC * returned true. * * @param object The object to buffer. */ public void bufferOnDecRC(ObjectReference object) { if (VM.VERIFY_ASSERTIONS) { VM.assertions._assert(!RCHeader.isGreen(object)); } unfilteredPurpleBuffer.insert(object); } /** @return The active global plan as an <code>MS</code> instance. */ @Inline private static TrialDeletion global() { return (TrialDeletion)((RCBase)VM.activePlan.global()).cycleDetector(); } }