Merge branch 'timeout'
This commit is contained in:
commit
4a3afd6c2c
5 changed files with 119 additions and 34 deletions
|
|
@ -42,6 +42,12 @@ function! s:_Send( msg ) abort
|
||||||
call ch_sendraw( s:ch, a:msg )
|
call ch_sendraw( s:ch, a:msg )
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#internal#channel#Timeout( id ) abort
|
||||||
|
py3 << EOF
|
||||||
|
_vimspector_session.OnRequestTimeout( vim.eval( 'a:id' ) )
|
||||||
|
EOF
|
||||||
|
endfunction
|
||||||
|
|
||||||
function! vimspector#internal#channel#StartDebugSession( config ) abort
|
function! vimspector#internal#channel#StartDebugSession( config ) abort
|
||||||
|
|
||||||
if exists( 's:ch' )
|
if exists( 's:ch' )
|
||||||
|
|
|
||||||
|
|
@ -39,8 +39,13 @@ function! s:_OnClose( channel ) abort
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:_Send( msg ) abort
|
function! s:_Send( msg ) abort
|
||||||
|
if ! exists( 's:job' )
|
||||||
|
echom "Can't send message: Job was not initialised correctly"
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
if job_status( s:job ) != 'run'
|
if job_status( s:job ) != 'run'
|
||||||
echom "Server isnt running"
|
echom "Can't send message: Job is not running"
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
@ -55,7 +60,7 @@ endfunction
|
||||||
|
|
||||||
function! vimspector#internal#job#StartDebugSession( config ) abort
|
function! vimspector#internal#job#StartDebugSession( config ) abort
|
||||||
if exists( 's:job' )
|
if exists( 's:job' )
|
||||||
echo "Job is already running"
|
echom "Not starging: Job is already running"
|
||||||
return v:none
|
return v:none
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
@ -72,8 +77,10 @@ function! vimspector#internal#job#StartDebugSession( config ) abort
|
||||||
\ }
|
\ }
|
||||||
\ )
|
\ )
|
||||||
|
|
||||||
|
echom 'Started job, status is: ' . job_status( s:job )
|
||||||
|
|
||||||
if job_status( s:job ) != 'run'
|
if job_status( s:job ) != 'run'
|
||||||
echom 'Fail whale. Job is ' . job_status( s:job )
|
echom 'Unable to start job, status is: ' . job_status( s:job )
|
||||||
return v:none
|
return v:none
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
@ -81,6 +88,11 @@ function! vimspector#internal#job#StartDebugSession( config ) abort
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! vimspector#internal#job#StopDebugSession() abort
|
function! vimspector#internal#job#StopDebugSession() abort
|
||||||
|
if !exists( 's:job' )
|
||||||
|
echom "Not stopping session: Job doesn't exist"
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
if job_status( s:job ) == 'run'
|
if job_status( s:job ) == 'run'
|
||||||
call job_stop( s:job, 'term' )
|
call job_stop( s:job, 'term' )
|
||||||
endif
|
endif
|
||||||
|
|
@ -89,9 +101,7 @@ function! vimspector#internal#job#StopDebugSession() abort
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! vimspector#internal#job#Reset() abort
|
function! vimspector#internal#job#Reset() abort
|
||||||
if exists( 's:job' )
|
|
||||||
call vimspector#internal#job#StopDebugSession()
|
call vimspector#internal#job#StopDebugSession()
|
||||||
endif
|
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! vimspector#internal#job#ForceRead() abort
|
function! vimspector#internal#job#ForceRead() abort
|
||||||
|
|
|
||||||
|
|
@ -15,13 +15,17 @@
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import json
|
import json
|
||||||
|
import vim
|
||||||
from collections import namedtuple
|
|
||||||
|
|
||||||
from vimspector import utils
|
from vimspector import utils
|
||||||
|
|
||||||
PendingRequest = namedtuple( 'PendingRequest',
|
|
||||||
[ 'msg', 'handler', 'failure_handler' ] )
|
class PendingRequest( object ):
|
||||||
|
def __init__( self, msg, handler, failure_handler, expiry_id ):
|
||||||
|
self.msg = msg
|
||||||
|
self.handler = handler
|
||||||
|
self.failure_handler = failure_handler
|
||||||
|
self.expiry_id = expiry_id
|
||||||
|
|
||||||
|
|
||||||
class DebugAdapterConnection( object ):
|
class DebugAdapterConnection( object ):
|
||||||
|
|
@ -36,18 +40,40 @@ class DebugAdapterConnection( object ):
|
||||||
self._next_message_id = 0
|
self._next_message_id = 0
|
||||||
self._outstanding_requests = {}
|
self._outstanding_requests = {}
|
||||||
|
|
||||||
def DoRequest( self, handler, msg, failure_handler=None ):
|
def DoRequest( self,
|
||||||
|
handler,
|
||||||
|
msg,
|
||||||
|
failure_handler=None,
|
||||||
|
timeout = 15000 ):
|
||||||
this_id = self._next_message_id
|
this_id = self._next_message_id
|
||||||
self._next_message_id += 1
|
self._next_message_id += 1
|
||||||
|
|
||||||
msg[ 'seq' ] = this_id
|
msg[ 'seq' ] = this_id
|
||||||
msg[ 'type' ] = 'request'
|
msg[ 'type' ] = 'request'
|
||||||
|
|
||||||
|
# TODO/FIXME: This is so messy
|
||||||
|
expiry_id = vim.eval(
|
||||||
|
'timer_start( {}, "vimspector#internal#channel#Timeout" )'.format(
|
||||||
|
timeout ) )
|
||||||
|
|
||||||
self._outstanding_requests[ this_id ] = PendingRequest( msg,
|
self._outstanding_requests[ this_id ] = PendingRequest( msg,
|
||||||
handler,
|
handler,
|
||||||
failure_handler )
|
failure_handler,
|
||||||
|
expiry_id )
|
||||||
self._SendMessage( msg )
|
self._SendMessage( msg )
|
||||||
|
|
||||||
|
def OnRequestTimeout( self, timer_id ):
|
||||||
|
request_id = None
|
||||||
|
for seq, request in self._outstanding_requests.items():
|
||||||
|
if request.expiry_id == timer_id:
|
||||||
|
request_id = seq
|
||||||
|
break
|
||||||
|
|
||||||
|
# Avoid modifying _outstanding_requests while looping
|
||||||
|
if request_id is not None:
|
||||||
|
request = self._outstanding_requests.pop( request_id )
|
||||||
|
self._AbortRequest( request, 'Timeout' )
|
||||||
|
|
||||||
def DoResponse( self, request, error, response ):
|
def DoResponse( self, request, error, response ):
|
||||||
this_id = self._next_message_id
|
this_id = self._next_message_id
|
||||||
self._next_message_id += 1
|
self._next_message_id += 1
|
||||||
|
|
@ -70,6 +96,22 @@ class DebugAdapterConnection( object ):
|
||||||
self._Write = None
|
self._Write = None
|
||||||
self._handler = None
|
self._handler = None
|
||||||
|
|
||||||
|
while self._outstanding_requests:
|
||||||
|
_, request = self._outstanding_requests.popitem()
|
||||||
|
self._AbortRequest( request, 'Closing down' )
|
||||||
|
|
||||||
|
def _AbortRequest( self, request, reason ):
|
||||||
|
self._logger.debug( '{}: Aborting request {}'.format( reason,
|
||||||
|
request.msg ) )
|
||||||
|
_KillTimer( request )
|
||||||
|
if request.failure_handler:
|
||||||
|
request.failure_handler( reason, {} )
|
||||||
|
else:
|
||||||
|
utils.UserMessage( 'Request for {} aborted: {}'.format(
|
||||||
|
request.msg[ 'command' ],
|
||||||
|
reason ) )
|
||||||
|
|
||||||
|
|
||||||
def OnData( self, data ):
|
def OnData( self, data ):
|
||||||
data = bytes( data, 'utf-8' )
|
data = bytes( data, 'utf-8' )
|
||||||
# self._logger.debug( 'Received ({0}/{1}): {2},'.format( type( data ),
|
# self._logger.debug( 'Received ({0}/{1}): {2},'.format( type( data ),
|
||||||
|
|
@ -110,6 +152,11 @@ class DebugAdapterConnection( object ):
|
||||||
if len( parts ) > 1:
|
if len( parts ) > 1:
|
||||||
headers = parts[ 0 ]
|
headers = parts[ 0 ]
|
||||||
for header_line in headers.split( bytes( '\r\n', 'utf-8' ) ):
|
for header_line in headers.split( bytes( '\r\n', 'utf-8' ) ):
|
||||||
|
if bytes( '\n', 'utf-8' ) in header_line:
|
||||||
|
# Work around bugs in cppdbg where mono spams nonesense to stdout.
|
||||||
|
# This is such a dodgyhack, but it fixes the issues.
|
||||||
|
header_line = header_line.split( bytes( '\n', 'utf-8' ) )[ -1 ]
|
||||||
|
|
||||||
if header_line.strip():
|
if header_line.strip():
|
||||||
key, value = str( header_line, 'utf-8' ).split( ':', 1 )
|
key, value = str( header_line, 'utf-8' ).split( ':', 1 )
|
||||||
self._headers[ key ] = value
|
self._headers[ key ] = value
|
||||||
|
|
@ -130,6 +177,7 @@ class DebugAdapterConnection( object ):
|
||||||
# Skip to reading headers. Because, what else can we do.
|
# Skip to reading headers. Because, what else can we do.
|
||||||
self._logger.error( 'Missing Content-Length header in: {0}'.format(
|
self._logger.error( 'Missing Content-Length header in: {0}'.format(
|
||||||
json.dumps( self._headers ) ) )
|
json.dumps( self._headers ) ) )
|
||||||
|
|
||||||
self._buffer = bytes( '', 'utf-8' )
|
self._buffer = bytes( '', 'utf-8' )
|
||||||
self._SetState( 'READ_HEADER' )
|
self._SetState( 'READ_HEADER' )
|
||||||
return
|
return
|
||||||
|
|
@ -170,6 +218,8 @@ class DebugAdapterConnection( object ):
|
||||||
self._logger.exception( 'Duplicate response: {}'.format( message ) )
|
self._logger.exception( 'Duplicate response: {}'.format( message ) )
|
||||||
return
|
return
|
||||||
|
|
||||||
|
_KillTimer( request )
|
||||||
|
|
||||||
if message[ 'success' ]:
|
if message[ 'success' ]:
|
||||||
if request.handler:
|
if request.handler:
|
||||||
request.handler( message )
|
request.handler( message )
|
||||||
|
|
@ -181,7 +231,7 @@ class DebugAdapterConnection( object ):
|
||||||
# TODO: Actually make this work
|
# TODO: Actually make this work
|
||||||
reason = fmt
|
reason = fmt
|
||||||
else:
|
else:
|
||||||
message = 'No reason'
|
reason = 'No reason'
|
||||||
|
|
||||||
self._logger.error( 'Request failed: {0}'.format( reason ) )
|
self._logger.error( 'Request failed: {0}'.format( reason ) )
|
||||||
if request.failure_handler:
|
if request.failure_handler:
|
||||||
|
|
@ -203,3 +253,9 @@ class DebugAdapterConnection( object ):
|
||||||
utils.UserMessage(
|
utils.UserMessage(
|
||||||
'Unhandled request: {0}'.format( message[ 'command' ] ),
|
'Unhandled request: {0}'.format( message[ 'command' ] ),
|
||||||
persist = True )
|
persist = True )
|
||||||
|
|
||||||
|
|
||||||
|
def _KillTimer( request ):
|
||||||
|
if request.expiry_id is not None:
|
||||||
|
vim.eval( 'timer_stop( {} )'.format( request.expiry_id ) )
|
||||||
|
request.expiry_id = None
|
||||||
|
|
|
||||||
|
|
@ -189,6 +189,10 @@ class DebugSession( object ):
|
||||||
if self._connection:
|
if self._connection:
|
||||||
self._connection.OnData( data )
|
self._connection.OnData( data )
|
||||||
|
|
||||||
|
def OnRequestTimeout( self, timer_id ):
|
||||||
|
if self._connection:
|
||||||
|
self._connection.OnRequestTimeout( timer_id )
|
||||||
|
|
||||||
def OnChannelClosed( self ):
|
def OnChannelClosed( self ):
|
||||||
self._connection = None
|
self._connection = None
|
||||||
|
|
||||||
|
|
@ -384,7 +388,7 @@ class DebugSession( object ):
|
||||||
# scope)
|
# scope)
|
||||||
state = { 'done': False }
|
state = { 'done': False }
|
||||||
|
|
||||||
def handler( self ):
|
def handler( *args ):
|
||||||
state[ 'done' ] = True
|
state[ 'done' ] = True
|
||||||
|
|
||||||
self._connection.DoRequest( handler, {
|
self._connection.DoRequest( handler, {
|
||||||
|
|
@ -392,11 +396,10 @@ class DebugSession( object ):
|
||||||
'arguments': {
|
'arguments': {
|
||||||
'terminateDebugee': True
|
'terminateDebugee': True
|
||||||
},
|
},
|
||||||
} )
|
}, failure_handler = handler, timeout = 5000 )
|
||||||
|
|
||||||
tries = 0
|
# This request times out after 5 seconds
|
||||||
while not state[ 'done' ] and tries < 10:
|
while not state[ 'done' ]:
|
||||||
tries = tries + 1
|
|
||||||
vim.eval( 'vimspector#internal#{}#ForceRead()'.format(
|
vim.eval( 'vimspector#internal#{}#ForceRead()'.format(
|
||||||
self._connection_type ) )
|
self._connection_type ) )
|
||||||
|
|
||||||
|
|
@ -404,7 +407,7 @@ class DebugSession( object ):
|
||||||
self._connection_type ) )
|
self._connection_type ) )
|
||||||
|
|
||||||
def _StopDebugAdapter( self, callback = None ):
|
def _StopDebugAdapter( self, callback = None ):
|
||||||
def handler( message ):
|
def handler( *args ):
|
||||||
vim.eval( 'vimspector#internal#{}#StopDebugSession()'.format(
|
vim.eval( 'vimspector#internal#{}#StopDebugSession()'.format(
|
||||||
self._connection_type ) )
|
self._connection_type ) )
|
||||||
|
|
||||||
|
|
@ -423,7 +426,7 @@ class DebugSession( object ):
|
||||||
'arguments': {
|
'arguments': {
|
||||||
'terminateDebugee': True
|
'terminateDebugee': True
|
||||||
},
|
},
|
||||||
} )
|
}, failure_handler = handler, timeout = 5000 )
|
||||||
|
|
||||||
def _SelectProcess( self, adapter_config, launch_config ):
|
def _SelectProcess( self, adapter_config, launch_config ):
|
||||||
atttach_config = adapter_config[ 'attach' ]
|
atttach_config = adapter_config[ 'attach' ]
|
||||||
|
|
|
||||||
|
|
@ -202,6 +202,7 @@ def AskForInput( prompt ):
|
||||||
|
|
||||||
|
|
||||||
def AppendToBuffer( buf, line_or_lines, modified=False ):
|
def AppendToBuffer( buf, line_or_lines, modified=False ):
|
||||||
|
try:
|
||||||
# After clearing the buffer (using buf[:] = None) there is always a single
|
# After clearing the buffer (using buf[:] = None) there is always a single
|
||||||
# empty line in the buffer object and no "is empty" method.
|
# empty line in the buffer object and no "is empty" method.
|
||||||
if len( buf ) > 1 or buf[ 0 ]:
|
if len( buf ) > 1 or buf[ 0 ]:
|
||||||
|
|
@ -213,7 +214,15 @@ def AppendToBuffer( buf, line_or_lines, modified=False ):
|
||||||
else:
|
else:
|
||||||
line = 1
|
line = 1
|
||||||
buf[:] = line_or_lines
|
buf[:] = line_or_lines
|
||||||
|
except vim.error as e:
|
||||||
|
# There seem to be a lot of Vim bugs that lead to E351, whose help says that
|
||||||
|
# this is an internal error. Ignore the error, but write a trace to the log.
|
||||||
|
if 'E315' in str( e ):
|
||||||
|
logging.getLogger( __name__ ).exception(
|
||||||
|
'Internal error while updating buffer' )
|
||||||
|
else:
|
||||||
|
raise e
|
||||||
|
finally:
|
||||||
if not modified:
|
if not modified:
|
||||||
buf.options[ 'modified' ] = False
|
buf.options[ 'modified' ] = False
|
||||||
|
|
||||||
|
|
@ -221,6 +230,7 @@ def AppendToBuffer( buf, line_or_lines, modified=False ):
|
||||||
return line
|
return line
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def ClearBuffer( buf ):
|
def ClearBuffer( buf ):
|
||||||
buf[:] = None
|
buf[:] = None
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue