allow crtl tests to run in parallel
authorJacob Lifshay <programmerjake@gmail.com>
Thu, 25 Aug 2022 09:32:10 +0000 (02:32 -0700)
committerJacob Lifshay <programmerjake@gmail.com>
Thu, 25 Aug 2022 09:37:47 +0000 (02:37 -0700)
.gitignore
src/openpower/decoder/test/_pyrtl.py
src/openpower/decoder/test/crtl_path.py [new file with mode: 0644]
src/openpower/decoder/test/pysim.py

index ea10fbc5f8dc796647afa2c7c47a501abebcbea7..ee28534dd7edb24f4ade229be0438fa5a9a6403c 100644 (file)
@@ -8,4 +8,5 @@ dist
 *.il
 /jit_test
 /jit_test.o
-/crtl
+/crtl*/
+/crtl*
index f7f33a9c1d4fadca08d1225f9c2bd9704b3d2c78..ed3039c8689689369d26f1d261b3594526c5e4fa 100644 (file)
@@ -4,6 +4,7 @@ from contextlib import contextmanager
 from nmigen.hdl.ast import SignalSet
 from nmigen.hdl.xfrm import ValueVisitor, StatementVisitor, LHSGroupFilter
 from nmigen.sim._base import BaseProcess
+from openpower.decoder.test.crtl_path import get_crtl_path
 
 __all__ = ["PyRTLProcess"]
 
@@ -462,7 +463,9 @@ class _FragmentCompiler:
             code += "#include \"common.h\"\n"
             code += emitter.flush()
 
-            file = open(f"crtl/{domain_process.name}.c", "w")
+            crtl = get_crtl_path()
+
+            file = open(os.path.join(crtl, f"{domain_process.name}.c"), "w")
             file.write(code)
             file.close()
 
diff --git a/src/openpower/decoder/test/crtl_path.py b/src/openpower/decoder/test/crtl_path.py
new file mode 100644 (file)
index 0000000..2782996
--- /dev/null
@@ -0,0 +1,47 @@
+# SPDX-License-Identifier: LGPL-3-or-later
+# Copyright 2022 Jacob Lifshay
+
+
+from contextlib import contextmanager
+from itertools import count
+import os
+from pathlib import Path
+import shutil
+from tempfile import NamedTemporaryFile
+from threading import local
+
+__ctrl_path = local()
+
+
+@contextmanager
+def __try_lock_file(path):
+    path = Path(path)
+    try:
+        file = path.open("xb")
+    except FileExistsError:
+        yield False
+        return
+    try:
+        yield True
+    finally:
+        file.close()
+        path.unlink()
+
+
+def get_crtl_path():
+    # type: () -> str
+    path = getattr(__ctrl_path, "path", None)
+    if path is not None:
+        assert isinstance(path, str), "invalid state"
+        return path
+    for i in range(10000):
+        path = f"crtl{i}"
+        with __try_lock_file(f"crtl{i}.lock") as locked:
+            if locked and next(Path(path).glob(".lock_*"), None) is None:
+                shutil.rmtree(path, ignore_errors=True)
+                Path(path).mkdir(parents=True, exist_ok=True)
+                tmpfile = NamedTemporaryFile(prefix=".lock_", dir=path)
+                __ctrl_path.tmpfile = tmpfile
+                __ctrl_path.path = path
+                return path
+    assert False, "can't create crtl* path"
index 0edf785e48b69234f5c4012edf05bbeac5d6ae3d..7201de17e1af35711e8bae65497923dd7a7bbc8f 100644 (file)
@@ -20,6 +20,7 @@ from cffi import FFI
 from os.path import dirname, join
 from collections import namedtuple
 
+from openpower.decoder.test.crtl_path import get_crtl_path
 
 __all__ = ["PySimEngine"]
 
@@ -316,14 +317,8 @@ class PySimEngine(BaseEngine):
 
         self._fragment = fragment
 
-        # blow away and recreate crtl subdirectory.  (hope like hell
-        # nobody is using this in their current working directory)
-        if PySimEngine._crtl_counter == 0:
-            shutil.rmtree("crtl", True)
-        try:
-            os.mkdir("crtl")
-        except FileExistsError:
-            pass
+        # create crtl directory
+        crtl = get_crtl_path()
 
         # "Processes" are the compiled modules.  Each module ends up
         # with its own run() function
@@ -353,7 +348,7 @@ class PySimEngine(BaseEngine):
             cdef += f"void run_{process.name}(void);\n"
 
         # write out the header file
-        with open("crtl/common.h", "w") as cdef_file :
+        with open(os.path.join(crtl, "common.h"), "w") as cdef_file:
             cdef_file.write(cdef)
 
         # same with c template: read template first
@@ -365,20 +360,23 @@ class PySimEngine(BaseEngine):
         src = template % (len(self._state.slots), len(self._state.slots))
 
         # write it out
-        with open("crtl/common.c", "w") as src_file:
+        with open(os.path.join(crtl, "common.c"), "w") as src_file:
             src_file.write(src)
 
         # build module named crtlNNN in crtl subdirectory
-        modulename = "crtl.crtl%d" % PySimEngine._crtl_counter
-        sources = ["crtl/common.c"]
-        sources += [f"crtl/{process.name}.c" for process in self._processes]
+        modulename = f"{crtl}.crtl{PySimEngine._crtl_counter}"
+        sources = [os.path.join(crtl, "common.c")]
+        for process in self._processes:
+            sources.append(os.path.join(crtl, f"{process.name}.c"))
         ffibuilder = FFI()
         ffibuilder.cdef(cdef)
         ffibuilder.set_source(modulename, cdef, sources=sources)
         ffibuilder.compile(verbose=True)
-        
+
         # append search path of crtl directory before attempting import
-        sys.path.append(os.path.join(os.getcwd()))
+        cwd = os.path.join(os.getcwd())
+        if cwd not in sys.path:
+            sys.path.append(cwd)
         self._state.crtl = importlib.import_module(modulename).lib
 
         # Use a counter to generate unique names for modules, because Python