/* * 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; import org.mmtk.plan.refcount.cd.CD; import org.mmtk.utility.Log; import org.mmtk.utility.Constants; //import org.mmtk.vm.Assert; //import org.mmtk.vm.ObjectModel; import org.mmtk.vm.VM; import org.vmmagic.unboxed.*; import org.vmmagic.pragma.*; /** * Each instance of this class corresponds to one reference counted * *space*. In other words, it maintains and performs actions with * respect to state that is global to a given reference counted space. * Each of the instance methods of this class may be called by any * thread (i.e. synchronization must be explicit in any instance or * class method). This contrasts with the RefCountLocal, where * instances correspond to *plan* instances and therefore to kernel * threads. Thus unlike this class, synchronization is not necessary * in the instance methods of RefCountLocal. */ @Uninterruptible public final class RCHeader implements Constants { /**************************************************************************** * * Class variables */ public static final int LOCAL_GC_BITS_REQUIRED = 2; public static final int GLOBAL_GC_BITS_REQUIRED = 0; /** How many bytes are used by all GC header fields? */ public static final int GC_HEADER_WORDS_REQUIRED = 1; static final Offset RC_HEADER_OFFSET = VM.objectModel.GC_HEADER_OFFSET(); /* Mask bits to signify the start/finish of logging an object */ public static final Word LOGGING_MASK = Word.one().lsh(2).minus(Word.one()); //...00011 public static final int LOG_BIT = 0; public static final Word LOGGED = Word.zero(); public static final Word UNLOGGED = Word.one(); public static final Word BEING_LOGGED = Word.one().lsh(2).minus(Word.one()); //...00011 public static final int DEC_KILL = 0; // dec to zero RC --> reclaim obj public static final int DEC_PURPLE = 1; // dec to non-zero RC, already buf'd public static final int DEC_BUFFER = -1; // dec to non-zero RC, need to bufr // See Bacon & Rajan ECOOP 2001 for notion of colors (purple, grey, // black, green). See also Jones & Lins for a description of "Lins' // algorithm", on which Bacon & Rajan's is based. public static final int FREED_OBJECT = 1 << 31; // The following are arranged to try to make the most common tests // fastest ("bufferd?", "green?" and "(green | purple)?") public static final int BUFFERED_MASK = 0x1; // .. xx0001 public static final int COLOR_MASK = 0x1e; // .. x11110 public static final int LO_COLOR_MASK = 0x6; // .. x00110 public static final int HI_COLOR_MASK = 0x18; // .. x11000 public static final int BLACK = 0x0; // .. xxxx0x public static final int GREY = 0x2; // .. xxxx1x public static final int WHITE = 0x4; // .. xx010x // green & purple *MUST* remain the highest colors in order to // preserve the (green | purple) test's precondition. public static final int PURPLE = 0x8; // .. x01xxx public static final int GREEN = 0x10; // .. x10xxx public static final int MARKED = GREY; // bits used to ensure retention of objects with zero RC public static final int FINALIZABLE = 0x20; // .. 100000 public static final int ROOT_REACHABLE = 0x40; // .. x10000 public static final int HARD_THRESHOLD = ROOT_REACHABLE; public static final int LIVE_THRESHOLD = FINALIZABLE; public static final int BITS_USED = 7; static final int INCREMENT_SHIFT = BITS_USED; static final int INCREMENT = 1<<INCREMENT_SHIFT; static final int AVAILABLE_BITS = BITS_IN_ADDRESS - BITS_USED; static final int INCREMENT_LIMIT = ~(1<<(BITS_IN_ADDRESS-1)); /**************************************************************************** * * Object Logging Methods */ /** * Return true if <code>object</code> is yet to be logged (for * coalescing RC). * * @param object The object in question * @return <code>true</code> if <code>object</code> needs to be logged. */ @Inline @Uninterruptible public static boolean logRequired(ObjectReference object) { Word value = VM.objectModel.readAvailableBitsWord(object); return value.and(LOGGING_MASK).EQ(UNLOGGED); } /** * Attempt to log <code>object</code> for coalescing RC. This is * used to handle a race to log the object, and returns * <code>true</code> if we are to log the object and * <code>false</code> if we lost the race to log the object. * * <p>If this method returns <code>true</code>, it leaves the object * in the <code>BEING_LOGGED</code> state. It is the responsibility * of the caller to change the object to <code>LOGGED</code> once * the logging is complete. * * @see #makeLogged(ObjectReference) * @param object The object in question * @return <code>true</code> if the race to log * <code>object</code>was won. */ @Inline @Uninterruptible public static boolean attemptToLog(ObjectReference object) { Word oldValue; do { oldValue = VM.objectModel.prepareAvailableBits(object); if (oldValue.and(LOGGING_MASK).EQ(LOGGED)) return false; } while ((oldValue.and(LOGGING_MASK).EQ(BEING_LOGGED)) || !VM.objectModel.attemptAvailableBits(object, oldValue, oldValue.or(BEING_LOGGED))); if (VM.VERIFY_ASSERTIONS) { Word value = VM.objectModel.readAvailableBitsWord(object); VM.assertions._assert(value.and(LOGGING_MASK).EQ(BEING_LOGGED)); } return true; } /** * Signify completion of logging <code>object</code>. * * <code>object</code> is left in the <code>LOGGED</code> state. * * @see #attemptToLog(ObjectReference) * @param object The object whose state is to be changed. */ @Inline @Uninterruptible public static void makeLogged(ObjectReference object) { Word value = VM.objectModel.readAvailableBitsWord(object); if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(value.and(LOGGING_MASK).NE(LOGGED)); VM.objectModel.writeAvailableBitsWord(object, value.and(LOGGING_MASK.not())); } /** * Change <code>object</code>'s state to <code>UNLOGGED</code>. * * @param object The object whose state is to be changed. */ @Inline @Uninterruptible public static void makeUnlogged(ObjectReference object) { Word value = VM.objectModel.readAvailableBitsWord(object); if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(value.and(LOGGING_MASK).EQ(LOGGED)); VM.objectModel.writeAvailableBitsWord(object, value.or(UNLOGGED)); } /**************************************************************************** * * Header manipulation */ /** * Perform any required initialization of the GC portion of the header. * * @param object the object ref to the storage to be initialized * @param typeRef the type reference for the instance being created * @param initialInc do we want to initialize this header with an * initial increment? */ @Inline public static void initializeHeader(ObjectReference object, ObjectReference typeRef, boolean initialInc) { if (VM.VERIFY_ASSERTIONS) { VM.assertions._assert(RCBase.isRCObject(object)); } // all objects are birthed with an RC of INCREMENT int initialValue = (initialInc) ? INCREMENT : 0; initialValue = CD.current().initializeHeader(typeRef, initialValue); object.toAddress().store(initialValue, RC_HEADER_OFFSET); } /** * Return true if given object is live * * @param object The object whose liveness is to be tested * @return True if the object is alive */ @Inline @Uninterruptible public static boolean isLiveRC(ObjectReference object) { return object.toAddress().loadInt(RC_HEADER_OFFSET) >= LIVE_THRESHOLD; } /** * Return the reference count for an object * * @param object The object whose refcount is to be returned * @return The reference ocunt */ @Inline @Uninterruptible public static int getRC(ObjectReference object) { return object.toAddress().loadInt(RC_HEADER_OFFSET) >> INCREMENT_SHIFT; } /** * Is the object live? * * @param object The object reference. * @return True if the object is live. */ public boolean isLive(ObjectReference object) { return isLiveRC(object); } /** * Return true if given object is unreachable from roots or other * objects (i.e. ignoring the finalizer list). Mark the object as a * finalizer object. * * @param object The object whose finalizability is to be tested * @return True if the object is finalizable */ @Inline @Uninterruptible public static boolean isFinalizable(ObjectReference object) { setFinalizer(object); return object.toAddress().loadInt(RC_HEADER_OFFSET) < HARD_THRESHOLD; } @NoInline @Uninterruptible public static void incRCOOL(ObjectReference object) { incRC(object); } /** * Increment the reference count of an object, clearing the "purple" * status of the object (if it were already purple). An object is * marked purple if it is a potential root of a garbage cycle. If * an object's RC is incremented, it must be live and therefore * should not be considered as a potential garbage cycle. This must * be an atomic operation if parallel GC is supported. * * @param object The object whose RC is to be incremented. */ @Inline @Uninterruptible public static void incRC(ObjectReference object) { int oldValue, newValue; if (VM.VERIFY_ASSERTIONS) { VM.assertions._assert(RCBase.isRCObject(object)); } do { oldValue = object.toAddress().prepareInt(RC_HEADER_OFFSET); newValue = oldValue + INCREMENT; if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(newValue <= INCREMENT_LIMIT); newValue = CD.current().notifyIncRC(newValue); } while (!object.toAddress().attempt(oldValue, newValue, RC_HEADER_OFFSET)); } /** * Decrement the reference count of an object. Return either * <code>DEC_KILL</code> if the count went to zero, * <code>DEC_BUFFER</code> if the count did not go to zero and the * object was not already in the purple buffer, and * <code>DEC_PURPLE</code> if the count did not go to zero and the * object was already in the purple buffer. This must be an atomic * operation if parallel GC is supported. * * @param object The object whose RC is to be decremented. * @return <code>DEC_KILL</code> if the count went to zero, * <code>DEC_BUFFER</code> if the count did not go to zero and the * object was not already in the purple buffer, and * <code>DEC_PURPLE</code> if the count did not go to zero and the * object was already in the purple buffer. */ @Inline @Uninterruptible public static int decRC(ObjectReference object) { int oldValue, newValue; int rtn; if (VM.VERIFY_ASSERTIONS) { VM.assertions._assert(RCBase.isRCObject(object)); if (!isLiveRC(object)) { Log.writeln(object); } VM.assertions._assert(isLiveRC(object)); } do { oldValue = object.toAddress().prepareInt(RC_HEADER_OFFSET); newValue = oldValue - INCREMENT; if (newValue < LIVE_THRESHOLD) { rtn = DEC_KILL; } else if (CD.current().shouldBufferOnDecRC(newValue)) { newValue = CD.current().updateHeaderOnBufferedDec(newValue); rtn = DEC_BUFFER; } else { newValue = CD.current().updateHeaderOnUnbufferedDec(newValue); rtn = DEC_PURPLE; } } while (!object.toAddress().attempt(oldValue, newValue, RC_HEADER_OFFSET)); return rtn; } @Inline @Uninterruptible public static boolean isBuffered(ObjectReference object) { return (object.toAddress().loadInt(RC_HEADER_OFFSET) & BUFFERED_MASK) == BUFFERED_MASK; } /**************************************************************************** * * Finalization and dealing with roots */ /** * Set the <code>ROOT_REACHABLE</code> bit for an object if it is * not already set. Return true if it was not already set, false * otherwise. * * @param object The object whose <code>ROOT_REACHABLE</code> bit is * to be set. * @return <code>true</code> if it was set by this call, * <code>false</code> if the bit was already set. */ @Inline @Uninterruptible public static boolean setRoot(ObjectReference object) { int oldValue, newValue; do { oldValue = object.toAddress().prepareInt(RC_HEADER_OFFSET); if ((oldValue & ROOT_REACHABLE) == ROOT_REACHABLE) return false; newValue = oldValue | ROOT_REACHABLE; } while (!object.toAddress().attempt(oldValue, newValue, RC_HEADER_OFFSET)); return true; } /** * Clear the <code>ROOT_REACHABLE</code> bit for an object. * * @param object The object whose <code>ROOT_REACHABLE</code> bit is * to be cleared. */ @Inline @Uninterruptible public static void unsetRoot(ObjectReference object) { int oldValue, newValue; do { oldValue = object.toAddress().prepareInt(RC_HEADER_OFFSET); newValue = oldValue & ~ROOT_REACHABLE; } while (!object.toAddress().attempt(oldValue, newValue, RC_HEADER_OFFSET)); } /** * Set the <code>FINALIZABLE</code> bit for an object. * * @param object The object whose <code>FINALIZABLE</code> bit is * to be set. */ @Inline @Uninterruptible static void setFinalizer(ObjectReference object) { int oldValue, newValue; do { oldValue = object.toAddress().prepareInt(RC_HEADER_OFFSET); newValue = oldValue | FINALIZABLE; } while (!object.toAddress().attempt(oldValue, newValue, RC_HEADER_OFFSET)); } /** * Clear the <code>FINALIZABLE</code> bit for an object. * * @param object The object whose <code>FINALIZABLE</code> bit is * to be cleared. */ @Inline @Uninterruptible public static void clearFinalizer(ObjectReference object) { int oldValue, newValue; do { oldValue = object.toAddress().prepareInt(RC_HEADER_OFFSET); newValue = oldValue & ~FINALIZABLE; } while (!object.toAddress().attempt(oldValue, newValue, RC_HEADER_OFFSET)); } /**************************************************************************** * * Trial deletion support */ /** * Decrement the reference count of an object. This is unsychronized. * * @param object The object whose RC is to be decremented. */ @Inline @Uninterruptible public static void unsyncDecRC(ObjectReference object) { int oldValue, newValue; oldValue = object.toAddress().loadInt(RC_HEADER_OFFSET); newValue = oldValue - INCREMENT; object.toAddress().store(newValue, RC_HEADER_OFFSET); } /** * Increment the reference count of an object. This is unsychronized. * * @param object The object whose RC is to be incremented. */ @Inline @Uninterruptible public static void unsyncIncRC(ObjectReference object) { int oldValue, newValue; oldValue = object.toAddress().loadInt(RC_HEADER_OFFSET); newValue = oldValue + INCREMENT; object.toAddress().store(newValue, RC_HEADER_OFFSET); } @Inline @Uninterruptible public static void print(ObjectReference object) { if (object.isNull()) return; Log.write(' '); Log.write(object.toAddress().loadInt(RC_HEADER_OFFSET)>>INCREMENT_SHIFT); Log.write(' '); switch (getHiRCColor(object)) { case PURPLE: Log.write('p'); break; case GREEN: Log.write('g'); break; } switch (getLoRCColor(object)) { case BLACK: Log.write('b'); break; case WHITE: Log.write('w'); break; case GREY: Log.write('g'); break; } if (isBuffered(object)) Log.write('b'); else Log.write('u'); } @Inline @Uninterruptible public static void clearBufferedBit(ObjectReference object) { int oldValue = object.toAddress().loadInt(RC_HEADER_OFFSET); int newValue = oldValue & ~(BUFFERED_MASK | PURPLE); object.toAddress().store(newValue, RC_HEADER_OFFSET); } @Inline @Uninterruptible public static boolean isBlack(ObjectReference object) { return getLoRCColor(object) == BLACK; } @Inline @Uninterruptible public static boolean isWhite(ObjectReference object) { return getLoRCColor(object) == WHITE; } @Inline @Uninterruptible public static boolean isGreen(ObjectReference object) { return getHiRCColor(object) == GREEN; } @Inline @Uninterruptible public static boolean isPurple(ObjectReference object) { return getHiRCColor(object) == PURPLE; } @Inline @Uninterruptible public static boolean isPurpleNotGrey(ObjectReference object) { return (object.toAddress().loadInt(RC_HEADER_OFFSET) & (PURPLE | GREY)) == PURPLE; } @Inline @Uninterruptible public static boolean isGrey(ObjectReference object) { return getLoRCColor(object) == GREY; } @Inline @Uninterruptible private static int getLoRCColor(ObjectReference object) { return LO_COLOR_MASK & object.toAddress().loadInt(RC_HEADER_OFFSET); } @Inline @Uninterruptible private static int getHiRCColor(ObjectReference object) { return HI_COLOR_MASK & object.toAddress().loadInt(RC_HEADER_OFFSET); } @Inline @Uninterruptible public static void makeBlack(ObjectReference object) { changeRCLoColor(object, BLACK); } @Inline @Uninterruptible public static void makeWhite(ObjectReference object) { changeRCLoColor(object, WHITE); } @Inline @Uninterruptible public static void makeGrey(ObjectReference object) { if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(getHiRCColor(object) != GREEN); changeRCLoColor(object, GREY); } @Inline @Uninterruptible private static void changeRCLoColor(ObjectReference object, int color) { int oldValue = object.toAddress().loadInt(RC_HEADER_OFFSET); int newValue = (oldValue & ~LO_COLOR_MASK) | color; object.toAddress().store(newValue, RC_HEADER_OFFSET); } @Inline @Uninterruptible public static boolean testAndMark(ObjectReference object, Word markState) { int oldValue = object.toAddress().loadInt(RC_HEADER_OFFSET); if ((oldValue & MARKED) == markState.toInt()) return false; int newValue = oldValue ^ MARKED; object.toAddress().store(newValue, RC_HEADER_OFFSET); return true; } @Inline @Uninterruptible public static boolean isMarked(ObjectReference object, Word markState) { int oldValue = object.toAddress().loadInt(RC_HEADER_OFFSET); return (oldValue & MARKED) == markState.toInt(); } /**************************************************************************** * * Misc */ }