From 445fd2fd7ff8ab711804fc27bdcf4535a30f5cb0 Mon Sep 17 00:00:00 2001 From: cozis Date: Thu, 4 Jun 2026 17:42:10 +0200 Subject: [PATCH] scheduler: Make sleep cancellable --- src/io_interface.zig | 28 +++++++++++- src/node.zig | 104 +++++++++++++++++++++++++------------------ src/scheduler.zig | 3 +- 3 files changed, 90 insertions(+), 45 deletions(-) diff --git a/src/io_interface.zig b/src/io_interface.zig index 2964e7d..c25b485 100644 --- a/src/io_interface.zig +++ b/src/io_interface.zig @@ -428,6 +428,7 @@ fn writeFile(node: *Node, file: File, offset: ?usize, header: []const u8, data: return node.writeFile(file.handle, offset, header, data) catch |err| switch (err) { error.InvalidHandle => error.NotOpenForWriting, error.OutOfMemory => error.SystemResources, + error.Canceled => @panic("TODO"), }; } @@ -467,6 +468,7 @@ fn dirCreateDir(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, permissio Node.CreateDirError.ComponentNotDirectory => Dir.CreateDirError.NotDir, Node.CreateDirError.ComponentNotFound => Dir.CreateDirError.FileNotFound, Node.CreateDirError.OutOfMemory => Dir.CreateDirError.SystemResources, + Node.CreateDirError.Canceled => @panic("TODO"), }; }; } @@ -506,6 +508,7 @@ fn dirOpenDir(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, options: Di Node.OpenDirError.ResolutionLimit => Dir.OpenError.NameTooLong, Node.OpenDirError.ComponentNotDirectory => Dir.OpenError.NotDir, Node.OpenDirError.ComponentNotFound => Dir.OpenError.FileNotFound, + Node.OpenDirError.Canceled => @panic("TODO"), }; }; @@ -551,6 +554,7 @@ fn dirCreateFile(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, flags: D Node.CreateFileError.ComponentNotDirectory => File.OpenError.NotDir, Node.CreateFileError.ComponentNotFound => File.OpenError.FileNotFound, Node.CreateFileError.OutOfMemory => File.OpenError.SystemResources, + Node.CreateFileError.Canceled => @panic("TODO"), }; }; @@ -566,6 +570,7 @@ fn dirCreateFile(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, flags: D Node.OpenFileError.ResolutionLimit => File.OpenError.NameTooLong, Node.OpenFileError.ComponentNotDirectory => File.OpenError.NotDir, Node.OpenFileError.ComponentNotFound => File.OpenError.FileNotFound, + Node.OpenFileError.Canceled => @panic("TODO"), }; }; @@ -601,6 +606,7 @@ fn dirOpenFile(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, flags: Dir Node.OpenFileError.ResolutionLimit => File.OpenError.NameTooLong, Node.OpenFileError.ComponentNotDirectory => File.OpenError.NotDir, Node.OpenFileError.ComponentNotFound => File.OpenError.FileNotFound, + Node.OpenFileError.Canceled => @panic("TODO"), }; }; @@ -628,6 +634,7 @@ fn dirRead(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.Read if (dr.state == .reset) { node.resetDir(dr.dir.handle) catch |e| switch (e) { error.InvalidHandle => return Dir.Reader.Error.AccessDenied, + error.Canceled => @panic("TODO"), }; dr.state = .reading; } @@ -636,6 +643,7 @@ fn dirRead(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.Read while (count < buffer.len) { const entry = node.readDir(dr.dir.handle) catch |e| switch (e) { error.InvalidHandle => return Dir.Reader.Error.AccessDenied, + error.Canceled => @panic("TODO"), error.NoMoreItems => { dr.state = .finished; return count; @@ -686,6 +694,7 @@ fn dirDeleteFile(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Dir.Dele Node.DeleteFileError.ComponentNotFound => Dir.DeleteFileError.FileNotFound, Node.DeleteFileError.NotFound => Dir.DeleteFileError.FileNotFound, Node.DeleteFileError.IsDirectory => Dir.DeleteFileError.IsDir, + Node.DeleteFileError.Canceled => @panic("TODO"), }; }; } @@ -706,6 +715,7 @@ fn dirDeleteDir(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Dir.Delet Node.DeleteDirError.NotFound => Dir.DeleteDirError.FileNotFound, Node.DeleteDirError.NotDirectory => Dir.DeleteDirError.NotDir, Node.DeleteDirError.DirNotEmpty => Dir.DeleteDirError.DirNotEmpty, + Node.DeleteDirError.Canceled => @panic("TODO"), }; }; } @@ -807,6 +817,7 @@ fn fileLength(userdata: ?*anyopaque, file: File) File.LengthError!u64 { const node: *Node = @ptrCast(@alignCast(userdata.?)); return node.fileSize(file.handle) catch |e| switch (e) { error.InvalidHandle => File.LengthError.Streaming, + error.Canceled => @panic("TODO"), }; } @@ -833,6 +844,7 @@ fn fileWritePositional( const written = node.writeFile(file.handle, cursor, header, &.{}) catch |err| switch (err) { error.InvalidHandle => return File.WritePositionalError.NotOpenForWriting, error.OutOfMemory => return File.WritePositionalError.SystemResources, + error.Canceled => @panic("TODO"), }; cursor = std.math.add(usize, cursor, written) catch return File.WritePositionalError.FileTooBig; copied += written; @@ -845,6 +857,7 @@ fn fileWritePositional( const written = node.writeFile(file.handle, cursor, bytes, &.{}) catch |err| switch (err) { error.InvalidHandle => return File.WritePositionalError.NotOpenForWriting, error.OutOfMemory => return File.WritePositionalError.SystemResources, + error.Canceled => @panic("TODO"), }; cursor = std.math.add(usize, cursor, written) catch return File.WritePositionalError.FileTooBig; copied += written; @@ -857,6 +870,7 @@ fn fileWritePositional( const written = node.writeFile(file.handle, cursor, pattern, &.{}) catch |err| switch (err) { error.InvalidHandle => return File.WritePositionalError.NotOpenForWriting, error.OutOfMemory => return File.WritePositionalError.SystemResources, + error.Canceled => @panic("TODO"), }; cursor = std.math.add(usize, cursor, written) catch return File.WritePositionalError.FileTooBig; copied += written; @@ -893,6 +907,7 @@ fn fileReadPositional(userdata: ?*anyopaque, file: File, data: []const []u8, off const bytes_read = node.readFile(file.handle, @intCast(offset + copied), buffer) catch |e| { return switch (e) { error.InvalidHandle => File.ReadPositionalError.NotOpenForReading, + error.Canceled => @panic("TODO"), }; }; copied += bytes_read; @@ -907,6 +922,7 @@ fn fileSeekBy(userdata: ?*anyopaque, file: File, offset: i64) File.SeekError!voi node.seekFileBy(file.handle, offset) catch |err| switch (err) { error.InvalidHandle => return File.SeekError.AccessDenied, error.NegativeOffset, error.Overflow => return File.SeekError.Unseekable, + error.Canceled => @panic("TODO"), }; } @@ -916,6 +932,7 @@ fn fileSeekTo(userdata: ?*anyopaque, file: File, offset: u64) File.SeekError!voi node.seekFileTo(file.handle, offset_usize) catch |err| switch (err) { error.InvalidHandle => return File.SeekError.AccessDenied, error.NegativeOffset, error.Overflow => return File.SeekError.Unseekable, + error.Canceled => @panic("TODO"), }; } @@ -1167,7 +1184,7 @@ fn sleep(userdata: ?*anyopaque, timeout: Timeout) Cancelable!void { if (delta_us == 0) return; - node.sleep(delta_us); + try node.sleep(delta_us); } fn random(userdata: ?*anyopaque, buffer: []u8) void { @@ -1219,6 +1236,7 @@ fn netListenIp(userdata: ?*anyopaque, address: *const net.IpAddress, options: ne error.AddressNotAvailable => return error.AddressUnavailable, error.AddressAlreadyUsed => return error.AddressInUse, error.OutOfMemory => return error.SystemResources, + error.Canceled => @panic("TODO"), }; return .{ @@ -1236,6 +1254,7 @@ fn netAccept(userdata: ?*anyopaque, listen_handle: net.Socket.Handle, options: n error.InvalidHandle => return error.SocketNotListening, error.AcceptQueueEmpty => return error.WouldBlock, error.OutOfMemory => return error.SystemResources, + error.Canceled => @panic("TODO"), }; return .{ @@ -1267,6 +1286,7 @@ fn netConnectIp(userdata: ?*anyopaque, address: *const net.IpAddress, options: n error.UnavailableHost => return error.HostUnreachable, error.PeerNotListeningOnAddress => return error.ConnectionRefused, error.OutOfMemory => return error.SystemResources, + error.Canceled => @panic("TODO"), }; return .{ @@ -1304,6 +1324,7 @@ fn netSend(userdata: ?*anyopaque, handle: net.Socket.Handle, messages: []net.Out error.InvalidHandle => return .{ error.SocketUnconnected, index }, error.NotConnected => return .{ error.SocketUnconnected, index }, error.OutOfMemory => return .{ error.SystemResources, index }, + error.Canceled => @panic("TODO"), }; message.data_len = written; } @@ -1323,6 +1344,7 @@ fn netRead(userdata: ?*anyopaque, fd: net.Socket.Handle, data: [][]u8) net.Strea const block = (copied == 0); const n = node.readSocket(fd, buffer, block) catch |e| switch (e) { error.InvalidHandle => return error.SocketUnconnected, + error.Canceled => @panic("TODO"), }; if (n == 0) break; @@ -1345,6 +1367,7 @@ fn netWrite(userdata: ?*anyopaque, handle: net.Socket.Handle, header: []const u8 error.InvalidHandle => return error.SocketUnconnected, error.NotConnected => return error.SocketUnconnected, error.OutOfMemory => return error.SystemResources, + error.Canceled => @panic("TODO"), }; if (data.len != 0) { @@ -1355,6 +1378,7 @@ fn netWrite(userdata: ?*anyopaque, handle: net.Socket.Handle, header: []const u8 error.InvalidHandle => return error.SocketUnconnected, error.NotConnected => return error.SocketUnconnected, error.OutOfMemory => return error.SystemResources, + error.Canceled => @panic("TODO"), }; } @@ -1366,6 +1390,7 @@ fn netWrite(userdata: ?*anyopaque, handle: net.Socket.Handle, header: []const u8 error.InvalidHandle => return error.SocketUnconnected, error.NotConnected => return error.SocketUnconnected, error.OutOfMemory => return error.SystemResources, + error.Canceled => @panic("TODO"), }; } } @@ -1396,6 +1421,7 @@ fn netShutdown(userdata: ?*anyopaque, handle: net.Socket.Handle, how: net.Shutdo node.closeSocket(handle) catch |err| switch (err) { error.InvalidHandle => return error.SocketUnconnected, + error.Canceled => @panic("TODO"), }; } diff --git a/src/node.zig b/src/node.zig index d5f4d28..47f54c6 100644 --- a/src/node.zig +++ b/src/node.zig @@ -135,8 +135,8 @@ pub fn processInit(self: *Node) std.process.Init { }; } -pub fn sleep(self: *Node, delta_us: u64) void { - self.scheduler.sleep(delta_us); +pub fn sleep(self: *Node, delta_us: u64) !void { + return self.scheduler.sleep(delta_us); } pub fn spawn(self: *Node, entry: NestedEntryPoint, context: *const anyopaque) !TaskID { @@ -183,6 +183,10 @@ const HandleError = error { InvalidHandle, }; +pub const CancelError = error { + Canceled, +}; + const NUM_SPECIAL_HANDLES = 3; fn handleToDesc(self: *Node, handle: Handle) HandleError!*Descriptor { @@ -216,17 +220,19 @@ fn descToHandle(self: *Node, desc: *Descriptor) Handle { unreachable; } -pub fn closeDir(self: *Node, handle: Handle) HandleError!void { - self.scheduler.sleep(10); +pub const CloseDirError = HandleError || CancelError; + +pub fn closeDir(self: *Node, handle: Handle) CloseDirError!void { + try self.scheduler.sleep(10); const desc = try self.handleToDescOfType(handle, .dir); self.file_system.closeDir(&desc.dir, self.gpa); desc.kind = .unused; } -pub const CreateDirError = HandleError || FileSystem.CreateError; +pub const CreateDirError = HandleError || FileSystem.CreateError || CancelError; pub fn createDir(self: *Node, parent: ?Handle, path: []const u8) CreateDirError!void { - self.scheduler.sleep(10); + try self.scheduler.sleep(10); return self.file_system.createDir( path, try self.handleToOpenDirOrNULL(parent), @@ -236,26 +242,28 @@ pub fn createDir(self: *Node, parent: ?Handle, path: []const u8) CreateDirError! pub const OpenDirError = error { DescriptorLimit, -} || HandleError || FileSystem.OpenError; +} || HandleError || FileSystem.OpenError || CancelError; pub fn openDir(self: *Node, parent: ?Handle, path: []const u8) OpenDirError!Handle { - self.scheduler.sleep(10); + try self.scheduler.sleep(10); const desc = self.unusedDesc() orelse return OpenDirError.DescriptorLimit; try self.file_system.openDir(path, try self.handleToOpenDirOrNULL(parent), &desc.dir); desc.kind = .dir; return self.descToHandle(desc); } -pub fn resetDir(self: *Node, handle: Handle) HandleError!void { - self.scheduler.sleep(2); +pub const ResetDirError = HandleError || CancelError; + +pub fn resetDir(self: *Node, handle: Handle) ResetDirError!void { + try self.scheduler.sleep(2); const desc = try self.handleToDescOfType(handle, .dir); self.file_system.resetDir(&desc.dir); } -pub const ReadDirError = HandleError || FileSystem.ReadDirError; +pub const ReadDirError = HandleError || FileSystem.ReadDirError || CancelError; pub fn readDir(self: *Node, handle: Handle) ReadDirError!FileSystem.ReadDir { - self.scheduler.sleep(10); + try self.scheduler.sleep(10); const desc = try self.handleToDescOfType(handle, .dir); return self.file_system.readDir(&desc.dir); } @@ -269,10 +277,10 @@ fn handleToOpenDirOrNULL(self: *Node, handle: ?Handle) HandleError!?*FileSystem. } } -pub const CreateFileError = HandleError || FileSystem.CreateError; +pub const CreateFileError = HandleError || FileSystem.CreateError || CancelError; pub fn createFile(self: *Node, parent: ?Handle, path: []const u8) CreateFileError!void { - self.scheduler.sleep(10); + try self.scheduler.sleep(10); return self.file_system.createFile( path, try self.handleToOpenDirOrNULL(parent), @@ -280,10 +288,10 @@ pub fn createFile(self: *Node, parent: ?Handle, path: []const u8) CreateFileErro ); } -pub const DeleteFileError = HandleError || FileSystem.DeleteFileError; +pub const DeleteFileError = HandleError || FileSystem.DeleteFileError || CancelError; pub fn deleteFile(self: *Node, parent: ?Handle, path: []const u8) DeleteFileError!void { - self.scheduler.sleep(10); + try self.scheduler.sleep(10); return self.file_system.deleteFile( path, try self.handleToOpenDirOrNULL(parent), @@ -291,10 +299,10 @@ pub fn deleteFile(self: *Node, parent: ?Handle, path: []const u8) DeleteFileErro ); } -pub const DeleteDirError = HandleError || FileSystem.DeleteDirError; +pub const DeleteDirError = HandleError || FileSystem.DeleteDirError || CancelError; pub fn deleteDir(self: *Node, parent: ?Handle, path: []const u8) DeleteDirError!void { - self.scheduler.sleep(10); + try self.scheduler.sleep(10); return self.file_system.deleteDir( path, try self.handleToOpenDirOrNULL(parent), @@ -304,31 +312,37 @@ pub fn deleteDir(self: *Node, parent: ?Handle, path: []const u8) DeleteDirError! pub const OpenFileError = error { DescriptorLimit, -} || HandleError || FileSystem.OpenError; +} || HandleError || FileSystem.OpenError || CancelError; pub fn openFile(self: *Node, parent: ?Handle, path: []const u8) OpenFileError!Handle { - self.scheduler.sleep(10); + try self.scheduler.sleep(10); const desc = self.unusedDesc() orelse return OpenFileError.DescriptorLimit; try self.file_system.openFile(path, try self.handleToOpenDirOrNULL(parent), &desc.file); desc.kind = .file; return self.descToHandle(desc); } -pub fn closeFile(self: *Node, handle: Handle) HandleError!void { - self.scheduler.sleep(10); +pub const CloseFileError = HandleError || CancelError; + +pub fn closeFile(self: *Node, handle: Handle) CloseFileError!void { + try self.scheduler.sleep(10); const desc = try self.handleToDescOfType(handle, .file); self.file_system.closeFile(&desc.file, self.gpa); desc.kind = .unused; } -pub fn fileSize(self: *Node, handle: Handle) HandleError!u64 { - self.scheduler.sleep(2); +pub const FileSizeError = HandleError || CancelError; + +pub fn fileSize(self: *Node, handle: Handle) FileSizeError!u64 { + try self.scheduler.sleep(2); const desc = try self.handleToDescOfType(handle, .file); return @intCast(self.file_system.fileSize(&desc.file)); } -pub fn readFile(self: *Node, handle: Handle, offset: ?usize, target: []u8) HandleError!usize { - self.scheduler.sleep(100); +pub const ReadFileError = HandleError || CancelError; + +pub fn readFile(self: *Node, handle: Handle, offset: ?usize, target: []u8) ReadFileError!usize { + try self.scheduler.sleep(100); if (handle == 0) { @panic("Not implemented yet"); // TODO: stdin } else if (handle == 1) { @@ -355,7 +369,7 @@ fn writeToStderr(self: *Node, source: []const u8) void { self.stderr_writer.interface.flush() catch {}; } -pub const WriteFileError = HandleError || Allocator.Error; +pub const WriteFileError = HandleError || Allocator.Error || CancelError; pub fn writeFile( self : *Node, @@ -365,7 +379,7 @@ pub fn writeFile( source: []const []const u8 ) WriteFileError!usize { - self.scheduler.sleep(100); + try self.scheduler.sleep(100); if (handle == 0) { return HandleError.InvalidHandle; @@ -400,16 +414,16 @@ pub fn writeFile( } } -pub const SeekFileError = HandleError || FileSystem.SeekError; +pub const SeekFileError = HandleError || FileSystem.SeekError || CancelError; pub fn seekFileTo(self: *Node, handle: Handle, offset: usize) SeekFileError!void { - self.scheduler.sleep(2); + try self.scheduler.sleep(2); const desc = try self.handleToDescOfType(handle, .file); self.file_system.seekFileTo(&desc.file, offset); } pub fn seekFileBy(self: *Node, handle: Handle, offset: i64) SeekFileError!void { - self.scheduler.sleep(2); + try self.scheduler.sleep(2); const desc = try self.handleToDescOfType(handle, .file); try self.file_system.seekFileBy(&desc.file, offset); } @@ -417,10 +431,10 @@ pub fn seekFileBy(self: *Node, handle: Handle, offset: i64) SeekFileError!void { pub const Address = Network.Address; pub const ListenError = error { DescriptorLimit, -} || Network.ListenError; +} || Network.ListenError || CancelError; pub fn listen(self: *Node, address: Address) ListenError!Handle { - self.scheduler.sleep(10); + try self.scheduler.sleep(10); const desc = self.unusedDesc() orelse return ListenError.DescriptorLimit; try self.network_host.listen(address, &desc.listen); desc.kind = .listen; @@ -429,10 +443,10 @@ pub fn listen(self: *Node, address: Address) ListenError!Handle { pub const AcceptError = error { DescriptorLimit, -} || HandleError || Network.AcceptError; +} || HandleError || Network.AcceptError || CancelError; pub fn accept(self: *Node, handle: Handle) AcceptError!Handle { - self.scheduler.sleep(10); + try self.scheduler.sleep(10); const old_desc = try self.handleToDescOfType(handle, .listen); const new_desc = self.unusedDesc() orelse return AcceptError.DescriptorLimit; try self.network_host.accept(&old_desc.listen, &new_desc.conn); @@ -442,17 +456,19 @@ pub fn accept(self: *Node, handle: Handle) AcceptError!Handle { pub const ConnectError = error { DescriptorLimit, -} || Network.ConnectError; +} || Network.ConnectError || CancelError; pub fn connect(self: *Node, address: Address) ConnectError!Handle { - self.scheduler.sleep(10); + try self.scheduler.sleep(10); const desc = self.unusedDesc() orelse return ConnectError.DescriptorLimit; try self.network_host.connect(address, &desc.conn); desc.kind = .conn; return self.descToHandle(desc); } -pub fn readSocket(self: *Node, handle: Handle, target: []u8, block: bool) HandleError!usize { +pub const ReadSocketError = HandleError || CancelError; + +pub fn readSocket(self: *Node, handle: Handle, target: []u8, block: bool) ReadSocketError!usize { if (target.len == 0) return 0; @@ -460,7 +476,7 @@ pub fn readSocket(self: *Node, handle: Handle, target: []u8, block: bool) Handle if (block) { while (true) { - self.scheduler.sleep(10); + try self.scheduler.sleep(10); const num = self.network_host.read(&desc.conn, target); @@ -478,16 +494,18 @@ pub fn readSocket(self: *Node, handle: Handle, target: []u8, block: bool) Handle } } -pub const WriteSocketError = HandleError || Network.SendError; +pub const WriteSocketError = HandleError || Network.SendError || CancelError; pub fn writeSocket(self: *Node, handle: Handle, source: []const u8) WriteSocketError!usize { - self.scheduler.sleep(10); + try self.scheduler.sleep(10); const desc = try self.handleToDescOfType(handle, .conn); return self.network_host.send(&desc.conn, source); } -pub fn closeSocket(self: *Node, handle: Handle) HandleError!void { - self.scheduler.sleep(10); +pub const CloseSocketError = HandleError || CancelError; + +pub fn closeSocket(self: *Node, handle: Handle) CloseSocketError!void { + try self.scheduler.sleep(10); const desc = try self.handleToDesc(handle); if (desc.kind == .conn) { self.network_host.closeConnSocket(&desc.conn); diff --git a/src/scheduler.zig b/src/scheduler.zig index 84662c5..3840aa3 100644 --- a/src/scheduler.zig +++ b/src/scheduler.zig @@ -316,13 +316,14 @@ fn taskReturned() callconv(.c) noreturn { } // Called by the current task to return control to the scheduler -pub fn sleep(self: *Scheduler, delta_us: u64) void { +pub fn sleep(self: *Scheduler, delta_us: u64) !void { const current = self.findTaskByID(self.current_id.?).?; current.state = .blocked; current.wakeup_time = self.current_time + delta_us; current.wakeup_tasks = null; current.wakeup_futex = null; contextSwitch(¤t.regs, &self.regs); + try self.checkCancel(); } pub fn futexWait(self: *Scheduler, ptr: *const u32, expected: u32) void {