neoclide / coc.nvim

Nodejs extension host for vim & neovim, load extensions like VSCode and host language servers.
Other
24.33k stars 956 forks source link

What will happen if both client and server starts a rpc-request at the same time ? #4756

Open skywind3000 opened 1 year ago

skywind3000 commented 1 year ago

I am reading the vim rpc implementation:

https://github.com/neoclide/coc.nvim/blob/eb63f778d09c37095352fb47f75e592d3d2d0192/autoload/coc/client.vim#L175-L182

The function ch_evalexpr will send a request to the server and wait for a response. However, the TCP socket is a bi-directional channel. If the server sends a request or a notification at the same time, ch_evalexpr will not receive the correct response but instead a new request/notification from the server.

And the code using coc#rpc#request(method, args) will get an unexpected return value.

This is likely to happen when the server and client are sending notifications/requests to each other frequently or when the server is busy.

Another potential issue may occur due to nested RPC calls from both sides.

1) client is requesting a server function 2) in that server function, it will request another client-side function. 3) the client is still waiting for the response from the server-side, but it will only receive a new rpc-request.

Sometimes, CoC becomes unresponsive in Vim on some low-end Windows machines, particularly within the first 5 seconds after opening a new file. If I input too much, CoC may freeze my gVim.

Is it possible that it is caused by this problem ?

chemzqm commented 1 year ago

If the server sends a request or a notification at the same time, ch_evalexpr will not receive the correct response but instead a new request/notification from the server.

The request response and notification from server side have different ID number with JSON encoded text, so vim should be able to distinguish them correctly.

1) client is requesting a server function 2) in that server function, it will request another client-side function. 3) the client is still waiting for the response from the server-side, but it will only receive a new rpc-request.

It could work most of the time, unless the client-side function sends a request to the server again. The request would block vim, so it should be avoided as much as possible, there is async request function coc#rpc#request_async which use notification.

particularly within the first 5 seconds after opening a new file. If I input too much, CoC may freeze my gVim.

Avoid invoke coc#rpc#request during user input.

skywind3000 commented 1 year ago

The request response and notification from server side have different ID number with JSON encoded text, so vim should be able to distinguish them correctly.

Unfortunately, vim can't handle request ID correctly in ch_evalexpr, it just wait an matched ID from the server side and will buffer any message before it.

step1: f_ch_evalexpr()

https://github.com/vim/vim/blob/6ffcc58be32aa1b337bc839cfe173b68cfde7085/src/channel.c#L5210-L5213

step2: ch_expr_common() -> channel_read_json_block():

https://github.com/vim/vim/blob/6ffcc58be32aa1b337bc839cfe173b68cfde7085/src/channel.c#L4546-L4550

In the channel_get_json() function, I found It will buffer the current message and wait for the next one when it meets an unexpected ID:

https://github.com/vim/vim/blob/6ffcc58be32aa1b337bc839cfe173b68cfde7085/src/channel.c#L2478-L2495

There may be an "order problem":

1) client sends an request with id = 100; 2) at the same time, server sends an request with id = 200; 3) client is expecting the response of the msg 100, but it will never receive it, because server is waiting for the response of 200.

The server would have no chance to handle msg 100 because it is still waiting for the response of 200, while the client is waiting for the response of 100 and can’t do anything about msg 200.