VHDL-LS / rust_hdl

Other
343 stars 65 forks source link

LSP Doesn't Properly shut down when receiving the shutdown and exit commands from client #142

Closed SethGower closed 1 year ago

SethGower commented 1 year ago

I have noticed that the Language server stays active even after the client has sent the shutdown command. This may be fine in the case of someone using an editor like VSCode, where you probably aren't starting and stopping the editor frequently. But it happens more with an editor like neovim. I have noticed that my RAM will slowly climb up over time, when I investigated it was because I had ~80 processes of vhdl_ls running, causing my system to be slow since all of my RAM (64GB) was being used. Once I killed all of the processes it fixed the issue. I have narrowed this down to VHDL LS since I have enabled debug logging in the client (nvim-lspconfig) and it sends the shutdown and exit commands. After these are sent, the PIDs are still active and using CPU time/RAM. Here's a basic output of the LSP Log for nvim

[START][2022-12-06 10:48:38] LSP logging initiated
[INFO][2022-12-06 10:48:39] .../vim/lsp/rpc.lua:261 "Starting RPC client"   {  args = {},  cmd = "vhdl_ls",  extra = {    cwd = "/home/sgower/Documents/Development/test/vhdl"  }}
[DEBUG][2022-12-06 10:48:39] .../vim/lsp/rpc.lua:347    "rpc.send"  {  id = 1,  jsonrpc = "2.0",  method = "initialize",  params = {    capabilities = {      callHierarchy = {        dynamicRegistration = false      },      textDocument = {        codeAction = {          codeActionLiteralSupport = {            codeActionKind = {              valueSet = { "", "Empty", "QuickFix", "Refactor", "RefactorExtract", "RefactorInline", "RefactorRewrite", "Source", "SourceOrganizeImports", "quickfix", "refactor", "refactor.extract", "refactor.inline", "refactor.rewrite", "source", "source.organizeImports" }            }          },          dataSupport = true,          dynamicRegistration = false,          resolveSupport = {            properties = { "edit" }          }        },        completion = {          completionItem = {            commitCharactersSupport = false,            deprecatedSupport = false,            documentationFormat = { "markdown", "plaintext" },            preselectSupport = false,            resolveSupport = {              properties = { "documentation", "detail", "additionalTextEdits" }            },            snippetSupport = true          },          completionItemKind = {            valueSet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 }          },          contextSupport = false,          dynamicRegistration = false        },        declaration = {          linkSupport = true        },        definition = {          linkSupport = true        },        documentHighlight = {          dynamicRegistration = false        },        documentSymbol = {          dynamicRegistration = false,          hierarchicalDocumentSymbolSupport = true,          symbolKind = {            valueSet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 }          }        },        hover = {          contentFormat = { "markdown", "plaintext" },          dynamicRegistration = false        },        implementation = {          linkSupport = true        },        publishDiagnostics = {          relatedInformation = true,          tagSupport = {            valueSet = { 1, 2 }          }        },        references = {          dynamicRegistration = false        },        rename = {          dynamicRegistration = false,          prepareSupport = true        },        signatureHelp = {          dynamicRegistration = false,          signatureInformation = {            activeParameterSupport = true,            documentationFormat = { "markdown", "plaintext" },            parameterInformation = {              labelOffsetSupport = true            }          }        },        synchronization = {          didSave = true,          dynamicRegistration = false,          willSave = false,          willSaveWaitUntil = false        },        typeDefinition = {          linkSupport = true        }      },      window = {        showDocument = {          support = false        },        showMessage = {          messageActionItem = {            additionalPropertiesSupport = false          }        },        workDoneProgress = true      },      workspace = {        applyEdit = true,        configuration = true,        symbol = {          dynamicRegistration = false,          hierarchicalWorkspaceSymbolSupport = true,          symbolKind = {            valueSet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 }          }        },        workspaceEdit = {          resourceOperations = { "rename", "create", "delete" }        },        workspaceFolders = true      }    },    clientInfo = {      name = "Neovim",      version = "0.7.2"    },    initializationOptions = vim.empty_dict(),    processId = 141758,    rootPath = "/home/sgower/Documents/Development/test/vhdl",    rootUri = "file:///home/sgower/Documents/Development/test/vhdl",    trace = "off",    workspaceFolders = { {        name = "/home/sgower/Documents/Development/test/vhdl",        uri = "file:///home/sgower/Documents/Development/test/vhdl"      } }  }}
[DEBUG][2022-12-06 10:48:39] .../vim/lsp/rpc.lua:454    "rpc.receive"   {  jsonrpc = "2.0",  method = "window/logMessage",  params = {    message = "Loaded Installation configuration file: /home/sgower/.local/share/rust_hdl/target/release/../../vhdl_libraries/vhdl_ls.toml",    type = 4  }}
[DEBUG][2022-12-06 10:48:39] .../vim/lsp/rpc.lua:454    "rpc.receive"   {  jsonrpc = "2.0",  method = "window/logMessage",  params = {    message = "Loaded workspace root configuration file: /home/sgower/Documents/Development/test/vhdl/vhdl_ls.toml",    type = 4  }}
[DEBUG][2022-12-06 10:48:39] .../vim/lsp/rpc.lua:454    "rpc.receive"   {  id = 1,  jsonrpc = "2.0",  result = {    capabilities = {      declarationProvider = true,      definitionProvider = true,      hoverProvider = true,      referencesProvider = true,      textDocumentSync = 2    }  }}
[INFO][2022-12-06 10:48:39] ...lsp/handlers.lua:459 "Loaded Installation configuration file: /home/sgower/.local/share/rust_hdl/target/release/../../vhdl_libraries/vhdl_ls.toml"
[INFO][2022-12-06 10:48:39] ...lsp/handlers.lua:459 "Loaded workspace root configuration file: /home/sgower/Documents/Development/test/vhdl/vhdl_ls.toml"
[DEBUG][2022-12-06 10:48:39] .../vim/lsp/rpc.lua:347    "rpc.send"  {  jsonrpc = "2.0",  method = "initialized",  params = vim.empty_dict()}
[DEBUG][2022-12-06 10:48:39] .../lua/vim/lsp.lua:982    "LSP[rust_hdl]" "server_capabilities"   {  declarationProvider = true,  definitionProvider = true,  hoverProvider = true,  referencesProvider = true,  textDocumentSync = 2}
[INFO][2022-12-06 10:48:39] .../lua/vim/lsp.lua:983 "LSP[rust_hdl]" "initialized"   {  resolved_capabilities = {    call_hierarchy = false,    code_action = false,    code_lens = false,    code_lens_resolve = false,    completion = false,    declaration = true,    document_formatting = false,    document_highlight = false,    document_range_formatting = false,    document_symbol = false,    execute_command = false,    find_references = true,    goto_definition = true,    hover = true,    implementation = false,    rename = false,    signature_help = false,    signature_help_trigger_characters = {},    text_document_did_change = 2,    text_document_open_close = true,    text_document_save = true,    text_document_save_include_text = false,    text_document_will_save = false,    text_document_will_save_wait_until = false,    type_definition = false,    workspace_folder_properties = {      changeNotifications = false,      supported = false    },    workspace_symbol = false  }}
[DEBUG][2022-12-06 10:48:39] .../vim/lsp/rpc.lua:347    "rpc.send"  {  jsonrpc = "2.0",  method = "textDocument/didOpen",  params = {    textDocument = {      languageId = "vhdl",      text = "library ieee;\n    use ieee.std_logic_1164.all;\n    use ieee.numeric_std.all;\n    use ieee.std_logic_unsigned.all;\n\nentity test is\n    port (\n        i_clk : in    std_logic;\n        i_a   : in    std_logic;\n        o_x   : out   std_logic\n    );\nend entity test;\n\narchitecture behavioral of test is\n\nbegin\n\n    o_x_pro : process (i_clk) is\n    begin\n\n        if rising_edge(i_clk) then\n            o_x <= i_a;\n        end if;\n\n    end process o_x_pro;\n\nend architecture behavioral;\n",      uri = "file:///home/sgower/Documents/Development/test/vhdl/test.vhd",      version = 0    }  }}
[INFO][2022-12-06 10:48:41] .../lua/vim/lsp.lua:1392    "exit_handler"  { {    _on_attach = <function 1>,    attached_buffers = {      [2] = true    },    cancel_request = <function 2>,    commands = {},    config = {      _on_attach = <function 3>,      autostart = true,      capabilities = {        callHierarchy = {          dynamicRegistration = false        },        textDocument = {          codeAction = {            codeActionLiteralSupport = {              codeActionKind = {                valueSet = { "", "Empty", "QuickFix", "Refactor", "RefactorExtract", "RefactorInline", "RefactorRewrite", "Source", "SourceOrganizeImports", "quickfix", "refactor", "refactor.extract", "refactor.inline", "refactor.rewrite", "source", "source.organizeImports" }              }            },            dataSupport = true,            dynamicRegistration = false,            resolveSupport = {              properties = { "edit" }            }          },          completion = {            completionItem = {              commitCharactersSupport = false,              deprecatedSupport = false,              documentationFormat = { "markdown", "plaintext" },              preselectSupport = false,              resolveSupport = {                properties = { "documentation", "detail", "additionalTextEdits" }              },              snippetSupport = true            },            completionItemKind = {              valueSet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 }            },            contextSupport = false,            dynamicRegistration = false          },          declaration = {            linkSupport = true          },          definition = {            linkSupport = true          },          documentHighlight = {            dynamicRegistration = false          },          documentSymbol = {            dynamicRegistration = false,            hierarchicalDocumentSymbolSupport = true,            symbolKind = {              valueSet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 }            }          },          hover = {            contentFormat = { "markdown", "plaintext" },            dynamicRegistration = false          },          implementation = {            linkSupport = true          },          publishDiagnostics = {            relatedInformation = true,            tagSupport = {              valueSet = { 1, 2 }            }          },          references = {            dynamicRegistration = false          },          rename = {            dynamicRegistration = false,            prepareSupport = true          },          signatureHelp = {            dynamicRegistration = false,            signatureInformation = {              activeParameterSupport = true,              documentationFormat = { "markdown", "plaintext" },              parameterInformation = {                labelOffsetSupport = true              }            }          },          synchronization = {            didSave = true,            dynamicRegistration = false,            willSave = false,            willSaveWaitUntil = false          },          typeDefinition = {            linkSupport = true          }        },        window = {          showDocument = {            support = false          },          showMessage = {            messageActionItem = {              additionalPropertiesSupport = false            }          },          workDoneProgress = true        },        workspace = {          applyEdit = true,          configuration = true,          symbol = {            dynamicRegistration = false,            hierarchicalWorkspaceSymbolSupport = true,            symbolKind = {              valueSet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 }            }          },          workspaceEdit = {            resourceOperations = { "rename", "create", "delete" }          },          workspaceFolders = true        }      },      cmd = { "vhdl_ls" },      cmd_cwd = "/home/sgower/Documents/Development/test/vhdl",      filetypes = { "vhdl" },      flags = {        debounce_text_changes = 150      },      get_language_id = <function 4>,      handlers = <1>{},      init_options = vim.empty_dict(),      log_level = 2,      message_level = 2,      name = "rust_hdl",      on_attach = <function 5>,      on_exit = <function 6>,      on_init = <function 7>,      root_dir = "/home/sgower/Documents/Development/test/vhdl",      settings = {},      workspace_folders = <2>{ {          name = "/home/sgower/Documents/Development/test/vhdl",          uri = "file:///home/sgower/Documents/Development/test/vhdl"        } },      <metatable> = <3>{        __tostring = <function 8>      }    },    handlers = <table 1>,    id = 1,    initialized = true,    is_stopped = <function 9>,    messages = {      messages = {},      name = "rust_hdl",      progress = {},      status = {}    },    name = "rust_hdl",    notify = <function 10>,    offset_encoding = "utf-16",    request = <function 11>,    request_sync = <function 12>,    requests = {},    resolved_capabilities = {      call_hierarchy = false,      code_action = false,      code_lens = false,      code_lens_resolve = false,      completion = false,      declaration = true,      document_formatting = false,      document_highlight = false,      document_range_formatting = false,      document_symbol = false,      execute_command = false,      find_references = true,      goto_definition = true,      hover = true,      implementation = false,      rename = false,      signature_help = false,      signature_help_trigger_characters = {},      text_document_did_change = 2,      text_document_open_close = true,      text_document_save = true,      text_document_save_include_text = false,      text_document_will_save = false,      text_document_will_save_wait_until = false,      type_definition = false,      workspace_folder_properties = {        changeNotifications = false,        supported = false      },      workspace_symbol = false    },    rpc = {      handle = <userdata 1>,      notify = <function 13>,      pid = 141762,      request = <function 14>    },    server_capabilities = {      declarationProvider = true,      definitionProvider = true,      hoverProvider = true,      referencesProvider = true,      textDocumentSync = 2    },    stop = <function 15>,    supports_method = <function 16>,    workspaceFolders = <table 2>,    workspace_did_change_configuration = <function 17>,    workspace_folders = <table 2>  } }
[DEBUG][2022-12-06 10:48:41] .../vim/lsp/rpc.lua:347    "rpc.send"  {  id = 2,  jsonrpc = "2.0",  method = "shutdown"}
[DEBUG][2022-12-06 10:48:41] .../vim/lsp/rpc.lua:454    "rpc.receive"   {  id = 2,  jsonrpc = "2.0"}
[DEBUG][2022-12-06 10:48:41] .../vim/lsp/rpc.lua:347    "rpc.send"  {  jsonrpc = "2.0",  method = "exit"}
[START][2022-12-06 10:48:43] LSP logging initiated
[INFO][2022-12-06 10:48:44] .../lua/vim/lsp.lua:1392    "exit_handler"  {}

