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

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

2
.gitignore vendored
View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -7,8 +7,7 @@ package akka.actor
import language.postfixOps import language.postfixOps
import java.util.concurrent.{ TimeUnit, CountDownLatch } import java.util.concurrent.{ TimeUnit, CountDownLatch }
import scala.concurrent.Await import scala.concurrent.Await
import scala.concurrent.util.Duration import scala.concurrent.duration._
import scala.concurrent.util.duration.intToDurationInt
import scala.math.BigInt.int2bigInt import scala.math.BigInt.int2bigInt
import scala.util.Random import scala.util.Random
import scala.util.control.NoStackTrace import scala.util.control.NoStackTrace
@ -195,7 +194,7 @@ object SupervisorHierarchySpec {
case x (x, x) case x (x, x)
} }
override val supervisorStrategy = OneForOneStrategy()(unwrap andThen { override val supervisorStrategy = OneForOneStrategy()(unwrap andThen {
case _: Failure if pongsToGo > 0 case (_: Failure, _) if pongsToGo > 0
log :+= Event("pongOfDeath resuming " + sender, identityHashCode(this)) log :+= Event("pongOfDeath resuming " + sender, identityHashCode(this))
Resume Resume
case (f: Failure, orig) case (f: Failure, orig)
@ -392,10 +391,10 @@ object SupervisorHierarchySpec {
// dont escalate from this one! // dont escalate from this one!
override val supervisorStrategy = OneForOneStrategy() { override val supervisorStrategy = OneForOneStrategy() {
case f: Failure f.directive case f: Failure f.directive
case OriginalRestartException(f: Failure) f.directive case OriginalRestartException(f: Failure) f.directive
case ActorInitializationException(f: Failure) f.directive case ActorInitializationException(_, _, f: Failure) f.directive
case _ Stop case _ Stop
} }
var children = Vector.empty[ActorRef] var children = Vector.empty[ActorRef]

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -5,7 +5,7 @@ package akka.event
import language.postfixOps import language.postfixOps
import scala.concurrent.util.duration._ import scala.concurrent.duration._
import akka.actor.{ Actor, ActorRef, ActorSystemImpl, ActorSystem, Props, UnhandledMessage } import akka.actor.{ Actor, ActorRef, ActorSystemImpl, ActorSystem, Props, UnhandledMessage }
import com.typesafe.config.ConfigFactory import com.typesafe.config.ConfigFactory
import scala.collection.JavaConverters._ import scala.collection.JavaConverters._
@ -282,4 +282,4 @@ class EventStreamSpec extends AkkaSpec(EventStreamSpec.config) {
msg foreach (expectMsg(_)) msg foreach (expectMsg(_))
} }
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -10,13 +10,13 @@ import akka.dispatch._
import akka.event.Logging.{ Warning, Error, Debug } import akka.event.Logging.{ Warning, Error, Debug }
import scala.util.control.NonFatal import scala.util.control.NonFatal
import akka.event.Logging import akka.event.Logging
import scala.Some import scala.collection.immutable
import akka.dispatch.ChildTerminated import akka.dispatch.ChildTerminated
import akka.actor.PreRestartException import akka.actor.PreRestartException
import akka.actor.Failed import akka.actor.Failed
import akka.actor.PostRestartException import akka.actor.PostRestartException
import akka.event.Logging.Debug import akka.event.Logging.Debug
import scala.concurrent.util.Duration import scala.concurrent.duration.Duration
private[akka] trait FaultHandling { this: ActorCell private[akka] trait FaultHandling { this: ActorCell
@ -160,7 +160,7 @@ private[akka] trait FaultHandling { this: ActorCell ⇒
} }
} }
final def handleInvokeFailure(childrenNotToSuspend: Iterable[ActorRef], t: Throwable, message: String): Unit = { final def handleInvokeFailure(childrenNotToSuspend: immutable.Iterable[ActorRef], t: Throwable, message: String): Unit = {
publish(Error(t, self.path.toString, clazz(actor), message)) publish(Error(t, self.path.toString, clazz(actor), message))
// prevent any further messages to be processed until the actor has been restarted // prevent any further messages to be processed until the actor has been restarted
if (!isFailed) try { if (!isFailed) try {

View file

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

View file

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

View file

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

View file

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

View file

@ -9,7 +9,7 @@ import com.typesafe.config.{ ConfigFactory, Config }
import akka.actor.{ Scheduler, DynamicAccess, ActorSystem } import akka.actor.{ Scheduler, DynamicAccess, ActorSystem }
import akka.event.Logging.Warning import akka.event.Logging.Warning
import akka.event.EventStream import akka.event.EventStream
import scala.concurrent.util.Duration import scala.concurrent.duration.Duration
/** /**
* DispatcherPrerequisites represents useful contextual pieces when constructing a MessageDispatcher * DispatcherPrerequisites represents useful contextual pieces when constructing a MessageDispatcher
@ -147,7 +147,7 @@ class Dispatchers(val settings: ActorSystem.Settings, val prerequisites: Dispatc
case "BalancingDispatcher" new BalancingDispatcherConfigurator(cfg, prerequisites) case "BalancingDispatcher" new BalancingDispatcherConfigurator(cfg, prerequisites)
case "PinnedDispatcher" new PinnedDispatcherConfigurator(cfg, prerequisites) case "PinnedDispatcher" new PinnedDispatcherConfigurator(cfg, prerequisites)
case fqn case fqn
val args = Seq(classOf[Config] -> cfg, classOf[DispatcherPrerequisites] -> prerequisites) val args = List(classOf[Config] -> cfg, classOf[DispatcherPrerequisites] -> prerequisites)
prerequisites.dynamicAccess.createInstanceFor[MessageDispatcherConfigurator](fqn, args).recover({ prerequisites.dynamicAccess.createInstanceFor[MessageDispatcherConfigurator](fqn, args).recover({
case exception case exception
throw new IllegalArgumentException( throw new IllegalArgumentException(

View file

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

View file

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

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