From c75350e7f21410a691df4913182bd346af5e7741 Mon Sep 17 00:00:00 2001 From: Jevgenijs Protopopovs Date: Tue, 12 Aug 2025 08:49:38 +0300 Subject: [PATCH 1/3] Await for all sanitizer subprocesses to terminate before closing the event loop. Fixes 'Event loop is closed' runtime error in BaseSubprocessTransport destructors --- sv-sanitizers.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sv-sanitizers.py b/sv-sanitizers.py index d81469f..7d14300 100755 --- a/sv-sanitizers.py +++ b/sv-sanitizers.py @@ -129,6 +129,7 @@ async def run_one(args, executable): else: return None finally: + await process.wait() processes.remove(process) async def run_worker(args, executable): @@ -147,6 +148,7 @@ async def run(args, executable): process.kill() for task in pending: task.cancel() + await asyncio.wait(tasks) return done.pop().result() def generate_witness(args, result): From b4ca2ce5d0c08caa4a0019fbc91fe0ee567bcda9 Mon Sep 17 00:00:00 2001 From: Jevgenijs Protopopovs Date: Tue, 12 Aug 2025 09:07:17 +0300 Subject: [PATCH 2/3] Ignore ProcessLookupError which rarely arises upon sending SIGKILL upon process that has already been awaited --- sv-sanitizers.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sv-sanitizers.py b/sv-sanitizers.py index 7d14300..b7aab9f 100755 --- a/sv-sanitizers.py +++ b/sv-sanitizers.py @@ -145,7 +145,10 @@ async def run(args, executable): global stop stop = True for process in processes: - process.kill() + try: + process.kill() + except ProcessLookupError as ex: + pass for task in pending: task.cancel() await asyncio.wait(tasks) From d38cbf01c9fb6e70e8dc263419152f6c45367f8d Mon Sep 17 00:00:00 2001 From: Jevgenijs Protopopovs Date: Thu, 14 Aug 2025 09:04:08 +0300 Subject: [PATCH 3/3] Add --timeout parameter to terminate all sanitizer processes after specified time reporting that no races were found --- sv-sanitizers.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sv-sanitizers.py b/sv-sanitizers.py index b7aab9f..ddcc4f9 100755 --- a/sv-sanitizers.py +++ b/sv-sanitizers.py @@ -19,6 +19,7 @@ def parse_args(): parser.add_argument("-d", "--data-model", type=str, choices=["ILP32", "LP64"], default="LP64") parser.add_argument("program", type=str) parser.add_argument("-j", "--jobs", type=int, default=1) + parser.add_argument("-t", "--timeout", type=int, required=False) parser.add_argument("-v", "--version", action="version", version=VERSION) args = parser.parse_args() @@ -141,7 +142,7 @@ async def run_worker(args, executable): async def run(args, executable): tasks = [asyncio.create_task(run_worker(args, executable), name=f"worker-{i}") for i in range(args.jobs)] - done, pending = await asyncio.wait(tasks, return_when="FIRST_COMPLETED") + done, pending = await asyncio.wait(tasks, return_when="FIRST_COMPLETED", timeout=args.timeout) global stop stop = True for process in processes: @@ -152,7 +153,7 @@ async def run(args, executable): for task in pending: task.cancel() await asyncio.wait(tasks) - return done.pop().result() + return done.pop().result() if done else ('true', b'') def generate_witness(args, result): if args.property == "no-data-race":