Handle case of invalidating stale discarding matches (MPSCR-35)

This commit is contained in:
Grigorii Kirgizov 2020-02-03 20:23:34 +03:00
parent 18fc4592f0
commit efd22872a9
3 changed files with 58 additions and 2 deletions

View File

@ -22,6 +22,7 @@ import jetbrains.mps.logic.reactor.program.IncrementalProgramSpec
import jetbrains.mps.logic.reactor.evaluation.RuleMatch
import jetbrains.mps.logic.reactor.evaluation.SessionToken
import jetbrains.mps.logic.reactor.program.Rule
import jetbrains.mps.logic.reactor.util.Id
import jetbrains.mps.logic.reactor.util.Profiler
import jetbrains.mps.logic.reactor.util.profile
@ -46,7 +47,8 @@ internal class ConstraintsProcessing(private var dispatchingFront: Dispatcher.Di
val profiler: Profiler? = null)
: StoreAwareJournalImpl(journal, logicalState)
{
private val execQueue: ExecutionQueue = ExecutionQueue(journal.index(), RuleOrdering(ruleIndex))
private val journalIndex: MatchJournal.Index = journal.index()
private val execQueue: ExecutionQueue = ExecutionQueue(journalIndex, RuleOrdering(ruleIndex))
private data class MatchCandidate(val rule: Rule, val occChunk: MatchJournal.OccChunk)
@ -208,10 +210,13 @@ internal class ConstraintsProcessing(private var dispatchingFront: Dispatcher.Di
push()
assert(active.alive)
val activationChunk: MatchJournal.OccChunk?
if (!active.stored) {
active.stored = true
logActivation(active)
activationChunk = logActivation(active)
active.revive(logicalState)
} else {
activationChunk = journalIndex.activatingChunkOf(Id(active))
}
profiler.profile("dispatch_${active.constraint().symbol()}") {
@ -223,6 +228,7 @@ internal class ConstraintsProcessing(private var dispatchingFront: Dispatcher.Di
val matches = dispatchingFront.matches().toList()
val newCurrentMatches =
// todo: assert that there can be no future matches on non-principal occurrences?
// with that move withPostponedMatches to else-branch
if (isFront() || !active.isPrincipal()) {
matches
} else {
@ -232,6 +238,13 @@ internal class ConstraintsProcessing(private var dispatchingFront: Dispatcher.Di
val currentMatches = execQueue.withPostponedMatches(active, newCurrentMatches)
val outStatus = currentMatches.fold(inStatus) { status, match ->
if (activationChunk != null && !isFront()) {
val discards = match.matchHeadReplaced().contains(active)
if (discards) {
dropDiscardingMatchesFor(activationChunk)
}
}
// TODO: paranoid check. should be isAlive() instead
// FIXME: move this check elsewhere
if (status.operational && active.stored && match.allStored())
@ -248,6 +261,12 @@ internal class ConstraintsProcessing(private var dispatchingFront: Dispatcher.Di
return outStatus
}
private fun dropDiscardingMatchesFor(ancestor: MatchJournal.OccChunk) =
this.dropDescendantsWhile(ancestor) { chunk ->
chunk is MatchJournal.MatchChunk
&& chunk.match.matchHeadReplaced().contains(ancestor.occ)
}
private inline fun FeedbackStatus.then(action: (FeedbackStatus) -> FeedbackStatus) : FeedbackStatus =
if (operational) action(this) else this

View File

@ -69,6 +69,13 @@ interface MatchJournal : MutableIterable<MatchJournal.Chunk> {
*/
fun reset(pastPos: Pos)
/**
* Removes [Chunk]s among descendants of [ancestor] if they satisfy [dropIf] predicate.
* Transitively removes descendants of removed chunks.
* Only erases future chunks and leaves journal position intact.
*/
fun dropDescendantsWhile(ancestor: Chunk, dropIf: (Chunk) -> Boolean)
/**
* Replay activated and discarded occurrences logged in journal between current and provided positions.
* Advances journal position to specified position.

View File

@ -175,6 +175,36 @@ internal open class MatchJournalImpl(
if (currentPos() != until) throw IllegalStateException()
}
override fun dropDescendantsWhile(ancestor: Chunk, dropIf: (Chunk) -> Boolean) {
// starts iterating from the Chunk which is next after current
// leaves 'current' and 'posPtr' intact
val droppedIds = mutableListOf<Int>()
val start = current
while (posPtr.hasNext()) {
current = posPtr.next()
if (!current.isDescendantOf(ancestor.id)) {
break
}
if (dropIf(current)) {
droppedIds.add(current.id)
// no need to 'resetOccurrences' because journal position is left intact
posPtr.remove()
} else if (current.justifications.intersects(droppedIds)) {
// drop descendants of dropped Chunks
posPtr.remove()
}
}
// rollback to the start
while (current != start) {
current = posPtr.previous()
}
// make ptr point right after 'current' in case we changed anything
if (posPtr.hasNext()) posPtr.next()
}
private fun resetOccurrences(occSpecs: Iterable<MatchJournal.Chunk.Entry>) =
occSpecs.forEach {
if (it.discarded) {