The path was ceremony. Under [flan dev] the daemon already decides where it wants to talk to the program and writes it into FLAN_AGENT_SOCKET, which the C side has always honoured over whatever the source named — so the argument was a value nothing read. Outside the daemon any path will do as long as the program says which one it picked. So [start] becomes a macro over two functions: no argument picks the daemon's socket if there is one and otherwise /tmp/flan-agent-<pid>-<clock>.sock, announced on stderr because a socket nobody can name is a socket nobody can connect to. The explicit form stays for a program that wants a fixed path. Extra arguments are spliced into [start-at] rather than dropped, so the arity refusal is still the checker's, at the call site. And the socket is removed on the way out. The bind stashes the path it bound and registers an atexit; the two paths that leave by _exit — the break loop's [abort] and the orphan handler — unlink it by hand, as the orphan handler already did for its own copy of the path. Nothing else takes it away: under the daemon it sits in a temp directory that is still never removed (FIX.org), and outside there is no daemon at all. test/programs/dev-loop.flan now names no socket, which puts the whole daemon block in test_dev.ml behind the zero-argument form; agent-auto.flan is the standalone half, reached only through the line the program printed, and it pins the second (agent/start) as a no-op and the socket as gone at exit.
76 lines
3.9 KiB
C
76 lines
3.9 KiB
C
/* The dev agent in a browser: the three calls, doing nothing, and saying so.
|
|
*
|
|
* Build selects this file over flan_agent.c on --target=web, and only there
|
|
* (see Build.select_csrcs). The reason is not the compile error that led here
|
|
* — emscripten's headers do not pull <sys/time.h> in transitively, so
|
|
* flan_agent.c fails on `struct timeval` at line 426 — because an #include
|
|
* would have fixed that and fixed nothing real. The reason is structural:
|
|
*
|
|
* THE AGENT IS A SOCKET SERVER, AND A BROWSER HAS NO SOCKETS.
|
|
*
|
|
* flan_agent_start binds an AF_UNIX socket and hands it to a listener thread.
|
|
* There is no such address family under emscripten, nothing to listen on, and
|
|
* nothing that could connect if there were. Adding the include produces an
|
|
* agent that compiles, links, starts, and can never accept a connection.
|
|
*
|
|
* ── Why a no-op here, when `barf` on the web signals instead ────────────
|
|
*
|
|
* NEXT.md decision 2 rejected a silent no-op for `barf` in as many words: a
|
|
* no-op write is how a save file disappears with nothing said. The two look
|
|
* contradictory and are not, and the difference is what the caller loses.
|
|
*
|
|
* `barf` is asked to make something durable. A no-op returns success to a
|
|
* program that now believes the bytes are on disk; the loss is real, it is the
|
|
* user's, and it is discovered later or never. So the web signals a condition
|
|
* and the program decides.
|
|
*
|
|
* The agent is asked to accept redefinitions from an editor. On the web there
|
|
* is no editor, no socket, and no session — `--dev` is refused by name on
|
|
* every wasm target, so a web build has no cells to install a redefinition
|
|
* into even if one arrived. There is nothing to lose because there was never
|
|
* anything there. sand.flan already says the same thing about a *native*
|
|
* release build, at the call site:
|
|
*
|
|
* "Building without --dev is fine — nothing has cells to install into, so a
|
|
* module is refused on the listener thread and the loop never notices."
|
|
*
|
|
* A web build reaches that same outcome by a shorter route. The no-op is not
|
|
* hiding a failure; it is the truthful implementation of "nothing is
|
|
* available here", which is the case where Odin's own `.Unsupported` stubs are
|
|
* right and the case decision 2 was careful to say it was not.
|
|
*
|
|
* ── Why not refuse vendor:agent on a web target ─────────────────────────
|
|
*
|
|
* It was the other candidate and it is ruled out by arithmetic, not taste.
|
|
* Flan has no conditional compilation, so a program cannot say "skip this on
|
|
* web". sand.flan calls (agent/start ...) unconditionally, Reach cannot prune
|
|
* a package something reachable calls into, and a build-time refusal would
|
|
* therefore mean sand.flan does not build for the browser at all without being
|
|
* edited into a second program. Refusing is only honest when the caller has a
|
|
* way to not ask; here it has none.
|
|
*
|
|
* ── What the return values say ──────────────────────────────────────────
|
|
*
|
|
* start returns -1, which is the same "could not listen" flan_agent.c returns
|
|
* for a path it cannot bind, so a caller that checks gets the answer it
|
|
* already knows how to read. poll and wait return 0 — no redefinitions
|
|
* installed — which is the truth and is what the native build returns on every
|
|
* frame that nothing arrived on. */
|
|
|
|
#include <stdint.h>
|
|
|
|
int32_t flan_agent_start(const uint8_t *path, int64_t len) {
|
|
(void)path; (void)len;
|
|
return -1;
|
|
}
|
|
|
|
/* The zero-argument form, which on a native build picks a socket path for
|
|
* itself. There is still nothing to bind here, so it answers exactly what the
|
|
* explicit form does and prints nothing: a path chosen for a socket that
|
|
* cannot exist would be a sentence about nothing. */
|
|
int32_t flan_agent_start_auto(void) { return -1; }
|
|
|
|
int32_t flan_agent_poll(void) { return 0; }
|
|
|
|
int32_t flan_agent_wait(int32_t ms) { (void)ms; return 0; }
|