package nachos.ag;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.LinkedList;
import nachos.machine.Lib;
import nachos.machine.Machine;
import nachos.threads.KThread;
import nachos.threads.PriorityScheduler;
import nachos.threads.ThreadedKernel;
import nachos.threads.Semaphore;
import nachos.threads.Lock;
/**
* A grader for priority scheduling.
*
* @author Sangxia Huang
*/
public class PriorityGraderS1 extends BasicTestGrader
{
protected final static String thdPrefix = "testThread #";
protected Set<ThreadHandler> ready;
protected Set<ThreadHandler> handlers;
protected Map<ThreadHandler, TreeNode> trees;
Lock[] lockSet;
ThreadHandler[] lockHolders;
LinkedList<ThreadHandler>[] lockWaiters;
int[] waitingForLockNo;
Semaphore[] joinSemaphores;
protected class TreeNode
{
protected LinkedList<TreeNode> children;
protected TreeNode parent;
protected int priority, currentDonate;
protected long time;
protected ThreadHandler thd;
public TreeNode (int priority, TreeNode parent, ThreadHandler th)
{
children = new LinkedList<TreeNode>();
this.parent = parent;
this.priority = priority;
currentDonate = 0;
time = Machine.timer().getTime();
thd = th;
if (parent != null)
parent.update();
}
public int getPriority ()
{
return Math.max(priority, currentDonate);
}
public void setPriority (int p)
{
priority = p;
if (parent != null)
parent.update();
}
public void addChild (TreeNode c)
{
children.add(c);
c.parent = this;
c.time = Machine.timer().getTime();
if (c.getPriority() > currentDonate)
{
currentDonate = Math.max(currentDonate, c.getPriority());
if (parent != null)
parent.update();
}
}
public long getTime ()
{
return time;
}
public void setTime (long t)
{
time = t;
}
public void update ()
{
int temp = 0;
for (TreeNode child : children)
temp = Math.max(temp, child.getPriority());
currentDonate = temp;
if (parent != null)
parent.update();
}
public void releaseChild (TreeNode x)
{
Lib.assertTrue(parent == null);
x.parent = null;
children.remove(x);
int temp = 0;
for (TreeNode child : children)
temp = Math.max(temp, child.getPriority());
currentDonate = temp;
}
public void printChild (int layer)
{
for (int i = 0; i < layer; ++i)
Lib.debug('Z', "\t");
Lib.debug('Z', thd.thread.getName() + "(orignal=" + priority
+ ", donated=" + currentDonate + ", time=" + +getTime() + ")");
for (TreeNode child : children)
child.printChild(layer + 1);
}
}
protected void acquireLock (int x)
{
assertTrue(0 <= x && x < lockSet.length, "acquire lock index invalid");
privilege.interrupt.tick(false);
if (lockHolders[x] != null)
{
assertTrue(!lockSet[x].isHeldByCurrentThread(), "acquiring lock #" + x
+ " which already holds");
Lib.debug('Z', "lock holder of " + x + " is "
+ lockHolders[x].thread.getName());
trees.get(lockHolders[x]).addChild(
trees.get(getThreadHandler(KThread.currentThread())));
lockWaiters[x].add(getThreadHandler(KThread.currentThread()));
waitingForLockNo[getTestThreadId(KThread.currentThread().getName())] = x;
lockSet[x].acquire();
}
else
{
Lib
.debug('Z', KThread.currentThread().getName() + " acquired lock #" + x);
lockHolders[x] = getThreadHandler(KThread.currentThread());
lockSet[x].acquire();
}
}
protected void releaseLock (int x)
{
assertTrue(0 <= x && x < lockSet.length, "release lock index invalid");
assertTrue(lockSet[x].isHeldByCurrentThread(),
"releasing an un-holding lock");
lockHolders[x] = null;
// ThreadHandler nextHolder=null;
trees.get(getThreadHandler(KThread.currentThread())).printChild(1);
for (ThreadHandler waiting : lockWaiters[x])
{
// if (nextHolder==null ||
// (trees.get(waiting).getPriority()>trees.get(nextHolder).getPriority()
// ||
// (trees.get(waiting).getPriority()==trees.get(nextHolder).getPriority()
// && trees.get(waiting).getTime()<trees.get(nextHolder).getTime())
// ||
// (trees.get(waiting).getPriority()==trees.get(nextHolder).getPriority()
// && trees.get(waiting).getTime()==trees.get(nextHolder).getTime()
// && waiting.thread.hashCode()>nextHolder.thread.hashCode()))
// )
// nextHolder=waiting;
trees.get(getThreadHandler(KThread.currentThread())).releaseChild(
trees.get(waiting));
}
// if (!lockWaiters[x].isEmpty())
// assertTrue(nextHolder!=null, "nextholder?");
// if (nextHolder!=null){
// Lib.debug('Z', nextHolder.thread.getName() + " acquired lock #" + x);
// assertTrue(lockWaiters[x].remove(nextHolder),
// "not found in lockwaiters");
// for (ThreadHandler waiting: lockWaiters[x]) {
// long temp=trees.get(waiting).getTime();
// trees.get(nextHolder)
// .addChild(trees.get(waiting));
// trees.get(waiting).setTime(temp);
// }
// lockHolders[x]=nextHolder;
// }
lockSet[x].release();
}
protected void runThread (int times, int locks)
{
int id = new Integer(KThread.currentThread().getName().substring(
thdPrefix.length(), KThread.currentThread().getName().length()))
.intValue();
Lib.debug('Z', id + " " + KThread.currentThread().getName());
int[] lockHolding = new int[locks + 1];
int holdingCount = 1;
lockHolding[0] = -1;
for (int i = 0; i < times; ++i)
{
Lib.debug('Z', KThread.currentThread().getName() + ": " + i
+ "-th iteration.");
int choice;
if (holdingCount > 1)
choice = Lib.random(3);
else
choice = Lib.random(2);
switch (choice)
{
case 0:
// set priority
int p = getRandomPriority();
Lib.debug('Z', Machine.timer().getTime() + ": "
+ KThread.currentThread().getName() + " setting priority to " + p);
boolean status = Machine.interrupt().disable();
ThreadedKernel.scheduler.setPriority(KThread.currentThread(), p);
trees.get(getThreadHandler(KThread.currentThread())).setPriority(p);
Machine.interrupt().restore(status);
break;
case 1:
// try to acquire a lock
if (lockHolding[holdingCount - 1] != lockSet.length - 1)
{
int index = Lib.random(lockSet.length
- lockHolding[holdingCount - 1] - 1);
lockHolding[holdingCount] = lockHolding[holdingCount - 1] + 1
+ index;
Lib.debug('Z', Machine.timer().getTime()
+ ": "
+ KThread.currentThread().getName()
+ "(priority="
+ trees.get(getThreadHandler(KThread.currentThread()))
.getPriority() + ")" + " acquiring lock #"
+ lockHolding[holdingCount]);
acquireLock(lockHolding[holdingCount]);
++holdingCount;
break;
}
case 2:
// release a lock
if (holdingCount > 1)
{
int index = Lib.random(holdingCount - 1) + 1;
Lib
.debug('Z', Machine.timer().getTime()
+ ": "
+ KThread.currentThread().getName()
+ "(priority="
+ trees.get(getThreadHandler(KThread.currentThread()))
.getPriority() + ")" + " releasing lock #"
+ lockHolding[index]);
releaseLock(lockHolding[index]);
System.arraycopy(lockHolding, index + 1, lockHolding, index,
holdingCount - index - 1);
--holdingCount;
}
break;
}
KThread.yield();
}
for (int i = 1; i < holdingCount; ++i)
{
releaseLock(lockHolding[i]);
KThread.yield();
}
Lib.debug('Z', KThread.currentThread().getName() + " finished.");
joinSemaphores[id].V();
}
public PriorityGraderS1.TreeNode getTreeNode (int priority, TreeNode parent,
ThreadHandler th)
{
return new TreeNode(priority, parent, th);
}
public int getRandomPriority ()
{
return Lib.random(PriorityScheduler.priorityMaximum
- PriorityScheduler.priorityMinimum)
+ PriorityScheduler.priorityMinimum;
}
public void assertCorrectScheduler ()
{
assertTrue(ThreadedKernel.scheduler.getClass().getSimpleName().equals(
"PriorityScheduler"), "This test need PriorityScheduler.");
}
@SuppressWarnings("unchecked")
@Override
public void run ()
{
assertCorrectScheduler();
final int threads = getIntegerArgument("threads");
final int times = getIntegerArgument("times");
final int locks = getIntegerArgument("locks");
assertTrue(threads > 0, "invalid argument: number of threads");
assertTrue(times > 0, "invalid argument: times of test");
assertTrue(locks > 0, "invalid argument: number of locks");
lockSet = new Lock[locks];
lockHolders = new ThreadHandler[locks];
lockWaiters = new LinkedList[locks];
joinSemaphores = new Semaphore[threads];
waitingForLockNo = new int[threads];
for (int i = 0; i < locks; ++i)
{
lockSet[i] = new Lock();
lockWaiters[i] = new LinkedList<ThreadHandler>();
}
ready = new HashSet<ThreadHandler>();
handlers = new HashSet<ThreadHandler>();
trees = new HashMap<ThreadHandler, TreeNode>();
for (int i = 0; i < threads; ++i)
{
joinSemaphores[i] = new Semaphore(0);
waitingForLockNo[i] = -1;
boolean intStatus = Machine.interrupt().disable();
ThreadHandler handler = forkNewThread(new Runnable()
{
@Override
public void run ()
{
runThread(times, locks);
}
});
handler.thread.setName(thdPrefix + i);
int p = getRandomPriority();
ThreadedKernel.scheduler.setPriority(handler.thread, p);
Lib.debug('Z', "created " + thdPrefix + i + " with priority " + p);
trees.put(handler, getTreeNode(p, null, handler));
handlers.add(handler);
ready.add(handler);
trees.get(handler).setTime(Machine.timer().getTime());
Machine.interrupt().restore(intStatus);
}
// for (ThreadHandler handler : handlers) {
// handler.thread.join();
// }
for (int i = 0; i < threads; ++i)
joinSemaphores[i].P();
ready.clear();
handlers.clear();
trees.clear();
done();
}
public int getTestThreadId (String x)
{
if (x.startsWith(thdPrefix))
return new Integer(x.substring(thdPrefix.length())).intValue();
else
return -1;
}
@Override
public void readyThread (KThread thread)
{
super.readyThread(thread);
if (ready != null)
{
if (!thread.getName().startsWith(thdPrefix))
return;
int id = getTestThreadId(thread.getName());
ThreadHandler handler = getThreadHandler(thread);
ready.add(handler);
if (trees.get(handler) != null)
trees.get(handler).setTime(Machine.timer().getTime());
if (waitingForLockNo[id] >= 0)
{
int lockId = waitingForLockNo[id];
waitingForLockNo[id] = -1;
lockHolders[lockId] = handler;
Lib.debug('Z', "removing " + lockId + "'s waiter #" + id);
assertTrue(lockWaiters[lockId].remove(handler),
"ready thread not found");
for (ThreadHandler waiting : lockWaiters[lockId])
{
long temp = trees.get(waiting).getTime();
trees.get(handler).addChild(trees.get(waiting));
trees.get(waiting).setTime(temp);
}
}
}
}
@Override
public void runningThread (KThread thread)
{
runningThread(thread, true);
}
public void runningThread (KThread thread, boolean checkSchedule)
{
super.runningThread(thread);
if (!thread.getName().startsWith(thdPrefix))
return;
if (ready != null)
{
assertTrue(ready.contains(getThreadHandler(thread)), thread.getName()
+ " is not in the ready queue");
}
if (ready != null)
{
ThreadHandler handler = getThreadHandler(thread);
assertTrue(handler != null, "handler==null");
ready.remove(handler);
// Lib.debug('Z', handler.thread.getName());
int ep = ThreadedKernel.scheduler.getEffectivePriority(handler.thread);
long et = trees.get(handler).getTime();
if (ep != trees.get(handler).getPriority())
{
trees.get(handler).printChild(1);
}
assertTrue(ep == trees.get(handler).getPriority(), thread.getName()
+ " does not have the correct" + " priority, have got " + ep
+ ", should be " + trees.get(handler).getPriority());
for (ThreadHandler tempHandler : ready)
{
// TODO check whether they are in readyQueue
int tp = ThreadedKernel.scheduler
.getEffectivePriority(tempHandler.thread);
// System.out.println(tempHandler.thread.getName());
long tt = trees.get(tempHandler).getTime();
// System.out.println("--" + tempHandler.thread.getName());
if (!tempHandler.thread.getName().startsWith(thdPrefix))
assertTrue(tp == trees.get(tempHandler).getPriority(),
tempHandler.thread.getName()
+ " does not have the correct priority - have got " + tp
+ ", should be " + trees.get(tempHandler).getPriority());
if (tp > ep)
{
trees.get(tempHandler).printChild(1);
trees.get(handler).printChild(1);
}
if (checkSchedule)
{
assertTrue(tp <= ep, tempHandler.thread.getName()
+ " has a higher priority than the running thread "
+ handler.thread.getName() + " (" + tp + " vs. " + ep + ")");
assertTrue(!(tp == ep && tt < et), tempHandler.thread.getName()
+ " arrived earlier than the running thread");
}
}
}
}
}