diff options
author | Jason Zaugg <jzaugg@gmail.com> | 2012-11-22 11:33:23 +0100 |
---|---|---|
committer | Jason Zaugg <jzaugg@gmail.com> | 2012-11-22 11:33:23 +0100 |
commit | e126dc2ded320c81868e6456853f1ecd2629c495 (patch) | |
tree | c1c872787723531a321b6ccc8be4e55c323a17e5 /src/main | |
parent | 7f8e9876ca83db4ed7792f09f218b341fbc6c2b2 (diff) | |
download | scala-async-e126dc2ded320c81868e6456853f1ecd2629c495.tar.gz scala-async-e126dc2ded320c81868e6456853f1ecd2629c495.tar.bz2 scala-async-e126dc2ded320c81868e6456853f1ecd2629c495.zip |
Frugality is no longer required with state IDs.
Change the way state IDs are allocated to avoid the need to ration them out.
Also make the logging tolerant of a toolbox setting that doesn't have a source file.
Diffstat (limited to 'src/main')
-rw-r--r-- | src/main/scala/scala/async/Async.scala | 17 | ||||
-rw-r--r-- | src/main/scala/scala/async/ExprBuilder.scala | 81 | ||||
-rw-r--r-- | src/main/scala/scala/async/StateAssigner.scala | 10 |
3 files changed, 52 insertions, 56 deletions
diff --git a/src/main/scala/scala/async/Async.scala b/src/main/scala/scala/async/Async.scala index bad693d..f9ed27e 100644 --- a/src/main/scala/scala/async/Async.scala +++ b/src/main/scala/scala/async/Async.scala @@ -52,11 +52,11 @@ abstract class AsyncBase { /** * A call to `await` must be nested in an enclosing `async` block. - * + * * A call to `await` does not block the current thread, rather it is a delimiter * used by the enclosing `async` macro. Code following the `await` * call is executed asynchronously, when the argument of `await` has been completed. - * + * * @param awaitable the future from which a value is awaited. * @tparam T the type of that value. * @return the value. @@ -88,7 +88,14 @@ abstract class AsyncBase { (vd.symbol, builder.name.fresh(vd.name)) }.toMap - AsyncUtils.vprintln(s"In file '${c.macroApplication.pos.source.path}':") + def location = try { + c.macroApplication.pos.source.path + } catch { + case _: UnsupportedOperationException => + c.macroApplication.pos.toString + } + + AsyncUtils.vprintln(s"In file '$location':") AsyncUtils.vprintln(s"${c.macroApplication}") AsyncUtils.vprintln(s"ANF transform expands to:\n $btree") @@ -96,8 +103,10 @@ abstract class AsyncBase { case Block(stats, expr) => (stats, expr) case tree => (Nil, tree) } + val startState = builder.stateAssigner.nextState() + val endState = Int.MaxValue - val asyncBlockBuilder = new builder.AsyncBlockBuilder(stats, expr, 0, 1000, 1000, renameMap) + val asyncBlockBuilder = new builder.AsyncBlockBuilder(stats, expr, startState, endState, renameMap) asyncBlockBuilder.asyncStates foreach (s => AsyncUtils.vprintln(s)) diff --git a/src/main/scala/scala/async/ExprBuilder.scala b/src/main/scala/scala/async/ExprBuilder.scala index f65e481..255349f 100644 --- a/src/main/scala/scala/async/ExprBuilder.scala +++ b/src/main/scala/scala/async/ExprBuilder.scala @@ -5,8 +5,6 @@ package scala.async import scala.reflect.macros.Context import scala.collection.mutable.ListBuffer -import scala.concurrent.Future -import AsyncUtils.vprintln /* * @author Philipp Haller @@ -16,7 +14,6 @@ final class ExprBuilder[C <: Context, FS <: FutureSystem](override val c: C, val builder => import c.universe._ - import Flag._ import defn._ private[async] object name { @@ -39,8 +36,6 @@ final class ExprBuilder[C <: Context, FS <: FutureSystem](override val c: C, val private[async] lazy val futureSystemOps = futureSystem.mkOps(c) - private val execContext = futureSystemOps.execContext - private def resetDuplicate(tree: Tree) = c.resetAllAttrs(tree.duplicate) private def mkResumeApply = Apply(Ident(name.resume), Nil) @@ -80,7 +75,7 @@ final class ExprBuilder[C <: Context, FS <: FutureSystem](override val c: C, val Ident(aw.resultName), TypeApply(Select(Select(Ident(name.tr), Try_get), newTermName("asInstanceOf")), List(TypeTree(aw.resultType))) ) - val updateState = mkStateTree(nextState) // or increment? + val updateState = mkStateTree(nextState) Some(mkHandlerCase(state, List(tryGetTree, updateState, mkResumeApply))) case _ => None @@ -150,7 +145,7 @@ final class ExprBuilder[C <: Context, FS <: FutureSystem](override val c: C, val /* Result type of an await call. */ var resultType: Type = null - var nextState: Int = state + 1 + var nextState: Int = -1 private val varDefs = ListBuffer[(TermName, Type)]() @@ -196,7 +191,7 @@ final class ExprBuilder[C <: Context, FS <: FutureSystem](override val c: C, val * @param awaitResultType the type of the result of await */ def complete(awaitArg: c.Tree, awaitResultName: TermName, awaitResultType: Tree, - nextState: Int = state + 1): this.type = { + nextState: Int): this.type = { val renamed = renamer.transform(awaitArg) awaitable = resetDuplicate(renamed) resultName = awaitResultName @@ -229,14 +224,13 @@ final class ExprBuilder[C <: Context, FS <: FutureSystem](override val c: C, val * * @param scrutTree tree of the scrutinee * @param cases list of case definitions - * @param stateFirstCase state of the right-hand side of the first case - * @param perCaseBudget maximum number of states per case + * @param caseStates starting state of the right-hand side of the each case * @return an `AsyncState` representing the match expression */ - def resultWithMatch(scrutTree: c.Tree, cases: List[CaseDef], stateFirstCase: Int, perCasebudget: Int): AsyncState = { + def resultWithMatch(scrutTree: c.Tree, cases: List[CaseDef], caseStates: List[Int]): AsyncState = { // 1. build list of changed cases val newCases = for ((cas, num) <- cases.zipWithIndex) yield cas match { - case CaseDef(pat, guard, rhs) => CaseDef(pat, guard, Block(mkStateTree(num * perCasebudget + stateFirstCase), mkResumeApply)) + case CaseDef(pat, guard, rhs) => CaseDef(pat, guard, Block(mkStateTree(caseStates(num)), mkResumeApply)) } // 2. insert changed match tree at the end of the current state this += Match(resetDuplicate(scrutTree), newCases) @@ -251,6 +245,8 @@ final class ExprBuilder[C <: Context, FS <: FutureSystem](override val c: C, val } } + val stateAssigner = new StateAssigner + /** * An `AsyncBlockBuilder` builds a `ListBuffer[AsyncState]` based on the expressions of a `Block(stats, expr)` (see `Async.asyncImpl`). * @@ -258,43 +254,37 @@ final class ExprBuilder[C <: Context, FS <: FutureSystem](override val c: C, val * @param expr the last expression of the block * @param startState the start state * @param endState the state to continue with - * @param budget the maximum number of states in this block * @param toRename a `Map` for renaming the given key symbols to the mangled value names */ class AsyncBlockBuilder(stats: List[c.Tree], expr: c.Tree, startState: Int, endState: Int, - budget: Int, private val toRename: Map[Symbol, c.Name]) { + private val toRename: Map[Symbol, c.Name]) { val asyncStates = ListBuffer[builder.AsyncState]() private var stateBuilder = new builder.AsyncStateBuilder(startState, toRename) // current state builder private var currState = startState - private var remainingBudget = budget - /* TODO Fall back to CPS plug-in if tree contains an `await` call. */ def checkForUnsupportedAwait(tree: c.Tree) = if (tree exists { case Apply(fun, _) if fun.symbol == Async_await => true case _ => false }) c.abort(tree.pos, "await unsupported in this position") //throw new FallbackToCpsException - def builderForBranch(tree: c.Tree, state: Int, nextState: Int, budget: Int): AsyncBlockBuilder = { + def builderForBranch(tree: c.Tree, state: Int, nextState: Int): AsyncBlockBuilder = { val (branchStats, branchExpr) = tree match { case Block(s, e) => (s, e) case _ => (List(tree), c.literalUnit.tree) } - new AsyncBlockBuilder(branchStats, branchExpr, state, nextState, budget, toRename) + new AsyncBlockBuilder(branchStats, branchExpr, state, nextState, toRename) } // populate asyncStates for (stat <- stats) stat match { // the val name = await(..) pattern case ValDef(mods, name, tpt, Apply(fun, args)) if fun.symbol == Async_await => - asyncStates += stateBuilder.complete(args.head, toRename(stat.symbol).toTermName, tpt).result // complete with await - if (remainingBudget > 0) - remainingBudget -= 1 - else - assert(false, "too many invocations of `await` in current method") - currState += 1 + val afterAwaitState = stateAssigner.nextState() + asyncStates += stateBuilder.complete(args.head, toRename(stat.symbol).toTermName, tpt, afterAwaitState).result // complete with await + currState = afterAwaitState stateBuilder = new builder.AsyncStateBuilder(currState, toRename) case ValDef(mods, name, tpt, rhs) if toRename contains stat.symbol => @@ -306,50 +296,42 @@ final class ExprBuilder[C <: Context, FS <: FutureSystem](override val c: C, val case If(cond, thenp, elsep) if stat exists isAwait => checkForUnsupportedAwait(cond) - val ifBudget: Int = remainingBudget / 2 - remainingBudget -= ifBudget //TODO test if budget > 0 - // state that we continue with after if-else: currState + ifBudget - - val thenBudget: Int = ifBudget / 2 - val elseBudget = ifBudget - thenBudget + val thenStartState = stateAssigner.nextState() + val elseStartState = stateAssigner.nextState() + val afterIfState = stateAssigner.nextState() asyncStates += // the two Int arguments are the start state of the then branch and the else branch, respectively - stateBuilder.resultWithIf(cond, currState + 1, currState + thenBudget) + stateBuilder.resultWithIf(cond, thenStartState, elseStartState) - List((thenp, currState + 1, thenBudget), (elsep, currState + thenBudget, elseBudget)) foreach { - case (tree, state, branchBudget) => - val builder = builderForBranch(tree, state, currState + ifBudget, branchBudget) + List((thenp, thenStartState), (elsep, elseStartState)) foreach { + case (tree, state) => + val builder = builderForBranch(tree, state, afterIfState) asyncStates ++= builder.asyncStates } - // create new state builder for state `currState + ifBudget` - currState = currState + ifBudget + currState = afterIfState stateBuilder = new builder.AsyncStateBuilder(currState, toRename) case Match(scrutinee, cases) if stat exists isAwait => checkForUnsupportedAwait(scrutinee) - val matchBudget: Int = remainingBudget / 2 - remainingBudget -= matchBudget //TODO test if budget > 0 - // state that we continue with after match: currState + matchBudget + val caseStates = cases.map(_ => stateAssigner.nextState()) + val afterMatchState = stateAssigner.nextState() - val perCaseBudget: Int = matchBudget / cases.size asyncStates += - // the two Int arguments are the start state of the first case and the per-case state budget, respectively - stateBuilder.resultWithMatch(scrutinee, cases, currState + 1, perCaseBudget) + stateBuilder.resultWithMatch(scrutinee, cases, caseStates) for ((cas, num) <- cases.zipWithIndex) { val (casStats, casExpr) = cas match { case CaseDef(_, _, Block(s, e)) => (s, e) case CaseDef(_, _, rhs) => (List(rhs), c.literalUnit.tree) } - val builder = new AsyncBlockBuilder(casStats, casExpr, currState + (num * perCaseBudget) + 1, currState + matchBudget, perCaseBudget, toRename) + val builder = new AsyncBlockBuilder(casStats, casExpr, caseStates(num), afterMatchState, toRename) asyncStates ++= builder.asyncStates } - // create new state builder for state `currState + matchBudget` - currState = currState + matchBudget + currState = afterMatchState stateBuilder = new builder.AsyncStateBuilder(currState, toRename) case ClassDef(_, name, _, _) => @@ -366,7 +348,7 @@ final class ExprBuilder[C <: Context, FS <: FutureSystem](override val c: C, val } // complete last state builder (representing the expressions after the last await) stateBuilder += expr - val lastState = stateBuilder.complete(endState).result + val lastState = stateBuilder.complete(endState).result() asyncStates += lastState def mkCombinedHandlerCases[T](): List[CaseDef] = { @@ -428,7 +410,7 @@ final class ExprBuilder[C <: Context, FS <: FutureSystem](override val c: C, val tree match { case cd: ClassDef => val kind = if (cd.symbol.asClass.isTrait) "trait" else "class" - reportUnsupportedAwait(tree, s"nested ${kind}") + reportUnsupportedAwait(tree, s"nested $kind") case md: ModuleDef => reportUnsupportedAwait(tree, "nested object") case _: Function => @@ -497,10 +479,6 @@ final class ExprBuilder[C <: Context, FS <: FutureSystem](override val c: C, val self.splice.apply(arg.splice) } - def mkInt_+(self: Expr[Int])(other: Expr[Int]) = reify { - self.splice + other.splice - } - def mkAny_==(self: Expr[Any])(other: Expr[Any]) = reify { self.splice == other.splice } @@ -521,5 +499,4 @@ final class ExprBuilder[C <: Context, FS <: FutureSystem](override val c: C, val tpe.member(c.universe.newTermName("await")) } } - } diff --git a/src/main/scala/scala/async/StateAssigner.scala b/src/main/scala/scala/async/StateAssigner.scala new file mode 100644 index 0000000..4f6c5a0 --- /dev/null +++ b/src/main/scala/scala/async/StateAssigner.scala @@ -0,0 +1,10 @@ +package scala.async + +private[async] final class StateAssigner { + private var current = -1 + + def nextState(): Int = { + current += 1 + current + } +}
\ No newline at end of file |