/*
* 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.jikesrvm.mm.mmtk;
import org.vmmagic.pragma.*;
import org.vmmagic.unboxed.*;
import org.jikesrvm.SizeConstants;
import org.jikesrvm.VM;
import org.jikesrvm.mm.mminterface.Selected;
import org.jikesrvm.runtime.Magic;
import org.jikesrvm.Services;
import org.mmtk.plan.TraceLocal;
/**
* This class manages the processing of finalizable objects.
*/
// can this be a linked list?
@Uninterruptible
public final class FinalizableProcessor extends org.mmtk.vm.FinalizableProcessor implements SizeConstants {
/********************************************************************
* Class fields
*/
/** The FinalizableProcessor singleton */
private static final FinalizableProcessor finalizableProcessor = new FinalizableProcessor();
/** Stress the system? */
private static final boolean STRESS = VM.ForceFrequentGC;
/** Initial size of the reference object table */
private static final int INITIAL_SIZE = STRESS ? 1 : 256;
/** Amount to grow the table by when it is filled */
private static final double GROWTH_FACTOR = 2.0;
/*************************************************************************
* Instance fields
*/
/** Used to ensure mutual exclusion during table manipulation */
private final Lock lock = new Lock("AddressTable");
/** The table of candidates */
protected volatile AddressArray table = AddressArray.create(INITIAL_SIZE);
/** The table of ready objects */
protected volatile Object[] readyForFinalize = new Object[INITIAL_SIZE];
/** Index of first entry created since last collection */
protected int nurseryIndex = 0;
/** Index of the first free slot in the table. */
protected volatile int maxIndex = 0;
/** Next object ready to be finalized */
private volatile int nextReadyIndex = 0;
/** Last object ready to be finalized */
private volatile int lastReadyIndex = 0;
/**
* Create a new table.
*/
protected FinalizableProcessor() {}
/**
* Allocate an entry in the table. This should be called from an unpreemptible
* context so that the entry can be filled. This method is responsible for growing
* the table if necessary.
*/
@NoInline
@UnpreemptibleNoWarn("Non-preemptible but yield when table needs to be grown")
public void add(Object object) {
lock.acquire();
while (maxIndex>=table.length() || maxIndex >= freeReady()) {
int newTableSize=-1;
int newReadyForFinalizeSize=-1;
AddressArray newTable=null;
Object[] newReadyForFinalize=null;
if (maxIndex>=table.length()) {
newTableSize=STRESS ? table.length() + 1 : (int)(table.length() * GROWTH_FACTOR);
}
if (maxIndex>=freeReady()) {
newReadyForFinalizeSize=table.length() + countReady();
if (newReadyForFinalizeSize<=readyForFinalize.length) {
newReadyForFinalizeSize=-1;
}
}
{
lock.release();
if (newTableSize>=0) {
newTable=AddressArray.create(newTableSize);
}
if (newReadyForFinalizeSize>=0) {
newReadyForFinalize=new Object[newReadyForFinalizeSize];
}
lock.acquire();
}
if (maxIndex>=table.length() && newTable!=null) {
for (int i=0; i < table.length(); i++) {
newTable.set(i, table.get(i));
}
table = newTable;
}
if (maxIndex>=freeReady() && newReadyForFinalize!=null) {
int j = 0;
for(int i=nextReadyIndex; i < lastReadyIndex && i < readyForFinalize.length; i++) {
newReadyForFinalize[j++] = readyForFinalize[i];
}
if (lastReadyIndex < nextReadyIndex) {
for(int i=0; i < lastReadyIndex; i++) {
newReadyForFinalize[j++] = readyForFinalize[i];
}
}
lastReadyIndex = j;
nextReadyIndex = 0;
readyForFinalize = newReadyForFinalize;
}
}
table.set(maxIndex++, Magic.objectAsAddress(object));
lock.release();
}
/**
* Clear the contents of the table. This is called when reference types are
* disabled to make it easier for VMs to change this setting at runtime.
*/
public void clear() {
maxIndex = 0;
}
/**
* Scan through all entries in the table and forward.
*
* Currently ignores the nursery hint.
*
* TODO parallelise this code?
*
* @param trace The trace
* @param nursery Is this a nursery collection ?
*/
@Override
public void forward(TraceLocal trace, boolean nursery) {
for (int i=0 ; i < maxIndex; i++) {
ObjectReference ref = table.get(i).toObjectReference();
table.set(i, trace.getForwardedFinalizable(ref).toAddress());
}
}
/**
* Scan through the list of references. Calls ReferenceProcessor's
* processReference method for each reference and builds a new
* list of those references still active.
*
* Depending on the value of <code>nursery</code>, we will either
* scan all references, or just those created since the last scan.
*
* TODO parallelise this code
*
* @param nursery Scan only the newly created references
*/
@Override
@UninterruptibleNoWarn
public void scan(TraceLocal trace, boolean nursery) {
int toIndex = nursery ? nurseryIndex : 0;
for (int fromIndex = toIndex; fromIndex < maxIndex; fromIndex++) {
ObjectReference ref = table.get(fromIndex).toObjectReference();
/* Determine liveness (and forward if necessary) */
if (trace.isLive(ref)) {
table.set(toIndex++, trace.getForwardedFinalizable(ref).toAddress());
continue;
}
/* Make ready for finalize */
ref = trace.retainForFinalize(ref);
/* Add to object table */
Offset offset = Word.fromIntZeroExtend(lastReadyIndex).lsh(LOG_BYTES_IN_ADDRESS).toOffset();
Selected.Plan.get().storeObjectReference(Magic.objectAsAddress(readyForFinalize).plus(offset), ref);
lastReadyIndex = (lastReadyIndex + 1) % readyForFinalize.length;
}
nurseryIndex = maxIndex = toIndex;
}
/**
* Get an object to run finalize().
*
* @return The object to finalize()
*/
@NoInline
@Unpreemptible("Non-preemptible but may pause if another thread is growing the table")
public Object getReady() {
lock.acquire();
Object result = null;
if (nextReadyIndex != lastReadyIndex) {
result = readyForFinalize[nextReadyIndex];
Services.setArrayUninterruptible(readyForFinalize, nextReadyIndex, null);
nextReadyIndex = (nextReadyIndex + 1) % readyForFinalize.length;
}
lock.release();
return result;
}
/***********************************************************************
* Statistics and debugging
*/
/**
* The number of entries in the table.
*/
public int count() {
return maxIndex;
}
/**
* The number of entries ready to be finalized.
*/
public int countReady() {
return ((lastReadyIndex - nextReadyIndex) + readyForFinalize.length) % readyForFinalize.length;
}
/**
* The number of entries ready to be finalized.
*/
public int freeReady() {
return readyForFinalize.length - countReady();
}
/***********************************************************************
* Static methods.
*/
/** Get the singleton */
public static FinalizableProcessor getProcessor() {
return finalizableProcessor;
}
/**
* Add a finalization candidate.
* @param object The object with a finalizer.
*/
@Unpreemptible("Non-preemptible but may pause if table needs to be grown")
public static void addCandidate(Object object) {
finalizableProcessor.add(object);
}
/**
* Get an object to call the finalize() method on it.
*/
@Unpreemptible("Non-preemptible but may pause if table is being grown")
public static Object getForFinalize() {
return finalizableProcessor.getReady();
}
/**
* The number of objects waiting for finalize() calls.
*/
public static int countReadyForFinalize() {
return finalizableProcessor.countReady();
}
}