mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-08 13:01:52 +00:00
4788dd3f68
The read cases' stream guard is now the pure-Rust topology.brep_data simulator (bd::guard, no C++ call), and the Rust side no longer defers on the C++ SmBrepData read verdict (cpp-guard:unvalidated-topology is gone). The C++ read of a modified stream runs in isolate::fresh_process_within (new: a per-child alarm, here 30 s), so a UB-164 crash or hang in MakeTopologyFromData is rejected and the streams that survive are compared. New built-in cases: 100 modified streams the guard accepts and Rust's BrepData::read_from_file reads (45 geometry-digit and 55 topology-integer overwrites). 70 pass, 26 rejected, 4 known mismatches where C++ builds an inconsistent radial list that the brep_p10 port rejects by design (plan/findings/topology.brep_p7--modified-stream-inconsistent-radial-list.md); the unit is partial. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SdcgEtgaA54W2nr8dK1EYp
99 lines
3.2 KiB
C++
99 lines
3.2 KiB
C++
// SPDX-License-Identifier: Apache-2.0
|
|
// Per-case isolation of the C++ library's process-global state (solidcore_ffi::isolate).
|
|
//
|
|
// Upstream keeps state in function statics that the first call in a process fixes (ND-3:
|
|
// SmFilletSolver::ComputePointValues' sdStaticStep). A case whose C++ side runs in a
|
|
// fresh forked child starts from the parent's state, which never ran the case code, and
|
|
// whatever the case latches dies with the child. The Rust wrapper runs the case's shim
|
|
// calls in the child, sends their result back through the pipe and calls
|
|
// sc_isolate_exit; the parent reads the pipe to its end and then sc_isolate_wait.
|
|
//
|
|
// * sc_isolate_fork(fd): forks while holding the shared native mutex, so no other thread
|
|
// is inside native code at the fork. The child cannot use that copy: a recursive
|
|
// mutex records its owner's kernel thread id, which differs in the child, so the
|
|
// child's first SC_TRY would block forever. The child, single-threaded, constructs a
|
|
// fresh (unlocked) mutex in its place. Returns the child's pid in the parent (*fd = the
|
|
// read end of the pipe), 0 in the child (*fd = the write end), or -1 when the pipe or
|
|
// fork fails (*fd = -1; the caller then runs the case in-process).
|
|
// * sc_isolate_alarm(seconds): in the child, replaces the 600 s alarm with one of
|
|
// `seconds` (1 to 600; others leave it), for a case whose native call may never end.
|
|
// * sc_isolate_exit(): ends the child (_exit(0): no atexit handlers, no stdio flush).
|
|
// * sc_isolate_wait(pid): 1 when the child exited with status 0, else 0 (killed by a
|
|
// signal, the 600 s alarm, or another exit).
|
|
#include "sc_shim.h"
|
|
|
|
#include <new>
|
|
|
|
#ifndef _WIN32
|
|
#include <cerrno>
|
|
#include <cstdio>
|
|
#include <signal.h>
|
|
#include <sys/wait.h>
|
|
#include <unistd.h>
|
|
#ifdef __linux__
|
|
#include <sys/prctl.h>
|
|
#endif
|
|
|
|
SC_EXPORT int64_t sc_isolate_fork(int32_t* fd)
|
|
{
|
|
if (fd == nullptr)
|
|
return -1;
|
|
*fd = -1;
|
|
int fds[2];
|
|
std::recursive_mutex& mutex = sc_native_mutex();
|
|
mutex.lock();
|
|
if (pipe(fds) != 0)
|
|
{
|
|
mutex.unlock();
|
|
return -1;
|
|
}
|
|
fflush(stdout);
|
|
fflush(stderr);
|
|
const pid_t pid = fork();
|
|
if (pid < 0)
|
|
{
|
|
close(fds[0]);
|
|
close(fds[1]);
|
|
mutex.unlock();
|
|
return -1;
|
|
}
|
|
if (pid == 0)
|
|
{
|
|
#ifdef __linux__
|
|
// (no core dump of an expected native crash)
|
|
prctl(PR_SET_DUMPABLE, 0);
|
|
#endif
|
|
// A child that writes into a closed pipe dies instead of hanging.
|
|
signal(SIGPIPE, SIG_DFL);
|
|
// (the inherited lock names the parent's thread id; see above)
|
|
new (&mutex) std::recursive_mutex();
|
|
alarm(600);
|
|
close(fds[0]);
|
|
*fd = fds[1];
|
|
return 0;
|
|
}
|
|
close(fds[1]);
|
|
mutex.unlock();
|
|
*fd = fds[0];
|
|
return int64_t(pid);
|
|
}
|
|
|
|
SC_EXPORT void sc_isolate_alarm(uint32_t seconds)
|
|
{
|
|
if (seconds >= 1 && seconds <= 600)
|
|
alarm(seconds);
|
|
}
|
|
|
|
SC_EXPORT void sc_isolate_exit() { _exit(0); }
|
|
|
|
SC_EXPORT int32_t sc_isolate_wait(int64_t pid)
|
|
{
|
|
int ws = 0;
|
|
pid_t w;
|
|
do
|
|
w = waitpid(pid_t(pid), &ws, 0);
|
|
while (w < 0 && errno == EINTR);
|
|
return (w == pid_t(pid) && WIFEXITED(ws) && WEXITSTATUS(ws) == 0) ? 1 : 0;
|
|
}
|
|
#endif
|