Skip to content

Pipeline

Description

Pipeline.

  • Chain of 3 stages: producer, transformer, consumer.
  • Producer sends 5 Events, then a sentinel (code == -1).
  • Transformer squares each code, forwards the sentinel, then exits.
  • Consumer sums results, frees the sentinel, exits.

Diagram

 producer ──Event──► stage1 mailbox ──► transformer
                                             │ Event→Event (code²)
 consumer ◄──Event── stage2 mailbox ◄── transformer
 (sentinel: Event code=-1 terminates each stage; consumer frees)

Source

pub fn pipeline(allocator: std.mem.Allocator, io: std.Io) !void {
    const stage1: MailboxHandle = try mailbox.new(io, allocator);
    const stage2: MailboxHandle = try mailbox.new(io, allocator);
    defer {
        var r1: std.DoublyLinkedList = mailbox.close(stage1);
        items.freeList(&r1, allocator);
        var r2: std.DoublyLinkedList = mailbox.close(stage2);
        items.freeList(&r2, allocator);
        mailbox.destroy(stage1, allocator);
        mailbox.destroy(stage2, allocator);
    }

    var prod_ctx: ProducerCtx = .{ .outbox = stage1, .alloc = allocator };
    var tran_ctx: StageCtx = .{ .inbox = stage1, .outbox = stage2, .alloc = allocator };
    var cons_ctx: ConsumerCtx = .{ .mbh = stage2, .alloc = allocator };

    var f_prod = try io.concurrent(producerFn, .{&prod_ctx});
    var f_tran = try io.concurrent(transformerFn, .{&tran_ctx});
    var f_cons = try io.concurrent(consumerFn, .{&cons_ctx});

    f_prod.await(io);
    f_tran.await(io);
    f_cons.await(io);

    // 0²+1²+2²+3²+4² = 30.
    std.log.info("pipeline: count={d} sum={d}", .{ cons_ctx.count, cons_ctx.sum });
    try helpers.expect(error.PipelineFailed, cons_ctx.count == 5, "wrong item count");
    try helpers.expect(error.PipelineFailed, cons_ctx.sum == 30, "wrong sum");
}

const ProducerCtx = struct {
    outbox: MailboxHandle,
    alloc: std.mem.Allocator,
};

fn producerFn(ctx: *ProducerCtx) void {
    var i: i32 = 0;
    while (i < 5) : (i += 1) {
        var slot: Slot = null;
        items.Event.EventPolyHelper.create(ctx.alloc, &slot) catch return;
        items.Event.EventPolyHelper.mustIdentifySlotAs(&slot).code = i;
        mailbox.send(ctx.outbox, &slot) catch {
            items.freeSlot(&slot, ctx.alloc);
            return;
        };
    }
    {
        var slot: Slot = null;
        items.Event.EventPolyHelper.create(ctx.alloc, &slot) catch return;
        items.Event.EventPolyHelper.mustIdentifySlotAs(&slot).code = -1;
        mailbox.send(ctx.outbox, &slot) catch items.freeSlot(&slot, ctx.alloc);
    }
}

const StageCtx = struct {
    inbox: MailboxHandle,
    outbox: MailboxHandle,
    alloc: std.mem.Allocator,
};

fn transformerFn(ctx: *StageCtx) void {
    while (true) {
        var slot: Slot = null;
        mailbox.receive(ctx.inbox, &slot, null) catch return;
        const ev: *items.Event = items.Event.EventPolyHelper.identifySlotAs(&slot) orelse {
            items.freeSlot(&slot, ctx.alloc);
            continue;
        };
        if (ev.code == -1) {
            mailbox.send(ctx.outbox, &slot) catch items.freeSlot(&slot, ctx.alloc);
            return;
        }
        ev.code = ev.code * ev.code;
        mailbox.send(ctx.outbox, &slot) catch items.freeSlot(&slot, ctx.alloc);
    }
}

const ConsumerCtx = struct {
    mbh: MailboxHandle,
    alloc: std.mem.Allocator,
    sum: i32 = 0,
    count: usize = 0,
};

fn consumerFn(ctx: *ConsumerCtx) void {
    while (true) {
        var slot: Slot = null;
        mailbox.receive(ctx.mbh, &slot, null) catch return;
        const ev: *items.Event = items.Event.EventPolyHelper.identifySlotAs(&slot) orelse {
            items.freeSlot(&slot, ctx.alloc);
            continue;
        };
        if (ev.code == -1) {
            items.freeSlot(&slot, ctx.alloc);
            return;
        }
        std.log.info("pipeline: result={d}", .{ev.code});
        ctx.sum += ev.code;
        ctx.count += 1;
        items.freeSlot(&slot, ctx.alloc);
    }
}

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