180 likes | 192 Views
Implement a Priority Scheduler with a PriorityQueue and ThreadState inner classes to handle priority donation. Solve the Priority Donation problem and schedule threads for limited resources like CPU, Lock, and Semaphore.
E N D
Project 3 Tutorial yuanhaoYU
Project Task • Implement class PriorityScheduler • complete an inner class PriorityQueue, whose instance stores queued thread • complete an inner class ThreadState utilized to record associated priority • make class PriorityScheduler functional. • solve Priority Donation problem
Scheduler • Scheduler is responsible for scheduling threads for all limited resources, CPU, Lock, and Semaphore • Black Box Perspective: • the resource (e.g. class Lock) is responsible for adding thread to a queue of scheduler • the resource requests scheduler return a thread from the queue • the queue here means a data structure storing threads waiting for the resource
Scheduler • Resource, Queue, and Scheduler Resource Lock C Lock Thread C holds the resource queue H Z Thread H waits for the Lock; it actually becomes a element of the queue Resource Lock contains a queue instance When thread C releases the Lock, Scheduler is utilized to decide which thread in the queue should be the next one
RoundRobinScheduler • What’s Round Robin scheduler?
Construct a new FifoQueue Instance in memory. It is usually called by initialization of resource RoundRobinScheduler public class RoundRobinScheduler extends Scheduler { public RoundRobinScheduler() { } public ThreadQueue newThreadQueue(boolean transferPriority) { return new FifoQueue(); } private class FifoQueue extends ThreadQueue { public void waitForAccess(KThread thread) { Lib.assertTrue(Machine.interrupt().disabled()); waitQueue.add(thread); } public KThread nextThread() { Lib.assertTrue(Machine.interrupt().disabled()); if (waitQueue.isEmpty()) return null; return (KThread) waitQueue.removeFirst(); } • Basically, each kind scheduler needs to design its own corresponding queue; • Round Robin Scheduler makes use of a FIFO queue: the FifoQueue class is declared in the Scheduler class; • Most importantly, the data structure storing threads should be a member of this class • called by resource and enqueuer a thread • called by resource and request a waiting thread
RoundRobinScheduler public void acquire(KThread thread) { Lib.assertTrue(Machine.interrupt().disabled()); Lib.assertTrue(waitQueue.isEmpty()); } public void print() { Lib.assertTrue(Machine.interrupt().disabled()); for (Iterator i=waitQueue.iterator(); i.hasNext(); ) System.out.print((KThread) i.next() + " "); } private LinkedList<KThread> waitQueue = new LinkedList<KThread>(); } } • resource inform queue that it is occupied by a thread • In RR, just check some conditions and do nothing • All threads waiting are actually stored here in waitQueue • This data structure is implemented by LinkedList • the whole class fifoQueue is designed to make this waitQueue act like a FIFO queue
RoundRobinScheduler • Resource, Queue, and Scheduler in RR C Lock Thread H and Z both wait for the Lock; they actually wait in the waitQueue; We can assume Z comes first and H comes last. this queue is actually a FIFO queue; All threads are actually stored in the member variable waitQueue of the class FifoQueue queue H Z When thread C releases the Lock, nextThread() function will be called by resource. The FifoQueue will dequeuer thread Z at front
Priority Scheduler • When resource requests a thread, priority scheduler should return the one with the highest priority • Features: • each thread is assigned with a priority • always select the highest priority thread • FCFS with the same priority level
Priority Scheduler • When request a thread in queue, priority scheduler should return the one with highest priority • Features: • each thread is assigned with a priority • always select the highest priority thread • FCFS with the same priority level P3 P2 P0 P1 Priority Scheduling Round Robin Scheduling 0 5 11 14 22
Priority Scheduler There should be a place to store the associated priority for each thread Thread with priority 1 Thread Z with priority 3 C Lock Thread H with priority 4 and Z both wait for the Lock; We can still assume Z comes first and H comes last. The data structure acting the queue to store threads should be implemented; The data structure should be a member of the queue class queue 1 1 1 1 H:4 Z:3 When thread C releases the Lock, nextThread() function will be called. Priority Scheduler should return the one with highest priority H.
PriorityScheduler • There are two inner class in PriorityScheduler • Class PriorityQueue is responsible for storing waiting threads • Class ThreadStateis responsible to record associated priority
PriorityScheduler • called by resource and return a instance of priority queue public class PriorityScheduler extends Scheduler { public PriorityScheduler() {..} public ThreadQueue newThreadQueue(boolean transferPriority) {..} public int getPriority(KThread thread) {..} public int getEffectivePriority(KThread thread) {..} public void setPriority(KThread thread, int priority) {..} public boolean increasePriority() {..} public boolean decreasePriority() {..} public static final int priorityDefault = 1; public static final int priorityMinimum = 0; public static final int priorityMaximum = 7; protected ThreadState getThreadState(KThread thread) {..} protected class PriorityQueue extends ThreadQueue {..} protected class ThreadState {...} } • Priority operation • In these functions, methods (functions) of ThreadState will be called to actually operate priority, since the priority is stored there • Inner class PriorityQueue • The data structure storing threads should be a member variable in the class • Inner class ThreadState • The data structure storing priority should be a member variable in the class
PriorityScheduler protected class PriorityQueue extends ThreadQueue { PriorityQueue(boolean transferPriority) {..} public void waitForAccess(KThread thread) {..} public void acquire(KThread thread) {..} public KThread nextThread() { Lib.assertTrue(Machine.interrupt().disabled()); // implement me return null; } protected ThreadState pickNextThread() { // implement me return null; } public boolean transferPriority; } • Implement these two functions to make sure that it returns the thread with highest priority • Attention: the data structure storing threads needs to be implemented • You could choose any suitable data structure like heap, linkedlist, but have to make sure it act like priority queue • So, there may be some additional functions you need to implement
PriorityScheduler protected class ThreadState { public ThreadState(KThread thread) {..} public int getPriority() {..} public int getEffectivePriority() {} // implement me public void setPriority(int priority) {} // implement me public void waitForAccess(PriorityQueue waitQueue) {} // implement me public void acquire(PriorityQueue waitQueue) { } // implement me protected KThread thread; protected int priority; } • The data structure storing priority • Consider how to store donated priority • You can implement priority donation here, since the priority is stored here. • To donate priority, you may need to find another thread holding a queue; consider how to find that thread
Priority Donation • When will it happen? L:2 Lock 1 Iter2: C donates to L 2->7 queue Z:4 1 C:3 1 1 1 Change priority from 3 to 7 C:3 Lock 2 Iter1: H donates to C 3->7 queue 1 1 1 1 H:3 1
Priority Donation • In Nachos, we can assume a thread only waits in one queue. • Logic: • Starts from a thread t0 • Check if it waits in a queue • Donate its real priority to the thread t1 holding the queue • Restore priority • in the dequeue part (why?)
Compile and Execute • When Nachos initializes, a particular scheduler will be created according to the configure file. ThreadedKernel.scheduler = nachos.threads.PriorityScheduler • Under Proj2 • Eclipse with Input: • -- • nachos.ag.ThreadGrader5 • -- • nachos.ag.ThreadGrader6