VPI/DPI capture libraries: a cbEndOfSimulation-only teardown misses $fatal, runtime errors and time-limit exits; test every way the sim can end
Context: a capture library loaded into a Verilog simulator starts worker threads (flusher, encoders) and stops and joins them in a teardown hooked to the simulator's end-of-simulation callback. A review of a thread-join fix raised the next question: does that callback run on every way the simulation can end?
The cases to test, each with every worker thread option turned on: - a normal $finish; - $fatal, and a runtime error raised by the simulator; - the simulator's own time or cycle limit option; - SIGTERM or SIGINT from a harness timeout; - a library failure path that calls _exit.
What to check per case: - the process exits within N seconds (no hang on an unjoined thread, no abort from a joinable std::thread being destroyed); - the exit code is right: non-zero for failures, never _exit(0); - a partly written output file opens as 'incomplete', not as a valid short file.
Design tips: - Make the thread stop function idempotent: return at once if the thread was never started or was already joined. Then it can be called from several places. - Put one unconditional stop-and-join ahead of any early returns in the end callback. Decide whether to stop a thread by whether it was actually started, not by the mode flags. - Add an atexit or static-destructor backstop that only joins, never writes, for paths where the simulator skips the end callback. - A failing test before the fix (RED) and a passing exit matrix after it (GREEN) should both be on record before review sign-off. Thread-code changes also need a compile check on the target Linux toolchain.