/*
* $Id$
*
* Copyright (C) 2003-2015 JNode.org
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; If not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.vm.memmgr.def;
import org.jnode.vm.Unsafe;
import org.jnode.vm.BaseVmArchitecture;
import org.jnode.vm.VmMagic;
import org.jnode.annotation.Inline;
import org.jnode.annotation.MagicPermission;
import org.jnode.annotation.NoInline;
import org.jnode.vm.classmgr.ObjectFlags;
import org.jnode.vm.classmgr.VmNormalClass;
import org.jnode.vm.classmgr.VmType;
import org.jnode.vm.facade.ObjectVisitor;
import org.jnode.vm.memmgr.HeapHelper;
import org.jnode.vm.scheduler.Monitor;
import org.vmmagic.pragma.Uninterruptible;
import org.vmmagic.unboxed.Address;
import org.vmmagic.unboxed.ObjectReference;
import org.vmmagic.unboxed.Offset;
import org.vmmagic.unboxed.Word;
/**
* @author epr
*/
@MagicPermission
final class GCMarkVisitor extends ObjectVisitor implements ObjectFlags,
Uninterruptible {
/**
* The marking stack
*/
private final GCStack stack;
/**
* The number of marked objects.
*/
private int markedObjects;
/**
* If true, all white and grey objects will be marked, otherwise only the
* grey objects will be marked
*/
private boolean rootSet;
private final BaseVmArchitecture arch;
// private final int slotSize;
private final DefaultHeapManager heapManager;
private final HeapHelper helper;
// private final ProcessChildVisitor processChildVisitor = new ProcessChildVisitor();
/**
* Create a new instance
*
* @param stack
*/
public GCMarkVisitor(DefaultHeapManager heapManager, BaseVmArchitecture arch,
GCStack stack) {
this.heapManager = heapManager;
this.stack = stack;
this.markedObjects = 0;
this.rootSet = false;
this.arch = arch;
this.helper = heapManager.getHelper();
// this.slotSize = arch.getReferenceSize();
}
/**
* @param object
* @return boolean
* @see org.jnode.vm.facade.ObjectVisitor#visit(java.lang.Object)
*/
public boolean visit(Object object) {
// Check the current color first, since a stackoverflow of
// the mark stack results in another iteration of visits.
final int gcColor = VmMagic.getObjectColor(object);
if (gcColor == GC_BLACK) {
return true;
} else if (rootSet || (gcColor == GC_GREY)) {
switch (gcColor) {
case GC_YELLOW:
//Avoid telling such stories in a garbage collector!!!
//Unsafe.debug("Yellow Object in the rootset.\n");
//Unsafe.debug("Perhaps corrupted Heap or bad luck.\n");
//Unsafe.debug("Continue as long as we don't have a");
//Unsafe.debug("gc map for the stack.\n Panic JNode once");
//Unsafe.debug("we have an exact GC.\n");
//Unsafe.die("Corrupted Heap\n");
return true;
case GC_WHITE: {
final boolean ok;
ok = helper.atomicChangeObjectColor(object, gcColor,
GC_GREY);
if (!ok) {
Unsafe.debug("Could not change object color. ");
}
break;
}
case GC_GREY:
break;
default: {
Unsafe.debug("color");
Unsafe.debug(gcColor);
helper.die("Unknown GC color on object");
}
}
stack.push(object);
mark();
}
final boolean rc = (!stack.isOverflow());
return rc;
}
/**
* Reset this visitor to its original state.
*/
@Inline
public void reset() {
this.markedObjects = 0;
}
/**
* Process all objects on the markstack, until the markstack is empty.
*/
@NoInline
protected final void mark() {
while (!stack.isEmpty()) {
final Object object = stack.pop();
markedObjects++;
VmType vmClass;
try {
vmClass = VmMagic.getObjectType(object);
} catch (NullPointerException ex) {
// This is a symptom of heap corruption
if (object == null) {
helper.die("GCMarkError: null object");
} else {
final Address objAddr = ObjectReference.fromObject(object).toAddress();
Unsafe.debug("Object address is ");
Unsafe.debug(objAddr);
Unsafe.debug(", tib is ");
Object[] tib = VmMagic.getTIB(object);
Unsafe.debug(ObjectReference.fromObject(tib).toAddress());
Unsafe.debug(", flags word is ");
Word flags = VmMagic.getObjectFlags(object);
Unsafe.debug(flags);
Unsafe.debug(", markedObjects is ");
Unsafe.debug(markedObjects);
Unsafe.debug('\n');
helper.die("GCMarkError: NPE");
}
throw ex;
}
if (vmClass == null) {
Unsafe.debug("Oops vmClass == null in (");
Unsafe.debug(markedObjects);
Unsafe.debug(")");
helper.die("vmClass == null in mark()");
} else if (vmClass.isArray()) {
if (!vmClass.isPrimitiveArray()) {
markArray(object);
}
} else {
markObject(object, (VmNormalClass) vmClass);
}
processChild(VmMagic.getTIB(object));
final Monitor monitor = helper.getInflatedMonitor(object, arch);
if (monitor != null) {
processChild(monitor);
}
final int gcColor = VmMagic.getObjectColor(object);
helper.atomicChangeObjectColor(object, gcColor, GC_BLACK);
}
}
/**
* Mark all elements in the given array. The array must contain references
* only.
*
* @param object
*/
@Inline
private void markArray(Object object) {
try {
final Object[] arr = (Object[]) object;
final int length = arr.length;
for (int i = 0; i < length; i++) {
final Object child = arr[i];
if (child != null) {
// Enable the following in the case of heap corruption
if (true) {
verifyChild(child, object, "array child", i, i);
}
processChild(child);
}
}
} catch (ClassCastException ex) {
System.out.println("object.class=" + object.getClass().getName());
throw ex;
}
}
/**
* Mark all instance variables of the given object.
*
* @param object
* @param vmClass
*/
@Inline
private void markObject(Object object, VmNormalClass vmClass) {
final int[] referenceOffsets = vmClass.getReferenceOffsets();
final int cnt = referenceOffsets.length;
if (cnt == 0) {
return;
}
final int size = vmClass.getObjectSize();
final Address objAddr = ObjectReference.fromObject(object).toAddress();
for (int i = 0; i < cnt; i++) {
final int offset = referenceOffsets[i];
if ((offset < 0) || (offset >= size)) {
Unsafe.debug("reference offset out of range!");
Unsafe.debug(vmClass.getName());
helper.die("Class internal error");
} else {
final ObjectReference child = objAddr.loadObjectReference(Offset.fromIntZeroExtend(offset));
if (child != null) {
// Enable the following in the case of heap corruption
if (false) {
verifyChild(child, object, "object child", i, offset);
}
processChild(child);
}
}
}
}
@Inline
private final void verifyChild(Object child, Object parent, String where, int i, int offset) {
if (child != null) {
final ObjectReference childRef = ObjectReference.fromObject(child);
if (!heapManager.isObject(childRef.toAddress())) {
Unsafe.debug("GCMarkError: in ");
Unsafe.debug(where);
Unsafe.debug(", i ");
Unsafe.debug(i);
Unsafe.debug(", offset ");
Unsafe.debug(offset);
Unsafe.debug(", parent type ");
Unsafe.debug(VmMagic.getObjectType(parent).getName());
Unsafe.debug(VmMagic.getObjectColor(parent));
Unsafe.debug("; child (");
Unsafe.debug(childRef.toAddress().toInt());
Unsafe.debug(") is not an object ");
Unsafe.debug(VmMagic.getObjectColor(childRef));
helper.die("Corrupted heap");
}
}
}
/**
* Process a child of an object (this child is a reference).
*
* @param child
*/
@Inline
final void processChild(Object child) {
final int gcColor = VmMagic.getObjectColor(child);
if (gcColor <= GC_WHITE) {
// Yellow or White
helper.atomicChangeObjectColor(child, gcColor, GC_GREY);
try {
// TEST for a valid vmclass.
stack.push(child);
} catch (NullPointerException ex) {
Unsafe.debug("\nObject address ");
Unsafe.debug(ObjectReference.fromObject(child).toAddress().toInt());
Unsafe.debug("\nObject TIB ");
Unsafe.debug(ObjectReference.fromObject(VmMagic.getTIB(child)).toAddress().toInt());
helper.die("NPE in processChild; probably corrupted heap");
}
}
}
/**
* Gets the number of objects marked by this visitor.
*
* @return int
*/
@Inline
public int getMarkedObjects() {
return markedObjects;
}
/**
* Gets the rootSet attribute.
*
* @return boolean
*/
@Inline
public boolean isRootSet() {
return rootSet;
}
/**
* Sets the rootSet attribute.
*
* @param b If true, all white and grey objects will be marked, otherwise
* only the grey objects will be marked.
*/
@Inline
public void setRootSet(boolean b) {
rootSet = b;
}
}