/*
* 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.mminterface;
import org.jikesrvm.VM;
import org.jikesrvm.mm.mmtk.Collection;
import org.jikesrvm.scheduler.RVMThread;
import org.jikesrvm.scheduler.Monitor;
import org.vmmagic.pragma.Unpreemptible;
import org.mmtk.plan.Plan;
/**
* Handshake handles mutator requests to initiate a collection, and
* wait for a collection to complete. It implements the process of
* suspending all mutator threads executing in Java and starting all
* the GC threads (CollectorThreads) for the processors that will
* be participating in a collection. This may not be all processors,
* if we exclude those executing in native code.
*
* Because the threading strategy within RVM is currently under
* revision, the logic here is also changing and somewhat "messy".
*
* @see CollectorThread
*/
public class Handshake {
/***********************************************************************
*
* Class variables
*/
public static final int verbose = 0;
/***********************************************************************
*
* Instance variables
*/
private Monitor lock;
protected boolean requestFlag;
public int gcTrigger; // reason for this GC
private int collectorThreadsParked;
public Handshake() {
reset();
}
public void boot() {
lock = new Monitor();
}
/**
* Call this if you know that a GC request has already been made and you'd like
* to wait on that GC to finish - presumably because you're trying to allocate
* and cannot reasonably do so before GC is done. Note, there CANNOT be a
* GC safe point between when you realize that there is already a GC request and
* when you call this method!
*/
@Unpreemptible
public void waitForGCToFinish() {
if (verbose >= 1) VM.sysWriteln("GC Message: Handshake.requestAndAwaitCompletion - yielding");
/* allow a gc thread to run */
RVMThread t=RVMThread.getCurrentThread();
t.assertAcceptableStates(RVMThread.IN_JAVA,
RVMThread.IN_JAVA_TO_BLOCK);
RVMThread.observeExecStatusAtSTW(t.getExecStatus());
t.block(RVMThread.gcBlockAdapter);
if (verbose >= 1) VM.sysWriteln("GC Message: Handshake.requestAndAwaitCompletion - mutator running");
}
/**
* Called by mutators to request a garbage collection and wait for
* it to complete.
*
* Waiting is actually just yielding the processor to schedule the
* collector thread, which will disable further thread switching on
* the processor until it has completed the collection.
*/
@Unpreemptible
public void requestAndAwaitCompletion(int why) {
request(why);
waitForGCToFinish();
}
/**
* Called by mutators to request an asynchronous garbage collection.
* After initiating a GC (if one is not already initiated), the
* caller continues until it yields to the GC. It may thus make
* this call at an otherwise unsafe point.
*/
@Unpreemptible("Change state of thread possibly context switching if generating exception")
public void requestAndContinue(int why) {
request(why);
}
@Unpreemptible
public void reset() {
if (lock!=null) {
lock.lockNoHandshake();
}
gcTrigger = Collection.UNKNOWN_GC_TRIGGER;
requestFlag = false;
if (lock!=null) {
lock.unlock();
}
}
@Unpreemptible
void parkCollectorThread() {
lock.lockNoHandshake();
collectorThreadsParked++;
lock.broadcast();
if (verbose>=1) VM.sysWriteln("GC Thread #",RVMThread.getCurrentThreadSlot()," parked.");
while (!requestFlag) {
if (verbose>=1) VM.sysWriteln("GC Thread #",RVMThread.getCurrentThreadSlot()," waiting for request.");
lock.waitWithHandshake();
}
if (verbose>=1) VM.sysWriteln("GC Thread #",RVMThread.getCurrentThreadSlot()," got request, unparking.");
collectorThreadsParked--;
lock.unlock();
}
/**
* Called by mutators to request a garbage collection.
*
* @return true if the completion flag is not already set.
*/
@Unpreemptible("Becoming another thread interrupts the current thread, avoid preemption in the process")
private boolean request(int why) {
if (verbose>=1) VM.sysWriteln("Thread #",RVMThread.getCurrentThreadSlot()," is trying to make a GC request");
lock.lockNoHandshake();
if (verbose>=1) VM.sysWriteln("Thread #",RVMThread.getCurrentThreadSlot()," acquired the lock for making a GC request");
if (why > gcTrigger) gcTrigger = why;
if (requestFlag) {
if (verbose >= 1) {
VM.sysWriteln("GC Message: mutator: already in progress");
}
} else {
// first mutator initiates collection by making all gc threads
// runnable at high priority
if (verbose >= 1) VM.sysWriteln("GC Message: Handshake - mutator: initiating collection");
if (!RVMThread.threadingInitialized) {
VM.sysWrite("GC required before system fully initialized");
VM.sysWriteln("Specify larger than default heapsize on command line");
RVMThread.dumpStack();
VM.shutdown(VM.EXIT_STATUS_MISC_TROUBLE);
}
requestFlag = true;
Plan.setCollectionTriggered();
lock.broadcast();
}
lock.unlock();
return true;
}
}