package cute.concolic.concurrency; import cute.Cute; import cute.concolic.Globals; import cute.concolic.Information; import cute.concolic.generateinputandschedule.GenerateInputAndSchedule; import cute.concolic.logging.RaceLog; import cute.concolic.pathconstraint.PathConstraint; import cute.concolic.pathconstraint.ScheduleConstraint; import cute.concolic.symbolicexecution.BranchHistory; import java.util.*; /** * Author: Koushik Sen <ksen@cs.uiuc.edu> */ public class Scheduler extends Thread { public Semaphore semaphore = new Semaphore(); public Semaphore blocker = new Semaphore(1); public Semaphore waitNotifySemaphore = new Semaphore(1); public IdentityHashMap numbersNotified = new IdentityHashMap(); // maps thread to thread to thread id (Thread -> Integer) public IdentityHashMap threadToLogicalId = new IdentityHashMap(); // reverse map of previous map (only ids of active threads are kept) public TreeMap logicalIdToThread = new TreeMap(); // maps thread id to Thread (Integer -> ThreadBase) public TreeMap logicalIdToThreadBase = new TreeMap(); // maps Lock object to Thread (Object -> Thread) public LockBase locks = new LockBase(); private HashMap read = new HashMap(); private HashMap write = new HashMap(); private HashMap lock = new HashMap(); private LinkedList postponed = new LinkedList(); private long lastAccessed; private int lastAccessestype = Scheduler.NONE; private WaitList waits = new WaitList(); private WaitList joins = new WaitList(); private Integer chosenThread = null; private Information information; private PathConstraint path; private cute.concolic.symbolicstate.State state; private BranchHistory history; private Random rand; private GenerateInputAndSchedule solver; public Scheduler(Information information, PathConstraint path, cute.concolic.symbolicstate.State state, BranchHistory history, Random rand, GenerateInputAndSchedule solver) { this.information = information; this.path = path; this.state = state; this.history = history; this.rand = rand; this.solver = solver; } public Thread getThread(Integer i){ return getThreadBaseFromId(i).getThread(); } public Integer getLogicalId(Thread t){ if(t==null) return null; Integer id = (Integer)threadToLogicalId.get(t); if(id==null) return null; else return id; } private ThreadBase getThreadBaseFromId(Integer thread) { return (ThreadBase)logicalIdToThreadBase.get(thread); } public ThreadBase getThreadBase(Thread t){ if(t==null) throw new NullPointerException(); Integer tmp = (Integer)threadToLogicalId.get(t); if(tmp==null){ tmp = new Integer(threadToLogicalId.size()); threadToLogicalId.put(t,tmp); synchronized(logicalIdToThread){ logicalIdToThread.put(tmp,t); } } ThreadBase tb = getThreadBaseFromId(tmp); if (tb==null){ tb = new ThreadBase(t); logicalIdToThreadBase.put(tmp,tb); } return tb; } public ThreadBase getThreadBase(){ Thread t = Thread.currentThread(); return getThreadBase(t); } public static final int READ = 1; public static final int WRITE = 2; public static final int LOCK = 3; public static final int NONE = 4; private RaceLog checkAndSetRaceTmp(HashMap threadToVarInfo,Integer thisTid,ThreadBase tb,boolean reportRace){ RaceLog rl = null; if(threadToVarInfo!=null){ VarInfo vinfoa; for (Iterator iterator = threadToVarInfo.keySet().iterator(); iterator.hasNext();) { Integer tid = (Integer) iterator.next(); if(!tid.equals(thisTid)){ vinfoa = (VarInfo)threadToVarInfo.get(tid); if(tb.isIndependent(vinfoa.getVc()) && !tb.intersects(vinfoa.getLocks())){ if(tb.getLastRaceAt(tid)<vinfoa.getIndex()){ tb.setLastRaceAt(tid,vinfoa.getIndex()); ScheduleConstraint pe2 = path.getSchedule(vinfoa.getIndex()); if(!pe2.getPostponed().contains(thisTid)) pe2.setRace(true); if(reportRace){ information.returnVal = Cute.EXIT_RACE; rl = new RaceLog(vinfoa.getIndex(),path.size()-1); // ExecutionLog.printRace(vinfoa.getIndex(),path.size()-1); // System.err.println("Possible data race between " // +vinfoa.getIndex()+" and " // +(path.size()-1)); } } } } } } return rl; } public RacePair checkAndSetRace(long addr, int accessType, IndexInfo ii){ ThreadBase tb = getThreadBase(); VarInfo vinfoa; Integer thisTid = (Integer)threadToLogicalId.get(Thread.currentThread()); HashMap threadToVarInfo=null; Long var = new Long(addr); RacePair rp = new RacePair(); if(accessType == WRITE || accessType == READ){ threadToVarInfo = (HashMap)write.get(var); rp.rl1 = checkAndSetRaceTmp(threadToVarInfo,thisTid,tb,true); } if(accessType==WRITE){ threadToVarInfo = (HashMap)read.get(var); rp.rl2 = checkAndSetRaceTmp(threadToVarInfo,thisTid,tb,true); } if(accessType==LOCK){ threadToVarInfo = (HashMap)lock.get(var); checkAndSetRaceTmp(threadToVarInfo,thisTid,tb,false); } vinfoa = new VarInfo(tb.getVc(),tb.getLocks(),path.size()-1); ii.index = vinfoa.getIndex(); HashMap tmp; if(accessType == WRITE){ tmp = write; } else if(accessType==READ){ tmp = read; } else { tmp = lock; } threadToVarInfo = (HashMap)tmp.get(var); if(threadToVarInfo==null){ threadToVarInfo = new HashMap(); tmp.put(var,threadToVarInfo); } threadToVarInfo.put(thisTid,vinfoa); return rp; } public void startBefore(Thread child){ blocker.acquire(); ThreadBase tb2 = getThreadBase(); ThreadBase tb = getThreadBase(child); tb.setVc(tb2.getVc()); tb.addOneToVc(child); tb2.addOneToVc(); information.nThreads++; blocker.release(); } public void startAfter(){ blocker.acquire(); ThreadBase tb = getThreadBase(); tb.enableAll(); blocker.release(); tb.acquire(); } public RacePair access(long addr,boolean read,IndexInfo ii){ blocker.acquire(); ThreadBase tb = getThreadBase(); tb.enableAll(); tb.toAccess = addr; tb.toAccessType = read?READ:WRITE; semaphore.release(); blocker.release(); tb.acquire(); blocker.acquire(); tb.toAccessType = NONE; RacePair rp = checkAndSetRace(addr,read?READ:WRITE,ii); blocker.release(); return rp; } public void lock(Object l){ blocker.acquire(); long addr = System.identityHashCode(l); if(locks.isHeldByCurrentThread(l)){ locks.incrementCount(l); blocker.release(); return; } ThreadBase tb = getThreadBase(); tb.enableAll(); tb.waitingOn = l; tb.toAccess = addr; tb.toAccessType = LOCK; semaphore.release(); blocker.release(); tb.acquire(); blocker.acquire(); tb.toAccessType = NONE; checkAndSetRace(addr,LOCK, new IndexInfo()); locks.acquire(l); tb.addLock(l); blocker.release(); } public void unlock(Object l){ blocker.acquire(); locks.decrementCount(l); if(!locks.isHeldByCurrentThread(l)){ getThreadBase().removeLock(); } blocker.release(); } public void waitBefore(Object l){ blocker.acquire(); ThreadBase tb = getThreadBase(); locks.release(l); tb.removeLock(); tb.enableAll(); tb.enabled = false; waits.add(l,Thread.currentThread()); semaphore.release(); blocker.release(); } public void waitAfter(Object l){ blocker.acquire(); waits.remove(l,Thread.currentThread()); long addr = System.identityHashCode(l); ThreadBase tb = getThreadBase(); tb.enableAll(); tb.waitingOn = l; tb.toAccess = addr; tb.toAccessType = LOCK; waitNotifySemaphore.release(); blocker.release(); tb.acquire(); blocker.acquire(); tb.toAccessType = NONE; checkAndSetRace(addr,LOCK, new IndexInfo()); locks.acquire(l); tb.addLock(l); blocker.release(); } public void notifyBefore(Object l){ blocker.acquire(); if(waits.contains(l) && !numbersNotified.containsKey(l)){ numbersNotified.put(l,new Integer(1)); } blocker.release(); } public void notifyAllBefore(Object l){ blocker.acquire(); if(waits.size(l)>0 && !numbersNotified.containsKey(l)){ numbersNotified.put(l,new Integer(waits.size(l))); } blocker.release(); } public void joinBefore(Thread child){ blocker.acquire(); ThreadBase tb = getThreadBase(); tb.enableAll(); tb.enabled = false; if(logicalIdToThread.containsKey(getLogicalId(child))){ joins.add(child,Thread.currentThread()); semaphore.release(); } blocker.release(); } public void joinAfter(Thread child){ blocker.acquire(); ThreadBase tb = getThreadBase(); if(!child.isAlive()){ tb.maxVc(getThreadBase(child).getVc()); } tb.enableAll(); if(joins.contains(child)){ joins.remove(child,Thread.currentThread()); } semaphore.release(); blocker.release(); tb.acquire(); } public void endBefore(){ blocker.acquire(); ThreadBase tb = getThreadBase(); tb.enabled=false; logicalIdToThread.remove(getLogicalId(Thread.currentThread())); state.popLocals(); if(joins.contains(Thread.currentThread())){ semaphore.decrement(joins.size(Thread.currentThread())); } for (Iterator iterator = tb.getLocks().iterator(); iterator.hasNext();) { Object l = iterator.next(); locks.release(l); } //tb.removeAllLocks(); semaphore.release(); blocker.release(); } public int getLockDepth(Object l){ return locks.getCount(l); } public void run() { while(true){ semaphore.acquire(); Thread.yield(); blocker.acquire(); assert numbersNotified.size()<=1; if(numbersNotified.size()==1){ Object l = numbersNotified.keySet().toArray()[0]; Integer N = (Integer)numbersNotified.get(l); if(N!=null && !locks.isHeldByAnyThread(l)){ int n = N.intValue(); if(n>0){ waitNotifySemaphore.decrement(n); numbersNotified.remove(l); blocker.release(); waitNotifySemaphore.acquire(); waitNotifySemaphore.release(); blocker.acquire(); } } } // // // blocker.acquire(); scheduleNext(); blocker.release(); } } public boolean isEnabled(Thread t){ ThreadBase tb = getThreadBase(t); return (t.isAlive() && tb.enabled && (tb.waitingOn==null || !locks.isHeldByAnyThread(tb.waitingOn))); } public void scheduleNext(){ if(information.searchMode==Globals.SEARCH_RANDOM){ scheduleNextRandom(); } else { scheduleNextDirected(); } } private void scheduleNextRandom() { int k = path.size(); int l = history.size(); boolean scheduled = false; ThreadBase toSchedule = null; int count = 0; ScheduleConstraint pe; Integer threadId = null; Integer lastThreadId = null; synchronized(logicalIdToThread){ if (k<l-1) { pe = (ScheduleConstraint)history.get(k); path.add(pe,true); threadId = pe.getThreadId(); toSchedule = getThreadBaseFromId(threadId); scheduled = true; } else { if(k==l-1){ pe = (ScheduleConstraint)history.get(k); path.add(pe,true); lastThreadId = pe.getThreadId(); TreeSet enabled = new TreeSet(); for (Iterator iterator = logicalIdToThread.keySet().iterator(); iterator.hasNext();) { Integer integer = (Integer) iterator.next(); Thread t = getThread(integer); if(isEnabled(t)){ enabled.add(integer); } } if(enabled.size()==1){ chosenThread = threadId = (Integer)enabled.first(); toSchedule = getThreadBaseFromId(threadId); scheduled = true; } else if(enabled.size()==2){ enabled.remove(lastThreadId); chosenThread = threadId = (Integer)enabled.first(); toSchedule = getThreadBaseFromId(threadId); scheduled = true; } else { enabled.remove(lastThreadId); int sz = enabled.size(); int nexti = rand.nextInt(); if(nexti<0) nexti = -nexti; nexti = nexti % sz; Integer integer = (Integer) enabled.toArray()[nexti]; toSchedule = getThreadBaseFromId(integer); chosenThread = threadId = integer; scheduled = true; } } else { pe = new ScheduleConstraint(); history.add(pe); path.add(pe,true); if(chosenThread!=null && logicalIdToThread.containsKey(chosenThread) && isEnabled(getThread(chosenThread))){ toSchedule = getThreadBaseFromId(chosenThread); threadId = chosenThread; scheduled = true; } else { count=0; for (Iterator iterator = logicalIdToThread.keySet().iterator(); iterator.hasNext();) { Integer integer = (Integer) iterator.next(); Thread t = getThread(integer); if(t.isAlive()) { count++; } } if(count==0){ pe.setThreadId(threadId); solver.predict(); } int sz = logicalIdToThread.keySet().size(); while(!scheduled) { int nexti = rand.nextInt(); if(nexti<0) nexti = -nexti; nexti = nexti % sz; Integer integer = (Integer) logicalIdToThread.keySet().toArray()[nexti]; Thread t = getThread(integer); if(isEnabled(t)){ toSchedule = getThreadBase(t); chosenThread = threadId = integer; scheduled = true; } } } } } if(!scheduled){ pe.setThreadId(threadId); solver.predict(); } else { pe.setThreadId(threadId); toSchedule.release(); } } } public void scheduleNextDirected(){ int k = path.size(); int l = history.size(); boolean scheduled = false; ThreadBase toSchedule = null; int count = 0; int enabledThreadCount = 0; ScheduleConstraint pe; Integer threadId = null; synchronized(logicalIdToThread){ if (k<l-1) { pe = (ScheduleConstraint)history.get(k); path.add(pe,true); threadId = pe.getThreadId(); toSchedule = getThreadBaseFromId(threadId); scheduled = true; } else { if(k==l-1){ pe = (ScheduleConstraint)history.get(k); path.add(pe,true); pe.setRace(false); postponed = pe.getPostponed(); assert(!postponed.contains(pe.getThreadId())); postponed.addLast(pe.getThreadId()); pe.postponeCurrentThread(); lastAccessed = getThreadBaseFromId(pe.getThreadId()).toAccess; lastAccessestype = getThreadBaseFromId(pe.getThreadId()).toAccessType; } else { pe = new ScheduleConstraint(); history.add(pe); path.add(pe,true); } for (Iterator iterator = logicalIdToThread.keySet().iterator(); iterator.hasNext();) { Integer integer = (Integer) iterator.next(); Thread t = getThread(integer); ThreadBase tb = getThreadBase(t); if(isEnabled(t)){ if(!scheduled && !postponed.contains(integer)){ toSchedule = tb; threadId = integer; scheduled = true; } enabledThreadCount++; } if(t.isAlive()) { count++; } } pe.setEnabledThreadCount(enabledThreadCount); if(count==0){ pe.setThreadId(threadId); solver.predict(); } for (Iterator iterator = postponed.listIterator(); !scheduled && iterator.hasNext();) { Integer integer = (Integer) iterator.next(); Thread t = getThread(integer); if(t!=null){ ThreadBase tb = getThreadBase(t); if(isEnabled(t)){ iterator.remove(); toSchedule = tb; threadId = integer; scheduled = true; } } } } if(!scheduled){ System.err.println("************************ Deadlock found ****************************"); pe.setThreadId(threadId); information.returnVal += Cute.EXIT_DEADLOCK; solver.predict(); } else { if(lastAccessed == toSchedule.toAccess && ((toSchedule.toAccessType==LOCK && lastAccessestype==LOCK) || (toSchedule.toAccessType == WRITE && lastAccessestype==READ) || (toSchedule.toAccessType == WRITE && lastAccessestype==WRITE) || (toSchedule.toAccessType == READ && lastAccessestype==READ))){ lastAccessestype = NONE; pe.setPostponed(postponed); } pe.setThreadId(threadId); toSchedule.release(); } } } }