diff --git a/akka-actor/src/main/scala/akka/pattern/CircuitBreaker.scala b/akka-actor/src/main/scala/akka/pattern/CircuitBreaker.scala index aa578baf38..628b847700 100644 --- a/akka-actor/src/main/scala/akka/pattern/CircuitBreaker.scala +++ b/akka-actor/src/main/scala/akka/pattern/CircuitBreaker.scala @@ -13,20 +13,13 @@ import scala.concurrent.{ ExecutionContext, Future, Promise, Await } import scala.concurrent.duration._ import scala.util.control.NonFatal import scala.util.Success +import akka.dispatch.ExecutionContexts.sameThreadExecutionContext /** * Companion object providing factory methods for Circuit Breaker which runs callbacks in caller's thread */ object CircuitBreaker { - /** - * Synchronous execution context to run in caller's thread - used by companion object factory methods - */ - private[CircuitBreaker] val syncExecutionContext = new ExecutionContext { - override def execute(runnable: Runnable): Unit = runnable.run() - override def reportFailure(t: Throwable): Unit = () - } - /** * Callbacks run in caller's thread when using withSyncCircuitBreaker, and in same ExecutionContext as the passed * in Future when using withCircuitBreaker. To use another ExecutionContext for the callbacks you can specify the @@ -38,7 +31,7 @@ object CircuitBreaker { * @param resetTimeout [[scala.concurrent.duration.FiniteDuration]] of time after which to attempt to close the circuit */ def apply(scheduler: Scheduler, maxFailures: Int, callTimeout: FiniteDuration, resetTimeout: FiniteDuration): CircuitBreaker = - new CircuitBreaker(scheduler, maxFailures, callTimeout, resetTimeout)(syncExecutionContext) + new CircuitBreaker(scheduler, maxFailures, callTimeout, resetTimeout)(sameThreadExecutionContext) /** * Callbacks run in caller's thread when using withSyncCircuitBreaker, and in same ExecutionContext as the passed @@ -301,7 +294,7 @@ class CircuitBreaker(scheduler: Scheduler, maxFailures: Int, callTimeout: Finite bodyFuture.onComplete({ case s: Success[_] if !deadline.isOverdue() ⇒ callSucceeds() case _ ⇒ callFails() - })(CircuitBreaker.syncExecutionContext) + })(sameThreadExecutionContext) bodyFuture } diff --git a/akka-channels/src/main/scala/akka/channels/macros/Helpers.scala b/akka-channels/src/main/scala/akka/channels/macros/Helpers.scala index 9e67754544..5a18741902 100644 --- a/akka-channels/src/main/scala/akka/channels/macros/Helpers.scala +++ b/akka-channels/src/main/scala/akka/channels/macros/Helpers.scala @@ -151,11 +151,10 @@ object Helpers { final def unwrapMsgType(u: Universe)(msg: u.Type): u.Type = { import u._ - if (msg <:< typeOf[WrappedMessage[_, _]]) - msg match { - case TypeRef(_, _, x :: _) ⇒ x - } - else msg + msg match { + case TypeRef(_, _, x :: _) if msg <:< typeOf[WrappedMessage[_, _]] ⇒ x + case x ⇒ x + } } } \ No newline at end of file diff --git a/akka-docs/rst/scala/code/docs/channels/ChannelDocSpec.scala b/akka-docs/rst/scala/code/docs/channels/ChannelDocSpec.scala index 31645655ff..f45247c8ad 100644 --- a/akka-docs/rst/scala/code/docs/channels/ChannelDocSpec.scala +++ b/akka-docs/rst/scala/code/docs/channels/ChannelDocSpec.scala @@ -57,10 +57,10 @@ class ChannelDocSpec extends AkkaSpec { import ChannelDocSpec._ - class A - class B - class C - class D + class MsgA + class MsgB + class MsgC + class MsgD "demonstrate why Typed Channels" in { def someActor = testActor @@ -79,7 +79,7 @@ class ChannelDocSpec extends AkkaSpec { type Example = // //#motivation-types - (A, B) :+: (C, D) :+: TNil + (MsgA, MsgB) :+: (MsgC, MsgD) :+: TNil //#motivation-types } @@ -114,12 +114,12 @@ class ChannelDocSpec extends AkkaSpec { implicit val dummySender: ChannelRef[(Any, Nothing) :+: TNil] = ??? implicit val timeout: Timeout = ??? // for the ask operations - val channelA: ChannelRef[(A, B) :+: TNil] = ??? - val channelB: ChannelRef[(B, C) :+: TNil] = ??? - val channelC: ChannelRef[(C, D) :+: TNil] = ??? + val channelA: ChannelRef[(MsgA, MsgB) :+: TNil] = ??? + val channelB: ChannelRef[(MsgB, MsgC) :+: TNil] = ??? + val channelC: ChannelRef[(MsgC, MsgD) :+: TNil] = ??? - val a = new A - val fA = Future { new A } + val a = new MsgA + val fA = Future { new MsgA } channelA <-!- a // send a to channelA a -!-> channelA // same thing as above @@ -128,8 +128,8 @@ class ChannelDocSpec extends AkkaSpec { fA -!-> channelA // same thing as above // ask the actor; return type given in full for illustration - val fB: Future[WrappedMessage[(B, Nothing) :+: TNil, B]] = channelA <-?- a - val fBunwrapped: Future[B] = fB.lub + val fB: Future[WrappedMessage[(MsgB, Nothing) :+: TNil, MsgB]] = channelA <-?- a + val fBunwrapped: Future[MsgB] = fB.lub a -?-> channelA // same thing as above diff --git a/akka-docs/rst/scala/typed-channels.rst b/akka-docs/rst/scala/typed-channels.rst index 94348bf9d3..0ea47bdfc0 100644 --- a/akka-docs/rst/scala/typed-channels.rst +++ b/akka-docs/rst/scala/typed-channels.rst @@ -1,8 +1,14 @@ .. _typed-channels: -############## -Typed Channels -############## +############################# +Typed Channels (EXPERIMENTAL) +############################# + +.. note:: + + *This is a preview of the upcoming Typed Channels support, its API may change + during development up to the released version where the EXPERIMENTAL label is + removed.* Motivation ========== @@ -43,197 +49,7 @@ to be useful, the system must be as reliable as you would expect a type system to be. This means that unless you step outside of it (i.e. doing the equivalent of ``.asInstanceOf[_]``) you shall be protected, failures shall be recognized and flagged. There are a number of challenges included in this -requirement, which are discussed in the following sections. If you are reading -this chapter for the first time and are not currently interested in exactly why -things are as they are, you may skip ahead to `Terminology`_. - -The Type Pollution Problem --------------------------- - -What if an actor accepts two different types of messages? It might be a main -communications channel which is forwarded to worker actors for performing some -long-running and/or dangerous task, plus an administrative channel for the -routing of requests. Or it might be a generic message throttler which accepts a -generic channel for passing it through (which delay where appropriate) and a -management channel for setting the throttling rate. In the second case it is -especially easy to see that those two channels will probably not be related, -their types will not be derived from a meaningful common supertype; instead the -least upper bound will probably be :class:`AnyRef`. If a typed channel -reference only had the capability to express a single type, this type would -then be no restriction anymore. - -One solution to this is to never expose references describe more than one -channel at a time. But where would these references come from? It would be very -difficult to make this construction process type-safe, and it would also be an -inconvenient restriction, since message ordering guarantees only apply for the -same sender–receive pair, and if there are relations between the messages sent -on multiple channels those would need more boilerplate code to realize than if -all interaction were possible through a single reference. - -The other solution thus is to express multiple channel types by a single -channel reference, which requires the implementation of type lists and -computations on these. And as we will see below it also requires the -specification of possibly multiple reply channels per input type, hence a type -map. The implementation chosen uses type lists like this: - -.. includecode:: code/docs/channels/ChannelDocSpec.scala#motivation-types - -This type expresses two channels: type ``A`` may stimulate replies of type -``B``, while type ``C`` may evoke replies of type ``D``. The type operator -``:+:`` is a binary type which form a list of these channel definitions, and -like every good list it ends with an empty terminator ``TNil``. - -The Reply Problem ------------------ - -Akka actors have the power to reply to any message they receive, which is also -a message send and shall also be covered by typed channels. Since the sending -actor is the one which will also receive the reply, this needs to be verified. -The solution to this problem is that in addition to the ``self`` reference, -which is implicitly picked up as the sender for untyped actor interactions, -there is also a ``selfChannel`` which describes the typed channels handled by -this actor. Thus at the call site of the message send it must be verified that -this actor can actually handle the reply for that given message send. - -The Sender Ping-Pong Problem ----------------------------- - -After successfully sending a message to an actor over a typed channel, that -actor will have a reference to the message’s sender, because normal Akka -message processing rules apply. For this sender reference there must exist a -typed channel reference which describes the possible reply types which are -applicable for each of the incoming message channels. We will see below how -this reference is provided in the code, the problem we want to highlight here -is a different one: the nature of any sender reference is that it is highly -dynamic, the compiler cannot possibly know who sent the message we are -currently processing. - -But this does not mean that all hope is lost: the solution is to do *all* -type-checking at the call site of the message send. The receiving actor just -needs to declare its channel descriptions in its own type, and channel -references are derived at construction from this type (implying the existence -of a typed ``actorOf``). Then the actor knows for each received message type -which the allowed reply types are. The typed channel for the sender reference -hence has the reply types for the current input channel as its own input types, -but what should the reply types be? This is the ping-pong problem: - -* ActorA sends MsgA to ActorB - -* ActorB replies with MsgB - -* ActorA replies with MsgC - -Every “reply” uses the sender channel, which is dynamic and hence only known -partially. But ActorB did not know who sent the message it just replied to and -hence it cannot check that it can process the possible replies following that -message send. Only ActorA could have known, because it knows its own channels -as well as ActorB’s channels completely. The solution is thus to recursively -verify the message send, following all reply channels until all possible -message types to be sent have been verified. This sounds horribly complex, but -the algorithm for doing so actually has a worst-case complexity of O(N) where N -is the number of input channels of ActorA or ActorB, whoever has fewer. - -The Parent Problem ------------------- - -There is one other actor reference which is available to ever actor: its -parent. Since the child–parent relationship is established permanently when the -child is created by the parent, this problem is easily solvable by encoding the -requirements of the child for its parent channel in its type signature having -the typed variant of ``actorOf`` verify this against the ``selfChannel``. - -Anecdotally, since the guardian actor does not care at all about message sent -to it, top-level type channel actors must declare their parent channel to be -empty. - -The Exposure/Restriction Problem --------------------------------- - -An actor may provide more than one service, either itself or by proxy, each -with their own set of channels. Only having references for the full set of -channels leads to a too wide spread of capabilities: in the example of the -message rate throttling actor its management channel is only meant to be used -by the actor which inserted it, not by the two actors between it was inserted. -Hence the manager will have to create a channel reference which excludes the -management channels before handing out the reference to other actors. - -Another variant of this problem is an actor which handles a channel whose input -type is a supertype for a number of derived channels. It should be allowed to -use the “superchannel” in place of any of the subchannels, but not the other -way around. The intuitive approach would be to model this by making the channel -reference contravariant in its channel types and define those channel types -accordingly. This does not work nicely, however, because Scala’s type system is -not well-suited to modeling such calculations on unordered type lists; it might -be possible but its implementation would be forbiddingly complex. - -Therefore this topic gained traction as macros became available: being able to -write down type calculations using standard collections and their -transformations reduces the implementation to a handful of lines. The “narrow” -operation implemented this way allows all narrowing of input channels and -widening of output channels down to ``(Nothing, Any)`` (which is to say: -removal). - -The Forwarding Problem ----------------------- - -One important feature of actors mentioned above is their composability which is -enabled by being able to forward or delegate messages. It is the nature of this -process that the sending party is not aware of the true destination of the -message, it only sees the façade in front of it. Above we have seen that the -sender ping-pong problem requires all verification to be performed at the -sender’s end, but if the sender does not know the final recipient, how can it -check that the message exchange is type-safe? - -The forwarding party—the middle-man—is also not in the position to make this -call, since all it has is the incomplete sender channel which is lacking reply -type information. The problem which arises lies precisely in these reply -sequences: the ping-pong scheme was verified against the middle-man, and if the -final recipient would reply to the forwarded request, that sender reference -would belong to a different channel and there is no single location in the -source code where all these pieces are known at compile time. - -The solution to this problem is not to allow forwarding in the normal untyped -:class:`ActorRef` sense. Replies must always be sent by the recipient of the -original message in order for the type checks at the sender site to be -effective. Since forwarding is an important communication pattern among actors, -support for it is thus provided in the form of the :meth:`ask` pattern combined -with the :meth:`pipe` pattern, which both are not add-ons but fully integrated -operations among typed channels. - -The JVM Erasure Problem ------------------------ - -When an actor with typed channels receives a message, this message needs to be -dispatched internally to the right channel, so that the right sender channel -can be presented and so on. This dispatch needs to work with the information -contained in the message, which due to the erasure of generic type information -is an incomplete image of the true channel types. Those full types exist only -at compile-time and reifying them into TypeTags at runtime for every message -send would be prohibitively expensive. This means that channels which erase to -the same JVM type cannot coexist within the same actor, message would not be -routable reliably in that case. - -The Actor Lookup Problem ------------------------- - -Everything up to this point has assumed that channel references are passed from -their point of creation to their point of use directly and in the regime of -strong, unerased types. This can also happen between actors by embedding them -in case classes with proper type information. But one particular useful feature -of Akka actors is that they have a stable identity by which they can be found, -a unique name. This name is represented as a :class:`String` and naturally does -not bear any type information concerning the actor’s channels. Thus, when -looking up an actor with ``system.actorFor(...)`` you will only get an untyped -:class:`ActorRef` and not a channel reference. This :class:`ActorRef` can of -course manually be wrapped in a channel reference bearing the desired channels, -but this is not a type-safe operation. - -The solution in this case must be a runtime check. There is an operation to -“narrow” an :class:`ActorRef` to a channel reference of given type, which -behind the scenes will send a message to the designated actor with a TypeTag -representing the requested channels. The actor will check these against its own -TypeTag and reply with the verification result. This check uses the same code -as the compile-time “narrow” operation introduced above. +requirement, which are discussed in `The Design Background`_ below. Terminology =========== @@ -477,7 +293,217 @@ possible if that subchannel was declared up-front. TypeTags are currently (Scala 2.10.0) not serializable, hence narrowing of :class:`ActorRef` does not work for remote references. +The Design Background +===================== + +This section outlines the most prominent challenges encountered during the +development of Typed Channels and the rationale for their solutions. It is not +necessary to understand this material in order to use Typed Channels, but it +may be useful to explain why certain things are as they are. + +The Type Pollution Problem +-------------------------- + +What if an actor accepts two different types of messages? It might be a main +communications channel which is forwarded to worker actors for performing some +long-running and/or dangerous task, plus an administrative channel for the +routing of requests. Or it might be a generic message throttler which accepts a +generic channel for passing it through (which delay where appropriate) and a +management channel for setting the throttling rate. In the second case it is +especially easy to see that those two channels will probably not be related, +their types will not be derived from a meaningful common supertype; instead the +least upper bound will probably be :class:`AnyRef`. If a typed channel +reference only had the capability to express a single type, this type would +then be no restriction anymore. + +One solution to this is to never expose references describe more than one +channel at a time. But where would these references come from? It would be very +difficult to make this construction process type-safe, and it would also be an +inconvenient restriction, since message ordering guarantees only apply for the +same sender–receive pair, and if there are relations between the messages sent +on multiple channels those would need more boilerplate code to realize than if +all interaction were possible through a single reference. + +The other solution thus is to express multiple channel types by a single +channel reference, which requires the implementation of type lists and +computations on these. And as we will see below it also requires the +specification of possibly multiple reply channels per input type, hence a type +map. The implementation chosen uses type lists like this: + +.. includecode:: code/docs/channels/ChannelDocSpec.scala#motivation-types + +This type expresses two channels: type ``A`` may stimulate replies of type +``B``, while type ``C`` may evoke replies of type ``D``. The type operator +``:+:`` is a binary type which form a list of these channel definitions, and +like every good list it ends with an empty terminator ``TNil``. + +The Reply Problem +----------------- + +Akka actors have the power to reply to any message they receive, which is also +a message send and shall also be covered by typed channels. Since the sending +actor is the one which will also receive the reply, this needs to be verified. +The solution to this problem is that in addition to the ``self`` reference, +which is implicitly picked up as the sender for untyped actor interactions, +there is also a ``selfChannel`` which describes the typed channels handled by +this actor. Thus at the call site of the message send it must be verified that +this actor can actually handle the reply for that given message send. + +The Sender Ping-Pong Problem +---------------------------- + +After successfully sending a message to an actor over a typed channel, that +actor will have a reference to the message’s sender, because normal Akka +message processing rules apply. For this sender reference there must exist a +typed channel reference which describes the possible reply types which are +applicable for each of the incoming message channels. We will see below how +this reference is provided in the code, the problem we want to highlight here +is a different one: the nature of any sender reference is that it is highly +dynamic, the compiler cannot possibly know who sent the message we are +currently processing. + +But this does not mean that all hope is lost: the solution is to do *all* +type-checking at the call site of the message send. The receiving actor just +needs to declare its channel descriptions in its own type, and channel +references are derived at construction from this type (implying the existence +of a typed ``actorOf``). Then the actor knows for each received message type +which the allowed reply types are. The typed channel for the sender reference +hence has the reply types for the current input channel as its own input types, +but what should the reply types be? This is the ping-pong problem: + +* ActorA sends MsgA to ActorB + +* ActorB replies with MsgB + +* ActorA replies with MsgC + +Every “reply” uses the sender channel, which is dynamic and hence only known +partially. But ActorB did not know who sent the message it just replied to and +hence it cannot check that it can process the possible replies following that +message send. Only ActorA could have known, because it knows its own channels +as well as ActorB’s channels completely. The solution is thus to recursively +verify the message send, following all reply channels until all possible +message types to be sent have been verified. This sounds horribly complex, but +the algorithm for doing so actually has a worst-case complexity of O(N) where N +is the number of input channels of ActorA or ActorB, whoever has fewer. + +The Parent Problem +------------------ + +There is one other actor reference which is available to every actor: its +parent. Since the child–parent relationship is established permanently when the +child is created by the parent, this problem is easily solvable by encoding the +requirements of the child for its parent channel in its type signature having +the typed variant of ``actorOf`` verify this against the ``selfChannel``. + +Anecdotally, since the guardian actor does not care at all about message sent +to it, top-level type channel actors must declare their parent channel to be +empty. + +The Exposure/Restriction Problem +-------------------------------- + +An actor may provide more than one service, either itself or by proxy, each +with their own set of channels. Only having references for the full set of +channels leads to a too wide spread of capabilities: in the example of the +message rate throttling actor its management channel is only meant to be used +by the actor which inserted it, not by the two actors between it was inserted. +Hence the manager will have to create a channel reference which excludes the +management channels before handing out the reference to other actors. + +Another variant of this problem is an actor which handles a channel whose input +type is a supertype for a number of derived channels. It should be allowed to +use the “superchannel” in place of any of the subchannels, but not the other +way around. The intuitive approach would be to model this by making the channel +reference contravariant in its channel types and define those channel types +accordingly. This does not work nicely, however, because Scala’s type system is +not well-suited to modeling such calculations on unordered type lists; it might +be possible but its implementation would be forbiddingly complex. + +Therefore this topic gained traction as macros became available: being able to +write down type calculations using standard collections and their +transformations reduces the implementation to a handful of lines. The “narrow” +operation implemented this way allows all narrowing of input channels and +widening of output channels down to ``(Nothing, Any)`` (which is to say: +removal). + +The Forwarding Problem +---------------------- + +One important feature of actors mentioned above is their composability which is +enabled by being able to forward or delegate messages. It is the nature of this +process that the sending party is not aware of the true destination of the +message, it only sees the façade in front of it. Above we have seen that the +sender ping-pong problem requires all verification to be performed at the +sender’s end, but if the sender does not know the final recipient, how can it +check that the message exchange is type-safe? + +The forwarding party—the middle-man—is also not in the position to make this +call, since all it has is the incomplete sender channel which is lacking reply +type information. The problem which arises lies precisely in these reply +sequences: the ping-pong scheme was verified against the middle-man, and if the +final recipient would reply to the forwarded request, that sender reference +would belong to a different channel and there is no single location in the +source code where all these pieces are known at compile time. + +The solution to this problem is not to allow forwarding in the normal untyped +:class:`ActorRef` sense. Replies must always be sent by the recipient of the +original message in order for the type checks at the sender site to be +effective. Since forwarding is an important communication pattern among actors, +support for it is thus provided in the form of the :meth:`ask` pattern combined +with the :meth:`pipe` pattern, which both are not add-ons but fully integrated +operations among typed channels. + +The JVM Erasure Problem +----------------------- + +When an actor with typed channels receives a message, this message needs to be +dispatched internally to the right channel, so that the right sender channel +can be presented and so on. This dispatch needs to work with the information +contained in the message, which due to the erasure of generic type information +is an incomplete image of the true channel types. Those full types exist only +at compile-time and reifying them into TypeTags at runtime for every message +send would be prohibitively expensive. This means that channels which erase to +the same JVM type cannot coexist within the same actor, message would not be +routable reliably in that case. + +The Actor Lookup Problem +------------------------ + +Everything up to this point has assumed that channel references are passed from +their point of creation to their point of use directly and in the regime of +strong, unerased types. This can also happen between actors by embedding them +in case classes with proper type information. But one particular useful feature +of Akka actors is that they have a stable identity by which they can be found, +a unique name. This name is represented as a :class:`String` and naturally does +not bear any type information concerning the actor’s channels. Thus, when +looking up an actor with ``system.actorFor(...)`` you will only get an untyped +:class:`ActorRef` and not a channel reference. This :class:`ActorRef` can of +course manually be wrapped in a channel reference bearing the desired channels, +but this is not a type-safe operation. + +The solution in this case must be a runtime check. There is an operation to +“narrow” an :class:`ActorRef` to a channel reference of given type, which +behind the scenes will send a message to the designated actor with a TypeTag +representing the requested channels. The actor will check these against its own +TypeTag and reply with the verification result. This check uses the same code +as the compile-time “narrow” operation introduced above. + How to read The Types ===================== +In case of errors in your code the compiler will try to inform you in the most +precise way it can, and that will then contain types like this:: + akka.channels.:+:[(com.example.Request, com.example.Reply), + akka.channels.:+:[(com.example.Command, Nothing), TNil]] + +These types look unwieldy because of two things: they use fully qualified names +for all the types (thankfully using the ``()`` sugar for :class:`Tuple2`), and +they do not employ infix notation. That same type there might look like this in +your source code:: + + (Request, Reply) :+: (Command, Nothing) :+: TNil + +As soon as someone finds the time, it would be nice if the IDEs learned to +print types making use of the file’s import statements and infix notation. diff --git a/project/AkkaBuild.scala b/project/AkkaBuild.scala index 123d5ed125..90448f45d4 100644 --- a/project/AkkaBuild.scala +++ b/project/AkkaBuild.scala @@ -437,7 +437,7 @@ object AkkaBuild extends Build { ) configs (MultiJvm) lazy val channels = Project( - id = "akka-channels", + id = "akka-channels-experimental", base = file("akka-channels"), dependencies = Seq(actor), settings = defaultSettings ++ Seq(