Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
9 changes: 6 additions & 3 deletions Sources/ContainerXPC/XPCClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,17 @@ extension XPCClient {
/// Send the provided message to the service.
@discardableResult
public func send(_ message: XPCMessage, responseTimeout: Duration? = nil) async throws -> XPCMessage {
try await withThrowingTaskGroup(of: XPCMessage.self, returning: XPCMessage.self) { group in
// Use a more robust timeout that cancels the XPC connection if it hangs
return try await withThrowingTaskGroup(of: XPCMessage.self, returning: XPCMessage.self) { group in
if let responseTimeout {
group.addTask {
try await Task.sleep(for: responseTimeout)
// Cancel the connection to unblock the XPC call
xpc_connection_cancel(self.connection)
let route = message.string(key: XPCMessage.routeKey) ?? "nil"
throw ContainerizationError(
.internalError,
message: "XPC timeout for request to \(self.service)/\(route)"
.timeout,
message: "XPC timeout after \(Int(responseTimeout.components.seconds))s for \(self.service)/\(route). Is the apiserver running? Try: container system start"
)
}
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/ContainerXPC/XPCServer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public struct XPCServer: Sendable {

public func listen() async throws {
let connections = AsyncStream<xpc_connection_t> { cont in
lock.withLock {
self.lock.withLock {
xpc_connection_set_event_handler(self.connection) { object in
switch xpc_get_type(object) {
case XPC_TYPE_CONNECTION:
Expand Down
5 changes: 3 additions & 2 deletions Sources/Helpers/APIServer/APIServer+Start.swift
Original file line number Diff line number Diff line change
Expand Up @@ -240,12 +240,13 @@ extension APIServer {
log: log
)

// Check for default network - don't block on creation during startup
// to avoid blocking if vmnet plugin fails. Users can create networks on-demand.
let defaultNetwork = try await service.list()
.filter { $0.id == ClientNetwork.defaultNetworkName }
.first
if defaultNetwork == nil {
let config = try NetworkConfiguration(id: ClientNetwork.defaultNetworkName, mode: .nat)
_ = try await service.create(configuration: config)
log.info("default network not present, can be created with 'container network create'")
}

let harness = NetworksHarness(service: service, log: log)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ public actor NetworksService {
"id": "\(configuration.id)",
"state": "\(networkState.state)",
])
return
continue
}
}
}
Expand Down
13 changes: 8 additions & 5 deletions Sources/Services/ContainerNetworkService/NetworkClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,14 @@ public struct NetworkClient: Sendable {

// Runtime Methods
extension NetworkClient {
/// Default timeout for network XPC calls.
private static let xpcTimeout: Duration = .seconds(5)

public func state() async throws -> NetworkState {
let request = XPCMessage(route: NetworkRoutes.state.rawValue)
let client = createClient()

let response = try await client.send(request)
let response = try await client.send(request, responseTimeout: Self.xpcTimeout)
let state = try response.state()
return state
}
Expand All @@ -55,7 +58,7 @@ extension NetworkClient {

let client = createClient()

let response = try await client.send(request)
let response = try await client.send(request, responseTimeout: Self.xpcTimeout)
let attachment = try response.attachment()
let additionalData = response.additionalData()
return (attachment, additionalData)
Expand All @@ -66,7 +69,7 @@ extension NetworkClient {
request.set(key: NetworkKeys.hostname.rawValue, value: hostname)

let client = createClient()
try await client.send(request)
try await client.send(request, responseTimeout: Self.xpcTimeout)
}

public func lookup(hostname: String) async throws -> Attachment? {
Expand All @@ -75,7 +78,7 @@ extension NetworkClient {

let client = createClient()

let response = try await client.send(request)
let response = try await client.send(request, responseTimeout: Self.xpcTimeout)
return try response.dataNoCopy(key: NetworkKeys.attachment.rawValue).map {
try JSONDecoder().decode(Attachment.self, from: $0)
}
Expand All @@ -86,7 +89,7 @@ extension NetworkClient {

let client = createClient()

let response = try await client.send(request)
let response = try await client.send(request, responseTimeout: Self.xpcTimeout)
return try response.allocatorDisabled()
}

Expand Down