fix: improve CallContext robustness

This commit is contained in:
CJ van den Berg 2024-10-22 20:52:41 +02:00
parent d7dd271163
commit 2b380bbef4
Signed by: neurocyte
GPG key ID: 8EB1E1BB660E3FB9

View file

@ -109,7 +109,7 @@ fn Pid(comptime own: Ownership) type {
return c.thespian_handle_is_expired(self.h); return c.thespian_handle_is_expired(self.h);
} }
pub fn wait_expired(self: Self, timeout_ns: isize) !void { pub fn wait_expired(self: Self, timeout_ns: isize) error{Timeout}!void {
var max_sleep: isize = timeout_ns; var max_sleep: isize = timeout_ns;
while (!self.expired()) { while (!self.expired()) {
if (max_sleep <= 0) return error.Timeout; if (max_sleep <= 0) return error.Timeout;
@ -798,9 +798,10 @@ pub const file_stream = struct {
const CallContext = struct { const CallContext = struct {
receiver: ReceiverT, receiver: ReceiverT,
from: pid,
to: pid_ref, to: pid_ref,
request: message, request: message,
response: *?message, response: ?message,
a: std.mem.Allocator, a: std.mem.Allocator,
done: std.Thread.ResetEvent = .{}, done: std.Thread.ResetEvent = .{},
@ -808,29 +809,21 @@ const CallContext = struct {
const ReceiverT = Receiver(*Self); const ReceiverT = Receiver(*Self);
pub fn call(a: std.mem.Allocator, to: pid_ref, timeout_ns: u64, request: message) CallError!message { pub fn call(a: std.mem.Allocator, to: pid_ref, timeout_ns: u64, request: message) CallError!message {
var response: ?message = null; const self = try a.create(Self);
var self: Self = .{ self.* = .{
.receiver = undefined, .receiver = undefined,
.from = self_pid().clone(),
.to = to, .to = to,
.request = request, .request = request,
.response = &response, .response = null,
.a = a, .a = a,
}; };
self.receiver = ReceiverT.init(receive_, &self); self.receiver = ReceiverT.init(receive_, self);
const proc = try spawn_link(a, &self, start, @typeName(Self)); const proc = try spawn_link(a, self, start, @typeName(Self));
defer proc.deinit(); defer proc.deinit();
errdefer { try self.done.timedWait(timeout_ns);
proc.send(.{"timeout"}) catch @panic("CallContext.send.timeout"); defer self.deinit(); // only deinit on success. if we timed out proc will have to deinit
self.done.wait(); return self.response orelse .{};
if (response) |resp| a.free(resp.buf);
}
self.done.timedWait(timeout_ns) catch |e| switch (e) {
error.Timeout => {
return e;
},
else => return e,
};
return response orelse .{};
} }
fn deinit(self: *Self) void { fn deinit(self: *Self) void {
@ -846,8 +839,13 @@ const CallContext = struct {
} }
fn receive_(self: *Self, _: pid_ref, m: message) result { fn receive_(self: *Self, _: pid_ref, m: message) result {
defer self.done.set(); defer {
self.response.* = m.clone(self.a) catch |e| return exit_error(e, @errorReturnTrace()); const expired = self.from.expired();
self.from.deinit();
self.done.set();
if (expired) self.deinit();
}
self.response = m.clone(self.a) catch |e| return exit_error(e, @errorReturnTrace());
return exit_normal(); return exit_normal();
} }
}; };