From 14b7fd1c981f087934a878b7044fe8e6d04c86c4 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Wed, 28 Oct 2026 03:56:25 +0100 Subject: [PATCH] fix: one thread at a time in the campaign cwd --- scripts/campaign.py | 22 +++++++++++++++------- tests/test_campaign.py | 3 +++ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/scripts/campaign.py b/scripts/campaign.py index 5648cb23d..4537075f7 100644 --- a/scripts/campaign.py +++ b/scripts/campaign.py @@ -69,6 +69,7 @@ import stat import subprocess import sys import tarfile +import threading import time import urllib.error import urllib.request @@ -454,15 +455,22 @@ def copy_regular(src: Path, dest: Path) -> None: shutil.copyfileobj(handle, out) +# The working directory belongs to the process. Two passes in threads that +# each saved and restored it could leave the process in the other's root, +# then in a folder deleted with its workspace. +_CWD_LOCK = threading.Lock() + + @contextlib.contextmanager def working_directory(path: Path) -> Iterator[None]: - """contextlib.chdir, which Python 3.9 lacks.""" - before = Path.cwd() - os.chdir(path) - try: - yield - finally: - os.chdir(before) + """contextlib.chdir, which Python 3.9 lacks, one thread at a time.""" + with _CWD_LOCK: + before = Path.cwd() + os.chdir(path) + try: + yield + finally: + os.chdir(before) @contextlib.contextmanager diff --git a/tests/test_campaign.py b/tests/test_campaign.py index fee154c1c..947ad3ade 100644 --- a/tests/test_campaign.py +++ b/tests/test_campaign.py @@ -1170,10 +1170,13 @@ class FakeCorePasses(Workspace): c.host.run = timed passes.append(c) threads = [threading.Thread(target=c.run, args=(set(), None)) for c in passes] + cwd = Path.cwd() for thread in threads: thread.start() for thread in threads: thread.join() + # Each pass moved into its own root and back: the process ends where it began. + self.assertEqual(Path.cwd(), cwd) ends = {name: sorted(end for _, end in runs) for name, runs in spans.items()} self.assertGreater(len(ends["a"]), 2) self.assertGreater(len(ends["b"]), 2)