Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

When opening the project for a second time suggestion database is not sent #7699

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,7 @@ class JsonConnectionController(
sender() ! ResponseError(Some(id), SessionAlreadyInitialisedError)

case MessageHandler.Disconnected =>
logger.info("Json session terminated [{}].", rpcSession.clientId)
context.system.eventStream.publish(JsonSessionTerminated(rpcSession))
context.stop(self)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import com.typesafe.scalalogging.LazyLogging
import org.enso.languageserver.data.{ClientId, Config}
import org.enso.languageserver.event.{
ExecutionContextCreated,
ExecutionContextDestroyed
ExecutionContextDestroyed,
JsonSessionTerminated
}
import org.enso.languageserver.monitoring.MonitoringProtocol.{Ping, Pong}
import org.enso.languageserver.runtime.handler._
Expand Down Expand Up @@ -84,6 +85,8 @@ final class ContextRegistry(
.subscribe(self, classOf[Api.ExecutionUpdate])
context.system.eventStream
.subscribe(self, classOf[Api.VisualizationEvaluationFailed])

context.system.eventStream.subscribe(self, classOf[JsonSessionTerminated])
}

override def receive: Receive =
Expand Down Expand Up @@ -346,6 +349,24 @@ final class ContextRegistry(
} else {
sender() ! AccessDenied
}

case JsonSessionTerminated(client) =>
store.contexts.getOrElse(client.clientId, Set()).foreach { contextId =>
val handler = context.actorOf(
DestroyContextHandler.props(
runtimeFailureMapper,
timeout,
runtime
)
)
store.getListener(contextId).foreach(context.stop)
handler.forward(Api.DestroyContextRequest(contextId))
context.become(
withStore(store.removeContext(client.clientId, contextId))
)
context.system.eventStream
.publish(ExecutionContextDestroyed(contextId, client.clientId))
}
}

private def getRuntimeStackItem(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import org.enso.languageserver.data.{
Config,
ReceivesSuggestionsDatabaseUpdates
}
import org.enso.languageserver.event.InitializedEvent
import org.enso.languageserver.event.{InitializedEvent, JsonSessionTerminated}
import org.enso.languageserver.filemanager.{
ContentRootManager,
FileDeletedEvent,
Expand Down Expand Up @@ -115,6 +115,8 @@ final class SuggestionsHandler(
.subscribe(self, InitializedEvent.SuggestionsRepoInitialized.getClass)
context.system.eventStream
.subscribe(self, InitializedEvent.TruffleContextInitialized.getClass)

context.system.eventStream.subscribe(self, classOf[JsonSessionTerminated])
}

override def receive: Receive =
Expand Down Expand Up @@ -313,6 +315,17 @@ final class SuggestionsHandler(
suggestionsRepo.currentVersion
.map(GetSuggestionsDatabaseResult(_, Seq()))
.pipeTo(sender())
if (state.shouldStartBackgroundProcessing) {
runtimeConnector ! Api.Request(Api.StartBackgroundProcessing())
context.become(
initialized(
projectName,
graph,
clients,
state.backgroundProcessingStarted()
)
)
}

case Completion(path, pos, selfType, returnType, tags, isStatic) =>
val selfTypes = selfType.toList.flatMap(ty => ty :: graph.getParents(ty))
Expand Down Expand Up @@ -397,6 +410,14 @@ final class SuggestionsHandler(
)
)
action.pipeTo(handler)(sender())
context.become(
initialized(
projectName,
graph,
clients,
state.backgroundProcessingStopped()
)
)

case ProjectNameUpdated(name, updates) =>
updates.foreach(sessionRouter ! _)
Expand Down Expand Up @@ -434,6 +455,29 @@ final class SuggestionsHandler(
state.suggestionLoadingComplete()
)
)

case JsonSessionTerminated(_) =>
val action = for {
_ <- suggestionsRepo.clean
} yield SearchProtocol.InvalidateModulesIndex

val handler = context.system.actorOf(
InvalidateModulesIndexHandler.props(
RuntimeFailureMapper(contentRootManager),
timeout,
runtimeConnector
)
)

action.pipeTo(handler)
context.become(
initialized(
projectName,
graph,
clients,
state.backgroundProcessingStopped()
)
)
}

/** Transition the initialization process.
Expand Down Expand Up @@ -744,6 +788,12 @@ object SuggestionsHandler {
_shouldStartBackgroundProcessing = false
this
}

/** @return the new state with the background processing stopped. */
def backgroundProcessingStopped(): State = {
_shouldStartBackgroundProcessing = true
this
}
}

