/*****************************************************************
* Memorial University of Newfoundland
* 8893 Concurrent Programming
*
* Solution to reader-writer problem to demo passing the baton as a
* means of condition synchronization using semaphores
*
* @author Dennis Peters
* @version 2001.02.07
****************************************************************/
public class Baton {
public static void main(String[] args) {
try {
Database db = new Database();
DBController contr = new DBController(3);
Thread r1 = new Thread(new Reader(1, contr, db));
Thread r2 = new Thread(new Reader(2, contr, db));
Thread r3 = new Thread(new Reader(3, contr, db));
Thread r4 = new Thread(new Reader(4, contr, db));
Thread r5 = new Thread(new Reader(5, contr, db));
Thread wr = new Thread(new Writer(6, contr, db));
wr.start();
r1.start();
r2.start();
r3.start();
r4.start();
r5.start();
int count = 0;
while (wr.isAlive() && count < 20) {
count++;
Thread.sleep(500);
}
}
catch (InterruptedException e) { }
System.out.println("Baton done");
System.exit(0);
}
}
/*****************************************************************
* Simulate the database
****************************************************************/
class Database {
public void read(int id)
{
System.out.println(id + " is reading");
}
public void write(int id)
{
System.out.println(id + " is writing");
}
}
/*****************************************************************
* Control access to the database so that:
* - writers have exclusive access w.r.t. other writers and
* readers.
* - readers can access concurrently up to NR at a time.
* This implementation uses semaphores and the technique of "passing
* the baton" to ensure that writers will eventually get access.
****************************************************************/
class DBController {
/** lock for readers */
Semaphore readWait;
/** lock for writers */
Semaphore writeWait;
/** CS lock */
Semaphore entry;
/** Number of Readers currently waiting */
int numReadWait;
/** Number of Writers currently waiting */
int numWriteWait;
/** Number of Readers currently in */
int numReaders;
/** Number of Writers currently in (0 or 1) */
int numWriters;
/** Maximum number of concurrent readers */
int maxReaders;
/**
* @param maxR Maximum number of concurrent readers
*/
DBController(int maxR)
{
readWait = new Semaphore(0);
writeWait = new Semaphore(0);
entry = new Semaphore(1);
numReadWait = 0;
numWriteWait = 0;
numReaders = 0;
numWriters = 0;
maxReaders = maxR;
}
/*****************************************************************
* Request permission to read
* @param id Requesting reader.
* @throws InterruptedException
****************************************************************/
public void readRequest(int id)
throws InterruptedException
{
entry.P();
if (numReaders == maxReaders || numWriters > 0 || numWriteWait > 0) {
// Request can't be granted, wait
numReadWait++;
System.out.println("read request from " + id + " BLOCKED.");
entry.V();
readWait.P();
}
numReaders++;
System.out.println("read request from " + id + " GRANTED.");
signal();
}
/*****************************************************************
* request permission to write
* @param id Requesting writer.
* @throws InterruptedException
****************************************************************/
public void writeRequest(int id)
throws InterruptedException
{
entry.P();
if (numReaders > 0 || numWriters > 0) {
// Request can't be granted, wait
numWriteWait++;
System.out.println("write request from " + id + " BLOCKED.");
entry.V();
writeWait.P();
}
numWriters++;
System.out.println("write request from " + id + " GRANTED.");
signal();
}
/*****************************************************************
* relinquish access to read
* @param id Reader releasing.
* @throws InterruptedException
****************************************************************/
public void readRelease(int id)
throws InterruptedException
{
entry.P();
System.out.println("read release from " + id);
numReaders--;
signal();
}
/*****************************************************************
* relinquish access to write
* @param id Writer releasing.
* @throws InterruptedException
****************************************************************/
public void writeRelease(int id)
throws InterruptedException
{
entry.P();
System.out.println("write release from " + id);
numWriters--;
signal();
}
/*****************************************************************
* signal some waiting process
****************************************************************/
private void signal()
throws InterruptedException
{
if (numWriteWait > 0 && numReaders == 0 && numWriters == 0) {
// Let in a writer
numWriteWait--;
writeWait.V();
} else if (numWriters == 0 && numWriteWait == 0
&& numReadWait > 0 && numReaders < maxReaders) {
// Let in a reader
// Note: by checking for numWriteWait == 0 we give writers priority.
numReadWait--;
readWait.V();
} else { // nobody waiting, allow entry again
entry.V();
}
}
}
/*****************************************************************
* Readers attempt to read from the database.
****************************************************************/
class Reader implements Runnable {
int myId; // each reader knows it's own Id
DBController cont;
Database db;
Reader(int id, DBController c, Database d)
{
myId = id;
cont = c;
db = d;
}
public void run()
{
try {
while (true) {
cont.readRequest(myId);
db.read(myId);
Thread.sleep(java.lang.Math.round(java.lang.Math.random()*1000)); // Sleep a random duration
cont.readRelease(myId);
}
}
catch (InterruptedException e) { } // Simply exit.
}
}
/*****************************************************************
* Writers attempt to write to the database.
****************************************************************/
class Writer implements Runnable {
int myId; // each reader knows it's own Id
DBController cont;
Database db;
Writer(int id, DBController c, Database d)
{
myId = id;
cont = c;
db = d;
}
public void run()
{
try {
while (true) {
cont.writeRequest(myId);
db.write(myId);
Thread.sleep(java.lang.Math.round(java.lang.Math.random()*2000)); // Sleep a random duration
cont.writeRelease(myId);
}
}
catch (InterruptedException e) { } // Simply exit.
}
}