Skip to content

Commit c932640

Browse files
authored
Merge pull request #2832 from arnavsharma990/fix-bsp-local-socket-cleanup
Delete local BSP socket file on shutdown
2 parents 9ed153b + c43eb20 commit c932640

File tree

2 files changed

+49
-11
lines changed

2 files changed

+49
-11
lines changed

frontend/src/main/scala/bloop/bsp/BspServer.scala

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ package bloop.bsp
22

33
import java.net.ServerSocket
44
import java.net.Socket
5+
import java.nio.file.Files
56
import java.nio.file.NoSuchFileException
7+
import java.nio.file.Path
68
import java.util.concurrent.ConcurrentHashMap
79
import java.util.concurrent.TimeUnit
810

@@ -32,6 +34,7 @@ object BspServer {
3234
private implicit val logContext: DebugFilter = DebugFilter.Bsp
3335

3436
import Commands.ValidatedBsp
37+
3538
private def initServer(handle: ServerHandle, state: State): Task[ServerSocket] = {
3639
state.logger.debug(s"Waiting for a connection at $handle...")
3740
val openSocket = handle.server
@@ -52,10 +55,13 @@ object BspServer {
5255
): Task[State] = {
5356
import state.logger
5457

55-
def listenToConnection(handle: ServerHandle, serverSocket: ServerSocket): Task[State] = {
58+
def listenToConnection(
59+
handle: ServerHandle,
60+
serverSocket: ServerSocket,
61+
shutdownHook: Option[Thread]
62+
): Task[State] = {
5663
val isCommunicationActive = Atomic(true)
5764
val connectionURI = handle.uri
58-
5965
// Do NOT change this log, it's used by clients to know when to start a connection
6066
logger.info(s"The server is listening for incoming connections at $connectionURI...")
6167
promiseWhenStarted.foreach(_.success(()))
@@ -122,11 +128,14 @@ object BspServer {
122128
case None => clients0
123129
}
124130
}
131+
125132
bspLogger.info {
126133
if (cancelled) "BSP server cancelled, closing socket..."
127134
else "BSP server stopped"
128135
}
136+
129137
server.cancelAllRequests()
138+
130139
ioScheduler.scheduleOnce(
131140
100,
132141
TimeUnit.MILLISECONDS,
@@ -138,15 +147,25 @@ object BspServer {
138147
}
139148
}
140149
)
141-
closeCommunication(latestState, socket, serverSocket)
150+
151+
val socketPath = handle match {
152+
case ServerHandle.UnixLocal(socketFile) => Some(socketFile.underlying)
153+
case _ => None
154+
}
155+
156+
shutdownHook.foreach { hook =>
157+
try Runtime.getRuntime.removeShutdownHook(hook)
158+
catch { case NonFatal(_) => () }
159+
}
160+
161+
closeCommunication(latestState, socket, serverSocket, socketPath)
142162
}
143163
}
144164

145165
process
146166
.doOnCancel(Task(stopListeting(cancelled = true)))
147167
.doOnFinish { errOpt =>
148-
for (err <- errOpt)
149-
err.printStackTrace(System.err)
168+
for (err <- errOpt) err.printStackTrace(System.err)
150169
Task(stopListeting(cancelled = false))
151170
}
152171
.map(_ => provider.stateAfterExecution)
@@ -160,13 +179,27 @@ object BspServer {
160179
}
161180

162181
initServer(handle, state).materialize.flatMap {
163-
case scala.util.Success(socket: ServerSocket) =>
164-
listenToConnection(handle, socket).onErrorRecover {
182+
case scala.util.Success(serverSocket: ServerSocket) =>
183+
val shutdownHook: Option[Thread] = handle match {
184+
case ServerHandle.UnixLocal(socketFile) =>
185+
val hook = new Thread {
186+
override def run(): Unit = {
187+
try Files.deleteIfExists(socketFile.underlying)
188+
catch { case NonFatal(_) => () }
189+
}
190+
}
191+
Runtime.getRuntime.addShutdownHook(hook)
192+
Some(hook)
193+
case _ => None
194+
}
195+
196+
listenToConnection(handle, serverSocket, shutdownHook).onErrorRecover {
165197
case t =>
166198
System.err.println("Exiting BSP server with:")
167199
t.printStackTrace(System.err)
168200
state.withError(s"Exiting BSP server with ${t.getMessage}", t)
169201
}
202+
170203
case scala.util.Failure(t: Throwable) =>
171204
promiseWhenStarted.foreach(p => if (!p.isCompleted) p.failure(t))
172205
Task.now(state.withError(s"BSP server failed to open a socket: '${t.getMessage}'", t))
@@ -176,7 +209,8 @@ object BspServer {
176209
def closeCommunication(
177210
latestState: State,
178211
socket: Socket,
179-
serverSocket: ServerSocket
212+
serverSocket: ServerSocket,
213+
socketPath: Option[Path] = None
180214
): Unit = {
181215
// Close any socket communication asap and swallow exceptions
182216
try {
@@ -185,6 +219,12 @@ object BspServer {
185219
finally {
186220
try serverSocket.close()
187221
catch { case NonFatal(_) => () }
222+
finally {
223+
socketPath.foreach { path =>
224+
try Files.deleteIfExists(path)
225+
catch { case NonFatal(_) => () }
226+
}
227+
}
188228
}
189229
} finally {
190230
// Guarantee that we always schedule the external classes directories deletion
@@ -214,5 +254,4 @@ object BspServer {
214254
()
215255
}
216256
}
217-
218257
}

frontend/src/test/scala/bloop/bsp/BspClientTest.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,7 @@ trait BspClientTest {
5252
cmd match {
5353
case cmd: Commands.UnixLocalBsp =>
5454
// We delete the socket file created by the BSP communication
55-
if (!Files.exists(cmd.socket.underlying)) ()
56-
else Files.delete(cmd.socket.underlying)
55+
Files.deleteIfExists(cmd.socket.underlying)
5756
case _: Commands.TcpBsp => ()
5857
}
5958
}

0 commit comments

Comments
 (0)