Skip to content

openkal 0.5.0: the declarations in C, the operations a C library needed, and a conformance suite - #1

Merged
Sunrisepeak merged 11 commits into
mainfrom
feat/musl-on-openkal
Aug 20, 2026
Merged

openkal 0.5.0: the declarations in C, the operations a C library needed, and a conformance suite#1
Sunrisepeak merged 11 commits into
mainfrom
feat/musl-on-openkal

Conversation

@Sunrisepeak

Copy link
Copy Markdown
Member

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 the
canonical 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/*.h is the C form. It includes no header: the consumer it
exists for is compiled with -nostdinc, and a header that required one of the
environment's own would be unusable by it. src/*.cppm is unchanged in what it
declares and now reaches it by including the same headers in a global module
fragment, so neither form is a transcription of the other.

SURFACE.txt is normative and both forms are compared against it:

tools/check-declarations.sh compiles a translation unit naming every entity, with -nostdinc
a test in each implementation the same list, through import openkal.*
conformance/src/declarations.c the same list again, compiled by every toolchain that builds the suite

Five 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 report
    ENOENT and EEXIST distinctly, and version 0.4 mapped both to one value.
  • kal_fs_open(base, name, len, flags, out) — creation, exclusion, truncation
    and 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 discipline
    before it has transferred anything.
  • KAL_TASK_PROP_THREAD_LOCAL — whether a started context observes the
    toolchain'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 9

A 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 an
implementation 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.

mcpp build                      the core set
mcpp build --features standard  every interface version 0.5 defines
mcpp build --features fs,task   the core set, and these two
mcpp build --features full      every interface, and abi, stability and cost

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 distinguish
an 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.

job rows
declarations ubuntu × gcc, ubuntu × llvm, macos × llvm, windows × llvm, windows × msvc
substitution ubuntu × gcc, ubuntu × llvm
conformance openkal-linux × {gcc, llvm}, openkal-macos × llvm, openkal-windows × {llvm, msvc}

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-linux 0.5.0 — reimplemented on the kernel's system-call ABI
  • mcpplibs/openkal-macos 0.3.0 — reimplemented on the kernel's own calls
  • mcpplibs/openkal-windows 0.1.0 — a third implementation, on the object manager
  • mcpplibs/openkal-musl — musl 1.2.5 on openkal, on all three systems

mcpplibs and others added 11 commits August 20, 2026 07:57
…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.
@Sunrisepeak
Sunrisepeak merged commit 579cd9d into main Aug 20, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant