diff --git a/ptvsd/daemon.py b/ptvsd/daemon.py index b142d90a..abb90102 100644 --- a/ptvsd/daemon.py +++ b/ptvsd/daemon.py @@ -5,7 +5,9 @@ import threading from ptvsd import wrapper from ptvsd.socket import ( close_socket, create_server, create_client, connect, Address) -from .exit_handlers import ExitHandlers, UnsupportedSignalError +from .exit_handlers import ( + ExitHandlers, UnsupportedSignalError, + kill_current_proc) from .session import PyDevdDebugSession from ._util import ( ClosedError, NotRunningError, ignore_errors, debug, lock_wait) @@ -82,7 +84,7 @@ class DaemonBase(object): self._wait_for_user = wait_for_user self._killonclose = killonclose - self._exiting = False + self._exiting_via_atexit_handler = False self._exithandlers = ExitHandlers() if addhandlers: @@ -282,29 +284,42 @@ class DaemonBase(object): with ignore_errors(): self._stop() - def _kill_after_single_session(self): - if self._killonclose: - if not self._exiting: - # Ensure the proc is exiting before closing socket. - # Note that this will trigger the atexit handler. - self._exiting = True - sys.exit(0) - else: - try: - self.close() - except DaemonClosedError: - pass - def _handle_session_disconnecting(self, session): debug('handling disconnecting session') if self._singlesession: - self._kill_after_single_session() + if self._killonclose: + with self._lock: + if not self._exiting_via_atexit_handler: + # Ensure the proc is exiting before closing + # socket. Note that we kill the proc instead + # of calling sys.exit(0). + # Note that this will trigger either the atexit + # handler or the signal handler. + kill_current_proc() + else: + try: + self.close() + except DaemonClosedError: + pass def _handle_session_closing(self, session): debug('handling closing session') if self._singlesession: - self._kill_after_single_session() + if self._killonclose: + with self._lock: + if not self._exiting_via_atexit_handler: + # Ensure the proc is exiting before closing + # socket. Note that we kill the proc instead + # of calling sys.exit(0). + # Note that this will trigger either the atexit + # handler or the signal handler. + kill_current_proc() + else: + try: + self.close() + except DaemonClosedError: + pass else: self._finish_session() @@ -397,7 +412,8 @@ class DaemonBase(object): def _handle_atexit(self): debug('handling atexit') - self._exiting = True + with self._lock: + self._exiting_via_atexit_handler = True session = self.session if session is not None: @@ -428,8 +444,7 @@ class DaemonBase(object): self.close() except DaemonClosedError: pass - if not self._exiting: - self._exiting = True + if not self._exiting_via_atexit_handler: sys.exit(0) # methods for subclasses to override diff --git a/ptvsd/wrapper.py b/ptvsd/wrapper.py index aaa2e159..a8008058 100644 --- a/ptvsd/wrapper.py +++ b/ptvsd/wrapper.py @@ -39,7 +39,6 @@ from ptvsd import _util import ptvsd.ipcjson as ipcjson # noqa import ptvsd.futures as futures # noqa import ptvsd.untangle as untangle # noqa -from ptvsd.exit_handlers import kill_current_proc from ptvsd.pathutils import PathUnNormcase # noqa from ptvsd.safe_repr import SafeRepr # noqa from ptvsd.version import __version__ # noqa @@ -1008,9 +1007,6 @@ class VSCLifecycleMsgProcessor(VSCodeMessageProcessorBase): self._stopped = False - # This is overridden in tests. - self._kill_current_proc = kill_current_proc - # adapter state self.start_reason = None self.debug_options = {} @@ -1108,30 +1104,28 @@ class VSCLifecycleMsgProcessor(VSCodeMessageProcessorBase): status = {'sent': False} - def finish_disconnect(): + def disconnect_response(): if status['sent']: return self.send_response(request) status['sent'] = True - if self.start_reason == 'launch': - if not self.closed: - # Closing the socket causes pydevd to resume all - # threads, so just terminate the process altogether. - # TODO: The wrapper should not be responsible for - # managing the process (e.g. killing it). - self._kill_current_proc() - - self._set_disconnected() - if self.start_reason == 'attach': - if not self._debuggerstopped: - self._handle_detach() self._notify_disconnecting( - pre_socket_close=finish_disconnect, + pre_socket_close=disconnect_response, ) - # The callback might never get called in notify_disconnecting, - # so we call it again here to make sure it is called. - finish_disconnect() + disconnect_response() + + self._set_disconnected() + + if self.start_reason == 'attach': + if not self._debuggerstopped: + self._handle_detach() + # TODO: We should be able drop the "launch" branch. + elif self.start_reason == 'launch': + if not self.closed: + # Closing the socket causes pydevd to resume all threads, + # so just terminate the process altogether. + sys.exit(0) # internal methods diff --git a/tests/helpers/pydevd/_binder.py b/tests/helpers/pydevd/_binder.py index 58ce78c2..5116e2a6 100644 --- a/tests/helpers/pydevd/_binder.py +++ b/tests/helpers/pydevd/_binder.py @@ -30,8 +30,7 @@ class PTVSD(ptvsd.daemon.Daemon): singlesession=singlesession, ) self.start() - session = self.start_session(client, 'ptvsd.Server') - session._msgprocessor._kill_current_proc = (lambda: None) + self.start_session(client, 'ptvsd.Server') self.server = server return self