package nachos.threads; import nachos.machine.*; /** * A <tt>Semaphore</tt> is a synchronization primitive with an unsigned value. * A semaphore has only two operations: * * <ul> * <li><tt>P()</tt>: waits until the semaphore's value is greater than zero, * then decrements it. * <li><tt>V()</tt>: increments the semaphore's value, and wakes up one thread * waiting in <tt>P()</tt> if possible. * </ul> * * <p> * Note that this API does not allow a thread to read the value of the * semaphore directly. Even if you did read the value, the only thing you would * know is what the value used to be. You don't know what the value is now, * because by the time you get the value, a context switch might have occurred, * and some other thread might have called <tt>P()</tt> or <tt>V()</tt>, so the * true value might now be different. */ public class Semaphore { /** * Allocate a new semaphore. * * @param initialValue the initial value of this semaphore. */ public Semaphore(int initialValue) { value = initialValue; } /** * Atomically wait for this semaphore to become non-zero and decrement it. */ public void P() { boolean intStatus = Machine.interrupt().disable(); if (value == 0) { waitQueue.waitForAccess(KThread.currentThread()); KThread.sleep(); } else { value--; } Machine.interrupt().restore(intStatus); } /** * Atomically increment this semaphore and wake up at most one other thread * sleeping on this semaphore. */ public void V() { boolean intStatus = Machine.interrupt().disable(); KThread thread = waitQueue.nextThread(); if (thread != null) { thread.ready(); } else { value++; } Machine.interrupt().restore(intStatus); } private static class PingTest implements Runnable { PingTest(Semaphore ping, Semaphore pong) { this.ping = ping; this.pong = pong; } public void run() { for (int i=0; i<10; i++) { ping.P(); pong.V(); } } private Semaphore ping; private Semaphore pong; } /** * Test if this module is working. */ public static void selfTest() { Semaphore ping = new Semaphore(0); Semaphore pong = new Semaphore(0); new KThread(new PingTest(ping, pong)).setName("ping").fork(); for (int i=0; i<10; i++) { ping.V(); pong.P(); } } private int value; private ThreadQueue waitQueue = ThreadedKernel.scheduler.newThreadQueue(false); }