package riso.general;
/** An instance of this class represents a counting semaphore.
* The file <tt>Semaphore.java</tt> was written by Stephen Hartley;
* see <a href="http://www.mcs.drexel.edu/~shartley/ConcProgJava/">http://www.mcs.drexel.edu/~shartley/ConcProgJava/</a>.
* This file is released under the terms of the GNU GPL by
* permission of Stephen Hartley:
*
* <pre>
* Date: Sun, 27 Jan 2002 07:47:54 -0500 (EST)
* From: "Stephen J. Hartley" <hartley@elvis.rowan.edu>
* To: "Robert Dodier" <robert_dodier@yahoo.com>
* Subject: Re: Permission to redistribute concurrent software?
*
* Robert,
*
* Yes, you have my permission. Please include a header
* comment on the files as to their origin. I am glad
* that my software was able to help you.
* I'd like to see the program sometime. Can you
* send me a URL (when ready) to where I could look
* over the source code?
* Thanks.
*
* Steve Hartley
*
* >I am releasing RISO under the terms of the GNU
* >General Public License (GPL). I would like to
* >redistribute Semaphore.java and
* >WouldBlockException.java under the GPL. Do I have your
* >permission to redistribute those two files under GPL?
* </pre>
*/
public class Semaphore {
// if value < 0, then abs(value) is the size of the P() queue
protected int value = 0;
public Semaphore() {value = 0;}
public Semaphore(int initial) {
// Don't be silent about bad initial value; tell the user!
if (initial < 0) throw new IllegalArgumentException("initial<0");
value = initial;
}
public synchronized void P() {
value--;
if (value < 0) {
while (true) { // we must be notified not interrupted
try {
wait();
break; // notify(), so P() succeeds
} catch (InterruptedException e) {
/*
* A race condition exists if a notify() occurs at about the same
* time that a waiting thread is interrupted by some other thread
* (a thread is interrupted if its interrupt() method is invoked).
* Suppose that the waiting set of this semaphore object has exactly
* one thread in it, and at about the same time, some thread wants to
* enter V() to call notify() and some other thread calls interrupt()
* on the waiting thread. The waiting thread is moved from the waiting
* set to the ready queue, where it must reacquire the semaphore object
* lock to reenter. One of two things can happen. The notify() is
* done and since the waiting set is empty, nothing happens; then the
* interrupted thread reenters P() inside the catch block and waits
* again. Or, the interrupted thread reenters P() and calls wait
* again, then gets notified. The signal is lost in the first case
* but not the second. Lost signals can lead to deadlock and violation
* of mutual exclusion.
* The fix is to move the `while(true)' outside the `value--;
* if (value < 0)' and to insert `value++' just before the `continue'.
* Alternatively, insert `if (value >= 0) break' just above `continue'.
* This is left as an exercise for those readers whose programs use
* interrupt().
*/
System.err.println
("Semaphore.P(): InterruptedException, wait again");
if (value >= 0) break; // race condition fix
else continue; // no V() yet
}
}
}
}
public synchronized void V() { // this technique prevents
value++; // barging since any caller of
if (value <= 0) notify(); // P() will wait even if it
} // enters before signaled thread
// do not do a `if (S.value() > 0) S.P(); else ...'
// because there is a race condition; use S.tryP() instead
public synchronized int value() {
return value;
}
public synchronized String toString() {
return String.valueOf(value);
}
public synchronized void tryP() throws WouldBlockException {
if (value > 0) this.P(); // nested locking, but already have lock
else throw new WouldBlockException();
}
public synchronized void interruptibleP() throws InterruptedException {
value--;
if (value < 0) {
try { wait(); }
catch (InterruptedException e) {
System.err.println
("Semaphore.interruptibleP(): InterruptedException");
value++; // backout, i.e., restore semaphore value
throw e;
}
}
}
}