CLOVER🍀

That was when it all began.

Scala ActorのDebugオブジェクト

ScalaのActorライブラリを見ていると、たまにDebugというクラスを見かけます。例えば…

Code from Reactor.scala... version 2.9.0.1

/*                     __                                               *\
**     ________ ___   / /  ___     Scala API                            **
**    / __/ __// _ | / /  / _ |    (c) 2005-2011, LAMP/EPFL             **
**  __\ \/ /__/ __ |/ /__/ __ |    http://scala-lang.org/               **
** /____/\___/_/ |_/____/_/ | |                                         **
**                          |/                                          **
\*                                                                      */


package scala.actors

import scala.actors.scheduler.{DelegatingScheduler, ExecutorScheduler,
                               ForkJoinScheduler, ThreadPoolConfig}
import java.util.concurrent.{ThreadPoolExecutor, TimeUnit, LinkedBlockingQueue}

private[actors] object Reactor {

  val scheduler = new DelegatingScheduler {
    def makeNewScheduler: IScheduler = {
      val sched = if (!ThreadPoolConfig.useForkJoin) {
        // default is non-daemon
        val workQueue = new LinkedBlockingQueue[Runnable]
        ExecutorScheduler(
          new ThreadPoolExecutor(ThreadPoolConfig.corePoolSize,
                                 ThreadPoolConfig.maxPoolSize,
                                 60000L,
                                 TimeUnit.MILLISECONDS,
                                 workQueue,
                                 new ThreadPoolExecutor.CallerRunsPolicy))
      } else {
        // default is non-daemon, non-fair
        val s = new ForkJoinScheduler(ThreadPoolConfig.corePoolSize, ThreadPoolConfig.maxPoolSize, false, false)
        s.start()
        s
      }
      Debug.info(this+": starting new "+sched+" ["+sched.getClass+"]")
      sched
    }
  }
…省略…

つまり、この部分ですね。

      Debug.info(this+": starting new "+sched+" ["+sched.getClass+"]")

なんか、Actor用のデバッグクラスっぽいのですが、普段はActorを使っても何も言ってくれません。どうなっているのか?ってことで、Debugクラスを見てみましょう。正確には、Debugオブジェクトですが。

Code from Debug.scala... version 2.9.0.1

/*                     __                                               *\
**     ________ ___   / /  ___     Scala API                            **
**    / __/ __// _ | / /  / _ |    (c) 2005-2011, LAMP/EPFL             **
**  __\ \/ /__/ __ |/ /__/ __ |    http://scala-lang.org/               **
** /____/\___/_/ |_/____/_/ | |                                         **
**                          |/                                          **
\*                                                                      */


package scala.actors

/**
 * Provides methods for generating debugging output.
 * 
 * @author Philipp Haller
 */
object Debug extends Logger("") {}

private[actors] class Logger(tag: String) {
  private var lev = 2

  def level = lev
  def level_= (lev: Int) = { this.lev = lev }

  private val tagString = if (tag == "") "" else " ["+tag+"]"

  def info(s: String) =
    if (lev > 2) System.out.println("Info" + tagString + ": " + s)

  def warning(s: String) =
    if (lev > 1) System.err.println("Warning" + tagString + ": " + s)

  def error(s: String) =
    if (lev > 0) System.err.println("Error" + tagString + ": " + s)

  def doInfo(b: => Unit) =
    if (lev > 2) b

  def doWarning(b: => Unit) =
    if (lev > 1) b

  def doError(b: => Unit) =
    if (lev > 0) b
}

@deprecated("this class is going to be removed in a future release", "2.7.7")
class Debug(tag: String) extends Logger(tag) {}

すごい単純。デフォルトのログレベルが2(Warning以上)なので、普段は何も出力されないってわけですね。でも、warnとかを書いているコード、見たことないんですけど…。

スケープゴート的に、こんなコードを用意。

import scala.actors.{Actor, Debug}
import scala.actors.Actor._

object DebugActor extends App {
  println("Create Debug Actor...")
  val debugActor = new DebugActor

  println("Debug Actor Start...")
  debugActor.start()

  println("Debug Actor Send Message...")
  debugActor ! "Hello World"

  println("Debug Actor Shutdown...")
  debugActor ! 'Shutdown
}

class DebugActor extends Actor {
  def act(): Unit = loop {
    react {
      case message: String => println("Received String Message[%s]".format(message))
      case 'Shutdown => exit('normal)
      case message => println("Received Unknown Message[%s]".format(message))
    }
  }
}

いたって簡単なActorです。では、実行。

$ fsc DebugActor.scala 
$ scala DebugActor
Create Debug Actor...
Debug Actor Start...
Debug Actor Send Message...
Debug Actor Shutdown...
Received String Message[Hello World]

では、最初を少しだけ変更。

import scala.actors.{Actor, Debug}
import scala.actors.Actor._

object DebugActor extends App {
  Debug.level = 3

  println("Create Debug Actor...")
  val debugActor = new DebugActor
  …省略…

これで実行してみましょう。

$ fsc DebugActor.scala 
$ scala DebugActor
Create Debug Actor...
Debug Actor Start...
Info: initializing scala.actors.Scheduler$@1d2940b3...
Info: scala.actors.scheduler.ThreadPoolConfig$@49ff0dde: java.version = 1.6.0_24
Info: scala.actors.scheduler.ThreadPoolConfig$@49ff0dde: java.vm.vendor = Sun Microsystems Inc.
Info: scala.actors.scheduler.ForkJoinScheduler@303020ad: parallelism 4
Info: scala.actors.scheduler.ForkJoinScheduler@303020ad: max pool size 256
Info: scala.actors.Scheduler$@1d2940b3: starting new scala.actors.scheduler.ForkJoinScheduler@303020ad [class scala.actors.scheduler.ForkJoinScheduler]
Debug Actor Send Message...
Debug Actor Shutdown...
Received String Message[Hello World]
Info: scala.actors.scheduler.ForkJoinScheduler@303020ad: all actors terminated
Info: scala.actors.scheduler.ForkJoinScheduler@303020ad: initiating shutdown...

なんか、盛大に出力が増えましたね。並列度、Schedulerの実装やSchedulerに属するActorが全て終了したことなどのメッセージが出力されるようになるようです。

知っていると便利…なのか?

あと、余談ですがActorで利用しているスレッドはデーモンスレッドではないようなので、Schedulerに属する全てのActorを終了しないとJavaVMが終了できないようですね。事実、上記コードからShutdownを投げている箇所を削除すると、ず〜っと終了を待ち続けるようになってしまいます。DaemonActor使えって?