/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* 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/eclipse-1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.mmtk.harness.vm;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import org.mmtk.harness.Harness;
import org.mmtk.harness.Mutator;
import org.mmtk.harness.Mutators;
import org.mmtk.harness.lang.Trace;
import org.mmtk.harness.lang.Trace.Item;
import org.mmtk.harness.lang.runtime.AllocationSite;
import org.mmtk.harness.lang.runtime.ObjectValue;
import org.mmtk.harness.lang.runtime.StackFrame;
import org.mmtk.plan.TraceLocal;
import org.mmtk.plan.TransitiveClosure;
import org.vmmagic.pragma.Uninterruptible;
import org.vmmagic.unboxed.*;
/**
*
*
*/
@Uninterruptible
public class Scanning extends org.mmtk.vm.Scanning {
public static final int THREAD_ITERATOR_TABLE_ENTRIES = 2048;
/**
* Table of thread iterator objects (to simulate JikesRVM thread iterator objects)
*/
private static volatile ObjectValue threadIteratorTable = null;
/**
* Initialize the thread iterator table
* @param m Calling mutator
*/
public static void initThreadIteratorTable(Mutator m) {
if (Harness.allocDuringCollection.getValue() && threadIteratorTable == null) {
threadIteratorTable = new ObjectValue(m.allocThreadIteratorTable());
}
}
/**
* @param m Mutator
* @param obj The iterator object
*/
public static void setThreadIteratorObject(Mutator m, ObjectReference obj) {
m.storeReferenceField(threadIteratorTable.getObjectValue(), m.getContext().getId() % THREAD_ITERATOR_TABLE_ENTRIES, obj);
}
/**
* Internal harness method to get all non-stack roots
* @return The set of roots
*/
public static Set<ObjectValue> getRoots() {
if (threadIteratorTable == null)
return Collections.emptySet();
return new HashSet<ObjectValue>(Arrays.asList(threadIteratorTable));
}
/**
* Delegated scanning of a object, processing each pointer field
* encountered.
* @param trace The trace
*
* @param object The object to be scanned.
*/
@Override
public void scanObject(TransitiveClosure trace, ObjectReference object) {
Trace.trace(Item.SCAN, "Scanning object %s", ObjectModel.addressAndSpaceString(object));
int refs = ObjectModel.getRefs(object);
Address first = object.toAddress().plus(ObjectModel.REFS_OFFSET);
for (int i=0; i < refs; i++) {
Trace.trace(Item.SCAN, " Edge %s", first.plus(i << LOG_BYTES_IN_ADDRESS).loadObjectReference());
trace.processEdge(object, first.plus(i << LOG_BYTES_IN_ADDRESS));
}
}
/**
* Invoke a specialized scan method. Note that these methods must have been allocated
* explicitly through Plan and PlanConstraints.
*
* @param id The specialized method id
* @param trace The trace the method has been specialized for
* @param object The object to be scanned
*/
@Override
public void specializedScanObject(int id, TransitiveClosure trace, ObjectReference object) {
scanObject(trace, object);
}
/**
* Delegated precopying of a object's children, processing each pointer field
* encountered.
*
* @param trace The trace object to use for precopying.
* @param object The object to be scanned.
*/
@Override
public void precopyChildren(TraceLocal trace, ObjectReference object) {
scanObject(trace, object);
}
private BlockingQueue<Mutator> mutatorsToScan = null;
/**
* Prepares for using the <code>computeAllRoots</code> method. The
* thread counter allows multiple GC threads to co-operatively
* iterate through the thread data structure (if load balancing
* parallel GC threads were not important, the thread counter could
* simply be replaced by a for loop).
*/
@Override
public synchronized void resetThreadCounter() {
assert mutatorsToScan.size() == 0;
mutatorsToScan = null;
ObjectValue.startRootDiscoveryPhase();
}
/**
* Pre-copy all potentially movable instances used in the course of
* GC. This includes the thread objects representing the GC threads
* themselves. It is crucial that these instances are forwarded
* <i>prior</i> to the GC proper. Since these instances <i>are
* not</i> enqueued for scanning, it is important that when roots
* are computed the same instances are explicitly scanned and
* included in the set of roots. The existence of this method
* allows the actions of calculating roots and forwarding GC
* instances to be decoupled.
* @param trace The MMTk trace object
*/
@Override
public void preCopyGCInstances(TraceLocal trace) {
/* None */
}
/**
* Computes static roots. This method establishes all such roots for
* collection and places them in the root locations queue. This method
* should not have side effects (such as copying or forwarding of
* objects). There are a number of important preconditions:
*
* <ul>
* <li> All objects used in the course of GC (such as the GC thread
* objects) need to be "pre-copied" prior to calling this method.
* <li> The <code>threadCounter</code> must be reset so that load
* balancing parallel GC can share the work of scanning threads.
* </ul>
*
* @param trace The trace to use for computing roots.
*/
@Override
public void computeStaticRoots(TraceLocal trace) {
if (threadIteratorTable != null) {
if (Trace.isEnabled(Item.ROOTS)) {
Trace.trace(Item.ROOTS, "Tracing root %s", ObjectModel.getString(threadIteratorTable.getObjectValue()));
}
threadIteratorTable.traceObject(trace);
if (StackFrame.ASSERT_WILL_NOT_MOVE) {
assert trace.willNotMoveInCurrentCollection(threadIteratorTable.getObjectValue()) :
threadIteratorTable.getObjectValue()+" has been traced but willNotMoveInCurrentCollection is still false";
}
if (Trace.isEnabled(Item.ROOTS)) {
Trace.trace(Item.ROOTS, "new value of %s", ObjectModel.getString(threadIteratorTable.getObjectValue()));
}
}
}
/**
* Computes global roots. This method establishes all such roots for
* collection and places them in the root locations queue. This method
* should not have side effects (such as copying or forwarding of
* objects). There are a number of important preconditions:
*
* <ul>
* <li> All objects used in the course of GC (such as the GC thread
* objects) need to be "pre-copied" prior to calling this method.
* <li> The <code>threadCounter</code> must be reset so that load
* balancing parallel GC can share the work of scanning threads.
* </ul>
*
* @param trace The trace to use for computing roots.
*/
@Override
public void computeGlobalRoots(TraceLocal trace) {
// none
}
/**
* Computes roots pointed to by threads, their associated registers
* and stacks. This method places these roots in the root values,
* root locations and interior root locations queues. This method
* should not have side effects (such as copying or forwarding of
* objects). There are a number of important preconditions:
*
* <ul>
* <li> All objects used in the course of GC (such as the GC thread
* objects) need to be "pre-copied" prior to calling this method.
* <li> The <code>threadCounter</code> must be reset so that load
* balancing parallel GC can share the work of scanning threads.
* </ul>
*
* @param trace The trace to use for computing roots.
*/
@Override
public void computeThreadRoots(TraceLocal trace) {
Trace.trace(Item.COLLECT,"Computing roots for mutators");
synchronized(this) {
if (mutatorsToScan == null) {
mutatorsToScan = Mutators.getAll();
}
}
while(true) {
Trace.trace(Item.COLLECT,"mutators to scan: %d",mutatorsToScan.size());
Mutator m = mutatorsToScan.poll();
if (m == null)
break;
if (Harness.allocDuringCollection.getValue()) {
Trace.trace(Item.COLLECT,"Allocating thread iterator object");
setThreadIteratorObject(m, m.alloc(0, 0, false, AllocationSite.INTERNAL_SITE_ID));
}
Trace.trace(Item.COLLECT,"Computing roots for mutator");
m.computeThreadRoots(trace);
if (Harness.allocDuringCollection.getValue()) {
setThreadIteratorObject(m, ObjectReference.nullReference());
}
}
}
/**
* Compute all roots out of the VM's boot image (if any). This method is a no-op
* in the case where the VM does not maintain an MMTk-visible Java space. However,
* when the VM does maintain a space (such as a boot image) which is visible to MMTk,
* that space could either be scanned by MMTk as part of its transitive closure over
* the whole heap, or as a (considerable) performance optimization, MMTk could avoid
* scanning the space if it is aware of all pointers out of that space. This method
* is used to establish the root set out of the scannable space in the case where
* such a space exists.
*
* @param trace The trace object to use to report root locations.
*/
@Override
public void computeBootImageRoots(TraceLocal trace) {
/* None */
}
}