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/build/target
|
||||
*/project/boot
|
||||
*/project/project.target.config-classes
|
||||
lib_managed
|
||||
etags
|
||||
tags
|
||||
|
|
@ -67,3 +68,4 @@ redis/
|
|||
beanstalk/
|
||||
.scalastyle
|
||||
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](www.typesafe.com/contribute/cla)
|
||||
* [Akka Contributor License Agreement](http://www.typesafe.com/contribute/cla)
|
||||
* [Akka Issue Tracker](http://doc.akka.io/docs/akka/current/project/issue-tracking.html)
|
||||
* [Scalariform](https://github.com/mdr/scalariform)
|
||||
|
||||
##Workflow##
|
||||
# Typesafe Project & Developer Guidelines
|
||||
|
||||
0. Sign the Akka Contributor License Agreement,
|
||||
we won't accept anything from anybody who has not signed it.
|
||||
1. Find-or-create a ticket in the issue tracker
|
||||
2. Assign that ticket to yourself
|
||||
3. Create a local branch with the following name format: wip-X-Y-Z
|
||||
where the X is the number of the ticket in the tracker,
|
||||
and Y is some brief keywords of the ticket title and Z is your initials or similar.
|
||||
Example: wip-2373-add-contributing-md-√
|
||||
4. Do what needs to be done (with tests and docs if applicable).
|
||||
Your branch should pass all tests before going any further.
|
||||
5. Push the branch to your clone of the Akka repository
|
||||
6. Create a Pull Request onto the applicable Akka branch,
|
||||
if the number of commits are more than a few, please squash the
|
||||
commits first.
|
||||
7. Change the status of your ticket to "Test"
|
||||
8. The Pull Request will be reviewed by the Akka committers
|
||||
9. Modify the Pull Request as agreed upon during the review,
|
||||
then push the changes to your branch in your Akka repository,
|
||||
the Pull Request should be automatically updated with the new
|
||||
content.
|
||||
10. Several cycles of review-then-change might occur.
|
||||
11. Pull Request is either merged by the Akka committers,
|
||||
or rejected, and the associated ticket will be updated to
|
||||
reflect that.
|
||||
12. Delete the local and remote wip-X-Y-Z
|
||||
These guidelines are meant to be a living document that should be changed and adapted as needed. We encourage changes that makes it easier to achieve our goals in an efficient way.
|
||||
|
||||
##Code Reviews##
|
||||
These guidelines mainly applies to 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,
|
||||
increase maintainability and spread knowledge about how things are solved.
|
||||
## General Workflow
|
||||
|
||||
Core review values:
|
||||
This is the process for committing code into master. There are of course exceptions to these rules, for example minor changes to comments and documentation, fixing a broken build etc.
|
||||
|
||||
* Rule: [The Boy Scout Rule](http://programmer.97things.oreilly.com/wiki/index.php/The_Boy_Scout_Rule)
|
||||
- Why: Small improvements add up over time, keeping the codebase in shape.
|
||||
* Rule: [Don't Repeat Yourself](http://programmer.97things.oreilly.com/wiki/index.php/Don't_Repeat_Yourself)
|
||||
- Why: Repetitions are not maintainable, keeping things DRY makes it easier to fix bugs and refactor,
|
||||
since you only need to apply the correction in one place, or perform the refactoring at one place.
|
||||
* Rule: Feature tests > Integration tests > Unit tests
|
||||
- Why: Without proving that a feature works, the code is only liability.
|
||||
Without proving that a feature works with other features, the code is of limited value.
|
||||
Without proving the individual parts of a feature works, the code is harder to debug.
|
||||
1. Make sure you have signed the [Typesafe CLA](http://www.typesafe.com/contribute/cla), if not, sign it online.
|
||||
2. Before starting to work on a feature or a fix, you have to make sure that:
|
||||
1. There is a ticket for your work in the project's issue tracker. If not, create it first.
|
||||
2. The ticket has been scheduled for the current milestone.
|
||||
3. The ticket is estimated by the team.
|
||||
4. The ticket have been discussed and prioritized by the team.
|
||||
3. You should always perform your work in a Git feature branch. The branch should be given a descriptive name that explains its intent. Some teams also like adding the ticket number and/or the [GitHub](http://github.com) user ID to the branch name, these details is up to each of the individual teams.
|
||||
4. When the feature or fix is completed you should open a [Pull Request](https://help.github.com/articles/using-pull-requests) on GitHub.
|
||||
5. The Pull Request should be reviewed by other maintainers (as many as feasible/practical). Note that the maintainers can consist of outside contributors, both within and outside Typesafe. Outside contributors (for example from EPFL or independent committers) are encouraged to participate in the review process, it is not a closed process.
|
||||
6. After the review you should fix the issues as needed (pushing a new commit for new review etc.), iterating until the reviewers give their thumbs up.
|
||||
7. Once the code has passed review the Pull Request can be merged into the master branch.
|
||||
|
||||
##Source style##
|
||||
## Pull Request Requirements
|
||||
|
||||
For a Pull Request to be considered at all it has to meet these requirements:
|
||||
|
||||
1. Live up to the current code standard:
|
||||
- Not violate [DRY](http://programmer.97things.oreilly.com/wiki/index.php/Don%27t_Repeat_Yourself).
|
||||
- [Boy Scout Rule](http://programmer.97things.oreilly.com/wiki/index.php/The_Boy_Scout_Rule) needs to have been applied.
|
||||
2. Regardless if the code introduces new features or fixes bugs or regressions, it must have comprehensive tests.
|
||||
3. The code must be well documented in the Typesafe's standard documentation format (see the ‘Documentation’ section below).
|
||||
|
||||
If these requirements are not met then the code should **not** be merged into master, or even reviewed - regardless of how good or important it is. No exceptions.
|
||||
|
||||
## Continuous Integration
|
||||
|
||||
Each project should be configured to use a continuous integration (CI) tool (i.e. a build server ala Jenkins). Typesafe has a Jenkins server farm that can be used. The CI tool should, on each push to master, build the **full** distribution and run **all** tests, and if something fails it should email out a notification with the failure report to the committer and the core team. The CI tool should also be used in conjunction with 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.
|
||||
|
||||
##Contributing Modules##
|
||||
## Contributing Modules
|
||||
|
||||
For external contributions of entire features, the normal way is to establish it
|
||||
as a stand-alone feature first, to show that there is a need for the feature. The
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import akka.japi.*;
|
|||
import scala.concurrent.Await;
|
||||
import scala.concurrent.Future;
|
||||
import scala.concurrent.Promise;
|
||||
import scala.concurrent.util.Duration;
|
||||
import scala.concurrent.duration.Duration;
|
||||
import akka.testkit.TestKitExtension;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
package akka.japi;
|
||||
|
||||
import akka.event.LoggingAdapter;
|
||||
import akka.event.NoLogging;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
|
@ -46,4 +48,10 @@ public class JavaAPITestBase {
|
|||
public void shouldBeSingleton() {
|
||||
assertSame(Option.none(), Option.none());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mustBeAbleToGetNoLogging() {
|
||||
LoggingAdapter a = NoLogging.getInstance();
|
||||
assertNotNull(a);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,8 @@ public class CustomRouteTest {
|
|||
// only to test compilability
|
||||
public void testRoute() {
|
||||
final ActorRef ref = system.actorOf(new Props().withRouter(new RoundRobinRouter(1)));
|
||||
final scala.Function1<scala.Tuple2<ActorRef, Object>, scala.collection.Iterable<Destination>> route = ExtractRoute.apply(ref);
|
||||
final scala.Function1<scala.Tuple2<ActorRef, Object>,
|
||||
scala.collection.immutable.Iterable<Destination>> route = ExtractRoute.apply(ref);
|
||||
route.apply(null);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,14 +4,14 @@
|
|||
package akka.util;
|
||||
|
||||
import org.junit.Test;
|
||||
import scala.concurrent.util.Duration;
|
||||
import scala.concurrent.duration.Duration;
|
||||
|
||||
public class JavaDuration {
|
||||
|
||||
@Test
|
||||
public void testCreation() {
|
||||
final Duration fivesec = Duration.create(5, "seconds");
|
||||
final Duration threemillis = Duration.parse("3 millis");
|
||||
final Duration threemillis = Duration.create("3 millis");
|
||||
final Duration diff = fivesec.minus(threemillis);
|
||||
assert diff.lt(fivesec);
|
||||
assert Duration.Zero().lteq(Duration.Inf());
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import language.postfixOps
|
|||
import akka.testkit._
|
||||
import akka.testkit.DefaultTimeout
|
||||
import akka.testkit.TestEvent._
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.duration._
|
||||
import akka.routing._
|
||||
import org.scalatest.BeforeAndAfterEach
|
||||
import akka.ConfigurationException
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import akka.actor.ActorDSL._
|
|||
//#import
|
||||
import akka.event.Logging.Warning
|
||||
import scala.concurrent.{ Await, Future }
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.duration._
|
||||
import java.util.concurrent.TimeoutException
|
||||
|
||||
class ActorDSLSpec extends AkkaSpec {
|
||||
|
|
@ -103,6 +103,32 @@ class ActorDSLSpec extends AkkaSpec {
|
|||
i.receive() must be("hi")
|
||||
}
|
||||
|
||||
"support becomeStacked" in {
|
||||
//#becomeStacked
|
||||
val a = actor(new Act {
|
||||
become { // this will replace the initial (empty) behavior
|
||||
case "info" ⇒ sender ! "A"
|
||||
case "switch" ⇒
|
||||
becomeStacked { // this will stack upon the "A" behavior
|
||||
case "info" ⇒ sender ! "B"
|
||||
case "switch" ⇒ unbecome() // return to the "A" behavior
|
||||
}
|
||||
case "lobotomize" ⇒ unbecome() // OH NOES: Actor.emptyBehavior
|
||||
}
|
||||
})
|
||||
//#becomeStacked
|
||||
|
||||
implicit def sender = testActor
|
||||
a ! "info"
|
||||
expectMsg("A")
|
||||
a ! "switch"
|
||||
a ! "info"
|
||||
expectMsg("B")
|
||||
a ! "switch"
|
||||
a ! "info"
|
||||
expectMsg("A")
|
||||
}
|
||||
|
||||
"support setup/teardown" in {
|
||||
//#simple-start-stop
|
||||
val a = actor(new Act {
|
||||
|
|
@ -188,7 +214,7 @@ class ActorDSLSpec extends AkkaSpec {
|
|||
become {
|
||||
case 1 ⇒ stash()
|
||||
case 2 ⇒
|
||||
testActor ! 2; unstashAll(); become {
|
||||
testActor ! 2; unstashAll(); becomeStacked {
|
||||
case 1 ⇒ testActor ! 1; unbecome()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ package akka.actor
|
|||
|
||||
import akka.testkit._
|
||||
import org.scalatest.BeforeAndAfterEach
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.Await
|
||||
import akka.pattern.ask
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import org.scalatest.matchers.MustMatchers
|
|||
|
||||
import akka.actor.Actor._
|
||||
import akka.testkit._
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.duration._
|
||||
import java.util.concurrent.atomic._
|
||||
import scala.concurrent.Await
|
||||
import akka.pattern.ask
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ package akka.actor
|
|||
import language.postfixOps
|
||||
|
||||
import akka.testkit._
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.Await
|
||||
import akka.pattern.ask
|
||||
import java.net.MalformedURLException
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import org.scalatest.matchers.MustMatchers
|
|||
|
||||
import akka.testkit._
|
||||
import akka.util.Timeout
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.Await
|
||||
import java.lang.IllegalStateException
|
||||
import scala.concurrent.Promise
|
||||
|
|
|
|||
|
|
@ -8,12 +8,16 @@ import akka.testkit._
|
|||
import org.scalatest.junit.JUnitSuite
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import scala.concurrent.Await
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.collection.JavaConverters
|
||||
import java.util.concurrent.{ TimeUnit, RejectedExecutionException, CountDownLatch, ConcurrentLinkedQueue }
|
||||
import scala.concurrent.duration._
|
||||
import java.util.concurrent.{ RejectedExecutionException, ConcurrentLinkedQueue }
|
||||
import akka.util.Timeout
|
||||
import akka.japi.Util.immutableSeq
|
||||
import scala.concurrent.Future
|
||||
import akka.pattern.ask
|
||||
import akka.dispatch._
|
||||
import com.typesafe.config.Config
|
||||
import java.util.concurrent.{ LinkedBlockingQueue, BlockingQueue, TimeUnit }
|
||||
import akka.util.Switch
|
||||
|
||||
class JavaExtensionSpec extends JavaExtension with JUnitSuite
|
||||
|
||||
|
|
@ -67,10 +71,57 @@ object ActorSystemSpec {
|
|||
}
|
||||
}
|
||||
|
||||
case class FastActor(latch: TestLatch, testActor: ActorRef) extends Actor {
|
||||
val ref1 = context.actorOf(Props.empty)
|
||||
val ref2 = context.actorFor(ref1.path.toString)
|
||||
testActor ! ref2.getClass
|
||||
latch.countDown()
|
||||
|
||||
def receive = {
|
||||
case _ ⇒
|
||||
}
|
||||
}
|
||||
|
||||
class SlowDispatcher(_config: Config, _prerequisites: DispatcherPrerequisites) extends MessageDispatcherConfigurator(_config, _prerequisites) {
|
||||
private val instance = new Dispatcher(
|
||||
prerequisites,
|
||||
config.getString("id"),
|
||||
config.getInt("throughput"),
|
||||
Duration(config.getNanoseconds("throughput-deadline-time"), TimeUnit.NANOSECONDS),
|
||||
mailboxType,
|
||||
configureExecutor(),
|
||||
Duration(config.getMilliseconds("shutdown-timeout"), TimeUnit.MILLISECONDS)) {
|
||||
val doneIt = new Switch
|
||||
override protected[akka] def registerForExecution(mbox: Mailbox, hasMessageHint: Boolean, hasSystemMessageHint: Boolean): Boolean = {
|
||||
val ret = super.registerForExecution(mbox, hasMessageHint, hasSystemMessageHint)
|
||||
doneIt.switchOn {
|
||||
TestKit.awaitCond(mbox.actor.actor != null, 1.second)
|
||||
mbox.actor.actor match {
|
||||
case FastActor(latch, _) ⇒ Await.ready(latch, 1.second)
|
||||
}
|
||||
}
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the same dispatcher instance for each invocation
|
||||
*/
|
||||
override def dispatcher(): MessageDispatcher = instance
|
||||
}
|
||||
|
||||
val config = s"""
|
||||
akka.extensions = ["akka.actor.TestExtension"]
|
||||
slow {
|
||||
type="${classOf[SlowDispatcher].getName}"
|
||||
}"""
|
||||
|
||||
}
|
||||
|
||||
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
||||
class ActorSystemSpec extends AkkaSpec("""akka.extensions = ["akka.actor.TestExtension"]""") with ImplicitSender {
|
||||
class ActorSystemSpec extends AkkaSpec(ActorSystemSpec.config) with ImplicitSender {
|
||||
|
||||
import ActorSystemSpec.FastActor
|
||||
|
||||
"An ActorSystem" must {
|
||||
|
||||
|
|
@ -102,8 +153,6 @@ class ActorSystemSpec extends AkkaSpec("""akka.extensions = ["akka.actor.TestExt
|
|||
}
|
||||
|
||||
"run termination callbacks in order" in {
|
||||
import scala.collection.JavaConverters._
|
||||
|
||||
val system2 = ActorSystem("TerminationCallbacks", AkkaSpec.testConf)
|
||||
val result = new ConcurrentLinkedQueue[Int]
|
||||
val count = 10
|
||||
|
|
@ -121,13 +170,11 @@ class ActorSystemSpec extends AkkaSpec("""akka.extensions = ["akka.actor.TestExt
|
|||
Await.ready(latch, 5 seconds)
|
||||
|
||||
val expected = (for (i ← 1 to count) yield i).reverse
|
||||
result.asScala.toSeq must be(expected)
|
||||
|
||||
immutableSeq(result) must be(expected)
|
||||
}
|
||||
|
||||
"awaitTermination after termination callbacks" in {
|
||||
import scala.collection.JavaConverters._
|
||||
|
||||
val system2 = ActorSystem("AwaitTermination", AkkaSpec.testConf)
|
||||
@volatile
|
||||
var callbackWasRun = false
|
||||
|
|
@ -168,6 +215,11 @@ class ActorSystemSpec extends AkkaSpec("""akka.extensions = ["akka.actor.TestExt
|
|||
Await.result(Future.sequence(waves), timeout.duration + 5.seconds) must be === Seq("done", "done", "done")
|
||||
}
|
||||
|
||||
"find actors that just have been created" in {
|
||||
system.actorOf(Props(new FastActor(TestLatch(), testActor)).withDispatcher("slow"))
|
||||
expectMsgType[Class[_]] must be(classOf[LocalActorRef])
|
||||
}
|
||||
|
||||
"reliable deny creation of actors while shutting down" in {
|
||||
val system = ActorSystem()
|
||||
import system.dispatcher
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
*/
|
||||
package akka.actor
|
||||
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.duration._
|
||||
import akka.testkit._
|
||||
import akka.testkit.TestEvent._
|
||||
import scala.concurrent.Await
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import akka.testkit.TestEvent._
|
|||
import akka.dispatch.BoundedDequeBasedMailbox
|
||||
import akka.pattern.ask
|
||||
import scala.concurrent.Await
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.duration._
|
||||
import akka.actor.ActorSystem.Settings
|
||||
import com.typesafe.config.{ Config, ConfigFactory }
|
||||
import org.scalatest.Assertions.intercept
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import akka.testkit.DefaultTimeout
|
|||
import akka.testkit.TestEvent._
|
||||
import scala.concurrent.Await
|
||||
import akka.pattern.ask
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.duration._
|
||||
import com.typesafe.config.{ Config, ConfigFactory }
|
||||
import org.scalatest.BeforeAndAfterEach
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import language.postfixOps
|
|||
|
||||
import akka.testkit.AkkaSpec
|
||||
import akka.dispatch.UnboundedMailbox
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.duration._
|
||||
|
||||
object ConsistencySpec {
|
||||
val config = """
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ package akka.actor
|
|||
|
||||
import language.postfixOps
|
||||
import akka.testkit._
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.duration._
|
||||
import java.util.concurrent.atomic._
|
||||
import scala.concurrent.Await
|
||||
import akka.pattern.ask
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import akka.testkit.AkkaSpec
|
|||
import com.typesafe.config.ConfigFactory
|
||||
import com.typesafe.config.ConfigParseOptions
|
||||
import akka.routing._
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.duration._
|
||||
|
||||
object DeployerSpec {
|
||||
val deployerConf = ConfigFactory.parseString("""
|
||||
|
|
|
|||
|
|
@ -8,13 +8,14 @@ import language.postfixOps
|
|||
import org.scalatest.{ BeforeAndAfterAll, BeforeAndAfterEach }
|
||||
import akka.testkit._
|
||||
import TestEvent.Mute
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.duration._
|
||||
import akka.event._
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import scala.concurrent.Await
|
||||
import akka.util.Timeout
|
||||
import scala.concurrent.util.Duration
|
||||
import scala.concurrent.util.FiniteDuration
|
||||
import org.scalatest.matchers.Matcher
|
||||
import org.scalatest.matchers.HavePropertyMatcher
|
||||
import org.scalatest.matchers.HavePropertyMatchResult
|
||||
|
||||
object FSMActorSpec {
|
||||
val timeout = Timeout(2 seconds)
|
||||
|
|
@ -201,6 +202,45 @@ class FSMActorSpec extends AkkaSpec(Map("akka.actor.debug.fsm" -> true)) with Im
|
|||
expectMsg(1 second, fsm.StopEvent(FSM.Shutdown, 1, null))
|
||||
}
|
||||
|
||||
"cancel all timers when terminated" in {
|
||||
val timerNames = List("timer-1", "timer-2", "timer-3")
|
||||
|
||||
// Lazy so fsmref can refer to checkTimersActive
|
||||
lazy val fsmref = TestFSMRef(new Actor with FSM[String, Null] {
|
||||
startWith("not-started", null)
|
||||
when("not-started") {
|
||||
case Event("start", _) ⇒ goto("started") replying "starting"
|
||||
}
|
||||
when("started", stateTimeout = 10 seconds) {
|
||||
case Event("stop", _) ⇒ stop()
|
||||
}
|
||||
onTransition {
|
||||
case "not-started" -> "started" ⇒
|
||||
for (timerName ← timerNames) setTimer(timerName, (), 10 seconds, false)
|
||||
}
|
||||
onTermination {
|
||||
case _ ⇒ {
|
||||
checkTimersActive(false)
|
||||
testActor ! "stopped"
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
def checkTimersActive(active: Boolean) {
|
||||
for (timer ← timerNames) fsmref.isTimerActive(timer) must be(active)
|
||||
fsmref.isStateTimerActive must be(active)
|
||||
}
|
||||
|
||||
checkTimersActive(false)
|
||||
|
||||
fsmref ! "start"
|
||||
expectMsg(1 second, "starting")
|
||||
checkTimersActive(true)
|
||||
|
||||
fsmref ! "stop"
|
||||
expectMsg(1 second, "stopped")
|
||||
}
|
||||
|
||||
"log events and transitions if asked to do so" in {
|
||||
import scala.collection.JavaConverters._
|
||||
val config = ConfigFactory.parseMap(Map("akka.loglevel" -> "DEBUG",
|
||||
|
|
|
|||
|
|
@ -7,8 +7,7 @@ package akka.actor
|
|||
import language.postfixOps
|
||||
|
||||
import akka.testkit._
|
||||
import scala.concurrent.util.Duration
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.duration._
|
||||
import akka.event.Logging
|
||||
|
||||
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
||||
|
|
|
|||
|
|
@ -6,8 +6,7 @@ package akka.actor
|
|||
import language.postfixOps
|
||||
|
||||
import akka.testkit._
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.util.Duration
|
||||
import scala.concurrent.duration._
|
||||
|
||||
object FSMTransitionSpec {
|
||||
|
||||
|
|
|
|||
|
|
@ -7,9 +7,8 @@ package akka.actor
|
|||
import language.postfixOps
|
||||
|
||||
import akka.testkit._
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.duration._
|
||||
import akka.actor.Actor._
|
||||
import scala.concurrent.util.Duration
|
||||
import scala.concurrent.Await
|
||||
import akka.pattern.{ ask, pipe }
|
||||
|
||||
|
|
|
|||
|
|
@ -7,16 +7,14 @@ package akka.actor
|
|||
import language.postfixOps
|
||||
import akka.util.ByteString
|
||||
import scala.concurrent.{ ExecutionContext, Await, Future, Promise }
|
||||
import scala.concurrent.util.{ Duration, Deadline }
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.duration._
|
||||
import scala.util.continuations._
|
||||
import akka.testkit._
|
||||
import akka.dispatch.MessageDispatcher
|
||||
import akka.pattern.ask
|
||||
import java.net.{ Socket, InetSocketAddress, InetAddress, SocketAddress }
|
||||
import scala.util.Failure
|
||||
import annotation.tailrec
|
||||
import scala.concurrent.util.FiniteDuration
|
||||
import scala.annotation.tailrec
|
||||
|
||||
object IOActorSpec {
|
||||
|
||||
|
|
@ -57,6 +55,8 @@ object IOActorSpec {
|
|||
|
||||
def receive = {
|
||||
|
||||
case _: IO.Connected ⇒ //don't care
|
||||
|
||||
case bytes: ByteString ⇒
|
||||
val source = sender
|
||||
socket write bytes
|
||||
|
|
@ -67,9 +67,9 @@ object IOActorSpec {
|
|||
|
||||
case IO.Closed(`socket`, cause) ⇒
|
||||
state(cause)
|
||||
throw cause match {
|
||||
case IO.Error(e) ⇒ e
|
||||
case _ ⇒ new RuntimeException("Socket closed")
|
||||
cause match {
|
||||
case IO.Error(e) ⇒ throw e
|
||||
case _ ⇒ throw new RuntimeException("Socket closed")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -156,6 +156,8 @@ object IOActorSpec {
|
|||
case IO.Read(socket, bytes) ⇒
|
||||
state(socket)(IO Chunk bytes)
|
||||
|
||||
case _: IO.Connected ⇒ //don't care
|
||||
|
||||
case IO.Closed(socket, cause) ⇒
|
||||
state -= socket
|
||||
|
||||
|
|
@ -183,6 +185,8 @@ object IOActorSpec {
|
|||
readResult map (source !)
|
||||
}
|
||||
|
||||
case _: IO.Connected ⇒ //don't care
|
||||
|
||||
case IO.Read(`socket`, bytes) ⇒
|
||||
state(IO Chunk bytes)
|
||||
|
||||
|
|
@ -278,7 +282,7 @@ class IOActorSpec extends AkkaSpec with DefaultTimeout {
|
|||
}
|
||||
|
||||
"an IO Actor" must {
|
||||
implicit val ec = system.dispatcher
|
||||
import system.dispatcher
|
||||
"run echo server" in {
|
||||
filterException[java.net.ConnectException] {
|
||||
val addressPromise = Promise[SocketAddress]()
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ package akka.actor
|
|||
import language.postfixOps
|
||||
import akka.testkit._
|
||||
import scala.concurrent.Await
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.duration._
|
||||
import akka.util.Timeout
|
||||
import scala.concurrent.Future
|
||||
import scala.util.Success
|
||||
|
|
@ -39,6 +39,22 @@ class LocalActorRefProviderSpec extends AkkaSpec(LocalActorRefProviderSpec.confi
|
|||
a must be === b
|
||||
}
|
||||
|
||||
"find child actor with URL encoded name using actorFor" in {
|
||||
val childName = "akka%3A%2F%2FClusterSystem%40127.0.0.1%3A2552"
|
||||
val a = system.actorOf(Props(new Actor {
|
||||
val child = context.actorOf(Props.empty, name = childName)
|
||||
def receive = {
|
||||
case "lookup" ⇒
|
||||
if (childName == child.path.name) sender ! context.actorFor(childName)
|
||||
else sender ! s"$childName is not ${child.path.name}!"
|
||||
}
|
||||
}))
|
||||
a.tell("lookup", testActor)
|
||||
val b = expectMsgType[ActorRef]
|
||||
b.isTerminated must be(false)
|
||||
b.path.name must be(childName)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
"An ActorRefFactory" must {
|
||||
|
|
|
|||
|
|
@ -6,11 +6,10 @@ package akka.actor
|
|||
|
||||
import language.postfixOps
|
||||
import akka.testkit._
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.duration._
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import scala.concurrent.Await
|
||||
import java.util.concurrent.TimeoutException
|
||||
import scala.concurrent.util.Duration
|
||||
|
||||
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
||||
class ReceiveTimeoutSpec extends AkkaSpec {
|
||||
|
|
|
|||
|
|
@ -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.DefaultTimeout
|
||||
import akka.testkit.TestLatch
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.util.Duration
|
||||
import scala.concurrent.duration._
|
||||
import akka.pattern.ask
|
||||
|
||||
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package akka.actor
|
|||
import language.postfixOps
|
||||
|
||||
import org.scalatest.BeforeAndAfterEach
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.duration._
|
||||
import java.util.concurrent.{ CountDownLatch, ConcurrentLinkedQueue, TimeUnit }
|
||||
import akka.testkit._
|
||||
import scala.concurrent.Await
|
||||
|
|
@ -214,5 +214,30 @@ class SchedulerSpec extends AkkaSpec with BeforeAndAfterEach with DefaultTimeout
|
|||
assert(elapsedTimeMs < 2000) // the precision is not ms exact
|
||||
cancellable.cancel()
|
||||
}
|
||||
|
||||
"adjust for scheduler inaccuracy" taggedAs TimingTest in {
|
||||
val startTime = System.nanoTime
|
||||
val n = 33
|
||||
val latch = new TestLatch(n)
|
||||
system.scheduler.schedule(150.millis, 150.millis) {
|
||||
latch.countDown()
|
||||
}
|
||||
Await.ready(latch, 6.seconds)
|
||||
val rate = n * 1000.0 / (System.nanoTime - startTime).nanos.toMillis
|
||||
rate must be(6.66 plusOrMinus (0.4))
|
||||
}
|
||||
|
||||
"not be affected by long running task" taggedAs TimingTest in {
|
||||
val startTime = System.nanoTime
|
||||
val n = 22
|
||||
val latch = new TestLatch(n)
|
||||
system.scheduler.schedule(225.millis, 225.millis) {
|
||||
Thread.sleep(80)
|
||||
latch.countDown()
|
||||
}
|
||||
Await.ready(latch, 6.seconds)
|
||||
val rate = n * 1000.0 / (System.nanoTime - startTime).nanos.toMillis
|
||||
rate must be(4.4 plusOrMinus (0.3))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,8 +7,7 @@ package akka.actor
|
|||
import language.postfixOps
|
||||
import java.util.concurrent.{ TimeUnit, CountDownLatch }
|
||||
import scala.concurrent.Await
|
||||
import scala.concurrent.util.Duration
|
||||
import scala.concurrent.util.duration.intToDurationInt
|
||||
import scala.concurrent.duration._
|
||||
import scala.math.BigInt.int2bigInt
|
||||
import scala.util.Random
|
||||
import scala.util.control.NoStackTrace
|
||||
|
|
@ -195,7 +194,7 @@ object SupervisorHierarchySpec {
|
|||
case x ⇒ (x, x)
|
||||
}
|
||||
override val supervisorStrategy = OneForOneStrategy()(unwrap andThen {
|
||||
case _: Failure if pongsToGo > 0 ⇒
|
||||
case (_: Failure, _) if pongsToGo > 0 ⇒
|
||||
log :+= Event("pongOfDeath resuming " + sender, identityHashCode(this))
|
||||
Resume
|
||||
case (f: Failure, orig) ⇒
|
||||
|
|
@ -394,7 +393,7 @@ object SupervisorHierarchySpec {
|
|||
override val supervisorStrategy = OneForOneStrategy() {
|
||||
case f: Failure ⇒ f.directive
|
||||
case OriginalRestartException(f: Failure) ⇒ f.directive
|
||||
case ActorInitializationException(f: Failure) ⇒ f.directive
|
||||
case ActorInitializationException(_, _, f: Failure) ⇒ f.directive
|
||||
case _ ⇒ Stop
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import java.util.concurrent.{ TimeUnit, CountDownLatch }
|
|||
import akka.testkit.AkkaSpec
|
||||
import akka.testkit.DefaultTimeout
|
||||
import akka.pattern.ask
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.duration._
|
||||
import scala.util.control.NonFatal
|
||||
|
||||
object SupervisorMiscSpec {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ package akka.actor
|
|||
import language.postfixOps
|
||||
|
||||
import org.scalatest.BeforeAndAfterEach
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.duration._
|
||||
import akka.{ Die, Ping }
|
||||
import akka.testkit.TestEvent._
|
||||
import akka.testkit._
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import language.postfixOps
|
|||
import org.scalatest.WordSpec
|
||||
import org.scalatest.matchers.MustMatchers
|
||||
import scala.concurrent.Await
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.duration._
|
||||
import akka.actor.Actor._
|
||||
import akka.testkit.{ TestKit, EventFilter, filterEvents, filterException, AkkaSpec, ImplicitSender, DefaultTimeout }
|
||||
import akka.dispatch.Dispatchers
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import akka.testkit.ImplicitSender
|
|||
import akka.testkit.DefaultTimeout
|
||||
import scala.concurrent.Await
|
||||
import akka.pattern.ask
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.duration._
|
||||
|
||||
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
||||
class Ticket669Spec extends AkkaSpec with BeforeAndAfterAll with ImplicitSender with DefaultTimeout {
|
||||
|
|
|
|||
|
|
@ -5,22 +5,21 @@ package akka.actor
|
|||
|
||||
import language.postfixOps
|
||||
import org.scalatest.{ BeforeAndAfterAll, BeforeAndAfterEach }
|
||||
import akka.util.Timeout
|
||||
import scala.annotation.tailrec
|
||||
import scala.collection.immutable
|
||||
import scala.concurrent.{ Await, Future, Promise }
|
||||
import scala.concurrent.util.Duration
|
||||
import scala.concurrent.util.duration._
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
import annotation.tailrec
|
||||
import scala.concurrent.duration._
|
||||
import akka.testkit.{ EventFilter, filterEvents, AkkaSpec }
|
||||
import akka.util.Timeout
|
||||
import akka.japi.{ Option ⇒ JOption }
|
||||
import akka.testkit.DefaultTimeout
|
||||
import akka.dispatch.{ Dispatchers }
|
||||
import akka.dispatch.Dispatchers
|
||||
import akka.pattern.ask
|
||||
import akka.serialization.JavaSerializer
|
||||
import akka.actor.TypedActor._
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
import java.lang.IllegalStateException
|
||||
import java.util.concurrent.{ TimeoutException, TimeUnit, CountDownLatch }
|
||||
import scala.concurrent.util.FiniteDuration
|
||||
|
||||
object TypedActorSpec {
|
||||
|
||||
|
|
@ -37,9 +36,9 @@ object TypedActorSpec {
|
|||
}
|
||||
"""
|
||||
|
||||
class CyclicIterator[T](val items: Seq[T]) extends Iterator[T] {
|
||||
class CyclicIterator[T](val items: immutable.Seq[T]) extends Iterator[T] {
|
||||
|
||||
private[this] val current: AtomicReference[Seq[T]] = new AtomicReference(items)
|
||||
private[this] val current = new AtomicReference(items)
|
||||
|
||||
def hasNext = items != Nil
|
||||
|
||||
|
|
|
|||
|
|
@ -21,8 +21,7 @@ import akka.event.Logging.Error
|
|||
import akka.pattern.ask
|
||||
import akka.testkit._
|
||||
import akka.util.Switch
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.util.Duration
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.{ Await, Future, Promise }
|
||||
import scala.annotation.tailrec
|
||||
|
||||
|
|
|
|||
|
|
@ -7,8 +7,7 @@ import java.util.concurrent.atomic.{ AtomicBoolean, AtomicInteger }
|
|||
import akka.testkit.{ filterEvents, EventFilter, AkkaSpec }
|
||||
import akka.actor.{ Props, Actor }
|
||||
import scala.concurrent.Await
|
||||
import scala.concurrent.util.Duration
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.duration._
|
||||
import akka.testkit.DefaultTimeout
|
||||
import akka.dispatch.{ PinnedDispatcher, Dispatchers, Dispatcher }
|
||||
import akka.pattern.ask
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import scala.collection.JavaConverters._
|
|||
import com.typesafe.config.ConfigFactory
|
||||
import akka.actor.Actor
|
||||
import akka.actor.Props
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.duration._
|
||||
|
||||
object DispatchersSpec {
|
||||
val config = """
|
||||
|
|
|
|||
|
|
@ -9,8 +9,7 @@ import language.postfixOps
|
|||
import akka.testkit.AkkaSpec
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import scala.collection.JavaConverters._
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.util.Duration
|
||||
import scala.concurrent.duration._
|
||||
import akka.actor.{ IOManager, ActorSystem }
|
||||
|
||||
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
||||
|
|
@ -25,8 +24,8 @@ class ConfigSpec extends AkkaSpec(ConfigFactory.defaultReference(ActorSystem.fin
|
|||
{
|
||||
import config._
|
||||
|
||||
getString("akka.version") must equal("2.1-SNAPSHOT")
|
||||
settings.ConfigVersion must equal("2.1-SNAPSHOT")
|
||||
getString("akka.version") must equal("2.2-SNAPSHOT")
|
||||
settings.ConfigVersion must equal("2.2-SNAPSHOT")
|
||||
|
||||
getBoolean("akka.daemonic") must equal(false)
|
||||
getBoolean("akka.actor.serialize-messages") must equal(false)
|
||||
|
|
@ -46,6 +45,9 @@ class ConfigSpec extends AkkaSpec(ConfigFactory.defaultReference(ActorSystem.fin
|
|||
|
||||
getInt("akka.actor.deployment.default.virtual-nodes-factor") must be(10)
|
||||
settings.DefaultVirtualNodesFactor must be(10)
|
||||
|
||||
getMilliseconds("akka.actor.unstarted-push-timeout") must be(10.seconds.toMillis)
|
||||
settings.UnstartedPushTimeout.duration must be(10.seconds)
|
||||
}
|
||||
|
||||
{
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import language.postfixOps
|
|||
import akka.actor.{ Actor, Props }
|
||||
import scala.concurrent.Future
|
||||
import scala.concurrent.Await
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.duration._
|
||||
import akka.testkit.{ AkkaSpec, DefaultTimeout }
|
||||
import akka.pattern.{ ask, pipe }
|
||||
import scala.concurrent.ExecutionException
|
||||
|
|
|
|||
|
|
@ -12,8 +12,7 @@ import akka.actor._
|
|||
import akka.testkit.{ EventFilter, filterEvents, filterException, AkkaSpec, DefaultTimeout, TestLatch }
|
||||
import scala.concurrent.{ Await, Awaitable, Future, Promise, ExecutionContext }
|
||||
import scala.util.control.NonFatal
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.util.Duration
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.ExecutionContext
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
import scala.runtime.NonLocalReturnControl
|
||||
|
|
@ -522,268 +521,6 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
|
|||
filterException[TimeoutException] { intercept[TimeoutException] { FutureSpec.ready(f3, 0 millis) } }
|
||||
}
|
||||
|
||||
//FIXME DATAFLOW
|
||||
/*"futureComposingWithContinuations" in {
|
||||
import Future.flow
|
||||
|
||||
val actor = system.actorOf(Props[TestActor])
|
||||
|
||||
val x = Future("Hello")
|
||||
val y = x flatMap (actor ? _) mapTo manifest[String]
|
||||
|
||||
val r = flow(x() + " " + y() + "!")
|
||||
|
||||
assert(Await.result(r, timeout.duration) === "Hello World!")
|
||||
|
||||
system.stop(actor)
|
||||
}
|
||||
|
||||
"futureComposingWithContinuationsFailureDivideZero" in {
|
||||
filterException[ArithmeticException] {
|
||||
import Future.flow
|
||||
|
||||
val x = Future("Hello")
|
||||
val y = x map (_.length)
|
||||
|
||||
val r = flow(x() + " " + y.map(_ / 0).map(_.toString).apply, 100)
|
||||
|
||||
intercept[java.lang.ArithmeticException](Await.result(r, timeout.duration))
|
||||
}
|
||||
}
|
||||
|
||||
"futureComposingWithContinuationsFailureCastInt" in {
|
||||
filterException[ClassCastException] {
|
||||
import Future.flow
|
||||
|
||||
val actor = system.actorOf(Props[TestActor])
|
||||
|
||||
val x = Future(3)
|
||||
val y = (actor ? "Hello").mapTo[Int]
|
||||
|
||||
val r = flow(x() + y(), 100)
|
||||
|
||||
intercept[ClassCastException](Await.result(r, timeout.duration))
|
||||
}
|
||||
}
|
||||
|
||||
"futureComposingWithContinuationsFailureCastNothing" in {
|
||||
filterException[ClassCastException] {
|
||||
import Future.flow
|
||||
|
||||
val actor = system.actorOf(Props[TestActor])
|
||||
|
||||
val x = Future("Hello")
|
||||
val y = actor ? "Hello" mapTo manifest[Nothing]
|
||||
|
||||
val r = flow(x() + y())
|
||||
|
||||
intercept[ClassCastException](Await.result(r, timeout.duration))
|
||||
}
|
||||
}
|
||||
|
||||
"futureCompletingWithContinuations" in {
|
||||
import Future.flow
|
||||
|
||||
val x, y, z = Promise[Int]()
|
||||
val ly, lz = new TestLatch
|
||||
|
||||
val result = flow {
|
||||
y completeWith x
|
||||
ly.open() // not within continuation
|
||||
|
||||
z << x
|
||||
lz.open() // within continuation, will wait for 'z' to complete
|
||||
z() + y()
|
||||
}
|
||||
|
||||
FutureSpec.ready(ly, 100 milliseconds)
|
||||
intercept[TimeoutException] { FutureSpec.ready(lz, 100 milliseconds) }
|
||||
|
||||
flow { x << 5 }
|
||||
|
||||
assert(Await.result(y, timeout.duration) === 5)
|
||||
assert(Await.result(z, timeout.duration) === 5)
|
||||
FutureSpec.ready(lz, timeout.duration)
|
||||
assert(Await.result(result, timeout.duration) === 10)
|
||||
|
||||
val a, b, c = Promise[Int]()
|
||||
|
||||
val result2 = flow {
|
||||
val n = (a << c).value.get.right.get + 10
|
||||
b << (c() - 2)
|
||||
a() + n * b()
|
||||
}
|
||||
|
||||
c completeWith Future(5)
|
||||
|
||||
assert(Await.result(a, timeout.duration) === 5)
|
||||
assert(Await.result(b, timeout.duration) === 3)
|
||||
assert(Await.result(result2, timeout.duration) === 50)
|
||||
}
|
||||
|
||||
"futureDataFlowShouldEmulateBlocking1" in {
|
||||
import Future.flow
|
||||
|
||||
val one, two = Promise[Int]()
|
||||
val simpleResult = flow {
|
||||
one() + two()
|
||||
}
|
||||
|
||||
assert(List(one, two, simpleResult).forall(_.isCompleted == false))
|
||||
|
||||
flow { one << 1 }
|
||||
|
||||
FutureSpec.ready(one, 1 minute)
|
||||
|
||||
assert(one.isCompleted)
|
||||
assert(List(two, simpleResult).forall(_.isCompleted == false))
|
||||
|
||||
flow { two << 9 }
|
||||
|
||||
FutureSpec.ready(two, 1 minute)
|
||||
|
||||
assert(List(one, two).forall(_.isCompleted == true))
|
||||
assert(Await.result(simpleResult, timeout.duration) === 10)
|
||||
|
||||
}
|
||||
|
||||
"futureDataFlowShouldEmulateBlocking2" in {
|
||||
import Future.flow
|
||||
val x1, x2, y1, y2 = Promise[Int]()
|
||||
val lx, ly, lz = new TestLatch
|
||||
val result = flow {
|
||||
lx.open()
|
||||
x1 << y1
|
||||
ly.open()
|
||||
x2 << y2
|
||||
lz.open()
|
||||
x1() + x2()
|
||||
}
|
||||
FutureSpec.ready(lx, 2 seconds)
|
||||
assert(!ly.isOpen)
|
||||
assert(!lz.isOpen)
|
||||
assert(List(x1, x2, y1, y2).forall(_.isCompleted == false))
|
||||
|
||||
flow { y1 << 1 } // When this is set, it should cascade down the line
|
||||
|
||||
FutureSpec.ready(ly, 2 seconds)
|
||||
assert(Await.result(x1, 1 minute) === 1)
|
||||
assert(!lz.isOpen)
|
||||
|
||||
flow { y2 << 9 } // When this is set, it should cascade down the line
|
||||
|
||||
FutureSpec.ready(lz, 2 seconds)
|
||||
assert(Await.result(x2, 1 minute) === 9)
|
||||
|
||||
assert(List(x1, x2, y1, y2).forall(_.isCompleted))
|
||||
|
||||
assert(Await.result(result, 1 minute) === 10)
|
||||
}
|
||||
|
||||
"dataFlowAPIshouldbeSlick" in {
|
||||
import Future.flow
|
||||
|
||||
val i1, i2, s1, s2 = new TestLatch
|
||||
|
||||
val callService1 = Future { i1.open(); FutureSpec.ready(s1, TestLatch.DefaultTimeout); 1 }
|
||||
val callService2 = Future { i2.open(); FutureSpec.ready(s2, TestLatch.DefaultTimeout); 9 }
|
||||
|
||||
val result = flow { callService1() + callService2() }
|
||||
|
||||
assert(!s1.isOpen)
|
||||
assert(!s2.isOpen)
|
||||
assert(!result.isCompleted)
|
||||
FutureSpec.ready(i1, 2 seconds)
|
||||
FutureSpec.ready(i2, 2 seconds)
|
||||
s1.open()
|
||||
s2.open()
|
||||
assert(Await.result(result, timeout.duration) === 10)
|
||||
}
|
||||
|
||||
"futureCompletingWithContinuationsFailure" in {
|
||||
filterException[ArithmeticException] {
|
||||
import Future.flow
|
||||
|
||||
val x, y, z = Promise[Int]()
|
||||
val ly, lz = new TestLatch
|
||||
|
||||
val result = flow {
|
||||
y << x
|
||||
ly.open()
|
||||
val oops = 1 / 0
|
||||
z << x
|
||||
lz.open()
|
||||
z() + y() + oops
|
||||
}
|
||||
intercept[TimeoutException] { FutureSpec.ready(ly, 100 milliseconds) }
|
||||
intercept[TimeoutException] { FutureSpec.ready(lz, 100 milliseconds) }
|
||||
flow { x << 5 }
|
||||
|
||||
assert(Await.result(y, timeout.duration) === 5)
|
||||
intercept[java.lang.ArithmeticException](Await.result(result, timeout.duration))
|
||||
assert(z.value === None)
|
||||
assert(!lz.isOpen)
|
||||
}
|
||||
}
|
||||
|
||||
"futureContinuationsShouldNotBlock" in {
|
||||
import Future.flow
|
||||
|
||||
val latch = new TestLatch
|
||||
val future = Future {
|
||||
FutureSpec.ready(latch, TestLatch.DefaultTimeout)
|
||||
"Hello"
|
||||
}
|
||||
|
||||
val result = flow {
|
||||
Some(future()).filter(_ == "Hello")
|
||||
}
|
||||
|
||||
assert(!result.isCompleted)
|
||||
|
||||
latch.open()
|
||||
|
||||
assert(Await.result(result, timeout.duration) === Some("Hello"))
|
||||
}
|
||||
|
||||
"futureFlowShouldBeTypeSafe" in {
|
||||
import Future.flow
|
||||
|
||||
val rString = flow {
|
||||
val x = Future(5)
|
||||
x().toString
|
||||
}
|
||||
|
||||
val rInt = flow {
|
||||
val x = rString.apply
|
||||
val y = Future(5)
|
||||
x.length + y()
|
||||
}
|
||||
|
||||
assert(checkType(rString, manifest[String]))
|
||||
assert(checkType(rInt, manifest[Int]))
|
||||
assert(!checkType(rInt, manifest[String]))
|
||||
assert(!checkType(rInt, manifest[Nothing]))
|
||||
assert(!checkType(rInt, manifest[Any]))
|
||||
|
||||
Await.result(rString, timeout.duration)
|
||||
Await.result(rInt, timeout.duration)
|
||||
}
|
||||
|
||||
"futureFlowSimpleAssign" in {
|
||||
import Future.flow
|
||||
|
||||
val x, y, z = Promise[Int]()
|
||||
|
||||
flow {
|
||||
z << x() + y()
|
||||
}
|
||||
flow { x << 40 }
|
||||
flow { y << 2 }
|
||||
|
||||
assert(Await.result(z, timeout.duration) === 42)
|
||||
}*/
|
||||
|
||||
"run callbacks async" in {
|
||||
val latch = Vector.fill(10)(new TestLatch)
|
||||
|
||||
|
|
@ -873,13 +610,6 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
|
|||
// failCount.get must be(0)
|
||||
}
|
||||
|
||||
//FIXME DATAFLOW
|
||||
/*"should capture first exception with dataflow" in {
|
||||
import Future.flow
|
||||
val f1 = flow { 40 / 0 }
|
||||
intercept[java.lang.ArithmeticException](Await result (f1, TestLatch.DefaultTimeout))
|
||||
}*/
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import com.typesafe.config.Config
|
|||
import akka.actor.{ RepointableRef, Props, DeadLetter, ActorSystem, ActorRefWithCell, ActorRef, ActorCell }
|
||||
import akka.testkit.AkkaSpec
|
||||
import scala.concurrent.{ Future, Promise, Await }
|
||||
import scala.concurrent.util.duration.intToDurationInt
|
||||
import scala.concurrent.duration._
|
||||
|
||||
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
||||
abstract class MailboxSpec extends AkkaSpec with BeforeAndAfterAll with BeforeAndAfterEach {
|
||||
|
|
|
|||
|
|
@ -6,11 +6,10 @@ import org.junit.runner.RunWith
|
|||
import org.scalatest.junit.JUnitRunner
|
||||
import com.typesafe.config.Config
|
||||
|
||||
import akka.actor.{ Props, InternalActorRef, ActorSystem, Actor }
|
||||
import akka.actor.{ Props, ActorSystem, Actor }
|
||||
import akka.pattern.ask
|
||||
import akka.testkit.{ DefaultTimeout, AkkaSpec }
|
||||
import scala.concurrent.Await
|
||||
import scala.concurrent.util.duration.intToDurationInt
|
||||
import scala.concurrent.duration._
|
||||
|
||||
object PriorityDispatcherSpec {
|
||||
val config = """
|
||||
|
|
@ -50,24 +49,32 @@ class PriorityDispatcherSpec extends AkkaSpec(PriorityDispatcherSpec.config) wit
|
|||
}
|
||||
|
||||
def testOrdering(dispatcherKey: String) {
|
||||
val msgs = (1 to 100) toList
|
||||
|
||||
// It's important that the actor under test is not a top level actor
|
||||
// with RepointableActorRef, since messages might be queued in
|
||||
// UnstartedCell and the sent to the PriorityQueue and consumed immediately
|
||||
// without the ordering taking place.
|
||||
val actor = system.actorOf(Props(new Actor {
|
||||
var acc: List[Int] = Nil
|
||||
context.actorOf(Props(new Actor {
|
||||
|
||||
val acc = scala.collection.mutable.ListBuffer[Int]()
|
||||
|
||||
scala.util.Random.shuffle(msgs) foreach { m ⇒ self ! m }
|
||||
|
||||
self.tell('Result, testActor)
|
||||
|
||||
def receive = {
|
||||
case i: Int ⇒ acc = i :: acc
|
||||
case 'Result ⇒ sender ! acc
|
||||
case i: Int ⇒ acc += i
|
||||
case 'Result ⇒ sender ! acc.toList
|
||||
}
|
||||
}).withDispatcher(dispatcherKey)).asInstanceOf[InternalActorRef]
|
||||
}).withDispatcher(dispatcherKey))
|
||||
|
||||
actor.suspend //Make sure the actor isn't treating any messages, let it buffer the incoming messages
|
||||
def receive = Actor.emptyBehavior
|
||||
|
||||
val msgs = (1 to 100).toList
|
||||
for (m ← msgs) actor ! m
|
||||
}))
|
||||
|
||||
actor.resume(causedByFailure = null) //Signal the actor to start treating it's message backlog
|
||||
|
||||
Await.result(actor.?('Result).mapTo[List[Int]], timeout.duration) must be === msgs.reverse
|
||||
expectMsgType[List[_]] must be === msgs
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import language.postfixOps
|
|||
|
||||
import org.scalatest.BeforeAndAfterEach
|
||||
import akka.testkit._
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.duration._
|
||||
import java.util.concurrent.atomic._
|
||||
import akka.actor.{ Props, Actor, ActorRef, ActorSystem }
|
||||
import java.util.Comparator
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ package akka.event
|
|||
|
||||
import language.postfixOps
|
||||
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.duration._
|
||||
import akka.actor.{ Actor, ActorRef, ActorSystemImpl, ActorSystem, Props, UnhandledMessage }
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import scala.collection.JavaConverters._
|
||||
|
|
|
|||
|
|
@ -6,10 +6,9 @@ package akka.event
|
|||
import language.postfixOps
|
||||
|
||||
import org.scalatest.{ BeforeAndAfterAll, BeforeAndAfterEach }
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.duration._
|
||||
import akka.testkit._
|
||||
import org.scalatest.WordSpec
|
||||
import scala.concurrent.util.Duration
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import scala.collection.JavaConverters._
|
||||
import java.util.Properties
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ package akka.pattern
|
|||
import language.postfixOps
|
||||
|
||||
import akka.testkit.AkkaSpec
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.Await
|
||||
import akka.testkit.DefaultTimeout
|
||||
import akka.util.Timeout
|
||||
|
|
|
|||
|
|
@ -4,8 +4,9 @@
|
|||
package akka.pattern
|
||||
|
||||
import akka.testkit._
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.{ Promise, Future, Await }
|
||||
import scala.annotation.tailrec
|
||||
|
||||
class CircuitBreakerMTSpec extends AkkaSpec {
|
||||
implicit val ec = system.dispatcher
|
||||
|
|
@ -14,8 +15,16 @@ class CircuitBreakerMTSpec extends AkkaSpec {
|
|||
val resetTimeout = 2.seconds.dilated
|
||||
val breaker = new CircuitBreaker(system.scheduler, 5, callTimeout, resetTimeout)
|
||||
|
||||
def openBreaker(): Unit =
|
||||
Await.ready(Future.sequence((1 to 5).map(_ ⇒ breaker.withCircuitBreaker(Future(throw new RuntimeException("FAIL"))).failed)), 1.second.dilated)
|
||||
def openBreaker(): Unit = {
|
||||
@tailrec def call(attemptsLeft: Int): Unit = {
|
||||
attemptsLeft must be > (0)
|
||||
if (Await.result(breaker.withCircuitBreaker(Future(throw new RuntimeException("FAIL"))) recover {
|
||||
case _: CircuitBreakerOpenException ⇒ false
|
||||
case _ ⇒ true
|
||||
}, remaining)) call(attemptsLeft - 1)
|
||||
}
|
||||
call(10)
|
||||
}
|
||||
|
||||
"allow many calls while in closed state with no errors" in {
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ package akka.pattern
|
|||
|
||||
import language.postfixOps
|
||||
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.duration._
|
||||
import akka.testkit._
|
||||
import org.scalatest.BeforeAndAfter
|
||||
import akka.actor.{ ActorSystem, Scheduler }
|
||||
|
|
|
|||
|
|
@ -9,8 +9,7 @@ import language.postfixOps
|
|||
import akka.testkit.AkkaSpec
|
||||
import akka.actor.{ Props, Actor }
|
||||
import scala.concurrent.{ Future, Promise, Await }
|
||||
import scala.concurrent.util.Duration
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.duration._
|
||||
|
||||
object PatternSpec {
|
||||
case class Work(duration: Duration)
|
||||
|
|
|
|||
|
|
@ -4,8 +4,7 @@ import akka.performance.workbench.PerformanceSpec
|
|||
import akka.actor._
|
||||
import java.util.concurrent.{ ThreadPoolExecutor, CountDownLatch, TimeUnit }
|
||||
import akka.dispatch._
|
||||
import scala.concurrent.util.Duration
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.duration._
|
||||
|
||||
// -server -Xms512M -Xmx1024M -XX:+UseParallelGC -Dbenchmark=true -Dbenchmark.repeatFactor=500
|
||||
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
||||
|
|
|
|||
|
|
@ -4,8 +4,7 @@ import akka.performance.workbench.PerformanceSpec
|
|||
import akka.actor._
|
||||
import java.util.concurrent.{ ThreadPoolExecutor, CountDownLatch, TimeUnit }
|
||||
import akka.dispatch._
|
||||
import scala.concurrent.util.Duration
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.duration._
|
||||
|
||||
// -server -Xms512M -Xmx1024M -XX:+UseParallelGC -Dbenchmark=true -Dbenchmark.repeatFactor=500
|
||||
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
||||
|
|
|
|||
|
|
@ -12,17 +12,18 @@ import java.io.PrintWriter
|
|||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import scala.collection.mutable.{ Map ⇒ MutableMap }
|
||||
import scala.collection.immutable
|
||||
import akka.actor.ActorSystem
|
||||
import akka.event.Logging
|
||||
|
||||
trait BenchResultRepository {
|
||||
def add(stats: Stats)
|
||||
|
||||
def get(name: String): Seq[Stats]
|
||||
def get(name: String): immutable.Seq[Stats]
|
||||
|
||||
def get(name: String, load: Int): Option[Stats]
|
||||
|
||||
def getWithHistorical(name: String, load: Int): Seq[Stats]
|
||||
def getWithHistorical(name: String, load: Int): immutable.Seq[Stats]
|
||||
|
||||
def isBaseline(stats: Stats): Boolean
|
||||
|
||||
|
|
@ -38,9 +39,9 @@ object BenchResultRepository {
|
|||
}
|
||||
|
||||
class FileBenchResultRepository extends BenchResultRepository {
|
||||
private val statsByName = MutableMap[String, Seq[Stats]]()
|
||||
private val statsByName = MutableMap[String, immutable.Seq[Stats]]()
|
||||
private val baselineStats = MutableMap[Key, Stats]()
|
||||
private val historicalStats = MutableMap[Key, Seq[Stats]]()
|
||||
private val historicalStats = MutableMap[Key, immutable.Seq[Stats]]()
|
||||
private def resultDir = BenchmarkConfig.config.getString("benchmark.resultDir")
|
||||
private val serDir = resultDir + "/ser"
|
||||
private def serDirExists: Boolean = new File(serDir).exists
|
||||
|
|
@ -51,13 +52,13 @@ class FileBenchResultRepository extends BenchResultRepository {
|
|||
case class Key(name: String, load: Int)
|
||||
|
||||
def add(stats: Stats): Unit = synchronized {
|
||||
val values = statsByName.getOrElseUpdate(stats.name, IndexedSeq.empty)
|
||||
val values = statsByName.getOrElseUpdate(stats.name, Vector.empty)
|
||||
statsByName(stats.name) = values :+ stats
|
||||
save(stats)
|
||||
}
|
||||
|
||||
def get(name: String): Seq[Stats] = synchronized {
|
||||
statsByName.getOrElse(name, IndexedSeq.empty)
|
||||
def get(name: String): immutable.Seq[Stats] = synchronized {
|
||||
statsByName.getOrElse(name, Vector.empty)
|
||||
}
|
||||
|
||||
def get(name: String, load: Int): Option[Stats] = synchronized {
|
||||
|
|
@ -68,13 +69,13 @@ class FileBenchResultRepository extends BenchResultRepository {
|
|||
baselineStats.get(Key(stats.name, stats.load)) == Some(stats)
|
||||
}
|
||||
|
||||
def getWithHistorical(name: String, load: Int): Seq[Stats] = synchronized {
|
||||
def getWithHistorical(name: String, load: Int): immutable.Seq[Stats] = synchronized {
|
||||
val key = Key(name, load)
|
||||
val historical = historicalStats.getOrElse(key, IndexedSeq.empty)
|
||||
val historical = historicalStats.getOrElse(key, Vector.empty)
|
||||
val baseline = baselineStats.get(key)
|
||||
val current = get(name, load)
|
||||
|
||||
val limited = (IndexedSeq.empty ++ historical ++ baseline ++ current).takeRight(maxHistorical)
|
||||
val limited = (Vector.empty ++ historical ++ baseline ++ current).takeRight(maxHistorical)
|
||||
limited.sortBy(_.timestamp)
|
||||
}
|
||||
|
||||
|
|
@ -94,7 +95,7 @@ class FileBenchResultRepository extends BenchResultRepository {
|
|||
}
|
||||
val historical = load(historicalFiles)
|
||||
for (h ← historical) {
|
||||
val values = historicalStats.getOrElseUpdate(Key(h.name, h.load), IndexedSeq.empty)
|
||||
val values = historicalStats.getOrElseUpdate(Key(h.name, h.load), Vector.empty)
|
||||
historicalStats(Key(h.name, h.load)) = values :+ h
|
||||
}
|
||||
}
|
||||
|
|
@ -120,7 +121,7 @@ class FileBenchResultRepository extends BenchResultRepository {
|
|||
}
|
||||
}
|
||||
|
||||
private def load(files: Iterable[File]): Seq[Stats] = {
|
||||
private def load(files: Iterable[File]): immutable.Seq[Stats] = {
|
||||
val result =
|
||||
for (f ← files) yield {
|
||||
var in: ObjectInputStream = null
|
||||
|
|
@ -132,11 +133,11 @@ class FileBenchResultRepository extends BenchResultRepository {
|
|||
case e: Throwable ⇒
|
||||
None
|
||||
} finally {
|
||||
if (in ne null) try { in.close() } catch { case ignore: Exception ⇒ }
|
||||
if (in ne null) try in.close() catch { case ignore: Exception ⇒ }
|
||||
}
|
||||
}
|
||||
|
||||
result.flatten.toSeq.sortBy(_.timestamp)
|
||||
result.flatten.toVector.sortBy(_.timestamp)
|
||||
}
|
||||
|
||||
loadFiles()
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package akka.performance.workbench
|
|||
import java.io.UnsupportedEncodingException
|
||||
import java.net.URLEncoder
|
||||
|
||||
import scala.collection.immutable.TreeMap
|
||||
import scala.collection.immutable
|
||||
|
||||
/**
|
||||
* Generates URLs to Google Chart API http://code.google.com/apis/chart/
|
||||
|
|
@ -16,7 +16,7 @@ object GoogleChartBuilder {
|
|||
/**
|
||||
* Builds a bar chart for tps in the statistics.
|
||||
*/
|
||||
def tpsChartUrl(statsByTimestamp: TreeMap[Long, Seq[Stats]], title: String, legend: Stats ⇒ String): String = {
|
||||
def tpsChartUrl(statsByTimestamp: immutable.TreeMap[Long, Seq[Stats]], title: String, legend: Stats ⇒ String): String = {
|
||||
if (statsByTimestamp.isEmpty) ""
|
||||
else {
|
||||
val loads = statsByTimestamp.values.head.map(_.load)
|
||||
|
|
@ -46,7 +46,7 @@ object GoogleChartBuilder {
|
|||
//sb.append("&")
|
||||
|
||||
// legend
|
||||
val legendStats = statsByTimestamp.values.map(_.head).toSeq
|
||||
val legendStats = statsByTimestamp.values.toVector.map(_.head)
|
||||
appendLegend(legendStats, sb, legend)
|
||||
sb.append("&")
|
||||
// bar spacing
|
||||
|
|
@ -60,10 +60,7 @@ object GoogleChartBuilder {
|
|||
val loadStr = loads.mkString(",")
|
||||
sb.append("chd=t:")
|
||||
val maxValue = allStats.map(_.tps).max
|
||||
val tpsSeries: Iterable[String] =
|
||||
for (statsSeq ← statsByTimestamp.values) yield {
|
||||
statsSeq.map(_.tps).mkString(",")
|
||||
}
|
||||
val tpsSeries: Iterable[String] = for (statsSeq ← statsByTimestamp.values) yield statsSeq.map(_.tps).mkString(",")
|
||||
sb.append(tpsSeries.mkString("|"))
|
||||
|
||||
// y range
|
||||
|
|
@ -83,7 +80,7 @@ object GoogleChartBuilder {
|
|||
/**
|
||||
* Builds a bar chart for all percentiles and the mean in the statistics.
|
||||
*/
|
||||
def percentilesAndMeanChartUrl(statistics: Seq[Stats], title: String, legend: Stats ⇒ String): String = {
|
||||
def percentilesAndMeanChartUrl(statistics: immutable.Seq[Stats], title: String, legend: Stats ⇒ String): String = {
|
||||
if (statistics.isEmpty) ""
|
||||
else {
|
||||
val current = statistics.last
|
||||
|
|
@ -146,13 +143,13 @@ object GoogleChartBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
private def percentileLabels(percentiles: TreeMap[Int, Long], sb: StringBuilder) {
|
||||
private def percentileLabels(percentiles: immutable.TreeMap[Int, Long], sb: StringBuilder) {
|
||||
sb.append("chxl=1:|")
|
||||
val s = percentiles.keys.toList.map(_ + "%").mkString("|")
|
||||
sb.append(s)
|
||||
}
|
||||
|
||||
private def appendLegend(statistics: Seq[Stats], sb: StringBuilder, legend: Stats ⇒ String) {
|
||||
private def appendLegend(statistics: immutable.Seq[Stats], sb: StringBuilder, legend: Stats ⇒ String) {
|
||||
val legends = statistics.map(legend(_))
|
||||
sb.append("chdl=")
|
||||
val s = legends.map(urlEncode(_)).mkString("|")
|
||||
|
|
@ -166,7 +163,7 @@ object GoogleChartBuilder {
|
|||
sb.append(s)
|
||||
}
|
||||
|
||||
private def dataSeries(allPercentiles: Seq[TreeMap[Int, Long]], meanValues: Seq[Double], sb: StringBuilder) {
|
||||
private def dataSeries(allPercentiles: immutable.Seq[immutable.TreeMap[Int, Long]], meanValues: immutable.Seq[Double], sb: StringBuilder) {
|
||||
val percentileSeries =
|
||||
for {
|
||||
percentiles ← allPercentiles
|
||||
|
|
@ -181,7 +178,7 @@ object GoogleChartBuilder {
|
|||
sb.append(series.mkString("|"))
|
||||
}
|
||||
|
||||
private def dataSeries(values: Seq[Double], sb: StringBuilder) {
|
||||
private def dataSeries(values: immutable.Seq[Double], sb: StringBuilder) {
|
||||
val series = values.map(formatDouble(_))
|
||||
sb.append(series.mkString("|"))
|
||||
}
|
||||
|
|
@ -198,7 +195,7 @@ object GoogleChartBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
def latencyAndThroughputChartUrl(statistics: Seq[Stats], title: String): String = {
|
||||
def latencyAndThroughputChartUrl(statistics: immutable.Seq[Stats], title: String): String = {
|
||||
if (statistics.isEmpty) ""
|
||||
else {
|
||||
val sb = new StringBuilder
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import scala.collection.immutable.TreeMap
|
|||
import org.apache.commons.math.stat.descriptive.DescriptiveStatistics
|
||||
import org.scalatest.BeforeAndAfterEach
|
||||
import akka.testkit.AkkaSpec
|
||||
import scala.concurrent.util.Duration
|
||||
import scala.concurrent.duration.Duration
|
||||
import com.typesafe.config.Config
|
||||
import java.util.concurrent.TimeUnit
|
||||
import akka.event.Logging
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import java.text.SimpleDateFormat
|
|||
import java.util.Date
|
||||
import akka.actor.ActorSystem
|
||||
import akka.event.Logging
|
||||
import scala.collection.immutable.TreeMap
|
||||
import scala.collection.immutable
|
||||
|
||||
class Report(
|
||||
system: ActorSystem,
|
||||
|
|
@ -19,7 +19,7 @@ class Report(
|
|||
val legendTimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm")
|
||||
val fileTimestampFormat = new SimpleDateFormat("yyyyMMddHHmmss")
|
||||
|
||||
def html(statistics: Seq[Stats]) {
|
||||
def html(statistics: immutable.Seq[Stats]) {
|
||||
|
||||
val current = statistics.last
|
||||
val sb = new StringBuilder
|
||||
|
|
@ -80,13 +80,13 @@ class Report(
|
|||
chartUrl
|
||||
}
|
||||
|
||||
def comparePercentilesAndMeanChart(stats: Stats): Seq[String] = {
|
||||
def comparePercentilesAndMeanChart(stats: Stats): immutable.Seq[String] = {
|
||||
for {
|
||||
compareName ← compareResultWith.toSeq
|
||||
compareName ← compareResultWith.to[immutable.Seq]
|
||||
compareStats ← resultRepository.get(compareName, stats.load)
|
||||
} yield {
|
||||
val chartTitle = stats.name + " vs. " + compareName + ", " + stats.load + " clients" + ", Percentiles and Mean (microseconds)"
|
||||
val chartUrl = GoogleChartBuilder.percentilesAndMeanChartUrl(Seq(compareStats, stats), chartTitle, _.name)
|
||||
val chartUrl = GoogleChartBuilder.percentilesAndMeanChartUrl(List(compareStats, stats), chartTitle, _.name)
|
||||
chartUrl
|
||||
}
|
||||
}
|
||||
|
|
@ -102,17 +102,17 @@ class Report(
|
|||
}
|
||||
}
|
||||
|
||||
def compareWithHistoricalTpsChart(statistics: Seq[Stats]): Option[String] = {
|
||||
def compareWithHistoricalTpsChart(statistics: immutable.Seq[Stats]): Option[String] = {
|
||||
|
||||
if (statistics.isEmpty) {
|
||||
None
|
||||
} else {
|
||||
val histTimestamps = resultRepository.getWithHistorical(statistics.head.name, statistics.head.load).map(_.timestamp)
|
||||
val statsByTimestamp = TreeMap[Long, Seq[Stats]]() ++
|
||||
val statsByTimestamp = immutable.TreeMap[Long, Seq[Stats]]() ++
|
||||
(for (ts ← histTimestamps) yield {
|
||||
val seq =
|
||||
for (stats ← statistics) yield {
|
||||
val withHistorical: Seq[Stats] = resultRepository.getWithHistorical(stats.name, stats.load)
|
||||
val withHistorical: immutable.Seq[Stats] = resultRepository.getWithHistorical(stats.name, stats.load)
|
||||
val cell = withHistorical.find(_.timestamp == ts)
|
||||
cell.getOrElse(Stats(stats.name, stats.load, ts))
|
||||
}
|
||||
|
|
@ -131,7 +131,7 @@ class Report(
|
|||
chartUrl
|
||||
}
|
||||
|
||||
def formatResultsTable(statsSeq: Seq[Stats]): String = {
|
||||
def formatResultsTable(statsSeq: immutable.Seq[Stats]): String = {
|
||||
|
||||
val name = statsSeq.head.name
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import akka.ConfigurationException
|
|||
import scala.concurrent.Await
|
||||
import akka.pattern.{ ask, gracefulStop }
|
||||
import akka.testkit.{ TestLatch, ImplicitSender, DefaultTimeout, AkkaSpec }
|
||||
import scala.concurrent.util.duration.intToDurationInt
|
||||
import scala.concurrent.duration._
|
||||
import akka.actor.UnstartedCell
|
||||
|
||||
object ConfiguredLocalRoutingSpec {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ class CustomRouteSpec extends AkkaSpec {
|
|||
provider.createRoutees(1)
|
||||
|
||||
{
|
||||
case (sender, message: String) ⇒ Seq(Destination(sender, target))
|
||||
case (sender, message: String) ⇒ List(Destination(sender, target))
|
||||
case (sender, message) ⇒ toAll(sender, provider.routees)
|
||||
}
|
||||
}
|
||||
|
|
@ -35,7 +35,7 @@ class CustomRouteSpec extends AkkaSpec {
|
|||
import akka.pattern.ask
|
||||
import akka.testkit.ExtractRoute
|
||||
import scala.concurrent.Await
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.duration._
|
||||
|
||||
val target = system.actorOf(Props.empty)
|
||||
val router = system.actorOf(Props.empty.withRouter(new MyRouter(target)))
|
||||
|
|
@ -43,8 +43,8 @@ class CustomRouteSpec extends AkkaSpec {
|
|||
val r = Await.result(router.ask(CurrentRoutees)(1 second).
|
||||
mapTo[RouterRoutees], 1 second)
|
||||
r.routees.size must be(1)
|
||||
route(testActor -> "hallo") must be(Seq(Destination(testActor, target)))
|
||||
route(testActor -> 12) must be(Seq(Destination(testActor, r.routees.head)))
|
||||
route(testActor -> "hallo") must be(List(Destination(testActor, target)))
|
||||
route(testActor -> 12) must be(List(Destination(testActor, r.routees.head)))
|
||||
//#test-route
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,13 +9,10 @@ import akka.testkit._
|
|||
import akka.testkit.TestEvent._
|
||||
import akka.actor.Props
|
||||
import scala.concurrent.Await
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.duration._
|
||||
import scala.collection.immutable
|
||||
import akka.actor.ActorRef
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import akka.pattern.ask
|
||||
import scala.concurrent.util.Duration
|
||||
import java.util.concurrent.TimeoutException
|
||||
import scala.concurrent.util.FiniteDuration
|
||||
import scala.util.Try
|
||||
|
||||
object ResizerSpec {
|
||||
|
|
@ -63,10 +60,10 @@ class ResizerSpec extends AkkaSpec(ResizerSpec.config) with DefaultTimeout with
|
|||
lowerBound = 2,
|
||||
upperBound = 3)
|
||||
|
||||
val c1 = resizer.capacity(IndexedSeq.empty[ActorRef])
|
||||
val c1 = resizer.capacity(immutable.IndexedSeq.empty[ActorRef])
|
||||
c1 must be(2)
|
||||
|
||||
val current = IndexedSeq(system.actorOf(Props[TestActor]), system.actorOf(Props[TestActor]))
|
||||
val current = immutable.IndexedSeq(system.actorOf(Props[TestActor]), system.actorOf(Props[TestActor]))
|
||||
val c2 = resizer.capacity(current)
|
||||
c2 must be(0)
|
||||
}
|
||||
|
|
@ -162,7 +159,7 @@ class ResizerSpec extends AkkaSpec(ResizerSpec.config) with DefaultTimeout with
|
|||
// sending in too quickly will result in skipped resize due to many resizeInProgress conflicts
|
||||
Thread.sleep(20.millis.dilated.toMillis)
|
||||
}
|
||||
within((((d * loops).asInstanceOf[FiniteDuration] / resizer.lowerBound) + 2.seconds.dilated).asInstanceOf[FiniteDuration]) {
|
||||
within((d * loops / resizer.lowerBound) + 2.seconds.dilated) {
|
||||
for (m ← 0 until loops) expectMsg("done")
|
||||
}
|
||||
}
|
||||
|
|
@ -176,19 +173,20 @@ class ResizerSpec extends AkkaSpec(ResizerSpec.config) with DefaultTimeout with
|
|||
routeeSize(router) must be(resizer.upperBound)
|
||||
}
|
||||
|
||||
"backoff" in {
|
||||
"backoff" in within(10 seconds) {
|
||||
|
||||
val resizer = DefaultResizer(
|
||||
lowerBound = 1,
|
||||
upperBound = 5,
|
||||
rampupRate = 1.0,
|
||||
backoffRate = 1.0,
|
||||
backoffThreshold = 0.20,
|
||||
backoffThreshold = 0.40,
|
||||
pressureThreshold = 1,
|
||||
messagesPerResize = 1)
|
||||
|
||||
val router = system.actorOf(Props(new Actor {
|
||||
def receive = {
|
||||
case n: Int if n <= 0 ⇒ // done
|
||||
case n: Int ⇒ Thread.sleep((n millis).dilated.toMillis)
|
||||
}
|
||||
}).withRouter(RoundRobinRouter(resizer = Some(resizer))))
|
||||
|
|
@ -205,12 +203,11 @@ class ResizerSpec extends AkkaSpec(ResizerSpec.config) with DefaultTimeout with
|
|||
Thread.sleep((300 millis).dilated.toMillis)
|
||||
|
||||
// let it cool down
|
||||
for (m ← 0 to 5) {
|
||||
router ! 1
|
||||
Thread.sleep((500 millis).dilated.toMillis)
|
||||
}
|
||||
awaitCond({
|
||||
router ! 0 // trigger resize
|
||||
routeeSize(router) < z
|
||||
}, interval = 500.millis.dilated)
|
||||
|
||||
awaitCond(Try(routeeSize(router) < (z)).getOrElse(false))
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,20 +5,20 @@ package akka.routing
|
|||
|
||||
import language.postfixOps
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import akka.actor._
|
||||
import scala.collection.mutable.LinkedList
|
||||
import scala.collection.immutable
|
||||
import akka.testkit._
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.Await
|
||||
import scala.concurrent.util.Duration
|
||||
import akka.ConfigurationException
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import akka.pattern.{ ask, pipe }
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import com.typesafe.config.Config
|
||||
import akka.dispatch.Dispatchers
|
||||
import akka.util.Collections.EmptyImmutableSeq
|
||||
import akka.util.Timeout
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
object RoutingSpec {
|
||||
|
||||
|
|
@ -55,11 +55,10 @@ object RoutingSpec {
|
|||
class MyRouter(config: Config) extends RouterConfig {
|
||||
val foo = config.getString("foo")
|
||||
def createRoute(routeeProvider: RouteeProvider): Route = {
|
||||
val routees = IndexedSeq(routeeProvider.context.actorOf(Props[Echo]))
|
||||
routeeProvider.registerRoutees(routees)
|
||||
routeeProvider.registerRoutees(List(routeeProvider.context.actorOf(Props[Echo])))
|
||||
|
||||
{
|
||||
case (sender, message) ⇒ Nil
|
||||
case (sender, message) ⇒ EmptyImmutableSeq
|
||||
}
|
||||
}
|
||||
def routerDispatcher: String = Dispatchers.DefaultDispatcherId
|
||||
|
|
@ -102,33 +101,35 @@ class RoutingSpec extends AkkaSpec(RoutingSpec.config) with DefaultTimeout with
|
|||
}
|
||||
|
||||
"be able to send their routees" in {
|
||||
class TheActor extends Actor {
|
||||
val routee1 = context.actorOf(Props[TestActor], "routee1")
|
||||
val routee2 = context.actorOf(Props[TestActor], "routee2")
|
||||
val routee3 = context.actorOf(Props[TestActor], "routee3")
|
||||
val router = context.actorOf(Props[TestActor].withRouter(
|
||||
ScatterGatherFirstCompletedRouter(
|
||||
routees = List(routee1, routee2, routee3),
|
||||
within = 5 seconds)))
|
||||
|
||||
case class TestRun(id: String, names: immutable.Iterable[String], actors: Int)
|
||||
val actor = system.actorOf(Props(new Actor {
|
||||
def receive = {
|
||||
case "doIt" ⇒ router ! CurrentRoutees
|
||||
case routees: RouterRoutees ⇒ testActor forward routees
|
||||
}
|
||||
}
|
||||
case TestRun(id, names, actors) ⇒
|
||||
val routerProps = Props[TestActor].withRouter(
|
||||
ScatterGatherFirstCompletedRouter(
|
||||
routees = names map { context.actorOf(Props(new TestActor), _) },
|
||||
within = 5 seconds))
|
||||
|
||||
val theActor = system.actorOf(Props(new TheActor), "theActor")
|
||||
theActor ! "doIt"
|
||||
val routees = expectMsgPF() {
|
||||
case RouterRoutees(routees) ⇒ routees.toSet
|
||||
1 to actors foreach { i ⇒ context.actorOf(routerProps, id + i).tell(CurrentRoutees, testActor) }
|
||||
}
|
||||
}))
|
||||
|
||||
routees.map(_.path.name) must be(Set("routee1", "routee2", "routee3"))
|
||||
val actors = 15
|
||||
val names = 1 to 20 map { "routee" + _ } toList
|
||||
|
||||
actor ! TestRun("test", names, actors)
|
||||
|
||||
1 to actors foreach { _ ⇒
|
||||
val routees = expectMsgType[RouterRoutees].routees
|
||||
routees.map(_.path.name) must be === names
|
||||
}
|
||||
expectNoMsg(500.millis)
|
||||
}
|
||||
|
||||
"use configured nr-of-instances when FromConfig" in {
|
||||
val router = system.actorOf(Props[TestActor].withRouter(FromConfig), "router1")
|
||||
Await.result(router ? CurrentRoutees, remaining).asInstanceOf[RouterRoutees].routees.size must be(3)
|
||||
router ! CurrentRoutees
|
||||
expectMsgType[RouterRoutees].routees.size must be(3)
|
||||
watch(router)
|
||||
system.stop(router)
|
||||
expectMsgType[Terminated]
|
||||
|
|
@ -136,7 +137,8 @@ class RoutingSpec extends AkkaSpec(RoutingSpec.config) with DefaultTimeout with
|
|||
|
||||
"use configured nr-of-instances when router is specified" in {
|
||||
val router = system.actorOf(Props[TestActor].withRouter(RoundRobinRouter(nrOfInstances = 2)), "router2")
|
||||
Await.result(router ? CurrentRoutees, remaining).asInstanceOf[RouterRoutees].routees.size must be(3)
|
||||
router ! CurrentRoutees
|
||||
expectMsgType[RouterRoutees].routees.size must be(3)
|
||||
system.stop(router)
|
||||
}
|
||||
|
||||
|
|
@ -151,7 +153,8 @@ class RoutingSpec extends AkkaSpec(RoutingSpec.config) with DefaultTimeout with
|
|||
}
|
||||
val router = system.actorOf(Props[TestActor].withRouter(RoundRobinRouter(resizer = Some(resizer))), "router3")
|
||||
Await.ready(latch, remaining)
|
||||
Await.result(router ? CurrentRoutees, remaining).asInstanceOf[RouterRoutees].routees.size must be(3)
|
||||
router ! CurrentRoutees
|
||||
expectMsgType[RouterRoutees].routees.size must be(3)
|
||||
system.stop(router)
|
||||
}
|
||||
|
||||
|
|
@ -252,15 +255,15 @@ class RoutingSpec extends AkkaSpec(RoutingSpec.config) with DefaultTimeout with
|
|||
val doneLatch = new TestLatch(connectionCount)
|
||||
|
||||
//lets create some connections.
|
||||
var actors = new LinkedList[ActorRef]
|
||||
var counters = new LinkedList[AtomicInteger]
|
||||
@volatile var actors = immutable.IndexedSeq[ActorRef]()
|
||||
@volatile var counters = immutable.IndexedSeq[AtomicInteger]()
|
||||
for (i ← 0 until connectionCount) {
|
||||
counters = counters :+ new AtomicInteger()
|
||||
|
||||
val actor = system.actorOf(Props(new Actor {
|
||||
def receive = {
|
||||
case "end" ⇒ doneLatch.countDown()
|
||||
case msg: Int ⇒ counters.get(i).get.addAndGet(msg)
|
||||
case msg: Int ⇒ counters(i).addAndGet(msg)
|
||||
}
|
||||
}))
|
||||
actors = actors :+ actor
|
||||
|
|
@ -279,10 +282,8 @@ class RoutingSpec extends AkkaSpec(RoutingSpec.config) with DefaultTimeout with
|
|||
//now wait some and do validations.
|
||||
Await.ready(doneLatch, remaining)
|
||||
|
||||
for (i ← 0 until connectionCount) {
|
||||
val counter = counters.get(i).get
|
||||
counter.get must be((iterationCount * (i + 1)))
|
||||
}
|
||||
for (i ← 0 until connectionCount)
|
||||
counters(i).get must be((iterationCount * (i + 1)))
|
||||
}
|
||||
|
||||
"deliver a broadcast message using the !" in {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import akka.actor._
|
|||
import java.io._
|
||||
import scala.concurrent.Await
|
||||
import akka.util.Timeout
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.duration._
|
||||
import scala.reflect.BeanInfo
|
||||
import com.google.protobuf.Message
|
||||
import akka.pattern.ask
|
||||
|
|
|
|||
|
|
@ -7,12 +7,32 @@ import language.postfixOps
|
|||
|
||||
import org.scalatest.WordSpec
|
||||
import org.scalatest.matchers.MustMatchers
|
||||
import scala.concurrent.util.Duration
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.Await
|
||||
|
||||
import java.util.concurrent.TimeUnit._
|
||||
import akka.testkit.AkkaSpec
|
||||
import akka.testkit.TestLatch
|
||||
import java.util.concurrent.TimeoutException
|
||||
import akka.testkit.LongRunningTest
|
||||
|
||||
class DurationSpec extends WordSpec with MustMatchers {
|
||||
class DurationSpec extends AkkaSpec {
|
||||
|
||||
"A HashedWheelTimer" must {
|
||||
|
||||
"not mess up long timeouts" taggedAs LongRunningTest in {
|
||||
val longish = Long.MaxValue.nanos
|
||||
val barrier = TestLatch()
|
||||
import system.dispatcher
|
||||
val job = system.scheduler.scheduleOnce(longish)(barrier.countDown())
|
||||
intercept[TimeoutException] {
|
||||
// this used to fire after 46 seconds due to wrap-around
|
||||
Await.ready(barrier, 90 seconds)
|
||||
}
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
"Duration" must {
|
||||
|
||||
|
|
@ -34,11 +54,12 @@ class DurationSpec extends WordSpec with MustMatchers {
|
|||
val one = 1.second
|
||||
val inf = Duration.Inf
|
||||
val minf = Duration.MinusInf
|
||||
val undefined = Duration.Undefined
|
||||
(-inf) must be(minf)
|
||||
intercept[IllegalArgumentException] { minf + inf }
|
||||
intercept[IllegalArgumentException] { inf - inf }
|
||||
intercept[IllegalArgumentException] { inf + minf }
|
||||
intercept[IllegalArgumentException] { minf - minf }
|
||||
(minf + inf) must be(undefined)
|
||||
(inf - inf) must be(undefined)
|
||||
(inf + minf) must be(undefined)
|
||||
(minf - minf) must be(undefined)
|
||||
(inf + inf) must be(inf)
|
||||
(inf - minf) must be(inf)
|
||||
(minf - inf) must be(minf)
|
||||
|
|
|
|||
|
|
@ -8,10 +8,12 @@ import akka.util.Unsafe;
|
|||
|
||||
final class AbstractActorRef {
|
||||
final static long cellOffset;
|
||||
final static long lookupOffset;
|
||||
|
||||
static {
|
||||
try {
|
||||
cellOffset = Unsafe.instance.objectFieldOffset(RepointableActorRef.class.getDeclaredField("_cellDoNotCallMeDirectly"));
|
||||
lookupOffset = Unsafe.instance.objectFieldOffset(RepointableActorRef.class.getDeclaredField("_lookupDoNotCallMeDirectly"));
|
||||
} catch(Throwable t){
|
||||
throw new ExceptionInInitializerError(t);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import scala.collection.Seq;
|
|||
public class JAPI {
|
||||
|
||||
public static <T> Seq<T> seq(T... ts) {
|
||||
return Util.arrayToSeq(ts);
|
||||
return Util.immutableSeq(ts);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,8 +24,8 @@ import java.util.concurrent.ThreadFactory;
|
|||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
import scala.concurrent.util.Duration;
|
||||
import scala.concurrent.util.FiniteDuration;
|
||||
import scala.concurrent.duration.Duration;
|
||||
import scala.concurrent.duration.FiniteDuration;
|
||||
import akka.event.LoggingAdapter;
|
||||
import akka.util.Unsafe;
|
||||
|
||||
|
|
@ -263,8 +263,11 @@ public class HashedWheelTimer implements Timer {
|
|||
|
||||
void scheduleTimeout(HashedWheelTimeout timeout, long delay) {
|
||||
// Prepare the required parameters to schedule the timeout object.
|
||||
final long relativeIndex = Math.max(1, (delay + tickDuration - 1) / tickDuration); // If relative index < 1 then it should be 1
|
||||
|
||||
long relativeIndex = (delay + tickDuration - 1) / tickDuration;
|
||||
// if the previous line had an overflow going on, then 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;
|
||||
|
||||
// Add the timeout to the wheel.
|
||||
|
|
@ -304,7 +307,7 @@ public class HashedWheelTimer implements Timer {
|
|||
|
||||
while (!shutdown()) {
|
||||
final long deadline = waitForNextTick();
|
||||
if (deadline > 0)
|
||||
if (deadline > Long.MIN_VALUE)
|
||||
notifyExpiredTimeouts(fetchExpiredTimeouts(deadline));
|
||||
}
|
||||
}
|
||||
|
|
@ -332,7 +335,7 @@ public class HashedWheelTimer implements Timer {
|
|||
HashedWheelTimeout timeout = i.next();
|
||||
if (timeout.remainingRounds <= 0) {
|
||||
i.remove();
|
||||
if (timeout.deadline <= deadline) {
|
||||
if (timeout.deadline - deadline <= 0) {
|
||||
expiredTimeouts.add(timeout);
|
||||
} else {
|
||||
// Handle the case where the timeout is put into a wrong
|
||||
|
|
@ -368,6 +371,12 @@ public class HashedWheelTimer implements Timer {
|
|||
expiredTimeouts.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* calculate goal nanoTime from startTime and current tick number,
|
||||
* then wait until that goal has been reached.
|
||||
*
|
||||
* @return Long.MIN_VALUE if received a shutdown request, current time otherwise (with Long.MIN_VALUE changed by +1)
|
||||
*/
|
||||
private long waitForNextTick() {
|
||||
long deadline = startTime + tickDuration * tick;
|
||||
|
||||
|
|
@ -378,7 +387,8 @@ public class HashedWheelTimer implements Timer {
|
|||
|
||||
if (sleepTimeMs <= 0) {
|
||||
tick += 1;
|
||||
return currentTime;
|
||||
if (currentTime == Long.MIN_VALUE) return -Long.MAX_VALUE;
|
||||
else return currentTime;
|
||||
}
|
||||
|
||||
// Check if we run on windows, as if thats the case we will need
|
||||
|
|
@ -394,7 +404,7 @@ public class HashedWheelTimer implements Timer {
|
|||
Thread.sleep(sleepTimeMs);
|
||||
} catch (InterruptedException e) {
|
||||
if (shutdown()) {
|
||||
return -1;
|
||||
return Long.MIN_VALUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ package akka.util.internal;
|
|||
|
||||
import java.util.Set;
|
||||
|
||||
import scala.concurrent.util.FiniteDuration;
|
||||
import scala.concurrent.duration.FiniteDuration;
|
||||
|
||||
/**
|
||||
* Schedules {@link TimerTask}s for one-time future execution in a background
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
akka {
|
||||
# Akka version, checked against the runtime version of Akka.
|
||||
version = "2.1-SNAPSHOT"
|
||||
version = "2.2-SNAPSHOT"
|
||||
|
||||
# Home directory of Akka, modules in the deploy directory will be loaded
|
||||
home = ""
|
||||
|
|
|
|||
|
|
@ -300,6 +300,11 @@ object Actor {
|
|||
def apply(x: Any) = throw new UnsupportedOperationException("Empty behavior apply()")
|
||||
}
|
||||
|
||||
/**
|
||||
* Default placeholder (null) used for "!" to indicate that there is no sender of the message,
|
||||
* that will be translated to the receiving system's deadLetters.
|
||||
*/
|
||||
final val noSender: ActorRef = null
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ package akka.actor
|
|||
|
||||
import java.io.{ ObjectOutputStream, NotSerializableException }
|
||||
import scala.annotation.tailrec
|
||||
import scala.collection.immutable.TreeSet
|
||||
import scala.concurrent.util.Duration
|
||||
import scala.collection.immutable
|
||||
import scala.concurrent.duration.Duration
|
||||
import scala.util.control.NonFatal
|
||||
import akka.actor.dungeon.ChildrenContainer
|
||||
import akka.actor.dungeon.ChildrenContainer.WaitingForChildren
|
||||
|
|
@ -76,8 +76,14 @@ trait ActorContext extends ActorRefFactory {
|
|||
|
||||
/**
|
||||
* Changes the Actor's behavior to become the new 'Receive' (PartialFunction[Any, Unit]) handler.
|
||||
* Puts the behavior on top of the hotswap stack.
|
||||
* If "discardOld" is true, an unbecome will be issued prior to pushing the new behavior to the stack
|
||||
* This method acts upon the behavior stack as follows:
|
||||
*
|
||||
* - if `discardOld = true` it will replace the top element (i.e. the current behavior)
|
||||
* - if `discardOld = false` it will keep the current behavior and push the given one atop
|
||||
*
|
||||
* The default of replacing the current behavior has been chosen to avoid memory leaks in
|
||||
* case client code is written without consulting this documentation first (i.e. always pushing
|
||||
* new closures and never issuing an `unbecome()`)
|
||||
*/
|
||||
def become(behavior: Actor.Receive, discardOld: Boolean = true): Unit
|
||||
|
||||
|
|
@ -102,7 +108,7 @@ trait ActorContext extends ActorRefFactory {
|
|||
* val goodLookup = context.actorFor("kid")
|
||||
* }}}
|
||||
*/
|
||||
def children: Iterable[ActorRef]
|
||||
def children: immutable.Iterable[ActorRef]
|
||||
|
||||
/**
|
||||
* Get the child with the given name if it exists.
|
||||
|
|
@ -167,14 +173,20 @@ trait UntypedActorContext extends ActorContext {
|
|||
|
||||
/**
|
||||
* Changes the Actor's behavior to become the new 'Procedure' handler.
|
||||
* Puts the behavior on top of the hotswap stack.
|
||||
* Replaces the current behavior at the top of the hotswap stack.
|
||||
*/
|
||||
def become(behavior: Procedure[Any]): Unit
|
||||
|
||||
/**
|
||||
* Changes the Actor's behavior to become the new 'Procedure' handler.
|
||||
* Puts the behavior on top of the hotswap stack.
|
||||
* If "discardOld" is true, an unbecome will be issued prior to pushing the new behavior to the stack
|
||||
* This method acts upon the behavior stack as follows:
|
||||
*
|
||||
* - if `discardOld = true` it will replace the top element (i.e. the current behavior)
|
||||
* - if `discardOld = false` it will keep the current behavior and push the given one atop
|
||||
*
|
||||
* The default of replacing the current behavior has been chosen to avoid memory leaks in
|
||||
* case client code is written without consulting this documentation first (i.e. always pushing
|
||||
* new closures and never issuing an `unbecome()`)
|
||||
*/
|
||||
def become(behavior: Procedure[Any], discardOld: Boolean): Unit
|
||||
|
||||
|
|
@ -196,6 +208,11 @@ private[akka] trait Cell {
|
|||
* The system internals where this Cell lives.
|
||||
*/
|
||||
def systemImpl: ActorSystemImpl
|
||||
/**
|
||||
* Start the cell: enqueued message must not be processed before this has
|
||||
* been called. The usual action is to attach the mailbox to a dispatcher.
|
||||
*/
|
||||
def start(): this.type
|
||||
/**
|
||||
* Recursively suspend this actor and all its children. Must not throw exceptions.
|
||||
*/
|
||||
|
|
@ -247,12 +264,12 @@ private[akka] trait Cell {
|
|||
*/
|
||||
def isLocal: Boolean
|
||||
/**
|
||||
* If the actor isLocal, returns whether messages are currently queued,
|
||||
* If the actor isLocal, returns whether "user messages" are currently queued,
|
||||
* “false” otherwise.
|
||||
*/
|
||||
def hasMessages: Boolean
|
||||
/**
|
||||
* If the actor isLocal, returns the number of messages currently queued,
|
||||
* If the actor isLocal, returns the number of "user messages" currently queued,
|
||||
* which may be a costly operation, 0 otherwise.
|
||||
*/
|
||||
def numberOfMessages: Int
|
||||
|
|
@ -275,7 +292,7 @@ private[akka] object ActorCell {
|
|||
|
||||
final val emptyBehaviorStack: List[Actor.Receive] = Nil
|
||||
|
||||
final val emptyActorRefSet: Set[ActorRef] = TreeSet.empty
|
||||
final val emptyActorRefSet: Set[ActorRef] = immutable.TreeSet.empty
|
||||
}
|
||||
|
||||
//ACTORCELL IS 64bytes and should stay that way unless very good reason not to (machine sympathy, cache line fit)
|
||||
|
|
@ -350,7 +367,7 @@ private[akka] class ActorCell(
|
|||
case w: WaitingForChildren ⇒ w.enqueue(message)
|
||||
}
|
||||
case Terminate() ⇒ terminate()
|
||||
case Supervise(child, uid) ⇒ supervise(child, uid)
|
||||
case Supervise(child, async, uid) ⇒ supervise(child, async, uid)
|
||||
case ChildTerminated(child) ⇒ todo = handleChildTerminated(child)
|
||||
case NoMessage ⇒ // only here to suppress warning
|
||||
}
|
||||
|
|
@ -480,20 +497,20 @@ private[akka] class ActorCell(
|
|||
}
|
||||
}
|
||||
|
||||
private def supervise(child: ActorRef, uid: Int): Unit = if (!isTerminating) {
|
||||
private def supervise(child: ActorRef, async: Boolean, uid: Int): Unit = if (!isTerminating) {
|
||||
// Supervise is the first thing we get from a new child, so store away the UID for later use in handleFailure()
|
||||
initChild(child) match {
|
||||
case Some(crs) ⇒
|
||||
crs.uid = uid
|
||||
handleSupervise(child)
|
||||
handleSupervise(child, async)
|
||||
if (system.settings.DebugLifecycle) publish(Debug(self.path.toString, clazz(actor), "now supervising " + child))
|
||||
case None ⇒ publish(Error(self.path.toString, clazz(actor), "received Supervise from unregistered child " + child + ", this will not end well"))
|
||||
}
|
||||
}
|
||||
|
||||
// future extension point
|
||||
protected def handleSupervise(child: ActorRef): Unit = child match {
|
||||
case r: RepointableActorRef ⇒ r.activate()
|
||||
protected def handleSupervise(child: ActorRef, async: Boolean): Unit = child match {
|
||||
case r: RepointableActorRef if async ⇒ r.point()
|
||||
case _ ⇒
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,13 +5,11 @@
|
|||
package akka.actor
|
||||
|
||||
import scala.collection.mutable.Queue
|
||||
import scala.concurrent.util.Duration
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.duration._
|
||||
import akka.pattern.ask
|
||||
import scala.concurrent.Await
|
||||
import akka.util.Timeout
|
||||
import scala.collection.immutable.TreeSet
|
||||
import scala.concurrent.util.Deadline
|
||||
import java.util.concurrent.TimeoutException
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
*/
|
||||
package akka.actor
|
||||
import scala.annotation.tailrec
|
||||
import scala.collection.immutable
|
||||
import akka.japi.Util.immutableSeq
|
||||
import java.net.MalformedURLException
|
||||
|
||||
object ActorPath {
|
||||
|
|
@ -20,6 +22,8 @@ object ActorPath {
|
|||
* http://www.ietf.org/rfc/rfc2396.txt
|
||||
*/
|
||||
val ElementRegex = """(?:[-\w:@&=+,.!~*'_;]|%\p{XDigit}{2})(?:[-\w:@&=+,.!~*'$_;]|%\p{XDigit}{2})*""".r
|
||||
|
||||
private[akka] final val emptyActorPath: immutable.Iterable[String] = List("")
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -68,23 +72,18 @@ sealed trait ActorPath extends Comparable[ActorPath] with Serializable {
|
|||
/**
|
||||
* ''Java API'': Recursively create a descendant’s path by appending all child names.
|
||||
*/
|
||||
def descendant(names: java.lang.Iterable[String]): ActorPath = {
|
||||
import scala.collection.JavaConverters._
|
||||
/(names.asScala)
|
||||
}
|
||||
def descendant(names: java.lang.Iterable[String]): ActorPath = /(immutableSeq(names))
|
||||
|
||||
/**
|
||||
* Sequence of names for this path from root to this. Performance implication: has to allocate a list.
|
||||
*/
|
||||
def elements: Iterable[String]
|
||||
def elements: immutable.Iterable[String]
|
||||
|
||||
/**
|
||||
* ''Java API'': Sequence of names for this path from root to this. Performance implication: has to allocate a list.
|
||||
*/
|
||||
def getElements: java.lang.Iterable[String] = {
|
||||
import scala.collection.JavaConverters._
|
||||
elements.asJava
|
||||
}
|
||||
def getElements: java.lang.Iterable[String] =
|
||||
scala.collection.JavaConverters.asJavaIterableConverter(elements).asJava
|
||||
|
||||
/**
|
||||
* Walk up the tree to obtain and return the RootActorPath.
|
||||
|
|
@ -112,7 +111,7 @@ final case class RootActorPath(address: Address, name: String = "/") extends Act
|
|||
|
||||
override def /(child: String): ActorPath = new ChildActorPath(this, child)
|
||||
|
||||
override val elements: Iterable[String] = List("")
|
||||
override def elements: immutable.Iterable[String] = ActorPath.emptyActorPath
|
||||
|
||||
override val toString: String = address + name
|
||||
|
||||
|
|
@ -121,7 +120,7 @@ final case class RootActorPath(address: Address, name: String = "/") extends Act
|
|||
else addr + name
|
||||
|
||||
override def compareTo(other: ActorPath): Int = other match {
|
||||
case r: RootActorPath ⇒ toString compareTo r.toString
|
||||
case r: RootActorPath ⇒ toString compareTo r.toString // FIXME make this cheaper by comparing address and name in isolation
|
||||
case c: ChildActorPath ⇒ 1
|
||||
}
|
||||
}
|
||||
|
|
@ -134,9 +133,9 @@ final class ChildActorPath(val parent: ActorPath, val name: String) extends Acto
|
|||
|
||||
override def /(child: String): ActorPath = new ChildActorPath(this, child)
|
||||
|
||||
override def elements: Iterable[String] = {
|
||||
override def elements: immutable.Iterable[String] = {
|
||||
@tailrec
|
||||
def rec(p: ActorPath, acc: List[String]): Iterable[String] = p match {
|
||||
def rec(p: ActorPath, acc: List[String]): immutable.Iterable[String] = p match {
|
||||
case r: RootActorPath ⇒ acc
|
||||
case _ ⇒ rec(p.parent, p.name :: acc)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ trait ScalaActorRef { ref: ActorRef ⇒
|
|||
* </pre>
|
||||
* <p/>
|
||||
*/
|
||||
def !(message: Any)(implicit sender: ActorRef = null): Unit
|
||||
def !(message: Any)(implicit sender: ActorRef = Actor.noSender): Unit
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -194,6 +194,7 @@ private[akka] abstract class InternalActorRef extends ActorRef with ScalaActorRe
|
|||
/*
|
||||
* Actor life-cycle management, invoked only internally (in response to user requests via ActorContext).
|
||||
*/
|
||||
def start(): Unit
|
||||
def resume(causedByFailure: Throwable): Unit
|
||||
def suspend(): Unit
|
||||
def restart(cause: Throwable): Unit
|
||||
|
|
@ -259,13 +260,16 @@ private[akka] class LocalActorRef private[akka] (
|
|||
|
||||
/*
|
||||
* Safe publication of this 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
|
||||
* frozen at the _end_ of the constructor, but we are publishing “this” before
|
||||
* that is reached).
|
||||
* This means that the result of newActorCell needs to be written to the val
|
||||
* actorCell before we call init and start, since we can start using "this"
|
||||
* object from another thread as soon as we run init.
|
||||
*/
|
||||
private val actorCell: ActorCell = newActorCell(_system, this, _props, _supervisor)
|
||||
actorCell.start(sendSupervise = true, ThreadLocalRandom.current.nextInt())
|
||||
actorCell.init(ThreadLocalRandom.current.nextInt(), sendSupervise = true)
|
||||
|
||||
protected def newActorCell(system: ActorSystemImpl, ref: InternalActorRef, props: Props, supervisor: InternalActorRef): ActorCell =
|
||||
new ActorCell(system, ref, props, supervisor)
|
||||
|
|
@ -279,6 +283,11 @@ private[akka] class LocalActorRef private[akka] (
|
|||
*/
|
||||
override def isTerminated: Boolean = actorCell.isTerminated
|
||||
|
||||
/**
|
||||
* Starts the actor after initialization.
|
||||
*/
|
||||
override def start(): Unit = actorCell.start()
|
||||
|
||||
/**
|
||||
* Suspends the actor so that it will not process messages until resumed. The
|
||||
* suspend request is processed asynchronously to the caller of this method
|
||||
|
|
@ -341,7 +350,7 @@ private[akka] class LocalActorRef private[akka] (
|
|||
|
||||
override def sendSystemMessage(message: SystemMessage): Unit = actorCell.sendSystemMessage(message)
|
||||
|
||||
override def !(message: Any)(implicit sender: ActorRef = null): Unit = actorCell.tell(message, sender)
|
||||
override def !(message: Any)(implicit sender: ActorRef = Actor.noSender): Unit = actorCell.tell(message, sender)
|
||||
|
||||
override def restart(cause: Throwable): Unit = actorCell.restart(cause)
|
||||
|
||||
|
|
@ -390,12 +399,13 @@ private[akka] trait MinimalActorRef extends InternalActorRef with LocalRef {
|
|||
override def getParent: InternalActorRef = Nobody
|
||||
override def getChild(names: Iterator[String]): InternalActorRef = if (names.forall(_.isEmpty)) this else Nobody
|
||||
|
||||
override def start(): Unit = ()
|
||||
override def suspend(): Unit = ()
|
||||
override def resume(causedByFailure: Throwable): Unit = ()
|
||||
override def stop(): Unit = ()
|
||||
override def isTerminated = false
|
||||
|
||||
override def !(message: Any)(implicit sender: ActorRef = null): Unit = ()
|
||||
override def !(message: Any)(implicit sender: ActorRef = Actor.noSender): Unit = ()
|
||||
|
||||
override def sendSystemMessage(message: SystemMessage): Unit = ()
|
||||
override def restart(cause: Throwable): Unit = ()
|
||||
|
|
@ -409,7 +419,10 @@ private[akka] trait MinimalActorRef extends InternalActorRef with LocalRef {
|
|||
* to the ActorSystem's EventStream
|
||||
*/
|
||||
@SerialVersionUID(1L)
|
||||
case class DeadLetter(message: Any, sender: ActorRef, recipient: ActorRef)
|
||||
case class DeadLetter(message: Any, sender: ActorRef, recipient: ActorRef) {
|
||||
require(sender ne null, "DeadLetter sender may not be null")
|
||||
require(recipient ne null, "DeadLetter recipient may not be null")
|
||||
}
|
||||
|
||||
private[akka] object DeadLetterActorRef {
|
||||
@SerialVersionUID(1L)
|
||||
|
|
@ -435,9 +448,12 @@ private[akka] class EmptyLocalActorRef(override val provider: ActorRefProvider,
|
|||
|
||||
override def sendSystemMessage(message: SystemMessage): Unit = specialHandle(message)
|
||||
|
||||
override def !(message: Any)(implicit sender: ActorRef = null): Unit = message match {
|
||||
case d: DeadLetter ⇒ specialHandle(d.message) // do NOT form endless loops, since deadLetters will resend!
|
||||
case _ ⇒ if (!specialHandle(message)) eventStream.publish(DeadLetter(message, sender, this))
|
||||
override def !(message: Any)(implicit sender: ActorRef = Actor.noSender): Unit = message match {
|
||||
case d: DeadLetter ⇒
|
||||
specialHandle(d.message) // do NOT form endless loops, since deadLetters will resend!
|
||||
case _ if !specialHandle(message) ⇒
|
||||
eventStream.publish(DeadLetter(message, if (sender eq Actor.noSender) provider.deadLetters else sender, this))
|
||||
case _ ⇒
|
||||
}
|
||||
|
||||
protected def specialHandle(msg: Any): Boolean = msg match {
|
||||
|
|
@ -520,7 +536,7 @@ private[akka] class VirtualPathContainer(
|
|||
|
||||
def hasChildren: Boolean = !children.isEmpty
|
||||
|
||||
def foreachChild(f: ActorRef ⇒ Unit) = {
|
||||
def foreachChild(f: ActorRef ⇒ Unit): Unit = {
|
||||
val iter = children.values.iterator
|
||||
while (iter.hasNext) f(iter.next)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,8 +8,9 @@ import akka.dispatch._
|
|||
import akka.routing._
|
||||
import akka.event._
|
||||
import akka.util.{ Switch, Helpers }
|
||||
import akka.japi.Util.immutableSeq
|
||||
import akka.util.Collections.EmptyImmutableSeq
|
||||
import scala.util.{ Success, Failure }
|
||||
import scala.util.control.NonFatal
|
||||
import scala.concurrent.{ Future, Promise }
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
|
||||
|
|
@ -41,8 +42,7 @@ trait ActorRefProvider {
|
|||
def deadLetters: ActorRef
|
||||
|
||||
/**
|
||||
* The root path for all actors within this actor system, including remote
|
||||
* address if enabled.
|
||||
* The root path for all actors within this actor system, not including any remote address information.
|
||||
*/
|
||||
def rootPath: ActorPath
|
||||
|
||||
|
|
@ -145,6 +145,11 @@ trait ActorRefProvider {
|
|||
* attempt is made to verify actual reachability).
|
||||
*/
|
||||
def getExternalAddressFor(addr: Address): Option[Address]
|
||||
|
||||
/**
|
||||
* Obtain the external address of the default transport.
|
||||
*/
|
||||
def getDefaultAddress: Address
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -271,10 +276,7 @@ trait ActorRefFactory {
|
|||
*
|
||||
* For maximum performance use a collection with efficient head & tail operations.
|
||||
*/
|
||||
def actorFor(path: java.lang.Iterable[String]): ActorRef = {
|
||||
import scala.collection.JavaConverters._
|
||||
provider.actorFor(lookupRoot, path.asScala)
|
||||
}
|
||||
def actorFor(path: java.lang.Iterable[String]): ActorRef = provider.actorFor(lookupRoot, immutableSeq(path))
|
||||
|
||||
/**
|
||||
* Construct an [[akka.actor.ActorSelection]] from the given path, which is
|
||||
|
|
@ -319,6 +321,10 @@ private[akka] object SystemGuardian {
|
|||
|
||||
/**
|
||||
* Local ActorRef provider.
|
||||
*
|
||||
* INTERNAL API!
|
||||
*
|
||||
* Depending on this class is not supported, only the [[ActorRefProvider]] interface is supported.
|
||||
*/
|
||||
class LocalActorRefProvider(
|
||||
_systemName: String,
|
||||
|
|
@ -375,7 +381,7 @@ class LocalActorRefProvider(
|
|||
override def stop(): Unit = stopped switchOn { terminationPromise.complete(causeOfTermination.map(Failure(_)).getOrElse(Success(()))) }
|
||||
override def isTerminated: Boolean = stopped.isOn
|
||||
|
||||
override def !(message: Any)(implicit sender: ActorRef = null): Unit = stopped.ifOff(message match {
|
||||
override def !(message: Any)(implicit sender: ActorRef = Actor.noSender): Unit = stopped.ifOff(message match {
|
||||
case Failed(ex, _) if sender ne null ⇒ causeOfTermination = Some(ex); sender.asInstanceOf[InternalActorRef].stop()
|
||||
case NullMessage ⇒ // do nothing
|
||||
case _ ⇒ log.error(this + " received unexpected message [" + message + "]")
|
||||
|
|
@ -383,7 +389,7 @@ class LocalActorRefProvider(
|
|||
|
||||
override def sendSystemMessage(message: SystemMessage): Unit = stopped ifOff {
|
||||
message match {
|
||||
case Supervise(_, _) ⇒ // TODO register child in some map to keep track of it and enable shutdown after all dead
|
||||
case Supervise(_, _, _) ⇒ // TODO register child in some map to keep track of it and enable shutdown after all dead
|
||||
case ChildTerminated(_) ⇒ stop()
|
||||
case _ ⇒ log.error(this + " received unexpected system message [" + message + "]")
|
||||
}
|
||||
|
|
@ -480,7 +486,7 @@ class LocalActorRefProvider(
|
|||
def registerExtraNames(_extras: Map[String, InternalActorRef]): Unit = extraNames ++= _extras
|
||||
|
||||
private def guardianSupervisorStrategyConfigurator =
|
||||
dynamicAccess.createInstanceFor[SupervisorStrategyConfigurator](settings.SupervisorStrategyClass, Seq()).get
|
||||
dynamicAccess.createInstanceFor[SupervisorStrategyConfigurator](settings.SupervisorStrategyClass, EmptyImmutableSeq).get
|
||||
|
||||
/**
|
||||
* Overridable supervision strategy to be used by the “/user” guardian.
|
||||
|
|
@ -516,6 +522,7 @@ class LocalActorRefProvider(
|
|||
cell.reserveChild("user")
|
||||
val ref = new LocalActorRef(system, Props(new Guardian(guardianStrategy)), rootGuardian, rootPath / "user")
|
||||
cell.initChild(ref)
|
||||
ref.start()
|
||||
ref
|
||||
}
|
||||
|
||||
|
|
@ -524,6 +531,7 @@ class LocalActorRefProvider(
|
|||
cell.reserveChild("system")
|
||||
val ref = new LocalActorRef(system, Props(new SystemGuardian(systemGuardianStrategy)), rootGuardian, rootPath / "system")
|
||||
cell.initChild(ref)
|
||||
ref.start()
|
||||
ref
|
||||
}
|
||||
|
||||
|
|
@ -585,16 +593,17 @@ class LocalActorRefProvider(
|
|||
if (settings.DebugRouterMisconfiguration && deployer.lookup(path).isDefined)
|
||||
log.warning("Configuration says that {} should be a router, but code disagrees. Remove the config or add a routerConfig to its Props.")
|
||||
|
||||
if (async) new RepointableActorRef(system, props, supervisor, path).initialize()
|
||||
if (async) new RepointableActorRef(system, props, supervisor, path).initialize(async)
|
||||
else new LocalActorRef(system, props, supervisor, path)
|
||||
case router ⇒
|
||||
val lookup = if (lookupDeploy) deployer.lookup(path) else None
|
||||
val fromProps = Iterator(props.deploy.copy(routerConfig = props.deploy.routerConfig withFallback router))
|
||||
val d = fromProps ++ deploy.iterator ++ lookup.iterator reduce ((a, b) ⇒ b withFallback a)
|
||||
val ref = new RoutedActorRef(system, props.withRouter(d.routerConfig), supervisor, path).initialize()
|
||||
if (async) ref else ref.activate()
|
||||
new RoutedActorRef(system, props.withRouter(d.routerConfig), supervisor, path).initialize(async)
|
||||
}
|
||||
}
|
||||
|
||||
def getExternalAddressFor(addr: Address): Option[Address] = if (addr == rootPath.address) Some(addr) else None
|
||||
|
||||
def getDefaultAddress: Address = rootPath.address
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ abstract class ActorSelection {
|
|||
|
||||
def tell(msg: Any, sender: ActorRef): Unit = target.tell(toMessage(msg, path), sender)
|
||||
|
||||
// FIXME make this so that "next" instead is the remaining path
|
||||
private def toMessage(msg: Any, path: Array[AnyRef]): Any = {
|
||||
var acc = msg
|
||||
var index = path.length - 1
|
||||
|
|
@ -70,5 +69,5 @@ object ActorSelection {
|
|||
trait ScalaActorSelection {
|
||||
this: ActorSelection ⇒
|
||||
|
||||
def !(msg: Any)(implicit sender: ActorRef = null) = tell(msg, sender)
|
||||
def !(msg: Any)(implicit sender: ActorRef = Actor.noSender) = tell(msg, sender)
|
||||
}
|
||||
|
|
@ -6,24 +6,24 @@ package akka.actor
|
|||
|
||||
import akka.event._
|
||||
import akka.dispatch._
|
||||
import akka.pattern.ask
|
||||
import akka.japi.Util.immutableSeq
|
||||
import com.typesafe.config.{ Config, ConfigFactory }
|
||||
import scala.annotation.tailrec
|
||||
import scala.concurrent.util.Duration
|
||||
import java.io.Closeable
|
||||
import scala.collection.immutable
|
||||
import scala.concurrent.duration.{ FiniteDuration, Duration }
|
||||
import scala.concurrent.{ Await, Awaitable, CanAwait, Future }
|
||||
import scala.util.{ Failure, Success }
|
||||
import scala.util.control.NonFatal
|
||||
import akka.util._
|
||||
import java.io.Closeable
|
||||
import akka.util.internal.{ HashedWheelTimer, ConcurrentIdentityHashMap }
|
||||
import java.util.concurrent.{ ThreadFactory, CountDownLatch, TimeoutException, RejectedExecutionException }
|
||||
import java.util.concurrent.TimeUnit.MILLISECONDS
|
||||
import akka.actor.dungeon.ChildrenContainer
|
||||
import scala.concurrent.util.FiniteDuration
|
||||
import util.{ Failure, Success }
|
||||
|
||||
object ActorSystem {
|
||||
|
||||
val Version: String = "2.1-SNAPSHOT"
|
||||
val Version: String = "2.2-SNAPSHOT"
|
||||
|
||||
val EnvHome: Option[String] = System.getenv("AKKA_HOME") match {
|
||||
case null | "" | "." ⇒ None
|
||||
|
|
@ -144,7 +144,7 @@ object ActorSystem {
|
|||
|
||||
final val LogLevel: String = getString("akka.loglevel")
|
||||
final val StdoutLogLevel: String = getString("akka.stdout-loglevel")
|
||||
final val EventHandlers: Seq[String] = getStringList("akka.event-handlers").asScala
|
||||
final val EventHandlers: immutable.Seq[String] = immutableSeq(getStringList("akka.event-handlers"))
|
||||
final val EventHandlerStartTimeout: Timeout = Timeout(Duration(getMilliseconds("akka.event-handler-startup-timeout"), MILLISECONDS))
|
||||
final val LogConfigOnStart: Boolean = config.getBoolean("akka.log-config-on-start")
|
||||
|
||||
|
|
@ -273,10 +273,7 @@ abstract class ActorSystem extends ActorRefFactory {
|
|||
/**
|
||||
* ''Java API'': Recursively create a descendant’s path by appending all child names.
|
||||
*/
|
||||
def descendant(names: java.lang.Iterable[String]): ActorPath = {
|
||||
import scala.collection.JavaConverters._
|
||||
/(names.asScala)
|
||||
}
|
||||
def descendant(names: java.lang.Iterable[String]): ActorPath = /(immutableSeq(names))
|
||||
|
||||
/**
|
||||
* Start-up time in milliseconds since the epoch.
|
||||
|
|
@ -536,7 +533,7 @@ private[akka] class ActorSystemImpl(val name: String, applicationConfig: Config,
|
|||
val scheduler: Scheduler = createScheduler()
|
||||
|
||||
val provider: ActorRefProvider = {
|
||||
val arguments = Seq(
|
||||
val arguments = Vector(
|
||||
classOf[String] -> name,
|
||||
classOf[Settings] -> settings,
|
||||
classOf[EventStream] -> eventStream,
|
||||
|
|
@ -676,9 +673,8 @@ private[akka] class ActorSystemImpl(val name: String, applicationConfig: Config,
|
|||
def hasExtension(ext: ExtensionId[_ <: Extension]): Boolean = findExtension(ext) != null
|
||||
|
||||
private def loadExtensions() {
|
||||
import scala.collection.JavaConversions._
|
||||
settings.config.getStringList("akka.extensions") foreach { fqcn ⇒
|
||||
dynamicAccess.getObjectFor[AnyRef](fqcn) recoverWith { case _ ⇒ dynamicAccess.createInstanceFor[AnyRef](fqcn, Seq()) } match {
|
||||
immutableSeq(settings.config.getStringList("akka.extensions")) foreach { fqcn ⇒
|
||||
dynamicAccess.getObjectFor[AnyRef](fqcn) recoverWith { case _ ⇒ dynamicAccess.createInstanceFor[AnyRef](fqcn, Nil) } match {
|
||||
case Success(p: ExtensionIdProvider) ⇒ registerExtension(p.lookup())
|
||||
case Success(p: ExtensionId[_]) ⇒ registerExtension(p)
|
||||
case Success(other) ⇒ log.error("[{}] is not an 'ExtensionIdProvider' or 'ExtensionId', skipping...", fqcn)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ package akka.actor
|
|||
import java.net.URI
|
||||
import java.net.URISyntaxException
|
||||
import java.net.MalformedURLException
|
||||
import annotation.tailrec
|
||||
import scala.annotation.tailrec
|
||||
import scala.collection.immutable
|
||||
|
||||
/**
|
||||
* The address specifies the physical location under which an Actor can be
|
||||
|
|
@ -71,11 +72,11 @@ private[akka] trait PathUtils {
|
|||
}
|
||||
|
||||
object RelativeActorPath extends PathUtils {
|
||||
def unapply(addr: String): Option[Iterable[String]] = {
|
||||
def unapply(addr: String): Option[immutable.Seq[String]] = {
|
||||
try {
|
||||
val uri = new URI(addr)
|
||||
if (uri.isAbsolute) None
|
||||
else Some(split(uri.getPath))
|
||||
else Some(split(uri.getRawPath))
|
||||
} catch {
|
||||
case _: URISyntaxException ⇒ None
|
||||
}
|
||||
|
|
@ -119,13 +120,12 @@ object AddressFromURIString {
|
|||
* Given an ActorPath it returns the Address and the path elements if the path is well-formed
|
||||
*/
|
||||
object ActorPathExtractor extends PathUtils {
|
||||
def unapply(addr: String): Option[(Address, Iterable[String])] =
|
||||
def unapply(addr: String): Option[(Address, immutable.Iterable[String])] =
|
||||
try {
|
||||
val uri = new URI(addr)
|
||||
if (uri.getPath == null) None
|
||||
else AddressFromURIString.unapply(uri) match {
|
||||
case None ⇒ None
|
||||
case Some(addr) ⇒ Some((addr, split(uri.getPath).drop(1)))
|
||||
uri.getRawPath match {
|
||||
case null ⇒ None
|
||||
case path ⇒ AddressFromURIString.unapply(uri).map((_, split(path).drop(1)))
|
||||
}
|
||||
} catch {
|
||||
case _: URISyntaxException ⇒ None
|
||||
|
|
|
|||
|
|
@ -4,13 +4,14 @@
|
|||
|
||||
package akka.actor
|
||||
|
||||
import scala.concurrent.util.Duration
|
||||
import scala.concurrent.duration.Duration
|
||||
import com.typesafe.config._
|
||||
import akka.routing._
|
||||
import akka.japi.Util.immutableSeq
|
||||
import java.util.concurrent.{ TimeUnit }
|
||||
import akka.util.WildcardTree
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
import annotation.tailrec
|
||||
import scala.annotation.tailrec
|
||||
|
||||
/**
|
||||
* This class represents deployment configuration for a given actor path. It is
|
||||
|
|
@ -79,7 +80,11 @@ trait Scope {
|
|||
@SerialVersionUID(1L)
|
||||
abstract class LocalScope extends Scope
|
||||
|
||||
//FIXME docs
|
||||
/**
|
||||
* The Local Scope is the default one, which is assumed on all deployments
|
||||
* which do not set a different scope. It is also the only scope handled by
|
||||
* the LocalActorRefProvider.
|
||||
*/
|
||||
case object LocalScope extends LocalScope {
|
||||
/**
|
||||
* Java API: get the singleton instance
|
||||
|
|
@ -134,16 +139,24 @@ private[akka] class Deployer(val settings: ActorSystem.Settings, val dynamicAcce
|
|||
}
|
||||
|
||||
def parseConfig(key: String, config: Config): Option[Deploy] = {
|
||||
|
||||
val deployment = config.withFallback(default)
|
||||
val router = createRouterConfig(deployment.getString("router"), key, config, deployment)
|
||||
Some(Deploy(key, deployment, router, NoScopeGiven))
|
||||
}
|
||||
|
||||
val routees = Vector() ++ deployment.getStringList("routees.paths").asScala
|
||||
|
||||
/**
|
||||
* Factory method for creating `RouterConfig`
|
||||
* @param routerType the configured name of the router, or FQCN
|
||||
* @param key the full configuration key of the deployment section
|
||||
* @param config the user defined config of the deployment, without defaults
|
||||
* @param deployment the deployment config, with defaults
|
||||
*/
|
||||
protected def createRouterConfig(routerType: String, key: String, config: Config, deployment: Config): RouterConfig = {
|
||||
val routees = immutableSeq(deployment.getStringList("routees.paths"))
|
||||
val nrOfInstances = deployment.getInt("nr-of-instances")
|
||||
val resizer = if (config.hasPath("resizer")) Some(DefaultResizer(deployment.getConfig("resizer"))) else None
|
||||
|
||||
val resizer: Option[Resizer] = if (config.hasPath("resizer")) Some(DefaultResizer(deployment.getConfig("resizer"))) else None
|
||||
|
||||
val router: RouterConfig = deployment.getString("router") match {
|
||||
routerType match {
|
||||
case "from-code" ⇒ NoRouter
|
||||
case "round-robin" ⇒ RoundRobinRouter(nrOfInstances, routees, resizer)
|
||||
case "random" ⇒ RandomRouter(nrOfInstances, routees, resizer)
|
||||
|
|
@ -156,7 +169,7 @@ private[akka] class Deployer(val settings: ActorSystem.Settings, val dynamicAcce
|
|||
val vnodes = deployment.getInt("virtual-nodes-factor")
|
||||
ConsistentHashingRouter(nrOfInstances, routees, resizer, virtualNodesFactor = vnodes)
|
||||
case fqn ⇒
|
||||
val args = Seq(classOf[Config] -> deployment)
|
||||
val args = List(classOf[Config] -> deployment)
|
||||
dynamicAccess.createInstanceFor[RouterConfig](fqn, args).recover({
|
||||
case exception ⇒ throw new IllegalArgumentException(
|
||||
("Cannot instantiate router [%s], defined in [%s], " +
|
||||
|
|
@ -165,7 +178,6 @@ private[akka] class Deployer(val settings: ActorSystem.Settings, val dynamicAcce
|
|||
.format(fqn, key), exception)
|
||||
}).get
|
||||
}
|
||||
}
|
||||
|
||||
Some(Deploy(key, deployment, router, NoScopeGiven))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
*/
|
||||
package akka.actor
|
||||
|
||||
import scala.util.control.NonFatal
|
||||
import scala.collection.immutable
|
||||
import java.lang.reflect.InvocationTargetException
|
||||
import scala.reflect.ClassTag
|
||||
import scala.util.Try
|
||||
|
|
@ -25,7 +25,7 @@ abstract class DynamicAccess {
|
|||
* val obj = DynamicAccess.createInstanceFor(clazz, Seq(classOf[Config] -> config, classOf[String] -> name))
|
||||
* }}}
|
||||
*/
|
||||
def createInstanceFor[T: ClassTag](clazz: Class[_], args: Seq[(Class[_], AnyRef)]): Try[T]
|
||||
def createInstanceFor[T: ClassTag](clazz: Class[_], args: immutable.Seq[(Class[_], AnyRef)]): Try[T]
|
||||
|
||||
/**
|
||||
* Obtain a `Class[_]` object loaded with the right class loader (i.e. the one
|
||||
|
|
@ -40,7 +40,7 @@ abstract class DynamicAccess {
|
|||
* `args` argument. The exact usage of args depends on which type is requested,
|
||||
* see the relevant requesting code for details.
|
||||
*/
|
||||
def createInstanceFor[T: ClassTag](fqcn: String, args: Seq[(Class[_], AnyRef)]): Try[T]
|
||||
def createInstanceFor[T: ClassTag](fqcn: String, args: immutable.Seq[(Class[_], AnyRef)]): Try[T]
|
||||
|
||||
/**
|
||||
* Obtain the Scala “object” instance for the given fully-qualified class name, if there is one.
|
||||
|
|
@ -70,7 +70,7 @@ class ReflectiveDynamicAccess(val classLoader: ClassLoader) extends DynamicAcces
|
|||
if (t.isAssignableFrom(c)) c else throw new ClassCastException(t + " is not assignable from " + c)
|
||||
})
|
||||
|
||||
override def createInstanceFor[T: ClassTag](clazz: Class[_], args: Seq[(Class[_], AnyRef)]): Try[T] =
|
||||
override def createInstanceFor[T: ClassTag](clazz: Class[_], args: immutable.Seq[(Class[_], AnyRef)]): Try[T] =
|
||||
Try {
|
||||
val types = args.map(_._1).toArray
|
||||
val values = args.map(_._2).toArray
|
||||
|
|
@ -81,7 +81,7 @@ class ReflectiveDynamicAccess(val classLoader: ClassLoader) extends DynamicAcces
|
|||
if (t.isInstance(obj)) obj.asInstanceOf[T] else throw new ClassCastException(clazz.getName + " is not a subtype of " + t)
|
||||
} recover { case i: InvocationTargetException if i.getTargetException ne null ⇒ throw i.getTargetException }
|
||||
|
||||
override def createInstanceFor[T: ClassTag](fqcn: String, args: Seq[(Class[_], AnyRef)]): Try[T] =
|
||||
override def createInstanceFor[T: ClassTag](fqcn: String, args: immutable.Seq[(Class[_], AnyRef)]): Try[T] =
|
||||
getClassFor(fqcn) flatMap { c ⇒ createInstanceFor(c, args) }
|
||||
|
||||
override def getObjectFor[T: ClassTag](fqcn: String): Try[T] = {
|
||||
|
|
|
|||
|
|
@ -98,5 +98,5 @@ abstract class ExtensionKey[T <: Extension](implicit m: ClassTag[T]) extends Ext
|
|||
def this(clazz: Class[T]) = this()(ClassTag(clazz))
|
||||
|
||||
override def lookup(): ExtensionId[T] = this
|
||||
def createExtension(system: ExtendedActorSystem): T = system.dynamicAccess.createInstanceFor[T](m.runtimeClass, Seq(classOf[ExtendedActorSystem] -> system)).get
|
||||
def createExtension(system: ExtendedActorSystem): T = system.dynamicAccess.createInstanceFor[T](m.runtimeClass, List(classOf[ExtendedActorSystem] -> system)).get
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ package akka.actor
|
|||
|
||||
import language.implicitConversions
|
||||
import akka.util._
|
||||
import scala.concurrent.util.Duration
|
||||
import scala.concurrent.duration.Duration
|
||||
import scala.collection.mutable
|
||||
import akka.routing.{ Deafen, Listen, Listeners }
|
||||
import scala.concurrent.util.FiniteDuration
|
||||
import scala.concurrent.duration.FiniteDuration
|
||||
|
||||
object FSM {
|
||||
|
||||
|
|
@ -238,7 +238,7 @@ object FSM {
|
|||
* setTimer("tock", TockMsg, 1 second, true) // repeating
|
||||
* setTimer("lifetime", TerminateMsg, 1 hour, false) // single-shot
|
||||
* cancelTimer("tock")
|
||||
* timerActive_? ("tock")
|
||||
* isTimerActive("tock")
|
||||
* </pre>
|
||||
*/
|
||||
trait FSM[S, D] extends Listeners with ActorLogging {
|
||||
|
|
@ -372,7 +372,15 @@ trait FSM[S, D] extends Listeners with ActorLogging {
|
|||
* timer does not exist, has previously been canceled or if it was a
|
||||
* single-shot timer whose message was already received.
|
||||
*/
|
||||
final def timerActive_?(name: String) = timers contains name
|
||||
@deprecated("Use isTimerActive(name) instead.", "2.2")
|
||||
final def timerActive_?(name: String) = isTimerActive(name)
|
||||
|
||||
/**
|
||||
* Inquire whether the named timer is still active. Returns true unless the
|
||||
* timer does not exist, has previously been canceled or if it was a
|
||||
* single-shot timer whose message was already received.
|
||||
*/
|
||||
final def isTimerActive(name: String) = timers contains name
|
||||
|
||||
/**
|
||||
* Set state timeout explicitly. This method can safely be used from within a
|
||||
|
|
@ -380,6 +388,11 @@ trait FSM[S, D] extends Listeners with ActorLogging {
|
|||
*/
|
||||
final def setStateTimeout(state: S, timeout: Timeout): Unit = stateTimeouts(state) = timeout
|
||||
|
||||
/**
|
||||
* Internal API, used for testing.
|
||||
*/
|
||||
private[akka] final def isStateTimerActive = timeoutFuture.isDefined
|
||||
|
||||
/**
|
||||
* Set handler which is called upon each state transition, i.e. not when
|
||||
* staying in the same state. This may use the pair extractor defined in the
|
||||
|
|
@ -427,6 +440,8 @@ trait FSM[S, D] extends Listeners with ActorLogging {
|
|||
/**
|
||||
* Set handler which is called upon reception of unhandled messages. Calling
|
||||
* this method again will overwrite the previous contents.
|
||||
*
|
||||
* The current state may be queried using ``stateName``.
|
||||
*/
|
||||
final def whenUnhandled(stateFunction: StateFunction): Unit =
|
||||
handleEvent = stateFunction orElse handleEventDefault
|
||||
|
|
@ -519,7 +534,7 @@ trait FSM[S, D] extends Listeners with ActorLogging {
|
|||
* Main actor receive() method
|
||||
* *******************************************
|
||||
*/
|
||||
override final def receive: Receive = {
|
||||
override def receive: Receive = {
|
||||
case TimeoutMarker(gen) ⇒
|
||||
if (generation == gen) {
|
||||
processMsg(StateTimeout, "state timeout")
|
||||
|
|
@ -632,6 +647,8 @@ trait FSM[S, D] extends Listeners with ActorLogging {
|
|||
case Failure(msg: AnyRef) ⇒ log.error(msg.toString)
|
||||
case _ ⇒
|
||||
}
|
||||
for (timer ← timers.values) timer.cancel()
|
||||
timers.clear()
|
||||
val stopEvent = StopEvent(reason, currentState.stateName, currentState.stateData)
|
||||
if (terminateEvent.isDefinedAt(stopEvent))
|
||||
terminateEvent(stopEvent)
|
||||
|
|
|
|||
|
|
@ -5,11 +5,13 @@ package akka.actor
|
|||
|
||||
import language.implicitConversions
|
||||
|
||||
import java.util.concurrent.TimeUnit
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
import scala.collection.JavaConversions._
|
||||
import java.lang.{ Iterable ⇒ JIterable }
|
||||
import scala.concurrent.util.Duration
|
||||
import java.util.concurrent.TimeUnit
|
||||
import akka.japi.Util.immutableSeq
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
import scala.collection.immutable
|
||||
import scala.concurrent.duration.Duration
|
||||
|
||||
/**
|
||||
* INTERNAL API
|
||||
*/
|
||||
|
|
@ -171,7 +173,7 @@ object SupervisorStrategy extends SupervisorStrategyLowPriorityImplicits {
|
|||
* Implicit conversion from `Seq` of Throwables to a `Decider`.
|
||||
* This maps the given Throwables to restarts, otherwise escalates.
|
||||
*/
|
||||
implicit def seqThrowable2Decider(trapExit: Seq[Class[_ <: Throwable]]): Decider = makeDecider(trapExit)
|
||||
implicit def seqThrowable2Decider(trapExit: immutable.Seq[Class[_ <: Throwable]]): Decider = makeDecider(trapExit)
|
||||
|
||||
type Decider = PartialFunction[Throwable, Directive]
|
||||
type JDecider = akka.japi.Function[Throwable, Directive]
|
||||
|
|
@ -181,21 +183,15 @@ object SupervisorStrategy extends SupervisorStrategyLowPriorityImplicits {
|
|||
* Decider builder which just checks whether one of
|
||||
* the given Throwables matches the cause and restarts, otherwise escalates.
|
||||
*/
|
||||
def makeDecider(trapExit: Array[Class[_]]): Decider =
|
||||
{ case x ⇒ if (trapExit exists (_ isInstance x)) Restart else Escalate }
|
||||
def makeDecider(trapExit: immutable.Seq[Class[_ <: Throwable]]): Decider = {
|
||||
case x ⇒ if (trapExit exists (_ isInstance x)) Restart else Escalate
|
||||
}
|
||||
|
||||
/**
|
||||
* Decider builder which just checks whether one of
|
||||
* the given Throwables matches the cause and restarts, otherwise escalates.
|
||||
*/
|
||||
def makeDecider(trapExit: Seq[Class[_ <: Throwable]]): Decider =
|
||||
{ case x ⇒ if (trapExit exists (_ isInstance x)) Restart else Escalate }
|
||||
|
||||
/**
|
||||
* Decider builder which just checks whether one of
|
||||
* the given Throwables matches the cause and restarts, otherwise escalates.
|
||||
*/
|
||||
def makeDecider(trapExit: JIterable[Class[_ <: Throwable]]): Decider = makeDecider(trapExit.toSeq)
|
||||
def makeDecider(trapExit: JIterable[Class[_ <: Throwable]]): Decider = makeDecider(immutableSeq(trapExit))
|
||||
|
||||
/**
|
||||
* Decider builder for Iterables of cause-directive pairs, e.g. a map obtained
|
||||
|
|
@ -220,20 +216,22 @@ object SupervisorStrategy extends SupervisorStrategyLowPriorityImplicits {
|
|||
*
|
||||
* INTERNAL API
|
||||
*/
|
||||
private[akka] def sort(in: Iterable[CauseDirective]): Seq[CauseDirective] =
|
||||
private[akka] def sort(in: Iterable[CauseDirective]): immutable.Seq[CauseDirective] =
|
||||
(new ArrayBuffer[CauseDirective](in.size) /: in) { (buf, ca) ⇒
|
||||
buf.indexWhere(_._1 isAssignableFrom ca._1) match {
|
||||
case -1 ⇒ buf append ca
|
||||
case x ⇒ buf insert (x, ca)
|
||||
}
|
||||
buf
|
||||
}
|
||||
}.to[immutable.IndexedSeq]
|
||||
|
||||
private[akka] def withinTimeRangeOption(withinTimeRange: Duration): Option[Duration] =
|
||||
if (withinTimeRange.isFinite && withinTimeRange >= Duration.Zero) Some(withinTimeRange) else None
|
||||
|
||||
private[akka] def maxNrOfRetriesOption(maxNrOfRetries: Int): Option[Int] =
|
||||
if (maxNrOfRetries < 0) None else Some(maxNrOfRetries)
|
||||
|
||||
private[akka] val escalateDefault = (_: Any) ⇒ Escalate
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -280,7 +278,7 @@ abstract class SupervisorStrategy {
|
|||
* @param children is a lazy collection (a view)
|
||||
*/
|
||||
def handleFailure(context: ActorContext, child: ActorRef, cause: Throwable, stats: ChildRestartStats, children: Iterable[ChildRestartStats]): Boolean = {
|
||||
val directive = if (decider.isDefinedAt(cause)) decider(cause) else Escalate //FIXME applyOrElse in Scala 2.10
|
||||
val directive = decider.applyOrElse(cause, escalateDefault)
|
||||
directive match {
|
||||
case Resume ⇒ resumeChild(child, cause); true
|
||||
case Restart ⇒ processFailure(context, true, child, cause, stats, children); true
|
||||
|
|
@ -334,10 +332,6 @@ case class AllForOneStrategy(maxNrOfRetries: Int = -1, withinTimeRange: Duration
|
|||
|
||||
def this(maxNrOfRetries: Int, withinTimeRange: Duration, trapExit: JIterable[Class[_ <: Throwable]]) =
|
||||
this(maxNrOfRetries, withinTimeRange)(SupervisorStrategy.makeDecider(trapExit))
|
||||
|
||||
def this(maxNrOfRetries: Int, withinTimeRange: Duration, trapExit: Array[Class[_]]) =
|
||||
this(maxNrOfRetries, withinTimeRange)(SupervisorStrategy.makeDecider(trapExit))
|
||||
|
||||
/*
|
||||
* this is a performance optimization to avoid re-allocating the pairs upon
|
||||
* every call to requestRestartPermission, assuming that strategies are shared
|
||||
|
|
@ -376,9 +370,6 @@ case class OneForOneStrategy(maxNrOfRetries: Int = -1, withinTimeRange: Duration
|
|||
def this(maxNrOfRetries: Int, withinTimeRange: Duration, trapExit: JIterable[Class[_ <: Throwable]]) =
|
||||
this(maxNrOfRetries, withinTimeRange)(SupervisorStrategy.makeDecider(trapExit))
|
||||
|
||||
def this(maxNrOfRetries: Int, withinTimeRange: Duration, trapExit: Array[Class[_]]) =
|
||||
this(maxNrOfRetries, withinTimeRange)(SupervisorStrategy.makeDecider(trapExit))
|
||||
|
||||
/*
|
||||
* this is a performance optimization to avoid re-allocating the pairs upon
|
||||
* every call to requestRestartPermission, assuming that strategies are shared
|
||||
|
|
|
|||
|
|
@ -6,8 +6,9 @@ package akka.actor
|
|||
import language.higherKinds
|
||||
import language.postfixOps
|
||||
|
||||
import scala.collection.immutable
|
||||
import scala.concurrent.{ ExecutionContext, Future }
|
||||
import scala.concurrent.util.Duration
|
||||
import scala.concurrent.duration.Duration
|
||||
import scala.util.control.NonFatal
|
||||
import akka.util.ByteString
|
||||
import java.net.{ SocketAddress, InetSocketAddress }
|
||||
|
|
@ -122,7 +123,7 @@ object IO {
|
|||
* @return a new SocketHandle that can be used to perform actions on the
|
||||
* new connection's SocketChannel.
|
||||
*/
|
||||
def accept(options: Seq[SocketOption] = Seq.empty)(implicit socketOwner: ActorRef): SocketHandle = {
|
||||
def accept(options: immutable.Seq[SocketOption] = Nil)(implicit socketOwner: ActorRef): SocketHandle = {
|
||||
val socket = SocketHandle(socketOwner, ioManager)
|
||||
ioManager ! Accept(socket, this, options)
|
||||
socket
|
||||
|
|
@ -250,7 +251,7 @@ object IO {
|
|||
*
|
||||
* Normally sent using IOManager.listen()
|
||||
*/
|
||||
case class Listen(server: ServerHandle, address: SocketAddress, options: Seq[ServerSocketOption] = Seq.empty) extends IOMessage
|
||||
case class Listen(server: ServerHandle, address: SocketAddress, options: immutable.Seq[ServerSocketOption] = Nil) extends IOMessage
|
||||
|
||||
/**
|
||||
* Message from an [[akka.actor.IOManager]] that the ServerSocketChannel is
|
||||
|
|
@ -272,7 +273,7 @@ object IO {
|
|||
*
|
||||
* Normally sent using [[akka.actor.IO.ServerHandle]].accept()
|
||||
*/
|
||||
case class Accept(socket: SocketHandle, server: ServerHandle, options: Seq[SocketOption] = Seq.empty) extends IOMessage
|
||||
case class Accept(socket: SocketHandle, server: ServerHandle, options: immutable.Seq[SocketOption] = Nil) extends IOMessage
|
||||
|
||||
/**
|
||||
* Message to an [[akka.actor.IOManager]] to create a SocketChannel connected
|
||||
|
|
@ -280,7 +281,7 @@ object IO {
|
|||
*
|
||||
* Normally sent using IOManager.connect()
|
||||
*/
|
||||
case class Connect(socket: SocketHandle, address: SocketAddress, options: Seq[SocketOption] = Seq.empty) extends IOMessage
|
||||
case class Connect(socket: SocketHandle, address: SocketAddress, options: immutable.Seq[SocketOption] = Nil) extends IOMessage
|
||||
|
||||
/**
|
||||
* Message from an [[akka.actor.IOManager]] that the SocketChannel has
|
||||
|
|
@ -832,7 +833,7 @@ final class IOManager private (system: ExtendedActorSystem) extends Extension {
|
|||
* @param option Seq of [[akka.actor.IO.ServerSocketOptions]] to setup on socket
|
||||
* @return a [[akka.actor.IO.ServerHandle]] to uniquely identify the created socket
|
||||
*/
|
||||
def listen(address: SocketAddress, options: Seq[IO.ServerSocketOption])(implicit owner: ActorRef): IO.ServerHandle = {
|
||||
def listen(address: SocketAddress, options: immutable.Seq[IO.ServerSocketOption])(implicit owner: ActorRef): IO.ServerHandle = {
|
||||
val server = IO.ServerHandle(owner, actor)
|
||||
actor ! IO.Listen(server, address, options)
|
||||
server
|
||||
|
|
@ -847,7 +848,7 @@ final class IOManager private (system: ExtendedActorSystem) extends Extension {
|
|||
* @param owner the ActorRef that will receive messages from the IOManagerActor
|
||||
* @return a [[akka.actor.IO.ServerHandle]] to uniquely identify the created socket
|
||||
*/
|
||||
def listen(address: SocketAddress)(implicit owner: ActorRef): IO.ServerHandle = listen(address, Seq.empty)
|
||||
def listen(address: SocketAddress)(implicit owner: ActorRef): IO.ServerHandle = listen(address, Nil)
|
||||
|
||||
/**
|
||||
* Create a ServerSocketChannel listening on a host and port. Messages will
|
||||
|
|
@ -860,7 +861,7 @@ final class IOManager private (system: ExtendedActorSystem) extends Extension {
|
|||
* @param owner the ActorRef that will receive messages from the IOManagerActor
|
||||
* @return a [[akka.actor.IO.ServerHandle]] to uniquely identify the created socket
|
||||
*/
|
||||
def listen(host: String, port: Int, options: Seq[IO.ServerSocketOption] = Seq.empty)(implicit owner: ActorRef): IO.ServerHandle =
|
||||
def listen(host: String, port: Int, options: immutable.Seq[IO.ServerSocketOption] = Nil)(implicit owner: ActorRef): IO.ServerHandle =
|
||||
listen(new InetSocketAddress(host, port), options)(owner)
|
||||
|
||||
/**
|
||||
|
|
@ -873,7 +874,7 @@ final class IOManager private (system: ExtendedActorSystem) extends Extension {
|
|||
* @param owner the ActorRef that will receive messages from the IOManagerActor
|
||||
* @return a [[akka.actor.IO.SocketHandle]] to uniquely identify the created socket
|
||||
*/
|
||||
def connect(address: SocketAddress, options: Seq[IO.SocketOption] = Seq.empty)(implicit owner: ActorRef): IO.SocketHandle = {
|
||||
def connect(address: SocketAddress, options: immutable.Seq[IO.SocketOption] = Nil)(implicit owner: ActorRef): IO.SocketHandle = {
|
||||
val socket = IO.SocketHandle(owner, actor)
|
||||
actor ! IO.Connect(socket, address, options)
|
||||
socket
|
||||
|
|
@ -991,7 +992,7 @@ final class IOManagerActor(val settings: Settings) extends Actor with ActorLoggi
|
|||
|
||||
private def forwardFailure(f: ⇒ Unit): Unit = try f catch { case NonFatal(e) ⇒ sender ! Status.Failure(e) }
|
||||
|
||||
private def setSocketOptions(socket: java.net.Socket, options: Seq[IO.SocketOption]) {
|
||||
private def setSocketOptions(socket: java.net.Socket, options: immutable.Seq[IO.SocketOption]) {
|
||||
options foreach {
|
||||
case IO.KeepAlive(on) ⇒ forwardFailure(socket.setKeepAlive(on))
|
||||
case IO.OOBInline(on) ⇒ forwardFailure(socket.setOOBInline(on))
|
||||
|
|
|
|||
|
|
@ -5,17 +5,18 @@
|
|||
package akka.actor
|
||||
|
||||
import java.io.ObjectStreamException
|
||||
import java.util.{ LinkedList ⇒ JLinkedList, ListIterator ⇒ JListIterator }
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import scala.collection.mutable.Queue
|
||||
import scala.concurrent.forkjoin.ThreadLocalRandom
|
||||
|
||||
import akka.actor.dungeon.ChildrenContainer
|
||||
import akka.dispatch.{ Envelope, Supervise, SystemMessage, Terminate }
|
||||
import akka.event.Logging.Warning
|
||||
import akka.util.Unsafe
|
||||
import akka.dispatch._
|
||||
import util.Try
|
||||
|
||||
/**
|
||||
* This actor ref starts out with some dummy cell (by default just enqueuing
|
||||
|
|
@ -32,17 +33,34 @@ private[akka] class RepointableActorRef(
|
|||
val path: ActorPath)
|
||||
extends ActorRefWithCell with RepointableRef {
|
||||
|
||||
import AbstractActorRef.cellOffset
|
||||
import AbstractActorRef.{ cellOffset, lookupOffset }
|
||||
|
||||
/*
|
||||
* H E R E B E D R A G O N S !
|
||||
*
|
||||
* There are two main functions of a Cell: message queueing and child lookup.
|
||||
* When switching out the UnstartedCell for its real replacement, the former
|
||||
* must be switched after all messages have been drained from the temporary
|
||||
* queue into the real mailbox, while the latter must be switched before
|
||||
* processing the very first message (i.e. before Cell.start()). Hence there
|
||||
* are two refs here, one for each function, and they are switched just so.
|
||||
*/
|
||||
@volatile private var _cellDoNotCallMeDirectly: Cell = _
|
||||
@volatile private var _lookupDoNotCallMeDirectly: Cell = _
|
||||
|
||||
def underlying: Cell = Unsafe.instance.getObjectVolatile(this, cellOffset).asInstanceOf[Cell]
|
||||
def lookup = Unsafe.instance.getObjectVolatile(this, lookupOffset).asInstanceOf[Cell]
|
||||
|
||||
@tailrec final def swapCell(next: Cell): Cell = {
|
||||
val old = underlying
|
||||
if (Unsafe.instance.compareAndSwapObject(this, cellOffset, old, next)) old else swapCell(next)
|
||||
}
|
||||
|
||||
@tailrec final def swapLookup(next: Cell): Cell = {
|
||||
val old = lookup
|
||||
if (Unsafe.instance.compareAndSwapObject(this, lookupOffset, old, next)) old else swapLookup(next)
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize: make a dummy cell which holds just a mailbox, then tell our
|
||||
* supervisor that we exist so that he can create the real Cell in
|
||||
|
|
@ -52,11 +70,16 @@ private[akka] class RepointableActorRef(
|
|||
*
|
||||
* This is protected so that others can have different initialization.
|
||||
*/
|
||||
def initialize(): this.type = {
|
||||
def initialize(async: Boolean): this.type =
|
||||
underlying match {
|
||||
case null ⇒
|
||||
val uid = ThreadLocalRandom.current.nextInt()
|
||||
swapCell(new UnstartedCell(system, this, props, supervisor, uid))
|
||||
supervisor.sendSystemMessage(Supervise(this, uid))
|
||||
swapLookup(underlying)
|
||||
supervisor.sendSystemMessage(Supervise(this, async, uid))
|
||||
if (!async) point()
|
||||
this
|
||||
case other ⇒ throw new IllegalStateException("initialize called more than once!")
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -65,21 +88,33 @@ private[akka] class RepointableActorRef(
|
|||
* modification of the `underlying` field, though it is safe to send messages
|
||||
* at any time.
|
||||
*/
|
||||
def activate(): this.type = {
|
||||
def point(): this.type =
|
||||
underlying match {
|
||||
case u: UnstartedCell ⇒ u.replaceWith(newCell(u))
|
||||
case _ ⇒ // this happens routinely for things which were created async=false
|
||||
}
|
||||
case u: UnstartedCell ⇒
|
||||
/*
|
||||
* The problem here was that if the real actor (which will start running
|
||||
* at cell.start()) creates children in its constructor, then this may
|
||||
* happen before the swapCell in u.replaceWith, meaning that those
|
||||
* children cannot be looked up immediately, e.g. if they shall become
|
||||
* routees.
|
||||
*/
|
||||
val cell = newCell(u)
|
||||
swapLookup(cell)
|
||||
cell.start()
|
||||
u.replaceWith(cell)
|
||||
this
|
||||
case null ⇒ throw new IllegalStateException("underlying cell is null")
|
||||
case _ ⇒ this // this happens routinely for things which were created async=false
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called by activate() to obtain the cell which is to replace the
|
||||
* unstarted cell. The cell must be fully functional.
|
||||
*/
|
||||
def newCell(old: Cell): Cell =
|
||||
new ActorCell(system, this, props, supervisor)
|
||||
.start(sendSupervise = false, old.asInstanceOf[UnstartedCell].uid)
|
||||
def newCell(old: UnstartedCell): Cell =
|
||||
new ActorCell(system, this, props, supervisor).init(old.uid, sendSupervise = false)
|
||||
|
||||
def start(): Unit = ()
|
||||
|
||||
def suspend(): Unit = underlying.suspend()
|
||||
|
||||
|
|
@ -89,7 +124,11 @@ private[akka] class RepointableActorRef(
|
|||
|
||||
def restart(cause: Throwable): Unit = underlying.restart(cause)
|
||||
|
||||
def isStarted: Boolean = !underlying.isInstanceOf[UnstartedCell]
|
||||
def isStarted: Boolean = underlying match {
|
||||
case _: UnstartedCell ⇒ false
|
||||
case null ⇒ throw new IllegalStateException("isStarted called before initialized")
|
||||
case _ ⇒ true
|
||||
}
|
||||
|
||||
def isTerminated: Boolean = underlying.isTerminated
|
||||
|
||||
|
|
@ -105,14 +144,14 @@ private[akka] class RepointableActorRef(
|
|||
case ".." ⇒ getParent.getChild(name)
|
||||
case "" ⇒ getChild(name)
|
||||
case other ⇒
|
||||
underlying.getChildByName(other) match {
|
||||
lookup.getChildByName(other) match {
|
||||
case Some(crs: ChildRestartStats) ⇒ crs.child.asInstanceOf[InternalActorRef].getChild(name)
|
||||
case _ ⇒ Nobody
|
||||
}
|
||||
}
|
||||
} else this
|
||||
|
||||
def !(message: Any)(implicit sender: ActorRef = null) = underlying.tell(message, sender)
|
||||
def !(message: Any)(implicit sender: ActorRef = Actor.noSender) = underlying.tell(message, sender)
|
||||
|
||||
def sendSystemMessage(message: SystemMessage) = underlying.sendSystemMessage(message)
|
||||
|
||||
|
|
@ -120,116 +159,116 @@ private[akka] class RepointableActorRef(
|
|||
protected def writeReplace(): AnyRef = SerializedActorRef(path)
|
||||
}
|
||||
|
||||
private[akka] class UnstartedCell(val systemImpl: ActorSystemImpl, val self: RepointableActorRef, val props: Props, val supervisor: InternalActorRef, val uid: Int)
|
||||
extends Cell {
|
||||
private[akka] class UnstartedCell(val systemImpl: ActorSystemImpl,
|
||||
val self: RepointableActorRef,
|
||||
val props: Props,
|
||||
val supervisor: InternalActorRef,
|
||||
val uid: Int) extends Cell {
|
||||
|
||||
/*
|
||||
* This lock protects all accesses to this cell’s queues. It also ensures
|
||||
* safe switching to the started ActorCell.
|
||||
*/
|
||||
val lock = new ReentrantLock
|
||||
private[this] final val lock = new ReentrantLock
|
||||
|
||||
// use Envelope to keep on-send checks in the same place
|
||||
val queue: Queue[Envelope] = Queue()
|
||||
val systemQueue: Queue[SystemMessage] = Queue()
|
||||
var suspendCount: Int = 0
|
||||
// use Envelope to keep on-send checks in the same place ACCESS MUST BE PROTECTED BY THE LOCK
|
||||
private[this] final val queue = new JLinkedList[Any]()
|
||||
|
||||
private def timeout = system.settings.UnstartedPushTimeout.duration.toMillis
|
||||
import systemImpl.settings.UnstartedPushTimeout.{ duration ⇒ timeout }
|
||||
|
||||
def replaceWith(cell: Cell): Unit = {
|
||||
lock.lock()
|
||||
def replaceWith(cell: Cell): Unit = locked {
|
||||
try {
|
||||
/*
|
||||
* The CallingThreadDispatcher nicely dives under the ReentrantLock and
|
||||
* breaks things by enqueueing into stale queues from within the message
|
||||
* processing which happens in-line for sendSystemMessage() and tell().
|
||||
* Since this is the only possible way to f*ck things up within this
|
||||
* lock, double-tap (well, N-tap, really); concurrent modification is
|
||||
* still not possible because 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)
|
||||
while (!queue.isEmpty) {
|
||||
queue.poll() match {
|
||||
case s: SystemMessage ⇒ cell.sendSystemMessage(s)
|
||||
case e: Envelope ⇒ cell.tell(e.message, e.sender)
|
||||
}
|
||||
}
|
||||
} finally try
|
||||
} finally {
|
||||
self.swapCell(cell)
|
||||
finally try
|
||||
for (_ ← 1 to suspendCount) cell.suspend()
|
||||
finally
|
||||
lock.unlock()
|
||||
}
|
||||
}
|
||||
|
||||
def system: ActorSystem = systemImpl
|
||||
def suspend(): Unit = {
|
||||
lock.lock()
|
||||
try suspendCount += 1
|
||||
finally lock.unlock()
|
||||
}
|
||||
def resume(causedByFailure: Throwable): Unit = {
|
||||
lock.lock()
|
||||
try suspendCount -= 1
|
||||
finally lock.unlock()
|
||||
}
|
||||
def restart(cause: Throwable): Unit = {
|
||||
lock.lock()
|
||||
try suspendCount -= 1
|
||||
finally lock.unlock()
|
||||
}
|
||||
def start(): this.type = this
|
||||
def suspend(): Unit = sendSystemMessage(Suspend())
|
||||
def resume(causedByFailure: Throwable): Unit = sendSystemMessage(Resume(causedByFailure))
|
||||
def restart(cause: Throwable): Unit = sendSystemMessage(Recreate(cause))
|
||||
def stop(): Unit = sendSystemMessage(Terminate())
|
||||
def isTerminated: Boolean = false
|
||||
def isTerminated: Boolean = locked {
|
||||
val cell = self.underlying
|
||||
if (cellIsReady(cell)) cell.isTerminated else false
|
||||
}
|
||||
def parent: InternalActorRef = supervisor
|
||||
def childrenRefs: ChildrenContainer = ChildrenContainer.EmptyChildrenContainer
|
||||
def getChildByName(name: String): Option[ChildRestartStats] = None
|
||||
|
||||
def tell(message: Any, sender: ActorRef): Unit = {
|
||||
if (lock.tryLock(timeout, TimeUnit.MILLISECONDS)) {
|
||||
val useSender = if (sender eq Actor.noSender) system.deadLetters else sender
|
||||
if (lock.tryLock(timeout.length, timeout.unit)) {
|
||||
try {
|
||||
if (self.underlying eq this) queue enqueue Envelope(message, sender, system)
|
||||
else self.underlying.tell(message, sender)
|
||||
} finally {
|
||||
lock.unlock()
|
||||
val cell = self.underlying
|
||||
if (cellIsReady(cell)) {
|
||||
cell.tell(message, useSender)
|
||||
} else if (!queue.offer(Envelope(message, useSender, system))) {
|
||||
system.eventStream.publish(Warning(self.path.toString, getClass, "dropping message of type " + message.getClass + " due to enqueue failure"))
|
||||
system.deadLetters ! DeadLetter(message, useSender, self)
|
||||
}
|
||||
} finally lock.unlock()
|
||||
} else {
|
||||
system.deadLetters ! DeadLetter(message, sender, self)
|
||||
}
|
||||
}
|
||||
def sendSystemMessage(msg: SystemMessage): Unit = {
|
||||
if (lock.tryLock(timeout, TimeUnit.MILLISECONDS)) {
|
||||
try {
|
||||
if (self.underlying eq this) systemQueue enqueue msg
|
||||
else self.underlying.sendSystemMessage(msg)
|
||||
} finally {
|
||||
lock.unlock()
|
||||
}
|
||||
} else {
|
||||
// FIXME: once we have guaranteed delivery of system messages, hook this in!
|
||||
system.eventStream.publish(Warning(self.path.toString, getClass, "dropping system message " + msg + " due to lock timeout"))
|
||||
system.deadLetters ! DeadLetter(msg, self, self)
|
||||
}
|
||||
}
|
||||
def isLocal = true
|
||||
def hasMessages: Boolean = {
|
||||
lock.lock()
|
||||
try {
|
||||
if (self.underlying eq this) !queue.isEmpty
|
||||
else self.underlying.hasMessages
|
||||
} finally {
|
||||
lock.unlock()
|
||||
}
|
||||
}
|
||||
def numberOfMessages: Int = {
|
||||
lock.lock()
|
||||
try {
|
||||
if (self.underlying eq this) queue.size
|
||||
else self.underlying.numberOfMessages
|
||||
} finally {
|
||||
lock.unlock()
|
||||
system.eventStream.publish(Warning(self.path.toString, getClass, "dropping message of type" + message.getClass + " due to lock timeout"))
|
||||
system.deadLetters ! DeadLetter(message, useSender, self)
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: once we have guaranteed delivery of system messages, hook this in!
|
||||
def sendSystemMessage(msg: SystemMessage): Unit =
|
||||
if (lock.tryLock(timeout.length, timeout.unit)) {
|
||||
try {
|
||||
val cell = self.underlying
|
||||
if (cellIsReady(cell)) {
|
||||
cell.sendSystemMessage(msg)
|
||||
} else {
|
||||
// systemMessages that are sent during replace need to jump to just after the last system message in the queue, so it's processed before other messages
|
||||
val wasEnqueued = if ((self.lookup ne this) && (self.underlying eq this) && !queue.isEmpty()) {
|
||||
@tailrec def tryEnqueue(i: JListIterator[Any] = queue.listIterator(), insertIntoIndex: Int = -1): Boolean =
|
||||
if (i.hasNext())
|
||||
tryEnqueue(i,
|
||||
if (i.next().isInstanceOf[SystemMessage]) i.nextIndex() // update last sysmsg seen so far
|
||||
else insertIntoIndex) // or just keep the last seen one
|
||||
else if (insertIntoIndex == -1) queue.offer(msg)
|
||||
else Try(queue.add(insertIntoIndex, msg)).isSuccess
|
||||
tryEnqueue()
|
||||
} else queue.offer(msg)
|
||||
|
||||
if (!wasEnqueued) {
|
||||
system.eventStream.publish(Warning(self.path.toString, getClass, "dropping system message " + msg + " due to enqueue failure"))
|
||||
system.deadLetters ! DeadLetter(msg, self, self)
|
||||
}
|
||||
}
|
||||
} finally lock.unlock()
|
||||
} else {
|
||||
system.eventStream.publish(Warning(self.path.toString, getClass, "dropping system message " + msg + " due to lock timeout"))
|
||||
system.deadLetters ! DeadLetter(msg, self, self)
|
||||
}
|
||||
|
||||
def isLocal = true
|
||||
|
||||
private[this] final def cellIsReady(cell: Cell): Boolean = (cell ne this) && (cell ne null)
|
||||
|
||||
def hasMessages: Boolean = locked {
|
||||
val cell = self.underlying
|
||||
if (cellIsReady(cell)) cell.hasMessages else !queue.isEmpty
|
||||
}
|
||||
|
||||
def numberOfMessages: Int = locked {
|
||||
val cell = self.underlying
|
||||
if (cellIsReady(cell)) cell.numberOfMessages else queue.size
|
||||
}
|
||||
|
||||
private[this] final def locked[T](body: ⇒ T): T = {
|
||||
lock.lock()
|
||||
try body finally lock.unlock()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,17 +4,18 @@
|
|||
|
||||
package akka.actor
|
||||
|
||||
import scala.concurrent.util.Duration
|
||||
import scala.concurrent.duration.Duration
|
||||
import akka.util.internal.{ TimerTask, HashedWheelTimer, Timeout ⇒ HWTimeout, Timer }
|
||||
import akka.event.LoggingAdapter
|
||||
import akka.dispatch.MessageDispatcher
|
||||
import java.io.Closeable
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
import java.util.concurrent.atomic.{ AtomicReference, AtomicLong }
|
||||
import scala.annotation.tailrec
|
||||
import akka.util.internal._
|
||||
import concurrent.ExecutionContext
|
||||
import scala.concurrent.util.FiniteDuration
|
||||
import scala.concurrent.duration.FiniteDuration
|
||||
|
||||
// The Scheduler trait is included in the documentation. KEEP THE LINES SHORT!!!
|
||||
//#scheduler
|
||||
/**
|
||||
* An Akka scheduler service. This one needs one special behavior: if
|
||||
|
|
@ -50,7 +51,8 @@ trait Scheduler {
|
|||
*/
|
||||
def schedule(
|
||||
initialDelay: FiniteDuration,
|
||||
interval: FiniteDuration)(f: ⇒ Unit)(implicit executor: ExecutionContext): Cancellable
|
||||
interval: FiniteDuration)(f: ⇒ Unit)(
|
||||
implicit executor: ExecutionContext): Cancellable
|
||||
|
||||
/**
|
||||
* Schedules a function to be run repeatedly with an initial delay and
|
||||
|
|
@ -93,7 +95,8 @@ trait Scheduler {
|
|||
* Scala API
|
||||
*/
|
||||
def scheduleOnce(
|
||||
delay: FiniteDuration)(f: ⇒ Unit)(implicit executor: ExecutionContext): Cancellable
|
||||
delay: FiniteDuration)(f: ⇒ Unit)(
|
||||
implicit executor: ExecutionContext): Cancellable
|
||||
}
|
||||
//#scheduler
|
||||
|
||||
|
|
@ -137,14 +140,17 @@ class DefaultScheduler(hashedWheelTimer: HashedWheelTimer, log: LoggingAdapter)
|
|||
val continuousCancellable = new ContinuousCancellable
|
||||
continuousCancellable.init(
|
||||
hashedWheelTimer.newTimeout(
|
||||
new TimerTask with ContinuousScheduling {
|
||||
new AtomicLong(System.nanoTime + initialDelay.toNanos) with TimerTask with ContinuousScheduling {
|
||||
def run(timeout: HWTimeout) {
|
||||
executor execute new Runnable {
|
||||
override def run = {
|
||||
receiver ! message
|
||||
// Check if the receiver is still alive and kicking before reschedule the task
|
||||
if (receiver.isTerminated) log.debug("Could not reschedule message to be sent because receiving actor {} has been terminated.", receiver)
|
||||
else scheduleNext(timeout, delay, continuousCancellable)
|
||||
else {
|
||||
val driftNanos = System.nanoTime - getAndAdd(delay.toNanos)
|
||||
scheduleNext(timeout, Duration.fromNanos(Math.max(delay.toNanos - driftNanos, 1)), continuousCancellable)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -162,11 +168,12 @@ class DefaultScheduler(hashedWheelTimer: HashedWheelTimer, log: LoggingAdapter)
|
|||
val continuousCancellable = new ContinuousCancellable
|
||||
continuousCancellable.init(
|
||||
hashedWheelTimer.newTimeout(
|
||||
new TimerTask with ContinuousScheduling {
|
||||
new AtomicLong(System.nanoTime + initialDelay.toNanos) with TimerTask with ContinuousScheduling {
|
||||
override def run(timeout: HWTimeout): Unit = executor.execute(new Runnable {
|
||||
override def run = {
|
||||
runnable.run()
|
||||
scheduleNext(timeout, delay, continuousCancellable)
|
||||
val driftNanos = System.nanoTime - getAndAdd(delay.toNanos)
|
||||
scheduleNext(timeout, Duration.fromNanos(Math.max(delay.toNanos - driftNanos, 1)), continuousCancellable)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
|
@ -199,8 +206,8 @@ class DefaultScheduler(hashedWheelTimer: HashedWheelTimer, log: LoggingAdapter)
|
|||
}
|
||||
|
||||
override def close(): Unit = {
|
||||
import scala.collection.JavaConverters._
|
||||
hashedWheelTimer.stop().asScala foreach execDirectly
|
||||
val i = hashedWheelTimer.stop().iterator()
|
||||
while (i.hasNext) execDirectly(i.next())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,13 +16,13 @@ import akka.AkkaException
|
|||
* def receive = {
|
||||
* case "open" ⇒
|
||||
* unstashAll()
|
||||
* context.become {
|
||||
* context.become({
|
||||
* case "write" ⇒ // do writing...
|
||||
* case "close" ⇒
|
||||
* unstashAll()
|
||||
* context.unbecome()
|
||||
* case msg ⇒ stash()
|
||||
* }
|
||||
* }, discardOld = false)
|
||||
* case "done" ⇒ // done
|
||||
* case msg ⇒ stash()
|
||||
* }
|
||||
|
|
|
|||
|
|
@ -4,22 +4,25 @@
|
|||
package akka.actor
|
||||
|
||||
import language.existentials
|
||||
import akka.japi.{ Creator, Option ⇒ JOption }
|
||||
import java.lang.reflect.{ InvocationTargetException, Method, InvocationHandler, Proxy }
|
||||
import akka.util.Timeout
|
||||
|
||||
import scala.util.control.NonFatal
|
||||
import scala.concurrent.util.Duration
|
||||
import scala.util.{ Try, Success, Failure }
|
||||
import scala.collection.immutable
|
||||
import scala.concurrent.duration.FiniteDuration
|
||||
import scala.concurrent.duration.Duration
|
||||
import scala.reflect.ClassTag
|
||||
import scala.concurrent.{ Await, Future }
|
||||
import akka.japi.{ Creator, Option ⇒ JOption }
|
||||
import akka.japi.Util.{ immutableSeq, immutableSingletonSeq }
|
||||
import akka.util.Timeout
|
||||
import akka.util.Reflect.instantiator
|
||||
import akka.serialization.{ JavaSerializer, SerializationExtension }
|
||||
import akka.dispatch._
|
||||
import java.util.concurrent.atomic.{ AtomicReference ⇒ AtomVar }
|
||||
import java.util.concurrent.TimeoutException
|
||||
import java.util.concurrent.TimeUnit.MILLISECONDS
|
||||
import scala.reflect.ClassTag
|
||||
import akka.serialization.{ JavaSerializer, SerializationExtension }
|
||||
import java.io.ObjectStreamException
|
||||
import scala.util.{ Try, Success, Failure }
|
||||
import scala.concurrent.util.FiniteDuration
|
||||
import java.lang.reflect.{ InvocationTargetException, Method, InvocationHandler, Proxy }
|
||||
|
||||
/**
|
||||
* A TypedActorFactory is something that can created TypedActor instances.
|
||||
|
|
@ -439,8 +442,8 @@ object TypedProps {
|
|||
* @return a sequence of interfaces that the specified class implements,
|
||||
* or a sequence containing only itself, if itself is an interface.
|
||||
*/
|
||||
def extractInterfaces(clazz: Class[_]): Seq[Class[_]] =
|
||||
if (clazz.isInterface) Seq[Class[_]](clazz) else clazz.getInterfaces.toList
|
||||
def extractInterfaces(clazz: Class[_]): immutable.Seq[Class[_]] =
|
||||
if (clazz.isInterface) immutableSingletonSeq(clazz) else immutableSeq(clazz.getInterfaces)
|
||||
|
||||
/**
|
||||
* Uses the supplied class as the factory for the TypedActor implementation,
|
||||
|
|
@ -489,7 +492,7 @@ object TypedProps {
|
|||
*/
|
||||
@SerialVersionUID(1L)
|
||||
case class TypedProps[T <: AnyRef] protected[TypedProps] (
|
||||
interfaces: Seq[Class[_]],
|
||||
interfaces: immutable.Seq[Class[_]],
|
||||
creator: () ⇒ T,
|
||||
dispatcher: String = TypedProps.defaultDispatcherId,
|
||||
deploy: Deploy = Props.defaultDeploy,
|
||||
|
|
@ -607,8 +610,7 @@ class TypedActorExtension(system: ExtendedActorSystem) extends TypedActorFactory
|
|||
protected def actorFactory: ActorRefFactory = system
|
||||
protected def typedActor = this
|
||||
|
||||
val serialization = SerializationExtension(system)
|
||||
val settings = system.settings
|
||||
import system.settings
|
||||
|
||||
/**
|
||||
* Default timeout for typed actor methods with non-void return type
|
||||
|
|
@ -635,22 +637,17 @@ class TypedActorExtension(system: ExtendedActorSystem) extends TypedActorFactory
|
|||
private[akka] def createActorRefProxy[R <: AnyRef, T <: R](props: TypedProps[T], proxyVar: AtomVar[R], actorRef: ⇒ ActorRef): R = {
|
||||
//Warning, do not change order of the following statements, it's some elaborate chicken-n-egg handling
|
||||
val actorVar = new AtomVar[ActorRef](null)
|
||||
val classLoader: ClassLoader = if (props.loader.nonEmpty) props.loader.get else props.interfaces.headOption.map(_.getClassLoader).orNull //If we have no loader, we arbitrarily take the loader of the first interface
|
||||
val proxy = Proxy.newProxyInstance(
|
||||
classLoader,
|
||||
(props.loader orElse props.interfaces.collectFirst { case any ⇒ any.getClassLoader }).orNull, //If we have no loader, we arbitrarily take the loader of the first interface
|
||||
props.interfaces.toArray,
|
||||
new TypedActorInvocationHandler(
|
||||
this,
|
||||
actorVar,
|
||||
if (props.timeout.isDefined) props.timeout.get else DefaultReturnTimeout)).asInstanceOf[R]
|
||||
new TypedActorInvocationHandler(this, actorVar, props.timeout getOrElse DefaultReturnTimeout)).asInstanceOf[R]
|
||||
|
||||
proxyVar match {
|
||||
case null ⇒
|
||||
actorVar.set(actorRef)
|
||||
if (proxyVar eq null) {
|
||||
actorVar set actorRef
|
||||
proxy
|
||||
case _ ⇒
|
||||
proxyVar.set(proxy) // Chicken and egg situation we needed to solve, set the proxy so that we can set the self-reference inside each receive
|
||||
actorVar.set(actorRef) //Make sure the InvocationHandler gets ahold of the actor reference, this is not a problem since the proxy hasn't escaped this method yet
|
||||
} else {
|
||||
proxyVar set proxy // Chicken and egg situation we needed to solve, set the proxy so that we can set the self-reference inside each receive
|
||||
actorVar set actorRef //Make sure the InvocationHandler gets ahold of the actor reference, this is not a problem since the proxy hasn't escaped this method yet
|
||||
proxyVar.get
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ import akka.japi.{ Creator }
|
|||
* }
|
||||
* }
|
||||
*
|
||||
* private static SupervisorStrategy strategy = new OneForOneStrategy(10, Duration.parse("1 minute"),
|
||||
* private static SupervisorStrategy strategy = new OneForOneStrategy(10, Duration.create("1 minute"),
|
||||
* new Function<Throwable, Directive>() {
|
||||
* @Override
|
||||
* public Directive apply(Throwable t) {
|
||||
|
|
|
|||
|
|
@ -6,10 +6,8 @@ package akka.actor.dsl
|
|||
|
||||
import scala.concurrent.Await
|
||||
import akka.actor.ActorLogging
|
||||
import scala.concurrent.util.Deadline
|
||||
import scala.collection.immutable.TreeSet
|
||||
import scala.concurrent.util.{ Duration, FiniteDuration }
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.duration._
|
||||
import akka.actor.Cancellable
|
||||
import akka.actor.{ Actor, Stash, SupervisorStrategy }
|
||||
import scala.collection.mutable.Queue
|
||||
|
|
@ -31,7 +29,9 @@ trait Creators { this: ActorDSL.type ⇒
|
|||
* for quickly trying things out in the REPL. It makes the following keywords
|
||||
* available:
|
||||
*
|
||||
* - `become` mapped to `context.become(_, discardOld = false)`
|
||||
* - `become` mapped to `context.become(_, discardOld = true)`
|
||||
*
|
||||
* - `becomeStacked` mapped to `context.become(_, discardOld = false)`
|
||||
*
|
||||
* - `unbecome` mapped to `context.unbecome`
|
||||
*
|
||||
|
|
@ -89,7 +89,14 @@ trait Creators { this: ActorDSL.type ⇒
|
|||
* stack is cleared upon restart. Use `unbecome()` to pop an element off
|
||||
* this stack.
|
||||
*/
|
||||
def become(r: Receive) = context.become(r, discardOld = false)
|
||||
def becomeStacked(r: Receive) = context.become(r, discardOld = false)
|
||||
|
||||
/**
|
||||
* Replace the behavior at the top of the behavior stack for this actor. The
|
||||
* stack is cleared upon restart. Use `unbecome()` to pop an element off
|
||||
* this stack or `becomeStacked()` to push a new element on top of it.
|
||||
*/
|
||||
def become(r: Receive) = context.become(r, discardOld = true)
|
||||
|
||||
/**
|
||||
* Pop the active behavior from the behavior stack of this actor. This stack
|
||||
|
|
|
|||
|
|
@ -6,10 +6,8 @@ package akka.actor.dsl
|
|||
|
||||
import scala.concurrent.Await
|
||||
import akka.actor.ActorLogging
|
||||
import scala.concurrent.util.Deadline
|
||||
import scala.collection.immutable.TreeSet
|
||||
import scala.concurrent.util.{ Duration, FiniteDuration }
|
||||
import scala.concurrent.util.duration._
|
||||
import scala.concurrent.duration._
|
||||
import akka.actor.Cancellable
|
||||
import akka.actor.Actor
|
||||
import scala.collection.mutable.Queue
|
||||
|
|
@ -129,10 +127,10 @@ trait Inbox { this: ActorDSL.type ⇒
|
|||
val next = clientsByTimeout.head.deadline
|
||||
import context.dispatcher
|
||||
if (currentDeadline.isEmpty) {
|
||||
currentDeadline = Some((next, context.system.scheduler.scheduleOnce(next.timeLeft.asInstanceOf[FiniteDuration], self, Kick)))
|
||||
currentDeadline = Some((next, context.system.scheduler.scheduleOnce(next.timeLeft, self, Kick)))
|
||||
} else if (currentDeadline.get._1 != next) {
|
||||
currentDeadline.get._2.cancel()
|
||||
currentDeadline = Some((next, context.system.scheduler.scheduleOnce(next.timeLeft.asInstanceOf[FiniteDuration], self, Kick)))
|
||||
currentDeadline = Some((next, context.system.scheduler.scheduleOnce(next.timeLeft, self, Kick)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -169,7 +167,7 @@ trait Inbox { this: ActorDSL.type ⇒
|
|||
* this method within an actor!</b>
|
||||
*/
|
||||
def receive(timeout: FiniteDuration = defaultTimeout): Any = {
|
||||
implicit val t = Timeout((timeout + extraTime).asInstanceOf[FiniteDuration])
|
||||
implicit val t = Timeout(timeout + extraTime)
|
||||
Await.result(receiver ? Get(Deadline.now + timeout), Duration.Inf)
|
||||
}
|
||||
|
||||
|
|
@ -186,7 +184,7 @@ trait Inbox { this: ActorDSL.type ⇒
|
|||
* this method within an actor!</b>
|
||||
*/
|
||||
def select[T](timeout: FiniteDuration = defaultTimeout)(predicate: PartialFunction[Any, T]): T = {
|
||||
implicit val t = Timeout((timeout + extraTime).asInstanceOf[FiniteDuration])
|
||||
implicit val t = Timeout(timeout + extraTime)
|
||||
predicate(Await.result(receiver ? Select(Deadline.now + timeout, predicate), Duration.Inf))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,14 +5,12 @@
|
|||
package akka.actor.dungeon
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import scala.collection.JavaConverters.asJavaIterableConverter
|
||||
import scala.util.control.NonFatal
|
||||
import scala.collection.immutable
|
||||
import akka.actor._
|
||||
import akka.actor.ActorCell
|
||||
import akka.actor.ActorPath.ElementRegex
|
||||
import akka.serialization.SerializationExtension
|
||||
import akka.util.{ Unsafe, Helpers }
|
||||
import akka.actor.ChildNameReserved
|
||||
|
||||
private[akka] trait Children { this: ActorCell ⇒
|
||||
|
||||
|
|
@ -24,8 +22,9 @@ private[akka] trait Children { this: ActorCell ⇒
|
|||
def childrenRefs: ChildrenContainer =
|
||||
Unsafe.instance.getObjectVolatile(this, AbstractActorCell.childrenOffset).asInstanceOf[ChildrenContainer]
|
||||
|
||||
final def children: Iterable[ActorRef] = childrenRefs.children
|
||||
final def getChildren(): java.lang.Iterable[ActorRef] = children.asJava
|
||||
final def children: immutable.Iterable[ActorRef] = childrenRefs.children
|
||||
final def getChildren(): java.lang.Iterable[ActorRef] =
|
||||
scala.collection.JavaConverters.asJavaIterableConverter(children).asJava
|
||||
|
||||
final def child(name: String): Option[ActorRef] = Option(getChild(name))
|
||||
final def getChild(name: String): ActorRef = childrenRefs.getByName(name) match {
|
||||
|
|
@ -53,19 +52,24 @@ private[akka] trait Children { this: ActorCell ⇒
|
|||
}
|
||||
|
||||
final def stop(actor: ActorRef): Unit = {
|
||||
val started = actor match {
|
||||
if (childrenRefs.getByRef(actor).isDefined) {
|
||||
@tailrec def shallDie(ref: ActorRef): Boolean = {
|
||||
val c = childrenRefs
|
||||
swapChildrenRefs(c, c.shallDie(ref)) || shallDie(ref)
|
||||
}
|
||||
|
||||
if (actor match {
|
||||
case r: RepointableRef ⇒ r.isStarted
|
||||
case _ ⇒ true
|
||||
}) shallDie(actor)
|
||||
}
|
||||
if (childrenRefs.getByRef(actor).isDefined && started) shallDie(actor)
|
||||
actor.asInstanceOf[InternalActorRef].stop()
|
||||
}
|
||||
|
||||
/*
|
||||
* low level CAS helpers
|
||||
*/
|
||||
|
||||
@inline private def swapChildrenRefs(oldChildren: ChildrenContainer, newChildren: ChildrenContainer): Boolean =
|
||||
@inline private final def swapChildrenRefs(oldChildren: ChildrenContainer, newChildren: ChildrenContainer): Boolean =
|
||||
Unsafe.instance.compareAndSwapObject(this, AbstractActorCell.childrenOffset, oldChildren, newChildren)
|
||||
|
||||
@tailrec final def reserveChild(name: String): Boolean = {
|
||||
|
|
@ -90,18 +94,6 @@ private[akka] trait Children { this: ActorCell ⇒
|
|||
}
|
||||
}
|
||||
|
||||
@tailrec final protected def shallDie(ref: ActorRef): Boolean = {
|
||||
val c = childrenRefs
|
||||
swapChildrenRefs(c, c.shallDie(ref)) || shallDie(ref)
|
||||
}
|
||||
|
||||
@tailrec final private def removeChild(ref: ActorRef): ChildrenContainer = {
|
||||
val c = childrenRefs
|
||||
val n = c.remove(ref)
|
||||
if (swapChildrenRefs(c, n)) n
|
||||
else removeChild(ref)
|
||||
}
|
||||
|
||||
@tailrec final protected def setChildrenTerminationReason(reason: ChildrenContainer.SuspendReason): Boolean = {
|
||||
childrenRefs match {
|
||||
case c: ChildrenContainer.TerminatingChildrenContainer ⇒
|
||||
|
|
@ -141,13 +133,21 @@ private[akka] trait Children { this: ActorCell ⇒
|
|||
|
||||
protected def getChildByRef(ref: ActorRef): Option[ChildRestartStats] = childrenRefs.getByRef(ref)
|
||||
|
||||
protected def getAllChildStats: Iterable[ChildRestartStats] = childrenRefs.stats
|
||||
protected def getAllChildStats: immutable.Iterable[ChildRestartStats] = childrenRefs.stats
|
||||
|
||||
protected def removeChildAndGetStateChange(child: ActorRef): Option[SuspendReason] = {
|
||||
childrenRefs match {
|
||||
@tailrec def removeChild(ref: ActorRef): ChildrenContainer = {
|
||||
val c = childrenRefs
|
||||
val n = c.remove(ref)
|
||||
if (swapChildrenRefs(c, n)) n else removeChild(ref)
|
||||
}
|
||||
|
||||
childrenRefs match { // The match must be performed BEFORE the removeChild
|
||||
case TerminatingChildrenContainer(_, _, reason) ⇒
|
||||
val newContainer = removeChild(child)
|
||||
if (!newContainer.isInstanceOf[TerminatingChildrenContainer]) Some(reason) else None
|
||||
removeChild(child) match {
|
||||
case _: TerminatingChildrenContainer ⇒ None
|
||||
case _ ⇒ Some(reason)
|
||||
}
|
||||
case _ ⇒
|
||||
removeChild(child)
|
||||
None
|
||||
|
|
@ -192,6 +192,7 @@ private[akka] trait Children { this: ActorCell ⇒
|
|||
// mailbox==null during RoutedActorCell constructor, where suspends are queued otherwise
|
||||
if (mailbox ne null) for (_ ← 1 to mailbox.suspendCount) actor.suspend()
|
||||
initChild(actor)
|
||||
actor.start()
|
||||
actor
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,10 +4,11 @@
|
|||
|
||||
package akka.actor.dungeon
|
||||
|
||||
import scala.collection.immutable.TreeMap
|
||||
import scala.collection.immutable
|
||||
|
||||
import akka.actor.{ InvalidActorNameException, ChildStats, ChildRestartStats, ChildNameReserved, ActorRef }
|
||||
import akka.dispatch.SystemMessage
|
||||
import akka.util.Collections.{ EmptyImmutableSeq, PartialImmutableValuesIterable }
|
||||
|
||||
/**
|
||||
* INTERNAL API
|
||||
|
|
@ -20,8 +21,8 @@ private[akka] trait ChildrenContainer {
|
|||
def getByName(name: String): Option[ChildStats]
|
||||
def getByRef(actor: ActorRef): Option[ChildRestartStats]
|
||||
|
||||
def children: Iterable[ActorRef]
|
||||
def stats: Iterable[ChildRestartStats]
|
||||
def children: immutable.Iterable[ActorRef]
|
||||
def stats: immutable.Iterable[ChildRestartStats]
|
||||
|
||||
def shallDie(actor: ActorRef): ChildrenContainer
|
||||
|
||||
|
|
@ -49,6 +50,18 @@ private[akka] object ChildrenContainer {
|
|||
case class Creation() extends SuspendReason with WaitingForChildren
|
||||
case object Termination extends SuspendReason
|
||||
|
||||
class ChildRestartsIterable(stats: immutable.MapLike[_, ChildStats, _]) extends PartialImmutableValuesIterable[ChildStats, ChildRestartStats] {
|
||||
override final def apply(c: ChildStats) = c.asInstanceOf[ChildRestartStats]
|
||||
override final def isDefinedAt(c: ChildStats) = c.isInstanceOf[ChildRestartStats]
|
||||
override final def valuesIterator = stats.valuesIterator
|
||||
}
|
||||
|
||||
class ChildrenIterable(stats: immutable.MapLike[_, ChildStats, _]) extends PartialImmutableValuesIterable[ChildStats, ActorRef] {
|
||||
override final def apply(c: ChildStats) = c.asInstanceOf[ChildRestartStats].child
|
||||
override final def isDefinedAt(c: ChildStats) = c.isInstanceOf[ChildRestartStats]
|
||||
override final def valuesIterator = stats.valuesIterator
|
||||
}
|
||||
|
||||
trait WaitingForChildren {
|
||||
private var todo: SystemMessage = null
|
||||
def enqueue(message: SystemMessage) = { message.next = todo; todo = message }
|
||||
|
|
@ -56,13 +69,13 @@ private[akka] object ChildrenContainer {
|
|||
}
|
||||
|
||||
trait EmptyChildrenContainer extends ChildrenContainer {
|
||||
val emptyStats = TreeMap.empty[String, ChildStats]
|
||||
val emptyStats = immutable.TreeMap.empty[String, ChildStats]
|
||||
override def add(name: String, stats: ChildRestartStats): ChildrenContainer = new NormalChildrenContainer(emptyStats.updated(name, stats))
|
||||
override def remove(child: ActorRef): ChildrenContainer = this
|
||||
override def getByName(name: String): Option[ChildRestartStats] = None
|
||||
override def getByRef(actor: ActorRef): Option[ChildRestartStats] = None
|
||||
override def children: Iterable[ActorRef] = Nil
|
||||
override def stats: Iterable[ChildRestartStats] = Nil
|
||||
override def children: immutable.Iterable[ActorRef] = EmptyImmutableSeq
|
||||
override def stats: immutable.Iterable[ChildRestartStats] = EmptyImmutableSeq
|
||||
override def shallDie(actor: ActorRef): ChildrenContainer = this
|
||||
override def reserve(name: String): ChildrenContainer = new NormalChildrenContainer(emptyStats.updated(name, ChildNameReserved))
|
||||
override def unreserve(name: String): ChildrenContainer = this
|
||||
|
|
@ -95,7 +108,7 @@ private[akka] object ChildrenContainer {
|
|||
* calling context.stop(child) and processing the ChildTerminated() system
|
||||
* message).
|
||||
*/
|
||||
class NormalChildrenContainer(val c: TreeMap[String, ChildStats]) extends ChildrenContainer {
|
||||
class NormalChildrenContainer(val c: immutable.TreeMap[String, ChildStats]) extends ChildrenContainer {
|
||||
|
||||
override def add(name: String, stats: ChildRestartStats): ChildrenContainer = new NormalChildrenContainer(c.updated(name, stats))
|
||||
|
||||
|
|
@ -108,9 +121,11 @@ private[akka] object ChildrenContainer {
|
|||
case _ ⇒ None
|
||||
}
|
||||
|
||||
override def children: Iterable[ActorRef] = c.values.view.collect { case ChildRestartStats(child, _, _) ⇒ child }
|
||||
override def children: immutable.Iterable[ActorRef] =
|
||||
if (c.isEmpty) EmptyImmutableSeq else new ChildrenIterable(c)
|
||||
|
||||
override def stats: Iterable[ChildRestartStats] = c.values.view.collect { case c: ChildRestartStats ⇒ c }
|
||||
override def stats: immutable.Iterable[ChildRestartStats] =
|
||||
if (c.isEmpty) EmptyImmutableSeq else new ChildRestartsIterable(c)
|
||||
|
||||
override def shallDie(actor: ActorRef): ChildrenContainer = TerminatingChildrenContainer(c, Set(actor), UserRequest)
|
||||
|
||||
|
|
@ -130,7 +145,7 @@ private[akka] object ChildrenContainer {
|
|||
}
|
||||
|
||||
object NormalChildrenContainer {
|
||||
def apply(c: TreeMap[String, ChildStats]): ChildrenContainer =
|
||||
def apply(c: immutable.TreeMap[String, ChildStats]): ChildrenContainer =
|
||||
if (c.isEmpty) EmptyChildrenContainer
|
||||
else new NormalChildrenContainer(c)
|
||||
}
|
||||
|
|
@ -145,7 +160,7 @@ private[akka] object ChildrenContainer {
|
|||
* type of container, depending on whether or not children are left and whether or not
|
||||
* the reason was “Terminating”.
|
||||
*/
|
||||
case class TerminatingChildrenContainer(c: TreeMap[String, ChildStats], toDie: Set[ActorRef], reason: SuspendReason)
|
||||
case class TerminatingChildrenContainer(c: immutable.TreeMap[String, ChildStats], toDie: Set[ActorRef], reason: SuspendReason)
|
||||
extends ChildrenContainer {
|
||||
|
||||
override def add(name: String, stats: ChildRestartStats): ChildrenContainer = copy(c.updated(name, stats))
|
||||
|
|
@ -166,9 +181,11 @@ private[akka] object ChildrenContainer {
|
|||
case _ ⇒ None
|
||||
}
|
||||
|
||||
override def children: Iterable[ActorRef] = c.values.view.collect { case ChildRestartStats(child, _, _) ⇒ child }
|
||||
override def children: immutable.Iterable[ActorRef] =
|
||||
if (c.isEmpty) EmptyImmutableSeq else new ChildrenIterable(c)
|
||||
|
||||
override def stats: Iterable[ChildRestartStats] = c.values.view.collect { case c: ChildRestartStats ⇒ c }
|
||||
override def stats: immutable.Iterable[ChildRestartStats] =
|
||||
if (c.isEmpty) EmptyImmutableSeq else new ChildRestartsIterable(c)
|
||||
|
||||
override def shallDie(actor: ActorRef): ChildrenContainer = copy(toDie = toDie + actor)
|
||||
|
||||
|
|
|
|||
|
|
@ -38,12 +38,11 @@ private[akka] trait Dispatch { this: ActorCell ⇒
|
|||
final def isTerminated: Boolean = mailbox.isClosed
|
||||
|
||||
/**
|
||||
* Start this cell, i.e. attach it to the dispatcher. The UID must reasonably
|
||||
* be different from the previous UID of a possible actor with the same path,
|
||||
* Initialize this cell, i.e. set up mailboxes and supervision. The UID must be
|
||||
* reasonably different from the previous UID of a possible actor with the same path,
|
||||
* which can be achieved by using ThreadLocalRandom.current.nextInt().
|
||||
*/
|
||||
final def start(sendSupervise: Boolean, uid: Int): this.type = {
|
||||
|
||||
final def init(uid: Int, sendSupervise: Boolean): this.type = {
|
||||
/*
|
||||
* Create the mailbox and enqueue the Create() message to ensure that
|
||||
* this is processed before anything else.
|
||||
|
|
@ -56,13 +55,18 @@ private[akka] trait Dispatch { this: ActorCell ⇒
|
|||
|
||||
if (sendSupervise) {
|
||||
// ➡➡➡ NEVER SEND THE SAME SYSTEM MESSAGE OBJECT TO TWO ACTORS ⬅⬅⬅
|
||||
parent.sendSystemMessage(akka.dispatch.Supervise(self, uid))
|
||||
parent.sendSystemMessage(akka.dispatch.Supervise(self, async = false, uid))
|
||||
parent ! NullMessage // read ScalaDoc of NullMessage to see why
|
||||
}
|
||||
this
|
||||
}
|
||||
|
||||
/**
|
||||
* Start this cell, i.e. attach it to the dispatcher.
|
||||
*/
|
||||
final def start(): this.type = {
|
||||
// This call is expected to start off the actor by scheduling its mailbox.
|
||||
dispatcher.attach(this)
|
||||
|
||||
this
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,13 +10,13 @@ import akka.dispatch._
|
|||
import akka.event.Logging.{ Warning, Error, Debug }
|
||||
import scala.util.control.NonFatal
|
||||
import akka.event.Logging
|
||||
import scala.Some
|
||||
import scala.collection.immutable
|
||||
import akka.dispatch.ChildTerminated
|
||||
import akka.actor.PreRestartException
|
||||
import akka.actor.Failed
|
||||
import akka.actor.PostRestartException
|
||||
import akka.event.Logging.Debug
|
||||
import scala.concurrent.util.Duration
|
||||
import scala.concurrent.duration.Duration
|
||||
|
||||
private[akka] trait FaultHandling { this: ActorCell ⇒
|
||||
|
||||
|
|
@ -160,7 +160,7 @@ private[akka] trait FaultHandling { this: ActorCell ⇒
|
|||
}
|
||||
}
|
||||
|
||||
final def handleInvokeFailure(childrenNotToSuspend: Iterable[ActorRef], t: Throwable, message: String): Unit = {
|
||||
final def handleInvokeFailure(childrenNotToSuspend: immutable.Iterable[ActorRef], t: Throwable, message: String): Unit = {
|
||||
publish(Error(t, self.path.toString, clazz(actor), message))
|
||||
// prevent any further messages to be processed until the actor has been restarted
|
||||
if (!isFailed) try {
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ import ReceiveTimeout.emptyReceiveTimeoutData
|
|||
import akka.actor.ActorCell
|
||||
import akka.actor.ActorCell.emptyCancellable
|
||||
import akka.actor.Cancellable
|
||||
import scala.concurrent.util.Duration
|
||||
import scala.concurrent.util.FiniteDuration
|
||||
import scala.concurrent.duration.Duration
|
||||
import scala.concurrent.duration.FiniteDuration
|
||||
|
||||
private[akka] object ReceiveTimeout {
|
||||
final val emptyReceiveTimeoutData: (Duration, Cancellable) = (Duration.Undefined, ActorCell.emptyCancellable)
|
||||
|
|
|
|||
|
|
@ -13,10 +13,10 @@ import akka.serialization.SerializationExtension
|
|||
import akka.util.{ Unsafe, Index }
|
||||
import scala.annotation.tailrec
|
||||
import scala.concurrent.forkjoin.{ ForkJoinTask, ForkJoinPool }
|
||||
import scala.concurrent.util.Duration
|
||||
import scala.concurrent.duration.Duration
|
||||
import scala.concurrent.{ ExecutionContext, Await, Awaitable }
|
||||
import scala.util.control.NonFatal
|
||||
import scala.concurrent.util.FiniteDuration
|
||||
import scala.concurrent.duration.FiniteDuration
|
||||
|
||||
final case class Envelope private (val message: Any, val sender: ActorRef)
|
||||
|
||||
|
|
@ -108,7 +108,7 @@ private[akka] case class Terminate() extends SystemMessage // sent to self from
|
|||
/**
|
||||
* INTERNAL API
|
||||
*/
|
||||
private[akka] case class Supervise(child: ActorRef, uid: Int) extends SystemMessage // sent to supervisor ActorRef from ActorCell.start
|
||||
private[akka] case class Supervise(child: ActorRef, async: Boolean, uid: Int) extends SystemMessage // sent to supervisor ActorRef from ActorCell.start
|
||||
/**
|
||||
* INTERNAL API
|
||||
*/
|
||||
|
|
@ -288,7 +288,7 @@ abstract class MessageDispatcher(val prerequisites: DispatcherPrerequisites) ext
|
|||
if (debug) actors.remove(this, actor.self)
|
||||
addInhabitants(-1)
|
||||
val mailBox = actor.swapMailbox(deadLetterMailbox)
|
||||
mailBox.becomeClosed() // FIXME reschedule in tell if possible race with cleanUp is detected in order to properly clean up
|
||||
mailBox.becomeClosed()
|
||||
mailBox.cleanUp()
|
||||
}
|
||||
|
||||
|
|
@ -420,7 +420,7 @@ abstract class MessageDispatcherConfigurator(val config: Config, val prerequisit
|
|||
case "unbounded" ⇒ UnboundedMailbox()
|
||||
case "bounded" ⇒ new BoundedMailbox(prerequisites.settings, config)
|
||||
case fqcn ⇒
|
||||
val args = Seq(classOf[ActorSystem.Settings] -> prerequisites.settings, classOf[Config] -> config)
|
||||
val args = List(classOf[ActorSystem.Settings] -> prerequisites.settings, classOf[Config] -> config)
|
||||
prerequisites.dynamicAccess.createInstanceFor[MailboxType](fqcn, args).recover({
|
||||
case exception ⇒
|
||||
throw new IllegalArgumentException(
|
||||
|
|
@ -436,7 +436,7 @@ abstract class MessageDispatcherConfigurator(val config: Config, val prerequisit
|
|||
case null | "" | "fork-join-executor" ⇒ new ForkJoinExecutorConfigurator(config.getConfig("fork-join-executor"), prerequisites)
|
||||
case "thread-pool-executor" ⇒ new ThreadPoolExecutorConfigurator(config.getConfig("thread-pool-executor"), prerequisites)
|
||||
case fqcn ⇒
|
||||
val args = Seq(
|
||||
val args = List(
|
||||
classOf[Config] -> config,
|
||||
classOf[DispatcherPrerequisites] -> prerequisites)
|
||||
prerequisites.dynamicAccess.createInstanceFor[ExecutorServiceConfigurator](fqcn, args).recover({
|
||||
|
|
|
|||
|
|
@ -6,12 +6,12 @@ package akka.dispatch
|
|||
|
||||
import akka.actor.{ ActorCell, ActorRef }
|
||||
import scala.annotation.tailrec
|
||||
import scala.concurrent.util.Duration
|
||||
import scala.concurrent.duration.Duration
|
||||
import akka.util.Helpers
|
||||
import java.util.{ Comparator, Iterator }
|
||||
import java.util.concurrent.{ Executor, LinkedBlockingQueue, ConcurrentLinkedQueue, ConcurrentSkipListSet }
|
||||
import akka.actor.ActorSystemImpl
|
||||
import scala.concurrent.util.FiniteDuration
|
||||
import scala.concurrent.duration.FiniteDuration
|
||||
|
||||
/**
|
||||
* An executor based event driven dispatcher which will try to redistribute work from busy actors to idle actors. It is assumed
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@ import akka.event.Logging
|
|||
import java.util.concurrent.atomic.AtomicReference
|
||||
import java.util.concurrent.{ ExecutorService, RejectedExecutionException }
|
||||
import scala.concurrent.forkjoin.ForkJoinPool
|
||||
import scala.concurrent.util.Duration
|
||||
import scala.concurrent.duration.Duration
|
||||
import scala.concurrent.Awaitable
|
||||
import scala.concurrent.util.FiniteDuration
|
||||
import scala.concurrent.duration.FiniteDuration
|
||||
|
||||
/**
|
||||
* The event-based ``Dispatcher`` binds a set of Actors to a thread pool backed up by a
|
||||
|
|
@ -93,7 +93,7 @@ class Dispatcher(
|
|||
*/
|
||||
protected[akka] def shutdown: Unit = {
|
||||
val newDelegate = executorServiceDelegate.copy() // Doesn't matter which one we copy
|
||||
val es = synchronized { // FIXME getAndSet using ARFU or Unsafe
|
||||
val es = synchronized {
|
||||
val service = executorServiceDelegate
|
||||
executorServiceDelegate = newDelegate // just a quick getAndSet
|
||||
service
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import com.typesafe.config.{ ConfigFactory, Config }
|
|||
import akka.actor.{ Scheduler, DynamicAccess, ActorSystem }
|
||||
import akka.event.Logging.Warning
|
||||
import akka.event.EventStream
|
||||
import scala.concurrent.util.Duration
|
||||
import scala.concurrent.duration.Duration
|
||||
|
||||
/**
|
||||
* DispatcherPrerequisites represents useful contextual pieces when constructing a MessageDispatcher
|
||||
|
|
@ -147,7 +147,7 @@ class Dispatchers(val settings: ActorSystem.Settings, val prerequisites: Dispatc
|
|||
case "BalancingDispatcher" ⇒ new BalancingDispatcherConfigurator(cfg, prerequisites)
|
||||
case "PinnedDispatcher" ⇒ new PinnedDispatcherConfigurator(cfg, prerequisites)
|
||||
case fqn ⇒
|
||||
val args = Seq(classOf[Config] -> cfg, classOf[DispatcherPrerequisites] -> prerequisites)
|
||||
val args = List(classOf[Config] -> cfg, classOf[DispatcherPrerequisites] -> prerequisites)
|
||||
prerequisites.dynamicAccess.createInstanceFor[MessageDispatcherConfigurator](fqn, args).recover({
|
||||
case exception ⇒
|
||||
throw new IllegalArgumentException(
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ object ExecutionContexts {
|
|||
* Futures is the Java API for Futures and Promises
|
||||
*/
|
||||
object Futures {
|
||||
|
||||
import scala.collection.JavaConverters.iterableAsScalaIterableConverter
|
||||
/**
|
||||
* Java API, equivalent to Future.apply
|
||||
*/
|
||||
|
|
@ -95,7 +95,7 @@ object Futures {
|
|||
*/
|
||||
def find[T <: AnyRef](futures: JIterable[Future[T]], predicate: JFunc[T, java.lang.Boolean], executor: ExecutionContext): Future[JOption[T]] = {
|
||||
implicit val ec = executor
|
||||
Future.find[T]((scala.collection.JavaConversions.iterableAsScalaIterable(futures)))(predicate.apply(_))(executor).map(JOption.fromScalaOption(_))
|
||||
Future.find[T](futures.asScala)(predicate.apply(_))(executor) map JOption.fromScalaOption
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -103,7 +103,7 @@ object Futures {
|
|||
* Returns a Future to the result of the first future in the list that is completed
|
||||
*/
|
||||
def firstCompletedOf[T <: AnyRef](futures: JIterable[Future[T]], executor: ExecutionContext): Future[T] =
|
||||
Future.firstCompletedOf(scala.collection.JavaConversions.iterableAsScalaIterable(futures))(executor)
|
||||
Future.firstCompletedOf(futures.asScala)(executor)
|
||||
|
||||
/**
|
||||
* Java API
|
||||
|
|
@ -113,14 +113,14 @@ object Futures {
|
|||
* or the result of the fold.
|
||||
*/
|
||||
def fold[T <: AnyRef, R <: AnyRef](zero: R, futures: JIterable[Future[T]], fun: akka.japi.Function2[R, T, R], executor: ExecutionContext): Future[R] =
|
||||
Future.fold(scala.collection.JavaConversions.iterableAsScalaIterable(futures))(zero)(fun.apply)(executor)
|
||||
Future.fold(futures.asScala)(zero)(fun.apply)(executor)
|
||||
|
||||
/**
|
||||
* Java API.
|
||||
* Reduces the results of the supplied futures and binary function.
|
||||
*/
|
||||
def reduce[T <: AnyRef, R >: T](futures: JIterable[Future[T]], fun: akka.japi.Function2[R, T, R], executor: ExecutionContext): Future[R] =
|
||||
Future.reduce[T, R](scala.collection.JavaConversions.iterableAsScalaIterable(futures))(fun.apply)(executor)
|
||||
Future.reduce[T, R](futures.asScala)(fun.apply)(executor)
|
||||
|
||||
/**
|
||||
* Java API.
|
||||
|
|
@ -129,9 +129,7 @@ object Futures {
|
|||
*/
|
||||
def sequence[A](in: JIterable[Future[A]], executor: ExecutionContext): Future[JIterable[A]] = {
|
||||
implicit val d = executor
|
||||
scala.collection.JavaConversions.iterableAsScalaIterable(in).foldLeft(Future(new JLinkedList[A]())) { (fr, fa) ⇒
|
||||
for (r ← fr; a ← fa) yield { r add a; r }
|
||||
}
|
||||
in.asScala.foldLeft(Future(new JLinkedList[A]())) { (fr, fa) ⇒ for (r ← fr; a ← fa) yield { r add a; r } }
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -142,7 +140,7 @@ object Futures {
|
|||
*/
|
||||
def traverse[A, B](in: JIterable[A], fn: JFunc[A, Future[B]], executor: ExecutionContext): Future[JIterable[B]] = {
|
||||
implicit val d = executor
|
||||
scala.collection.JavaConversions.iterableAsScalaIterable(in).foldLeft(Future(new JLinkedList[B]())) { (fr, a) ⇒
|
||||
in.asScala.foldLeft(Future(new JLinkedList[B]())) { (fr, a) ⇒
|
||||
val fb = fn(a)
|
||||
for (r ← fr; b ← fb) yield { r add b; r }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,11 +9,11 @@ import akka.AkkaException
|
|||
import akka.actor.{ ActorCell, ActorRef, Cell, ActorSystem, InternalActorRef, DeadLetter }
|
||||
import akka.util.{ Unsafe, BoundedBlockingQueue }
|
||||
import akka.event.Logging.Error
|
||||
import scala.concurrent.util.Duration
|
||||
import scala.concurrent.duration.Duration
|
||||
import scala.annotation.tailrec
|
||||
import scala.util.control.NonFatal
|
||||
import com.typesafe.config.Config
|
||||
import scala.concurrent.util.FiniteDuration
|
||||
import scala.concurrent.duration.FiniteDuration
|
||||
|
||||
/**
|
||||
* INTERNAL API
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue