Fixing a few bugs in the statistics printout.
[jpf-core.git] / src / peers / gov / nasa / jpf / vm / JPF_java_lang_Thread.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.JPF;
21 import gov.nasa.jpf.JPFException;
22 import gov.nasa.jpf.annotation.MJI;
23 import gov.nasa.jpf.util.JPFLogger;
24
25
26 /**
27  * MJI NativePeer class for java.lang.Thread library abstraction
28  * 
29  * NOTE - this implementation depends on all live thread objects being
30  * in ThreadList
31  */
32 public class JPF_java_lang_Thread extends NativePeer {
33
34   static JPFLogger log = JPF.getLogger("gov.nasa.jpf.vm.ThreadInfo");
35   
36   
37   /**
38    * This method is the common initializer for all Thread ctors, and the only
39    * single location where we can init our ThreadInfo, but it is PRIVATE
40    */
41   @MJI
42   public void init0__Ljava_lang_ThreadGroup_2Ljava_lang_Runnable_2Ljava_lang_String_2J__V (MJIEnv env,
43                          int objRef, int groupRef, int runnableRef, int nameRef, long stackSize) {
44     VM vm = env.getVM();
45
46     // we only need to create the ThreadInfo - its initialization will take care
47     // of proper linkage to the java.lang.Thread object (objRef)
48     vm.createThreadInfo( objRef, groupRef, runnableRef, nameRef);
49   }
50
51   @MJI
52   public boolean isAlive____Z (MJIEnv env, int objref) {
53     ThreadInfo ti = env.getThreadInfoForObjRef(objref);
54     if (ti != null){
55       return ti.isAlive();
56     } else {
57       return false; // not in ThreadList anymore
58     }
59   }
60
61   @MJI
62   public void setDaemon0__Z__V (MJIEnv env, int objref, boolean isDaemon) {
63     ThreadInfo ti = env.getThreadInfoForObjRef(objref);
64     ti.setDaemon(isDaemon);
65   }
66
67   @MJI
68   public void dumpStack____V (MJIEnv env, int clsObjRef){
69     ThreadInfo ti = env.getThreadInfo();
70     ti.printStackTrace(); // this is not correct, we should go through VM.print
71   }
72
73   @MJI
74   public void setName0__Ljava_lang_String_2__V (MJIEnv env, int objref, int nameRef) {
75     // it bails if you try to set a null name
76     if (nameRef == MJIEnv.NULL) {
77       env.throwException("java.lang.IllegalArgumentException");
78
79       return;
80     }
81
82     // we have to intercept this to cache the name as a Java object
83     // (to be stored in ThreadData)
84     // luckily enough, it's copied into the java.lang.Thread object
85     // as a char[], i.e. does not have to preserve identity
86     // Note the nastiness in here - the java.lang.Thread object is only used
87     // to get the initial values into ThreadData, and gets inconsistent
88     // if this method is called (just works because the 'name' field is only
89     // directly accessed from within the Thread ctors)
90     ThreadInfo ti = env.getThreadInfoForObjRef(objref);
91     ti.setName(env.getStringObject(nameRef));
92   }
93
94   @MJI
95   public void setPriority0__I__V (MJIEnv env, int objref, int prio) {
96     // again, we have to cache this in ThreadData for performance reasons
97     ThreadInfo ti = env.getThreadInfoForObjRef(objref);
98     
99     if (prio != ti.getPriority()){
100       ti.setPriority(prio);
101     
102       // this could cause a context switch in a priority based scheduler
103       if (ti.getScheduler().setsPriorityCG(ti)){
104         env.repeatInvocation();
105         return;
106       }
107     }
108   }
109
110   @MJI
111   public int countStackFrames____I (MJIEnv env, int objref) {
112     ThreadInfo ti = env.getThreadInfoForObjRef(objref);
113     return ti.countStackFrames();
114   }
115
116   @MJI
117   public int currentThread____Ljava_lang_Thread_2 (MJIEnv env, int clsObjRef) {
118     ThreadInfo ti = env.getThreadInfo();
119     return ti.getThreadObjectRef();
120   }
121
122   @MJI
123   public boolean holdsLock__Ljava_lang_Object_2__Z (MJIEnv env, int clsObjRef, int objref) {
124     ThreadInfo  ti = env.getThreadInfo();
125     ElementInfo ei = env.getElementInfo(objref);
126
127     return ei.isLockedBy(ti);
128   }
129
130   @MJI
131   public void interrupt____V (MJIEnv env, int objref) {
132     ThreadInfo tiCurrent = env.getThreadInfo();
133     ThreadInfo tiInterrupted = env.getThreadInfoForObjRef(objref);
134
135     if (!tiCurrent.isFirstStepInsn()) {
136       tiInterrupted.interrupt();
137     }
138     
139     if (tiCurrent.getScheduler().setsInterruptCG(tiCurrent, tiInterrupted)) {
140       env.repeatInvocation();
141       return;
142     }
143   }
144
145   // these could be in the model, but we keep it symmetric, which also saves
146   // us the effort of avoiding unwanted shared object field access CGs
147   @MJI
148   public boolean isInterrupted____Z (MJIEnv env, int objref) {
149     ThreadInfo ti = env.getThreadInfoForObjRef(objref);
150     return ti.isInterrupted(false);
151   }
152
153   @MJI
154   public boolean interrupted____Z (MJIEnv env, int clsObjRef) {
155     ThreadInfo ti = env.getThreadInfo();
156     return ti.isInterrupted(true);
157   }
158
159   @MJI
160   public void start____V (MJIEnv env, int objref) {
161     ThreadInfo tiCurrent = env.getThreadInfo();
162     ThreadInfo tiStarted = env.getThreadInfoForObjRef(objref);
163     VM vm = tiCurrent.getVM();
164
165     //--- actions that are only performed upon first execution
166     if (!tiCurrent.isFirstStepInsn()){
167       if (tiStarted.isStopped()) {
168         // don't do anything but set it terminated - it hasn't acquired any resources yet.
169         // note that apparently host VMs don't schedule this thread, so there is no handler invocation
170         tiStarted.setTerminated();
171         return;
172       }
173
174       if (!tiStarted.isNew()) {
175         // alredy running, throw a IllegalThreadStateException. If it already terminated, it just gets
176         // silently ignored in Java 1.4, but the 1.5 spec explicitly marks this
177         // as illegal, so we adopt this by throwing an IllegalThreadState, too
178         env.throwException("java.lang.IllegalThreadStateException");
179         return;
180       }
181
182       int runnableRef = tiStarted.getRunnableRef();
183       if (runnableRef == MJIEnv.NULL) {
184         // note that we don't set the 'tiSuspended' field, since java.lang.Thread doesn't
185         runnableRef = objref;
186       }
187
188       ElementInfo eiTarget = env.getElementInfo(runnableRef);
189       ClassInfo   ci = eiTarget.getClassInfo();
190       MethodInfo  miRun = ci.getMethod("run()V", true);
191
192       // we do direct call run() invocation so that we have a well defined
193       // exit point (DIRECTCALLRETURN) in case the thread is stopped or there is
194       // a fail-safe UncaughtExceptionHandler set
195       DirectCallStackFrame runFrame = miRun.createRunStartStackFrame(tiStarted);
196       runFrame.setReferenceArgument(0, runnableRef, null);
197             
198       tiStarted.pushFrame(runFrame);
199       tiStarted.setState(ThreadInfo.State.RUNNING);
200       
201       vm.notifyThreadStarted(tiStarted);
202     }
203     
204     //--- scheduling point
205     if (tiCurrent.getScheduler().setsStartCG(tiCurrent, tiStarted)){
206       env.repeatInvocation();
207     }
208     // everything that would follow would be re-executed
209   }
210
211   @MJI
212   public void yield____V (MJIEnv env, int clsObjRef) {
213     ThreadInfo ti = env.getThreadInfo();
214     if (ti.getScheduler().setsYieldCG(ti)){
215       env.repeatInvocation();
216     }
217   }
218
219   @MJI
220   public void sleep__JI__V (MJIEnv env, int clsObjRef, long millis, int nanos) {
221     ThreadInfo ti = env.getThreadInfo();
222
223     // check scheduling point
224     if (ti.getScheduler().setsSleepCG(ti, millis, nanos)){
225       ti.setSleeping();
226       env.repeatInvocation();
227       return;
228     }
229     
230     if (ti.isSleeping()){
231       ti.setRunning();
232     }
233   }
234
235   @MJI
236   public void suspend____V (MJIEnv env, int threadObjRef) {
237     ThreadInfo tiCurrent = env.getThreadInfo();
238     ThreadInfo tiSuspended = env.getThreadInfoForObjRef(threadObjRef);
239
240     if (tiSuspended.isTerminated()) {
241       return;  // nothing to do, it's already gone
242     }
243     
244     if (!tiCurrent.isFirstStepInsn()){ // do this just once
245       tiSuspended.suspend();
246     }
247     
248     // scheduling point
249     if (tiCurrent.getScheduler().setsSuspendCG(tiCurrent, tiSuspended)){
250       env.repeatInvocation();      
251     }
252   }
253
254   @MJI
255   public void resume____V (MJIEnv env, int threadObjRef) {
256     ThreadInfo tiCurrent = env.getThreadInfo();
257     ThreadInfo tiResumed = env.getThreadInfoForObjRef(threadObjRef);
258
259     if (tiCurrent == tiResumed){
260       return;  // no self resume prior to suspension
261     }
262
263     if (tiResumed.isTerminated()) {
264       return;  // nothing to resume
265     }
266
267     if (!tiCurrent.isFirstStepInsn()) { // do this just once
268       tiResumed.resume();
269     }
270     
271     // check scheduling point
272     if (tiCurrent.getScheduler().setsResumeCG(tiCurrent, tiResumed)){
273       env.repeatInvocation();
274       return;
275     }
276   }
277
278   /*
279    * the join() workhorse. We use lockfree waits instead of a simple wait from a synchronized block
280    * to save states
281    */
282   protected void join0 (MJIEnv env, int joineeRef, long timeout){
283     ThreadInfo tiCurrent = env.getThreadInfo();
284     ThreadInfo tiJoinee = env.getThreadInfoForObjRef(joineeRef);
285     ElementInfo eiJoinee = env.getModifiableElementInfo(joineeRef); // the thread object to wait on
286
287     if (timeout < 0) {
288       env.throwException("java.lang.IllegalArgumentException", "timeout value is negative");
289       return;
290     }
291       
292     if (tiCurrent.isInterrupted(true)){ // interrupt status is set, throw and bail      
293       // since we use lock-free waits, we need to remove ourselves from the lock contender list
294       eiJoinee.setMonitorWithoutLocked(tiCurrent);
295       
296       // note that we have to throw even if the thread to join to is not alive anymore
297       env.throwInterrupt();
298       return;
299     }
300   
301     if (!tiCurrent.isFirstStepInsn()){ // to be executed only once    
302       if (tiJoinee.isAlive()) {
303         // block in first top half so that following transitions see this thread as not runnable
304         eiJoinee.wait( tiCurrent, timeout, false);
305       } else {
306         return; // nothing to do
307       }
308     }    
309
310     if (tiCurrent.getScheduler().setsJoinCG(tiCurrent, tiJoinee, timeout)) {
311       env.repeatInvocation();
312       return;
313     }
314     
315     // unblock in bottom half
316     switch (tiCurrent.getState()) {
317       case WAITING:
318       case TIMEOUT_WAITING:
319         throw new JPFException("blocking join without transition break");        
320       
321       case UNBLOCKED:
322         // Thread was owning the lock when it joined - we have to wait until
323         // we can reacquire it
324         eiJoinee.lockNotified(tiCurrent);
325         break;
326
327       case TIMEDOUT:
328         eiJoinee.resumeNonlockedWaiter(tiCurrent);
329         break;
330
331       case RUNNING:
332         if (tiJoinee.isAlive()) { // we still need to wait
333           eiJoinee.wait(tiCurrent, timeout, false); // no need for a new CG
334           env.repeatInvocation();
335         }
336         break;
337     }
338   }
339
340   @MJI
341   public void join____V (MJIEnv env, int objref){
342     join0(env,objref,0);
343   }
344
345   @MJI
346   public void join__J__V (MJIEnv env, int objref, long millis) {
347     join0(env,objref,millis);
348
349   }
350
351   @MJI
352   public void join__JI__V (MJIEnv env, int objref, long millis, int nanos) {
353     join0(env,objref,millis); // <2do> we ignore nanos for now
354   }
355
356   @MJI
357   public int getState0____I (MJIEnv env, int objref) {
358     // return the state index with respect to one of the public Thread.States
359     ThreadInfo ti = env.getThreadInfoForObjRef(objref);
360
361     switch (ti.getState()) {
362       case NEW:
363         return 1;
364       case RUNNING:
365         return 2;
366       case BLOCKED:
367         return 0;
368       case UNBLOCKED:
369         return 2;
370       case WAITING:
371         return 5;
372       case TIMEOUT_WAITING:
373         return 4;
374       case SLEEPING:
375         return 4;
376       case NOTIFIED:
377         return 0;
378       case INTERRUPTED:
379         return 0;
380       case TIMEDOUT:
381         return 2;
382       case TERMINATED:
383         return 3;
384       default:
385         throw new JPFException("illegal thread state: " + ti.getState());
386     }
387   }
388
389   @MJI
390   public long getId____J (MJIEnv env, int objref) {
391     ThreadInfo ti = env.getThreadInfoForObjRef(objref);
392     return ti.getId();
393   }
394   
395   @MJI
396   public void stop____V (MJIEnv env, int threadRef) {
397     stop__Ljava_lang_Throwable_2__V(env, threadRef, MJIEnv.NULL);
398   }
399
400   @MJI
401   public void stop__Ljava_lang_Throwable_2__V (MJIEnv env, int threadRef, int throwableRef) {
402     ThreadInfo tiCurrent = env.getThreadInfo();
403     ThreadInfo tiStopped = env.getThreadInfoForObjRef(threadRef);
404
405     if (tiStopped.isTerminated() || tiStopped.isStopped()) {
406       return; // silently ignored
407     }
408
409     if (tiCurrent.getScheduler().setsStopCG(tiCurrent, tiStopped)){
410       env.repeatInvocation();
411       return;
412     }
413
414     // stop thread in bottom half
415     tiStopped.setStopped(throwableRef);
416   }
417 }