I have three threads. One for sending packets to wireguard client, other one for sending packets to an endpoint taken from a list of servers and a third thread for a switching endpoint function. My current logic works but LLMs told me to use atomics instead of variables for thread syncing, but that is slower. So I’m asking you here for some advice.
Curent logic is that wgToServer() is setting a timestamp and setting packet_arrived flag to false:
fn wgToServer(
timer: *?std.Io.Clock.Timestamp,
packet_arrived: *bool,
io: std.Io,
wg_sock: *std.Io.net.Socket,
serv_sock: *std.Io.net.Socket,
buf: []u8,
servers: []std.Io.net.IpAddress,
current_id: *usize,
) !void {
while (true) {
// --- Handle WireGuard -> server ---
if (std.Io.net.Socket.receive(wg_sock, io, buf[0..])) |recv| {
std.log.debug("Received {d} bytes from WireGuard", .{recv.data.len});
const packet = buf[0..recv.data.len];
std.log.debug("Trying to send to {f}", .{&servers[current_id.*]});
if (std.Io.net.Socket.send(serv_sock, io, &servers[current_id.*], packet)) {
// Unwrap timer optional
if (timer.*) |*t| if (packet_arrived.*) {
// Reset timer to sync threads and set packet_arrived state
t.* = std.Io.Clock.Timestamp.now(io, .awake);
packet_arrived.* = false;
};
} else |err| {
std.log.err(
"Backend {f} failed: {any}",
.{ servers[current_id.*], err },
);
}
} else |err| {
return err;
}
}
}
serverToWg() Is setting packet_arrived to true and setting it to false if the packets come from the wrong IP
fn serverToWg(
packet_arrived: *bool,
io: std.Io,
wg_sock: *std.Io.net.Socket,
serv_sock: *std.Io.net.Socket,
srv_buf: []u8,
servers: []std.Io.net.IpAddress,
current_id: *usize,
wg_addr: std.Io.net.IpAddress,
) !void {
while (true) {
// --- Handle server -> WireGuard ---
if (std.Io.net.Socket.receive(serv_sock, io, srv_buf[0..])) |recv| {
const addr = recv.from;
std.log.debug("Received {d} bytes, server: {f}", .{ recv.data.len, addr });
const packet = srv_buf[0..recv.data.len];
const server = servers[current_id.*];
if (!std.Io.net.IpAddress.eql(&addr, &server)) {
std.log.warn("Wrong server responding: {f}\nCorrect server: {f}", .{ addr, server });
// If Received packet comes before sending packet is out at startup, set the correct state and discard it
packet_arrived.* = false;
continue;
}
if (std.Io.net.Socket.send(wg_sock, io, &wg_addr, packet)) {
// Confirm packet came from the server
packet_arrived.* = true;
} else |err| {
std.log.err(
"Backend {f} failed: {any}",
.{ wg_addr, err },
);
}
} else |err| {
return err;
}
}
}
switcher() function is currently checking packet_arrived flag if false, check elapsed time taken from a timestamp is less than duration specified of the sleep, sleep for the remainder and check the packet_arrived again. If all lines up, switch the endpoints. Next, I’m setting a new timestamp to sync the threads and starting a sleep for a duration:
fn switcher(
io: std.Io,
seconds: usize,
timer: *?std.Io.Clock.Timestamp,
servers: []std.Io.net.IpAddress,
current_id: *usize,
packet_arrived: *bool,
) !void {
// Unwrap timer optional
if (timer.*) |*t| {
// Declare constants once before the main loop
const duration: u64 = std.time.ns_per_s * seconds;
while (true) {
const elapsed = t.untilNow(io).raw.nanoseconds;
std.log.debug("Timer time elapsed: {d}", .{elapsed});
std.log.debug("Timer time duration: {d}", .{duration});
std.log.debug("Timer packet_arrived state: {}", .{packet_arrived.*});
// Main check is if packet has arrived
if (!packet_arrived.*) {
// Second check is if enough time has passsed before switching
if (elapsed < duration) {
try io.sleep(.fromNanoseconds(duration - elapsed), .awake);
continue;
}
const new_id = (current_id.* + 1) % servers.len;
current_id.* = new_id;
std.log.info("Switched servers endpoints!", .{});
std.log.info("Current endpoint: {f}", .{&servers[current_id.*]});
// Reset packet state
packet_arrived.* = true;
}
// Reset timer to sync threads
t.* = std.Io.Clock.Timestamp.now(io, .awake);
try io.sleep(.fromNanoseconds(duration), .awake);
}
} else {
std.log.err("Switcher got called but timer variable value is: {any}", .{timer});
return error.FailedToUnwrap;
}
}
One person suggested using a state, but I don’t see how state can work with a timer.
I mean, this is proven that it works, but I want to consult here with other possibilities and options