hminle
hminle

Reputation: 521

Implement Actor model without Akka in Scala

I am doing my small research that implement Actor without Akka I found one implementation of Actor in Scala. (How to implement actor model without Akka?)

It's very simple. Because I have not enough reputation to add the comment, so I create this question. I wonder if I use Actor like below.

1/ How can I shutdown that actor from main thread?

2/ How can I add feature similar to Akka, like parent actor, kill request, and become method?

import scala.concurrent._

trait Actor[T] {
      implicit val context = ExecutionContext.fromExecutor(java.util.concurrent.Executors.newFixedThreadPool(1))
      def receive: T => Unit
      def !(m: T) = Future { receive(m) }
}

This is my own example when trying to adapt the above code snippet

import scala.concurrent._

/**
  * Created by hminle on 10/21/2016.
  */
trait Message
case class HelloMessage(hello: String) extends Message
case class GoodByeMessage(goodBye: String) extends Message

object State extends Enumeration {
  type State = Value
  val Waiting, Running, Terminating = Value
}

trait Actor[T] {
  implicit val context = ExecutionContext.fromExecutor(java.util.concurrent.Executors.newFixedThreadPool(1))
  private var state: State.State = State.Waiting
  def handleMessage: T => Unit ={
    if(state == State.Waiting) handleMessageWhenWaiting
    else if(state == State.Running) handleMessageWhenRunning
    else handleMessageWhenTerminating
  }
  def !(m: T) = Future {handleMessage(m)}
  def handleMessageWhenWaiting: T => Unit
  def handleMessageWhenRunning: T => Unit
  def handleMessageWhenTerminating: T => Unit
  def transitionTo(destinationState: State.State): Unit = {
    this.state = destinationState
  }
}

class Component1 extends Actor[Message]{
  def handleMessageWhenRunning = {
    case HelloMessage(hello) => {
      println(Thread.currentThread().getName + hello)
    }
    case GoodByeMessage(goodBye) => {
      println(Thread.currentThread().getName + goodBye)
      transitionTo(State.Terminating)
    }
  }

  def handleMessageWhenWaiting = {
    case m => {
      println(Thread.currentThread().getName + " I am waiting, I am not ready to run")
      transitionTo(State.Running)
    }
  }

  def handleMessageWhenTerminating = {
    case m => {
      println(Thread.currentThread().getName + " I am terminating, I cannot handle any message")
      //need to shutdown here
    }
  }

}
class Component2(component1: Actor[Message]) extends Actor[Message]{
  def handleMessageWhenRunning = {
    case HelloMessage(hello) => {
      println(Thread.currentThread().getName + hello)
      component1 ! HelloMessage("hello 1")
    }
    case GoodByeMessage(goodBye) => {
      println(Thread.currentThread().getName + goodBye)
      component1 ! GoodByeMessage("goodbye 1")
      transitionTo(State.Terminating)
    }
  }

  def handleMessageWhenWaiting = {
    case m => {
      println(Thread.currentThread().getName + " I am waiting, I am not ready to run")
      transitionTo(State.Running)
    }
  }

  def handleMessageWhenTerminating = {
    case m => {
      println(Thread.currentThread().getName + " I am terminating, I cannot handle any message")
      //need to shutdown here
    }
  }
}
object ActorExample extends App {
  val a = new Component1
  val b = new Component2(a)
  b ! HelloMessage("hello World 2")
  b ! HelloMessage("hello World 2, 2nd")
  b ! GoodByeMessage("Good bye 2")
  println(Thread.currentThread().getName)
}

Upvotes: 0

Views: 173

Answers (1)

user6860682
user6860682

Reputation:

You can look at Actor model implementation in scalazand take ideas from it, source code in scalaz actor is easier for insight than akka. You have freedom of choice about architecture: you can use mailboxes based on ConcurrentLinkedQueue like in Akka, use CAS for AtomicReffernce like in scalaz, in your case you use Future mechanism. IMO, you must write a context of your actor system, so solve first and second items in your question it's the variant of ActorContext:

val contextStack = new ThreadLocal[List[ActorContext]] 

and shutdown can look like this:

1.

case Kill                       ⇒ throw new ActorKilledException("Kill")
case PoisonPill                 ⇒ self.stop()

2. For storing parent actor and similar task, you must store reference on them:

def parent: ActorRef

it's hard to say about advantages of every technique (CAS, mailboxes), it's possible variants to your research.

Upvotes: 0

Related Questions