Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 102 additions & 8 deletions src/fiberscript/syscalls.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ const std = @import("std");
const testing = std.testing;
const c = @import("wren.zig");

/// Special marker type for syscalls that complete asynchronously.
/// When a syscall returns this type, the fiber should remain suspended
/// until the operation completes and a result is provided via other means.
pub const Pending = struct {};

/// Example context type for testing
const TestContext = struct {
output_buffer: std.ArrayList(u8),
Expand Down Expand Up @@ -201,6 +206,60 @@ pub fn RequestUnion(comptime Syscalls: type) type {
});
}

/// Generate a union type representing the return values of each syscall.
pub fn ResultUnion(comptime Syscalls: type) type {
const syscall_fields = std.meta.fields(Syscalls);

var union_fields: [syscall_fields.len]std.builtin.Type.UnionField = undefined;

// Build union fields from each syscall's return type
for (syscall_fields, 0..) |field, i| {
const ptr_info = @typeInfo(field.type).pointer;
const func_info = @typeInfo(ptr_info.child).@"fn";
var return_type = func_info.return_type.?;
if (@typeInfo(return_type) == .error_union) {
return_type = @typeInfo(return_type).error_union.payload;
}
const alignment = if (return_type == void) 0 else @alignOf(return_type);
union_fields[i] = .{
.name = field.name,
.type = return_type,
.alignment = alignment,
};
}

var enum_fields: [syscall_fields.len]std.builtin.Type.EnumField = undefined;
for (syscall_fields, 0..) |field, i| {
enum_fields[i] = .{ .name = field.name, .value = i };
}

const TagEnum = @Type(.{
.@"enum" = .{
.tag_type = u32,
.fields = &enum_fields,
.decls = &[_]std.builtin.Type.Declaration{},
.is_exhaustive = true,
},
});

return @Type(.{
.@"union" = .{
.layout = .auto,
.tag_type = TagEnum,
.fields = &union_fields,
.decls = &[_]std.builtin.Type.Declaration{},
},
});
}

/// Wrapper union distinguishing between immediate results and pending operations.
pub fn SyscallResult(comptime Syscalls: type) type {
return union(enum) {
immediate: ResultUnion(Syscalls),
pending: void,
};
}

/// Generate slot parsing function for a Request union
pub fn generateSlotParser(comptime Request: type, comptime Syscalls: type) type {
return struct {
Expand Down Expand Up @@ -288,12 +347,14 @@ pub fn generateSlotParser(comptime Request: type, comptime Syscalls: type) type
pub fn generateTrampoline(comptime Syscalls: type, comptime Context: type) type {
return struct {
const RequestType = RequestUnion(Syscalls);
const ResultType = ResultUnion(Syscalls);
const DispatchResult = SyscallResult(Syscalls);

syscalls: Syscalls,
context: *Context,

/// Process a single request from a yielding fiber
pub fn dispatch(self: *@This(), request: RequestType) !void {
pub fn dispatch(self: *@This(), request: RequestType) !DispatchResult {
const syscall_fields = comptime std.meta.fields(Syscalls);

switch (request) {
Expand All @@ -307,9 +368,20 @@ pub fn generateTrampoline(comptime Syscalls: type, comptime Context: type) type
inline for (syscall_fields) |field| {
if (comptime std.mem.eql(u8, @tagName(tag), field.name)) {
const syscall_fn = @field(self.syscalls, field.name);
const result = syscall_fn(self.context, payload) catch |err| return err;
_ = result;
return;
const ptr_info = @typeInfo(@TypeOf(syscall_fn)).pointer;
const func_info = @typeInfo(ptr_info.child).@"fn";
const ReturnType = func_info.return_type.?;

if (ReturnType == Pending) {
_ = try syscall_fn(self.context, payload);
return .{ .pending = {} };
} else if (ReturnType == void) {
try syscall_fn(self.context, payload);
return .{ .immediate = @unionInit(ResultType, field.name, {}) };
} else {
const value = try syscall_fn(self.context, payload);
return .{ .immediate = @unionInit(ResultType, field.name, value) };
}
}
}

Expand Down Expand Up @@ -668,14 +740,29 @@ test "can generate trampoline dispatcher" {

// Test dispatching a print request
const print_request = Trampoline.RequestType{ .print = .{ .message = "hello trampoline" } };
try trampoline.dispatch(print_request);
const print_result = try trampoline.dispatch(print_request);
switch (print_result) {
.immediate => |im| switch (im) {
.print => |msg| try testing.expectEqualStrings("hello trampoline", msg),
else => try testing.expect(false),
},
else => try testing.expect(false),
}

// Verify the syscall was called
try testing.expectEqualStrings("hello trampoline", context.output_buffer.items);

// Test dispatching a readFile request
const read_request = Trampoline.RequestType{ .readFile = .{ .path = "test.txt" } };
try trampoline.dispatch(read_request);
const read_result = try trampoline.dispatch(read_request);
switch (read_result) {
.immediate => |im| switch (im) {
.readFile => |contents| try testing.expectEqualStrings("file contents", contents),
else => try testing.expect(false),
},
else => try testing.expect(false),
}

}

test "can generate foreign class system for requests" {
Expand Down Expand Up @@ -778,8 +865,15 @@ test "complete syscalls system integration" {
// 4. Dispatch foreign class request to get union
const request = try RequestClasses.dispatchRequest(&foreign_request);

// 5. Process through trampoline
try trampoline.dispatch(request);
// 5. Process through trampoline and verify return value
const result = try trampoline.dispatch(request);
switch (result) {
.immediate => |im| switch (im) {
.print => |msg| try testing.expectEqualStrings("Hello from foreign class!", msg),
else => try testing.expect(false),
},
else => try testing.expect(false),
}

// 6. Verify syscall was executed
try testing.expectEqualStrings("Hello from foreign class!", context.output_buffer.items);
Expand Down
6 changes: 5 additions & 1 deletion src/fiberscript/vm_context.zig
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,11 @@ pub const VMContext = struct {
},

else => {
try self.trampoliner.dispatch(request);
const dispatch_result = try self.trampoliner.dispatch(request);
switch (dispatch_result) {
.immediate => |_| {},
.pending => {},
}
},
}
}
Expand Down