Make the embedded subprocess_helper work on non-Linux hosts - #4864
Make the embedded subprocess_helper work on non-Linux hosts#4864zarubaf wants to merge 1 commit into
Conversation
|
Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). View this failed invocation of the CLA check for more information. For the most up to date status, view the checks section at the bottom of the pull request. |
72f703f to
8b5d836
Compare
| } | ||
|
|
||
| static std::string* const cleanup_path = new std::string(helper_path); | ||
| std::atexit([] { unlink(cleanup_path->c_str()); }); |
There was a problem hiding this comment.
I'd much prefer to have static destructors handle this instead of an atexit handler.
Just add a FileName function to the CleanableFD, make it virtual and have the mac version return an override with the unlink in its destructor (and the linux one does the close it currently has). This means the normal static destructors deal with closing the FD on linux and removing the file in mac.
| "Failed to seek subprocess helper in memfd: ", Strerror(errno))); | ||
| } | ||
| return std::move(fd); | ||
| // Every spawn names this fd through /proc/self/fd, so it has to stay open for |
There was a problem hiding this comment.
We need to make sure that the FD is closed by the static destructors or some other mechanism. Just leaking it is not ok. Especially because we care about the linux build in a way that we (unfortunately) don't for the mac build.
There was a problem hiding this comment.
You are right. The memfd is owned by MemfdSubprocessHelper and closed by the inherited ~CleanableFd() when the function-local static is destroyed.
|
|
||
| // Runs the helper straight out of an anonymous in-memory file, so that nothing | ||
| // on disk needs to outlive the build that produced it. | ||
| absl::StatusOr<std::string> CreateSubprocessHelper() { |
There was a problem hiding this comment.
Instead maybe make CleanableFD a virtual class with a
virtual std::string file_name() const = 0;
and have this (in both versions) return absl::StatusOr<std::unique_ptr>
xls/common/subprocess.cc fails to compile on macOS:
fatal error: 'linux/memfd.h' file not found
Embedding subprocess_helper and running it from a memfd replaced the
previous runfiles lookup, but the mechanism it uses is Linux specific:
neither memfd_create() nor /proc/self/fd exists on macOS, and the include
is unconditional.
This breaks more than the compile. //xls/common:subprocess is reachable
from all four binaries built by nightly-macos.yml (interpreter_main,
ir_converter_main, opt_main, codegen_main), so the macOS nightly build
cannot succeed while this is present.
CleanableFd becomes the owner of whatever storage the helper lives in, with
a virtual file_name() naming the path to spawn, and CreateSubprocessHelper()
hands back a unique_ptr to one of two implementations:
- MemfdSubprocessHelper keeps the existing memfd behavior and resolves
through /proc/self/fd. The descriptor stays open for as long as the
object lives and is closed by the inherited destructor.
- TempFileSubprocessHelper writes the helper to a private mkstemp() file
under $TMPDIR and unlinks it on destruction. Its descriptor is closed as
soon as the file is written, because macOS refuses to exec a file that
is still open for writing (ETXTBSY).
Both are held in a function-local static, so the static destructor releases
the storage on either platform: no leaked descriptor on Linux and no atexit
handler on macOS.
The temporary file gives up the "nothing on disk has to survive" property
of the memfd path. macOS has no fexecve() and no /proc, so exec needs a
real path.
Verified on macOS arm64 (-c opt), on top of the minimum-OS and hermetic
LLVM SDK changes needed to build there at all:
- //xls/common:subprocess fails to compile before, builds after
- //xls/common:subprocess_test passes
- the four nightly-macos.yml targets fail before, build after
Context: google#1434
8b5d836 to
231a413
Compare
|
Thanks for taking a look. Verified locally on my Mac. |
allight
left a comment
There was a problem hiding this comment.
Just a few minor changes. Otherwise LGTM
| int fd() const { return fd_; } | ||
|
|
||
| private: | ||
| int fd_ = -1; |
There was a problem hiding this comment.
Just have the FD be only in the linux override.
| close(fd); | ||
| return written; | ||
| } | ||
| if (fchmod(fd, 0700) != 0) { |
There was a problem hiding this comment.
0500
No need to have write permission anymore.
| return helper; | ||
| } | ||
|
|
||
| #else |
There was a problem hiding this comment.
#else // for MACOS and other non-linux
xls/common/subprocess.ccdoes not compile on macOS:2c355ce53("Embed subprocess_helper in subprocess.cc") replaced the runfiles lookup with an embedded copy executed from a memfd. The mechanism is Linux specific — neithermemfd_create()nor/proc/self/fdexists on macOS — and the include is unconditional.The blast radius is larger than one file.
//xls/common:subprocessis reachable from all four binaries thatnightly-macos.ymlbuilds (interpreter_main,ir_converter_main,opt_main,codegen_main), so the macOS nightly build cannot succeed while this is present.Change
Helper creation moves behind a single
CreateSubprocessHelper()returning a path to spawn:StatusOr— it has to stay open for the process lifetime for/proc/self/fd/Nto resolve, which the previous function-local static did implicitly.mkstemp()file under$TMPDIR, marked executable, closed, and unlinked viaatexit. Closing before exec matters: macOS returnsETXTBSYfor a file still open for writing.The tradeoff, and a question
The temporary file gives up the "no dependency on build artifacts continuing to exist" property that motivated the memfd approach. I could not find a way to preserve it on macOS: there is no
fexecve()and no/proc, so exec needs a real path.If you would rather not have a temp file on macOS at all, the alternative is falling back to the previous
GetXlsRunfilePath()lookup on non-Linux and keeping memfd on Linux. That keeps nothing on disk that was not already on disk, at the cost of reintroducing the runfiles dependency for that platform. Happy to switch if that is preferred — I picked the temp file because it keeps behavior uniform across platforms.Verification
macOS arm64,
-c opt, on top of the minimum-OS and hermetic LLVM SDK changes needed to build there at all://xls/common:subprocessfails to compile before, builds afternightly-macos.ymltargets fail before, build after//xls/common:subprocess_testpasses — though only with one additional local workaround, noted belowSince
mainis currently red on an unrelated lockfile issue, this was checked oncf36eb5a3, the last green ancestor.The
subprocess_testcaveat: linking any Mach-O shared library currently fails because rules_cc appends a GNU-style-Wl,-soname=that ld64 rejects (ld64.lld: error: unknown argument '-soname=...'). I worked around it locally with--nointerface_shared_objectsto run the test. That is a separate problem, not addressed here.Context: #1434