Merge branch 'master' into wip-2605-java-pattern-ricklatrine
Conflicts: akka-docs/rst/java/howto.rst
This commit is contained in:
commit
c17b1eb263
464 changed files with 7889 additions and 3723 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -7,6 +7,7 @@ project/plugins/project
|
||||||
project/boot/*
|
project/boot/*
|
||||||
*/project/build/target
|
*/project/build/target
|
||||||
*/project/boot
|
*/project/boot
|
||||||
|
*/project/project.target.config-classes
|
||||||
lib_managed
|
lib_managed
|
||||||
etags
|
etags
|
||||||
tags
|
tags
|
||||||
|
|
@ -67,3 +68,4 @@ redis/
|
||||||
beanstalk/
|
beanstalk/
|
||||||
.scalastyle
|
.scalastyle
|
||||||
bin/
|
bin/
|
||||||
|
.worksheet
|
||||||
|
|
|
||||||
126
CONTRIBUTING.md
126
CONTRIBUTING.md
|
|
@ -1,63 +1,97 @@
|
||||||
#Contributing to Akka#
|
# Contributing to Akka
|
||||||
|
|
||||||
Greetings traveller!
|
## Infrastructure
|
||||||
|
|
||||||
##Infrastructure##
|
* [Akka Contributor License Agreement](http://www.typesafe.com/contribute/cla)
|
||||||
|
|
||||||
* [Akka Contributor License Agreement](www.typesafe.com/contribute/cla)
|
|
||||||
* [Akka Issue Tracker](http://doc.akka.io/docs/akka/current/project/issue-tracking.html)
|
* [Akka Issue Tracker](http://doc.akka.io/docs/akka/current/project/issue-tracking.html)
|
||||||
* [Scalariform](https://github.com/mdr/scalariform)
|
* [Scalariform](https://github.com/mdr/scalariform)
|
||||||
|
|
||||||
##Workflow##
|
# Typesafe Project & Developer Guidelines
|
||||||
|
|
||||||
0. Sign the Akka Contributor License Agreement,
|
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.
|
||||||
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
|
|
||||||
|
|
||||||
##Code Reviews##
|
These guidelines mainly applies to Typesafe’s “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,
|
## General Workflow
|
||||||
increase maintainability and spread knowledge about how things are solved.
|
|
||||||
|
|
||||||
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)
|
1. Make sure you have signed the [Typesafe CLA](http://www.typesafe.com/contribute/cla), if not, sign it online.
|
||||||
- Why: Small improvements add up over time, keeping the codebase in shape.
|
2. Before starting to work on a feature or a fix, you have to make sure that:
|
||||||
* Rule: [Don't Repeat Yourself](http://programmer.97things.oreilly.com/wiki/index.php/Don't_Repeat_Yourself)
|
1. There is a ticket for your work in the project's issue tracker. If not, create it first.
|
||||||
- Why: Repetitions are not maintainable, keeping things DRY makes it easier to fix bugs and refactor,
|
2. The ticket has been scheduled for the current milestone.
|
||||||
since you only need to apply the correction in one place, or perform the refactoring at one place.
|
3. The ticket is estimated by the team.
|
||||||
* Rule: Feature tests > Integration tests > Unit tests
|
4. The ticket have been discussed and prioritized by the team.
|
||||||
- Why: Without proving that a feature works, the code is only liability.
|
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.
|
||||||
Without proving that a feature works with other features, the code is of limited value.
|
4. When the feature or fix is completed you should open a [Pull Request](https://help.github.com/articles/using-pull-requests) on GitHub.
|
||||||
Without proving the individual parts of a feature works, the code is harder to debug.
|
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 Typesafe’s 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.
|
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
|
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
|
as a stand-alone feature first, to show that there is a need for the feature. The
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import akka.japi.*;
|
||||||
import scala.concurrent.Await;
|
import scala.concurrent.Await;
|
||||||
import scala.concurrent.Future;
|
import scala.concurrent.Future;
|
||||||
import scala.concurrent.Promise;
|
import scala.concurrent.Promise;
|
||||||
import scala.concurrent.util.Duration;
|
import scala.concurrent.duration.Duration;
|
||||||
import akka.testkit.TestKitExtension;
|
import akka.testkit.TestKitExtension;
|
||||||
import org.junit.AfterClass;
|
import org.junit.AfterClass;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
package akka.japi;
|
package akka.japi;
|
||||||
|
|
||||||
|
import akka.event.LoggingAdapter;
|
||||||
|
import akka.event.NoLogging;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
@ -46,4 +48,10 @@ public class JavaAPITestBase {
|
||||||
public void shouldBeSingleton() {
|
public void shouldBeSingleton() {
|
||||||
assertSame(Option.none(), Option.none());
|
assertSame(Option.none(), Option.none());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void mustBeAbleToGetNoLogging() {
|
||||||
|
LoggingAdapter a = NoLogging.getInstance();
|
||||||
|
assertNotNull(a);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,8 @@ public class CustomRouteTest {
|
||||||
// only to test compilability
|
// only to test compilability
|
||||||
public void testRoute() {
|
public void testRoute() {
|
||||||
final ActorRef ref = system.actorOf(new Props().withRouter(new RoundRobinRouter(1)));
|
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);
|
route.apply(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,14 @@
|
||||||
package akka.util;
|
package akka.util;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import scala.concurrent.util.Duration;
|
import scala.concurrent.duration.Duration;
|
||||||
|
|
||||||
public class JavaDuration {
|
public class JavaDuration {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCreation() {
|
public void testCreation() {
|
||||||
final Duration fivesec = Duration.create(5, "seconds");
|
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);
|
final Duration diff = fivesec.minus(threemillis);
|
||||||
assert diff.lt(fivesec);
|
assert diff.lt(fivesec);
|
||||||
assert Duration.Zero().lteq(Duration.Inf());
|
assert Duration.Zero().lteq(Duration.Inf());
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import language.postfixOps
|
||||||
import akka.testkit._
|
import akka.testkit._
|
||||||
import akka.testkit.DefaultTimeout
|
import akka.testkit.DefaultTimeout
|
||||||
import akka.testkit.TestEvent._
|
import akka.testkit.TestEvent._
|
||||||
import scala.concurrent.util.duration._
|
import scala.concurrent.duration._
|
||||||
import akka.routing._
|
import akka.routing._
|
||||||
import org.scalatest.BeforeAndAfterEach
|
import org.scalatest.BeforeAndAfterEach
|
||||||
import akka.ConfigurationException
|
import akka.ConfigurationException
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import akka.actor.ActorDSL._
|
||||||
//#import
|
//#import
|
||||||
import akka.event.Logging.Warning
|
import akka.event.Logging.Warning
|
||||||
import scala.concurrent.{ Await, Future }
|
import scala.concurrent.{ Await, Future }
|
||||||
import scala.concurrent.util.duration._
|
import scala.concurrent.duration._
|
||||||
import java.util.concurrent.TimeoutException
|
import java.util.concurrent.TimeoutException
|
||||||
|
|
||||||
class ActorDSLSpec extends AkkaSpec {
|
class ActorDSLSpec extends AkkaSpec {
|
||||||
|
|
@ -103,6 +103,32 @@ class ActorDSLSpec extends AkkaSpec {
|
||||||
i.receive() must be("hi")
|
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 {
|
"support setup/teardown" in {
|
||||||
//#simple-start-stop
|
//#simple-start-stop
|
||||||
val a = actor(new Act {
|
val a = actor(new Act {
|
||||||
|
|
@ -188,7 +214,7 @@ class ActorDSLSpec extends AkkaSpec {
|
||||||
become {
|
become {
|
||||||
case 1 ⇒ stash()
|
case 1 ⇒ stash()
|
||||||
case 2 ⇒
|
case 2 ⇒
|
||||||
testActor ! 2; unstashAll(); become {
|
testActor ! 2; unstashAll(); becomeStacked {
|
||||||
case 1 ⇒ testActor ! 1; unbecome()
|
case 1 ⇒ testActor ! 1; unbecome()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ package akka.actor
|
||||||
|
|
||||||
import akka.testkit._
|
import akka.testkit._
|
||||||
import org.scalatest.BeforeAndAfterEach
|
import org.scalatest.BeforeAndAfterEach
|
||||||
import scala.concurrent.util.duration._
|
import scala.concurrent.duration._
|
||||||
import scala.concurrent.Await
|
import scala.concurrent.Await
|
||||||
import akka.pattern.ask
|
import akka.pattern.ask
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import org.scalatest.matchers.MustMatchers
|
||||||
|
|
||||||
import akka.actor.Actor._
|
import akka.actor.Actor._
|
||||||
import akka.testkit._
|
import akka.testkit._
|
||||||
import scala.concurrent.util.duration._
|
import scala.concurrent.duration._
|
||||||
import java.util.concurrent.atomic._
|
import java.util.concurrent.atomic._
|
||||||
import scala.concurrent.Await
|
import scala.concurrent.Await
|
||||||
import akka.pattern.ask
|
import akka.pattern.ask
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ package akka.actor
|
||||||
import language.postfixOps
|
import language.postfixOps
|
||||||
|
|
||||||
import akka.testkit._
|
import akka.testkit._
|
||||||
import scala.concurrent.util.duration._
|
import scala.concurrent.duration._
|
||||||
import scala.concurrent.Await
|
import scala.concurrent.Await
|
||||||
import akka.pattern.ask
|
import akka.pattern.ask
|
||||||
import java.net.MalformedURLException
|
import java.net.MalformedURLException
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import org.scalatest.matchers.MustMatchers
|
||||||
|
|
||||||
import akka.testkit._
|
import akka.testkit._
|
||||||
import akka.util.Timeout
|
import akka.util.Timeout
|
||||||
import scala.concurrent.util.duration._
|
import scala.concurrent.duration._
|
||||||
import scala.concurrent.Await
|
import scala.concurrent.Await
|
||||||
import java.lang.IllegalStateException
|
import java.lang.IllegalStateException
|
||||||
import scala.concurrent.Promise
|
import scala.concurrent.Promise
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,16 @@ import akka.testkit._
|
||||||
import org.scalatest.junit.JUnitSuite
|
import org.scalatest.junit.JUnitSuite
|
||||||
import com.typesafe.config.ConfigFactory
|
import com.typesafe.config.ConfigFactory
|
||||||
import scala.concurrent.Await
|
import scala.concurrent.Await
|
||||||
import scala.concurrent.util.duration._
|
import scala.concurrent.duration._
|
||||||
import scala.collection.JavaConverters
|
import java.util.concurrent.{ RejectedExecutionException, ConcurrentLinkedQueue }
|
||||||
import java.util.concurrent.{ TimeUnit, RejectedExecutionException, CountDownLatch, ConcurrentLinkedQueue }
|
|
||||||
import akka.util.Timeout
|
import akka.util.Timeout
|
||||||
|
import akka.japi.Util.immutableSeq
|
||||||
import scala.concurrent.Future
|
import scala.concurrent.Future
|
||||||
import akka.pattern.ask
|
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
|
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])
|
@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 {
|
"An ActorSystem" must {
|
||||||
|
|
||||||
|
|
@ -102,8 +153,6 @@ class ActorSystemSpec extends AkkaSpec("""akka.extensions = ["akka.actor.TestExt
|
||||||
}
|
}
|
||||||
|
|
||||||
"run termination callbacks in order" in {
|
"run termination callbacks in order" in {
|
||||||
import scala.collection.JavaConverters._
|
|
||||||
|
|
||||||
val system2 = ActorSystem("TerminationCallbacks", AkkaSpec.testConf)
|
val system2 = ActorSystem("TerminationCallbacks", AkkaSpec.testConf)
|
||||||
val result = new ConcurrentLinkedQueue[Int]
|
val result = new ConcurrentLinkedQueue[Int]
|
||||||
val count = 10
|
val count = 10
|
||||||
|
|
@ -121,13 +170,11 @@ class ActorSystemSpec extends AkkaSpec("""akka.extensions = ["akka.actor.TestExt
|
||||||
Await.ready(latch, 5 seconds)
|
Await.ready(latch, 5 seconds)
|
||||||
|
|
||||||
val expected = (for (i ← 1 to count) yield i).reverse
|
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 {
|
"awaitTermination after termination callbacks" in {
|
||||||
import scala.collection.JavaConverters._
|
|
||||||
|
|
||||||
val system2 = ActorSystem("AwaitTermination", AkkaSpec.testConf)
|
val system2 = ActorSystem("AwaitTermination", AkkaSpec.testConf)
|
||||||
@volatile
|
@volatile
|
||||||
var callbackWasRun = false
|
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")
|
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 {
|
"reliable deny creation of actors while shutting down" in {
|
||||||
val system = ActorSystem()
|
val system = ActorSystem()
|
||||||
import system.dispatcher
|
import system.dispatcher
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
*/
|
*/
|
||||||
package akka.actor
|
package akka.actor
|
||||||
|
|
||||||
import scala.concurrent.util.duration._
|
import scala.concurrent.duration._
|
||||||
import akka.testkit._
|
import akka.testkit._
|
||||||
import akka.testkit.TestEvent._
|
import akka.testkit.TestEvent._
|
||||||
import scala.concurrent.Await
|
import scala.concurrent.Await
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import akka.testkit.TestEvent._
|
||||||
import akka.dispatch.BoundedDequeBasedMailbox
|
import akka.dispatch.BoundedDequeBasedMailbox
|
||||||
import akka.pattern.ask
|
import akka.pattern.ask
|
||||||
import scala.concurrent.Await
|
import scala.concurrent.Await
|
||||||
import scala.concurrent.util.duration._
|
import scala.concurrent.duration._
|
||||||
import akka.actor.ActorSystem.Settings
|
import akka.actor.ActorSystem.Settings
|
||||||
import com.typesafe.config.{ Config, ConfigFactory }
|
import com.typesafe.config.{ Config, ConfigFactory }
|
||||||
import org.scalatest.Assertions.intercept
|
import org.scalatest.Assertions.intercept
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import akka.testkit.DefaultTimeout
|
||||||
import akka.testkit.TestEvent._
|
import akka.testkit.TestEvent._
|
||||||
import scala.concurrent.Await
|
import scala.concurrent.Await
|
||||||
import akka.pattern.ask
|
import akka.pattern.ask
|
||||||
import scala.concurrent.util.duration._
|
import scala.concurrent.duration._
|
||||||
import com.typesafe.config.{ Config, ConfigFactory }
|
import com.typesafe.config.{ Config, ConfigFactory }
|
||||||
import org.scalatest.BeforeAndAfterEach
|
import org.scalatest.BeforeAndAfterEach
|
||||||
import org.scalatest.junit.JUnitSuite
|
import org.scalatest.junit.JUnitSuite
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import language.postfixOps
|
||||||
|
|
||||||
import akka.testkit.AkkaSpec
|
import akka.testkit.AkkaSpec
|
||||||
import akka.dispatch.UnboundedMailbox
|
import akka.dispatch.UnboundedMailbox
|
||||||
import scala.concurrent.util.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
object ConsistencySpec {
|
object ConsistencySpec {
|
||||||
val config = """
|
val config = """
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ package akka.actor
|
||||||
|
|
||||||
import language.postfixOps
|
import language.postfixOps
|
||||||
import akka.testkit._
|
import akka.testkit._
|
||||||
import scala.concurrent.util.duration._
|
import scala.concurrent.duration._
|
||||||
import java.util.concurrent.atomic._
|
import java.util.concurrent.atomic._
|
||||||
import scala.concurrent.Await
|
import scala.concurrent.Await
|
||||||
import akka.pattern.ask
|
import akka.pattern.ask
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import akka.testkit.AkkaSpec
|
||||||
import com.typesafe.config.ConfigFactory
|
import com.typesafe.config.ConfigFactory
|
||||||
import com.typesafe.config.ConfigParseOptions
|
import com.typesafe.config.ConfigParseOptions
|
||||||
import akka.routing._
|
import akka.routing._
|
||||||
import scala.concurrent.util.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
object DeployerSpec {
|
object DeployerSpec {
|
||||||
val deployerConf = ConfigFactory.parseString("""
|
val deployerConf = ConfigFactory.parseString("""
|
||||||
|
|
|
||||||
|
|
@ -8,13 +8,14 @@ import language.postfixOps
|
||||||
import org.scalatest.{ BeforeAndAfterAll, BeforeAndAfterEach }
|
import org.scalatest.{ BeforeAndAfterAll, BeforeAndAfterEach }
|
||||||
import akka.testkit._
|
import akka.testkit._
|
||||||
import TestEvent.Mute
|
import TestEvent.Mute
|
||||||
import scala.concurrent.util.duration._
|
import scala.concurrent.duration._
|
||||||
import akka.event._
|
import akka.event._
|
||||||
import com.typesafe.config.ConfigFactory
|
import com.typesafe.config.ConfigFactory
|
||||||
import scala.concurrent.Await
|
import scala.concurrent.Await
|
||||||
import akka.util.Timeout
|
import akka.util.Timeout
|
||||||
import scala.concurrent.util.Duration
|
import org.scalatest.matchers.Matcher
|
||||||
import scala.concurrent.util.FiniteDuration
|
import org.scalatest.matchers.HavePropertyMatcher
|
||||||
|
import org.scalatest.matchers.HavePropertyMatchResult
|
||||||
|
|
||||||
object FSMActorSpec {
|
object FSMActorSpec {
|
||||||
val timeout = Timeout(2 seconds)
|
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))
|
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 {
|
"log events and transitions if asked to do so" in {
|
||||||
import scala.collection.JavaConverters._
|
import scala.collection.JavaConverters._
|
||||||
val config = ConfigFactory.parseMap(Map("akka.loglevel" -> "DEBUG",
|
val config = ConfigFactory.parseMap(Map("akka.loglevel" -> "DEBUG",
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,7 @@ package akka.actor
|
||||||
import language.postfixOps
|
import language.postfixOps
|
||||||
|
|
||||||
import akka.testkit._
|
import akka.testkit._
|
||||||
import scala.concurrent.util.Duration
|
import scala.concurrent.duration._
|
||||||
import scala.concurrent.util.duration._
|
|
||||||
import akka.event.Logging
|
import akka.event.Logging
|
||||||
|
|
||||||
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,7 @@ package akka.actor
|
||||||
import language.postfixOps
|
import language.postfixOps
|
||||||
|
|
||||||
import akka.testkit._
|
import akka.testkit._
|
||||||
import scala.concurrent.util.duration._
|
import scala.concurrent.duration._
|
||||||
import scala.concurrent.util.Duration
|
|
||||||
|
|
||||||
object FSMTransitionSpec {
|
object FSMTransitionSpec {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,8 @@ package akka.actor
|
||||||
import language.postfixOps
|
import language.postfixOps
|
||||||
|
|
||||||
import akka.testkit._
|
import akka.testkit._
|
||||||
import scala.concurrent.util.duration._
|
import scala.concurrent.duration._
|
||||||
import akka.actor.Actor._
|
import akka.actor.Actor._
|
||||||
import scala.concurrent.util.Duration
|
|
||||||
import scala.concurrent.Await
|
import scala.concurrent.Await
|
||||||
import akka.pattern.{ ask, pipe }
|
import akka.pattern.{ ask, pipe }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,16 +7,14 @@ package akka.actor
|
||||||
import language.postfixOps
|
import language.postfixOps
|
||||||
import akka.util.ByteString
|
import akka.util.ByteString
|
||||||
import scala.concurrent.{ ExecutionContext, Await, Future, Promise }
|
import scala.concurrent.{ ExecutionContext, Await, Future, Promise }
|
||||||
import scala.concurrent.util.{ Duration, Deadline }
|
import scala.concurrent.duration._
|
||||||
import scala.concurrent.util.duration._
|
|
||||||
import scala.util.continuations._
|
import scala.util.continuations._
|
||||||
import akka.testkit._
|
import akka.testkit._
|
||||||
import akka.dispatch.MessageDispatcher
|
import akka.dispatch.MessageDispatcher
|
||||||
import akka.pattern.ask
|
import akka.pattern.ask
|
||||||
import java.net.{ Socket, InetSocketAddress, InetAddress, SocketAddress }
|
import java.net.{ Socket, InetSocketAddress, InetAddress, SocketAddress }
|
||||||
import scala.util.Failure
|
import scala.util.Failure
|
||||||
import annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
import scala.concurrent.util.FiniteDuration
|
|
||||||
|
|
||||||
object IOActorSpec {
|
object IOActorSpec {
|
||||||
|
|
||||||
|
|
@ -57,6 +55,8 @@ object IOActorSpec {
|
||||||
|
|
||||||
def receive = {
|
def receive = {
|
||||||
|
|
||||||
|
case _: IO.Connected ⇒ //don't care
|
||||||
|
|
||||||
case bytes: ByteString ⇒
|
case bytes: ByteString ⇒
|
||||||
val source = sender
|
val source = sender
|
||||||
socket write bytes
|
socket write bytes
|
||||||
|
|
@ -67,9 +67,9 @@ object IOActorSpec {
|
||||||
|
|
||||||
case IO.Closed(`socket`, cause) ⇒
|
case IO.Closed(`socket`, cause) ⇒
|
||||||
state(cause)
|
state(cause)
|
||||||
throw cause match {
|
cause match {
|
||||||
case IO.Error(e) ⇒ e
|
case IO.Error(e) ⇒ throw e
|
||||||
case _ ⇒ new RuntimeException("Socket closed")
|
case _ ⇒ throw new RuntimeException("Socket closed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -156,6 +156,8 @@ object IOActorSpec {
|
||||||
case IO.Read(socket, bytes) ⇒
|
case IO.Read(socket, bytes) ⇒
|
||||||
state(socket)(IO Chunk bytes)
|
state(socket)(IO Chunk bytes)
|
||||||
|
|
||||||
|
case _: IO.Connected ⇒ //don't care
|
||||||
|
|
||||||
case IO.Closed(socket, cause) ⇒
|
case IO.Closed(socket, cause) ⇒
|
||||||
state -= socket
|
state -= socket
|
||||||
|
|
||||||
|
|
@ -183,6 +185,8 @@ object IOActorSpec {
|
||||||
readResult map (source !)
|
readResult map (source !)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case _: IO.Connected ⇒ //don't care
|
||||||
|
|
||||||
case IO.Read(`socket`, bytes) ⇒
|
case IO.Read(`socket`, bytes) ⇒
|
||||||
state(IO Chunk bytes)
|
state(IO Chunk bytes)
|
||||||
|
|
||||||
|
|
@ -278,7 +282,7 @@ class IOActorSpec extends AkkaSpec with DefaultTimeout {
|
||||||
}
|
}
|
||||||
|
|
||||||
"an IO Actor" must {
|
"an IO Actor" must {
|
||||||
implicit val ec = system.dispatcher
|
import system.dispatcher
|
||||||
"run echo server" in {
|
"run echo server" in {
|
||||||
filterException[java.net.ConnectException] {
|
filterException[java.net.ConnectException] {
|
||||||
val addressPromise = Promise[SocketAddress]()
|
val addressPromise = Promise[SocketAddress]()
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ package akka.actor
|
||||||
import language.postfixOps
|
import language.postfixOps
|
||||||
import akka.testkit._
|
import akka.testkit._
|
||||||
import scala.concurrent.Await
|
import scala.concurrent.Await
|
||||||
import scala.concurrent.util.duration._
|
import scala.concurrent.duration._
|
||||||
import akka.util.Timeout
|
import akka.util.Timeout
|
||||||
import scala.concurrent.Future
|
import scala.concurrent.Future
|
||||||
import scala.util.Success
|
import scala.util.Success
|
||||||
|
|
@ -39,6 +39,22 @@ class LocalActorRefProviderSpec extends AkkaSpec(LocalActorRefProviderSpec.confi
|
||||||
a must be === b
|
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 {
|
"An ActorRefFactory" must {
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,10 @@ package akka.actor
|
||||||
|
|
||||||
import language.postfixOps
|
import language.postfixOps
|
||||||
import akka.testkit._
|
import akka.testkit._
|
||||||
import scala.concurrent.util.duration._
|
import scala.concurrent.duration._
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
import scala.concurrent.Await
|
import scala.concurrent.Await
|
||||||
import java.util.concurrent.TimeoutException
|
import java.util.concurrent.TimeoutException
|
||||||
import scala.concurrent.util.Duration
|
|
||||||
|
|
||||||
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
||||||
class ReceiveTimeoutSpec extends AkkaSpec {
|
class ReceiveTimeoutSpec extends AkkaSpec {
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -15,8 +15,7 @@ import java.util.concurrent.{ TimeUnit, CountDownLatch }
|
||||||
import akka.testkit.AkkaSpec
|
import akka.testkit.AkkaSpec
|
||||||
import akka.testkit.DefaultTimeout
|
import akka.testkit.DefaultTimeout
|
||||||
import akka.testkit.TestLatch
|
import akka.testkit.TestLatch
|
||||||
import scala.concurrent.util.duration._
|
import scala.concurrent.duration._
|
||||||
import scala.concurrent.util.Duration
|
|
||||||
import akka.pattern.ask
|
import akka.pattern.ask
|
||||||
|
|
||||||
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package akka.actor
|
||||||
import language.postfixOps
|
import language.postfixOps
|
||||||
|
|
||||||
import org.scalatest.BeforeAndAfterEach
|
import org.scalatest.BeforeAndAfterEach
|
||||||
import scala.concurrent.util.duration._
|
import scala.concurrent.duration._
|
||||||
import java.util.concurrent.{ CountDownLatch, ConcurrentLinkedQueue, TimeUnit }
|
import java.util.concurrent.{ CountDownLatch, ConcurrentLinkedQueue, TimeUnit }
|
||||||
import akka.testkit._
|
import akka.testkit._
|
||||||
import scala.concurrent.Await
|
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
|
assert(elapsedTimeMs < 2000) // the precision is not ms exact
|
||||||
cancellable.cancel()
|
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))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,7 @@ package akka.actor
|
||||||
import language.postfixOps
|
import language.postfixOps
|
||||||
import java.util.concurrent.{ TimeUnit, CountDownLatch }
|
import java.util.concurrent.{ TimeUnit, CountDownLatch }
|
||||||
import scala.concurrent.Await
|
import scala.concurrent.Await
|
||||||
import scala.concurrent.util.Duration
|
import scala.concurrent.duration._
|
||||||
import scala.concurrent.util.duration.intToDurationInt
|
|
||||||
import scala.math.BigInt.int2bigInt
|
import scala.math.BigInt.int2bigInt
|
||||||
import scala.util.Random
|
import scala.util.Random
|
||||||
import scala.util.control.NoStackTrace
|
import scala.util.control.NoStackTrace
|
||||||
|
|
@ -195,7 +194,7 @@ object SupervisorHierarchySpec {
|
||||||
case x ⇒ (x, x)
|
case x ⇒ (x, x)
|
||||||
}
|
}
|
||||||
override val supervisorStrategy = OneForOneStrategy()(unwrap andThen {
|
override val supervisorStrategy = OneForOneStrategy()(unwrap andThen {
|
||||||
case _: Failure if pongsToGo > 0 ⇒
|
case (_: Failure, _) if pongsToGo > 0 ⇒
|
||||||
log :+= Event("pongOfDeath resuming " + sender, identityHashCode(this))
|
log :+= Event("pongOfDeath resuming " + sender, identityHashCode(this))
|
||||||
Resume
|
Resume
|
||||||
case (f: Failure, orig) ⇒
|
case (f: Failure, orig) ⇒
|
||||||
|
|
@ -392,10 +391,10 @@ object SupervisorHierarchySpec {
|
||||||
|
|
||||||
// don’t escalate from this one!
|
// don’t escalate from this one!
|
||||||
override val supervisorStrategy = OneForOneStrategy() {
|
override val supervisorStrategy = OneForOneStrategy() {
|
||||||
case f: Failure ⇒ f.directive
|
case f: Failure ⇒ f.directive
|
||||||
case OriginalRestartException(f: Failure) ⇒ f.directive
|
case OriginalRestartException(f: Failure) ⇒ f.directive
|
||||||
case ActorInitializationException(f: Failure) ⇒ f.directive
|
case ActorInitializationException(_, _, f: Failure) ⇒ f.directive
|
||||||
case _ ⇒ Stop
|
case _ ⇒ Stop
|
||||||
}
|
}
|
||||||
|
|
||||||
var children = Vector.empty[ActorRef]
|
var children = Vector.empty[ActorRef]
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import java.util.concurrent.{ TimeUnit, CountDownLatch }
|
||||||
import akka.testkit.AkkaSpec
|
import akka.testkit.AkkaSpec
|
||||||
import akka.testkit.DefaultTimeout
|
import akka.testkit.DefaultTimeout
|
||||||
import akka.pattern.ask
|
import akka.pattern.ask
|
||||||
import scala.concurrent.util.duration._
|
import scala.concurrent.duration._
|
||||||
import scala.util.control.NonFatal
|
import scala.util.control.NonFatal
|
||||||
|
|
||||||
object SupervisorMiscSpec {
|
object SupervisorMiscSpec {
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ package akka.actor
|
||||||
import language.postfixOps
|
import language.postfixOps
|
||||||
|
|
||||||
import org.scalatest.BeforeAndAfterEach
|
import org.scalatest.BeforeAndAfterEach
|
||||||
import scala.concurrent.util.duration._
|
import scala.concurrent.duration._
|
||||||
import akka.{ Die, Ping }
|
import akka.{ Die, Ping }
|
||||||
import akka.testkit.TestEvent._
|
import akka.testkit.TestEvent._
|
||||||
import akka.testkit._
|
import akka.testkit._
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import language.postfixOps
|
||||||
import org.scalatest.WordSpec
|
import org.scalatest.WordSpec
|
||||||
import org.scalatest.matchers.MustMatchers
|
import org.scalatest.matchers.MustMatchers
|
||||||
import scala.concurrent.Await
|
import scala.concurrent.Await
|
||||||
import scala.concurrent.util.duration._
|
import scala.concurrent.duration._
|
||||||
import akka.actor.Actor._
|
import akka.actor.Actor._
|
||||||
import akka.testkit.{ TestKit, EventFilter, filterEvents, filterException, AkkaSpec, ImplicitSender, DefaultTimeout }
|
import akka.testkit.{ TestKit, EventFilter, filterEvents, filterException, AkkaSpec, ImplicitSender, DefaultTimeout }
|
||||||
import akka.dispatch.Dispatchers
|
import akka.dispatch.Dispatchers
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import akka.testkit.ImplicitSender
|
||||||
import akka.testkit.DefaultTimeout
|
import akka.testkit.DefaultTimeout
|
||||||
import scala.concurrent.Await
|
import scala.concurrent.Await
|
||||||
import akka.pattern.ask
|
import akka.pattern.ask
|
||||||
import scala.concurrent.util.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
||||||
class Ticket669Spec extends AkkaSpec with BeforeAndAfterAll with ImplicitSender with DefaultTimeout {
|
class Ticket669Spec extends AkkaSpec with BeforeAndAfterAll with ImplicitSender with DefaultTimeout {
|
||||||
|
|
|
||||||
|
|
@ -5,22 +5,21 @@ package akka.actor
|
||||||
|
|
||||||
import language.postfixOps
|
import language.postfixOps
|
||||||
import org.scalatest.{ BeforeAndAfterAll, BeforeAndAfterEach }
|
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.{ Await, Future, Promise }
|
||||||
import scala.concurrent.util.Duration
|
import scala.concurrent.duration._
|
||||||
import scala.concurrent.util.duration._
|
|
||||||
import java.util.concurrent.atomic.AtomicReference
|
|
||||||
import annotation.tailrec
|
|
||||||
import akka.testkit.{ EventFilter, filterEvents, AkkaSpec }
|
import akka.testkit.{ EventFilter, filterEvents, AkkaSpec }
|
||||||
|
import akka.util.Timeout
|
||||||
import akka.japi.{ Option ⇒ JOption }
|
import akka.japi.{ Option ⇒ JOption }
|
||||||
import akka.testkit.DefaultTimeout
|
import akka.testkit.DefaultTimeout
|
||||||
import akka.dispatch.{ Dispatchers }
|
import akka.dispatch.Dispatchers
|
||||||
import akka.pattern.ask
|
import akka.pattern.ask
|
||||||
import akka.serialization.JavaSerializer
|
import akka.serialization.JavaSerializer
|
||||||
import akka.actor.TypedActor._
|
import akka.actor.TypedActor._
|
||||||
|
import java.util.concurrent.atomic.AtomicReference
|
||||||
import java.lang.IllegalStateException
|
import java.lang.IllegalStateException
|
||||||
import java.util.concurrent.{ TimeoutException, TimeUnit, CountDownLatch }
|
import java.util.concurrent.{ TimeoutException, TimeUnit, CountDownLatch }
|
||||||
import scala.concurrent.util.FiniteDuration
|
|
||||||
|
|
||||||
object TypedActorSpec {
|
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
|
def hasNext = items != Nil
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,7 @@ import akka.event.Logging.Error
|
||||||
import akka.pattern.ask
|
import akka.pattern.ask
|
||||||
import akka.testkit._
|
import akka.testkit._
|
||||||
import akka.util.Switch
|
import akka.util.Switch
|
||||||
import scala.concurrent.util.duration._
|
import scala.concurrent.duration._
|
||||||
import scala.concurrent.util.Duration
|
|
||||||
import scala.concurrent.{ Await, Future, Promise }
|
import scala.concurrent.{ Await, Future, Promise }
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,7 @@ import java.util.concurrent.atomic.{ AtomicBoolean, AtomicInteger }
|
||||||
import akka.testkit.{ filterEvents, EventFilter, AkkaSpec }
|
import akka.testkit.{ filterEvents, EventFilter, AkkaSpec }
|
||||||
import akka.actor.{ Props, Actor }
|
import akka.actor.{ Props, Actor }
|
||||||
import scala.concurrent.Await
|
import scala.concurrent.Await
|
||||||
import scala.concurrent.util.Duration
|
import scala.concurrent.duration._
|
||||||
import scala.concurrent.util.duration._
|
|
||||||
import akka.testkit.DefaultTimeout
|
import akka.testkit.DefaultTimeout
|
||||||
import akka.dispatch.{ PinnedDispatcher, Dispatchers, Dispatcher }
|
import akka.dispatch.{ PinnedDispatcher, Dispatchers, Dispatcher }
|
||||||
import akka.pattern.ask
|
import akka.pattern.ask
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import scala.collection.JavaConverters._
|
||||||
import com.typesafe.config.ConfigFactory
|
import com.typesafe.config.ConfigFactory
|
||||||
import akka.actor.Actor
|
import akka.actor.Actor
|
||||||
import akka.actor.Props
|
import akka.actor.Props
|
||||||
import scala.concurrent.util.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
object DispatchersSpec {
|
object DispatchersSpec {
|
||||||
val config = """
|
val config = """
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,7 @@ import language.postfixOps
|
||||||
import akka.testkit.AkkaSpec
|
import akka.testkit.AkkaSpec
|
||||||
import com.typesafe.config.ConfigFactory
|
import com.typesafe.config.ConfigFactory
|
||||||
import scala.collection.JavaConverters._
|
import scala.collection.JavaConverters._
|
||||||
import scala.concurrent.util.duration._
|
import scala.concurrent.duration._
|
||||||
import scala.concurrent.util.Duration
|
|
||||||
import akka.actor.{ IOManager, ActorSystem }
|
import akka.actor.{ IOManager, ActorSystem }
|
||||||
|
|
||||||
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
||||||
|
|
@ -25,8 +24,8 @@ class ConfigSpec extends AkkaSpec(ConfigFactory.defaultReference(ActorSystem.fin
|
||||||
{
|
{
|
||||||
import config._
|
import config._
|
||||||
|
|
||||||
getString("akka.version") must equal("2.1-SNAPSHOT")
|
getString("akka.version") must equal("2.2-SNAPSHOT")
|
||||||
settings.ConfigVersion must equal("2.1-SNAPSHOT")
|
settings.ConfigVersion must equal("2.2-SNAPSHOT")
|
||||||
|
|
||||||
getBoolean("akka.daemonic") must equal(false)
|
getBoolean("akka.daemonic") must equal(false)
|
||||||
getBoolean("akka.actor.serialize-messages") 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)
|
getInt("akka.actor.deployment.default.virtual-nodes-factor") must be(10)
|
||||||
settings.DefaultVirtualNodesFactor 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import language.postfixOps
|
||||||
import akka.actor.{ Actor, Props }
|
import akka.actor.{ Actor, Props }
|
||||||
import scala.concurrent.Future
|
import scala.concurrent.Future
|
||||||
import scala.concurrent.Await
|
import scala.concurrent.Await
|
||||||
import scala.concurrent.util.duration._
|
import scala.concurrent.duration._
|
||||||
import akka.testkit.{ AkkaSpec, DefaultTimeout }
|
import akka.testkit.{ AkkaSpec, DefaultTimeout }
|
||||||
import akka.pattern.{ ask, pipe }
|
import akka.pattern.{ ask, pipe }
|
||||||
import scala.concurrent.ExecutionException
|
import scala.concurrent.ExecutionException
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,7 @@ import akka.actor._
|
||||||
import akka.testkit.{ EventFilter, filterEvents, filterException, AkkaSpec, DefaultTimeout, TestLatch }
|
import akka.testkit.{ EventFilter, filterEvents, filterException, AkkaSpec, DefaultTimeout, TestLatch }
|
||||||
import scala.concurrent.{ Await, Awaitable, Future, Promise, ExecutionContext }
|
import scala.concurrent.{ Await, Awaitable, Future, Promise, ExecutionContext }
|
||||||
import scala.util.control.NonFatal
|
import scala.util.control.NonFatal
|
||||||
import scala.concurrent.util.duration._
|
import scala.concurrent.duration._
|
||||||
import scala.concurrent.util.Duration
|
|
||||||
import scala.concurrent.ExecutionContext
|
import scala.concurrent.ExecutionContext
|
||||||
import org.scalatest.junit.JUnitSuite
|
import org.scalatest.junit.JUnitSuite
|
||||||
import scala.runtime.NonLocalReturnControl
|
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) } }
|
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 {
|
"run callbacks async" in {
|
||||||
val latch = Vector.fill(10)(new TestLatch)
|
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)
|
// 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))
|
|
||||||
}*/
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import com.typesafe.config.Config
|
||||||
import akka.actor.{ RepointableRef, Props, DeadLetter, ActorSystem, ActorRefWithCell, ActorRef, ActorCell }
|
import akka.actor.{ RepointableRef, Props, DeadLetter, ActorSystem, ActorRefWithCell, ActorRef, ActorCell }
|
||||||
import akka.testkit.AkkaSpec
|
import akka.testkit.AkkaSpec
|
||||||
import scala.concurrent.{ Future, Promise, Await }
|
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])
|
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
||||||
abstract class MailboxSpec extends AkkaSpec with BeforeAndAfterAll with BeforeAndAfterEach {
|
abstract class MailboxSpec extends AkkaSpec with BeforeAndAfterAll with BeforeAndAfterEach {
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,10 @@ import org.junit.runner.RunWith
|
||||||
import org.scalatest.junit.JUnitRunner
|
import org.scalatest.junit.JUnitRunner
|
||||||
import com.typesafe.config.Config
|
import com.typesafe.config.Config
|
||||||
|
|
||||||
import akka.actor.{ Props, InternalActorRef, ActorSystem, Actor }
|
import akka.actor.{ Props, ActorSystem, Actor }
|
||||||
import akka.pattern.ask
|
import akka.pattern.ask
|
||||||
import akka.testkit.{ DefaultTimeout, AkkaSpec }
|
import akka.testkit.{ DefaultTimeout, AkkaSpec }
|
||||||
import scala.concurrent.Await
|
import scala.concurrent.duration._
|
||||||
import scala.concurrent.util.duration.intToDurationInt
|
|
||||||
|
|
||||||
object PriorityDispatcherSpec {
|
object PriorityDispatcherSpec {
|
||||||
val config = """
|
val config = """
|
||||||
|
|
@ -50,24 +49,32 @@ class PriorityDispatcherSpec extends AkkaSpec(PriorityDispatcherSpec.config) wit
|
||||||
}
|
}
|
||||||
|
|
||||||
def testOrdering(dispatcherKey: String) {
|
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 {
|
val actor = system.actorOf(Props(new Actor {
|
||||||
var acc: List[Int] = Nil
|
context.actorOf(Props(new Actor {
|
||||||
|
|
||||||
def receive = {
|
val acc = scala.collection.mutable.ListBuffer[Int]()
|
||||||
case i: Int ⇒ acc = i :: acc
|
|
||||||
case 'Result ⇒ sender ! acc
|
|
||||||
}
|
|
||||||
}).withDispatcher(dispatcherKey)).asInstanceOf[InternalActorRef]
|
|
||||||
|
|
||||||
actor.suspend //Make sure the actor isn't treating any messages, let it buffer the incoming messages
|
scala.util.Random.shuffle(msgs) foreach { m ⇒ self ! m }
|
||||||
|
|
||||||
val msgs = (1 to 100).toList
|
self.tell('Result, testActor)
|
||||||
for (m ← msgs) actor ! m
|
|
||||||
|
|
||||||
actor.resume(causedByFailure = null) //Signal the actor to start treating it's message backlog
|
def receive = {
|
||||||
|
case i: Int ⇒ acc += i
|
||||||
|
case 'Result ⇒ sender ! acc.toList
|
||||||
|
}
|
||||||
|
}).withDispatcher(dispatcherKey))
|
||||||
|
|
||||||
Await.result(actor.?('Result).mapTo[List[Int]], timeout.duration) must be === msgs.reverse
|
def receive = Actor.emptyBehavior
|
||||||
|
|
||||||
|
}))
|
||||||
|
|
||||||
|
expectMsgType[List[_]] must be === msgs
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import language.postfixOps
|
||||||
|
|
||||||
import org.scalatest.BeforeAndAfterEach
|
import org.scalatest.BeforeAndAfterEach
|
||||||
import akka.testkit._
|
import akka.testkit._
|
||||||
import scala.concurrent.util.duration._
|
import scala.concurrent.duration._
|
||||||
import java.util.concurrent.atomic._
|
import java.util.concurrent.atomic._
|
||||||
import akka.actor.{ Props, Actor, ActorRef, ActorSystem }
|
import akka.actor.{ Props, Actor, ActorRef, ActorSystem }
|
||||||
import java.util.Comparator
|
import java.util.Comparator
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ package akka.event
|
||||||
|
|
||||||
import language.postfixOps
|
import language.postfixOps
|
||||||
|
|
||||||
import scala.concurrent.util.duration._
|
import scala.concurrent.duration._
|
||||||
import akka.actor.{ Actor, ActorRef, ActorSystemImpl, ActorSystem, Props, UnhandledMessage }
|
import akka.actor.{ Actor, ActorRef, ActorSystemImpl, ActorSystem, Props, UnhandledMessage }
|
||||||
import com.typesafe.config.ConfigFactory
|
import com.typesafe.config.ConfigFactory
|
||||||
import scala.collection.JavaConverters._
|
import scala.collection.JavaConverters._
|
||||||
|
|
@ -282,4 +282,4 @@ class EventStreamSpec extends AkkaSpec(EventStreamSpec.config) {
|
||||||
msg foreach (expectMsg(_))
|
msg foreach (expectMsg(_))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,9 @@ package akka.event
|
||||||
import language.postfixOps
|
import language.postfixOps
|
||||||
|
|
||||||
import org.scalatest.{ BeforeAndAfterAll, BeforeAndAfterEach }
|
import org.scalatest.{ BeforeAndAfterAll, BeforeAndAfterEach }
|
||||||
import scala.concurrent.util.duration._
|
import scala.concurrent.duration._
|
||||||
import akka.testkit._
|
import akka.testkit._
|
||||||
import org.scalatest.WordSpec
|
import org.scalatest.WordSpec
|
||||||
import scala.concurrent.util.Duration
|
|
||||||
import com.typesafe.config.ConfigFactory
|
import com.typesafe.config.ConfigFactory
|
||||||
import scala.collection.JavaConverters._
|
import scala.collection.JavaConverters._
|
||||||
import java.util.Properties
|
import java.util.Properties
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ package akka.pattern
|
||||||
import language.postfixOps
|
import language.postfixOps
|
||||||
|
|
||||||
import akka.testkit.AkkaSpec
|
import akka.testkit.AkkaSpec
|
||||||
import scala.concurrent.util.duration._
|
import scala.concurrent.duration._
|
||||||
import scala.concurrent.Await
|
import scala.concurrent.Await
|
||||||
import akka.testkit.DefaultTimeout
|
import akka.testkit.DefaultTimeout
|
||||||
import akka.util.Timeout
|
import akka.util.Timeout
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,9 @@
|
||||||
package akka.pattern
|
package akka.pattern
|
||||||
|
|
||||||
import akka.testkit._
|
import akka.testkit._
|
||||||
import scala.concurrent.util.duration._
|
import scala.concurrent.duration._
|
||||||
import scala.concurrent.{ Promise, Future, Await }
|
import scala.concurrent.{ Promise, Future, Await }
|
||||||
|
import scala.annotation.tailrec
|
||||||
|
|
||||||
class CircuitBreakerMTSpec extends AkkaSpec {
|
class CircuitBreakerMTSpec extends AkkaSpec {
|
||||||
implicit val ec = system.dispatcher
|
implicit val ec = system.dispatcher
|
||||||
|
|
@ -14,8 +15,16 @@ class CircuitBreakerMTSpec extends AkkaSpec {
|
||||||
val resetTimeout = 2.seconds.dilated
|
val resetTimeout = 2.seconds.dilated
|
||||||
val breaker = new CircuitBreaker(system.scheduler, 5, callTimeout, resetTimeout)
|
val breaker = new CircuitBreaker(system.scheduler, 5, callTimeout, resetTimeout)
|
||||||
|
|
||||||
def openBreaker(): Unit =
|
def openBreaker(): Unit = {
|
||||||
Await.ready(Future.sequence((1 to 5).map(_ ⇒ breaker.withCircuitBreaker(Future(throw new RuntimeException("FAIL"))).failed)), 1.second.dilated)
|
@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 {
|
"allow many calls while in closed state with no errors" in {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ package akka.pattern
|
||||||
|
|
||||||
import language.postfixOps
|
import language.postfixOps
|
||||||
|
|
||||||
import scala.concurrent.util.duration._
|
import scala.concurrent.duration._
|
||||||
import akka.testkit._
|
import akka.testkit._
|
||||||
import org.scalatest.BeforeAndAfter
|
import org.scalatest.BeforeAndAfter
|
||||||
import akka.actor.{ ActorSystem, Scheduler }
|
import akka.actor.{ ActorSystem, Scheduler }
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,7 @@ import language.postfixOps
|
||||||
import akka.testkit.AkkaSpec
|
import akka.testkit.AkkaSpec
|
||||||
import akka.actor.{ Props, Actor }
|
import akka.actor.{ Props, Actor }
|
||||||
import scala.concurrent.{ Future, Promise, Await }
|
import scala.concurrent.{ Future, Promise, Await }
|
||||||
import scala.concurrent.util.Duration
|
import scala.concurrent.duration._
|
||||||
import scala.concurrent.util.duration._
|
|
||||||
|
|
||||||
object PatternSpec {
|
object PatternSpec {
|
||||||
case class Work(duration: Duration)
|
case class Work(duration: Duration)
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,7 @@ import akka.performance.workbench.PerformanceSpec
|
||||||
import akka.actor._
|
import akka.actor._
|
||||||
import java.util.concurrent.{ ThreadPoolExecutor, CountDownLatch, TimeUnit }
|
import java.util.concurrent.{ ThreadPoolExecutor, CountDownLatch, TimeUnit }
|
||||||
import akka.dispatch._
|
import akka.dispatch._
|
||||||
import scala.concurrent.util.Duration
|
import scala.concurrent.duration._
|
||||||
import scala.concurrent.util.duration._
|
|
||||||
|
|
||||||
// -server -Xms512M -Xmx1024M -XX:+UseParallelGC -Dbenchmark=true -Dbenchmark.repeatFactor=500
|
// -server -Xms512M -Xmx1024M -XX:+UseParallelGC -Dbenchmark=true -Dbenchmark.repeatFactor=500
|
||||||
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,7 @@ import akka.performance.workbench.PerformanceSpec
|
||||||
import akka.actor._
|
import akka.actor._
|
||||||
import java.util.concurrent.{ ThreadPoolExecutor, CountDownLatch, TimeUnit }
|
import java.util.concurrent.{ ThreadPoolExecutor, CountDownLatch, TimeUnit }
|
||||||
import akka.dispatch._
|
import akka.dispatch._
|
||||||
import scala.concurrent.util.Duration
|
import scala.concurrent.duration._
|
||||||
import scala.concurrent.util.duration._
|
|
||||||
|
|
||||||
// -server -Xms512M -Xmx1024M -XX:+UseParallelGC -Dbenchmark=true -Dbenchmark.repeatFactor=500
|
// -server -Xms512M -Xmx1024M -XX:+UseParallelGC -Dbenchmark=true -Dbenchmark.repeatFactor=500
|
||||||
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
||||||
|
|
|
||||||
|
|
@ -12,17 +12,18 @@ import java.io.PrintWriter
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import scala.collection.mutable.{ Map ⇒ MutableMap }
|
import scala.collection.mutable.{ Map ⇒ MutableMap }
|
||||||
|
import scala.collection.immutable
|
||||||
import akka.actor.ActorSystem
|
import akka.actor.ActorSystem
|
||||||
import akka.event.Logging
|
import akka.event.Logging
|
||||||
|
|
||||||
trait BenchResultRepository {
|
trait BenchResultRepository {
|
||||||
def add(stats: Stats)
|
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 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
|
def isBaseline(stats: Stats): Boolean
|
||||||
|
|
||||||
|
|
@ -38,9 +39,9 @@ object BenchResultRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
class FileBenchResultRepository extends 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 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 def resultDir = BenchmarkConfig.config.getString("benchmark.resultDir")
|
||||||
private val serDir = resultDir + "/ser"
|
private val serDir = resultDir + "/ser"
|
||||||
private def serDirExists: Boolean = new File(serDir).exists
|
private def serDirExists: Boolean = new File(serDir).exists
|
||||||
|
|
@ -51,13 +52,13 @@ class FileBenchResultRepository extends BenchResultRepository {
|
||||||
case class Key(name: String, load: Int)
|
case class Key(name: String, load: Int)
|
||||||
|
|
||||||
def add(stats: Stats): Unit = synchronized {
|
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
|
statsByName(stats.name) = values :+ stats
|
||||||
save(stats)
|
save(stats)
|
||||||
}
|
}
|
||||||
|
|
||||||
def get(name: String): Seq[Stats] = synchronized {
|
def get(name: String): immutable.Seq[Stats] = synchronized {
|
||||||
statsByName.getOrElse(name, IndexedSeq.empty)
|
statsByName.getOrElse(name, Vector.empty)
|
||||||
}
|
}
|
||||||
|
|
||||||
def get(name: String, load: Int): Option[Stats] = synchronized {
|
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)
|
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 key = Key(name, load)
|
||||||
val historical = historicalStats.getOrElse(key, IndexedSeq.empty)
|
val historical = historicalStats.getOrElse(key, Vector.empty)
|
||||||
val baseline = baselineStats.get(key)
|
val baseline = baselineStats.get(key)
|
||||||
val current = get(name, load)
|
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)
|
limited.sortBy(_.timestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -94,7 +95,7 @@ class FileBenchResultRepository extends BenchResultRepository {
|
||||||
}
|
}
|
||||||
val historical = load(historicalFiles)
|
val historical = load(historicalFiles)
|
||||||
for (h ← historical) {
|
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
|
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 =
|
val result =
|
||||||
for (f ← files) yield {
|
for (f ← files) yield {
|
||||||
var in: ObjectInputStream = null
|
var in: ObjectInputStream = null
|
||||||
|
|
@ -132,11 +133,11 @@ class FileBenchResultRepository extends BenchResultRepository {
|
||||||
case e: Throwable ⇒
|
case e: Throwable ⇒
|
||||||
None
|
None
|
||||||
} finally {
|
} 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()
|
loadFiles()
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package akka.performance.workbench
|
||||||
import java.io.UnsupportedEncodingException
|
import java.io.UnsupportedEncodingException
|
||||||
import java.net.URLEncoder
|
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/
|
* 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.
|
* 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) ""
|
if (statsByTimestamp.isEmpty) ""
|
||||||
else {
|
else {
|
||||||
val loads = statsByTimestamp.values.head.map(_.load)
|
val loads = statsByTimestamp.values.head.map(_.load)
|
||||||
|
|
@ -46,7 +46,7 @@ object GoogleChartBuilder {
|
||||||
//sb.append("&")
|
//sb.append("&")
|
||||||
|
|
||||||
// legend
|
// legend
|
||||||
val legendStats = statsByTimestamp.values.map(_.head).toSeq
|
val legendStats = statsByTimestamp.values.toVector.map(_.head)
|
||||||
appendLegend(legendStats, sb, legend)
|
appendLegend(legendStats, sb, legend)
|
||||||
sb.append("&")
|
sb.append("&")
|
||||||
// bar spacing
|
// bar spacing
|
||||||
|
|
@ -60,10 +60,7 @@ object GoogleChartBuilder {
|
||||||
val loadStr = loads.mkString(",")
|
val loadStr = loads.mkString(",")
|
||||||
sb.append("chd=t:")
|
sb.append("chd=t:")
|
||||||
val maxValue = allStats.map(_.tps).max
|
val maxValue = allStats.map(_.tps).max
|
||||||
val tpsSeries: Iterable[String] =
|
val tpsSeries: Iterable[String] = for (statsSeq ← statsByTimestamp.values) yield statsSeq.map(_.tps).mkString(",")
|
||||||
for (statsSeq ← statsByTimestamp.values) yield {
|
|
||||||
statsSeq.map(_.tps).mkString(",")
|
|
||||||
}
|
|
||||||
sb.append(tpsSeries.mkString("|"))
|
sb.append(tpsSeries.mkString("|"))
|
||||||
|
|
||||||
// y range
|
// y range
|
||||||
|
|
@ -83,7 +80,7 @@ object GoogleChartBuilder {
|
||||||
/**
|
/**
|
||||||
* Builds a bar chart for all percentiles and the mean in the statistics.
|
* 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) ""
|
if (statistics.isEmpty) ""
|
||||||
else {
|
else {
|
||||||
val current = statistics.last
|
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:|")
|
sb.append("chxl=1:|")
|
||||||
val s = percentiles.keys.toList.map(_ + "%").mkString("|")
|
val s = percentiles.keys.toList.map(_ + "%").mkString("|")
|
||||||
sb.append(s)
|
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(_))
|
val legends = statistics.map(legend(_))
|
||||||
sb.append("chdl=")
|
sb.append("chdl=")
|
||||||
val s = legends.map(urlEncode(_)).mkString("|")
|
val s = legends.map(urlEncode(_)).mkString("|")
|
||||||
|
|
@ -166,7 +163,7 @@ object GoogleChartBuilder {
|
||||||
sb.append(s)
|
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 =
|
val percentileSeries =
|
||||||
for {
|
for {
|
||||||
percentiles ← allPercentiles
|
percentiles ← allPercentiles
|
||||||
|
|
@ -181,7 +178,7 @@ object GoogleChartBuilder {
|
||||||
sb.append(series.mkString("|"))
|
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(_))
|
val series = values.map(formatDouble(_))
|
||||||
sb.append(series.mkString("|"))
|
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) ""
|
if (statistics.isEmpty) ""
|
||||||
else {
|
else {
|
||||||
val sb = new StringBuilder
|
val sb = new StringBuilder
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import scala.collection.immutable.TreeMap
|
||||||
import org.apache.commons.math.stat.descriptive.DescriptiveStatistics
|
import org.apache.commons.math.stat.descriptive.DescriptiveStatistics
|
||||||
import org.scalatest.BeforeAndAfterEach
|
import org.scalatest.BeforeAndAfterEach
|
||||||
import akka.testkit.AkkaSpec
|
import akka.testkit.AkkaSpec
|
||||||
import scala.concurrent.util.Duration
|
import scala.concurrent.duration.Duration
|
||||||
import com.typesafe.config.Config
|
import com.typesafe.config.Config
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import akka.event.Logging
|
import akka.event.Logging
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import java.text.SimpleDateFormat
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import akka.actor.ActorSystem
|
import akka.actor.ActorSystem
|
||||||
import akka.event.Logging
|
import akka.event.Logging
|
||||||
import scala.collection.immutable.TreeMap
|
import scala.collection.immutable
|
||||||
|
|
||||||
class Report(
|
class Report(
|
||||||
system: ActorSystem,
|
system: ActorSystem,
|
||||||
|
|
@ -19,7 +19,7 @@ class Report(
|
||||||
val legendTimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm")
|
val legendTimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm")
|
||||||
val fileTimestampFormat = new SimpleDateFormat("yyyyMMddHHmmss")
|
val fileTimestampFormat = new SimpleDateFormat("yyyyMMddHHmmss")
|
||||||
|
|
||||||
def html(statistics: Seq[Stats]) {
|
def html(statistics: immutable.Seq[Stats]) {
|
||||||
|
|
||||||
val current = statistics.last
|
val current = statistics.last
|
||||||
val sb = new StringBuilder
|
val sb = new StringBuilder
|
||||||
|
|
@ -80,13 +80,13 @@ class Report(
|
||||||
chartUrl
|
chartUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
def comparePercentilesAndMeanChart(stats: Stats): Seq[String] = {
|
def comparePercentilesAndMeanChart(stats: Stats): immutable.Seq[String] = {
|
||||||
for {
|
for {
|
||||||
compareName ← compareResultWith.toSeq
|
compareName ← compareResultWith.to[immutable.Seq]
|
||||||
compareStats ← resultRepository.get(compareName, stats.load)
|
compareStats ← resultRepository.get(compareName, stats.load)
|
||||||
} yield {
|
} yield {
|
||||||
val chartTitle = stats.name + " vs. " + compareName + ", " + stats.load + " clients" + ", Percentiles and Mean (microseconds)"
|
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
|
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) {
|
if (statistics.isEmpty) {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
val histTimestamps = resultRepository.getWithHistorical(statistics.head.name, statistics.head.load).map(_.timestamp)
|
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 {
|
(for (ts ← histTimestamps) yield {
|
||||||
val seq =
|
val seq =
|
||||||
for (stats ← statistics) yield {
|
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)
|
val cell = withHistorical.find(_.timestamp == ts)
|
||||||
cell.getOrElse(Stats(stats.name, stats.load, ts))
|
cell.getOrElse(Stats(stats.name, stats.load, ts))
|
||||||
}
|
}
|
||||||
|
|
@ -131,7 +131,7 @@ class Report(
|
||||||
chartUrl
|
chartUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
def formatResultsTable(statsSeq: Seq[Stats]): String = {
|
def formatResultsTable(statsSeq: immutable.Seq[Stats]): String = {
|
||||||
|
|
||||||
val name = statsSeq.head.name
|
val name = statsSeq.head.name
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import akka.ConfigurationException
|
||||||
import scala.concurrent.Await
|
import scala.concurrent.Await
|
||||||
import akka.pattern.{ ask, gracefulStop }
|
import akka.pattern.{ ask, gracefulStop }
|
||||||
import akka.testkit.{ TestLatch, ImplicitSender, DefaultTimeout, AkkaSpec }
|
import akka.testkit.{ TestLatch, ImplicitSender, DefaultTimeout, AkkaSpec }
|
||||||
import scala.concurrent.util.duration.intToDurationInt
|
import scala.concurrent.duration._
|
||||||
import akka.actor.UnstartedCell
|
import akka.actor.UnstartedCell
|
||||||
|
|
||||||
object ConfiguredLocalRoutingSpec {
|
object ConfiguredLocalRoutingSpec {
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ class CustomRouteSpec extends AkkaSpec {
|
||||||
provider.createRoutees(1)
|
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)
|
case (sender, message) ⇒ toAll(sender, provider.routees)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -35,7 +35,7 @@ class CustomRouteSpec extends AkkaSpec {
|
||||||
import akka.pattern.ask
|
import akka.pattern.ask
|
||||||
import akka.testkit.ExtractRoute
|
import akka.testkit.ExtractRoute
|
||||||
import scala.concurrent.Await
|
import scala.concurrent.Await
|
||||||
import scala.concurrent.util.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
val target = system.actorOf(Props.empty)
|
val target = system.actorOf(Props.empty)
|
||||||
val router = system.actorOf(Props.empty.withRouter(new MyRouter(target)))
|
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).
|
val r = Await.result(router.ask(CurrentRoutees)(1 second).
|
||||||
mapTo[RouterRoutees], 1 second)
|
mapTo[RouterRoutees], 1 second)
|
||||||
r.routees.size must be(1)
|
r.routees.size must be(1)
|
||||||
route(testActor -> "hallo") must be(Seq(Destination(testActor, target)))
|
route(testActor -> "hallo") must be(List(Destination(testActor, target)))
|
||||||
route(testActor -> 12) must be(Seq(Destination(testActor, r.routees.head)))
|
route(testActor -> 12) must be(List(Destination(testActor, r.routees.head)))
|
||||||
//#test-route
|
//#test-route
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,10 @@ import akka.testkit._
|
||||||
import akka.testkit.TestEvent._
|
import akka.testkit.TestEvent._
|
||||||
import akka.actor.Props
|
import akka.actor.Props
|
||||||
import scala.concurrent.Await
|
import scala.concurrent.Await
|
||||||
import scala.concurrent.util.duration._
|
import scala.concurrent.duration._
|
||||||
|
import scala.collection.immutable
|
||||||
import akka.actor.ActorRef
|
import akka.actor.ActorRef
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
|
||||||
import akka.pattern.ask
|
import akka.pattern.ask
|
||||||
import scala.concurrent.util.Duration
|
|
||||||
import java.util.concurrent.TimeoutException
|
|
||||||
import scala.concurrent.util.FiniteDuration
|
|
||||||
import scala.util.Try
|
import scala.util.Try
|
||||||
|
|
||||||
object ResizerSpec {
|
object ResizerSpec {
|
||||||
|
|
@ -63,10 +60,10 @@ class ResizerSpec extends AkkaSpec(ResizerSpec.config) with DefaultTimeout with
|
||||||
lowerBound = 2,
|
lowerBound = 2,
|
||||||
upperBound = 3)
|
upperBound = 3)
|
||||||
|
|
||||||
val c1 = resizer.capacity(IndexedSeq.empty[ActorRef])
|
val c1 = resizer.capacity(immutable.IndexedSeq.empty[ActorRef])
|
||||||
c1 must be(2)
|
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)
|
val c2 = resizer.capacity(current)
|
||||||
c2 must be(0)
|
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
|
// sending in too quickly will result in skipped resize due to many resizeInProgress conflicts
|
||||||
Thread.sleep(20.millis.dilated.toMillis)
|
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")
|
for (m ← 0 until loops) expectMsg("done")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -176,20 +173,21 @@ class ResizerSpec extends AkkaSpec(ResizerSpec.config) with DefaultTimeout with
|
||||||
routeeSize(router) must be(resizer.upperBound)
|
routeeSize(router) must be(resizer.upperBound)
|
||||||
}
|
}
|
||||||
|
|
||||||
"backoff" in {
|
"backoff" in within(10 seconds) {
|
||||||
|
|
||||||
val resizer = DefaultResizer(
|
val resizer = DefaultResizer(
|
||||||
lowerBound = 1,
|
lowerBound = 1,
|
||||||
upperBound = 5,
|
upperBound = 5,
|
||||||
rampupRate = 1.0,
|
rampupRate = 1.0,
|
||||||
backoffRate = 1.0,
|
backoffRate = 1.0,
|
||||||
backoffThreshold = 0.20,
|
backoffThreshold = 0.40,
|
||||||
pressureThreshold = 1,
|
pressureThreshold = 1,
|
||||||
messagesPerResize = 1)
|
messagesPerResize = 1)
|
||||||
|
|
||||||
val router = system.actorOf(Props(new Actor {
|
val router = system.actorOf(Props(new Actor {
|
||||||
def receive = {
|
def receive = {
|
||||||
case n: Int ⇒ Thread.sleep((n millis).dilated.toMillis)
|
case n: Int if n <= 0 ⇒ // done
|
||||||
|
case n: Int ⇒ Thread.sleep((n millis).dilated.toMillis)
|
||||||
}
|
}
|
||||||
}).withRouter(RoundRobinRouter(resizer = Some(resizer))))
|
}).withRouter(RoundRobinRouter(resizer = Some(resizer))))
|
||||||
|
|
||||||
|
|
@ -205,12 +203,11 @@ class ResizerSpec extends AkkaSpec(ResizerSpec.config) with DefaultTimeout with
|
||||||
Thread.sleep((300 millis).dilated.toMillis)
|
Thread.sleep((300 millis).dilated.toMillis)
|
||||||
|
|
||||||
// let it cool down
|
// let it cool down
|
||||||
for (m ← 0 to 5) {
|
awaitCond({
|
||||||
router ! 1
|
router ! 0 // trigger resize
|
||||||
Thread.sleep((500 millis).dilated.toMillis)
|
routeeSize(router) < z
|
||||||
}
|
}, interval = 500.millis.dilated)
|
||||||
|
|
||||||
awaitCond(Try(routeeSize(router) < (z)).getOrElse(false))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,20 +5,20 @@ package akka.routing
|
||||||
|
|
||||||
import language.postfixOps
|
import language.postfixOps
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
|
||||||
import akka.actor._
|
import akka.actor._
|
||||||
import scala.collection.mutable.LinkedList
|
import scala.collection.immutable
|
||||||
import akka.testkit._
|
import akka.testkit._
|
||||||
import scala.concurrent.util.duration._
|
import scala.concurrent.duration._
|
||||||
import scala.concurrent.Await
|
import scala.concurrent.Await
|
||||||
import scala.concurrent.util.Duration
|
|
||||||
import akka.ConfigurationException
|
import akka.ConfigurationException
|
||||||
import com.typesafe.config.ConfigFactory
|
import com.typesafe.config.ConfigFactory
|
||||||
import akka.pattern.{ ask, pipe }
|
import akka.pattern.{ ask, pipe }
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
import com.typesafe.config.Config
|
import com.typesafe.config.Config
|
||||||
import akka.dispatch.Dispatchers
|
import akka.dispatch.Dispatchers
|
||||||
|
import akka.util.Collections.EmptyImmutableSeq
|
||||||
import akka.util.Timeout
|
import akka.util.Timeout
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
|
|
||||||
object RoutingSpec {
|
object RoutingSpec {
|
||||||
|
|
||||||
|
|
@ -55,11 +55,10 @@ object RoutingSpec {
|
||||||
class MyRouter(config: Config) extends RouterConfig {
|
class MyRouter(config: Config) extends RouterConfig {
|
||||||
val foo = config.getString("foo")
|
val foo = config.getString("foo")
|
||||||
def createRoute(routeeProvider: RouteeProvider): Route = {
|
def createRoute(routeeProvider: RouteeProvider): Route = {
|
||||||
val routees = IndexedSeq(routeeProvider.context.actorOf(Props[Echo]))
|
routeeProvider.registerRoutees(List(routeeProvider.context.actorOf(Props[Echo])))
|
||||||
routeeProvider.registerRoutees(routees)
|
|
||||||
|
|
||||||
{
|
{
|
||||||
case (sender, message) ⇒ Nil
|
case (sender, message) ⇒ EmptyImmutableSeq
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
def routerDispatcher: String = Dispatchers.DefaultDispatcherId
|
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 {
|
"be able to send their routees" in {
|
||||||
class TheActor extends Actor {
|
case class TestRun(id: String, names: immutable.Iterable[String], actors: Int)
|
||||||
val routee1 = context.actorOf(Props[TestActor], "routee1")
|
val actor = system.actorOf(Props(new Actor {
|
||||||
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)))
|
|
||||||
|
|
||||||
def receive = {
|
def receive = {
|
||||||
case "doIt" ⇒ router ! CurrentRoutees
|
case TestRun(id, names, actors) ⇒
|
||||||
case routees: RouterRoutees ⇒ testActor forward routees
|
val routerProps = Props[TestActor].withRouter(
|
||||||
|
ScatterGatherFirstCompletedRouter(
|
||||||
|
routees = names map { context.actorOf(Props(new TestActor), _) },
|
||||||
|
within = 5 seconds))
|
||||||
|
|
||||||
|
1 to actors foreach { i ⇒ context.actorOf(routerProps, id + i).tell(CurrentRoutees, testActor) }
|
||||||
}
|
}
|
||||||
}
|
}))
|
||||||
|
|
||||||
val theActor = system.actorOf(Props(new TheActor), "theActor")
|
val actors = 15
|
||||||
theActor ! "doIt"
|
val names = 1 to 20 map { "routee" + _ } toList
|
||||||
val routees = expectMsgPF() {
|
|
||||||
case RouterRoutees(routees) ⇒ routees.toSet
|
|
||||||
}
|
|
||||||
|
|
||||||
routees.map(_.path.name) must be(Set("routee1", "routee2", "routee3"))
|
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 {
|
"use configured nr-of-instances when FromConfig" in {
|
||||||
val router = system.actorOf(Props[TestActor].withRouter(FromConfig), "router1")
|
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)
|
watch(router)
|
||||||
system.stop(router)
|
system.stop(router)
|
||||||
expectMsgType[Terminated]
|
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 {
|
"use configured nr-of-instances when router is specified" in {
|
||||||
val router = system.actorOf(Props[TestActor].withRouter(RoundRobinRouter(nrOfInstances = 2)), "router2")
|
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)
|
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")
|
val router = system.actorOf(Props[TestActor].withRouter(RoundRobinRouter(resizer = Some(resizer))), "router3")
|
||||||
Await.ready(latch, remaining)
|
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)
|
system.stop(router)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -252,15 +255,15 @@ class RoutingSpec extends AkkaSpec(RoutingSpec.config) with DefaultTimeout with
|
||||||
val doneLatch = new TestLatch(connectionCount)
|
val doneLatch = new TestLatch(connectionCount)
|
||||||
|
|
||||||
//lets create some connections.
|
//lets create some connections.
|
||||||
var actors = new LinkedList[ActorRef]
|
@volatile var actors = immutable.IndexedSeq[ActorRef]()
|
||||||
var counters = new LinkedList[AtomicInteger]
|
@volatile var counters = immutable.IndexedSeq[AtomicInteger]()
|
||||||
for (i ← 0 until connectionCount) {
|
for (i ← 0 until connectionCount) {
|
||||||
counters = counters :+ new AtomicInteger()
|
counters = counters :+ new AtomicInteger()
|
||||||
|
|
||||||
val actor = system.actorOf(Props(new Actor {
|
val actor = system.actorOf(Props(new Actor {
|
||||||
def receive = {
|
def receive = {
|
||||||
case "end" ⇒ doneLatch.countDown()
|
case "end" ⇒ doneLatch.countDown()
|
||||||
case msg: Int ⇒ counters.get(i).get.addAndGet(msg)
|
case msg: Int ⇒ counters(i).addAndGet(msg)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
actors = actors :+ actor
|
actors = actors :+ actor
|
||||||
|
|
@ -279,10 +282,8 @@ class RoutingSpec extends AkkaSpec(RoutingSpec.config) with DefaultTimeout with
|
||||||
//now wait some and do validations.
|
//now wait some and do validations.
|
||||||
Await.ready(doneLatch, remaining)
|
Await.ready(doneLatch, remaining)
|
||||||
|
|
||||||
for (i ← 0 until connectionCount) {
|
for (i ← 0 until connectionCount)
|
||||||
val counter = counters.get(i).get
|
counters(i).get must be((iterationCount * (i + 1)))
|
||||||
counter.get must be((iterationCount * (i + 1)))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"deliver a broadcast message using the !" in {
|
"deliver a broadcast message using the !" in {
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import akka.actor._
|
||||||
import java.io._
|
import java.io._
|
||||||
import scala.concurrent.Await
|
import scala.concurrent.Await
|
||||||
import akka.util.Timeout
|
import akka.util.Timeout
|
||||||
import scala.concurrent.util.duration._
|
import scala.concurrent.duration._
|
||||||
import scala.reflect.BeanInfo
|
import scala.reflect.BeanInfo
|
||||||
import com.google.protobuf.Message
|
import com.google.protobuf.Message
|
||||||
import akka.pattern.ask
|
import akka.pattern.ask
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,32 @@ import language.postfixOps
|
||||||
|
|
||||||
import org.scalatest.WordSpec
|
import org.scalatest.WordSpec
|
||||||
import org.scalatest.matchers.MustMatchers
|
import org.scalatest.matchers.MustMatchers
|
||||||
import scala.concurrent.util.Duration
|
import scala.concurrent.duration._
|
||||||
import scala.concurrent.util.duration._
|
import scala.concurrent.Await
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit._
|
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 {
|
"Duration" must {
|
||||||
|
|
||||||
|
|
@ -34,11 +54,12 @@ class DurationSpec extends WordSpec with MustMatchers {
|
||||||
val one = 1.second
|
val one = 1.second
|
||||||
val inf = Duration.Inf
|
val inf = Duration.Inf
|
||||||
val minf = Duration.MinusInf
|
val minf = Duration.MinusInf
|
||||||
|
val undefined = Duration.Undefined
|
||||||
(-inf) must be(minf)
|
(-inf) must be(minf)
|
||||||
intercept[IllegalArgumentException] { minf + inf }
|
(minf + inf) must be(undefined)
|
||||||
intercept[IllegalArgumentException] { inf - inf }
|
(inf - inf) must be(undefined)
|
||||||
intercept[IllegalArgumentException] { inf + minf }
|
(inf + minf) must be(undefined)
|
||||||
intercept[IllegalArgumentException] { minf - minf }
|
(minf - minf) must be(undefined)
|
||||||
(inf + inf) must be(inf)
|
(inf + inf) must be(inf)
|
||||||
(inf - minf) must be(inf)
|
(inf - minf) must be(inf)
|
||||||
(minf - inf) must be(minf)
|
(minf - inf) must be(minf)
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,12 @@ import akka.util.Unsafe;
|
||||||
|
|
||||||
final class AbstractActorRef {
|
final class AbstractActorRef {
|
||||||
final static long cellOffset;
|
final static long cellOffset;
|
||||||
|
final static long lookupOffset;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
try {
|
try {
|
||||||
cellOffset = Unsafe.instance.objectFieldOffset(RepointableActorRef.class.getDeclaredField("_cellDoNotCallMeDirectly"));
|
cellOffset = Unsafe.instance.objectFieldOffset(RepointableActorRef.class.getDeclaredField("_cellDoNotCallMeDirectly"));
|
||||||
|
lookupOffset = Unsafe.instance.objectFieldOffset(RepointableActorRef.class.getDeclaredField("_lookupDoNotCallMeDirectly"));
|
||||||
} catch(Throwable t){
|
} catch(Throwable t){
|
||||||
throw new ExceptionInInitializerError(t);
|
throw new ExceptionInInitializerError(t);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import scala.collection.Seq;
|
||||||
public class JAPI {
|
public class JAPI {
|
||||||
|
|
||||||
public static <T> Seq<T> seq(T... ts) {
|
public static <T> Seq<T> seq(T... ts) {
|
||||||
return Util.arrayToSeq(ts);
|
return Util.immutableSeq(ts);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,8 @@ import java.util.concurrent.ThreadFactory;
|
||||||
import java.util.concurrent.locks.ReadWriteLock;
|
import java.util.concurrent.locks.ReadWriteLock;
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||||
|
|
||||||
import scala.concurrent.util.Duration;
|
import scala.concurrent.duration.Duration;
|
||||||
import scala.concurrent.util.FiniteDuration;
|
import scala.concurrent.duration.FiniteDuration;
|
||||||
import akka.event.LoggingAdapter;
|
import akka.event.LoggingAdapter;
|
||||||
import akka.util.Unsafe;
|
import akka.util.Unsafe;
|
||||||
|
|
||||||
|
|
@ -263,8 +263,11 @@ public class HashedWheelTimer implements Timer {
|
||||||
|
|
||||||
void scheduleTimeout(HashedWheelTimeout timeout, long delay) {
|
void scheduleTimeout(HashedWheelTimeout timeout, long delay) {
|
||||||
// Prepare the required parameters to schedule the timeout object.
|
// 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 we’ll just schedule this timeout
|
||||||
|
// one tick early; that shouldn’t matter since we’re talking 270 years here
|
||||||
|
if (relativeIndex < 0) relativeIndex = delay / tickDuration;
|
||||||
|
if (relativeIndex == 0) relativeIndex = 1;
|
||||||
final long remainingRounds = relativeIndex / wheel.length;
|
final long remainingRounds = relativeIndex / wheel.length;
|
||||||
|
|
||||||
// Add the timeout to the wheel.
|
// Add the timeout to the wheel.
|
||||||
|
|
@ -304,7 +307,7 @@ public class HashedWheelTimer implements Timer {
|
||||||
|
|
||||||
while (!shutdown()) {
|
while (!shutdown()) {
|
||||||
final long deadline = waitForNextTick();
|
final long deadline = waitForNextTick();
|
||||||
if (deadline > 0)
|
if (deadline > Long.MIN_VALUE)
|
||||||
notifyExpiredTimeouts(fetchExpiredTimeouts(deadline));
|
notifyExpiredTimeouts(fetchExpiredTimeouts(deadline));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -332,7 +335,7 @@ public class HashedWheelTimer implements Timer {
|
||||||
HashedWheelTimeout timeout = i.next();
|
HashedWheelTimeout timeout = i.next();
|
||||||
if (timeout.remainingRounds <= 0) {
|
if (timeout.remainingRounds <= 0) {
|
||||||
i.remove();
|
i.remove();
|
||||||
if (timeout.deadline <= deadline) {
|
if (timeout.deadline - deadline <= 0) {
|
||||||
expiredTimeouts.add(timeout);
|
expiredTimeouts.add(timeout);
|
||||||
} else {
|
} else {
|
||||||
// Handle the case where the timeout is put into a wrong
|
// Handle the case where the timeout is put into a wrong
|
||||||
|
|
@ -368,6 +371,12 @@ public class HashedWheelTimer implements Timer {
|
||||||
expiredTimeouts.clear();
|
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() {
|
private long waitForNextTick() {
|
||||||
long deadline = startTime + tickDuration * tick;
|
long deadline = startTime + tickDuration * tick;
|
||||||
|
|
||||||
|
|
@ -378,7 +387,8 @@ public class HashedWheelTimer implements Timer {
|
||||||
|
|
||||||
if (sleepTimeMs <= 0) {
|
if (sleepTimeMs <= 0) {
|
||||||
tick += 1;
|
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
|
// 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);
|
Thread.sleep(sleepTimeMs);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
if (shutdown()) {
|
if (shutdown()) {
|
||||||
return -1;
|
return Long.MIN_VALUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ package akka.util.internal;
|
||||||
|
|
||||||
import java.util.Set;
|
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
|
* Schedules {@link TimerTask}s for one-time future execution in a background
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
akka {
|
akka {
|
||||||
# Akka version, checked against the runtime version of 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 directory of Akka, modules in the deploy directory will be loaded
|
||||||
home = ""
|
home = ""
|
||||||
|
|
|
||||||
|
|
@ -300,6 +300,11 @@ object Actor {
|
||||||
def apply(x: Any) = throw new UnsupportedOperationException("Empty behavior apply()")
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ package akka.actor
|
||||||
|
|
||||||
import java.io.{ ObjectOutputStream, NotSerializableException }
|
import java.io.{ ObjectOutputStream, NotSerializableException }
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
import scala.collection.immutable.TreeSet
|
import scala.collection.immutable
|
||||||
import scala.concurrent.util.Duration
|
import scala.concurrent.duration.Duration
|
||||||
import scala.util.control.NonFatal
|
import scala.util.control.NonFatal
|
||||||
import akka.actor.dungeon.ChildrenContainer
|
import akka.actor.dungeon.ChildrenContainer
|
||||||
import akka.actor.dungeon.ChildrenContainer.WaitingForChildren
|
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.
|
* Changes the Actor's behavior to become the new 'Receive' (PartialFunction[Any, Unit]) handler.
|
||||||
* Puts the behavior on top of the hotswap stack.
|
* This method acts upon the behavior stack as follows:
|
||||||
* If "discardOld" is true, an unbecome will be issued prior to pushing the new behavior to the stack
|
*
|
||||||
|
* - 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
|
def become(behavior: Actor.Receive, discardOld: Boolean = true): Unit
|
||||||
|
|
||||||
|
|
@ -102,7 +108,7 @@ trait ActorContext extends ActorRefFactory {
|
||||||
* val goodLookup = context.actorFor("kid")
|
* val goodLookup = context.actorFor("kid")
|
||||||
* }}}
|
* }}}
|
||||||
*/
|
*/
|
||||||
def children: Iterable[ActorRef]
|
def children: immutable.Iterable[ActorRef]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the child with the given name if it exists.
|
* 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.
|
* 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
|
def become(behavior: Procedure[Any]): Unit
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Changes the Actor's behavior to become the new 'Procedure' handler.
|
* Changes the Actor's behavior to become the new 'Procedure' handler.
|
||||||
* Puts the behavior on top of the hotswap stack.
|
* This method acts upon the behavior stack as follows:
|
||||||
* If "discardOld" is true, an unbecome will be issued prior to pushing the new behavior to the stack
|
*
|
||||||
|
* - 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
|
def become(behavior: Procedure[Any], discardOld: Boolean): Unit
|
||||||
|
|
||||||
|
|
@ -196,6 +208,11 @@ private[akka] trait Cell {
|
||||||
* The system internals where this Cell lives.
|
* The system internals where this Cell lives.
|
||||||
*/
|
*/
|
||||||
def systemImpl: ActorSystemImpl
|
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.
|
* Recursively suspend this actor and all its children. Must not throw exceptions.
|
||||||
*/
|
*/
|
||||||
|
|
@ -247,12 +264,12 @@ private[akka] trait Cell {
|
||||||
*/
|
*/
|
||||||
def isLocal: Boolean
|
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.
|
* “false” otherwise.
|
||||||
*/
|
*/
|
||||||
def hasMessages: Boolean
|
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.
|
* which may be a costly operation, 0 otherwise.
|
||||||
*/
|
*/
|
||||||
def numberOfMessages: Int
|
def numberOfMessages: Int
|
||||||
|
|
@ -275,7 +292,7 @@ private[akka] object ActorCell {
|
||||||
|
|
||||||
final val emptyBehaviorStack: List[Actor.Receive] = Nil
|
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)
|
//ACTORCELL IS 64bytes and should stay that way unless very good reason not to (machine sympathy, cache line fit)
|
||||||
|
|
@ -349,10 +366,10 @@ private[akka] class ActorCell(
|
||||||
case null ⇒ faultResume(inRespToFailure)
|
case null ⇒ faultResume(inRespToFailure)
|
||||||
case w: WaitingForChildren ⇒ w.enqueue(message)
|
case w: WaitingForChildren ⇒ w.enqueue(message)
|
||||||
}
|
}
|
||||||
case Terminate() ⇒ terminate()
|
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 ChildTerminated(child) ⇒ todo = handleChildTerminated(child)
|
||||||
case NoMessage ⇒ // only here to suppress warning
|
case NoMessage ⇒ // only here to suppress warning
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
case e @ (_: InterruptedException | NonFatal(_)) ⇒ handleInvokeFailure(Nil, e, "error while processing " + message)
|
case e @ (_: InterruptedException | NonFatal(_)) ⇒ handleInvokeFailure(Nil, e, "error while processing " + message)
|
||||||
|
|
@ -480,21 +497,21 @@ 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()
|
// Supervise is the first thing we get from a new child, so store away the UID for later use in handleFailure()
|
||||||
initChild(child) match {
|
initChild(child) match {
|
||||||
case Some(crs) ⇒
|
case Some(crs) ⇒
|
||||||
crs.uid = uid
|
crs.uid = uid
|
||||||
handleSupervise(child)
|
handleSupervise(child, async)
|
||||||
if (system.settings.DebugLifecycle) publish(Debug(self.path.toString, clazz(actor), "now supervising " + child))
|
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"))
|
case None ⇒ publish(Error(self.path.toString, clazz(actor), "received Supervise from unregistered child " + child + ", this will not end well"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// future extension point
|
// future extension point
|
||||||
protected def handleSupervise(child: ActorRef): Unit = child match {
|
protected def handleSupervise(child: ActorRef, async: Boolean): Unit = child match {
|
||||||
case r: RepointableActorRef ⇒ r.activate()
|
case r: RepointableActorRef if async ⇒ r.point()
|
||||||
case _ ⇒
|
case _ ⇒
|
||||||
}
|
}
|
||||||
|
|
||||||
final protected def clearActorFields(actorInstance: Actor): Unit = {
|
final protected def clearActorFields(actorInstance: Actor): Unit = {
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,11 @@
|
||||||
package akka.actor
|
package akka.actor
|
||||||
|
|
||||||
import scala.collection.mutable.Queue
|
import scala.collection.mutable.Queue
|
||||||
import scala.concurrent.util.Duration
|
import scala.concurrent.duration._
|
||||||
import scala.concurrent.util.duration._
|
|
||||||
import akka.pattern.ask
|
import akka.pattern.ask
|
||||||
import scala.concurrent.Await
|
import scala.concurrent.Await
|
||||||
import akka.util.Timeout
|
import akka.util.Timeout
|
||||||
import scala.collection.immutable.TreeSet
|
import scala.collection.immutable.TreeSet
|
||||||
import scala.concurrent.util.Deadline
|
|
||||||
import java.util.concurrent.TimeoutException
|
import java.util.concurrent.TimeoutException
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@
|
||||||
*/
|
*/
|
||||||
package akka.actor
|
package akka.actor
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
|
import scala.collection.immutable
|
||||||
|
import akka.japi.Util.immutableSeq
|
||||||
import java.net.MalformedURLException
|
import java.net.MalformedURLException
|
||||||
|
|
||||||
object ActorPath {
|
object ActorPath {
|
||||||
|
|
@ -20,6 +22,8 @@ object ActorPath {
|
||||||
* http://www.ietf.org/rfc/rfc2396.txt
|
* http://www.ietf.org/rfc/rfc2396.txt
|
||||||
*/
|
*/
|
||||||
val ElementRegex = """(?:[-\w:@&=+,.!~*'_;]|%\p{XDigit}{2})(?:[-\w:@&=+,.!~*'$_;]|%\p{XDigit}{2})*""".r
|
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 descendant’s path by appending all child names.
|
* ''Java API'': Recursively create a descendant’s path by appending all child names.
|
||||||
*/
|
*/
|
||||||
def descendant(names: java.lang.Iterable[String]): ActorPath = {
|
def descendant(names: java.lang.Iterable[String]): ActorPath = /(immutableSeq(names))
|
||||||
import scala.collection.JavaConverters._
|
|
||||||
/(names.asScala)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sequence of names for this path from root to this. Performance implication: has to allocate a list.
|
* 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.
|
* ''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] = {
|
def getElements: java.lang.Iterable[String] =
|
||||||
import scala.collection.JavaConverters._
|
scala.collection.JavaConverters.asJavaIterableConverter(elements).asJava
|
||||||
elements.asJava
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Walk up the tree to obtain and return the RootActorPath.
|
* 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 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
|
override val toString: String = address + name
|
||||||
|
|
||||||
|
|
@ -121,7 +120,7 @@ final case class RootActorPath(address: Address, name: String = "/") extends Act
|
||||||
else addr + name
|
else addr + name
|
||||||
|
|
||||||
override def compareTo(other: ActorPath): Int = other match {
|
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
|
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 /(child: String): ActorPath = new ChildActorPath(this, child)
|
||||||
|
|
||||||
override def elements: Iterable[String] = {
|
override def elements: immutable.Iterable[String] = {
|
||||||
@tailrec
|
@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 r: RootActorPath ⇒ acc
|
||||||
case _ ⇒ rec(p.parent, p.name :: acc)
|
case _ ⇒ rec(p.parent, p.name :: acc)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -153,7 +153,7 @@ trait ScalaActorRef { ref: ActorRef ⇒
|
||||||
* </pre>
|
* </pre>
|
||||||
* <p/>
|
* <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).
|
* Actor life-cycle management, invoked only internally (in response to user requests via ActorContext).
|
||||||
*/
|
*/
|
||||||
|
def start(): Unit
|
||||||
def resume(causedByFailure: Throwable): Unit
|
def resume(causedByFailure: Throwable): Unit
|
||||||
def suspend(): Unit
|
def suspend(): Unit
|
||||||
def restart(cause: Throwable): Unit
|
def restart(cause: Throwable): Unit
|
||||||
|
|
@ -259,13 +260,16 @@ private[akka] class LocalActorRef private[akka] (
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Safe publication of this class’s fields is guaranteed by mailbox.setActor()
|
* Safe publication of this class’s fields is guaranteed by mailbox.setActor()
|
||||||
* which is called indirectly from actorCell.start() (if you’re wondering why
|
* which is called indirectly from actorCell.init() (if you’re wondering why
|
||||||
* this is at all important, remember that under the JMM final fields are only
|
* 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
|
* frozen at the _end_ of the constructor, but we are publishing “this” before
|
||||||
* that is reached).
|
* 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)
|
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 =
|
protected def newActorCell(system: ActorSystemImpl, ref: InternalActorRef, props: Props, supervisor: InternalActorRef): ActorCell =
|
||||||
new ActorCell(system, ref, props, supervisor)
|
new ActorCell(system, ref, props, supervisor)
|
||||||
|
|
@ -279,6 +283,11 @@ private[akka] class LocalActorRef private[akka] (
|
||||||
*/
|
*/
|
||||||
override def isTerminated: Boolean = actorCell.isTerminated
|
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
|
* Suspends the actor so that it will not process messages until resumed. The
|
||||||
* suspend request is processed asynchronously to the caller of this method
|
* 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 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)
|
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 getParent: InternalActorRef = Nobody
|
||||||
override def getChild(names: Iterator[String]): InternalActorRef = if (names.forall(_.isEmpty)) this else 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 suspend(): Unit = ()
|
||||||
override def resume(causedByFailure: Throwable): Unit = ()
|
override def resume(causedByFailure: Throwable): Unit = ()
|
||||||
override def stop(): Unit = ()
|
override def stop(): Unit = ()
|
||||||
override def isTerminated = false
|
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 sendSystemMessage(message: SystemMessage): Unit = ()
|
||||||
override def restart(cause: Throwable): Unit = ()
|
override def restart(cause: Throwable): Unit = ()
|
||||||
|
|
@ -409,7 +419,10 @@ private[akka] trait MinimalActorRef extends InternalActorRef with LocalRef {
|
||||||
* to the ActorSystem's EventStream
|
* to the ActorSystem's EventStream
|
||||||
*/
|
*/
|
||||||
@SerialVersionUID(1L)
|
@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 {
|
private[akka] object DeadLetterActorRef {
|
||||||
@SerialVersionUID(1L)
|
@SerialVersionUID(1L)
|
||||||
|
|
@ -435,9 +448,12 @@ private[akka] class EmptyLocalActorRef(override val provider: ActorRefProvider,
|
||||||
|
|
||||||
override def sendSystemMessage(message: SystemMessage): Unit = specialHandle(message)
|
override def sendSystemMessage(message: SystemMessage): Unit = specialHandle(message)
|
||||||
|
|
||||||
override def !(message: Any)(implicit sender: ActorRef = null): Unit = message match {
|
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 d: DeadLetter ⇒
|
||||||
case _ ⇒ if (!specialHandle(message)) eventStream.publish(DeadLetter(message, sender, this))
|
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 {
|
protected def specialHandle(msg: Any): Boolean = msg match {
|
||||||
|
|
@ -520,7 +536,7 @@ private[akka] class VirtualPathContainer(
|
||||||
|
|
||||||
def hasChildren: Boolean = !children.isEmpty
|
def hasChildren: Boolean = !children.isEmpty
|
||||||
|
|
||||||
def foreachChild(f: ActorRef ⇒ Unit) = {
|
def foreachChild(f: ActorRef ⇒ Unit): Unit = {
|
||||||
val iter = children.values.iterator
|
val iter = children.values.iterator
|
||||||
while (iter.hasNext) f(iter.next)
|
while (iter.hasNext) f(iter.next)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,9 @@ import akka.dispatch._
|
||||||
import akka.routing._
|
import akka.routing._
|
||||||
import akka.event._
|
import akka.event._
|
||||||
import akka.util.{ Switch, Helpers }
|
import akka.util.{ Switch, Helpers }
|
||||||
|
import akka.japi.Util.immutableSeq
|
||||||
|
import akka.util.Collections.EmptyImmutableSeq
|
||||||
import scala.util.{ Success, Failure }
|
import scala.util.{ Success, Failure }
|
||||||
import scala.util.control.NonFatal
|
|
||||||
import scala.concurrent.{ Future, Promise }
|
import scala.concurrent.{ Future, Promise }
|
||||||
import java.util.concurrent.atomic.AtomicLong
|
import java.util.concurrent.atomic.AtomicLong
|
||||||
|
|
||||||
|
|
@ -41,8 +42,7 @@ trait ActorRefProvider {
|
||||||
def deadLetters: ActorRef
|
def deadLetters: ActorRef
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The root path for all actors within this actor system, including remote
|
* The root path for all actors within this actor system, not including any remote address information.
|
||||||
* address if enabled.
|
|
||||||
*/
|
*/
|
||||||
def rootPath: ActorPath
|
def rootPath: ActorPath
|
||||||
|
|
||||||
|
|
@ -145,6 +145,11 @@ trait ActorRefProvider {
|
||||||
* attempt is made to verify actual reachability).
|
* attempt is made to verify actual reachability).
|
||||||
*/
|
*/
|
||||||
def getExternalAddressFor(addr: Address): Option[Address]
|
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.
|
* For maximum performance use a collection with efficient head & tail operations.
|
||||||
*/
|
*/
|
||||||
def actorFor(path: java.lang.Iterable[String]): ActorRef = {
|
def actorFor(path: java.lang.Iterable[String]): ActorRef = provider.actorFor(lookupRoot, immutableSeq(path))
|
||||||
import scala.collection.JavaConverters._
|
|
||||||
provider.actorFor(lookupRoot, path.asScala)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct an [[akka.actor.ActorSelection]] from the given path, which is
|
* Construct an [[akka.actor.ActorSelection]] from the given path, which is
|
||||||
|
|
@ -319,6 +321,10 @@ private[akka] object SystemGuardian {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Local ActorRef provider.
|
* Local ActorRef provider.
|
||||||
|
*
|
||||||
|
* INTERNAL API!
|
||||||
|
*
|
||||||
|
* Depending on this class is not supported, only the [[ActorRefProvider]] interface is supported.
|
||||||
*/
|
*/
|
||||||
class LocalActorRefProvider(
|
class LocalActorRefProvider(
|
||||||
_systemName: String,
|
_systemName: String,
|
||||||
|
|
@ -375,7 +381,7 @@ class LocalActorRefProvider(
|
||||||
override def stop(): Unit = stopped switchOn { terminationPromise.complete(causeOfTermination.map(Failure(_)).getOrElse(Success(()))) }
|
override def stop(): Unit = stopped switchOn { terminationPromise.complete(causeOfTermination.map(Failure(_)).getOrElse(Success(()))) }
|
||||||
override def isTerminated: Boolean = stopped.isOn
|
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 Failed(ex, _) if sender ne null ⇒ causeOfTermination = Some(ex); sender.asInstanceOf[InternalActorRef].stop()
|
||||||
case NullMessage ⇒ // do nothing
|
case NullMessage ⇒ // do nothing
|
||||||
case _ ⇒ log.error(this + " received unexpected message [" + message + "]")
|
case _ ⇒ log.error(this + " received unexpected message [" + message + "]")
|
||||||
|
|
@ -383,7 +389,7 @@ class LocalActorRefProvider(
|
||||||
|
|
||||||
override def sendSystemMessage(message: SystemMessage): Unit = stopped ifOff {
|
override def sendSystemMessage(message: SystemMessage): Unit = stopped ifOff {
|
||||||
message match {
|
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 ChildTerminated(_) ⇒ stop()
|
||||||
case _ ⇒ log.error(this + " received unexpected system message [" + message + "]")
|
case _ ⇒ log.error(this + " received unexpected system message [" + message + "]")
|
||||||
}
|
}
|
||||||
|
|
@ -480,7 +486,7 @@ class LocalActorRefProvider(
|
||||||
def registerExtraNames(_extras: Map[String, InternalActorRef]): Unit = extraNames ++= _extras
|
def registerExtraNames(_extras: Map[String, InternalActorRef]): Unit = extraNames ++= _extras
|
||||||
|
|
||||||
private def guardianSupervisorStrategyConfigurator =
|
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.
|
* Overridable supervision strategy to be used by the “/user” guardian.
|
||||||
|
|
@ -516,6 +522,7 @@ class LocalActorRefProvider(
|
||||||
cell.reserveChild("user")
|
cell.reserveChild("user")
|
||||||
val ref = new LocalActorRef(system, Props(new Guardian(guardianStrategy)), rootGuardian, rootPath / "user")
|
val ref = new LocalActorRef(system, Props(new Guardian(guardianStrategy)), rootGuardian, rootPath / "user")
|
||||||
cell.initChild(ref)
|
cell.initChild(ref)
|
||||||
|
ref.start()
|
||||||
ref
|
ref
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -524,6 +531,7 @@ class LocalActorRefProvider(
|
||||||
cell.reserveChild("system")
|
cell.reserveChild("system")
|
||||||
val ref = new LocalActorRef(system, Props(new SystemGuardian(systemGuardianStrategy)), rootGuardian, rootPath / "system")
|
val ref = new LocalActorRef(system, Props(new SystemGuardian(systemGuardianStrategy)), rootGuardian, rootPath / "system")
|
||||||
cell.initChild(ref)
|
cell.initChild(ref)
|
||||||
|
ref.start()
|
||||||
ref
|
ref
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -585,16 +593,17 @@ class LocalActorRefProvider(
|
||||||
if (settings.DebugRouterMisconfiguration && deployer.lookup(path).isDefined)
|
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.")
|
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)
|
else new LocalActorRef(system, props, supervisor, path)
|
||||||
case router ⇒
|
case router ⇒
|
||||||
val lookup = if (lookupDeploy) deployer.lookup(path) else None
|
val lookup = if (lookupDeploy) deployer.lookup(path) else None
|
||||||
val fromProps = Iterator(props.deploy.copy(routerConfig = props.deploy.routerConfig withFallback router))
|
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 d = fromProps ++ deploy.iterator ++ lookup.iterator reduce ((a, b) ⇒ b withFallback a)
|
||||||
val ref = new RoutedActorRef(system, props.withRouter(d.routerConfig), supervisor, path).initialize()
|
new RoutedActorRef(system, props.withRouter(d.routerConfig), supervisor, path).initialize(async)
|
||||||
if (async) ref else ref.activate()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def getExternalAddressFor(addr: Address): Option[Address] = if (addr == rootPath.address) Some(addr) else None
|
def getExternalAddressFor(addr: Address): Option[Address] = if (addr == rootPath.address) Some(addr) else None
|
||||||
|
|
||||||
|
def getDefaultAddress: Address = rootPath.address
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ abstract class ActorSelection {
|
||||||
|
|
||||||
def tell(msg: Any, sender: ActorRef): Unit = target.tell(toMessage(msg, path), sender)
|
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 = {
|
private def toMessage(msg: Any, path: Array[AnyRef]): Any = {
|
||||||
var acc = msg
|
var acc = msg
|
||||||
var index = path.length - 1
|
var index = path.length - 1
|
||||||
|
|
@ -70,5 +69,5 @@ object ActorSelection {
|
||||||
trait ScalaActorSelection {
|
trait ScalaActorSelection {
|
||||||
this: ActorSelection ⇒
|
this: ActorSelection ⇒
|
||||||
|
|
||||||
def !(msg: Any)(implicit sender: ActorRef = null) = tell(msg, sender)
|
def !(msg: Any)(implicit sender: ActorRef = Actor.noSender) = tell(msg, sender)
|
||||||
}
|
}
|
||||||
|
|
@ -6,24 +6,24 @@ package akka.actor
|
||||||
|
|
||||||
import akka.event._
|
import akka.event._
|
||||||
import akka.dispatch._
|
import akka.dispatch._
|
||||||
import akka.pattern.ask
|
import akka.japi.Util.immutableSeq
|
||||||
import com.typesafe.config.{ Config, ConfigFactory }
|
import com.typesafe.config.{ Config, ConfigFactory }
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
import scala.concurrent.util.Duration
|
import scala.collection.immutable
|
||||||
import java.io.Closeable
|
import scala.concurrent.duration.{ FiniteDuration, Duration }
|
||||||
import scala.concurrent.{ Await, Awaitable, CanAwait, Future }
|
import scala.concurrent.{ Await, Awaitable, CanAwait, Future }
|
||||||
|
import scala.util.{ Failure, Success }
|
||||||
import scala.util.control.NonFatal
|
import scala.util.control.NonFatal
|
||||||
import akka.util._
|
import akka.util._
|
||||||
|
import java.io.Closeable
|
||||||
import akka.util.internal.{ HashedWheelTimer, ConcurrentIdentityHashMap }
|
import akka.util.internal.{ HashedWheelTimer, ConcurrentIdentityHashMap }
|
||||||
import java.util.concurrent.{ ThreadFactory, CountDownLatch, TimeoutException, RejectedExecutionException }
|
import java.util.concurrent.{ ThreadFactory, CountDownLatch, TimeoutException, RejectedExecutionException }
|
||||||
import java.util.concurrent.TimeUnit.MILLISECONDS
|
import java.util.concurrent.TimeUnit.MILLISECONDS
|
||||||
import akka.actor.dungeon.ChildrenContainer
|
import akka.actor.dungeon.ChildrenContainer
|
||||||
import scala.concurrent.util.FiniteDuration
|
|
||||||
import util.{ Failure, Success }
|
|
||||||
|
|
||||||
object ActorSystem {
|
object ActorSystem {
|
||||||
|
|
||||||
val Version: String = "2.1-SNAPSHOT"
|
val Version: String = "2.2-SNAPSHOT"
|
||||||
|
|
||||||
val EnvHome: Option[String] = System.getenv("AKKA_HOME") match {
|
val EnvHome: Option[String] = System.getenv("AKKA_HOME") match {
|
||||||
case null | "" | "." ⇒ None
|
case null | "" | "." ⇒ None
|
||||||
|
|
@ -144,7 +144,7 @@ object ActorSystem {
|
||||||
|
|
||||||
final val LogLevel: String = getString("akka.loglevel")
|
final val LogLevel: String = getString("akka.loglevel")
|
||||||
final val StdoutLogLevel: String = getString("akka.stdout-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 EventHandlerStartTimeout: Timeout = Timeout(Duration(getMilliseconds("akka.event-handler-startup-timeout"), MILLISECONDS))
|
||||||
final val LogConfigOnStart: Boolean = config.getBoolean("akka.log-config-on-start")
|
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 descendant’s path by appending all child names.
|
* ''Java API'': Recursively create a descendant’s path by appending all child names.
|
||||||
*/
|
*/
|
||||||
def descendant(names: java.lang.Iterable[String]): ActorPath = {
|
def descendant(names: java.lang.Iterable[String]): ActorPath = /(immutableSeq(names))
|
||||||
import scala.collection.JavaConverters._
|
|
||||||
/(names.asScala)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start-up time in milliseconds since the epoch.
|
* 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 scheduler: Scheduler = createScheduler()
|
||||||
|
|
||||||
val provider: ActorRefProvider = {
|
val provider: ActorRefProvider = {
|
||||||
val arguments = Seq(
|
val arguments = Vector(
|
||||||
classOf[String] -> name,
|
classOf[String] -> name,
|
||||||
classOf[Settings] -> settings,
|
classOf[Settings] -> settings,
|
||||||
classOf[EventStream] -> eventStream,
|
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
|
def hasExtension(ext: ExtensionId[_ <: Extension]): Boolean = findExtension(ext) != null
|
||||||
|
|
||||||
private def loadExtensions() {
|
private def loadExtensions() {
|
||||||
import scala.collection.JavaConversions._
|
immutableSeq(settings.config.getStringList("akka.extensions")) foreach { fqcn ⇒
|
||||||
settings.config.getStringList("akka.extensions") foreach { fqcn ⇒
|
dynamicAccess.getObjectFor[AnyRef](fqcn) recoverWith { case _ ⇒ dynamicAccess.createInstanceFor[AnyRef](fqcn, Nil) } match {
|
||||||
dynamicAccess.getObjectFor[AnyRef](fqcn) recoverWith { case _ ⇒ dynamicAccess.createInstanceFor[AnyRef](fqcn, Seq()) } match {
|
|
||||||
case Success(p: ExtensionIdProvider) ⇒ registerExtension(p.lookup())
|
case Success(p: ExtensionIdProvider) ⇒ registerExtension(p.lookup())
|
||||||
case Success(p: ExtensionId[_]) ⇒ registerExtension(p)
|
case Success(p: ExtensionId[_]) ⇒ registerExtension(p)
|
||||||
case Success(other) ⇒ log.error("[{}] is not an 'ExtensionIdProvider' or 'ExtensionId', skipping...", fqcn)
|
case Success(other) ⇒ log.error("[{}] is not an 'ExtensionIdProvider' or 'ExtensionId', skipping...", fqcn)
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,8 @@ package akka.actor
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.net.URISyntaxException
|
import java.net.URISyntaxException
|
||||||
import java.net.MalformedURLException
|
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
|
* The address specifies the physical location under which an Actor can be
|
||||||
|
|
@ -71,11 +72,11 @@ private[akka] trait PathUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
object RelativeActorPath extends PathUtils {
|
object RelativeActorPath extends PathUtils {
|
||||||
def unapply(addr: String): Option[Iterable[String]] = {
|
def unapply(addr: String): Option[immutable.Seq[String]] = {
|
||||||
try {
|
try {
|
||||||
val uri = new URI(addr)
|
val uri = new URI(addr)
|
||||||
if (uri.isAbsolute) None
|
if (uri.isAbsolute) None
|
||||||
else Some(split(uri.getPath))
|
else Some(split(uri.getRawPath))
|
||||||
} catch {
|
} catch {
|
||||||
case _: URISyntaxException ⇒ None
|
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
|
* Given an ActorPath it returns the Address and the path elements if the path is well-formed
|
||||||
*/
|
*/
|
||||||
object ActorPathExtractor extends PathUtils {
|
object ActorPathExtractor extends PathUtils {
|
||||||
def unapply(addr: String): Option[(Address, Iterable[String])] =
|
def unapply(addr: String): Option[(Address, immutable.Iterable[String])] =
|
||||||
try {
|
try {
|
||||||
val uri = new URI(addr)
|
val uri = new URI(addr)
|
||||||
if (uri.getPath == null) None
|
uri.getRawPath match {
|
||||||
else AddressFromURIString.unapply(uri) match {
|
case null ⇒ None
|
||||||
case None ⇒ None
|
case path ⇒ AddressFromURIString.unapply(uri).map((_, split(path).drop(1)))
|
||||||
case Some(addr) ⇒ Some((addr, split(uri.getPath).drop(1)))
|
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
case _: URISyntaxException ⇒ None
|
case _: URISyntaxException ⇒ None
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,14 @@
|
||||||
|
|
||||||
package akka.actor
|
package akka.actor
|
||||||
|
|
||||||
import scala.concurrent.util.Duration
|
import scala.concurrent.duration.Duration
|
||||||
import com.typesafe.config._
|
import com.typesafe.config._
|
||||||
import akka.routing._
|
import akka.routing._
|
||||||
|
import akka.japi.Util.immutableSeq
|
||||||
import java.util.concurrent.{ TimeUnit }
|
import java.util.concurrent.{ TimeUnit }
|
||||||
import akka.util.WildcardTree
|
import akka.util.WildcardTree
|
||||||
import java.util.concurrent.atomic.AtomicReference
|
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
|
* This class represents deployment configuration for a given actor path. It is
|
||||||
|
|
@ -79,7 +80,11 @@ trait Scope {
|
||||||
@SerialVersionUID(1L)
|
@SerialVersionUID(1L)
|
||||||
abstract class LocalScope extends Scope
|
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 {
|
case object LocalScope extends LocalScope {
|
||||||
/**
|
/**
|
||||||
* Java API: get the singleton instance
|
* 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] = {
|
def parseConfig(key: String, config: Config): Option[Deploy] = {
|
||||||
|
|
||||||
val deployment = config.withFallback(default)
|
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 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
|
routerType match {
|
||||||
|
|
||||||
val router: RouterConfig = deployment.getString("router") match {
|
|
||||||
case "from-code" ⇒ NoRouter
|
case "from-code" ⇒ NoRouter
|
||||||
case "round-robin" ⇒ RoundRobinRouter(nrOfInstances, routees, resizer)
|
case "round-robin" ⇒ RoundRobinRouter(nrOfInstances, routees, resizer)
|
||||||
case "random" ⇒ RandomRouter(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")
|
val vnodes = deployment.getInt("virtual-nodes-factor")
|
||||||
ConsistentHashingRouter(nrOfInstances, routees, resizer, virtualNodesFactor = vnodes)
|
ConsistentHashingRouter(nrOfInstances, routees, resizer, virtualNodesFactor = vnodes)
|
||||||
case fqn ⇒
|
case fqn ⇒
|
||||||
val args = Seq(classOf[Config] -> deployment)
|
val args = List(classOf[Config] -> deployment)
|
||||||
dynamicAccess.createInstanceFor[RouterConfig](fqn, args).recover({
|
dynamicAccess.createInstanceFor[RouterConfig](fqn, args).recover({
|
||||||
case exception ⇒ throw new IllegalArgumentException(
|
case exception ⇒ throw new IllegalArgumentException(
|
||||||
("Cannot instantiate router [%s], defined in [%s], " +
|
("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)
|
.format(fqn, key), exception)
|
||||||
}).get
|
}).get
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(Deploy(key, deployment, router, NoScopeGiven))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
*/
|
*/
|
||||||
package akka.actor
|
package akka.actor
|
||||||
|
|
||||||
import scala.util.control.NonFatal
|
import scala.collection.immutable
|
||||||
import java.lang.reflect.InvocationTargetException
|
import java.lang.reflect.InvocationTargetException
|
||||||
import scala.reflect.ClassTag
|
import scala.reflect.ClassTag
|
||||||
import scala.util.Try
|
import scala.util.Try
|
||||||
|
|
@ -25,7 +25,7 @@ abstract class DynamicAccess {
|
||||||
* val obj = DynamicAccess.createInstanceFor(clazz, Seq(classOf[Config] -> config, classOf[String] -> name))
|
* 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
|
* 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,
|
* `args` argument. The exact usage of args depends on which type is requested,
|
||||||
* see the relevant requesting code for details.
|
* 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.
|
* 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)
|
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 {
|
Try {
|
||||||
val types = args.map(_._1).toArray
|
val types = args.map(_._1).toArray
|
||||||
val values = args.map(_._2).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)
|
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 }
|
} 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) }
|
getClassFor(fqcn) flatMap { c ⇒ createInstanceFor(c, args) }
|
||||||
|
|
||||||
override def getObjectFor[T: ClassTag](fqcn: String): Try[T] = {
|
override def getObjectFor[T: ClassTag](fqcn: String): Try[T] = {
|
||||||
|
|
|
||||||
|
|
@ -98,5 +98,5 @@ abstract class ExtensionKey[T <: Extension](implicit m: ClassTag[T]) extends Ext
|
||||||
def this(clazz: Class[T]) = this()(ClassTag(clazz))
|
def this(clazz: Class[T]) = this()(ClassTag(clazz))
|
||||||
|
|
||||||
override def lookup(): ExtensionId[T] = this
|
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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,10 @@ package akka.actor
|
||||||
|
|
||||||
import language.implicitConversions
|
import language.implicitConversions
|
||||||
import akka.util._
|
import akka.util._
|
||||||
import scala.concurrent.util.Duration
|
import scala.concurrent.duration.Duration
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
import akka.routing.{ Deafen, Listen, Listeners }
|
import akka.routing.{ Deafen, Listen, Listeners }
|
||||||
import scala.concurrent.util.FiniteDuration
|
import scala.concurrent.duration.FiniteDuration
|
||||||
|
|
||||||
object FSM {
|
object FSM {
|
||||||
|
|
||||||
|
|
@ -238,7 +238,7 @@ object FSM {
|
||||||
* setTimer("tock", TockMsg, 1 second, true) // repeating
|
* setTimer("tock", TockMsg, 1 second, true) // repeating
|
||||||
* setTimer("lifetime", TerminateMsg, 1 hour, false) // single-shot
|
* setTimer("lifetime", TerminateMsg, 1 hour, false) // single-shot
|
||||||
* cancelTimer("tock")
|
* cancelTimer("tock")
|
||||||
* timerActive_? ("tock")
|
* isTimerActive("tock")
|
||||||
* </pre>
|
* </pre>
|
||||||
*/
|
*/
|
||||||
trait FSM[S, D] extends Listeners with ActorLogging {
|
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
|
* timer does not exist, has previously been canceled or if it was a
|
||||||
* single-shot timer whose message was already received.
|
* 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
|
* 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
|
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
|
* 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
|
* 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
|
* Set handler which is called upon reception of unhandled messages. Calling
|
||||||
* this method again will overwrite the previous contents.
|
* this method again will overwrite the previous contents.
|
||||||
|
*
|
||||||
|
* The current state may be queried using ``stateName``.
|
||||||
*/
|
*/
|
||||||
final def whenUnhandled(stateFunction: StateFunction): Unit =
|
final def whenUnhandled(stateFunction: StateFunction): Unit =
|
||||||
handleEvent = stateFunction orElse handleEventDefault
|
handleEvent = stateFunction orElse handleEventDefault
|
||||||
|
|
@ -519,7 +534,7 @@ trait FSM[S, D] extends Listeners with ActorLogging {
|
||||||
* Main actor receive() method
|
* Main actor receive() method
|
||||||
* *******************************************
|
* *******************************************
|
||||||
*/
|
*/
|
||||||
override final def receive: Receive = {
|
override def receive: Receive = {
|
||||||
case TimeoutMarker(gen) ⇒
|
case TimeoutMarker(gen) ⇒
|
||||||
if (generation == gen) {
|
if (generation == gen) {
|
||||||
processMsg(StateTimeout, "state timeout")
|
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 Failure(msg: AnyRef) ⇒ log.error(msg.toString)
|
||||||
case _ ⇒
|
case _ ⇒
|
||||||
}
|
}
|
||||||
|
for (timer ← timers.values) timer.cancel()
|
||||||
|
timers.clear()
|
||||||
val stopEvent = StopEvent(reason, currentState.stateName, currentState.stateData)
|
val stopEvent = StopEvent(reason, currentState.stateName, currentState.stateData)
|
||||||
if (terminateEvent.isDefinedAt(stopEvent))
|
if (terminateEvent.isDefinedAt(stopEvent))
|
||||||
terminateEvent(stopEvent)
|
terminateEvent(stopEvent)
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,13 @@ package akka.actor
|
||||||
|
|
||||||
import language.implicitConversions
|
import language.implicitConversions
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
import scala.collection.mutable.ArrayBuffer
|
|
||||||
import scala.collection.JavaConversions._
|
|
||||||
import java.lang.{ Iterable ⇒ JIterable }
|
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
|
* INTERNAL API
|
||||||
*/
|
*/
|
||||||
|
|
@ -171,7 +173,7 @@ object SupervisorStrategy extends SupervisorStrategyLowPriorityImplicits {
|
||||||
* Implicit conversion from `Seq` of Throwables to a `Decider`.
|
* Implicit conversion from `Seq` of Throwables to a `Decider`.
|
||||||
* This maps the given Throwables to restarts, otherwise escalates.
|
* 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 Decider = PartialFunction[Throwable, Directive]
|
||||||
type JDecider = akka.japi.Function[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
|
* Decider builder which just checks whether one of
|
||||||
* the given Throwables matches the cause and restarts, otherwise escalates.
|
* the given Throwables matches the cause and restarts, otherwise escalates.
|
||||||
*/
|
*/
|
||||||
def makeDecider(trapExit: Array[Class[_]]): Decider =
|
def makeDecider(trapExit: immutable.Seq[Class[_ <: Throwable]]): Decider = {
|
||||||
{ case x ⇒ if (trapExit exists (_ isInstance x)) Restart else Escalate }
|
case x ⇒ if (trapExit exists (_ isInstance x)) Restart else Escalate
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decider builder which just checks whether one of
|
* Decider builder which just checks whether one of
|
||||||
* the given Throwables matches the cause and restarts, otherwise escalates.
|
* the given Throwables matches the cause and restarts, otherwise escalates.
|
||||||
*/
|
*/
|
||||||
def makeDecider(trapExit: Seq[Class[_ <: Throwable]]): Decider =
|
def makeDecider(trapExit: JIterable[Class[_ <: Throwable]]): Decider = makeDecider(immutableSeq(trapExit))
|
||||||
{ 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)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decider builder for Iterables of cause-directive pairs, e.g. a map obtained
|
* Decider builder for Iterables of cause-directive pairs, e.g. a map obtained
|
||||||
|
|
@ -220,20 +216,22 @@ object SupervisorStrategy extends SupervisorStrategyLowPriorityImplicits {
|
||||||
*
|
*
|
||||||
* INTERNAL API
|
* 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) ⇒
|
(new ArrayBuffer[CauseDirective](in.size) /: in) { (buf, ca) ⇒
|
||||||
buf.indexWhere(_._1 isAssignableFrom ca._1) match {
|
buf.indexWhere(_._1 isAssignableFrom ca._1) match {
|
||||||
case -1 ⇒ buf append ca
|
case -1 ⇒ buf append ca
|
||||||
case x ⇒ buf insert (x, ca)
|
case x ⇒ buf insert (x, ca)
|
||||||
}
|
}
|
||||||
buf
|
buf
|
||||||
}
|
}.to[immutable.IndexedSeq]
|
||||||
|
|
||||||
private[akka] def withinTimeRangeOption(withinTimeRange: Duration): Option[Duration] =
|
private[akka] def withinTimeRangeOption(withinTimeRange: Duration): Option[Duration] =
|
||||||
if (withinTimeRange.isFinite && withinTimeRange >= Duration.Zero) Some(withinTimeRange) else None
|
if (withinTimeRange.isFinite && withinTimeRange >= Duration.Zero) Some(withinTimeRange) else None
|
||||||
|
|
||||||
private[akka] def maxNrOfRetriesOption(maxNrOfRetries: Int): Option[Int] =
|
private[akka] def maxNrOfRetriesOption(maxNrOfRetries: Int): Option[Int] =
|
||||||
if (maxNrOfRetries < 0) None else Some(maxNrOfRetries)
|
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)
|
* @param children is a lazy collection (a view)
|
||||||
*/
|
*/
|
||||||
def handleFailure(context: ActorContext, child: ActorRef, cause: Throwable, stats: ChildRestartStats, children: Iterable[ChildRestartStats]): Boolean = {
|
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 {
|
directive match {
|
||||||
case Resume ⇒ resumeChild(child, cause); true
|
case Resume ⇒ resumeChild(child, cause); true
|
||||||
case Restart ⇒ processFailure(context, true, child, cause, stats, children); 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]]) =
|
def this(maxNrOfRetries: Int, withinTimeRange: Duration, trapExit: JIterable[Class[_ <: Throwable]]) =
|
||||||
this(maxNrOfRetries, withinTimeRange)(SupervisorStrategy.makeDecider(trapExit))
|
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
|
* this is a performance optimization to avoid re-allocating the pairs upon
|
||||||
* every call to requestRestartPermission, assuming that strategies are shared
|
* 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]]) =
|
def this(maxNrOfRetries: Int, withinTimeRange: Duration, trapExit: JIterable[Class[_ <: Throwable]]) =
|
||||||
this(maxNrOfRetries, withinTimeRange)(SupervisorStrategy.makeDecider(trapExit))
|
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
|
* this is a performance optimization to avoid re-allocating the pairs upon
|
||||||
* every call to requestRestartPermission, assuming that strategies are shared
|
* every call to requestRestartPermission, assuming that strategies are shared
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,9 @@ package akka.actor
|
||||||
import language.higherKinds
|
import language.higherKinds
|
||||||
import language.postfixOps
|
import language.postfixOps
|
||||||
|
|
||||||
|
import scala.collection.immutable
|
||||||
import scala.concurrent.{ ExecutionContext, Future }
|
import scala.concurrent.{ ExecutionContext, Future }
|
||||||
import scala.concurrent.util.Duration
|
import scala.concurrent.duration.Duration
|
||||||
import scala.util.control.NonFatal
|
import scala.util.control.NonFatal
|
||||||
import akka.util.ByteString
|
import akka.util.ByteString
|
||||||
import java.net.{ SocketAddress, InetSocketAddress }
|
import java.net.{ SocketAddress, InetSocketAddress }
|
||||||
|
|
@ -122,7 +123,7 @@ object IO {
|
||||||
* @return a new SocketHandle that can be used to perform actions on the
|
* @return a new SocketHandle that can be used to perform actions on the
|
||||||
* new connection's SocketChannel.
|
* 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)
|
val socket = SocketHandle(socketOwner, ioManager)
|
||||||
ioManager ! Accept(socket, this, options)
|
ioManager ! Accept(socket, this, options)
|
||||||
socket
|
socket
|
||||||
|
|
@ -250,7 +251,7 @@ object IO {
|
||||||
*
|
*
|
||||||
* Normally sent using IOManager.listen()
|
* 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
|
* Message from an [[akka.actor.IOManager]] that the ServerSocketChannel is
|
||||||
|
|
@ -272,7 +273,7 @@ object IO {
|
||||||
*
|
*
|
||||||
* Normally sent using [[akka.actor.IO.ServerHandle]].accept()
|
* 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
|
* Message to an [[akka.actor.IOManager]] to create a SocketChannel connected
|
||||||
|
|
@ -280,7 +281,7 @@ object IO {
|
||||||
*
|
*
|
||||||
* Normally sent using IOManager.connect()
|
* 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
|
* 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
|
* @param option Seq of [[akka.actor.IO.ServerSocketOptions]] to setup on socket
|
||||||
* @return a [[akka.actor.IO.ServerHandle]] to uniquely identify the created 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)
|
val server = IO.ServerHandle(owner, actor)
|
||||||
actor ! IO.Listen(server, address, options)
|
actor ! IO.Listen(server, address, options)
|
||||||
server
|
server
|
||||||
|
|
@ -847,7 +848,7 @@ final class IOManager private (system: ExtendedActorSystem) extends Extension {
|
||||||
* @param owner the ActorRef that will receive messages from the IOManagerActor
|
* @param owner the ActorRef that will receive messages from the IOManagerActor
|
||||||
* @return a [[akka.actor.IO.ServerHandle]] to uniquely identify the created socket
|
* @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
|
* 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
|
* @param owner the ActorRef that will receive messages from the IOManagerActor
|
||||||
* @return a [[akka.actor.IO.ServerHandle]] to uniquely identify the created socket
|
* @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)
|
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
|
* @param owner the ActorRef that will receive messages from the IOManagerActor
|
||||||
* @return a [[akka.actor.IO.SocketHandle]] to uniquely identify the created socket
|
* @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)
|
val socket = IO.SocketHandle(owner, actor)
|
||||||
actor ! IO.Connect(socket, address, options)
|
actor ! IO.Connect(socket, address, options)
|
||||||
socket
|
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 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 {
|
options foreach {
|
||||||
case IO.KeepAlive(on) ⇒ forwardFailure(socket.setKeepAlive(on))
|
case IO.KeepAlive(on) ⇒ forwardFailure(socket.setKeepAlive(on))
|
||||||
case IO.OOBInline(on) ⇒ forwardFailure(socket.setOOBInline(on))
|
case IO.OOBInline(on) ⇒ forwardFailure(socket.setOOBInline(on))
|
||||||
|
|
|
||||||
|
|
@ -5,17 +5,18 @@
|
||||||
package akka.actor
|
package akka.actor
|
||||||
|
|
||||||
import java.io.ObjectStreamException
|
import java.io.ObjectStreamException
|
||||||
|
import java.util.{ LinkedList ⇒ JLinkedList, ListIterator ⇒ JListIterator }
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import java.util.concurrent.locks.ReentrantLock
|
import java.util.concurrent.locks.ReentrantLock
|
||||||
|
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
import scala.collection.mutable.Queue
|
|
||||||
import scala.concurrent.forkjoin.ThreadLocalRandom
|
import scala.concurrent.forkjoin.ThreadLocalRandom
|
||||||
|
|
||||||
import akka.actor.dungeon.ChildrenContainer
|
import akka.actor.dungeon.ChildrenContainer
|
||||||
import akka.dispatch.{ Envelope, Supervise, SystemMessage, Terminate }
|
|
||||||
import akka.event.Logging.Warning
|
import akka.event.Logging.Warning
|
||||||
import akka.util.Unsafe
|
import akka.util.Unsafe
|
||||||
|
import akka.dispatch._
|
||||||
|
import util.Try
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This actor ref starts out with some dummy cell (by default just enqueuing
|
* This actor ref starts out with some dummy cell (by default just enqueuing
|
||||||
|
|
@ -32,17 +33,34 @@ private[akka] class RepointableActorRef(
|
||||||
val path: ActorPath)
|
val path: ActorPath)
|
||||||
extends ActorRefWithCell with RepointableRef {
|
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 _cellDoNotCallMeDirectly: Cell = _
|
||||||
|
@volatile private var _lookupDoNotCallMeDirectly: Cell = _
|
||||||
|
|
||||||
def underlying: Cell = Unsafe.instance.getObjectVolatile(this, cellOffset).asInstanceOf[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 = {
|
@tailrec final def swapCell(next: Cell): Cell = {
|
||||||
val old = underlying
|
val old = underlying
|
||||||
if (Unsafe.instance.compareAndSwapObject(this, cellOffset, old, next)) old else swapCell(next)
|
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
|
* 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
|
* supervisor that we exist so that he can create the real Cell in
|
||||||
|
|
@ -52,12 +70,17 @@ private[akka] class RepointableActorRef(
|
||||||
*
|
*
|
||||||
* This is protected so that others can have different initialization.
|
* This is protected so that others can have different initialization.
|
||||||
*/
|
*/
|
||||||
def initialize(): this.type = {
|
def initialize(async: Boolean): this.type =
|
||||||
val uid = ThreadLocalRandom.current.nextInt()
|
underlying match {
|
||||||
swapCell(new UnstartedCell(system, this, props, supervisor, uid))
|
case null ⇒
|
||||||
supervisor.sendSystemMessage(Supervise(this, uid))
|
val uid = ThreadLocalRandom.current.nextInt()
|
||||||
this
|
swapCell(new UnstartedCell(system, this, props, supervisor, uid))
|
||||||
}
|
swapLookup(underlying)
|
||||||
|
supervisor.sendSystemMessage(Supervise(this, async, uid))
|
||||||
|
if (!async) point()
|
||||||
|
this
|
||||||
|
case other ⇒ throw new IllegalStateException("initialize called more than once!")
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is supposed to be called by the supervisor in handleSupervise()
|
* This method is supposed to be called by the supervisor in handleSupervise()
|
||||||
|
|
@ -65,21 +88,33 @@ private[akka] class RepointableActorRef(
|
||||||
* modification of the `underlying` field, though it is safe to send messages
|
* modification of the `underlying` field, though it is safe to send messages
|
||||||
* at any time.
|
* at any time.
|
||||||
*/
|
*/
|
||||||
def activate(): this.type = {
|
def point(): this.type =
|
||||||
underlying match {
|
underlying match {
|
||||||
case u: UnstartedCell ⇒ u.replaceWith(newCell(u))
|
case u: UnstartedCell ⇒
|
||||||
case _ ⇒ // this happens routinely for things which were created async=false
|
/*
|
||||||
|
* 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
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is called by activate() to obtain the cell which is to replace the
|
* This is called by activate() to obtain the cell which is to replace the
|
||||||
* unstarted cell. The cell must be fully functional.
|
* unstarted cell. The cell must be fully functional.
|
||||||
*/
|
*/
|
||||||
def newCell(old: Cell): Cell =
|
def newCell(old: UnstartedCell): Cell =
|
||||||
new ActorCell(system, this, props, supervisor)
|
new ActorCell(system, this, props, supervisor).init(old.uid, sendSupervise = false)
|
||||||
.start(sendSupervise = false, old.asInstanceOf[UnstartedCell].uid)
|
|
||||||
|
def start(): Unit = ()
|
||||||
|
|
||||||
def suspend(): Unit = underlying.suspend()
|
def suspend(): Unit = underlying.suspend()
|
||||||
|
|
||||||
|
|
@ -89,7 +124,11 @@ private[akka] class RepointableActorRef(
|
||||||
|
|
||||||
def restart(cause: Throwable): Unit = underlying.restart(cause)
|
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
|
def isTerminated: Boolean = underlying.isTerminated
|
||||||
|
|
||||||
|
|
@ -105,14 +144,14 @@ private[akka] class RepointableActorRef(
|
||||||
case ".." ⇒ getParent.getChild(name)
|
case ".." ⇒ getParent.getChild(name)
|
||||||
case "" ⇒ getChild(name)
|
case "" ⇒ getChild(name)
|
||||||
case other ⇒
|
case other ⇒
|
||||||
underlying.getChildByName(other) match {
|
lookup.getChildByName(other) match {
|
||||||
case Some(crs: ChildRestartStats) ⇒ crs.child.asInstanceOf[InternalActorRef].getChild(name)
|
case Some(crs: ChildRestartStats) ⇒ crs.child.asInstanceOf[InternalActorRef].getChild(name)
|
||||||
case _ ⇒ Nobody
|
case _ ⇒ Nobody
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else this
|
} 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)
|
def sendSystemMessage(message: SystemMessage) = underlying.sendSystemMessage(message)
|
||||||
|
|
||||||
|
|
@ -120,116 +159,116 @@ private[akka] class RepointableActorRef(
|
||||||
protected def writeReplace(): AnyRef = SerializedActorRef(path)
|
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)
|
private[akka] class UnstartedCell(val systemImpl: ActorSystemImpl,
|
||||||
extends Cell {
|
val self: RepointableActorRef,
|
||||||
|
val props: Props,
|
||||||
|
val supervisor: InternalActorRef,
|
||||||
|
val uid: Int) extends Cell {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This lock protects all accesses to this cell’s queues. It also ensures
|
* This lock protects all accesses to this cell’s queues. It also ensures
|
||||||
* safe switching to the started ActorCell.
|
* 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
|
// use Envelope to keep on-send checks in the same place ACCESS MUST BE PROTECTED BY THE LOCK
|
||||||
val queue: Queue[Envelope] = Queue()
|
private[this] final val queue = new JLinkedList[Any]()
|
||||||
val systemQueue: Queue[SystemMessage] = Queue()
|
|
||||||
var suspendCount: Int = 0
|
|
||||||
|
|
||||||
private def timeout = system.settings.UnstartedPushTimeout.duration.toMillis
|
import systemImpl.settings.UnstartedPushTimeout.{ duration ⇒ timeout }
|
||||||
|
|
||||||
def replaceWith(cell: Cell): Unit = {
|
def replaceWith(cell: Cell): Unit = locked {
|
||||||
lock.lock()
|
|
||||||
try {
|
try {
|
||||||
/*
|
while (!queue.isEmpty) {
|
||||||
* The CallingThreadDispatcher nicely dives under the ReentrantLock and
|
queue.poll() match {
|
||||||
* breaks things by enqueueing into stale queues from within the message
|
case s: SystemMessage ⇒ cell.sendSystemMessage(s)
|
||||||
* processing which happens in-line for sendSystemMessage() and tell().
|
case e: Envelope ⇒ cell.tell(e.message, e.sender)
|
||||||
* 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 we’re 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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally try
|
} finally {
|
||||||
self.swapCell(cell)
|
self.swapCell(cell)
|
||||||
finally try
|
}
|
||||||
for (_ ← 1 to suspendCount) cell.suspend()
|
|
||||||
finally
|
|
||||||
lock.unlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def system: ActorSystem = systemImpl
|
def system: ActorSystem = systemImpl
|
||||||
def suspend(): Unit = {
|
def start(): this.type = this
|
||||||
lock.lock()
|
def suspend(): Unit = sendSystemMessage(Suspend())
|
||||||
try suspendCount += 1
|
def resume(causedByFailure: Throwable): Unit = sendSystemMessage(Resume(causedByFailure))
|
||||||
finally lock.unlock()
|
def restart(cause: Throwable): Unit = sendSystemMessage(Recreate(cause))
|
||||||
}
|
|
||||||
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 stop(): Unit = sendSystemMessage(Terminate())
|
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 parent: InternalActorRef = supervisor
|
||||||
def childrenRefs: ChildrenContainer = ChildrenContainer.EmptyChildrenContainer
|
def childrenRefs: ChildrenContainer = ChildrenContainer.EmptyChildrenContainer
|
||||||
def getChildByName(name: String): Option[ChildRestartStats] = None
|
def getChildByName(name: String): Option[ChildRestartStats] = None
|
||||||
|
|
||||||
def tell(message: Any, sender: ActorRef): Unit = {
|
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 {
|
try {
|
||||||
if (self.underlying eq this) queue enqueue Envelope(message, sender, system)
|
val cell = self.underlying
|
||||||
else self.underlying.tell(message, sender)
|
if (cellIsReady(cell)) {
|
||||||
} finally {
|
cell.tell(message, useSender)
|
||||||
lock.unlock()
|
} 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 {
|
} else {
|
||||||
system.deadLetters ! DeadLetter(message, sender, self)
|
system.eventStream.publish(Warning(self.path.toString, getClass, "dropping message of type" + message.getClass + " due to lock timeout"))
|
||||||
}
|
system.deadLetters ! DeadLetter(message, useSender, 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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,17 +4,18 @@
|
||||||
|
|
||||||
package akka.actor
|
package akka.actor
|
||||||
|
|
||||||
import scala.concurrent.util.Duration
|
import scala.concurrent.duration.Duration
|
||||||
import akka.util.internal.{ TimerTask, HashedWheelTimer, Timeout ⇒ HWTimeout, Timer }
|
import akka.util.internal.{ TimerTask, HashedWheelTimer, Timeout ⇒ HWTimeout, Timer }
|
||||||
import akka.event.LoggingAdapter
|
import akka.event.LoggingAdapter
|
||||||
import akka.dispatch.MessageDispatcher
|
import akka.dispatch.MessageDispatcher
|
||||||
import java.io.Closeable
|
import java.io.Closeable
|
||||||
import java.util.concurrent.atomic.AtomicReference
|
import java.util.concurrent.atomic.{ AtomicReference, AtomicLong }
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
import akka.util.internal._
|
import akka.util.internal._
|
||||||
import concurrent.ExecutionContext
|
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
|
//#scheduler
|
||||||
/**
|
/**
|
||||||
* An Akka scheduler service. This one needs one special behavior: if
|
* An Akka scheduler service. This one needs one special behavior: if
|
||||||
|
|
@ -50,7 +51,8 @@ trait Scheduler {
|
||||||
*/
|
*/
|
||||||
def schedule(
|
def schedule(
|
||||||
initialDelay: FiniteDuration,
|
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
|
* Schedules a function to be run repeatedly with an initial delay and
|
||||||
|
|
@ -93,7 +95,8 @@ trait Scheduler {
|
||||||
* Scala API
|
* Scala API
|
||||||
*/
|
*/
|
||||||
def scheduleOnce(
|
def scheduleOnce(
|
||||||
delay: FiniteDuration)(f: ⇒ Unit)(implicit executor: ExecutionContext): Cancellable
|
delay: FiniteDuration)(f: ⇒ Unit)(
|
||||||
|
implicit executor: ExecutionContext): Cancellable
|
||||||
}
|
}
|
||||||
//#scheduler
|
//#scheduler
|
||||||
|
|
||||||
|
|
@ -137,14 +140,17 @@ class DefaultScheduler(hashedWheelTimer: HashedWheelTimer, log: LoggingAdapter)
|
||||||
val continuousCancellable = new ContinuousCancellable
|
val continuousCancellable = new ContinuousCancellable
|
||||||
continuousCancellable.init(
|
continuousCancellable.init(
|
||||||
hashedWheelTimer.newTimeout(
|
hashedWheelTimer.newTimeout(
|
||||||
new TimerTask with ContinuousScheduling {
|
new AtomicLong(System.nanoTime + initialDelay.toNanos) with TimerTask with ContinuousScheduling {
|
||||||
def run(timeout: HWTimeout) {
|
def run(timeout: HWTimeout) {
|
||||||
executor execute new Runnable {
|
executor execute new Runnable {
|
||||||
override def run = {
|
override def run = {
|
||||||
receiver ! message
|
receiver ! message
|
||||||
// Check if the receiver is still alive and kicking before reschedule the task
|
// 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)
|
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
|
val continuousCancellable = new ContinuousCancellable
|
||||||
continuousCancellable.init(
|
continuousCancellable.init(
|
||||||
hashedWheelTimer.newTimeout(
|
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(timeout: HWTimeout): Unit = executor.execute(new Runnable {
|
||||||
override def run = {
|
override def run = {
|
||||||
runnable.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 = {
|
override def close(): Unit = {
|
||||||
import scala.collection.JavaConverters._
|
val i = hashedWheelTimer.stop().iterator()
|
||||||
hashedWheelTimer.stop().asScala foreach execDirectly
|
while (i.hasNext) execDirectly(i.next())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,13 +16,13 @@ import akka.AkkaException
|
||||||
* def receive = {
|
* def receive = {
|
||||||
* case "open" ⇒
|
* case "open" ⇒
|
||||||
* unstashAll()
|
* unstashAll()
|
||||||
* context.become {
|
* context.become({
|
||||||
* case "write" ⇒ // do writing...
|
* case "write" ⇒ // do writing...
|
||||||
* case "close" ⇒
|
* case "close" ⇒
|
||||||
* unstashAll()
|
* unstashAll()
|
||||||
* context.unbecome()
|
* context.unbecome()
|
||||||
* case msg ⇒ stash()
|
* case msg ⇒ stash()
|
||||||
* }
|
* }, discardOld = false)
|
||||||
* case "done" ⇒ // done
|
* case "done" ⇒ // done
|
||||||
* case msg ⇒ stash()
|
* case msg ⇒ stash()
|
||||||
* }
|
* }
|
||||||
|
|
|
||||||
|
|
@ -4,22 +4,25 @@
|
||||||
package akka.actor
|
package akka.actor
|
||||||
|
|
||||||
import language.existentials
|
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.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 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.util.Reflect.instantiator
|
||||||
|
import akka.serialization.{ JavaSerializer, SerializationExtension }
|
||||||
import akka.dispatch._
|
import akka.dispatch._
|
||||||
import java.util.concurrent.atomic.{ AtomicReference ⇒ AtomVar }
|
import java.util.concurrent.atomic.{ AtomicReference ⇒ AtomVar }
|
||||||
import java.util.concurrent.TimeoutException
|
import java.util.concurrent.TimeoutException
|
||||||
import java.util.concurrent.TimeUnit.MILLISECONDS
|
import java.util.concurrent.TimeUnit.MILLISECONDS
|
||||||
import scala.reflect.ClassTag
|
|
||||||
import akka.serialization.{ JavaSerializer, SerializationExtension }
|
|
||||||
import java.io.ObjectStreamException
|
import java.io.ObjectStreamException
|
||||||
import scala.util.{ Try, Success, Failure }
|
import java.lang.reflect.{ InvocationTargetException, Method, InvocationHandler, Proxy }
|
||||||
import scala.concurrent.util.FiniteDuration
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A TypedActorFactory is something that can created TypedActor instances.
|
* 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,
|
* @return a sequence of interfaces that the specified class implements,
|
||||||
* or a sequence containing only itself, if itself is an interface.
|
* or a sequence containing only itself, if itself is an interface.
|
||||||
*/
|
*/
|
||||||
def extractInterfaces(clazz: Class[_]): Seq[Class[_]] =
|
def extractInterfaces(clazz: Class[_]): immutable.Seq[Class[_]] =
|
||||||
if (clazz.isInterface) Seq[Class[_]](clazz) else clazz.getInterfaces.toList
|
if (clazz.isInterface) immutableSingletonSeq(clazz) else immutableSeq(clazz.getInterfaces)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Uses the supplied class as the factory for the TypedActor implementation,
|
* Uses the supplied class as the factory for the TypedActor implementation,
|
||||||
|
|
@ -489,7 +492,7 @@ object TypedProps {
|
||||||
*/
|
*/
|
||||||
@SerialVersionUID(1L)
|
@SerialVersionUID(1L)
|
||||||
case class TypedProps[T <: AnyRef] protected[TypedProps] (
|
case class TypedProps[T <: AnyRef] protected[TypedProps] (
|
||||||
interfaces: Seq[Class[_]],
|
interfaces: immutable.Seq[Class[_]],
|
||||||
creator: () ⇒ T,
|
creator: () ⇒ T,
|
||||||
dispatcher: String = TypedProps.defaultDispatcherId,
|
dispatcher: String = TypedProps.defaultDispatcherId,
|
||||||
deploy: Deploy = Props.defaultDeploy,
|
deploy: Deploy = Props.defaultDeploy,
|
||||||
|
|
@ -607,8 +610,7 @@ class TypedActorExtension(system: ExtendedActorSystem) extends TypedActorFactory
|
||||||
protected def actorFactory: ActorRefFactory = system
|
protected def actorFactory: ActorRefFactory = system
|
||||||
protected def typedActor = this
|
protected def typedActor = this
|
||||||
|
|
||||||
val serialization = SerializationExtension(system)
|
import system.settings
|
||||||
val settings = system.settings
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default timeout for typed actor methods with non-void return type
|
* Default timeout for typed actor methods with non-void return type
|
||||||
|
|
@ -635,23 +637,18 @@ class TypedActorExtension(system: ExtendedActorSystem) extends TypedActorFactory
|
||||||
private[akka] def createActorRefProxy[R <: AnyRef, T <: R](props: TypedProps[T], proxyVar: AtomVar[R], actorRef: ⇒ ActorRef): R = {
|
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
|
//Warning, do not change order of the following statements, it's some elaborate chicken-n-egg handling
|
||||||
val actorVar = new AtomVar[ActorRef](null)
|
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(
|
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,
|
props.interfaces.toArray,
|
||||||
new TypedActorInvocationHandler(
|
new TypedActorInvocationHandler(this, actorVar, props.timeout getOrElse DefaultReturnTimeout)).asInstanceOf[R]
|
||||||
this,
|
|
||||||
actorVar,
|
|
||||||
if (props.timeout.isDefined) props.timeout.get else DefaultReturnTimeout)).asInstanceOf[R]
|
|
||||||
|
|
||||||
proxyVar match {
|
if (proxyVar eq null) {
|
||||||
case null ⇒
|
actorVar set actorRef
|
||||||
actorVar.set(actorRef)
|
proxy
|
||||||
proxy
|
} else {
|
||||||
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
|
||||||
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
|
||||||
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
|
||||||
proxyVar.get
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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>() {
|
* new Function<Throwable, Directive>() {
|
||||||
* @Override
|
* @Override
|
||||||
* public Directive apply(Throwable t) {
|
* public Directive apply(Throwable t) {
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,8 @@ package akka.actor.dsl
|
||||||
|
|
||||||
import scala.concurrent.Await
|
import scala.concurrent.Await
|
||||||
import akka.actor.ActorLogging
|
import akka.actor.ActorLogging
|
||||||
import scala.concurrent.util.Deadline
|
|
||||||
import scala.collection.immutable.TreeSet
|
import scala.collection.immutable.TreeSet
|
||||||
import scala.concurrent.util.{ Duration, FiniteDuration }
|
import scala.concurrent.duration._
|
||||||
import scala.concurrent.util.duration._
|
|
||||||
import akka.actor.Cancellable
|
import akka.actor.Cancellable
|
||||||
import akka.actor.{ Actor, Stash, SupervisorStrategy }
|
import akka.actor.{ Actor, Stash, SupervisorStrategy }
|
||||||
import scala.collection.mutable.Queue
|
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
|
* for quickly trying things out in the REPL. It makes the following keywords
|
||||||
* available:
|
* 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`
|
* - `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
|
* stack is cleared upon restart. Use `unbecome()` to pop an element off
|
||||||
* this stack.
|
* 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
|
* Pop the active behavior from the behavior stack of this actor. This stack
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,8 @@ package akka.actor.dsl
|
||||||
|
|
||||||
import scala.concurrent.Await
|
import scala.concurrent.Await
|
||||||
import akka.actor.ActorLogging
|
import akka.actor.ActorLogging
|
||||||
import scala.concurrent.util.Deadline
|
|
||||||
import scala.collection.immutable.TreeSet
|
import scala.collection.immutable.TreeSet
|
||||||
import scala.concurrent.util.{ Duration, FiniteDuration }
|
import scala.concurrent.duration._
|
||||||
import scala.concurrent.util.duration._
|
|
||||||
import akka.actor.Cancellable
|
import akka.actor.Cancellable
|
||||||
import akka.actor.Actor
|
import akka.actor.Actor
|
||||||
import scala.collection.mutable.Queue
|
import scala.collection.mutable.Queue
|
||||||
|
|
@ -129,10 +127,10 @@ trait Inbox { this: ActorDSL.type ⇒
|
||||||
val next = clientsByTimeout.head.deadline
|
val next = clientsByTimeout.head.deadline
|
||||||
import context.dispatcher
|
import context.dispatcher
|
||||||
if (currentDeadline.isEmpty) {
|
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) {
|
} else if (currentDeadline.get._1 != next) {
|
||||||
currentDeadline.get._2.cancel()
|
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>
|
* this method within an actor!</b>
|
||||||
*/
|
*/
|
||||||
def receive(timeout: FiniteDuration = defaultTimeout): Any = {
|
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)
|
Await.result(receiver ? Get(Deadline.now + timeout), Duration.Inf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -186,7 +184,7 @@ trait Inbox { this: ActorDSL.type ⇒
|
||||||
* this method within an actor!</b>
|
* this method within an actor!</b>
|
||||||
*/
|
*/
|
||||||
def select[T](timeout: FiniteDuration = defaultTimeout)(predicate: PartialFunction[Any, T]): T = {
|
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))
|
predicate(Await.result(receiver ? Select(Deadline.now + timeout, predicate), Duration.Inf))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,14 +5,12 @@
|
||||||
package akka.actor.dungeon
|
package akka.actor.dungeon
|
||||||
|
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
import scala.collection.JavaConverters.asJavaIterableConverter
|
|
||||||
import scala.util.control.NonFatal
|
import scala.util.control.NonFatal
|
||||||
|
import scala.collection.immutable
|
||||||
import akka.actor._
|
import akka.actor._
|
||||||
import akka.actor.ActorCell
|
|
||||||
import akka.actor.ActorPath.ElementRegex
|
import akka.actor.ActorPath.ElementRegex
|
||||||
import akka.serialization.SerializationExtension
|
import akka.serialization.SerializationExtension
|
||||||
import akka.util.{ Unsafe, Helpers }
|
import akka.util.{ Unsafe, Helpers }
|
||||||
import akka.actor.ChildNameReserved
|
|
||||||
|
|
||||||
private[akka] trait Children { this: ActorCell ⇒
|
private[akka] trait Children { this: ActorCell ⇒
|
||||||
|
|
||||||
|
|
@ -24,8 +22,9 @@ private[akka] trait Children { this: ActorCell ⇒
|
||||||
def childrenRefs: ChildrenContainer =
|
def childrenRefs: ChildrenContainer =
|
||||||
Unsafe.instance.getObjectVolatile(this, AbstractActorCell.childrenOffset).asInstanceOf[ChildrenContainer]
|
Unsafe.instance.getObjectVolatile(this, AbstractActorCell.childrenOffset).asInstanceOf[ChildrenContainer]
|
||||||
|
|
||||||
final def children: Iterable[ActorRef] = childrenRefs.children
|
final def children: immutable.Iterable[ActorRef] = childrenRefs.children
|
||||||
final def getChildren(): java.lang.Iterable[ActorRef] = children.asJava
|
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 child(name: String): Option[ActorRef] = Option(getChild(name))
|
||||||
final def getChild(name: String): ActorRef = childrenRefs.getByName(name) match {
|
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 = {
|
final def stop(actor: ActorRef): Unit = {
|
||||||
val started = actor match {
|
if (childrenRefs.getByRef(actor).isDefined) {
|
||||||
case r: RepointableRef ⇒ r.isStarted
|
@tailrec def shallDie(ref: ActorRef): Boolean = {
|
||||||
case _ ⇒ true
|
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()
|
actor.asInstanceOf[InternalActorRef].stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* low level CAS helpers
|
* low level CAS helpers
|
||||||
*/
|
*/
|
||||||
|
@inline private final def swapChildrenRefs(oldChildren: ChildrenContainer, newChildren: ChildrenContainer): Boolean =
|
||||||
@inline private def swapChildrenRefs(oldChildren: ChildrenContainer, newChildren: ChildrenContainer): Boolean =
|
|
||||||
Unsafe.instance.compareAndSwapObject(this, AbstractActorCell.childrenOffset, oldChildren, newChildren)
|
Unsafe.instance.compareAndSwapObject(this, AbstractActorCell.childrenOffset, oldChildren, newChildren)
|
||||||
|
|
||||||
@tailrec final def reserveChild(name: String): Boolean = {
|
@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 = {
|
@tailrec final protected def setChildrenTerminationReason(reason: ChildrenContainer.SuspendReason): Boolean = {
|
||||||
childrenRefs match {
|
childrenRefs match {
|
||||||
case c: ChildrenContainer.TerminatingChildrenContainer ⇒
|
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 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] = {
|
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) ⇒
|
case TerminatingChildrenContainer(_, _, reason) ⇒
|
||||||
val newContainer = removeChild(child)
|
removeChild(child) match {
|
||||||
if (!newContainer.isInstanceOf[TerminatingChildrenContainer]) Some(reason) else None
|
case _: TerminatingChildrenContainer ⇒ None
|
||||||
|
case _ ⇒ Some(reason)
|
||||||
|
}
|
||||||
case _ ⇒
|
case _ ⇒
|
||||||
removeChild(child)
|
removeChild(child)
|
||||||
None
|
None
|
||||||
|
|
@ -192,6 +192,7 @@ private[akka] trait Children { this: ActorCell ⇒
|
||||||
// mailbox==null during RoutedActorCell constructor, where suspends are queued otherwise
|
// mailbox==null during RoutedActorCell constructor, where suspends are queued otherwise
|
||||||
if (mailbox ne null) for (_ ← 1 to mailbox.suspendCount) actor.suspend()
|
if (mailbox ne null) for (_ ← 1 to mailbox.suspendCount) actor.suspend()
|
||||||
initChild(actor)
|
initChild(actor)
|
||||||
|
actor.start()
|
||||||
actor
|
actor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,11 @@
|
||||||
|
|
||||||
package akka.actor.dungeon
|
package akka.actor.dungeon
|
||||||
|
|
||||||
import scala.collection.immutable.TreeMap
|
import scala.collection.immutable
|
||||||
|
|
||||||
import akka.actor.{ InvalidActorNameException, ChildStats, ChildRestartStats, ChildNameReserved, ActorRef }
|
import akka.actor.{ InvalidActorNameException, ChildStats, ChildRestartStats, ChildNameReserved, ActorRef }
|
||||||
import akka.dispatch.SystemMessage
|
import akka.dispatch.SystemMessage
|
||||||
|
import akka.util.Collections.{ EmptyImmutableSeq, PartialImmutableValuesIterable }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* INTERNAL API
|
* INTERNAL API
|
||||||
|
|
@ -20,8 +21,8 @@ private[akka] trait ChildrenContainer {
|
||||||
def getByName(name: String): Option[ChildStats]
|
def getByName(name: String): Option[ChildStats]
|
||||||
def getByRef(actor: ActorRef): Option[ChildRestartStats]
|
def getByRef(actor: ActorRef): Option[ChildRestartStats]
|
||||||
|
|
||||||
def children: Iterable[ActorRef]
|
def children: immutable.Iterable[ActorRef]
|
||||||
def stats: Iterable[ChildRestartStats]
|
def stats: immutable.Iterable[ChildRestartStats]
|
||||||
|
|
||||||
def shallDie(actor: ActorRef): ChildrenContainer
|
def shallDie(actor: ActorRef): ChildrenContainer
|
||||||
|
|
||||||
|
|
@ -49,6 +50,18 @@ private[akka] object ChildrenContainer {
|
||||||
case class Creation() extends SuspendReason with WaitingForChildren
|
case class Creation() extends SuspendReason with WaitingForChildren
|
||||||
case object Termination extends SuspendReason
|
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 {
|
trait WaitingForChildren {
|
||||||
private var todo: SystemMessage = null
|
private var todo: SystemMessage = null
|
||||||
def enqueue(message: SystemMessage) = { message.next = todo; todo = message }
|
def enqueue(message: SystemMessage) = { message.next = todo; todo = message }
|
||||||
|
|
@ -56,13 +69,13 @@ private[akka] object ChildrenContainer {
|
||||||
}
|
}
|
||||||
|
|
||||||
trait EmptyChildrenContainer extends 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 add(name: String, stats: ChildRestartStats): ChildrenContainer = new NormalChildrenContainer(emptyStats.updated(name, stats))
|
||||||
override def remove(child: ActorRef): ChildrenContainer = this
|
override def remove(child: ActorRef): ChildrenContainer = this
|
||||||
override def getByName(name: String): Option[ChildRestartStats] = None
|
override def getByName(name: String): Option[ChildRestartStats] = None
|
||||||
override def getByRef(actor: ActorRef): Option[ChildRestartStats] = None
|
override def getByRef(actor: ActorRef): Option[ChildRestartStats] = None
|
||||||
override def children: Iterable[ActorRef] = Nil
|
override def children: immutable.Iterable[ActorRef] = EmptyImmutableSeq
|
||||||
override def stats: Iterable[ChildRestartStats] = Nil
|
override def stats: immutable.Iterable[ChildRestartStats] = EmptyImmutableSeq
|
||||||
override def shallDie(actor: ActorRef): ChildrenContainer = this
|
override def shallDie(actor: ActorRef): ChildrenContainer = this
|
||||||
override def reserve(name: String): ChildrenContainer = new NormalChildrenContainer(emptyStats.updated(name, ChildNameReserved))
|
override def reserve(name: String): ChildrenContainer = new NormalChildrenContainer(emptyStats.updated(name, ChildNameReserved))
|
||||||
override def unreserve(name: String): ChildrenContainer = this
|
override def unreserve(name: String): ChildrenContainer = this
|
||||||
|
|
@ -95,7 +108,7 @@ private[akka] object ChildrenContainer {
|
||||||
* calling context.stop(child) and processing the ChildTerminated() system
|
* calling context.stop(child) and processing the ChildTerminated() system
|
||||||
* message).
|
* 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))
|
override def add(name: String, stats: ChildRestartStats): ChildrenContainer = new NormalChildrenContainer(c.updated(name, stats))
|
||||||
|
|
||||||
|
|
@ -108,9 +121,11 @@ private[akka] object ChildrenContainer {
|
||||||
case _ ⇒ None
|
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)
|
override def shallDie(actor: ActorRef): ChildrenContainer = TerminatingChildrenContainer(c, Set(actor), UserRequest)
|
||||||
|
|
||||||
|
|
@ -130,7 +145,7 @@ private[akka] object ChildrenContainer {
|
||||||
}
|
}
|
||||||
|
|
||||||
object NormalChildrenContainer {
|
object NormalChildrenContainer {
|
||||||
def apply(c: TreeMap[String, ChildStats]): ChildrenContainer =
|
def apply(c: immutable.TreeMap[String, ChildStats]): ChildrenContainer =
|
||||||
if (c.isEmpty) EmptyChildrenContainer
|
if (c.isEmpty) EmptyChildrenContainer
|
||||||
else new NormalChildrenContainer(c)
|
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
|
* type of container, depending on whether or not children are left and whether or not
|
||||||
* the reason was “Terminating”.
|
* 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 {
|
extends ChildrenContainer {
|
||||||
|
|
||||||
override def add(name: String, stats: ChildRestartStats): ChildrenContainer = copy(c.updated(name, stats))
|
override def add(name: String, stats: ChildRestartStats): ChildrenContainer = copy(c.updated(name, stats))
|
||||||
|
|
@ -166,9 +181,11 @@ private[akka] object ChildrenContainer {
|
||||||
case _ ⇒ None
|
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)
|
override def shallDie(actor: ActorRef): ChildrenContainer = copy(toDie = toDie + actor)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,12 +38,11 @@ private[akka] trait Dispatch { this: ActorCell ⇒
|
||||||
final def isTerminated: Boolean = mailbox.isClosed
|
final def isTerminated: Boolean = mailbox.isClosed
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start this cell, i.e. attach it to the dispatcher. The UID must reasonably
|
* Initialize this cell, i.e. set up mailboxes and supervision. The UID must be
|
||||||
* be different from the previous UID of a possible actor with the same path,
|
* reasonably different from the previous UID of a possible actor with the same path,
|
||||||
* which can be achieved by using ThreadLocalRandom.current.nextInt().
|
* 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
|
* Create the mailbox and enqueue the Create() message to ensure that
|
||||||
* this is processed before anything else.
|
* this is processed before anything else.
|
||||||
|
|
@ -56,13 +55,18 @@ private[akka] trait Dispatch { this: ActorCell ⇒
|
||||||
|
|
||||||
if (sendSupervise) {
|
if (sendSupervise) {
|
||||||
// ➡➡➡ NEVER SEND THE SAME SYSTEM MESSAGE OBJECT TO TWO ACTORS ⬅⬅⬅
|
// ➡➡➡ 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
|
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.
|
// This call is expected to start off the actor by scheduling its mailbox.
|
||||||
dispatcher.attach(this)
|
dispatcher.attach(this)
|
||||||
|
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,13 +10,13 @@ import akka.dispatch._
|
||||||
import akka.event.Logging.{ Warning, Error, Debug }
|
import akka.event.Logging.{ Warning, Error, Debug }
|
||||||
import scala.util.control.NonFatal
|
import scala.util.control.NonFatal
|
||||||
import akka.event.Logging
|
import akka.event.Logging
|
||||||
import scala.Some
|
import scala.collection.immutable
|
||||||
import akka.dispatch.ChildTerminated
|
import akka.dispatch.ChildTerminated
|
||||||
import akka.actor.PreRestartException
|
import akka.actor.PreRestartException
|
||||||
import akka.actor.Failed
|
import akka.actor.Failed
|
||||||
import akka.actor.PostRestartException
|
import akka.actor.PostRestartException
|
||||||
import akka.event.Logging.Debug
|
import akka.event.Logging.Debug
|
||||||
import scala.concurrent.util.Duration
|
import scala.concurrent.duration.Duration
|
||||||
|
|
||||||
private[akka] trait FaultHandling { this: ActorCell ⇒
|
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))
|
publish(Error(t, self.path.toString, clazz(actor), message))
|
||||||
// prevent any further messages to be processed until the actor has been restarted
|
// prevent any further messages to be processed until the actor has been restarted
|
||||||
if (!isFailed) try {
|
if (!isFailed) try {
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@ import ReceiveTimeout.emptyReceiveTimeoutData
|
||||||
import akka.actor.ActorCell
|
import akka.actor.ActorCell
|
||||||
import akka.actor.ActorCell.emptyCancellable
|
import akka.actor.ActorCell.emptyCancellable
|
||||||
import akka.actor.Cancellable
|
import akka.actor.Cancellable
|
||||||
import scala.concurrent.util.Duration
|
import scala.concurrent.duration.Duration
|
||||||
import scala.concurrent.util.FiniteDuration
|
import scala.concurrent.duration.FiniteDuration
|
||||||
|
|
||||||
private[akka] object ReceiveTimeout {
|
private[akka] object ReceiveTimeout {
|
||||||
final val emptyReceiveTimeoutData: (Duration, Cancellable) = (Duration.Undefined, ActorCell.emptyCancellable)
|
final val emptyReceiveTimeoutData: (Duration, Cancellable) = (Duration.Undefined, ActorCell.emptyCancellable)
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,10 @@ import akka.serialization.SerializationExtension
|
||||||
import akka.util.{ Unsafe, Index }
|
import akka.util.{ Unsafe, Index }
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
import scala.concurrent.forkjoin.{ ForkJoinTask, ForkJoinPool }
|
import scala.concurrent.forkjoin.{ ForkJoinTask, ForkJoinPool }
|
||||||
import scala.concurrent.util.Duration
|
import scala.concurrent.duration.Duration
|
||||||
import scala.concurrent.{ ExecutionContext, Await, Awaitable }
|
import scala.concurrent.{ ExecutionContext, Await, Awaitable }
|
||||||
import scala.util.control.NonFatal
|
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)
|
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
|
* 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
|
* INTERNAL API
|
||||||
*/
|
*/
|
||||||
|
|
@ -288,7 +288,7 @@ abstract class MessageDispatcher(val prerequisites: DispatcherPrerequisites) ext
|
||||||
if (debug) actors.remove(this, actor.self)
|
if (debug) actors.remove(this, actor.self)
|
||||||
addInhabitants(-1)
|
addInhabitants(-1)
|
||||||
val mailBox = actor.swapMailbox(deadLetterMailbox)
|
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()
|
mailBox.cleanUp()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -420,7 +420,7 @@ abstract class MessageDispatcherConfigurator(val config: Config, val prerequisit
|
||||||
case "unbounded" ⇒ UnboundedMailbox()
|
case "unbounded" ⇒ UnboundedMailbox()
|
||||||
case "bounded" ⇒ new BoundedMailbox(prerequisites.settings, config)
|
case "bounded" ⇒ new BoundedMailbox(prerequisites.settings, config)
|
||||||
case fqcn ⇒
|
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({
|
prerequisites.dynamicAccess.createInstanceFor[MailboxType](fqcn, args).recover({
|
||||||
case exception ⇒
|
case exception ⇒
|
||||||
throw new IllegalArgumentException(
|
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 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 "thread-pool-executor" ⇒ new ThreadPoolExecutorConfigurator(config.getConfig("thread-pool-executor"), prerequisites)
|
||||||
case fqcn ⇒
|
case fqcn ⇒
|
||||||
val args = Seq(
|
val args = List(
|
||||||
classOf[Config] -> config,
|
classOf[Config] -> config,
|
||||||
classOf[DispatcherPrerequisites] -> prerequisites)
|
classOf[DispatcherPrerequisites] -> prerequisites)
|
||||||
prerequisites.dynamicAccess.createInstanceFor[ExecutorServiceConfigurator](fqcn, args).recover({
|
prerequisites.dynamicAccess.createInstanceFor[ExecutorServiceConfigurator](fqcn, args).recover({
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,12 @@ package akka.dispatch
|
||||||
|
|
||||||
import akka.actor.{ ActorCell, ActorRef }
|
import akka.actor.{ ActorCell, ActorRef }
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
import scala.concurrent.util.Duration
|
import scala.concurrent.duration.Duration
|
||||||
import akka.util.Helpers
|
import akka.util.Helpers
|
||||||
import java.util.{ Comparator, Iterator }
|
import java.util.{ Comparator, Iterator }
|
||||||
import java.util.concurrent.{ Executor, LinkedBlockingQueue, ConcurrentLinkedQueue, ConcurrentSkipListSet }
|
import java.util.concurrent.{ Executor, LinkedBlockingQueue, ConcurrentLinkedQueue, ConcurrentSkipListSet }
|
||||||
import akka.actor.ActorSystemImpl
|
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
|
* An executor based event driven dispatcher which will try to redistribute work from busy actors to idle actors. It is assumed
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,9 @@ import akka.event.Logging
|
||||||
import java.util.concurrent.atomic.AtomicReference
|
import java.util.concurrent.atomic.AtomicReference
|
||||||
import java.util.concurrent.{ ExecutorService, RejectedExecutionException }
|
import java.util.concurrent.{ ExecutorService, RejectedExecutionException }
|
||||||
import scala.concurrent.forkjoin.ForkJoinPool
|
import scala.concurrent.forkjoin.ForkJoinPool
|
||||||
import scala.concurrent.util.Duration
|
import scala.concurrent.duration.Duration
|
||||||
import scala.concurrent.Awaitable
|
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
|
* 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 = {
|
protected[akka] def shutdown: Unit = {
|
||||||
val newDelegate = executorServiceDelegate.copy() // Doesn't matter which one we copy
|
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
|
val service = executorServiceDelegate
|
||||||
executorServiceDelegate = newDelegate // just a quick getAndSet
|
executorServiceDelegate = newDelegate // just a quick getAndSet
|
||||||
service
|
service
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import com.typesafe.config.{ ConfigFactory, Config }
|
||||||
import akka.actor.{ Scheduler, DynamicAccess, ActorSystem }
|
import akka.actor.{ Scheduler, DynamicAccess, ActorSystem }
|
||||||
import akka.event.Logging.Warning
|
import akka.event.Logging.Warning
|
||||||
import akka.event.EventStream
|
import akka.event.EventStream
|
||||||
import scala.concurrent.util.Duration
|
import scala.concurrent.duration.Duration
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DispatcherPrerequisites represents useful contextual pieces when constructing a MessageDispatcher
|
* 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 "BalancingDispatcher" ⇒ new BalancingDispatcherConfigurator(cfg, prerequisites)
|
||||||
case "PinnedDispatcher" ⇒ new PinnedDispatcherConfigurator(cfg, prerequisites)
|
case "PinnedDispatcher" ⇒ new PinnedDispatcherConfigurator(cfg, prerequisites)
|
||||||
case fqn ⇒
|
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({
|
prerequisites.dynamicAccess.createInstanceFor[MessageDispatcherConfigurator](fqn, args).recover({
|
||||||
case exception ⇒
|
case exception ⇒
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ object ExecutionContexts {
|
||||||
* Futures is the Java API for Futures and Promises
|
* Futures is the Java API for Futures and Promises
|
||||||
*/
|
*/
|
||||||
object Futures {
|
object Futures {
|
||||||
|
import scala.collection.JavaConverters.iterableAsScalaIterableConverter
|
||||||
/**
|
/**
|
||||||
* Java API, equivalent to Future.apply
|
* 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]] = {
|
def find[T <: AnyRef](futures: JIterable[Future[T]], predicate: JFunc[T, java.lang.Boolean], executor: ExecutionContext): Future[JOption[T]] = {
|
||||||
implicit val ec = executor
|
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
|
* 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] =
|
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
|
* Java API
|
||||||
|
|
@ -113,14 +113,14 @@ object Futures {
|
||||||
* or the result of the fold.
|
* 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] =
|
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.
|
* Java API.
|
||||||
* Reduces the results of the supplied futures and binary function.
|
* 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] =
|
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.
|
* Java API.
|
||||||
|
|
@ -129,9 +129,7 @@ object Futures {
|
||||||
*/
|
*/
|
||||||
def sequence[A](in: JIterable[Future[A]], executor: ExecutionContext): Future[JIterable[A]] = {
|
def sequence[A](in: JIterable[Future[A]], executor: ExecutionContext): Future[JIterable[A]] = {
|
||||||
implicit val d = executor
|
implicit val d = executor
|
||||||
scala.collection.JavaConversions.iterableAsScalaIterable(in).foldLeft(Future(new JLinkedList[A]())) { (fr, fa) ⇒
|
in.asScala.foldLeft(Future(new JLinkedList[A]())) { (fr, fa) ⇒ for (r ← fr; a ← fa) yield { r add a; r } }
|
||||||
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]] = {
|
def traverse[A, B](in: JIterable[A], fn: JFunc[A, Future[B]], executor: ExecutionContext): Future[JIterable[B]] = {
|
||||||
implicit val d = executor
|
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)
|
val fb = fn(a)
|
||||||
for (r ← fr; b ← fb) yield { r add b; r }
|
for (r ← fr; b ← fb) yield { r add b; r }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,11 @@ import akka.AkkaException
|
||||||
import akka.actor.{ ActorCell, ActorRef, Cell, ActorSystem, InternalActorRef, DeadLetter }
|
import akka.actor.{ ActorCell, ActorRef, Cell, ActorSystem, InternalActorRef, DeadLetter }
|
||||||
import akka.util.{ Unsafe, BoundedBlockingQueue }
|
import akka.util.{ Unsafe, BoundedBlockingQueue }
|
||||||
import akka.event.Logging.Error
|
import akka.event.Logging.Error
|
||||||
import scala.concurrent.util.Duration
|
import scala.concurrent.duration.Duration
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
import scala.util.control.NonFatal
|
import scala.util.control.NonFatal
|
||||||
import com.typesafe.config.Config
|
import com.typesafe.config.Config
|
||||||
import scala.concurrent.util.FiniteDuration
|
import scala.concurrent.duration.FiniteDuration
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* INTERNAL API
|
* INTERNAL API
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue