Mercurial > hg > Members > kono > jpf-core
diff src/main/gov/nasa/jpf/vm/FinalizerThreadInfo.java @ 0:61d41facf527
initial v8 import (history reset)
author | Peter Mehlitz <Peter.C.Mehlitz@nasa.gov> |
---|---|
date | Fri, 23 Jan 2015 10:14:01 -0800 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/gov/nasa/jpf/vm/FinalizerThreadInfo.java Fri Jan 23 10:14:01 2015 -0800 @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2014, United States Government, as represented by the + * Administrator of the National Aeronautics and Space Administration. + * All rights reserved. + * + * The Java Pathfinder core (jpf-core) platform is licensed under the + * Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package gov.nasa.jpf.vm; + +import gov.nasa.jpf.vm.choice.BreakGenerator; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Nastaran Shafiei <nastaran.shafiei@gmail.com> + * + * This class represents the finalizer thread in VM which is responsible for + * executing finalize() methods upon garbage collection of finalizable objects. + * By a finalizable object, we mean an object, the class of which overrides the + * Object.finalize() method. By default, we do not allow for processing finalizers, + * to enforce that one needs to set the property "vm.process_finalizers" to true. + * + * If "vm.process_finalizers" is set to true, during vm initialization we create + * a FinalizerThreadInfo object per process. ApplicationContext keeps a reference + * to FinalizerThreadInfo of the process. This thread is alive during the entire + * process execution. The finalizer thread is "always" waiting on an internal + * private lock. The JPF Thread object corresponding to the FinalizerThreadInfo + * is encapsulated by the model class gov.nasa.jpf.FinalizerThread. The thread + * encapsulated by FinalizerThread has a queue of objects called "finalizeQueue" + * which is kept at the SUT level. This queue is initially empty, and it is + * populated during the sweep() phase of the garbage collection. During sweep(), + * before removing unmark objects from the heap, any unmark finalizable object is + * marked and added to finalizeQueue. + * + * In VM.forward(), after each garbage collection, VM checks if finalizeQueue of + * the current process finalizer thread is not empty. If so, VM schedules the + * finalizer thread to execute next, i.e. finalizer thread stops waiting and its + * state becomes runnable. To accomplish that, VM replaces the next choiceGenerator + * with a new choice generator from which only finalizer thread can proceed. + * + * After finalizer thread processes the finalize() methods of all objects in + * finalizeQueue, the queue becomes empty and the thread waits on its internal lock + * again. After the process terminates, we still keep the finalizer thread to be + * processed after the last garbage collection involving the process. The runnable + * finalizer thread terminates itself when it has processed its finalizeQueue and + * there is no other alive thread in the process. + */ +public class FinalizerThreadInfo extends ThreadInfo { + + static final String FINALIZER_NAME = "finalizer"; + + ChoiceGenerator<?> replacedCG; + + protected FinalizerThreadInfo (VM vm, ApplicationContext appCtx, int id) { + super(vm, id, appCtx); + + ci = appCtx.getSystemClassLoader().getResolvedClassInfo("gov.nasa.jpf.FinalizerThread"); + threadData.name = FINALIZER_NAME; + + tempFinalizeQueue = new ArrayList<ElementInfo>(); + } + + protected void createFinalizerThreadObject (SystemClassLoaderInfo sysCl){ + Heap heap = getHeap(); + + ElementInfo eiThread = heap.newObject( ci, this); + objRef = eiThread.getObjectRef(); + + ElementInfo eiName = heap.newString(FINALIZER_NAME, this); + int nameRef = eiName.getObjectRef(); + eiThread.setReferenceField("name", nameRef); + + // Since we create FinalizerThread upon VM initialization, they are assigned to the + // same group as the main thread + int grpRef = ThreadInfo.getCurrentThread().getThreadGroupRef(); + eiThread.setReferenceField("group", grpRef); + + eiThread.setIntField("priority", Thread.MAX_PRIORITY-2); + + ClassInfo ciPermit = sysCl.getResolvedClassInfo("java.lang.Thread$Permit"); + ElementInfo eiPermit = heap.newObject( ciPermit, this); + eiPermit.setBooleanField("blockPark", true); + eiThread.setReferenceField("permit", eiPermit.getObjectRef()); + + addToThreadGroup(getElementInfo(grpRef)); + + addId( objRef, id); + + threadData.name = FINALIZER_NAME; + + // start the thread by pushing Thread.run() + startFinalizerThread(); + + eiThread.setBooleanField("done", false); + ElementInfo finalizeQueue = getHeap().newArray("Ljava/lang/Object;", 0, this); + eiThread.setReferenceField("finalizeQueue", finalizeQueue.getObjectRef()); + + // create an internal private lock used for lock-free wait + ElementInfo lock = getHeap().newObject(appCtx.getSystemClassLoader().objectClassInfo, this); + eiThread.setReferenceField("semaphore", lock.getObjectRef()); + + // make it wait on the internal private lock until its finalizeQueue is populated. This way, + // we avoid scheduling points from including FinalizerThreads + waitOnSemaphore(); + + assert this.isWaiting(); + } + + /** + * Pushes a frame corresponding to Thread.run() into the finalizer thread stack to + * start the thread. + */ + protected void startFinalizerThread() { + MethodInfo mi = ci.getMethod("run()V", false); + DirectCallStackFrame frame = mi.createDirectCallStackFrame(this, 0); + frame.setReferenceArgument(0, objRef, frame); + pushFrame(frame); + } + + public boolean hasQueuedFinalizers() { + ElementInfo queue = getFinalizeQueue(); + return (queue!=null && queue.asReferenceArray().length>0); + } + + public ElementInfo getFinalizeQueue() { + ElementInfo ei = vm.getModifiableElementInfo(objRef); + ElementInfo queue = null; + + if(ei!=null) { + int queueRef = ei.getReferenceField("finalizeQueue"); + queue = vm.getModifiableElementInfo(queueRef); + return queue; + } + + return queue; + } + + // all finalizable objects collected during garbage collection are temporarily added to this list + // when VM schedule the finalizer thread, it add all elements to FinalizerThread.finalizeQueue at + // the SUT level. + List<ElementInfo> tempFinalizeQueue; + + /** + * This method is invoked by the sweep() phase of the garbage collection process (GenericHeap.sweep()). + * It adds a given finalizable object to the finalizeQueue array of gov.nasa.jpf.FinalizerThread. + * + * NOTE: this might return a new ElementInfo since we have to modify it (setting the finalized flag) + */ + public ElementInfo getFinalizerQueuedInstance(ElementInfo ei) { + ei = ei.getModifiableInstance(); + + // make sure we process this object finalizer only once + ei.setFinalized(); + + tempFinalizeQueue.add(ei); + + return ei; + } + + /** + * This method adds all finalizable objects observed during the last garbage collection + * to finalizeQueue of FinalizerThread corresponding to this thread + */ + void processNewFinalizables() { + if(!tempFinalizeQueue.isEmpty()) { + + ElementInfo oldQueue = getFinalizeQueue(); + int[] oldValues = oldQueue.asReferenceArray(); + int len = oldValues.length; + + int n = tempFinalizeQueue.size(); + + ElementInfo newQueue = getHeap().newArray("Ljava/lang/Object;", len+n, this); + int[] newValues = newQueue.asReferenceArray(); + + System.arraycopy(oldValues, 0, newValues, 0, len); + + for(ElementInfo ei: tempFinalizeQueue) { + newValues[len++] = ei.getObjectRef(); + } + + vm.getModifiableElementInfo(objRef).setReferenceField("finalizeQueue", newQueue.getObjectRef()); + tempFinalizeQueue.clear(); + + assert hasQueuedFinalizers(); + } + } + + public boolean scheduleFinalizer() { + if(hasQueuedFinalizers() && !isRunnable()) { + SystemState ss = vm.getSystemState(); + replacedCG = ss.getNextChoiceGenerator(); + + // NOTE - before we get here we have already made sure that nextCg is not Cascaded. + // we have to set nextCg to null before setting the nextCG, otherwise, the new CG is + // mistakenly considered as "Cascaded" + ss.nextCg = null; + + // this doesn't have any choice (we need to run the finalizer), and since we don't have + // anything to re-execute (this is called from VM.forward()), we need to be in control of + // type and registration of the CG, hence this doesn't go through the Scheduler/SyncPolicy + ss.setNextChoiceGenerator(new BreakGenerator("finalize", this, false)); + checkNextChoiceGeneratorSet("no transition break prior to finalize"); + + // stop waiting and process finalizers + notifyOnSemaphore(); + assert this.isRunnable(); + + return true; + } + + return false; + } + + protected void waitOnSemaphore() { + int lockRef = vm.getElementInfo(objRef).getReferenceField("semaphore"); + ElementInfo lock = vm.getModifiableElementInfo(lockRef); + + lock.wait(this, 0, false); + } + + protected void notifyOnSemaphore() { + int lockRef = vm.getElementInfo(objRef).getReferenceField("semaphore"); + ElementInfo lock = vm.getModifiableElementInfo(lockRef); + + lock.notifies(vm.getSystemState(), this, false); + } + + // It returns true if the finalizer thread is waiting and do not have any object to + // process + protected boolean isIdle() { + if(this.isWaiting()) { + if(this.lockRef == vm.getElementInfo(objRef).getReferenceField("semaphore")) { + return true; + } + } + return false; + } + + @Override + public boolean isSystemThread() { + return true; + } +}