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

improvement: show bsp as connected after the first message from build server #5707

Merged
merged 2 commits into from
Oct 6, 2023
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 @@ -454,10 +454,14 @@ object BuildServerConnection {
def setupServer(): Future[LauncherConnection] = {
connect().map { case conn @ SocketConnection(_, output, input, _, _) =>
val tracePrinter = Trace.setupTracePrinter("BSP", bspTraceRoot)
val requestMonitor =
if (addLivenessMonitor) Some(new RequestMonitorImpl) else None
val bspStatusOpt =
if (addLivenessMonitor)
Some(new BspStatus(languageClient, serverName, config.icons))
else None
val requestMonitorOpt =
bspStatusOpt.map(new RequestMonitorImpl(_))
val wrapper: MessageConsumer => MessageConsumer =
requestMonitor.map(_.wrapper).getOrElse(identity)
requestMonitorOpt.map(_.wrapper).getOrElse(identity)
val launcher =
new Launcher.Builder[MetalsBuildServer]()
.traceMessages(tracePrinter.orNull)
Expand All @@ -484,17 +488,16 @@ object BuildServerConnection {
}

val optServerLivenessMonitor =
requestMonitor.map {
new ServerLivenessMonitor(
_,
() => server.workspaceBuildTargets(),
languageClient,
config.metalsToIdleTime,
config.pingInterval,
serverName,
config.icons,
)
}
for {
bspStatus <- bspStatusOpt
requestMonitor <- requestMonitorOpt
} yield new ServerLivenessMonitor(
requestMonitor,
() => server.workspaceBuildTargets(),
config.metalsToIdleTime,
config.pingInterval,
bspStatus,
)

LauncherConnection(
conn,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import java.util.concurrent.Executors
import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.ScheduledFuture
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicReference

import scala.concurrent.duration.Duration
Expand All @@ -23,7 +24,7 @@ trait RequestMonitor {
def lastIncoming: Option[Long]
}

class RequestMonitorImpl extends RequestMonitor {
class RequestMonitorImpl(bspStatus: BspStatus) extends RequestMonitor {
@volatile private var lastOutgoing_ : Option[Long] = None
@volatile private var lastIncoming_ : Option[Long] = None

Expand All @@ -44,7 +45,10 @@ class RequestMonitorImpl extends RequestMonitor {
}

private def outgoingMessage() = lastOutgoing_ = now
private def incomingMessage(): Unit = lastIncoming_ = now
private def incomingMessage(): Unit = {
bspStatus.connected()
lastIncoming_ = now
}
private def now = Some(System.currentTimeMillis())

def lastOutgoing: Option[Long] = lastOutgoing_
Expand All @@ -54,22 +58,14 @@ class RequestMonitorImpl extends RequestMonitor {
class ServerLivenessMonitor(
requestMonitor: RequestMonitor,
ping: () => Unit,
client: MetalsLanguageClient,
metalsIdleInterval: Duration,
pingInterval: Duration,
serverName: String,
icons: Icons,
bspStatus: BspStatus,
) {
private val state: AtomicReference[ServerLivenessMonitor.State] =
new AtomicReference(ServerLivenessMonitor.Idle)
@volatile private var isServerResponsive = true
val scheduler: ScheduledExecutorService = Executors.newScheduledThreadPool(1)
val connectedParams: MetalsStatusParams =
ServerLivenessMonitor.connectedParams(serverName, icons)
val noResponseParams: MetalsStatusParams =
ServerLivenessMonitor.noResponseParams(serverName, icons)

client.metalsStatus(connectedParams)
scribe.debug("starting server liveness monitor")

def runnable(): Runnable = new Runnable {
Expand Down Expand Up @@ -99,12 +95,9 @@ class ServerLivenessMonitor(
case _ =>
}
if (currState == ServerLivenessMonitor.Running) {
if (notResponding && isServerResponsive) {
scribe.debug("server liveness monitor detected no response")
client.metalsStatus(noResponseParams)
} else if (!notResponding && !isServerResponsive)
client.metalsStatus(connectedParams)
isServerResponsive = !notResponding
if (notResponding) {
bspStatus.noResponse()
}
}
scribe.debug("server liveness monitor: pinging build server...")
ping()
Expand All @@ -122,13 +115,13 @@ class ServerLivenessMonitor(
TimeUnit.MILLISECONDS,
)

def isBuildServerResponsive: Boolean = isServerResponsive
def isBuildServerResponsive: Boolean = bspStatus.isBuildServerResponsive

def shutdown(): Unit = {
scribe.debug("shutting down server liveness monitor")
scheduled.cancel(true)
scheduler.shutdown()
client.metalsStatus(ServerLivenessMonitor.disconnectedParams)
bspStatus.disconnected()
}

def getState: ServerLivenessMonitor.State = state.get()
Expand All @@ -147,6 +140,29 @@ object ServerLivenessMonitor {
object FirstPing extends State
object Running extends State

}

class BspStatus(
client: MetalsLanguageClient,
serverName: String,
icons: Icons,
) {
private val isServerResponsive = new AtomicBoolean(false)

def connected(): Unit =
if (isServerResponsive.compareAndSet(false, true))
client.metalsStatus(BspStatus.connectedParams(serverName, icons))
def noResponse(): Unit =
if (isServerResponsive.compareAndSet(true, false)) {
scribe.debug("server liveness monitor detected no response")
client.metalsStatus(BspStatus.noResponseParams(serverName, icons))
}
def disconnected(): Unit = client.metalsStatus(BspStatus.disconnectedParams)

def isBuildServerResponsive: Boolean = isServerResponsive.get()
}

object BspStatus {
def connectedParams(serverName: String, icons: Icons): MetalsStatusParams =
MetalsStatusParams(
s"$serverName ${icons.link}",
Expand All @@ -167,5 +183,4 @@ object ServerLivenessMonitor {
command = ServerCommands.ConnectBuildServer.id,
commandTooltip = "Reconnect.",
).withStatusType(StatusType.bsp)

}
4 changes: 2 additions & 2 deletions tests/unit/src/main/scala/tests/TestingClient.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ import scala.meta.internal.builds.BspErrorHandler
import scala.meta.internal.builds.BuildTool
import scala.meta.internal.builds.BuildTools
import scala.meta.internal.decorations.PublishDecorationsParams
import scala.meta.internal.metals.BspStatus
import scala.meta.internal.metals.Buffers
import scala.meta.internal.metals.ClientCommands
import scala.meta.internal.metals.FileOutOfScalaCliBspScope
import scala.meta.internal.metals.Icons
import scala.meta.internal.metals.Messages._
import scala.meta.internal.metals.MetalsEnrichments._
import scala.meta.internal.metals.ServerCommands
import scala.meta.internal.metals.ServerLivenessMonitor
import scala.meta.internal.metals.TextEdits
import scala.meta.internal.metals.WorkspaceChoicePopup
import scala.meta.internal.metals.clients.language.MetalsInputBoxParams
Expand Down Expand Up @@ -373,7 +373,7 @@ class TestingClient(workspace: AbsolutePath, val buffers: Buffers)
) {
bspError
} else if (
params.getMessage() == ServerLivenessMonitor
params.getMessage() == BspStatus
.noResponseParams("Bill", Icons.default)
.logMessage(Icons.default)
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ package tests

import scala.concurrent.duration.Duration

import scala.meta.internal.metals.BspStatus
import scala.meta.internal.metals.Icons
import scala.meta.internal.metals.Messages
import scala.meta.internal.metals.MetalsEnrichments._
import scala.meta.internal.metals.MetalsServerConfig
import scala.meta.internal.metals.ServerLivenessMonitor

import bill.Bill
import ch.epfl.scala.bsp4j.BuildTargetIdentifier
Expand Down Expand Up @@ -60,7 +60,7 @@ class ServerLivenessMonitorLspSuite extends BaseLspSuite("liveness-monitor") {
)
)
_ = Thread.sleep(sleepTime)
noResponseParams = ServerLivenessMonitor.noResponseParams(
noResponseParams = BspStatus.noResponseParams(
"Bill",
Icons.default,
)
Expand Down
12 changes: 8 additions & 4 deletions tests/unit/src/test/scala/tests/ServerLivenessMonitorSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import scala.concurrent.ExecutionContext
import scala.concurrent.ExecutionContextExecutorService
import scala.concurrent.duration.Duration

import scala.meta.internal.metals.BspStatus
import scala.meta.internal.metals.Icons
import scala.meta.internal.metals.RequestMonitor
import scala.meta.internal.metals.ServerLivenessMonitor
Expand All @@ -22,15 +23,15 @@ class ServerLivenessMonitorSuite extends BaseSuite {
val pingInterval = Duration("3s")
val server = new ResponsiveServer(pingInterval)
val client = new CountMessageRequestsClient
val bspStatus = new BspStatus(client, "responsive-server", Icons.default)
val livenessMonitor = new ServerLivenessMonitor(
server,
() => server.sendRequest(true),
client,
metalsIdleInterval = pingInterval * 4,
pingInterval,
"responsive-server",
Icons.default,
bspStatus,
)
bspStatus.connected()
Thread.sleep(pingInterval.toMillis * 3 / 2)
assertEquals(livenessMonitor.getState, ServerLivenessMonitor.Idle)
server.sendRequest(false)
Expand All @@ -46,6 +47,7 @@ class ServerLivenessMonitorSuite extends BaseSuite {
server.sendRequest(false)
assertEquals(livenessMonitor.getState, ServerLivenessMonitor.Running)
assert(client.showMessageRequests == 0)
livenessMonitor.shutdown()
}
}

Expand Down Expand Up @@ -85,7 +87,9 @@ class CountMessageRequestsClient extends NoopLanguageClient {
var showMessageRequests = 0

override def metalsStatus(params: MetalsStatusParams): Unit =
if (params == ServerLivenessMonitor.disconnectedParams) {
if (
params == BspStatus.noResponseParams("responsive-server", Icons.default)
) {
showMessageRequests += 1
}
}
Loading