library: the harness keeps the subscribers, and an id belongs to whoever opened it
Three services had each written the same thing and got it three different ways: input polled the process list to notice a dead subscriber, and only when someone else subscribed; the power service never noticed at all; the device manager noticed drivers but not subscribers. The harness owns the table now, driven by the events a protocol declares — it registers on the reserved verb, frames each event once, posts to everyone interested without waiting on any of them, and reclaims a slot when the kernel says its owner died. Interest masks moved to the envelope, so a subscriber that wants only mice asks the same way everywhere. Two consequences the plan had not foreseen. The device manager now hears a supervised child's death twice, once as its supervisor and once as a subscriber, so restart backoff counted every crash twice and gave up after half as many; it retires the id before counting. And the kernel's published exit table had eight slots for what is now six subscriptions in a plain boot, so it holds sixteen. The other half is a hole the design named early and left standing: a backend handed out a small integer and then honoured it from anyone. A process that guessed a file's node id read another client's file; a display layer had no owner at all, so any client could reconfigure or destroy any layer; a USB device token was never checked against the client that opened it. Each is now bound to the task that opened it, and a wrong owner gets exactly what an unknown id gets — the refusal must not become the oracle the identical answers elsewhere were designed to remove. Closing a file changed with it: it used to succeed unconditionally, which would have told a caller which ids existed. Suite 111/111, with a new case in which one process holds a file and a layer, hands both ids to a second process, and finds them untouched after that process has tried everything with them.
This commit is contained in:
@@ -13,7 +13,8 @@
|
||||
//! - **subscribe** is the *reserved* verb, not one of this protocol's own: its shape — a
|
||||
//! synchronous call whose attached capability is the subscriber's endpoint — is exactly
|
||||
//! what `envelope.operation_subscribe` means everywhere. The interest mask travels as the
|
||||
//! packet's tail (`Subscribe`), because a reserved verb carries no typed request.
|
||||
//! packet's tail (`envelope.Subscription`), because a reserved verb carries no typed
|
||||
//! request; what this protocol supplies is the *meaning* of its bits — the device classes.
|
||||
//! - **publish** is this protocol's one verb: a source sends one `InputEvent` and the
|
||||
//! service answers at once, so publishing never blocks on a slow subscriber.
|
||||
//! - **delivery** is an event push: the service `ipc_send`s each event to every interested
|
||||
@@ -294,12 +295,6 @@ pub const InputEvent = extern struct {
|
||||
|
||||
// --- the contract -----------------------------------------------------------
|
||||
|
||||
/// The body of a `subscribe` — the envelope's reserved verb 2, whose shape (a call whose
|
||||
/// capability is the subscriber's own endpoint) this protocol adopts wholesale. A reserved
|
||||
/// verb has no typed request, so the mask travels as the packet's tail and `encodeSubscribe`
|
||||
/// is how a client lays it down. Zero means every class.
|
||||
pub const Subscribe = extern struct { device_mask: u32 = 0 };
|
||||
|
||||
pub const Protocol = envelope.Define(.{
|
||||
.name = "input",
|
||||
.version = 1,
|
||||
@@ -334,23 +329,12 @@ pub fn eventOfDevice(device: u32) ?Event {
|
||||
}
|
||||
|
||||
/// Frame a `subscribe` request: the reserved verb's header, then the interest mask. Null if
|
||||
/// the buffer is too small. Spelled here rather than at each caller so the one place that
|
||||
/// knows a reserved verb carries its body in the tail is the protocol module.
|
||||
/// the buffer is too small. The mask itself is the envelope's `Subscription` — the interest
|
||||
/// a reserved subscribe carries is universal, and the *meaning* of its bits (here: the
|
||||
/// device classes above) is what each protocol supplies. Kept as a named helper because
|
||||
/// `device_mask` is what an input caller calls it.
|
||||
pub fn encodeSubscribe(device_mask: u32, buffer: []u8) ?[]u8 {
|
||||
const total = envelope.prefix_size + @sizeOf(Subscribe);
|
||||
if (buffer.len < total) return null;
|
||||
const header = envelope.Header{ .operation = envelope.operation_subscribe };
|
||||
const body = Subscribe{ .device_mask = device_mask };
|
||||
@memcpy(buffer[0..envelope.prefix_size], std.mem.asBytes(&header));
|
||||
@memcpy(buffer[envelope.prefix_size..][0..@sizeOf(Subscribe)], std.mem.asBytes(&body));
|
||||
return buffer[0..total];
|
||||
}
|
||||
|
||||
/// The interest mask out of a `subscribe` packet's tail, on the provider's side. A caller
|
||||
/// that sent no mask at all means every class, which is what a zero mask means anyway.
|
||||
pub fn decodeSubscribe(tail: []const u8) Subscribe {
|
||||
if (tail.len < @sizeOf(Subscribe)) return .{};
|
||||
return std.mem.bytesToValue(Subscribe, tail[0..@sizeOf(Subscribe)]);
|
||||
return envelope.encodeSubscribe(device_mask, buffer);
|
||||
}
|
||||
|
||||
test "an event of every class fits the push floor, header included" {
|
||||
@@ -388,7 +372,9 @@ test "a subscribe carries its mask in the tail of the reserved verb" {
|
||||
var buffer: [envelope.packet_maximum]u8 = undefined;
|
||||
const packet = encodeSubscribe(device_mouse, &buffer).?;
|
||||
try std.testing.expectEqual(envelope.operation_subscribe, envelope.headerOf(packet).?.operation);
|
||||
try std.testing.expectEqual(device_mouse, decodeSubscribe(packet[envelope.prefix_size..]).device_mask);
|
||||
// The provider side of this is the service harness's, which reads the same
|
||||
// interest mask out of the tail for every protocol.
|
||||
try std.testing.expectEqual(device_mouse, envelope.decodeSubscribe(packet[envelope.prefix_size..]).interest);
|
||||
// A caller that sent nothing at all reads as the every-class mask.
|
||||
try std.testing.expectEqual(@as(u32, 0), decodeSubscribe(&.{}).device_mask);
|
||||
try std.testing.expectEqual(@as(u32, 0), envelope.decodeSubscribe(&.{}).interest);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user