Richard Imaoka's Blog

2017年より職業Scalaプログラマになった、リチャード・伊真岡のブログです。

Akka Internal - (Akkaの内部動作を知る) Actorのreceiveメソッドはどのように呼ばれるか?

Akkaの!メソッドでメッセージを「受け取る」側の動作はどうなっているのか?

前回の記事ではAkkaの!メソッドでメッセージを「送る側」の動作が完全なNon-Blockingであることを確認しました。

richard-imaoka.hatenablog.com

今回はメッセージを受け取る側、すなわちActorのreceiveメソッドがどのように呼ばれるのかを見ていきます。

  def receive = {
    ...
  }

Actorのreceiveメソッド内にブレークポイントを置いてデバッグすると、一番最初にJavaのForkJoinWorkerThreadが動いていることがわかる

Actorのreceiveメソッドはメッセージを送る側(この記事の場合mainスレッド)とは別のスレッドで動きます。その際にForkJoinWorkerThreadを使っているのですね。

f:id:richard-imaoka:20151027054815p:plain

Image courtesy of digitalart at FreeDigitalPhotos.net

//scala.concurrent.forkjoin.ForkJoinWorkerThread
    public void run() {
        ...
            this.pool.runWorker(this.workQueue);
   }

さらにここから、MailboxクラスのprocessMailbox()というメソッドが呼ばれているのがわかります。前回の記事で見たように、MailboxはMessaging Queueを持っていて、これがNon-Blocking (lock-free) queueになっています。

//akka.dispatch.Mailbox
  override final def run(): Unit = {
    try {
      ...
        processMailbox() //Then deal with messages
      ...
    }
  }

というわけで、このMessaging QueueはmainスレッドからもActorが動いている別スレッドからもアクセスされる、スレッド間共有オブジェクトです。

processMailbox()メソッドの中身は以下のようになっています。"next"というのはここではMessaging Queueから取り出したメッセージになっていて、actor (ActorCell)のinvokeメソッドはActorのreceiveメソッドを呼び出すようになっています。

//akka.dispatch.Maibox

  @tailrec private final def processMailbox(
    ...
    if (shouldProcessMessage) {
      val next = dequeue()
      ...
        actor invoke next
      ...
      }
    }

f:id:richard-imaoka:20151027054834p:plain

Image courtesy of digitalart at FreeDigitalPhotos.net