Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion zig/src/core/lanes/drafted.zig
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ pub const Drafted = struct {
absorbs: std.ArrayList(dr.Absorb) = .empty,
holds: std.ArrayList(dr.Hold) = .empty,
firsts: std.ArrayList(u32) = .empty,
/// The target reads held drafts from the drafter itself (Window.held), so windows without fillers pass as they are.
held_on_target: bool = false,

/// The wrapper, with the target told the drafter's taps before any forward.
pub fn init(gpa: Allocator, target: be.Backend, drafter: dr.Drafter) !Drafted {
Expand Down Expand Up @@ -217,11 +219,13 @@ pub const Drafted = struct {
lane.tree_first = null;
};
var total: usize = 0;
var through = x.held_on_target;
for (windows) |w| {
if (w.held > 0 and w.tokens.len != 0) return error.MixedDrafts;
total += w.held;
if (through and w.held > 0 and (x.lanes.getPtr(w.stream) orelse return error.NotDrafting).filler > 0) through = false;
}
if (total == 0) return x.target.verify(x.windows.items, out);
if (total == 0 or through) return x.target.verify(x.windows.items, out);
try x.tokens.resize(x.gpa, total);
x.streams.clearRetainingCapacity();
x.outs.clearRetainingCapacity();
Expand Down
78 changes: 78 additions & 0 deletions zig/src/core/lanes/drafted_held_test.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
//! Drafted.verify with held_on_target: windows of drafting streams reach the target with Window.held as asked.
const std = @import("std");
const be = @import("backend.zig");
const dr = @import("drafter.zig");
const Stream = @import("stream.zig").Stream;
const Drafted = @import("drafted.zig").Drafted;

/// A target that records each verify's held rows and host tokens, and a drafter that only counts host readbacks.
const Fake = struct {
seen: u32 = 0,
tokens: [4]u32 = @splat(0),
readbacks: u32 = 0,

fn cast(ptr: *anyopaque) *Fake {
return @ptrCast(@alignCast(ptr));
}
fn verify(ptr: *anyopaque, windows: []const be.Window, _: []be.Verified) anyerror!void {
const f = cast(ptr);
f.seen = windows[0].held;
@memcpy(f.tokens[0..windows[0].tokens.len], windows[0].tokens);
}
fn held(ptr: *anyopaque, _: []const *Stream, out: []const []u32) anyerror!void {
cast(ptr).readbacks += 1;
for (out) |o| @memset(o, 5);
}
fn none(_: *anyopaque) []const u32 {
return &.{};
}
fn facts(_: *anyopaque) dr.Facts {
return .{ .depth = 3, .step_ms = 0, .prior = &.{}, .stream_bytes = 0 };
}
fn unused(_: *anyopaque, _: *Stream) anyerror!void {
return error.Unused;
}
fn absorb(_: *anyopaque, _: []const dr.Absorb) anyerror!void {
return error.Unused;
}
fn hold(_: *anyopaque, _: []const dr.Hold) anyerror!void {
return error.Unused;
}
fn release(_: *anyopaque, _: *Stream) void {}
fn first(_: *anyopaque, _: *Stream, _: u64) anyerror!u64 {
return error.Unused;
}
fn queue(_: *anyopaque, _: *Stream, _: be.Feed, _: u64) anyerror!u64 {
return error.Unused;
}
fn read(_: *anyopaque, _: u64) anyerror!u32 {
return error.Unused;
}
fn keep(_: *anyopaque, _: []const be.Window, _: []const []const u32) anyerror!void {}
fn draft(_: *anyopaque, _: []const be.DraftRequest) anyerror!void {}
};

test "held_on_target passes held windows through, and a stream with fillers still gets host tokens" {
var f: Fake = .{};
const target: be.Backend = .{ .ptr = &f, .vtable = &.{ .prefill = Fake.unused, .first = Fake.first, .queue = Fake.queue, .read = Fake.read, .verify = Fake.verify, .keep = Fake.keep, .draft = Fake.draft, .release = Fake.release } };
const drafter: dr.Drafter = .{ .ptr = &f, .vtable = &.{ .taps = Fake.none, .facts = Fake.facts, .open = Fake.unused, .absorb = Fake.absorb, .hold = Fake.hold, .held = Fake.held, .release = Fake.release } };
var x: Drafted = .{ .gpa = std.testing.allocator, .target = target, .drafter = drafter, .held_on_target = true };
defer x.deinit();
var s: Stream = undefined;
try x.lanes.put(std.testing.allocator, &s, .{ .rows = true });
var sampled: [4]u32 = undefined;
var drafts: [3]u32 = undefined;
const w = [_]be.Window{.{ .stream = &s, .pending = 9, .held = 3, .tokens = &.{}, .parents = null, .positions = &.{ 1, 2, 3, 4 } }};
var o = [_]be.Verified{.{ .sampled = &sampled, .drafts = &drafts }};
try x.backend().verify(&w, &o);
try std.testing.expectEqual(@as(u32, 3), f.seen);
try std.testing.expectEqual(@as(u32, 0), f.readbacks);
x.lanes.getPtr(&s).?.filler = 3; // fillers: the pending token as host tokens, no readback
try x.backend().verify(&w, &o);
try std.testing.expectEqual(@as(u32, 0), f.seen);
try std.testing.expectEqualSlices(u32, &.{ 9, 9, 9 }, f.tokens[0..3]);
x.held_on_target = false; // off: the drafter's drafts as host tokens
try x.backend().verify(&w, &o);
try std.testing.expectEqual(@as(u32, 1), f.readbacks);
try std.testing.expectEqualSlices(u32, &.{ 5, 5, 5 }, f.tokens[0..3]);
}
1 change: 1 addition & 0 deletions zig/src/core/lanes/drafted_test.zig
Original file line number Diff line number Diff line change
Expand Up @@ -594,4 +594,5 @@ test "the drafter wrapper sees the committed callback before its first hold" {
test {
_ = @import("drafted_role_test.zig");
_ = @import("drafted_step_test.zig");
_ = @import("drafted_held_test.zig");
}
Loading