diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Controller.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/Controller.kt index 4848b1bd..f7d2c338 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Controller.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/Controller.kt @@ -33,7 +33,9 @@ interface Controller { fun reactivate(occ: Occurrence): FeedbackStatus - fun state(): ProcessingState + fun logicalState(): LogicalStateObservable + + fun clearState() /** For tests only */ fun evaluate(occ: Occurrence): StoreView diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/LogicalObserver.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/LogicalObserver.kt index 2dda89e9..a767fc75 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/LogicalObserver.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/LogicalObserver.kt @@ -17,7 +17,7 @@ package jetbrains.mps.logic.reactor.core import jetbrains.mps.logic.reactor.core.internal.LogicalImpl -import jetbrains.mps.logic.reactor.core.internal.StateFrameStack +import jetbrains.mps.logic.reactor.core.internal.LogicalState import jetbrains.mps.logic.reactor.logical.Logical /** @@ -35,8 +35,8 @@ interface LogicalObserver { // Helps ensuring absence of memory leak of StateFrameStack through variable's internal arrays of observers internal fun checkForwardingObserverUniqueInstance(logical: Logical<*>): Boolean = with(logical as LogicalImpl<*>) { - valueObservers.map { it.second }.filterIsInstance().toHashSet().size <= 1 && - parentObservers.map { it.second }.filterIsInstance().toHashSet().size <= 1 + valueObservers.map { it.second }.filterIsInstance().toHashSet().size <= 1 && + parentObservers.map { it.second }.filterIsInstance().toHashSet().size <= 1 } diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/ProcessingState.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/LogicalStateObservable.kt similarity index 64% rename from reactor/Core/src/jetbrains/mps/logic/reactor/core/ProcessingState.kt rename to reactor/Core/src/jetbrains/mps/logic/reactor/core/LogicalStateObservable.kt index 62a13612..484bf09d 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/ProcessingState.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/LogicalStateObservable.kt @@ -22,10 +22,26 @@ import jetbrains.mps.logic.reactor.logical.Logical * @author Fedor Isakov */ -interface ProcessingState { +interface LogicalStateObservable { - fun addForwardingObserver(logical: Logical<*>, observer: LogicalObserver) + fun addForwardingObserver(logical: Logical<*>, observer: ForwardingLogicalObserver) - fun removeForwardingObserver(logical: Logical<*>, observer: LogicalObserver) - -} \ No newline at end of file + fun removeForwardingObserver(logical: Logical<*>, observer: ForwardingLogicalObserver) + + fun init(controller: Controller): InitToken + + interface InitToken { + + fun clear() + + } + +} + +interface ForwardingLogicalObserver { + + fun valueUpdated(logical: Logical<*>, controller: Controller) + + fun parentUpdated(logical: Logical<*>, controller: Controller) + +} diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Occurrence.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/Occurrence.kt index b4515fc4..1df666e1 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Occurrence.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/Occurrence.kt @@ -23,7 +23,6 @@ import jetbrains.mps.logic.reactor.evaluation.ConstraintOccurrence import jetbrains.mps.logic.reactor.logical.Logical import jetbrains.mps.logic.reactor.logical.LogicalContext -import jetbrains.mps.logic.reactor.logical.MetaLogical import jetbrains.mps.logic.reactor.program.Constraint @@ -34,40 +33,18 @@ fun justsOf(vararg elements: Int) = TIntHashSet(elements) fun justsFromCollection(collection: Collection) = TIntHashSet(collection) fun justsCopy(other: Justs) = TIntHashSet(other) - -data class OccurrenceObserver(val occurrence: Occurrence, val controller: Controller) : LogicalObserver { - - override fun valueUpdated(logical: Logical<*>) = doReactivate() - - override fun parentUpdated(logical: Logical<*>) = doReactivate() - - private fun doReactivate() { - if (occurrence.alive) { - val status = controller.reactivate(occurrence) - // FIXME propagate the status further up the call stack - handleFeedbackStatus(status) - } - } - - private fun handleFeedbackStatus(status: FeedbackStatus) { - if (status is FeedbackStatus.FAILED) { - throw status.failure.failureCause() - } - } -} - /** * Class representing a single constraint occurrence. * * @author Fedor Isakov */ -class Occurrence (controller: Controller, +class Occurrence (observable: LogicalStateObservable, val constraint: Constraint, val logicalContext: LogicalContext, val arguments: List<*>, val justifications: Justs, val ruleUniqueTag: Any? = null): - ConstraintOccurrence + ConstraintOccurrence, ForwardingLogicalObserver { var alive = true @@ -77,7 +54,7 @@ class Occurrence (controller: Controller, val identity = System.identityHashCode(this) init { - revive(controller) + revive(observable) } override fun constraint(): Constraint = constraint @@ -90,34 +67,49 @@ class Occurrence (controller: Controller, override fun justifications(): Justs = justifications - fun terminate(controller: Controller) { - val obs = OccurrenceObserver(this, controller) + override fun valueUpdated(logical: Logical<*>, controller: Controller) = doReactivate(controller) + + override fun parentUpdated(logical: Logical<*>, controller: Controller) = doReactivate(controller) + + fun terminate(observable: LogicalStateObservable) { for (a in arguments) { if (a is Logical<*>) { - controller.state().removeForwardingObserver(a, obs) + observable.removeForwardingObserver(a, this) } } alive = false } - fun revive(controller: Controller) { - val obs = OccurrenceObserver(this, controller) + fun revive(observable: LogicalStateObservable) { for (a in arguments) { if (a is Logical<*>) { - controller.state().addForwardingObserver(a, obs) + observable.addForwardingObserver(a, this) } } alive = true } - override fun toString(): String = "${constraint().symbol()}(${arguments().joinToString()})" + private fun doReactivate(controller: Controller) { + if (alive) { + val status = controller.reactivate(this) + // FIXME propagate the status further up the call stack + handleFeedbackStatus(status) + } + } + private fun handleFeedbackStatus(status: FeedbackStatus) { + if (status is FeedbackStatus.FAILED) { + throw status.failure.failureCause() + } + } + override fun toString(): String = "${constraint().symbol()}(${arguments().joinToString()})" + } -fun Constraint.occurrence(controller: Controller, +fun Constraint.occurrence(observable: LogicalStateObservable, arguments: List<*>, justifications: Justs, logicalContext: LogicalContext, ruleUniqueTag: Any? = null): Occurrence = - Occurrence(controller, this, logicalContext, arguments, justifications, ruleUniqueTag) + Occurrence(observable, this, logicalContext, arguments, justifications, ruleUniqueTag) diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ProcessingStateImpl.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ConstraintsProcessing.kt similarity index 93% rename from reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ProcessingStateImpl.kt rename to reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ConstraintsProcessing.kt index 635b58cf..5e122342 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ProcessingStateImpl.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ConstraintsProcessing.kt @@ -27,26 +27,24 @@ import jetbrains.mps.logic.reactor.util.profile /** - * Processing of constraints comprises several major activities: + * Represents the processing of constraints, which comprises several major activities: * * - finding match(-es) corresponding to an active occurrence * - checking the guard condition * - deactivating discarded occurrences from the match * - processing the body of a constraints rule. * - * A ProcessingState maintains a stack of [StateFrame] instances through [StateFrameStack]. - * It also serves as a proxy for observers of [Logical] that are added during program evaluation. - * * @author Fedor Isakov */ -internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.DispatchingFront, - journal: MatchJournalImpl, - private val ruleIndex: RuleIndex, - private val ispec: IncrementalProgramSpec = IncrementalProgramSpec.DefaultSpec, - val trace: EvaluationTrace = EvaluationTrace.NULL, - val profiler: Profiler? = null) - : StoreAwareJournalImpl(journal) +internal class ConstraintsProcessing(private var dispatchingFront: Dispatcher.DispatchingFront, + journal: MatchJournalImpl, + private val ruleIndex: RuleIndex, + val logicalState: LogicalState = LogicalState(), + private val ispec: IncrementalProgramSpec = IncrementalProgramSpec.DefaultSpec, + val trace: EvaluationTrace = EvaluationTrace.NULL, + val profiler: Profiler? = null) + : StoreAwareJournalImpl(journal, logicalState) { private val ruleOrdering: RuleOrdering = RuleOrdering(ruleIndex) @@ -202,7 +200,7 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp val histView = view() resetStore() // clear observers val rules = ArrayList().apply { ruleIndex.forEach { add(it) } } - return SessionTokenImpl(histView, rules, dispatchingFront.state()) + return SessionTokenImpl(histView, rules, dispatchingFront.state(), logicalState) } /** @@ -217,7 +215,7 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp if (!active.stored) { active.stored = true logActivation(active) - active.revive(controller) + active.revive(logicalState) } profiler.profile("dispatch_${active.constraint().symbol()}") { @@ -308,7 +306,7 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp profiler.profile("terminateOccurrence") { - occ.terminate(controller) + occ.terminate(logicalState) } diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ControllerImpl.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ControllerImpl.kt index 209069d1..b8979f4d 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ControllerImpl.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ControllerImpl.kt @@ -30,23 +30,26 @@ import jetbrains.mps.logic.reactor.util.profile internal class ControllerImpl ( val supervisor: Supervisor, - val state: ProcessingStateImpl, + val processing: ConstraintsProcessing, val ispec: IncrementalProgramSpec = IncrementalProgramSpec.DefaultSpec, val trace: EvaluationTrace = EvaluationTrace.NULL, val profiler: Profiler? = null) : Controller { + + private val initToken: LogicalStateObservable.InitToken = processing.init(this) + /** For tests only */ - override fun storeView(): StoreView = state.storeView() + override fun storeView(): StoreView = processing.storeView() /** For tests only */ override fun evaluate(occ: Occurrence): StoreView { // create the internal occurrence - val active = occ.constraint().occurrence(this, occ.arguments(), occ.justifications(), object: LogicalContext { + val active = occ.constraint().occurrence(logicalState(), occ.arguments(), occ.justifications(), object: LogicalContext { override fun variable(metaLogical: MetaLogical): Logical? = null }) trace.activate(occ) - val status = state.processActivated(this, active, NORMAL()) + val status = processing.processActivated(this, active, NORMAL()) if (status is FAILED) { throw status.failure.failureCause() } @@ -55,13 +58,13 @@ internal class ControllerImpl ( fun incrLaunch(constraint: Constraint, rulesDiff: RulesDiff): FeedbackStatus { profiler.profile("invalidation") { - state.invalidateRuleMatches(rulesDiff.removed) + processing.invalidateRuleMatches(rulesDiff.removed) } profiler.profile("adding_matches") { - state.addRuleMatches(rulesDiff.added) + processing.addRuleMatches(rulesDiff.added) } return profiler.profile("reexecution") { - state.launchQueue(this) + processing.launchQueue(this) } } @@ -71,13 +74,17 @@ internal class ControllerImpl ( // fixme: is it valid to always provide current justifications? // while this method is used only in one place at program kick-off, yes, it's initial justs provided. - activateConstraint(constraint, state.justs(), context) + activateConstraint(constraint, processing.justs(), context) return context.currentStatus() } - override fun state(): ProcessingStateImpl = - state + override fun logicalState(): LogicalStateObservable = + processing + + override fun clearState() { + initToken.clear() + } override fun ask(invocation: PredicateInvocation): Boolean { val solver = invocation.predicate().symbol().solver() @@ -96,7 +103,7 @@ internal class ControllerImpl ( profiler.profile("reactivate_${occ.constraint.symbol()}") { trace.reactivate(occ) - state.processActivated(this, occ, NORMAL()) + processing.processActivated(this, occ, NORMAL()) } @@ -146,9 +153,9 @@ internal class ControllerImpl ( } } - val savedPos = state.currentPos() + val savedPos = processing.currentPos() - val currentJusts = state.justs() + val currentJusts = processing.justs() for (item in body) { val itemOk = when (item) { @@ -199,7 +206,7 @@ internal class ControllerImpl ( if (!altOk) { // all constraints activated up to a failure are lost - state.reset(savedPos) + processing.reset(savedPos) } else { // body finished normally @@ -216,9 +223,9 @@ internal class ControllerImpl ( profiler.profile("activate_${constraint.symbol()}") { - constraint.occurrence(this, args, justsCopy(justs), context.logicalContext, context.ruleUniqueTag).let { occ -> + constraint.occurrence(logicalState(), args, justsCopy(justs), context.logicalContext, context.ruleUniqueTag).let { occ -> trace.activate(occ) - state.processActivated(this, occ, status) + processing.processActivated(this, occ, status) } } @@ -333,10 +340,11 @@ fun createController( ControllerImpl( supervisor, - ProcessingStateImpl( + ConstraintsProcessing( Dispatcher(ruleIndex).front(), MatchJournalImpl(), ruleIndex, + LogicalState(), IncrementalProgramSpec.DefaultSpec, trace, profiler diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/EvaluationSessionImpl.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/EvaluationSessionImpl.kt index 8ffde2af..44538d27 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/EvaluationSessionImpl.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/EvaluationSessionImpl.kt @@ -49,24 +49,25 @@ internal class EvaluationSessionImpl private constructor ( val ruleIndex = RuleIndex(program.rulesLists()) if (ispec is IncrementalProgramSpec.NonIncrSpec || token == null) { - val state = ProcessingStateImpl( + val processing + = ConstraintsProcessing( Dispatcher(ruleIndex).front(), MatchJournalImpl(ispec), - ruleIndex, ispec, trace, profiler + ruleIndex, LogicalState(), ispec, trace, profiler ) - this.controller = ControllerImpl(supervisor, state, ispec, trace, profiler) + this.controller = ControllerImpl(supervisor, processing, ispec, trace, profiler) return controller.activate(main) } else { val tkn = token as SessionTokenImpl - val state = ProcessingStateImpl( + val processing = ConstraintsProcessing( Dispatcher(ruleIndex, tkn.getFrontState()).front(), MatchJournalImpl(ispec, tkn.journalView), - ruleIndex, ispec, trace, profiler + ruleIndex, tkn.logicalState, ispec, trace, profiler ) - this.controller = ControllerImpl(supervisor, state, ispec, trace, profiler) + this.controller = ControllerImpl(supervisor, processing, ispec, trace, profiler) return controller.incrLaunch(main, rulesDiff) } } @@ -108,7 +109,7 @@ internal class EvaluationSessionImpl private constructor ( override fun start(supervisor: Supervisor): EvaluationResult { var session = Backend.ourBackend.ourSession.get() if (session != null) throw IllegalStateException("session already active") - + @Suppress("UNCHECKED_CAST") val durations = parameters[ParameterKey.of("profiling.data", MutableMap::class.java)] as MutableMap? @@ -125,6 +126,7 @@ internal class EvaluationSessionImpl private constructor ( } } finally { + session.controller.clearState() try { profiler?.run { formattedData().entries.forEach { e -> durations.put(e.key, e.value) } @@ -138,7 +140,7 @@ internal class EvaluationSessionImpl private constructor ( } return object : EvaluationResult { - private val token = session.controller.state.endSession() + private val token = session.controller.processing.endSession() override fun token(): SessionToken = token diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ExecutionQueue.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ExecutionQueue.kt index 34aac132..26150678 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ExecutionQueue.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ExecutionQueue.kt @@ -43,10 +43,10 @@ internal class ExecutionQueue( private val seen: MutableSet = HashSet() - fun run(controller: Controller, state: ProcessingStateImpl): FeedbackStatus { + fun run(controller: Controller, processing: ConstraintsProcessing): FeedbackStatus { var status: FeedbackStatus = FeedbackStatus.NORMAL() if (execQueue.isNotEmpty()) { - state.resetStore() + processing.resetStore() var prevPos: MatchJournal.Pos? = null do { @@ -55,21 +55,21 @@ internal class ExecutionQueue( // Handles the case when several matches are added to the same position. // Then shouldn't replay, because currentPos is valid and more recent (!) than execPos. if (execPos.pos != prevPos) { - state.replay(controller, execPos.pos) + processing.replay(processing.logicalState, execPos.pos) lastIncrementalRootPos = execPos.pos } prevPos = execPos.pos // If the occurrence is still in the store after replay (i.e. if it's valid to activate it) if (execPos.activeOcc.stored) { - status = state.reactivate(controller, execPos.activeOcc) - // Leave journal state as it was at the point of failure + status = processing.reactivate(controller, execPos.activeOcc) + // Leave journal processing as it was at the point of failure if (!status.operational) return status } } while (execQueue.isNotEmpty()) } // Also replay to the end after queue is fully executed - state.replay(controller, state.last().toPos()) + processing.replay(processing.logicalState, processing.last().toPos()) return status } diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/LogicalState.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/LogicalState.kt new file mode 100644 index 00000000..b9bc3f23 --- /dev/null +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/LogicalState.kt @@ -0,0 +1,186 @@ +/* + * Copyright 2014-2019 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package jetbrains.mps.logic.reactor.core.internal + +import jetbrains.mps.logic.reactor.core.* +import jetbrains.mps.logic.reactor.logical.Logical +import java.util.* + +/** + * Captures the state of logical variables in respect to observers. + * Serves as a middle layer between logical variables and observers, the latter being notified of changes in the + * state in context of the currenly active controller. + * Keeps a reference to [Controller] instance, which is used to notify the observers. + * + * NOTE: the following is no longer relevant, as the "stack frame" functionality has been removed. + * + * Internally [LogicalState] maintains a stack of [LogicalStateFrame]s capturing processing + * state related to logical variable observers. The top [LogicalStateFrame] contains + * the current relevant set of [LogicalObserver]s. Events corresponding to + * adding and removing observers are forwarded to the top frame. + * + * Frames are added on events which could require reverting processing state to + * the point before them, which can be achieved by simply dropping a frame and + * thus dropping all added observers. + */ + +class LogicalState : LogicalStateObservable, LogicalObserver +{ + // invariant: never empty + private val stateFrames = LinkedList() + + private var controller: Controller? = null + + init { + stateFrames.push(LogicalStateFrame()) + } + + override fun init(controller: Controller): LogicalStateObservable.InitToken { + assert(this@LogicalState.controller === null) + this.controller = controller + + return object : LogicalStateObservable.InitToken { + override fun clear() { + assert(this@LogicalState.controller !== null) + this@LogicalState.controller = null + } + } + } + + fun currentFrame() : LogicalStateFrame = + stateFrames.first + + fun push() : LogicalStateFrame { + return currentFrame() +// val newFrame = StateFrame(stateFrames.first) +// stateFrames.push(newFrame) +// return newFrame + } + + fun reset() { + // Clear logicals from this forwarding observer on full reset + currentFrame().observed().forEach { + it.removeObserver(this) + } + reset(stateFrames.last) + } + + fun reset(frame: LogicalStateFrame) { + val it = stateFrames.iterator() + while (it.hasNext()) { + val next = it.next() + if (frame === next) { // referential equality + return + } + it.remove() + } + throw IllegalStateException("invalid frame") + } + + override fun addForwardingObserver(logical: Logical<*>, observer: ForwardingLogicalObserver) { + if (!currentFrame().isObserving(logical)) { + logical.addObserver(this) + } + currentFrame().addForwardingObserver(logical, observer) +// assert(checkForwardingObserverUniqueInstance(logical)) + } + + override fun removeForwardingObserver(logical: Logical<*>, observer: ForwardingLogicalObserver) { + currentFrame().removeForwardingObserver(logical, observer) + if (!currentFrame().isObserving(logical)) { + logical.removeObserver(this) + } + } + + override fun valueUpdated(logical: Logical<*>) { + // forward to the top frame + currentFrame().valueUpdated(logical, controller!!) + } + + override fun parentUpdated(logical: Logical<*>) { + // forward to the top frame + currentFrame().parentUpdated(logical, controller!!) + } + +} + +/** + * NOTE: this class is deprecated and the "stack of frames" functionality is no longer functioning. + * To be removed soon. + * + * A [LogicalStateFrame] captures a logical state corresponding to currently relevant + * observers of changes of logical variables. It roughly corresponds to a bunch of event + * that update a set of such observers. By dropping a frame the processing state + * can be reverted. + * + * The main (and only) example of such event is an occurrence activation and the + * corresponding observer reactivates occurrence on changes of logical variables + * used as its arguments. + * + * @author Fedor Isakov + */ + +@Deprecated("to be removed") +class LogicalStateFrame : ForwardingLogicalObserver +{ + // FIXME use Vector instead of ConsList +// private var observers: PersMap>, PersList> = Maps.of() + + private val observers = IdentityHashMap, ArrayList>() + +// constructor(prototype: StateFrame) : this() { +// this.observers = prototype.observers +// } + + fun addForwardingObserver(logical: Logical<*>, observer: ForwardingLogicalObserver) { +// val logicalId = Id(logical) +// this.observers = observers.assoc(logicalId, +// observers[logicalId]?.prepend(observer) ?: Lists.of(observer)) + observers.getOrPut(logical) { arrayListOf() }.add(observer) + } + + fun removeForwardingObserver(logical: Logical<*>, observer: ForwardingLogicalObserver) { +// val logicalId = Id(logical) +// observers[logicalId]?.let { +// this.observers = observers.assoc(logicalId, it.remove(observer)!!) +// } + observers[logical]?.remove(observer) + } + + fun isObserving(logical: Logical<*>) = observers.containsKey(logical) +// observers[Id(logical)] != null + + fun observed(): Sequence> = observers.keys.asSequence() +// observers.keys().asSequence().map { it.wrapped } + + override fun valueUpdated(logical: Logical<*>, controller: Controller) { + observers[logical]?.let { list -> + for (observer in ArrayList(list)) { + observer.valueUpdated(logical, controller) + } + } + } + + override fun parentUpdated(logical: Logical<*>, controller: Controller) { + observers[logical]?.let { list -> + for (observer in ArrayList(list)) { + observer.parentUpdated(logical, controller) + } + } + } + +} \ No newline at end of file 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 315dcdd0..e636d8f0 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 @@ -67,7 +67,7 @@ interface MatchJournal : MutableIterable { * Advances journal position to specified position. * @throws IllegalStateException when position is not from the future (relative to current pos). */ - fun replay(controller: Controller, futurePos: Pos) + fun replay(observable: LogicalStateObservable, futurePos: Pos) /** diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchJournalImpl.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchJournalImpl.kt index 2cd58d9d..28575954 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchJournalImpl.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchJournalImpl.kt @@ -16,10 +16,7 @@ package jetbrains.mps.logic.reactor.core.internal -import jetbrains.mps.logic.reactor.core.Controller -import jetbrains.mps.logic.reactor.core.Justs -import jetbrains.mps.logic.reactor.core.Occurrence -import jetbrains.mps.logic.reactor.core.justsOf +import jetbrains.mps.logic.reactor.core.* import jetbrains.mps.logic.reactor.core.internal.MatchJournal.* import jetbrains.mps.logic.reactor.evaluation.ConstraintOccurrence import jetbrains.mps.logic.reactor.evaluation.MatchJournalView @@ -110,25 +107,27 @@ internal open class MatchJournalImpl( if (currentPos() != pastPos) throw IllegalStateException() } - override fun replay(controller: Controller, futurePos: MatchJournal.Pos) { + override fun replay(observable: LogicalStateObservable, futurePos: MatchJournal.Pos) { while (posPtr.hasNext()) { current = posPtr.next() if (futurePos.chunk === current) { - replayOccurrences(controller, current.entries.take(futurePos.entriesCount)) + replayOccurrences(observable, current.entries.take(futurePos.entriesCount)) return } - replayOccurrences(controller, current.entries) + replayOccurrences(observable, current.entries) } if (currentPos() != futurePos) throw IllegalStateException() } - private fun replayOccurrences(controller: Controller, occSpecs: Iterable) = + private fun replayOccurrences(observable: LogicalStateObservable, occSpecs: Iterable) = occSpecs.forEach { if (it.discarded) { - it.occ.terminate(controller) +// it.occ.terminate(observable) + it.occ.alive = false it.occ.stored = false } else { - it.occ.revive(controller) +// it.occ.revive(observable) + it.occ.alive = true it.occ.stored = true } } diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/SessionTokenImpl.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/SessionTokenImpl.kt index af6bdc9a..f46d5dcd 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/SessionTokenImpl.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/SessionTokenImpl.kt @@ -24,8 +24,10 @@ import jetbrains.mps.logic.reactor.program.Rule data class SessionTokenImpl( private val journalView: MatchJournal.View, private val rules: Iterable, - private val frontState: DispatchingFrontState -) : SessionToken { + private val frontState: DispatchingFrontState, + val logicalState: LogicalState +) : SessionToken +{ override fun getJournalView(): MatchJournalView = journalView override fun getRules(): Iterable = rules fun getFrontState(): DispatchingFrontState = frontState diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/StateFrame.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/StateFrame.kt deleted file mode 100644 index b049c84a..00000000 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/StateFrame.kt +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2014-2019 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package jetbrains.mps.logic.reactor.core.internal - -import jetbrains.mps.logic.reactor.core.LogicalObserver -import jetbrains.mps.logic.reactor.logical.Logical -import jetbrains.mps.logic.reactor.util.* - -/** - * A [StateFrame] captures a processing state corresponding to current relevant - * observers of changes of logical variables. It roughly corresponds to an event - * that updates a set of such observers. By dropping a frame the processing state - * can be reverted. - * - * The main (and only) example of such event is an occurrence activation and the - * corresponding observer reactivates occurrence on changes of logical variables - * used as its arguments. - * - * @author Fedor Isakov - */ - -internal class StateFrame constructor() : LogicalObserver -{ - // FIXME use Vector instead of ConsList - private var observers: PersMap>, PersList> = Maps.of() - - constructor(prototype: StateFrame) : this() { - this.observers = prototype.observers - } - - fun addForwardingObserver(logical: Logical<*>, observer: LogicalObserver) { - val logicalId = Id(logical) - this.observers = observers.assoc(logicalId, - observers[logicalId]?.prepend(observer) ?: Lists.of(observer)) - } - - fun removeForwardingObserver(logical: Logical<*>, observer: LogicalObserver) { - val logicalId = Id(logical) - observers[logicalId]?.let { - this.observers = observers.assoc(logicalId, it.remove(observer)!!) - } - } - - fun isObserving(logical: Logical<*>) = - observers[Id(logical)] != null - - fun observed(): Sequence> = - observers.keys().asSequence().map { it.wrapped } - - override fun valueUpdated(logical: Logical<*>) { - observers[Id(logical)]?.let { list -> - for (observer in list) { - observer.valueUpdated(logical) - } - } - } - - override fun parentUpdated(logical: Logical<*>) { - observers[Id(logical)]?.let { list -> - for (observer in list) { - observer.parentUpdated(logical) - } - } - } - -} \ No newline at end of file diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/StateFrameStack.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/StateFrameStack.kt deleted file mode 100644 index eb1b2531..00000000 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/StateFrameStack.kt +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2014-2019 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package jetbrains.mps.logic.reactor.core.internal - -import jetbrains.mps.logic.reactor.core.* -import jetbrains.mps.logic.reactor.logical.Logical -import java.util.* - - -/** - * A [StateFrameStack] maintains a stack of [StateFrame]s capturing processing - * state related to logical variable observers. The top [StateFrame] contains - * the current relevant set of [LogicalObserver]s. Events corresponding to - * adding and removing observers are forwarded to the top frame. - * - * Frames are added on events which could require reverting processing state to - * the point before them, which can be achieved by simply dropping a frame and - * thus dropping all added observers. - */ - -internal open class StateFrameStack() : ProcessingState, LogicalObserver -{ - // invariant: never empty - private val stateFrames = LinkedList() - - init { - stateFrames.push(StateFrame()) - } - - fun currentFrame() : StateFrame = - stateFrames.first - - fun push() : StateFrame { - val newFrame = StateFrame(stateFrames.first) - stateFrames.push(newFrame) - return newFrame - } - - fun reset() { - // Clear logicals from this forwarding observer on full reset - currentFrame().observed().forEach { - it.removeObserver(this) - } - reset(stateFrames.last) - } - - fun reset(frame: StateFrame) { - val it = stateFrames.iterator() - while (it.hasNext()) { - val next = it.next() - if (frame === next) { // referential equality - return - } - it.remove() - } - throw IllegalStateException("invalid frame") - } - - override fun addForwardingObserver(logical: Logical<*>, observer: LogicalObserver) { - if (!currentFrame().isObserving(logical)) { - logical.addObserver(this) - } - currentFrame().addForwardingObserver(logical, observer) -// assert(checkForwardingObserverUniqueInstance(logical)) - } - - override fun removeForwardingObserver(logical: Logical<*>, observer: LogicalObserver) { - currentFrame().removeForwardingObserver(logical, observer) - if (!currentFrame().isObserving(logical)) { - logical.removeObserver(this) - } - } - - override fun valueUpdated(logical: Logical<*>) { - // forward to the top frame - currentFrame().valueUpdated(logical) - } - - override fun parentUpdated(logical: Logical<*>) { - // forward to the top frame - currentFrame().parentUpdated(logical) - } - -} \ No newline at end of file diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/StoreAwareJournal.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/StoreAwareJournal.kt index f3fafdda..efe5b4b9 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/StoreAwareJournal.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/StoreAwareJournal.kt @@ -16,15 +16,15 @@ package jetbrains.mps.logic.reactor.core.internal -import jetbrains.mps.logic.reactor.core.ProcessingState +import jetbrains.mps.logic.reactor.core.LogicalStateObservable import jetbrains.mps.logic.reactor.program.IncrementalProgramSpec import java.lang.IllegalArgumentException /** - * [MatchJournal] which also maintains observers in [ProcessingState] in sync with its current position. + * [MatchJournal] which also maintains observers in [LogicalStateObservable] in sync with its current position. */ -interface StoreAwareJournal : MatchJournal, ProcessingState { +interface StoreAwareJournal : MatchJournal, LogicalStateObservable { // Only for testing push() in Impl fun testPush() @@ -41,12 +41,13 @@ interface StoreAwareJournal : MatchJournal, ProcessingState { } -internal open class StoreAwareJournalImpl(private val journal: MatchJournal, private val state: StateFrameStack = StateFrameStack()) - : MatchJournal by journal, StoreAwareJournal, ProcessingState by state +internal open class StoreAwareJournalImpl(private val journal: MatchJournal, + private val state: LogicalState = LogicalState()) + : MatchJournal by journal, StoreAwareJournal, LogicalStateObservable by state { private class FramePos( - val frame: StateFrame, + val frame: LogicalStateFrame, chunk: MatchJournal.Chunk, entriesCount: Int = 0 ) : MatchJournal.Pos(chunk, entriesCount) diff --git a/reactor/Test/test/RulesHelper.kt b/reactor/Test/test/RulesHelper.kt index 73feb077..a9ac09c4 100644 --- a/reactor/Test/test/RulesHelper.kt +++ b/reactor/Test/test/RulesHelper.kt @@ -174,15 +174,15 @@ fun equals(left: Any, right: Any): ConjBuilder.() -> Unit = { fun occurrence(id: String, vararg args: Any): Occurrence = MockConstraint(ConstraintSymbol.symbol(id, args.size)) - .occurrence(MockController(), listOf(* args), justsOf(), noLogicalContext) + .occurrence(MockController().logicalState(), listOf(* args), justsOf(), noLogicalContext) fun taggedOccurrence(ruleUniqueTag: Any, id: String, vararg args: Any): Occurrence = MockConstraint(ConstraintSymbol.symbol(id, args.size)) - .occurrence(MockController(), listOf(* args), justsOf(), noLogicalContext, ruleUniqueTag) + .occurrence(MockController().logicalState(), listOf(* args), justsOf(), noLogicalContext, ruleUniqueTag) fun justifiedOccurrence(id: String, justs: Justs, vararg args: Any): Occurrence = MockConstraint(ConstraintSymbol.symbol(id, args.size), true) - .occurrence(MockController(), listOf(* args), justs, noLogicalContext) + .occurrence(MockController().logicalState(), listOf(* args), justs, noLogicalContext) fun justifiedOccurrence(id: String, justs: Collection, vararg args: Any): Occurrence = justifiedOccurrence(id, justsFromCollection(justs), * args) @@ -201,7 +201,11 @@ private val noLogicalContext = object : LogicalContext { } class MockController : Controller { - + + override fun clearState() { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + override fun ask(invocation: PredicateInvocation): Boolean { TODO("not implemented") //To change body of created functions use File | Settings | File Templates. } @@ -218,11 +222,15 @@ class MockController : Controller { TODO("not implemented") //To change body of created functions use File | Settings | File Templates. } - override fun state(): ProcessingState = object : ProcessingState { - override fun addForwardingObserver(logical: Logical<*>, observer: LogicalObserver) { + override fun logicalState(): LogicalStateObservable = object : LogicalStateObservable { + override fun addForwardingObserver(logical: Logical<*>, observer: ForwardingLogicalObserver) { } - override fun removeForwardingObserver(logical: Logical<*>, observer: LogicalObserver) { + override fun removeForwardingObserver(logical: Logical<*>, observer: ForwardingLogicalObserver) { + } + + override fun init(controller: Controller): LogicalStateObservable.InitToken { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. } } diff --git a/reactor/Test/test/TestStoreAwareJournal.kt b/reactor/Test/test/TestStoreAwareJournal.kt index 8eef2264..4a62c561 100644 --- a/reactor/Test/test/TestStoreAwareJournal.kt +++ b/reactor/Test/test/TestStoreAwareJournal.kt @@ -162,7 +162,7 @@ class TestStoreAwareJournal { nchunks shouldBe 4 * 2 // 4 rules, each activates 1 principal occurrence // try replay the last chunk - replay(mockController, lastPos) + replay(mockController.logicalState(), lastPos) // should change nothing storeView().constraintSymbols() shouldBe setOf(sym0("qux"), sym0("lax")) @@ -175,11 +175,11 @@ class TestStoreAwareJournal { storeView().allOccurrences().count() shouldBe 0 view().chunks.size shouldBe nchunks - replay(mockController, initPos) + replay(mockController.logicalState(), initPos) storeView().constraintSymbols() shouldBe setOf() - replay(mockController, fooPos) + replay(mockController.logicalState(), fooPos) storeView().constraintSymbols() shouldBe setOf(sym0("foo")) // although storeView() results change, journal remains the same @@ -231,7 +231,7 @@ class TestStoreAwareJournal { storeView().allOccurrences().count() shouldBe 0 - replay(mockController, savedPos) + replay(mockController.logicalState(), savedPos) storeView().allOccurrences() shouldBe oldStore currentPos() shouldBe savedPos @@ -473,7 +473,7 @@ class TestStoreAwareJournal { // store is not longer valid after removing chunks from history, so reset it resetStore() // move to the point where we want to insert new rule - replay(mockController, continueFrom) + replay(mockController.logicalState(), continueFrom) // according to the history 'qux' wasn't activated at this point & 'bar1' wasn't discarded storeView().constraintSymbols() shouldBe setOf() @@ -498,7 +498,7 @@ class TestStoreAwareJournal { assertNotEquals(lastPos, currentPos()) // finally, purely go the the end, applying the rest of the history to the store - replay(mockController, lastPos) + replay(mockController.logicalState(), lastPos) currentPos().chunk shouldBeSame lastPos.chunk // we inserted in the middle -- the last chunk should remain the same storeView().constraintSymbols() shouldBe setOf(sym0("bar1"), sym0("qux"), sym0("marker"))