package nachos.threads;
import java.util.ArrayList;
import java.util.LinkedList;
import nachos.machine.*;
/**
* 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);
}
public int getPriority(KThread thread) {
Lib.assertTrue(Machine.interrupt().disabled());
return getThreadState(thread).getPriority();
}
public int getEffectivePriority(KThread thread) {
Lib.assertTrue(Machine.interrupt().disabled());
return getThreadState(thread).getEffectivePriority();
}
public void setPriority(KThread thread, int priority) {
Lib.assertTrue(Machine.interrupt().disabled());
Lib.assertTrue(priority >= priorityMinimum &&
priority <= priorityMaximum);
getThreadState(thread).setPriority(priority);
}
public boolean increasePriority() {
boolean intStatus = Machine.interrupt().disable();
KThread thread = KThread.currentThread();
int priority = getPriority(thread);
if (priority == priorityMaximum)
return false;
setPriority(thread, priority+1);
Machine.interrupt().restore(intStatus);
return true;
}
public boolean decreasePriority() {
boolean intStatus = Machine.interrupt().disable();
KThread thread = KThread.currentThread();
int priority = getPriority(thread);
if (priority == priorityMinimum)
return false;
setPriority(thread, priority-1);
Machine.interrupt().restore(intStatus);
return true;
}
/**
* 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.
*/
public 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.
*/
protected class PriorityQueue extends ThreadQueue {
/** The List which acts like a max heap/priority queue*/
public ArrayList<KThread> heap = new ArrayList<KThread>();
public KThread lockOwner;
PriorityQueue(boolean transferPriority) {
this.transferPriority = transferPriority;
}
public void waitForAccess(KThread thread) {
Lib.assertTrue(Machine.interrupt().disabled());
getThreadState(thread).waitForAccess(this);
}
public void acquire(KThread thread) {
Lib.assertTrue(Machine.interrupt().disabled());
getThreadState(thread).acquire(this);
}
public void add(KThread kt)
{
heap.add(kt);
siftUp();
}
private void siftDown()
{
if (heap.size() <=1)return;
int left,right;
int me=0;
int size=heap.size();
int max;
ThreadState leftTS, rightTS, myTS;
myTS=getThreadState(heap.get(me));
while(true)
{
max=me;
left=(me+1)*2-1;
if (left<size)
{
leftTS=getThreadState(heap.get(left));
if(leftTS.getEffectivePriority() > myTS.getEffectivePriority())
max=left;
}else return;
right=(me+1)*2;
if(right<size){
rightTS=getThreadState(heap.get(right));
if (rightTS.getEffectivePriority() > myTS.getEffectivePriority()
&& rightTS.getEffectivePriority() > leftTS.getEffectivePriority())
{
max=right;
}
}
if (max==me)return;
else
{
ThreadState childTS = getThreadState(heap.get(max));
if (myTS.getEffectivePriority() == childTS.getEffectivePriority() &&
myTS.thread.timeAddedToWaitQ > childTS.thread.timeAddedToWaitQ)
{
swap(me,max);
me = max;
}else
{
if (myTS.getEffectivePriority() < childTS.getEffectivePriority())
{
swap(me,max);
me = max;
}else{
break;
}
}
swap(me,max);
}
}
}
private void siftUp()
{
if (heap.size() <=1)return;
int me = heap.size()-1;
ThreadState myTS, parentTS;
while(me!=0)
{
int parent=(me-1)/2;
myTS=getThreadState(heap.get(me));
parentTS=getThreadState(heap.get(parent));
if (myTS.getEffectivePriority() == parentTS.getEffectivePriority() &&
myTS.thread.timeAddedToWaitQ < parentTS.thread.timeAddedToWaitQ)
{
swap(me,parent);
me = parent;
}else
{
if (myTS.getEffectivePriority() > parentTS.getEffectivePriority())
{
swap(me,parent);
me = parent;
}else{
break;
}
}
}
}
private void siftUp(int siftIndex)
{
if (heap.size() <=1)return;
int me = siftIndex;
ThreadState myTS, parentTS;
while(me!=0)
{
int parent=(me-1)/2;
myTS=getThreadState(heap.get(me));
parentTS=getThreadState(heap.get(parent));
if (myTS.getEffectivePriority() == parentTS.getEffectivePriority() &&
myTS.thread.timeAddedToWaitQ < parentTS.thread.timeAddedToWaitQ)
{
swap(me,parent);
me = parent;
}else
{
if (myTS.getEffectivePriority() > parentTS.getEffectivePriority())
{
swap(me,parent);
me = parent;
}else{
break;
}
}
}
}
public void sort()
{
for (int i =0;i<heap.size();i++)
{
siftUp(i);
}
}
private void swap(int a, int b)
{
KThread temp = heap.get(a);
heap.set(a, heap.get(b));
heap.set(b, temp);
}
private KThread dequeue()
{
if (heap.size()==0 || heap.get(0)==null) return null;
swap(0,heap.size()-1);
KThread returnThread=heap.get(heap.size()-1);
heap.remove(heap.size()-1);
siftDown();
return returnThread;
}
private KThread peek()
{
return heap.get(0);
}
public KThread nextThread() {
Lib.assertTrue(Machine.interrupt().disabled());
KThread t = this.dequeue();
if(t!= null){
ThreadState ts = getThreadState(t);
ts.waitIn = null;
ts.donatedPriority = 0;
}
return t;
}
/**
* 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 ThreadState pickNextThread() {
return getThreadState(this.peek());
}
public void print() {
Lib.assertTrue(Machine.interrupt().disabled());
// implement me (if you want)
}
/**
* <tt>true</tt> if this queue should transfer priority from waiting
* threads to the owning thread.
*/
public boolean transferPriority;
}
/**
* The scheduling state of a thread. This should include the thread's
* priority, its effective priority, any objects it owns, and the queue
* it's waiting for, if any.
*
* @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;
setPriority(priorityDefault);
}
/**
* Return the priority of the associated thread.
*
* @return the priority of the associated thread.
*/
public int getPriority() {
return priority;
}
/**
* Return the effective priority of the associated thread.
*
* @return the effective priority of the associated thread.
*/
public int getEffectivePriority() {
if (priority >=donatedPriority)
return priority;
else return donatedPriority;
}
/**
* Set the priority of the associated thread to the specified value.
*
* @param priority the new priority.
*/
public void setPriority(int priority) {
if (this.priority == priority)
return;
if(waitIn != null && waitIn.transferPriority)
computeDonations(thread);
this.priority = priority;
if (waitIn!=null)
{
if(!waitIn.heap.isEmpty())
{
waitIn.sort();
}
}
}
/**
* Called when <tt>waitForAccess(thread)</tt> (where <tt>thread</tt> is
* the associated thread) is invoked on the specified priority queue.
* The associated thread is therefore waiting for access to the
* resource guarded by <tt>waitQueue</tt>. This method is only called
* if the associated thread cannot immediately obtain access.
*
* @param waitQueue the queue that the associated thread is
* now waiting on.
*
* @see nachos.threads.ThreadQueue#waitForAccess
*/
public void waitForAccess(PriorityQueue waitQueue) {
thread.timeAddedToWaitQ = Machine.timer().getTime();
waitQueue.add(thread);
this.waitIn=waitQueue;
//System.out.println("Waiting Thread = " + thread.getName());
if(waitQueue.transferPriority)
computeDonations(thread);
}
/**
* Called when the associated thread has acquired access to whatever is
* guarded by <tt>waitQueue</tt>. This can occur either as a result of
* <tt>acquire(thread)</tt> being invoked on <tt>waitQueue</tt> (where
* <tt>thread</tt> is the associated thread), or as a result of
* <tt>nextThread()</tt> being invoked on <tt>waitQueue</tt>.
*
* @see nachos.threads.ThreadQueue#acquire
* @see nachos.threads.ThreadQueue#nextThread
*/
public void acquire(PriorityQueue waitQueue) {
waitQueue.lockOwner=this.thread;
//System.out.println("Got Access: " + thread.getName());
}
public void computeDonations(KThread t) {
ThreadState ts=getThreadState(t);//current thread state we're looking at
LinkedList<ThreadState> seenStates = new LinkedList<ThreadState>();
while(!seenStates.contains(ts))
{
seenStates.add(ts);
ThreadState lo = ts;
if(ts.thread.joiningThread != null) //do join donation
{
lo = getThreadState(ts.thread.joiningThread);//lock owner
if(lo == ts) break;
else if (ts.getEffectivePriority() > lo.getEffectivePriority())
{
lo.priority = ts.getEffectivePriority();
if (lo.waitIn!=null)
lo.waitIn.sort();
}
}else if(ts.waitIn != null && ts.waitIn.transferPriority && ts.waitIn.lockOwner != null){ //wait queue donation
lo = getThreadState(ts.waitIn.lockOwner);//lock owner
if(lo == ts) break;
else if (ts.getEffectivePriority() > lo.getEffectivePriority())
{
lo.donatedPriority = ts.getEffectivePriority();
if (lo.waitIn!=null)
lo.waitIn.sort();
}
}
ts = lo;
}
}
/** The thread with which this object is associated. */
protected KThread thread;
/** The priority of the associated thread. */
protected int priority;
public PriorityQueue waitIn;
public int donatedPriority=0;
}
}
nachos priority scheduler with nonation
最新推荐文章于 2021-11-14 20:13:58 发布