/** Creates a configuration object used to create a [[SuggestionsHandler]].
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package org.enso.interpreter.instrument.command;

import com.oracle.truffle.api.TruffleLogger;
import java.util.UUID;
import java.util.logging.Level;
import org.enso.interpreter.instrument.execution.RuntimeContext;
import org.enso.interpreter.instrument.job.DeserializeLibrarySuggestionsJob;
import org.enso.interpreter.runtime.EnsoContext;
import org.enso.polyglot.runtime.Runtime$Api$InvalidateModulesIndexResponse;
import scala.Option;
import scala.concurrent.ExecutionContext;
import scala.concurrent.Future;
import scala.runtime.BoxedUnit;

/** A command that invalidates the modules index. */
public final class InvalidateModulesIndexCommand extends AsynchronousCommand {

/**
* Create a command that invalidates the modules index.
*
* @param maybeRequestId an option with request id
*/
public InvalidateModulesIndexCommand(Option<UUID> maybeRequestId) {
super(maybeRequestId);
}

@Override
public Future<BoxedUnit> executeAsynchronously(RuntimeContext ctx, ExecutionContext ec) {
return Future.apply(
() -> {
TruffleLogger logger = ctx.executionService().getLogger();
long writeCompilationLockTimestamp = ctx.locking().acquireWriteCompilationLock();
try {
ctx.jobControlPlane().abortAllJobs();

EnsoContext context = ctx.executionService().getContext();
context.getTopScope().getModules().forEach(module -> module.setIndexed(false));
ctx.jobControlPlane().stopBackgroundJobs();

context
.getPackageRepository()
.getLoadedPackages()
.foreach(
pkg -> {
ctx.jobProcessor()
.runBackground(new DeserializeLibrarySuggestionsJob(pkg.libraryName()));
return BoxedUnit.UNIT;
});

reply(new Runtime$Api$InvalidateModulesIndexResponse(), ctx);
} finally {
ctx.locking().releaseWriteCompilationLock();
logger.log(
Level.FINEST,
"Kept write compilation lock [{0}] for {1} milliseconds.",
new Object[] {
this.getClass().getSimpleName(),
System.currentTimeMillis() - writeCompilationLockTimestamp
});
}

return BoxedUnit.UNIT;
},
ec);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ object CommandFactory {
case payload: Api.SetExpressionValueNotification =>
new SetExpressionValueCmd(payload)

case payload: Api.InvalidateModulesIndexRequest =>
new InvalidateModulesIndexCmd(request.requestId, payload)
case _: Api.InvalidateModulesIndexRequest =>
new InvalidateModulesIndexCommand(request.requestId)

case _: Api.GetTypeGraphRequest =>
new GetTypeGraphCommand(request.requestId)
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ trait JobControlPlane {
*/
def startBackgroundJobs(): Boolean

/** Stops background jobs processing.
*
* @return `true` if the call stopped background job, `false` if they are
* already stopped.
*/
def stopBackgroundJobs(): Boolean

/** Finds the first in-progress job satisfying the `filter` condition
*/
def jobInProgress[T](filter: PartialFunction[Job[_], Option[T]]): Option[T]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,14 @@ final class JobExecutionEngine(
result
}

/** @inheritdoc */
override def stopBackgroundJobs(): Boolean =
synchronized {
val result = isBackgroundJobsStarted
isBackgroundJobsStarted = false
result
}

/** @inheritdoc */
override def stop(): Unit = {
val allJobs = runningJobsRef.get()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1268,4 +1268,86 @@ class RuntimeSuggestionUpdatesTest
context.consumeOut shouldEqual List("Hello World!")
}

it should "invalidate modules index on command" in {
val contextId = UUID.randomUUID()
val requestId = UUID.randomUUID()
val moduleName = "Enso_Test.Test.Main"

val code =
"""from Standard.Base import all
|
|main = IO.println "Hello World!"
|""".stripMargin.linesIterator.mkString("\n")
val mainFile = context.writeMain(code)

// create context
context.send(Api.Request(requestId, Api.CreateContextRequest(contextId)))
context.receive shouldEqual Some(
Api.Response(requestId, Api.CreateContextResponse(contextId))
)

// open file
context.send(
Api.Request(Api.OpenFileNotification(mainFile, code))
)
context.receiveNone shouldEqual None

// push main
context.send(
Api.Request(
requestId,
Api.PushContextRequest(
contextId,
Api.StackItem.ExplicitCall(
Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "main"),
None,
Vector()
)
)
)
)
val updates1 = context.receiveNIgnoreExpressionUpdates(4)
updates1.length shouldEqual 4
updates1 should contain allOf (
Api.Response(requestId, Api.PushContextResponse(contextId)),
context.executionComplete(contextId),
Api.Response(Api.BackgroundJobsStartedNotification())
)
val indexedModules = updates1.collect {
case Api.Response(
None,
Api.SuggestionsDatabaseModuleUpdateNotification(moduleName, _, _, _)
) =>
moduleName
}
indexedModules should contain theSameElementsAs Seq(moduleName)
context.consumeOut shouldEqual List("Hello World!")

// clear indexes
context.send(Api.Request(requestId, Api.InvalidateModulesIndexRequest()))
context.receiveN(1) should contain theSameElementsAs Seq(
Api.Response(requestId, Api.InvalidateModulesIndexResponse())
)

// recompute
context.send(
Api.Request(requestId, Api.RecomputeContextRequest(contextId, None, None))
)
val updates2 = context.receiveNIgnoreExpressionUpdates(4)
updates2.length shouldEqual 4
updates2 should contain allOf (
Api.Response(requestId, Api.RecomputeContextResponse(contextId)),
context.executionComplete(contextId),
Api.Response(Api.BackgroundJobsStartedNotification())
)
val indexedModules2 = updates1.collect {
case Api.Response(
None,
Api.SuggestionsDatabaseModuleUpdateNotification(moduleName, _, _, _)
) =>
moduleName
}
indexedModules2 should contain theSameElementsAs Seq(moduleName)
context.consumeOut shouldEqual List("Hello World!")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,8 @@ final class SerializationManager(
compiler.context.logSerializationManager(
debugLogLevel,
"Restored IR from cache for module [{0}] at stage [{1}].",
Array[Object](module.getName, loadedCache.compilationStage())
module.getName,
loadedCache.compilationStage()
)

if (!relinkedIrChecks.contains(false)) {
Expand Down
Loading