Skip to content

Commit 36ba163

Browse files
committed
Fix races during server shutdown in tests/CSharpLanguageServer.Tests/Tooling.fs
1 parent a49cb2c commit 36ba163

File tree

1 file changed

+59
-37
lines changed

1 file changed

+59
-37
lines changed

Diff for: tests/CSharpLanguageServer.Tests/Tooling.fs

+59-37
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ let processClientEvent (state: ClientState) (post: ClientEvent -> unit) msg : As
182182

183183
rc.Reply(())
184184

185-
return state
185+
return { state with ServerProcess = None }
186186

187187
| GetState rc ->
188188
rc.Reply(state)
@@ -203,7 +203,7 @@ let processClientEvent (state: ClientState) (post: ClientEvent -> unit) msg : As
203203
return { state with ServerStderrLineReadTask = nextStdErrLineReadTask }
204204

205205
| RpcMessageReceived result ->
206-
let readJsonRpcHeader (stdout: Stream) =
206+
let tryReadJsonRpcHeader (stdout: Stream) =
207207
let headerBytes = List<byte>()
208208

209209
let terminatorReached () =
@@ -213,45 +213,61 @@ let processClientEvent (state: ClientState) (post: ClientEvent -> unit) msg : As
213213
&& headerBytes[headerBytes.Count - 2] = (byte '\r')
214214
&& headerBytes[headerBytes.Count - 1] = (byte '\n')
215215

216-
while not (terminatorReached()) do
216+
let mutable eof = false
217+
while (not eof && not (terminatorReached())) do
217218
let b: int = stdout.ReadByte()
218219
if b < 0 then
219-
let failureMessage = (sprintf "readJsonRpcHeader: EOF, b=%d; bytes=%d; \"%s\"" (int b) (headerBytes.Count) (headerBytes.ToArray() |> Encoding.UTF8.GetString))
220-
failwith failureMessage
220+
eof <- true
221221
else
222222
headerBytes.Add(byte b)
223223
()
224224

225-
let headers =
226-
Encoding.UTF8.GetString(headerBytes.ToArray())
227-
|> _.Split("\r\n")
228-
|> Seq.filter (fun s -> s.Length > 0)
229-
|> Seq.map (_.Split(":"))
225+
match eof with
226+
| true ->
227+
None
228+
229+
| false ->
230+
let headers =
231+
Encoding.UTF8.GetString(headerBytes.ToArray())
232+
|> _.Split("\r\n")
233+
|> Seq.filter (fun s -> s.Length > 0)
234+
|> Seq.map (_.Split(":"))
230235

231-
let contentLength =
232-
headers
233-
|> Seq.filter (fun a -> a[0].ToLower().Trim() = "content-length")
234-
|> Seq.map (fun a -> a[1] |> Int32.Parse)
235-
|> Seq.head
236+
let contentLength =
237+
headers
238+
|> Seq.filter (fun a -> a[0].ToLower().Trim() = "content-length")
239+
|> Seq.map (fun a -> a[1] |> Int32.Parse)
240+
|> Seq.head
236241

237-
{| ContentLength = contentLength; ContentType = None |}
242+
Some {| ContentLength = contentLength; ContentType = None |}
238243

239244
let readNextJsonRpcMessage () =
240-
try
241-
let stdout = state.ServerProcess.Value.StandardOutput.BaseStream
242-
if not stdout.CanRead then
243-
failwith "stdout.CanRead = false"
244-
let header = readJsonRpcHeader stdout
245-
let content: byte[] = Array.zeroCreate header.ContentLength
246-
let bytesRead = stdout.Read(content)
247-
if bytesRead <> header.ContentLength then
248-
failwith "readNextJsonRpcMessage: could not read full content"
249-
250-
let msg = Encoding.UTF8.GetString(content) |> JObject.Parse
251-
252-
post (RpcMessageReceived (Ok (Some msg)))
253-
with ex ->
254-
post (RpcMessageReceived (Error ex))
245+
match state.ServerProcess with
246+
| None ->
247+
logMessage "RpcMessageReceived" "no state.ServerProcess, will not read next rpc message"
248+
()
249+
250+
| Some serverProcess ->
251+
try
252+
let stdout = serverProcess.StandardOutput.BaseStream
253+
if not stdout.CanRead then
254+
failwith "stdout.CanRead = false"
255+
let headerMaybe = tryReadJsonRpcHeader stdout
256+
match headerMaybe with
257+
| None ->
258+
logMessage "RpcMessageReceived" "readNextJsonRpcMessage: EOF received when reading header"
259+
()
260+
| Some header ->
261+
let content: byte[] = Array.zeroCreate header.ContentLength
262+
let bytesRead = stdout.Read(content)
263+
if bytesRead <> header.ContentLength then
264+
failwith "readNextJsonRpcMessage: could not read full content"
265+
266+
let msg = Encoding.UTF8.GetString(content) |> JObject.Parse
267+
268+
post (RpcMessageReceived (Ok (Some msg)))
269+
with ex ->
270+
post (RpcMessageReceived (Error ex))
255271

256272
match result with
257273
| Error e ->
@@ -365,14 +381,20 @@ let processClientEvent (state: ClientState) (post: ClientEvent -> unit) msg : As
365381
| SendRpcMessage rpcMsg ->
366382
let rpcMsgJson = (string rpcMsg)
367383

368-
let serverStdin = state.ServerProcess.Value.StandardInput
369-
let formattedMessage = String.Format("Content-Length: {0}\r\n\r\n{1}", rpcMsgJson.Length, rpcMsgJson)
370-
serverStdin.Write(formattedMessage)
371-
serverStdin.Flush()
384+
match state.ServerProcess with
385+
| None ->
386+
logMessage "SendRpcMessage" (sprintf "dropping rpcMsg as there is no state.ServerProcess: %s " rpcMsgJson)
387+
return state
388+
389+
| Some serverProcess ->
390+
let serverStdin = serverProcess.StandardInput
391+
let formattedMessage = String.Format("Content-Length: {0}\r\n\r\n{1}", rpcMsgJson.Length, rpcMsgJson)
392+
serverStdin.Write(formattedMessage)
393+
serverStdin.Flush()
372394

373-
let newRpcLog = state.RpcLog @ [{ Source = Client; TimestampMS = 0; Message = rpcMsg }]
395+
let newRpcLog = state.RpcLog @ [{ Source = Client; TimestampMS = 0; Message = rpcMsg }]
374396

375-
return { state with RpcLog = newRpcLog }
397+
return { state with RpcLog = newRpcLog }
376398

377399
| EmitLogMessage (timestamp, logger, msg) ->
378400
let offsetMs = int (timestamp - state.SetupTimestamp).TotalMilliseconds

0 commit comments

Comments
 (0)