Merge pull request #578 from akka/wip-scala210M5-√

READY FOR ACTION: Wip scala210 m5 √
This commit is contained in:
Viktor Klang (√) 2012-07-23 13:21:19 -07:00
commit eb742d8a52
312 changed files with 3797 additions and 9145 deletions

View file

@ -1,3 +1,7 @@
/**
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.actor;
import akka.actor.ActorSystem;

View file

@ -4,7 +4,10 @@ import akka.util.Timeout;
import akka.actor.ActorSystem;
import akka.japi.*;
import akka.util.Duration;
import scala.concurrent.Await;
import scala.concurrent.Future;
import scala.concurrent.Promise;
import scala.concurrent.util.Duration;
import akka.testkit.TestKitExtension;
import org.junit.AfterClass;
import org.junit.BeforeClass;
@ -17,7 +20,7 @@ import java.util.LinkedList;
import java.lang.Iterable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static akka.japi.Util.manifest;
import static akka.japi.Util.classTag;
import akka.testkit.AkkaSpec;
@ -53,7 +56,7 @@ public class JavaFutureTests {
public String apply(String s) {
return s + " World";
}
});
}, system.dispatcher());
assertEquals("Hello World", Await.result(f2, timeout));
}
@ -61,14 +64,14 @@ public class JavaFutureTests {
@Test
public void mustBeAbleToExecuteAnOnResultCallback() throws Throwable {
final CountDownLatch latch = new CountDownLatch(1);
Promise<String> cf = Futures.promise(system.dispatcher());
Future<String> f = cf;
Promise<String> cf = Futures.promise();
Future<String> f = cf.future();
f.onSuccess(new OnSuccess<String>() {
public void onSuccess(String result) {
if (result.equals("foo"))
latch.countDown();
}
});
}, system.dispatcher());
cf.success("foo");
assertTrue(latch.await(5000, TimeUnit.MILLISECONDS));
@ -78,14 +81,14 @@ public class JavaFutureTests {
@Test
public void mustBeAbleToExecuteAnOnExceptionCallback() throws Throwable {
final CountDownLatch latch = new CountDownLatch(1);
Promise<String> cf = Futures.promise(system.dispatcher());
Future<String> f = cf;
Promise<String> cf = Futures.promise();
Future<String> f = cf.future();
f.onFailure(new OnFailure() {
public void onFailure(Throwable t) {
if (t instanceof NullPointerException)
latch.countDown();
}
});
}, system.dispatcher());
Throwable exception = new NullPointerException();
cf.failure(exception);
@ -96,13 +99,13 @@ public class JavaFutureTests {
@Test
public void mustBeAbleToExecuteAnOnCompleteCallback() throws Throwable {
final CountDownLatch latch = new CountDownLatch(1);
Promise<String> cf = Futures.promise(system.dispatcher());
Future<String> f = cf;
Promise<String> cf = Futures.promise();
Future<String> f = cf.future();
f.onComplete(new OnComplete<String>() {
public void onComplete(Throwable t, String r) {
latch.countDown();
}
});
}, system.dispatcher());
cf.success("foo");
assertTrue(latch.await(5000, TimeUnit.MILLISECONDS));
@ -112,13 +115,13 @@ public class JavaFutureTests {
@Test
public void mustBeAbleToForeachAFuture() throws Throwable {
final CountDownLatch latch = new CountDownLatch(1);
Promise<String> cf = Futures.promise(system.dispatcher());
Future<String> f = cf;
Promise<String> cf = Futures.promise();
Future<String> f = cf.future();
f.foreach(new Foreach<String>() {
public void each(String future) {
latch.countDown();
}
});
},system.dispatcher());
cf.success("foo");
assertTrue(latch.await(5000, TimeUnit.MILLISECONDS));
@ -128,18 +131,18 @@ public class JavaFutureTests {
@Test
public void mustBeAbleToFlatMapAFuture() throws Throwable {
final CountDownLatch latch = new CountDownLatch(1);
Promise<String> cf = Futures.promise(system.dispatcher());
Promise<String> cf = Futures.promise();
cf.success("1000");
Future<String> f = cf;
Future<String> f = cf.future();
Future<Integer> r = f.flatMap(new Mapper<String, Future<Integer>>() {
public Future<Integer> checkedApply(String r) throws Throwable {
if (false) throw new IOException("Just here to make sure this compiles.");
latch.countDown();
Promise<Integer> cf = Futures.promise(system.dispatcher());
Promise<Integer> cf = Futures.promise();
cf.success(Integer.parseInt(r));
return cf;
return cf.future();
}
});
}, system.dispatcher());
assertEquals(Await.result(f, timeout), "1000");
assertEquals(Await.result(r, timeout).intValue(), 1000);
@ -149,14 +152,14 @@ public class JavaFutureTests {
@Test
public void mustBeAbleToFilterAFuture() throws Throwable {
final CountDownLatch latch = new CountDownLatch(1);
Promise<String> cf = Futures.promise(system.dispatcher());
Future<String> f = cf;
Promise<String> cf = Futures.promise();
Future<String> f = cf.future();
Future<String> r = f.filter(Filter.filterOf(new Function<String, Boolean>() {
public Boolean apply(String r) {
latch.countDown();
return r.equals("foo");
}
}));
}), system.dispatcher());
cf.success("foo");
assertTrue(latch.await(5000, TimeUnit.MILLISECONDS));
@ -277,27 +280,27 @@ public class JavaFutureTests {
@Test
public void blockMustBeCallable() throws Exception {
Promise<String> p = Futures.promise(system.dispatcher());
Promise<String> p = Futures.promise();
Duration d = Duration.create(1, TimeUnit.SECONDS);
p.success("foo");
Await.ready(p, d);
assertEquals(Await.result(p, d), "foo");
Await.ready(p.future(), d);
assertEquals(Await.result(p.future(), d), "foo");
}
@Test
public void mapToMustBeCallable() throws Exception {
Promise<Object> p = Futures.promise(system.dispatcher());
Future<String> f = p.future().mapTo(manifest(String.class));
Promise<Object> p = Futures.promise();
Future<String> f = p.future().mapTo(classTag(String.class));
Duration d = Duration.create(1, TimeUnit.SECONDS);
p.success("foo");
Await.ready(p, d);
assertEquals(Await.result(p, d), "foo");
Await.ready(p.future(), d);
assertEquals(Await.result(p.future(), d), "foo");
}
@Test
public void recoverToMustBeCallable() throws Exception {
final IllegalStateException fail = new IllegalStateException("OHNOES");
Promise<Object> p = Futures.promise(system.dispatcher());
Promise<Object> p = Futures.promise();
Future<Object> f = p.future().recover(new Recover<Object>() {
public Object recover(Throwable t) throws Throwable {
if (t == fail)
@ -305,7 +308,7 @@ public class JavaFutureTests {
else
throw t;
}
});
}, system.dispatcher());
Duration d = Duration.create(1, TimeUnit.SECONDS);
p.failure(fail);
assertEquals(Await.result(f, d), "foo");
@ -314,15 +317,15 @@ public class JavaFutureTests {
@Test
public void recoverWithToMustBeCallable() throws Exception{
final IllegalStateException fail = new IllegalStateException("OHNOES");
Promise<Object> p = Futures.promise(system.dispatcher());
Promise<Object> p = Futures.promise();
Future<Object> f = p.future().recoverWith(new Recover<Future<Object>>() {
public Future<Object> recover(Throwable t) throws Throwable {
if (t == fail)
return Futures.<Object> successful("foo", system.dispatcher()).future();
return Futures.<Object>successful("foo");
else
throw t;
}
});
}, system.dispatcher());
Duration d = Duration.create(1, TimeUnit.SECONDS);
p.failure(fail);
assertEquals(Await.result(f, d), "foo");

View file

@ -4,6 +4,7 @@
package akka.util;
import org.junit.Test;
import scala.concurrent.util.Duration;
public class JavaDuration {

View file

@ -3,10 +3,12 @@
*/
package akka.actor
import language.postfixOps
import akka.testkit._
import akka.testkit.DefaultTimeout
import akka.testkit.TestEvent._
import akka.util.duration._
import scala.concurrent.util.duration._
import akka.routing._
import org.scalatest.BeforeAndAfterEach
import akka.ConfigurationException

View file

@ -6,8 +6,8 @@ package akka.actor
import akka.testkit._
import org.scalatest.BeforeAndAfterEach
import akka.util.duration._
import akka.dispatch.Await
import scala.concurrent.util.duration._
import scala.concurrent.Await
import akka.pattern.ask
object ActorFireForgetRequestReplySpec {
@ -87,7 +87,7 @@ class ActorFireForgetRequestReplySpec extends AkkaSpec with BeforeAndAfterEach w
actor.isTerminated must be(false)
actor ! "Die"
state.finished.await
1.second.dilated.sleep()
Thread.sleep(1.second.dilated.toMillis)
actor.isTerminated must be(true)
system.stop(supervisor)
}

View file

@ -4,14 +4,16 @@
package akka.actor
import language.postfixOps
import org.scalatest.BeforeAndAfterEach
import org.scalatest.matchers.MustMatchers
import akka.actor.Actor._
import akka.testkit._
import akka.util.duration._
import scala.concurrent.util.duration._
import java.util.concurrent.atomic._
import akka.dispatch.Await
import scala.concurrent.Await
import akka.pattern.ask
import java.util.UUID.{ randomUUID newUuid }
@ -116,6 +118,30 @@ class ActorLifeCycleSpec extends AkkaSpec with BeforeAndAfterEach with ImplicitS
expectNoMsg(1 seconds)
system.stop(supervisor)
}
"clear the behavior stack upon restart" in {
case class Become(recv: ActorContext Receive)
val a = system.actorOf(Props(new Actor {
def receive = {
case Become(beh) context.become(beh(context), discardOld = false); sender ! "ok"
case x sender ! 42
}
}))
a ! "hello"
expectMsg(42)
a ! Become(ctx {
case "fail" throw new RuntimeException("buh")
case x ctx.sender ! 43
})
expectMsg("ok")
a ! "hello"
expectMsg(43)
EventFilter[RuntimeException]("buh", occurrences = 1) intercept {
a ! "fail"
}
a ! "hello"
expectMsg(42)
}
}
}

View file

@ -3,9 +3,11 @@
*/
package akka.actor
import language.postfixOps
import akka.testkit._
import akka.util.duration._
import akka.dispatch.Await
import scala.concurrent.util.duration._
import scala.concurrent.Await
import akka.pattern.ask
import java.net.MalformedURLException

View file

