aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilipp Haller <hallerp@gmail.com>2013-05-23 15:52:31 +0200
committerPhilipp Haller <hallerp@gmail.com>2013-05-23 15:52:31 +0200
commit8b740b436a3de111269e004e595fda70f6533704 (patch)
tree41ec001a0e5eb72940d0c32473121775d3220d8a
parentffab92802c130c7e881440d360e7cef051b7f3d4 (diff)
downloadscala-async-topic/wip-futuresystem-extension.tar.gz
scala-async-topic/wip-futuresystem-extension.tar.bz2
scala-async-topic/wip-futuresystem-extension.zip
Enable generalized state machinestopic/wip-futuresystem-extension
- async { } block can now generate a subclass of an existing trait - future system can directly return the state machine instance from async, without spawning an actual future - adds iterator-based async implementation with future system
-rw-r--r--src/main/scala/scala/async/Async.scala12
-rw-r--r--src/main/scala/scala/async/FutureSystem.scala38
-rw-r--r--src/main/scala/scala/async/iterators/Async.scala109
-rw-r--r--src/main/scala/scala/async/iterators/IteratorState.scala50
-rw-r--r--src/test/scala/scala/async/run/cps/CPSSpec.scala3
5 files changed, 199 insertions, 13 deletions
diff --git a/src/main/scala/scala/async/Async.scala b/src/main/scala/scala/async/Async.scala
index c480b62..ed0f0ef 100644
--- a/src/main/scala/scala/async/Async.scala
+++ b/src/main/scala/scala/async/Async.scala
@@ -118,7 +118,8 @@ abstract class AsyncBase {
lazy val stateMachine: ClassDef = {
val body: List[Tree] = {
val stateVar = ValDef(Modifiers(Flag.MUTABLE), name.state, TypeTree(definitions.IntTpe), Literal(Constant(0)))
- val result = ValDef(NoMods, name.result, TypeTree(futureSystemOps.promType[T]), futureSystemOps.createProm[T].tree)
+ val result =
+ ValDef(NoMods, name.result, TypeTree(futureSystemOps.promType[T]), futureSystemOps.createPromTree[T](Ident(name.stateMachine)))
val execContext = ValDef(NoMods, name.execContext, TypeTree(), futureSystemOps.execContext.tree)
val applyDefDef: DefDef = {
val applyVParamss = List(List(ValDef(Modifiers(Flag.PARAM), name.tr, TypeTree(futureSystemOps.resultType[Any]), EmptyTree)))
@@ -145,16 +146,13 @@ abstract class AsyncBase {
val code: c.Expr[futureSystem.Fut[T]] = {
val isSimple = asyncStates.size == 1
val tree =
- if (isSimple)
- Block(Nil, futureSystemOps.spawn(body.tree)) // generate lean code for the simple case of `async { 1 + 1 }`
- else {
- Block(List[Tree](
+ Block(
+ List[Tree](
stateMachine,
ValDef(NoMods, name.stateMachine, TypeTree(stateMachineType), Apply(Select(New(Ident(name.stateMachineT)), nme.CONSTRUCTOR), Nil)),
- futureSystemOps.spawn(Apply(selectStateMachine(name.apply), Nil))
+ futureSystemOps.spawn(Ident(name.stateMachine))
),
futureSystemOps.promiseToFuture(c.Expr[futureSystem.Prom[T]](selectStateMachine(name.result))).tree)
- }
c.Expr[futureSystem.Fut[T]](tree)
}
diff --git a/src/main/scala/scala/async/FutureSystem.scala b/src/main/scala/scala/async/FutureSystem.scala
index f20854b..ee660d3 100644
--- a/src/main/scala/scala/async/FutureSystem.scala
+++ b/src/main/scala/scala/async/FutureSystem.scala
@@ -43,6 +43,8 @@ trait FutureSystem {
/** Create an empty promise */
def createProm[A: WeakTypeTag]: Expr[Prom[A]]
+ def createPromTree[A: WeakTypeTag](stateMachine: Tree): Tree
+
/** Extract a future from the given promise. */
def promiseToFuture[A: WeakTypeTag](prom: Expr[Prom[A]]): Expr[Fut[A]]
@@ -68,8 +70,13 @@ trait FutureSystem {
/** Result value of a completion */
def resultValue(name: TermName, resultType: Type): Tree
- def spawn(tree: Tree): Tree =
- future(c.Expr[Unit](tree))(execContext).tree
+ def spawn(tree: Tree): Tree = {
+ val utils = TransformUtils[c.type](c)
+ import utils.{name, defn}
+
+ val applyTree = Apply(Select(tree, name.apply), Nil)
+ future(c.Expr[Unit](applyTree))(execContext).tree
+ }
def castTo[A: WeakTypeTag](future: Expr[Fut[Any]]): Expr[Fut[A]]
}
@@ -89,10 +96,16 @@ trait TryBasedFutureSystem extends FutureSystem {
protected def completePromWithTry[A: WeakTypeTag](prom: Expr[Prom[A]], value: Expr[scala.util.Try[A]]): Expr[Unit]
def completeProm[A: WeakTypeTag](prom: Expr[Prom[A]], value: Expr[A]): Expr[Unit] =
- completePromWithTry(prom, reify(scala.util.Success(value.splice)))
+ completePromWithTry(prom, reify {
+ import scala.util.Success
+ Success(value.splice)
+ })
def completePromWithExceptionTopLevel[A: WeakTypeTag](prom: Expr[Prom[A]], exception: Expr[Throwable]): Expr[Unit] =
- completePromWithTry(prom, reify(scala.util.Failure(exception.splice)))
+ completePromWithTry(prom, reify {
+ import scala.util.Failure
+ Failure(exception.splice)
+ })
def completePromWithFailedResult[A: WeakTypeTag](prom: Expr[Prom[A]], resultName: TermName): Expr[Unit] = {
val result = c.Expr[scala.util.Try[A]](
@@ -134,6 +147,8 @@ object ScalaConcurrentFutureSystem extends TryBasedFutureSystem {
import c.universe._
+ val utils = TransformUtils[c.type](c)
+
def execContext: Expr[ExecContext] = c.Expr(c.inferImplicitValue(c.weakTypeOf[ExecutionContext]) match {
case EmptyTree => c.abort(c.macroApplication.pos, "Unable to resolve implicit ExecutionContext")
case context => context
@@ -147,6 +162,11 @@ object ScalaConcurrentFutureSystem extends TryBasedFutureSystem {
Promise[A]()
}
+ def createPromTree[A: WeakTypeTag](stateMachine: Tree): Tree = {
+ // ignore stateMachine
+ (reify { Promise[A]() }).tree
+ }
+
def promiseToFuture[A: WeakTypeTag](prom: Expr[Prom[A]]) = reify {
prom.splice.future
}
@@ -197,6 +217,14 @@ object IdentityFutureSystem extends TryBasedFutureSystem {
new Prom(null.asInstanceOf[A])
}
+ def createPromTree[A: WeakTypeTag](stateMachine: Tree): Tree = {
+ val utils = TransformUtils[c.type](c)
+ val asyncTree = Select(Ident(newTermName("scala")), newTermName("async"))
+ val fsTree = Select(asyncTree, newTermName("IdentityFutureSystem"))
+ Apply(Select(New(AppliedTypeTree(Select(fsTree, newTypeName("Prom")), List(TypeTree(weakTypeOf[A])))), nme.CONSTRUCTOR),
+ List(utils.defaultValue(weakTypeOf[A])))
+ }
+
def promiseToFuture[A: WeakTypeTag](prom: Expr[Prom[A]]) = reify {
prom.splice.a
}
@@ -205,7 +233,7 @@ object IdentityFutureSystem extends TryBasedFutureSystem {
def onComplete[A, U](future: Expr[Fut[A]], fun: Expr[scala.util.Try[A] => U],
execContext: Expr[ExecContext]): Expr[Unit] = reify {
- fun.splice.apply(util.Success(future.splice))
+ fun.splice.apply(scala.util.Success(future.splice))
c.literalUnit.splice
}
diff --git a/src/main/scala/scala/async/iterators/Async.scala b/src/main/scala/scala/async/iterators/Async.scala
new file mode 100644
index 0000000..897bc8e
--- /dev/null
+++ b/src/main/scala/scala/async/iterators/Async.scala
@@ -0,0 +1,109 @@
+package scala.async.iterators
+
+import scala.language.experimental.macros
+
+import scala.reflect.macros.Context
+import scala.async.{ AsyncBase, FutureSystem }
+import scala.concurrent.{ Promise, Future, ExecutionContext }
+import scala.util.Try
+
+object Async extends AsyncBase {
+
+ lazy val futureSystem = IteratorsFutureSystem
+ type FS = IteratorsFutureSystem.type
+
+ def async[T](body: T) = macro asyncImpl[T]
+
+ override def asyncImpl[T: c.WeakTypeTag](c: Context)(body: c.Expr[T]): c.Expr[IteratorState[T]] = super.asyncImpl[T](c)(body)
+
+}
+
+object IteratorsFutureSystem extends FutureSystem {
+ type Prom[A] = IteratorState[A]
+ type Fut[A] = IteratorState[A]
+ type Result[A] = Try[A]
+ type ExecContext = Unit
+
+ def mkOps(context: Context): Ops { val c: context.type } = new Ops {
+ val c: context.type = context
+ import c.universe._
+
+ def promType[A: WeakTypeTag]: Type = c.weakTypeOf[Prom[A]]
+
+ def stateMachineType[A: WeakTypeTag]: Type =
+ // The generated state machine will extend trait `IteratorState`
+ c.weakTypeOf[scala.async.iterators.IteratorState[A]]
+
+ def execContextType: Type = c.weakTypeOf[Unit]
+ def resultType[A: WeakTypeTag]: Type = c.weakTypeOf[Result[A]]
+
+ /**
+ * @param tree ident referring to state machine
+ */
+ override def spawn(tree: Tree): Tree = {
+ // don't call future here, but return state machine
+ tree
+ }
+
+ def execContext: Expr[ExecContext] = c.literalUnit
+
+ def castTo[A: WeakTypeTag](future: Expr[Fut[Any]]): Expr[Fut[A]] = ???
+
+ def completeProm[A: WeakTypeTag](prom: Expr[Prom[A]], value: Expr[A]): Expr[Unit] = reify {
+ prom.splice.result = value.splice
+ }
+
+ def completePromWithExceptionTopLevel[A: WeakTypeTag](prom: Expr[Prom[A]], exception: Expr[Throwable]): Expr[Unit] =
+ reify { ??? }
+
+ def completePromWithFailedResult[A: WeakTypeTag](prom: Expr[Prom[A]], resultName: TermName): Expr[Unit] =
+ reify { () }
+
+ def createProm[A: WeakTypeTag]: Expr[Prom[A]] = ???
+
+ def createPromTree[A: WeakTypeTag](stateMachine: Tree): Tree = {
+ // return `this` state machine
+ This(tpnme.EMPTY)
+ }
+
+ def future[A: WeakTypeTag](a: Expr[A])(execContext: Expr[ExecContext]): Expr[Fut[A]] =
+ reify { ??? }
+
+ def onComplete[A, U](future: Expr[Fut[A]], fun: Expr[Result[A] => U],
+ execContext: Expr[ExecContext]): Expr[Unit] = reify {
+ /* do nothing */
+ }
+
+ def promiseToFuture[A: WeakTypeTag](prom: Expr[Prom[A]]): Expr[Fut[A]] = prom
+
+ /** `methodSym( (_: Foo).bar(null: A, null: B)` will return the symbol of `bar`, after overload resolution. */
+ private def methodSym(apply: c.Expr[Any]): Symbol = {
+ val tree2: Tree = c.typeCheck(apply.tree)
+ tree2.collect {
+ case s: SymTree if s.symbol.isMethod => s.symbol
+ }.headOption.getOrElse(sys.error(s"Unable to find a method symbol in ${apply.tree}"))
+ }
+
+ lazy val Try_isFailure = methodSym(reify((null: scala.util.Try[Any]).isFailure))
+ lazy val Try_get = methodSym(reify((null: scala.util.Try[Any]).get))
+
+ def isFailedResult(name: TermName): Expr[Boolean] =
+ c.Expr[Boolean](Select(Ident(name), Try_isFailure))
+
+ def resultValue(name: TermName, resultType: Type): Tree =
+ TypeApply(Select(Select(Ident(name), Try_get), newTermName("asInstanceOf")), List(TypeTree(resultType)))
+
+/*
+ lazy val IS_result = methodSym(reify((null: scala.async.iterators.IteratorState[Any]).result))
+ lazy val IS_isFailed = methodSym(reify((null: scala.async.iterators.IteratorState[Any]).isFailed))
+
+ // <resultName> = name.result.asInstanceOf[<resultType>]
+ def resultValue(name: TermName, resultType: Type): Tree =
+ TypeApply(Select(Select(Ident(name), IS_result), newTermName("asInstanceOf")), List(TypeTree(resultType)))
+
+ def isFailedResult(name: TermName): Expr[Boolean] =
+ c.Expr[Boolean](Select(Ident(name), IS_isFailed))
+*/
+ }
+
+}
diff --git a/src/main/scala/scala/async/iterators/IteratorState.scala b/src/main/scala/scala/async/iterators/IteratorState.scala
new file mode 100644
index 0000000..5b8cf06
--- /dev/null
+++ b/src/main/scala/scala/async/iterators/IteratorState.scala
@@ -0,0 +1,50 @@
+package scala.async.iterators
+
+import scala.util.{Try, Success, Failure}
+
+trait IteratorState[T] {
+
+ def apply(v: Try[Any]): Unit
+ def apply: Unit
+ def `result$async` : IteratorState[T]
+
+ private[this] var _value: Option[T] = None
+ private[this] var _exc: Throwable = null
+
+ def result_= (value: T) =
+ _value = Some(value)
+
+ def result: T = {
+ if (_exc != null)
+ throw _exc
+ else
+ _value.get
+ }
+
+ def exception_= (exc: Throwable) =
+ _exc = exc
+
+ def exception: Throwable =
+ _exc
+
+ def isFailed: Boolean =
+ _exc != null
+
+ def onComplete(cont: IteratorState[_]) = {
+ // cont will always be `this`
+ /* do nothing */
+ }
+
+ def next: T = {
+ // continue iteration with next state
+ this.apply(Success(result))
+ // return current result
+ result
+ }
+
+ def hasNext: Boolean = {
+ println("invoking apply")
+ apply
+ _value.nonEmpty
+ }
+}
diff --git a/src/test/scala/scala/async/run/cps/CPSSpec.scala b/src/test/scala/scala/async/run/cps/CPSSpec.scala
index b56c6ad..208206f 100644
--- a/src/test/scala/scala/async/run/cps/CPSSpec.scala
+++ b/src/test/scala/scala/async/run/cps/CPSSpec.scala
@@ -14,7 +14,7 @@ import scala.util.continuations._
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.junit.Test
-
+/*
@RunWith(classOf[JUnit4])
class CPSSpec {
@@ -47,3 +47,4 @@ class CPSSpec {
}
}
+*/