From 0e0cc6d4aec83c0020858e11663f0d82e45287ba Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Mon, 22 Feb 2021 11:49:25 +0000 Subject: [PATCH] Fix restart when using CodeLLDB Fix restarts always getting stuck "Initializing" when using CodeLLDB. When using the restart command we re-use the configuration dict as-is, so always re-use the same TCP port for the lldb socket. Originally it was thought this was due to a race condition, having the port still open, but it's not. When doing a restart, or reset, we shut down the server after we get the response to the disconnect message. CodeLLDB then also sends a 'terminated' message. Previously we were forcefully closing the socket before killing the app, after we get the 'disconnect' response. This meant that the OS buffer for the socket to localhost: still contained the terminated message at the point that we force-closed the socket and killed the server. The result was that the firt messages read from the "new" socket to locahost: were the last messagse written by the previous process, trikcing vimspector into thinking that the server terminated the process (before responding to the initialize request). ANyway, the solution is to ensure that we read all messages from the previous instance before considering it done. This is done by killing the server if there is one *first* and then trying to read any messages from the socket until it closes (reads EOF). The tricky part is for when we didn't start the server (i.e. in a multi-session setup). Here we simply _have_ to close the socket because we can't know when we've received all of the messages, and we shouldn't expect to receive any 'terminated' events after 'disconnect'. --- autoload/vimspector/internal/channel.vim | 39 +++++++++++++++++++----- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/autoload/vimspector/internal/channel.vim b/autoload/vimspector/internal/channel.vim index b44436b..e033cff 100644 --- a/autoload/vimspector/internal/channel.vim +++ b/autoload/vimspector/internal/channel.vim @@ -95,21 +95,44 @@ EOF endfunction function! vimspector#internal#channel#StopDebugSession() abort - if exists( 's:ch' ) && ch_status( s:ch ) ==# 'open' + + if exists( 's:job' ) + " We started the job, so we need to kill it and wait to read all the data + " from the socket + + if job_status( s:job ) ==# 'run' + call job_stop( s:job, 'term' ) + endif + + while job_status( s:job ) ==# 'run' + call job_stop( s:job, 'kill' ) + endwhile + + unlet s:job + + if exists( 's:ch' ) && count( [ 'closed', 'fail' ], ch_status( s:ch ) ) == 0 + " We're going to block on this channel reading, then manually call the + " close callback, so remove the automatic close callback to avoid tricky + " re-entrancy + call ch_setoptions( s:ch, { 'close_cb': '' } ) + endif + + elseif exists( 's:ch' ) && + \ count( [ 'closed', 'fail' ], ch_status( s:ch ) ) == 0 + " channel is open, close it and trigger the callback. The callback is _not_ " triggered when manually calling ch_close. if we get here and the channel " is not open, then we there is a _OnClose callback waiting for us, so do " nothing. call ch_close( s:ch ) - call s:_OnClose( s:ch ) endif - if exists( 's:job' ) - if job_status( s:job ) ==# 'run' - call job_stop( s:job, 'kill' ) - endif - unlet s:job - endif + " block until we've read all data from the socket and handled it. + while count( [ 'open', 'buffered' ], ch_status( s:ch ) ) == 1 + let data = ch_read( s:ch, { 'timeout': 10 } ) + call s:_OnServerData( s:ch, data ) + endwhile + call s:_OnClose( s:ch ) endfunction function! vimspector#internal#channel#Reset() abort