Send the disconnect response before closing the client socket. (#531)

(see #530)

We were sending the "disconnect" response only after closing the socket, meaning the response was never actually sent. This PR fixes that by ensuring the response is sent as late as possible, whether or not it is a "single session" situation.
This commit is contained in:
Eric Snow 2018-06-27 14:25:48 -06:00 committed by GitHub
parent 523d9c2def
commit 4476d0da6f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 30 additions and 6 deletions

View file

@ -47,12 +47,16 @@ class DebugSession(Startable, Closeable):
self._notify_disconnecting = notify_disconnecting
self._sock = sock
self._pre_socket_close = None
if ownsock:
# Close the socket *after* calling sys.exit() (via notify_closing).
def handle_closing(before):
if before:
return
debug('closing session socket')
proc = self._msgprocessor
if self._pre_socket_close is not None:
self._pre_socket_close()
if proc is not None:
try:
proc.wait_while_connected(10) # seconds
@ -134,8 +138,9 @@ class DebugSession(Startable, Closeable):
# internal methods for VSCodeMessageProcessor
def _handle_vsc_disconnect(self):
def _handle_vsc_disconnect(self, pre_socket_close=None):
debug('disconnecting')
self._pre_socket_close = pre_socket_close # TODO: Fail if already set?
self._notify_disconnecting(self)
def _handle_vsc_close(self):

View file

@ -811,6 +811,7 @@ class VSCodeMessageProcessorBase(ipcjson.SocketIO, ipcjson.IpcChannel):
self._notify_closing = notify_closing
self.server_thread = None
self._closing = False
self._closed = False
self.readylock = threading.Lock()
self.readylock.acquire() # Unlock at the end of start().
@ -824,6 +825,10 @@ class VSCodeMessageProcessorBase(ipcjson.SocketIO, ipcjson.IpcChannel):
with self._connlock:
return _util.is_locked(self._connected)
@property
def closed(self):
return self._closed or self._closing
@property
def listening(self):
# TODO: must be disconnected?
@ -882,9 +887,9 @@ class VSCodeMessageProcessorBase(ipcjson.SocketIO, ipcjson.IpcChannel):
def close(self):
"""Stop the message processor and release its resources."""
if self._closed:
if self.closed:
return
self._closed = True
self._closing = True
debug('raw closing')
self._notify_closing()
@ -895,6 +900,7 @@ class VSCodeMessageProcessorBase(ipcjson.SocketIO, ipcjson.IpcChannel):
with self._connlock:
_util.lock_release(self._listening)
_util.lock_release(self._connected)
self._closed = True
# VSC protocol handlers
@ -1080,8 +1086,20 @@ class VSCLifecycleMsgProcessor(VSCodeMessageProcessorBase):
# TODO: docstring
if self._debuggerstopped: # A "terminated" event must have been sent.
self._wait_until_exiting(self.EXITWAIT)
self._notify_disconnecting()
self.send_response(request)
status = {'sent': False}
def disconnect_response():
if status['sent']:
return
self.send_response(request)
status['sent'] = True
self._notify_disconnecting(
pre_socket_close=disconnect_response,
)
disconnect_response()
self._set_disconnected()
if self.start_reason == 'attach':
@ -1089,7 +1107,7 @@ class VSCLifecycleMsgProcessor(VSCodeMessageProcessorBase):
self._handle_detach()
# TODO: We should be able drop the "launch" branch.
elif self.start_reason == 'launch':
if not self._closed:
if not self.closed:
# Closing the socket causes pydevd to resume all threads,
# so just terminate the process altogether.
sys.exit(0)

View file

@ -512,6 +512,7 @@ class LifecycleTests(TestsBase, unittest.TestCase):
done2, waitscript2 = lockfile2.wait_in_script(timeout=5)
filename = self.write_script('spam.py', waitscript1 + waitscript2)
addr = Address('localhost', 8888)
#DebugAdapter.VERBOSE = True
with DebugAdapter.start_for_attach(addr, filename) as adapter:
with DebugClient() as editor:
# Attach initially.