// // Copyright (C) 2009 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.ThreadChoiceFromSet; /** * <p> * SchedulerFactory that limits the search by imposing a maximum number of * thread preemptions (i.e., preempting context switches) that can occur per execution * path. This factory internally creates * <code>ContextBoundingThreadChoiceFromSet</code> choice generators. * </p> * <p> * Configuration parameters:<br> * <p> * <code>vm.scheduler_factory.class=gov.nasa.jpf.vm.ContextBoundingSchedulerFactory</code> * <br> * setting scheduler factory class to this class * </p> * <p> * <code>cg.max_number_of_preemptions=N</code><br> * where <code>N</code> is the number of allowed preemptions per execution path * </p> * * <p> * The idea of context bounding is based on the PLDI 2007 paper<br> * <i> * "Iterative Context Bounding for Systematic Testing of Multithreaded Programs" * </i><br> * by Madanlal Musuvathi and Shaz Qadeer<br> * * This factory does not implement the paper's <i>iterative</i> context-bounding * algorithm, i.e., the specific order of states that get explored. (For that, * one needs to use a search strategy.) This factory simply provides a limit on * the number of preemptions that are allowed and can be used with various * search strategies, including DFS, BFS, heuristics, etc. * * @author Igor Andjelkovic (igor.andjelkovic@gmail.com) * @author Mirko Stojmenovic (mirko.stojmenovic@gmail.com) * @author Steven Lauterburg (slauter2@cs.uiuc.edu) */ public class ContextBoundingSchedulerFactory extends DefaultSchedulerFactory { private boolean isPossibleToPreempt; private int numOfPreemptions; private int maxNumOfPreemptions; public ContextBoundingSchedulerFactory(Config config, VM vm, SystemState ss) { super(config, vm, ss); if (config.containsKey("cg.max_number_of_preemptions")) maxNumOfPreemptions = config.getInt("cg.max_number_of_preemptions"); else maxNumOfPreemptions = -1; } protected ThreadInfo[] filter(ThreadInfo[] list) { if (maxNumOfPreemptions == -1) return list; SystemState ss = VM.getVM().getSystemState(); ContextBoundingThreadChoiceFromSet previousCG = (ContextBoundingThreadChoiceFromSet) ss.getLastChoiceGeneratorOfType(ContextBoundingThreadChoiceFromSet.class); if (previousCG != null) { numOfPreemptions = previousCG.getNumOfPreemptions(); if (previousCG.hasPreemptionOccured()) numOfPreemptions++; } else { numOfPreemptions = 0; } ThreadInfo currentThread = ThreadInfo.getCurrentThread(); isPossibleToPreempt = false; for (ThreadInfo ti : list) { if (ti.equals(currentThread)) { isPossibleToPreempt = true; break; } } return (isPossibleToPreempt && numOfPreemptions >= maxNumOfPreemptions) ? new ThreadInfo[] { currentThread } : list; } protected ChoiceGenerator<ThreadInfo> getRunnableCG(String id, ThreadInfo ti) { ThreadInfo[] choices = getRunnablesIfChoices(ti); if (choices != null) { return createContextBoundingThreadChoiceFromSet(id, choices, true); } else { return null; } } public ChoiceGenerator<ThreadInfo> createMonitorEnterCG(ElementInfo ei, ThreadInfo ti) { if (ti.isBlocked()) { // we have to return something if (ss.isAtomic()) { ss.setBlockedInAtomicSection(); } return createContextBoundingThreadChoiceFromSet("monitorEnter", getRunnables(ti), true); } else { if (ss.isAtomic()) { return null; } return getSyncCG("monitorEnter", ei, ti); } } public ChoiceGenerator<ThreadInfo> createWaitCG(ElementInfo ei, ThreadInfo ti, long timeOut) { if (ss.isAtomic()) { ss.setBlockedInAtomicSection(); } return createContextBoundingThreadChoiceFromSet("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 createContextBoundingThreadChoiceFromSet("notify",waiters, false); } } public ChoiceGenerator<ThreadInfo> createThreadTerminateCG( ThreadInfo terminateThread) { // terminateThread is already TERMINATED at this point ThreadList tl = vm.getThreadList(); if (tl.getMatchingCount(vm.getTimedoutRunnablePredicate()) > 0) { return createContextBoundingThreadChoiceFromSet( "terminate", getRunnablesWithout(terminateThread), true); } else { return null; } } private ContextBoundingThreadChoiceFromSet createContextBoundingThreadChoiceFromSet(String id, ThreadInfo[] ti, boolean isSchedulingPoint) { ContextBoundingThreadChoiceFromSet tcg = new ContextBoundingThreadChoiceFromSet( id, ti, isSchedulingPoint); tcg.setPossibleToPreempt(isPossibleToPreempt); tcg.setNumOfPreemptions(numOfPreemptions); return tcg; } static class ContextBoundingThreadChoiceFromSet extends ThreadChoiceFromSet { private ThreadInfo currentThread; private boolean isPossibleToPreempt; private int numOfPreemptions; private boolean hasPreemptionOccured; public ContextBoundingThreadChoiceFromSet( String id, ThreadInfo[] set, boolean isSchedulingPoint) { super(id, set, isSchedulingPoint); this.currentThread = ThreadInfo.getCurrentThread(); } public boolean isPossibleToPreempt() { return isPossibleToPreempt; } public void setPossibleToPreempt(boolean isPossibleToPreempt) { this.isPossibleToPreempt = isPossibleToPreempt; } public boolean hasPreemptionOccured() { return hasPreemptionOccured; } public void setPreemptionOccured(boolean hasPreemptionOccured) { this.hasPreemptionOccured = hasPreemptionOccured; } public int getNumOfPreemptions() { return numOfPreemptions; } public void setNumOfPreemptions(int numOfPreemptions) { this.numOfPreemptions = numOfPreemptions; } public ThreadInfo getNextChoice() { if ((count >= 0) && (count < values.length)) { hasPreemptionOccured = isPossibleToPreempt && !currentThread.equals(values[count]); return values[count]; } else { // we don't raise an exception here because this might be (mis)used // from a listener, which shouldn't produce JPFExceptions return null; } } } }