package nachos.threads; import nachos.machine.*; import java.util.ArrayList; //////////////////////////////////////////////////////////////////////////////// // // PRIORITY SCHEDULER // //////////////////////////////////////////////////////////////////////////////// public final class PriorityScheduler extends Scheduler { public static final int priorityDefault = 1; public static final int priorityMinimum = 0; public static final int priorityMaximum = 7; // ------------------------------------------------------------------------- public PriorityScheduler() { } // ------------------------------------------------------------------------- public static void selfTest() { PrioritySchedulerTest.runTest(); } // ------------------------------------------------------------------------- public ThreadQueue newThreadQueue(boolean transferPriority) { return new PriorityQueue(transferPriority); } // ------------------------------------------------------------------------- public int getPriority(KThread thread) { Lib.assertTrue(Machine.interrupt().disabled()); return ThreadState.read(thread).priority; } // ------------------------------------------------------------------------- public int getEffectivePriority(KThread thread) { Lib.assertTrue(Machine.interrupt().disabled()); // Get the default priority of the thread. final ThreadState state = ThreadState.read(thread); int priority = state.priority; // Loop over each queue owned by the thread. If it is a queue that // transfers priority, loop over each thread waiting on it and search // for priorities that are less than the one calculated above. for (final PriorityQueue queue : state.owned) if (queue.transferPriority) for (final KThread pendingOwner : queue.pendingOwners) priority = Math.min(priority, this.getEffectivePriority(pendingOwner)); // Return the effective priority calculated above. return priority; } // ------------------------------------------------------------------------- public void setPriority(KThread thread, int priority) { Lib.assertTrue(Machine.interrupt().disabled()); Lib.assertTrue(priority >= priorityMinimum && priority <= priorityMaximum); ThreadState.read(thread).priority = priority; } //////////////////////////////////////////////////////////////////////////// // // PRIORITY QUEUE // //////////////////////////////////////////////////////////////////////////// private final class PriorityQueue extends ThreadQueue { private final boolean transferPriority; /** A list of threads that will eventually own the resource. */ private final ArrayList<KThread> pendingOwners = new ArrayList<KThread>(); /** The current owner of the resource, or null if there is no owner. */ private KThread owner = null; // --------------------------------------------------------------------- private PriorityQueue(boolean transferPriority) { this.transferPriority = transferPriority; } // --------------------------------------------------------------------- public void waitForAccess(KThread thread) { Lib.assertTrue(Machine.interrupt().disabled()); // // ??? Semaphore.P() will call ThreadQueue.waitForAccess(), not // ThreadQueue.acquire(), when there are no pending owners of // the queue. This seems to contradict the documentation: // // * Notify this thread queue that the specified thread is // * waiting for access. This method should only be called if // * the thread cannot immediately obtain access (e.g. if the // * thread wants to acquire a lock but another thread already // * holds the lock). // // Lib.assertTrue(this.owner != null); // // This implementation will add the thread to the queue, and // that means it is possible this object will have entries in // the queue when there is no owner. // // // ??? KThread.ready() calls ThreadQueue.waitForAccess() multiple // times. // // Lib.assertTrue(!this.pendingOwners.contains(thread)); // // This implementation will add the thread only once. // if (!this.pendingOwners.contains(thread)) this.pendingOwners.add(thread); } // --------------------------------------------------------------------- public void print() { System.out.print("owner = " + this.owner); for (final KThread thread : this.pendingOwners) System.out.print(" [*] " + thread); } // --------------------------------------------------------------------- public void acquire(KThread thread) { Lib.assertTrue(Machine.interrupt().disabled()); Lib.assertTrue(this.owner == null); Lib.assertTrue(this.pendingOwners.size() == 0); // Record in the thread state that the thread owns this instance. ThreadState.read(thread).owned.add(this); // Record in this instance that the thread is the owner. this.owner = thread; } // --------------------------------------------------------------------- public KThread nextThread() { Lib.assertTrue(Machine.interrupt().disabled()); // // ??? Semaphore.V() calls this method when there is no owner. // // Lib.assertTrue(this.owner != null); // // Return null if there are no threads waiting on this instance. KThread thread = null; if (this.pendingOwners.size() > 0) { // Mark the oldest thread as the next owner. int index = 0; thread = this.pendingOwners.get(index); int priority = getEffectivePriority(thread); // Search for threads that have higher priorities. for (int i = 1; i < this.pendingOwners.size(); i++) { final KThread t = this.pendingOwners.get(i); final int p = getEffectivePriority(t); if (p < priority) { index = i; priority = p; thread = t; } } // Remove the best match from the queue. this.pendingOwners.remove(index); } // Inform the old owner it no longer owns this instance. if (this.owner != null) ThreadState.read(this.owner).owned.remove(this); // Record in this instance that the new thread is the owner. this.owner = thread; // Record in the thread state that the new thead owns this instance. if (this.owner != null) ThreadState.read(this.owner).owned.add(this); // Return the new owner of this instance, or null. return this.owner; } } //////////////////////////////////////////////////////////////////////////// // // THREAD STATE // //////////////////////////////////////////////////////////////////////////// private final static class ThreadState { private int priority = priorityDefault; /** An array of resources owned by the thread instance. */ private final ArrayList<PriorityQueue> owned = new ArrayList<PriorityQueue>(); // --------------------------------------------------------------------- private ThreadState() { } // --------------------------------------------------------------------- private static ThreadState read(KThread thread) { if (thread.schedulingState == null) thread.schedulingState = new ThreadState(); return (ThreadState) thread.schedulingState; } } }