Zigrupt is a high-performance event broadcasting library for Zig, heavily inspired by the original LMAX Disruptor. It lets application threads publish events once and have multiple handlers process every event, in order, on their own threads. A fixed-capacity ring buffer, lock-free coordination, and attention to CPU cache lines keep the path between producers and handlers lean.
Warning
Zigrupt is under active development. The API is not yet stable and may change.
The current implementation supports single/multi-producer operation, broadcast handlers, batching, custom waiting strategies, draining, and restart. This is a starting point for the library, not its final scope or a claim of full LMAX parity.
Use Zig 0.16.0. Building or using the library does not require Node.
In a Zig project, once the repository is public:
zig fetch --save=zigrupt https://github.com/sarthakvk/zigrupt/archive/refs/heads/main.tar.gzCommit the generated dependency URL and hash. For reproducible updates, use
https://github.com/sarthakvk/zigrupt/archive/<full-commit-sha>.tar.gz with a
reviewed commit. Add
b.dependency("zigrupt", .{ .target = target, .optimize = optimize }).module("zigrupt")
as the executable's zigrupt import. See the
complete installation guide and
consumer build file for the full setup, including
installation from a checkout before public availability.
This is the executable basic example:
const std = @import("std");
const zigrupt = @import("zigrupt");
const Event = struct { value: usize };
var total: usize = 0; // Only the handler writes; main reads after stop joins it.
fn handle(events: []const Event) usize {
for (events) |event| total += event.value;
return events.len;
}
const Bus = zigrupt.EventBus(
Event,
false,
&.{handle},
zigrupt.waiting_strategy.BusySpin,
zigrupt.waiting_strategy.BusySpin,
);
pub fn main() !void {
var bus = try Bus.init(std.heap.page_allocator, 64, 8);
defer bus.deinit();
try bus.start();
errdefer bus.stop() catch {}; // Drain before freeing on a publishing error.
for (1..11) |value| try bus.produce(.{ .value = value });
try bus.stop(); // Publishing is finished; drain and join the handler.
if (total != 55) return error.UnexpectedTotal;
std.debug.print("basic: sum = {d}\n", .{total});
}From a checkout, run zig build example-basic; expected output is
basic: sum = 55. zig build check-examples runs all six examples and checks
results.
Handlers run on separate threads. Returning a count acknowledges a consumed
prefix. Event values are copied, but pointers/slices inside them still refer to
caller-owned storage. Finish and join every producer before stop(), keep
referenced data alive through consumption, and stop before deinit().
- Full documentation source, concepts, and usage guides.
- Executable examples and existing behavior tests:
zig build test. - Benchmark methodology and caveats:
devbox run bench --standalone --smokefor a short Zig-only check on Linux. - Contributing, including formatting, installation verification, and documentation checks.
- Published documentation and local preview/build instructions.
- MIT license.
