I will never get tired of vendoring dependencies. ha ha. It is possible I am insane. I had to do a lot of pruning to get these not to be ridiculous (especially the unicode data, which had nearly 1 million lines of... stuff).
148 lines
3.9 KiB
Zig
148 lines
3.9 KiB
Zig
const std = @import("std");
|
|
const assert = std.debug.assert;
|
|
const Allocator = std.mem.Allocator;
|
|
const Instant = std.time.Instant;
|
|
const xev = @import("xev");
|
|
|
|
const EXPECTED = "RANG TANG DING DONG I AM THE JAPANESE SANDMAN";
|
|
|
|
/// This is a global var decremented for the test without any locks. That's
|
|
/// how the original is written and that's how we're going to do it.
|
|
var packet_counter: usize = 1e6;
|
|
var send_cb_called: usize = 0;
|
|
var recv_cb_called: usize = 0;
|
|
|
|
pub const std_options: std.Options = .{
|
|
.log_level = .info,
|
|
};
|
|
|
|
pub fn main() !void {
|
|
try run(1, 1);
|
|
}
|
|
|
|
pub fn run(comptime n_senders: comptime_int, comptime n_receivers: comptime_int) !void {
|
|
const base_port = 12345;
|
|
|
|
var thread_pool = xev.ThreadPool.init(.{});
|
|
defer thread_pool.deinit();
|
|
defer thread_pool.shutdown();
|
|
|
|
var loop = try xev.Loop.init(.{
|
|
.entries = std.math.pow(u13, 2, 12),
|
|
.thread_pool = &thread_pool,
|
|
});
|
|
defer loop.deinit();
|
|
|
|
var receivers: [n_receivers]Receiver = undefined;
|
|
for (&receivers, 0..) |*r, i| {
|
|
const addr = try std.net.Address.parseIp4("127.0.0.1", @as(u16, @intCast(base_port + i)));
|
|
r.* = .{ .udp = try xev.UDP.init(addr) };
|
|
try r.udp.bind(addr);
|
|
r.udp.read(
|
|
&loop,
|
|
&r.c_recv,
|
|
&r.udp_state,
|
|
.{ .slice = &r.recv_buf },
|
|
Receiver,
|
|
r,
|
|
Receiver.readCallback,
|
|
);
|
|
}
|
|
|
|
var senders: [n_senders]Sender = undefined;
|
|
for (&senders, 0..) |*s, i| {
|
|
const addr = try std.net.Address.parseIp4(
|
|
"127.0.0.1",
|
|
@as(u16, @intCast(base_port + (i % n_receivers))),
|
|
);
|
|
s.* = .{ .udp = try xev.UDP.init(addr) };
|
|
s.udp.write(
|
|
&loop,
|
|
&s.c_send,
|
|
&s.udp_state,
|
|
addr,
|
|
.{ .slice = EXPECTED },
|
|
Sender,
|
|
s,
|
|
Sender.writeCallback,
|
|
);
|
|
}
|
|
|
|
const start_time = try Instant.now();
|
|
try loop.run(.until_done);
|
|
const end_time = try Instant.now();
|
|
|
|
const elapsed = @as(f64, @floatFromInt(end_time.since(start_time)));
|
|
std.log.info("udp_pummel_{d}v{d}: {d:.0}f/s received, {d:.0}f/s sent, {d} received, {d} sent in {d:.1} seconds", .{
|
|
n_senders,
|
|
n_receivers,
|
|
@as(f64, @floatFromInt(recv_cb_called)) / (elapsed / std.time.ns_per_s),
|
|
@as(f64, @floatFromInt(send_cb_called)) / (elapsed / std.time.ns_per_s),
|
|
recv_cb_called,
|
|
send_cb_called,
|
|
elapsed / std.time.ns_per_s,
|
|
});
|
|
}
|
|
|
|
const Sender = struct {
|
|
udp: xev.UDP,
|
|
udp_state: xev.UDP.State = undefined,
|
|
c_send: xev.Completion = undefined,
|
|
|
|
fn writeCallback(
|
|
_: ?*Sender,
|
|
l: *xev.Loop,
|
|
_: *xev.Completion,
|
|
_: *xev.UDP.State,
|
|
_: xev.UDP,
|
|
_: xev.WriteBuffer,
|
|
r: xev.UDP.WriteError!usize,
|
|
) xev.CallbackAction {
|
|
_ = r catch unreachable;
|
|
|
|
if (packet_counter == 0) {
|
|
l.stop();
|
|
return .disarm;
|
|
}
|
|
|
|
packet_counter -|= 1;
|
|
send_cb_called += 1;
|
|
|
|
return .rearm;
|
|
}
|
|
};
|
|
|
|
const Receiver = struct {
|
|
udp: xev.UDP,
|
|
udp_state: xev.UDP.State = undefined,
|
|
c_recv: xev.Completion = undefined,
|
|
recv_buf: [65536]u8 = undefined,
|
|
|
|
fn readCallback(
|
|
_: ?*Receiver,
|
|
_: *xev.Loop,
|
|
_: *xev.Completion,
|
|
_: *xev.UDP.State,
|
|
_: std.net.Address,
|
|
_: xev.UDP,
|
|
b: xev.ReadBuffer,
|
|
r: xev.UDP.ReadError!usize,
|
|
) xev.CallbackAction {
|
|
const n = r catch |err| {
|
|
switch (err) {
|
|
error.EOF => {},
|
|
else => std.log.warn("err={}", .{err}),
|
|
}
|
|
|
|
return .disarm;
|
|
};
|
|
|
|
if (!std.mem.eql(u8, b.slice[0..n], EXPECTED)) {
|
|
@panic("Unexpected data.");
|
|
}
|
|
|
|
recv_cb_called += 1;
|
|
return .rearm;
|
|
}
|
|
};
|