Merge branch 'master' into wip-2605-java-pattern-ricklatrine

Conflicts:
	akka-docs/rst/java/howto.rst
This commit is contained in:
RickLatrine 2012-12-08 11:49:06 +01:00
commit c17b1eb263
464 changed files with 7889 additions and 3723 deletions

2
.gitignore vendored
View file

@ -7,6 +7,7 @@ project/plugins/project
project/boot/*
*/project/build/target
*/project/boot
*/project/project.target.config-classes
lib_managed
etags
tags
@ -67,3 +68,4 @@ redis/
beanstalk/
.scalastyle
bin/
.worksheet

View file

@ -1,63 +1,97 @@
#Contributing to Akka#
# Contributing to Akka
Greetings traveller!
## Infrastructure
##Infrastructure##
* [Akka Contributor License Agreement](www.typesafe.com/contribute/cla)
* [Akka Contributor License Agreement](http://www.typesafe.com/contribute/cla)
* [Akka Issue Tracker](http://doc.akka.io/docs/akka/current/project/issue-tracking.html)
* [Scalariform](https://github.com/mdr/scalariform)
##Workflow##
# Typesafe Project & Developer Guidelines
0. Sign the Akka Contributor License Agreement,
we won't accept anything from anybody who has not signed it.
1. Find-or-create a ticket in the issue tracker
2. Assign that ticket to yourself
3. Create a local branch with the following name format: wip-X-Y-Z
where the X is the number of the ticket in the tracker,
and Y is some brief keywords of the ticket title and Z is your initials or similar.
Example: wip-2373-add-contributing-md-√
4. Do what needs to be done (with tests and docs if applicable).
Your branch should pass all tests before going any further.
5. Push the branch to your clone of the Akka repository
6. Create a Pull Request onto the applicable Akka branch,
if the number of commits are more than a few, please squash the
commits first.
7. Change the status of your ticket to "Test"
8. The Pull Request will be reviewed by the Akka committers
9. Modify the Pull Request as agreed upon during the review,
then push the changes to your branch in your Akka repository,
the Pull Request should be automatically updated with the new
content.
10. Several cycles of review-then-change might occur.
11. Pull Request is either merged by the Akka committers,
or rejected, and the associated ticket will be updated to
reflect that.
12. Delete the local and remote wip-X-Y-Z
These guidelines are meant to be a living document that should be changed and adapted as needed. We encourage changes that makes it easier to achieve our goals in an efficient way.
##Code Reviews##
These guidelines mainly applies to Typesafes “mature” projects - not necessarily to projects of the type collection of scripts etc.
Akka utilizes peer code reviews to streamline the codebase, reduce the defect ratio,
increase maintainability and spread knowledge about how things are solved.
## General Workflow
Core review values:
This is the process for committing code into master. There are of course exceptions to these rules, for example minor changes to comments and documentation, fixing a broken build etc.
* Rule: [The Boy Scout Rule](http://programmer.97things.oreilly.com/wiki/index.php/The_Boy_Scout_Rule)
- Why: Small improvements add up over time, keeping the codebase in shape.
* Rule: [Don't Repeat Yourself](http://programmer.97things.oreilly.com/wiki/index.php/Don't_Repeat_Yourself)
- Why: Repetitions are not maintainable, keeping things DRY makes it easier to fix bugs and refactor,
since you only need to apply the correction in one place, or perform the refactoring at one place.
* Rule: Feature tests > Integration tests > Unit tests
- Why: Without proving that a feature works, the code is only liability.
Without proving that a feature works with other features, the code is of limited value.
Without proving the individual parts of a feature works, the code is harder to debug.
1. Make sure you have signed the [Typesafe CLA](http://www.typesafe.com/contribute/cla), if not, sign it online.
2. Before starting to work on a feature or a fix, you have to make sure that:
1. There is a ticket for your work in the project's issue tracker. If not, create it first.
2. The ticket has been scheduled for the current milestone.
3. The ticket is estimated by the team.
4. The ticket have been discussed and prioritized by the team.
3. You should always perform your work in a Git feature branch. The branch should be given a descriptive name that explains its intent. Some teams also like adding the ticket number and/or the [GitHub](http://github.com) user ID to the branch name, these details is up to each of the individual teams.
4. When the feature or fix is completed you should open a [Pull Request](https://help.github.com/articles/using-pull-requests) on GitHub.
5. The Pull Request should be reviewed by other maintainers (as many as feasible/practical). Note that the maintainers can consist of outside contributors, both within and outside Typesafe. Outside contributors (for example from EPFL or independent committers) are encouraged to participate in the review process, it is not a closed process.
6. After the review you should fix the issues as needed (pushing a new commit for new review etc.), iterating until the reviewers give their thumbs up.
7. Once the code has passed review the Pull Request can be merged into the master branch.
##Source style##
## Pull Request Requirements
For a Pull Request to be considered at all it has to meet these requirements:
1. Live up to the current code standard:
- Not violate [DRY](http://programmer.97things.oreilly.com/wiki/index.php/Don%27t_Repeat_Yourself).
- [Boy Scout Rule](http://programmer.97things.oreilly.com/wiki/index.php/The_Boy_Scout_Rule) needs to have been applied.
2. Regardless if the code introduces new features or fixes bugs or regressions, it must have comprehensive tests.
3. The code must be well documented in the Typesafe's standard documentation format (see the Documentation section below).
If these requirements are not met then the code should **not** be merged into master, or even reviewed - regardless of how good or important it is. No exceptions.
## Continuous Integration
Each project should be configured to use a continuous integration (CI) tool (i.e. a build server ala Jenkins). Typesafe has a Jenkins server farm that can be used. The CI tool should, on each push to master, build the **full** distribution and run **all** tests, and if something fails it should email out a notification with the failure report to the committer and the core team. The CI tool should also be used in conjunction with Typesafes Pull Request Validator (discussed below).
## Documentation
All documentation should be generated using the sbt-site-plugin, *or* publish artifacts to a repository that can be consumed by the typesafe stack.
All documentation must abide by the following maxims:
- Example code should be run as part of an automated test suite.
- Version should be **programmatically** specifiable to the build.
- Generation should be **completely automated** and available for scripting.
- Artifacts that must be included in the Typesafe Stack should be published to a maven “documentation” repository as documentation artifacts.
All documentation is preferred to be in Typesafe's standard documentation format [reStructuredText](http://doc.akka.io/docs/akka/snapshot/dev/documentation.html) compiled using Typesafe's customized [Sphinx](http://sphinx.pocoo.org/) based documentation generation system, which among other things allows all code in the documentation to be externalized into compiled files and imported into the documentation.
For more info, or for a starting point for new projects, look at the [Typesafe Documentation Template project](https://github.com/typesafehub/doc-template).
For larger projects that have invested a lot of time and resources into their current documentation and samples scheme (like for example Play), it is understandable that it will take some time to migrate to this new model. In these cases someone from the project needs to take the responsibility of manual QA and verifier for the documentation and samples.
## Work In Progress
It is ok to work on a public feature branch in the GitHub repository. Something that can sometimes be useful for early feedback etc. If so then it is preferable to name the branch accordingly. This can be done by either prefix the name with ``wip-`` as in Work In Progress, or use hierarchical names like ``wip/..``, ``feature/..`` or ``topic/..``. Either way is fine as long as it is clear that it is work in progress and not ready for merge. This work can temporarily have a lower standard. However, to be merged into master it will have to go through the regular process outlined above, with Pull Request, review etc..
Also, to facilitate both well-formed commits and working together, the ``wip`` and ``feature``/``topic`` identifiers also have special meaning. Any branch labelled with ``wip`` is considered “git-unstable” and may be rebased and have its history rewritten. Any branch with ``feature``/``topic`` in the name is considered “stable” enough for others to depend on when a group is working on a feature.
## Creating Commits And Writing Commit Messages
Follow these guidelines when creating public commits and writing commit messages.
1. If your work spans multiple local commits (for example; if you do safe point commits while working in a feature branch or work in a branch for long time doing merges/rebases etc.) then please do not commit it all but rewrite the history by squashing the commits into a single big commit which you write a good commit message for (like discussed in the following sections). For more info read this article: [Git Workflow](http://sandofsky.com/blog/git-workflow.html). Every commit should be able to be used in isolation, cherry picked etc.
2. First line should be a descriptive sentence what the commit is doing. It should be possible to fully understand what the commit does by just reading this single line. It is **not ok** to only list the ticket number, type "minor fix" or similar. Include reference to ticket number, prefixed with #, at the end of the first line. If the commit is a small fix, then you are done. If not, go to 3.
3. Following the single line description should be a blank line followed by an enumerated list with the details of the commit.
4. Add keywords for your commit (depending on the degree of automation we reach, the list may change over time):
* ``Review by @gituser`` - if you want to notify someone on the team. The others can, and are encouraged to participate.
* ``Fix/Fixing/Fixes/Close/Closing/Refs #ticket`` - if you want to mark the ticket as fixed in the issue tracker (Assembla understands this).
* ``backport to _branch name_`` - if the fix needs to be cherry-picked to another branch (like 2.9.x, 2.10.x, etc)
Example:
Adding monadic API to Future. Fixes #2731
* Details 1
* Details 2
* Details 3
## Source style
Akka uses [Scalariform](https://github.com/mdr/scalariform) to enforce some of the code style rules.
##Contributing Modules##
## Contributing Modules
For external contributions of entire features, the normal way is to establish it
as a stand-alone feature first, to show that there is a need for the feature. The

View file

@ -7,7 +7,7 @@ import akka.japi.*;
import scala.concurrent.Await;
import scala.concurrent.Future;
import scala.concurrent.Promise;
import scala.concurrent.util.Duration;
import scala.concurrent.duration.Duration;
import akka.testkit.TestKitExtension;
import org.junit.AfterClass;
import org.junit.BeforeClass;

View file

@ -1,5 +1,7 @@
package akka.japi;
import akka.event.LoggingAdapter;
import akka.event.NoLogging;
import org.junit.Test;
import static org.junit.Assert.*;
@ -46,4 +48,10 @@ public class JavaAPITestBase {
public void shouldBeSingleton() {
assertSame(Option.none(), Option.none());
}
@Test
public void mustBeAbleToGetNoLogging() {
LoggingAdapter a = NoLogging.getInstance();
assertNotNull(a);
}
}

View file

@ -15,7 +15,8 @@ public class CustomRouteTest {
// only to test compilability
public void testRoute() {
final ActorRef ref = system.actorOf(new Props().withRouter(new RoundRobinRouter(1)));
final scala.Function1<scala.Tuple2<ActorRef, Object>, scala.collection.Iterable<Destination>> route = ExtractRoute.apply(ref);
final scala.Function1<scala.Tuple2<ActorRef, Object>,
scala.collection.immutable.Iterable<Destination>> route = ExtractRoute.apply(ref);
route.apply(null);
}

View file

@ -4,14 +4,14 @@
package akka.util;
import org.junit.Test;
import scala.concurrent.util.Duration;
import scala.concurrent.duration.Duration;
public class JavaDuration {
@Test
public void testCreation() {
final Duration fivesec = Duration.create(5, "seconds");
final Duration threemillis = Duration.parse("3 millis");
final Duration threemillis = Duration.create("3 millis");
final Duration diff = fivesec.minus(threemillis);
assert diff.lt(fivesec);
assert Duration.Zero().lteq(Duration.Inf());

View file

@ -8,7 +8,7 @@ import language.postfixOps
import akka.testkit._
import akka.testkit.DefaultTimeout
import akka.testkit.TestEvent._
import scala.concurrent.util.duration._
import scala.concurrent.duration._
import akka.routing._
import org.scalatest.BeforeAndAfterEach
import akka.ConfigurationException

View file

@ -12,7 +12,7 @@ import akka.actor.ActorDSL._
//#import
import akka.event.Logging.Warning
import scala.concurrent.{ Await, Future }
import scala.concurrent.util.duration._
import scala.concurrent.duration._
import java.util.concurrent.TimeoutException
class ActorDSLSpec extends AkkaSpec {
@ -103,6 +103,32 @@ class ActorDSLSpec extends AkkaSpec {
i.receive() must be("hi")
}
"support becomeStacked" in {
//#becomeStacked
val a = actor(new Act {
become { // this will replace the initial (empty) behavior
case "info" sender ! "A"
case "switch"
becomeStacked { // this will stack upon the "A" behavior
case "info" sender ! "B"
case "switch" unbecome() // return to the "A" behavior
}
case "lobotomize" unbecome() // OH NOES: Actor.emptyBehavior
}
})
//#becomeStacked
implicit def sender = testActor
a ! "info"
expectMsg("A")
a ! "switch"
a ! "info"
expectMsg("B")
a ! "switch"
a ! "info"
expectMsg("A")
}
"support setup/teardown" in {
//#simple-start-stop
val a = actor(new Act {
@ -188,7 +214,7 @@ class ActorDSLSpec extends AkkaSpec {
become {
case 1 stash()
case 2
testActor ! 2; unstashAll(); become {
testActor ! 2; unstashAll(); becomeStacked {
case 1 testActor ! 1; unbecome()
}
}

View file

@ -6,7 +6,7 @@ package akka.actor
import akka.testkit._
import org.scalatest.BeforeAndAfterEach
import scala.concurrent.util.duration._
import scala.concurrent.duration._
import scala.concurrent.Await
import akka.pattern.ask

View file

@ -11,7 +11,7 @@ import org.scalatest.matchers.MustMatchers
import akka.actor.Actor._
import akka.testkit._
import scala.concurrent.util.duration._
import scala.concurrent.duration._
import java.util.concurrent.atomic._
import scala.concurrent.Await
import akka.pattern.ask

View file

@ -6,7 +6,7 @@ package akka.actor
import language.postfixOps
import akka.testkit._
import scala.concurrent.util.duration._
import scala.concurrent.duration._
import scala.concurrent.Await
import akka.pattern.ask
import java.net.MalformedURLException

View file

@ -11,7 +11,7 @@ import org.scalatest.matchers.MustMatchers
import akka.testkit._
import akka.util.Timeout
import scala.concurrent.util.duration._
import scala.concurrent.duration._
import scala.concurrent.Await
import java.lang.IllegalStateException
import scala.concurrent.Promise

View file

@ -8,12 +8,16 @@ import akka.testkit._
import org.scalatest.junit.JUnitSuite
import com.typesafe.config.ConfigFactory
import scala.concurrent.Await
import scala.concurrent.util.duration._
import scala.collection.JavaConverters
import java.util.concurrent.{ TimeUnit, RejectedExecutionException, CountDownLatch, ConcurrentLinkedQueue }
import scala.concurrent.duration._
import java.util.concurrent.{ RejectedExecutionException, ConcurrentLinkedQueue }
import akka.util.Timeout
import akka.japi.Util.immutableSeq
import scala.concurrent.Future
import akka.pattern.ask
import akka.dispatch._
import com.typesafe.config.Config
import java.util.concurrent.{ LinkedBlockingQueue, BlockingQueue, TimeUnit }
import akka.util.Switch
class JavaExtensionSpec extends JavaExtension with JUnitSuite
@ -67,10 +71,57 @@ object ActorSystemSpec {
}
}
case class FastActor(latch: TestLatch, testActor: ActorRef) extends Actor {
val ref1 = context.actorOf(Props.empty)
val ref2 = context.actorFor(ref1.path.toString)
testActor ! ref2.getClass
latch.countDown()
def receive = {
case _
}
}
class SlowDispatcher(_config: Config, _prerequisites: DispatcherPrerequisites) extends MessageDispatcherConfigurator(_config, _prerequisites) {
private val instance = 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)) {
val doneIt = new Switch
override protected[akka] def registerForExecution(mbox: Mailbox, hasMessageHint: Boolean, hasSystemMessageHint: Boolean): Boolean = {
val ret = super.registerForExecution(mbox, hasMessageHint, hasSystemMessageHint)
doneIt.switchOn {
TestKit.awaitCond(mbox.actor.actor != null, 1.second)
mbox.actor.actor match {
case FastActor(latch, _) Await.ready(latch, 1.second)
}
}
ret
}
}
/**
* Returns the same dispatcher instance for each invocation
*/
override def dispatcher(): MessageDispatcher = instance
}
val config = s"""
akka.extensions = ["akka.actor.TestExtension"]
slow {
type="${classOf[SlowDispatcher].getName}"
}"""
}
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
class ActorSystemSpec extends AkkaSpec("""akka.extensions = ["akka.actor.TestExtension"]""") with ImplicitSender {
class ActorSystemSpec extends AkkaSpec(ActorSystemSpec.config) with ImplicitSender {
import ActorSystemSpec.FastActor
"An ActorSystem" must {
@ -102,8 +153,6 @@ class ActorSystemSpec extends AkkaSpec("""akka.extensions = ["akka.actor.TestExt
}
"run termination callbacks in order" in {
import scala.collection.JavaConverters._
val system2 = ActorSystem("TerminationCallbacks", AkkaSpec.testConf)
val result = new ConcurrentLinkedQueue[Int]
val count = 10
@ -121,13 +170,11 @@ class ActorSystemSpec extends AkkaSpec("""akka.extensions = ["akka.actor.TestExt
Await.ready(latch, 5 seconds)
val expected = (for (i 1 to count) yield i).reverse
result.asScala.toSeq must be(expected)
immutableSeq(result) must be(expected)
}
"awaitTermination after termination callbacks" in {
import scala.collection.JavaConverters._
val system2 = ActorSystem("AwaitTermination", AkkaSpec.testConf)
@volatile
var callbackWasRun = false
@ -168,6 +215,11 @@ class ActorSystemSpec extends AkkaSpec("""akka.extensions = ["akka.actor.TestExt
Await.result(Future.sequence(waves), timeout.duration + 5.seconds) must be === Seq("done", "done", "done")
}
"find actors that just have been created" in {
system.actorOf(Props(new FastActor(TestLatch(), testActor)).withDispatcher("slow"))
expectMsgType[Class[_]] must be(classOf[LocalActorRef])
}
"reliable deny creation of actors while shutting down" in {
val system = ActorSystem()
import system.dispatcher

View file

@ -3,7 +3,7 @@
*/
package akka.actor
import scala.concurrent.util.duration._
import scala.concurrent.duration._
import akka.testkit._
import akka.testkit.TestEvent._
import scala.concurrent.Await

View file

@ -11,7 +11,7 @@ import akka.testkit.TestEvent._
import akka.dispatch.BoundedDequeBasedMailbox
import akka.pattern.ask
import scala.concurrent.Await
import scala.concurrent.util.duration._
import scala.concurrent.duration._
import akka.actor.ActorSystem.Settings
import com.typesafe.config.{ Config, ConfigFactory }
import org.scalatest.Assertions.intercept

View file

@ -10,7 +10,7 @@ import akka.testkit.DefaultTimeout
import akka.testkit.TestEvent._
import scala.concurrent.Await
import akka.pattern.ask
import scala.concurrent.util.duration._
import scala.concurrent.duration._
import com.typesafe.config.{ Config, ConfigFactory }
import org.scalatest.BeforeAndAfterEach
import org.scalatest.junit.JUnitSuite

View file

@ -4,7 +4,7 @@ import language.postfixOps
import akka.testkit.AkkaSpec
import akka.dispatch.UnboundedMailbox
import scala.concurrent.util.duration._
import scala.concurrent.duration._
object ConsistencySpec {
val config = """

View file

@ -6,7 +6,7 @@ package akka.actor
import language.postfixOps
import akka.testkit._
import scala.concurrent.util.duration._
import scala.concurrent.duration._
import java.util.concurrent.atomic._
import scala.concurrent.Await
import akka.pattern.ask

View file

@ -10,7 +10,7 @@ import akka.testkit.AkkaSpec
import com.typesafe.config.ConfigFactory
import com.typesafe.config.ConfigParseOptions
import akka.routing._
import scala.concurrent.util.duration._
import scala.concurrent.duration._
object DeployerSpec {
val deployerConf = ConfigFactory.parseString("""

View file

@ -8,13 +8,14 @@ import language.postfixOps
import org.scalatest.{ BeforeAndAfterAll, BeforeAndAfterEach }
import akka.testkit._
import TestEvent.Mute
import scala.concurrent.util.duration._
import scala.concurrent.duration._
import akka.event._
import com.typesafe.config.ConfigFactory
import scala.concurrent.Await
import akka.util.Timeout
import scala.concurrent.util.Duration
import scala.concurrent.util.FiniteDuration
import org.scalatest.matchers.Matcher
import org.scalatest.matchers.HavePropertyMatcher
import org.scalatest.matchers.HavePropertyMatchResult
object FSMActorSpec {
val timeout = Timeout(2 seconds)
@ -201,6 +202,45 @@ class FSMActorSpec extends AkkaSpec(Map("akka.actor.debug.fsm" -> true)) with Im
expectMsg(1 second, fsm.StopEvent(FSM.Shutdown, 1, null))
}
"cancel all timers when terminated" in {
val timerNames = List("timer-1", "timer-2", "timer-3")
// Lazy so fsmref can refer to checkTimersActive
lazy val fsmref = TestFSMRef(new Actor with FSM[String, Null] {
startWith("not-started", null)
when("not-started") {
case Event("start", _) goto("started") replying "starting"
}
when("started", stateTimeout = 10 seconds) {
case Event("stop", _) stop()
}
onTransition {
case "not-started" -> "started"
for (timerName timerNames) setTimer(timerName, (), 10 seconds, false)
}
onTermination {
case _ {
checkTimersActive(false)
testActor ! "stopped"
}
}
})
def checkTimersActive(active: Boolean) {
for (timer timerNames) fsmref.isTimerActive(timer) must be(active)
fsmref.isStateTimerActive must be(active)
}
checkTimersActive(false)
fsmref ! "start"
expectMsg(1 second, "starting")
checkTimersActive(true)
fsmref ! "stop"
expectMsg(1 second, "stopped")
}
"log events and transitions if asked to do so" in {
import scala.collection.JavaConverters._
val config = ConfigFactory.parseMap(Map("akka.loglevel" -> "DEBUG",

View file

@ -7,8 +7,7 @@ package akka.actor
import language.postfixOps
import akka.testkit._
import scala.concurrent.util.Duration
import scala.concurrent.util.duration._
import scala.concurrent.duration._
import akka.event.Logging
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])

View file

@ -6,8 +6,7 @@ package akka.actor
import language.postfixOps
import akka.testkit._
import scala.concurrent.util.duration._
import scala.concurrent.util.Duration
import scala.concurrent.duration._
object FSMTransitionSpec {

View file

@ -7,9 +7,8 @@ package akka.actor
import language.postfixOps
import akka.testkit._
import scala.concurrent.util.duration._
import scala.concurrent.duration._
import akka.actor.Actor._
import scala.concurrent.util.Duration
import scala.concurrent.Await
import akka.pattern.{ ask, pipe }

View file

@ -7,16 +7,14 @@ package akka.actor
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.concurrent.duration._
import scala.util.continuations._
import akka.testkit._
import akka.dispatch.MessageDispatcher
import akka.pattern.ask
import java.net.{ Socket, InetSocketAddress, InetAddress, SocketAddress }
import scala.util.Failure
import annotation.tailrec
import scala.concurrent.util.FiniteDuration
import scala.annotation.tailrec
object IOActorSpec {
@ -57,6 +55,8 @@ object IOActorSpec {
def receive = {
case _: IO.Connected //don't care
case bytes: ByteString
val source = sender
socket write bytes
@ -67,9 +67,9 @@ object IOActorSpec {
case IO.Closed(`socket`, cause)
state(cause)
throw cause match {
case IO.Error(e) e
case _ new RuntimeException("Socket closed")
cause match {
case IO.Error(e) throw e
case _ throw new RuntimeException("Socket closed")
}
}
@ -156,6 +156,8 @@ object IOActorSpec {
case IO.Read(socket, bytes)
state(socket)(IO Chunk bytes)
case _: IO.Connected //don't care
case IO.Closed(socket, cause)
state -= socket
@ -183,6 +185,8 @@ object IOActorSpec {
readResult map (source !)
}
case _: IO.Connected //don't care
case IO.Read(`socket`, bytes)
state(IO Chunk bytes)
@ -278,7 +282,7 @@ class IOActorSpec extends AkkaSpec with DefaultTimeout {
}
"an IO Actor" must {
implicit val ec = system.dispatcher
import system.dispatcher
"run echo server" in {
filterException[java.net.ConnectException] {
val addressPromise = Promise[SocketAddress]()

View file

@ -7,7 +7,7 @@ package akka.actor
import language.postfixOps
import akka.testkit._
import scala.concurrent.Await
import scala.concurrent.util.duration._
import scala.concurrent.duration._
import akka.util.Timeout
import scala.concurrent.Future
import scala.util.Success
@ -39,6 +39,22 @@ class LocalActorRefProviderSpec extends AkkaSpec(LocalActorRefProviderSpec.confi
a must be === b
}
"find child actor with URL encoded name using actorFor" in {
val childName = "akka%3A%2F%2FClusterSystem%40127.0.0.1%3A2552"
val a = system.actorOf(Props(new Actor {
val child = context.actorOf(Props.empty, name = childName)
def receive = {
case "lookup"
if (childName == child.path.name) sender ! context.actorFor(childName)
else sender ! s"$childName is not ${child.path.name}!"
}
}))
a.tell("lookup", testActor)
val b = expectMsgType[ActorRef]
b.isTerminated must be(false)
b.path.name must be(childName)
}
}
"An ActorRefFactory" must {

View file

@ -6,11 +6,10 @@ package akka.actor
import language.postfixOps
import akka.testkit._
import scala.concurrent.util.duration._
import scala.concurrent.duration._
import java.util.concurrent.atomic.AtomicInteger
import scala.concurrent.Await
import java.util.concurrent.TimeoutException
import scala.concurrent.util.Duration
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
class ReceiveTimeoutSpec extends AkkaSpec {

View file

@ -0,0 +1,27 @@
/**
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.actor
import org.scalatest.WordSpec
import org.scalatest.matchers.MustMatchers
import java.net.URLEncoder
import scala.collection.immutable
class RelativeActorPathSpec extends WordSpec with MustMatchers {
def elements(path: String): immutable.Seq[String] = RelativeActorPath.unapply(path).getOrElse(Nil)
"RelativeActorPath" must {
"match single name" in {
elements("foo") must be(List("foo"))
}
"match path separated names" in {
elements("foo/bar/baz") must be(List("foo", "bar", "baz"))
}
"match url encoded name" in {
val name = URLEncoder.encode("akka://ClusterSystem@127.0.0.1:2552", "UTF-8")
elements(name) must be(List(name))
}
}
}

View file

@ -15,8 +15,7 @@ import java.util.concurrent.{ TimeUnit, CountDownLatch }
import akka.testkit.AkkaSpec
import akka.testkit.DefaultTimeout
import akka.testkit.TestLatch
import scala.concurrent.util.duration._
import scala.concurrent.util.Duration
import scala.concurrent.duration._
import akka.pattern.ask
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])

View file

@ -3,7 +3,7 @@ package akka.actor
import language.postfixOps
import org.scalatest.BeforeAndAfterEach
import scala.concurrent.util.duration._
import scala.concurrent.duration._
import java.util.concurrent.{ CountDownLatch, ConcurrentLinkedQueue, TimeUnit }
import akka.testkit._
import scala.concurrent.Await
@ -214,5 +214,30 @@ class SchedulerSpec extends AkkaSpec with BeforeAndAfterEach with DefaultTimeout
assert(elapsedTimeMs < 2000) // the precision is not ms exact
cancellable.cancel()
}
"adjust for scheduler inaccuracy" taggedAs TimingTest in {
val startTime = System.nanoTime
val n = 33
val latch = new TestLatch(n)
system.scheduler.schedule(150.millis, 150.millis) {
latch.countDown()
}
Await.ready(latch, 6.seconds)
val rate = n * 1000.0 / (System.nanoTime - startTime).nanos.toMillis
rate must be(6.66 plusOrMinus (0.4))
}
"not be affected by long running task" taggedAs TimingTest in {
val startTime = System.nanoTime
val n = 22
val latch = new TestLatch(n)
system.scheduler.schedule(225.millis, 225.millis) {
Thread.sleep(80)
latch.countDown()
}
Await.ready(latch, 6.seconds)
val rate = n * 1000.0 / (System.nanoTime - startTime).nanos.toMillis
rate must be(4.4 plusOrMinus (0.3))
}
}
}

View file

@ -7,8 +7,7 @@ package akka.actor
import language.postfixOps
import java.util.concurrent.{ TimeUnit, CountDownLatch }
import scala.concurrent.Await
import scala.concurrent.util.Duration
import scala.concurrent.util.duration.intToDurationInt
import scala.concurrent.duration._
import scala.math.BigInt.int2bigInt
import scala.util.Random
import scala.util.control.NoStackTrace
@ -195,7 +194,7 @@ object SupervisorHierarchySpec {
case x (x, x)
}
override val supervisorStrategy = OneForOneStrategy()(unwrap andThen {
case _: Failure if pongsToGo > 0
case (_: Failure, _) if pongsToGo > 0
log :+= Event("pongOfDeath resuming " + sender, identityHashCode(this))
Resume
case (f: Failure, orig)
@ -394,7 +393,7 @@ object SupervisorHierarchySpec {
override val supervisorStrategy = OneForOneStrategy() {
case f: Failure f.directive
case OriginalRestartException(f: Failure) f.directive
case ActorInitializationException(f: Failure) f.directive
case ActorInitializationException(_, _, f: Failure) f.directive
case _ Stop
}

View file

@ -12,7 +12,7 @@ import java.util.concurrent.{ TimeUnit, CountDownLatch }
import akka.testkit.AkkaSpec
import akka.testkit.DefaultTimeout
import akka.pattern.ask
import scala.concurrent.util.duration._
import scala.concurrent.duration._
import scala.util.control.NonFatal
object SupervisorMiscSpec {

View file

@ -7,7 +7,7 @@ package akka.actor
import language.postfixOps
import org.scalatest.BeforeAndAfterEach
import scala.concurrent.util.duration._
import scala.concurrent.duration._
import akka.{ Die, Ping }
import akka.testkit.TestEvent._
import akka.testkit._

View file

@ -8,7 +8,7 @@ import language.postfixOps
import org.scalatest.WordSpec
import org.scalatest.matchers.MustMatchers
import scala.concurrent.Await
import scala.concurrent.util.duration._
import scala.concurrent.duration._
import akka.actor.Actor._
import akka.testkit.{ TestKit, EventFilter, filterEvents, filterException, AkkaSpec, ImplicitSender, DefaultTimeout }
import akka.dispatch.Dispatchers

View file

@ -14,7 +14,7 @@ import akka.testkit.ImplicitSender
import akka.testkit.DefaultTimeout
import scala.concurrent.Await
import akka.pattern.ask
import scala.concurrent.util.duration._
import scala.concurrent.duration._
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
class Ticket669Spec extends AkkaSpec with BeforeAndAfterAll with ImplicitSender with DefaultTimeout {

View file

@ -5,22 +5,21 @@ package akka.actor
import language.postfixOps
import org.scalatest.{ BeforeAndAfterAll, BeforeAndAfterEach }
import akka.util.Timeout
import scala.annotation.tailrec
import scala.collection.immutable
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 scala.concurrent.duration._
import akka.testkit.{ EventFilter, filterEvents, AkkaSpec }
import akka.util.Timeout
import akka.japi.{ Option JOption }
import akka.testkit.DefaultTimeout
import akka.dispatch.{ Dispatchers }
import akka.dispatch.Dispatchers
import akka.pattern.ask
import akka.serialization.JavaSerializer
import akka.actor.TypedActor._
import java.util.concurrent.atomic.AtomicReference
import java.lang.IllegalStateException
import java.util.concurrent.{ TimeoutException, TimeUnit, CountDownLatch }
import scala.concurrent.util.FiniteDuration
object TypedActorSpec {
@ -37,9 +36,9 @@ object TypedActorSpec {
}
"""
class CyclicIterator[T](val items: Seq[T]) extends Iterator[T] {
class CyclicIterator[T](val items: immutable.Seq[T]) extends Iterator[T] {
private[this] val current: AtomicReference[Seq[T]] = new AtomicReference(items)
private[this] val current = new AtomicReference(items)
def hasNext = items != Nil

View file

@ -21,8 +21,7 @@ import akka.event.Logging.Error
import akka.pattern.ask
import akka.testkit._
import akka.util.Switch
import scala.concurrent.util.duration._
import scala.concurrent.util.Duration
import scala.concurrent.duration._
import scala.concurrent.{ Await, Future, Promise }
import scala.annotation.tailrec

View file

@ -7,8 +7,7 @@ import java.util.concurrent.atomic.{ AtomicBoolean, AtomicInteger }
import akka.testkit.{ filterEvents, EventFilter, AkkaSpec }
import akka.actor.{ Props, Actor }
import scala.concurrent.Await
import scala.concurrent.util.Duration
import scala.concurrent.util.duration._
import scala.concurrent.duration._
import akka.testkit.DefaultTimeout
import akka.dispatch.{ PinnedDispatcher, Dispatchers, Dispatcher }
import akka.pattern.ask

View file

@ -14,7 +14,7 @@ import scala.collection.JavaConverters._
import com.typesafe.config.ConfigFactory
import akka.actor.Actor
import akka.actor.Props
import scala.concurrent.util.duration._
import scala.concurrent.duration._
object DispatchersSpec {
val config = """

View file

@ -9,8 +9,7 @@ import language.postfixOps
import akka.testkit.AkkaSpec
import com.typesafe.config.ConfigFactory
import scala.collection.JavaConverters._
import scala.concurrent.util.duration._
import scala.concurrent.util.Duration
import scala.concurrent.duration._
import akka.actor.{ IOManager, ActorSystem }
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
@ -25,8 +24,8 @@ class ConfigSpec extends AkkaSpec(ConfigFactory.defaultReference(ActorSystem.fin
{
import config._
getString("akka.version") must equal("2.1-SNAPSHOT")
settings.ConfigVersion must equal("2.1-SNAPSHOT")
getString("akka.version") must equal("2.2-SNAPSHOT")
settings.ConfigVersion must equal("2.2-SNAPSHOT")
getBoolean("akka.daemonic") must equal(false)
getBoolean("akka.actor.serialize-messages") must equal(false)
@ -46,6 +45,9 @@ class ConfigSpec extends AkkaSpec(ConfigFactory.defaultReference(ActorSystem.fin
getInt("akka.actor.deployment.default.virtual-nodes-factor") must be(10)
settings.DefaultVirtualNodesFactor must be(10)
getMilliseconds("akka.actor.unstarted-push-timeout") must be(10.seconds.toMillis)
settings.UnstartedPushTimeout.duration must be(10.seconds)
}
{

View file

@ -8,7 +8,7 @@ import language.postfixOps
import akka.actor.{ Actor, Props }
import scala.concurrent.Future
import scala.concurrent.Await
import scala.concurrent.util.duration._
import scala.concurrent.duration._
import akka.testkit.{ AkkaSpec, DefaultTimeout }
import akka.pattern.{ ask, pipe }
import scala.concurrent.ExecutionException

View file

@ -12,8 +12,7 @@ import akka.actor._
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.duration._
import scala.concurrent.ExecutionContext
import org.scalatest.junit.JUnitSuite
import scala.runtime.NonLocalReturnControl
@ -522,268 +521,6 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
filterException[TimeoutException] { intercept[TimeoutException] { FutureSpec.ready(f3, 0 millis) } }
}
//FIXME DATAFLOW
/*"futureComposingWithContinuations" in {
import Future.flow
val actor = system.actorOf(Props[TestActor])
val x = Future("Hello")
val y = x flatMap (actor ? _) mapTo manifest[String]
val r = flow(x() + " " + y() + "!")
assert(Await.result(r, timeout.duration) === "Hello World!")
system.stop(actor)
}
"futureComposingWithContinuationsFailureDivideZero" in {
filterException[ArithmeticException] {
import Future.flow
val x = Future("Hello")
val y = x map (_.length)
val r = flow(x() + " " + y.map(_ / 0).map(_.toString).apply, 100)
intercept[java.lang.ArithmeticException](Await.result(r, timeout.duration))
}
}
"futureComposingWithContinuationsFailureCastInt" in {
filterException[ClassCastException] {
import Future.flow
val actor = system.actorOf(Props[TestActor])
val x = Future(3)
val y = (actor ? "Hello").mapTo[Int]
val r = flow(x() + y(), 100)
intercept[ClassCastException](Await.result(r, timeout.duration))
}
}
"futureComposingWithContinuationsFailureCastNothing" in {
filterException[ClassCastException] {
import Future.flow
val actor = system.actorOf(Props[TestActor])
val x = Future("Hello")
val y = actor ? "Hello" mapTo manifest[Nothing]
val r = flow(x() + y())
intercept[ClassCastException](Await.result(r, timeout.duration))
}
}
"futureCompletingWithContinuations" in {
import Future.flow
val x, y, z = Promise[Int]()
val ly, lz = new TestLatch
val result = flow {
y completeWith x
ly.open() // not within continuation
z << x
lz.open() // within continuation, will wait for 'z' to complete
z() + y()
}
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)
FutureSpec.ready(lz, timeout.duration)
assert(Await.result(result, timeout.duration) === 10)
val a, b, c = Promise[Int]()
val result2 = flow {
val n = (a << c).value.get.right.get + 10
b << (c() - 2)
a() + n * b()
}
c completeWith Future(5)
assert(Await.result(a, timeout.duration) === 5)
assert(Await.result(b, timeout.duration) === 3)
assert(Await.result(result2, timeout.duration) === 50)
}
"futureDataFlowShouldEmulateBlocking1" in {
import Future.flow
val one, two = Promise[Int]()
val simpleResult = flow {
one() + two()
}
assert(List(one, two, simpleResult).forall(_.isCompleted == false))
flow { one << 1 }
FutureSpec.ready(one, 1 minute)
assert(one.isCompleted)
assert(List(two, simpleResult).forall(_.isCompleted == false))
flow { two << 9 }
FutureSpec.ready(two, 1 minute)
assert(List(one, two).forall(_.isCompleted == true))
assert(Await.result(simpleResult, timeout.duration) === 10)
}
"futureDataFlowShouldEmulateBlocking2" in {
import Future.flow
val x1, x2, y1, y2 = Promise[Int]()
val lx, ly, lz = new TestLatch
val result = flow {
lx.open()
x1 << y1
ly.open()
x2 << y2
lz.open()
x1() + x2()
}
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
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
FutureSpec.ready(lz, 2 seconds)
assert(Await.result(x2, 1 minute) === 9)
assert(List(x1, x2, y1, y2).forall(_.isCompleted))
assert(Await.result(result, 1 minute) === 10)
}
"dataFlowAPIshouldbeSlick" in {
import Future.flow
val i1, i2, s1, s2 = new TestLatch
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)
FutureSpec.ready(i1, 2 seconds)
FutureSpec.ready(i2, 2 seconds)
s1.open()
s2.open()
assert(Await.result(result, timeout.duration) === 10)
}
"futureCompletingWithContinuationsFailure" in {
filterException[ArithmeticException] {
import Future.flow
val x, y, z = Promise[Int]()
val ly, lz = new TestLatch
val result = flow {
y << x
ly.open()
val oops = 1 / 0
z << x
lz.open()
z() + y() + oops
}
intercept[TimeoutException] { FutureSpec.ready(ly, 100 milliseconds) }
intercept[TimeoutException] { FutureSpec.ready(lz, 100 milliseconds) }
flow { x << 5 }
assert(Await.result(y, timeout.duration) === 5)
intercept[java.lang.ArithmeticException](Await.result(result, timeout.duration))
assert(z.value === None)
assert(!lz.isOpen)
}
}
"futureContinuationsShouldNotBlock" in {
import Future.flow
val latch = new TestLatch
val future = Future {
FutureSpec.ready(latch, TestLatch.DefaultTimeout)
"Hello"
}
val result = flow {
Some(future()).filter(_ == "Hello")
}
assert(!result.isCompleted)
latch.open()
assert(Await.result(result, timeout.duration) === Some("Hello"))
}
"futureFlowShouldBeTypeSafe" in {
import Future.flow
val rString = flow {
val x = Future(5)
x().toString
}
val rInt = flow {
val x = rString.apply
val y = Future(5)
x.length + y()
}
assert(checkType(rString, manifest[String]))
assert(checkType(rInt, manifest[Int]))
assert(!checkType(rInt, manifest[String]))
assert(!checkType(rInt, manifest[Nothing]))
assert(!checkType(rInt, manifest[Any]))
Await.result(rString, timeout.duration)
Await.result(rInt, timeout.duration)
}
"futureFlowSimpleAssign" in {
import Future.flow
val x, y, z = Promise[Int]()
flow {
z << x() + y()
}
flow { x << 40 }
flow { y << 2 }
assert(Await.result(z, timeout.duration) === 42)
}*/
"run callbacks async" in {
val latch = Vector.fill(10)(new TestLatch)
@ -873,13 +610,6 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
// 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))
}*/
}
}

View file

@ -11,7 +11,7 @@ import com.typesafe.config.Config
import akka.actor.{ RepointableRef, Props, DeadLetter, ActorSystem, ActorRefWithCell, ActorRef, ActorCell }
import akka.testkit.AkkaSpec
import scala.concurrent.{ Future, Promise, Await }
import scala.concurrent.util.duration.intToDurationInt
import scala.concurrent.duration._
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
abstract class MailboxSpec extends AkkaSpec with BeforeAndAfterAll with BeforeAndAfterEach {

View file

@ -6,11 +6,10 @@ import org.junit.runner.RunWith
import org.scalatest.junit.JUnitRunner
import com.typesafe.config.Config
import akka.actor.{ Props, InternalActorRef, ActorSystem, Actor }
import akka.actor.{ Props, ActorSystem, Actor }
import akka.pattern.ask
import akka.testkit.{ DefaultTimeout, AkkaSpec }
import scala.concurrent.Await
import scala.concurrent.util.duration.intToDurationInt
import scala.concurrent.duration._
object PriorityDispatcherSpec {
val config = """
@ -50,24 +49,32 @@ class PriorityDispatcherSpec extends AkkaSpec(PriorityDispatcherSpec.config) wit
}
def testOrdering(dispatcherKey: String) {
val msgs = (1 to 100) toList
// It's important that the actor under test is not a top level actor
// with RepointableActorRef, since messages might be queued in
// UnstartedCell and the sent to the PriorityQueue and consumed immediately
// without the ordering taking place.
val actor = system.actorOf(Props(new Actor {
var acc: List[Int] = Nil
context.actorOf(Props(new Actor {
val acc = scala.collection.mutable.ListBuffer[Int]()
scala.util.Random.shuffle(msgs) foreach { m self ! m }
self.tell('Result, testActor)
def receive = {
case i: Int acc = i :: acc
case 'Result sender ! acc
case i: Int acc += i
case 'Result sender ! acc.toList
}
}).withDispatcher(dispatcherKey)).asInstanceOf[InternalActorRef]
}).withDispatcher(dispatcherKey))
actor.suspend //Make sure the actor isn't treating any messages, let it buffer the incoming messages
def receive = Actor.emptyBehavior
val msgs = (1 to 100).toList
for (m msgs) actor ! m
}))
actor.resume(causedByFailure = null) //Signal the actor to start treating it's message backlog
Await.result(actor.?('Result).mapTo[List[Int]], timeout.duration) must be === msgs.reverse
expectMsgType[List[_]] must be === msgs
}
}

View file

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

View file

@ -5,7 +5,7 @@ package akka.event
import language.postfixOps
import scala.concurrent.util.duration._
import scala.concurrent.duration._
import akka.actor.{ Actor, ActorRef, ActorSystemImpl, ActorSystem, Props, UnhandledMessage }
import com.typesafe.config.ConfigFactory
import scala.collection.JavaConverters._

View file

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

View file

@ -6,7 +6,7 @@ package akka.pattern
import language.postfixOps
import akka.testkit.AkkaSpec
import scala.concurrent.util.duration._
import scala.concurrent.duration._
import scala.concurrent.Await
import akka.testkit.DefaultTimeout
import akka.util.Timeout

View file

@ -4,8 +4,9 @@
package akka.pattern
import akka.testkit._
import scala.concurrent.util.duration._
import scala.concurrent.duration._
import scala.concurrent.{ Promise, Future, Await }
import scala.annotation.tailrec
class CircuitBreakerMTSpec extends AkkaSpec {
implicit val ec = system.dispatcher
@ -14,8 +15,16 @@ class CircuitBreakerMTSpec extends AkkaSpec {
val resetTimeout = 2.seconds.dilated
val breaker = new CircuitBreaker(system.scheduler, 5, callTimeout, resetTimeout)
def openBreaker(): Unit =
Await.ready(Future.sequence((1 to 5).map(_ breaker.withCircuitBreaker(Future(throw new RuntimeException("FAIL"))).failed)), 1.second.dilated)
def openBreaker(): Unit = {
@tailrec def call(attemptsLeft: Int): Unit = {
attemptsLeft must be > (0)
if (Await.result(breaker.withCircuitBreaker(Future(throw new RuntimeException("FAIL"))) recover {
case _: CircuitBreakerOpenException false
case _ true
}, remaining)) call(attemptsLeft - 1)
}
call(10)
}
"allow many calls while in closed state with no errors" in {

View file

@ -6,7 +6,7 @@ package akka.pattern
import language.postfixOps
import scala.concurrent.util.duration._
import scala.concurrent.duration._
import akka.testkit._
import org.scalatest.BeforeAndAfter
import akka.actor.{ ActorSystem, Scheduler }

View file

@ -9,8 +9,7 @@ import language.postfixOps
import akka.testkit.AkkaSpec
import akka.actor.{ Props, Actor }
import scala.concurrent.{ Future, Promise, Await }
import scala.concurrent.util.Duration
import scala.concurrent.util.duration._
import scala.concurrent.duration._
object PatternSpec {
case class Work(duration: Duration)

View file

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

View file

@ -12,17 +12,18 @@ import java.io.PrintWriter
import java.text.SimpleDateFormat
import java.util.Date
import scala.collection.mutable.{ Map MutableMap }
import scala.collection.immutable
import akka.actor.ActorSystem
import akka.event.Logging
trait BenchResultRepository {
def add(stats: Stats)
def get(name: String): Seq[Stats]
def get(name: String): immutable.Seq[Stats]
def get(name: String, load: Int): Option[Stats]
def getWithHistorical(name: String, load: Int): Seq[Stats]
def getWithHistorical(name: String, load: Int): immutable.Seq[Stats]
def isBaseline(stats: Stats): Boolean
@ -38,9 +39,9 @@ object BenchResultRepository {
}
class FileBenchResultRepository extends BenchResultRepository {
private val statsByName = MutableMap[String, Seq[Stats]]()
private val statsByName = MutableMap[String, immutable.Seq[Stats]]()
private val baselineStats = MutableMap[Key, Stats]()
private val historicalStats = MutableMap[Key, Seq[Stats]]()
private val historicalStats = MutableMap[Key, immutable.Seq[Stats]]()
private def resultDir = BenchmarkConfig.config.getString("benchmark.resultDir")
private val serDir = resultDir + "/ser"
private def serDirExists: Boolean = new File(serDir).exists
@ -51,13 +52,13 @@ class FileBenchResultRepository extends BenchResultRepository {
case class Key(name: String, load: Int)
def add(stats: Stats): Unit = synchronized {
val values = statsByName.getOrElseUpdate(stats.name, IndexedSeq.empty)
val values = statsByName.getOrElseUpdate(stats.name, Vector.empty)
statsByName(stats.name) = values :+ stats
save(stats)
}
def get(name: String): Seq[Stats] = synchronized {
statsByName.getOrElse(name, IndexedSeq.empty)
def get(name: String): immutable.Seq[Stats] = synchronized {
statsByName.getOrElse(name, Vector.empty)
}
def get(name: String, load: Int): Option[Stats] = synchronized {
@ -68,13 +69,13 @@ class FileBenchResultRepository extends BenchResultRepository {
baselineStats.get(Key(stats.name, stats.load)) == Some(stats)
}
def getWithHistorical(name: String, load: Int): Seq[Stats] = synchronized {
def getWithHistorical(name: String, load: Int): immutable.Seq[Stats] = synchronized {
val key = Key(name, load)
val historical = historicalStats.getOrElse(key, IndexedSeq.empty)
val historical = historicalStats.getOrElse(key, Vector.empty)
val baseline = baselineStats.get(key)
val current = get(name, load)
val limited = (IndexedSeq.empty ++ historical ++ baseline ++ current).takeRight(maxHistorical)
val limited = (Vector.empty ++ historical ++ baseline ++ current).takeRight(maxHistorical)
limited.sortBy(_.timestamp)
}
@ -94,7 +95,7 @@ class FileBenchResultRepository extends BenchResultRepository {
}
val historical = load(historicalFiles)
for (h historical) {
val values = historicalStats.getOrElseUpdate(Key(h.name, h.load), IndexedSeq.empty)
val values = historicalStats.getOrElseUpdate(Key(h.name, h.load), Vector.empty)
historicalStats(Key(h.name, h.load)) = values :+ h
}
}
@ -120,7 +121,7 @@ class FileBenchResultRepository extends BenchResultRepository {
}
}
private def load(files: Iterable[File]): Seq[Stats] = {
private def load(files: Iterable[File]): immutable.Seq[Stats] = {
val result =
for (f files) yield {
var in: ObjectInputStream = null
@ -132,11 +133,11 @@ class FileBenchResultRepository extends BenchResultRepository {
case e: Throwable
None
} finally {
if (in ne null) try { in.close() } catch { case ignore: Exception }
if (in ne null) try in.close() catch { case ignore: Exception }
}
}
result.flatten.toSeq.sortBy(_.timestamp)
result.flatten.toVector.sortBy(_.timestamp)
}
loadFiles()

View file

@ -3,7 +3,7 @@ package akka.performance.workbench
import java.io.UnsupportedEncodingException
import java.net.URLEncoder
import scala.collection.immutable.TreeMap
import scala.collection.immutable
/**
* Generates URLs to Google Chart API http://code.google.com/apis/chart/
@ -16,7 +16,7 @@ object GoogleChartBuilder {
/**
* Builds a bar chart for tps in the statistics.
*/
def tpsChartUrl(statsByTimestamp: TreeMap[Long, Seq[Stats]], title: String, legend: Stats String): String = {
def tpsChartUrl(statsByTimestamp: immutable.TreeMap[Long, Seq[Stats]], title: String, legend: Stats String): String = {
if (statsByTimestamp.isEmpty) ""
else {
val loads = statsByTimestamp.values.head.map(_.load)
@ -46,7 +46,7 @@ object GoogleChartBuilder {
//sb.append("&")
// legend
val legendStats = statsByTimestamp.values.map(_.head).toSeq
val legendStats = statsByTimestamp.values.toVector.map(_.head)
appendLegend(legendStats, sb, legend)
sb.append("&")
// bar spacing
@ -60,10 +60,7 @@ object GoogleChartBuilder {
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(",")
}
val tpsSeries: Iterable[String] = for (statsSeq statsByTimestamp.values) yield statsSeq.map(_.tps).mkString(",")
sb.append(tpsSeries.mkString("|"))
// y range
@ -83,7 +80,7 @@ object GoogleChartBuilder {
/**
* Builds a bar chart for all percentiles and the mean in the statistics.
*/
def percentilesAndMeanChartUrl(statistics: Seq[Stats], title: String, legend: Stats String): String = {
def percentilesAndMeanChartUrl(statistics: immutable.Seq[Stats], title: String, legend: Stats String): String = {
if (statistics.isEmpty) ""
else {
val current = statistics.last
@ -146,13 +143,13 @@ object GoogleChartBuilder {
}
}
private def percentileLabels(percentiles: TreeMap[Int, Long], sb: StringBuilder) {
private def percentileLabels(percentiles: immutable.TreeMap[Int, Long], sb: StringBuilder) {
sb.append("chxl=1:|")
val s = percentiles.keys.toList.map(_ + "%").mkString("|")
sb.append(s)
}
private def appendLegend(statistics: Seq[Stats], sb: StringBuilder, legend: Stats String) {
private def appendLegend(statistics: immutable.Seq[Stats], sb: StringBuilder, legend: Stats String) {
val legends = statistics.map(legend(_))
sb.append("chdl=")
val s = legends.map(urlEncode(_)).mkString("|")
@ -166,7 +163,7 @@ object GoogleChartBuilder {
sb.append(s)
}
private def dataSeries(allPercentiles: Seq[TreeMap[Int, Long]], meanValues: Seq[Double], sb: StringBuilder) {
private def dataSeries(allPercentiles: immutable.Seq[immutable.TreeMap[Int, Long]], meanValues: immutable.Seq[Double], sb: StringBuilder) {
val percentileSeries =
for {
percentiles allPercentiles
@ -181,7 +178,7 @@ object GoogleChartBuilder {
sb.append(series.mkString("|"))
}
private def dataSeries(values: Seq[Double], sb: StringBuilder) {
private def dataSeries(values: immutable.Seq[Double], sb: StringBuilder) {
val series = values.map(formatDouble(_))
sb.append(series.mkString("|"))
}
@ -198,7 +195,7 @@ object GoogleChartBuilder {
}
}
def latencyAndThroughputChartUrl(statistics: Seq[Stats], title: String): String = {
def latencyAndThroughputChartUrl(statistics: immutable.Seq[Stats], title: String): String = {
if (statistics.isEmpty) ""
else {
val sb = new StringBuilder

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 scala.concurrent.util.Duration
import scala.concurrent.duration.Duration
import com.typesafe.config.Config
import java.util.concurrent.TimeUnit
import akka.event.Logging

View file

@ -5,7 +5,7 @@ import java.text.SimpleDateFormat
import java.util.Date
import akka.actor.ActorSystem
import akka.event.Logging
import scala.collection.immutable.TreeMap
import scala.collection.immutable
class Report(
system: ActorSystem,
@ -19,7 +19,7 @@ class Report(
val legendTimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm")
val fileTimestampFormat = new SimpleDateFormat("yyyyMMddHHmmss")
def html(statistics: Seq[Stats]) {
def html(statistics: immutable.Seq[Stats]) {
val current = statistics.last
val sb = new StringBuilder
@ -80,13 +80,13 @@ class Report(
chartUrl
}
def comparePercentilesAndMeanChart(stats: Stats): Seq[String] = {
def comparePercentilesAndMeanChart(stats: Stats): immutable.Seq[String] = {
for {
compareName compareResultWith.toSeq
compareName compareResultWith.to[immutable.Seq]
compareStats resultRepository.get(compareName, stats.load)
} yield {
val chartTitle = stats.name + " vs. " + compareName + ", " + stats.load + " clients" + ", Percentiles and Mean (microseconds)"
val chartUrl = GoogleChartBuilder.percentilesAndMeanChartUrl(Seq(compareStats, stats), chartTitle, _.name)
val chartUrl = GoogleChartBuilder.percentilesAndMeanChartUrl(List(compareStats, stats), chartTitle, _.name)
chartUrl
}
}
@ -102,17 +102,17 @@ class Report(
}
}
def compareWithHistoricalTpsChart(statistics: Seq[Stats]): Option[String] = {
def compareWithHistoricalTpsChart(statistics: immutable.Seq[Stats]): Option[String] = {
if (statistics.isEmpty) {
None
} else {
val histTimestamps = resultRepository.getWithHistorical(statistics.head.name, statistics.head.load).map(_.timestamp)
val statsByTimestamp = TreeMap[Long, Seq[Stats]]() ++
val statsByTimestamp = immutable.TreeMap[Long, Seq[Stats]]() ++
(for (ts histTimestamps) yield {
val seq =
for (stats statistics) yield {
val withHistorical: Seq[Stats] = resultRepository.getWithHistorical(stats.name, stats.load)
val withHistorical: immutable.Seq[Stats] = resultRepository.getWithHistorical(stats.name, stats.load)
val cell = withHistorical.find(_.timestamp == ts)
cell.getOrElse(Stats(stats.name, stats.load, ts))
}
@ -131,7 +131,7 @@ class Report(
chartUrl
}
def formatResultsTable(statsSeq: Seq[Stats]): String = {
def formatResultsTable(statsSeq: immutable.Seq[Stats]): String = {
val name = statsSeq.head.name

View file

@ -12,7 +12,7 @@ import akka.ConfigurationException
import scala.concurrent.Await
import akka.pattern.{ ask, gracefulStop }
import akka.testkit.{ TestLatch, ImplicitSender, DefaultTimeout, AkkaSpec }
import scala.concurrent.util.duration.intToDurationInt
import scala.concurrent.duration._
import akka.actor.UnstartedCell
object ConfiguredLocalRoutingSpec {

View file

@ -19,7 +19,7 @@ class CustomRouteSpec extends AkkaSpec {
provider.createRoutees(1)
{
case (sender, message: String) Seq(Destination(sender, target))
case (sender, message: String) List(Destination(sender, target))
case (sender, message) toAll(sender, provider.routees)
}
}
@ -35,7 +35,7 @@ class CustomRouteSpec extends AkkaSpec {
import akka.pattern.ask
import akka.testkit.ExtractRoute
import scala.concurrent.Await
import scala.concurrent.util.duration._
import scala.concurrent.duration._
val target = system.actorOf(Props.empty)
val router = system.actorOf(Props.empty.withRouter(new MyRouter(target)))
@ -43,8 +43,8 @@ class CustomRouteSpec extends AkkaSpec {
val r = Await.result(router.ask(CurrentRoutees)(1 second).
mapTo[RouterRoutees], 1 second)
r.routees.size must be(1)
route(testActor -> "hallo") must be(Seq(Destination(testActor, target)))
route(testActor -> 12) must be(Seq(Destination(testActor, r.routees.head)))
route(testActor -> "hallo") must be(List(Destination(testActor, target)))
route(testActor -> 12) must be(List(Destination(testActor, r.routees.head)))
//#test-route
}

View file

@ -9,13 +9,10 @@ import akka.testkit._
import akka.testkit.TestEvent._
import akka.actor.Props
import scala.concurrent.Await
import scala.concurrent.util.duration._
import scala.concurrent.duration._
import scala.collection.immutable
import akka.actor.ActorRef
import java.util.concurrent.atomic.AtomicInteger
import akka.pattern.ask
import scala.concurrent.util.Duration
import java.util.concurrent.TimeoutException
import scala.concurrent.util.FiniteDuration
import scala.util.Try
object ResizerSpec {
@ -63,10 +60,10 @@ class ResizerSpec extends AkkaSpec(ResizerSpec.config) with DefaultTimeout with
lowerBound = 2,
upperBound = 3)
val c1 = resizer.capacity(IndexedSeq.empty[ActorRef])
val c1 = resizer.capacity(immutable.IndexedSeq.empty[ActorRef])
c1 must be(2)
val current = IndexedSeq(system.actorOf(Props[TestActor]), system.actorOf(Props[TestActor]))
val current = immutable.IndexedSeq(system.actorOf(Props[TestActor]), system.actorOf(Props[TestActor]))
val c2 = resizer.capacity(current)
c2 must be(0)
}
@ -162,7 +159,7 @@ class ResizerSpec extends AkkaSpec(ResizerSpec.config) with DefaultTimeout with
// sending in too quickly will result in skipped resize due to many resizeInProgress conflicts
Thread.sleep(20.millis.dilated.toMillis)
}
within((((d * loops).asInstanceOf[FiniteDuration] / resizer.lowerBound) + 2.seconds.dilated).asInstanceOf[FiniteDuration]) {
within((d * loops / resizer.lowerBound) + 2.seconds.dilated) {
for (m 0 until loops) expectMsg("done")
}
}
@ -176,19 +173,20 @@ class ResizerSpec extends AkkaSpec(ResizerSpec.config) with DefaultTimeout with
routeeSize(router) must be(resizer.upperBound)
}
"backoff" in {
"backoff" in within(10 seconds) {
val resizer = DefaultResizer(
lowerBound = 1,
upperBound = 5,
rampupRate = 1.0,
backoffRate = 1.0,
backoffThreshold = 0.20,
backoffThreshold = 0.40,
pressureThreshold = 1,
messagesPerResize = 1)
val router = system.actorOf(Props(new Actor {
def receive = {
case n: Int if n <= 0 // done
case n: Int Thread.sleep((n millis).dilated.toMillis)
}
}).withRouter(RoundRobinRouter(resizer = Some(resizer))))
@ -205,12 +203,11 @@ class ResizerSpec extends AkkaSpec(ResizerSpec.config) with DefaultTimeout with
Thread.sleep((300 millis).dilated.toMillis)
// let it cool down
for (m 0 to 5) {
router ! 1
Thread.sleep((500 millis).dilated.toMillis)
}
awaitCond({
router ! 0 // trigger resize
routeeSize(router) < z
}, interval = 500.millis.dilated)
awaitCond(Try(routeeSize(router) < (z)).getOrElse(false))
}
}

View file

@ -5,20 +5,20 @@ package akka.routing
import language.postfixOps
import java.util.concurrent.atomic.AtomicInteger
import akka.actor._
import scala.collection.mutable.LinkedList
import scala.collection.immutable
import akka.testkit._
import scala.concurrent.util.duration._
import scala.concurrent.duration._
import scala.concurrent.Await
import scala.concurrent.util.Duration
import akka.ConfigurationException
import com.typesafe.config.ConfigFactory
import akka.pattern.{ ask, pipe }
import java.util.concurrent.ConcurrentHashMap
import com.typesafe.config.Config
import akka.dispatch.Dispatchers
import akka.util.Collections.EmptyImmutableSeq
import akka.util.Timeout
import java.util.concurrent.atomic.AtomicInteger
object RoutingSpec {
@ -55,11 +55,10 @@ object RoutingSpec {
class MyRouter(config: Config) extends RouterConfig {
val foo = config.getString("foo")
def createRoute(routeeProvider: RouteeProvider): Route = {
val routees = IndexedSeq(routeeProvider.context.actorOf(Props[Echo]))
routeeProvider.registerRoutees(routees)
routeeProvider.registerRoutees(List(routeeProvider.context.actorOf(Props[Echo])))
{
case (sender, message) Nil
case (sender, message) EmptyImmutableSeq
}
}
def routerDispatcher: String = Dispatchers.DefaultDispatcherId
@ -102,33 +101,35 @@ class RoutingSpec extends AkkaSpec(RoutingSpec.config) with DefaultTimeout with
}
"be able to send their routees" in {
class TheActor extends Actor {
val routee1 = context.actorOf(Props[TestActor], "routee1")
val routee2 = context.actorOf(Props[TestActor], "routee2")
val routee3 = context.actorOf(Props[TestActor], "routee3")
val router = context.actorOf(Props[TestActor].withRouter(
ScatterGatherFirstCompletedRouter(
routees = List(routee1, routee2, routee3),
within = 5 seconds)))
case class TestRun(id: String, names: immutable.Iterable[String], actors: Int)
val actor = system.actorOf(Props(new Actor {
def receive = {
case "doIt" router ! CurrentRoutees
case routees: RouterRoutees testActor forward routees
}
}
case TestRun(id, names, actors)
val routerProps = Props[TestActor].withRouter(
ScatterGatherFirstCompletedRouter(
routees = names map { context.actorOf(Props(new TestActor), _) },
within = 5 seconds))
val theActor = system.actorOf(Props(new TheActor), "theActor")
theActor ! "doIt"
val routees = expectMsgPF() {
case RouterRoutees(routees) routees.toSet
1 to actors foreach { i context.actorOf(routerProps, id + i).tell(CurrentRoutees, testActor) }
}
}))
routees.map(_.path.name) must be(Set("routee1", "routee2", "routee3"))
val actors = 15
val names = 1 to 20 map { "routee" + _ } toList
actor ! TestRun("test", names, actors)
1 to actors foreach { _
val routees = expectMsgType[RouterRoutees].routees
routees.map(_.path.name) must be === names
}
expectNoMsg(500.millis)
}
"use configured nr-of-instances when FromConfig" in {
val router = system.actorOf(Props[TestActor].withRouter(FromConfig), "router1")
Await.result(router ? CurrentRoutees, remaining).asInstanceOf[RouterRoutees].routees.size must be(3)
router ! CurrentRoutees
expectMsgType[RouterRoutees].routees.size must be(3)
watch(router)
system.stop(router)
expectMsgType[Terminated]
@ -136,7 +137,8 @@ class RoutingSpec extends AkkaSpec(RoutingSpec.config) with DefaultTimeout with
"use configured nr-of-instances when router is specified" in {
val router = system.actorOf(Props[TestActor].withRouter(RoundRobinRouter(nrOfInstances = 2)), "router2")
Await.result(router ? CurrentRoutees, remaining).asInstanceOf[RouterRoutees].routees.size must be(3)
router ! CurrentRoutees
expectMsgType[RouterRoutees].routees.size must be(3)
system.stop(router)
}
@ -151,7 +153,8 @@ class RoutingSpec extends AkkaSpec(RoutingSpec.config) with DefaultTimeout with
}
val router = system.actorOf(Props[TestActor].withRouter(RoundRobinRouter(resizer = Some(resizer))), "router3")
Await.ready(latch, remaining)
Await.result(router ? CurrentRoutees, remaining).asInstanceOf[RouterRoutees].routees.size must be(3)
router ! CurrentRoutees
expectMsgType[RouterRoutees].routees.size must be(3)
system.stop(router)
}
@ -252,15 +255,15 @@ class RoutingSpec extends AkkaSpec(RoutingSpec.config) with DefaultTimeout with
val doneLatch = new TestLatch(connectionCount)
//lets create some connections.
var actors = new LinkedList[ActorRef]
var counters = new LinkedList[AtomicInteger]
@volatile var actors = immutable.IndexedSeq[ActorRef]()
@volatile var counters = immutable.IndexedSeq[AtomicInteger]()
for (i 0 until connectionCount) {
counters = counters :+ new AtomicInteger()
val actor = system.actorOf(Props(new Actor {
def receive = {
case "end" doneLatch.countDown()
case msg: Int counters.get(i).get.addAndGet(msg)
case msg: Int counters(i).addAndGet(msg)
}
}))
actors = actors :+ actor
@ -279,10 +282,8 @@ class RoutingSpec extends AkkaSpec(RoutingSpec.config) with DefaultTimeout with
//now wait some and do validations.
Await.ready(doneLatch, remaining)
for (i 0 until connectionCount) {
val counter = counters.get(i).get
counter.get must be((iterationCount * (i + 1)))
}
for (i 0 until connectionCount)
counters(i).get must be((iterationCount * (i + 1)))
}
"deliver a broadcast message using the !" in {

View file

@ -11,7 +11,7 @@ import akka.actor._
import java.io._
import scala.concurrent.Await
import akka.util.Timeout
import scala.concurrent.util.duration._
import scala.concurrent.duration._
import scala.reflect.BeanInfo
import com.google.protobuf.Message
import akka.pattern.ask

View file

@ -7,12 +7,32 @@ import language.postfixOps
import org.scalatest.WordSpec
import org.scalatest.matchers.MustMatchers
import scala.concurrent.util.Duration
import scala.concurrent.util.duration._
import scala.concurrent.duration._
import scala.concurrent.Await
import java.util.concurrent.TimeUnit._
import akka.testkit.AkkaSpec
import akka.testkit.TestLatch
import java.util.concurrent.TimeoutException
import akka.testkit.LongRunningTest
class DurationSpec extends WordSpec with MustMatchers {
class DurationSpec extends AkkaSpec {
"A HashedWheelTimer" must {
"not mess up long timeouts" taggedAs LongRunningTest in {
val longish = Long.MaxValue.nanos
val barrier = TestLatch()
import system.dispatcher
val job = system.scheduler.scheduleOnce(longish)(barrier.countDown())
intercept[TimeoutException] {
// this used to fire after 46 seconds due to wrap-around
Await.ready(barrier, 90 seconds)
}
job.cancel()
}
}
"Duration" must {
@ -34,11 +54,12 @@ class DurationSpec extends WordSpec with MustMatchers {
val one = 1.second
val inf = Duration.Inf
val minf = Duration.MinusInf
val undefined = Duration.Undefined
(-inf) must be(minf)
intercept[IllegalArgumentException] { minf + inf }
intercept[IllegalArgumentException] { inf - inf }
intercept[IllegalArgumentException] { inf + minf }
intercept[IllegalArgumentException] { minf - minf }
(minf + inf) must be(undefined)
(inf - inf) must be(undefined)
(inf + minf) must be(undefined)
(minf - minf) must be(undefined)
(inf + inf) must be(inf)
(inf - minf) must be(inf)
(minf - inf) must be(minf)

View file

@ -8,10 +8,12 @@ import akka.util.Unsafe;
final class AbstractActorRef {
final static long cellOffset;
final static long lookupOffset;
static {
try {
cellOffset = Unsafe.instance.objectFieldOffset(RepointableActorRef.class.getDeclaredField("_cellDoNotCallMeDirectly"));
lookupOffset = Unsafe.instance.objectFieldOffset(RepointableActorRef.class.getDeclaredField("_lookupDoNotCallMeDirectly"));
} catch(Throwable t){
throw new ExceptionInInitializerError(t);
}

View file

@ -5,7 +5,7 @@ import scala.collection.Seq;
public class JAPI {
public static <T> Seq<T> seq(T... ts) {
return Util.arrayToSeq(ts);
return Util.immutableSeq(ts);
}
}

View file

@ -24,8 +24,8 @@ import java.util.concurrent.ThreadFactory;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import scala.concurrent.util.Duration;
import scala.concurrent.util.FiniteDuration;
import scala.concurrent.duration.Duration;
import scala.concurrent.duration.FiniteDuration;
import akka.event.LoggingAdapter;
import akka.util.Unsafe;
@ -263,8 +263,11 @@ public class HashedWheelTimer implements Timer {
void scheduleTimeout(HashedWheelTimeout timeout, long delay) {
// Prepare the required parameters to schedule the timeout object.
final long relativeIndex = Math.max(1, (delay + tickDuration - 1) / tickDuration); // If relative index < 1 then it should be 1
long relativeIndex = (delay + tickDuration - 1) / tickDuration;
// if the previous line had an overflow going on, then well just schedule this timeout
// one tick early; that shouldnt matter since were talking 270 years here
if (relativeIndex < 0) relativeIndex = delay / tickDuration;
if (relativeIndex == 0) relativeIndex = 1;
final long remainingRounds = relativeIndex / wheel.length;
// Add the timeout to the wheel.
@ -304,7 +307,7 @@ public class HashedWheelTimer implements Timer {
while (!shutdown()) {
final long deadline = waitForNextTick();
if (deadline > 0)
if (deadline > Long.MIN_VALUE)
notifyExpiredTimeouts(fetchExpiredTimeouts(deadline));
}
}
@ -332,7 +335,7 @@ public class HashedWheelTimer implements Timer {
HashedWheelTimeout timeout = i.next();
if (timeout.remainingRounds <= 0) {
i.remove();
if (timeout.deadline <= deadline) {
if (timeout.deadline - deadline <= 0) {
expiredTimeouts.add(timeout);
} else {
// Handle the case where the timeout is put into a wrong
@ -368,6 +371,12 @@ public class HashedWheelTimer implements Timer {
expiredTimeouts.clear();
}
/**
* calculate goal nanoTime from startTime and current tick number,
* then wait until that goal has been reached.
*
* @return Long.MIN_VALUE if received a shutdown request, current time otherwise (with Long.MIN_VALUE changed by +1)
*/
private long waitForNextTick() {
long deadline = startTime + tickDuration * tick;
@ -378,7 +387,8 @@ public class HashedWheelTimer implements Timer {
if (sleepTimeMs <= 0) {
tick += 1;
return currentTime;
if (currentTime == Long.MIN_VALUE) return -Long.MAX_VALUE;
else return currentTime;
}
// Check if we run on windows, as if thats the case we will need
@ -394,7 +404,7 @@ public class HashedWheelTimer implements Timer {
Thread.sleep(sleepTimeMs);
} catch (InterruptedException e) {
if (shutdown()) {
return -1;
return Long.MIN_VALUE;
}
}
}

View file

@ -17,7 +17,7 @@ package akka.util.internal;
import java.util.Set;
import scala.concurrent.util.FiniteDuration;
import scala.concurrent.duration.FiniteDuration;
/**
* Schedules {@link TimerTask}s for one-time future execution in a background

View file

@ -7,7 +7,7 @@
akka {
# Akka version, checked against the runtime version of Akka.
version = "2.1-SNAPSHOT"
version = "2.2-SNAPSHOT"
# Home directory of Akka, modules in the deploy directory will be loaded
home = ""

View file

@ -300,6 +300,11 @@ object Actor {
def apply(x: Any) = throw new UnsupportedOperationException("Empty behavior apply()")
}
/**
* Default placeholder (null) used for "!" to indicate that there is no sender of the message,
* that will be translated to the receiving system's deadLetters.
*/
final val noSender: ActorRef = null
}
/**

View file

@ -6,8 +6,8 @@ package akka.actor
import java.io.{ ObjectOutputStream, NotSerializableException }
import scala.annotation.tailrec
import scala.collection.immutable.TreeSet
import scala.concurrent.util.Duration
import scala.collection.immutable
import scala.concurrent.duration.Duration
import scala.util.control.NonFatal
import akka.actor.dungeon.ChildrenContainer
import akka.actor.dungeon.ChildrenContainer.WaitingForChildren
@ -76,8 +76,14 @@ trait ActorContext extends ActorRefFactory {
/**
* Changes the Actor's behavior to become the new 'Receive' (PartialFunction[Any, Unit]) handler.
* Puts the behavior on top of the hotswap stack.
* If "discardOld" is true, an unbecome will be issued prior to pushing the new behavior to the stack
* This method acts upon the behavior stack as follows:
*
* - if `discardOld = true` it will replace the top element (i.e. the current behavior)
* - if `discardOld = false` it will keep the current behavior and push the given one atop
*
* The default of replacing the current behavior has been chosen to avoid memory leaks in
* case client code is written without consulting this documentation first (i.e. always pushing
* new closures and never issuing an `unbecome()`)
*/
def become(behavior: Actor.Receive, discardOld: Boolean = true): Unit
@ -102,7 +108,7 @@ trait ActorContext extends ActorRefFactory {
* val goodLookup = context.actorFor("kid")
* }}}
*/
def children: Iterable[ActorRef]
def children: immutable.Iterable[ActorRef]
/**
* Get the child with the given name if it exists.
@ -167,14 +173,20 @@ trait UntypedActorContext extends ActorContext {
/**
* Changes the Actor's behavior to become the new 'Procedure' handler.
* Puts the behavior on top of the hotswap stack.
* Replaces the current behavior at the top of the hotswap stack.
*/
def become(behavior: Procedure[Any]): Unit
/**
* Changes the Actor's behavior to become the new 'Procedure' handler.
* Puts the behavior on top of the hotswap stack.
* If "discardOld" is true, an unbecome will be issued prior to pushing the new behavior to the stack
* This method acts upon the behavior stack as follows:
*
* - if `discardOld = true` it will replace the top element (i.e. the current behavior)
* - if `discardOld = false` it will keep the current behavior and push the given one atop
*
* The default of replacing the current behavior has been chosen to avoid memory leaks in
* case client code is written without consulting this documentation first (i.e. always pushing
* new closures and never issuing an `unbecome()`)
*/
def become(behavior: Procedure[Any], discardOld: Boolean): Unit
@ -196,6 +208,11 @@ private[akka] trait Cell {
* The system internals where this Cell lives.
*/
def systemImpl: ActorSystemImpl
/**
* Start the cell: enqueued message must not be processed before this has
* been called. The usual action is to attach the mailbox to a dispatcher.
*/
def start(): this.type
/**
* Recursively suspend this actor and all its children. Must not throw exceptions.
*/
@ -247,12 +264,12 @@ private[akka] trait Cell {
*/
def isLocal: Boolean
/**
* If the actor isLocal, returns whether messages are currently queued,
* If the actor isLocal, returns whether "user messages" are currently queued,
* false otherwise.
*/
def hasMessages: Boolean
/**
* If the actor isLocal, returns the number of messages currently queued,
* If the actor isLocal, returns the number of "user messages" currently queued,
* which may be a costly operation, 0 otherwise.
*/
def numberOfMessages: Int
@ -275,7 +292,7 @@ private[akka] object ActorCell {
final val emptyBehaviorStack: List[Actor.Receive] = Nil
final val emptyActorRefSet: Set[ActorRef] = TreeSet.empty
final val emptyActorRefSet: Set[ActorRef] = immutable.TreeSet.empty
}
//ACTORCELL IS 64bytes and should stay that way unless very good reason not to (machine sympathy, cache line fit)
@ -350,7 +367,7 @@ private[akka] class ActorCell(
case w: WaitingForChildren w.enqueue(message)
}
case Terminate() terminate()
case Supervise(child, uid) supervise(child, uid)
case Supervise(child, async, uid) supervise(child, async, uid)
case ChildTerminated(child) todo = handleChildTerminated(child)
case NoMessage // only here to suppress warning
}
@ -480,20 +497,20 @@ private[akka] class ActorCell(
}
}
private def supervise(child: ActorRef, uid: Int): Unit = if (!isTerminating) {
private def supervise(child: ActorRef, async: Boolean, uid: Int): Unit = if (!isTerminating) {
// Supervise is the first thing we get from a new child, so store away the UID for later use in handleFailure()
initChild(child) match {
case Some(crs)
crs.uid = uid
handleSupervise(child)
handleSupervise(child, async)
if (system.settings.DebugLifecycle) publish(Debug(self.path.toString, clazz(actor), "now supervising " + child))
case None publish(Error(self.path.toString, clazz(actor), "received Supervise from unregistered child " + child + ", this will not end well"))
}
}
// future extension point
protected def handleSupervise(child: ActorRef): Unit = child match {
case r: RepointableActorRef r.activate()
protected def handleSupervise(child: ActorRef, async: Boolean): Unit = child match {
case r: RepointableActorRef if async r.point()
case _
}

View file

@ -5,13 +5,11 @@
package akka.actor
import scala.collection.mutable.Queue
import scala.concurrent.util.Duration
import scala.concurrent.util.duration._
import scala.concurrent.duration._
import akka.pattern.ask
import scala.concurrent.Await
import akka.util.Timeout
import scala.collection.immutable.TreeSet
import scala.concurrent.util.Deadline
import java.util.concurrent.TimeoutException
import java.util.concurrent.atomic.AtomicInteger
import java.util.concurrent.TimeUnit

View file

@ -3,6 +3,8 @@
*/
package akka.actor
import scala.annotation.tailrec
import scala.collection.immutable
import akka.japi.Util.immutableSeq
import java.net.MalformedURLException
object ActorPath {
@ -20,6 +22,8 @@ object ActorPath {
* http://www.ietf.org/rfc/rfc2396.txt
*/
val ElementRegex = """(?:[-\w:@&=+,.!~*'_;]|%\p{XDigit}{2})(?:[-\w:@&=+,.!~*'$_;]|%\p{XDigit}{2})*""".r
private[akka] final val emptyActorPath: immutable.Iterable[String] = List("")
}
/**
@ -68,23 +72,18 @@ sealed trait ActorPath extends Comparable[ActorPath] with Serializable {
/**
* ''Java API'': Recursively create a descendants path by appending all child names.
*/
def descendant(names: java.lang.Iterable[String]): ActorPath = {
import scala.collection.JavaConverters._
/(names.asScala)
}
def descendant(names: java.lang.Iterable[String]): ActorPath = /(immutableSeq(names))
/**
* Sequence of names for this path from root to this. Performance implication: has to allocate a list.
*/
def elements: Iterable[String]
def elements: immutable.Iterable[String]
/**
* ''Java API'': Sequence of names for this path from root to this. Performance implication: has to allocate a list.
*/
def getElements: java.lang.Iterable[String] = {
import scala.collection.JavaConverters._
elements.asJava
}
def getElements: java.lang.Iterable[String] =
scala.collection.JavaConverters.asJavaIterableConverter(elements).asJava
/**
* Walk up the tree to obtain and return the RootActorPath.
@ -112,7 +111,7 @@ final case class RootActorPath(address: Address, name: String = "/") extends Act
override def /(child: String): ActorPath = new ChildActorPath(this, child)
override val elements: Iterable[String] = List("")
override def elements: immutable.Iterable[String] = ActorPath.emptyActorPath
override val toString: String = address + name
@ -121,7 +120,7 @@ final case class RootActorPath(address: Address, name: String = "/") extends Act
else addr + name
override def compareTo(other: ActorPath): Int = other match {
case r: RootActorPath toString compareTo r.toString
case r: RootActorPath toString compareTo r.toString // FIXME make this cheaper by comparing address and name in isolation
case c: ChildActorPath 1
}
}
@ -134,9 +133,9 @@ final class ChildActorPath(val parent: ActorPath, val name: String) extends Acto
override def /(child: String): ActorPath = new ChildActorPath(this, child)
override def elements: Iterable[String] = {
override def elements: immutable.Iterable[String] = {
@tailrec
def rec(p: ActorPath, acc: List[String]): Iterable[String] = p match {
def rec(p: ActorPath, acc: List[String]): immutable.Iterable[String] = p match {
case r: RootActorPath acc
case _ rec(p.parent, p.name :: acc)
}

View file

@ -153,7 +153,7 @@ trait ScalaActorRef { ref: ActorRef ⇒
* </pre>
* <p/>
*/
def !(message: Any)(implicit sender: ActorRef = null): Unit
def !(message: Any)(implicit sender: ActorRef = Actor.noSender): Unit
}
@ -194,6 +194,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 start(): Unit
def resume(causedByFailure: Throwable): Unit
def suspend(): Unit
def restart(cause: Throwable): Unit
@ -259,13 +260,16 @@ private[akka] class LocalActorRef private[akka] (
/*
* Safe publication of this classs fields is guaranteed by mailbox.setActor()
* which is called indirectly from actorCell.start() (if youre wondering why
* which is called indirectly from actorCell.init() (if youre wondering why
* this is at all important, remember that under the JMM final fields are only
* frozen at the _end_ of the constructor, but we are publishing this before
* that is reached).
* This means that the result of newActorCell needs to be written to the val
* actorCell before we call init and start, since we can start using "this"
* object from another thread as soon as we run init.
*/
private val actorCell: ActorCell = newActorCell(_system, this, _props, _supervisor)
actorCell.start(sendSupervise = true, ThreadLocalRandom.current.nextInt())
actorCell.init(ThreadLocalRandom.current.nextInt(), sendSupervise = true)
protected def newActorCell(system: ActorSystemImpl, ref: InternalActorRef, props: Props, supervisor: InternalActorRef): ActorCell =
new ActorCell(system, ref, props, supervisor)
@ -279,6 +283,11 @@ private[akka] class LocalActorRef private[akka] (
*/
override def isTerminated: Boolean = actorCell.isTerminated
/**
* Starts the actor after initialization.
*/
override def start(): Unit = actorCell.start()
/**
* Suspends the actor so that it will not process messages until resumed. The
* suspend request is processed asynchronously to the caller of this method
@ -341,7 +350,7 @@ private[akka] class LocalActorRef private[akka] (
override def sendSystemMessage(message: SystemMessage): Unit = actorCell.sendSystemMessage(message)
override def !(message: Any)(implicit sender: ActorRef = null): Unit = actorCell.tell(message, sender)
override def !(message: Any)(implicit sender: ActorRef = Actor.noSender): Unit = actorCell.tell(message, sender)
override def restart(cause: Throwable): Unit = actorCell.restart(cause)
@ -390,12 +399,13 @@ private[akka] trait MinimalActorRef extends InternalActorRef with LocalRef {
override def getParent: InternalActorRef = Nobody
override def getChild(names: Iterator[String]): InternalActorRef = if (names.forall(_.isEmpty)) this else Nobody
override def start(): Unit = ()
override def suspend(): Unit = ()
override def resume(causedByFailure: Throwable): Unit = ()
override def stop(): Unit = ()
override def isTerminated = false
override def !(message: Any)(implicit sender: ActorRef = null): Unit = ()
override def !(message: Any)(implicit sender: ActorRef = Actor.noSender): Unit = ()
override def sendSystemMessage(message: SystemMessage): Unit = ()
override def restart(cause: Throwable): Unit = ()
@ -409,7 +419,10 @@ private[akka] trait MinimalActorRef extends InternalActorRef with LocalRef {
* to the ActorSystem's EventStream
*/
@SerialVersionUID(1L)
case class DeadLetter(message: Any, sender: ActorRef, recipient: ActorRef)
case class DeadLetter(message: Any, sender: ActorRef, recipient: ActorRef) {
require(sender ne null, "DeadLetter sender may not be null")
require(recipient ne null, "DeadLetter recipient may not be null")
}
private[akka] object DeadLetterActorRef {
@SerialVersionUID(1L)
@ -435,9 +448,12 @@ private[akka] class EmptyLocalActorRef(override val provider: ActorRefProvider,
override def sendSystemMessage(message: SystemMessage): Unit = specialHandle(message)
override def !(message: Any)(implicit sender: ActorRef = null): Unit = message match {
case d: DeadLetter specialHandle(d.message) // do NOT form endless loops, since deadLetters will resend!
case _ if (!specialHandle(message)) eventStream.publish(DeadLetter(message, sender, this))
override def !(message: Any)(implicit sender: ActorRef = Actor.noSender): Unit = message match {
case d: DeadLetter
specialHandle(d.message) // do NOT form endless loops, since deadLetters will resend!
case _ if !specialHandle(message)
eventStream.publish(DeadLetter(message, if (sender eq Actor.noSender) provider.deadLetters else sender, this))
case _
}
protected def specialHandle(msg: Any): Boolean = msg match {
@ -520,7 +536,7 @@ private[akka] class VirtualPathContainer(
def hasChildren: Boolean = !children.isEmpty
def foreachChild(f: ActorRef Unit) = {
def foreachChild(f: ActorRef Unit): Unit = {
val iter = children.values.iterator
while (iter.hasNext) f(iter.next)
}

View file

@ -8,8 +8,9 @@ import akka.dispatch._
import akka.routing._
import akka.event._
import akka.util.{ Switch, Helpers }
import akka.japi.Util.immutableSeq
import akka.util.Collections.EmptyImmutableSeq
import scala.util.{ Success, Failure }
import scala.util.control.NonFatal
import scala.concurrent.{ Future, Promise }
import java.util.concurrent.atomic.AtomicLong
@ -41,8 +42,7 @@ trait ActorRefProvider {
def deadLetters: ActorRef
/**
* The root path for all actors within this actor system, including remote
* address if enabled.
* The root path for all actors within this actor system, not including any remote address information.
*/
def rootPath: ActorPath
@ -145,6 +145,11 @@ trait ActorRefProvider {
* attempt is made to verify actual reachability).
*/
def getExternalAddressFor(addr: Address): Option[Address]
/**
* Obtain the external address of the default transport.
*/
def getDefaultAddress: Address
}
/**
@ -271,10 +276,7 @@ trait ActorRefFactory {
*
* For maximum performance use a collection with efficient head & tail operations.
*/
def actorFor(path: java.lang.Iterable[String]): ActorRef = {
import scala.collection.JavaConverters._
provider.actorFor(lookupRoot, path.asScala)
}
def actorFor(path: java.lang.Iterable[String]): ActorRef = provider.actorFor(lookupRoot, immutableSeq(path))
/**
* Construct an [[akka.actor.ActorSelection]] from the given path, which is
@ -319,6 +321,10 @@ private[akka] object SystemGuardian {
/**
* Local ActorRef provider.
*
* INTERNAL API!
*
* Depending on this class is not supported, only the [[ActorRefProvider]] interface is supported.
*/
class LocalActorRefProvider(
_systemName: String,
@ -375,7 +381,7 @@ class LocalActorRefProvider(
override def stop(): Unit = stopped switchOn { terminationPromise.complete(causeOfTermination.map(Failure(_)).getOrElse(Success(()))) }
override def isTerminated: Boolean = stopped.isOn
override def !(message: Any)(implicit sender: ActorRef = null): Unit = stopped.ifOff(message match {
override def !(message: Any)(implicit sender: ActorRef = Actor.noSender): Unit = stopped.ifOff(message match {
case Failed(ex, _) if sender ne null causeOfTermination = Some(ex); sender.asInstanceOf[InternalActorRef].stop()
case NullMessage // do nothing
case _ log.error(this + " received unexpected message [" + message + "]")
@ -383,7 +389,7 @@ class LocalActorRefProvider(
override def sendSystemMessage(message: SystemMessage): Unit = stopped ifOff {
message match {
case Supervise(_, _) // TODO register child in some map to keep track of it and enable shutdown after all dead
case Supervise(_, _, _) // TODO register child in some map to keep track of it and enable shutdown after all dead
case ChildTerminated(_) stop()
case _ log.error(this + " received unexpected system message [" + message + "]")
}
@ -480,7 +486,7 @@ class LocalActorRefProvider(
def registerExtraNames(_extras: Map[String, InternalActorRef]): Unit = extraNames ++= _extras
private def guardianSupervisorStrategyConfigurator =
dynamicAccess.createInstanceFor[SupervisorStrategyConfigurator](settings.SupervisorStrategyClass, Seq()).get
dynamicAccess.createInstanceFor[SupervisorStrategyConfigurator](settings.SupervisorStrategyClass, EmptyImmutableSeq).get
/**
* Overridable supervision strategy to be used by the /user guardian.
@ -516,6 +522,7 @@ class LocalActorRefProvider(
cell.reserveChild("user")
val ref = new LocalActorRef(system, Props(new Guardian(guardianStrategy)), rootGuardian, rootPath / "user")
cell.initChild(ref)
ref.start()
ref
}
@ -524,6 +531,7 @@ class LocalActorRefProvider(
cell.reserveChild("system")
val ref = new LocalActorRef(system, Props(new SystemGuardian(systemGuardianStrategy)), rootGuardian, rootPath / "system")
cell.initChild(ref)
ref.start()
ref
}
@ -585,16 +593,17 @@ class LocalActorRefProvider(
if (settings.DebugRouterMisconfiguration && deployer.lookup(path).isDefined)
log.warning("Configuration says that {} should be a router, but code disagrees. Remove the config or add a routerConfig to its Props.")
if (async) new RepointableActorRef(system, props, supervisor, path).initialize()
if (async) new RepointableActorRef(system, props, supervisor, path).initialize(async)
else new LocalActorRef(system, props, supervisor, path)
case router
val lookup = if (lookupDeploy) deployer.lookup(path) else None
val fromProps = Iterator(props.deploy.copy(routerConfig = props.deploy.routerConfig withFallback router))
val d = fromProps ++ deploy.iterator ++ lookup.iterator reduce ((a, b) b withFallback a)
val ref = new RoutedActorRef(system, props.withRouter(d.routerConfig), supervisor, path).initialize()
if (async) ref else ref.activate()
new RoutedActorRef(system, props.withRouter(d.routerConfig), supervisor, path).initialize(async)
}
}
def getExternalAddressFor(addr: Address): Option[Address] = if (addr == rootPath.address) Some(addr) else None
def getDefaultAddress: Address = rootPath.address
}

View file

@ -23,7 +23,6 @@ abstract class ActorSelection {
def tell(msg: Any, sender: ActorRef): Unit = target.tell(toMessage(msg, path), sender)
// FIXME make this so that "next" instead is the remaining path
private def toMessage(msg: Any, path: Array[AnyRef]): Any = {
var acc = msg
var index = path.length - 1
@ -70,5 +69,5 @@ object ActorSelection {
trait ScalaActorSelection {
this: ActorSelection
def !(msg: Any)(implicit sender: ActorRef = null) = tell(msg, sender)
def !(msg: Any)(implicit sender: ActorRef = Actor.noSender) = tell(msg, sender)
}

View file

@ -6,24 +6,24 @@ package akka.actor
import akka.event._
import akka.dispatch._
import akka.pattern.ask
import akka.japi.Util.immutableSeq
import com.typesafe.config.{ Config, ConfigFactory }
import scala.annotation.tailrec
import scala.concurrent.util.Duration
import java.io.Closeable
import scala.collection.immutable
import scala.concurrent.duration.{ FiniteDuration, Duration }
import scala.concurrent.{ Await, Awaitable, CanAwait, Future }
import scala.util.{ Failure, Success }
import scala.util.control.NonFatal
import akka.util._
import java.io.Closeable
import akka.util.internal.{ HashedWheelTimer, ConcurrentIdentityHashMap }
import java.util.concurrent.{ ThreadFactory, CountDownLatch, TimeoutException, RejectedExecutionException }
import java.util.concurrent.TimeUnit.MILLISECONDS
import akka.actor.dungeon.ChildrenContainer
import scala.concurrent.util.FiniteDuration
import util.{ Failure, Success }
object ActorSystem {
val Version: String = "2.1-SNAPSHOT"
val Version: String = "2.2-SNAPSHOT"
val EnvHome: Option[String] = System.getenv("AKKA_HOME") match {
case null | "" | "." None
@ -144,7 +144,7 @@ object ActorSystem {
final val LogLevel: String = getString("akka.loglevel")
final val StdoutLogLevel: String = getString("akka.stdout-loglevel")
final val EventHandlers: Seq[String] = getStringList("akka.event-handlers").asScala
final val EventHandlers: immutable.Seq[String] = immutableSeq(getStringList("akka.event-handlers"))
final val EventHandlerStartTimeout: Timeout = Timeout(Duration(getMilliseconds("akka.event-handler-startup-timeout"), MILLISECONDS))
final val LogConfigOnStart: Boolean = config.getBoolean("akka.log-config-on-start")
@ -273,10 +273,7 @@ abstract class ActorSystem extends ActorRefFactory {
/**
* ''Java API'': Recursively create a descendants path by appending all child names.
*/
def descendant(names: java.lang.Iterable[String]): ActorPath = {
import scala.collection.JavaConverters._
/(names.asScala)
}
def descendant(names: java.lang.Iterable[String]): ActorPath = /(immutableSeq(names))
/**
* Start-up time in milliseconds since the epoch.
@ -536,7 +533,7 @@ private[akka] class ActorSystemImpl(val name: String, applicationConfig: Config,
val scheduler: Scheduler = createScheduler()
val provider: ActorRefProvider = {
val arguments = Seq(
val arguments = Vector(
classOf[String] -> name,
classOf[Settings] -> settings,
classOf[EventStream] -> eventStream,
@ -676,9 +673,8 @@ private[akka] class ActorSystemImpl(val name: String, applicationConfig: Config,
def hasExtension(ext: ExtensionId[_ <: Extension]): Boolean = findExtension(ext) != null
private def loadExtensions() {
import scala.collection.JavaConversions._
settings.config.getStringList("akka.extensions") foreach { fqcn
dynamicAccess.getObjectFor[AnyRef](fqcn) recoverWith { case _ dynamicAccess.createInstanceFor[AnyRef](fqcn, Seq()) } match {
immutableSeq(settings.config.getStringList("akka.extensions")) foreach { fqcn
dynamicAccess.getObjectFor[AnyRef](fqcn) recoverWith { case _ dynamicAccess.createInstanceFor[AnyRef](fqcn, Nil) } match {
case Success(p: ExtensionIdProvider) registerExtension(p.lookup())
case Success(p: ExtensionId[_]) registerExtension(p)
case Success(other) log.error("[{}] is not an 'ExtensionIdProvider' or 'ExtensionId', skipping...", fqcn)

View file

@ -5,7 +5,8 @@ package akka.actor
import java.net.URI
import java.net.URISyntaxException
import java.net.MalformedURLException
import annotation.tailrec
import scala.annotation.tailrec
import scala.collection.immutable
/**
* The address specifies the physical location under which an Actor can be
@ -71,11 +72,11 @@ private[akka] trait PathUtils {
}
object RelativeActorPath extends PathUtils {
def unapply(addr: String): Option[Iterable[String]] = {
def unapply(addr: String): Option[immutable.Seq[String]] = {
try {
val uri = new URI(addr)
if (uri.isAbsolute) None
else Some(split(uri.getPath))
else Some(split(uri.getRawPath))
} catch {
case _: URISyntaxException None
}
@ -119,13 +120,12 @@ object AddressFromURIString {
* Given an ActorPath it returns the Address and the path elements if the path is well-formed
*/
object ActorPathExtractor extends PathUtils {
def unapply(addr: String): Option[(Address, Iterable[String])] =
def unapply(addr: String): Option[(Address, immutable.Iterable[String])] =
try {
val uri = new URI(addr)
if (uri.getPath == null) None
else AddressFromURIString.unapply(uri) match {
case None None
case Some(addr) Some((addr, split(uri.getPath).drop(1)))
uri.getRawPath match {
case null None
case path AddressFromURIString.unapply(uri).map((_, split(path).drop(1)))
}
} catch {
case _: URISyntaxException None

View file

@ -4,13 +4,14 @@
package akka.actor
import scala.concurrent.util.Duration
import scala.concurrent.duration.Duration
import com.typesafe.config._
import akka.routing._
import akka.japi.Util.immutableSeq
import java.util.concurrent.{ TimeUnit }
import akka.util.WildcardTree
import java.util.concurrent.atomic.AtomicReference
import annotation.tailrec
import scala.annotation.tailrec
/**
* This class represents deployment configuration for a given actor path. It is
@ -79,7 +80,11 @@ trait Scope {
@SerialVersionUID(1L)
abstract class LocalScope extends Scope
//FIXME docs
/**
* The Local Scope is the default one, which is assumed on all deployments
* which do not set a different scope. It is also the only scope handled by
* the LocalActorRefProvider.
*/
case object LocalScope extends LocalScope {
/**
* Java API: get the singleton instance
@ -134,16 +139,24 @@ private[akka] class Deployer(val settings: ActorSystem.Settings, val dynamicAcce
}
def parseConfig(key: String, config: Config): Option[Deploy] = {
val deployment = config.withFallback(default)
val router = createRouterConfig(deployment.getString("router"), key, config, deployment)
Some(Deploy(key, deployment, router, NoScopeGiven))
}
val routees = Vector() ++ deployment.getStringList("routees.paths").asScala
/**
* Factory method for creating `RouterConfig`
* @param routerType the configured name of the router, or FQCN
* @param key the full configuration key of the deployment section
* @param config the user defined config of the deployment, without defaults
* @param deployment the deployment config, with defaults
*/
protected def createRouterConfig(routerType: String, key: String, config: Config, deployment: Config): RouterConfig = {
val routees = immutableSeq(deployment.getStringList("routees.paths"))
val nrOfInstances = deployment.getInt("nr-of-instances")
val resizer = if (config.hasPath("resizer")) Some(DefaultResizer(deployment.getConfig("resizer"))) else None
val resizer: Option[Resizer] = if (config.hasPath("resizer")) Some(DefaultResizer(deployment.getConfig("resizer"))) else None
val router: RouterConfig = deployment.getString("router") match {
routerType match {
case "from-code" NoRouter
case "round-robin" RoundRobinRouter(nrOfInstances, routees, resizer)
case "random" RandomRouter(nrOfInstances, routees, resizer)
@ -156,7 +169,7 @@ private[akka] class Deployer(val settings: ActorSystem.Settings, val dynamicAcce
val vnodes = deployment.getInt("virtual-nodes-factor")
ConsistentHashingRouter(nrOfInstances, routees, resizer, virtualNodesFactor = vnodes)
case fqn
val args = Seq(classOf[Config] -> deployment)
val args = List(classOf[Config] -> deployment)
dynamicAccess.createInstanceFor[RouterConfig](fqn, args).recover({
case exception throw new IllegalArgumentException(
("Cannot instantiate router [%s], defined in [%s], " +
@ -165,7 +178,6 @@ private[akka] class Deployer(val settings: ActorSystem.Settings, val dynamicAcce
.format(fqn, key), exception)
}).get
}
}
Some(Deploy(key, deployment, router, NoScopeGiven))
}
}

View file

@ -3,7 +3,7 @@
*/
package akka.actor
import scala.util.control.NonFatal
import scala.collection.immutable
import java.lang.reflect.InvocationTargetException
import scala.reflect.ClassTag
import scala.util.Try
@ -25,7 +25,7 @@ abstract class DynamicAccess {
* val obj = DynamicAccess.createInstanceFor(clazz, Seq(classOf[Config] -> config, classOf[String] -> name))
* }}}
*/
def createInstanceFor[T: ClassTag](clazz: Class[_], args: Seq[(Class[_], AnyRef)]): Try[T]
def createInstanceFor[T: ClassTag](clazz: Class[_], args: immutable.Seq[(Class[_], AnyRef)]): Try[T]
/**
* Obtain a `Class[_]` object loaded with the right class loader (i.e. the one
@ -40,7 +40,7 @@ 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: ClassTag](fqcn: String, args: Seq[(Class[_], AnyRef)]): Try[T]
def createInstanceFor[T: ClassTag](fqcn: String, args: immutable.Seq[(Class[_], AnyRef)]): Try[T]
/**
* Obtain the Scala object instance for the given fully-qualified class name, if there is one.
@ -70,7 +70,7 @@ class ReflectiveDynamicAccess(val classLoader: ClassLoader) extends DynamicAcces
if (t.isAssignableFrom(c)) c else throw new ClassCastException(t + " is not assignable from " + c)
})
override def createInstanceFor[T: ClassTag](clazz: Class[_], args: Seq[(Class[_], AnyRef)]): Try[T] =
override def createInstanceFor[T: ClassTag](clazz: Class[_], args: immutable.Seq[(Class[_], AnyRef)]): Try[T] =
Try {
val types = args.map(_._1).toArray
val values = args.map(_._2).toArray
@ -81,7 +81,7 @@ class ReflectiveDynamicAccess(val classLoader: ClassLoader) extends DynamicAcces
if (t.isInstance(obj)) obj.asInstanceOf[T] else throw new ClassCastException(clazz.getName + " is not a subtype of " + t)
} recover { case i: InvocationTargetException if i.getTargetException ne null throw i.getTargetException }
override def createInstanceFor[T: ClassTag](fqcn: String, args: Seq[(Class[_], AnyRef)]): Try[T] =
override def createInstanceFor[T: ClassTag](fqcn: String, args: immutable.Seq[(Class[_], AnyRef)]): Try[T] =
getClassFor(fqcn) flatMap { c createInstanceFor(c, args) }
override def getObjectFor[T: ClassTag](fqcn: String): Try[T] = {

View file

@ -98,5 +98,5 @@ abstract class ExtensionKey[T <: Extension](implicit m: ClassTag[T]) extends Ext
def this(clazz: Class[T]) = this()(ClassTag(clazz))
override def lookup(): ExtensionId[T] = this
def createExtension(system: ExtendedActorSystem): T = system.dynamicAccess.createInstanceFor[T](m.runtimeClass, Seq(classOf[ExtendedActorSystem] -> system)).get
def createExtension(system: ExtendedActorSystem): T = system.dynamicAccess.createInstanceFor[T](m.runtimeClass, List(classOf[ExtendedActorSystem] -> system)).get
}

View file

@ -5,10 +5,10 @@ package akka.actor
import language.implicitConversions
import akka.util._
import scala.concurrent.util.Duration
import scala.concurrent.duration.Duration
import scala.collection.mutable
import akka.routing.{ Deafen, Listen, Listeners }
import scala.concurrent.util.FiniteDuration
import scala.concurrent.duration.FiniteDuration
object FSM {
@ -238,7 +238,7 @@ object FSM {
* setTimer("tock", TockMsg, 1 second, true) // repeating
* setTimer("lifetime", TerminateMsg, 1 hour, false) // single-shot
* cancelTimer("tock")
* timerActive_? ("tock")
* isTimerActive("tock")
* </pre>
*/
trait FSM[S, D] extends Listeners with ActorLogging {
@ -372,7 +372,15 @@ trait FSM[S, D] extends Listeners with ActorLogging {
* timer does not exist, has previously been canceled or if it was a
* single-shot timer whose message was already received.
*/
final def timerActive_?(name: String) = timers contains name
@deprecated("Use isTimerActive(name) instead.", "2.2")
final def timerActive_?(name: String) = isTimerActive(name)
/**
* Inquire whether the named timer is still active. Returns true unless the
* timer does not exist, has previously been canceled or if it was a
* single-shot timer whose message was already received.
*/
final def isTimerActive(name: String) = timers contains name
/**
* Set state timeout explicitly. This method can safely be used from within a
@ -380,6 +388,11 @@ trait FSM[S, D] extends Listeners with ActorLogging {
*/
final def setStateTimeout(state: S, timeout: Timeout): Unit = stateTimeouts(state) = timeout
/**
* Internal API, used for testing.
*/
private[akka] final def isStateTimerActive = timeoutFuture.isDefined
/**
* Set handler which is called upon each state transition, i.e. not when
* staying in the same state. This may use the pair extractor defined in the
@ -427,6 +440,8 @@ trait FSM[S, D] extends Listeners with ActorLogging {
/**
* Set handler which is called upon reception of unhandled messages. Calling
* this method again will overwrite the previous contents.
*
* The current state may be queried using ``stateName``.
*/
final def whenUnhandled(stateFunction: StateFunction): Unit =
handleEvent = stateFunction orElse handleEventDefault
@ -519,7 +534,7 @@ trait FSM[S, D] extends Listeners with ActorLogging {
* Main actor receive() method
* *******************************************
*/
override final def receive: Receive = {
override def receive: Receive = {
case TimeoutMarker(gen)
if (generation == gen) {
processMsg(StateTimeout, "state timeout")
@ -632,6 +647,8 @@ trait FSM[S, D] extends Listeners with ActorLogging {
case Failure(msg: AnyRef) log.error(msg.toString)
case _
}
for (timer timers.values) timer.cancel()
timers.clear()
val stopEvent = StopEvent(reason, currentState.stateName, currentState.stateData)
if (terminateEvent.isDefinedAt(stopEvent))
terminateEvent(stopEvent)

View file

@ -5,11 +5,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 scala.concurrent.util.Duration
import java.util.concurrent.TimeUnit
import akka.japi.Util.immutableSeq
import scala.collection.mutable.ArrayBuffer
import scala.collection.immutable
import scala.concurrent.duration.Duration
/**
* INTERNAL API
*/
@ -171,7 +173,7 @@ object SupervisorStrategy extends SupervisorStrategyLowPriorityImplicits {
* Implicit conversion from `Seq` of Throwables to a `Decider`.
* This maps the given Throwables to restarts, otherwise escalates.
*/
implicit def seqThrowable2Decider(trapExit: Seq[Class[_ <: Throwable]]): Decider = makeDecider(trapExit)
implicit def seqThrowable2Decider(trapExit: immutable.Seq[Class[_ <: Throwable]]): Decider = makeDecider(trapExit)
type Decider = PartialFunction[Throwable, Directive]
type JDecider = akka.japi.Function[Throwable, Directive]
@ -181,21 +183,15 @@ object SupervisorStrategy extends SupervisorStrategyLowPriorityImplicits {
* Decider builder which just checks whether one of
* the given Throwables matches the cause and restarts, otherwise escalates.
*/
def makeDecider(trapExit: Array[Class[_]]): Decider =
{ case x if (trapExit exists (_ isInstance x)) Restart else Escalate }
def makeDecider(trapExit: immutable.Seq[Class[_ <: Throwable]]): Decider = {
case x if (trapExit exists (_ isInstance x)) Restart else Escalate
}
/**
* Decider builder which just checks whether one of
* the given Throwables matches the cause and restarts, otherwise escalates.
*/
def makeDecider(trapExit: Seq[Class[_ <: Throwable]]): Decider =
{ case x if (trapExit exists (_ isInstance x)) Restart else Escalate }
/**
* Decider builder which just checks whether one of
* the given Throwables matches the cause and restarts, otherwise escalates.
*/
def makeDecider(trapExit: JIterable[Class[_ <: Throwable]]): Decider = makeDecider(trapExit.toSeq)
def makeDecider(trapExit: JIterable[Class[_ <: Throwable]]): Decider = makeDecider(immutableSeq(trapExit))
/**
* Decider builder for Iterables of cause-directive pairs, e.g. a map obtained
@ -220,20 +216,22 @@ object SupervisorStrategy extends SupervisorStrategyLowPriorityImplicits {
*
* INTERNAL API
*/
private[akka] def sort(in: Iterable[CauseDirective]): Seq[CauseDirective] =
private[akka] def sort(in: Iterable[CauseDirective]): immutable.Seq[CauseDirective] =
(new ArrayBuffer[CauseDirective](in.size) /: in) { (buf, ca)
buf.indexWhere(_._1 isAssignableFrom ca._1) match {
case -1 buf append ca
case x buf insert (x, ca)
}
buf
}
}.to[immutable.IndexedSeq]
private[akka] def withinTimeRangeOption(withinTimeRange: Duration): Option[Duration] =
if (withinTimeRange.isFinite && withinTimeRange >= Duration.Zero) Some(withinTimeRange) else None
private[akka] def maxNrOfRetriesOption(maxNrOfRetries: Int): Option[Int] =
if (maxNrOfRetries < 0) None else Some(maxNrOfRetries)
private[akka] val escalateDefault = (_: Any) Escalate
}
/**
@ -280,7 +278,7 @@ abstract class SupervisorStrategy {
* @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
val directive = decider.applyOrElse(cause, escalateDefault)
directive match {
case Resume resumeChild(child, cause); true
case Restart processFailure(context, true, child, cause, stats, children); true
@ -334,10 +332,6 @@ case class AllForOneStrategy(maxNrOfRetries: Int = -1, withinTimeRange: Duration
def this(maxNrOfRetries: Int, withinTimeRange: Duration, trapExit: JIterable[Class[_ <: Throwable]]) =
this(maxNrOfRetries, withinTimeRange)(SupervisorStrategy.makeDecider(trapExit))
def this(maxNrOfRetries: Int, withinTimeRange: Duration, trapExit: Array[Class[_]]) =
this(maxNrOfRetries, withinTimeRange)(SupervisorStrategy.makeDecider(trapExit))
/*
* this is a performance optimization to avoid re-allocating the pairs upon
* every call to requestRestartPermission, assuming that strategies are shared
@ -376,9 +370,6 @@ case class OneForOneStrategy(maxNrOfRetries: Int = -1, withinTimeRange: Duration
def this(maxNrOfRetries: Int, withinTimeRange: Duration, trapExit: JIterable[Class[_ <: Throwable]]) =
this(maxNrOfRetries, withinTimeRange)(SupervisorStrategy.makeDecider(trapExit))
def this(maxNrOfRetries: Int, withinTimeRange: Duration, trapExit: Array[Class[_]]) =
this(maxNrOfRetries, withinTimeRange)(SupervisorStrategy.makeDecider(trapExit))
/*
* this is a performance optimization to avoid re-allocating the pairs upon
* every call to requestRestartPermission, assuming that strategies are shared

View file

@ -6,8 +6,9 @@ package akka.actor
import language.higherKinds
import language.postfixOps
import scala.collection.immutable
import scala.concurrent.{ ExecutionContext, Future }
import scala.concurrent.util.Duration
import scala.concurrent.duration.Duration
import scala.util.control.NonFatal
import akka.util.ByteString
import java.net.{ SocketAddress, InetSocketAddress }
@ -122,7 +123,7 @@ object IO {
* @return a new SocketHandle that can be used to perform actions on the
* new connection's SocketChannel.
*/
def accept(options: Seq[SocketOption] = Seq.empty)(implicit socketOwner: ActorRef): SocketHandle = {
def accept(options: immutable.Seq[SocketOption] = Nil)(implicit socketOwner: ActorRef): SocketHandle = {
val socket = SocketHandle(socketOwner, ioManager)
ioManager ! Accept(socket, this, options)
socket
@ -250,7 +251,7 @@ object IO {
*
* Normally sent using IOManager.listen()
*/
case class Listen(server: ServerHandle, address: SocketAddress, options: Seq[ServerSocketOption] = Seq.empty) extends IOMessage
case class Listen(server: ServerHandle, address: SocketAddress, options: immutable.Seq[ServerSocketOption] = Nil) extends IOMessage
/**
* Message from an [[akka.actor.IOManager]] that the ServerSocketChannel is
@ -272,7 +273,7 @@ object IO {
*
* Normally sent using [[akka.actor.IO.ServerHandle]].accept()
*/
case class Accept(socket: SocketHandle, server: ServerHandle, options: Seq[SocketOption] = Seq.empty) extends IOMessage
case class Accept(socket: SocketHandle, server: ServerHandle, options: immutable.Seq[SocketOption] = Nil) extends IOMessage
/**
* Message to an [[akka.actor.IOManager]] to create a SocketChannel connected
@ -280,7 +281,7 @@ object IO {
*
* Normally sent using IOManager.connect()
*/
case class Connect(socket: SocketHandle, address: SocketAddress, options: Seq[SocketOption] = Seq.empty) extends IOMessage
case class Connect(socket: SocketHandle, address: SocketAddress, options: immutable.Seq[SocketOption] = Nil) extends IOMessage
/**
* Message from an [[akka.actor.IOManager]] that the SocketChannel has
@ -832,7 +833,7 @@ final class IOManager private (system: ExtendedActorSystem) extends Extension {
* @param option Seq of [[akka.actor.IO.ServerSocketOptions]] to setup on socket
* @return a [[akka.actor.IO.ServerHandle]] to uniquely identify the created socket
*/
def listen(address: SocketAddress, options: Seq[IO.ServerSocketOption])(implicit owner: ActorRef): IO.ServerHandle = {
def listen(address: SocketAddress, options: immutable.Seq[IO.ServerSocketOption])(implicit owner: ActorRef): IO.ServerHandle = {
val server = IO.ServerHandle(owner, actor)
actor ! IO.Listen(server, address, options)
server
@ -847,7 +848,7 @@ final class IOManager private (system: ExtendedActorSystem) extends Extension {
* @param owner the ActorRef that will receive messages from the IOManagerActor
* @return a [[akka.actor.IO.ServerHandle]] to uniquely identify the created socket
*/
def listen(address: SocketAddress)(implicit owner: ActorRef): IO.ServerHandle = listen(address, Seq.empty)
def listen(address: SocketAddress)(implicit owner: ActorRef): IO.ServerHandle = listen(address, Nil)
/**
* Create a ServerSocketChannel listening on a host and port. Messages will
@ -860,7 +861,7 @@ final class IOManager private (system: ExtendedActorSystem) extends Extension {
* @param owner the ActorRef that will receive messages from the IOManagerActor
* @return a [[akka.actor.IO.ServerHandle]] to uniquely identify the created socket
*/
def listen(host: String, port: Int, options: Seq[IO.ServerSocketOption] = Seq.empty)(implicit owner: ActorRef): IO.ServerHandle =
def listen(host: String, port: Int, options: immutable.Seq[IO.ServerSocketOption] = Nil)(implicit owner: ActorRef): IO.ServerHandle =
listen(new InetSocketAddress(host, port), options)(owner)
/**
@ -873,7 +874,7 @@ final class IOManager private (system: ExtendedActorSystem) extends Extension {
* @param owner the ActorRef that will receive messages from the IOManagerActor
* @return a [[akka.actor.IO.SocketHandle]] to uniquely identify the created socket
*/
def connect(address: SocketAddress, options: Seq[IO.SocketOption] = Seq.empty)(implicit owner: ActorRef): IO.SocketHandle = {
def connect(address: SocketAddress, options: immutable.Seq[IO.SocketOption] = Nil)(implicit owner: ActorRef): IO.SocketHandle = {
val socket = IO.SocketHandle(owner, actor)
actor ! IO.Connect(socket, address, options)
socket
@ -991,7 +992,7 @@ final class IOManagerActor(val settings: Settings) extends Actor with ActorLoggi
private def forwardFailure(f: Unit): Unit = try f catch { case NonFatal(e) sender ! Status.Failure(e) }
private def setSocketOptions(socket: java.net.Socket, options: Seq[IO.SocketOption]) {
private def setSocketOptions(socket: java.net.Socket, options: immutable.Seq[IO.SocketOption]) {
options foreach {
case IO.KeepAlive(on) forwardFailure(socket.setKeepAlive(on))
case IO.OOBInline(on) forwardFailure(socket.setOOBInline(on))

View file

@ -5,17 +5,18 @@
package akka.actor
import java.io.ObjectStreamException
import java.util.{ LinkedList JLinkedList, ListIterator JListIterator }
import java.util.concurrent.TimeUnit
import java.util.concurrent.locks.ReentrantLock
import scala.annotation.tailrec
import scala.collection.mutable.Queue
import scala.concurrent.forkjoin.ThreadLocalRandom
import akka.actor.dungeon.ChildrenContainer
import akka.dispatch.{ Envelope, Supervise, SystemMessage, Terminate }
import akka.event.Logging.Warning
import akka.util.Unsafe
import akka.dispatch._
import util.Try
/**
* This actor ref starts out with some dummy cell (by default just enqueuing
@ -32,17 +33,34 @@ private[akka] class RepointableActorRef(
val path: ActorPath)
extends ActorRefWithCell with RepointableRef {
import AbstractActorRef.cellOffset
import AbstractActorRef.{ cellOffset, lookupOffset }
/*
* H E R E B E D R A G O N S !
*
* There are two main functions of a Cell: message queueing and child lookup.
* When switching out the UnstartedCell for its real replacement, the former
* must be switched after all messages have been drained from the temporary
* queue into the real mailbox, while the latter must be switched before
* processing the very first message (i.e. before Cell.start()). Hence there
* are two refs here, one for each function, and they are switched just so.
*/
@volatile private var _cellDoNotCallMeDirectly: Cell = _
@volatile private var _lookupDoNotCallMeDirectly: Cell = _
def underlying: Cell = Unsafe.instance.getObjectVolatile(this, cellOffset).asInstanceOf[Cell]
def lookup = Unsafe.instance.getObjectVolatile(this, lookupOffset).asInstanceOf[Cell]
@tailrec final def swapCell(next: Cell): Cell = {
val old = underlying
if (Unsafe.instance.compareAndSwapObject(this, cellOffset, old, next)) old else swapCell(next)
}
@tailrec final def swapLookup(next: Cell): Cell = {
val old = lookup
if (Unsafe.instance.compareAndSwapObject(this, lookupOffset, old, next)) old else swapLookup(next)
}
/**
* Initialize: make a dummy cell which holds just a mailbox, then tell our
* supervisor that we exist so that he can create the real Cell in
@ -52,11 +70,16 @@ private[akka] class RepointableActorRef(
*
* This is protected so that others can have different initialization.
*/
def initialize(): this.type = {
def initialize(async: Boolean): this.type =
underlying match {
case null
val uid = ThreadLocalRandom.current.nextInt()
swapCell(new UnstartedCell(system, this, props, supervisor, uid))
supervisor.sendSystemMessage(Supervise(this, uid))
swapLookup(underlying)
supervisor.sendSystemMessage(Supervise(this, async, uid))
if (!async) point()
this
case other throw new IllegalStateException("initialize called more than once!")
}
/**
@ -65,21 +88,33 @@ private[akka] class RepointableActorRef(
* modification of the `underlying` field, though it is safe to send messages
* at any time.
*/
def activate(): this.type = {
def point(): this.type =
underlying match {
case u: UnstartedCell u.replaceWith(newCell(u))
case _ // this happens routinely for things which were created async=false
}
case u: UnstartedCell
/*
* The problem here was that if the real actor (which will start running
* at cell.start()) creates children in its constructor, then this may
* happen before the swapCell in u.replaceWith, meaning that those
* children cannot be looked up immediately, e.g. if they shall become
* routees.
*/
val cell = newCell(u)
swapLookup(cell)
cell.start()
u.replaceWith(cell)
this
case null throw new IllegalStateException("underlying cell is null")
case _ this // this happens routinely for things which were created async=false
}
/**
* This is called by activate() to obtain the cell which is to replace the
* unstarted cell. The cell must be fully functional.
*/
def newCell(old: Cell): Cell =
new ActorCell(system, this, props, supervisor)
.start(sendSupervise = false, old.asInstanceOf[UnstartedCell].uid)
def newCell(old: UnstartedCell): Cell =
new ActorCell(system, this, props, supervisor).init(old.uid, sendSupervise = false)
def start(): Unit = ()
def suspend(): Unit = underlying.suspend()
@ -89,7 +124,11 @@ private[akka] class RepointableActorRef(
def restart(cause: Throwable): Unit = underlying.restart(cause)
def isStarted: Boolean = !underlying.isInstanceOf[UnstartedCell]
def isStarted: Boolean = underlying match {
case _: UnstartedCell false
case null throw new IllegalStateException("isStarted called before initialized")
case _ true
}
def isTerminated: Boolean = underlying.isTerminated
@ -105,14 +144,14 @@ private[akka] class RepointableActorRef(
case ".." getParent.getChild(name)
case "" getChild(name)
case other
underlying.getChildByName(other) match {
lookup.getChildByName(other) match {
case Some(crs: ChildRestartStats) crs.child.asInstanceOf[InternalActorRef].getChild(name)
case _ Nobody
}
}
} else this
def !(message: Any)(implicit sender: ActorRef = null) = underlying.tell(message, sender)
def !(message: Any)(implicit sender: ActorRef = Actor.noSender) = underlying.tell(message, sender)
def sendSystemMessage(message: SystemMessage) = underlying.sendSystemMessage(message)
@ -120,116 +159,116 @@ private[akka] class RepointableActorRef(
protected def writeReplace(): AnyRef = SerializedActorRef(path)
}
private[akka] class UnstartedCell(val systemImpl: ActorSystemImpl, val self: RepointableActorRef, val props: Props, val supervisor: InternalActorRef, val uid: Int)
extends Cell {
private[akka] class UnstartedCell(val systemImpl: ActorSystemImpl,
val self: RepointableActorRef,
val props: Props,
val supervisor: InternalActorRef,
val uid: Int) extends Cell {
/*
* This lock protects all accesses to this cells queues. It also ensures
* safe switching to the started ActorCell.
*/
val lock = new ReentrantLock
private[this] final val lock = new ReentrantLock
// use Envelope to keep on-send checks in the same place
val queue: Queue[Envelope] = Queue()
val systemQueue: Queue[SystemMessage] = Queue()
var suspendCount: Int = 0
// use Envelope to keep on-send checks in the same place ACCESS MUST BE PROTECTED BY THE LOCK
private[this] final val queue = new JLinkedList[Any]()
private def timeout = system.settings.UnstartedPushTimeout.duration.toMillis
import systemImpl.settings.UnstartedPushTimeout.{ duration timeout }
def replaceWith(cell: Cell): Unit = {
lock.lock()
def replaceWith(cell: Cell): Unit = locked {
try {
/*
* The CallingThreadDispatcher nicely dives under the ReentrantLock and
* breaks things by enqueueing into stale queues from within the message
* processing which happens in-line for sendSystemMessage() and tell().
* Since this is the only possible way to f*ck things up within this
* lock, double-tap (well, N-tap, really); concurrent modification is
* still not possible because were the only thread accessing the queues.
*/
while (systemQueue.nonEmpty || queue.nonEmpty) {
while (systemQueue.nonEmpty) {
val msg = systemQueue.dequeue()
cell.sendSystemMessage(msg)
}
if (queue.nonEmpty) {
val envelope = queue.dequeue()
cell.tell(envelope.message, envelope.sender)
while (!queue.isEmpty) {
queue.poll() match {
case s: SystemMessage cell.sendSystemMessage(s)
case e: Envelope cell.tell(e.message, e.sender)
}
}
} finally try
} finally {
self.swapCell(cell)
finally try
for (_ 1 to suspendCount) cell.suspend()
finally
lock.unlock()
}
}
def system: ActorSystem = systemImpl
def suspend(): Unit = {
lock.lock()
try suspendCount += 1
finally lock.unlock()
}
def resume(causedByFailure: Throwable): Unit = {
lock.lock()
try suspendCount -= 1
finally lock.unlock()
}
def restart(cause: Throwable): Unit = {
lock.lock()
try suspendCount -= 1
finally lock.unlock()
}
def start(): this.type = this
def suspend(): Unit = sendSystemMessage(Suspend())
def resume(causedByFailure: Throwable): Unit = sendSystemMessage(Resume(causedByFailure))
def restart(cause: Throwable): Unit = sendSystemMessage(Recreate(cause))
def stop(): Unit = sendSystemMessage(Terminate())
def isTerminated: Boolean = false
def isTerminated: Boolean = locked {
val cell = self.underlying
if (cellIsReady(cell)) cell.isTerminated else false
}
def parent: InternalActorRef = supervisor
def childrenRefs: ChildrenContainer = ChildrenContainer.EmptyChildrenContainer
def getChildByName(name: String): Option[ChildRestartStats] = None
def tell(message: Any, sender: ActorRef): Unit = {
if (lock.tryLock(timeout, TimeUnit.MILLISECONDS)) {
val useSender = if (sender eq Actor.noSender) system.deadLetters else sender
if (lock.tryLock(timeout.length, timeout.unit)) {
try {
if (self.underlying eq this) queue enqueue Envelope(message, sender, system)
else self.underlying.tell(message, sender)
} finally {
lock.unlock()
val cell = self.underlying
if (cellIsReady(cell)) {
cell.tell(message, useSender)
} else if (!queue.offer(Envelope(message, useSender, system))) {
system.eventStream.publish(Warning(self.path.toString, getClass, "dropping message of type " + message.getClass + " due to enqueue failure"))
system.deadLetters ! DeadLetter(message, useSender, self)
}
} finally lock.unlock()
} else {
system.deadLetters ! DeadLetter(message, sender, self)
}
}
def sendSystemMessage(msg: SystemMessage): Unit = {
if (lock.tryLock(timeout, TimeUnit.MILLISECONDS)) {
try {
if (self.underlying eq this) systemQueue enqueue msg
else self.underlying.sendSystemMessage(msg)
} finally {
lock.unlock()
}
} else {
// FIXME: once we have guaranteed delivery of system messages, hook this in!
system.eventStream.publish(Warning(self.path.toString, getClass, "dropping system message " + msg + " due to lock timeout"))
system.deadLetters ! DeadLetter(msg, self, self)
}
}
def isLocal = true
def hasMessages: Boolean = {
lock.lock()
try {
if (self.underlying eq this) !queue.isEmpty
else self.underlying.hasMessages
} finally {
lock.unlock()
}
}
def numberOfMessages: Int = {
lock.lock()
try {
if (self.underlying eq this) queue.size
else self.underlying.numberOfMessages
} finally {
lock.unlock()
system.eventStream.publish(Warning(self.path.toString, getClass, "dropping message of type" + message.getClass + " due to lock timeout"))
system.deadLetters ! DeadLetter(message, useSender, self)
}
}
// FIXME: once we have guaranteed delivery of system messages, hook this in!
def sendSystemMessage(msg: SystemMessage): Unit =
if (lock.tryLock(timeout.length, timeout.unit)) {
try {
val cell = self.underlying
if (cellIsReady(cell)) {
cell.sendSystemMessage(msg)
} else {
// systemMessages that are sent during replace need to jump to just after the last system message in the queue, so it's processed before other messages
val wasEnqueued = if ((self.lookup ne this) && (self.underlying eq this) && !queue.isEmpty()) {
@tailrec def tryEnqueue(i: JListIterator[Any] = queue.listIterator(), insertIntoIndex: Int = -1): Boolean =
if (i.hasNext())
tryEnqueue(i,
if (i.next().isInstanceOf[SystemMessage]) i.nextIndex() // update last sysmsg seen so far
else insertIntoIndex) // or just keep the last seen one
else if (insertIntoIndex == -1) queue.offer(msg)
else Try(queue.add(insertIntoIndex, msg)).isSuccess
tryEnqueue()
} else queue.offer(msg)
if (!wasEnqueued) {
system.eventStream.publish(Warning(self.path.toString, getClass, "dropping system message " + msg + " due to enqueue failure"))
system.deadLetters ! DeadLetter(msg, self, self)
}
}
} finally lock.unlock()
} else {
system.eventStream.publish(Warning(self.path.toString, getClass, "dropping system message " + msg + " due to lock timeout"))
system.deadLetters ! DeadLetter(msg, self, self)
}
def isLocal = true
private[this] final def cellIsReady(cell: Cell): Boolean = (cell ne this) && (cell ne null)
def hasMessages: Boolean = locked {
val cell = self.underlying
if (cellIsReady(cell)) cell.hasMessages else !queue.isEmpty
}
def numberOfMessages: Int = locked {
val cell = self.underlying
if (cellIsReady(cell)) cell.numberOfMessages else queue.size
}
private[this] final def locked[T](body: T): T = {
lock.lock()
try body finally lock.unlock()
}
}

View file

@ -4,17 +4,18 @@
package akka.actor
import scala.concurrent.util.Duration
import scala.concurrent.duration.Duration
import akka.util.internal.{ TimerTask, HashedWheelTimer, Timeout HWTimeout, Timer }
import akka.event.LoggingAdapter
import akka.dispatch.MessageDispatcher
import java.io.Closeable
import java.util.concurrent.atomic.AtomicReference
import java.util.concurrent.atomic.{ AtomicReference, AtomicLong }
import scala.annotation.tailrec
import akka.util.internal._
import concurrent.ExecutionContext
import scala.concurrent.util.FiniteDuration
import scala.concurrent.duration.FiniteDuration
// The Scheduler trait is included in the documentation. KEEP THE LINES SHORT!!!
//#scheduler
/**
* An Akka scheduler service. This one needs one special behavior: if
@ -50,7 +51,8 @@ trait Scheduler {
*/
def schedule(
initialDelay: FiniteDuration,
interval: FiniteDuration)(f: Unit)(implicit executor: ExecutionContext): Cancellable
interval: FiniteDuration)(f: Unit)(
implicit executor: ExecutionContext): Cancellable
/**
* Schedules a function to be run repeatedly with an initial delay and
@ -93,7 +95,8 @@ trait Scheduler {
* Scala API
*/
def scheduleOnce(
delay: FiniteDuration)(f: Unit)(implicit executor: ExecutionContext): Cancellable
delay: FiniteDuration)(f: Unit)(
implicit executor: ExecutionContext): Cancellable
}
//#scheduler
@ -137,14 +140,17 @@ class DefaultScheduler(hashedWheelTimer: HashedWheelTimer, log: LoggingAdapter)
val continuousCancellable = new ContinuousCancellable
continuousCancellable.init(
hashedWheelTimer.newTimeout(
new TimerTask with ContinuousScheduling {
new AtomicLong(System.nanoTime + initialDelay.toNanos) with TimerTask with ContinuousScheduling {
def run(timeout: HWTimeout) {
executor execute new Runnable {
override def run = {
receiver ! message
// Check if the receiver is still alive and kicking before reschedule the task
if (receiver.isTerminated) log.debug("Could not reschedule message to be sent because receiving actor {} has been terminated.", receiver)
else scheduleNext(timeout, delay, continuousCancellable)
else {
val driftNanos = System.nanoTime - getAndAdd(delay.toNanos)
scheduleNext(timeout, Duration.fromNanos(Math.max(delay.toNanos - driftNanos, 1)), continuousCancellable)
}
}
}
}
@ -162,11 +168,12 @@ class DefaultScheduler(hashedWheelTimer: HashedWheelTimer, log: LoggingAdapter)
val continuousCancellable = new ContinuousCancellable
continuousCancellable.init(
hashedWheelTimer.newTimeout(
new TimerTask with ContinuousScheduling {
new AtomicLong(System.nanoTime + initialDelay.toNanos) with TimerTask with ContinuousScheduling {
override def run(timeout: HWTimeout): Unit = executor.execute(new Runnable {
override def run = {
runnable.run()
scheduleNext(timeout, delay, continuousCancellable)
val driftNanos = System.nanoTime - getAndAdd(delay.toNanos)
scheduleNext(timeout, Duration.fromNanos(Math.max(delay.toNanos - driftNanos, 1)), continuousCancellable)
}
})
},
@ -199,8 +206,8 @@ class DefaultScheduler(hashedWheelTimer: HashedWheelTimer, log: LoggingAdapter)
}
override def close(): Unit = {
import scala.collection.JavaConverters._
hashedWheelTimer.stop().asScala foreach execDirectly
val i = hashedWheelTimer.stop().iterator()
while (i.hasNext) execDirectly(i.next())
}
}

View file

@ -16,13 +16,13 @@ import akka.AkkaException
* def receive = {
* case "open"
* unstashAll()
* context.become {
* context.become({
* case "write" // do writing...
* case "close"
* unstashAll()
* context.unbecome()
* case msg stash()
* }
* }, discardOld = false)
* case "done" // done
* case msg stash()
* }

View file

@ -4,22 +4,25 @@
package akka.actor
import language.existentials
import akka.japi.{ Creator, Option JOption }
import java.lang.reflect.{ InvocationTargetException, Method, InvocationHandler, Proxy }
import akka.util.Timeout
import scala.util.control.NonFatal
import scala.concurrent.util.Duration
import scala.util.{ Try, Success, Failure }
import scala.collection.immutable
import scala.concurrent.duration.FiniteDuration
import scala.concurrent.duration.Duration
import scala.reflect.ClassTag
import scala.concurrent.{ Await, Future }
import akka.japi.{ Creator, Option JOption }
import akka.japi.Util.{ immutableSeq, immutableSingletonSeq }
import akka.util.Timeout
import akka.util.Reflect.instantiator
import akka.serialization.{ JavaSerializer, SerializationExtension }
import akka.dispatch._
import java.util.concurrent.atomic.{ AtomicReference AtomVar }
import java.util.concurrent.TimeoutException
import java.util.concurrent.TimeUnit.MILLISECONDS
import scala.reflect.ClassTag
import akka.serialization.{ JavaSerializer, SerializationExtension }
import java.io.ObjectStreamException
import scala.util.{ Try, Success, Failure }
import scala.concurrent.util.FiniteDuration
import java.lang.reflect.{ InvocationTargetException, Method, InvocationHandler, Proxy }
/**
* A TypedActorFactory is something that can created TypedActor instances.
@ -439,8 +442,8 @@ object TypedProps {
* @return a sequence of interfaces that the specified class implements,
* or a sequence containing only itself, if itself is an interface.
*/
def extractInterfaces(clazz: Class[_]): Seq[Class[_]] =
if (clazz.isInterface) Seq[Class[_]](clazz) else clazz.getInterfaces.toList
def extractInterfaces(clazz: Class[_]): immutable.Seq[Class[_]] =
if (clazz.isInterface) immutableSingletonSeq(clazz) else immutableSeq(clazz.getInterfaces)
/**
* Uses the supplied class as the factory for the TypedActor implementation,
@ -489,7 +492,7 @@ object TypedProps {
*/
@SerialVersionUID(1L)
case class TypedProps[T <: AnyRef] protected[TypedProps] (
interfaces: Seq[Class[_]],
interfaces: immutable.Seq[Class[_]],
creator: () T,
dispatcher: String = TypedProps.defaultDispatcherId,
deploy: Deploy = Props.defaultDeploy,
@ -607,8 +610,7 @@ class TypedActorExtension(system: ExtendedActorSystem) extends TypedActorFactory
protected def actorFactory: ActorRefFactory = system
protected def typedActor = this
val serialization = SerializationExtension(system)
val settings = system.settings
import system.settings
/**
* Default timeout for typed actor methods with non-void return type
@ -635,22 +637,17 @@ class TypedActorExtension(system: ExtendedActorSystem) extends TypedActorFactory
private[akka] def createActorRefProxy[R <: AnyRef, T <: R](props: TypedProps[T], proxyVar: AtomVar[R], actorRef: ActorRef): R = {
//Warning, do not change order of the following statements, it's some elaborate chicken-n-egg handling
val actorVar = new AtomVar[ActorRef](null)
val classLoader: ClassLoader = if (props.loader.nonEmpty) props.loader.get else props.interfaces.headOption.map(_.getClassLoader).orNull //If we have no loader, we arbitrarily take the loader of the first interface
val proxy = Proxy.newProxyInstance(
classLoader,
(props.loader orElse props.interfaces.collectFirst { case any any.getClassLoader }).orNull, //If we have no loader, we arbitrarily take the loader of the first interface
props.interfaces.toArray,
new TypedActorInvocationHandler(
this,
actorVar,
if (props.timeout.isDefined) props.timeout.get else DefaultReturnTimeout)).asInstanceOf[R]
new TypedActorInvocationHandler(this, actorVar, props.timeout getOrElse DefaultReturnTimeout)).asInstanceOf[R]
proxyVar match {
case null
actorVar.set(actorRef)
if (proxyVar eq null) {
actorVar set actorRef
proxy
case _
proxyVar.set(proxy) // Chicken and egg situation we needed to solve, set the proxy so that we can set the self-reference inside each receive
actorVar.set(actorRef) //Make sure the InvocationHandler gets ahold of the actor reference, this is not a problem since the proxy hasn't escaped this method yet
} else {
proxyVar set proxy // Chicken and egg situation we needed to solve, set the proxy so that we can set the self-reference inside each receive
actorVar set actorRef //Make sure the InvocationHandler gets ahold of the actor reference, this is not a problem since the proxy hasn't escaped this method yet
proxyVar.get
}
}

View file

@ -36,7 +36,7 @@ import akka.japi.{ Creator }
* }
* }
*
* private static SupervisorStrategy strategy = new OneForOneStrategy(10, Duration.parse("1 minute"),
* private static SupervisorStrategy strategy = new OneForOneStrategy(10, Duration.create("1 minute"),
* new Function<Throwable, Directive>() {
* @Override
* public Directive apply(Throwable t) {

View file

@ -6,10 +6,8 @@ package akka.actor.dsl
import scala.concurrent.Await
import akka.actor.ActorLogging
import scala.concurrent.util.Deadline
import scala.collection.immutable.TreeSet
import scala.concurrent.util.{ Duration, FiniteDuration }
import scala.concurrent.util.duration._
import scala.concurrent.duration._
import akka.actor.Cancellable
import akka.actor.{ Actor, Stash, SupervisorStrategy }
import scala.collection.mutable.Queue
@ -31,7 +29,9 @@ trait Creators { this: ActorDSL.type ⇒
* for quickly trying things out in the REPL. It makes the following keywords
* available:
*
* - `become` mapped to `context.become(_, discardOld = false)`
* - `become` mapped to `context.become(_, discardOld = true)`
*
* - `becomeStacked` mapped to `context.become(_, discardOld = false)`
*
* - `unbecome` mapped to `context.unbecome`
*
@ -89,7 +89,14 @@ trait Creators { this: ActorDSL.type ⇒
* stack is cleared upon restart. Use `unbecome()` to pop an element off
* this stack.
*/
def become(r: Receive) = context.become(r, discardOld = false)
def becomeStacked(r: Receive) = context.become(r, discardOld = false)
/**
* Replace the behavior at the top of the behavior stack for this actor. The
* stack is cleared upon restart. Use `unbecome()` to pop an element off
* this stack or `becomeStacked()` to push a new element on top of it.
*/
def become(r: Receive) = context.become(r, discardOld = true)
/**
* Pop the active behavior from the behavior stack of this actor. This stack

View file

@ -6,10 +6,8 @@ package akka.actor.dsl
import scala.concurrent.Await
import akka.actor.ActorLogging
import scala.concurrent.util.Deadline
import scala.collection.immutable.TreeSet
import scala.concurrent.util.{ Duration, FiniteDuration }
import scala.concurrent.util.duration._
import scala.concurrent.duration._
import akka.actor.Cancellable
import akka.actor.Actor
import scala.collection.mutable.Queue
@ -129,10 +127,10 @@ trait Inbox { this: ActorDSL.type ⇒
val next = clientsByTimeout.head.deadline
import context.dispatcher
if (currentDeadline.isEmpty) {
currentDeadline = Some((next, context.system.scheduler.scheduleOnce(next.timeLeft.asInstanceOf[FiniteDuration], self, Kick)))
currentDeadline = Some((next, context.system.scheduler.scheduleOnce(next.timeLeft, self, Kick)))
} else if (currentDeadline.get._1 != next) {
currentDeadline.get._2.cancel()
currentDeadline = Some((next, context.system.scheduler.scheduleOnce(next.timeLeft.asInstanceOf[FiniteDuration], self, Kick)))
currentDeadline = Some((next, context.system.scheduler.scheduleOnce(next.timeLeft, self, Kick)))
}
}
}
@ -169,7 +167,7 @@ trait Inbox { this: ActorDSL.type ⇒
* this method within an actor!</b>
*/
def receive(timeout: FiniteDuration = defaultTimeout): Any = {
implicit val t = Timeout((timeout + extraTime).asInstanceOf[FiniteDuration])
implicit val t = Timeout(timeout + extraTime)
Await.result(receiver ? Get(Deadline.now + timeout), Duration.Inf)
}
@ -186,7 +184,7 @@ trait Inbox { this: ActorDSL.type ⇒
* this method within an actor!</b>
*/
def select[T](timeout: FiniteDuration = defaultTimeout)(predicate: PartialFunction[Any, T]): T = {
implicit val t = Timeout((timeout + extraTime).asInstanceOf[FiniteDuration])
implicit val t = Timeout(timeout + extraTime)
predicate(Await.result(receiver ? Select(Deadline.now + timeout, predicate), Duration.Inf))
}

View file

@ -5,14 +5,12 @@
package akka.actor.dungeon
import scala.annotation.tailrec
import scala.collection.JavaConverters.asJavaIterableConverter
import scala.util.control.NonFatal
import scala.collection.immutable
import akka.actor._
import akka.actor.ActorCell
import akka.actor.ActorPath.ElementRegex
import akka.serialization.SerializationExtension
import akka.util.{ Unsafe, Helpers }
import akka.actor.ChildNameReserved
private[akka] trait Children { this: ActorCell
@ -24,8 +22,9 @@ private[akka] trait Children { this: ActorCell ⇒
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
final def children: immutable.Iterable[ActorRef] = childrenRefs.children
final def getChildren(): java.lang.Iterable[ActorRef] =
scala.collection.JavaConverters.asJavaIterableConverter(children).asJava
final def child(name: String): Option[ActorRef] = Option(getChild(name))
final def getChild(name: String): ActorRef = childrenRefs.getByName(name) match {
@ -53,19 +52,24 @@ private[akka] trait Children { this: ActorCell ⇒
}
final def stop(actor: ActorRef): Unit = {
val started = actor match {
if (childrenRefs.getByRef(actor).isDefined) {
@tailrec def shallDie(ref: ActorRef): Boolean = {
val c = childrenRefs
swapChildrenRefs(c, c.shallDie(ref)) || shallDie(ref)
}
if (actor match {
case r: RepointableRef r.isStarted
case _ true
}) shallDie(actor)
}
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 =
@inline private final def swapChildrenRefs(oldChildren: ChildrenContainer, newChildren: ChildrenContainer): Boolean =
Unsafe.instance.compareAndSwapObject(this, AbstractActorCell.childrenOffset, oldChildren, newChildren)
@tailrec final def reserveChild(name: String): Boolean = {
@ -90,18 +94,6 @@ private[akka] trait Children { this: ActorCell ⇒
}
}
@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
@ -141,13 +133,21 @@ private[akka] trait Children { this: ActorCell ⇒
protected def getChildByRef(ref: ActorRef): Option[ChildRestartStats] = childrenRefs.getByRef(ref)
protected def getAllChildStats: Iterable[ChildRestartStats] = childrenRefs.stats
protected def getAllChildStats: immutable.Iterable[ChildRestartStats] = childrenRefs.stats
protected def removeChildAndGetStateChange(child: ActorRef): Option[SuspendReason] = {
childrenRefs match {
@tailrec def removeChild(ref: ActorRef): ChildrenContainer = {
val c = childrenRefs
val n = c.remove(ref)
if (swapChildrenRefs(c, n)) n else removeChild(ref)
}
childrenRefs match { // The match must be performed BEFORE the removeChild
case TerminatingChildrenContainer(_, _, reason)
val newContainer = removeChild(child)
if (!newContainer.isInstanceOf[TerminatingChildrenContainer]) Some(reason) else None
removeChild(child) match {
case _: TerminatingChildrenContainer None
case _ Some(reason)
}
case _
removeChild(child)
None
@ -192,6 +192,7 @@ private[akka] trait Children { this: ActorCell ⇒
// mailbox==null during RoutedActorCell constructor, where suspends are queued otherwise
if (mailbox ne null) for (_ 1 to mailbox.suspendCount) actor.suspend()
initChild(actor)
actor.start()
actor
}
}

View file

@ -4,10 +4,11 @@
package akka.actor.dungeon
import scala.collection.immutable.TreeMap
import scala.collection.immutable
import akka.actor.{ InvalidActorNameException, ChildStats, ChildRestartStats, ChildNameReserved, ActorRef }
import akka.dispatch.SystemMessage
import akka.util.Collections.{ EmptyImmutableSeq, PartialImmutableValuesIterable }
/**
* INTERNAL API
@ -20,8 +21,8 @@ private[akka] trait ChildrenContainer {
def getByName(name: String): Option[ChildStats]
def getByRef(actor: ActorRef): Option[ChildRestartStats]
def children: Iterable[ActorRef]
def stats: Iterable[ChildRestartStats]
def children: immutable.Iterable[ActorRef]
def stats: immutable.Iterable[ChildRestartStats]
def shallDie(actor: ActorRef): ChildrenContainer
@ -49,6 +50,18 @@ private[akka] object ChildrenContainer {
case class Creation() extends SuspendReason with WaitingForChildren
case object Termination extends SuspendReason
class ChildRestartsIterable(stats: immutable.MapLike[_, ChildStats, _]) extends PartialImmutableValuesIterable[ChildStats, ChildRestartStats] {
override final def apply(c: ChildStats) = c.asInstanceOf[ChildRestartStats]
override final def isDefinedAt(c: ChildStats) = c.isInstanceOf[ChildRestartStats]
override final def valuesIterator = stats.valuesIterator
}
class ChildrenIterable(stats: immutable.MapLike[_, ChildStats, _]) extends PartialImmutableValuesIterable[ChildStats, ActorRef] {
override final def apply(c: ChildStats) = c.asInstanceOf[ChildRestartStats].child
override final def isDefinedAt(c: ChildStats) = c.isInstanceOf[ChildRestartStats]
override final def valuesIterator = stats.valuesIterator
}
trait WaitingForChildren {
private var todo: SystemMessage = null
def enqueue(message: SystemMessage) = { message.next = todo; todo = message }
@ -56,13 +69,13 @@ private[akka] object ChildrenContainer {
}
trait EmptyChildrenContainer extends ChildrenContainer {
val emptyStats = TreeMap.empty[String, ChildStats]
val emptyStats = immutable.TreeMap.empty[String, ChildStats]
override def add(name: String, stats: ChildRestartStats): ChildrenContainer = new NormalChildrenContainer(emptyStats.updated(name, stats))
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 children: immutable.Iterable[ActorRef] = EmptyImmutableSeq
override def stats: immutable.Iterable[ChildRestartStats] = EmptyImmutableSeq
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
@ -95,7 +108,7 @@ private[akka] object ChildrenContainer {
* calling context.stop(child) and processing the ChildTerminated() system
* message).
*/
class NormalChildrenContainer(val c: TreeMap[String, ChildStats]) extends ChildrenContainer {
class NormalChildrenContainer(val c: immutable.TreeMap[String, ChildStats]) extends ChildrenContainer {
override def add(name: String, stats: ChildRestartStats): ChildrenContainer = new NormalChildrenContainer(c.updated(name, stats))
@ -108,9 +121,11 @@ private[akka] object ChildrenContainer {
case _ None
}
override def children: Iterable[ActorRef] = c.values.view.collect { case ChildRestartStats(child, _, _) child }
override def children: immutable.Iterable[ActorRef] =
if (c.isEmpty) EmptyImmutableSeq else new ChildrenIterable(c)
override def stats: Iterable[ChildRestartStats] = c.values.view.collect { case c: ChildRestartStats c }
override def stats: immutable.Iterable[ChildRestartStats] =
if (c.isEmpty) EmptyImmutableSeq else new ChildRestartsIterable(c)
override def shallDie(actor: ActorRef): ChildrenContainer = TerminatingChildrenContainer(c, Set(actor), UserRequest)
@ -130,7 +145,7 @@ private[akka] object ChildrenContainer {
}
object NormalChildrenContainer {
def apply(c: TreeMap[String, ChildStats]): ChildrenContainer =
def apply(c: immutable.TreeMap[String, ChildStats]): ChildrenContainer =
if (c.isEmpty) EmptyChildrenContainer
else new NormalChildrenContainer(c)
}
@ -145,7 +160,7 @@ private[akka] object ChildrenContainer {
* 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)
case class TerminatingChildrenContainer(c: immutable.TreeMap[String, ChildStats], toDie: Set[ActorRef], reason: SuspendReason)
extends ChildrenContainer {
override def add(name: String, stats: ChildRestartStats): ChildrenContainer = copy(c.updated(name, stats))
@ -166,9 +181,11 @@ private[akka] object ChildrenContainer {
case _ None
}
override def children: Iterable[ActorRef] = c.values.view.collect { case ChildRestartStats(child, _, _) child }
override def children: immutable.Iterable[ActorRef] =
if (c.isEmpty) EmptyImmutableSeq else new ChildrenIterable(c)
override def stats: Iterable[ChildRestartStats] = c.values.view.collect { case c: ChildRestartStats c }
override def stats: immutable.Iterable[ChildRestartStats] =
if (c.isEmpty) EmptyImmutableSeq else new ChildRestartsIterable(c)
override def shallDie(actor: ActorRef): ChildrenContainer = copy(toDie = toDie + actor)

View file

@ -38,12 +38,11 @@ private[akka] trait Dispatch { this: ActorCell ⇒
final def isTerminated: Boolean = mailbox.isClosed
/**
* Start this cell, i.e. attach it to the dispatcher. The UID must reasonably
* be different from the previous UID of a possible actor with the same path,
* Initialize this cell, i.e. set up mailboxes and supervision. The UID must be
* reasonably different from the previous UID of a possible actor with the same path,
* which can be achieved by using ThreadLocalRandom.current.nextInt().
*/
final def start(sendSupervise: Boolean, uid: Int): this.type = {
final def init(uid: Int, sendSupervise: Boolean): this.type = {
/*
* Create the mailbox and enqueue the Create() message to ensure that
* this is processed before anything else.
@ -56,13 +55,18 @@ private[akka] trait Dispatch { this: ActorCell ⇒
if (sendSupervise) {
// ➡➡➡ NEVER SEND THE SAME SYSTEM MESSAGE OBJECT TO TWO ACTORS ⬅⬅⬅
parent.sendSystemMessage(akka.dispatch.Supervise(self, uid))
parent.sendSystemMessage(akka.dispatch.Supervise(self, async = false, uid))
parent ! NullMessage // read ScalaDoc of NullMessage to see why
}
this
}
/**
* Start this cell, i.e. attach it to the dispatcher.
*/
final def start(): this.type = {
// This call is expected to start off the actor by scheduling its mailbox.
dispatcher.attach(this)
this
}

View file

@ -10,13 +10,13 @@ import akka.dispatch._
import akka.event.Logging.{ Warning, Error, Debug }
import scala.util.control.NonFatal
import akka.event.Logging
import scala.Some
import scala.collection.immutable
import akka.dispatch.ChildTerminated
import akka.actor.PreRestartException
import akka.actor.Failed
import akka.actor.PostRestartException
import akka.event.Logging.Debug
import scala.concurrent.util.Duration
import scala.concurrent.duration.Duration
private[akka] trait FaultHandling { this: ActorCell
@ -160,7 +160,7 @@ private[akka] trait FaultHandling { this: ActorCell ⇒
}
}
final def handleInvokeFailure(childrenNotToSuspend: Iterable[ActorRef], t: Throwable, message: String): Unit = {
final def handleInvokeFailure(childrenNotToSuspend: immutable.Iterable[ActorRef], 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 {

View file

@ -8,8 +8,8 @@ import ReceiveTimeout.emptyReceiveTimeoutData
import akka.actor.ActorCell
import akka.actor.ActorCell.emptyCancellable
import akka.actor.Cancellable
import scala.concurrent.util.Duration
import scala.concurrent.util.FiniteDuration
import scala.concurrent.duration.Duration
import scala.concurrent.duration.FiniteDuration
private[akka] object ReceiveTimeout {
final val emptyReceiveTimeoutData: (Duration, Cancellable) = (Duration.Undefined, ActorCell.emptyCancellable)

View file

@ -13,10 +13,10 @@ import akka.serialization.SerializationExtension
import akka.util.{ Unsafe, Index }
import scala.annotation.tailrec
import scala.concurrent.forkjoin.{ ForkJoinTask, ForkJoinPool }
import scala.concurrent.util.Duration
import scala.concurrent.duration.Duration
import scala.concurrent.{ ExecutionContext, Await, Awaitable }
import scala.util.control.NonFatal
import scala.concurrent.util.FiniteDuration
import scala.concurrent.duration.FiniteDuration
final case class Envelope private (val message: Any, val sender: ActorRef)
@ -108,7 +108,7 @@ private[akka] case class Terminate() extends SystemMessage // sent to self from
/**
* INTERNAL API
*/
private[akka] case class Supervise(child: ActorRef, uid: Int) extends SystemMessage // sent to supervisor ActorRef from ActorCell.start
private[akka] case class Supervise(child: ActorRef, async: Boolean, uid: Int) extends SystemMessage // sent to supervisor ActorRef from ActorCell.start
/**
* INTERNAL API
*/
@ -288,7 +288,7 @@ abstract class MessageDispatcher(val prerequisites: DispatcherPrerequisites) ext
if (debug) actors.remove(this, actor.self)
addInhabitants(-1)
val mailBox = actor.swapMailbox(deadLetterMailbox)
mailBox.becomeClosed() // FIXME reschedule in tell if possible race with cleanUp is detected in order to properly clean up
mailBox.becomeClosed()
mailBox.cleanUp()
}
@ -420,7 +420,7 @@ abstract class MessageDispatcherConfigurator(val config: Config, val prerequisit
case "unbounded" UnboundedMailbox()
case "bounded" new BoundedMailbox(prerequisites.settings, config)
case fqcn
val args = Seq(classOf[ActorSystem.Settings] -> prerequisites.settings, classOf[Config] -> config)
val args = List(classOf[ActorSystem.Settings] -> prerequisites.settings, classOf[Config] -> config)
prerequisites.dynamicAccess.createInstanceFor[MailboxType](fqcn, args).recover({
case exception
throw new IllegalArgumentException(
@ -436,7 +436,7 @@ abstract class MessageDispatcherConfigurator(val config: Config, val prerequisit
case null | "" | "fork-join-executor" new ForkJoinExecutorConfigurator(config.getConfig("fork-join-executor"), prerequisites)
case "thread-pool-executor" new ThreadPoolExecutorConfigurator(config.getConfig("thread-pool-executor"), prerequisites)
case fqcn
val args = Seq(
val args = List(
classOf[Config] -> config,
classOf[DispatcherPrerequisites] -> prerequisites)
prerequisites.dynamicAccess.createInstanceFor[ExecutorServiceConfigurator](fqcn, args).recover({

View file

@ -6,12 +6,12 @@ package akka.dispatch
import akka.actor.{ ActorCell, ActorRef }
import scala.annotation.tailrec
import scala.concurrent.util.Duration
import scala.concurrent.duration.Duration
import akka.util.Helpers
import java.util.{ Comparator, Iterator }
import java.util.concurrent.{ Executor, LinkedBlockingQueue, ConcurrentLinkedQueue, ConcurrentSkipListSet }
import akka.actor.ActorSystemImpl
import scala.concurrent.util.FiniteDuration
import scala.concurrent.duration.FiniteDuration
/**
* An executor based event driven dispatcher which will try to redistribute work from busy actors to idle actors. It is assumed

View file

@ -10,9 +10,9 @@ 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.duration.Duration
import scala.concurrent.Awaitable
import scala.concurrent.util.FiniteDuration
import scala.concurrent.duration.FiniteDuration
/**
* The event-based ``Dispatcher`` binds a set of Actors to a thread pool backed up by a
@ -93,7 +93,7 @@ class Dispatcher(
*/
protected[akka] def shutdown: Unit = {
val newDelegate = executorServiceDelegate.copy() // Doesn't matter which one we copy
val es = synchronized { // FIXME getAndSet using ARFU or Unsafe
val es = synchronized {
val service = executorServiceDelegate
executorServiceDelegate = newDelegate // just a quick getAndSet
service

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 scala.concurrent.util.Duration
import scala.concurrent.duration.Duration
/**
* DispatcherPrerequisites represents useful contextual pieces when constructing a MessageDispatcher
@ -147,7 +147,7 @@ class Dispatchers(val settings: ActorSystem.Settings, val prerequisites: Dispatc
case "BalancingDispatcher" new BalancingDispatcherConfigurator(cfg, prerequisites)
case "PinnedDispatcher" new PinnedDispatcherConfigurator(cfg, prerequisites)
case fqn
val args = Seq(classOf[Config] -> cfg, classOf[DispatcherPrerequisites] -> prerequisites)
val args = List(classOf[Config] -> cfg, classOf[DispatcherPrerequisites] -> prerequisites)
prerequisites.dynamicAccess.createInstanceFor[MessageDispatcherConfigurator](fqn, args).recover({
case exception
throw new IllegalArgumentException(

View file

@ -68,7 +68,7 @@ object ExecutionContexts {
* Futures is the Java API for Futures and Promises
*/
object Futures {
import scala.collection.JavaConverters.iterableAsScalaIterableConverter
/**
* Java API, equivalent to Future.apply
*/
@ -95,7 +95,7 @@ object Futures {
*/
def find[T <: AnyRef](futures: JIterable[Future[T]], predicate: JFunc[T, java.lang.Boolean], executor: ExecutionContext): Future[JOption[T]] = {
implicit val ec = executor
Future.find[T]((scala.collection.JavaConversions.iterableAsScalaIterable(futures)))(predicate.apply(_))(executor).map(JOption.fromScalaOption(_))
Future.find[T](futures.asScala)(predicate.apply(_))(executor) map JOption.fromScalaOption
}
/**
@ -103,7 +103,7 @@ object Futures {
* Returns a Future to the result of the first future in the list that is completed
*/
def firstCompletedOf[T <: AnyRef](futures: JIterable[Future[T]], executor: ExecutionContext): Future[T] =
Future.firstCompletedOf(scala.collection.JavaConversions.iterableAsScalaIterable(futures))(executor)
Future.firstCompletedOf(futures.asScala)(executor)
/**
* Java API
@ -113,14 +113,14 @@ object Futures {
* or the result of the fold.
*/
def fold[T <: AnyRef, R <: AnyRef](zero: R, futures: JIterable[Future[T]], fun: akka.japi.Function2[R, T, R], executor: ExecutionContext): Future[R] =
Future.fold(scala.collection.JavaConversions.iterableAsScalaIterable(futures))(zero)(fun.apply)(executor)
Future.fold(futures.asScala)(zero)(fun.apply)(executor)
/**
* Java API.
* Reduces the results of the supplied futures and binary function.
*/
def reduce[T <: AnyRef, R >: T](futures: JIterable[Future[T]], fun: akka.japi.Function2[R, T, R], executor: ExecutionContext): Future[R] =
Future.reduce[T, R](scala.collection.JavaConversions.iterableAsScalaIterable(futures))(fun.apply)(executor)
Future.reduce[T, R](futures.asScala)(fun.apply)(executor)
/**
* Java API.
@ -129,9 +129,7 @@ object Futures {
*/
def sequence[A](in: JIterable[Future[A]], executor: ExecutionContext): Future[JIterable[A]] = {
implicit val d = executor
scala.collection.JavaConversions.iterableAsScalaIterable(in).foldLeft(Future(new JLinkedList[A]())) { (fr, fa)
for (r fr; a fa) yield { r add a; r }
}
in.asScala.foldLeft(Future(new JLinkedList[A]())) { (fr, fa) for (r fr; a fa) yield { r add a; r } }
}
/**
@ -142,7 +140,7 @@ object Futures {
*/
def traverse[A, B](in: JIterable[A], fn: JFunc[A, Future[B]], executor: ExecutionContext): Future[JIterable[B]] = {
implicit val d = executor
scala.collection.JavaConversions.iterableAsScalaIterable(in).foldLeft(Future(new JLinkedList[B]())) { (fr, a)
in.asScala.foldLeft(Future(new JLinkedList[B]())) { (fr, a)
val fb = fn(a)
for (r fr; b fb) yield { r add b; r }
}

View file

@ -9,11 +9,11 @@ import akka.AkkaException
import akka.actor.{ ActorCell, ActorRef, Cell, ActorSystem, InternalActorRef, DeadLetter }
import akka.util.{ Unsafe, BoundedBlockingQueue }
import akka.event.Logging.Error
import scala.concurrent.util.Duration
import scala.concurrent.duration.Duration
import scala.annotation.tailrec
import scala.util.control.NonFatal
import com.typesafe.config.Config
import scala.concurrent.util.FiniteDuration
import scala.concurrent.duration.FiniteDuration
/**
* INTERNAL API

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