project-2.java

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;
        }
    }
}
Valid HTML 4.01 Valid CSS