Using the basic test file

library ieee;
    use ieee.std_logic_1164.all;
    use ieee.numeric_std.all;
    use ieee.std_logic_unsigned.all;

entity test is
    port (
        i_clk : in    std_logic;
        i_a   : in    std_logic;
        o_x   : out   std_logic
    );
end entity test;

architecture behavioral of test is

begin

    o_x_pro : process (i_clk) is
    begin

        if rising_edge(i_clk) then
            o_x <= i_a;
        end if;

    end process o_x_pro;

end architecture behavioral;

and the vhdl_ls.toml

[libraries]
work.files = [
    "./*.vhd"
]

As you can see in the last few lines of the log it sends the shutdown and exit commands, however vhdl_ls doesn't actually exit. This can be compared to the shutdown process of another language server (sumneko-lua-language-server)

[DEBUG][2022-12-06 10:55:14] .../vim/lsp/rpc.lua:347    "rpc.send"  {  id = 4,  jsonrpc = "2.0",  method = "shutdown"}
[DEBUG][2022-12-06 10:55:14] .../vim/lsp/rpc.lua:454    "rpc.receive"   {  id = 4,  jsonrpc = "2.0",  result = true}
[DEBUG][2022-12-06 10:55:14] .../vim/lsp/rpc.lua:347    "rpc.send"  {  jsonrpc = "2.0",  method = "exit"}
kraigher commented 1 year ago

Here is a good starting point https://github.com/VHDL-LS/rust_hdl/blob/master/vhdl_ls/src/stdio_server.rs#L171 if you want to make a PR

SethGower commented 1 year ago

Thanks, I found that. I'll take a look later and maybe dig into it. Main reason I made the issue is just for transparency to make sure it was known so the PR isn't unexpected

kraigher commented 1 year ago

I found that we had a masking of the Exit notification handling to return from the mainloop. But we also join the io threads there and I suspect they never exit. I now removed the mainloop return and forwarded the Exit notification to the server exit method which will call process::exit.

Could you try latest master and see if it is fixed?

LukasVik commented 1 year ago

I can confirm that this does not happen anymore in VSCode. I think issue can be closed.

kraigher commented 1 year ago

@LukasVik I will take your word for it.