// // Copyright (C) 2006 United States Government as represented by the // Administrator of the National Aeronautics and Space Administration // (NASA). All Rights Reserved. // // This software is distributed under the NASA Open Source Agreement // (NOSA), version 1.3. The NOSA has been approved by the Open Source // Initiative. See the file NOSA-1.3-JPF at the top of the distribution // directory tree for the complete NOSA document. // // THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY // KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT // LIMITED TO, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL CONFORM TO // SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR // A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT // THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT // DOCUMENTATION, IF PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE. // package gov.nasa.jpf.vm; import gov.nasa.jpf.Config; import gov.nasa.jpf.vm.choice.BreakGenerator; import gov.nasa.jpf.vm.choice.ThreadChoiceFromSet; import java.util.Random; /** * the general policy is that we only create Thread CGs here (based on their * status), but we don't change any thread or lock status. This has to happen * in the instruction before calling the factory */ public class DefaultSchedulerFactory implements SchedulerFactory { protected VM vm; protected SystemState ss; //--- those are configured boolean breakStart; boolean breakYield; boolean breakSleep; boolean breakSyncExit; boolean breakArrayAccess; boolean breakSingleChoice; public DefaultSchedulerFactory (Config config, VM vm, SystemState ss) { this.vm = vm; this.ss = ss; breakStart = config.getBoolean("cg.threads.break_start", false); breakYield = config.getBoolean("cg.threads.break_yield", false); breakSleep = config.getBoolean("cg.threads.break_sleep", false); breakSyncExit = config.getBoolean("cg.threads.break_sync_exit", false); breakArrayAccess = config.getBoolean("cg.threads.break_arrays", false); breakSingleChoice = config.getBoolean("cg.break_single_choice"); } /*************************************** internal helpers *****************/ /** * post process a list of choices. This is our primary interface towards * subclasses (together with overriding the relevant ainsn APIs */ protected ThreadInfo[] filter ( ThreadInfo[] list) { // we have nothing to do, but subclasses can use it to // shuffle the order (e.g. to avoid the IdleFilter probblem), // or to filter out the top priorities return list; } protected ChoiceGenerator<ThreadInfo> getRunnableCG (String id, ThreadInfo ti) { ThreadInfo[] choices; if (id.equals(THREAD_YIELD) || id.equals(THREAD_START)) { choices = getRunnablesExcept(ti); } else{ choices = getRunnablesIfChoices(ti); } if (choices != null) { return new ThreadChoiceFromSet(id, choices, true); } else { return null; } } protected ChoiceGenerator<ThreadInfo> getSyncCG (String id, ElementInfo ei, ThreadInfo ti) { return getRunnableCG(id, ti); } /**************************************** our choice acquisition methods ***/ /** * get list of all runnable threads */ protected ThreadInfo[] getRunnables(ThreadInfo ti) { return filter(vm.getThreadList().getAllMatching(vm.getTimedoutRunnablePredicate())); } /** * return a list of runnable choices, or null if there is only one */ protected ThreadInfo[] getRunnablesIfChoices(ThreadInfo ti) { int n = vm.getThreadList().getMatchingCount(vm.getTimedoutRunnablePredicate()); if ((n > 1) || (n == 1 && breakSingleChoice)){ return filter(vm.getThreadList().getAllMatching(vm.getTimedoutRunnablePredicate())); } else { return null; } } /** * Used by yield to get a list of runnable threads but put itself to the end of the list, in * case there is no runnable threads at that time. * * @author chupanw * @param ti * @return an array of runnable threads */ protected ThreadInfo[] getRunnablesExcept(ThreadInfo ti) { int n = vm.getThreadList().getMatchingCount(vm.getTimedoutRunnablePredicate()); if ((n > 1) || (n == 1 && breakSingleChoice)) { ThreadInfo[] list = vm.getThreadList().getAllMatching(vm.getTimedoutRunnablePredicate()); for (int i = 0; i < list.length; i++) { if (list[i] == ti) { list[i] = list[list.length-1]; list[list.length-1] = ti; break; } } // randomizeThreadList(list); return list; } else{ return null; } } @SuppressWarnings("unused") private void randomizeThreadList(ThreadInfo[] list) { int upperBound = list.length-2; // we don't change the last ThreadInfo // no need to switch the order if upper bound is less than 1 if (upperBound < 1) { return; } Random rand = new Random(); for (int i = 0; i < 10; i++) { int posA = rand.nextInt(upperBound); int posB = rand.nextInt(upperBound); if (posA == posB) { continue; } ThreadInfo tmp = list[posA]; list[posA] = list[posB]; list[posB] = tmp; } } protected ThreadInfo[] getRunnablesWith (ThreadInfo ti) { return filter(vm.getThreadList().getAllMatchingWith(ti, vm.getTimedoutRunnablePredicate())); } protected ThreadInfo[] getRunnablesWithout (ThreadInfo ti) { return filter(vm.getThreadList().getAllMatchingWithout(ti, vm.getTimedoutRunnablePredicate())); } /************************************ the public interface towards the insns ***/ protected ChoiceGenerator<ThreadInfo> createEnterCG (String id, ElementInfo ei, ThreadInfo ti){ if (ti.isBlocked()) { // we have to return something if (ss.isAtomic()) { ss.setBlockedInAtomicSection(); } return new ThreadChoiceFromSet( id, getRunnables(ti), true); } else { if (ss.isAtomic()) { return null; } return getSyncCG( id, ei, ti); } } protected ChoiceGenerator<ThreadInfo> createExitCG (String id, ElementInfo ei, ThreadInfo ti){ if (breakSyncExit){ if (!ss.isAtomic()) { return getSyncCG( id, ei, ti); } } return null; // nothing, left mover } public ChoiceGenerator<ThreadInfo> createSyncMethodEnterCG (ElementInfo ei, ThreadInfo ti) { return createEnterCG( SYNC_METHOD_ENTER, ei, ti); } public ChoiceGenerator<ThreadInfo> createSyncMethodExitCG (ElementInfo ei, ThreadInfo ti) { return createExitCG( SYNC_METHOD_EXIT, ei, ti); } public ChoiceGenerator<ThreadInfo> createMonitorEnterCG (ElementInfo ei, ThreadInfo ti) { return createEnterCG( MONITOR_ENTER, ei, ti); } public ChoiceGenerator<ThreadInfo> createMonitorExitCG (ElementInfo ei, ThreadInfo ti) { return createExitCG( MONITOR_EXIT, ei, ti); } public ChoiceGenerator<ThreadInfo> createWaitCG (ElementInfo ei, ThreadInfo ti, long timeOut) { if (ss.isAtomic()) { ss.setBlockedInAtomicSection(); } return new ThreadChoiceFromSet( WAIT, getRunnables(ti), true); } public ChoiceGenerator<ThreadInfo> createNotifyCG (ElementInfo ei, ThreadInfo ti) { if (ss.isAtomic()) { return null; } ThreadInfo[] waiters = ei.getWaitingThreads(); if (waiters.length < 2) { // if there are less than 2 threads waiting, there is no nondeterminism return null; } else { return new ThreadChoiceFromSet( NOTIFY, waiters, false); } } public ChoiceGenerator<ThreadInfo> createNotifyAllCG (ElementInfo ei, ThreadInfo ti) { return null; // no choice here } public ChoiceGenerator<ThreadInfo> createSharedFieldAccessCG (ElementInfo ei, ThreadInfo ti) { if (ss.isAtomic()) { return null; } return getSyncCG( SHARED_FIELD_ACCESS, ei, ti); } public ChoiceGenerator<ThreadInfo> createSharedObjectExposureCG (ElementInfo ei, ThreadInfo ti) { if (ss.isAtomic()) { return null; } return getSyncCG( SHARED_OBJECT_EXPOSURE, ei, ti); } public ChoiceGenerator<ThreadInfo> createParkCG (ElementInfo ei, ThreadInfo tiPark, boolean isAbsoluteTime, long timeOut){ // we treat this like a wait, but don't differentiate between absolute and relative timeout. Note it has to be a right mover // note that tiPark is already blocked at this point if (ss.isAtomic()) { ss.setBlockedInAtomicSection(); } return new ThreadChoiceFromSet( PARK, getRunnables(tiPark), true); } public ChoiceGenerator<ThreadInfo> createUnparkCG (ThreadInfo tiUnparked) { // note that tiUnparked is already runnable at this point return getRunnableCG( UNPARK, tiUnparked); } public ChoiceGenerator<ThreadInfo> createSharedArrayAccessCG (ElementInfo ei, ThreadInfo ti) { // the array object (ei) is shared, otherwise we won't get here if (breakArrayAccess) { if (ss.isAtomic()) { return null; } /** // <2do> CG sequence based POR should be optional ArrayInstruction ainsn = (ArrayInstruction)ti.getPC(); boolean isRead = ainsn.isRead(); int aref = ei.getThreadInfoForId(); for (ChoiceGenerator<?> cg = ss.getChoiceGenerator(); cg != null; cg = cg.getPreviousChoiceGenerator()){ if (cg.getThreadInfoForId() != ti || cg.getChoiceType() != ThreadInfo.class){ break; // different thread or different choice type -> we need a CG } Instruction cgInsn = cg.getInsn(); if (!(cgInsn instanceof ArrayInstruction)){ break; // not an aload/astore -> we need a CG } ArrayInstruction cgAinsn = (ArrayInstruction)cgInsn; // this is only an approximation since the array ref stored in the insn // might have changed. Note this only works if the insn arrayRef is // stored AFTER this gets executed if (cgAinsn.getArrayRef(ti) != aref){ break; } if (cgAinsn.isRead() == isRead){ return null; // same op on same array in same thread -> no new CG required } // if we get here, this is a complement op on the same array in the same thread // -> skip over all prev. CG insns of the same type } **/ ChoiceGenerator<ThreadInfo> cg = getSyncCG( SHARED_ARRAY_ACCESS, ei, ti); return cg; } else { return null; } } public ChoiceGenerator<ThreadInfo> createThreadStartCG (ThreadInfo newThread) { // NOTE if newThread is sync and blocked, it already will be blocked // before this gets called // we've been going forth & back a number of times with this. The idea was // that it would be more intuitive to see a transition break every time we // start a new thread, but this causes significant state bloat in case of // pure starter threads, i.e. something that simply does // ... // t.start(); // return // // because we get a state branch at the "t.start()" and the "return". // It should be safe to go on, since the important thing is to set the new thread // runnable. if (breakStart) { if (ss.isAtomic()) { // this is dangerous - POR now depends on Thread.start() being a right mover // If the current thread doesn't have a scheduling point before termination, // we might loose paths (Thread.start() will warn about it) return null; } return getRunnableCG( THREAD_START, newThread); } else { return null; } } public ChoiceGenerator<ThreadInfo> createBeginAtomicCG (ThreadInfo atomicThread) { return getRunnableCG( BEGIN_ATOMIC, atomicThread); } public ChoiceGenerator<ThreadInfo> createEndAtomicCG (ThreadInfo atomicThread) { return getRunnableCG( END_ATOMIC, atomicThread); } public ChoiceGenerator<ThreadInfo> createThreadYieldCG (ThreadInfo yieldThread) { if (breakYield) { if (ss.isAtomic()) { return null; } return getRunnableCG(THREAD_YIELD, yieldThread); } else { return null; } } public ChoiceGenerator<ThreadInfo> createInterruptCG (ThreadInfo interruptedThread) { if (ss.isAtomic()) { return null; } return getRunnableCG( THREAD_INTERRUPT, interruptedThread); } public ChoiceGenerator<ThreadInfo> createThreadSleepCG (ThreadInfo sleepThread, long millis, int nanos) { if (breakSleep) { if (ss.isAtomic()) { return null; } // we treat this as a simple reschedule return getRunnableCG( THREAD_SLEEP, sleepThread); } else { return null; } } public ChoiceGenerator<ThreadInfo> createThreadTerminateCG (ThreadInfo terminateThread) { // terminateThread is already TERMINATED at this point ThreadList tl = vm.getThreadList(); // NOTE returning null does not directly define an end state - that's up to // a subsequent call to vm.isEndState() // <2do> FIXME this is redundant and error prone if (tl.hasAnyMatching(vm.getAlivePredicate())) { return new ThreadChoiceFromSet( THREAD_TERMINATE, getRunnablesWithout(terminateThread), true); } else { return null; } } public ChoiceGenerator<ThreadInfo> createThreadSuspendCG (ThreadInfo suspendedThread) { return getRunnableCG( THREAD_SUSPEND, suspendedThread); } public ChoiceGenerator<ThreadInfo> createThreadResumeCG (ThreadInfo resumedThread) { return getRunnableCG( THREAD_RESUME, resumedThread); } public ChoiceGenerator<ThreadInfo> createThreadStopCG (ThreadInfo stoppedThread) { return null; // left mover, there will be still a terminateCG //return getRunnableCG( THREAD_STOP); } /** * used to break the transition for reasons not related to the executing instruction type * (e.g. max transition length exceeded). */ public ChoiceGenerator<ThreadInfo> createBreakTransitionCG (String reason, ThreadInfo currentThread) { ChoiceGenerator<ThreadInfo> cg = getRunnableCG( reason, currentThread); if (cg != null) { return cg; } else { return new BreakGenerator(reason, currentThread, false); } } public ChoiceGenerator<ThreadInfo> createPreFinalizeCG (ThreadInfo finalizerThread) { ThreadInfo[] choices = {finalizerThread}; return new ThreadChoiceFromSet( PRE_FINALIZE, choices, true); } public ChoiceGenerator<ThreadInfo> createPostFinalizeCG (ThreadInfo finalizerThread) { return new ThreadChoiceFromSet( POST_FINALIZE, getRunnablesWithout(finalizerThread), true); } }