2 * Copyright (C) 2014, United States Government, as represented by the
3 * Administrator of the National Aeronautics and Space Administration.
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
10 * http://www.apache.org/licenses/LICENSE-2.0.
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.
18 package gov.nasa.jpf.vm;
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;
27 * MJI NativePeer class for java.lang.Thread library abstraction
29 * NOTE - this implementation depends on all live thread objects being
32 public class JPF_java_lang_Thread extends NativePeer {
34 static JPFLogger log = JPF.getLogger("gov.nasa.jpf.vm.ThreadInfo");
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
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) {
46 // TODO: Fix for Groovy's model-checking
47 // we only need to create the ThreadInfo - its initialization will take care
48 // of proper linkage to the java.lang.Thread object (objRef)
49 vm.createThreadInfo( objRef, groupRef, runnableRef, nameRef);
53 public boolean isAlive____Z (MJIEnv env, int objref) {
54 ThreadInfo ti = env.getThreadInfoForObjRef(objref);
58 return false; // not in ThreadList anymore
63 public void setDaemon0__Z__V (MJIEnv env, int objref, boolean isDaemon) {
64 ThreadInfo ti = env.getThreadInfoForObjRef(objref);
65 ti.setDaemon(isDaemon);
69 public void dumpStack____V (MJIEnv env, int clsObjRef){
70 ThreadInfo ti = env.getThreadInfo();
71 ti.printStackTrace(); // this is not correct, we should go through VM.print
75 public void setName0__Ljava_lang_String_2__V (MJIEnv env, int objref, int nameRef) {
76 // it bails if you try to set a null name
77 if (nameRef == MJIEnv.NULL) {
78 env.throwException("java.lang.IllegalArgumentException");
83 // we have to intercept this to cache the name as a Java object
84 // (to be stored in ThreadData)
85 // luckily enough, it's copied into the java.lang.Thread object
86 // as a char[], i.e. does not have to preserve identity
87 // Note the nastiness in here - the java.lang.Thread object is only used
88 // to get the initial values into ThreadData, and gets inconsistent
89 // if this method is called (just works because the 'name' field is only
90 // directly accessed from within the Thread ctors)
91 ThreadInfo ti = env.getThreadInfoForObjRef(objref);
92 ti.setName(env.getStringObject(nameRef));
96 public void setPriority0__I__V (MJIEnv env, int objref, int prio) {
97 // again, we have to cache this in ThreadData for performance reasons
98 ThreadInfo ti = env.getThreadInfoForObjRef(objref);
100 if (prio != ti.getPriority()){
101 ti.setPriority(prio);
103 // this could cause a context switch in a priority based scheduler
104 if (ti.getScheduler().setsPriorityCG(ti)){
105 env.repeatInvocation();
112 public int countStackFrames____I (MJIEnv env, int objref) {
113 ThreadInfo ti = env.getThreadInfoForObjRef(objref);
114 return ti.countStackFrames();
118 public int currentThread____Ljava_lang_Thread_2 (MJIEnv env, int clsObjRef) {
119 ThreadInfo ti = env.getThreadInfo();
120 return ti.getThreadObjectRef();
124 public boolean holdsLock__Ljava_lang_Object_2__Z (MJIEnv env, int clsObjRef, int objref) {
125 ThreadInfo ti = env.getThreadInfo();
126 ElementInfo ei = env.getElementInfo(objref);
128 return ei.isLockedBy(ti);
132 public void interrupt____V (MJIEnv env, int objref) {
133 ThreadInfo tiCurrent = env.getThreadInfo();
134 ThreadInfo tiInterrupted = env.getThreadInfoForObjRef(objref);
136 if (!tiCurrent.isFirstStepInsn()) {
137 tiInterrupted.interrupt();
140 if (tiCurrent.getScheduler().setsInterruptCG(tiCurrent, tiInterrupted)) {
141 env.repeatInvocation();
146 // these could be in the model, but we keep it symmetric, which also saves
147 // us the effort of avoiding unwanted shared object field access CGs
149 public boolean isInterrupted____Z (MJIEnv env, int objref) {
150 ThreadInfo ti = env.getThreadInfoForObjRef(objref);
151 return ti.isInterrupted(false);
155 public boolean interrupted____Z (MJIEnv env, int clsObjRef) {
156 ThreadInfo ti = env.getThreadInfo();
157 return ti.isInterrupted(true);
161 public void start____V (MJIEnv env, int objref) {
162 ThreadInfo tiCurrent = env.getThreadInfo();
163 ThreadInfo tiStarted = env.getThreadInfoForObjRef(objref);
164 VM vm = tiCurrent.getVM();
166 //--- actions that are only performed upon first execution
167 if (!tiCurrent.isFirstStepInsn()){
168 if (tiStarted.isStopped()) {
169 // don't do anything but set it terminated - it hasn't acquired any resources yet.
170 // note that apparently host VMs don't schedule this thread, so there is no handler invocation
171 tiStarted.setTerminated();
175 if (!tiStarted.isNew()) {
176 // alredy running, throw a IllegalThreadStateException. If it already terminated, it just gets
177 // silently ignored in Java 1.4, but the 1.5 spec explicitly marks this
178 // as illegal, so we adopt this by throwing an IllegalThreadState, too
179 env.throwException("java.lang.IllegalThreadStateException");
183 int runnableRef = tiStarted.getRunnableRef();
184 if (runnableRef == MJIEnv.NULL) {
185 // note that we don't set the 'tiSuspended' field, since java.lang.Thread doesn't
186 runnableRef = objref;
189 ElementInfo eiTarget = env.getElementInfo(runnableRef);
190 ClassInfo ci = eiTarget.getClassInfo();
191 MethodInfo miRun = ci.getMethod("run()V", true);
193 // we do direct call run() invocation so that we have a well defined
194 // exit point (DIRECTCALLRETURN) in case the thread is stopped or there is
195 // a fail-safe UncaughtExceptionHandler set
196 DirectCallStackFrame runFrame = miRun.createRunStartStackFrame(tiStarted);
197 runFrame.setReferenceArgument(0, runnableRef, null);
199 tiStarted.pushFrame(runFrame);
200 tiStarted.setState(ThreadInfo.State.RUNNING);
202 vm.notifyThreadStarted(tiStarted);
205 //--- scheduling point
206 if (tiCurrent.getScheduler().setsStartCG(tiCurrent, tiStarted)){
207 env.repeatInvocation();
209 // everything that would follow would be re-executed
213 public void yield____V (MJIEnv env, int clsObjRef) {
214 ThreadInfo ti = env.getThreadInfo();
215 if (ti.getScheduler().setsYieldCG(ti)){
216 env.repeatInvocation();
221 public void sleep__JI__V (MJIEnv env, int clsObjRef, long millis, int nanos) {
222 ThreadInfo ti = env.getThreadInfo();
224 // check scheduling point
225 if (ti.getScheduler().setsSleepCG(ti, millis, nanos)){
227 env.repeatInvocation();
231 if (ti.isSleeping()){
237 public void suspend____V (MJIEnv env, int threadObjRef) {
238 ThreadInfo tiCurrent = env.getThreadInfo();
239 ThreadInfo tiSuspended = env.getThreadInfoForObjRef(threadObjRef);
241 if (tiSuspended.isTerminated()) {
242 return; // nothing to do, it's already gone
245 if (!tiCurrent.isFirstStepInsn()){ // do this just once
246 tiSuspended.suspend();
250 if (tiCurrent.getScheduler().setsSuspendCG(tiCurrent, tiSuspended)){
251 env.repeatInvocation();
256 public void resume____V (MJIEnv env, int threadObjRef) {
257 ThreadInfo tiCurrent = env.getThreadInfo();
258 ThreadInfo tiResumed = env.getThreadInfoForObjRef(threadObjRef);
260 if (tiCurrent == tiResumed){
261 return; // no self resume prior to suspension
264 if (tiResumed.isTerminated()) {
265 return; // nothing to resume
268 if (!tiCurrent.isFirstStepInsn()) { // do this just once
272 // check scheduling point
273 if (tiCurrent.getScheduler().setsResumeCG(tiCurrent, tiResumed)){
274 env.repeatInvocation();
280 * the join() workhorse. We use lockfree waits instead of a simple wait from a synchronized block
283 protected void join0 (MJIEnv env, int joineeRef, long timeout){
284 ThreadInfo tiCurrent = env.getThreadInfo();
285 ThreadInfo tiJoinee = env.getThreadInfoForObjRef(joineeRef);
286 ElementInfo eiJoinee = env.getModifiableElementInfo(joineeRef); // the thread object to wait on
289 env.throwException("java.lang.IllegalArgumentException", "timeout value is negative");
293 if (tiCurrent.isInterrupted(true)){ // interrupt status is set, throw and bail
294 // since we use lock-free waits, we need to remove ourselves from the lock contender list
295 eiJoinee.setMonitorWithoutLocked(tiCurrent);
297 // note that we have to throw even if the thread to join to is not alive anymore
298 env.throwInterrupt();
302 if (!tiCurrent.isFirstStepInsn()){ // to be executed only once
303 if (tiJoinee.isAlive()) {
304 // block in first top half so that following transitions see this thread as not runnable
305 eiJoinee.wait( tiCurrent, timeout, false);
307 return; // nothing to do
311 if (tiCurrent.getScheduler().setsJoinCG(tiCurrent, tiJoinee, timeout)) {
312 env.repeatInvocation();
316 // unblock in bottom half
317 switch (tiCurrent.getState()) {
319 case TIMEOUT_WAITING:
320 throw new JPFException("blocking join without transition break");
323 // Thread was owning the lock when it joined - we have to wait until
324 // we can reacquire it
325 eiJoinee.lockNotified(tiCurrent);
329 eiJoinee.resumeNonlockedWaiter(tiCurrent);
333 if (tiJoinee.isAlive()) { // we still need to wait
334 eiJoinee.wait(tiCurrent, timeout, false); // no need for a new CG
335 env.repeatInvocation();
342 public void join____V (MJIEnv env, int objref){
347 public void join__J__V (MJIEnv env, int objref, long millis) {
348 join0(env,objref,millis);
353 public void join__JI__V (MJIEnv env, int objref, long millis, int nanos) {
354 join0(env,objref,millis); // <2do> we ignore nanos for now
358 public int getState0____I (MJIEnv env, int objref) {
359 // return the state index with respect to one of the public Thread.States
360 ThreadInfo ti = env.getThreadInfoForObjRef(objref);
362 switch (ti.getState()) {
373 case TIMEOUT_WAITING:
386 throw new JPFException("illegal thread state: " + ti.getState());
391 public long getId____J (MJIEnv env, int objref) {
392 ThreadInfo ti = env.getThreadInfoForObjRef(objref);
397 public void stop____V (MJIEnv env, int threadRef) {
398 stop__Ljava_lang_Throwable_2__V(env, threadRef, MJIEnv.NULL);
402 public void stop__Ljava_lang_Throwable_2__V (MJIEnv env, int threadRef, int throwableRef) {
403 ThreadInfo tiCurrent = env.getThreadInfo();
404 ThreadInfo tiStopped = env.getThreadInfoForObjRef(threadRef);
406 if (tiStopped.isTerminated() || tiStopped.isStopped()) {
407 return; // silently ignored
410 if (tiCurrent.getScheduler().setsStopCG(tiCurrent, tiStopped)){
411 env.repeatInvocation();
415 // stop thread in bottom half
416 tiStopped.setStopped(throwableRef);