package posa
import scala.actors.Actor
/**
* Actor representing a philosopher.
* When receives message n, will eat n times and terminate. Eat sequence:
* 1. picks up left chopstick (may block until available)
* 2. picks up right chopstick (may block until available)
* 3. eats
* 4. puts down right chopstick
* 5. puts down left chopstick
*/
class Philosopher(id: Int, leftChopstick: Int, rightChopstick: Int) extends Actor {
def act() {
receive {
case n: Int =>
// Repeat the eat sequence n times
for (i <- 1 to n) {
while (!Dinner.pickUpChopstick(id, leftChopstick, true)) {}
while (!Dinner.pickUpChopstick(id, rightChopstick, false)) {}
Console.printf("Philosopher %s eats.%n", id.toString)
Dinner.putDownChopstick(id, rightChopstick, false)
Dinner.putDownChopstick(id, leftChopstick, true)
}
exit()
}
}
}
/**
* Dinner of 5 philosophers, each eating 5 times.
*/
object Dinner extends App {
// Map to print out side
val side: Map[Boolean, String] = Map(true -> "Left", false -> "Right")
// Array of Booleans representing chopsticks, true means they are on the table
val chopsticks: Array[Boolean] = Array.fill(5)(true)
// List of Philosopher actors, and their respective chopsticks
val philosophers: List[Philosopher] = List(
new Philosopher(1, 0, 1),
new Philosopher(2, 1, 2),
new Philosopher(3, 2, 3),
new Philosopher(4, 3, 4),
new Philosopher(5, 4, 0)
)
// Let the feast begin
philosophers.foreach(_.start)
Console.println("Dinner is starting!")
philosophers.foreach(_ ! 5)
while (philosophers.exists(_.getState != Actor.State.Terminated)) {}
Console.println("Dinner is over!")
/**
* Synchronized method, attempting to pick up a given chopstick by a philosopher.
* Returns true if the chopstick was available and picked up successfully.
* In order to prevent deadlocks, left hand side chopsticks can only be picked up if there are at least 2 chopsticks on the table.
* Right hand side chopsticks can be picked up as long as they are on the table (philosophers always pick up left chopstick first).
* The synchronization is the JVM native implementation of the Monitor Object pattern.
* In this case the monitor of the Dinner object is used, ensuring that only one chopstick can be picked up at a time.
*/
def pickUpChopstick(philosopher: Int, chopstick: Int, isLeft: Boolean): Boolean = synchronized {
val result = chopsticks(chopstick) && (!isLeft || chopsticks.count(c => c) > 1)
if (result) {
Console.printf("Philosopher %s picks up %s chopstick.%n", philosopher.toString, side(isLeft))
chopsticks(chopstick) = false;
}
result
}
/**
* Puts down the given chopstick by a philosopher.
* No synchronization is needed.
*/
def putDownChopstick(philosopher: Int, chopstick: Int, isLeft: Boolean) = {
Console.printf("Philosopher %s puts down %s chopstick.%n", philosopher.toString, side(isLeft))
chopsticks(chopstick) = true
}
}