fix: prevent another race in call()

This commit is contained in:
CJ van den Berg 2024-04-24 17:44:53 +02:00
parent e5983cdc8f
commit a98cb1f5e6

View file

@ -691,38 +691,34 @@ const CallContext = struct {
request: message, request: message,
response: *?message, response: *?message,
a: std.mem.Allocator, a: std.mem.Allocator,
mut: std.Thread.Mutex = std.Thread.Mutex{}, done: std.Thread.ResetEvent = .{},
cond: std.Thread.Condition = std.Thread.Condition{},
timeout: bool = false,
const Self = @This(); const Self = @This();
const ReceiverT = Receiver(*Self); const ReceiverT = Receiver(*Self);
pub fn call(a: std.mem.Allocator, to: pid_ref, timeout_ns: u64, request: message) error{ OutOfMemory, ThespianSpawnFailed, Timeout }!message { pub fn call(a: std.mem.Allocator, to: pid_ref, timeout_ns: u64, request: message) error{ OutOfMemory, ThespianSpawnFailed, Timeout }!message {
var response: ?message = null; var response: ?message = null;
var self = try a.create(Self); var self: Self = .{
self.* = .{ .receiver = undefined,
.receiver = ReceiverT.init(receive_, self),
.to = to, .to = to,
.request = request, .request = request,
.response = &response, .response = &response,
.a = a, .a = a,
}; };
self.receiver = ReceiverT.init(receive_, &self);
self.mut.lock(); const proc = try spawn_link(a, &self, start, @typeName(Self));
errdefer self.mut.unlock();
const proc = try spawn_link(a, self, start, @typeName(Self));
defer proc.deinit(); defer proc.deinit();
errdefer {
self.cond.timedWait(&self.mut, timeout_ns) catch |e| switch (e) { proc.send(.{"timeout"}) catch @panic("CallContext.send.timeout");
self.done.wait();
if (response) |resp| a.free(resp.buf);
}
self.done.timedWait(timeout_ns) catch |e| switch (e) {
error.Timeout => { error.Timeout => {
self.timeout = true;
return e; return e;
}, },
else => return e, else => return e,
}; };
return response orelse .{}; return response orelse .{};
} }
@ -731,10 +727,7 @@ const CallContext = struct {
} }
fn start(self: *Self) result { fn start(self: *Self) result {
errdefer { errdefer self.done.set();
self.cond.signal();
self.deinit();
}
_ = set_trap(true); _ = set_trap(true);
try self.to.link(); try self.to.link();
try self.to.send_raw(self.request); try self.to.send_raw(self.request);
@ -742,12 +735,8 @@ const CallContext = struct {
} }
fn receive_(self: *Self, _: pid_ref, m: message) result { fn receive_(self: *Self, _: pid_ref, m: message) result {
defer self.deinit(); defer self.done.set();
defer self.cond.signal(); self.response.* = m.clone(self.a) catch |e| return exit_error(e);
self.mut.lock();
defer self.mut.unlock();
if (!self.timeout)
self.response.* = m.clone(self.a) catch |e| return exit_error(e);
return exit_normal(); return exit_normal();
} }
}; };