library: five protocols speak the envelope
The folded header stops being a rule in a document and becomes the layout on the wire. Verbs number from sixteen, leaving describe, enumerate, subscribe and unsubscribe reserved and answered the same way by every provider — none of them writes a line to do it. What each protocol used to carry in a field of its own now travels in the header: a vfs node and a display layer are the packet's target, and a reply opens with a status the envelope stamps rather than one each protocol spelled for itself. Display gains the most. One forty-byte request had served eleven verbs, so attach_scanout smuggled stride through x, refresh through y and format through colour, and every coordinate crossed as a bitcast. Per-operation structs end all three: the fields have their own names and their own signs, and the tile payload grows to 224 bytes because the prefix shrank. Scanout loses a message maximum of 64 it had no business declaring — it answers calls, and the floor for a call is 256 — and virtio-gpu stops hard-coding that number at its harness. Two changes are semantic rather than notational. A directory now ends at an entry with no name, because the fixed part of a reply always travels and a zero-length reply no longer exists to mean anything. And input joins the service harness, the last loop in the tree that answered no ping and heard no terminate; its subscriber table, its pruning and its fan-out are the same code, and a shutdown now asks it to stop instead of killing it. A new conformance case reads the registry's own listing and asks every protocol it finds for its name, its version and its verb count, then offers a verb nobody defines and requires -ENOSYS — the envelope's promise, checked against providers rather than against itself. What it cannot reach in that boot it names on the serial line instead of passing quietly. Suite 110/110.
This commit is contained in:
@@ -3,7 +3,8 @@
|
||||
//! every conversation depend on the contract by name; neither reaches into the
|
||||
//! other's files. Pure flat wire types: no protocol module imports anything.
|
||||
//!
|
||||
//! One module here is not a protocol but the shape the others are written in:
|
||||
//! One module here is not a protocol but the shape the others are written in,
|
||||
//! and therefore the one module every other one imports:
|
||||
//!
|
||||
//! envelope : the packet prefix + comptime Define (docs/os-development/protocol-namespace.md)
|
||||
//!
|
||||
@@ -19,10 +20,12 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
// Not a protocol, hence not `-protocol`: the envelope is what a protocol is
|
||||
// defined *through*, so it is built first and handed to every protocol
|
||||
// below as their one import.
|
||||
const envelope = b.addModule("envelope", .{ .root_source_file = b.path("envelope/envelope.zig") });
|
||||
|
||||
for ([_]struct { name: []const u8, root: []const u8 }{
|
||||
// Not a protocol, hence not `-protocol`: the envelope is what a
|
||||
// protocol is defined *through*.
|
||||
.{ .name = "envelope", .root = "envelope/envelope.zig" },
|
||||
.{ .name = "vfs-protocol", .root = "vfs/vfs-protocol.zig" },
|
||||
.{ .name = "input-protocol", .root = "input/input-protocol.zig" },
|
||||
.{ .name = "block-protocol", .root = "block/block-protocol.zig" },
|
||||
@@ -32,21 +35,35 @@ pub fn build(b: *std.Build) void {
|
||||
.{ .name = "scanout-protocol", .root = "scanout/scanout-protocol.zig" },
|
||||
.{ .name = "power-protocol", .root = "power/power-protocol.zig" },
|
||||
}) |protocol| {
|
||||
_ = b.addModule(protocol.name, .{ .root_source_file = b.path(protocol.root) });
|
||||
_ = b.addModule(protocol.name, .{
|
||||
.root_source_file = b.path(protocol.root),
|
||||
.imports = &.{.{ .name = "envelope", .module = envelope }},
|
||||
});
|
||||
}
|
||||
|
||||
// Standalone `zig build test` for this domain alone; the root build keeps
|
||||
// its aggregate test step.
|
||||
const test_step = b.step("test", "Run the protocol unit tests");
|
||||
// The envelope tests itself with no import of its own — everything else
|
||||
// imports it, so it is built separately rather than importing itself.
|
||||
const envelope_tests = b.addTest(.{
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path("envelope/envelope.zig"), // framing round trips, verb numbering, dispatch, the floors
|
||||
.target = b.resolveTargetQuery(.{}),
|
||||
}),
|
||||
});
|
||||
test_step.dependOn(&b.addRunArtifact(envelope_tests).step);
|
||||
|
||||
for ([_][]const u8{
|
||||
"envelope/envelope.zig", // framing round trips, verb numbering, dispatch, the floors
|
||||
"vfs/vfs-protocol.zig", // NodeKind / DirectoryEntry sizes + op values
|
||||
"input/input-protocol.zig", // event numbering + the push-floor budget
|
||||
"display/display-protocol.zig", // pack(): native pixel encoding per format
|
||||
}) |root| {
|
||||
const protocol_tests = b.addTest(.{
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path(root),
|
||||
.target = b.resolveTargetQuery(.{}),
|
||||
.imports = &.{.{ .name = "envelope", .module = envelope }},
|
||||
}),
|
||||
});
|
||||
test_step.dependOn(&b.addRunArtifact(protocol_tests).step);
|
||||
|
||||
Reference in New Issue
Block a user