Fixing a few bugs in the statistics printout.
[jpf-core.git] / src / main / gov / nasa / jpf / vm / FinalizerThreadInfo.java
1 /*
2  * Copyright (C) 2014, United States Government, as represented by the
3  * Administrator of the National Aeronautics and Space Administration.
4  * All rights reserved.
5  *
6  * The Java Pathfinder core (jpf-core) platform is licensed under the
7  * Apache License, Version 2.0 (the "License"); you may not use this file except
8  * in compliance with the License. You may obtain a copy of the License at
9  * 
10  *        http://www.apache.org/licenses/LICENSE-2.0. 
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and 
16  * limitations under the License.
17  */
18 package gov.nasa.jpf.vm;
19
20 import gov.nasa.jpf.vm.choice.BreakGenerator;
21 import java.util.ArrayList;
22 import java.util.List;
23
24 /**
25  * @author Nastaran Shafiei <nastaran.shafiei@gmail.com>
26  *  
27  * This class represents the finalizer thread in VM which is responsible for
28  * executing finalize() methods upon garbage collection of finalizable objects.
29  * By a finalizable object, we mean an object, the class of which overrides the
30  * Object.finalize() method. By default, we do not allow for processing finalizers, 
31  * to enforce that one needs to set the property "vm.process_finalizers" to true.
32  * 
33  * If "vm.process_finalizers" is set to true, during vm initialization we create
34  * a FinalizerThreadInfo object per process. ApplicationContext keeps a reference
35  * to FinalizerThreadInfo of the process. This thread is alive during the entire
36  * process execution. The finalizer thread is "always" waiting on an internal
37  * private lock. The JPF Thread object corresponding to the FinalizerThreadInfo 
38  * is encapsulated by the model class gov.nasa.jpf.FinalizerThread. The thread 
39  * encapsulated by FinalizerThread has a queue of objects called "finalizeQueue"
40  * which is kept at the SUT level. This queue is initially empty, and it is 
41  * populated during the sweep() phase of the garbage collection. During sweep(), 
42  * before removing unmark objects from the heap, any unmark finalizable object is 
43  * marked and added to finalizeQueue. 
44  * 
45  * In VM.forward(), after each garbage collection, VM checks if finalizeQueue of 
46  * the current process finalizer thread is not empty. If so, VM schedules the
47  * finalizer thread to execute next, i.e. finalizer thread stops waiting and its 
48  * state becomes runnable. To accomplish that, VM replaces the next choiceGenerator 
49  * with a new choice generator from which only finalizer thread can proceed.
50  *  
51  * After finalizer thread processes the finalize() methods of all objects in
52  * finalizeQueue, the queue becomes empty and the thread waits on its internal lock
53  * again. After the process terminates, we still keep the finalizer thread to be 
54  * processed after the last garbage collection involving the process. The runnable
55  * finalizer thread terminates itself when it has processed its finalizeQueue and 
56  * there is no other alive thread in the process.
57  */
58 public class FinalizerThreadInfo extends ThreadInfo {
59   
60   static final String FINALIZER_NAME = "finalizer";
61   
62   ChoiceGenerator<?> replacedCG;
63   
64   protected FinalizerThreadInfo (VM vm, ApplicationContext appCtx, int id) {
65     super(vm, id, appCtx);
66     
67     ci = appCtx.getSystemClassLoader().getResolvedClassInfo("gov.nasa.jpf.FinalizerThread");
68     threadData.name = FINALIZER_NAME;
69     
70     tempFinalizeQueue = new ArrayList<ElementInfo>();
71   }
72   
73   protected void createFinalizerThreadObject (SystemClassLoaderInfo sysCl){
74     Heap heap = getHeap();
75
76     ElementInfo eiThread = heap.newObject( ci, this);
77     objRef = eiThread.getObjectRef();
78     
79     ElementInfo eiName = heap.newString(FINALIZER_NAME, this);
80     int nameRef = eiName.getObjectRef();
81     eiThread.setReferenceField("name", nameRef);
82     
83     // Since we create FinalizerThread upon VM initialization, they are assigned to the
84     // same group as the main thread
85     int grpRef = ThreadInfo.getCurrentThread().getThreadGroupRef();
86     eiThread.setReferenceField("group", grpRef);
87     
88     eiThread.setIntField("priority", Thread.MAX_PRIORITY-2);
89
90     ClassInfo ciPermit = sysCl.getResolvedClassInfo("java.lang.Thread$Permit");
91     ElementInfo eiPermit = heap.newObject( ciPermit, this);
92     eiPermit.setBooleanField("blockPark", true);
93     eiThread.setReferenceField("permit", eiPermit.getObjectRef());
94
95     addToThreadGroup(getElementInfo(grpRef));
96     
97     addId( objRef, id);
98     
99     threadData.name = FINALIZER_NAME;
100
101     // start the thread by pushing Thread.run()
102     startFinalizerThread();
103     
104     eiThread.setBooleanField("done", false);
105     ElementInfo finalizeQueue = getHeap().newArray("Ljava/lang/Object;", 0, this);
106     eiThread.setReferenceField("finalizeQueue", finalizeQueue.getObjectRef());
107     
108     // create an internal private lock used for lock-free wait
109     ElementInfo lock = getHeap().newObject(appCtx.getSystemClassLoader().objectClassInfo, this);
110     eiThread.setReferenceField("semaphore", lock.getObjectRef());
111     
112     // make it wait on the internal private lock until its finalizeQueue is populated. This way,
113     // we avoid scheduling points from including FinalizerThreads
114     waitOnSemaphore();
115
116     assert this.isWaiting();
117   }
118   
119   /**
120    * Pushes a frame corresponding to Thread.run() into the finalizer thread stack to
121    * start the thread.
122    */
123   protected void startFinalizerThread() {
124     MethodInfo mi = ci.getMethod("run()V", false);
125     DirectCallStackFrame frame = mi.createDirectCallStackFrame(this, 0);
126     frame.setReferenceArgument(0, objRef, frame);
127     pushFrame(frame);
128   }
129   
130   public boolean hasQueuedFinalizers() {
131     ElementInfo queue = getFinalizeQueue();
132     return (queue!=null && queue.asReferenceArray().length>0);
133   }
134   
135   public ElementInfo getFinalizeQueue() {
136     ElementInfo ei = vm.getModifiableElementInfo(objRef);
137     ElementInfo queue = null;
138     
139     if(ei!=null) {
140       int queueRef = ei.getReferenceField("finalizeQueue");
141       queue = vm.getModifiableElementInfo(queueRef);
142       return queue;
143     }
144     
145     return queue;
146   }
147   
148   // all finalizable objects collected during garbage collection are temporarily added to this list
149   // when VM schedule the finalizer thread, it add all elements to FinalizerThread.finalizeQueue at
150   // the SUT level.
151   List<ElementInfo> tempFinalizeQueue;
152   
153   /** 
154    * This method is invoked by the sweep() phase of the garbage collection process (GenericHeap.sweep()).
155    * It adds a given finalizable object to the finalizeQueue array of gov.nasa.jpf.FinalizerThread.
156    * 
157    * NOTE: this might return a new ElementInfo since we have to modify it (setting the finalized flag)
158    */
159   public ElementInfo getFinalizerQueuedInstance(ElementInfo ei) {
160     ei = ei.getModifiableInstance();
161     
162     // make sure we process this object finalizer only once
163     ei.setFinalized();
164     
165     tempFinalizeQueue.add(ei);
166     
167     return ei;
168   }
169   
170   /**
171    * This method adds all finalizable objects observed during the last garbage collection
172    * to finalizeQueue of FinalizerThread corresponding to this thread
173    */
174   void processNewFinalizables() {
175     if(!tempFinalizeQueue.isEmpty()) {
176       
177       ElementInfo oldQueue = getFinalizeQueue();
178       int[] oldValues = oldQueue.asReferenceArray();    
179       int len = oldValues.length;
180       
181       int n = tempFinalizeQueue.size();
182       
183       ElementInfo newQueue = getHeap().newArray("Ljava/lang/Object;", len+n, this);
184       int[] newValues = newQueue.asReferenceArray();
185       
186       System.arraycopy(oldValues, 0, newValues, 0, len);
187       
188       for(ElementInfo ei: tempFinalizeQueue) {
189         newValues[len++] = ei.getObjectRef();
190       }
191       
192       vm.getModifiableElementInfo(objRef).setReferenceField("finalizeQueue", newQueue.getObjectRef());
193       tempFinalizeQueue.clear();
194       
195       assert hasQueuedFinalizers();
196     }
197   }
198   
199   public boolean scheduleFinalizer() {
200     if(hasQueuedFinalizers() && !isRunnable()) {
201       SystemState ss = vm.getSystemState();
202       replacedCG = ss.getNextChoiceGenerator();
203       
204       // NOTE - before we get here we have already made sure that nextCg is not Cascaded. 
205       // we have to set nextCg to null before setting the nextCG, otherwise, the new CG is 
206       // mistakenly considered as "Cascaded"
207       ss.nextCg = null;
208       
209       // this doesn't have any choice (we need to run the finalizer), and since we don't have
210       // anything to re-execute (this is called from VM.forward()), we need to be in control of 
211       // type and registration of the CG, hence this doesn't go through the Scheduler/SyncPolicy
212       ss.setNextChoiceGenerator(new BreakGenerator("finalize", this, false));
213       checkNextChoiceGeneratorSet("no transition break prior to finalize");
214       
215       // stop waiting and process finalizers
216       notifyOnSemaphore();
217       assert this.isRunnable();
218       
219       return true;
220     } 
221     
222     return false;
223   }
224   
225   protected void waitOnSemaphore() {
226     int lockRef = vm.getElementInfo(objRef).getReferenceField("semaphore");
227     ElementInfo lock = vm.getModifiableElementInfo(lockRef);
228     
229     lock.wait(this, 0, false);
230   }
231   
232   protected void notifyOnSemaphore() {
233     int lockRef = vm.getElementInfo(objRef).getReferenceField("semaphore");
234     ElementInfo lock = vm.getModifiableElementInfo(lockRef);
235     
236     lock.notifies(vm.getSystemState(), this, false);
237   }
238   
239   // It returns true if the finalizer thread is waiting and do not have any object to
240   // process
241   protected boolean isIdle() {
242     if(this.isWaiting()) {
243       if(this.lockRef == vm.getElementInfo(objRef).getReferenceField("semaphore")) {
244         return true;
245       }
246     }
247     return false;
248   }
249   
250   @Override
251   public boolean isSystemThread() {
252     return true;
253   }
254 }