alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Akka/Scala example source code file (DiningHakkersOnFsm.java)

This example Akka source code file (DiningHakkersOnFsm.java) is included in my "Source Code Warehouse" project. The intent of this project is to help you more easily find Akka and Scala source code examples by using tags.

All credit for the original source code belongs to akka.io; I'm just trying to make examples easier to find. (For my Scala work, see my Scala examples and tutorials.)

Akka tags/keywords

actor, actorref, akka, chopstick, concurrent, duration, hakker, list, milliseconds, seconds, string, taken, takenby, takenchopsticks, time

The DiningHakkersOnFsm.java Akka example source code

package sample.fsm;

import akka.actor.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import scala.concurrent.duration.Duration;
import scala.concurrent.duration.FiniteDuration;

import static java.util.concurrent.TimeUnit.*;
import static sample.fsm.Messages.*;

// Akka adaptation of
// http://www.dalnefre.com/wp/2010/08/dining-philosophers-in-humus/

public class DiningHakkersOnFsm {
  /**
   * Some states the chopstick can be in
   */
  public static enum CS {
    Available,
    Taken
  }

  /**
   * Some state container for the chopstick
   */
  public static final class TakenBy {
    public final ActorRef hakker;
    public TakenBy(ActorRef hakker){
      this.hakker = hakker;
    }
  }

  /*
  * A chopstick is an actor, it can be taken, and put back
  */
  public static class Chopstick extends AbstractLoggingFSM<CS, TakenBy> {
    {
    // A chopstick begins its existence as available and taken by no one
    startWith(CS.Available, new TakenBy(context().system().deadLetters()));

    // When a chopstick is available, it can be taken by a some hakker
    when(CS.Available,
      matchEventEquals(Take, (take, data) ->
        goTo(CS.Taken).using(new TakenBy(sender())).replying(new Taken(self()))));

    // When a chopstick is taken by a hakker
    // It will refuse to be taken by other hakkers
    // But the owning hakker can put it back
    when(CS.Taken,
      matchEventEquals(Take, (take, data) ->
        stay().replying(new Busy(self()))).
      event((event, data) -> (event == Put) && (data.hakker == sender()), (event, data) ->
        goTo(CS.Available).using(new TakenBy(context().system().deadLetters()))));

    // Initialze the chopstick
    initialize();
    }
  }

  /**
  * Some fsm hakker states
  */
  public static enum HS {
    Waiting,
    Thinking,
    Hungry,
    WaitForOtherChopstick,
    FirstChopstickDenied,
    Eating
  }

  /**
  * Some state container to keep track of which chopsticks we have
  */
  public static final class TakenChopsticks {
    public final ActorRef left;
    public final ActorRef right;

    public TakenChopsticks(ActorRef left, ActorRef right) {
      this.left = left;
      this.right = right;
    }
  }

  /*
  * A fsm hakker is an awesome dude or dudette who either thinks about hacking or has to eat ;-)
  */
  public static class Hakker extends AbstractLoggingFSM<HS, TakenChopsticks> {
    private String name;
    private ActorRef left;
    private ActorRef right;

    public Hakker(String name, ActorRef left, ActorRef right) {
      this.name = name;
      this.left = left;
      this.right = right;
    }

