Files
container/Sources/ContainerizationOS/AsyncSignalHandler.swift
T
Danny Canter 6bc4bf5124 Source code documentation updates (#9)
This change adds documentation to quite a few existing public types that
didn't have a blurb before.

Additionally, this fixes a couple things that I think either didn't make
sense when going to document them:
- Rename ConnectionStream to VsockConnectionStream. This type only
functions for vsock connections.
- Deletes NsLock+Closure. This was not used anywhere.
- Rename ContainerizationOCI/Config.swift to ImageConfig.swift.

Signed-off-by: Danny Canter <danny_canter@apple.com>
2025-06-05 16:16:18 -07:00

94 lines
3.4 KiB
Swift

//===----------------------------------------------------------------------===//
// Copyright © 2025 Apple Inc. and the containerization project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
import Foundation
import Synchronization
/// Async friendly wrapper around DispatchSourceSignal. Provides an AsyncStream
/// interface to get notified of received signals.
public final class AsyncSignalHandler: Sendable {
/// An async stream that returns the signal that was caught, if ever
public var signals: AsyncStream<Int32> {
let (stream, cont) = AsyncStream.makeStream(of: Int32.self)
self.state.withLock {
$0.conts.append(cont)
}
cont.onTermination = { @Sendable _ in
self.cancel()
}
return stream
}
/// Cancel every AsyncStream of signals, as well as the underlying
/// DispatchSignalSource's for each registered signal.
public func cancel() {
self.state.withLock {
if $0.conts.isEmpty {
return
}
for cont in $0.conts {
cont.finish()
}
for source in $0.sources {
source.cancel()
}
$0.conts.removeAll()
$0.sources.removeAll()
}
}
struct State: Sendable {
var conts: [AsyncStream<Int32>.Continuation] = []
nonisolated(unsafe) var sources: [any DispatchSourceSignal] = []
}
// We keep a reference to the continuation object that is created for
// our AsyncStream and tell our singal handler to yield a value to it
// returing a value to the consumer
private func handler(_ sig: Int32) {
self.state.withLock {
for cont in $0.conts {
cont.yield(sig)
}
}
}
private let state: Mutex<State> = .init(State())
/// Create a new `AsyncSignalHandler` for the list of given signals `notify`.
/// The default signal handlers for these signals are removed and async handlers
/// added in their place. The async signal handlers that are installed simply
/// yield to a stream if and when a signal is caught.
public static func create(notify on: [Int32]) -> AsyncSignalHandler {
let out = AsyncSignalHandler()
var sources = [any DispatchSourceSignal]()
for sig in on {
signal(sig, SIG_IGN)
let source = DispatchSource.makeSignalSource(signal: sig)
source.setEventHandler {
out.handler(sig)
}
source.resume()
// Retain a reference to our signal sources so that they
// do not go out of scope.
sources.append(source)
}
out.state.withLock { $0.sources = sources }
return out
}
}