1
0

diplomacy: detect and report cycles in the diplomatic graph

This commit is contained in:
Wesley W. Terpstra 2017-09-27 11:46:06 -07:00
parent 5af08966d8
commit e35d3df6ea

View File

@ -199,6 +199,11 @@ trait OutwardNode[DO, UO, BO <: Data] extends BaseNode with OutwardNodeHandle[DO
protected[diplomacy] val oParams: Seq[DO] protected[diplomacy] val oParams: Seq[DO]
} }
abstract class CycleException(kind: String, loop: Seq[String]) extends Exception(s"Diplomatic ${kind} cycle detected involving ${loop}")
case class StarCycleException(loop: Seq[String] = Nil) extends CycleException("star", loop)
case class DownwardCycleException(loop: Seq[String] = Nil) extends CycleException("downward", loop)
case class UpwardCycleException(loop: Seq[String] = Nil) extends CycleException("upward", loop)
case class Edges[EI, EO](in: EI, out: EO) case class Edges[EI, EO](in: EI, out: EO)
sealed abstract class MixedNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data]( sealed abstract class MixedNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data](
inner: InwardNodeImp [DI, UI, EI, BI], inner: InwardNodeImp [DI, UI, EI, BI],
@ -212,31 +217,37 @@ sealed abstract class MixedNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data](
protected[diplomacy] def mapParamsD(n: Int, p: Seq[DI]): Seq[DO] protected[diplomacy] def mapParamsD(n: Int, p: Seq[DI]): Seq[DO]
protected[diplomacy] def mapParamsU(n: Int, p: Seq[UO]): Seq[UI] protected[diplomacy] def mapParamsU(n: Int, p: Seq[UO]): Seq[UI]
private var starCycleGuard = false
protected[diplomacy] lazy val (oPortMapping, iPortMapping, oStar, iStar) = { protected[diplomacy] lazy val (oPortMapping, iPortMapping, oStar, iStar) = {
val oStars = oBindings.filter { case (_,_,b,_,_) => b == BIND_STAR }.size try {
val iStars = iBindings.filter { case (_,_,b,_,_) => b == BIND_STAR }.size if (starCycleGuard) throw StarCycleException()
val oKnown = oBindings.map { case (_, n, b, _, _) => b match { val oStars = oBindings.filter { case (_,_,b,_,_) => b == BIND_STAR }.size
case BIND_ONCE => 1 val iStars = iBindings.filter { case (_,_,b,_,_) => b == BIND_STAR }.size
case BIND_QUERY => n.iStar val oKnown = oBindings.map { case (_, n, b, _, _) => b match {
case BIND_STAR => 0 }}.foldLeft(0)(_+_) case BIND_ONCE => 1
val iKnown = iBindings.map { case (_, n, b, _, _) => b match { case BIND_QUERY => n.iStar
case BIND_ONCE => 1 case BIND_STAR => 0 }}.foldLeft(0)(_+_)
case BIND_QUERY => n.oStar val iKnown = iBindings.map { case (_, n, b, _, _) => b match {
case BIND_STAR => 0 }}.foldLeft(0)(_+_) case BIND_ONCE => 1
val (iStar, oStar) = resolveStar(iKnown, oKnown, iStars, oStars) case BIND_QUERY => n.oStar
val oSum = oBindings.map { case (_, n, b, _, _) => b match { case BIND_STAR => 0 }}.foldLeft(0)(_+_)
case BIND_ONCE => 1 val (iStar, oStar) = resolveStar(iKnown, oKnown, iStars, oStars)
case BIND_QUERY => n.iStar val oSum = oBindings.map { case (_, n, b, _, _) => b match {
case BIND_STAR => oStar }}.scanLeft(0)(_+_) case BIND_ONCE => 1
val iSum = iBindings.map { case (_, n, b, _, _) => b match { case BIND_QUERY => n.iStar
case BIND_ONCE => 1 case BIND_STAR => oStar }}.scanLeft(0)(_+_)
case BIND_QUERY => n.oStar val iSum = iBindings.map { case (_, n, b, _, _) => b match {
case BIND_STAR => iStar }}.scanLeft(0)(_+_) case BIND_ONCE => 1
val oTotal = oSum.lastOption.getOrElse(0) case BIND_QUERY => n.oStar
val iTotal = iSum.lastOption.getOrElse(0) case BIND_STAR => iStar }}.scanLeft(0)(_+_)
require(numPO.contains(oTotal), s"${name} has ${oTotal} outputs, expected ${numPO}${lazyModule.line}") val oTotal = oSum.lastOption.getOrElse(0)
require(numPI.contains(iTotal), s"${name} has ${iTotal} inputs, expected ${numPI}${lazyModule.line}") val iTotal = iSum.lastOption.getOrElse(0)
(oSum.init zip oSum.tail, iSum.init zip iSum.tail, oStar, iStar) require(numPO.contains(oTotal), s"${name} has ${oTotal} outputs, expected ${numPO}${lazyModule.line}")
require(numPI.contains(iTotal), s"${name} has ${iTotal} inputs, expected ${numPI}${lazyModule.line}")
(oSum.init zip oSum.tail, iSum.init zip iSum.tail, oStar, iStar)
} catch {
case c: StarCycleException => throw c.copy(loop = s"${name}${lazyModule.line}" +: c.loop)
}
} }
lazy val oPorts = oBindings.flatMap { case (i, n, _, p, s) => lazy val oPorts = oBindings.flatMap { case (i, n, _, p, s) =>
@ -248,15 +259,30 @@ sealed abstract class MixedNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data](
(start until end) map { j => (j, n, p, s) } (start until end) map { j => (j, n, p, s) }
} }
private var oParamsCycleGuard = false
protected[diplomacy] lazy val oParams: Seq[DO] = { protected[diplomacy] lazy val oParams: Seq[DO] = {
val o = mapParamsD(oPorts.size, iPorts.map { case (i, n, _, _) => n.oParams(i) }) try {
require (o.size == oPorts.size, s"Bug in diplomacy; ${name} has ${o.size} != ${oPorts.size} down/up outer parameters${lazyModule.line}") if (oParamsCycleGuard) throw DownwardCycleException()
o.map(outer.mixO(_, this)) oParamsCycleGuard = true
val o = mapParamsD(oPorts.size, iPorts.map { case (i, n, _, _) => n.oParams(i) })
require (o.size == oPorts.size, s"Bug in diplomacy; ${name} has ${o.size} != ${oPorts.size} down/up outer parameters${lazyModule.line}")
o.map(outer.mixO(_, this))
} catch {
case c: DownwardCycleException => throw c.copy(loop = s"${name}${lazyModule.line}" +: c.loop)
}
} }
private var iParamsCycleGuard = false
protected[diplomacy] lazy val iParams: Seq[UI] = { protected[diplomacy] lazy val iParams: Seq[UI] = {
val i = mapParamsU(iPorts.size, oPorts.map { case (o, n, _, _) => n.iParams(o) }) try {
require (i.size == iPorts.size, s"Bug in diplomacy; ${name} has ${i.size} != ${iPorts.size} up/down inner parameters${lazyModule.line}") if (iParamsCycleGuard) throw UpwardCycleException()
i.map(inner.mixI(_, this)) iParamsCycleGuard = true
val i = mapParamsU(iPorts.size, oPorts.map { case (o, n, _, _) => n.iParams(o) })
require (i.size == iPorts.size, s"Bug in diplomacy; ${name} has ${i.size} != ${iPorts.size} up/down inner parameters${lazyModule.line}")
i.map(inner.mixI(_, this))
} catch {
case c: UpwardCycleException => throw c.copy(loop = s"${name}${lazyModule.line}" +: c.loop)
}
} }
protected[diplomacy] def gco = if (iParams.size != 1) None else inner.getO(iParams(0)) protected[diplomacy] def gco = if (iParams.size != 1) None else inner.getO(iParams(0))