Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -261,8 +261,7 @@ let package = Package(
.target(
name: "TerminalProgress",
dependencies: [
.product(name: "ContainerizationOS", package: "containerization"),
.product(name: "SendableProperty", package: "containerization"),
.product(name: "ContainerizationOS", package: "containerization")
]
),
.testTarget(
Expand Down
40 changes: 23 additions & 17 deletions Sources/Services/ContainerNetworkService/ReservedVmnetNetwork.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,21 @@ import ContainerizationExtras
import Dispatch
import Foundation
import Logging
import SendableProperty
import Synchronization
import SystemConfiguration
import XPC
import vmnet

/// Creates a vmnet network with reservation APIs.
@available(macOS 26, *)
public final class ReservedVmnetNetwork: Network {
@SendablePropertyUnchecked
private var _state: NetworkState
private let log: Logger
private struct State {
var networkState: NetworkState
var network: vmnet_network_ref?
}

@SendableProperty
private var network: vmnet_network_ref?
@SendableProperty
private var interface: interface_ref?
private let networkLock = NSLock()
private let stateMutex: Mutex<State>
private let log: Logger

/// Configure a bridge network that allows external system access using
/// network address translation.
Expand All @@ -51,23 +49,25 @@ public final class ReservedVmnetNetwork: Network {

log.info("creating vmnet network")
self.log = log
_state = .created(configuration)
let initialState = State(networkState: .created(configuration))
stateMutex = Mutex(initialState)
log.info("created vmnet network")
}

public var state: NetworkState {
get async { _state }
stateMutex.withLock { $0.networkState }
}

public nonisolated func withAdditionalData(_ handler: (XPCMessage?) throws -> Void) throws {
try networkLock.withLock {
try handler(network.map { try Self.serialize_network_ref(ref: $0) })
try stateMutex.withLock { state in
try handler(state.network.map { try Self.serialize_network_ref(ref: $0) })
}
}

public func start() async throws {
guard case .created(let configuration) = _state else {
throw ContainerizationError(.invalidArgument, message: "cannot start network that is in \(_state.state) state")
let networkState = stateMutex.withLock { $0.networkState }
guard case .created(let configuration) = networkState else {
throw ContainerizationError(.invalidArgument, message: "cannot start network that is in \(networkState.state) state")
}

try startNetwork(configuration: configuration, log: log)
Expand Down Expand Up @@ -125,7 +125,6 @@ public final class ReservedVmnetNetwork: Network {
guard let network = vmnet_network_create(vmnetConfiguration, &status), status == .VMNET_SUCCESS else {
throw ContainerizationError(.unsupported, message: "failed to create vmnet network with status \(status)")
}
self.network = network

// retrieve the subnet since the caller may not have provided one
var subnetAddr = in_addr()
Expand All @@ -137,7 +136,14 @@ public final class ReservedVmnetNetwork: Network {
let upper = IPv4Address(fromValue: lower.value + ~maskValue)
let runningSubnet = try CIDRAddress(lower: lower, upper: upper)
let runningGateway = IPv4Address(fromValue: runningSubnet.lower.value + 1)
self._state = .running(configuration, NetworkStatus(address: runningSubnet.description, gateway: runningGateway.description))
let networkState = NetworkState.running(configuration, NetworkStatus(address: runningSubnet.description, gateway: runningGateway.description))

let newNetwork = { network } // A workaround for "'inout sending' parameter '$0' cannot be task-isolated at end of function".
stateMutex.withLock { state in
state.network = newNetwork()
state.networkState = networkState
}

log.info(
"started vmnet network",
metadata: [
Expand Down
4 changes: 2 additions & 2 deletions Sources/SocketForwarder/UDPForwarder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ private final class UDPProxyBackend: ChannelInboundHandler, Sendable {
self.serverAddress = serverAddress
self.frontendChannel = frontendChannel
self.log = log
let state = State(queuedPayloads: Deque(), channel: nil)
self.state = Mutex(state)
let initialState = State(queuedPayloads: Deque(), channel: nil)
self.state = Mutex(initialState)
}

func channelRead(context: ChannelHandlerContext, data: NIOAny) {
Expand Down
6 changes: 4 additions & 2 deletions Sources/TerminalProgress/ProgressBar+Terminal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,10 @@ extension ProgressBar {
var text = text

// Clears previously printed characters if the new string is shorter.
text += String(repeating: " ", count: max(printedWidth - text.count, 0))
printedWidth = text.count
printedWidth.withLock {
text += String(repeating: " ", count: max($0 - text.count, 0))
$0 = text.count
}
state.withLock {
$0.output = text
}
Expand Down
4 changes: 1 addition & 3 deletions Sources/TerminalProgress/ProgressBar.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,13 @@
//===----------------------------------------------------------------------===//

import Foundation
import SendableProperty
import Synchronization

/// A progress bar that updates itself as tasks are completed.
public final class ProgressBar: Sendable {
let config: ProgressConfig
let state: Mutex<State>
@SendableProperty
var printedWidth = 0
let printedWidth = Mutex(0)
let term: FileHandle?
let termQueue = DispatchQueue(label: "com.apple.container.ProgressBar")
private let standardError = StandardError()
Expand Down