"""Fork safety — what keeps cad_pool's forkserver cheap. cad_pool preloads the OCP-importing modules into a forkserver and forks per job (~30 ms) instead of spawning and re-importing OCCT (~1.3 s). That is only sound if importing this binding starts no threads and leaves no state that a fork would invalidate. """ from __future__ import annotations import multiprocessing import threading def test_no_threads_after_import(): import OCP # noqa: F401 assert threading.active_count() == 1, threading.enumerate() def _child(conn): # Runs after the fork: exercise the kernel to prove the inherited image is # usable rather than merely present. import io from OCP.BinTools import BinTools from OCP.TopAbs import TopAbs_FACE from OCP.TopExp import TopExp from OCP.TopoDS import TopoDS_Shape from OCP.TopTools import TopTools_IndexedMapOfShape from .conftest import DATA shape = TopoDS_Shape() BinTools.Read_s(shape, io.BytesIO((DATA / "fused.brep").read_bytes())) faces = TopTools_IndexedMapOfShape() TopExp.MapShapes_s(shape, TopAbs_FACE, faces) out = io.BytesIO() BinTools.Write_s(shape, out) conn.send((faces.Extent(), len(out.getvalue()))) conn.close() def test_forkserver_child_can_use_the_kernel(): import OCP # noqa: F401 ctx = multiprocessing.get_context("forkserver") ctx.set_forkserver_preload(["OCP", "OCP.TopoDS", "OCP.BinTools"]) parent, child = ctx.Pipe() proc = ctx.Process(target=_child, args=(child,)) proc.start() faces, size = parent.recv() proc.join(30) assert proc.exitcode == 0 assert faces == 12 assert size > 0