# Buggy code
def _finalize(self, why, terminate_debuggee):
    # If the IDE started a session, and then disconnected before issuing "launch"
    # or "attach", the main thread will be blocked waiting for the first server
    # connection to come in - unblock it, so that we can exit.
    servers.dont_wait_for_first_connection()

    if self.server:
        if self.server.is_connected:
            if terminate_debuggee and self.launcher and self.launcher.is_connected:
                # If we were specifically asked to terminate the debuggee, and we
                # can ask the launcher to kill it, do so instead of disconnecting
                # from the server to prevent debuggee from running any more code.
                self.launcher.terminate_debuggee()
            else:
                # Otherwise, let the server handle it the best it can.
                try:
                    self.server.channel.request(
                        "disconnect", {"terminateDebuggee": terminate_debuggee}
                    )
                except Exception:
                    pass
        self.server.detach_from_session()

    if self.launcher and self.launcher.is_connected:
        # If there was a server, we just disconnected from it above, which should
        # cause the debuggee process to exit - so let's wait for that first.
        if self.server:
            log.info('{0} waiting for "exited" event...', self)
            if not self.wait_for(
                lambda: self.launcher.exit_code is not None, timeout=5
            ):
                log.warning('{0} timed out waiting for "exited" event.', self)

        # Terminate the debuggee process if it's still alive for any reason -
        # whether it's because there was no server to handle graceful shutdown,
        # or because the server couldn't handle it for some reason.
        self.launcher.terminate_debuggee()

        # Wait until the launcher message queue fully drains. There is no timeout
        # here, because the final "terminated" event will only come after reading
        # user input in wait-on-exit scenarios.
        log.info("{0} waiting for {1} to disconnect...", self, self.launcher)
        self.wait_for(lambda: not self.launcher.is_connected)

        try:
            self.launcher.channel.close()
        except Exception:
            log.exception()

    if self.ide:
        if self.ide.is_connected:
            # Tell the IDE that debugging is over, but don't close the channel until it
            # tells us to, via the "disconnect" request.
            try:
                self.ide.channel.send_event("terminated")
            except Exception:
                pass

        if (
            self.ide.start_request is not None
            and self.ide.start_request.command == "launch"
        ):
            servers.stop_listening()
            log.info('"launch" session ended - killing remaining debuggee processes.')

            pids_killed = set()
            if self.launcher and self.launcher.pid is not None:
                # Already killed above.
                pids_killed.add(self.launcher.pid)

            while True:
                conns = [
                    conn
                    for conn in servers.connections()
                    if conn.pid not in pids_killed
                ]
                if not len(conns):
                    break
                for conn in conns:
                    log.info("Killing {0}", conn)
                    try:
                        os.kill(conn.pid, signal.SIGTERM)
                    except Exception:
                        log.exception("Failed to kill {0}", conn)
                    pids_killed.add(conn.pid)


