Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PR: Wait on all shutdown threads (IPython console) #20562

Merged
merged 3 commits into from
Feb 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions spyder/app/tests/test_mainwindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,6 @@ def test_leaks(main_window, qtbot):

Many other ways of leaking exist but are not covered here.
"""
def wait_all_shutdown():
objects = gc.get_objects()
for o in objects:
if isinstance(o, KernelHandler):
o.wait_shutdown_thread()

def ns_fun(main_window, qtbot):
# Wait until the window is fully up
Expand All @@ -148,7 +143,7 @@ def ns_fun(main_window, qtbot):
# Count initial objects
# Only one of each should be present, but because of many leaks,
# this is most likely not the case. Here only closing is tested
wait_all_shutdown()
KernelHandler.wait_all_shutdown_threads()
gc.collect()
objects = gc.get_objects()
n_code_editor_init = 0
Expand Down Expand Up @@ -183,7 +178,7 @@ def ns_fun(main_window, qtbot):
main_window.ipyconsole.restart()

# Wait until the shells are closed
wait_all_shutdown()
KernelHandler.wait_all_shutdown_threads()
return n_shell_init, n_code_editor_init

n_shell_init, n_code_editor_init = ns_fun(main_window, qtbot)
Expand Down
37 changes: 22 additions & 15 deletions spyder/plugins/ipythonconsole/utils/kernel_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,14 @@ class KernelHandler(QObject):
The kernel raised an error while connecting.
"""

_shutdown_thread_list = []
"""List of running shutdown threads"""

_shutdown_thread_list_lock = Lock()
"""
Lock to add threads to _shutdown_thread_list or clear that list.
"""

def __init__(
self,
connection_file,
Expand Down Expand Up @@ -165,7 +173,6 @@ def __init__(
self.handle_comm_ready)

# Internal
self._shutdown_thread = None
self._shutdown_lock = Lock()
self._stdout_thread = None
self._stderr_thread = None
Expand Down Expand Up @@ -489,7 +496,6 @@ def init_kernel_client(cls, connection_file, hostname, sshkey, password):
def close(self, shutdown_kernel=True, now=False):
"""Close kernel"""
self.close_comm()

if shutdown_kernel and self.kernel_manager is not None:
km = self.kernel_manager
km.stop_restarter()
Expand All @@ -503,7 +509,8 @@ def close(self, shutdown_kernel=True, now=False):
shutdown_thread.run = self._thread_shutdown_kernel
shutdown_thread.start()
shutdown_thread.finished.connect(self.after_shutdown)
self._shutdown_thread = shutdown_thread
with self._shutdown_thread_list_lock:
self._shutdown_thread_list.append(shutdown_thread)

if (
self.kernel_client is not None
Expand All @@ -515,7 +522,6 @@ def after_shutdown(self):
"""Cleanup after shutdown"""
self.close_std_threads()
self.kernel_comm.remove(only_closing=True)
self._shutdown_thread = None

def _thread_shutdown_kernel(self):
"""Shutdown kernel."""
Expand All @@ -531,18 +537,19 @@ def _thread_shutdown_kernel(self):
# kernel was externally killed
pass

def wait_shutdown_thread(self):
@classmethod
def wait_all_shutdown_threads(cls):
"""Wait shutdown thread."""
thread = self._shutdown_thread
if thread is None:
return
if thread.isRunning():
try:
thread.kernel_manager._kill_kernel()
except Exception:
pass
thread.quit()
thread.wait()
with cls._shutdown_thread_list_lock:
for thread in cls._shutdown_thread_list:
if thread.isRunning():
try:
thread.kernel_manager._kill_kernel()
except Exception:
pass
thread.quit()
thread.wait()
cls._shutdown_thread_list = []

def copy(self):
"""Copy kernel."""
Expand Down
6 changes: 2 additions & 4 deletions spyder/plugins/ipythonconsole/widgets/main_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -1644,10 +1644,8 @@ def close_all_clients(self):
client.close_client(is_last_client)
open_clients.remove(client)

# Wait for all KernelHandler's to shutdown.
for client in self.clients:
if client.kernel_handler:
client.kernel_handler.wait_shutdown_thread()
# Wait for all KernelHandler threads to shutdown.
KernelHandler.wait_all_shutdown_threads()

# Close cached kernel
self.close_cached_kernel()
Expand Down