From c40a238570353a23c6e81f07fe93120365ff0911 Mon Sep 17 00:00:00 2001 From: Dmitry Bushev Date: Wed, 10 Feb 2021 17:43:24 +0300 Subject: [PATCH 1/4] feat: abandon terminating instance of LS --- .../LanguageServerController.scala | 23 ++++++++++++------- .../LanguageServerGatewayImpl.scala | 1 + .../LanguageServerRegistry.scala | 5 +++- .../DefaultDistributionConfiguration.scala | 9 ++++++-- 4 files changed, 27 insertions(+), 11 deletions(-) diff --git a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerController.scala b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerController.scala index d004f41c2156..4d18dbbf02ba 100644 --- a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerController.scala +++ b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerController.scala @@ -31,6 +31,7 @@ import org.enso.projectmanager.infrastructure.languageserver.LanguageServerBootL import org.enso.projectmanager.infrastructure.languageserver.LanguageServerController._ import org.enso.projectmanager.infrastructure.languageserver.LanguageServerProtocol._ import org.enso.projectmanager.infrastructure.languageserver.LanguageServerRegistry.ServerShutDown +import org.enso.projectmanager.infrastructure.languageserver.ShutdownHookActivator.RegisterShutdownHook import org.enso.projectmanager.model.Project import org.enso.projectmanager.service.LoggingServiceDescriptor import org.enso.projectmanager.util.UnhandledLogging @@ -70,6 +71,8 @@ class LanguageServerController( import context.{dispatcher, system} + private var isShutdownHookRegistered: Boolean = false + private val descriptor = LanguageServerDescriptor( name = s"language-server-${project.id}", @@ -221,6 +224,9 @@ class LanguageServerController( case ServerDied => log.error(s"Language server died [$connectionInfo]") context.stop(self) + + case RegisterShutdownHook(_, _) => + isShutdownHookRegistered = true } private def removeClient( @@ -244,6 +250,9 @@ class LanguageServerController( private def shutDownServer(maybeRequester: Option[ActorRef]): Unit = { log.debug(s"Shutting down a language server for project ${project.id}") context.children.foreach(_ ! GracefulStop) + if (!isShutdownHookRegistered) { + context.parent ! ServerShutDown(project.id) + } val cancellable = context.system.scheduler .scheduleOnce(timeoutConfig.shutdownTimeout, self, ShutdownTimeout) @@ -278,8 +287,9 @@ class LanguageServerController( maybeRequester.foreach(_ ! ServerShutdownTimedOut) stop() - case StartServer(_, _, _, _) => - sender() ! PreviousInstanceNotShutDown + case m: StartServer => + // This instance has not yet been shut down. Retry + context.parent.forward(m) } private def waitingForChildren(): Receive = { case Terminated(_) => @@ -349,16 +359,13 @@ object LanguageServerController { case object ShutDownServer - /** Signals boot timeout. - */ + /** Signals boot timeout. */ case object BootTimeout - /** Boot command. - */ + /** Boot command. */ case object Boot - /** Signals shutdown timeout. - */ + /** Signals shutdown timeout. */ case object ShutdownTimeout case object ServerDied diff --git a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerGatewayImpl.scala b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerGatewayImpl.scala index 456684ada6df..8f885f7e2ad1 100644 --- a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerGatewayImpl.scala +++ b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerGatewayImpl.scala @@ -131,6 +131,7 @@ class LanguageServerGatewayImpl[ Sync[F] .effect { shutdownHookActivator ! RegisterShutdownHook(projectId, hook) + registry ! RegisterShutdownHook(projectId, hook) } } diff --git a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerRegistry.scala b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerRegistry.scala index a5215ef0b165..7b030deea93e 100644 --- a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerRegistry.scala +++ b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerRegistry.scala @@ -19,6 +19,7 @@ import org.enso.projectmanager.infrastructure.languageserver.LanguageServerProto StopServer } import org.enso.projectmanager.infrastructure.languageserver.LanguageServerRegistry.ServerShutDown +import org.enso.projectmanager.infrastructure.languageserver.ShutdownHookActivator.RegisterShutdownHook import org.enso.projectmanager.service.LoggingServiceDescriptor import org.enso.projectmanager.util.UnhandledLogging import org.enso.projectmanager.versionmanagement.DistributionConfiguration @@ -71,7 +72,7 @@ class LanguageServerRegistry( loggingServiceDescriptor, executor ), - s"language-server-controller-${project.id}" + s"language-server-controller-${project.id}-${UUID.randomUUID()}" ) context.watch(controller) controller.forward(msg) @@ -112,6 +113,8 @@ class LanguageServerRegistry( case CheckIfServerIsRunning(projectId) => sender() ! serverControllers.contains(projectId) + case m @ RegisterShutdownHook(projectId, _) => + serverControllers.get(projectId).foreach(_ ! m) } } diff --git a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/versionmanagement/DefaultDistributionConfiguration.scala b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/versionmanagement/DefaultDistributionConfiguration.scala index 0a3083144df9..22dad296113c 100644 --- a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/versionmanagement/DefaultDistributionConfiguration.scala +++ b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/versionmanagement/DefaultDistributionConfiguration.scala @@ -11,7 +11,10 @@ import org.enso.runtimeversionmanager.distribution.{ DistributionManager, TemporaryDirectoryManager } -import org.enso.runtimeversionmanager.locking.{FileLockManager, ResourceManager} +import org.enso.runtimeversionmanager.locking.{ + ResourceManager, + ThreadSafeFileLockManager +} import org.enso.runtimeversionmanager.releases.ReleaseProvider import org.enso.runtimeversionmanager.releases.engine.{ EngineRelease, @@ -38,7 +41,9 @@ object DefaultDistributionConfiguration lazy val distributionManager = new DistributionManager(environment) /** @inheritdoc */ - lazy val lockManager = new FileLockManager(distributionManager.paths.locks) + lazy val lockManager = new ThreadSafeFileLockManager( + distributionManager.paths.locks + ) /** @inheritdoc */ lazy val resourceManager = new ResourceManager(lockManager) From fb7ce670256f0045df71729507be863dfea39d48 Mon Sep 17 00:00:00 2001 From: Dmitry Bushev Date: Wed, 10 Feb 2021 18:49:03 +0300 Subject: [PATCH 2/4] refactor: isShutdownHookRegistered variable --- .../LanguageServerController.scala | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerController.scala b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerController.scala index 4d18dbbf02ba..36cf3b2f0157 100644 --- a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerController.scala +++ b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerController.scala @@ -71,8 +71,6 @@ class LanguageServerController( import context.{dispatcher, system} - private var isShutdownHookRegistered: Boolean = false - private val descriptor = LanguageServerDescriptor( name = s"language-server-${project.id}", @@ -158,7 +156,8 @@ class LanguageServerController( private def supervising( connectionInfo: LanguageServerConnectionInfo, serverProcessManager: ActorRef, - clients: Set[UUID] = Set.empty + clients: Set[UUID] = Set.empty, + isShutdownHookRegistered: Boolean = false ): Receive = { case StartServer(clientId, _, requestedEngineVersion, _) => if (requestedEngineVersion != engineVersion) { @@ -190,11 +189,12 @@ class LanguageServerController( serverProcessManager, clients, clientId, - Some(sender()) + Some(sender()), + isShutdownHookRegistered ) case ShutDownServer => - shutDownServer(None) + shutDownServer(None, isShutdownHookRegistered) case ClientDisconnected(clientId) => removeClient( @@ -202,7 +202,8 @@ class LanguageServerController( serverProcessManager, clients, clientId, - None + None, + isShutdownHookRegistered ) case RenameProject(_, oldName, newName) => @@ -226,7 +227,15 @@ class LanguageServerController( context.stop(self) case RegisterShutdownHook(_, _) => - isShutdownHookRegistered = true + context.become( + supervising( + connectionInfo, + serverProcessManager, + clients, + isShutdownHookRegistered = true + ) + ) + } private def removeClient( @@ -234,11 +243,12 @@ class LanguageServerController( serverProcessManager: ActorRef, clients: Set[UUID], clientId: UUID, - maybeRequester: Option[ActorRef] + maybeRequester: Option[ActorRef], + isShutdownHookRegistered: Boolean ): Unit = { val updatedClients = clients - clientId if (updatedClients.isEmpty) { - shutDownServer(maybeRequester) + shutDownServer(maybeRequester, isShutdownHookRegistered) } else { sender() ! CannotDisconnectOtherClients context.become( @@ -247,7 +257,10 @@ class LanguageServerController( } } - private def shutDownServer(maybeRequester: Option[ActorRef]): Unit = { + private def shutDownServer( + maybeRequester: Option[ActorRef], + isShutdownHookRegistered: Boolean + ): Unit = { log.debug(s"Shutting down a language server for project ${project.id}") context.children.foreach(_ ! GracefulStop) if (!isShutdownHookRegistered) { From 95ee17daed4a450537ddcaa68b1bdf612da916e3 Mon Sep 17 00:00:00 2001 From: Dmitry Bushev Date: Thu, 11 Feb 2021 14:48:50 +0300 Subject: [PATCH 3/4] feat: revert async LS restart --- .../LanguageServerController.scala | 37 +++++-------------- .../LanguageServerRegistry.scala | 11 +++++- 2 files changed, 20 insertions(+), 28 deletions(-) diff --git a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerController.scala b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerController.scala index 36cf3b2f0157..b3ca31efc2af 100644 --- a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerController.scala +++ b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerController.scala @@ -31,7 +31,6 @@ import org.enso.projectmanager.infrastructure.languageserver.LanguageServerBootL import org.enso.projectmanager.infrastructure.languageserver.LanguageServerController._ import org.enso.projectmanager.infrastructure.languageserver.LanguageServerProtocol._ import org.enso.projectmanager.infrastructure.languageserver.LanguageServerRegistry.ServerShutDown -import org.enso.projectmanager.infrastructure.languageserver.ShutdownHookActivator.RegisterShutdownHook import org.enso.projectmanager.model.Project import org.enso.projectmanager.service.LoggingServiceDescriptor import org.enso.projectmanager.util.UnhandledLogging @@ -156,8 +155,7 @@ class LanguageServerController( private def supervising( connectionInfo: LanguageServerConnectionInfo, serverProcessManager: ActorRef, - clients: Set[UUID] = Set.empty, - isShutdownHookRegistered: Boolean = false + clients: Set[UUID] = Set.empty ): Receive = { case StartServer(clientId, _, requestedEngineVersion, _) => if (requestedEngineVersion != engineVersion) { @@ -189,12 +187,11 @@ class LanguageServerController( serverProcessManager, clients, clientId, - Some(sender()), - isShutdownHookRegistered + Some(sender()) ) case ShutDownServer => - shutDownServer(None, isShutdownHookRegistered) + shutDownServer(None) case ClientDisconnected(clientId) => removeClient( @@ -202,8 +199,7 @@ class LanguageServerController( serverProcessManager, clients, clientId, - None, - isShutdownHookRegistered + None ) case RenameProject(_, oldName, newName) => @@ -226,16 +222,6 @@ class LanguageServerController( log.error(s"Language server died [$connectionInfo]") context.stop(self) - case RegisterShutdownHook(_, _) => - context.become( - supervising( - connectionInfo, - serverProcessManager, - clients, - isShutdownHookRegistered = true - ) - ) - } private def removeClient( @@ -243,12 +229,11 @@ class LanguageServerController( serverProcessManager: ActorRef, clients: Set[UUID], clientId: UUID, - maybeRequester: Option[ActorRef], - isShutdownHookRegistered: Boolean + maybeRequester: Option[ActorRef] ): Unit = { val updatedClients = clients - clientId if (updatedClients.isEmpty) { - shutDownServer(maybeRequester, isShutdownHookRegistered) + shutDownServer(maybeRequester) } else { sender() ! CannotDisconnectOtherClients context.become( @@ -258,14 +243,10 @@ class LanguageServerController( } private def shutDownServer( - maybeRequester: Option[ActorRef], - isShutdownHookRegistered: Boolean + maybeRequester: Option[ActorRef] ): Unit = { log.debug(s"Shutting down a language server for project ${project.id}") context.children.foreach(_ ! GracefulStop) - if (!isShutdownHookRegistered) { - context.parent ! ServerShutDown(project.id) - } val cancellable = context.system.scheduler .scheduleOnce(timeoutConfig.shutdownTimeout, self, ShutdownTimeout) @@ -302,7 +283,7 @@ class LanguageServerController( case m: StartServer => // This instance has not yet been shut down. Retry - context.parent.forward(m) + context.parent.forward(Retry(m)) } private def waitingForChildren(): Receive = { case Terminated(_) => @@ -383,4 +364,6 @@ object LanguageServerController { case object ServerDied + case class Retry(message: Any) + } diff --git a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerRegistry.scala b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerRegistry.scala index 7b030deea93e..bda1bed8480d 100644 --- a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerRegistry.scala +++ b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerRegistry.scala @@ -9,6 +9,7 @@ import org.enso.projectmanager.boot.configuration.{ SupervisionConfig, TimeoutConfig } +import org.enso.projectmanager.infrastructure.languageserver.LanguageServerController.Retry import org.enso.projectmanager.infrastructure.languageserver.LanguageServerProtocol.{ CheckIfServerIsRunning, KillThemAll, @@ -24,6 +25,8 @@ import org.enso.projectmanager.service.LoggingServiceDescriptor import org.enso.projectmanager.util.UnhandledLogging import org.enso.projectmanager.versionmanagement.DistributionConfiguration +import scala.concurrent.duration._ + /** An actor that routes request regarding lang. server lifecycle to the * right controller that manages the server. * It creates a controller actor, if a server doesn't exist. @@ -72,7 +75,7 @@ class LanguageServerRegistry( loggingServiceDescriptor, executor ), - s"language-server-controller-${project.id}-${UUID.randomUUID()}" + s"language-server-controller-${project.id}" ) context.watch(controller) controller.forward(msg) @@ -115,6 +118,12 @@ class LanguageServerRegistry( case m @ RegisterShutdownHook(projectId, _) => serverControllers.get(projectId).foreach(_ ! m) + + case Retry(message) => + context.system.scheduler.scheduleOnce(200.millis, self, message)( + context.dispatcher, + context.sender() + ) } } From fad9dc2ed8128ed395e9bac6606cba872bc5a456 Mon Sep 17 00:00:00 2001 From: Dmitry Bushev Date: Thu, 11 Feb 2021 14:51:48 +0300 Subject: [PATCH 4/4] cleanup --- .../languageserver/LanguageServerController.scala | 4 +--- .../languageserver/LanguageServerGatewayImpl.scala | 1 - .../languageserver/LanguageServerRegistry.scala | 4 ---- 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerController.scala b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerController.scala index b3ca31efc2af..3a12ab860f62 100644 --- a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerController.scala +++ b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerController.scala @@ -242,9 +242,7 @@ class LanguageServerController( } } - private def shutDownServer( - maybeRequester: Option[ActorRef] - ): Unit = { + private def shutDownServer(maybeRequester: Option[ActorRef]): Unit = { log.debug(s"Shutting down a language server for project ${project.id}") context.children.foreach(_ ! GracefulStop) val cancellable = diff --git a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerGatewayImpl.scala b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerGatewayImpl.scala index 8f885f7e2ad1..456684ada6df 100644 --- a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerGatewayImpl.scala +++ b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerGatewayImpl.scala @@ -131,7 +131,6 @@ class LanguageServerGatewayImpl[ Sync[F] .effect { shutdownHookActivator ! RegisterShutdownHook(projectId, hook) - registry ! RegisterShutdownHook(projectId, hook) } } diff --git a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerRegistry.scala b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerRegistry.scala index bda1bed8480d..d6a42ad8e865 100644 --- a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerRegistry.scala +++ b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerRegistry.scala @@ -20,7 +20,6 @@ import org.enso.projectmanager.infrastructure.languageserver.LanguageServerProto StopServer } import org.enso.projectmanager.infrastructure.languageserver.LanguageServerRegistry.ServerShutDown -import org.enso.projectmanager.infrastructure.languageserver.ShutdownHookActivator.RegisterShutdownHook import org.enso.projectmanager.service.LoggingServiceDescriptor import org.enso.projectmanager.util.UnhandledLogging import org.enso.projectmanager.versionmanagement.DistributionConfiguration @@ -116,9 +115,6 @@ class LanguageServerRegistry( case CheckIfServerIsRunning(projectId) => sender() ! serverControllers.contains(projectId) - case m @ RegisterShutdownHook(projectId, _) => - serverControllers.get(projectId).foreach(_ ! m) - case Retry(message) => context.system.scheduler.scheduleOnce(200.millis, self, message)( context.dispatcher,