Performs implementation-agnostic thread setup (maybeAttachSignalStack), then calls the given
thread entry point f with args and handles the result.
fn callFn(comptime f: anytype, args: anytype) switch (Impl)
fn callFn(comptime f: anytype, args: anytype) switch (Impl) {
WindowsThreadImpl => windows.NTSTATUS,
LinuxThreadImpl => u8,
PosixThreadImpl => ?*anyopaque,
else => unreachable,
} {
maybeAttachSignalStack();
const default_value = switch (Impl) {
WindowsThreadImpl => .SUCCESS,
LinuxThreadImpl => 0,
PosixThreadImpl => null,
else => unreachable,
};
const bad_fn_ret = "expected return type of startFn to be 'u8', 'noreturn', '!noreturn', 'void', or '!void'";
switch (@typeInfo(@typeInfo(@TypeOf(f)).@"fn".return_type.?)) {
.noreturn => {
@call(.auto, f, args);
},
.void => {
@call(.auto, f, args);
return default_value;
},
.int => |info| {
if (info.bits != 8) {
@compileError(bad_fn_ret);
}
const status = @call(.auto, f, args);
switch (Impl) {
WindowsThreadImpl => return @fromBackingInt(@intCast(status)),
LinuxThreadImpl => return status,
// pthreads don't support exit status, ignore value
PosixThreadImpl => return default_value,
else => unreachable,
}
},
.error_union => |info| {
switch (info.payload) {
void, noreturn => {
@call(.auto, f, args) catch |err| {
std.debug.print("error: {s}\n", .{@errorName(err)});
if (@errorReturnTrace()) |trace| {
std.debug.dumpErrorReturnTrace(trace);
}
};
return default_value;
},
else => {
@compileError(bad_fn_ret);
},
}
},
else => {
@compileError(bad_fn_ret);
},
}
}