From fef518e1c37ecec2ae75b48a696833ea70cfc0c2 Mon Sep 17 00:00:00 2001 From: Danny Canter Date: Sun, 8 Jun 2025 02:07:23 -0700 Subject: [PATCH] LinuxProcess: Resource adjustments (#33) - The kqueue handlers for stdio didn't have logic to exit on zero byte reads. - The agent wasn't getting closed explicitly in .delete() - In LinuxContainer we should call delete just for sanity, if for nothing more than ensuring the agent vsock fd is closed. Signed-off-by: Danny Canter --- Sources/Containerization/LinuxContainer.swift | 3 +++ Sources/Containerization/LinuxProcess.swift | 26 ++++++++++++++++--- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/Sources/Containerization/LinuxContainer.swift b/Sources/Containerization/LinuxContainer.swift index 9ae31ea1..4489ff1b 100644 --- a/Sources/Containerization/LinuxContainer.swift +++ b/Sources/Containerization/LinuxContainer.swift @@ -645,6 +645,9 @@ extension LinuxContainer { ) } + // Lets free up the init procs resources, as this includes the open agent conn. + try? await startedState.process.delete() + try await startedState.vm.stop() try state.stopped() } diff --git a/Sources/Containerization/LinuxProcess.swift b/Sources/Containerization/LinuxProcess.swift index 547632b9..0b1adbea 100644 --- a/Sources/Containerization/LinuxProcess.swift +++ b/Sources/Containerization/LinuxProcess.swift @@ -71,15 +71,21 @@ public final class LinuxProcess: Sendable { var stdout: FileHandle? var stderr: FileHandle? - func close() throws { + mutating func close() throws { if let stdin { try stdin.close() + stdin.readabilityHandler = nil + self.stdin = nil } if let stdout { try stdout.close() + stdout.readabilityHandler = nil + self.stdout = nil } if let stderr { try stderr.close() + stderr.readabilityHandler = nil + self.stderr = nil } } } @@ -194,6 +200,7 @@ extension LinuxProcess { try handle.write(contentsOf: data) } catch { self.logger?.error("failed to write to stdin: \(error)") + return } } } @@ -208,7 +215,12 @@ extension LinuxProcess { // as it always allocates. We can likely do the read loop ourselves // with a buffer we allocate once on creation of the process. do { - try stdout.writer.write(handle.availableData) + let data = handle.availableData + if data.isEmpty { + handles[1]?.readabilityHandler = nil + return + } + try stdout.writer.write(data) } catch { self.logger?.error("failed to write to stdout: \(error)") } @@ -218,7 +230,12 @@ extension LinuxProcess { if let stderr = self.ioSetup.stderr { handles[2]?.readabilityHandler = { handle in do { - try stderr.writer.write(handle.availableData) + let data = handle.availableData + if data.isEmpty { + handles[2]?.readabilityHandler = nil + return + } + try stderr.writer.write(data) } catch { self.logger?.error("failed to write to stderr: \(error)") } @@ -334,5 +351,8 @@ extension LinuxProcess { $0.stdinRelay?.cancel() try $0.stdio.close() } + + // Finally, close our agent conn. + try await self.agent.close() } }