The unit a program belongs to is a resource, not a flag --- clause 7.1 said so - #26
Merged
Conversation
Three declarations --- `kal_process_spawn', `..._with' and `..._bound' --- were never three operations. They were one operation and three MODIFIERS of it, spelled apart for exactly one reason: clause 8 forbids adding an argument to a declaration that exists, so each modifier had to arrive as a new name. ⭐ The 0.10 comment on `..._bound' predicted where that ends, in terms: "Declaring every combination is how an interface acquires four spawns and then eight." Two more modifiers then arrived TOGETHER --- the directory a program runs in, and terminating what a started program itself started --- and the evidence that they arrive together is a single consumer signature whose child calls `setpgid(0, 0)' and `chdir(cwd)' on adjacent lines. Four orthogonal modifiers is sixteen declarations, each written in every implementation. ⇒ The modifiers moved into `struct kal_spawn' and the family collapsed to one declaration. `..._with' and `..._bound' are REMOVED rather than kept beside it: openkal has no users outside this ecosystem, so there is nobody to keep a second spelling for, and an interface offering two ways to say one thing must explain the difference for ever. A caller written against the old shape fails to COMPILE, which is the loud failure and not the quiet one.⚠️ Clause 8 was set aside to do this, deliberately and once. SPEC entry 14 says so, says what it costs --- `kal_spawn' is frozen by clause 5.3, so a future modifier carrying a PARAMETER rather than a flag will still need its own declaration --- and says that this is not a precedent for doing it again once there is written code to protect.⚠️ The conformance suite observes the REFUSAL and not the effect, and says why: openkal has no operation that reads a working directory (deliberately --- it has no ambient one) and none that enumerates processes, so nothing written against openkal alone can tell whether an implementation honoured either flag. They are in the same category as the streams a spawn is given. The effects are observed where they can be, in openkal-musl's probes. Surface 101 -> 99. Conformance against openkal-linux: 140 held, 0 did not.
…remember it A macro does not cross a module boundary, and twice a capability word was added to a header, used from a module consumer, and failed to compile there: `KAL_LOCK_*' in 0.10 and `KAL_SPAWN_*' in 0.11. Both were found by a build in another repository. The interface modules give the well-named subset (`kal::fs::lock::exclusive'); what was missing was anything that made the mapping COMPLETE. ⇒ src/macros.cppm is generated from the headers and CI regenerates and diffs it, so a macro added without a module spelling is a red build here rather than a consumer's compile error two repositories away. 80 constants; the one function-like macro is listed as skipped, so "absent" is a stated fact rather than a gap.⚠️ THE DECLARED NAME CANNOT BE THE MACRO'S NAME. The macro is still defined when the constant is declared, so `constexpr kal_uintptr KAL_VERSION_MAJOR = …' expands on the left as well and becomes `constexpr kal_uintptr 0u = …'. Lower-casing gives an identifier the preprocessor does not touch, and matches the spelling the interface modules already use.⚠️ ⚠️ AND THE GUARD DID NOT BITE THE FIRST TIME. `git diff` against an untracked file reports nothing, so the step passed while checking nothing --- which is worse than having no step at all. Verified in both directions after tracking the file: adding a macro to a header produces a diff, restoring the header clears it.
…al means The generated constants keep the C spelling with a `_M' suffix rather than being lower-cased. The C spelling is what a reader is looking for --- they arrived here because a header said `KAL_SPAWN_OWN_JOB' --- so it is kept and marked; `KAL_SPAWN_OWN_JOB_M' is a different token, so the preprocessor leaves it alone.⚠️ ⚠️ AND SPEC ENTRY 15 CORRECTS SOMETHING I HAD WRITTEN AS AN IMPLEMENTATION LIMIT. openkal-windows refuses KAL_SPAWN_OWN_JOB, and I recorded the reason as "nowhere to keep the job handle". That reads as a shortcoming of that implementation. It is not. Clause 7.1 states the test mechanically: an implementation that must maintain a translation table, a registry, or a name resolver in order to satisfy this specification indicates that THE SPECIFICATION has taken a shape borrowed from one environment, and the shape is at fault. Two implementations satisfy the flag with no stored state because `getpgid(pid) == pid' recovers the association from the kernel; the third would need a registry. So the flag assumes an environment where the unit is discoverable from a member, and that assumption is the defect. ⇒ Entry 15 records it as OPEN rather than settled, and says why the obvious reshape --- a unit the caller holds --- is not simply asserted here: Windows makes such a unit externally and assigns members, while a process group is made by a member from inside, and a shape natural to one and not the other is the same defect spelled the other way round.
…1 said so 0.11 first spelled this as `KAL_SPAWN_OWN_JOB': make the started program a unit, and let `kal_process_terminate' reach it afterwards. Two implementations satisfied that with no stored state, because `getpgid(pid) == pid' recovers the association from the kernel. The third could not: it can FORM a unit --- a job object is exactly this --- but cannot recover one from a process handle, and `kal_process' is one word already holding the process. ⭐ CLAUSE 7.1 STATES MECHANICALLY WHAT THAT MEANS: an implementation that must maintain a translation table, a registry, or a name resolver in order to satisfy this specification indicates that THE SPECIFICATION has taken a shape borrowed from one environment, and the shape is at fault. So the flag was the defect.⚠️ AND THE OBVIOUS REPAIR WOULD HAVE MOVED THE DEFECT RATHER THAN REMOVED IT. An operation that opens an empty unit is natural where units are created and then filled, and impossible where a unit is created BY its first member --- a process group's identity is a process's, and there is nothing to open. That is the same clause 7.1 finding spelled the other way round. ⇒ So the identity is established at the first start and reported to the caller: `kal_spawn.job' is null, or points at a word that is zero (form a unit, and its identity is written here) or non-zero (join that one). Both kinds of system perform that remembering nothing. ⭐ Two things improve that were not the point: · `kal_process_terminate' now means ONE program, always. Under the flag its meaning turned on a property of the handle no caller could see, which is the kind of hidden state this interface exists to avoid. · Several programs can be started into one unit, which a flag cannot say --- and a shell runner with one timeout over several commands wants exactly that.⚠️ Recorded rather than glossed, at the declarations: a unit must NOT be ended by releasing it (this system's job objects can be asked to, and must not be, or one operation would mean two things); and a process group is named by an identifier that is reused, so the two kinds of unit are not equally strong. Conformance against openkal-linux: 141 held, 0 did not hold.
…self ⭐ THE FLAG BECAME A HANDLE, AND CLAUSE 7.1 IS WHAT DECIDED IT. `KAL_SPAWN_OWN_JOB' asked that a started program form a unit and that `kal_process_terminate' reach it afterwards. Two implementations satisfied that free --- `getpgid(pid) == pid' recovers the association from the kernel --- and the third could not: it can form a unit but cannot recover one from a process handle. Clause 7.1 says an implementation that must keep a registry to satisfy this specification indicates the SPECIFICATION took a shape borrowed from one environment.⚠️ AND THE OBVIOUS REPAIR WOULD ONLY HAVE MOVED IT. An operation that opens an empty unit is natural where units are filled after creation and impossible where a unit is created BY its first member. So the identity is established at the first start and reported back: `kal_spawn.job' is null, or points at zero (form one, receive its identity) or at a name (join it). Both kinds of system do that remembering nothing. `kal_process_job_enter' puts THE CALLER into a unit, which the spawn field cannot say and which a C library needs: a copy made by `fork' wants to lead a unit BEFORE it replaces itself, and that is what every shell and every runner with a timeout is written as. ⭐ Two things improve that were not the goal: `kal_process_terminate' means one program again, always --- under the flag its meaning turned on invisible state --- and several programs can share one unit, which a flag cannot express. Recorded at the declarations rather than left to be found: releasing a unit must not end it (this system's job objects can be asked to, and must not be, or one operation would mean two things), and a unit named by a process identifier is weaker than one named by a handle, because identifiers are reused.
… signals ⭐⭐ THE DECOMPOSITION IS THE DESIGN. "Signals" is not one capability, it is a Unix mechanism carrying eight different uses, and this interface already expressed seven of them: terminating a program, terminating a unit, a stream whose far end has gone, a started program that ended, a bound upon waiting, waking another context. The eighth had no spelling at all --- THIS PROGRAM HAS BEEN ASKED TO END --- and it is the one a shell, a server and a runner with a timeout are all written around.⚠️ AND SIGNALS THEMSELVES CANNOT BE THE INTERFACE. They are one environment's shape: a numbered set, a disposition, a handler interrupting whatever ran. The other system has console control routines, which arrive ON A CONTEXT OF THEIR OWN and cover different causes; two targets have no processes to signal at all. Clause 7.1 names that shape exactly. ⇒ `kal_process_stop_requested' answers a word: zero, then non-zero, never cleared. Read it, or WAIT on it with `kal_task_wait' --- which this specification already has and `openkal.timeout' already bounds. No new concept, and none of the re-entrancy rules a handler forces on every caller.⚠️ Stated rather than left to be discovered: it is a notice and not a veto; it does not say who asked; and it says NOTHING about an end that cannot be observed --- the un-declinable signal, the abrupt termination, `kal_process_job_terminate' --- because nothing anywhere gets to notice those. ⭐ That symmetry is why this is implementable on both kinds of system rather than on one.⚠️ ⚠️ AND THE CONFORMANCE SUITE CAUGHT ITS OWN AUTHOR AGAIN, for the third time: "the capability word contains no position the specification has not assigned". A new property bit and an unextended mask is evidently the mistake I make every release, and it is evidently the check that finds it. Surface 102 -> 103. Conformance against openkal-linux: 141 held, 0 did not.
The proposal as written had the unit as a FLAG on the spawn. Clause 7.1 decided against it — an implementation that must keep a registry to satisfy a specification indicates the specification borrowed a shape — and the obvious repair, an operation that opens an empty unit, would have moved the same defect to the other kind of system rather than removing it. ⇒ §2.4 records both the first shape and why it changed, rather than presenting the final one as though it had been obvious. §2.5 adds the stop-request word and the decomposition that produced it: "signals" is eight uses in one mechanism, this interface already expressed seven, and only the eighth needed a spelling.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
0.11 first spelled this as
KAL_SPAWN_OWN_JOB': make the started program a unit, and letkal_process_terminate' reach it afterwards. Two implementationssatisfied that with no stored state, because
getpgid(pid) == pid' recovers the association from the kernel. The third could not: it can FORM a unit --- a job object is exactly this --- but cannot recover one from a process handle, andkal_process' is one word already holding the process.⭐ CLAUSE 7.1 STATES MECHANICALLY WHAT THAT MEANS: an implementation that must
maintain a translation table, a registry, or a name resolver in order to satisfy
this specification indicates that THE SPECIFICATION has taken a shape borrowed
from one environment, and the shape is at fault. So the flag was the defect.
operation that opens an empty unit is natural where units are created and then
filled, and impossible where a unit is created BY its first member --- a process
group's identity is a process's, and there is nothing to open. That is the same
clause 7.1 finding spelled the other way round.
⇒ So the identity is established at the first start and reported to the caller:
`kal_spawn.job' is null, or points at a word that is zero (form a unit, and its
identity is written here) or non-zero (join that one). Both kinds of system
perform that remembering nothing.
⭐ Two things improve that were not the point:
· `kal_process_terminate' now means ONE program, always. Under the flag its
meaning turned on a property of the handle no caller could see, which is
the kind of hidden state this interface exists to avoid.
· Several programs can be started into one unit, which a flag cannot say ---
and a shell runner with one timeout over several commands wants exactly that.
releasing it (this system's job objects can be asked to, and must not be, or one
operation would mean two things); and a process group is named by an identifier
that is reused, so the two kinds of unit are not equally strong.
Conformance against openkal-linux: 141 held, 0 did not hold.