From 50fcfd0d8a31a7ca50cfc544a228eee305ca2925 Mon Sep 17 00:00:00 2001 From: Grigorii Kirgizov Date: Wed, 5 Jun 2019 18:39:49 +0300 Subject: [PATCH] Add initial version of logic for handling future matches in processActivated() --- .../reactor/core/internal/MatchJournal.kt | 34 ++++++---- .../core/internal/ProcessingStateImpl.kt | 62 +++++++++++++++++-- 2 files changed, 77 insertions(+), 19 deletions(-) diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchJournal.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchJournal.kt index e36fd869..a3877bb5 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchJournal.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchJournal.kt @@ -38,6 +38,8 @@ interface MatchJournal : MutableIterable { fun logActivation(occ: Occurrence) fun currentPos(): Pos + fun isCurrent(): Boolean = currentPos().chunk() == this.last() + fun resetPos() fun reset(pastPos: Pos) fun replay(controller: Controller, futurePos: Pos) @@ -89,6 +91,25 @@ interface MatchJournal : MutableIterable { && other.chunk() === chunk() && other.entriesInChunk() == entriesInChunk() } + + data class OccurrencePos(val occ: Occurrence, private val chunk: MatchJournal.Chunk, private val offset: Int): MatchJournal.Pos() + { + companion object { + fun get(index: MatchJournal.Index, occ: Occurrence): OccurrencePos? { + val idOcc = Id(occ) + val chunk = index.activatingChunkOf(idOcc) ?: return null + val i = chunk.entriesLog().indexOfFirst { entry -> + Id(entry.occ) == idOcc && !entry.isDiscarded + } + val offset = i + 1 + return if (offset >= 0) OccurrencePos(occ, chunk, offset) else null + } + } + + override fun chunk(): MatchJournal.Chunk = chunk + override fun entriesInChunk(): Int = offset + } + } @@ -228,7 +249,6 @@ internal open class MatchJournalImpl( } } - class IndexImpl(ispec: IncrementalProgramSpec, chunks: Iterable): MatchJournal.Index { private val chunkOrder: Map @@ -257,18 +277,6 @@ internal open class MatchJournalImpl( val co = compareBy{ chunkOrder[it.chunk().id] }.compare(lhs, rhs) return if (co == 0) lhs.entriesInChunk().compareTo(rhs.entriesInChunk()) else co } - - // Compare occurrences by their activating chunk - // todo: test case for two principal occurrences activated in the same chunk??? - fun compare(lhs: Occurrence, rhs: Occurrence): Int { - val cl = activatingChunkOf(Id(lhs)) - val cr = activatingChunkOf(Id(rhs)) - - if (cl == null && cr == null) return 0 - if (cl == null) return -1 - if (cr == null) return 1 - return this.compare(cl, cr) - } } diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ProcessingStateImpl.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ProcessingStateImpl.kt index 62434ec8..ea3648c1 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ProcessingStateImpl.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ProcessingStateImpl.kt @@ -25,7 +25,6 @@ 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 -import org.jetbrains.kotlin.utils.mapToIndex import java.util.* @@ -110,11 +109,18 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp val profiler: Profiler? = null) : StoreAwareJournalImpl(journal) { - private val ruleOrder: Map = ruleIndex.map { it.uniqueTag() }.mapToIndex() + private val ruleOrder: Map = HashMap().apply { + ruleIndex.forEachIndexed { index, rule -> put(rule.uniqueTag(), index) } + } + private val journalIndex: MatchJournal.Index = journal.index() - private val execQueue: Queue = LinkedList() -// private val execQueue: Queue = PriorityQueue(this.count() / 3) { lhs, rhs -> journalIndex.compare(lhs.pos, rhs.pos) } + private val postponedMatches: MutableMap, List> = HashMap() + + private val execQueue: Queue = + PriorityQueue(1 + this.count() / 2) { + lhs, rhs -> journalIndex.compare(lhs.pos, rhs.pos) + } private data class ExecPos(val pos: MatchJournal.Pos, val activeOcc: Occurrence) @@ -319,7 +325,6 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp active.stored = true trace.activate(active) - // todo: need doing it at reactivation? logActivation(active) active.revive(controller) @@ -329,7 +334,20 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp this.dispatchingFront = dispatchingFront.expand(active) - val outStatus = dispatchingFront.matches().toList().fold(inStatus) { status, match -> + val matches = dispatchingFront.matches().toList() + val currentMatches = + // todo: assert that there can be no future matches on non-principal occurrences? +// if (isCurrent() || !active.isPrincipal()) { + if (true) { + matches + } else { + val newCurrentMatches = handleFutureMatches(matches) + val allMatches = newCurrentMatches + (postponedMatches.remove(Id(active)) ?: emptyList()) + // Sort according to rule priorities + allMatches.sortedBy { ruleOrder[it.rule().uniqueTag()] } + } + + val outStatus = currentMatches.fold(inStatus) { status, match -> // TODO: paranoid check. should be isAlive() instead // FIXME: move this check elsewhere if (status.operational && active.stored && match.allStored()) @@ -347,6 +365,38 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp } } + // TODO: provide ispec to ProcessingStateImpl and use it + private fun Occurrence.isPrincipal() = this.constraint().isPrincipal() + + // Determines, filters out and enqueues (to execution queue) future matches. Returns only current matches. + private fun handleFutureMatches(matches: List): List { + val currentPos = currentPos() + val currentMatches = mutableListOf() + + for (m in matches) { + latestMatchedOccurrence(m)?.let { pos -> + // if it is a future match + if (journalIndex.compare(currentPos, pos) > 0) { + val idOcc = Id(pos.occ) + postponedMatches[idOcc] = (postponedMatches[idOcc] ?: emptyList()) + listOf(m) + execQueue.offer(ExecPos(pos, pos.occ)) + } else { + currentMatches.add(m) + } + } + } + return currentMatches + } + + // The latest matched occurrence from match's head is (by definition) the occurrence which activated this match. + private fun latestMatchedOccurrence(match: RuleMatchEx): MatchJournal.OccurrencePos? = + match.signature().mapNotNull { occSig -> + occSig?.wrapped?.let { occ -> + MatchJournal.OccurrencePos.get(journalIndex, occ) + } + }.maxWith(journalIndex) // compare positions: find latest + + private inline fun FeedbackStatus.then(action: (FeedbackStatus) -> FeedbackStatus) : FeedbackStatus = if (operational) action(this) else this