    {
    //All hakkers start waiting
    startWith(HS.Waiting, new TakenChopsticks(null, null));

    when(HS.Waiting,
      matchEventEquals(Think, (think, data) -> {
        System.out.println(String.format("%s starts to think", name));
        return startThinking(Duration.create(5, SECONDS));
    }));

    //When a hakker is thinking it can become hungry
    //and try to pick up its chopsticks and eat
    when(HS.Thinking,
      matchEventEquals(StateTimeout(), (event, data) -> {
        left.tell(Take, self());
        right.tell(Take, self());
        return goTo(HS.Hungry);
    }));

    // When a hakker is hungry it tries to pick up its chopsticks and eat
    // When it picks one up, it goes into wait for the other
    // If the hakkers first attempt at grabbing a chopstick fails,
    // it starts to wait for the response of the other grab
    when(HS.Hungry,
      matchEvent(Taken.class, (taken, data) -> taken.chopstick == left,
        (taken, data) -> goTo(HS.WaitForOtherChopstick).using(new TakenChopsticks(left, null))).
      event(Taken.class, (taken, data) -> taken.chopstick == right,
        (taken, data) -> goTo(HS.WaitForOtherChopstick).using(new TakenChopsticks(null, right))).
      event(Busy.class,
        (busy, data) -> goTo(HS.FirstChopstickDenied)));

    // When a hakker is waiting for the last chopstick it can either obtain it
    // and start eating, or the other chopstick was busy, and the hakker goes
    // back to think about how he should obtain his chopsticks :-)
    when(HS.WaitForOtherChopstick,
      matchEvent(Taken.class,
        (taken, data) -> (taken.chopstick == left && data.left == null && data.right != null),
        (taken, data) -> startEating(left, right)).
      event(Taken.class,
        (taken, data) -> (taken.chopstick == right && data.left != null && data.right == null),
        (taken, data) -> startEating(left, right)).
      event(Busy.class, (busy, data) -> {
        if (data.left != null) left.tell(Put, self());
        if (data.right != null) right.tell(Put, self());
        return startThinking(Duration.create(10, MILLISECONDS));
      }));

    // When the results of the other grab comes back,
    // he needs to put it back if he got the other one.
    // Then go back and think and try to grab the chopsticks again
    when(HS.FirstChopstickDenied,
      matchEvent(Taken.class, (taken, data) -> {
        taken.chopstick.tell(Put, self());
        return startThinking(Duration.create(10, MILLISECONDS));
      }).
      event(Busy.class, (busy, data) ->
        startThinking(Duration.create(10, MILLISECONDS))));

    // When a hakker is eating, he can decide to start to think,
    // then he puts down his chopsticks and starts to think
    when(HS.Eating,
      matchEventEquals(StateTimeout(), (event, data) -> {
        left.tell(Put, self());
        right.tell(Put, self());
        System.out.println(String.format("%s puts down his chopsticks and starts to think", name));
        return startThinking(Duration.create(5, SECONDS));
      }));

    // Initialize the hakker
    initialize();
    }

    private FSM.State<HS, TakenChopsticks> startEating(ActorRef left, ActorRef right) {
      System.out.println(String.format("%s has picked up %s and %s and starts to eat",
        name, left.path().name(), right.path().name()));
      return goTo(HS.Eating).using(new TakenChopsticks(left, right)).forMax(Duration.create(5, SECONDS));
    }

    private FSM.State<HS, TakenChopsticks> startThinking(FiniteDuration duration) {
      return goTo(HS.Thinking).using(new TakenChopsticks(null, null)).forMax(duration);
    }
  }

  /*
  * Alright, here's our test-harness
  */
  public static void main(String[] args) {
    ActorSystem system = ActorSystem.create();
    //Create 5 chopsticks
    ActorRef[] chopsticks = new ActorRef[5];
    for (int i = 0; i < 5; i++)
      chopsticks[i] = system.actorOf(Props.create(Chopstick.class), "Chopstick" + i);

    //Create 5 awesome hakkers and assign them their left and right chopstick
    List<String> names = Arrays.asList("Ghosh", "Boner", "Klang", "Krasser", "Manie");
    List<ActorRef> hakkers = new ArrayList<>();
    int i = 0;
    for (String name: names) {
      hakkers.add(system.actorOf(Props.create(Hakker.class, name, chopsticks[i], chopsticks[(i + 1) % 5]), name));
      i++;
    }
    //Signal all hakkers that they should start thinking, and watch the show
    hakkers.stream().forEach(hakker -> hakker.tell(Think, ActorRef.noSender()));
  }
}

Other Akka source code examples

Here is a short list of links related to this Akka DiningHakkersOnFsm.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.