Skip to content

Tag-first dispatch, the other way round from 023

Description

Tag-first dispatch, the other way round from 023.

  • 023 has the item. It asks each type to cast: fromPoly.
  • This one starts from the tag alone. It asks each type to confirm: isIt.
  • Only after a tag is confirmed does mustFromPoly reach the item.
  • A pool hook works this way because it has no item — see AlwaysCreateHooks.

  • Push an Event, a Sensor and a Timer into one mixed list.

  • Read each tag once, confirm it, then reach the item.
  • The last branch handles a tag nobody claimed. Always write it.

Diagram

 alloc.create (Event)  ──► list
 alloc.create (Sensor) ──► list
 alloc.create (Timer)  ──► list
      │ list.popFirst
 tag ──► isIt ──► mustFromPoly ──► item
      │ freeItem per node

Source

pub fn tag_first_dispatch_loop(allocator: std.mem.Allocator, io: std.Io) !void {
    _ = io;
    var list: polynode.ItemList = .{};

    defer items.freeList(&list, allocator);

    {
        var slot: Slot = null;
        try items.Event.EventPolyHelper.create(allocator, &slot);
        items.Event.EventPolyHelper.mustFromSlot(&slot).code = 7;
        list.appendFromSlot(&slot);
    }

    {
        var slot: Slot = null;
        try items.Sensor.SensorPolyHelper.create(allocator, &slot);
        items.Sensor.SensorPolyHelper.mustFromSlot(&slot).value = 2.71;
        list.appendFromSlot(&slot);
    }

    {
        var slot: Slot = null;
        try items.Timer.TimerPolyHelper.create(allocator, &slot);
        list.appendFromSlot(&slot);
    }

    var events: usize = 0;
    var sensors: usize = 0;
    var timers: usize = 0;

    while (list.popFirst()) |poly| {
        // One read. Every branch below asks about this tag.
        const tag: *const anyopaque = poly.*.tag;

        if (items.Event.EventPolyHelper.isIt(tag)) {
            // The tag is proven, so this cast cannot fail.
            const ev = items.Event.EventPolyHelper.mustFromPoly(poly);
            try helpers.expect(error.TagFirstDispatchFailed, ev.*.code == 7, "wrong event code");
            events += 1;
        } else if (items.Sensor.SensorPolyHelper.isIt(tag)) {
            const sn = items.Sensor.SensorPolyHelper.mustFromPoly(poly);
            try helpers.expect(error.TagFirstDispatchFailed, sn.*.value == 2.71, "wrong sensor value");
            sensors += 1;
        } else if (items.Timer.TimerPolyHelper.isIt(tag)) {
            // A branch that never reaches the item. The tag was enough.
            timers += 1;
        } else {
            // Nobody claimed the tag. This branch cannot free the item —
            // destroy needs the type, and here there is none.
            return error.UnknownTag;
        }

        items.freeItem(poly, allocator);
    }

    try helpers.expect(error.TagFirstDispatchFailed, events == 1, "wrong event count");
    try helpers.expect(error.TagFirstDispatchFailed, sensors == 1, "wrong sensor count");
    try helpers.expect(error.TagFirstDispatchFailed, timers == 1, "wrong timer count");
}

const items = @import("../items/items.zig");
const helpers = @import("../helpers/helpers.zig");
const polynode = @import("matryoshka").polynode;
const std = @import("std");
const Slot = polynode.Slot;