Monitor Softnet process and throw if it terminates prematurely (#270)

This commit is contained in:
Nikolay Edigaryev 2022-10-12 20:00:13 +04:00 committed by GitHub
parent 44650e9713
commit 667de2a199
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 47 additions and 12 deletions

View File

@ -54,6 +54,15 @@
"revision" : "f05e450f0b909c0e80670a47516c4b9700b9e5da"
}
},
{
"identity" : "swift-atomics",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-atomics.git",
"state" : {
"revision" : "919eb1d83e02121cdb434c7bfc1f0c66ef17febe",
"version" : "1.0.2"
}
},
{
"identity" : "swift-collections",
"kind" : "remoteSourceControl",

View File

@ -17,6 +17,7 @@ let package = Package(
.package(url: "https://github.com/malcommac/SwiftDate", from: "6.3.1"),
.package(url: "https://github.com/sushichop/Puppy", from: "0.5.1"),
.package(url: "https://github.com/antlr/antlr4", branch: "dev"),
.package(url: "https://github.com/apple/swift-atomics.git", .upToNextMajor(from: "1.0.0")),
],
targets: [
.executableTarget(name: "tart", dependencies: [
@ -27,6 +28,7 @@ let package = Package(
.product(name: "SwiftDate", package: "SwiftDate"),
.product(name: "Puppy", package: "Puppy"),
.product(name: "Antlr4Static", package: "Antlr4"),
.product(name: "Atomics", package: "swift-atomics"),
], exclude: [
"OCI/Reference/Makefile",
"OCI/Reference/Reference.g4",

View File

@ -2,6 +2,6 @@ import Virtualization
protocol Network {
func attachment() -> VZNetworkDeviceAttachment
func run() throws
func stop() throws
func run(_ sema: DispatchSemaphore) throws
func stop() async throws
}

View File

@ -12,11 +12,11 @@ class NetworkBridged: Network {
VZBridgedNetworkDeviceAttachment(interface: interface)
}
func run() throws {
func run(_ sema: DispatchSemaphore) throws {
// no-op, only used for Softnet
}
func stop() throws {
func stop() async throws {
// no-op, only used for Softnet
}
}

View File

@ -6,11 +6,11 @@ class NetworkShared: Network {
VZNATNetworkDeviceAttachment()
}
func run() throws {
func run(_ sema: DispatchSemaphore) throws {
// no-op, only used for Softnet
}
func stop() throws {
func stop() async throws {
// no-op, only used for Softnet
}
}

View File

@ -1,12 +1,16 @@
import Foundation
import Virtualization
import Atomics
enum SoftnetError: Error {
case InitializationFailed(why: String)
case RuntimeFailed(why: String)
}
class Softnet: Network {
private let process = Process()
private var monitorTask: Task<Void, Error>? = nil
private let monitorTaskFinished = ManagedAtomic<Bool>(false)
let vmFD: Int32
@ -35,13 +39,33 @@ class Softnet: Network {
process.standardInput = FileHandle(fileDescriptor: softnetFD, closeOnDealloc: false)
}
func run() throws {
func run(_ sema: DispatchSemaphore) throws {
try process.run()
monitorTask = Task {
// Wait for the Softnet to finish
process.waitUntilExit()
// Signal to the caller that the Softnet has finished
sema.signal()
// Signal to ourselves that the Softnet has finished
monitorTaskFinished.store(true, ordering: .sequentiallyConsistent)
}
}
func stop() throws {
process.interrupt()
process.waitUntilExit()
func stop() async throws {
if monitorTaskFinished.load(ordering: .sequentiallyConsistent) {
// Consume the monitor task's value to ensure the task has finished
_ = try await monitorTask?.value
throw SoftnetError.RuntimeFailed(why: "Softnet process terminated prematurely")
} else {
process.interrupt()
// Consume the monitor task's value to ensure the task has finished
_ = try await monitorTask?.value
}
}
private func setSocketBuffers(_ fd: Int32, _ sizeBytes: Int) throws {

View File

@ -209,7 +209,7 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject {
}
func run(_ recovery: Bool) async throws {
try network.run()
try network.run(sema)
DispatchQueue.main.sync {
Task {
@ -239,7 +239,7 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject {
}
}
try network.stop()
try await network.stop()
}
static func craftConfiguration(