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

Akka/Scala example source code file (CoronerSpec.scala)

This example Akka source code file (CoronerSpec.scala) 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

a, concurrent, coroner, coroner's, duration, list, lockingthread, report, semaphore, string, test, testing, testkit, thread, time, xxxx

The CoronerSpec.scala Akka example source code

/**
 * Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
 */
package akka.testkit

import java.io._
import java.lang.management.ManagementFactory
import java.util.concurrent.Semaphore
import java.util.concurrent.locks.ReentrantLock
import org.scalatest.WordSpec
import org.scalatest.Matchers
import scala.concurrent.duration._
import scala.concurrent.Await

@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
class CoronerSpec extends WordSpec with Matchers {

  private def captureOutput[A](f: PrintStream ⇒ A): (A, String) = {
    val bytes = new ByteArrayOutputStream()
    val out = new PrintStream(bytes, true, "UTF-8")
    val result = f(out)
    (result, new String(bytes.toByteArray(), "UTF-8"))
  }

  "A Coroner" must {

    "generate a report if enough time passes" in {
      val (_, report) = captureOutput(out ⇒ {
        val coroner = Coroner.watch(100.milliseconds, "XXXX", out)
        Await.ready(coroner, 5.seconds)
        coroner.cancel()
      })
      report should include("Coroner's Report")
      report should include("XXXX")
    }

    "not generate a report if cancelled early" in {
      val (_, report) = captureOutput(out ⇒ {
        val coroner = Coroner.watch(60.seconds, "XXXX", out)
        coroner.cancel()
        Await.ready(coroner, 1.seconds)
      })
      report should be("")
    }

    "display thread counts if enabled" in {
      val (_, report) = captureOutput(out ⇒ {
        val coroner = Coroner.watch(60.seconds, "XXXX", out, displayThreadCounts = true)
        coroner.cancel()
        Await.ready(coroner, 1.second)
      })
      report should include("Coroner Thread Count starts at ")
      report should include("Coroner Thread Count started at ")
      report should include("XXXX")
      report should not include ("Coroner's Report")
    }

    "display deadlock information in its report" in {

      // Create two threads that each recursively synchronize on a list of
      // objects. Give each thread the same objects, but in reversed order.
      // Control execution of the threads so that they each hold an object
      // that the other wants to synchronize on. BOOM! Deadlock. Generate a
      // report, then clean up and check the report contents.

      final case class LockingThread(name: String, thread: Thread, ready: Semaphore, proceed: Semaphore)

      def lockingThread(name: String, initialLocks: List[ReentrantLock]): LockingThread = {
        val ready = new Semaphore(0)
        val proceed = new Semaphore(0)
        val t = new Thread(new Runnable {
          def run = try recursiveLock(initialLocks) catch { case _: InterruptedException ⇒ () }

          def recursiveLock(locks: List[ReentrantLock]) {
            locks match {
              case Nil ⇒ ()
              case lock :: rest ⇒ {
                ready.release()
                proceed.acquire()
                lock.lockInterruptibly() // Allows us to break deadlock and free threads
                try {
                  recursiveLock(rest)
                } finally {
                  lock.unlock()
                }
              }
            }
          }
        }, name)
        t.start()
        LockingThread(name, t, ready, proceed)
      }

      val x = new ReentrantLock()
      val y = new ReentrantLock()
      val a = lockingThread("deadlock-thread-a", List(x, y))
      val b = lockingThread("deadlock-thread-b", List(y, x))

      // Walk threads into deadlock
      a.ready.acquire()
      b.ready.acquire()
      a.proceed.release()
      b.proceed.release()
      a.ready.acquire()
      b.ready.acquire()
      a.proceed.release()
      b.proceed.release()

      val (_, report) = captureOutput(Coroner.printReport("Deadlock test", _))

      a.thread.interrupt()
      b.thread.interrupt()

      report should include("Coroner's Report")

      // Split test based on JVM capabilities. Not all JVMs can detect
      // deadlock between ReentrantLocks. However, we need to use
      // ReentrantLocks because normal, monitor-based locks cannot be
      // un-deadlocked once this test is finished.

      val threadMx = ManagementFactory.getThreadMXBean()
      if (threadMx.isSynchronizerUsageSupported()) {
        val sectionHeading = "Deadlocks found for monitors and ownable synchronizers"
        report should include(sectionHeading)
        val deadlockSection = report.split(sectionHeading)(1)
        deadlockSection should include("deadlock-thread-a")
        deadlockSection should include("deadlock-thread-b")
      } else {
        val sectionHeading = "Deadlocks found for monitors, but NOT ownable synchronizers"
        report should include(sectionHeading)
        val deadlockSection = report.split(sectionHeading)(1)
        deadlockSection should include("None")
        deadlockSection should not include ("deadlock-thread-a")
        deadlockSection should not include ("deadlock-thread-b")
      }
    }

  }
}

Other Akka source code examples

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

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

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2024 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.