openkal 0.5.0: the declarations in C, the operations a C library needed, and a conformance suite - #1
Merged
Merged
Conversation
…needed The specification says its contract is a C application binary interface and distributed only C++ modules. A C translation unit has no import, and the consumer clause 1 names first is a C library, so the contract was unreachable by the party it exists for. The declarations are now stated once as C headers and exported by the modules, so there is one declaration and two ways to reach it rather than two declarations that agree. What the modules add is what C++ can check and C cannot: the layouts clause 5.3 freezes are asserted, and each interface's capability word becomes a type of its own, so testing a file-system position against the task word is a diagnostic rather than a plausible answer. Five error values are added. kal_err_not_found was already required by clause 7.7 and did not exist, so both implementations reported a missing name as kal_err_invalid and a caller could not distinguish absence from a bad handle. The other four are conditions a C library must distinguish and the closed set could not express. Four operations are added, each because reconstructing it above what existed leaves the caller silently wrong rather than merely bounded: kal_fs_open states truncation, exclusion and appending as one intent; kal_fs_truncate sets the length of an open file; kal_fs_file_info answers about a handle rather than about a name that may since have been reused; kal_stream_props answers the question a C library must ask before it has transferred anything. One property is added. KAL_TASK_PROP_THREAD_LOCAL reports whether a started context observes the toolchain's thread-local storage, which a C library keeps its per-context state in and therefore cannot be ported without.
…implementation provides An interface is the unit of provision and one an implementation does not provide is absent as a link-time definition, so a suite that examined all eight unconditionally would fail to link against a conforming implementation of five and would report nothing rather than reporting five. Each interface is a feature, and a section whose feature is inactive reports itself as not examined. Three counts rather than two. A suite reporting only what held and what did not cannot distinguish an interface that behaved from an interface it never examined; every unmade observation carries the reason it was not made. Three kinds of examination beyond behaviour, each optional: the shapes clause 5.3 freezes checked against the implementation that was linked, the same operation twenty thousand times, and what an operation costs. A cost is reported and never asserted. The suite depends on openkal and on the language. It writes its report through openkal.stream and takes its durations from openkal.time, because it must run against an implementation in a program that carries no other runtime.
… the suite from one place Three changes, all of them consequences of building the specification with more than one compiler family. The C form of the declarations obtained the width of a machine word from __UINTPTR_TYPE__, which two of the three compilers publish. The third publishes the property the type is defined by and not the type, so the type is now written from the property under that compiler. Taking it from that compiler's own header would have given the file an include, and the consumer the C form exists for --- a C library being ported onto openkal, compiled with -nostdinc --- has none. The same compiler spells the attribute on a function that does not return differently again in its C mode. A caller that does not know the function cannot return is a caller the compiler warns about at the point where control appears to continue, which is the point the attribute exists to remove, so the spelling is written out rather than the attribute omitted. tools/check-declarations.sh needs a driver it can pass -nostdinc to and that compiler has no such spelling, so a header that failed to compile as C under it would go unnoticed. conformance/src/declarations.c names the same fifty-one entities and is compiled by every toolchain that builds the suite; the tool writes it, and detects a copy that has fallen behind the list, since a stale copy would be compiled happily by all three. No name is referenced in an evaluated context: the suite is composable, and a file requiring all fifty-one definitions would fail to link against a conforming implementation of three interfaces. tools/run-conformance.sh supplies the implementation under examination. The suite's manifest names openkal and no implementation, which is what makes it an instrument rather than an implementation's dependant; supplying one is a few lines of manifest, and they are here rather than repeated in every implementation's continuous integration. It also sets the one variable the suite needs a runner to set, which is the difference between ninety-one observations and ninety. Continuous integration now covers three compiler families across three systems, and asserts composability directly: a run that selects three interfaces produces a report on three rather than a link failure.
…piling programs above an implementation Two additions, and neither was visible in the specification text. Both became visible when ninety-seven ordinary programs were compiled above an implementation and run. kal_fs_set_modified is the inverse of an enquiry the interface already had. A program that copies a file and preserves its dates, one that extracts an archive, and one whose whole purpose is to mark a file as current could not be written above the interface, and each is a program a C library above openkal is expected to host. It takes the open file rather than the name, for the reason kal_fs_file_info does; it requires the file to have been opened for writing, because one environment decides at the point of opening what may afterwards be done with a file; and the conformance procedure compares whole seconds, because the three environments record the time to a nanosecond, to a microsecond and to a hundred nanoseconds and an interface requiring more would be requiring of every environment what one of them happens to provide. Clause 7.11. "." is now reserved and denotes the directory itself. openkal names things relative to a directory a program holds, and every operation that answers a question about a thing takes a name --- so a program holding a directory had no way to ask a question about that directory. One reserved word rather than five more operations; it does not introduce a way to ascend, and ".." remains invalid. Two of the three environments reserve the same word already and the third expresses the same thing as an empty name beside a handle. Clause 7.12. The three fixed widths the operations use are now openkal's own typedefs rather than the compiler's spellings at each use. That reached for a spelling one of the three compilers this specification is built with does not have, and it stated in eight places a decision that belongs in one. The conformance suite gains three observations, and the reserved name is observed in both of the ways a program uses it, because an implementation may accept it in one operation and not in the other --- one of the three accepted it in neither until this was written. Ninety-five observations.
…e third compiler The suite builds a mutex out of openkal's suspension primitive, and a mutex needs an atomic exchange and an atomic compare-and-exchange. Two of the three compilers this package is built with publish those as builtins of the same name. The third publishes them under different names, in a different form, and only for a signed thirty-two-bit word. okc.atomic states them once. It states rather than imports them: the suite depends on openkal and the language and does not import std, because it must run against an implementation in a program that carries no other runtime, and a suite reporting on an implementation while resting on facilities that implementation may be the only supplier of would be reporting on itself.
The suite observed that a started context's identity differs from the starting context's, and an implementation that answered zero for every context it started satisfied it --- zero does differ from the starting context's identity, and it is useless to a consumer, which reads it as "no entry" however many times it is written. What that implementation produced was a C library above openkal reading its own per-context state through a null pointer, four layers from the answer. Two observations replace the assumption. A started context's identity is not zero, and the identities of four contexts that ran at the same time are distinct from each other rather than each from the starting one's --- the second being what an implementation answering one wrong value for all of them fails.
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.
What this changes
Version 0.4 stated its contract as a C application binary interface and
distributed only C++ modules. A C translation unit has no
import, and thecanonical consumer of this contract — a C library ported onto openkal — is a C
translation unit. This release distributes the declarations in both forms and
states how the two are held to declare the same entities.
The rest of the release is what porting musl 1.2.5 onto openkal found missing.
The declarations, in two forms
include/openkal/*.his the C form. It includes no header: the consumer itexists for is compiled with
-nostdinc, and a header that required one of theenvironment's own would be unusable by it.
src/*.cppmis unchanged in what itdeclares and now reaches it by including the same headers in a global module
fragment, so neither form is a transcription of the other.
SURFACE.txtis normative and both forms are compared against it:tools/check-declarations.sh-nostdincimport openkal.*conformance/src/declarations.cFive error values, four operations, one property
Each was added because an implementation of a C library above openkal could not
be written without it, and each is recorded in SPEC.md with the case that
required it.
kal_err_not_found,kal_err_exists,kal_err_not_empty,kal_err_is_directory,kal_err_not_directory— a C library must reportENOENTandEEXISTdistinctly, and version 0.4 mapped both to one value.kal_fs_open(base, name, len, flags, out)— creation, exclusion, truncationand appending are conditions of the opening, not operations that follow it. A
program that opened and then truncated could be stopped between the two.
kal_fs_truncate,kal_fs_file_info.KAL_STREAM_PROP_INTERACTIVE— a C library must choose a buffering disciplinebefore it has transferred anything.
KAL_TASK_PROP_THREAD_LOCAL— whether a started context observes thetoolchain's thread-local storage. One implementation cannot provide it, and a
C library above openkal must know rather than assume.
conformance/— the behavioural half of clause 9A sub-project, arranged as the mcpp benchmark harness is: C++23, one module per
concern, interface and implementation separated. It depends on openkal and the
language; there is no
import std, because it must run against animplementation in a program that carries no other runtime.
It is composable, because openkal is. An implementation provides an interface in
whole or not at all, so a suite that examined all eight unconditionally would
fail to link against a conforming implementation of three and would report
nothing rather than reporting three. Each interface is a feature.
It reports three counts and the third is the one to read:
91 held, 0 did not hold, 0 not observed. A suite reporting only the first two cannot distinguishan interface that behaved from an interface it never examined.
Continuous integration
Three compiler families across three systems, because a contract that holds only
under the compiler its author used is a description of that compiler.
The conformance job additionally asserts composability directly: a run selecting
three interfaces produces a report on three rather than a link failure.
Companion changes
mcpplibs/openkal-linux0.5.0 — reimplemented on the kernel's system-call ABImcpplibs/openkal-macos0.3.0 — reimplemented on the kernel's own callsmcpplibs/openkal-windows0.1.0 — a third implementation, on the object managermcpplibs/openkal-musl— musl 1.2.5 on openkal, on all three systems