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 // 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);
52 public boolean isAlive____Z (MJIEnv env, int objref) {
53 ThreadInfo ti = env.getThreadInfoForObjRef(objref);
57 return false; // not in ThreadList anymore
62 public void setDaemon0__Z__V (MJIEnv env, int objref, boolean isDaemon) {
63 ThreadInfo ti = env.getThreadInfoForObjRef(objref);
64 ti.setDaemon(isDaemon);
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
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");
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));
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);
99 if (prio != ti.getPriority()){
100 ti.setPriority(prio);
102 // this could cause a context switch in a priority based scheduler
103 if (ti.getScheduler().setsPriorityCG(ti)){
104 env.repeatInvocation();
111 public int countStackFrames____I (MJIEnv env, int objref) {
112 ThreadInfo ti = env.getThreadInfoForObjRef(objref);
113 return ti.countStackFrames();
117 public int currentThread____Ljava_lang_Thread_2 (MJIEnv env, int clsObjRef) {
118 ThreadInfo ti = env.getThreadInfo();
119 return ti.getThreadObjectRef();
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);
127 return ei.isLockedBy(ti);
131 public void interrupt____V (MJIEnv env, int objref) {
132 ThreadInfo tiCurrent = env.getThreadInfo();
133 ThreadInfo tiInterrupted = env.getThreadInfoForObjRef(objref);
135 if (!tiCurrent.isFirstStepInsn()) {
136 tiInterrupted.interrupt();
139 if (tiCurrent.getScheduler().setsInterruptCG(tiCurrent, tiInterrupted)) {
140 env.repeatInvocation();
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
148 public boolean isInterrupted____Z (MJIEnv env, int objref) {
149 ThreadInfo ti = env.getThreadInfoForObjRef(objref);
150 return ti.isInterrupted(false);
154 public boolean interrupted____Z (MJIEnv env, int clsObjRef) {
155 ThreadInfo ti = env.getThreadInfo();
156 return ti.isInterrupted(true);
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();
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();
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");
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;
188 ElementInfo eiTarget = env.getElementInfo(runnableRef);
189 ClassInfo ci = eiTarget.getClassInfo();
190 MethodInfo miRun = ci.getMethod("run()V", true);
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);
198 tiStarted.pushFrame(runFrame);
199 tiStarted.setState(ThreadInfo.State.RUNNING);
201 vm.notifyThreadStarted(tiStarted);
204 //--- scheduling point
205 if (tiCurrent.getScheduler().setsStartCG(tiCurrent, tiStarted)){
206 env.repeatInvocation();
208 // everything that would follow would be re-executed
212 public void yield____V (MJIEnv env, int clsObjRef) {
213 ThreadInfo ti = env.getThreadInfo();
214 if (ti.getScheduler().setsYieldCG(ti)){
215 env.repeatInvocation();
220 public void sleep__JI__V (MJIEnv env, int clsObjRef, long millis, int nanos) {
221 ThreadInfo ti = env.getThreadInfo();
223 // check scheduling point
224 if (ti.getScheduler().setsSleepCG(ti, millis, nanos)){
226 env.repeatInvocation();
230 if (ti.isSleeping()){
236 public void suspend____V (MJIEnv env, int threadObjRef) {
237 ThreadInfo tiCurrent = env.getThreadInfo();
238 ThreadInfo tiSuspended = env.getThreadInfoForObjRef(threadObjRef);
240 if (tiSuspended.isTerminated()) {
241 return; // nothing to do, it's already gone
244 if (!tiCurrent.isFirstStepInsn()){ // do this just once
245 tiSuspended.suspend();
249 if (tiCurrent.getScheduler().setsSuspendCG(tiCurrent, tiSuspended)){
250 env.repeatInvocation();
255 public void resume____V (MJIEnv env, int threadObjRef) {
256 ThreadInfo tiCurrent = env.getThreadInfo();
257 ThreadInfo tiResumed = env.getThreadInfoForObjRef(threadObjRef);
259 if (tiCurrent == tiResumed){
260 return; // no self resume prior to suspension
263 if (tiResumed.isTerminated()) {
264 return; // nothing to resume
267 if (!tiCurrent.isFirstStepInsn()) { // do this just once
271 // check scheduling point
272 if (tiCurrent.getScheduler().setsResumeCG(tiCurrent, tiResumed)){
273 env.repeatInvocation();
279 * the join() workhorse. We use lockfree waits instead of a simple wait from a synchronized block
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
288 env.throwException("java.lang.IllegalArgumentException", "timeout value is negative");
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);
296 // note that we have to throw even if the thread to join to is not alive anymore
297 env.throwInterrupt();
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);
306 return; // nothing to do
310 if (tiCurrent.getScheduler().setsJoinCG(tiCurrent, tiJoinee, timeout)) {
311 env.repeatInvocation();
315 // unblock in bottom half
316 switch (tiCurrent.getState()) {
318 case TIMEOUT_WAITING:
319 throw new JPFException("blocking join without transition break");
322 // Thread was owning the lock when it joined - we have to wait until
323 // we can reacquire it
324 eiJoinee.lockNotified(tiCurrent);
328 eiJoinee.resumeNonlockedWaiter(tiCurrent);
332 if (tiJoinee.isAlive()) { // we still need to wait
333 eiJoinee.wait(tiCurrent, timeout, false); // no need for a new CG
334 env.repeatInvocation();
341 public void join____V (MJIEnv env, int objref){
346 public void join__J__V (MJIEnv env, int objref, long millis) {
347 join0(env,objref,millis);
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
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);
361 switch (ti.getState()) {
372 case TIMEOUT_WAITING:
385 throw new JPFException("illegal thread state: " + ti.getState());
390 public long getId____J (MJIEnv env, int objref) {
391 ThreadInfo ti = env.getThreadInfoForObjRef(objref);
396 public void stop____V (MJIEnv env, int threadRef) {
397 stop__Ljava_lang_Throwable_2__V(env, threadRef, MJIEnv.NULL);
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);
405 if (tiStopped.isTerminated() || tiStopped.isStopped()) {
406 return; // silently ignored
409 if (tiCurrent.getScheduler().setsStopCG(tiCurrent, tiStopped)){
410 env.repeatInvocation();
414 // stop thread in bottom half
415 tiStopped.setStopped(throwableRef);