package nachos.threads;
import nachos.machine.*;
import java.util.TreeSet;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Iterator;
import java.util.Stack;
import java.util.PriorityQueue;
import java.util.Comparator;
/**
* A scheduler that chooses threads based on their priorities.
*
* <p>
* A priority scheduler associates a priority with each thread. The next thread
* to be dequeued is always a thread with priority no less than any other
* waiting thread's priority. Like a round-robin scheduler, the thread that is
* dequeued is, among all the threads of the same (highest) priority, the
* thread that has been waiting longest.
*
* <p>
* Essentially, a priority scheduler gives access in a round-robin fassion to
* all the highest-priority threads, and ignores all other threads. This has
* the potential to
* starve a thread if there's always a thread waiting with higher priority.
*
* <p>
* A priority scheduler must partially solve the priority inversion problem; in
* particular, priority must be donated through locks, and through joins.
*/
public class PriorityScheduler extends Scheduler {
/**
* Allocate a new priority scheduler.
*/
public PriorityScheduler() {
}
/**
* Allocate a new priority thread queue.
*
* @param transferPriority <tt>true</tt> if this queue should
* transfer priority from waiting threads
* to the owning thread.
* @return a new priority thread queue.
*/
public ThreadQueue newThreadQueue(boolean transferPriority) {
return new PriorityQueue(transferPriority);
}
/**
* getPriority()
*/
public int getPriority(KThread thread) {
Lib.assertTrue(Machine.interrupt().disabled());
return getThreadState(thread).getPriority();
}
/**
* getEffectivePriority()
*/
public int getEffectivePriority(KThread thread) {
Lib.assertTrue(Machine.interrupt().disabled());
return getThreadState(thread).getEffectivePriority();
}
/**
* setPriority()
*/
public void setPriority(KThread thread, int priority) {
Lib.assertTrue(Machine.interrupt().disabled());
Lib.assertTrue(priority >= priorityMinimum &&
priority <= priorityMaximum);
getThreadState(thread).setPriority(priority);
}
/**
* The self test
*/
public static void selfTest() {
PrioritySchedulerTest.runTest();
}
/**
* The default priority for a new thread. Do not change this value.
*/
public static final int priorityDefault = 1;
/**
* The minimum priority that a thread can have. Do not change this value.
*/
public static final int priorityMinimum = 0;
/**
* The maximum priority that a thread can have. Do not change this value.
*/
public static final int priorityMaximum = 7;
/**
* Return the scheduling state of the specified thread.
*
* @param thread the thread whose scheduling state to return.
* @return the scheduling state of the specified thread.
*/
protected ThreadState getThreadState(KThread thread) {
if (thread.schedulingState == null) {
thread.schedulingState = new ThreadState(thread);
}
return (ThreadState) thread.schedulingState;
}
/**
* A <tt>ThreadQueue</tt> that sorts threads by priority. Only
* parts of this are implemented and you should complete it, by adding
* code wherever you see fit and whichever method you see fit.
*/
protected class PriorityQueue extends ThreadQueue {
PriorityQueue(boolean transferPriority) {
this.transferPriority = transferPriority;
}
/**
* Tests if <tt>PriorityQueue</tt> enables priority transfer
* @return True if <tt>PriorityQueue</tt> has enabled priority transfer
*/
public boolean transferPriority() {
return this.transferPriority;
}
/**
* The thread declares its intent to wait for access to the
* "resource" guarded by this priority queue. This method is only called
* if the thread cannot immediately obtain access.
*
* @param thread The thread
*
* @see nachos.threads.ThreadQueue#waitForAccess
*/
public void waitForAccess(KThread thread) {
Lib.assertTrue(Machine.interrupt().disabled());
waitQueue.offer(thread);
}
/* print(): Prints the priority queue, for potential debugging
*/
public void print() {
System.out.println("***Printing waitQueue***");
Iterator itr = waitQueue.iterator();
int i = 0;
while (itr.hasNext()) {
KThread thread = (KThread)itr.next();
System.out.println(i + ":\t" + thread.getName());
i++;
}
}
/**
* The specified thread has received exclusive access, without using
* <tt>waitForAccess()</tt> or <tt>nextThread()</tt>. Assert that no
* threads are waiting for access.
*/
public void acquire(KThread thread) {
Lib.assertTrue(Machine.interrupt().disabled());
Lib.assertTrue(waitQueue.isEmpty());
getThreadState(thread).addResource(this);
// Record who currently has access to the resource
currentOwner = thread;
}
/**
* Select the next thread in the ThreadQueue
*/
public KThread nextThread() {
Lib.assertTrue(Machine.interrupt().disabled());
/* Current owner is releasing this resource, so remove this resource from the owners list of resources */
if(currentOwner != null) {
getThreadState(currentOwner).removeResource(this);
}
if (waitQueue.isEmpty()) {
// No one owns this queue any more
currentOwner = null;
} else {
/* Record the new current owner */
currentOwner = (KThread) waitQueue.poll();
}
return currentOwner;
}
/**
* Return the next thread that <tt>nextThread()</tt> would return,
* without modifying the state of this queue.
*
* @return the next thread that <tt>nextThread()</tt> would
* return.
*/
protected KThread pickNextThread() {
return (KThread) waitQueue.peek();
}
/**
* Returns true if queue is empty
* @return True if queue is empty
*/
public boolean empty() {
return (this.waitQueue.size() == 0);
}
/**
* <tt>true</tt> if this queue should transfer priority from waiting
* threads to the owning thread.
*/
public boolean transferPriority;
/** Records who currently has access to the resource */
private KThread currentOwner = null;
/** Priority Queue to hold threads. Returns them based on priority */
private java.util.PriorityQueue waitQueue = new java.util.PriorityQueue<KThread>(11, new CompareThreadsByPriority());
}
/** Class to compare threads */
public class CompareThreadsByPriority implements Comparator<KThread> {
public int compare(KThread x, KThread y) {
return getThreadState(x).getEffectivePriority() - getThreadState(y).getEffectivePriority();
}
}
/**
* The scheduling state of a thread. This should include the thread's
* priority, its effective priority, any objects it owns, and the queues
* it's waiting for, etc. This is a convenience class so that
* no modification to the KThread class are needed for a new scheduler.
* Each scheduler keeps track of scheduler specific KThread information
* in its own declaration of the ThreadState class.
*
* @see nachos.threads.KThread#schedulingState
*/
protected class ThreadState {
/**
* Allocate a new <tt>ThreadState</tt> object and associate it with the
* specified thread.
*
* @param thread the thread this state belongs to.
*/
public ThreadState(KThread thread) {
this.thread = thread;
this.priority = priorityDefault;
this.ownedResources = new LinkedList();
}
/**
* Return the priority of the associated thread.
*
* @return the priority of the associated thread.
*/
public int getPriority() {
return priority;
}
/**
* Set the priority of the associated thread to the specified value.
*
* @param priority the new priority.
*/
public void setPriority(int priority) {
this.priority = priority;
}
/**
* Adds resource to list of owned resources
*/
public void addResource(PriorityQueue resource) {
this.ownedResources.add(resource);
}
/**
* Remove a resource. Returns true if it sucessfully removes
*/
public boolean removeResource(PriorityQueue resource) {
return this.ownedResources.remove(resource);
}
/**
* Return the effective priority of the associated thread.
*
* @return the effective priority of the associated thread.
*/
public int getEffectivePriority() {
// Initialize effective priority to actual priority
int effectivePriority = this.priority;
/* Loop over priority queue to find the "highest" priority thread waiting on
* the current thread
*/
for(PriorityQueue q : this.ownedResources) {
// Only transfer priority if this queue allows priority to be transferred
if(q.transferPriority() && !q.empty()) {
if(effectivePriority > getThreadState(q.pickNextThread()).getEffectivePriority()) {
effectivePriority = getThreadState(q.pickNextThread()).getEffectivePriority();
}
}
}
return effectivePriority;
}
/** The thread with which this object is associated. */
protected KThread thread;
/** The priority of the associated thread. */
protected int priority;
/** List of resource owned by this thread */
protected LinkedList<PriorityQueue> ownedResources;
}
}