Skip to content

Commit 009f481

Browse files
iris: auto-cleanup managed threads in tests via thread_container_scope
Tests that construct Controller but never call controller.stop() were leaking the log-server uvicorn thread (and other managed threads registered with the process-wide default ThreadContainer). These non-daemon threads survived the test and were dumped by pytest_sessionfinish. Wrap the autouse _thread_cleanup fixture with thread_container_scope() so every iris test runs with a fresh ThreadContainer that is stopped on teardown. The container's stop() signals server.should_exit on any spawn_server threads and joins them. The existing thread-snapshot leak warning remains as a safety net for threads created outside any container. Fixes #4871. Co-authored-by: Russell Power <rjpower@users.noreply.github.com>
1 parent 8400841 commit 009f481

1 file changed

Lines changed: 15 additions & 10 deletions

File tree

lib/iris/tests/conftest.py

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
import pytest
1717
from iris.cluster.config import load_config, make_local_config
18+
from iris.managed_thread import thread_container_scope
1819
from iris.rpc import config_pb2
1920
from iris.test_util import SentinelFile
2021
from rigging.timing import Duration, ExponentialBackoff
@@ -107,18 +108,22 @@ def sentinel(tmp_path) -> SentinelFile:
107108

108109

109110
@pytest.fixture(autouse=True)
110-
def _thread_cleanup():
111-
"""Ensure no new non-daemon threads leak from each test.
112-
113-
Takes a snapshot of threads before the test and checks that no new
114-
non-daemon threads remain after teardown. Waits briefly for threads
115-
that are in the process of shutting down.
116-
117-
This fixture helps catch tests that don't properly clean up their threads,
118-
which can cause tests to hang or interfere with each other.
111+
def _thread_cleanup(request):
112+
"""Isolate each test's managed threads and warn on leaks.
113+
114+
Installs a fresh ThreadContainer via thread_container_scope() so every
115+
component that calls get_thread_container() (e.g. Controller._start_local_log_server)
116+
registers its threads into a per-test container that is stopped on teardown.
117+
This ensures log-server uvicorn threads and other managed threads are joined
118+
even when a test constructs a Controller without calling stop().
119+
120+
As a safety net, takes a snapshot of threads before the test and warns
121+
about any non-daemon threads created outside any container that survive
122+
teardown.
119123
"""
120124
before = {t.ident for t in threading.enumerate()}
121-
yield
125+
with thread_container_scope(name=f"test:{request.node.name}"):
126+
yield
122127

123128
def _no_leaked_threads() -> bool:
124129
return not any(

0 commit comments

Comments
 (0)