Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
skywind3000 opened this issue Sep 12, 2023 · 2 comments

Comments

@skywind3000
Copy link

skywind3000 commented Sep 12, 2023

I am reading the vim rpc implementation:

function! s:request(method, args) dict
let channel = coc#client#get_channel(self)
if empty(channel) | return '' | endif
try
if s:is_vim
let res = ch_evalexpr(channel, [a:method, a:args], {'timeout': 60 * 1000})
if type(res) == 1 && res ==# ''
throw 'request '.a:method. ' '.string(a:args).' timeout after 60s'

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
Copy link
Member

chemzqm commented Sep 13, 2023

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
Copy link
Author

skywind3000 commented Sep 13, 2023

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants