From cc66529b59545826df8cf34ac50ec080f15357c8 Mon Sep 17 00:00:00 2001 From: Danny Canter Date: Tue, 28 Oct 2025 02:24:53 -0700 Subject: [PATCH] VirtualMachineManager: Take in EventLoopGroup directly (#367) --- .../VZVirtualMachineInstance.swift | 21 ++++++++++++++----- .../VZVirtualMachineManager.swift | 5 +++++ Sources/Containerization/Vminitd.swift | 7 ++++--- Sources/Integration/Suite.swift | 4 ++++ 4 files changed, 29 insertions(+), 8 deletions(-) diff --git a/Sources/Containerization/VZVirtualMachineInstance.swift b/Sources/Containerization/VZVirtualMachineInstance.swift index 677af7e0..035009e8 100644 --- a/Sources/Containerization/VZVirtualMachineInstance.swift +++ b/Sources/Containerization/VZVirtualMachineInstance.swift @@ -76,13 +76,14 @@ struct VZVirtualMachineInstance: Sendable { // `vm` isn't used concurrently. private nonisolated(unsafe) let vm: VZVirtualMachine private let queue: DispatchQueue - private let group: MultiThreadedEventLoopGroup private let lock: AsyncMutex + private let group: EventLoopGroup + private let ownsGroup: Bool private let timeSyncer: TimeSyncer private let logger: Logger? public init( - group: MultiThreadedEventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount), + group: EventLoopGroup? = nil, logger: Logger? = nil, with: (inout Configuration) throws -> Void ) throws { @@ -91,9 +92,16 @@ struct VZVirtualMachineInstance: Sendable { try self.init(group: group, config: config, logger: logger) } - init(group: MultiThreadedEventLoopGroup, config: Configuration, logger: Logger?) throws { + init(group: EventLoopGroup?, config: Configuration, logger: Logger?) throws { + if let group { + self.ownsGroup = false + self.group = group + } else { + self.ownsGroup = true + self.group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) + } + self.config = config - self.group = group self.lock = .init(VendedConnections()) self.queue = DispatchQueue(label: "com.apple.containerization.vzvm.\(UUID().uuidString)") self.mounts = try config.mountAttachments() @@ -160,7 +168,10 @@ extension VZVirtualMachineInstance: VirtualMachineInstance { } connections.agents.removeAll() - try await self.group.shutdownGracefully() + if self.ownsGroup { + try await self.group.shutdownGracefully() + } + try await self.vm.stop(queue: self.queue) } } diff --git a/Sources/Containerization/VZVirtualMachineManager.swift b/Sources/Containerization/VZVirtualMachineManager.swift index f6588629..73d03df0 100644 --- a/Sources/Containerization/VZVirtualMachineManager.swift +++ b/Sources/Containerization/VZVirtualMachineManager.swift @@ -19,6 +19,7 @@ import ContainerizationError import ContainerizationOCI import Foundation import Logging +import NIOCore /// A virtualization.framework backed `VirtualMachineManager` implementation. public struct VZVirtualMachineManager: VirtualMachineManager { @@ -26,6 +27,7 @@ public struct VZVirtualMachineManager: VirtualMachineManager { private let initialFilesystem: Mount private let rosetta: Bool private let nestedVirtualization: Bool + private let group: EventLoopGroup? private let logger: Logger? public init( @@ -33,12 +35,14 @@ public struct VZVirtualMachineManager: VirtualMachineManager { initialFilesystem: Mount, rosetta: Bool = false, nestedVirtualization: Bool = false, + group: EventLoopGroup? = nil, logger: Logger? = nil ) { self.kernel = kernel self.initialFilesystem = initialFilesystem self.rosetta = rosetta self.nestedVirtualization = nestedVirtualization + self.group = group self.logger = logger } @@ -49,6 +53,7 @@ public struct VZVirtualMachineManager: VirtualMachineManager { let useNestedVirtualization = vmConfig.nestedVirtualization || self.nestedVirtualization return try VZVirtualMachineInstance( + group: self.group, logger: self.logger, with: { instanceConfig in instanceConfig.cpus = vmConfig.cpus diff --git a/Sources/Containerization/Vminitd.swift b/Sources/Containerization/Vminitd.swift index 5b8f1b0e..225f3b2b 100644 --- a/Sources/Containerization/Vminitd.swift +++ b/Sources/Containerization/Vminitd.swift @@ -19,6 +19,7 @@ import ContainerizationOCI import ContainerizationOS import Foundation import GRPC +import NIOCore import NIOPosix /// A remote connection into the vminitd Linux guest agent via a port (vsock). @@ -35,7 +36,7 @@ public struct Vminitd: Sendable { self.client = client } - public init(connection: FileHandle, group: MultiThreadedEventLoopGroup) { + public init(connection: FileHandle, group: EventLoopGroup) { self.client = .init(connection: connection, group: group) } @@ -450,7 +451,7 @@ extension Hosts { } extension Vminitd.Client { - public init(socket: String, group: MultiThreadedEventLoopGroup) { + public init(socket: String, group: EventLoopGroup) { var config = ClientConnection.Configuration.default( target: .unixDomainSocket(socket), eventLoopGroup: group @@ -461,7 +462,7 @@ extension Vminitd.Client { self = .init(channel: ClientConnection(configuration: config)) } - public init(connection: FileHandle, group: MultiThreadedEventLoopGroup) { + public init(connection: FileHandle, group: EventLoopGroup) { var config = ClientConnection.Configuration.default( target: .connectedSocket(connection.fileDescriptor), eventLoopGroup: group diff --git a/Sources/Integration/Suite.swift b/Sources/Integration/Suite.swift index b0b71106..813e6754 100644 --- a/Sources/Integration/Suite.swift +++ b/Sources/Integration/Suite.swift @@ -23,6 +23,7 @@ import ContainerizationOS import Foundation import Logging import NIOCore +import NIOPosix import Synchronization actor UnpackCoordinator { @@ -159,6 +160,8 @@ struct IntegrationSuite: AsyncParsableCommand { .appendingPathComponent(name) } + static let eventLoop = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) + func bootstrap(_ testID: String) async throws -> (rootfs: Containerization.Mount, vmm: VirtualMachineManager, image: Containerization.Image, bootlog: URL) { let reference = "ghcr.io/linuxcontainers/alpine:3.20" let store = Self.imageStore @@ -221,6 +224,7 @@ struct IntegrationSuite: AsyncParsableCommand { VZVirtualMachineManager( kernel: testKernel, initialFilesystem: initfs, + group: Self.eventLoop ), image, bootlogURL