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 replaced
} }
def transformInc[T](result: Result[T]): Result[T] = 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 = { def taskToKey: Incomplete => Incomplete = {
case in @ Incomplete(Some(node: Task[_]), _, _, _, _) => in.copy(node = transformNode(node)) case in @ Incomplete(Some(node: Task[_]), _, _, _, _) => in.copy(node = transformNode(node))
case i => i 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 = { def liftAnonymous: Incomplete => Incomplete = {
case i @ Incomplete(node, tpe, None, causes, None) => case i @ Incomplete(node, tpe, None, causes, None) =>
causes.find( inc => inc.message.isDefined || inc.directCause.isDefined) match { 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 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] = def runKeep[T](root: A[T])(implicit strategy: Strategy): RMap[A,Result] =
{ {
assert(state.isEmpty, "Execute already running/ran.") assert(state.isEmpty, "Execute already running/ran.")
@ -64,7 +64,10 @@ final class Execute[A[_] <: AnyRef](checkCycles: Boolean)(implicit view: NodeVie
{ {
pre { pre {
assert( !reverse.isEmpty, "Nothing to process." ) 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() (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) def call[T](node: A[T], target: A[T])(implicit strategy: Strategy)
{ {
if(checkCycles) cycleCheck(node, target) if(checkCycles) cycleCheck(node, target)
@ -296,6 +298,10 @@ final class Execute[A[_] <: AnyRef](checkCycles: Boolean)(implicit view: NodeVie
// cyclic reference checking // 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]) def cycleCheck[T](node: A[T], target: A[T])
{ {
if(node eq target) cyclic(node, target, "Cannot call self") if(node eq target) cyclic(node, target, "Cannot call self")