package nachos.threads; import nachos.machine.*; import java.util.*; /** * A scheduler that chooses threads using a lottery. * * <p> * A lottery scheduler associates a number of tickets with each thread. When a * thread needs to be dequeued, a random lottery is held, among all the tickets * of all the threads waiting to be dequeued. The thread that holds the winning * ticket is chosen. * * <p> * Note that a lottery scheduler must be able to handle a lot of tickets * (sometimes billions), so it is not acceptable to maintain state for every * ticket. * * <p> * A lottery scheduler must partially solve the priority inversion problem; in * particular, tickets must be transferred through locks, and through joins. * Unlike a priority scheduler, these tickets add (as opposed to just taking * the maximum). */ public class LotteryScheduler extends PriorityScheduler { /** * Allocate a new lottery scheduler. */ public LotteryScheduler() { } /** * Allocate a new lottery thread queue. * * @param transferPriority <tt>true</tt> if this queue should * transfer tickets from waiting threads * to the owning thread. * @return a new lottery thread queue. */ public ThreadQueue newThreadQueue(boolean transferPriority) { return new LotteryQueue(transferPriority); } public void setPriority(KThread thread, int priority) { Lib.assertTrue(Machine.interrupt().disabled()); getThreadState(thread).setPriority(priority); } public boolean increasePriority() { boolean intStatus = Machine.interrupt().disable(); KThread thread = KThread.currentThread(); setPriority(thread, getPriority(thread) + 1); Machine.interrupt().restore(intStatus); return true; } public boolean decreasePriority() { boolean intStatus = Machine.interrupt().disable(); KThread thread = KThread.currentThread(); setPriority(thread, getPriority(thread) - 1); Machine.interrupt().restore(intStatus); return true; } protected ThreadState getThreadState(KThread thread) { if (thread.schedulingState == null) thread.schedulingState = new LotteryThreadState(thread); return (ThreadState) thread.schedulingState; } public class LotteryQueue extends PriorityQueue { public LotteryQueue(boolean transferPriority) { super(transferPriority); donationController = new LotteryDonationController(queue); } public KThread nextThread() { Lib.assertTrue(Machine.interrupt().disabled()); if(queue.isEmpty()) return null; ThreadState ts = pickNextThread(); queue.remove(threadStates.get(ts)); threadStates.remove(ts); if(transferPriority) donationController.resetMaximumPriority(ts); Lib.debug('P', "NextThread: " + ts.thread + ", priority = " + ts.getPriority() + ", effective priority = " + ts.getEffectivePriority() + ", size = " + queue.size()); acquire(ts.thread); return ts.thread; } protected ThreadState pickNextThread() { int sum = ((LotteryDonationController)donationController).sum; if(sum == 0) return queue.peek().state; int r = (new Random()).nextInt(sum); for(ThreadState ts : threadStates.keySet()) { r -= ts.getEffectivePriority(); if(r < 0) return ts; } System.out.println(sum + " : " + r); Lib.assertNotReached("Failed to pick next thread"); return null; } } protected class LotteryDonationController extends DonationController { public LotteryDonationController(java.util.PriorityQueue<PriorityQueue.ThreadWrapper> queue) { super(queue); } public void setTarget(ThreadState t) { if(target != null) target.retractDonatedPriority(this); target = t; target.donatePriority(this, sum); } public void resetMaximumPriority(ThreadState t) { sum -= t.getEffectivePriority(); target.donatePriority(this, sum); } public void transferPriority(ThreadState t) { sum += t.getEffectivePriority(); target.donatePriority(this, t.getEffectivePriority()); } public int sum = 0; } protected class LotteryThreadState extends ThreadState { public LotteryThreadState(KThread thread) { super(thread); effectivePriority = new LotteryTicketDesc(0); } protected class LotteryTicketDesc extends EffectivePriorityDesc { public LotteryTicketDesc(int priority) { super(priority); } public int getEffectivePriority() { return priority + max_donation; } public void donate(DonationController q, int priority) { if(this.donations.containsKey(q)) max_donation -= this.donations.get(q); this.donations.put(q, priority); max_donation += priority; } public void retract(DonationController q) { if(donations.containsKey(q)) { int p = donations.get(q); donations.remove(q); max_donation -= p; } } } } }