Docstoc

Design Document.doc - Google Code

Document Sample
Design Document.doc - Google Code Powered By Docstoc
					NETS3604 – Nachos – Phase 1 – Threads Implementation

               NETS3604 – Operating Systems Internals
                                  Nachos – Threads Implementation
                                          Design Document

Design Group Members:
      Josh Green              (200423871)
      Justin Byrnes           (200419131)
      Nick Cooper             (200419416)
      Sam Thorogood           (200425548)
      Raphael Speyer          (200027393)

Modified Project Files:
       nachos/ag/ThreadGrader.java
       nachos/threads/KThread.java
       nachos/threads/Condition2.java
       nachos/threads/PriorityScheduler.java
       nachos/proj1/nachos.conf
       nachos/proj1/Design Document.doc (This Document)

Task 1: (Thread Join Method)
Design:
   The following behavior is expected from the join method:
        The current thread will wait for this thread to finish
        If this thread is already finished, the method will return immediately
   Pre-conditions to this method are:
        Join must not be called upon the current thread
        Join must not be called more than once on a particular thread.
   Post-conditions to this method are:
        The joined thread will have completed execution.

        To satisfy these requirements, a Semaphore will be used inside the join method. The initial value of the
semaphore shall be 0. Upon termination of the thread, the semaphore will be incremented. The join method will
cause the current thread to sleep until the semaphore becomes a non-zero value, it will decrement it, then
increment it again.

Implementation:
        The following code was used to implement the KThread class. All modifications and additions by the
design team have been highlighted in yellow. (Note: unchanged functions have not been listed)

package nachos.threads;
import nachos.machine.*;

public class KThread {
      private void runThread() {
            begin();
            target.run();
            joinSemaphore.V();
            finish();
      }


3/16/2013                                                                                            Page 122
NETS3604 – Nachos – Phase 1 – Threads Implementation

       /**
        * Waits for this thread to finish. If this thread is already finished,
        * return immediately. This method must only be called once; the second call
        * is not guaranteed to return. This thread must not be the current thread.
        */
       public void join() {
             Lib.debug(dbgThread, "Joining to thread: " + toString());

               Lib.assertTrue(this != currentThread);

               joinSemaphore.P();
               joinSemaphore.V();
       }

       private Semaphore joinSemaphore = new Semaphore(0);
}

Testing:
        Testing will be done using the selfTest() method of the KThread module. The autograder ThreadGrader
will call this function as a part of its testing. The test cases covered by the KThread.selfTest() function are listed
below:
     Case 1: Join called on a thread that has not begun to run yet, joining thread should wait
     Case 2: Join called on an already running thread, joining thread should wait
     Case 3: Join called on an already terminated thread, joining thread should return immediately
The following code was used to provide this testing functionality:

       private static class Parent implements Runnable {
             Parent(KThread child, int loop1, int loop2) {
                   this.child = child;
                   this.loop1 = loop1;
                   this.loop2 = loop2;
             }

               public void run() {
                     System.out.println("P has started");
                     for(int i = 0; i < loop1; i++) {
                           System.out.println("P: doing stuff");
                           currentThread.yield();
                     }
                     System.out.println("P joining child");
                     this.child.join();
                     System.out.println("P has joined");
                     for(int i = 0; i < loop2; i++) {
                           System.out.println("P: doing more stuff");
                           currentThread.yield();
                     }
                     System.out.println("P: finished doing stuff");
               }

               private KThread child = null;
               private int loop1, loop2;
       }

       private static class Child implements Runnable {
             Child(int loop) {



3/16/2013                                                                                                 Page 222
NETS3604 – Nachos – Phase 1 – Threads Implementation
                    this.loop = loop;
             }

             public void run() {
                   System.out.println("C: about to do stuff");
                   for(int i = 0; i < loop; i++) {
                         System.out.println("C: doing stuff");
                         currentThread.yield();
                   }
                   System.out.println("C: finished doing stuff");
             }

             int loop;
      }

      /**
       * Tests whether this module is working.
       */
      public static void selfTest() {
            Lib.debug(dbgThread, "Enter KThread.selfTest");

             //Ping test
             System.out.println("Ping test:");
             KThread ping = new KThread(new PingTest(1)).setName("forked thread");
             ping.fork();
             new PingTest(0).run();
             ping.join();

             KThread child, parent;

             //Case 1: Join called on a thread that has not begun to run yet, joining
             //thread should wait
             System.out.println("Case 1:");
             child = new KThread(new Child(2));
             parent = new KThread(new Parent(child, 0, 1));
             parent.setName("Parent").fork();
             currentThread.yield();
             child.setName("Child").fork();
             parent.join();

             //Case 2: Join called on an already running thread, joining thread should
             //wait
             System.out.println("Case 2:");
             child = new KThread(new Child(3));
             child.setName("Child").fork();
             new Parent(child, 1, 1).run();

             //Case 3: Join called on an already terminated thread, joining thread should
             //return immediately
             System.out.println("Case 3:");
             child = new KThread(new Child(1));
             child.setName("Child").fork();
             currentThread.yield();
             currentThread.yield();
             new Parent(child, 1, 1).run();
      }




3/16/2013                                                                            Page 322
NETS3604 – Nachos – Phase 1 – Threads Implementation
The following output was observed from the system after calling KThread.selfTest():