@ -4,15 +4,17 @@
package akka.actor
import language.postfixOps
import org.scalatest.WordSpec
import org.scalatest.matchers.MustMatchers
import akka.testkit._
import akka.util.Timeout
import akka.util.duration._
import scala.concurrent.util.duration._
import scala.concurrent.Await
import java.lang.IllegalStateException
import java.util.concurrent.{ CountDownLatch, TimeUnit }
import akka.dispatch.{ Await, DefaultPromise, Promise, Future }
import scala.concurrent.Promise
import akka.pattern.ask
import akka.serialization.JavaSerializer
@ -52,7 +54,7 @@ object ActorRefSpec {
}
private def work {
1.second.dilated.sleep
Thread.sleep(1.second.dilated.toMillis)
}
}
@ -121,7 +123,7 @@ class ActorRefSpec extends AkkaSpec with DefaultTimeout {
to.success(r)
r
} catch {
case e
case e: Throwable
to.failure(e)
throw e
}
@ -129,7 +131,7 @@ class ActorRefSpec extends AkkaSpec with DefaultTimeout {
def wrap[T](f: Promise[Actor] T): T = {
val result = Promise[Actor]()
val r = f(result)
Await.result(result, 1 minute)
Await.result(result.future, 1 minute)
r
}

View file

@ -3,16 +3,18 @@
*/
package akka.actor
import language.postfixOps
import akka.testkit._
import org.scalatest.junit.JUnitSuite
import com.typesafe.config.ConfigFactory
import akka.dispatch.Await
import akka.util.duration._
import scala.concurrent.Await
import scala.concurrent.util.duration._
import scala.collection.JavaConverters
import java.util.concurrent.{ TimeUnit, RejectedExecutionException, CountDownLatch, ConcurrentLinkedQueue }
import akka.pattern.ask
import akka.util.Timeout
import akka.dispatch.Future
import scala.concurrent.Future
class JavaExtensionSpec extends JavaExtension with JUnitSuite
@ -103,7 +105,7 @@ class ActorSystemSpec extends AkkaSpec("""akka.extensions = ["akka.actor.TestExt
for (i 1 to count) {
system2.registerOnTermination {
(i % 3).millis.dilated.sleep()
Thread.sleep((i % 3).millis.dilated.toMillis)
result add i
latch.countDown()
}
@ -125,7 +127,7 @@ class ActorSystemSpec extends AkkaSpec("""akka.extensions = ["akka.actor.TestExt
var callbackWasRun = false
system2.registerOnTermination {
50.millis.dilated.sleep()
Thread.sleep(50.millis.dilated.toMillis)
callbackWasRun = true
}

View file

@ -3,9 +3,9 @@
*/
package akka.actor
import akka.util.duration._
import scala.concurrent.util.duration._
import akka.testkit._
import akka.dispatch.Await
import scala.concurrent.Await
import akka.util.Timeout
import akka.pattern.{ ask, AskTimeoutException }

View file

@ -3,12 +3,15 @@
*/
package akka.actor
import language.postfixOps
import akka.testkit._
import akka.testkit.DefaultTimeout
import akka.testkit.TestEvent._
import akka.dispatch.{ Await, BoundedDequeBasedMailbox }
import akka.dispatch.BoundedDequeBasedMailbox
import akka.pattern.ask
import akka.util.duration._
import scala.concurrent.Await
import scala.concurrent.util.duration._
import akka.actor.ActorSystem.Settings
import com.typesafe.config.{ Config, ConfigFactory }
import org.scalatest.BeforeAndAfterEach

View file

@ -3,12 +3,14 @@
*/
package akka.actor
import language.postfixOps
import akka.testkit._
import akka.testkit.DefaultTimeout
import akka.testkit.TestEvent._
import akka.dispatch.Await
import scala.concurrent.Await
import akka.pattern.ask
import akka.util.duration._
import scala.concurrent.util.duration._
import com.typesafe.config.{ Config, ConfigFactory }
import org.scalatest.BeforeAndAfterEach
import org.scalatest.junit.JUnitSuite

View file

@ -1,8 +1,10 @@
package akka.actor
import language.postfixOps
import akka.testkit.AkkaSpec
import akka.dispatch.UnboundedMailbox
import akka.util.duration._
import scala.concurrent.util.duration._
object ConsistencySpec {
val config = """

View file

@ -4,10 +4,12 @@
package akka.actor
import language.postfixOps
import akka.testkit._
import akka.util.duration._
import scala.concurrent.util.duration._
import java.util.concurrent.atomic._
import akka.dispatch.Await
import scala.concurrent.Await
import akka.pattern.ask
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])

View file

@ -4,11 +4,13 @@
package akka.actor
import language.postfixOps
import akka.testkit.AkkaSpec
import com.typesafe.config.ConfigFactory
import com.typesafe.config.ConfigParseOptions
import akka.routing._
import akka.util.duration._
import scala.concurrent.util.duration._
object DeployerSpec {
val deployerConf = ConfigFactory.parseString("""

View file

@ -4,14 +4,17 @@
package akka.actor
import language.postfixOps
import org.scalatest.{ BeforeAndAfterAll, BeforeAndAfterEach }
import akka.testkit._
import TestEvent.Mute
import akka.util.duration._
import scala.concurrent.util.duration._
import akka.event._
import com.typesafe.config.ConfigFactory
import akka.dispatch.Await
import akka.util.{ Timeout, Duration }
import scala.concurrent.Await
import akka.util.Timeout
import scala.concurrent.util.Duration
object FSMActorSpec {
val timeout = Timeout(2 seconds)

View file

@ -4,9 +4,11 @@
package akka.actor
import language.postfixOps
import akka.testkit._
import akka.util.Duration
import akka.util.duration._
import scala.concurrent.util.Duration
import scala.concurrent.util.duration._
import akka.event.Logging
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
@ -145,7 +147,7 @@ object FSMTimingSpec {
}
def resume(actorRef: ActorRef): Unit = actorRef match {
case l: ActorRefWithCell l.resume()
case l: ActorRefWithCell l.resume(inResponseToFailure = false)
case _
}

View file

@ -3,9 +3,11 @@
*/
package akka.actor
import language.postfixOps
import akka.testkit._
import akka.util.duration._
import akka.util.Duration
import scala.concurrent.util.duration._
import scala.concurrent.util.Duration
object FSMTransitionSpec {

View file

@ -4,12 +4,14 @@
package akka.actor
import language.postfixOps
import akka.testkit._
import akka.util.duration._
import Actor._
import akka.util.Duration
import akka.dispatch.Await
import akka.pattern.ask
import scala.concurrent.util.duration._
import akka.actor.Actor._
import scala.concurrent.util.Duration
import scala.concurrent.Await
import akka.pattern.{ ask, pipe }
object ForwardActorSpec {
val ExpectedMessage = "FOO"
@ -31,12 +33,10 @@ object ForwardActorSpec {
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
class ForwardActorSpec extends AkkaSpec {
import ForwardActorSpec._
implicit val ec = system.dispatcher
"A Forward Actor" must {
"forward actor reference when invoking forward on tell" in {
val latch = new TestLatch(1)
val replyTo = system.actorOf(Props(new Actor { def receive = { case ExpectedMessage testActor ! ExpectedMessage } }))
val chain = createForwardingChain(system)
@ -47,7 +47,7 @@ class ForwardActorSpec extends AkkaSpec {
"forward actor reference when invoking forward on ask" in {
val chain = createForwardingChain(system)
chain.ask(ExpectedMessage)(5 seconds) onSuccess { case ExpectedMessage testActor ! ExpectedMessage }
chain.ask(ExpectedMessage)(5 seconds) pipeTo testActor
expectMsg(5 seconds, ExpectedMessage)
}
}

View file

@ -4,11 +4,15 @@
package akka.actor
import akka.util.{ ByteString, Duration, Deadline }
import akka.util.duration._
import language.postfixOps
import akka.util.ByteString
import scala.concurrent.{ ExecutionContext, Await, Future, Promise }
import scala.concurrent.util.{ Duration, Deadline }
import scala.concurrent.util.duration._
import scala.util.continuations._
import akka.testkit._
import akka.dispatch.{ Await, Future, Promise, ExecutionContext, MessageDispatcher }
import akka.dispatch.MessageDispatcher
import java.net.{ SocketAddress }
import akka.pattern.ask
@ -242,7 +246,7 @@ class IOActorSpec extends AkkaSpec with DefaultTimeout {
*/
def retry[T](count: Option[Int] = None, timeout: Option[Duration] = None, delay: Option[Duration] = Some(100 millis), filter: Option[Throwable Boolean] = None)(future: Future[T])(implicit executor: ExecutionContext): Future[T] = {
val promise = Promise[T]()(executor)
val promise = Promise[T]()
val timer: Option[Deadline] = timeout match {
case Some(duration) Some(duration fromNow)
@ -267,15 +271,16 @@ class IOActorSpec extends AkkaSpec with DefaultTimeout {
run(0)
promise
promise.future
}
"an IO Actor" must {
implicit val ec = system.dispatcher
"run echo server" in {
filterException[java.net.ConnectException] {
val addressPromise = Promise[SocketAddress]()
val server = system.actorOf(Props(new SimpleEchoServer(addressPromise)))
val address = Await.result(addressPromise, TestLatch.DefaultTimeout)
val address = Await.result(addressPromise.future, TestLatch.DefaultTimeout)
val client = system.actorOf(Props(new SimpleEchoClient(address)))
val f1 = retry() { client ? ByteString("Hello World!1") }
val f2 = retry() { client ? ByteString("Hello World!2") }
@ -292,7 +297,7 @@ class IOActorSpec extends AkkaSpec with DefaultTimeout {
filterException[java.net.ConnectException] {
val addressPromise = Promise[SocketAddress]()
val server = system.actorOf(Props(new SimpleEchoServer(addressPromise)))
val address = Await.result(addressPromise, TestLatch.DefaultTimeout)
val address = Await.result(addressPromise.future, TestLatch.DefaultTimeout)
val client = system.actorOf(Props(new SimpleEchoClient(address)))
val list = List.range(0, 100)
val f = Future.traverse(list)(i retry() { client ? ByteString(i.toString) })
@ -306,7 +311,7 @@ class IOActorSpec extends AkkaSpec with DefaultTimeout {
filterException[java.net.ConnectException] {
val addressPromise = Promise[SocketAddress]()
val server = system.actorOf(Props(new KVStore(addressPromise)))
val address = Await.result(addressPromise, TestLatch.DefaultTimeout)
val address = Await.result(addressPromise.future, TestLatch.DefaultTimeout)
val client1 = system.actorOf(Props(new KVClient(address)))
val client2 = system.actorOf(Props(new KVClient(address)))
val f1 = retry() { client1 ? KVSet("hello", "World") }

View file

@ -4,10 +4,13 @@
package akka.actor
import language.postfixOps
import akka.testkit._
import akka.util.duration._
import scala.concurrent.Await
import scala.concurrent.util.duration._
import akka.util.Timeout
import akka.dispatch.{ Await, Future }
import scala.concurrent.Future
object LocalActorRefProviderSpec {
val config = """
@ -38,7 +41,7 @@ class LocalActorRefProviderSpec extends AkkaSpec(LocalActorRefProviderSpec.confi
}
"An ActorRefFactory" must {
implicit val ec = system.dispatcher
"only create one instance of an actor with a specific address in a concurrent environment" in {
val impl = system.asInstanceOf[ActorSystemImpl]
val provider = impl.provider

View file

@ -4,11 +4,13 @@
package akka.actor
import language.postfixOps
import akka.testkit._
import akka.util.duration._
import scala.concurrent.util.duration._
import java.util.concurrent.atomic.AtomicInteger
import akka.dispatch.Await
import scala.concurrent.Await
import java.util.concurrent.TimeoutException
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])

View file

@ -4,17 +4,19 @@
package akka.actor
import language.postfixOps
import java.lang.Thread.sleep
import org.scalatest.BeforeAndAfterAll
import akka.dispatch.Await
import scala.concurrent.Await
import akka.testkit.TestEvent._
import akka.testkit.EventFilter
import java.util.concurrent.{ TimeUnit, CountDownLatch }
import akka.testkit.AkkaSpec
import akka.testkit.DefaultTimeout
import akka.testkit.TestLatch
import akka.util.duration._
import akka.util.Duration
import scala.concurrent.util.duration._
import scala.concurrent.util.Duration
import akka.pattern.ask
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])

View file

@ -1,10 +1,12 @@
package akka.actor
import language.postfixOps
import org.scalatest.BeforeAndAfterEach
import akka.util.duration._
import scala.concurrent.util.duration._
import java.util.concurrent.{ CountDownLatch, ConcurrentLinkedQueue, TimeUnit }
import akka.testkit._
import akka.dispatch.Await
import scala.concurrent.Await
import akka.pattern.ask
import java.util.concurrent.atomic.AtomicInteger
@ -113,9 +115,9 @@ class SchedulerSpec extends AkkaSpec with BeforeAndAfterEach with DefaultTimeout
val timeout = collectCancellable(system.scheduler.schedule(initialDelay, delay) {
ticks.incrementAndGet()
})
10.milliseconds.dilated.sleep()
Thread.sleep(10.milliseconds.dilated.toMillis)
timeout.cancel()
(initialDelay + 100.milliseconds.dilated).sleep()
Thread.sleep((initialDelay + 100.milliseconds.dilated).toMillis)
ticks.get must be(0)
}
@ -128,9 +130,9 @@ class SchedulerSpec extends AkkaSpec with BeforeAndAfterEach with DefaultTimeout
val timeout = collectCancellable(system.scheduler.schedule(initialDelay, delay) {
ticks.incrementAndGet()
})
(initialDelay + 100.milliseconds.dilated).sleep()
Thread.sleep((initialDelay + 100.milliseconds.dilated).toMillis)
timeout.cancel()
(delay + 100.milliseconds.dilated).sleep()
Thread.sleep((delay + 100.milliseconds.dilated).toMillis)
ticks.get must be(1)
}

View file

@ -4,12 +4,26 @@
package akka.actor
import akka.testkit._
import language.postfixOps
import java.util.concurrent.{ TimeUnit, CountDownLatch }
import akka.dispatch.Await
import scala.concurrent.Await
import scala.concurrent.util.Duration
import scala.concurrent.util.duration.intToDurationInt
import scala.math.BigInt.int2bigInt
import scala.util.Random
import scala.util.control.NoStackTrace
import com.typesafe.config.{ ConfigFactory, Config }
import SupervisorStrategy.{ Resume, Restart, Directive }
import akka.actor.SupervisorStrategy.seqThrowable2Decider
import akka.dispatch.{ MessageDispatcher, DispatcherPrerequisites, DispatcherConfigurator, Dispatcher }
import akka.pattern.ask
import akka.util.Duration
import akka.util.duration._
import akka.testkit.{ ImplicitSender, EventFilter, DefaultTimeout, AkkaSpec }
import akka.testkit.{ filterException, duration2TestDuration, TestLatch }
import akka.testkit.TestEvent.Mute
object SupervisorHierarchySpec {
class FireWorkerException(msg: String) extends Exception(msg)
@ -29,10 +43,361 @@ object SupervisorHierarchySpec {
countDown.countDown()
}
}
class Resumer extends Actor {
override def supervisorStrategy = OneForOneStrategy() { case _ SupervisorStrategy.Resume }
def receive = {
case "spawn" sender ! context.actorOf(Props[Resumer])
case "fail" throw new Exception("expected")
case "ping" sender ! "pong"
}
}
case class Event(msg: Any) { val time: Long = System.nanoTime }
case class ErrorLog(msg: String, log: Vector[Event])
case class Failure(directive: Directive, log: Vector[Event]) extends RuntimeException with NoStackTrace {
override def toString = "Failure(" + directive + ")"
}
val strategy = OneForOneStrategy() { case Failure(directive, _) directive }
val config = ConfigFactory.parseString("""
hierarchy {
type = "akka.actor.SupervisorHierarchySpec$MyDispatcherConfigurator"
}
akka.loglevel = INFO
akka.actor.debug.fsm = on
""")
class MyDispatcherConfigurator(config: Config, prerequisites: DispatcherPrerequisites)
extends DispatcherConfigurator(config, prerequisites) {
private val instance: MessageDispatcher =
new Dispatcher(prerequisites,
config.getString("id"),
config.getInt("throughput"),
Duration(config.getNanoseconds("throughput-deadline-time"), TimeUnit.NANOSECONDS),
mailboxType,
configureExecutor(),
Duration(config.getMilliseconds("shutdown-timeout"), TimeUnit.MILLISECONDS)) {
override def suspend(cell: ActorCell): Unit = {
val a = cell.actor.asInstanceOf[Hierarchy]
a.log :+= Event("suspended")
super.suspend(cell)
}
override def resume(cell: ActorCell): Unit = {
val a = cell.actor.asInstanceOf[Hierarchy]
a.log :+= Event("resumed")
super.resume(cell)
}
}
override def dispatcher(): MessageDispatcher = instance
}
class Hierarchy(depth: Int, breadth: Int, listener: ActorRef) extends Actor {
override def preStart {
if (depth > 1)
for (_ 1 to breadth)
context.watch(context.actorOf(Props(new Hierarchy(depth - 1, breadth, listener)).withDispatcher("hierarchy")))
listener ! self
}
override def postRestart(cause: Throwable) {
cause match {
case Failure(_, l) log = l
}
log :+= Event("restarted")
}
override def supervisorStrategy = strategy
override def preRestart(cause: Throwable, msg: Option[Any]): Unit = {
// do not scrap children
}
override def postStop {
if (failed || suspended) {
listener ! ErrorLog("not resumed (" + failed + ", " + suspended + ")", log)
}
}
var failed = false
var suspended = false
var log = Vector.empty[Event]
def check(msg: Any) = {
suspended = false
log :+= Event(msg)
if (failed) {
listener ! ErrorLog("processing message while failed", log)
failed = false
context stop self
}
}
def receive = new Receive {
val handler: Receive = {
case f @ Failure(Resume, _) suspended = true; throw f.copy(log = log)
case f: Failure failed = true; throw f.copy(log = log)
case "ping" Thread.sleep((Random.nextFloat * 1.03).toLong); sender ! "pong"
case Terminated(_) listener ! ErrorLog("terminating", log); context stop self
}
override def isDefinedAt(msg: Any) = handler.isDefinedAt(msg)
override def apply(msg: Any) = { check(msg); handler(msg) }
}
}
case class Work(n: Int)
sealed trait Action
case class Ping(ref: ActorRef) extends Action
case class Fail(ref: ActorRef, directive: Directive) extends Action
sealed trait State
case object Idle extends State
case object Init extends State
case object Stress extends State
case object Finishing extends State
case object LastPing extends State
case object Stopping extends State
case object Failed extends State
/*
* This stress test will construct a supervision hierarchy of configurable
* depth and breadth and then randomly fail and check its actors. The actors
* perform certain checks internally (verifying that they do not run when
* suspended, for example), and they are checked for health by the test
* procedure.
*
* Execution happens in phases (which is the reason for FSM):
*
* Idle:
* - upon reception of Init message, construct hierary and go to Init state
*
* Init:
* - receive refs of all contained actors
*
* Stress:
* - deal out actions (Fail or "ping"), keeping the hierarchy busy
* - whenever all actors are in the "pinged" list (i.e. have not yet
* answered with a "pong"), delay processing of the next Work() by
* 100 millis
* - when receiving a Work() while all actors are "pinged", stop the
* hierarchy and go to the Stopping state
*
* Finishing:
* - after dealing out the last action, wait for the outstanding "pong"
* messages
* - when last "pong" is received, goto LastPing state
* - upon state timeout, stop the hierarchy and go to the Failed state
*
* LastPing:
* - upon entering this state, send a "ping" to all actors
* - when last "pong" is received, goto Stopping state
* - upon state timeout, stop the hierarchy and go to the Failed state
*
* Stopping:
* - upon entering this state, stop the hierarchy
* - upon termination of the hierarchy send back successful result
*
* Whenever an ErrorLog is received, goto Failed state
*
* Failed:
* - accumulate ErrorLog messages
* - upon termination of the hierarchy send back failed result and print
* the logs, merged and in chronological order.
*
* TODO RK: also test Stop directive, and keep a complete list of all
* actors ever created, then verify after stop()ping the hierarchy that
* all are terminated, transfer them to a WeakHashMap and verify that
* they are indeed GCed
*
* TODO RK: make hierarchy construction stochastic so that it includes
* different breadth (including the degenerate breadth-1 case).
*
* TODO RK: also test Escalate by adding an exception with a `var depth`
* which gets decremented within the supervisor and gets handled when zero
* is reached (Restart resolution)
*
* TODO RK: also test exceptions during recreate
*
* TODO RK: also test recreate including terminating children
*
* TODO RK: also verify that preRestart is not called more than once per instance
*/
class StressTest(testActor: ActorRef, depth: Int, breadth: Int) extends Actor with LoggingFSM[State, Null] {
import context.system
override def supervisorStrategy = strategy
var children = Vector.empty[ActorRef]
var idleChildren = Vector.empty[ActorRef]
var pingChildren = Set.empty[ActorRef]
val nextJob = Iterator.continually(Random.nextFloat match {
case x if x >= 0.5
// ping one child
val pick = ((x - 0.5) * 2 * idleChildren.size).toInt
val ref = idleChildren(pick)
idleChildren = idleChildren.take(pick) ++ idleChildren.drop(pick + 1)
pingChildren += ref
Ping(ref)
case x
// fail one child
val pick = ((if (x >= 0.25) x - 0.25 else x) * 4 * children.size).toInt
Fail(children(pick), if (x > 0.25) Restart else Resume)
})
val familySize = ((1 - BigInt(breadth).pow(depth)) / (1 - breadth)).toInt
var hierarchy: ActorRef = _
override def preRestart(cause: Throwable, msg: Option[Any]) {
throw new ActorKilledException("I want to DIE")
}
override def postRestart(cause: Throwable) {
throw new ActorKilledException("I said I wanted to DIE, dammit!")
}
override def postStop {
testActor ! "stressTestStopped"
}
startWith(Idle, null)
when(Idle) {
case Event(Init, _)
hierarchy = context.watch(context.actorOf(Props(new Hierarchy(depth, breadth, self)).withDispatcher("hierarchy")))
setTimer("phase", StateTimeout, 5 seconds, false)
goto(Init)
}
when(Init) {
case Event(ref: ActorRef, _)
if (idleChildren.nonEmpty || pingChildren.nonEmpty)
throw new IllegalStateException("received unexpected child " + children.size)
children :+= ref
if (children.size == familySize) {
idleChildren = children
goto(Stress)
} else stay
case Event(StateTimeout, _)
testActor ! "only got %d out of %d refs".format(children.size, familySize)
stop()
}
onTransition {
case Init -> Stress
self ! Work(familySize * 1000)
// set timeout for completion of the whole test (i.e. including Finishing and Stopping)
setTimer("phase", StateTimeout, 60 seconds, false)
}
val workSchedule = 250.millis
when(Stress) {
case Event(w: Work, _) if idleChildren.isEmpty
context stop hierarchy
goto(Failed)
case Event(Work(x), _) if x > 0
nextJob.next match {
case Ping(ref) ref ! "ping"
case Fail(ref, dir) ref ! Failure(dir, Vector.empty)
}
if (idleChildren.nonEmpty) self ! Work(x - 1)
else context.system.scheduler.scheduleOnce(workSchedule, self, Work(x - 1))
stay
case Event(Work(_), _) if (pingChildren.isEmpty) goto(LastPing) else goto(Finishing)
case Event("pong", _)
pingChildren -= sender
idleChildren :+= sender
stay
}
when(Finishing) {
case Event("pong", _)
pingChildren -= sender
idleChildren :+= sender
if (pingChildren.isEmpty) goto(LastPing) else stay
}
onTransition {
case _ -> LastPing
idleChildren foreach (_ ! "ping")
pingChildren ++= idleChildren
idleChildren = Vector.empty
}
when(LastPing) {
case Event("pong", _)
pingChildren -= sender
idleChildren :+= sender
if (pingChildren.isEmpty) goto(Stopping) else stay
}
onTransition {
case _ -> Stopping context stop hierarchy
}
when(Stopping, stateTimeout = 5 seconds) {
case Event(Terminated(r), _) if r == hierarchy
testActor ! "stressTestSuccessful"
stop
case Event(StateTimeout, _)
testActor ! "timeout in Stopping"
stop
}
var errors = Vector.empty[(ActorRef, ErrorLog)]
when(Failed, stateTimeout = 5 seconds) {
case Event(e: ErrorLog, _)
errors :+= sender -> e
stay
case Event(Terminated(r), _) if r == hierarchy
printErrors()
testActor ! "stressTestFailed"
stop
case Event(StateTimeout, _)
printErrors()
testActor ! "timeout in Failed"
stop
case Event("pong", _) stay // dont care?
}
def printErrors(): Unit = {
val merged = errors flatMap {
case (ref, ErrorLog(msg, log))
println(ref + " " + msg)
log map (l (l.time, ref, l.msg.toString))
}
merged.sorted foreach println
}
whenUnhandled {
case Event(e: ErrorLog, _)
errors :+= sender -> e
// dont stop the hierarchy, that is going to happen all by itself and in the right order
goto(Failed)
case Event(StateTimeout, _)
println("pingChildren:\n" + pingChildren.mkString("\n"))
context stop hierarchy
goto(Failed)
case Event(msg, _)
testActor ! ("received unexpected msg: " + msg)
stop
}
initialize
}
}
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
class SupervisorHierarchySpec extends AkkaSpec with DefaultTimeout {
class SupervisorHierarchySpec extends AkkaSpec(SupervisorHierarchySpec.config) with DefaultTimeout with ImplicitSender {
import SupervisorHierarchySpec._
"A Supervisor Hierarchy" must {
@ -81,6 +446,67 @@ class SupervisorHierarchySpec extends AkkaSpec with DefaultTimeout {
assert(countDownMax.await(2, TimeUnit.SECONDS))
}
}
"resume children after Resume" in {
val boss = system.actorOf(Props[Resumer], "resumer")
boss ! "spawn"
val middle = expectMsgType[ActorRef]
middle ! "spawn"
val worker = expectMsgType[ActorRef]
worker ! "ping"
expectMsg("pong")
EventFilter[Exception]("expected", occurrences = 1) intercept {
middle ! "fail"
}
middle ! "ping"
expectMsg("pong")
worker ! "ping"
expectMsg("pong")
}
"suspend children while failing" in {
val latch = TestLatch()
val slowResumer = system.actorOf(Props(new Actor {
override def supervisorStrategy = OneForOneStrategy() { case _ Await.ready(latch, 4.seconds.dilated); SupervisorStrategy.Resume }
def receive = {
case "spawn" sender ! context.actorOf(Props[Resumer])
}
}), "slowResumer")
slowResumer ! "spawn"
val boss = expectMsgType[ActorRef]
boss ! "spawn"
val middle = expectMsgType[ActorRef]
middle ! "spawn"
val worker = expectMsgType[ActorRef]
worker ! "ping"
expectMsg("pong")
EventFilter[Exception]("expected", occurrences = 1) intercept {
boss ! "fail"
}
worker ! "ping"
expectNoMsg(2 seconds)
latch.countDown()
expectMsg("pong")
}
"survive being stressed" in {
system.eventStream.publish(Mute(EventFilter[Failure]()))
system.eventStream.publish(Mute(EventFilter.warning(start = "received dead letter")))
val fsm = system.actorOf(Props(new StressTest(testActor, 6, 3)), "stressTest")
fsm ! FSM.SubscribeTransitionCallBack(system.actorOf(Props(new Actor {
def receive = {
case s: FSM.CurrentState[_] log.info("{}", s)
case t: FSM.Transition[_] log.info("{}", t)
}
})))
fsm ! Init
expectMsg(70 seconds, "stressTestSuccessful")
expectMsg("stressTestStopped")
}
}
}

View file

@ -3,14 +3,17 @@
*/
package akka.actor
import language.postfixOps
import akka.testkit.{ filterEvents, EventFilter }
import akka.dispatch.{ PinnedDispatcher, Dispatchers, Await }
import scala.concurrent.Await
import akka.dispatch.{ PinnedDispatcher, Dispatchers }
import java.util.concurrent.{ TimeUnit, CountDownLatch }
import akka.testkit.AkkaSpec
import akka.testkit.DefaultTimeout
import akka.pattern.ask
import akka.util.duration._
import akka.util.NonFatal
import scala.concurrent.util.duration._
import scala.util.control.NonFatal
object SupervisorMiscSpec {
val config = """
@ -134,6 +137,9 @@ class SupervisorMiscSpec extends AkkaSpec(SupervisorMiscSpec.config) with Defaul
}))
parent ! "engage"
expectMsg("green")
EventFilter[IllegalStateException]("handleChildTerminated failed", occurrences = 1) intercept {
system.stop(parent)
}
}
}

View file

@ -4,13 +4,15 @@
package akka.actor
import language.postfixOps
import org.scalatest.BeforeAndAfterEach
import akka.util.duration._
import scala.concurrent.util.duration._
import akka.{ Die, Ping }
import akka.testkit.TestEvent._
import akka.testkit._
import java.util.concurrent.atomic.AtomicInteger
import akka.dispatch.Await
import scala.concurrent.Await
import akka.pattern.ask
object SupervisorSpec {
@ -339,7 +341,12 @@ class SupervisorSpec extends AkkaSpec with BeforeAndAfterEach with ImplicitSende
OneForOneStrategy(maxNrOfRetries = 3, withinTimeRange = 10 seconds)(classOf[Exception] :: Nil))))
val dyingProps = Props(new Actor {
if (inits.incrementAndGet % 2 == 0) throw new IllegalStateException("Don't wanna!")
val init = inits.getAndIncrement()
if (init % 3 == 1) throw new IllegalStateException("Don't wanna!")
override def preRestart(cause: Throwable, msg: Option[Any]) {
if (init % 3 == 0) throw new IllegalStateException("Don't wanna!")
}
def receive = {
case Ping sender ! PongMessage
@ -349,16 +356,20 @@ class SupervisorSpec extends AkkaSpec with BeforeAndAfterEach with ImplicitSende
throw e
}
})
val dyingActor = Await.result((supervisor ? dyingProps).mapTo[ActorRef], timeout.duration)
supervisor ! dyingProps
val dyingActor = expectMsgType[ActorRef]
filterEvents(EventFilter[RuntimeException]("Expected", occurrences = 1),
EventFilter[IllegalStateException]("error while creating actor", occurrences = 1)) {
filterEvents(
EventFilter[RuntimeException]("Expected", occurrences = 1),
EventFilter[PreRestartException]("Don't wanna!", occurrences = 1),
EventFilter[PostRestartException]("Don't wanna!", occurrences = 1)) {
intercept[RuntimeException] {
Await.result(dyingActor.?(DieReply)(DilatedTimeout), DilatedTimeout)
}
}
Await.result(dyingActor.?(Ping)(DilatedTimeout), DilatedTimeout) must be === PongMessage
dyingActor ! Ping
expectMsg(PongMessage)
inits.get must be(3)

View file

@ -3,15 +3,15 @@
*/
package akka.actor
import language.postfixOps
import org.scalatest.WordSpec
import org.scalatest.matchers.MustMatchers
import akka.util.duration._
import scala.concurrent.Await
import scala.concurrent.util.duration._
import akka.actor.Actor._
import akka.testkit.{ TestKit, EventFilter, filterEvents, filterException }
import akka.testkit.AkkaSpec
import akka.testkit.ImplicitSender
import akka.testkit.DefaultTimeout
import akka.dispatch.{ Await, Dispatchers }
import akka.testkit.{ TestKit, EventFilter, filterEvents, filterException, AkkaSpec, ImplicitSender, DefaultTimeout }
import akka.dispatch.Dispatchers
import akka.pattern.ask
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])

View file

@ -3,6 +3,8 @@
*/
package akka.actor
import language.postfixOps
import java.util.concurrent.{ CountDownLatch, TimeUnit }
import akka.actor._
import org.scalatest.BeforeAndAfterAll
@ -10,9 +12,9 @@ import akka.testkit.{ TestKit, filterEvents, EventFilter }
import akka.testkit.AkkaSpec
import akka.testkit.ImplicitSender
import akka.testkit.DefaultTimeout
import akka.dispatch.Await
import scala.concurrent.Await
import akka.pattern.ask
import akka.util.duration._
import scala.concurrent.util.duration._
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
class Ticket669Spec extends AkkaSpec with BeforeAndAfterAll with ImplicitSender with DefaultTimeout {

View file

@ -1,20 +1,21 @@
package akka.actor
/**
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.actor
import language.postfixOps
import org.scalatest.{ BeforeAndAfterAll, BeforeAndAfterEach }
import akka.util.Duration
import akka.util.Timeout
import akka.util.duration._
import scala.concurrent.{ Await, Future, Promise }
import scala.concurrent.util.Duration
import scala.concurrent.util.duration._
import java.util.concurrent.atomic.AtomicReference
import annotation.tailrec
import akka.testkit.{ EventFilter, filterEvents, AkkaSpec }
import akka.serialization.SerializationExtension
import akka.japi.{ Creator, Option JOption }
import akka.japi.{ Option JOption }
import akka.testkit.DefaultTimeout
import akka.dispatch.{ Await, Dispatchers, Future, Promise }
import akka.dispatch.{ Dispatchers }
import akka.pattern.ask
import akka.serialization.JavaSerializer
import akka.actor.TypedActor._
@ -108,7 +109,7 @@ object TypedActorSpec {
def pigdog = "Pigdog"
def futurePigdog(): Future[String] = Promise.successful(pigdog)
def futurePigdog(): Future[String] = Promise.successful(pigdog).future
def futurePigdog(delay: Long): Future[String] = {
Thread.sleep(delay)
@ -117,7 +118,7 @@ object TypedActorSpec {
def futurePigdog(delay: Long, numbered: Int): Future[String] = {
Thread.sleep(delay)
Promise.successful(pigdog + numbered)
Promise.successful(pigdog + numbered).future
}
def futureComposePigdogFrom(foo: Foo): Future[String] = {

View file

@ -3,6 +3,8 @@
*/
package akka.actor.dispatch
import language.postfixOps
import java.rmi.RemoteException
import java.util.concurrent.{ TimeUnit, CountDownLatch, ConcurrentHashMap }
import java.util.concurrent.atomic.{ AtomicLong, AtomicInteger }
@ -18,8 +20,11 @@ import akka.dispatch._
import akka.event.Logging.Error
import akka.pattern.ask
import akka.testkit._
import akka.util.{ Timeout, Switch, Duration }
import akka.util.duration._
import akka.util.{ Timeout, Switch }
import scala.concurrent.util.duration._
import scala.concurrent.util.Duration
import scala.concurrent.{ Await, Future, Promise }
import scala.annotation.tailrec
object ActorModelSpec {
@ -154,7 +159,7 @@ object ActorModelSpec {
try {
await(deadline)(stops == dispatcher.stops.get)
} catch {
case e
case e: Throwable
system.eventStream.publish(Error(e, dispatcher.toString, dispatcher.getClass, "actual: stops=" + dispatcher.stops.get +
" required: stops=" + stops))
throw e
@ -211,7 +216,7 @@ object ActorModelSpec {
await(deadline)(stats.msgsProcessed.get() == msgsProcessed)
await(deadline)(stats.restarts.get() == restarts)
} catch {
case e
case e: Throwable
system.eventStream.publish(Error(e,
Option(dispatcher).toString,
(Option(dispatcher) getOrElse this).getClass,
@ -222,16 +227,16 @@ object ActorModelSpec {
}
}
def await(until: Long)(condition: Boolean): Unit = try {
while (System.currentTimeMillis() <= until) {
try {
if (condition) return else Thread.sleep(25)
} catch {
case e: InterruptedException
}
@tailrec def await(until: Long)(condition: Boolean): Unit = if (System.currentTimeMillis() <= until) {
var done = false
try {
done = condition
if (!done) Thread.sleep(25)
} catch {
case e: InterruptedException
}
throw new AssertionError("await failed")
}
if (!done) await(until)(condition)
} else throw new AssertionError("await failed")
}
abstract class ActorModelSpec(config: String) extends AkkaSpec(config) with DefaultTimeout {
@ -343,7 +348,7 @@ abstract class ActorModelSpec(config: String) extends AkkaSpec(config) with Defa
assertNoCountDown(done, 1000, "Should not process messages while suspended")
assertRefDefaultZero(a)(registers = 1, msgsReceived = 1, suspensions = 1)
a.resume
a.resume(inResponseToFailure = false)
assertCountDown(done, 3.seconds.dilated.toMillis, "Should resume processing of messages when resumed")
assertRefDefaultZero(a)(registers = 1, msgsReceived = 1, msgsProcessed = 1,
suspensions = 1, resumes = 1)
@ -408,9 +413,9 @@ abstract class ActorModelSpec(config: String) extends AkkaSpec(config) with Defa
val a = newTestActor(dispatcher.id)
val f1 = a ? Reply("foo")
val f2 = a ? Reply("bar")
val f3 = try { a ? Interrupt } catch { case ie: InterruptedException Promise.failed(new ActorInterruptedException(ie)) }
val f3 = try { a ? Interrupt } catch { case ie: InterruptedException Promise.failed(new ActorInterruptedException(ie)).future }
val f4 = a ? Reply("foo2")
val f5 = try { a ? Interrupt } catch { case ie: InterruptedException Promise.failed(new ActorInterruptedException(ie)) }
val f5 = try { a ? Interrupt } catch { case ie: InterruptedException Promise.failed(new ActorInterruptedException(ie)).future }
val f6 = a ? Reply("bar2")
assert(Await.result(f1, timeout.duration) === "foo")

View file

@ -1,13 +1,16 @@
package akka.actor.dispatch
import language.postfixOps
import java.util.concurrent.{ CountDownLatch, TimeUnit }
import java.util.concurrent.atomic.{ AtomicBoolean, AtomicInteger }
import akka.testkit.{ filterEvents, EventFilter, AkkaSpec }
import akka.actor.{ Props, Actor }
import akka.util.Duration
import akka.util.duration._
import scala.concurrent.Await
import scala.concurrent.util.Duration
import scala.concurrent.util.duration._
import akka.testkit.DefaultTimeout
import akka.dispatch.{ Await, PinnedDispatcher, Dispatchers, Dispatcher }
import akka.dispatch.{ PinnedDispatcher, Dispatchers, Dispatcher }
import akka.pattern.ask
object DispatcherActorSpec {

View file

@ -3,6 +3,8 @@
*/
package akka.actor.dispatch
import language.postfixOps
import java.util.concurrent.{ CountDownLatch, TimeUnit }
import scala.reflect.{ Manifest }
import akka.dispatch._
@ -12,7 +14,7 @@ import scala.collection.JavaConverters._
import com.typesafe.config.ConfigFactory
import akka.actor.Actor
import akka.actor.Props
import akka.util.duration._
import scala.concurrent.util.duration._
object DispatchersSpec {
val config = """

View file

@ -6,7 +6,8 @@ import akka.testkit._
import akka.actor.{ Props, Actor }
import akka.testkit.AkkaSpec
import org.scalatest.BeforeAndAfterEach
import akka.dispatch.{ Await, PinnedDispatcher, Dispatchers }
import akka.dispatch.{ PinnedDispatcher, Dispatchers }
import scala.concurrent.Await
import akka.pattern.ask
object PinnedActorSpec {

View file

@ -5,7 +5,7 @@ import akka.actor._
import akka.actor.Actor._
import akka.routing._
import java.util.concurrent.atomic.AtomicInteger
import akka.dispatch.Await
import scala.concurrent.Await
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
class ListenerSpec extends AkkaSpec {

View file

@ -4,11 +4,13 @@
package akka.config
import language.postfixOps
import akka.testkit.AkkaSpec
import com.typesafe.config.ConfigFactory
import scala.collection.JavaConverters._
import akka.util.duration._
import akka.util.Duration
import scala.concurrent.util.duration._
import scala.concurrent.util.Duration
import akka.actor.ActorSystem
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])

View file

@ -3,15 +3,18 @@
*/
package akka.dataflow
import language.postfixOps
import akka.actor.{ Actor, Props }
import akka.dispatch.{ Future, Await }
import akka.util.duration._
import akka.testkit.AkkaSpec
import akka.testkit.DefaultTimeout
import scala.concurrent.Future
import scala.concurrent.Await
import scala.concurrent.util.duration._
import akka.testkit.{ AkkaSpec, DefaultTimeout }
import akka.pattern.{ ask, pipe }
import scala.concurrent.ExecutionException
class Future2ActorSpec extends AkkaSpec with DefaultTimeout {
implicit val ec = system.dispatcher
"The Future2Actor bridge" must {
"support convenient sending to multiple destinations" in {
@ -41,9 +44,9 @@ class Future2ActorSpec extends AkkaSpec with DefaultTimeout {
}
}))
Await.result(actor ? "do", timeout.duration) must be(31)
intercept[AssertionError] {
intercept[ExecutionException] {
Await.result(actor ? "ex", timeout.duration)
}
}.getCause.isInstanceOf[AssertionError] must be(true)
}
}
}

View file

@ -3,6 +3,7 @@ package akka.dispatch
import akka.testkit.AkkaSpec
import akka.testkit.DefaultTimeout
import java.util.concurrent.{ ExecutorService, Executor, Executors }
import scala.concurrent.ExecutionContext
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
class ExecutionContextSpec extends AkkaSpec with DefaultTimeout {
@ -18,12 +19,12 @@ class ExecutionContextSpec extends AkkaSpec with DefaultTimeout {
val executorService: ExecutorService with ExecutionContext = ExecutionContext.fromExecutorService(es)
executorService must not be (null)
val jExecutor: ExecutionContextExecutor = ExecutionContexts.fromExecutor(es)
/*val jExecutor: ExecutionContextExecutor = ExecutionContext.fromExecutor(es)
jExecutor must not be (null)
val jExecutorService: ExecutionContextExecutorService = ExecutionContexts.fromExecutorService(es)
jExecutorService must not be (null)
*/
} finally {
es.shutdown
}

View file

@ -1,5 +1,7 @@
package akka.dispatch
import language.postfixOps
import org.scalatest.BeforeAndAfterAll
import org.scalatest.prop.Checkers
import org.scalacheck._
@ -7,18 +9,26 @@ import org.scalacheck.Arbitrary._
import org.scalacheck.Prop._
import org.scalacheck.Gen._
import akka.actor._
import akka.testkit.{ EventFilter, filterEvents, filterException }
import akka.util.duration._
import akka.testkit.AkkaSpec
import akka.testkit.{ EventFilter, filterEvents, filterException, AkkaSpec, DefaultTimeout, TestLatch }
import scala.concurrent.{ Await, Awaitable, Future, Promise, ExecutionContext }
import scala.util.control.NonFatal
import scala.concurrent.util.duration._
import scala.concurrent.util.Duration
import scala.concurrent.ExecutionContext
import org.scalatest.junit.JUnitSuite
import akka.testkit.DefaultTimeout
import akka.testkit.TestLatch
import scala.runtime.NonLocalReturnControl
import akka.pattern.ask
import java.lang.{ IllegalStateException, ArithmeticException }
import java.util.concurrent._
object FutureSpec {
def ready[T](awaitable: Awaitable[T], atMost: Duration): awaitable.type =
try Await.ready(awaitable, atMost) catch {
case t: TimeoutException throw t
case e if NonFatal(e) awaitable //swallow
}
class TestActor extends Actor {
def receive = {
case "Hello" sender ! "World"
@ -30,10 +40,10 @@ object FutureSpec {
class TestDelayActor(await: TestLatch) extends Actor {
def receive = {
case "Hello" Await.ready(await, TestLatch.DefaultTimeout); sender ! "World"
case "NoReply" Await.ready(await, TestLatch.DefaultTimeout)
case "Hello" FutureSpec.ready(await, TestLatch.DefaultTimeout); sender ! "World"
case "NoReply" FutureSpec.ready(await, TestLatch.DefaultTimeout)
case "Failure"
Await.ready(await, TestLatch.DefaultTimeout)
FutureSpec.ready(await, TestLatch.DefaultTimeout)
sender ! Status.Failure(new RuntimeException("Expected exception; to test fault-tolerance"))
}
}
@ -44,15 +54,15 @@ class JavaFutureSpec extends JavaFutureTests with JUnitSuite
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with DefaultTimeout {
import FutureSpec._
implicit val ec: ExecutionContext = system.dispatcher
"A Promise" when {
"never completed" must {
behave like emptyFuture(_(Promise()))
behave like emptyFuture(_(Promise().future))
"return supplied value on timeout" in {
val failure = Promise.failed[String](new RuntimeException("br0ken"))
val otherFailure = Promise.failed[String](new RuntimeException("last"))
val empty = Promise[String]()
val timedOut = Promise.successful[String]("Timedout")
val failure = Promise.failed[String](new RuntimeException("br0ken")).future
val otherFailure = Promise.failed[String](new RuntimeException("last")).future
val empty = Promise[String]().future
val timedOut = Promise.successful[String]("Timedout").future
Await.result(failure fallbackTo timedOut, timeout.duration) must be("Timedout")
Await.result(timedOut fallbackTo empty, timeout.duration) must be("Timedout")
@ -64,47 +74,49 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
}
"completed with a result" must {
val result = "test value"
val future = Promise[String]().complete(Right(result))
val future = Promise[String]().complete(Right(result)).future
behave like futureWithResult(_(future, result))
}
"completed with an exception" must {
val message = "Expected Exception"
val future = Promise[String]().complete(Left(new RuntimeException(message)))
val future = Promise[String]().complete(Left(new RuntimeException(message))).future
behave like futureWithException[RuntimeException](_(future, message))
}
"completed with an InterruptedException" must {
val message = "Boxed InterruptedException"
val future = Promise[String]().complete(Left(new InterruptedException(message)))
val future = Promise[String]().complete(Left(new InterruptedException(message))).future
behave like futureWithException[RuntimeException](_(future, message))
}
"completed with a NonLocalReturnControl" must {
val result = "test value"
val future = Promise[String]().complete(Left(new NonLocalReturnControl[String]("test", result)))
val future = Promise[String]().complete(Left(new NonLocalReturnControl[String]("test", result))).future
behave like futureWithResult(_(future, result))
}
"have different ECs" in {
def namedCtx(n: String) = ExecutionContexts.fromExecutorService(
Executors.newSingleThreadExecutor(new ThreadFactory {
def newThread(r: Runnable) = new Thread(r, n)
}))
def namedCtx(n: String) =
ExecutionContext.fromExecutorService(
Executors.newSingleThreadExecutor(new ThreadFactory { def newThread(r: Runnable) = new Thread(r, n) }))
val A = namedCtx("A")
val B = namedCtx("B")
// create a promise with ctx A
val p = Promise[String]()(A)
val p = Promise[String]()
// I would expect that any callback from p
// is executed in the context of p
val result = p map { _ + Thread.currentThread().getName() }
val result = {
implicit val ec = A
p.future map { _ + Thread.currentThread().getName() }
}
p.completeWith(Future { "Hi " }(B))
try {
Await.result(result, timeout.duration) must be === "Hi A"
} finally {
A.asInstanceOf[ExecutorService].shutdown()
B.asInstanceOf[ExecutorService].shutdown()
A.shutdown()
B.shutdown()
}
}
}
@ -117,12 +129,12 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
val latch = new TestLatch
val result = "test value"
val future = Future {
Await.ready(latch, TestLatch.DefaultTimeout)
FutureSpec.ready(latch, TestLatch.DefaultTimeout)
result
}
test(future)
latch.open()
Await.ready(future, timeout.duration)
FutureSpec.ready(future, timeout.duration)
}
}
"is completed" must {
@ -130,11 +142,11 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
val latch = new TestLatch
val result = "test value"
val future = Future {
Await.ready(latch, TestLatch.DefaultTimeout)
FutureSpec.ready(latch, TestLatch.DefaultTimeout)
result
}
latch.open()
Await.ready(future, timeout.duration)
FutureSpec.ready(future, timeout.duration)
test(future, result)
}
}
@ -142,13 +154,13 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
"pass checks" in {
filterException[ArithmeticException] {
check({ (future: Future[Int], actions: List[FutureAction])
def wrap[T](f: Future[T]): Either[Throwable, T] = FutureSpec.ready(f, timeout.duration).value.get
val result = (future /: actions)(_ /: _)
val expected = (Await.ready(future, timeout.duration).value.get /: actions)(_ /: _)
((Await.ready(result, timeout.duration).value.get, expected) match {
case (Right(a), Right(b)) a == b
val expected = (wrap(future) /: actions)(_ /: _)
((wrap(result), expected) match {
case (Right(a), Right(b)) a == b
case (Left(a), Left(b)) if a.toString == b.toString true
case (Left(a), Left(b)) if a.getStackTrace.isEmpty || b.getStackTrace.isEmpty
a.getClass.toString == b.getClass.toString
case (Left(a), Left(b)) if a.getStackTrace.isEmpty || b.getStackTrace.isEmpty a.getClass.toString == b.getClass.toString
case _ false
}) :| result.value.get.toString + " is expected to be " + expected.toString
}, minSuccessful(10000), workers(4))
@ -162,7 +174,7 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
behave like futureWithResult { test
val actor = system.actorOf(Props[TestActor])
val future = actor ? "Hello"
Await.ready(future, timeout.duration)
FutureSpec.ready(future, timeout.duration)
test(future, "World")
system.stop(actor)
}
@ -172,7 +184,7 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
filterException[RuntimeException] {
val actor = system.actorOf(Props[TestActor])
val future = actor ? "Failure"
Await.ready(future, timeout.duration)
FutureSpec.ready(future, timeout.duration)
test(future, "Expected exception; to test fault-tolerance")
system.stop(actor)
}
@ -186,7 +198,7 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
val actor1 = system.actorOf(Props[TestActor])
val actor2 = system.actorOf(Props(new Actor { def receive = { case s: String sender ! s.toUpperCase } }))
val future = actor1 ? "Hello" flatMap { case s: String actor2 ? s }
Await.ready(future, timeout.duration)
FutureSpec.ready(future, timeout.duration)
test(future, "WORLD")
system.stop(actor1)
system.stop(actor2)
@ -198,20 +210,20 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
val actor1 = system.actorOf(Props[TestActor])
val actor2 = system.actorOf(Props(new Actor { def receive = { case s: String sender ! Status.Failure(new ArithmeticException("/ by zero")) } }))
val future = actor1 ? "Hello" flatMap { case s: String actor2 ? s }
Await.ready(future, timeout.duration)
FutureSpec.ready(future, timeout.duration)
test(future, "/ by zero")
system.stop(actor1)
system.stop(actor2)
}
}
}
"will throw a MatchError when matching wrong type" must {
behave like futureWithException[MatchError] { test
filterException[MatchError] {
"will throw a NoSuchElementException when matching wrong type" must {
behave like futureWithException[NoSuchElementException] { test
filterException[NoSuchElementException] {
val actor1 = system.actorOf(Props[TestActor])
val actor2 = system.actorOf(Props(new Actor { def receive = { case s: String sender ! s.toUpperCase } }))
val future = actor1 ? "Hello" flatMap { case i: Int actor2 ? i }
Await.ready(future, timeout.duration)
FutureSpec.ready(future, timeout.duration)
test(future, "World (of class java.lang.String)")
system.stop(actor1)
system.stop(actor2)
@ -253,7 +265,7 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
}
"support pattern matching within a for-comprehension" in {
filterException[MatchError] {
filterException[NoSuchElementException] {
case class Req[T](req: T)
case class Res[T](res: T)
val actor = system.actorOf(Props(new Actor {
@ -276,7 +288,7 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
} yield b + "-" + c
Await.result(future1, timeout.duration) must be("10-14")
intercept[MatchError] { Await.result(future2, timeout.duration) }
intercept[NoSuchElementException] { Await.result(future2, timeout.duration) }
system.stop(actor)
}
}
@ -331,15 +343,16 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
"recoverWith from exceptions" in {
val o = new IllegalStateException("original")
val r = new IllegalStateException("recovered")
val yay = Promise.successful("yay!").future
intercept[IllegalStateException] {
Await.result(Promise.failed[String](o) recoverWith { case _ if false == true Promise.successful("yay!") }, timeout.duration)
Await.result(Promise.failed[String](o).future recoverWith { case _ if false == true yay }, timeout.duration)
} must be(o)
Await.result(Promise.failed[String](o) recoverWith { case _ Promise.successful("yay!") }, timeout.duration) must equal("yay!")
Await.result(Promise.failed[String](o).future recoverWith { case _ yay }, timeout.duration) must equal("yay!")
intercept[IllegalStateException] {
Await.result(Promise.failed[String](o) recoverWith { case _ Promise.failed[String](r) }, timeout.duration)
Await.result(Promise.failed[String](o).future recoverWith { case _ Promise.failed[String](r).future }, timeout.duration)
} must be(r)
}
@ -355,7 +368,7 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
}
"firstCompletedOf" in {
val futures = Vector.fill[Future[Int]](10)(Promise[Int]()) :+ Promise.successful[Int](5)
val futures = Vector.fill[Future[Int]](10)(Promise[Int]().future) :+ Promise.successful[Int](5).future
Await.result(Future.firstCompletedOf(futures), timeout.duration) must be(5)
}
@ -383,18 +396,18 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
val timeout = 10000 millis
val f = new IllegalStateException("test")
intercept[IllegalStateException] {
Await.result(Promise.failed[String](f) zip Promise.successful("foo"), timeout)
Await.result(Promise.failed[String](f).future zip Promise.successful("foo").future, timeout)
} must be(f)
intercept[IllegalStateException] {
Await.result(Promise.successful("foo") zip Promise.failed[String](f), timeout)
Await.result(Promise.successful("foo").future zip Promise.failed[String](f).future, timeout)
} must be(f)
intercept[IllegalStateException] {
Await.result(Promise.failed[String](f) zip Promise.failed[String](f), timeout)
Await.result(Promise.failed[String](f).future zip Promise.failed[String](f).future, timeout)
} must be(f)
Await.result(Promise.successful("foo") zip Promise.successful("foo"), timeout) must be(("foo", "foo"))
Await.result(Promise.successful("foo").future zip Promise.successful("foo").future, timeout) must be(("foo", "foo"))
}
"fold by composing" in {
@ -484,7 +497,7 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
val latch = new TestLatch
val actor = system.actorOf(Props[TestActor])
actor ? "Hello" onSuccess { case "World" latch.open() }
Await.ready(latch, 5 seconds)
FutureSpec.ready(latch, 5 seconds)
system.stop(actor)
}
@ -515,7 +528,7 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
intercept[ThrowableTest] { Await.result(f1, timeout.duration) }
val latch = new TestLatch
val f2 = Future { Await.ready(latch, 5 seconds); "success" }
val f2 = Future { FutureSpec.ready(latch, 5 seconds); "success" }
f2 foreach (_ throw new ThrowableTest("dispatcher foreach"))
f2 onSuccess { case _ throw new ThrowableTest("dispatcher receive") }
val f3 = f2 map (s s.toUpperCase)
@ -530,18 +543,19 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
"shouldBlockUntilResult" in {
val latch = new TestLatch
val f = Future { Await.ready(latch, 5 seconds); 5 }
val f = Future { FutureSpec.ready(latch, 5 seconds); 5 }
val f2 = Future { Await.result(f, timeout.duration) + 5 }
intercept[TimeoutException](Await.ready(f2, 100 millis))
intercept[TimeoutException](FutureSpec.ready(f2, 100 millis))
latch.open()
assert(Await.result(f2, timeout.duration) === 10)
val f3 = Future { Thread.sleep(100); 5 }
filterException[TimeoutException] { intercept[TimeoutException] { Await.ready(f3, 0 millis) } }
filterException[TimeoutException] { intercept[TimeoutException] { FutureSpec.ready(f3, 0 millis) } }
}
"futureComposingWithContinuations" in {
//FIXME DATAFLOW
/*"futureComposingWithContinuations" in {
import Future.flow
val actor = system.actorOf(Props[TestActor])
@ -614,14 +628,14 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
z() + y()
}
Await.ready(ly, 100 milliseconds)
intercept[TimeoutException] { Await.ready(lz, 100 milliseconds) }
FutureSpec.ready(ly, 100 milliseconds)
intercept[TimeoutException] { FutureSpec.ready(lz, 100 milliseconds) }
flow { x << 5 }
assert(Await.result(y, timeout.duration) === 5)
assert(Await.result(z, timeout.duration) === 5)
Await.ready(lz, timeout.duration)
FutureSpec.ready(lz, timeout.duration)
assert(Await.result(result, timeout.duration) === 10)
val a, b, c = Promise[Int]()
@ -651,14 +665,14 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
flow { one << 1 }
Await.ready(one, 1 minute)
FutureSpec.ready(one, 1 minute)
assert(one.isCompleted)
assert(List(two, simpleResult).forall(_.isCompleted == false))
flow { two << 9 }
Await.ready(two, 1 minute)
FutureSpec.ready(two, 1 minute)
assert(List(one, two).forall(_.isCompleted == true))
assert(Await.result(simpleResult, timeout.duration) === 10)
@ -677,20 +691,20 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
lz.open()
x1() + x2()
}
Await.ready(lx, 2 seconds)
FutureSpec.ready(lx, 2 seconds)
assert(!ly.isOpen)
assert(!lz.isOpen)
assert(List(x1, x2, y1, y2).forall(_.isCompleted == false))
flow { y1 << 1 } // When this is set, it should cascade down the line
Await.ready(ly, 2 seconds)
FutureSpec.ready(ly, 2 seconds)
assert(Await.result(x1, 1 minute) === 1)
assert(!lz.isOpen)
flow { y2 << 9 } // When this is set, it should cascade down the line
Await.ready(lz, 2 seconds)
FutureSpec.ready(lz, 2 seconds)
assert(Await.result(x2, 1 minute) === 9)
assert(List(x1, x2, y1, y2).forall(_.isCompleted))
@ -703,16 +717,16 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
val i1, i2, s1, s2 = new TestLatch
val callService1 = Future { i1.open(); Await.ready(s1, TestLatch.DefaultTimeout); 1 }
val callService2 = Future { i2.open(); Await.ready(s2, TestLatch.DefaultTimeout); 9 }
val callService1 = Future { i1.open(); FutureSpec.ready(s1, TestLatch.DefaultTimeout); 1 }
val callService2 = Future { i2.open(); FutureSpec.ready(s2, TestLatch.DefaultTimeout); 9 }
val result = flow { callService1() + callService2() }
assert(!s1.isOpen)
assert(!s2.isOpen)
assert(!result.isCompleted)
Await.ready(i1, 2 seconds)
Await.ready(i2, 2 seconds)
FutureSpec.ready(i1, 2 seconds)
FutureSpec.ready(i2, 2 seconds)
s1.open()
s2.open()
assert(Await.result(result, timeout.duration) === 10)
@ -733,8 +747,8 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
lz.open()
z() + y() + oops
}
intercept[TimeoutException] { Await.ready(ly, 100 milliseconds) }
intercept[TimeoutException] { Await.ready(lz, 100 milliseconds) }
intercept[TimeoutException] { FutureSpec.ready(ly, 100 milliseconds) }
intercept[TimeoutException] { FutureSpec.ready(lz, 100 milliseconds) }
flow { x << 5 }
assert(Await.result(y, timeout.duration) === 5)
@ -749,7 +763,7 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
val latch = new TestLatch
val future = Future {
Await.ready(latch, TestLatch.DefaultTimeout)
FutureSpec.ready(latch, TestLatch.DefaultTimeout)
"Hello"
}
@ -800,41 +814,41 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
flow { y << 2 }
assert(Await.result(z, timeout.duration) === 42)
}
}*/
"run callbacks async" in {
val latch = Vector.fill(10)(new TestLatch)
val f1 = Future { latch(0).open(); Await.ready(latch(1), TestLatch.DefaultTimeout); "Hello" }
val f2 = f1 map { s latch(2).open(); Await.ready(latch(3), TestLatch.DefaultTimeout); s.length }
val f1 = Future { latch(0).open(); FutureSpec.ready(latch(1), TestLatch.DefaultTimeout); "Hello" }
val f2 = f1 map { s latch(2).open(); FutureSpec.ready(latch(3), TestLatch.DefaultTimeout); s.length }
f2 foreach (_ latch(4).open())
Await.ready(latch(0), TestLatch.DefaultTimeout)
FutureSpec.ready(latch(0), TestLatch.DefaultTimeout)
f1 must not be ('completed)
f2 must not be ('completed)
latch(1).open()
Await.ready(latch(2), TestLatch.DefaultTimeout)
FutureSpec.ready(latch(2), TestLatch.DefaultTimeout)
f1 must be('completed)
f2 must not be ('completed)
val f3 = f1 map { s latch(5).open(); Await.ready(latch(6), TestLatch.DefaultTimeout); s.length * 2 }
val f3 = f1 map { s latch(5).open(); FutureSpec.ready(latch(6), TestLatch.DefaultTimeout); s.length * 2 }
f3 foreach (_ latch(3).open())
Await.ready(latch(5), TestLatch.DefaultTimeout)
FutureSpec.ready(latch(5), TestLatch.DefaultTimeout)
f3 must not be ('completed)
latch(6).open()
Await.ready(latch(4), TestLatch.DefaultTimeout)
FutureSpec.ready(latch(4), TestLatch.DefaultTimeout)
f2 must be('completed)
f3 must be('completed)
val p1 = Promise[String]()
val f4 = p1 map { s latch(7).open(); Await.ready(latch(8), TestLatch.DefaultTimeout); s.length }
val f4 = p1.future map { s latch(7).open(); FutureSpec.ready(latch(8), TestLatch.DefaultTimeout); s.length }
f4 foreach (_ latch(9).open())
p1 must not be ('completed)
@ -842,38 +856,62 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
p1 complete Right("Hello")
Await.ready(latch(7), TestLatch.DefaultTimeout)
FutureSpec.ready(latch(7), TestLatch.DefaultTimeout)
p1 must be('completed)
f4 must not be ('completed)
latch(8).open()
Await.ready(latch(9), TestLatch.DefaultTimeout)
FutureSpec.ready(latch(9), TestLatch.DefaultTimeout)
Await.ready(f4, timeout.duration) must be('completed)
FutureSpec.ready(f4, timeout.duration) must be('completed)
}
"should not deadlock with nested await (ticket 1313)" in {
val simple = Future() map (_ Await.result((Future(()) map (_ ())), timeout.duration))
Await.ready(simple, timeout.duration) must be('completed)
FutureSpec.ready(simple, timeout.duration) must be('completed)
val l1, l2 = new TestLatch
val complex = Future() map { _
Future.blocking()
//FIXME implement _taskStack for Futures
val nested = Future(())
nested foreach (_ l1.open())
Await.ready(l1, TestLatch.DefaultTimeout) // make sure nested is completed
FutureSpec.ready(l1, TestLatch.DefaultTimeout) // make sure nested is completed
nested foreach (_ l2.open())
Await.ready(l2, TestLatch.DefaultTimeout)
FutureSpec.ready(l2, TestLatch.DefaultTimeout)
}
Await.ready(complex, timeout.duration) must be('completed)
FutureSpec.ready(complex, timeout.duration) must be('completed)
}
"should capture first exception with dataflow" in {
"re-use the same thread for nested futures with batching ExecutionContext" in {
val failCount = new java.util.concurrent.atomic.AtomicInteger
val f = Future() flatMap { _
val originalThread = Thread.currentThread
// run some nested futures
val nested =
for (i 1 to 100)
yield Future.successful("abc") flatMap { _
if (Thread.currentThread ne originalThread)
failCount.incrementAndGet
// another level of nesting
Future.successful("xyz") map { _
if (Thread.currentThread ne originalThread)
failCount.incrementAndGet
}
}
Future.sequence(nested)
}
Await.ready(f, timeout.duration)
// TODO re-enable once we're using the batching dispatcher
// failCount.get must be(0)
}
//FIXME DATAFLOW
/*"should capture first exception with dataflow" in {
import Future.flow
val f1 = flow { 40 / 0 }
intercept[java.lang.ArithmeticException](Await result (f1, TestLatch.DefaultTimeout))
}
}*/
}
}
@ -888,17 +926,17 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
"contain a value" in { f((future, result) future.value must be(Some(Right(result)))) }
"return result with 'get'" in { f((future, result) Await.result(future, timeout.duration) must be(result)) }
"return result with 'Await.result'" in { f((future, result) Await.result(future, timeout.duration) must be(result)) }
"not timeout" in { f((future, _) Await.ready(future, 0 millis)) }
"not timeout" in { f((future, _) FutureSpec.ready(future, 0 millis)) }
"filter result" in {
f { (future, result)
Await.result((future filter (_ true)), timeout.duration) must be(result)
(evaluating { Await.result((future filter (_ false)), timeout.duration) } must produce[MatchError]).getMessage must startWith(result.toString)
(evaluating { Await.result((future filter (_ false)), timeout.duration) } must produce[java.util.NoSuchElementException]).getMessage must endWith(result.toString)
}
}
"transform result with map" in { f((future, result) Await.result((future map (_.toString.length)), timeout.duration) must be(result.toString.length)) }
"compose result with flatMap" in {
f { (future, result)
val r = for (r future; p Promise.successful("foo")) yield r.toString + p
val r = for (r future; p Promise.successful("foo").future) yield r.toString + p
Await.result(r, timeout.duration) must be(result.toString + "foo")
}
}
@ -906,13 +944,13 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
f { (future, result)
val p = Promise[Any]()
future foreach p.success
Await.result(p, timeout.duration) must be(result)
Await.result(p.future, timeout.duration) must be(result)
}
}
"zip properly" in {
f { (future, result)
Await.result(future zip Promise.successful("foo"), timeout.duration) must be((result, "foo"))
(evaluating { Await.result(future zip Promise.failed(new RuntimeException("ohnoes")), timeout.duration) } must produce[RuntimeException]).getMessage must be("ohnoes")
Await.result(future zip Promise.successful("foo").future, timeout.duration) must be((result, "foo"))
(evaluating { Await.result(future zip Promise.failed(new RuntimeException("ohnoes")).future, timeout.duration) } must produce[RuntimeException]).getMessage must be("ohnoes")
}
}
"not recover from exception" in { f((future, result) Await.result(future.recover({ case _ "pigdog" }), timeout.duration) must be(result)) }
@ -920,10 +958,10 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
f { (future, result)
val p = Promise[Any]()
future.onSuccess { case x p.success(x) }
Await.result(p, timeout.duration) must be(result)
Await.result(p.future, timeout.duration) must be(result)
}
}
"not project a failure" in { f((future, result) (evaluating { Await.result(future.failed, timeout.duration) } must produce[NoSuchElementException]).getMessage must be("Future.failed not completed with a throwable. Instead completed with: " + result)) }
"not project a failure" in { f((future, result) (evaluating { Await.result(future.failed, timeout.duration) } must produce[NoSuchElementException]).getMessage must be("Future.failed not completed with a throwable.")) }
"not perform action on exception" is pending
"cast using mapTo" in { f((future, result) Await.result(future.mapTo[Boolean].recover({ case _: ClassCastException false }), timeout.duration) must be(false)) }
}
@ -937,20 +975,20 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
future.value.get.left.get.getMessage must be(message)
})
}
"throw exception with 'get'" in { f((future, message) (evaluating { Await.result(future, timeout.duration) } must produce[E]).getMessage must be(message)) }
"throw exception with 'Await.result'" in { f((future, message) (evaluating { Await.result(future, timeout.duration) } must produce[E]).getMessage must be(message)) }
"throw exception with 'get'" in { f((future, message) (evaluating { Await.result(future, timeout.duration) } must produce[java.lang.Exception]).getMessage must be(message)) }
"throw exception with 'Await.result'" in { f((future, message) (evaluating { Await.result(future, timeout.duration) } must produce[java.lang.Exception]).getMessage must be(message)) }
"retain exception with filter" in {
f { (future, message)
(evaluating { Await.result(future filter (_ true), timeout.duration) } must produce[E]).getMessage must be(message)
(evaluating { Await.result(future filter (_ false), timeout.duration) } must produce[E]).getMessage must be(message)
(evaluating { Await.result(future filter (_ true), timeout.duration) } must produce[java.lang.Exception]).getMessage must be(message)
(evaluating { Await.result(future filter (_ false), timeout.duration) } must produce[java.lang.Exception]).getMessage must be(message)
}
}
"retain exception with map" in { f((future, message) (evaluating { Await.result(future map (_.toString.length), timeout.duration) } must produce[E]).getMessage must be(message)) }
"retain exception with flatMap" in { f((future, message) (evaluating { Await.result(future flatMap (_ Promise.successful[Any]("foo")), timeout.duration) } must produce[E]).getMessage must be(message)) }
"retain exception with map" in { f((future, message) (evaluating { Await.result(future map (_.toString.length), timeout.duration) } must produce[java.lang.Exception]).getMessage must be(message)) }
"retain exception with flatMap" in { f((future, message) (evaluating { Await.result(future flatMap (_ Promise.successful[Any]("foo").future), timeout.duration) } must produce[java.lang.Exception]).getMessage must be(message)) }
"not perform action with foreach" is pending
"zip properly" in {
f { (future, message) (evaluating { Await.result(future zip Promise.successful("foo"), timeout.duration) } must produce[E]).getMessage must be(message) }
f { (future, message) (evaluating { Await.result(future zip Promise.successful("foo").future, timeout.duration) } must produce[java.lang.Exception]).getMessage must be(message) }
}
"recover from exception" in { f((future, message) Await.result(future.recover({ case e if e.getMessage == message "pigdog" }), timeout.duration) must be("pigdog")) }
"not perform action on result" is pending
@ -959,10 +997,10 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
f { (future, message)
val p = Promise[Any]()
future.onFailure { case _ p.success(message) }
Await.result(p, timeout.duration) must be(message)
Await.result(p.future, timeout.duration) must be(message)
}
}
"always cast successfully using mapTo" in { f((future, message) (evaluating { Await.result(future.mapTo[java.lang.Thread], timeout.duration) } must produce[E]).getMessage must be(message)) }
"always cast successfully using mapTo" in { f((future, message) (evaluating { Await.result(future.mapTo[java.lang.Thread], timeout.duration) } must produce[java.lang.Exception]).getMessage must be(message)) }
}
sealed trait IntAction { def apply(that: Int): Int }
@ -979,17 +1017,17 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
case class MapAction(action: IntAction) extends FutureAction {
def /:(that: Either[Throwable, Int]): Either[Throwable, Int] = that match {
case Left(e) that
case Right(r) try { Right(action(r)) } catch { case e: RuntimeException Left(e) }
case Right(r) try { Right(action(r)) } catch { case e if NonFatal(e) Left(e) }
}
def /:(that: Future[Int]): Future[Int] = that map (action(_))
def /:(that: Future[Int]): Future[Int] = that map action.apply
}
case class FlatMapAction(action: IntAction) extends FutureAction {
def /:(that: Either[Throwable, Int]): Either[Throwable, Int] = that match {
case Left(e) that
case Right(r) try { Right(action(r)) } catch { case e: RuntimeException Left(e) }
case Right(r) try { Right(action(r)) } catch { case e if NonFatal(e) Left(e) }
}
def /:(that: Future[Int]): Future[Int] = that flatMap (n Future(action(n)))
def /:(that: Future[Int]): Future[Int] = that flatMap (n Future.successful(action(n)))
}
implicit def arbFuture: Arbitrary[Future[Int]] = Arbitrary(for (n arbitrary[Int]) yield Future(n))

View file

@ -3,15 +3,15 @@
*/
package akka.dispatch
import language.postfixOps
import java.util.concurrent.{ ConcurrentLinkedQueue, BlockingQueue }
import org.scalatest.{ BeforeAndAfterEach, BeforeAndAfterAll }
import com.typesafe.config.Config
import akka.actor.{ RepointableRef, Props, DeadLetter, ActorSystem, ActorRefWithCell, ActorRef, ActorCell }
import akka.testkit.AkkaSpec
import akka.util.duration.intToDurationInt
import scala.concurrent.{ Future, Promise, Await }
import scala.concurrent.util.duration.intToDurationInt
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
abstract class MailboxSpec extends AkkaSpec with BeforeAndAfterAll with BeforeAndAfterEach {
@ -76,7 +76,7 @@ abstract class MailboxSpec extends AkkaSpec with BeforeAndAfterAll with BeforeAn
}
})
t.start
result
result.future
}
def createMessageInvocation(msg: Any): Envelope = Envelope(msg, system.deadLetters, system)

View file

@ -1,14 +1,16 @@
package akka.dispatch
import language.postfixOps
import org.junit.runner.RunWith
import org.scalatest.junit.JUnitRunner
import com.typesafe.config.Config
import akka.actor.{ Props, InternalActorRef, ActorSystem, Actor }
import akka.pattern.ask
import akka.testkit.{ DefaultTimeout, AkkaSpec }
import akka.util.duration.intToDurationInt
import scala.concurrent.Await
import scala.concurrent.util.duration.intToDurationInt
object PriorityDispatcherSpec {
val config = """
@ -63,7 +65,7 @@ class PriorityDispatcherSpec extends AkkaSpec(PriorityDispatcherSpec.config) wit
val msgs = (1 to 100).toList
for (m msgs) actor ! m
actor.resume //Signal the actor to start treating it's message backlog
actor.resume(inResponseToFailure = false) //Signal the actor to start treating it's message backlog
Await.result(actor.?('Result).mapTo[List[Int]], timeout.duration) must be === msgs.reverse
}

View file

@ -4,9 +4,11 @@
package akka.event
import language.postfixOps
import org.scalatest.BeforeAndAfterEach
import akka.testkit._
import akka.util.duration._
import scala.concurrent.util.duration._
import java.util.concurrent.atomic._
import akka.actor.{ Props, Actor, ActorRef, ActorSystem }
import java.util.Comparator

View file

@ -3,8 +3,10 @@
*/
package akka.event
import language.postfixOps
import akka.testkit.AkkaSpec
import akka.util.duration._
import scala.concurrent.util.duration._
import akka.actor.{ Actor, ActorRef, ActorSystemImpl, ActorSystem, Props, UnhandledMessage }
import com.typesafe.config.ConfigFactory
import scala.collection.JavaConverters._

View file

@ -3,11 +3,13 @@
*/
package akka.event
import language.postfixOps
import org.scalatest.{ BeforeAndAfterAll, BeforeAndAfterEach }
import akka.util.duration._
import scala.concurrent.util.duration._
import akka.testkit._
import org.scalatest.WordSpec
import akka.util.Duration
import scala.concurrent.util.Duration
import com.typesafe.config.ConfigFactory
import scala.collection.JavaConverters._
import java.util.Properties

View file

@ -3,15 +3,21 @@
*/
package akka.pattern
import akka.testkit.AkkaSpec
import akka.util.duration._
import akka.testkit.DefaultTimeout
import language.postfixOps
class AskSpec extends AkkaSpec with DefaultTimeout {
import akka.testkit.AkkaSpec
import scala.concurrent.util.duration._
import scala.concurrent.Await
import akka.testkit.DefaultTimeout
import akka.actor.{ Props, ActorRef }
import akka.util.Timeout
class AskSpec extends AkkaSpec {
"The “ask” pattern" must {
"return broken promises on DeadLetters" in {
implicit val timeout = Timeout(5 seconds)
val dead = system.actorFor("/system/deadLetters")
val f = dead.ask(42)(1 second)
f.isCompleted must be(true)
@ -22,6 +28,7 @@ class AskSpec extends AkkaSpec with DefaultTimeout {
}
"return broken promises on EmptyLocalActorRefs" in {
implicit val timeout = Timeout(5 seconds)
val empty = system.actorFor("unknown")
val f = empty ? 3.14
f.isCompleted must be(true)
@ -31,6 +38,45 @@ class AskSpec extends AkkaSpec with DefaultTimeout {
}
}
"return broken promises on unsupported ActorRefs" in {
implicit val timeout = Timeout(5 seconds)
val f = ask(null: ActorRef, 3.14)
f.isCompleted must be(true)
intercept[IllegalArgumentException] {
Await.result(f, remaining)
}.getMessage must be === "Unsupported type of ActorRef for the recipient. Question not sent to [null]"
}
"return broken promises on 0 timeout" in {
implicit val timeout = Timeout(0 seconds)
val echo = system.actorOf(Props(ctx { case x ctx.sender ! x }))
val f = echo ? "foo"
val expectedMsg = "Timeout length for an `ask` must be greater or equal to 1. Question not sent to [%s]" format echo
intercept[IllegalArgumentException] {
Await.result(f, remaining)
}.getMessage must be === expectedMsg
}
"return broken promises on < 0 timeout" in {
implicit val timeout = Timeout(-1000 seconds)
val echo = system.actorOf(Props(ctx { case x ctx.sender ! x }))
val f = echo ? "foo"
val expectedMsg = "Timeout length for an `ask` must be greater or equal to 1. Question not sent to [%s]" format echo
intercept[IllegalArgumentException] {
Await.result(f, remaining)
}.getMessage must be === expectedMsg
}
"return broken promises on infinite timeout" in {
implicit val timeout = Timeout.never
val echo = system.actorOf(Props(ctx { case x ctx.sender ! x }))
val f = echo ? "foo"
val expectedMsg = "Timeouts to `ask` must be finite. Question not sent to [%s]" format echo
intercept[IllegalArgumentException] {
Await.result(f, remaining)
}.getMessage must be === expectedMsg
}
}
}

View file

@ -4,12 +4,11 @@
package akka.pattern
import akka.testkit._
import akka.util.duration._
import akka.dispatch.{ Promise, Await, Future }
import akka.actor.ActorSystem
import scala.concurrent.util.duration._
import scala.concurrent.{ Promise, Future, Await }
class CircuitBreakerMTSpec extends AkkaSpec {
implicit val ec = system.dispatcher
"A circuit breaker being called by many threads" must {
val breaker = new CircuitBreaker(system.scheduler, 5, 100.millis.dilated, 500.millis.dilated)
@ -33,7 +32,7 @@ class CircuitBreakerMTSpec extends AkkaSpec {
val futures = for (i 1 to 100) yield breaker.withCircuitBreaker(Future {
Thread.sleep(10); "success"
}) recoverWith {
case _: CircuitBreakerOpenException Promise.successful("CBO")
case _: CircuitBreakerOpenException Promise.successful("CBO").future
}
val result = Await.result(Future.sequence(futures), 5.second.dilated)
@ -53,7 +52,7 @@ class CircuitBreakerMTSpec extends AkkaSpec {
val futures = for (i 1 to 100) yield breaker.withCircuitBreaker(Future {
Thread.sleep(10); "succeed"
}) recoverWith {
case _: CircuitBreakerOpenException Promise.successful("CBO")
case _: CircuitBreakerOpenException Promise.successful("CBO").future
}
val result = Await.result(Future.sequence(futures), 5.second.dilated)
@ -72,7 +71,7 @@ class CircuitBreakerMTSpec extends AkkaSpec {
val futures = (1 to 100) map {
i
breaker.withCircuitBreaker(Future { Thread.sleep(10); "succeed" }) recoverWith {
case _: CircuitBreakerOpenException Promise.successful("CBO")
case _: CircuitBreakerOpenException Promise.successful("CBO").future
}
}

View file

@ -1,70 +1,54 @@
/**
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.pattern
import akka.util.duration._
import language.postfixOps
import scala.concurrent.util.duration._
import akka.testkit._
import org.scalatest.BeforeAndAfter
import akka.dispatch.Future
import akka.dispatch.Await
import akka.actor.{ ActorSystem, Scheduler }
import concurrent.{ ExecutionContext, Future, Await }
object CircuitBreakerSpec {
class TestException extends RuntimeException
class Breaker(val instance: CircuitBreaker)(implicit system: ActorSystem) {
val halfOpenLatch = new TestLatch(1)
val openLatch = new TestLatch(1)
val closedLatch = new TestLatch(1)
def apply(): CircuitBreaker = instance
instance.onClose(closedLatch.countDown()).onHalfOpen(halfOpenLatch.countDown()).onOpen(openLatch.countDown())
}
def shortCallTimeoutCb()(implicit system: ActorSystem, ec: ExecutionContext): Breaker =
new Breaker(new CircuitBreaker(system.scheduler, 1, 50.millis.dilated, 500.millis.dilated))
def shortResetTimeoutCb()(implicit system: ActorSystem, ec: ExecutionContext): Breaker =
new Breaker(new CircuitBreaker(system.scheduler, 1, 1000.millis.dilated, 50.millis.dilated))
def longCallTimeoutCb()(implicit system: ActorSystem, ec: ExecutionContext): Breaker =
new Breaker(new CircuitBreaker(system.scheduler, 1, 5 seconds, 500.millis.dilated))
def longResetTimeoutCb()(implicit system: ActorSystem, ec: ExecutionContext): Breaker =
new Breaker(new CircuitBreaker(system.scheduler, 1, 100.millis.dilated, 5 seconds))
def multiFailureCb()(implicit system: ActorSystem, ec: ExecutionContext): Breaker =
new Breaker(new CircuitBreaker(system.scheduler, 5, 200.millis.dilated, 500.millis.dilated))
}
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
class CircuitBreakerSpec extends AkkaSpec with BeforeAndAfter {
import CircuitBreakerSpec.TestException
implicit def ec = system.dispatcher
implicit def s = system
val awaitTimeout = 2.seconds.dilated
@volatile
var breakers: TestCircuitBreakers = null
class TestCircuitBreakers {
val halfOpenLatch = new TestLatch(1)
val openLatch = new TestLatch(1)
val closedLatch = new TestLatch(1)
val shortCallTimeoutCb = new CircuitBreaker(system.scheduler, 1, 50.millis.dilated, 500.millis.dilated)
.onClose(closedLatch.countDown())
.onHalfOpen(halfOpenLatch.countDown())
.onOpen(openLatch.countDown())
val shortResetTimeoutCb = new CircuitBreaker(system.scheduler, 1, 1000.millis.dilated, 50.millis.dilated)
.onClose(closedLatch.countDown())
.onHalfOpen(halfOpenLatch.countDown())
.onOpen(openLatch.countDown())
val longCallTimeoutCb = new CircuitBreaker(system.scheduler, 1, 5 seconds, 500.millis.dilated)
.onClose(closedLatch.countDown())
.onHalfOpen(halfOpenLatch.countDown())
.onOpen(openLatch.countDown())
val longResetTimeoutCb = new CircuitBreaker(system.scheduler, 1, 100.millis.dilated, 5 seconds)
.onClose(closedLatch.countDown())
.onHalfOpen(halfOpenLatch.countDown())
.onOpen(openLatch.countDown())
val multiFailureCb = new CircuitBreaker(system.scheduler, 5, 200.millis.dilated, 500.millis.dilated)
.onClose(closedLatch.countDown())
.onHalfOpen(halfOpenLatch.countDown())
.onOpen(openLatch.countDown())
}
before {
breakers = new TestCircuitBreakers
}
def checkLatch(latch: TestLatch) {
Await.ready(latch, awaitTimeout)
}
def checkLatch(latch: TestLatch): Unit = Await.ready(latch, awaitTimeout)
def throwException = throw new TestException
@ -72,171 +56,151 @@ class CircuitBreakerSpec extends AkkaSpec with BeforeAndAfter {
"A synchronous circuit breaker that is open" must {
"throw exceptions when called before reset timeout" in {
val breaker = CircuitBreakerSpec.longResetTimeoutCb()
intercept[TestException] {
breakers.longResetTimeoutCb.withSyncCircuitBreaker(throwException)
}
checkLatch(breakers.openLatch)
intercept[TestException] { breaker().withSyncCircuitBreaker(throwException) }
intercept[CircuitBreakerOpenException] {
breakers.longResetTimeoutCb.withSyncCircuitBreaker(sayHi)
}
checkLatch(breaker.openLatch)
intercept[CircuitBreakerOpenException] { breaker().withSyncCircuitBreaker(sayHi) }
}
"transition to half-open on reset timeout" in {
intercept[TestException] {
breakers.shortResetTimeoutCb.withSyncCircuitBreaker(throwException)
}
checkLatch(breakers.halfOpenLatch)
val breaker = CircuitBreakerSpec.shortResetTimeoutCb()
intercept[TestException] { breaker().withSyncCircuitBreaker(throwException) }
checkLatch(breaker.halfOpenLatch)
}
}
"A synchronous circuit breaker that is half-open" must {
"pass through next call and close on success" in {
intercept[TestException] {
breakers.shortResetTimeoutCb.withSyncCircuitBreaker(throwException)
}
checkLatch(breakers.halfOpenLatch)
assert("hi" == breakers.shortResetTimeoutCb.withSyncCircuitBreaker(sayHi))
checkLatch(breakers.closedLatch)
val breaker = CircuitBreakerSpec.shortResetTimeoutCb()
intercept[TestException] { breaker().withSyncCircuitBreaker(throwException) }
checkLatch(breaker.halfOpenLatch)
assert("hi" == breaker().withSyncCircuitBreaker(sayHi))
checkLatch(breaker.closedLatch)
}
"open on exception in call" in {
intercept[TestException] {
breakers.shortResetTimeoutCb.withSyncCircuitBreaker(throwException)
}
checkLatch(breakers.halfOpenLatch)
intercept[TestException] {
breakers.shortResetTimeoutCb.withSyncCircuitBreaker(throwException)
}
checkLatch(breakers.openLatch)
val breaker = CircuitBreakerSpec.shortResetTimeoutCb()
intercept[TestException] { breaker().withSyncCircuitBreaker(throwException) }
checkLatch(breaker.halfOpenLatch)
intercept[TestException] { breaker().withSyncCircuitBreaker(throwException) }
checkLatch(breaker.openLatch)
}
}
"A synchronous circuit breaker that is closed" must {
"allow calls through" in {
breakers.longCallTimeoutCb.withSyncCircuitBreaker(sayHi) must be("hi")
val breaker = CircuitBreakerSpec.longCallTimeoutCb()
breaker().withSyncCircuitBreaker(sayHi) must be("hi")
}
"increment failure count on failure" in {
intercept[TestException] {
breakers.longCallTimeoutCb.withSyncCircuitBreaker(throwException)
}
checkLatch(breakers.openLatch)
breakers.longCallTimeoutCb.currentFailureCount must be(1)
val breaker = CircuitBreakerSpec.longCallTimeoutCb()
breaker().currentFailureCount must be(0)
intercept[TestException] { breaker().withSyncCircuitBreaker(throwException) }
checkLatch(breaker.openLatch)
breaker().currentFailureCount must be(1)
}
"reset failure count after success" in {
val breaker = CircuitBreakerSpec.multiFailureCb()
breaker().currentFailureCount must be(0)
intercept[TestException] {
breakers.multiFailureCb.withSyncCircuitBreaker(throwException)
val ct = Thread.currentThread() // Ensure that the thunk is executed in the tests thread
breaker().withSyncCircuitBreaker({ if (Thread.currentThread() eq ct) throwException else "fail" })
}
breakers.multiFailureCb.currentFailureCount must be(1)
breakers.multiFailureCb.withSyncCircuitBreaker(sayHi)
breakers.multiFailureCb.currentFailureCount must be(0)
breaker().currentFailureCount must be === 1
breaker().withSyncCircuitBreaker(sayHi)
breaker().currentFailureCount must be === 0
}
"increment failure count on callTimeout" in {
breakers.shortCallTimeoutCb.withSyncCircuitBreaker({
100.millis.dilated.sleep()
})
breakers.shortCallTimeoutCb.currentFailureCount must be(1)
val breaker = CircuitBreakerSpec.shortCallTimeoutCb()
breaker().withSyncCircuitBreaker(Thread.sleep(100.millis.dilated.toMillis))
awaitCond(breaker().currentFailureCount == 1, remaining)
}
}
"An asynchronous circuit breaker that is open" must {
"throw exceptions when called before reset timeout" in {
breakers.longResetTimeoutCb.withCircuitBreaker(Future(throwException))
val breaker = CircuitBreakerSpec.longResetTimeoutCb()
breaker().withCircuitBreaker(Future(throwException))
checkLatch(breakers.openLatch)
checkLatch(breaker.openLatch)
intercept[CircuitBreakerOpenException] {
Await.result(
breakers.longResetTimeoutCb.withCircuitBreaker(Future(sayHi)),
awaitTimeout)
}
intercept[CircuitBreakerOpenException] { Await.result(breaker().withCircuitBreaker(Future(sayHi)), awaitTimeout) }
}
"transition to half-open on reset timeout" in {
breakers.shortResetTimeoutCb.withCircuitBreaker(Future(throwException))
checkLatch(breakers.halfOpenLatch)
val breaker = CircuitBreakerSpec.shortResetTimeoutCb()
breaker().withCircuitBreaker(Future(throwException))
checkLatch(breaker.halfOpenLatch)
}
}
"An asynchronous circuit breaker that is half-open" must {
"pass through next call and close on success" in {
breakers.shortResetTimeoutCb.withCircuitBreaker(Future(throwException))
checkLatch(breakers.halfOpenLatch)
Await.result(
breakers.shortResetTimeoutCb.withCircuitBreaker(Future(sayHi)),
awaitTimeout) must be("hi")
checkLatch(breakers.closedLatch)
val breaker = CircuitBreakerSpec.shortResetTimeoutCb()
breaker().withCircuitBreaker(Future(throwException))
checkLatch(breaker.halfOpenLatch)
Await.result(breaker().withCircuitBreaker(Future(sayHi)), awaitTimeout) must be("hi")
checkLatch(breaker.closedLatch)
}
"re-open on exception in call" in {
breakers.shortResetTimeoutCb.withCircuitBreaker(Future(throwException))
checkLatch(breakers.halfOpenLatch)
intercept[TestException] {
Await.result(
breakers.shortResetTimeoutCb.withCircuitBreaker(Future(throwException)),
awaitTimeout)
}
checkLatch(breakers.openLatch)
val breaker = CircuitBreakerSpec.shortResetTimeoutCb()
breaker().withCircuitBreaker(Future(throwException))
checkLatch(breaker.halfOpenLatch)
intercept[TestException] { Await.result(breaker().withCircuitBreaker(Future(throwException)), awaitTimeout) }
checkLatch(breaker.openLatch)
}
"re-open on async failure" in {
breakers.shortResetTimeoutCb.withCircuitBreaker(Future(throwException))
checkLatch(breakers.halfOpenLatch)
val breaker = CircuitBreakerSpec.shortResetTimeoutCb()
breaker().withCircuitBreaker(Future(throwException))
checkLatch(breaker.halfOpenLatch)
breakers.shortResetTimeoutCb.withCircuitBreaker(Future(throwException))
checkLatch(breakers.openLatch)
breaker().withCircuitBreaker(Future(throwException))
checkLatch(breaker.openLatch)
}
}
"An asynchronous circuit breaker that is closed" must {
"allow calls through" in {
Await.result(
breakers.longCallTimeoutCb.withCircuitBreaker(Future(sayHi)),
awaitTimeout) must be("hi")
val breaker = CircuitBreakerSpec.longCallTimeoutCb()
Await.result(breaker().withCircuitBreaker(Future(sayHi)), awaitTimeout) must be("hi")
}
"increment failure count on exception" in {
intercept[TestException] {
Await.result(
breakers.longCallTimeoutCb.withCircuitBreaker(Future(throwException)),
awaitTimeout)
}
checkLatch(breakers.openLatch)
breakers.longCallTimeoutCb.currentFailureCount must be(1)
val breaker = CircuitBreakerSpec.longCallTimeoutCb()
intercept[TestException] { Await.result(breaker().withCircuitBreaker(Future(throwException)), awaitTimeout) }
checkLatch(breaker.openLatch)
breaker().currentFailureCount must be(1)
}
"increment failure count on async failure" in {
breakers.longCallTimeoutCb.withCircuitBreaker(Future(throwException))
checkLatch(breakers.openLatch)
breakers.longCallTimeoutCb.currentFailureCount must be(1)
val breaker = CircuitBreakerSpec.longCallTimeoutCb()
breaker().withCircuitBreaker(Future(throwException))
checkLatch(breaker.openLatch)
breaker().currentFailureCount must be(1)
}
"reset failure count after success" in {
breakers.multiFailureCb.withCircuitBreaker(Future(sayHi))
val latch = TestLatch(4)
for (n 1 to 4) breakers.multiFailureCb.withCircuitBreaker(Future(throwException))
awaitCond(breakers.multiFailureCb.currentFailureCount == 4, awaitTimeout)
breakers.multiFailureCb.withCircuitBreaker(Future(sayHi))
awaitCond(breakers.multiFailureCb.currentFailureCount == 0, awaitTimeout)
val breaker = CircuitBreakerSpec.multiFailureCb()
breaker().withCircuitBreaker(Future(sayHi))
for (n 1 to 4) breaker().withCircuitBreaker(Future(throwException))
awaitCond(breaker().currentFailureCount == 4, awaitTimeout)
breaker().withCircuitBreaker(Future(sayHi))
awaitCond(breaker().currentFailureCount == 0, awaitTimeout)
}
"increment failure count on callTimeout" in {
breakers.shortCallTimeoutCb.withCircuitBreaker {
Future {
100.millis.dilated.sleep()
sayHi
}
}
checkLatch(breakers.openLatch)
breakers.shortCallTimeoutCb.currentFailureCount must be(1)
val breaker = CircuitBreakerSpec.shortCallTimeoutCb()
breaker().withCircuitBreaker(Future { Thread.sleep(100.millis.dilated.toMillis); sayHi })
checkLatch(breaker.openLatch)
breaker().currentFailureCount must be(1)
}
}

View file

@ -4,25 +4,26 @@
package akka.pattern
import language.postfixOps
import akka.testkit.AkkaSpec
import akka.actor.Props
import akka.actor.Actor
import akka.util.Duration
import akka.util.duration._
import akka.dispatch.{ Future, Promise, Await }
import akka.actor.{ Props, Actor }
import scala.concurrent.{ Future, Promise, Await }
import scala.concurrent.util.Duration
import scala.concurrent.util.duration._
object PatternSpec {
case class Work(duration: Duration)
class TargetActor extends Actor {
def receive = {
case Work(duration) duration.sleep()
case Work(duration) Thread.sleep(duration.toMillis)
}
}
}
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
class PatternSpec extends AkkaSpec {
implicit val ec = system.dispatcher
import PatternSpec._
"pattern.gracefulStop" must {
@ -48,16 +49,16 @@ class PatternSpec extends AkkaSpec {
"pattern.after" must {
"be completed successfully eventually" in {
val f = after(1 second, using = system.scheduler)(Promise.successful(5))
val f = after(1 second, using = system.scheduler)(Promise.successful(5).future)
val r = Future.firstCompletedOf(Seq(Promise[Int](), f))
val r = Future.firstCompletedOf(Seq(Promise[Int]().future, f))
Await.result(r, remaining) must be(5)
}
"be completed abnormally eventually" in {
val f = after(1 second, using = system.scheduler)(Promise.failed(new IllegalStateException("Mexico")))
val f = after(1 second, using = system.scheduler)(Promise.failed(new IllegalStateException("Mexico")).future)
val r = Future.firstCompletedOf(Seq(Promise[Int](), f))
val r = Future.firstCompletedOf(Seq(Promise[Int]().future, f))
intercept[IllegalStateException] { Await.result(r, remaining) }.getMessage must be("Mexico")
}
}

View file

@ -4,8 +4,8 @@ import akka.performance.workbench.PerformanceSpec
import akka.actor._
import java.util.concurrent.{ ThreadPoolExecutor, CountDownLatch, TimeUnit }
import akka.dispatch._
import akka.util.Duration
import akka.util.duration._
import scala.concurrent.util.Duration
import scala.concurrent.util.duration._
// -server -Xms512M -Xmx1024M -XX:+UseParallelGC -Dbenchmark=true -Dbenchmark.repeatFactor=500
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])

View file

@ -4,8 +4,8 @@ import akka.performance.workbench.PerformanceSpec
import akka.actor._
import java.util.concurrent.{ ThreadPoolExecutor, CountDownLatch, TimeUnit }
import akka.dispatch._
import akka.util.Duration
import akka.util.duration._
import scala.concurrent.util.Duration
import scala.concurrent.util.duration._
// -server -Xms512M -Xmx1024M -XX:+UseParallelGC -Dbenchmark=true -Dbenchmark.repeatFactor=500
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])

View file

@ -1,5 +1,7 @@
package akka.performance.trading.system
import language.postfixOps
import akka.performance.trading.domain.Orderbook
import akka.performance.trading.domain.OrderbookRepository
import akka.actor.Actor._
@ -44,7 +46,7 @@ class AkkaTradingSystem(val system: ActorSystem) extends TradingSystem {
def matchingEngineDispatcher: Option[String] = Some("benchmark.trading-dispatcher")
override val orderbooksGroupedByMatchingEngine: List[List[Orderbook]] =
for (groupOfSymbols: List[String] OrderbookRepository.orderbookSymbolsGroupedByMatchingEngine)
for (groupOfSymbols OrderbookRepository.orderbookSymbolsGroupedByMatchingEngine)
yield groupOfSymbols map (s Orderbook(s, false, system))
var matchingEngineForOrderbook: Map[String, ActorRef] = Map()

View file

@ -102,20 +102,21 @@ class FileBenchResultRepository extends BenchResultRepository {
private def save(stats: Stats) {
new File(serDir).mkdirs
if (!serDirExists) return
val timestamp = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date(stats.timestamp))
val name = stats.name + "--" + timestamp + "--" + stats.load + ".ser"
val f = new File(serDir, name)
var out: ObjectOutputStream = null
try {
out = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(f)))
out.writeObject(stats)
} catch {
case e: Exception
val errMsg = "Failed to save [%s] to [%s], due to [%s]".format(stats, f.getAbsolutePath, e.getMessage)
throw new RuntimeException(errMsg)
} finally {
if (out ne null) try { out.close() } catch { case ignore: Exception }
if (serDirExists) {
val timestamp = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date(stats.timestamp))
val name = stats.name + "--" + timestamp + "--" + stats.load + ".ser"
val f = new File(serDir, name)
var out: ObjectOutputStream = null
try {
out = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(f)))
out.writeObject(stats)
} catch {
case e: Exception
val errMsg = "Failed to save [%s] to [%s], due to [%s]".format(stats, f.getAbsolutePath, e.getMessage)
throw new RuntimeException(errMsg)
} finally {
if (out ne null) try { out.close() } catch { case ignore: Exception }
}
}
}
@ -142,19 +143,20 @@ class FileBenchResultRepository extends BenchResultRepository {
def saveHtmlReport(content: String, fileName: String) {
new File(htmlDir).mkdirs
if (!htmlDirExists) return
val f = new File(htmlDir, fileName)
var writer: PrintWriter = null
try {
writer = new PrintWriter(new FileWriter(f))
writer.print(content)
writer.flush()
} catch {
case e: Exception
val errMsg = "Failed to save report to [%s], due to [%s]".format(f.getAbsolutePath, e.getMessage)
throw new RuntimeException(errMsg)
} finally {
if (writer ne null) try { writer.close() } catch { case ignore: Exception }
if (htmlDirExists) {
val f = new File(htmlDir, fileName)
var writer: PrintWriter = null
try {
writer = new PrintWriter(new FileWriter(f))
writer.print(content)
writer.flush()
} catch {
case e: Exception
val errMsg = "Failed to save report to [%s], due to [%s]".format(f.getAbsolutePath, e.getMessage)
throw new RuntimeException(errMsg)
} finally {
if (writer ne null) try { writer.close() } catch { case ignore: Exception }
}
}
}

View file

@ -17,131 +17,133 @@ object GoogleChartBuilder {
* Builds a bar chart for tps in the statistics.
*/
def tpsChartUrl(statsByTimestamp: TreeMap[Long, Seq[Stats]], title: String, legend: Stats String): String = {
if (statsByTimestamp.isEmpty) return ""
if (statsByTimestamp.isEmpty) ""
else {
val loads = statsByTimestamp.values.head.map(_.load)
val allStats = statsByTimestamp.values.flatten
val loads = statsByTimestamp.values.head.map(_.load)
val allStats = statsByTimestamp.values.flatten
val sb = new StringBuilder
sb.append(BaseUrl)
// bar chart
sb.append("cht=bvg")
sb.append("&")
// size
sb.append("chs=").append(ChartWidth).append("x").append(ChartHeight)
sb.append("&")
// title
sb.append("chtt=").append(urlEncode(title))
sb.append("&")
// axis locations
sb.append("chxt=y,x")
sb.append("&")
// labels
sb.append("chxl=1:|")
sb.append(loads.mkString("|"))
sb.append("&")
val sb = new StringBuilder
sb.append(BaseUrl)
// bar chart
sb.append("cht=bvg")
sb.append("&")
// size
sb.append("chs=").append(ChartWidth).append("x").append(ChartHeight)
sb.append("&")
// title
sb.append("chtt=").append(urlEncode(title))
sb.append("&")
// axis locations
sb.append("chxt=y,x")
sb.append("&")
// labels
sb.append("chxl=1:|")
sb.append(loads.mkString("|"))
sb.append("&")
// label color and font
//sb.append("chxs=2,D65D82,11.5,0,lt,D65D82")
//sb.append("&")
// label color and font
//sb.append("chxs=2,D65D82,11.5,0,lt,D65D82")
//sb.append("&")
// legend
val legendStats = statsByTimestamp.values.map(_.head).toSeq
appendLegend(legendStats, sb, legend)
sb.append("&")
// bar spacing
sb.append("chbh=a,4,20")
sb.append("&")
// bar colors
barColors(statsByTimestamp.size, sb)
sb.append("&")
// legend
val legendStats = statsByTimestamp.values.map(_.head).toSeq
appendLegend(legendStats, sb, legend)
sb.append("&")
// bar spacing
sb.append("chbh=a,4,20")
sb.append("&")
// bar colors
barColors(statsByTimestamp.size, sb)
sb.append("&")
// data series
val loadStr = loads.mkString(",")
sb.append("chd=t:")
val maxValue = allStats.map(_.tps).max
val tpsSeries: Iterable[String] =
for (statsSeq statsByTimestamp.values) yield {
statsSeq.map(_.tps).mkString(",")
}
sb.append(tpsSeries.mkString("|"))
// data series
val loadStr = loads.mkString(",")
sb.append("chd=t:")
val maxValue = allStats.map(_.tps).max
val tpsSeries: Iterable[String] =
for (statsSeq statsByTimestamp.values) yield {
statsSeq.map(_.tps).mkString(",")
}
sb.append(tpsSeries.mkString("|"))
// y range
sb.append("&")
sb.append("chxr=0,0,").append(maxValue)
sb.append("&")
sb.append("chds=0,").append(maxValue)
sb.append("&")
// y range
sb.append("&")
sb.append("chxr=0,0,").append(maxValue)
sb.append("&")
sb.append("chds=0,").append(maxValue)
sb.append("&")
// grid lines
appendGridSpacing(maxValue.toLong, sb)
// grid lines
appendGridSpacing(maxValue.toLong, sb)
return sb.toString
sb.toString
}
}
/**
* Builds a bar chart for all percentiles and the mean in the statistics.
*/
def percentilesAndMeanChartUrl(statistics: Seq[Stats], title: String, legend: Stats String): String = {
if (statistics.isEmpty) return ""
if (statistics.isEmpty) ""
else {
val current = statistics.last
val current = statistics.last
val sb = new StringBuilder
sb.append(BaseUrl)
// bar chart
sb.append("cht=bvg")
sb.append("&")
// size
sb.append("chs=").append(ChartWidth).append("x").append(ChartHeight)
sb.append("&")
// title
sb.append("chtt=").append(urlEncode(title))
sb.append("&")
// axis locations
sb.append("chxt=y,x,y")
sb.append("&")
// labels
percentileLabels(current.percentiles, sb)
sb.append("|mean")
sb.append("|2:|min|mean|median")
sb.append("&")
// label positions
sb.append("chxp=2,").append(current.min).append(",").append(current.mean).append(",")
.append(current.median)
sb.append("&")
// label color and font
sb.append("chxs=2,D65D82,11.5,0,lt,D65D82")
sb.append("&")
// lines for min, mean, median
sb.append("chxtc=2,-1000")
sb.append("&")
// legend
appendLegend(statistics, sb, legend)
sb.append("&")
// bar spacing
sb.append("chbh=a,4,20")
sb.append("&")
// bar colors
barColors(statistics.size, sb)
sb.append("&")
val sb = new StringBuilder
sb.append(BaseUrl)
// bar chart
sb.append("cht=bvg")
sb.append("&")
// size
sb.append("chs=").append(ChartWidth).append("x").append(ChartHeight)
sb.append("&")
// title
sb.append("chtt=").append(urlEncode(title))
sb.append("&")
// axis locations
sb.append("chxt=y,x,y")
sb.append("&")
// labels
percentileLabels(current.percentiles, sb)
sb.append("|mean")
sb.append("|2:|min|mean|median")
sb.append("&")
// label positions
sb.append("chxp=2,").append(current.min).append(",").append(current.mean).append(",")
.append(current.median)
sb.append("&")
// label color and font
sb.append("chxs=2,D65D82,11.5,0,lt,D65D82")
sb.append("&")
// lines for min, mean, median
sb.append("chxtc=2,-1000")
sb.append("&")
// legend
appendLegend(statistics, sb, legend)
sb.append("&")
// bar spacing
sb.append("chbh=a,4,20")
sb.append("&")
// bar colors
barColors(statistics.size, sb)
sb.append("&")
// data series
val maxValue = statistics.map(_.percentiles.last._2).max
sb.append("chd=t:")
dataSeries(statistics.map(_.percentiles), statistics.map(_.mean), sb)
// data series
val maxValue = statistics.map(_.percentiles.last._2).max
sb.append("chd=t:")
dataSeries(statistics.map(_.percentiles), statistics.map(_.mean), sb)
// y range
sb.append("&")
sb.append("chxr=0,0,").append(maxValue).append("|2,0,").append(maxValue)
sb.append("&")
sb.append("chds=0,").append(maxValue)
sb.append("&")
// y range
sb.append("&")
sb.append("chxr=0,0,").append(maxValue).append("|2,0,").append(maxValue)
sb.append("&")
sb.append("chds=0,").append(maxValue)
sb.append("&")
// grid lines
appendGridSpacing(maxValue, sb)
// grid lines
appendGridSpacing(maxValue, sb)
return sb.toString
sb.toString
}
}
private def percentileLabels(percentiles: TreeMap[Int, Long], sb: StringBuilder) {
@ -197,108 +199,109 @@ object GoogleChartBuilder {
}
def latencyAndThroughputChartUrl(statistics: Seq[Stats], title: String): String = {
if (statistics.isEmpty) return ""
if (statistics.isEmpty) ""
else {
val sb = new StringBuilder
sb.append(BaseUrl)
// line chart
sb.append("cht=lxy")
sb.append("&")
// size
sb.append("chs=").append(ChartWidth).append("x").append(ChartHeight)
sb.append("&")
// title
sb.append("chtt=").append(urlEncode(title))
sb.append("&")
// axis locations
sb.append("chxt=x,y,r,x,y,r")
sb.append("&")
// labels
sb.append("chxl=3:|clients|4:|Latency+(us)|5:|Throughput+(tps)")
sb.append("&")
// label color and font
sb.append("chxs=0,676767,11.5,0,lt,676767|1,676767,11.5,0,lt,676767|2,676767,11.5,0,lt,676767")
sb.append("&")
sb.append("chco=")
val seriesColors = List("25B33B", "3072F3", "FF0000", "37F0ED", "FF9900")
sb.append(seriesColors.mkString(","))
sb.append("&")
// legend
sb.append("chdl=5th%20Percentile|Median|95th%20Percentile|Mean|Throughput")
sb.append("&")
val sb = new StringBuilder
sb.append(BaseUrl)
// line chart
sb.append("cht=lxy")
sb.append("&")
// size
sb.append("chs=").append(ChartWidth).append("x").append(ChartHeight)
sb.append("&")
// title
sb.append("chtt=").append(urlEncode(title))
sb.append("&")
// axis locations
sb.append("chxt=x,y,r,x,y,r")
sb.append("&")
// labels
sb.append("chxl=3:|clients|4:|Latency+(us)|5:|Throughput+(tps)")
sb.append("&")
// label color and font
sb.append("chxs=0,676767,11.5,0,lt,676767|1,676767,11.5,0,lt,676767|2,676767,11.5,0,lt,676767")
sb.append("&")
sb.append("chco=")
val seriesColors = List("25B33B", "3072F3", "FF0000", "37F0ED", "FF9900")
sb.append(seriesColors.mkString(","))
sb.append("&")
// legend
sb.append("chdl=5th%20Percentile|Median|95th%20Percentile|Mean|Throughput")
sb.append("&")
sb.append("chdlp=b")
sb.append("&")
sb.append("chdlp=b")
sb.append("&")
sb.append("chls=1|1|1")
sb.append("&")
sb.append("chls=1|1|1")
sb.append("&")
sb.append("chls=1|1|1")
sb.append("&")
sb.append("chls=1|1|1")
sb.append("&")
// margins
sb.append("chma=5,5,5,25")
sb.append("&")
// margins
sb.append("chma=5,5,5,25")
sb.append("&")
// data points
sb.append("chm=")
val chmStr = seriesColors.zipWithIndex.map(each "o," + each._1 + "," + each._2 + ",-1,7").mkString("|")
sb.append(chmStr)
sb.append("&")
// data points
sb.append("chm=")
val chmStr = seriesColors.zipWithIndex.map(each "o," + each._1 + "," + each._2 + ",-1,7").mkString("|")
sb.append(chmStr)
sb.append("&")
// data series
val loadStr = statistics.map(_.load).mkString(",")
sb.append("chd=t:")
val maxP = 95
val percentiles = List(5, 50, maxP)
val maxValue = statistics.map(_.percentiles(maxP)).max
val percentileSeries: List[String] =
for (p percentiles) yield {
loadStr + "|" + statistics.map(_.percentiles(p)).mkString(",")
}
sb.append(percentileSeries.mkString("|"))
// data series
val loadStr = statistics.map(_.load).mkString(",")
sb.append("chd=t:")
val maxP = 95
val percentiles = List(5, 50, maxP)
val maxValue = statistics.map(_.percentiles(maxP)).max
val percentileSeries: List[String] =
for (p percentiles) yield {
loadStr + "|" + statistics.map(_.percentiles(p)).mkString(",")
sb.append("|")
sb.append(loadStr).append("|")
val meanSeries = statistics.map(s formatDouble(s.mean)).mkString(",")
sb.append(meanSeries)
sb.append("|")
val maxTps: Double = statistics.map(_.tps).max
sb.append(loadStr).append("|")
val tpsSeries = statistics.map(s formatDouble(s.tps)).mkString(",")
sb.append(tpsSeries)
val minLoad = statistics.head.load
val maxLoad = statistics.last.load
// y range
sb.append("&")
sb.append("chxr=0,").append(minLoad).append(",").append(maxLoad).append(",4").append("|1,0,").append(maxValue).append("|2,0,")
.append(formatDouble(maxTps))
sb.append("&")
sb.append("chds=")
for (p percentiles) {
sb.append(minLoad).append(",").append(maxLoad)
sb.append(",0,").append(maxValue)
sb.append(",")
}
sb.append(percentileSeries.mkString("|"))
sb.append("|")
sb.append(loadStr).append("|")
val meanSeries = statistics.map(s formatDouble(s.mean)).mkString(",")
sb.append(meanSeries)
sb.append("|")
val maxTps: Double = statistics.map(_.tps).max
sb.append(loadStr).append("|")
val tpsSeries = statistics.map(s formatDouble(s.tps)).mkString(",")
sb.append(tpsSeries)
val minLoad = statistics.head.load
val maxLoad = statistics.last.load
// y range
sb.append("&")
sb.append("chxr=0,").append(minLoad).append(",").append(maxLoad).append(",4").append("|1,0,").append(maxValue).append("|2,0,")
.append(formatDouble(maxTps))
sb.append("&")
sb.append("chds=")
for (p percentiles) {
sb.append(minLoad).append(",").append(maxLoad)
sb.append(",0,").append(maxValue)
sb.append(",0,").append(formatDouble(maxValue))
sb.append(",")
sb.append(minLoad).append(",").append(maxLoad)
sb.append(",0,").append(formatDouble(maxTps))
sb.append("&")
// label positions
sb.append("chxp=3,").append("50").append("|4,").append("100").append("|5,").append("100")
sb.append("&")
// grid lines
appendGridSpacing(maxValue, sb)
sb.toString
}
sb.append(minLoad).append(",").append(maxLoad)
sb.append(",0,").append(formatDouble(maxValue))
sb.append(",")
sb.append(minLoad).append(",").append(maxLoad)
sb.append(",0,").append(formatDouble(maxTps))
sb.append("&")
// label positions
sb.append("chxp=3,").append("50").append("|4,").append("100").append("|5,").append("100")
sb.append("&")
// grid lines
appendGridSpacing(maxValue, sb)
return sb.toString
}
def formatDouble(value: Double): String = {

View file

@ -4,7 +4,7 @@ import scala.collection.immutable.TreeMap
import org.apache.commons.math.stat.descriptive.DescriptiveStatistics
import org.scalatest.BeforeAndAfterEach
import akka.testkit.AkkaSpec
import akka.util.Duration
import scala.concurrent.util.Duration
import com.typesafe.config.Config
import java.util.concurrent.TimeUnit
import akka.event.Logging

View file

@ -3,14 +3,16 @@
*/
package akka.routing
import language.postfixOps
import java.util.concurrent.atomic.AtomicInteger
import org.junit.runner.RunWith
import akka.actor.{ Props, Deploy, Actor, ActorRef }
import akka.ConfigurationException
import akka.dispatch.Await
import scala.concurrent.Await
import akka.pattern.{ ask, gracefulStop }
import akka.testkit.{ TestLatch, ImplicitSender, DefaultTimeout, AkkaSpec }
import akka.util.duration.intToDurationInt
import scala.concurrent.util.duration.intToDurationInt
import akka.actor.UnstartedCell
object ConfiguredLocalRoutingSpec {

View file

@ -3,15 +3,17 @@
*/
package akka.routing
import language.postfixOps
import akka.actor.Actor
import akka.testkit._
import akka.actor.Props
import akka.dispatch.Await
import akka.util.duration._
import scala.concurrent.Await
import scala.concurrent.util.duration._
import akka.actor.ActorRef
import java.util.concurrent.atomic.AtomicInteger
import akka.pattern.ask
import akka.util.Duration
import scala.concurrent.util.Duration
import java.util.concurrent.TimeoutException
object ResizerSpec {
@ -172,7 +174,7 @@ class ResizerSpec extends AkkaSpec(ResizerSpec.config) with DefaultTimeout with
val router = system.actorOf(Props(new Actor {
def receive = {
case d: Duration d.dilated.sleep; sender ! "done"
case d: Duration Thread.sleep(d.dilated.toMillis); sender ! "done"
case "echo" sender ! "reply"
}
}).withRouter(RoundRobinRouter(resizer = Some(resizer))))
@ -219,26 +221,25 @@ class ResizerSpec extends AkkaSpec(ResizerSpec.config) with DefaultTimeout with
val router = system.actorOf(Props(new Actor {
def receive = {
case n: Int
(n millis).dilated.sleep
case n: Int Thread.sleep((n millis).dilated.toMillis)
}
}).withRouter(RoundRobinRouter(resizer = Some(resizer))))
// put some pressure on the router
for (m 0 to 5) {
router ! 100
(5 millis).dilated.sleep
Thread.sleep((5 millis).dilated.toMillis)
}
val z = Await.result(router ? CurrentRoutees, 5 seconds).asInstanceOf[RouterRoutees].routees.size
z must be >= (2)
(300 millis).dilated.sleep
Thread.sleep((300 millis).dilated.toMillis)
// let it cool down
for (m 0 to 5) {
router ! 1
(500 millis).dilated.sleep
Thread.sleep((500 millis).dilated.toMillis)
}
awaitCond(

View file

@ -3,13 +3,15 @@
*/
package akka.routing
import language.postfixOps
import java.util.concurrent.atomic.AtomicInteger
import akka.actor._
import scala.collection.mutable.LinkedList
import akka.testkit._
import akka.util.duration._
import akka.dispatch.Await
import akka.util.Duration
import scala.concurrent.util.duration._
import scala.concurrent.Await
import scala.concurrent.util.Duration
import akka.ConfigurationException
import com.typesafe.config.ConfigFactory
import akka.pattern.{ ask, pipe }
@ -65,7 +67,7 @@ object RoutingSpec {
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
class RoutingSpec extends AkkaSpec(RoutingSpec.config) with DefaultTimeout with ImplicitSender {
implicit val ec = system.dispatcher
import akka.routing.RoutingSpec._
"routers in general" must {

View file

@ -4,12 +4,14 @@
package akka.serialization
import language.postfixOps
import akka.testkit.{ AkkaSpec, EventFilter }
import akka.actor._
import java.io._
import akka.dispatch.Await
import scala.concurrent.Await
import akka.util.Timeout
import akka.util.duration._
import scala.concurrent.util.duration._
import scala.reflect.BeanInfo
import com.google.protobuf.Message
import akka.pattern.ask

View file

@ -3,9 +3,13 @@
*/
package akka.util
import language.postfixOps
import org.scalatest.WordSpec
import org.scalatest.matchers.MustMatchers
import duration._
import scala.concurrent.util.Duration
import scala.concurrent.util.duration._
import java.util.concurrent.TimeUnit._
class DurationSpec extends WordSpec with MustMatchers {
@ -13,8 +17,8 @@ class DurationSpec extends WordSpec with MustMatchers {
"Duration" must {
"form a one-dimensional vector field" in {
val zero = 0.seconds
val one = 1.second
val zero = 0 seconds
val one = 1 second
val two = one + one
val three = 3 * one
(0 * one) must be(zero)
@ -51,7 +55,7 @@ class DurationSpec extends WordSpec with MustMatchers {
assert(minf != one)
}
"check its range" in {
/*"check its range" in {
for (unit Seq(DAYS, HOURS, MINUTES, SECONDS, MILLISECONDS, MICROSECONDS, NANOSECONDS)) {
val x = unit.convert(Long.MaxValue, NANOSECONDS)
val dur = Duration(x, unit)
@ -78,7 +82,7 @@ class DurationSpec extends WordSpec with MustMatchers {
intercept[IllegalArgumentException] { Duration("%.0f".format(x + 10000000d) + unit.toString.toLowerCase) }
intercept[IllegalArgumentException] { Duration("-%.0f".format(x + 10000000d) + unit.toString.toLowerCase) }
}
}
}*/
"support fromNow" in {
val dead = 2.seconds.fromNow
@ -86,7 +90,7 @@ class DurationSpec extends WordSpec with MustMatchers {
// view bounds vs. very local type inference vs. operator precedence: sigh
dead.timeLeft must be > (1 second: Duration)
dead2.timeLeft must be > (1 second: Duration)
1.second.sleep
Thread.sleep(1.second.toMillis)
dead.timeLeft must be < (1 second: Duration)
dead2.timeLeft must be < (1 second: Duration)
}

View file

@ -4,13 +4,14 @@
package akka.util
import org.scalatest.matchers.MustMatchers
import akka.dispatch.{ Future, Await }
import scala.concurrent.Future
import akka.testkit.AkkaSpec
import scala.concurrent.Await
import scala.util.Random
import akka.testkit.DefaultTimeout
class IndexSpec extends AkkaSpec with MustMatchers with DefaultTimeout {
implicit val ec = system.dispatcher
private def emptyIndex = new Index[String, Int](100, _ compareTo _)
private def indexWithValues = {

View file

@ -1,7 +1,7 @@
/**
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.util
/*package akka.util
import org.scalatest.matchers.MustMatchers
import akka.testkit.AkkaSpec
@ -59,4 +59,4 @@ class NonFatalSpec extends AkkaSpec with MustMatchers {
}
}
}*/

View file

@ -2,8 +2,9 @@
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.actor;
package akka.actor.cell;
import akka.actor.ActorCell;
import akka.util.Unsafe;
final class AbstractActorCell {
@ -13,9 +14,9 @@ final class AbstractActorCell {
static {
try {
mailboxOffset = Unsafe.instance.objectFieldOffset(ActorCell.class.getDeclaredField("_mailboxDoNotCallMeDirectly"));
childrenOffset = Unsafe.instance.objectFieldOffset(ActorCell.class.getDeclaredField("_childrenRefsDoNotCallMeDirectly"));
nextNameOffset = Unsafe.instance.objectFieldOffset(ActorCell.class.getDeclaredField("_nextNameDoNotCallMeDirectly"));
mailboxOffset = Unsafe.instance.objectFieldOffset(ActorCell.class.getDeclaredField("akka$actor$cell$Dispatch$$_mailboxDoNotCallMeDirectly"));
childrenOffset = Unsafe.instance.objectFieldOffset(ActorCell.class.getDeclaredField("akka$actor$cell$Children$$_childrenRefsDoNotCallMeDirectly"));
nextNameOffset = Unsafe.instance.objectFieldOffset(ActorCell.class.getDeclaredField("akka$actor$cell$Children$$_nextNameDoNotCallMeDirectly"));
} catch(Throwable t){
throw new ExceptionInInitializerError(t);
}

View file

@ -1,29 +0,0 @@
/**
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.dispatch;
import akka.util.Unsafe;
abstract class AbstractPromise {
private volatile Object _ref = DefaultPromise.EmptyPending();
final static long _refOffset; // Memory offset to _ref field
static {
try {
_refOffset = Unsafe.instance.objectFieldOffset(AbstractPromise.class.getDeclaredField("_ref"));
} catch(Throwable t){
throw new ExceptionInInitializerError(t);
}
}
protected final boolean updateState(Object oldState, Object newState) {
return Unsafe.instance.compareAndSwapObject(this, _refOffset, oldState, newState);
}
protected final Object getState() {
return _ref;
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,119 +0,0 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
* http://creativecommons.org/publicdomain/zero/1.0/
*/
package akka.jsr166y;
/**
* A thread managed by a {@link ForkJoinPool}, which executes
* {@link ForkJoinTask}s.
* This class is subclassable solely for the sake of adding
* functionality -- there are no overridable methods dealing with
* scheduling or execution. However, you can override initialization
* and termination methods surrounding the main task processing loop.
* If you do create such a subclass, you will also need to supply a
* custom {@link ForkJoinPool.ForkJoinWorkerThreadFactory} to use it
* in a {@code ForkJoinPool}.
*
* @since 1.7
* @author Doug Lea
*/
public class ForkJoinWorkerThread extends Thread {
/*
* ForkJoinWorkerThreads are managed by ForkJoinPools and perform
* ForkJoinTasks. For explanation, see the internal documentation
* of class ForkJoinPool.
*/
final ForkJoinPool.WorkQueue workQueue; // Work-stealing mechanics
final ForkJoinPool pool; // the pool this thread works in
/**
* Creates a ForkJoinWorkerThread operating in the given pool.
*
* @param pool the pool this thread works in
* @throws NullPointerException if pool is null
*/
protected ForkJoinWorkerThread(ForkJoinPool pool) {
super(pool.nextWorkerName());
setDaemon(true);
Thread.UncaughtExceptionHandler ueh = pool.ueh;
if (ueh != null)
setUncaughtExceptionHandler(ueh);
this.pool = pool;
pool.registerWorker(this.workQueue = new ForkJoinPool.WorkQueue
(pool, this, pool.localMode));
}
/**
* Returns the pool hosting this thread.
*
* @return the pool
*/
public ForkJoinPool getPool() {
return pool;
}
/**
* Returns the index number of this thread in its pool. The
* returned value ranges from zero to the maximum number of
* threads (minus one) that have ever been created in the pool.
* This method may be useful for applications that track status or
* collect results per-worker rather than per-task.
*
* @return the index number
*/
public int getPoolIndex() {
return workQueue.poolIndex;
}
/**
* Initializes internal state after construction but before
* processing any tasks. If you override this method, you must
* invoke {@code super.onStart()} at the beginning of the method.
* Initialization requires care: Most fields must have legal
* default values, to ensure that attempted accesses from other
* threads work correctly even before this thread starts
* processing tasks.
*/
protected void onStart() {
}
/**
* Performs cleanup associated with termination of this worker
* thread. If you override this method, you must invoke
* {@code super.onTermination} at the end of the overridden method.
*
* @param exception the exception causing this thread to abort due
* to an unrecoverable error, or {@code null} if completed normally
*/
protected void onTermination(Throwable exception) {
}
/**
* This method is required to be public, but should never be
* called explicitly. It performs the main run loop to execute
* {@link ForkJoinTask}s.
*/
public void run() {
Throwable exception = null;
try {
onStart();
pool.runWorker(workQueue);
} catch (Throwable ex) {
exception = ex;
} finally {
try {
onTermination(exception);
} catch (Throwable ex) {
if (exception == null)
exception = ex;
} finally {
pool.deregisterWorker(this, exception);
}
}
}
}

View file

@ -1,164 +0,0 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
* http://creativecommons.org/publicdomain/zero/1.0/
*/
package akka.jsr166y;
/**
* A recursive resultless {@link ForkJoinTask}. This class
* establishes conventions to parameterize resultless actions as
* {@code Void} {@code ForkJoinTask}s. Because {@code null} is the
* only valid value of type {@code Void}, methods such as {@code join}
* always return {@code null} upon completion.
*
* <p><b>Sample Usages.</b> Here is a simple but complete ForkJoin
* sort that sorts a given {@code long[]} array:
*
* <pre> {@code
* static class SortTask extends RecursiveAction {
* final long[] array; final int lo, hi;
* SortTask(long[] array, int lo, int hi) {
* this.array = array; this.lo = lo; this.hi = hi;
* }
* SortTask(long[] array) { this(array, 0, array.length); }
* protected void compute() {
* if (hi - lo < THRESHOLD)
* sortSequentially(lo, hi);
* else {
* int mid = (lo + hi) >>> 1;
* invokeAll(new SortTask(array, lo, mid),
* new SortTask(array, mid, hi));
* merge(lo, mid, hi);
* }
* }
* // implementation details follow:
* final static int THRESHOLD = 1000;
* void sortSequentially(int lo, int hi) {
* Arrays.sort(array, lo, hi);
* }
* void merge(int lo, int mid, int hi) {
* long[] buf = Arrays.copyOfRange(array, lo, mid);
* for (int i = 0, j = lo, k = mid; i < buf.length; j++)
* array[j] = (k == hi || buf[i] < array[k]) ?
* buf[i++] : array[k++];
* }
* }}</pre>
*
* You could then sort {@code anArray} by creating {@code new
* SortTask(anArray)} and invoking it in a ForkJoinPool. As a more
* concrete simple example, the following task increments each element
* of an array:
* <pre> {@code
* class IncrementTask extends RecursiveAction {
* final long[] array; final int lo, hi;
* IncrementTask(long[] array, int lo, int hi) {
* this.array = array; this.lo = lo; this.hi = hi;
* }
* protected void compute() {
* if (hi - lo < THRESHOLD) {
* for (int i = lo; i < hi; ++i)
* array[i]++;
* }
* else {
* int mid = (lo + hi) >>> 1;
* invokeAll(new IncrementTask(array, lo, mid),
* new IncrementTask(array, mid, hi));
* }
* }
* }}</pre>
*
* <p>The following example illustrates some refinements and idioms
* that may lead to better performance: RecursiveActions need not be
* fully recursive, so long as they maintain the basic
* divide-and-conquer approach. Here is a class that sums the squares
* of each element of a double array, by subdividing out only the
* right-hand-sides of repeated divisions by two, and keeping track of
* them with a chain of {@code next} references. It uses a dynamic
* threshold based on method {@code getSurplusQueuedTaskCount}, but
* counterbalances potential excess partitioning by directly
* performing leaf actions on unstolen tasks rather than further
* subdividing.
*
* <pre> {@code
* double sumOfSquares(ForkJoinPool pool, double[] array) {
* int n = array.length;
* Applyer a = new Applyer(array, 0, n, null);
* pool.invoke(a);
* return a.result;
* }
*
* class Applyer extends RecursiveAction {
* final double[] array;
* final int lo, hi;
* double result;
* Applyer next; // keeps track of right-hand-side tasks
* Applyer(double[] array, int lo, int hi, Applyer next) {
* this.array = array; this.lo = lo; this.hi = hi;
* this.next = next;
* }
*
* double atLeaf(int l, int h) {
* double sum = 0;
* for (int i = l; i < h; ++i) // perform leftmost base step
* sum += array[i] * array[i];
* return sum;
* }
*
* protected void compute() {
* int l = lo;
* int h = hi;
* Applyer right = null;
* while (h - l > 1 && getSurplusQueuedTaskCount() <= 3) {
* int mid = (l + h) >>> 1;
* right = new Applyer(array, mid, h, right);
* right.fork();
* h = mid;
* }
* double sum = atLeaf(l, h);
* while (right != null) {
* if (right.tryUnfork()) // directly calculate if not stolen
* sum += right.atLeaf(right.lo, right.hi);
* else {
* right.join();
* sum += right.result;
* }
* right = right.next;
* }
* result = sum;
* }
* }}</pre>
*
* @since 1.7
* @author Doug Lea
*/
public abstract class RecursiveAction extends ForkJoinTask<Void> {
private static final long serialVersionUID = 5232453952276485070L;
/**
* The main computation performed by this task.
*/
protected abstract void compute();
/**
* Always returns {@code null}.
*
* @return {@code null} always
*/
public final Void getRawResult() { return null; }
/**
* Requires null completion value.
*/
protected final void setRawResult(Void mustBeNull) { }
/**
* Implements execution conventions for RecursiveActions.
*/
protected final boolean exec() {
compute();
return true;
}
}

View file

@ -1,68 +0,0 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
* http://creativecommons.org/publicdomain/zero/1.0/
*/
package akka.jsr166y;
/**
* A recursive result-bearing {@link ForkJoinTask}.
*
* <p>For a classic example, here is a task computing Fibonacci numbers:
*
* <pre> {@code
* class Fibonacci extends RecursiveTask<Integer> {
* final int n;
* Fibonacci(int n) { this.n = n; }
* Integer compute() {
* if (n <= 1)
* return n;
* Fibonacci f1 = new Fibonacci(n - 1);
* f1.fork();
* Fibonacci f2 = new Fibonacci(n - 2);
* return f2.compute() + f1.join();
* }
* }}</pre>
*
* However, besides being a dumb way to compute Fibonacci functions
* (there is a simple fast linear algorithm that you'd use in
* practice), this is likely to perform poorly because the smallest
* subtasks are too small to be worthwhile splitting up. Instead, as
* is the case for nearly all fork/join applications, you'd pick some
* minimum granularity size (for example 10 here) for which you always
* sequentially solve rather than subdividing.
*
* @since 1.7
* @author Doug Lea
*/
public abstract class RecursiveTask<V> extends ForkJoinTask<V> {
private static final long serialVersionUID = 5232453952276485270L;
/**
* The result of the computation.
*/
V result;
/**
* The main computation performed by this task.
*/
protected abstract V compute();
public final V getRawResult() {
return result;
}
protected final void setRawResult(V value) {
result = value;
}
/**
* Implements execution conventions for RecursiveTask.
*/
protected final boolean exec() {
result = compute();
return true;
}
}

View file

@ -1,197 +0,0 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
* http://creativecommons.org/publicdomain/zero/1.0/
*/
package akka.jsr166y;
import java.util.Random;
/**
* A random number generator isolated to the current thread. Like the
* global {@link java.util.Random} generator used by the {@link
* java.lang.Math} class, a {@code ThreadLocalRandom} is initialized
* with an internally generated seed that may not otherwise be
* modified. When applicable, use of {@code ThreadLocalRandom} rather
* than shared {@code Random} objects in concurrent programs will
* typically encounter much less overhead and contention. Use of
* {@code ThreadLocalRandom} is particularly appropriate when multiple
* tasks (for example, each a {@link ForkJoinTask}) use random numbers
* in parallel in thread pools.
*
* <p>Usages of this class should typically be of the form:
* {@code ThreadLocalRandom.current().nextX(...)} (where
* {@code X} is {@code Int}, {@code Long}, etc).
* When all usages are of this form, it is never possible to
* accidently share a {@code ThreadLocalRandom} across multiple threads.
*
* <p>This class also provides additional commonly used bounded random
* generation methods.
*
* @since 1.7
* @author Doug Lea
*/
public class ThreadLocalRandom extends Random {
// same constants as Random, but must be redeclared because private
private static final long multiplier = 0x5DEECE66DL;
private static final long addend = 0xBL;
private static final long mask = (1L << 48) - 1;
/**
* The random seed. We can't use super.seed.
*/
private long rnd;
/**
* Initialization flag to permit calls to setSeed to succeed only
* while executing the Random constructor. We can't allow others
* since it would cause setting seed in one part of a program to
* unintentionally impact other usages by the thread.
*/
boolean initialized;
// Padding to help avoid memory contention among seed updates in
// different TLRs in the common case that they are located near
// each other.
private long pad0, pad1, pad2, pad3, pad4, pad5, pad6, pad7;
/**
* The actual ThreadLocal
*/
private static final ThreadLocal<ThreadLocalRandom> localRandom =
new ThreadLocal<ThreadLocalRandom>() {
protected ThreadLocalRandom initialValue() {
return new ThreadLocalRandom();
}
};
/**
* Constructor called only by localRandom.initialValue.
*/
ThreadLocalRandom() {
super();
initialized = true;
}
/**
* Returns the current thread's {@code ThreadLocalRandom}.
*
* @return the current thread's {@code ThreadLocalRandom}
*/
public static ThreadLocalRandom current() {
return localRandom.get();
}
/**
* Throws {@code UnsupportedOperationException}. Setting seeds in
* this generator is not supported.
*
* @throws UnsupportedOperationException always
*/
public void setSeed(long seed) {
if (initialized)
throw new UnsupportedOperationException();
rnd = (seed ^ multiplier) & mask;
}
protected int next(int bits) {
rnd = (rnd * multiplier + addend) & mask;
return (int) (rnd >>> (48-bits));
}
/**
* Returns a pseudorandom, uniformly distributed value between the
* given least value (inclusive) and bound (exclusive).
*
* @param least the least value returned
* @param bound the upper bound (exclusive)
* @throws IllegalArgumentException if least greater than or equal
* to bound
* @return the next value
*/
public int nextInt(int least, int bound) {
if (least >= bound)
throw new IllegalArgumentException();
return nextInt(bound - least) + least;
}
/**
* Returns a pseudorandom, uniformly distributed value
* between 0 (inclusive) and the specified value (exclusive).
*
* @param n the bound on the random number to be returned. Must be
* positive.
* @return the next value
* @throws IllegalArgumentException if n is not positive
*/
public long nextLong(long n) {
if (n <= 0)
throw new IllegalArgumentException("n must be positive");
// Divide n by two until small enough for nextInt. On each
// iteration (at most 31 of them but usually much less),
// randomly choose both whether to include high bit in result
// (offset) and whether to continue with the lower vs upper
// half (which makes a difference only if odd).
long offset = 0;
while (n >= Integer.MAX_VALUE) {
int bits = next(2);
long half = n >>> 1;
long nextn = ((bits & 2) == 0) ? half : n - half;
if ((bits & 1) == 0)
offset += n - nextn;
n = nextn;
}
return offset + nextInt((int) n);
}
/**
* Returns a pseudorandom, uniformly distributed value between the
* given least value (inclusive) and bound (exclusive).
*
* @param least the least value returned
* @param bound the upper bound (exclusive)
* @return the next value
* @throws IllegalArgumentException if least greater than or equal
* to bound
*/
public long nextLong(long least, long bound) {
if (least >= bound)
throw new IllegalArgumentException();
return nextLong(bound - least) + least;
}
/**
* Returns a pseudorandom, uniformly distributed {@code double} value
* between 0 (inclusive) and the specified value (exclusive).
*
* @param n the bound on the random number to be returned. Must be
* positive.
* @return the next value
* @throws IllegalArgumentException if n is not positive
*/
public double nextDouble(double n) {
if (n <= 0)
throw new IllegalArgumentException("n must be positive");
return nextDouble() * n;
}
/**
* Returns a pseudorandom, uniformly distributed value between the
* given least value (inclusive) and bound (exclusive).
*
* @param least the least value returned
* @param bound the upper bound (exclusive)
* @return the next value
* @throws IllegalArgumentException if least greater than or equal
* to bound
*/
public double nextDouble(double least, double bound) {
if (least >= bound)
throw new IllegalArgumentException();
return nextDouble() * (bound - least) + least;
}
private static final long serialVersionUID = -5851777807851030925L;
}

View file

@ -16,7 +16,7 @@
package akka.util.internal;
import akka.event.LoggingAdapter;
import akka.util.Duration;
import scala.concurrent.util.Duration;
import java.util.*;
import java.util.concurrent.ThreadFactory;

View file

@ -1,89 +0,0 @@
/*
* Copyright 2009 Red Hat, Inc.
*
* Red Hat licenses this file to you under the Apache License, version 2.0
* (the "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package akka.util.internal;
import java.util.regex.Pattern;
/**
* Accesses the system property swallowing a {@link SecurityException}.
*
* @author <a href="http://www.jboss.org/netty/">The Netty Project</a>
* @author <a href="http://gleamynode.net/">Trustin Lee</a>
*
* @version $Rev: 2161 $, $Date: 2010-02-18 11:12:15 +0900 (Thu, 18 Feb 2010) $
*
*/
public class SystemPropertyUtil {
/**
* Returns the value of the Java system property with the specified
* {@code key}.
*
* @return the property value.
* {@code null} if there's no such property or if an access to the
* specified property is not allowed.
*/
public static String get(String key) {
try {
return System.getProperty(key);
} catch (Exception e) {
return null;
}
}
/**
* Returns the value of the Java system property with the specified
* {@code key}, while falling back to the specified default value if
* the property access fails.
*
* @return the property value.
* {@code def} if there's no such property or if an access to the
* specified property is not allowed.
*/
public static String get(String key, String def) {
String value = get(key);
if (value == null) {
value = def;
}
return value;
}
/**
* Returns the value of the Java system property with the specified
* {@code key}, while falling back to the specified default value if
* the property access fails.
*
* @return the property value.
* {@code def} if there's no such property or if an access to the
* specified property is not allowed.
*/
public static int get(String key, int def) {
String value = get(key);
if (value == null) {
return def;
}
if (Pattern.matches("-?[0-9]+", value)) {
return Integer.parseInt(value);
} else {
return def;
}
}
private SystemPropertyUtil() {
// Unused
}
}

View file

@ -15,7 +15,7 @@
*/
package akka.util.internal;
import akka.util.Duration;
import scala.concurrent.util.Duration;
import java.util.Set;
import java.util.concurrent.TimeUnit;

View file

@ -122,12 +122,36 @@ class InvalidActorNameException(message: String) extends AkkaException(message)
/**
* An ActorInitializationException is thrown when the the initialization logic for an Actor fails.
*/
class ActorInitializationException private[akka] (actor: ActorRef, message: String, cause: Throwable)
extends AkkaException(message, cause) /*with NoStackTrace*/ {
class ActorInitializationException private[akka] (val actor: ActorRef, message: String, cause: Throwable)
extends AkkaException(message, cause) {
def this(msg: String) = this(null, msg, null)
def this(actor: ActorRef, msg: String) = this(actor, msg, null)
}
/**
* A PreRestartException is thrown when the preRestart() method failed.
*
* @param actor is the actor whose preRestart() hook failed
* @param cause is the exception thrown by that actor within preRestart()
* @param origCause is the exception which caused the restart in the first place
* @param msg is the message which was optionally passed into preRestart()
*/
class PreRestartException private[akka] (actor: ActorRef, cause: Throwable, val origCause: Throwable, val msg: Option[Any])
extends ActorInitializationException(actor, "exception in preRestart(" + origCause.getClass + ", " + msg.map(_.getClass) + ")", cause) {
}
/**
* A PostRestartException is thrown when constructor or postRestart() method
* fails during a restart attempt.
*
* @param actor is the actor whose constructor or postRestart() hook failed
* @param cause is the exception thrown by that actor within preRestart()
* @param origCause is the exception which caused the restart in the first place
*/
class PostRestartException private[akka] (actor: ActorRef, cause: Throwable, val origCause: Throwable)
extends ActorInitializationException(actor, "exception post restart (" + origCause.getClass + ")", cause) {
}
/**
* InvalidMessageException is thrown when an invalid message is sent to an Actor.
* Technically it's only "null" which is an InvalidMessageException but who knows,

View file

@ -4,20 +4,17 @@
package akka.actor
import akka.dispatch._
import scala.annotation.tailrec
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeUnit.MILLISECONDS
import akka.event.Logging.{ Debug, Warning, Error }
import akka.japi.Procedure
import java.io.{ NotSerializableException, ObjectOutputStream }
import akka.serialization.SerializationExtension
import akka.event.Logging.LogEventException
import collection.immutable.{ TreeSet, TreeMap }
import akka.util.{ Unsafe, Duration, Helpers, NonFatal }
import java.util.concurrent.atomic.AtomicLong
import java.io.{ ObjectOutputStream, NotSerializableException }
//TODO: everything here for current compatibility - could be limited more
import scala.annotation.tailrec
import scala.collection.immutable.TreeSet
import scala.concurrent.util.Duration
import scala.util.control.NonFatal
import akka.actor.cell.ChildrenContainer
import akka.dispatch.{ Watch, Unwatch, Terminate, SystemMessage, Suspend, Supervise, Resume, Recreate, NoMessage, MessageDispatcher, Envelope, Create, ChildTerminated }
import akka.event.Logging.{ LogEvent, Debug }
import akka.japi.Procedure
/**
* The actor context - the view of the actor cell from the actor.
@ -93,9 +90,9 @@ trait ActorContext extends ActorRefFactory {
def sender: ActorRef
/**
* Returns all supervised children; this method returns a view onto the
* internal collection of children. Targeted lookups should be using
* `actorFor` instead for performance reasons:
* Returns all supervised children; this method returns a view (i.e. a lazy
* collection) onto the internal collection of children. Targeted lookups
* should be using `actorFor` instead for performance reasons:
*
* {{{
* val badLookup = context.children find (_.path.name == "kid")
@ -191,7 +188,7 @@ private[akka] trait Cell {
/**
* Recursively resume this actor and all its children.
*/
def resume(): Unit
def resume(inResponseToFailure: Boolean): Unit
/**
* Restart this actor (will recursively restart or stop all children).
*/
@ -212,7 +209,11 @@ private[akka] trait Cell {
/**
* All children of this actor, including only reserved-names.
*/
def childrenRefs: ActorCell.ChildrenContainer
def childrenRefs: ChildrenContainer
/**
* Get the stats for the named child, if that exists.
*/
def getChildByName(name: String): Option[ChildRestartStats]
/**
* Enqueue a message to be sent to the actor; may or may not actually
* schedule the actor to run, depending on which type of cell it is.
@ -255,436 +256,102 @@ private[akka] object ActorCell {
def cancel() {}
}
final val emptyReceiveTimeoutData: (Duration, Cancellable) = (Duration.Undefined, emptyCancellable)
final val emptyBehaviorStack: List[Actor.Receive] = Nil
final val emptyActorRefSet: Set[ActorRef] = TreeSet.empty
sealed trait SuspendReason
case object UserRequest extends SuspendReason
case class Recreation(cause: Throwable) extends SuspendReason
case object Termination extends SuspendReason
trait ChildrenContainer {
def add(child: ActorRef): ChildrenContainer
def remove(child: ActorRef): ChildrenContainer
def getByName(name: String): Option[ChildRestartStats]
def getByRef(actor: ActorRef): Option[ChildRestartStats]
def children: Iterable[ActorRef]
def stats: Iterable[ChildRestartStats]
def shallDie(actor: ActorRef): ChildrenContainer
/**
* reserve that name or throw an exception
*/
def reserve(name: String): ChildrenContainer
/**
* cancel a reservation
*/
def unreserve(name: String): ChildrenContainer
}
trait EmptyChildrenContainer extends ChildrenContainer {
val emptyStats = TreeMap.empty[String, ChildStats]
def add(child: ActorRef): ChildrenContainer =
new NormalChildrenContainer(emptyStats.updated(child.path.name, ChildRestartStats(child)))
def remove(child: ActorRef): ChildrenContainer = this
def getByName(name: String): Option[ChildRestartStats] = None
def getByRef(actor: ActorRef): Option[ChildRestartStats] = None
def children: Iterable[ActorRef] = Nil
def stats: Iterable[ChildRestartStats] = Nil
def shallDie(actor: ActorRef): ChildrenContainer = this
def reserve(name: String): ChildrenContainer = new NormalChildrenContainer(emptyStats.updated(name, ChildNameReserved))
def unreserve(name: String): ChildrenContainer = this
override def toString = "no children"
}
/**
* This is the empty container, shared among all leaf actors.
*/
object EmptyChildrenContainer extends EmptyChildrenContainer
/**
* This is the empty container which is installed after the last child has
* terminated while stopping; it is necessary to distinguish from the normal
* empty state while calling handleChildTerminated() for the last time.
*/
object TerminatedChildrenContainer extends EmptyChildrenContainer {
override def add(child: ActorRef): ChildrenContainer = this
override def reserve(name: String): ChildrenContainer =
throw new IllegalStateException("cannot reserve actor name '" + name + "': already terminated")
}
/**
* Normal children container: we do have at least one child, but none of our
* children are currently terminating (which is the time period between
* calling context.stop(child) and processing the ChildTerminated() system
* message).
*/
class NormalChildrenContainer(c: TreeMap[String, ChildStats]) extends ChildrenContainer {
def add(child: ActorRef): ChildrenContainer =
new NormalChildrenContainer(c.updated(child.path.name, ChildRestartStats(child)))
def remove(child: ActorRef): ChildrenContainer = NormalChildrenContainer(c - child.path.name)
def getByName(name: String): Option[ChildRestartStats] = c.get(name) match {
case s @ Some(_: ChildRestartStats) s.asInstanceOf[Option[ChildRestartStats]]
case _ None
}
def getByRef(actor: ActorRef): Option[ChildRestartStats] = c.get(actor.path.name) match {
case c @ Some(crs: ChildRestartStats) if (crs.child == actor) c.asInstanceOf[Option[ChildRestartStats]]
case _ None
}
def children: Iterable[ActorRef] = c.values.view.collect { case ChildRestartStats(child, _, _) child }
def stats: Iterable[ChildRestartStats] = c.values.collect { case c: ChildRestartStats c }
def shallDie(actor: ActorRef): ChildrenContainer = TerminatingChildrenContainer(c, Set(actor), UserRequest)
def reserve(name: String): ChildrenContainer =
if (c contains name)
throw new InvalidActorNameException("actor name " + name + " is not unique!")
else new NormalChildrenContainer(c.updated(name, ChildNameReserved))
def unreserve(name: String): ChildrenContainer = c.get(name) match {
case Some(ChildNameReserved) NormalChildrenContainer(c - name)
case _ this
}
override def toString =
if (c.size > 20) c.size + " children"
else c.mkString("children:\n ", "\n ", "")
}
object NormalChildrenContainer {
def apply(c: TreeMap[String, ChildStats]): ChildrenContainer =
if (c.isEmpty) EmptyChildrenContainer
else new NormalChildrenContainer(c)
}
/**
* Waiting state: there are outstanding termination requests (i.e. context.stop(child)
* was called but the corresponding ChildTerminated() system message has not yet been
* processed). There could be no specific reason (UserRequested), we could be Restarting
* or Terminating.
*
* Removing the last child which was supposed to be terminating will return a different
* type of container, depending on whether or not children are left and whether or not
* the reason was Terminating.
*/
case class TerminatingChildrenContainer(c: TreeMap[String, ChildStats], toDie: Set[ActorRef], reason: SuspendReason)
extends ChildrenContainer {
def add(child: ActorRef): ChildrenContainer = copy(c.updated(child.path.name, ChildRestartStats(child)))
def remove(child: ActorRef): ChildrenContainer = {
val t = toDie - child
if (t.isEmpty) reason match {
case Termination TerminatedChildrenContainer
case _ NormalChildrenContainer(c - child.path.name)
}
else copy(c - child.path.name, t)
}
def getByName(name: String): Option[ChildRestartStats] = c.get(name) match {
case s @ Some(_: ChildRestartStats) s.asInstanceOf[Option[ChildRestartStats]]
case _ None
}
def getByRef(actor: ActorRef): Option[ChildRestartStats] = c.get(actor.path.name) match {
case c @ Some(crs: ChildRestartStats) if (crs.child == actor) c.asInstanceOf[Option[ChildRestartStats]]
case _ None
}
def children: Iterable[ActorRef] = c.values.view.collect { case ChildRestartStats(child, _, _) child }
def stats: Iterable[ChildRestartStats] = c.values.collect { case c: ChildRestartStats c }
def shallDie(actor: ActorRef): ChildrenContainer = copy(toDie = toDie + actor)
def reserve(name: String): ChildrenContainer = reason match {
case Termination throw new IllegalStateException("cannot reserve actor name '" + name + "': terminating")
case _
if (c contains name)
throw new InvalidActorNameException("actor name " + name + " is not unique!")
else copy(c = c.updated(name, ChildNameReserved))
}
def unreserve(name: String): ChildrenContainer = c.get(name) match {
case Some(ChildNameReserved) copy(c = c - name)
case _ this
}
override def toString =
if (c.size > 20) c.size + " children"
else c.mkString("children (" + toDie.size + " terminating):\n ", "\n ", "\n") + toDie
}
}
//ACTORCELL IS 64bytes and should stay that way unless very good reason not to (machine sympathy, cache line fit)
//vars don't need volatile since it's protected with the mailbox status
//Make sure that they are not read/written outside of a message processing (systemInvoke/invoke)
/**
* Everything in here is completely Akka PRIVATE. You will not find any
* supported APIs in this place. This is not the API you were looking
* for! (waves hand)
*/
private[akka] class ActorCell(
val system: ActorSystemImpl,
val self: InternalActorRef,
val props: Props,
@volatile var parent: InternalActorRef) extends UntypedActorContext with Cell {
val parent: InternalActorRef)
extends UntypedActorContext with Cell
with cell.ReceiveTimeout
with cell.Children
with cell.Dispatch
with cell.DeathWatch
with cell.FaultHandling {
import AbstractActorCell.{ mailboxOffset, childrenOffset, nextNameOffset }
import ActorCell._
final def isLocal = true
final def systemImpl = system
protected final def guardian = self
protected final def lookupRoot = self
final def provider = system.provider
override final def receiveTimeout: Option[Duration] = receiveTimeoutData._1 match {
case Duration.Undefined None
case duration Some(duration)
}
final def setReceiveTimeout(timeout: Option[Duration]): Unit = setReceiveTimeout(timeout.getOrElse(Duration.Undefined))
override final def setReceiveTimeout(timeout: Duration): Unit =
receiveTimeoutData = (
if (Duration.Undefined == timeout || timeout.toMillis < 1) Duration.Undefined else timeout,
receiveTimeoutData._2)
final override def resetReceiveTimeout(): Unit = setReceiveTimeout(None)
/**
* In milliseconds
*/
var receiveTimeoutData: (Duration, Cancellable) = emptyReceiveTimeoutData
@volatile
private var _childrenRefsDoNotCallMeDirectly: ChildrenContainer = EmptyChildrenContainer
def childrenRefs: ChildrenContainer = Unsafe.instance.getObjectVolatile(this, childrenOffset).asInstanceOf[ChildrenContainer]
private def swapChildrenRefs(oldChildren: ChildrenContainer, newChildren: ChildrenContainer): Boolean =
Unsafe.instance.compareAndSwapObject(this, childrenOffset, oldChildren, newChildren)
@tailrec private def reserveChild(name: String): Boolean = {
val c = childrenRefs
swapChildrenRefs(c, c.reserve(name)) || reserveChild(name)
}
@tailrec private def unreserveChild(name: String): Boolean = {
val c = childrenRefs
swapChildrenRefs(c, c.unreserve(name)) || unreserveChild(name)
}
@tailrec private def addChild(ref: ActorRef): Boolean = {
val c = childrenRefs
swapChildrenRefs(c, c.add(ref)) || addChild(ref)
}
@tailrec private def shallDie(ref: ActorRef): Boolean = {
val c = childrenRefs
swapChildrenRefs(c, c.shallDie(ref)) || shallDie(ref)
}
@tailrec private def removeChild(ref: ActorRef): ChildrenContainer = {
val c = childrenRefs
val n = c.remove(ref)
if (swapChildrenRefs(c, n)) n
else removeChild(ref)
}
@tailrec private def setChildrenTerminationReason(reason: SuspendReason): Boolean = {
childrenRefs match {
case c: TerminatingChildrenContainer swapChildrenRefs(c, c.copy(reason = reason)) || setChildrenTerminationReason(reason)
case _ false
}
}
private def isTerminating = childrenRefs match {
case TerminatingChildrenContainer(_, _, Termination) true
case TerminatedChildrenContainer true
case _ false
}
private def isNormal = childrenRefs match {
case TerminatingChildrenContainer(_, _, Termination | _: Recreation) false
case _ true
}
private def _actorOf(props: Props, name: String, async: Boolean): ActorRef = {
if (system.settings.SerializeAllCreators && !props.creator.isInstanceOf[NoSerializationVerificationNeeded]) {
val ser = SerializationExtension(system)
ser.serialize(props.creator) match {
case Left(t) throw t
case Right(bytes) ser.deserialize(bytes, props.creator.getClass) match {
case Left(t) throw t
case _ //All good
}
}
}
/*
* in case we are currently terminating, fail external attachChild requests
* (internal calls cannot happen anyway because we are suspended)
*/
if (isTerminating) throw new IllegalStateException("cannot create children while terminating or terminated")
else {
reserveChild(name)
// this name will either be unreserved or overwritten with a real child below
val actor =
try {
provider.actorOf(systemImpl, props, self, self.path / name,
systemService = false, deploy = None, lookupDeploy = true, async = async)
} catch {
case NonFatal(e)
unreserveChild(name)
throw e
}
addChild(actor)
actor
}
}
def actorOf(props: Props): ActorRef = _actorOf(props, randomName(), async = false)
def actorOf(props: Props, name: String): ActorRef = _actorOf(props, checkName(name), async = false)
private def checkName(name: String): String = {
import ActorPath.ElementRegex
name match {
case null throw new InvalidActorNameException("actor name must not be null")
case "" throw new InvalidActorNameException("actor name must not be empty")
case ElementRegex() name
case _ throw new InvalidActorNameException("illegal actor name '" + name + "', must conform to " + ElementRegex)
}
}
private[akka] def attachChild(props: Props, name: String): ActorRef =
_actorOf(props, checkName(name), async = true)
private[akka] def attachChild(props: Props): ActorRef =
_actorOf(props, randomName(), async = true)
final def stop(actor: ActorRef): Unit = {
val started = actor match {
case r: RepointableRef r.isStarted
case _ true
}
if (childrenRefs.getByRef(actor).isDefined && started) shallDie(actor)
actor.asInstanceOf[InternalActorRef].stop()
}
private[this] var _actor: Actor = _
def actor: Actor = _actor
protected def actor_=(a: Actor): Unit = _actor = a
var currentMessage: Envelope = _
var actor: Actor = _
private var behaviorStack: List[Actor.Receive] = emptyBehaviorStack
var watching: Set[ActorRef] = emptyActorRefSet
var watchedBy: Set[ActorRef] = emptyActorRefSet
@volatile private var _nextNameDoNotCallMeDirectly = 0L
final protected def randomName(): String = {
@tailrec def inc(): Long = {
val current = Unsafe.instance.getLongVolatile(this, nextNameOffset)
if (Unsafe.instance.compareAndSwapLong(this, nextNameOffset, current, current + 1)) current
else inc()
/*
* MESSAGE PROCESSING
*/
//Memory consistency is handled by the Mailbox (reading mailbox status then processing messages, then writing mailbox status
final def systemInvoke(message: SystemMessage): Unit = try {
message match {
case Create() create()
case Recreate(cause) faultRecreate(cause)
case Watch(watchee, watcher) addWatcher(watchee, watcher)
case Unwatch(watchee, watcher) remWatcher(watchee, watcher)
case Suspend() faultSuspend()
case Resume(inRespToFailure) faultResume(inRespToFailure)
case Terminate() terminate()
case Supervise(child) supervise(child)
case ChildTerminated(child) handleChildTerminated(child)
case NoMessage // only here to suppress warning
}
Helpers.base64(inc())
} catch {
case e @ (_: InterruptedException | NonFatal(_)) handleInvokeFailure(e, "error while processing " + message)
}
@volatile private var _mailboxDoNotCallMeDirectly: Mailbox = _ //This must be volatile since it isn't protected by the mailbox status
//Memory consistency is handled by the Mailbox (reading mailbox status then processing messages, then writing mailbox status
final def invoke(messageHandle: Envelope): Unit = try {
currentMessage = messageHandle
cancelReceiveTimeout() // FIXME: leave this here???
messageHandle.message match {
case msg: AutoReceivedMessage autoReceiveMessage(messageHandle)
case msg receiveMessage(msg)
}
currentMessage = null // reset current message after successful invocation
} catch {
case e @ (_: InterruptedException | NonFatal(_)) handleInvokeFailure(e, e.getMessage)
} finally {
checkReceiveTimeout // Reschedule receive timeout
}
/**
* INTERNAL API
*
* Returns a reference to the current mailbox
def autoReceiveMessage(msg: Envelope): Unit = {
if (system.settings.DebugAutoReceive)
publish(Debug(self.path.toString, clazz(actor), "received AutoReceiveMessage " + msg))
msg.message match {
case Failed(cause) handleFailure(sender, cause)
case t: Terminated watchedActorTerminated(t.actor); receiveMessage(t)
case Kill throw new ActorKilledException("Kill")
case PoisonPill self.stop()
case SelectParent(m) parent.tell(m, msg.sender)
case SelectChildName(name, m) for (c getChildByName(name)) c.child.tell(m, msg.sender)
case SelectChildPattern(p, m) for (c children if p.matcher(c.path.name).matches) c.tell(m, msg.sender)
}
}
final def receiveMessage(msg: Any): Unit = behaviorStack.head.applyOrElse(msg, actor.unhandled)
/*
* ACTOR CONTEXT IMPLEMENTATION
*/
@inline final def mailbox: Mailbox = Unsafe.instance.getObjectVolatile(this, mailboxOffset).asInstanceOf[Mailbox]
/**
* INTERNAL API
*
* replaces the current mailbox using getAndSet semantics
*/
@tailrec final def swapMailbox(newMailbox: Mailbox): Mailbox = {
val oldMailbox = mailbox
if (!Unsafe.instance.compareAndSwapObject(this, mailboxOffset, oldMailbox, newMailbox)) swapMailbox(newMailbox)
else oldMailbox
}
final def hasMessages: Boolean = mailbox.hasMessages
final def numberOfMessages: Int = mailbox.numberOfMessages
val dispatcher: MessageDispatcher = system.dispatchers.lookup(props.dispatcher)
/**
* UntypedActorContext impl
*/
final def getDispatcher(): MessageDispatcher = dispatcher
final def isTerminated: Boolean = mailbox.isClosed
final def start(): this.type = {
/*
* Create the mailbox and enqueue the Create() message to ensure that
* this is processed before anything else.
*/
swapMailbox(dispatcher.createMailbox(this))
mailbox.setActor(this)
// ➡➡➡ NEVER SEND THE SAME SYSTEM MESSAGE OBJECT TO TWO ACTORS ⬅⬅⬅
mailbox.systemEnqueue(self, Create())
// This call is expected to start off the actor by scheduling its mailbox.
dispatcher.attach(this)
this
}
// ➡➡➡ NEVER SEND THE SAME SYSTEM MESSAGE OBJECT TO TWO ACTORS ⬅⬅⬅
final def suspend(): Unit = dispatcher.systemDispatch(this, Suspend())
// ➡➡➡ NEVER SEND THE SAME SYSTEM MESSAGE OBJECT TO TWO ACTORS ⬅⬅⬅
final def resume(): Unit = dispatcher.systemDispatch(this, Resume())
// ➡➡➡ NEVER SEND THE SAME SYSTEM MESSAGE OBJECT TO TWO ACTORS ⬅⬅⬅
final def stop(): Unit = dispatcher.systemDispatch(this, Terminate())
override final def watch(subject: ActorRef): ActorRef = subject match {
case a: InternalActorRef
if (a != self && !watching.contains(a)) {
a.sendSystemMessage(Watch(a, self)) // ➡➡➡ NEVER SEND THE SAME SYSTEM MESSAGE OBJECT TO TWO ACTORS ⬅⬅⬅
watching += a
}
a
}
override final def unwatch(subject: ActorRef): ActorRef = subject match {
case a: InternalActorRef
if (a != self && watching.contains(a)) {
a.sendSystemMessage(Unwatch(a, self)) // ➡➡➡ NEVER SEND THE SAME SYSTEM MESSAGE OBJECT TO TWO ACTORS ⬅⬅⬅
watching -= a
}
a
}
final def children: Iterable[ActorRef] = childrenRefs.children
/**
* Impl UntypedActorContext
*/
final def getChildren(): java.lang.Iterable[ActorRef] =
scala.collection.JavaConverters.asJavaIterableConverter(children).asJava
def tell(message: Any, sender: ActorRef): Unit =
dispatcher.dispatch(this, Envelope(message, if (sender eq null) system.deadLetters else sender, system))
override def sendSystemMessage(message: SystemMessage): Unit = dispatcher.systemDispatch(this, message)
final def sender: ActorRef = currentMessage match {
case null system.deadLetters
@ -692,6 +359,25 @@ private[akka] class ActorCell(
case _ system.deadLetters
}
def become(behavior: Actor.Receive, discardOld: Boolean = true): Unit =
behaviorStack = behavior :: (if (discardOld && behaviorStack.nonEmpty) behaviorStack.tail else behaviorStack)
def become(behavior: Procedure[Any]): Unit = become(behavior, false)
def become(behavior: Procedure[Any], discardOld: Boolean): Unit =
become({ case msg behavior.apply(msg) }: Actor.Receive, discardOld)
def unbecome(): Unit = {
val original = behaviorStack
behaviorStack =
if (original.isEmpty || original.tail.isEmpty) actor.receive :: emptyBehaviorStack
else original.tail
}
/*
* ACTOR INSTANCE HANDLING
*/
//This method is in charge of setting up the contextStack and create a new instance of the Actor
protected def newActor(): Actor = {
contextStack.set(this :: contextStack.get)
@ -712,325 +398,43 @@ private[akka] class ActorCell(
}
}
//Memory consistency is handled by the Mailbox (reading mailbox status then processing messages, then writing mailbox status
final def systemInvoke(message: SystemMessage) {
def create(): Unit = if (isNormal) {
try {
val created = newActor()
actor = created
created.preStart()
checkReceiveTimeout
if (system.settings.DebugLifecycle) system.eventStream.publish(Debug(self.path.toString, clazz(created), "started (" + created + ")"))
} catch {
case NonFatal(i: InstantiationException)
throw new ActorInitializationException(self,
"""exception during creation, this problem is likely to occur because the class of the Actor you tried to create is either,
private def create(): Unit = if (isNormal) {
try {
val created = newActor()
actor = created
created.preStart()
checkReceiveTimeout
if (system.settings.DebugLifecycle) publish(Debug(self.path.toString, clazz(created), "started (" + created + ")"))
} catch {
case NonFatal(i: InstantiationException)
throw new ActorInitializationException(self,
"""exception during creation, this problem is likely to occur because the class of the Actor you tried to create is either,
a non-static inner class (in which case make it a static inner class or use Props(new ...) or Props( new UntypedActorFactory ... )
or is missing an appropriate, reachable no-args constructor.
""", i.getCause)
case NonFatal(e) throw new ActorInitializationException(self, "exception during creation", e)
}
}
def recreate(cause: Throwable): Unit = if (isNormal) {
try {
val failedActor = actor
if (system.settings.DebugLifecycle) system.eventStream.publish(Debug(self.path.toString, clazz(failedActor), "restarting"))
if (failedActor ne null) {
val c = currentMessage //One read only plz
try {
if (failedActor.context ne null) failedActor.preRestart(cause, if (c ne null) Some(c.message) else None)
} finally {
clearActorFields(failedActor)
}
}
childrenRefs match {
case ct: TerminatingChildrenContainer
setChildrenTerminationReason(Recreation(cause))
dispatcher suspend this
case _
doRecreate(cause, failedActor)
}
} catch {
case NonFatal(e) throw new ActorInitializationException(self, "exception during creation", e match {
case i: InstantiationException i.getCause
case other other
})
}
}
def suspend(): Unit = if (isNormal) dispatcher suspend this
def resume(): Unit = if (isNormal) dispatcher resume this
def addWatcher(watchee: ActorRef, watcher: ActorRef): Unit = {
val watcheeSelf = watchee == self
val watcherSelf = watcher == self
if (watcheeSelf && !watcherSelf) {
if (!watchedBy.contains(watcher)) {
watchedBy += watcher
if (system.settings.DebugLifecycle) system.eventStream.publish(Debug(self.path.toString, clazz(actor), "now monitoring " + watcher))
}
} else if (!watcheeSelf && watcherSelf) {
watch(watchee)
} else {
system.eventStream.publish(Warning(self.path.toString, clazz(actor), "BUG: illegal Watch(%s,%s) for %s".format(watchee, watcher, self)))
}
}
def remWatcher(watchee: ActorRef, watcher: ActorRef): Unit = {
val watcheeSelf = watchee == self
val watcherSelf = watcher == self
if (watcheeSelf && !watcherSelf) {
if (watchedBy.contains(watcher)) {
watchedBy -= watcher
if (system.settings.DebugLifecycle) system.eventStream.publish(Debug(self.path.toString, clazz(actor), "stopped monitoring " + watcher))
}
} else if (!watcheeSelf && watcherSelf) {
unwatch(watchee)
} else {
system.eventStream.publish(Warning(self.path.toString, clazz(actor), "BUG: illegal Unwatch(%s,%s) for %s".format(watchee, watcher, self)))
}
}
def terminate() {
setReceiveTimeout(None)
cancelReceiveTimeout
// stop all children, which will turn childrenRefs into TerminatingChildrenContainer (if there are children)
children foreach stop
childrenRefs match {
case ct: TerminatingChildrenContainer
setChildrenTerminationReason(Termination)
// do not process normal messages while waiting for all children to terminate
dispatcher suspend this
if (system.settings.DebugLifecycle) system.eventStream.publish(Debug(self.path.toString, clazz(actor), "stopping"))
case _ doTerminate()
}
}
def supervise(child: ActorRef): Unit = if (!isTerminating) {
if (childrenRefs.getByRef(child).isEmpty) addChild(child)
handleSupervise(child)
if (system.settings.DebugLifecycle) system.eventStream.publish(Debug(self.path.toString, clazz(actor), "now supervising " + child))
}
try {
message match {
case Create() create()
case Recreate(cause) recreate(cause)
case Watch(watchee, watcher) addWatcher(watchee, watcher)
case Unwatch(watchee, watcher) remWatcher(watchee, watcher)
case Suspend() suspend()
case Resume() resume()
case Terminate() terminate()
case Supervise(child) supervise(child)
case ChildTerminated(child) handleChildTerminated(child)
case NoMessage // only here to suppress warning
}
} catch {
case e @ (_: InterruptedException | NonFatal(_)) handleInvokeFailure(e, "error while processing " + message)
case NonFatal(e) throw new ActorInitializationException(self, "exception during creation", e)
}
}
//Memory consistency is handled by the Mailbox (reading mailbox status then processing messages, then writing mailbox status
final def invoke(messageHandle: Envelope): Unit = try {
currentMessage = messageHandle
cancelReceiveTimeout() // FIXME: leave this here???
messageHandle.message match {
case msg: AutoReceivedMessage autoReceiveMessage(messageHandle)
case msg receiveMessage(msg)
}
currentMessage = null // reset current message after successful invocation
} catch {
case e @ (_: InterruptedException | NonFatal(_)) handleInvokeFailure(e, e.getMessage)
} finally {
checkReceiveTimeout // Reschedule receive timeout
}
final def handleInvokeFailure(t: Throwable, message: String): Unit = try {
dispatcher.reportFailure(new LogEventException(Error(t, self.path.toString, clazz(actor), message), t))
// prevent any further messages to be processed until the actor has been restarted
dispatcher.suspend(this)
if (actor ne null) actor.supervisorStrategy.handleSupervisorFailing(self, children)
} finally {
t match { // Wrap InterruptedExceptions and rethrow
case _: InterruptedException parent.tell(Failed(new ActorInterruptedException(t)), self); throw t
case _ parent.tell(Failed(t), self)
}
}
def become(behavior: Actor.Receive, discardOld: Boolean = true): Unit =
behaviorStack = behavior :: (if (discardOld && behaviorStack.nonEmpty) behaviorStack.tail else behaviorStack)
/**
* UntypedActorContext impl
*/
def become(behavior: Procedure[Any]): Unit = become(behavior, false)
/*
* UntypedActorContext impl
*/
def become(behavior: Procedure[Any], discardOld: Boolean): Unit =
become({ case msg behavior.apply(msg) }: Actor.Receive, discardOld)
def unbecome(): Unit = {
val original = behaviorStack
behaviorStack =
if (original.isEmpty || original.tail.isEmpty) actor.receive :: emptyBehaviorStack
else original.tail
}
def autoReceiveMessage(msg: Envelope): Unit = {
if (system.settings.DebugAutoReceive)
system.eventStream.publish(Debug(self.path.toString, clazz(actor), "received AutoReceiveMessage " + msg))
msg.message match {
case Failed(cause) handleFailure(sender, cause)
case t: Terminated watching -= t.actor; receiveMessage(t)
case Kill throw new ActorKilledException("Kill")
case PoisonPill self.stop()
case SelectParent(m) parent.tell(m, msg.sender)
case SelectChildName(name, m) for (c childrenRefs getByName name) c.child.tell(m, msg.sender)
case SelectChildPattern(p, m) for (c children if p.matcher(c.path.name).matches) c.tell(m, msg.sender)
}
}
final def receiveMessage(msg: Any): Unit = {
//FIXME replace with behaviorStack.head.applyOrElse(msg, unhandled) + "-optimize"
val head = behaviorStack.head
if (head.isDefinedAt(msg)) head.apply(msg) else actor.unhandled(msg)
}
private def doTerminate() {
val a = actor
try {
try {
if (a ne null) a.postStop()
} finally {
dispatcher.detach(this)
}
} finally {
try {
parent.sendSystemMessage(ChildTerminated(self))
if (!watchedBy.isEmpty) {
val terminated = Terminated(self)(existenceConfirmed = true)
try {
watchedBy foreach {
watcher
try watcher.tell(terminated, self) catch {
case NonFatal(t) system.eventStream.publish(Error(t, self.path.toString, clazz(a), "deathwatch"))
}
}
} finally watchedBy = emptyActorRefSet
}
if (!watching.isEmpty) {
try {
watching foreach { // ➡➡➡ NEVER SEND THE SAME SYSTEM MESSAGE OBJECT TO TWO ACTORS ⬅⬅⬅
case watchee: InternalActorRef try watchee.sendSystemMessage(Unwatch(watchee, self)) catch {
case NonFatal(t) system.eventStream.publish(Error(t, self.path.toString, clazz(a), "deathwatch"))
}
}
} finally watching = emptyActorRefSet
}
if (system.settings.DebugLifecycle)
system.eventStream.publish(Debug(self.path.toString, clazz(a), "stopped"))
} finally {
behaviorStack = emptyBehaviorStack
clearActorFields(a)
actor = null
}
}
}
private def doRecreate(cause: Throwable, failedActor: Actor): Unit = try {
// after all killed children have terminated, recreate the rest, then go on to start the new instance
actor.supervisorStrategy.handleSupervisorRestarted(cause, self, children)
val freshActor = newActor()
actor = freshActor // this must happen before postRestart has a chance to fail
if (freshActor eq failedActor) setActorFields(freshActor, this, self) // If the creator returns the same instance, we need to restore our nulled out fields.
freshActor.postRestart(cause)
if (system.settings.DebugLifecycle) system.eventStream.publish(Debug(self.path.toString, clazz(freshActor), "restarted"))
dispatcher.resume(this)
} catch {
case NonFatal(e) try {
dispatcher.reportFailure(new LogEventException(Error(e, self.path.toString, clazz(actor), "error while creating actor"), e))
// prevent any further messages to be processed until the actor has been restarted
dispatcher.suspend(this)
actor.supervisorStrategy.handleSupervisorFailing(self, children) // FIXME Should this be called on actor or failedActor?
clearActorFields(actor) // If this fails, we need to ensure that preRestart isn't called.
} finally {
parent.tell(Failed(new ActorInitializationException(self, "exception during re-creation", e)), self)
}
}
final def handleFailure(child: ActorRef, cause: Throwable): Unit = childrenRefs.getByRef(child) match {
case Some(stats) if (!actor.supervisorStrategy.handleFailure(this, child, cause, stats, childrenRefs.stats)) throw cause
case None system.eventStream.publish(Warning(self.path.toString, clazz(actor), "dropping Failed(" + cause + ") from unknown child " + child))
}
final def handleChildTerminated(child: ActorRef): Unit = try {
childrenRefs match {
case tc @ TerminatingChildrenContainer(_, _, reason)
val n = removeChild(child)
actor.supervisorStrategy.handleChildTerminated(this, child, children)
if (!n.isInstanceOf[TerminatingChildrenContainer]) reason match {
case Recreation(cause) doRecreate(cause, actor) // doRecreate since this is the continuation of "recreate"
case Termination doTerminate()
case _
}
case _
removeChild(child)
actor.supervisorStrategy.handleChildTerminated(this, child, children)
}
} catch {
case NonFatal(e)
try {
dispatcher suspend this
actor.supervisorStrategy.handleSupervisorFailing(self, children)
} finally {
parent.tell(Failed(e), self)
}
private def supervise(child: ActorRef): Unit = if (!isTerminating) {
addChild(child)
handleSupervise(child)
if (system.settings.DebugLifecycle) publish(Debug(self.path.toString, clazz(actor), "now supervising " + child))
}
// future extension point
protected def handleSupervise(child: ActorRef): Unit = child match {
case r: RepointableActorRef r.activate()
case _
}
// ➡➡➡ NEVER SEND THE SAME SYSTEM MESSAGE OBJECT TO TWO ACTORS ⬅⬅⬅
final def restart(cause: Throwable): Unit = dispatcher.systemDispatch(this, Recreate(cause))
final def checkReceiveTimeout() {
val recvtimeout = receiveTimeoutData
if (Duration.Undefined != recvtimeout._1 && !mailbox.hasMessages) {
recvtimeout._2.cancel() //Cancel any ongoing future
//Only reschedule if desired and there are currently no more messages to be processed
receiveTimeoutData = (recvtimeout._1, system.scheduler.scheduleOnce(recvtimeout._1, self, ReceiveTimeout))
} else cancelReceiveTimeout()
}
final def cancelReceiveTimeout(): Unit =
if (receiveTimeoutData._2 ne emptyCancellable) {
receiveTimeoutData._2.cancel()
receiveTimeoutData = (receiveTimeoutData._1, emptyCancellable)
}
final def clearActorFields(actorInstance: Actor): Unit = {
final protected def clearActorFields(actorInstance: Actor): Unit = {
setActorFields(actorInstance, context = null, self = system.deadLetters)
currentMessage = null
behaviorStack = emptyBehaviorStack
}
final def setActorFields(actorInstance: Actor, context: ActorContext, self: ActorRef) {
final protected def setActorFields(actorInstance: Actor, context: ActorContext, self: ActorRef) {
@tailrec
def lookupAndSetField(clazz: Class[_], actor: Actor, name: String, value: Any): Boolean = {
val success = try {
@ -1044,7 +448,7 @@ private[akka] class ActorCell(
if (success) true
else {
val parent = clazz.getSuperclass
val parent: Class[_] = clazz.getSuperclass
if (parent eq null) throw new IllegalActorStateException(toString + " is not an Actor since it have not mixed in the 'Actor' trait")
lookupAndSetField(parent, actor, name, value)
}
@ -1055,6 +459,9 @@ private[akka] class ActorCell(
}
}
private final def clazz(o: AnyRef): Class[_] = if (o eq null) this.getClass else o.getClass
// logging is not the main purpose, and if it fails theres nothing we can do
protected final def publish(e: LogEvent): Unit = try system.eventStream.publish(e) catch { case NonFatal(_) }
protected final def clazz(o: AnyRef): Class[_] = if (o eq null) this.getClass else o.getClass
}

View file

@ -191,7 +191,7 @@ private[akka] abstract class InternalActorRef extends ActorRef with ScalaActorRe
/*
* Actor life-cycle management, invoked only internally (in response to user requests via ActorContext).
*/
def resume(): Unit
def resume(inResponseToFailure: Boolean): Unit
def suspend(): Unit
def restart(cause: Throwable): Unit
def stop(): Unit
@ -262,10 +262,7 @@ private[akka] class LocalActorRef private[akka] (
* that is reached).
*/
private val actorCell: ActorCell = newActorCell(_system, this, _props, _supervisor)
actorCell.start()
// ➡➡➡ NEVER SEND THE SAME SYSTEM MESSAGE OBJECT TO TWO ACTORS ⬅⬅⬅
_supervisor.sendSystemMessage(akka.dispatch.Supervise(this))
actorCell.start(sendSupervise = true)
protected def newActorCell(system: ActorSystemImpl, ref: InternalActorRef, props: Props, supervisor: InternalActorRef): ActorCell =
new ActorCell(system, ref, props, supervisor)
@ -291,7 +288,7 @@ private[akka] class LocalActorRef private[akka] (
/**
* Resumes a suspended actor.
*/
override def resume(): Unit = actorCell.resume()
override def resume(inResponseToFailure: Boolean): Unit = actorCell.resume(inResponseToFailure)
/**
* Shuts down the actor and its message queue
@ -307,7 +304,7 @@ private[akka] class LocalActorRef private[akka] (
* to inject synthetic actor paths like /temp.
*/
protected def getSingleChild(name: String): InternalActorRef =
actorCell.childrenRefs.getByName(name) match {
actorCell.getChildByName(name) match {
case Some(crs) crs.child.asInstanceOf[InternalActorRef]
case None Nobody
}
@ -391,7 +388,7 @@ private[akka] trait MinimalActorRef extends InternalActorRef with LocalRef {
override def getChild(names: Iterator[String]): InternalActorRef = if (names.forall(_.isEmpty)) this else Nobody
override def suspend(): Unit = ()
override def resume(): Unit = ()
override def resume(inResponseToFailure: Boolean): Unit = ()
override def stop(): Unit = ()
override def isTerminated = false

View file

@ -4,12 +4,13 @@
package akka.actor
import java.util.concurrent.atomic.AtomicLong
import akka.dispatch._
import akka.routing._
import akka.AkkaException
import akka.event._
import akka.util.{ NonFatal, Switch, Helpers }
import akka.util.{ Switch, Helpers }
import scala.util.control.NonFatal
import scala.concurrent.{ Future, Promise }
import java.util.concurrent.atomic.AtomicLong
/**
* Interface for all ActorRef providers to implement.
@ -361,9 +362,7 @@ class LocalActorRefProvider(
def provider: ActorRefProvider = LocalActorRefProvider.this
override def stop(): Unit = stopped switchOn {
terminationFuture.complete(causeOfTermination.toLeft(()))
}
override def stop(): Unit = stopped switchOn { terminationPromise.complete(causeOfTermination.toLeft(())) }
override def isTerminated: Boolean = stopped.isOn
@ -458,7 +457,9 @@ class LocalActorRefProvider(
def dispatcher: MessageDispatcher = system.dispatcher
lazy val terminationFuture: Promise[Unit] = Promise[Unit]()(dispatcher)
lazy val terminationPromise: Promise[Unit] = Promise[Unit]()
def terminationFuture: Future[Unit] = terminationPromise.future
@volatile
private var extraNames: Map[String, InternalActorRef] = Map()

View file

@ -2,6 +2,9 @@
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.actor
import language.implicitConversions
import java.util.regex.Pattern
import akka.util.Helpers

View file

@ -9,12 +9,15 @@ import akka.dispatch._
import akka.pattern.ask
import com.typesafe.config.{ Config, ConfigFactory }
import scala.annotation.tailrec
import scala.concurrent.util.Duration
import java.io.Closeable
import akka.dispatch.Await.{ Awaitable, CanAwait }
import scala.concurrent.{ Await, Awaitable, CanAwait, Future }
import scala.util.control.NonFatal
import akka.util._
import akka.util.internal.{ HashedWheelTimer, ConcurrentIdentityHashMap }
import java.util.concurrent.{ ThreadFactory, CountDownLatch, TimeoutException, RejectedExecutionException }
import java.util.concurrent.TimeUnit.MILLISECONDS
import akka.actor.cell.ChildrenContainer
object ActorSystem {
@ -506,13 +509,12 @@ private[akka] class ActorSystemImpl(val name: String, applicationConfig: Config,
def actorOf(props: Props): ActorRef = guardian.underlying.attachChild(props)
def stop(actor: ActorRef): Unit = {
implicit val timeout = settings.CreationTimeout
val path = actor.path
val guard = guardian.path
val sys = systemGuardian.path
path.parent match {
case `guard` Await.result(guardian ? StopChild(actor), timeout.duration)
case `sys` Await.result(systemGuardian ? StopChild(actor), timeout.duration)
case `guard` guardian ! StopChild(actor)
case `sys` systemGuardian ! StopChild(actor)
case _ actor.asInstanceOf[InternalActorRef].stop()
}
}
@ -586,6 +588,7 @@ private[akka] class ActorSystemImpl(val name: String, applicationConfig: Config,
def start(): this.type = _start
private lazy val terminationCallbacks = {
implicit val d = dispatcher
val callbacks = new TerminationCallbacks
terminationFuture onComplete (_ callbacks.run)
callbacks
@ -659,7 +662,7 @@ private[akka] class ActorSystemImpl(val name: String, applicationConfig: Config,
instance //Profit!
}
} catch {
case t
case t: Throwable
extensions.remove(ext, inProcessOfRegistration) //In case shit hits the fan, remove the inProcess signal
throw t //Escalate to caller
} finally {
@ -698,19 +701,30 @@ private[akka] class ActorSystemImpl(val name: String, applicationConfig: Config,
node match {
case wc: ActorRefWithCell
val cell = wc.underlying
indent + "-> " + node.path.name + " " + Logging.simpleName(node) + " " +
(if (indent.isEmpty) "-> " else indent.dropRight(1) + "⌊-> ") +
node.path.name + " " + Logging.simpleName(node) + " " +
(cell match {
case real: ActorCell if (real.actor ne null) real.actor.getClass else "null"
case _ Logging.simpleName(cell)
}) +
(cell match {
case real: ActorCell " status=" + real.mailbox.status
case _ ""
}) +
" " + (cell.childrenRefs match {
case ActorCell.TerminatingChildrenContainer(_, toDie, reason)
case ChildrenContainer.TerminatingChildrenContainer(_, toDie, reason)
"Terminating(" + reason + ")" +
(toDie.toSeq.sorted mkString ("\n" + indent + " toDie: ", "\n" + indent + " ", ""))
(toDie.toSeq.sorted mkString ("\n" + indent + " | toDie: ", "\n" + indent + " | ", ""))
case x @ (ChildrenContainer.TerminatedChildrenContainer | ChildrenContainer.EmptyChildrenContainer) x.toString
case n: ChildrenContainer.NormalChildrenContainer n.c.size + " children"
case x Logging.simpleName(x)
}) +
(if (cell.childrenRefs.children.isEmpty) "" else "\n") +
(cell.childrenRefs.children.toSeq.sorted map (printNode(_, indent + " |")) mkString ("\n"))
({
val children = cell.childrenRefs.children.toSeq.sorted
val bulk = children.dropRight(1) map (printNode(_, indent + " |"))
bulk ++ (children.lastOption map (printNode(_, indent + " ")))
} mkString ("\n"))
case _
indent + node.path.name + " " + Logging.simpleName(node)
}

View file

@ -4,7 +4,7 @@
package akka.actor
import akka.util.Duration
import scala.concurrent.util.Duration
import com.typesafe.config._
import akka.routing._
import java.util.concurrent.{ TimeUnit }
@ -139,7 +139,7 @@ private[akka] class Deployer(val settings: ActorSystem.Settings, val dynamicAcce
val deployment = config.withFallback(default)
val routees = deployment.getStringList("routees.paths").asScala.toSeq
val routees = Vector() ++ deployment.getStringList("routees.paths").asScala
val nrOfInstances = deployment.getInt("nr-of-instances")

View file

@ -3,8 +3,9 @@
*/
package akka.actor
import akka.util.NonFatal
import scala.util.control.NonFatal
import java.lang.reflect.InvocationTargetException
import scala.reflect.ClassTag
/**
* The DynamicAccess implementation is the class which is used for
@ -24,7 +25,7 @@ abstract class DynamicAccess {
* val obj = DynamicAccess.createInstanceFor(clazz, Seq(classOf[Config] -> config, classOf[String] -> name))
* }}}
*/
def createInstanceFor[T: ClassManifest](clazz: Class[_], args: Seq[(Class[_], AnyRef)]): Either[Throwable, T] = {
def createInstanceFor[T: ClassTag](clazz: Class[_], args: Seq[(Class[_], AnyRef)]): Either[Throwable, T] = {
val types = args.map(_._1).toArray
val values = args.map(_._2).toArray
withErrorHandling {
@ -40,7 +41,7 @@ abstract class DynamicAccess {
* Obtain a `Class[_]` object loaded with the right class loader (i.e. the one
* returned by `classLoader`).
*/
def getClassFor[T: ClassManifest](fqcn: String): Either[Throwable, Class[_ <: T]]
def getClassFor[T: ClassTag](fqcn: String): Either[Throwable, Class[_ <: T]]
/**
* Obtain an object conforming to the type T, which is expected to be
@ -49,12 +50,12 @@ abstract class DynamicAccess {
* `args` argument. The exact usage of args depends on which type is requested,
* see the relevant requesting code for details.
*/
def createInstanceFor[T: ClassManifest](fqcn: String, args: Seq[(Class[_], AnyRef)]): Either[Throwable, T]
def createInstanceFor[T: ClassTag](fqcn: String, args: Seq[(Class[_], AnyRef)]): Either[Throwable, T]
/**
* Obtain the Scala object instance for the given fully-qualified class name, if there is one.
*/
def getObjectFor[T: ClassManifest](fqcn: String): Either[Throwable, T]
def getObjectFor[T: ClassTag](fqcn: String): Either[Throwable, T]
/**
* This is the class loader to be used in those special cases where the
@ -89,7 +90,7 @@ abstract class DynamicAccess {
*/
class ReflectiveDynamicAccess(val classLoader: ClassLoader) extends DynamicAccess {
//FIXME switch to Scala Reflection for 2.10
override def getClassFor[T: ClassManifest](fqcn: String): Either[Throwable, Class[_ <: T]] =
override def getClassFor[T: ClassTag](fqcn: String): Either[Throwable, Class[_ <: T]] =
try {
val c = classLoader.loadClass(fqcn).asInstanceOf[Class[_ <: T]]
val t = classManifest[T].erasure
@ -98,7 +99,7 @@ class ReflectiveDynamicAccess(val classLoader: ClassLoader) extends DynamicAcces
case NonFatal(e) Left(e)
}
override def createInstanceFor[T: ClassManifest](fqcn: String, args: Seq[(Class[_], AnyRef)]): Either[Throwable, T] =
override def createInstanceFor[T: ClassTag](fqcn: String, args: Seq[(Class[_], AnyRef)]): Either[Throwable, T] =
getClassFor(fqcn).fold(Left(_), { c
val types = args.map(_._1).toArray
val values = args.map(_._2).toArray
@ -111,7 +112,7 @@ class ReflectiveDynamicAccess(val classLoader: ClassLoader) extends DynamicAcces
}
})
override def getObjectFor[T: ClassManifest](fqcn: String): Either[Throwable, T] = {
override def getObjectFor[T: ClassTag](fqcn: String): Either[Throwable, T] = {
getClassFor(fqcn).fold(Left(_), { c
withErrorHandling {
val module = c.getDeclaredField("MODULE$")

View file

@ -3,6 +3,8 @@
*/
package akka.actor
import scala.reflect.ClassTag
/**
* The basic ActorSystem covers all that is needed for locally running actors,
* using futures and so on. In addition, more features can hook into it and
@ -92,12 +94,12 @@ trait ExtensionIdProvider {
* }
* }}}
*/
abstract class ExtensionKey[T <: Extension](implicit m: ClassManifest[T]) extends ExtensionId[T] with ExtensionIdProvider {
def this(clazz: Class[T]) = this()(ClassManifest.fromClass(clazz))
abstract class ExtensionKey[T <: Extension](implicit m: ClassTag[T]) extends ExtensionId[T] with ExtensionIdProvider {
def this(clazz: Class[T]) = this()(ClassTag(clazz))
override def lookup(): ExtensionId[T] = this
def createExtension(system: ExtendedActorSystem): T =
system.dynamicAccess.createInstanceFor[T](m.erasure, Seq(classOf[ExtendedActorSystem] -> system)) match {
system.dynamicAccess.createInstanceFor[T](m.runtimeClass, Seq(classOf[ExtendedActorSystem] -> system)) match {
case Left(ex) throw ex
case Right(r) r
}

View file

@ -3,8 +3,10 @@
*/
package akka.actor
import akka.util._
import language.implicitConversions
import akka.util._
import scala.concurrent.util.Duration
import scala.collection.mutable
import akka.routing.{ Deafen, Listen, Listeners }

View file

@ -3,12 +3,13 @@
*/
package akka.actor
import language.implicitConversions
import java.util.concurrent.TimeUnit
import scala.collection.mutable.ArrayBuffer
import scala.collection.JavaConversions._
import java.lang.{ Iterable JIterable }
import akka.util.Duration
import scala.concurrent.util.Duration
/**
* INTERNAL API
*/
@ -207,19 +208,31 @@ object SupervisorStrategy extends SupervisorStrategyLowPriorityImplicits {
}
/**
* An Akka SupervisorStrategy is the policy to apply for crashing children
* An Akka SupervisorStrategy is the policy to apply for crashing children.
*
* <b>IMPORTANT:</b>
*
* You should not normally need to create new subclasses, instead use the
* existing [[akka.actor.OneForOneStrategy]] or [[akka.actor.AllForOneStrategy]],
* but if you do, please read the docs of the methods below carefully, as
* incorrect implementations may lead to blocked actor systems (i.e.
* permanently suspended actors).
*/
abstract class SupervisorStrategy {
import SupervisorStrategy._
/**
* Returns the Decider that is associated with this SupervisorStrategy
* Returns the Decider that is associated with this SupervisorStrategy.
* The Decider is invoked by the default implementation of `handleFailure`
* to obtain the Directive to be applied.
*/
def decider: Decider
/**
* This method is called after the child has been removed from the set of children.
* It does not need to do anything special. Exceptions thrown from this method
* do NOT make the actor fail if this happens during termination.
*/
def handleChildTerminated(context: ActorContext, child: ActorRef, children: Iterable[ActorRef]): Unit
@ -228,27 +241,48 @@ abstract class SupervisorStrategy {
*/
def processFailure(context: ActorContext, restart: Boolean, child: ActorRef, cause: Throwable, stats: ChildRestartStats, children: Iterable[ChildRestartStats]): Unit
//FIXME docs
def handleSupervisorFailing(supervisor: ActorRef, children: Iterable[ActorRef]): Unit =
if (children.nonEmpty) children.foreach(_.asInstanceOf[InternalActorRef].suspend())
//FIXME docs
def handleSupervisorRestarted(cause: Throwable, supervisor: ActorRef, children: Iterable[ActorRef]): Unit =
if (children.nonEmpty) children.foreach(_.asInstanceOf[InternalActorRef].restart(cause))
/**
* Returns whether it processed the failure or not
* This is the main entry point: in case of a childs failure, this method
* must try to handle the failure by resuming, restarting or stopping the
* child (and returning `true`), or it returns `false` to escalate the
* failure, which will lead to this actor re-throwing the exception which
* caused the failure. The exception will not be wrapped.
*
* @param children is a lazy collection (a view)
*/
def handleFailure(context: ActorContext, child: ActorRef, cause: Throwable, stats: ChildRestartStats, children: Iterable[ChildRestartStats]): Boolean = {
val directive = if (decider.isDefinedAt(cause)) decider(cause) else Escalate //FIXME applyOrElse in Scala 2.10
directive match {
case Resume child.asInstanceOf[InternalActorRef].resume(); true
case Resume resumeChild(child); true
case Restart processFailure(context, true, child, cause, stats, children); true
case Stop processFailure(context, false, child, cause, stats, children); true
case Escalate false
}
}
/**
* Resume the previously failed child: <b>do never apply this to a child which
* is not the currently failing child</b>. Suspend/resume needs to be done in
* matching pairs, otherwise actors will wake up too soon or never at all.
*/
final def resumeChild(child: ActorRef): Unit = child.asInstanceOf[InternalActorRef].resume(inResponseToFailure = true)
/**
* Restart the given child, possibly suspending it first.
*
* <b>IMPORTANT:</b>
*
* If the child is the currently failing one, it will already have been
* suspended, hence `suspendFirst` is false. If the child is not the
* currently failing one, then it did not request this treatment and is
* therefore not prepared to be resumed without prior suspend.
*/
final def restartChild(child: ActorRef, cause: Throwable, suspendFirst: Boolean): Unit = {
val c = child.asInstanceOf[InternalActorRef]
if (suspendFirst) c.suspend()
c.restart(cause)
}
}
/**
@ -287,7 +321,7 @@ case class AllForOneStrategy(maxNrOfRetries: Int = -1, withinTimeRange: Duration
def processFailure(context: ActorContext, restart: Boolean, child: ActorRef, cause: Throwable, stats: ChildRestartStats, children: Iterable[ChildRestartStats]): Unit = {
if (children.nonEmpty) {
if (restart && children.forall(_.requestRestartPermission(retriesWindow)))
children.foreach(_.child.asInstanceOf[InternalActorRef].restart(cause))
children foreach (crs restartChild(crs.child, cause, suspendFirst = (crs.child != child)))
else
for (c children) context.stop(c.child)
}
@ -329,7 +363,7 @@ case class OneForOneStrategy(maxNrOfRetries: Int = -1, withinTimeRange: Duration
def processFailure(context: ActorContext, restart: Boolean, child: ActorRef, cause: Throwable, stats: ChildRestartStats, children: Iterable[ChildRestartStats]): Unit = {
if (restart && stats.requestRestartPermission(retriesWindow))
child.asInstanceOf[InternalActorRef].restart(cause)
restartChild(child, cause, suspendFirst = false)
else
context.stop(child) //TODO optimization to drop child here already?
}

View file

@ -3,8 +3,13 @@
*/
package akka.actor
import akka.dispatch.{ Future, ExecutionContext }
import akka.util.{ ByteString, Duration, NonFatal }
import language.higherKinds
import language.postfixOps
import scala.concurrent.{ ExecutionContext, Future }
import scala.concurrent.util.Duration
import scala.util.control.NonFatal
import akka.util.ByteString
import java.net.{ SocketAddress, InetSocketAddress }
import java.io.IOException
import java.nio.ByteBuffer
@ -559,7 +564,7 @@ object IO {
* A mutable reference to an [[akka.actor.IO.Iteratee]]. Not thread safe.
*
* Designed for use within an [[akka.actor.Actor]], although all actions
* perfomed on the Iteratee are processed within a [[akka.dispatch.Future]]
* perfomed on the Iteratee are processed within a [[scala.concurrent.Future]]
* so it is not safe to refer to the Actor's state from within this Iteratee.
* Messages should instead be sent to the Actor in order to modify state.
*

View file

@ -4,9 +4,13 @@
package akka.actor
import language.existentials
import akka.dispatch._
import akka.japi.Creator
import scala.reflect.ClassTag
import akka.routing._
import akka.util.Reflect
/**
* Factory for Props instances.
@ -48,8 +52,8 @@ object Props {
*
* Scala API.
*/
def apply[T <: Actor: ClassManifest](): Props =
default.withCreator(implicitly[ClassManifest[T]].erasure.asInstanceOf[Class[_ <: Actor]])
def apply[T <: Actor: ClassTag](): Props =
default.withCreator(implicitly[ClassTag[T]].runtimeClass.asInstanceOf[Class[_ <: Actor]])
/**
* Returns a Props that has default values except for "creator" which will be a function that creates an instance
@ -185,10 +189,5 @@ case class Props(
* able to optimize serialization.
*/
private[akka] case class FromClassCreator(clazz: Class[_ <: Actor]) extends Function0[Actor] {
def apply(): Actor = try clazz.newInstance catch {
case iae: IllegalAccessException
val ctor = clazz.getDeclaredConstructor()
ctor.setAccessible(true)
ctor.newInstance()
}
def apply(): Actor = Reflect.instantiate(clazz)
}

View file

@ -16,6 +16,7 @@ import akka.dispatch.MessageDispatcher
import java.util.concurrent.locks.ReentrantLock
import akka.event.Logging.Warning
import scala.collection.mutable.Queue
import akka.actor.cell.ChildrenContainer
/**
* This actor ref starts out with some dummy cell (by default just enqueuing
@ -76,11 +77,11 @@ private[akka] class RepointableActorRef(
* This is called by activate() to obtain the cell which is to replace the
* unstarted cell. The cell must be fully functional.
*/
def newCell(): Cell = new ActorCell(system, this, props, supervisor).start()
def newCell(): Cell = new ActorCell(system, this, props, supervisor).start(sendSupervise = false)
def suspend(): Unit = underlying.suspend()
def resume(): Unit = underlying.resume()
def resume(inResponseToFailure: Boolean): Unit = underlying.resume(inResponseToFailure)
def stop(): Unit = underlying.stop()
@ -102,7 +103,7 @@ private[akka] class RepointableActorRef(
case ".." getParent.getChild(name)
case "" getChild(name)
case other
underlying.childrenRefs.getByName(other) match {
underlying.getChildByName(other) match {
case Some(crs) crs.child.asInstanceOf[InternalActorRef].getChild(name)
case None Nobody
}
@ -129,6 +130,7 @@ private[akka] class UnstartedCell(val systemImpl: ActorSystemImpl, val self: Rep
// use Envelope to keep on-send checks in the same place
val queue: Queue[Envelope] = Queue()
val systemQueue: Queue[SystemMessage] = Queue()
var suspendCount = 0
def replaceWith(cell: Cell): Unit = {
lock.lock()
@ -161,18 +163,21 @@ private[akka] class UnstartedCell(val systemImpl: ActorSystemImpl, val self: Rep
if (interrupted) throw new InterruptedException
} finally try
self.swapCell(cell)
finally try
for (_ 1 to suspendCount) cell.suspend()
finally
lock.unlock()
}
def system: ActorSystem = systemImpl
def suspend(): Unit = {}
def resume(): Unit = {}
def restart(cause: Throwable): Unit = {}
def suspend(): Unit = { lock.lock(); try suspendCount += 1 finally lock.unlock() }
def resume(inResponseToFailure: Boolean): Unit = { lock.lock(); try suspendCount -= 1 finally lock.unlock() }
def restart(cause: Throwable): Unit = { lock.lock(); try suspendCount -= 1 finally lock.unlock() }
def stop(): Unit = sendSystemMessage(Terminate())
def isTerminated: Boolean = false
def parent: InternalActorRef = supervisor
def childrenRefs: ActorCell.ChildrenContainer = ActorCell.EmptyChildrenContainer
def childrenRefs: ChildrenContainer = ChildrenContainer.EmptyChildrenContainer
def getChildByName(name: String): Option[ChildRestartStats] = None
def tell(message: Any, sender: ActorRef): Unit = {
lock.lock()
try {

View file

@ -4,7 +4,7 @@
package akka.actor
import akka.util.Duration
import scala.concurrent.util.Duration
import akka.util.internal.{ TimerTask, HashedWheelTimer, Timeout HWTimeout, Timer }
import akka.event.LoggingAdapter
import akka.dispatch.MessageDispatcher

View file

@ -1,17 +1,22 @@
package akka.actor
/**
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.actor
import language.existentials
import akka.japi.{ Creator, Option JOption }
import java.lang.reflect.{ InvocationTargetException, Method, InvocationHandler, Proxy }
import akka.util.{ Timeout, NonFatal, Duration }
import java.util.concurrent.atomic.{ AtomicReference AtomVar }
import akka.util.Timeout
import scala.util.control.NonFatal
import scala.concurrent.util.Duration
import scala.concurrent.{ Await, Future }
import akka.util.Reflect.instantiator
import akka.dispatch._
import java.util.concurrent.atomic.{ AtomicReference AtomVar }
import java.util.concurrent.TimeoutException
import java.util.concurrent.TimeUnit.MILLISECONDS
import akka.actor.TypedActor.TypedActorInvocationHandler
import scala.reflect.ClassTag
import akka.serialization.{ JavaSerializer, SerializationExtension }
import java.io.ObjectStreamException
@ -403,9 +408,9 @@ object TypedActor extends ExtensionId[TypedActorExtension] with ExtensionIdProvi
case m if m.returnsJOption_? || m.returnsOption_?
val f = ask(actor, m)(timeout)
(try { Await.ready(f, timeout.duration).value } catch { case _: TimeoutException None }) match {
case None | Some(Right(null)) if (m.returnsJOption_?) JOption.none[Any] else None
case Some(Right(joption: AnyRef)) joption
case Some(Left(ex)) throw ex
case None | Some(Right(null)) if (m.returnsJOption_?) JOption.none[Any] else None
case Some(Right(joption)) joption.asInstanceOf[AnyRef]
case Some(Left(ex)) throw ex
}
case m Await.result(ask(actor, m)(timeout), timeout.duration).asInstanceOf[AnyRef]
}
@ -462,7 +467,7 @@ object TypedProps {
* Scala API
*/
def apply[T <: AnyRef](interface: Class[_ >: T], implementation: Class[T]): TypedProps[T] =
new TypedProps[T](extractInterfaces(interface), () implementation.newInstance())
new TypedProps[T](extractInterfaces(interface), instantiator(implementation))
/**
* Uses the supplied thunk as the factory for the TypedActor implementation,
@ -481,8 +486,8 @@ object TypedProps {
*
* Scala API
*/
def apply[T <: AnyRef: ClassManifest](): TypedProps[T] =
new TypedProps[T](implicitly[ClassManifest[T]].erasure.asInstanceOf[Class[T]])
def apply[T <: AnyRef: ClassTag](): TypedProps[T] =
new TypedProps[T](implicitly[ClassTag[T]].runtimeClass.asInstanceOf[Class[T]])
}
/**
@ -506,7 +511,7 @@ case class TypedProps[T <: AnyRef] protected[TypedProps] (
*/
def this(implementation: Class[T]) =
this(interfaces = TypedProps.extractInterfaces(implementation),
creator = () implementation.newInstance())
creator = instantiator(implementation))
/**
* Uses the supplied Creator as the factory for the TypedActor implementation,
@ -518,7 +523,7 @@ case class TypedProps[T <: AnyRef] protected[TypedProps] (
*/
def this(interface: Class[_ >: T], implementation: Creator[T]) =
this(interfaces = TypedProps.extractInterfaces(interface),
creator = () implementation.create())
creator = implementation.create _)
/**
* Uses the supplied class as the factory for the TypedActor implementation,
@ -530,7 +535,7 @@ case class TypedProps[T <: AnyRef] protected[TypedProps] (
*/
def this(interface: Class[_ >: T], implementation: Class[T]) =
this(interfaces = TypedProps.extractInterfaces(interface),
creator = () implementation.newInstance())
creator = instantiator(implementation))
/**
* Returns a new TypedProps with the specified dispatcher set.

View file

@ -0,0 +1,188 @@
/**
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.actor.cell
import scala.annotation.tailrec
import scala.collection.JavaConverters.asJavaIterableConverter
import scala.util.control.NonFatal
import akka.actor.{ RepointableRef, Props, NoSerializationVerificationNeeded, InvalidActorNameException, InternalActorRef, ChildRestartStats, ActorRef }
import akka.actor.ActorCell
import akka.actor.ActorPath.ElementRegex
import akka.serialization.SerializationExtension
import akka.util.{ Unsafe, Helpers }
private[akka] trait Children { this: ActorCell
import ChildrenContainer._
@volatile
private var _childrenRefsDoNotCallMeDirectly: ChildrenContainer = EmptyChildrenContainer
def childrenRefs: ChildrenContainer =
Unsafe.instance.getObjectVolatile(this, AbstractActorCell.childrenOffset).asInstanceOf[ChildrenContainer]
final def children: Iterable[ActorRef] = childrenRefs.children
final def getChildren(): java.lang.Iterable[ActorRef] = children.asJava
def actorOf(props: Props): ActorRef = makeChild(this, props, randomName(), async = false)
def actorOf(props: Props, name: String): ActorRef = makeChild(this, props, checkName(name), async = false)
private[akka] def attachChild(props: Props): ActorRef = makeChild(this, props, randomName(), async = true)
private[akka] def attachChild(props: Props, name: String): ActorRef = makeChild(this, props, checkName(name), async = true)
@volatile private var _nextNameDoNotCallMeDirectly = 0L
final protected def randomName(): String = {
@tailrec def inc(): Long = {
val current = Unsafe.instance.getLongVolatile(this, AbstractActorCell.nextNameOffset)
if (Unsafe.instance.compareAndSwapLong(this, AbstractActorCell.nextNameOffset, current, current + 1)) current
else inc()
}
Helpers.base64(inc())
}
final def stop(actor: ActorRef): Unit = {
val started = actor match {
case r: RepointableRef r.isStarted
case _ true
}
if (childrenRefs.getByRef(actor).isDefined && started) shallDie(actor)
actor.asInstanceOf[InternalActorRef].stop()
}
/*
* low level CAS helpers
*/
@inline private def swapChildrenRefs(oldChildren: ChildrenContainer, newChildren: ChildrenContainer): Boolean =
Unsafe.instance.compareAndSwapObject(this, AbstractActorCell.childrenOffset, oldChildren, newChildren)
@tailrec final protected def reserveChild(name: String): Boolean = {
val c = childrenRefs
swapChildrenRefs(c, c.reserve(name)) || reserveChild(name)
}
@tailrec final protected def unreserveChild(name: String): Boolean = {
val c = childrenRefs
swapChildrenRefs(c, c.unreserve(name)) || unreserveChild(name)
}
final protected def addChild(ref: ActorRef): Boolean = {
@tailrec def rec(): Boolean = {
val c = childrenRefs
swapChildrenRefs(c, c.add(ref)) || rec()
}
/*
* This does not need to check getByRef every tailcall, because the change
* cannot happen in that direction as a race: the only entity removing a
* child is the actor itself, and the only entity which could be racing is
* somebody who calls attachChild, and there we are guaranteed that that
* child cannot yet have died (since it has not yet been created).
*/
if (childrenRefs.getByRef(ref).isEmpty) rec() else false
}
@tailrec final protected def shallDie(ref: ActorRef): Boolean = {
val c = childrenRefs
swapChildrenRefs(c, c.shallDie(ref)) || shallDie(ref)
}
@tailrec final private def removeChild(ref: ActorRef): ChildrenContainer = {
val c = childrenRefs
val n = c.remove(ref)
if (swapChildrenRefs(c, n)) n
else removeChild(ref)
}
@tailrec final protected def setChildrenTerminationReason(reason: ChildrenContainer.SuspendReason): Boolean = {
childrenRefs match {
case c: ChildrenContainer.TerminatingChildrenContainer
swapChildrenRefs(c, c.copy(reason = reason)) || setChildrenTerminationReason(reason)
case _ false
}
}
final protected def setTerminated(): Unit = Unsafe.instance.putObjectVolatile(this, AbstractActorCell.childrenOffset, TerminatedChildrenContainer)
/*
* ActorCell-internal API
*/
protected def isNormal = childrenRefs.isNormal
protected def isTerminating = childrenRefs.isTerminating
protected def suspendChildren(skip: Set[ActorRef] = Set.empty): Unit =
childrenRefs.stats foreach {
case ChildRestartStats(child, _, _) if !(skip contains child) child.asInstanceOf[InternalActorRef].suspend()
case _
}
protected def resumeChildren(): Unit =
childrenRefs.stats foreach (_.child.asInstanceOf[InternalActorRef].resume(inResponseToFailure = false))
def getChildByName(name: String): Option[ChildRestartStats] = childrenRefs.getByName(name)
protected def getChildByRef(ref: ActorRef): Option[ChildRestartStats] = childrenRefs.getByRef(ref)
protected def getAllChildStats: Iterable[ChildRestartStats] = childrenRefs.stats
protected def removeChildAndGetStateChange(child: ActorRef): Option[SuspendReason] = {
childrenRefs match {
case TerminatingChildrenContainer(_, _, reason)
val newContainer = removeChild(child)
if (!newContainer.isInstanceOf[TerminatingChildrenContainer]) Some(reason) else None
case _
removeChild(child)
None
}
}
/*
* Private helpers
*/
private def checkName(name: String): String = {
name match {
case null throw new InvalidActorNameException("actor name must not be null")
case "" throw new InvalidActorNameException("actor name must not be empty")
case ElementRegex() name
case _ throw new InvalidActorNameException("illegal actor name '" + name + "', must conform to " + ElementRegex)
}
}
private def makeChild(cell: ActorCell, props: Props, name: String, async: Boolean): ActorRef = {
if (cell.system.settings.SerializeAllCreators && !props.creator.isInstanceOf[NoSerializationVerificationNeeded]) {
val ser = SerializationExtension(cell.system)
ser.serialize(props.creator) match {
case Left(t) throw t
case Right(bytes) ser.deserialize(bytes, props.creator.getClass) match {
case Left(t) throw t
case _ //All good
}
}
}
/*
* in case we are currently terminating, fail external attachChild requests
* (internal calls cannot happen anyway because we are suspended)
*/
if (cell.childrenRefs.isTerminating) throw new IllegalStateException("cannot create children while terminating or terminated")
else {
reserveChild(name)
// this name will either be unreserved or overwritten with a real child below
val actor =
try {
cell.provider.actorOf(cell.systemImpl, props, cell.self, cell.self.path / name,
systemService = false, deploy = None, lookupDeploy = true, async = async)
} catch {
case NonFatal(e)
unreserveChild(name)
throw e
}
addChild(actor)
actor
}
}
}

View file

@ -0,0 +1,195 @@
/**
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.actor.cell
import scala.collection.immutable.TreeMap
import akka.actor.{ InvalidActorNameException, ChildStats, ChildRestartStats, ChildNameReserved, ActorRef }
/**
* INTERNAL API
*/
private[akka] trait ChildrenContainer {
def add(child: ActorRef): ChildrenContainer
def remove(child: ActorRef): ChildrenContainer
def getByName(name: String): Option[ChildRestartStats]
def getByRef(actor: ActorRef): Option[ChildRestartStats]
def children: Iterable[ActorRef]
def stats: Iterable[ChildRestartStats]
def shallDie(actor: ActorRef): ChildrenContainer
// reserve that name or throw an exception
def reserve(name: String): ChildrenContainer
// cancel a reservation
def unreserve(name: String): ChildrenContainer
def isTerminating: Boolean = false
def isNormal: Boolean = true
}
/**
* INTERNAL API
*
* This object holds the classes performing the logic of managing the children
* of an actor, hence they are intimately tied to ActorCell.
*/
private[akka] object ChildrenContainer {
sealed trait SuspendReason
case object UserRequest extends SuspendReason
case class Recreation(cause: Throwable) extends SuspendReason
case object Termination extends SuspendReason
trait EmptyChildrenContainer extends ChildrenContainer {
val emptyStats = TreeMap.empty[String, ChildStats]
override def add(child: ActorRef): ChildrenContainer =
new NormalChildrenContainer(emptyStats.updated(child.path.name, ChildRestartStats(child)))
override def remove(child: ActorRef): ChildrenContainer = this
override def getByName(name: String): Option[ChildRestartStats] = None
override def getByRef(actor: ActorRef): Option[ChildRestartStats] = None
override def children: Iterable[ActorRef] = Nil
override def stats: Iterable[ChildRestartStats] = Nil
override def shallDie(actor: ActorRef): ChildrenContainer = this
override def reserve(name: String): ChildrenContainer = new NormalChildrenContainer(emptyStats.updated(name, ChildNameReserved))
override def unreserve(name: String): ChildrenContainer = this
}
/**
* This is the empty container, shared among all leaf actors.
*/
object EmptyChildrenContainer extends EmptyChildrenContainer {
override def toString = "no children"
}
/**
* This is the empty container which is installed after the last child has
* terminated while stopping; it is necessary to distinguish from the normal
* empty state while calling handleChildTerminated() for the last time.
*/
object TerminatedChildrenContainer extends EmptyChildrenContainer {
override def add(child: ActorRef): ChildrenContainer = this
override def reserve(name: String): ChildrenContainer =
throw new IllegalStateException("cannot reserve actor name '" + name + "': already terminated")
override def isTerminating: Boolean = true
override def isNormal: Boolean = false
override def toString = "terminated"
}
/**
* Normal children container: we do have at least one child, but none of our
* children are currently terminating (which is the time period between
* calling context.stop(child) and processing the ChildTerminated() system
* message).
*/
class NormalChildrenContainer(val c: TreeMap[String, ChildStats]) extends ChildrenContainer {
override def add(child: ActorRef): ChildrenContainer =
new NormalChildrenContainer(c.updated(child.path.name, ChildRestartStats(child)))
override def remove(child: ActorRef): ChildrenContainer = NormalChildrenContainer(c - child.path.name)
override def getByName(name: String): Option[ChildRestartStats] = c.get(name) match {
case s @ Some(_: ChildRestartStats) s.asInstanceOf[Option[ChildRestartStats]]
case _ None
}
override def getByRef(actor: ActorRef): Option[ChildRestartStats] = c.get(actor.path.name) match {
case c @ Some(crs: ChildRestartStats) if (crs.child == actor) c.asInstanceOf[Option[ChildRestartStats]]
case _ None
}
override def children: Iterable[ActorRef] = c.values.view.collect { case ChildRestartStats(child, _, _) child }
override def stats: Iterable[ChildRestartStats] = c.values.view.collect { case c: ChildRestartStats c }
override def shallDie(actor: ActorRef): ChildrenContainer = TerminatingChildrenContainer(c, Set(actor), UserRequest)
override def reserve(name: String): ChildrenContainer =
if (c contains name)
throw new InvalidActorNameException("actor name " + name + " is not unique!")
else new NormalChildrenContainer(c.updated(name, ChildNameReserved))
override def unreserve(name: String): ChildrenContainer = c.get(name) match {
case Some(ChildNameReserved) NormalChildrenContainer(c - name)
case _ this
}
override def toString =
if (c.size > 20) c.size + " children"
else c.mkString("children:\n ", "\n ", "")
}
object NormalChildrenContainer {
def apply(c: TreeMap[String, ChildStats]): ChildrenContainer =
if (c.isEmpty) EmptyChildrenContainer
else new NormalChildrenContainer(c)
}
/**
* Waiting state: there are outstanding termination requests (i.e. context.stop(child)
* was called but the corresponding ChildTerminated() system message has not yet been
* processed). There could be no specific reason (UserRequested), we could be Restarting
* or Terminating.
*
* Removing the last child which was supposed to be terminating will return a different
* type of container, depending on whether or not children are left and whether or not
* the reason was Terminating.
*/
case class TerminatingChildrenContainer(c: TreeMap[String, ChildStats], toDie: Set[ActorRef], reason: SuspendReason)
extends ChildrenContainer {
override def add(child: ActorRef): ChildrenContainer = copy(c.updated(child.path.name, ChildRestartStats(child)))
override def remove(child: ActorRef): ChildrenContainer = {
val t = toDie - child
if (t.isEmpty) reason match {
case Termination TerminatedChildrenContainer
case _ NormalChildrenContainer(c - child.path.name)
}
else copy(c - child.path.name, t)
}
override def getByName(name: String): Option[ChildRestartStats] = c.get(name) match {
case s @ Some(_: ChildRestartStats) s.asInstanceOf[Option[ChildRestartStats]]
case _ None
}
override def getByRef(actor: ActorRef): Option[ChildRestartStats] = c.get(actor.path.name) match {
case c @ Some(crs: ChildRestartStats) if (crs.child == actor) c.asInstanceOf[Option[ChildRestartStats]]
case _ None
}
override def children: Iterable[ActorRef] = c.values.view.collect { case ChildRestartStats(child, _, _) child }
override def stats: Iterable[ChildRestartStats] = c.values.view.collect { case c: ChildRestartStats c }
override def shallDie(actor: ActorRef): ChildrenContainer = copy(toDie = toDie + actor)
override def reserve(name: String): ChildrenContainer = reason match {
case Termination throw new IllegalStateException("cannot reserve actor name '" + name + "': terminating")
case _
if (c contains name)
throw new InvalidActorNameException("actor name " + name + " is not unique!")
else copy(c = c.updated(name, ChildNameReserved))
}
override def unreserve(name: String): ChildrenContainer = c.get(name) match {
case Some(ChildNameReserved) copy(c = c - name)
case _ this
}
override def isTerminating: Boolean = reason == Termination
override def isNormal: Boolean = reason == UserRequest
override def toString =
if (c.size > 20) c.size + " children"
else c.mkString("children (" + toDie.size + " terminating):\n ", "\n ", "\n") + toDie
}
}

View file

@ -0,0 +1,95 @@
/**
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.actor.cell
import akka.actor.{ Terminated, InternalActorRef, ActorRef, ActorCell, Actor }
import akka.dispatch.{ Watch, Unwatch }
import akka.event.Logging.{ Warning, Error, Debug }
import scala.util.control.NonFatal
private[akka] trait DeathWatch { this: ActorCell
private var watching: Set[ActorRef] = ActorCell.emptyActorRefSet
private var watchedBy: Set[ActorRef] = ActorCell.emptyActorRefSet
override final def watch(subject: ActorRef): ActorRef = subject match {
case a: InternalActorRef
if (a != self && !watching.contains(a)) {
a.sendSystemMessage(Watch(a, self)) // ➡➡➡ NEVER SEND THE SAME SYSTEM MESSAGE OBJECT TO TWO ACTORS ⬅⬅⬅
watching += a
}
a
}
override final def unwatch(subject: ActorRef): ActorRef = subject match {
case a: InternalActorRef
if (a != self && watching.contains(a)) {
a.sendSystemMessage(Unwatch(a, self)) // ➡➡➡ NEVER SEND THE SAME SYSTEM MESSAGE OBJECT TO TWO ACTORS ⬅⬅⬅
watching -= a
}
a
}
protected def watchedActorTerminated(ref: ActorRef): Unit = watching -= ref
protected def tellWatchersWeDied(actor: Actor): Unit = {
if (!watchedBy.isEmpty) {
val terminated = Terminated(self)(existenceConfirmed = true)
try {
watchedBy foreach {
watcher
try watcher.tell(terminated, self) catch {
case NonFatal(t) publish(Error(t, self.path.toString, clazz(actor), "deathwatch"))
}
}
} finally watchedBy = ActorCell.emptyActorRefSet
}
}
protected def unwatchWatchedActors(actor: Actor): Unit = {
if (!watching.isEmpty) {
try {
watching foreach { // ➡➡➡ NEVER SEND THE SAME SYSTEM MESSAGE OBJECT TO TWO ACTORS ⬅⬅⬅
case watchee: InternalActorRef try watchee.sendSystemMessage(Unwatch(watchee, self)) catch {
case NonFatal(t) publish(Error(t, self.path.toString, clazz(actor), "deathwatch"))
}
}
} finally watching = ActorCell.emptyActorRefSet
}
}
protected def addWatcher(watchee: ActorRef, watcher: ActorRef): Unit = {
val watcheeSelf = watchee == self
val watcherSelf = watcher == self
if (watcheeSelf && !watcherSelf) {
if (!watchedBy.contains(watcher)) {
watchedBy += watcher
if (system.settings.DebugLifecycle) publish(Debug(self.path.toString, clazz(actor), "now monitoring " + watcher))
}
} else if (!watcheeSelf && watcherSelf) {
watch(watchee)
} else {
publish(Warning(self.path.toString, clazz(actor), "BUG: illegal Watch(%s,%s) for %s".format(watchee, watcher, self)))
}
}
protected def remWatcher(watchee: ActorRef, watcher: ActorRef): Unit = {
val watcheeSelf = watchee == self
val watcherSelf = watcher == self
if (watcheeSelf && !watcherSelf) {
if (watchedBy.contains(watcher)) {
watchedBy -= watcher
if (system.settings.DebugLifecycle) publish(Debug(self.path.toString, clazz(actor), "stopped monitoring " + watcher))
}
} else if (!watcheeSelf && watcherSelf) {
unwatch(watchee)
} else {
publish(Warning(self.path.toString, clazz(actor), "BUG: illegal Unwatch(%s,%s) for %s".format(watchee, watcher, self)))
}
}
}

View file

@ -0,0 +1,78 @@
/**
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.actor.cell
import scala.annotation.tailrec
import akka.actor.{ ActorRef, ActorCell }
import akka.dispatch.{ Terminate, SystemMessage, Suspend, Resume, Recreate, MessageDispatcher, Mailbox, Envelope, Create }
import akka.util.Unsafe
private[akka] trait Dispatch { this: ActorCell
@volatile private var _mailboxDoNotCallMeDirectly: Mailbox = _ //This must be volatile since it isn't protected by the mailbox status
@inline final def mailbox: Mailbox = Unsafe.instance.getObjectVolatile(this, AbstractActorCell.mailboxOffset).asInstanceOf[Mailbox]
@tailrec final def swapMailbox(newMailbox: Mailbox): Mailbox = {
val oldMailbox = mailbox
if (!Unsafe.instance.compareAndSwapObject(this, AbstractActorCell.mailboxOffset, oldMailbox, newMailbox)) swapMailbox(newMailbox)
else oldMailbox
}
final def hasMessages: Boolean = mailbox.hasMessages
final def numberOfMessages: Int = mailbox.numberOfMessages
val dispatcher: MessageDispatcher = system.dispatchers.lookup(props.dispatcher)
/**
* UntypedActorContext impl
*/
final def getDispatcher(): MessageDispatcher = dispatcher
final def isTerminated: Boolean = mailbox.isClosed
final def start(sendSupervise: Boolean): this.type = {
/*
* Create the mailbox and enqueue the Create() message to ensure that
* this is processed before anything else.
*/
swapMailbox(dispatcher.createMailbox(this))
mailbox.setActor(this)
// ➡➡➡ NEVER SEND THE SAME SYSTEM MESSAGE OBJECT TO TWO ACTORS ⬅⬅⬅
mailbox.systemEnqueue(self, Create())
if (sendSupervise) {
// ➡➡➡ NEVER SEND THE SAME SYSTEM MESSAGE OBJECT TO TWO ACTORS ⬅⬅⬅
parent.sendSystemMessage(akka.dispatch.Supervise(self))
}
// This call is expected to start off the actor by scheduling its mailbox.
dispatcher.attach(this)
this
}
// ➡➡➡ NEVER SEND THE SAME SYSTEM MESSAGE OBJECT TO TWO ACTORS ⬅⬅⬅
final def suspend(): Unit = dispatcher.systemDispatch(this, Suspend())
// ➡➡➡ NEVER SEND THE SAME SYSTEM MESSAGE OBJECT TO TWO ACTORS ⬅⬅⬅
final def resume(inResponseToFailure: Boolean): Unit = dispatcher.systemDispatch(this, Resume(inResponseToFailure))
// ➡➡➡ NEVER SEND THE SAME SYSTEM MESSAGE OBJECT TO TWO ACTORS ⬅⬅⬅
final def restart(cause: Throwable): Unit = dispatcher.systemDispatch(this, Recreate(cause))
// ➡➡➡ NEVER SEND THE SAME SYSTEM MESSAGE OBJECT TO TWO ACTORS ⬅⬅⬅
final def stop(): Unit = dispatcher.systemDispatch(this, Terminate())
def tell(message: Any, sender: ActorRef): Unit =
dispatcher.dispatch(this, Envelope(message, if (sender eq null) system.deadLetters else sender, system))
override def sendSystemMessage(message: SystemMessage): Unit = dispatcher.systemDispatch(this, message)
}

View file

@ -0,0 +1,210 @@
/**
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.actor.cell
import scala.annotation.tailrec
import akka.actor.{ PreRestartException, PostRestartException, InternalActorRef, Failed, ActorRef, ActorInterruptedException, ActorCell, Actor }
import akka.dispatch.{ Envelope, ChildTerminated }
import akka.event.Logging.{ Warning, Error, Debug }
import scala.util.control.NonFatal
private[akka] trait FaultHandling { this: ActorCell
/* =================
* T H E R U L E S
* =================
*
* Actors can be suspended for two reasons:
* - they fail
* - their supervisor gets suspended
*
* In particular they are not suspended multiple times because of cascading
* own failures, i.e. while currentlyFailed() they do not fail again. In case
* of a restart, failures in constructor/preStart count as new failures.
*/
private def suspendNonRecursive(): Unit = dispatcher suspend this
private def resumeNonRecursive(): Unit = dispatcher resume this
/*
* have we told our supervisor that we Failed() and have not yet heard back?
* (actually: we might have heard back but not yet acted upon it, in case of
* a restart with dying children)
* might well be replaced by ref to a Cancellable in the future (see #2299)
*/
private var _failed = false
private def isFailed: Boolean = _failed
private def setFailed(): Unit = _failed = true
private def clearFailed(): Unit = _failed = false
/**
* Do re-create the actor in response to a failure.
*/
protected def faultRecreate(cause: Throwable): Unit =
if (isNormal) {
val failedActor = actor
if (system.settings.DebugLifecycle) publish(Debug(self.path.toString, clazz(failedActor), "restarting"))
if (failedActor ne null) {
val optionalMessage = if (currentMessage ne null) Some(currentMessage.message) else None
try {
// if the actor fails in preRestart, we can do nothing but log it: its best-effort
if (failedActor.context ne null) failedActor.preRestart(cause, optionalMessage)
} catch {
case NonFatal(e)
val ex = new PreRestartException(self, e, cause, optionalMessage)
publish(Error(ex, self.path.toString, clazz(failedActor), e.getMessage))
} finally {
clearActorFields(failedActor)
}
}
assert(mailbox.isSuspended, "mailbox must be suspended during restart, status=" + mailbox.status)
if (!setChildrenTerminationReason(ChildrenContainer.Recreation(cause))) finishRecreate(cause, failedActor)
} else {
// need to keep that suspend counter balanced
faultResume(inResponseToFailure = false)
}
/**
* Do suspend the actor in response to a failure of a parent (i.e. the
* recursive suspend feature).
*/
protected def faultSuspend(): Unit = {
// done always to keep that suspend counter balanced
suspendNonRecursive()
suspendChildren()
}
/**
* Do resume the actor in response to a failure.
*
* @param inResponseToFailure signifies if it was our own failure which
* prompted this action.
*/
protected def faultResume(inResponseToFailure: Boolean): Unit = {
// done always to keep that suspend counter balanced
// must happen atomically
try resumeNonRecursive()
finally if (inResponseToFailure) clearFailed()
resumeChildren()
}
protected def terminate() {
setReceiveTimeout(None)
cancelReceiveTimeout
// stop all children, which will turn childrenRefs into TerminatingChildrenContainer (if there are children)
children foreach stop
val wasTerminating = isTerminating
if (setChildrenTerminationReason(ChildrenContainer.Termination)) {
if (!wasTerminating) {
// do not process normal messages while waiting for all children to terminate
suspendNonRecursive()
// do not propagate failures during shutdown to the supervisor
setFailed()
if (system.settings.DebugLifecycle) publish(Debug(self.path.toString, clazz(actor), "stopping"))
}
} else {
setTerminated()
finishTerminate()
}
}
final def handleInvokeFailure(t: Throwable, message: String): Unit = {
publish(Error(t, self.path.toString, clazz(actor), message))
// prevent any further messages to be processed until the actor has been restarted
if (!isFailed) try {
suspendNonRecursive()
setFailed()
// suspend children
val skip: Set[ActorRef] = currentMessage match {
case Envelope(Failed(`t`), child) Set(child)
case _ Set.empty
}
suspendChildren(skip)
// tell supervisor
t match { // Wrap InterruptedExceptions and rethrow
case _: InterruptedException parent.tell(Failed(new ActorInterruptedException(t)), self); throw t
case _ parent.tell(Failed(t), self)
}
} catch {
case NonFatal(e)
publish(Error(e, self.path.toString, clazz(actor), "emergency stop: exception in failure handling"))
try children foreach stop
finally finishTerminate()
}
}
private def finishTerminate() {
val a = actor
try if (a ne null) a.postStop()
finally try dispatcher.detach(this)
finally try parent.sendSystemMessage(ChildTerminated(self))
finally try tellWatchersWeDied(a)
finally try unwatchWatchedActors(a)
finally {
if (system.settings.DebugLifecycle)
publish(Debug(self.path.toString, clazz(a), "stopped"))
clearActorFields(a)
actor = null
}
}
private def finishRecreate(cause: Throwable, failedActor: Actor): Unit = try {
try resumeNonRecursive()
finally clearFailed() // must happen in any case, so that failure is propagated
// need to keep a snapshot of the surviving children before the new actor instance creates new ones
val survivors = children
val freshActor = newActor()
actor = freshActor // this must happen before postRestart has a chance to fail
if (freshActor eq failedActor) setActorFields(freshActor, this, self) // If the creator returns the same instance, we need to restore our nulled out fields.
freshActor.postRestart(cause)
if (system.settings.DebugLifecycle) publish(Debug(self.path.toString, clazz(freshActor), "restarted"))
// only after parent is up and running again do restart the children which were not stopped
survivors foreach (child
try child.asInstanceOf[InternalActorRef].restart(cause)
catch {
case NonFatal(e) publish(Error(e, self.path.toString, clazz(freshActor), "restarting " + child))
})
} catch {
case NonFatal(e)
clearActorFields(actor) // in order to prevent preRestart() from happening again
handleInvokeFailure(new PostRestartException(self, e, cause), e.getMessage)
}
final protected def handleFailure(child: ActorRef, cause: Throwable): Unit = getChildByRef(child) match {
case Some(stats) if (!actor.supervisorStrategy.handleFailure(this, child, cause, stats, getAllChildStats)) throw cause
case None publish(Warning(self.path.toString, clazz(actor), "dropping Failed(" + cause + ") from unknown child " + child))
}
final protected def handleChildTerminated(child: ActorRef): Unit = {
val status = removeChildAndGetStateChange(child)
/*
* if this fails, we do nothing in case of terminating/restarting state,
* otherwise tell the supervisor etc. (in that second case, the match
* below will hit the empty default case, too)
*/
try actor.supervisorStrategy.handleChildTerminated(this, child, children)
catch {
case NonFatal(e) handleInvokeFailure(e, "handleChildTerminated failed")
}
/*
* if the removal changed the state of the (terminating) children container,
* then we are continuing the previously suspended recreate/terminate action
*/
status match {
case Some(ChildrenContainer.Recreation(cause)) finishRecreate(cause, actor)
case Some(ChildrenContainer.Termination) finishTerminate()
case _
}
}
}

View file

@ -0,0 +1,54 @@
/**
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.actor.cell
import ReceiveTimeout.emptyReceiveTimeoutData
import akka.actor.ActorCell
import akka.actor.ActorCell.emptyCancellable
import akka.actor.Cancellable
import scala.concurrent.util.Duration
private[akka] object ReceiveTimeout {
final val emptyReceiveTimeoutData: (Duration, Cancellable) = (Duration.Undefined, ActorCell.emptyCancellable)
}
private[akka] trait ReceiveTimeout { this: ActorCell
import ReceiveTimeout._
import ActorCell._
private var receiveTimeoutData: (Duration, Cancellable) = emptyReceiveTimeoutData
final def receiveTimeout: Option[Duration] = receiveTimeoutData._1 match {
case Duration.Undefined None
case duration Some(duration)
}
final def setReceiveTimeout(timeout: Option[Duration]): Unit = setReceiveTimeout(timeout.getOrElse(Duration.Undefined))
final def setReceiveTimeout(timeout: Duration): Unit =
receiveTimeoutData = (
if (Duration.Undefined == timeout || timeout.toMillis < 1) Duration.Undefined else timeout,
receiveTimeoutData._2)
final def resetReceiveTimeout(): Unit = setReceiveTimeout(None)
final def checkReceiveTimeout() {
val recvtimeout = receiveTimeoutData
if (Duration.Undefined != recvtimeout._1 && !mailbox.hasMessages) {
recvtimeout._2.cancel() //Cancel any ongoing future
//Only reschedule if desired and there are currently no more messages to be processed
receiveTimeoutData = (recvtimeout._1, system.scheduler.scheduleOnce(recvtimeout._1, self, akka.actor.ReceiveTimeout))
} else cancelReceiveTimeout()
}
final def cancelReceiveTimeout(): Unit =
if (receiveTimeoutData._2 ne emptyCancellable) {
receiveTimeoutData._2.cancel()
receiveTimeoutData = (receiveTimeoutData._1, emptyCancellable)
}
}

View file

@ -4,6 +4,8 @@
package akka
import language.implicitConversions
package object actor {
implicit def actorRef2Scala(ref: ActorRef): ScalaActorRef = ref.asInstanceOf[ScalaActorRef]
implicit def scala2ActorRef(ref: ScalaActorRef): ActorRef = ref.asInstanceOf[ActorRef]

View file

@ -5,16 +5,17 @@
package akka.dispatch
import java.util.concurrent._
import akka.event.Logging.Error
import akka.event.Logging.{ Error, LogEventException }
import akka.actor._
import akka.actor.ActorSystem
import scala.annotation.tailrec
import akka.event.EventStream
import com.typesafe.config.Config
import akka.serialization.SerializationExtension
import akka.event.Logging.LogEventException
import akka.jsr166y.{ ForkJoinTask, ForkJoinPool }
import akka.util.{ Unsafe, Duration, NonFatal, Index }
import akka.util.{ Unsafe, Index }
import scala.annotation.tailrec
import scala.concurrent.forkjoin.{ ForkJoinTask, ForkJoinPool }
import scala.concurrent.util.Duration
import scala.concurrent.{ ExecutionContext, Await, Awaitable }
import scala.util.control.NonFatal
final case class Envelope private (val message: Any, val sender: ActorRef)
@ -89,7 +90,7 @@ private[akka] case class Suspend() extends SystemMessage // sent to self from Ac
/**
* INTERNAL API
*/
private[akka] case class Resume() extends SystemMessage // sent to self from ActorCell.resume
private[akka] case class Resume(inResponseToFailure: Boolean) extends SystemMessage // sent to self from ActorCell.resume
/**
* INTERNAL API
*/
@ -122,88 +123,6 @@ final case class TaskInvocation(eventStream: EventStream, runnable: Runnable, cl
} finally cleanup()
}
/**
* Java API to create ExecutionContexts
*/
object ExecutionContexts {
/**
* Creates an ExecutionContext from the given ExecutorService
*/
def fromExecutorService(e: ExecutorService): ExecutionContextExecutorService =
new ExecutionContext.WrappedExecutorService(e)
/**
* Creates an ExecutionContext from the given Executor
*/
def fromExecutor(e: Executor): ExecutionContextExecutor =
new ExecutionContext.WrappedExecutor(e)
}
object ExecutionContext {
implicit def defaultExecutionContext(implicit system: ActorSystem): ExecutionContext = system.dispatcher
/**
* Creates an ExecutionContext from the given ExecutorService
*/
def fromExecutorService(e: ExecutorService): ExecutionContext with ExecutorService = new WrappedExecutorService(e)
/**
* Creates an ExecutionContext from the given Executor
*/
def fromExecutor(e: Executor): ExecutionContext with Executor = new WrappedExecutor(e)
/**
* Internal Akka use only
*/
private[akka] class WrappedExecutorService(val executor: ExecutorService) extends ExecutorServiceDelegate with ExecutionContextExecutorService {
override def reportFailure(t: Throwable): Unit = t match {
case e: LogEventException e.getCause.printStackTrace()
case _ t.printStackTrace()
}
}
/**
* Internal Akka use only
*/
private[akka] class WrappedExecutor(val executor: Executor) extends ExecutionContextExecutor {
override final def execute(runnable: Runnable): Unit = executor.execute(runnable)
override def reportFailure(t: Throwable): Unit = t match {
case e: LogEventException e.getCause.printStackTrace()
case _ t.printStackTrace()
}
}
}
/**
* Union interface since Java does not support union types
*/
trait ExecutionContextExecutor extends ExecutionContext with Executor
/**
* Union interface since Java does not support union types
*/
trait ExecutionContextExecutorService extends ExecutionContextExecutor with ExecutorService
/**
* An ExecutionContext is essentially the same thing as a java.util.concurrent.Executor
* This interface/trait exists to decouple the concept of execution from Actors & MessageDispatchers
* It is also needed to provide a fallback implicit default instance (in the companion object).
*/
trait ExecutionContext {
/**
* Submits the runnable for execution
*/
def execute(runnable: Runnable): Unit
/**
* Failed tasks should call reportFailure to let the ExecutionContext
* log the problem or whatever is appropriate for the implementation.
*/
def reportFailure(t: Throwable): Unit
}
/**
* INTERNAL API
*/
@ -226,8 +145,7 @@ private[akka] object MessageDispatcher {
def printActors: Unit = if (debug) {
for {
d actors.keys
val c = println(d + " inhabitants: " + d.inhabitants)
a actors.valueIterator(d)
a { println(d + " inhabitants: " + d.inhabitants); actors.valueIterator(d) }
} {
val status = if (a.isTerminated) " (terminated)" else " (alive)"
val messages = a match {
@ -289,25 +207,21 @@ abstract class MessageDispatcher(val prerequisites: DispatcherPrerequisites) ext
/**
* Detaches the specified actor instance from this dispatcher
*/
final def detach(actor: ActorCell): Unit = try {
unregister(actor)
} finally {
ifSensibleToDoSoThenScheduleShutdown()
}
final def detach(actor: ActorCell): Unit = try unregister(actor) finally ifSensibleToDoSoThenScheduleShutdown()
final def execute(runnable: Runnable): Unit = {
final override def execute(runnable: Runnable): Unit = {
val invocation = TaskInvocation(eventStream, runnable, taskCleanup)
addInhabitants(+1)
try {
executeTask(invocation)
} catch {
case t
case t: Throwable
addInhabitants(-1)
throw t
}
}
def reportFailure(t: Throwable): Unit = t match {
override def reportFailure(t: Throwable): Unit = t match {
case e: LogEventException prerequisites.eventStream.publish(e.event)
case _ prerequisites.eventStream.publish(Error(t, getClass.getName, getClass, t.getMessage))
}
@ -392,7 +306,7 @@ abstract class MessageDispatcher(val prerequisites: DispatcherPrerequisites) ext
def suspend(actor: ActorCell): Unit = {
val mbox = actor.mailbox
if ((mbox.actor eq actor) && (mbox.dispatcher eq this))
mbox.becomeSuspended()
mbox.suspend()
}
/*
@ -400,7 +314,7 @@ abstract class MessageDispatcher(val prerequisites: DispatcherPrerequisites) ext
*/
def resume(actor: ActorCell): Unit = {
val mbox = actor.mailbox
if ((mbox.actor eq actor) && (mbox.dispatcher eq this) && mbox.becomeOpen())
if ((mbox.actor eq actor) && (mbox.dispatcher eq this) && mbox.resume())
registerForExecution(mbox, false, false)
}
@ -576,7 +490,7 @@ object ForkJoinExecutorConfigurator {
final override def setRawResult(u: Unit): Unit = ()
final override def getRawResult(): Unit = ()
final override def exec(): Boolean = try { mailbox.run; true } catch {
case anything
case anything: Throwable
val t = Thread.currentThread
t.getUncaughtExceptionHandler match {
case null

View file

@ -5,8 +5,9 @@
package akka.dispatch
import akka.actor.{ ActorCell, ActorRef }
import annotation.tailrec
import akka.util.{ Duration, Helpers }
import scala.annotation.tailrec
import scala.concurrent.util.Duration
import akka.util.Helpers
import java.util.{ Comparator, Iterator }
import java.util.concurrent.{ Executor, LinkedBlockingQueue, ConcurrentLinkedQueue, ConcurrentSkipListSet }
import akka.actor.ActorSystemImpl

View file

@ -0,0 +1,116 @@
/**
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.dispatch
import java.util.concurrent.{ Executor }
import scala.concurrent._
import scala.annotation.tailrec
/**
* All Batchables are automatically batched when submitted to a BatchingExecutor
*/
private[akka] trait Batchable extends Runnable
/**
* Mixin trait for an Executor
* which groups multiple nested `Runnable.run()` calls
* into a single Runnable passed to the original
* Executor. This can be a useful optimization
* because it bypasses the original context's task
* queue and keeps related (nested) code on a single
* thread which may improve CPU affinity. However,
* if tasks passed to the Executor are blocking
* or expensive, this optimization can prevent work-stealing
* and make performance worse. Also, some ExecutionContext
* may be fast enough natively that this optimization just
* adds overhead.
* The default ExecutionContext.global is already batching
* or fast enough not to benefit from it; while
* `fromExecutor` and `fromExecutorService` do NOT add
* this optimization since they don't know whether the underlying
* executor will benefit from it.
* A batching executor can create deadlocks if code does
* not use `scala.concurrent.blocking` when it should,
* because tasks created within other tasks will block
* on the outer task completing.
* This executor may run tasks in any order, including LIFO order.
* There are no ordering guarantees.
*
* WARNING: The underlying Executor's execute-method must not execute the submitted Runnable
* in the calling thread synchronously. It must enqueue/handoff the Runnable.
*/
private[akka] trait BatchingExecutor extends Executor {
// invariant: if "_tasksLocal.get ne null" then we are inside BatchingRunnable.run; if it is null, we are outside
private val _tasksLocal = new ThreadLocal[List[Runnable]]()
private class Batch(val initial: List[Runnable]) extends Runnable with BlockContext {
private var parentBlockContext: BlockContext = _
// this method runs in the delegate ExecutionContext's thread
override def run(): Unit = {
require(_tasksLocal.get eq null)
val prevBlockContext = BlockContext.current
BlockContext.withBlockContext(this) {
try {
parentBlockContext = prevBlockContext
@tailrec def processBatch(batch: List[Runnable]): Unit = batch match {
case Nil ()
case head :: tail
_tasksLocal set tail
try {
head.run()
} catch {
case t: Throwable
// if one task throws, move the
// remaining tasks to another thread
// so we can throw the exception
// up to the invoking executor
val remaining = _tasksLocal.get
_tasksLocal set Nil
unbatchedExecute(new Batch(remaining)) //TODO what if this submission fails?
throw t // rethrow
}
processBatch(_tasksLocal.get) // since head.run() can add entries, always do _tasksLocal.get here
}
processBatch(initial)
} finally {
_tasksLocal.remove()
parentBlockContext = null
}
}
}
override def blockOn[T](thunk: T)(implicit permission: CanAwait): T = {
// if we know there will be blocking, we don't want to keep tasks queued up because it could deadlock.
{
val tasks = _tasksLocal.get
_tasksLocal set Nil
if ((tasks ne null) && tasks.nonEmpty)
unbatchedExecute(new Batch(tasks))
}
// now delegate the blocking to the previous BC
require(parentBlockContext ne null)
parentBlockContext.blockOn(thunk)
}
}
protected def unbatchedExecute(r: Runnable): Unit = super.execute(r)
abstract override def execute(runnable: Runnable): Unit = {
if (batchable(runnable)) { // If we can batch the runnable
_tasksLocal.get match {
case null unbatchedExecute(new Batch(List(runnable))) // If we aren't in batching mode yet, enqueue batch
case some _tasksLocal.set(runnable :: some) // If we are already in batching mode, add to batch
}
} else unbatchedExecute(runnable) // If not batchable, just delegate to underlying
}
/** Override this to define which runnables will be batched. */
def batchable(runnable: Runnable): Boolean = runnable.isInstanceOf[Batchable]
}

View file

@ -5,11 +5,13 @@
package akka.dispatch
import akka.event.Logging.Error
import java.util.concurrent.atomic.AtomicReference
import akka.actor.ActorCell
import akka.util.Duration
import java.util.concurrent._
import akka.event.Logging
import java.util.concurrent.atomic.AtomicReference
import java.util.concurrent.{ ExecutorService, RejectedExecutionException }
import scala.concurrent.forkjoin.ForkJoinPool
import scala.concurrent.util.Duration
import scala.concurrent.Awaitable
/**
* The event-based ``Dispatcher`` binds a set of Actors to a thread pool backed up by a

View file

@ -9,7 +9,7 @@ import com.typesafe.config.{ ConfigFactory, Config }
import akka.actor.{ Scheduler, DynamicAccess, ActorSystem }
import akka.event.Logging.Warning
import akka.event.EventStream
import akka.util.Duration
import scala.concurrent.util.Duration
/**
* DispatcherPrerequisites represents useful contextual pieces when constructing a MessageDispatcher

Some files were not shown because too many files have changed in this diff Show more