review comments
- some API docs - require names for top-level actors - allow names for children - flag error when no channels declared
This commit is contained in:
parent
c362e8168f
commit
86ded1fb0b
9 changed files with 109 additions and 60 deletions
|
|
@ -17,9 +17,10 @@ object ChannelExt extends ExtensionKey[ChannelExtension]
|
|||
|
||||
class ChannelExtension(system: ExtendedActorSystem) extends Extension {
|
||||
|
||||
// kick-start the universe (needed due to thread safety issues in runtime mirror)
|
||||
// FIXME: kick-start the universe (needed due to thread safety issues in runtime mirror)
|
||||
// see https://issues.scala-lang.org/browse/SI-6240
|
||||
private val t = typeTag[(Int, Int) :+: TNil]
|
||||
|
||||
def actorOf[Ch <: ChannelList](factory: ⇒ Actor with Channels[TNil, Ch]): ChannelRef[Ch] =
|
||||
new ChannelRef[Ch](system.actorOf(Props(factory)))
|
||||
def actorOf[Ch <: ChannelList](factory: ⇒ Actor with Channels[TNil, Ch], name: String): ChannelRef[Ch] =
|
||||
new ChannelRef[Ch](system.actorOf(Props(factory), name))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,27 +6,38 @@ package akka.channels
|
|||
|
||||
import language.experimental.{ macros ⇒ makkros }
|
||||
import akka.actor.ActorRef
|
||||
import scala.reflect.runtime.universe.TypeTag
|
||||
import scala.reflect.macros.Context
|
||||
import scala.annotation.tailrec
|
||||
import scala.reflect.macros.Universe
|
||||
import akka.actor.Actor
|
||||
import akka.actor.ActorContext
|
||||
import scala.concurrent.Future
|
||||
import akka.util.Timeout
|
||||
import akka.AkkaException
|
||||
import scala.util.control.NoStackTrace
|
||||
|
||||
case class NarrowingException(errors: String) extends AkkaException(errors) with NoStackTrace
|
||||
|
||||
/**
|
||||
* A channel reference, holding a type list of all channels supported by the
|
||||
* underlying actor. This actor’s reference can be obtained as the `actorRef`
|
||||
* member.
|
||||
*/
|
||||
class ChannelRef[+T <: ChannelList](val actorRef: ActorRef) extends AnyVal {
|
||||
|
||||
/**
|
||||
* Send a message over this channel, “tell” semantics, returning the message.
|
||||
*/
|
||||
def <-!-[M](msg: M): M = macro macros.Tell.impl[T, M]
|
||||
|
||||
/**
|
||||
* Eventually send the value contained in the future over this channel,
|
||||
* “tell” semantics, returning a Future which is completed after sending
|
||||
* with the value which was sent (“Future.andThen” semantics).
|
||||
*/
|
||||
def <-!-[M](future: Future[M]): Future[M] = macro macros.Tell.futureImpl[T, M]
|
||||
|
||||
/**
|
||||
* Send a message over this channel, “ask” semantics, returning a Future
|
||||
* which will be completed with the reply message or a TimeoutException.
|
||||
* If the message is a Future itself, eventually send the Future’s value.
|
||||
*/
|
||||
def <-?-[M](msg: M): Future[_] = macro macros.Ask.impl[ChannelList, Any, T, M]
|
||||
|
||||
/**
|
||||
* Narrow this ChannelRef by removing channels or narrowing input types or
|
||||
* widening output types.
|
||||
*/
|
||||
def narrow[C <: ChannelList]: ChannelRef[C] = macro macros.Narrow.impl[C, T]
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,6 +51,13 @@ trait Channels[P <: ChannelList, C <: ChannelList] { this: Actor ⇒
|
|||
*/
|
||||
def createChild[Pa <: ChannelList, Ch <: ChannelList](factory: Actor with Channels[Pa, Ch]): ChannelRef[Ch] = macro macros.CreateChild.impl[C, Pa, Ch]
|
||||
|
||||
/**
|
||||
* Create a child actor with properly typed ChannelRef and the given name,
|
||||
* verifying that this actor can handle everything which the child tries to
|
||||
* send via its `parent` ChannelRef.
|
||||
*/
|
||||
def createChild[Pa <: ChannelList, Ch <: ChannelList](factory: Actor with Channels[Pa, Ch], name: String): ChannelRef[Ch] = macro macros.CreateChild.implName[C, Pa, Ch]
|
||||
|
||||
/**
|
||||
* Properly typed ChannelRef for the context.parent.
|
||||
*/
|
||||
|
|
@ -131,6 +138,8 @@ trait Channels[P <: ChannelList, C <: ChannelList] { this: Actor ⇒
|
|||
|
||||
private def verifyCompleteness() {
|
||||
val channels = inputChannels(ru)(channelListTypeTag.tpe)
|
||||
if (channels.isEmpty)
|
||||
throw ActorInitializationException("Actor with Channels cannot have no channels")
|
||||
val classes = channels groupBy (e ⇒ channelListTypeTag.mirror.runtimeClass(e.widen))
|
||||
val missing = classes.keySet -- behavior.keySet
|
||||
if (missing.nonEmpty) {
|
||||
|
|
@ -162,10 +171,7 @@ trait Channels[P <: ChannelList, C <: ChannelList] { this: Actor ⇒
|
|||
case c: CheckType[_] ⇒ true
|
||||
case _ ⇒
|
||||
val msgClass = x.getClass
|
||||
index find (_ isAssignableFrom msgClass) match {
|
||||
case None ⇒ false
|
||||
case Some(cls) ⇒ true
|
||||
}
|
||||
index exists (_ isAssignableFrom msgClass)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import scala.concurrent.{ ExecutionContext, Future }
|
|||
import scala.reflect.runtime.{ universe ⇒ ru }
|
||||
import scala.util.Success
|
||||
import akka.dispatch.ExecutionContexts
|
||||
import scala.util.control.NoStackTrace
|
||||
|
||||
sealed trait ChannelList
|
||||
sealed trait TNil extends ChannelList
|
||||
|
|
@ -26,8 +27,23 @@ sealed trait ReplyChannels[T <: ChannelList] extends ChannelList
|
|||
*/
|
||||
sealed trait UnknownDoNotWriteMeDown
|
||||
|
||||
/**
|
||||
* This exception is used to signal errors when trying to `.narrow` an
|
||||
* ActorRef into a ChannelRef: if the actor finds the requested channel types
|
||||
* incompatible with its selfChannel, it will return errors in the same format
|
||||
* as would occur during compilation of a `ChannelRef.narrow` operation.
|
||||
*/
|
||||
case class NarrowingException(message: String) extends akka.AkkaException(message) with NoStackTrace
|
||||
|
||||
class ActorRefOps(val ref: ActorRef) extends AnyVal {
|
||||
import macros.Helpers._
|
||||
|
||||
/**
|
||||
* Send a query to the actor and check whether it supports the requested
|
||||
* channel types; the normal timeout semantics of the `ask` pattern apply.
|
||||
* The Future will be completed either with the desired ChannelRef or with
|
||||
* an exception (TimeoutException or NarrowingException).
|
||||
*/
|
||||
def narrow[C <: ChannelList](implicit timeout: Timeout, ec: ExecutionContext, tt: ru.TypeTag[C]): Future[ChannelRef[C]] = {
|
||||
import Channels._
|
||||
ref ? CheckType(tt) map {
|
||||
|
|
|
|||
|
|
@ -22,20 +22,35 @@ object CreateChild {
|
|||
|
||||
import c.universe._
|
||||
|
||||
if (weakTypeOf[ParentChannels] =:= weakTypeOf[Nothing]) {
|
||||
c.abort(c.enclosingPosition, "Parent argument must not be Nothing")
|
||||
}
|
||||
if (weakTypeOf[ChildChannels] =:= weakTypeOf[Nothing]) {
|
||||
c.abort(c.enclosingPosition, "channel list must not be Nothing")
|
||||
}
|
||||
verify(c)(weakTypeOf[ParentChannels], weakTypeOf[ChildChannels], weakTypeOf[MyChannels])
|
||||
|
||||
val missing = missingChannels(c.universe)(weakTypeOf[MyChannels], inputChannels(c.universe)(weakTypeOf[ParentChannels]))
|
||||
if (missing.isEmpty) {
|
||||
implicit val t = c.TypeTag[ChildChannels](c.weakTypeOf[ChildChannels])
|
||||
reify(new ChannelRef[ChildChannels](c.prefix.splice.context.actorOf(Props(factory.splice))))
|
||||
} else {
|
||||
c.abort(c.enclosingPosition, s"This actor cannot support a child requiring channels ${missing mkString ", "}")
|
||||
}
|
||||
implicit val t = c.TypeTag[ChildChannels](c.weakTypeOf[ChildChannels])
|
||||
reify(new ChannelRef[ChildChannels](c.prefix.splice.context.actorOf(Props(factory.splice))))
|
||||
}
|
||||
|
||||
def implName[MyChannels <: ChannelList: c.WeakTypeTag, ParentChannels <: ChannelList: c.WeakTypeTag, ChildChannels <: ChannelList: c.WeakTypeTag](
|
||||
c: Context {
|
||||
type PrefixType = Actor with Channels[_, MyChannels]
|
||||
})(factory: c.Expr[Actor with Channels[ParentChannels, ChildChannels]], name: c.Expr[String]): c.Expr[ChannelRef[ChildChannels]] = {
|
||||
|
||||
import c.universe._
|
||||
|
||||
verify(c)(weakTypeOf[ParentChannels], weakTypeOf[ChildChannels], weakTypeOf[MyChannels])
|
||||
|
||||
implicit val t = c.TypeTag[ChildChannels](c.weakTypeOf[ChildChannels])
|
||||
reify(new ChannelRef[ChildChannels](c.prefix.splice.context.actorOf(Props(factory.splice), name.splice)))
|
||||
}
|
||||
|
||||
def verify(c: Context)(parent: c.Type, child: c.Type, mine: c.Type): Unit = {
|
||||
import c.universe._
|
||||
|
||||
val nothing = weakTypeOf[Nothing]
|
||||
if (parent =:= nothing) abort(c, "Parent argument must not be Nothing")
|
||||
if (child =:= nothing) abort(c, "channel list must not be Nothing")
|
||||
|
||||
val missing = missingChannels(c.universe)(mine, inputChannels(c.universe)(parent))
|
||||
if (missing.nonEmpty)
|
||||
abort(c, s"This actor cannot support a child requiring channels ${missing mkString ", "}")
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue