mirror of https://github.com/cirruslabs/tart.git
Monitor Softnet process and throw if it terminates prematurely (#270)
This commit is contained in:
parent
44650e9713
commit
667de2a199
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
Loading…
Reference in New Issue