# Traceback (ConnectionRefusedError)
C:\Python37\python.exe c:\Users\kanadig\.vscode\extensions\ms-python.python-2020.2.60897-dev\pythonFiles\lib\python\new_ptvsd\wheels\ptvsd\launcher C:\GIT\issues\issue9214/test_mp.py
Could not connect to 127.0.0.1: 50734
Traceback (most recent call last):
File "c:\Users\kanadig\.vscode\extensions\ms-python.python-2020.2.60897-dev\pythonFiles\lib\python\new_ptvsd\wheels\ptvsd\_vendored\pydevd\_pydevd_bundle\pydevd_comm.py", line 514, in start_client
s.connect((host, port))
ConnectionRefusedError: [WinError 10061] No connection could be made because the target machine actively refused it
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "c:\Users\kanadig\.vscode\extensions\ms-python.python-2020.2.60897-dev\pythonFiles\lib\python\new_ptvsd\wheels\ptvsd\_vendored\pydevd\pydevd.py", line 2550, in settrace
client_access_token,
File "c:\Users\kanadig\.vscode\extensions\ms-python.python-2020.2.60897-dev\pythonFiles\lib\python\new_ptvsd\wheels\ptvsd\_vendored\pydevd\pydevd.py", line 2610, in _locked_settrace
py_db.connect(host, port)  # Note: connect can raise error.
File "c:\Users\kanadig\.vscode\extensions\ms-python.python-2020.2.60897-dev\pythonFiles\lib\python\new_ptvsd\wheels\ptvsd\_vendored\pydevd\pydevd.py", line 1179, in connect
s = start_client(host, port)
File "c:\Users\kanadig\.vscode\extensions\ms-python.python-2020.2.60897-dev\pythonFiles\lib\python\new_ptvsd\wheels\ptvsd\_vendored\pydevd\_pydevd_bundle\pydevd_comm.py", line 514, in start_client
s.connect((host, port))
ConnectionRefusedError: [WinError 10061] No connection could be made because the target machine actively refused it
Error in atexit._run_exitfuncs:
Traceback (most recent call last):
File "C:\Python37\lib\concurrent\futures\process.py", line 102, in _python_exit
thread_wakeup.wakeup()
File "C:\Python37\lib\concurrent\futures\process.py", line 90, in wakeup
self._writer.send_bytes(b"")
File "C:\Python37\lib\multiprocessing\connection.py", line 183, in send_bytes
self._check_closed()
File "C:\Python37\lib\multiprocessing\connection.py", line 136, in _check_closed
raise OSError("handle is closed")
OSError: handle is closed

# Fixed code
def _finalize(self, why, terminate_debuggee):
    # If the IDE started a session, and then disconnected before issuing "launch"
    # or "attach", the main thread will be blocked waiting for the first server
    # connection to come in - unblock it, so that we can exit.
    servers.dont_wait_for_first_connection()

    if self.server:
        if self.server.is_connected:
            if terminate_debuggee and self.launcher and self.launcher.is_connected:
                # If we were specifically asked to terminate the debuggee, and we
                # can ask the launcher to kill it, do so instead of disconnecting
                # from the server to prevent debuggee from running any more code.
                self.launcher.terminate_debuggee()
            else:
                # Otherwise, let the server handle it the best it can.
                try:
                    self.server.channel.request(
                        "disconnect", {"terminateDebuggee": terminate_debuggee}
                    )
                except Exception:
                    pass
        self.server.detach_from_session()

    if self.launcher and self.launcher.is_connected:
        # If there was a server, we just disconnected from it above, which should
        # cause the debuggee process to exit - so let's wait for that first.
        if self.server:
            log.info('{0} waiting for "exited" event...', self)
            if not self.wait_for(
                lambda: self.launcher.exit_code is not None, timeout=5
            ):
                log.warning('{0} timed out waiting for "exited" event.', self)

        # Terminate the debuggee process if it's still alive for any reason -
        # whether it's because there was no server to handle graceful shutdown,
        # or because the server couldn't handle it for some reason.
        self.launcher.terminate_debuggee()

        # Wait until the launcher message queue fully drains. There is no timeout
        # here, because the final "terminated" event will only come after reading
        # user input in wait-on-exit scenarios.
        log.info("{0} waiting for {1} to disconnect...", self, self.launcher)
        self.wait_for(lambda: not self.launcher.is_connected)

        try:
            self.launcher.channel.close()
        except Exception:
            log.exception()

    if self.ide:
        if self.ide.is_connected:
            # Tell the IDE that debugging is over, but don't close the channel until it
            # tells us to, via the "disconnect" request.
            try:
                self.ide.channel.send_event("terminated")
            except Exception:
                pass

        if (
            self.ide.start_request is not None
            and self.ide.start_request.command == "launch"
        ):
            servers.stop_serving()
            log.info('"launch" session ended - killing remaining debuggee processes.')

            pids_killed = set()
            if self.launcher and self.launcher.pid is not None:
                # Already killed above.
                pids_killed.add(self.launcher.pid)

            while True:
                conns = [
                    conn
                    for conn in servers.connections()
                    if conn.pid not in pids_killed
                ]
                if not len(conns):
                    break
                for conn in conns:
                    log.info("Killing {0}", conn)
                    try:
                        os.kill(conn.pid, signal.SIGTERM)
                    except Exception:
                        log.exception("Failed to kill {0}", conn)
                    pids_killed.add(conn.pid)