From 615d8c49cabf3002ef17e71323787f4a6eb172a7 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Fri, 28 Aug 2026 10:56:05 +0800 Subject: [PATCH] Record what a placed stream outlives, and the value two interfaces disagree about Two properties of kal_spawn_streams that an implementation and a caller have had to discover by running. Neither declaration changes and neither is added to, so the surface and clause 8 are untouched. A STREAM PLACED IN A SPAWN IS THE STARTED PROGRAM'S. kal_process_channel already requires a caller to release the far end after the spawn --- a parent that does not never observes the end of input on its own --- so an implementation that did not carry the stream across would make that instruction impossible to follow. The same holds for a caller that opens a file in order to place its stream, and that caller had nothing to read. ZERO IS RESERVED IN THIS STRUCTURE AND IN NO OTHER. kal_stream has no distinguished value, and an implementation whose streams are its environment's own descriptors answers kal_stdin() with zero; openkal-linux does. The two readings agree at position `in' and cannot be told apart anywhere else, so a caller that places its own standard input at position `out' is asking for something this structure cannot express. Recorded with what a caller should do about it --- report the request as unsupported rather than pass on a word that will be read as inheritance --- and with the one thing an implementation can do to remove the ambiguity for every caller. Found by openkal-musl while answering openkal-linux#13: classifying "this descriptor has no stream" by the handle's VALUE refused every spawn, because the value zero is standard input. --- include/openkal/process.h | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/include/openkal/process.h b/include/openkal/process.h index 6b48de5..525baa3 100644 --- a/include/openkal/process.h +++ b/include/openkal/process.h @@ -17,7 +17,41 @@ struct kal_process { kal_uintptr h; }; /* How a started program's standard streams are supplied. A stream handle of * zero denotes that the program inherits the corresponding stream of its * parent, which is what an environment without a general mechanism for - * passing handles can always provide. */ + * passing handles can always provide. + * + * A STREAM PLACED HERE IS THE STARTED PROGRAM'S FOR AS LONG AS THAT PROGRAM + * RUNS, AND THE CALLER MAY RELEASE ITS OWN REFERENCE AS SOON AS THE SPAWN HAS + * RETURNED. `kal_process_channel' below already requires this of a caller in as + * many words --- a parent that does not release `theirs' after the spawn never + * observes the end of input on `mine' --- so an implementation that did not + * carry the stream across would make that instruction impossible to follow. It + * is stated here for streams in general, because a caller that places the stream + * of a file it opened for the purpose is in the same position and had nothing to + * read. + * + * ⚠️ ZERO IS RESERVED HERE AND IS NOT RESERVED IN `openkal.stream', WHICH IS A + * COLLISION AND IS RECORDED RATHER THAN REPAIRED. + * + * `kal_stream' has no distinguished value: an implementation whose streams are + * its environment's own descriptors answers `kal_stdin()' with zero, and + * openkal-linux does. The two readings agree at position `in' --- placing + * standard input at standard input and inheriting it are the same act --- and + * cannot be told apart anywhere else, so a caller that places its own standard + * input at position `out' or `err' is asking for something this structure cannot + * express. + * + * ⇒ A caller that cannot tolerate the ambiguity DOES NOT PASS THE VALUE: it + * reports the request as unsupported, which is what a library above this + * interface can act upon, rather than passing on a word that will be read as + * inheritance. An implementation MAY remove the ambiguity for its own resources + * by not answering any stream enquiry with zero, and one that does so removes it + * for every caller. + * + * Repairing it in this structure would mean a second declaration --- clause 8 + * forbids altering this one --- and the case it would serve is a caller that + * sends a program's output to its own standard input. Recorded here so that the + * next implementation meets it in the specification rather than in a program + * that wrote to the wrong stream. */ struct kal_spawn_streams { kal_uintptr in; kal_uintptr out;