For single-threaded languages such as JavaScript, it might be a situation when a long-running operation blocks the entire application. For example, some code calculates a value using a complex algorithm that significantly incurs CPU and memory usage. Or a controller waits for a response from a query that runs against a huge database without properly configured indexes. You might want to collect metrics, analyze data structures, parse files, etc and do all of this not in the main thread but in the background process to avoid performance costs and ensure the stability of the application.

In JavaScript, each task in the event loop should be processed completely before any other task can be processed next. On the one hand, it simplifies the program without worrying about concurrency issues. On the other hand, it blocks the execution of any other JavaScript code, even the UI becomes unresponsive, and the user can’t interact with the application anymore.

To prevent this bottleneck we can move the CPU-intensive computations out of JavaScript’s event loop to separate worker threads that run tasks in parallel and use all available CPU cores automatically.

Background processes in VSCode

Currently, I am working on the VSCode extension that uses a text editor as a playground for executing JavaScript code with MongoDB Shell API support.

Image for post

The challenge is that a user of the extension can write any code in the editor, execution of which can lead to unexpected results. We don’t want to force the user to reopen the VSCode workspace each time they accidentally built an infinite loop. At the same time, we want to allow users to write any possible code because making mistakes is a natural part of the learning process. So even the buggy code has the right to exist in the playground.

To solve this problem we decided to take advantage of the language server that we added to our extension to support MongoDB Shell signatures and autocompletion. The VSCode language server follows the Microsoft LSP specification and can run in its own process to avoid performance issues, and communicate with the code editor through the language server protocol.

The language server protocol (LSP) is mainly used as a tool between the editor (the client) and a language smartness provider (the server) to integrate features like autocomplete, go to definition, find all references, list document symbols, signature help and much more. You can find the complete list of supported features in the official documentation.

It is possible to implement most of these features by directly using VSCode language API. The language server, however, provides an alternative way of implementing such language support, which gives you more flexibility, more configuration options, and the possibility to share the code with other applications or even external developer tools such as Sublime Text, JetBrains, Atom, etc.

Language features can be resource-consuming. Validation and autocompletion processes require parsing of the user input, building, and traversing abstract syntax trees. Since the language server can perform all these actions in a background process, the VSCode’s performance remains unaffected.

We can also extend the language server and client with custom methods to leverage it as a background worker. Because the language server is a separate JSON RPC enabled process, it provides an option to handle any processing of the main UI thread.

But what will happen if one of delegated to the language server assignments includes an infinite loop? The language server design does not allow interaction with the server directly. All the requests to the language server should be passed by the language client. In this case, if the server is unresponsive because of handling a never-ending operation, there is nothing that the client can do to interrupt this process.

Unfortunately, the language server API does not support creating child processes, but we can use native node modules instead, and delegate this task to worker threads.

In this blog post, we will:

  • generate a simple VSCode extension;
  • configure the language server with an example of the autocomplete feature;
  • add a long-running operation with the possibility to cancel it;
  • explain why the language server can’t cancel an infinite loop;
  • use worker threads to be able to terminate infinite loops.

#lsp #mongodb #vscode

The language server with child threads
1.05 GEEK