Skip to content

Batch processing

Description

Batch processing.

  • Main sends 10 Events, then a ShutdownCommand sentinel.
  • Worker blocks on the first item via mailbox.receive.
  • Worker then empties the rest with mailbox.receive_batch.
  • Sentinel found in either place ends the worker.

Diagram

 main ──Event×10 + ShutdownCommand──► mailbox
 worker: receive (first item) ──► freeSlot
         receive_batch (rest) ──► walk + freeItem
         (ShutdownCommand in batch → exit)

Source

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

    var ctx: WorkerCtx = .{ .mbh = mbh, .alloc = allocator };
    var fut = try io.concurrent(batchWorkerFn, .{&ctx});

    const n: usize = 10;
    var i: usize = 0;
    while (i < n) : (i += 1) {
        var slot: Slot = null;
        defer items.Event.EventPolyHelper.destroy(allocator, &slot);
        try items.Event.EventPolyHelper.create(allocator, &slot);
        items.Event.EventPolyHelper.mustIdentifySlotAs(&slot).code = @intCast(i);
        try mailbox.send(mbh, &slot);
    }

    // Signal worker to stop — all n items are already queued before this.
    {
        var slot: Slot = null;
        defer items.ShutdownCommand.ShutdownCommandPolyHelper.destroy(allocator, &slot);
        try items.ShutdownCommand.ShutdownCommandPolyHelper.create(allocator, &slot);
        try mailbox.send(mbh, &slot);
    }

    fut.await(io);

    const total = ctx.first_count + ctx.batch_count;
    std.log.info("batch: first={d} batch={d} total={d}", .{ ctx.first_count, ctx.batch_count, total });
    try helpers.expect(error.BatchProcessingFailed, total == n, "wrong total");
    try helpers.expect(error.BatchProcessingFailed, ctx.first_count > 0, "no items received as first");
}

const WorkerCtx = struct {
    mbh: MailboxHandle,
    alloc: std.mem.Allocator,
    first_count: usize = 0,
    batch_count: usize = 0,
};

fn batchWorkerFn(ctx: *WorkerCtx) void {
    while (true) {
        var slot: Slot = null;
        mailbox.receive(ctx.mbh, &slot, null) catch return;
        const poly: *PolyNode = slot.?;

        if (items.ShutdownCommand.ShutdownCommandPolyHelper.identifyNodeAs(poly)) |_| {
            items.freeSlot(&slot, ctx.alloc);
            return;
        }

        items.freeSlot(&slot, ctx.alloc);
        ctx.first_count += 1;

        var batch: std.DoublyLinkedList = mailbox.receive_batch(ctx.mbh) catch return;
        while (batch.popFirst()) |node| {
            const bpoly: *PolyNode = @fieldParentPtr("node", node);
            if (items.ShutdownCommand.ShutdownCommandPolyHelper.identifyNodeAs(bpoly)) |_| {
                items.freeItem(bpoly, ctx.alloc);
                return;
            }
            items.freeItem(bpoly, ctx.alloc);
            ctx.batch_count += 1;
        }
    }
}

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 PolyNode = polynode.PolyNode;
const Slot = polynode.Slot;
const MailboxHandle = mailbox.MailboxHandle;