main: handle various forms of process termination more safely

This attempts to solve the problem where, if the rotator is actively
rotating and the program is killed, the LabJack will not be reset and
the rotator will keep running. We install various signal handlers to
try to catch common cases (ctrl+c, terminal getting closed out from
under us). There are still ways to end the process that leave the
LabJack running (such as if it crashes, though there are currently no
known crashes), but I don't think it's possible to completely avoid
that.
This commit is contained in:
torque 2024-07-31 12:07:29 -07:00
parent c8cfc95938
commit 43692d8247
Signed by: torque
SSH Key Fingerprint: SHA256:nCrXefBNo6EbjNSQhv0nXmEg/VuNq3sMF5b8zETw3Tk
3 changed files with 68 additions and 7 deletions

View File

@ -10,7 +10,7 @@ const log = std.log.scoped(.RotCtl);
writer: std.io.BufferedWriter(512, std.net.Stream.Writer),
running: bool,
rotator: YaesuController,
rotator: *YaesuController,
pub fn run(allocator: std.mem.Allocator) !void {
// var server = std.net.StreamServer.init(.{ .reuse_address = true });
@ -30,7 +30,7 @@ pub fn run(allocator: std.mem.Allocator) !void {
var interface: RotCtl = .{
.writer = undefined,
.running = true,
.rotator = try YaesuController.init(allocator),
.rotator = try YaesuController.create(allocator),
};
while (interface.running) {

View File

@ -8,6 +8,8 @@ const log = std.log.scoped(.yaesu_controller);
const YaesuController = @This();
pub var singleton: ?*YaesuController = null;
control_thread: std.Thread,
lock: *std.Thread.Mutex,
controller: *const Controller,
@ -23,7 +25,7 @@ pub const CalibrationRoutine = enum {
};
pub fn calibrate(allocator: std.mem.Allocator, routine: CalibrationRoutine) !void {
const controller = try YaesuController.init(allocator);
const controller = try YaesuController.create(allocator);
defer {
controller.quit();
controller.control_thread.join();
@ -35,7 +37,12 @@ pub fn calibrate(allocator: std.mem.Allocator, routine: CalibrationRoutine) !voi
}
}
pub fn init(allocator: std.mem.Allocator) !YaesuController {
pub fn create(allocator: std.mem.Allocator) !*YaesuController {
if (singleton) |_| {
log.err("Controller singleton already exists.", .{});
return error.AlreadyInitialized;
}
const controller = try allocator.create(Controller);
errdefer allocator.destroy(controller);
controller.* = try Controller.init(allocator);
@ -43,11 +50,16 @@ pub fn init(allocator: std.mem.Allocator) !YaesuController {
// do this in the main thread so we can throw the error about it synchronously.
try controller.connectLabjack();
return .{
const self = try allocator.create(YaesuController);
errdefer allocator.destroy(self);
self.* = .{
.control_thread = try std.Thread.spawn(.{}, runController, .{controller}),
.lock = &controller.lock,
.controller = controller,
};
singleton = self;
return self;
}
fn inRange(request: f64, comptime dof: enum { azimuth, elevation }) bool {

View File

@ -10,6 +10,45 @@ const udev = @import("udev_rules");
const log = std.log.scoped(.main);
fn die() noreturn {
if (YaesuController.singleton) |controller| {
controller.quit();
controller.control_thread.join();
}
std.process.exit(1);
}
fn posixSignalHandler(signal: c_int) callconv(.C) void {
_ = signal;
die();
}
fn windowsEventHandler(code: std.os.windows.DWORD) callconv(std.os.windows.WINAPI) std.os.windows.BOOL {
_ = code;
die();
}
fn addAbortHandler() !void {
if (comptime builtin.os.tag == .windows) {
try std.os.windows.SetConsoleCtrlHandler(windowsEventHandler, true);
} else {
const action = std.posix.Sigaction{
.handler = .{ .handler = posixSignalHandler },
.mask = std.posix.empty_sigset,
.flags = 0,
};
for ([_]u6{
std.posix.SIG.INT,
std.posix.SIG.HUP,
std.posix.SIG.QUIT,
}) |sig| {
try std.posix.sigaction(sig, &action, null);
}
}
}
fn printStderr(comptime fmt: []const u8, args: anytype) void {
std.debug.print(fmt ++ "\n", args);
}
@ -66,9 +105,14 @@ pub fn main() !u8 {
defer Config.deinit();
addAbortHandler() catch {
log.err("Could not install quit handler.", .{});
return 1;
};
RotCtl.run(allocator) catch |err| {
log.err("rotator controller ceased unexpectedly! {s}", .{@errorName(err)});
return 1;
die();
};
} else if (std.mem.eql(u8, args[1], commands.calibrate)) {
if (args.len < 3 or args.len > 4) {
@ -86,9 +130,14 @@ pub fn main() !u8 {
return 1;
};
addAbortHandler() catch {
log.err("Could not install quit handler.", .{});
return 1;
};
YaesuController.calibrate(allocator, routine) catch |err| {
log.err("Calibration failed: {s}", .{@errorName(err)});
return 1;
die();
};
} else if (std.mem.eql(u8, args[1], commands.help)) {
if (args.len != 3) {