From b6c36a0abd98cfe4b6e71cbc99e410e66d0b6d1b Mon Sep 17 00:00:00 2001 From: kevin Heifner Date: Thu, 4 Dec 2025 10:51:47 -0600 Subject: [PATCH 1/2] Support multiple threads writing to subprocess_results.log --- tests/TestHarness/testUtils.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/tests/TestHarness/testUtils.py b/tests/TestHarness/testUtils.py index 7b202c6318..d6b593371f 100644 --- a/tests/TestHarness/testUtils.py +++ b/tests/TestHarness/testUtils.py @@ -17,6 +17,7 @@ import shutil import sys from pathlib import Path +import threading ########################################################################################### @@ -82,6 +83,8 @@ class Utils: ConfigDir=f"{DataPath}/" TimeFmt='%Y-%m-%dT%H:%M:%S.%f' + # lock to serialize writes to subprocess_results.log across threads + _check_output_lock = threading.Lock() @staticmethod def timestamp(): @@ -92,19 +95,20 @@ def checkOutputFileWrite(time, cmd, output, error): stop=Utils.timestamp() os.makedirs(Utils.TestLogRoot, exist_ok=True) os.makedirs(Utils.DataPath, exist_ok=True) - if not hasattr(Utils, "checkOutputFile"): + + # Ensure filename is set + if not hasattr(Utils, "checkOutputFilename"): Utils.checkOutputFilename=f"{Utils.DataPath}/subprocess_results.log" - if Utils.Debug: Utils.Print("opening %s in dir: %s" % (Utils.checkOutputFilename, os.getcwd())) - Utils.checkOutputFile=open(Utils.checkOutputFilename,"w") - else: - Utils.checkOutputFile=open(Utils.checkOutputFilename,"a") - - Utils.checkOutputFile.write(Utils.FileDivider + "\n") - Utils.checkOutputFile.write("start={%s}\n" % (time)) - Utils.checkOutputFile.write("cmd={%s}\n" % (" ".join(cmd))) - Utils.checkOutputFile.write("cout={%s}\n" % (output)) - Utils.checkOutputFile.write("cerr={%s}\n" % (error)) - Utils.checkOutputFile.write("stop={%s}\n" % (stop)) + + # Serialize concurrent writes and open file per write to avoid sharing closed handles + with Utils._check_output_lock: + with open(Utils.checkOutputFilename, "a") as f: + f.write(Utils.FileDivider + "\n") + f.write("start={%s}\n" % (time)) + f.write("cmd={%s}\n" % (" ".join(cmd))) + f.write("cout={%s}\n" % (output)) + f.write("cerr={%s}\n" % (error)) + f.write("stop={%s}\n" % (stop)) @staticmethod def Print(*args, **kwargs): From f4824016fdfabb5ddb48db385ff6037c91d654ad Mon Sep 17 00:00:00 2001 From: kevin Heifner Date: Thu, 4 Dec 2025 12:17:54 -0600 Subject: [PATCH 2/2] Hold lock for Utils.checkOutputFilename access --- tests/TestHarness/testUtils.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/TestHarness/testUtils.py b/tests/TestHarness/testUtils.py index d6b593371f..6fe1ed1cbb 100644 --- a/tests/TestHarness/testUtils.py +++ b/tests/TestHarness/testUtils.py @@ -93,15 +93,15 @@ def timestamp(): @staticmethod def checkOutputFileWrite(time, cmd, output, error): stop=Utils.timestamp() - os.makedirs(Utils.TestLogRoot, exist_ok=True) - os.makedirs(Utils.DataPath, exist_ok=True) - - # Ensure filename is set - if not hasattr(Utils, "checkOutputFilename"): - Utils.checkOutputFilename=f"{Utils.DataPath}/subprocess_results.log" - # Serialize concurrent writes and open file per write to avoid sharing closed handles with Utils._check_output_lock: + os.makedirs(Utils.TestLogRoot, exist_ok=True) + os.makedirs(Utils.DataPath, exist_ok=True) + + # Ensure filename is set + if not hasattr(Utils, "checkOutputFilename"): + Utils.checkOutputFilename=f"{Utils.DataPath}/subprocess_results.log" + with open(Utils.checkOutputFilename, "a") as f: f.write(Utils.FileDivider + "\n") f.write("start={%s}\n" % (time))