From 816763f9cd2dd92555dad27c5c90bd2b9f3928b0 Mon Sep 17 00:00:00 2001 From: Andrew McGuier Date: Mon, 11 Oct 2021 12:28:15 -0400 Subject: [PATCH] Websocket CPU efficiency improvements. (#711) I see high cpu usage using the `fsdocs watch` command in current release version. It appears that the loop inside the socket is running constantly since it never yields to receive socket messages from the client, causing the process to use 100% of available CPU. Also, since the reload creates a new socket connection (and suave creates a new handler) this compounds since a new infinite loop is created after every reload. I've replaced the mutable boolean with a ManualResetEvent which all connected sockets block on until a reload is called for. Once the socket connection is closed the handler exits. I chose ManualResetEvent over AutoResetEvent because we want all connected sockets to restart at the same time, rather than individually, since someone might have multiple tabs open. --- .../BuildCommand.fs | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/src/FSharp.Formatting.CommandTool/BuildCommand.fs b/src/FSharp.Formatting.CommandTool/BuildCommand.fs index 1363dc6aa..8ae0518ac 100644 --- a/src/FSharp.Formatting.CommandTool/BuildCommand.fs +++ b/src/FSharp.Formatting.CommandTool/BuildCommand.fs @@ -290,21 +290,15 @@ module Serve = tag.Replace("{{PORT}}", string port) - let mutable signalHotReload = false + + let signalHotReload = new System.Threading.ManualResetEvent(false); let socketHandler (webSocket : WebSocket) _ = socket { - while true do + signalHotReload.WaitOne() |> ignore + signalHotReload.Reset() |> ignore let emptyResponse = [||] |> ByteSegment - //not sure what this was needed for - //do! - // refreshEvent.Publish - // |> Control.Async.AwaitEvent - // |> Suave.Sockets.SocketOp.ofAsync - //do! webSocket.send Text (ByteSegment (Encoding.UTF8.GetBytes "refreshed")) true - if signalHotReload then - printfn "Triggering hot reload on the client" - do! webSocket.send Close emptyResponse true - signalHotReload <- false + printfn "Triggering hot reload on the client" + do! webSocket.send Close emptyResponse true } let startWebServer outputDirectory localPort = @@ -842,7 +836,7 @@ type CoreBuildOptions(watch) = if runDocContentPhase2() then regenerateSearchIndex() ) - Serve.signalHotReload <- true + Serve.signalHotReload.Set() |> ignore } |> Async.Start ) @@ -860,7 +854,7 @@ type CoreBuildOptions(watch) = if runGeneratePhase2() then regenerateSearchIndex() ) - Serve.signalHotReload <- true + Serve.signalHotReload.Set() |> ignore } |> Async.Start )