p2/threads/LotteryScheduler.java

211 lines
5.8 KiB
Java

package nachos.threads;
import nachos.machine.*;
import java.util.TreeSet;
import java.util.HashSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Random;
/**
* 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()
{
num_tickets=0;
rng=new Random();
thread_original_tickets=new HashMap<KThread,Integer>();
thread_effective_tickets=new HashMap<KThread,Integer>();
upstream=new HashMap<KThread,HashMap<KThread,Integer>>();
}
public int getPriority(KThread thread)
{
Lib.assertTrue(Machine.interrupt().disabled());
init_thread_tickets(thread);
return thread_original_tickets.get(thread);
}
public int getEffectivePriority(KThread thread)
{
Lib.assertTrue(Machine.interrupt().disabled());
init_thread_tickets(thread);
return thread_effective_tickets.get(thread);
}
public void setPriority(KThread thread,int priority)
{
Lib.assertTrue(Machine.interrupt().disabled());
Lib.assertTrue(priority>=min_tickets&&priority<=max_tickets);
init_thread_tickets(thread);
update(thread,priority-thread_original_tickets.get(thread));
}
public boolean increasePriority()
{
boolean intStatus = Machine.interrupt().disable();
KThread thread = KThread.currentThread();
int priority=getPriority(thread);
if(priority==priorityMaximum)
return false;
update(thread,1);
Machine.interrupt().restore(intStatus);
return true;
}
public boolean decreasePriority()
{
boolean intStatus = Machine.interrupt().disable();
KThread thread = KThread.currentThread();
int priority=getPriority(thread);
if(priority==priorityMinimum)
return false;
update(thread,-1);
Machine.interrupt().restore(intStatus);
return true;
}
/**
* 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 lottery_queue(transferPriority);
}
protected void init_thread_tickets(KThread x)
{
if(!thread_original_tickets.containsKey(x))
{
num_tickets+=1;
thread_original_tickets.put(x,1);
thread_effective_tickets.put(x,1);
upstream.put(x,new HashMap<KThread,Integer>());
}
}
protected void update(KThread x,int amount)
{
//System.out.println("Update effective tickets by "+amount+": "+x);
num_tickets+=amount;
thread_effective_tickets.put(x,thread_effective_tickets.get(x)+amount);
HashMap<KThread,Integer> tmp=upstream.get(x);
for(KThread y:tmp.keySet())
if(tmp.get(y)!=0)
update(y,tmp.get(y)*amount);
}
protected void add_upstream(KThread x,KThread y)
{
update(y,thread_effective_tickets.get(x));
if(!upstream.get(x).containsKey(y))
upstream.get(x).put(y,1);
else
upstream.get(x).put(y,upstream.get(x).get(y)+1);
}
protected void remove_upstream(KThread x,KThread y)
{
update(y,-thread_effective_tickets.get(x));
upstream.get(x).put(y,upstream.get(x).get(y)-1);
}
protected class lottery_queue extends ThreadQueue
{
public lottery_queue(boolean transfer)
{
this.transfer=transfer;
acquiring_process=null;
waiting_queue=new HashSet<KThread>();
}
public void waitForAccess(KThread thread)
{
init_thread_tickets(thread);
waiting_queue.add(thread);
if(transfer&&acquiring_process!=null)
add_upstream(thread,acquiring_process);
}
public void acquire(KThread thread)
{
init_thread_tickets(thread);
acquiring_process=thread;
if(transfer)
for(KThread x:waiting_queue)
add_upstream(x,thread);
}
public KThread nextThread()
{
if(waiting_queue.isEmpty())
{
acquiring_process=null;
return null;
}
KThread result=null;
int waiting_sum=0;
for(KThread x:waiting_queue)
waiting_sum+=thread_effective_tickets.get(x);
int win=rng.nextInt(waiting_sum),acc=0;
for(KThread x:waiting_queue)
if(acc<=win&&win<acc+thread_effective_tickets.get(x))
{
result=x;
break;
}
else
acc+=thread_effective_tickets.get(x);
if(transfer&&acquiring_process!=null)
for(KThread x:waiting_queue)
remove_upstream(x,acquiring_process);
waiting_queue.remove(result);
acquiring_process=result;
if(transfer)
for(KThread x:waiting_queue)
add_upstream(x,acquiring_process);
return result;
}
public void print()
{
}
public boolean transfer;
private KThread acquiring_process;
private HashSet<KThread> waiting_queue;
}
protected int num_tickets=0;
protected static HashMap<KThread,Integer> thread_original_tickets;
protected static HashMap<KThread,Integer> thread_effective_tickets;
protected static HashMap<KThread,HashMap<KThread,Integer>> upstream;
public static final int min_tickets=1;
public static final int max_tickets=Integer.MAX_VALUE;
protected static Random rng;
}