The two sweeps do not race a printing fixture against a clock
dev-chatty.flan outlives the surveys' twenty seconds by design, and unlike dev-repl it prints while it does -- so the two backends stop at different lines and the diff reports on scheduling rather than on lowering. It joins dev-loop and dev-watch in the excluded-by-name list in both sweeps, with the distinction written down. rt_flush_out is guarded on __wasm__: the pipe it is careful about belongs to a merged flan dev, which is only ever a native host, and wasm32 need not answer for a descriptor mode its runtime may model differently. And three comments that went false with the _exit: the atexit registration in the merged entry point is no longer there for rt_die, which unlinks the socket for itself now, so both places that said so say what it is actually left covering.
This commit is contained in:
parent
95058502f5
commit
8e5fcf54cc
28
lib/dev.ml
28
lib/dev.ml
@ -3146,15 +3146,19 @@ int flan_merged_program_state(void) {
|
||||
* message has already sent two investigations in this repository to the wrong
|
||||
* place. Gone is the honest state, and the client already has words for it.
|
||||
*
|
||||
* [atexit] covers exit(3), which is the compiler thread's own way out and the
|
||||
* way out of a program that somehow returns from main. It does NOT cover the
|
||||
* two places a program dies where it stands: rt_die in flan_rt.c and die_now
|
||||
* in flan_agent.c both take _exit, because the atexit chain and the ELF
|
||||
* destructors want the loader lock a dlopening listener thread may be holding
|
||||
* — and in this build that chain also holds OCaml's shutdown. Both of them
|
||||
* therefore unlink this path by hand, which is why this function's body is
|
||||
* written out three times in the tree rather than shared: it is four lines, and
|
||||
* the alternative is a runtime that has to link against the daemon. */
|
||||
* [atexit] covers exit(3), and what is left taking exit(3) here is narrower
|
||||
* than it was: the two places a program dies where it stands — rt_die in
|
||||
* flan_rt.c and die_now in flan_agent.c — both take _exit, because the atexit
|
||||
* chain and the ELF destructors want the loader lock a dlopening listener
|
||||
* thread may be holding, and in this build that chain also holds OCaml's
|
||||
* shutdown. Both therefore unlink this path by hand, so the same four lines
|
||||
* exist in three places rather than one — sharing them would mean a runtime
|
||||
* that links against the daemon, which is a worse trade than the repetition.
|
||||
*
|
||||
* What is left for this one is every exit(3) nobody planned — an OCaml fatal
|
||||
* on the compiler thread most of all, since [Stdlib.exit] ends in the C one.
|
||||
* Four lines on a path nobody means to take is the right price for a socket
|
||||
* that never sits on disk refusing connects. */
|
||||
static void flan_merged_unlink_sock(void) {
|
||||
const char *s = getenv("FLAN_DEV_SOCK");
|
||||
if (s != NULL && *s != '\0') unlink(s);
|
||||
@ -3234,9 +3238,9 @@ int main(int argc, char **argv) {
|
||||
* own shutdown to that chain. The hand-written [flan_merged_unlink_sock] that
|
||||
* used to sit under this function's _exit went with it for the same reason —
|
||||
* there is no way out of here any more to unlink on. The [atexit]
|
||||
* registration above stays, because it is not for this path: it is for
|
||||
* exit(3), which is what flan_rt.c's rt_die takes, and a merged daemon that
|
||||
* dies through a trap must not leave a socket refusing connects behind it. */
|
||||
* registration above stays, and no longer because of the trap: rt_die takes
|
||||
* _exit now and unlinks for itself, exactly as die_now does. What is left
|
||||
* for it is written at [flan_merged_unlink_sock]. */
|
||||
for (;;) {
|
||||
if (setjmp(program_return) == 0) {
|
||||
rc = flan_program_main(argc, argv);
|
||||
|
||||
@ -394,7 +394,9 @@ void flan_escape_bytes(const uint8_t *p, int64_t n, flan_slice *out) {
|
||||
* redirected stdout is not, so without this the error appears above the output
|
||||
* that led to it. */
|
||||
|
||||
#if !defined(__wasm__)
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
|
||||
/* That flush, made incapable of waiting.
|
||||
@ -416,12 +418,24 @@ void flan_escape_bytes(const uint8_t *p, int64_t n, flan_slice *out) {
|
||||
* The return value is ignored deliberately, twice over: a failed fcntl leaves
|
||||
* the old blocking behaviour, which is what this code did before, and a flush
|
||||
* that reports EAGAIN has done as much as it is going to. There is nothing a
|
||||
* dying process can do about either. */
|
||||
* dying process can do about either.
|
||||
*
|
||||
* Guarded on __wasm__, which this file otherwise does only for the valgrind
|
||||
* client request below. The hazard is a pipe whose reader is
|
||||
* the compiler thread of a merged `flan dev', which is a native host and only
|
||||
* ever a native host — a wasm32 module has no compiler beside it and no such
|
||||
* pipe. So the guard is not a portability apology: it says where the problem
|
||||
* can exist, and keeps wasm32 from having to answer for a descriptor mode its
|
||||
* runtime may model differently. */
|
||||
#if defined(__wasm__)
|
||||
static void rt_flush_out(void) { (void)fflush(stdout); }
|
||||
#else
|
||||
static void rt_flush_out(void) {
|
||||
int flags = fcntl(1, F_GETFL, 0);
|
||||
if (flags >= 0) (void)fcntl(1, F_SETFL, flags | O_NONBLOCK);
|
||||
(void)fflush(stdout);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* [_exit] and not [exit], for the reason die_now gives in
|
||||
* vendor/agent/flan_agent.c and gives at length: this runs on the game thread,
|
||||
|
||||
@ -59,9 +59,11 @@ fi
|
||||
corpus=${SURVEY_CORPUS:-$root}
|
||||
out=$(mktemp -d); trap 'rm -rf "$out"' EXIT
|
||||
|
||||
# The two that run until something stops them, excluded by name for the reason
|
||||
# the x86 sweep excludes them: a timeout cannot tell them from a hang.
|
||||
forever="dev-loop dev-watch"
|
||||
# The ones that run until something stops them, excluded by name for the reason
|
||||
# the x86 sweep excludes them: a timeout cannot tell them from a hang -- and in
|
||||
# dev-chatty's case cannot even give the two sides the same truncation, since
|
||||
# it prints 4K a frame for as long as it is allowed to.
|
||||
forever="dev-loop dev-watch dev-chatty"
|
||||
|
||||
TIMEOUT=${TIMEOUT:-20}
|
||||
|
||||
|
||||
@ -61,10 +61,19 @@ corpus=${SURVEY_CORPUS:-$root}
|
||||
|
||||
out=$(mktemp -d); trap 'rm -rf "$out"' EXIT
|
||||
|
||||
# The two that run until something stops them. Not a failure and not a match;
|
||||
# The ones that run until something stops them. Not a failure and not a match;
|
||||
# they are excluded by name because a timeout cannot tell them apart from a
|
||||
# backend that hung.
|
||||
forever="dev-loop dev-watch"
|
||||
#
|
||||
# dev-chatty is the third and is here for a sharper reason than the other two.
|
||||
# It also outlives the timeout -- it is sized to outlast test_dev's checks and
|
||||
# is killed with the connection -- but what makes it unusable here is that it
|
||||
# *prints* while it does, 4K a frame. Two backends stopped by a clock stop at
|
||||
# different lines, so the diff is a report about scheduling rather than about
|
||||
# lowering, and it fails the alias every run. dev-repl outlives the timeout in
|
||||
# the same way and is not listed, because it prints nothing and the two
|
||||
# truncations are both empty.
|
||||
forever="dev-loop dev-watch dev-chatty"
|
||||
|
||||
TIMEOUT=${TIMEOUT:-20}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user