detect cyclic references after freeze and translate nodes to keys

This commit is contained in:
Mark Harrah 2011-04-07 22:51:25 -04:00
parent 0b4d8fb505
commit 76ab8f8e53
2 changed files with 23 additions and 4 deletions

View File

@ -178,11 +178,24 @@ object EvaluateTask
replaced
}
def transformInc[T](result: Result[T]): Result[T] =
result.toEither.left.map { i => Incomplete.transformBU(i)(liftAnonymous andThen taskToKey ) }
result.toEither.left.map { i => Incomplete.transformBU(i)(convertCyclicInc andThen liftAnonymous andThen taskToKey ) }
def taskToKey: Incomplete => Incomplete = {
case in @ Incomplete(Some(node: Task[_]), _, _, _, _) => in.copy(node = transformNode(node))
case i => i
}
type AnyCyclic = Execute[Task]#CyclicException[_]
def convertCyclicInc: Incomplete => Incomplete = {
case in @ Incomplete(_, _, _, _, Some(c: AnyCyclic)) => in.copy(directCause = Some(new RuntimeException(convertCyclic(c))) )
case i => i
}
def convertCyclic(c: AnyCyclic): String =
(c.caller, c.target) match {
case (caller: Task[_], target: Task[_]) =>
c.toString + (if(caller eq target) "(task: " + name(caller) + ")" else "(caller: " + name(caller) + ", target: " + name(target) + ")" )
case _ => c.toString
}
def name(node: Task[_]): String =
node.info.name orElse transformNode(node).map(Project.display) getOrElse ("<anon-" + System.identityHashCode(node).toHexString + ">")
def liftAnonymous: Incomplete => Incomplete = {
case i @ Incomplete(node, tpe, None, causes, None) =>
causes.find( inc => inc.message.isDefined || inc.directCause.isDefined) match {

View File

@ -47,7 +47,7 @@ final class Execute[A[_] <: AnyRef](checkCycles: Boolean)(implicit view: NodeVie
def dump: String = "State: " + state.toString + "\n\nResults: " + results + "\n\nCalls: " + callers + "\n\n"
def run[T](root: A[T])(implicit strategy: Strategy): Result[T] = runKeep(root)(strategy)(root)
def run[T](root: A[T])(implicit strategy: Strategy): Result[T] = try { runKeep(root)(strategy)(root) } catch { case i: Incomplete => Inc(i) }
def runKeep[T](root: A[T])(implicit strategy: Strategy): RMap[A,Result] =
{
assert(state.isEmpty, "Execute already running/ran.")
@ -64,7 +64,10 @@ final class Execute[A[_] <: AnyRef](checkCycles: Boolean)(implicit view: NodeVie
{
pre {
assert( !reverse.isEmpty, "Nothing to process." )
assert( state.values.exists( _ == Running ), "Nothing running")
if( !state.values.exists( _ == Running ) ) {
snapshotCycleCheck()
assert(false, "Nothing running.")
}
}
(strategy.take()).process()
@ -78,7 +81,6 @@ final class Execute[A[_] <: AnyRef](checkCycles: Boolean)(implicit view: NodeVie
}
}
def call[T](node: A[T], target: A[T])(implicit strategy: Strategy)
{
if(checkCycles) cycleCheck(node, target)
@ -296,6 +298,10 @@ final class Execute[A[_] <: AnyRef](checkCycles: Boolean)(implicit view: NodeVie
// cyclic reference checking
def snapshotCycleCheck(): Unit =
for( (called: A[c], callers) <- callers.toSeq; caller <- callers)
cycleCheck(caller.asInstanceOf[A[c]], called)
def cycleCheck[T](node: A[T], target: A[T])
{
if(node eq target) cyclic(node, target, "Cannot call self")