Case 1:                                                       C: doing stuff
Parent (#4) is now ready (Priority: 1)                        Child (#5) is now ready (Priority: 1)
main (#0) is now ready (Priority: 1)                          Running Thread: Child (#5) (Priority: 1)
Running Thread: Parent (#4) (Priority: 1)                     C: doing stuff
P has started                                                 Child (#5) is now ready (Priority: 1)
P joining child                                               Running Thread: Child (#5) (Priority: 1)
Running Thread: main (#0) (Priority: 1)                       C: finished doing stuff
Child (#3) is now ready (Priority: 1)                         main (#0) is now ready (Priority: 1)
Running Thread: Child (#3) (Priority: 1)                      Finished Thread: Child (#5)
C: about to do stuff                                          Running Thread: main (#0) (Priority: 1)
C: doing stuff                                                P has joined
Child (#3) is now ready (Priority: 1)                         P: doing more stuff
Running Thread: Child (#3) (Priority: 1)                      main (#0) is now ready (Priority: 1)
C: doing stuff                                                Running Thread: main (#0) (Priority: 1)
Child (#3) is now ready (Priority: 1)                         P: finished doing stuff
Running Thread: Child (#3) (Priority: 1)                      Case 3:
C: finished doing stuff                                       Child (#6) is now ready (Priority: 1)
Parent (#4) is now ready (Priority: 1)                        main (#0) is now ready (Priority: 1)
Finished Thread: Child (#3)                                   Running Thread: Child (#6) (Priority: 1)
Running Thread: Parent (#4) (Priority: 1)                     C: about to do stuff
P has joined                                                  C: doing stuff
P: doing more stuff                                           Child (#6) is now ready (Priority: 1)
Parent (#4) is now ready (Priority: 1)                        Running Thread: main (#0) (Priority: 1)
Running Thread: Parent (#4) (Priority: 1)                     main (#0) is now ready (Priority: 1)
P: finished doing stuff                                       Running Thread: Child (#6) (Priority: 1)
main (#0) is now ready (Priority: 1)                          C: finished doing stuff
Finished Thread: Parent (#4)                                  Finished Thread: Child (#6)
Running Thread: main (#0) (Priority: 1)                       Running Thread: main (#0) (Priority: 1)
Case 2:                                                       P has started
Child (#5) is now ready (Priority: 1)                         P: doing stuff
P has started                                                 main (#0) is now ready (Priority: 1)
P: doing stuff                                                Running Thread: main (#0) (Priority: 1)
main (#0) is now ready (Priority: 1)                          P joining child
Running Thread: Child (#5) (Priority: 1)                      main (#0) is now ready (Priority: 1)
C: about to do stuff                                          Running Thread: main (#0) (Priority: 1)
C: doing stuff                                                P has joined
Child (#5) is now ready (Priority: 1)                         P: doing more stuff
Running Thread: main (#0) (Priority: 1)                       main (#0) is now ready (Priority: 1)
P joining child                                               Running Thread: main (#0) (Priority: 1)
Running Thread: Child (#5) (Priority: 1)                      P: finished doing stuff



Why the test output results are correct:
Case 1: It can be seen from the output that the parent thread calls join on the child thread before the child thread
has been forked. The parent then sleeps while the child runs and is notified after the child has finished.

Case 2: The output shows that both the parent and child begin doing work. When the parent calls join on the
child thread, it is clear that the child finishes doing its work and terminates before the parent resumes its work.

Case 3: The output shows that the child thread is forked, does its work and then terminates all before the parent
is run. The output also shows that the parent returns immediately from the call to join.

Task 2: (Condition Variable not using Semaphores)
Design:


3/16/2013                                                                                                Page 422
NETS3604 – Nachos – Phase 1 – Threads Implementation
       The Condition2 class shall implement three methods, sleep, wake, and wakeAll. The behavior of each of
these methods is described below:
    1. sleep method
        Atomically release the lock associated with this condition variable and put the current thread to sleep
           until another thread calls wake or wakeAll
       Pre-conditions of this method:
        Current thread must hold the associated lock
       Post-conditions of this method:
        The associated lock will be required before sleep returns

   2. wake method
       Wake up one thread that previously called sleep on this condition variable
      Pre-conditions of this method:
       The currentThread must hold the associated lock

   3. wakeAll method
       Wakeup all threads that are sleeping on this condition variable
      Pre-conditions of this method:
       The currentThread must hold the associated lock

        To satisfy these requirements the Condition2 class shall store a variable of type ThreadQueue to store
the threads that are sleeping on this variable, and a Lock variable to store the lock associated with this condition.
        All threads that call sleep() shall perform the following actions sequentially:
            1. currentThread placed upon the ThreadQueue
            2. The lock released
            3. The currentThread put to sleep
            4. Reacquire the lock
            5. Return from sleep()

       Upon calling wake() the following actions shall be performed:
          1. Take a thread off the queue and place it on the ready queue

       Upon calling wakeAll() the following actions shall be performed:
          1. Take all threads off the queue sequentially and place each on the ready queue

Implementation:
        The following code was used to implement the Condition2 class. All modifications and additions by the
design team have been highlighted in yellow. (Note: unchanged functions have not been listed)

package nachos.threads;

import nachos.machine.*;

public class Condition2 {
      /**
       * Allocate a new condition variable.
       *
       * @param conditionLock
       *            the lock associated with this condition variable. The current
       *            thread must hold this lock whenever it uses <tt>sleep()</tt>,



3/16/2013                                                                                                Page 522
NETS3604 – Nachos – Phase 1 – Threads Implementation
       *            <tt>wake()</tt>, or <tt>wakeAll()</tt>.
       */
      public Condition2(Lock conditionLock) {
            this.conditionLock = conditionLock;

             //Make a non-donating threadQueue (since no-one ever "owns" the condition)
             this.waitQueue = ThreadedKernel.scheduler.newThreadQueue(false);
      }

      /**
       * Atomically release the associated lock and go to sleep on this condition
       * variable until another thread wakes it using <tt>wake()</tt>. The
       * current thread must hold the associated lock. The thread will
       * automatically reacquire the lock before <tt>sleep()</tt> returns.
       */
      public void sleep() {
            Lib.assertTrue(conditionLock.isHeldByCurrentThread());
            boolean prevStatus = Machine.interrupt().disable();

             // make the current thread accessible to a call to wake()
             waitQueue.waitForAccess(KThread.currentThread());

             conditionLock.release(); // allow wake to be called
             KThread.sleep();
             conditionLock.acquire(); // continue from where we were

             Machine.interrupt().restore(prevStatus);
      }

      /**
       * Wake up at most one thread sleeping on this condition variable. The
       * current thread must hold the associated lock.
       */
      public void wake() {
            Lib.assertTrue(conditionLock.isHeldByCurrentThread());
            boolean prevStatus = Machine.interrupt().disable();

             KThread nextThread = waitQueue.nextThread();
             if (nextThread != null) {
                   nextThread.ready();
             }

             Machine.interrupt().restore(prevStatus);
      }

      /**
        * Wake up all threads sleeping on this condition variable. The current
        * thread must hold the associated lock.
        */
      public void wakeAll() {
             Lib.assertTrue(conditionLock.isHeldByCurrentThread());
             boolean prevStatus = Machine.interrupt().disable();
             Machine.interrupt().disable();
             KThread nextThread;
             while ((nextThread = waitQueue.nextThread()) != null) {
                   nextThread.ready();
             }
             Machine.interrupt().restore(prevStatus);
      }



3/16/2013                                                                           Page 622
NETS3604 – Nachos – Phase 1 – Threads Implementation

       private Lock conditionLock;
       private ThreadQueue waitQueue;
}

Testing:
       Testing will be done using the selfTest() method of the Condition2 module. The autograder
ThreadGrader will call this function as a part of its testing. The test cases covered by the Condition2.selfTest()
function are listed below:
    Case 1: Tests the condition variables using a few threads
    Case 2: Tests the condition variables using many threads

The following code was used to provide this testing functionality:

       public static void selfTest() {

               final   Lock lock = new Lock();
               final   Condition condition = new Condition(lock);
               final   LinkedList<String> pipe = new LinkedList<String>();
               final   int pipeMaxSize = 3;
               final   int throughput = 10;
               final   int numProducers = 5;

               Runnable consuming = new Runnable() {
                     public void run() {
                           String name = KThread.currentThread().getName();
                           for (int i = 0; i < throughput; i++) {
                                 lock.acquire();
                                 while (pipe.isEmpty()) {
                                       System.out.println(name
                                                   + " waits for something to be put into
pipe");
                                            condition.sleep();
                                      }
                                      String packet = pipe.removeFirst();
                                      System.out.println(name + " removes " + packet
                                                  + " from front of pipe");

                                      condition.wakeAll();
                                      lock.release();
                              }
                       }
               };

               Runnable producing = new Runnable() {
                     public void run() {
                           String name = KThread.currentThread().getName();
                           for (int i = 1; i <= throughput; i++) {
                                 lock.acquire();
                                 while (pipe.size() >= pipeMaxSize) {
                                       System.out.println(name
                                                   + " waits for pipe to be smaller than "
                                                   + pipeMaxSize);
                                       condition.sleep();
                                 }
                                 String packet = "{" + name + "-" + i + "}";



3/16/2013                                                                                               Page 722
NETS3604 – Nachos – Phase 1 – Threads Implementation

                                    System.out.println(name + " adds " + packet
                                                + " to the end of the pipe");
                                    pipe.addLast(packet);
                                    condition.wakeAll();
                                    lock.release();
                             }
                      }
              };

              // case 1
              System.out.println("\tCase 1:");
              KThread consumer = new KThread(consuming);
              KThread producer = new KThread(producing);

              consumer.setName("consumer").fork();
              producer.setName("producer").fork();

              producer.join();
              consumer.join();

              // case 2
              System.out.println("\tCase 2:");
              LinkedList<KThread> consumers = new LinkedList<KThread>();
              LinkedList<KThread> producers = new LinkedList<KThread>();
              for (int i = 0; i < numProducers; i++) {
                    consumers.addLast(new KThread(consuming));
                    producers.addLast(new KThread(producing));
              }
              for (int i = 0; i < numProducers; i++) {
                    consumers.get(i).setName("consumer" + i).fork();
                    producers.get(i).setName("producer" + i).fork();
              }
              for (int i = 0; i < numProducers; i++) {
                    producers.get(i).join();
                    consumers.get(i).join();
              }

       }

The following output was observed from the system after calling Condition2.selfTest():

Case 1:                                                    producer (#8) is now ready (Priority: 1)
consumer (#7) is now ready (Priority: 1)                   consumer removes {producer-2} from front of
producer (#8) is now ready (Priority: 1)                   pipe
Running Thread: consumer (#7) (Priority: 1)                consumer removes {producer-3} from front of
consumer waits for something to be put into                pipe
pipe                                                       consumer waits for something to be put into
Running Thread: producer (#8) (Priority: 1)                pipe
producer adds {producer-1} to the end of the               Running Thread: producer (#8) (Priority: 1)
pipe                                                       producer adds {producer-4} to the end of the
consumer (#7) is now ready (Priority: 1)                   pipe
producer adds {producer-2} to the end of the               consumer (#7) is now ready (Priority: 1)
pipe                                                       producer adds {producer-5} to the end of the
producer adds {producer-3} to the end of the               pipe
pipe                                                       producer adds {producer-6} to the end of the
producer waits for pipe to be smaller than 3               pipe
Running Thread: consumer (#7) (Priority: 1)                producer waits for pipe to be smaller than 3
consumer removes {producer-1} from front of                Running Thread: consumer (#7) (Priority: 1)
pipe



3/16/2013                                                                                      Page 822
NETS3604 – Nachos – Phase 1 – Threads Implementation
consumer removes {producer-4} from front of                  consumer1 (#11) is now ready (Priority: 1)
pipe                                                         producer1 (#12) is now ready (Priority: 1)
producer (#8) is now ready (Priority: 1)                     consumer2 (#13) is now ready (Priority: 1)
consumer removes {producer-5} from front of                  producer2 (#14) is now ready (Priority: 1)
pipe                                                         consumer3 (#15) is now ready (Priority: 1)
consumer removes {producer-6} from front of                  producer3 (#16) is now ready (Priority: 1)
pipe                                                         consumer4 (#17) is now ready (Priority: 1)
consumer waits for something to be put into                  producer4 (#18) is now ready (Priority: 1)
pipe                                                         Running Thread: consumer0 (#9) (Priority: 1)
Running Thread: producer (#8) (Priority: 1)                  consumer0 waits for something to be put into
producer adds {producer-7} to the end of the                 pipe
pipe                                                         Running Thread: producer0 (#10) (Priority: 1)
consumer (#7) is now ready (Priority: 1)                     producer0 adds {producer0-1} to the end of the
producer adds {producer-8} to the end of the                 pipe
pipe                                                         consumer0 (#9) is now ready (Priority: 1)
producer (#8) is now ready (Priority: 1)                     producer0 adds {producer0-2} to the end of the
Running Thread: consumer (#7) (Priority: 1)                  pipe
Running Thread: producer (#8) (Priority: 1)                  producer0 adds {producer0-3} to the end of the
producer adds {producer-9} to the end of the                 pipe
pipe                                                         producer0 waits for pipe to be smaller than 3
consumer (#7) is now ready (Priority: 1)                     Running Thread: consumer1 (#11) (Priority: 1)
Running Thread: consumer (#7) (Priority: 1)                  consumer1 removes {producer0-1} from front of
consumer removes {producer-7} from front of                  pipe
pipe                                                         producer0 (#10) is now ready (Priority: 1)
producer (#8) is now ready (Priority: 1)                     consumer1 removes {producer0-2} from front of
Running Thread: producer (#8) (Priority: 1)                  pipe
producer adds {producer-10} to the end of the                consumer1 removes {producer0-3} from front of
pipe                                                         pipe
consumer (#7) is now ready (Priority: 1)                     consumer1 waits for something to be put into
main (#0) is now ready (Priority: 1)                         pipe
Finished Thread: producer (#8)                               consumer1 (#11) is now ready (Priority: 1)
Running Thread: consumer (#7) (Priority: 1)                  Running Thread: producer1 (#12) (Priority: 1)
consumer removes {producer-8} from front of                  producer1 adds {producer1-1} to the end of the
pipe                                                         pipe
consumer removes {producer-9} from front of                  producer1 adds {producer1-2} to the end of the
pipe                                                         pipe
consumer removes {producer-10} from front of                 producer1 adds {producer1-3} to the end of the
pipe                                                         pipe
Finished Thread: consumer (#7)                               producer1 waits for pipe to be smaller than 3
Running Thread: main (#0) (Priority: 1)                      Running Thread: consumer2 (#13) (Priority: 1)
                                                             consumer2 removes {producer1-1} from front of
       Case 2:                                               pipe
consumer0 (#9) is now ready (Priority: 1)                    producer1 (#12) is now ready (Priority: 1)
producer0 (#10) is now ready (Priority: 1)                      <<REMOVED REST OF TEST OUTPUT (6 pages)>>

Case 1: The consumer threads must wait (by calling sleep) if the pipe is empty, and the producers must wait if it
is full. When the state of the pipe changes, all threads waiting on the condition variable are notified (by calling
wake). We can see from the output that the consumer does indeed wait until the thread is non-empty before
attempting to consume, and the producer waits until it is not full. Also, the consumers and producers are
notified immediately as soon as they are able to become active again, and we can see that the often do become
active as soon as they are allowed.

Case 2: Here, more producers and consumers are added, all dependent on the same lock and condition variable.
The wakeAll() method is used to bring all of the waiting threads into the ready state. We can also see that when
more producers and consumer are added, the restrictions are still respected. Any of the producers must wait for
the correct conditions, before they are continue execution, putting packets into the pipe, and all the consumers
must also wait for the pipe they pipe is non-empty. The threads are able to successfully share the resource
without breaking the limitations or going into deadlock.Task 5: (Priority Scheduling with Donation)
Design:



3/16/2013                                                                                               Page 922
NETS3604 – Nachos – Phase 1 – Threads Implementation
        The PriorityScheduler class must implement functions to manage scheduling of threads within the
kernel, and two classes; PriorityQueue and ThreadState to provide a mechanism for allowing high priority
threads to execute before low priority threads.
        According to the PriorityScheduler documentation:
               “Essentially, a priority scheduler gives access in a round-robin fashion to all the
               highest-priority threads, and ignores all other threads.”
        To provide this functionality, the following rules are implemented in the classes.
     All threads waiting in a queue are sorted by their effective priority
     All threads store a sorted map of queues they own, and the highest priority thread stored in that queue.
        This map is called the thread’s priority cache.
     The effective priority of a thread is the highest priority stored in its priority cache.
     When a thread is selected from the queue to acquire the resource (nextThread()) that thread is removed
        from the queue and the new thread is told to aquire the queue (acquire()) The previous owner of the
        queue is told it no longer owns this queue, and the cached priority of this queue is removed. The new
        owner of the queue gets the highest priority in the queue added to its priority cache and its effective
        priority updated.
     When a thread is added to the waiting queue (waitForAccess()), it may have the highest priority of all
        threads in the queue. The owner of the queue then needs to update its effective priority to be greater than
        or equal to the new waiting thread.
     When a change occurs to a thread’s effective priority, it must reinsert itself into the correct position in
        the queue and as a result may end up affecting the effective priority of that queue’s owner.

        The above rules allow the priority scheduler to provide a mechanism for running high priority threads
before low, and a mechanism for solving any priority inversion issues involved through members of a queue
donating their priority to the queue’s owner.

Implementation:
        The following code was used to implement the PriorityScheduler class. All modifications and additions
by the design team have been highlighted in yellow. (Note: unchanged functions have not been listed)

package nachos.threads;

import nachos.machine.*;

import   java.util.LinkedList;
import   java.util.Map;
import   java.util.Queue;
import   java.util.SortedMap;
import   java.util.TreeMap;
import   java.util.Comparator;
import   java.util.TreeSet;

public class PriorityScheduler extends Scheduler {

         protected class PriorityQueue extends ThreadQueue {

               public KThread nextThread() {
                     Lib.assertTrue(Machine.interrupt().disabled());

                       ThreadState nextThread = pickNextThread();
                       if(nextThread != null) {
                             nextThread.acquire(this);



3/16/2013                                                                                             Page 1022
NETS3604 – Nachos – Phase 1 – Threads Implementation
                          return nextThread.thread;
                    }
                    return null;
             }

             /**
              * 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() {
                   while(!queue.isEmpty()) {
                         Integer lastKey = queue.lastKey();
                         Queue<ThreadState> qlist = queue.get(lastKey);

                          if(qlist == null || qlist.isEmpty())
                                 queue.remove(lastKey);
                          else {
                                 return qlist.peek();
                          }
                    }
                    return null;
             }

            public SortedMap<Integer, Queue<ThreadState>> queue = new
TreeMap<Integer,Queue<ThreadState>>();

             /**
              * <tt>true</tt> if this queue should transfer priority from waiting
              * threads to the owning thread.
              */
             public boolean transferPriority;
             public ThreadState currentOwner = null;
      }

      /**
       * 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 {
            /**
              * Return the effective priority of the associated thread.
              *
              * @return the effective priority of the associated thread.
              */
            public int getEffectivePriority() {
                   PriorityDonation donation = priorityCache.last();
                   return donation.priority;
            }

             /**
              * Set the priority of the associated thread to the specified value.
              *
              * @param priority
              *            the new priority.



3/16/2013                                                                           Page 1122
NETS3604 – Nachos – Phase 1 – Threads Implementation
              */
             public void setPriority(int priority) {
                   if (this.priority == priority)
                         return;

                    revokeDonation(null);
                    registerDonation(priority,null);
             }

             /**
               * 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) {
                    assert(waitQueue != null);
                    //System.out.println(thread+" started waiting for "+waitQueue);
                    waiting = waitQueue;
                    requeue(waiting);
             }

             /**
              * 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) {
                   assert(waitQueue != null);

                    //Make sure this thread is completely removed from the queue:
                    //(before any donation changes are made)
                    dequeue(waitQueue);

                    //If there was a previous owner, perform handover:
                    //(only if there was a donation)
                    if(waitQueue.currentOwner != null && waitQueue.transferPriority) {
                          waitQueue.currentOwner.revokeDonation(waitQueue);
                          waitQueue.currentOwner = null;
                    }

                    //Declare ownership of this resource
                    waitQueue.currentOwner = this;

                    //If we were waiting on this resource then stop:
                    if(waiting == waitQueue){
                          waiting = null;



3/16/2013                                                                           Page 1222
NETS3604 – Nachos – Phase 1 – Threads Implementation
                    }

                    //Perform donation if necessary:
                    if(waitQueue.transferPriority){
                          registerDonation(waitQueue);
                    }
             }

             protected void dequeue(PriorityQueue waitQueue) {
                   //Check if we actually need to do anything:
                   if(waitQueue == null)
                         return;

                    //Where should this thread have been in the queue?
                    int effectivePriority = getEffectivePriority();

                  if(waitQueue.queue.containsKey(effectivePriority)) {
                        Queue<ThreadState> qlist =
waitQueue.queue.get(effectivePriority);
                        qlist.remove(this);

                          //Check if it is empty (may need to cleanup)
                          if(qlist.isEmpty()) {
                                waitQueue.queue.remove(effectivePriority);

                                 //The currentOwner no-longer has any threads
                                 //at this level of priority
                                 notifyQueueOwner(waitQueue);
                          }
                    }
             }

             protected void requeue() {
                   requeue(waiting);
             }

             protected void requeue(PriorityQueue waitQueue){
                   //Do we really need to do anything?
                   if(waitQueue == null)
                         return;

                    //Check if we need to update the queue ordering:
                    int effectivePriority = getEffectivePriority();

                  for(Map.Entry<Integer, Queue<ThreadState>> e :
waitQueue.queue.entrySet()) {
                        if(e.getValue().contains(this)) {
                              if(e.getKey().equals(effectivePriority)) {
                                    //We haven't changed so the order is correct.
                                    return;
                              } else {
                                    //We've changed, so the order is wrong
                                    e.getValue().remove(this);
                                    break;
                              }
                        }
                  }




3/16/2013                                                                       Page 1322
NETS3604 – Nachos – Phase 1 – Threads Implementation
                    //Add in this thread to the queue again:
                    Queue<ThreadState> qlist = null;
                    if(waitQueue.queue.containsKey(effectivePriority)) {
                          //Priority level already exists in the queue, retireve it:
                          qlist = waitQueue.queue.get(effectivePriority);
                    } else {
                          //Priority Level does not exist in the queue yet:
                          qlist = new LinkedList<ThreadState>();
                          waitQueue.queue.put(effectivePriority, qlist);
                    }
                    qlist.add(this);

                    ///We're now in the queue in a new position, lets notify the owner
                    notifyQueueOwner(waitQueue);
             }

             protected int getLeadPriority(PriorityQueue queue){
                   //Figure out the leading priority in this queue
                   if(queue != null)
                   while(!queue.queue.isEmpty()){
                         int qleader = queue.queue.lastKey();
                         Queue<ThreadState> list = queue.queue.get(qleader);
                         if(list.isEmpty()){
                                //Clean up that bucket
                                queue.queue.remove(qleader);
                         }else{
                                //Found the lead:
                                return qleader;
                         }
                   }
                   return priorityMinimum;
             }

            protected void notifyQueueOwner(PriorityQueue waitQueue){
                  //Better update their donation info: (if required)
                  if(waitQueue.currentOwner != null &&
waitQueue.transferPriority/*waitQueue.currentOwner != this*/){
                        ThreadState currentOwner = waitQueue.currentOwner;

                          currentOwner.revokeDonation(waitQueue);
                          currentOwner.registerDonation(waitQueue);
                    }
             }

             protected void registerDonation(PriorityQueue queue){
                   registerDonation(getLeadPriority(queue),queue);
             }

             protected void registerDonation(int lead, PriorityQueue queue){
                   PriorityDonation donation = new PriorityDonation();
                   donation.priority = lead;
                   donation.queue = queue;
                   priorityCache.add(donation);

                    //Effective Priority has changed!
                    requeue();
             }

             protected void revokeDonation(PriorityQueue queue){



3/16/2013                                                                         Page 1422
NETS3604 – Nachos – Phase 1 – Threads Implementation
                      //Find this queue amongst the cache:
                      for(PriorityDonation d : priorityCache){
                            if(d.queue == queue){
                                  priorityCache.remove(d);

                                      //Effective Priority has changed!
                                      requeue();
                                      return;
                              }
                      }
               }

               protected class PriorityDonation{
                     public int priority;
                     public PriorityQueue queue;
               }

               protected class CompareDonation          implements Comparator{
                     public int compare(Object          o1, Object o2){
                           PriorityDonation d1          = (PriorityDonation)o1;
                           PriorityDonation d2          = (PriorityDonation)o2;

                              if(d1.priority < d2.priority){
                                     return -1;
                              }else if(d1.priority == d2.priority){
                                     return 0;
                              }else{
                                     return 1;
                              }
                      }
               }

            protected TreeSet<PriorityDonation> priorityCache = new
TreeSet<PriorityDonation>(new CompareDonation());
            protected PriorityQueue waiting = null;

               /** The thread with which this object is associated. */
               protected KThread thread;

               /** The priority of the associated thread. */
               protected int priority;
       }
}

Testing:
        Testing will be done using the selfTest() method of the PriorityScheduler module. The autograder
ThreadGrader will call this function as a part of its testing. The test cases covered by the
PriorityScheduler.selfTest() function are listed below:
    Case 1: Tests priority scheduler without donation
    Case 2: Tests priority scheduler without donation, altering priorities of threads after they've started
        running
    Case 3: Tests priority donation
The following code was used to provide this testing functionality:




3/16/2013                                                                                            Page 1522
NETS3604 – Nachos – Phase 1 – Threads Implementation
       public static void selfTestRun( KThread t1, int t1p, KThread t2, int t2p, KThread t3, int t3p ) {

              boolean int_state;

              int_state = Machine.interrupt().disable();
              ThreadedKernel.scheduler.setPriority( t1, t1p );
              ThreadedKernel.scheduler.setPriority( t2, t2p );
              ThreadedKernel.scheduler.setPriority( t3, t3p );
              Machine.interrupt().restore( int_state );

              t1.setName("a").fork();
              t2.setName("b").fork();
              t3.setName("c").fork();
              t1.join();
              t2.join();
              t3.join();

       }

       /**
        * Tests whether this module is working.
        */
       public static void selfTest() {

              KThread t1, t2, t3;
              final Lock lock;
              final Condition2 condition;

              /*
               * Case 1: Tests priority scheduler without donation
               *
               * This runs t1 with priority 7, and t2 with priority 4.
               *
               */

              System.out.println( "Case 1:" );

              t1 = new KThread(new Runnable() {
                            public void run() {
                                    System.out.println( KThread.currentThread().getName() + " started
working" );
                                   for( int i = 0; i < 10; ++i ) {
                                          System.out.println( KThread.currentThread().getName() + "
working " + i );
                                          KThread.yield();
                                   }
                                   System.out.println( KThread.currentThread().getName() + " finished
working" );
                            }
                     });




3/16/2013                                                                                      Page 1622
NETS3604 – Nachos – Phase 1 – Threads Implementation
              t2 = new KThread(new Runnable() {
                            public void run() {
                                    System.out.println( KThread.currentThread().getName() + " started
working" );
                                   for( int i = 0; i < 10; ++i ) {
                                          System.out.println( KThread.currentThread().getName() + "
working " + i );
                                          KThread.yield();
                                   }
                                   System.out.println( KThread.currentThread().getName() + " finished
working" );
                            }

                     });

              selfTestRun( t1, 7, t2, 4 );

              /*
               * Case 2: Tests priority scheduler without donation, altering
               * priorities of threads after they've started running
               *
               * This runs t1 with priority 7, and t2 with priority 4, but
               * half-way through t1's process its priority is lowered to 2.
               *
               */

              System.out.println( "Case 2:" );

              t1 = new KThread(new Runnable() {
                            public void run() {
                                    System.out.println( KThread.currentThread().getName() + " started
working" );
                                   for( int i = 0; i < 10; ++i ) {
                                          System.out.println( KThread.currentThread().getName() + "
working " + i );
                                             KThread.yield();
                                             if( i == 4 ) {
                                                    System.out.println( KThread.currentThread().getName() +
" reached 1/2 way, changing priority" );
                                                    boolean int_state = Machine.interrupt().disable();
                                                    ThreadedKernel.scheduler.setPriority( 2 );
                                                    Machine.interrupt().restore( int_state );
                                             }
                                   }
                                   System.out.println( KThread.currentThread().getName() + " finished
working" );
                            }
                     });

              t2 = new KThread(new Runnable() {
                            public void run() {



3/16/2013                                                                                        Page 1722
NETS3604 – Nachos – Phase 1 – Threads Implementation
                                   System.out.println( KThread.currentThread().getName() + " started
working" );
                                   for( int i = 0; i < 10; ++i ) {
                                          System.out.println( KThread.currentThread().getName() + "
working " + i );
                                             KThread.yield();
                                   }
                                   System.out.println( KThread.currentThread().getName() + " finished
working" );
                            }

                     });

              selfTestRun( t1, 7, t2, 4 );

              /*
               * Case 3: Tests priority donation
               *
               * This runs t1 with priority 7, t2 with priority 6 and t3 with
               * priority 4. t1 will wait on a lock, and while t2 would normally
               * then steal all available CPU, priority donation will ensure that
               * t3 is given control in order to help unlock t1.
               *
               */

              System.out.println( "Case 3:" );

              lock = new Lock();
              condition = new Condition2( lock );

              t1 = new KThread(new Runnable() {
                     public void run() {
                            lock.acquire();
                            System.out.println( KThread.currentThread().getName() + " active" );
                            lock.release();
                     }
              });

              t2 = new KThread(new Runnable() {
                            public void run() {
                                    System.out.println( KThread.currentThread().getName() + " started
working" );
                                   for( int i = 0; i < 3; ++i ) {
                                          System.out.println( KThread.currentThread().getName() + "
working " + i );
                                          KThread.yield();
                                   }
                                   System.out.println( KThread.currentThread().getName() + " finished
working" );
                            }




3/16/2013                                                                                      Page 1822
NETS3604 – Nachos – Phase 1 – Threads Implementation
                      });

               t3 = new KThread(new Runnable() {
                      public void run() {
                             lock.acquire();

                              boolean int_state = Machine.interrupt().disable();
                              ThreadedKernel.scheduler.setPriority( 2 );
                              Machine.interrupt().restore( int_state );

                              KThread.yield();

                              // t1.acquire() will now have to realise that t3 owns the lock it wants to
obtain
                              // so program execution will continue here.

                             System.out.println( KThread.currentThread().getName() + " active ('a' wants
its lock back so we are here)" );
                             lock.release();
                             KThread.yield();
                             lock.acquire();
                             System.out.println( KThread.currentThread().getName() + " active-again (should
be after 'a' and 'b' done)" );
                             lock.release();

                      }
               });

               selfTestRun( t1, 6, t2, 4, t3, 7 );

         }



The following output was observed from the system after calling PriorityScheduler.selfTest():

Case 1:                                                    a working 5
a (#19) is now ready (Priority: 7)                         a (#19) is now ready (Priority: 7)
main (#0) is now ready (Priority: 1)                       Running Thread: a (#19) (Priority: 7)
Running Thread: a (#19) (Priority: 7)                      a working 6
a started working                                          a (#19) is now ready (Priority: 7)
a working 0                                                Running Thread: a (#19) (Priority: 7)
a (#19) is now ready (Priority: 7)                         a working 7
Running Thread: a (#19) (Priority: 7)                      a (#19) is now ready (Priority: 7)
a working 1                                                Running Thread: a (#19) (Priority: 7)
a (#19) is now ready (Priority: 7)                         a working 8
Running Thread: a (#19) (Priority: 7)                      a (#19) is now ready (Priority: 7)
a working 2                                                Running Thread: a (#19) (Priority: 7)
a (#19) is now ready (Priority: 7)                         a working 9
Running Thread: a (#19) (Priority: 7)                      a (#19) is now ready (Priority: 7)
a working 3                                                Running Thread: a (#19) (Priority: 7)
a (#19) is now ready (Priority: 7)                         a finished working
Running Thread: a (#19) (Priority: 7)                      Finished Thread: a (#19)
a working 4                                                Running Thread: main (#0) (Priority: 1)
a (#19) is now ready (Priority: 7)                         b (#20) is now ready (Priority: 4)
Running Thread: a (#19) (Priority: 7)                      Running Thread: b (#20) (Priority: 4)




3/16/2013                                                                                       Page 1922
NETS3604 – Nachos – Phase 1 – Threads Implementation
b started working                                      b working 2
b working 0                                            b (#22) is now ready (Priority: 4)
b (#20) is now ready (Priority: 4)                     Running Thread: b (#22) (Priority: 4)
Running Thread: b (#20) (Priority: 4)                  b working 3
b working 1                                            b (#22) is now ready (Priority: 4)
b (#20) is now ready (Priority: 4)                     Running Thread: b (#22) (Priority: 4)
Running Thread: b (#20) (Priority: 4)                  b working 4
b working 2                                            b (#22) is now ready (Priority: 4)
b (#20) is now ready (Priority: 4)                     Running Thread: b (#22) (Priority: 4)
Running Thread: b (#20) (Priority: 4)                  b working 5
b working 3                                            b (#22) is now ready (Priority: 4)
b (#20) is now ready (Priority: 4)                     Running Thread: b (#22) (Priority: 4)
Running Thread: b (#20) (Priority: 4)                  b working 6
b working 4                                            b (#22) is now ready (Priority: 4)
b (#20) is now ready (Priority: 4)                     Running Thread: b (#22) (Priority: 4)
Running Thread: b (#20) (Priority: 4)                  b working 7
b working 5                                            b (#22) is now ready (Priority: 4)
b (#20) is now ready (Priority: 4)                     Running Thread: b (#22) (Priority: 4)
Running Thread: b (#20) (Priority: 4)                  b working 8
b working 6                                            b (#22) is now ready (Priority: 4)
b (#20) is now ready (Priority: 4)                     Running Thread: b (#22) (Priority: 4)
Running Thread: b (#20) (Priority: 4)                  b working 9
b working 7                                            b (#22) is now ready (Priority: 4)
b (#20) is now ready (Priority: 4)                     Running Thread: b (#22) (Priority: 4)
Running Thread: b (#20) (Priority: 4)                  b (#22) is now ready (Priority: 4)
b working 8                                            Running Thread: b (#22) (Priority: 4)
b (#20) is now ready (Priority: 4)                     b finished working
Running Thread: b (#20) (Priority: 4)                  Finished Thread: b (#22)
b working 9                                            Running Thread: a (#21) (Priority: 2)
b (#20) is now ready (Priority: 4)                     a working 6
Running Thread: b (#20) (Priority: 4)                  a (#21) is now ready (Priority: 2)
b finished working                                     Running Thread: a (#21) (Priority: 2)
main (#0) is now ready (Priority: 1)                   a working 7
Finished Thread: b (#20)                               a (#21) is now ready (Priority: 2)
Running Thread: main (#0) (Priority: 1)                Running Thread: a (#21) (Priority: 2)
Case 2:                                                a working 8
a (#21) is now ready (Priority: 7)                     a (#21) is now ready (Priority: 2)
b (#22) is now ready (Priority: 4)                     Running Thread: a (#21) (Priority: 2)
Running Thread: a (#21) (Priority: 7)                  a working 9
a started working                                      a (#21) is now ready (Priority: 2)
a working 0                                            Running Thread: a (#21) (Priority: 2)
a (#21) is now ready (Priority: 7)                     a finished working
Running Thread: a (#21) (Priority: 7)                  main (#0) is now ready (Priority: 1)
a working 1                                            Finished Thread: a (#21)
a (#21) is now ready (Priority: 7)                     Running Thread: main (#0) (Priority: 1)
Running Thread: a (#21) (Priority: 7)                  Case 3:
a working 2                                            a (#23) is now ready (Priority: 6)
a (#21) is now ready (Priority: 7)                     b (#24) is now ready (Priority: 4)
Running Thread: a (#21) (Priority: 7)                  c (#25) is now ready (Priority: 7)
a working 3                                            Running Thread: c (#25) (Priority: 7)
a (#21) is now ready (Priority: 7)                     c (#25) is now ready (Priority: 2)
Running Thread: a (#21) (Priority: 7)                  Running Thread: a (#23) (Priority: 6)
a working 4                                            Running Thread: c (#25) (Priority: 6)
a (#21) is now ready (Priority: 7)                     c active ('a' wants its lock back so we are
Running Thread: a (#21) (Priority: 7)                  here)
a reached 1/2 way, changing priority                   a (#23) is now ready (Priority: 6)
a working 5                                            c (#25) is now ready (Priority: 2)
a (#21) is now ready (Priority: 2)                     Running Thread: a (#23) (Priority: 6)
Running Thread: b (#22) (Priority: 4)                  a active
b started working                                      main (#0) is now ready (Priority: 1)
b working 0                                            Finished Thread: a (#23)
b (#22) is now ready (Priority: 4)                     Running Thread: b (#24) (Priority: 4)
Running Thread: b (#22) (Priority: 4)                  b started working
b working 1                                            b working 0
b (#22) is now ready (Priority: 4)                     b (#24) is now ready (Priority: 4)
Running Thread: b (#22) (Priority: 4)                  Running Thread: b (#24) (Priority: 4)



3/16/2013                                                                                 Page 2022
NETS3604 – Nachos – Phase 1 – Threads Implementation
b working 1                                                  Finished Thread: b (#24)
b (#24) is now ready (Priority: 4)                           Running Thread: c (#25) (Priority: 2)
Running Thread: b (#24) (Priority: 4)                        c active-again (should be after 'a' and 'b'
b working 2                                                  done)
b (#24) is now ready (Priority: 4)                           Finished Thread: c (#25)
Running Thread: b (#24) (Priority: 4)                        Running Thread: main (#0) (Priority: 1)
b finished working
Case 1: Simple test of normal interleaved thread functionality - without priority donation taking part.

Case 2: Once again, testing without donation - however altering thread priority during execution to observe the
order change when a thread yields.

Case 3: Priority donation testing, by running three threads. We observe that normally this case should deadlock
without donation - as the low priority thread would not be given a chance to execute and release its lock on the
desired resource. However, here we can observe that the low-priority thread (c) is given help in order to help
unlock thread 'a'.




3/16/2013                                                                                             Page 2122

				
DOCUMENT INFO
Shared By:
Categories:
Tags:
Stats:
views:0
posted:3/17/2013
language:Latin
pages:21