0.9.0 — one idea has one spelling, and every declaration can cross a boundary - #20
Merged
Conversation
…a boundary
Compile-time portability was the floor and is no longer mistaken for the goal.
Every interface, operation and structure is now shaped so that it can also be
crossed at run time, and clause 3's table states for each which boundaries it
crosses --- a statement about the declarations, which an implementation cannot
violate and only a declaration can.
Thirteen of the fifteen interfaces could not be crossed, and five causes stood
in the way rather than fifteen:
ten exported data objects every report becomes an operation, and one
that varies between the RESOURCES of an
interface takes the resource
five internal pointers a value is copied into the caller's buffer and
the length reported is the value's own
seven two-word results a transfer returns one signed word: the count,
or the negated condition when none moved
two entries worded as a call an entry is an address at which a context
begins; no return address exists
one clause 6.5 settles availability at production, which
an artifact produced once for many
environments cannot use, so `openkal.exec'
reports it as well
The transfer result was going to gain an out-parameter. It did not: the only
consumer of the two-word form collapsed the pair by hand at every site and by
one rule --- report what moved, or the condition when nothing did --- so the
pair was never the shape a caller wanted. `kal_io_result` is gone.
Also, and not incidentally:
* `standard` and `hosted` are withdrawn. A class named after a kind of
environment is falsified by an environment nobody had in mind, and this one
was falsified inside its own ecosystem: a C library is hosted above an
implementation providing none of the three interfaces the class promised.
* `kal_memory_granularity` exists because a C library above openkal had no
way to learn the quantum and fixed it at four kilobytes when it was built,
which is wrong on every machine whose quantum differs from the one it was
built for.
* `kal_node_info` carries its own size, reports which fields it filled, and
carries an opaque identity --- three questions that were conflated and that
left two different files reported as the same file, with no error.
* Links enter `openkal.fs`, and resolution is stated in `fs.h` beside every
operation it governs. It was stated only in clause 11, and two call sites
of one implementation consequently chose opposite directions.
* `kal_fs_open_file` is deleted; `kal_fs_stream` and `kal_spawn_streams`
carry their type; `kal_endpoint.addr_len` no longer freezes a per-word-size
difference; clause 7.2 requires a forged handle to be refused; a name
length bound is reportable.
* `kal_version` and `kal_interfaces` are exported by every implementation and
belong to no interface, so a consumer with no linker to ask can ask.
Two defects in the suite's own machinery were found while running it, and both
were producing false greens:
* `run-conformance.sh` chose the binary with `find | head -1`. `target`
accumulates a directory per fingerprint, so a run reported `143 held` from
a binary built two days earlier that contained none of the observations
just added. Suites are now removed before building, and finding more than
one afterwards is a condition rather than a choice.
* `full` was defined as `["standard", …]`. Withdrawing `standard` left it
naming four kinds of examination and no interface: 168 held became 119
held, with an exit status of zero both times.
168 observations hold against openkal-linux 0.7.0, up from 143.
⚠️ ⚠️ THE PROPERTY A DISTRIBUTED BINARY RESTS UPON WAS NOT MERELY UNTESTED. THERE WAS NO ARTIFACT OF THE SHAPE THAT COULD TEST IT. The conformance suite asserts that one artifact, built and run in one place, behaves as this specification says. What a distributed binary needs is stronger: that one binary, BUILT ONCE, behaves as specified against an implementation IT WAS NOT COMPILED AGAINST. Every target had exactly one implementation, the choice was made at dependency resolution, and the implementation was linked in --- so there was nothing to run twice. `tools/run-abi-test.sh` produces one. It builds openkal-linux as a shared object; builds a second implementation FROM THE SAME OBJECTS, with six names renamed out of the way and answered by `conformance/abi/interposer.cpp` instead; builds one probe against neither by name; and runs that one binary against each, asserting that it is not rebuilt in between. ⭐ EVERY ANSWER IN THE SECOND RUN IS A BRANCH NOTHING HERE HAD EVER TAKEN: satisfies-floor yes -> no an implementation older than the consumer's declarations, which is what the version operation exists to let a consumer refuse granularity 4096 -> 65536 a consumer that fixed the quantum when it was built is wrong here and nowhere else has-space yes -> no an optional interface declined AT RUN TIME, where there is no linker to report it exec-available yes -> no clause 6.5's objection was that a path no artifact takes is a path nothing has verified. This is the artifact that takes it knows-identity yes -> no an implementation that cannot distinguish one node from another leaves the position clear, and a caller is TOLD rather than told wrongly The interposer is not a port: it delegates every operation to the implementation beneath and changes six answers. That is a second implementation in the only sense a consumer can observe --- a different shared object, exporting the same surface, answering differently --- and it is a hundred lines rather than three thousand.
⚠️ ⚠️ THIS README TOLD A READER TO ASK FOR `openkal = "0.5.1"` WHILE THE PACKAGE WAS AT 0.9.0 --- four minor versions, each of which had changed the surface. A reader following it got declarations that do not match the documentation around them, and nothing said so. ⭐ THE STALENESS IS NOT THE FINDING. That it was invisible is. The surface is checked against SURFACE.txt, the declarations are checked in both forms, the behaviour is checked by the conformance suite --- and the one line a reader actually types was checked by nobody. `tools/check-readme-versions.sh` checks that every `<package> = "<version>"` in a README agrees with that package's own manifest. It reports `?` rather than passing where the package's tree is not beside it, so its "no" and its "did not run" do not read the same --- which is the failure mode this ecosystem keeps meeting and is worth two lines to avoid.
The self-review of this change found four things it had missed, all of the same shape: a place that names the surface and is not compiled by anything that would notice it had moved. examples/ was one. Three programs there still returned kal_io_result, called kal_fs_open_file, and borrowed environment strings by pointer. They build in continuous integration, so they would have failed there; they had not been looked at because nothing about them says which version they are written for. The portable program's manifest also asked for openkal 0.8.0 while claiming to demonstrate the current contract to a reader who copies it. The frozen-layout probe in this workflow asserted kal_node_info's 0.8 layout (size at 0, 24 bytes). It now asserts the 0.9 one, and asserts the property that layout exists to have: self_size is at offset zero, because an implementation built later reads it before it touches anything else. Both widths were compiled locally before this was pushed. SURFACE.txt's self-description group was spelled `# openkal ---', which does not match the `# openkal.<name>' pattern openkal-linux derives its module imports from. The two names in it were listed without an import and its generated test failed naming the names rather than the heading. check-readme-versions.sh now checks manifests as well as READMEs, which is where the drift above was. Writing that found a defect in the tool itself: a manifest with no version pin makes grep exit 1, and under pipefail that ended the loop -- so the survey stopped at the first such file and reported "ok" having examined eight of eleven pins. It now carries a denominator, and a floor that denominator cannot supply: the package's own root manifest must have been reached.
Five things that could not have been written before the change was implemented: two aarch64 defects the leg had carried since it was written, an interposer whose value is that nothing of its shape existed, eight jobs that could only be green when the change was already published, a test that asserted a limitation that was then lifted, and a defect three layers up whose every ingredient was correct.
The last defect this change produced, and the one introduced BY the correction the design recommends: openkal's granularity is not musl's page size, an implementation with no memory management unit answers one, and the assertion that should have caught it passed because one is a positive power of two.
…ot about aarch64 The leg that surfaces a defect is not the axis the defect is on. musl does not pass the vectors to .init_array and glibc does, which x86_64-linux-musl settles and aarch64 alone would have mis-attributed; and openkal-windows's .def check was right with the wrong scope, reporting a number that was of the names it knew about.
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.
Compile-time portability was the floor and was being mistaken for the goal. Every
interface, operation and structure is now shaped so that it can also be crossed
at run time, and clause 3's table states for each which boundaries it crosses.
Thirteen of the fifteen interfaces could not be crossed, and five causes stood
in the way rather than fifteen:
openkal.execreports it as well⭐ The transfer result was going to gain an out-parameter. It did not. The only
consumer of the two-word form collapsed the pair by hand at every site and by one
rule — report what moved, or the condition when nothing did. The pair was never
the shape a caller wanted.
kal_io_resultis deleted.Also
standardandhostedare withdrawn. A class named after a kind ofenvironment is falsified by an environment nobody had in mind, and this one was
falsified inside its own ecosystem: a C library is hosted above an
implementation providing none of the three interfaces the class promised.
kal_memory_granularity— a C library above openkal had no way to learn thequantum and fixed it at 4 KiB when it was built. One real system has two
quanta (4 KiB pages, 64 KiB allocations), which is why this is one number
defined as the coarsest that is always safe rather than a page size.
kal_node_infocarries its own size, reports which fields it filled, andcarries an opaque identity. Those three questions were conflated, and the
consequence was two different files reported as the same file with no error.
openkal.fs, and resolution is stated infs.hbeside everyoperation it governs. It was stated only in clause 11, and two call sites of
one implementation consequently chose opposite directions.
kal_fs_open_filedeleted; typed handles atkal_fs_streamandkal_spawn_streams;kal_endpoint.addr_lenno longer freezes a per-word-sizedifference; clause 7.2 requires a forged handle to be refused; a name-length
bound is reportable;
kal_versionandkal_interfacesare exported by everyimplementation and belong to no interface.
One binary, two implementations
tools/run-abi-test.shis new and is the reason this could land at all. Theproperty a distributed binary rests upon was not merely untested — there was no
artifact of the shape that could test it, because every target had exactly one
implementation and it was linked in.
It builds openkal-linux as a shared object, builds a second implementation from
the same objects with six names renamed and answered by an interposer, builds one
probe against neither by name, and runs that one binary against each. Every answer
in the second run is a branch nothing here had ever taken: an implementation older
than the consumer's declarations, a coarser quantum, an optional interface
declined at run time, executable memory withheld, and an implementation that
cannot distinguish one node from another.
Two false greens in the suite's own machinery, both found while running it
run-conformance.shchose its binary withfind | head -1.targetaccumulates a directory per fingerprint, so a run reported
143 heldfrom abinary built two days earlier containing none of the observations just added.
fullwas defined as["standard", …]. Withdrawingstandardleft it namingfour kinds of examination and no interface: 168 held became 119 held, with an
exit status of zero both times.
168 observations hold against openkal-linux 0.7.0, up from 143.