/* This source file is part of the Swift System open source project Copyright (c) 2022 - 2025 Apple Inc. and the Swift System project authors Licensed under Apache License v2.0 with Runtime Library Exception See https://swift.org/LICENSE.txt for license information */ #if SYSTEM_PACKAGE_DARWIN #if canImport(Darwin, _version: 310) import Darwin.Mach #else @preconcurrency import Darwin.Mach #endif @available(System 1.4.0, *) public protocol MachPortRight {} @available(System 1.4.0, *) @inlinable internal func _machPrecondition( file: StaticString = #file, line: UInt = #line, _ body: @autoclosure () -> kern_return_t ) { let kr = body() let expected = KERN_SUCCESS precondition(kr == expected, file: file, line: line) } @available(System 1.4.0, *) @frozen public enum Mach { @available(System 1.4.0, *) public struct Port: ~Copyable { @usableFromInline internal var _name: mach_port_name_t @usableFromInline internal var _context: mach_port_context_t /// Transfer ownership of an existing unmanaged Mach port right into a /// `Mach.Port` by name. /// /// This initializer traps if `name` is `MACH_PORT_NULL`, or if `name` is /// `MACH_PORT_DEAD` and the `RightType` is `Mach.ReceiveRight`. /// /// If the type of the right does not match the `RightType` of the /// `Mach.Port` being constructed, behavior is undefined. /// /// The underlying port right will be automatically deallocated at the /// end of the `Mach.Port` instance's lifetime. /// /// This initializer makes a syscall to guard the right. public init(name: mach_port_name_t) { precondition(name != mach_port_name_t(MACH_PORT_NULL), "Mach.Port cannot be initialized with MACH_PORT_NULL") self._name = name if RightType.self == ReceiveRight.self { precondition( _name != (0xFFFFFFFF as mach_port_name_t) /* MACH_PORT_DEAD */, "Receive rights cannot be dead names" ) let secret = mach_port_context_t(arc4random()) _machPrecondition(mach_port_guard(mach_task_self_, name, secret, 0)) self._context = secret } else { self._context = 0 } } /// Borrow access to the port name in a block that can perform /// non-consuming operations. /// /// Take care when using this function; many operations consume rights, /// and send-once rights are easily consumed. /// /// If the right is consumed, behavior is undefined. /// /// The body block may optionally return something, which will then be /// returned to the caller of withBorrowedName. @inlinable public func withBorrowedName( body: (mach_port_name_t) -> ReturnType ) -> ReturnType { return body(_name) } deinit { if RightType.self == ReceiveRight.self { precondition( _name != (0xFFFFFFFF as mach_port_name_t) /* MACH_PORT_DEAD */, "Receive rights cannot be dead names" ) _machPrecondition( mach_port_destruct(mach_task_self_, _name, 0, _context) ) } else { assert( RightType.self == SendRight.self || RightType.self == SendOnceRight.self ) _machPrecondition(mach_port_deallocate(mach_task_self_, _name)) } } } /// Possible errors that can be thrown by Mach.Port operations. public enum PortRightError : Error { /// Returned when an operation cannot be completed, because the Mach /// port right has become a dead name. This is caused by deallocation of the /// receive right on the other end. case deadName } /// The MachPortRight type used to manage a receive right. @frozen public struct ReceiveRight: MachPortRight {} /// The MachPortRight type used to manage a send right. @frozen public struct SendRight: MachPortRight {} /// The MachPortRight type used to manage a send-once right. /// /// Send-once rights are the most restrictive type of Mach port rights. /// They cannot create other rights, and are consumed upon use. /// /// Upon destruction a send-once notification will be sent to the /// receiving end. @frozen public struct SendOnceRight: MachPortRight {} } @available(System 1.4.0, *) extension Mach.Port where RightType == Mach.ReceiveRight { /// Transfer ownership of an existing, unmanaged, but already guarded, /// Mach port right into a Mach.Port by name. /// /// This initializer aborts if name is MACH_PORT_NULL. /// /// If the type of the right does not match the type T of Mach.Port /// being constructed, the behavior is undefined. /// /// The underlying port right will be automatically deallocated when /// the Mach.Port object is destroyed. @available(System 1.4.0, *) public init(name: mach_port_name_t, context: mach_port_context_t) { precondition(name != mach_port_name_t(MACH_PORT_NULL), "Mach.Port cannot be initialized with MACH_PORT_NULL") self._name = name self._context = context } /// Allocate a new Mach port with a receive right, creating a /// Mach.Port to manage it. /// /// This initializer will abort if the right could not be created. /// Callers may assert that a valid right is always returned. @inlinable @available(System 1.4.0, *) public init() { var storage: mach_port_name_t = mach_port_name_t(MACH_PORT_NULL) _machPrecondition( mach_port_allocate(mach_task_self_, MACH_PORT_RIGHT_RECEIVE, &storage) ) // name-only init will guard ReceiveRights self.init(name: storage) } /// Transfer ownership of the underlying port right to the caller. /// /// Returns a tuple containing the Mach port name representing the right, /// and the context value used to guard the right. /// /// This operation liberates the right from management by the Mach.Port, /// and the underlying right will no longer be automatically deallocated. /// /// After this function completes, the Mach.Port is destroyed and no longer /// usable. @available(System 1.4.0, *) public consuming func relinquish( ) -> (name: mach_port_name_t, context: mach_port_context_t) { let destructured = (name: _name, context: _context) discard self return destructured } /// Remove guard and transfer ownership of the underlying port right to /// the caller. /// /// Returns the Mach port name representing the right. /// /// This operation liberates the right from management by the Mach.Port, /// and the underlying right will no longer be automatically deallocated. /// /// After this function completes, the Mach.Port is destroyed and no longer /// usable. /// /// This function makes a syscall to remove the guard from /// Mach.ReceiveRights. Use relinquish() to avoid the syscall and extract /// the context value along with the port name. @inlinable @available(System 1.4.0, *) public consuming func unguardAndRelinquish() -> mach_port_name_t { let (name, context) = self.relinquish() _machPrecondition(mach_port_unguard(mach_task_self_, name, context)) return name } /// Borrow access to the port name in a block that can perform /// non-consuming operations. /// /// Take care when using this function; many operations consume rights. /// /// If the right is consumed, behavior is undefined. /// /// The body block may optionally return something, which will then be /// returned to the caller of withBorrowedName. @inlinable @available(System 1.4.0, *) public func withBorrowedName( body: (mach_port_name_t, mach_port_context_t) -> ReturnType ) -> ReturnType { return body(_name, _context) } /// Create a send-once right for a given receive right. /// /// This does not affect the makeSendCount of the receive right. /// /// This function will abort if the right could not be created. /// Callers may assert that a valid right is always returned. @inlinable @available(System 1.4.0, *) public func makeSendOnceRight() -> Mach.Port { // send once rights do not coalesce var newRight: mach_port_name_t = mach_port_name_t(MACH_PORT_NULL) var newRightType: mach_port_type_t = MACH_PORT_TYPE_NONE _machPrecondition( mach_port_extract_right( mach_task_self_, _name, mach_msg_type_name_t(MACH_MSG_TYPE_MAKE_SEND_ONCE), &newRight, &newRightType ) ) // The value of newRight is validated by the Mach.Port initializer precondition(newRightType == MACH_MSG_TYPE_MOVE_SEND_ONCE) return Mach.Port(name: newRight) } /// Create a send right for a given receive right. /// /// This increments the makeSendCount of the receive right. /// /// This function will abort if the right could not be created. /// Callers may assert that a valid right is always returned. @inlinable @available(System 1.4.0, *) public func makeSendRight() -> Mach.Port { let how = MACH_MSG_TYPE_MAKE_SEND // name is the same because send and recv rights are coalesced _machPrecondition( mach_port_insert_right( mach_task_self_, _name, _name, mach_msg_type_name_t(how) ) ) return Mach.Port(name: _name) } /// Access the make-send count. /// /// Each get/set of this property makes a syscall. @inlinable @available(System 1.4.0, *) public var makeSendCount: mach_port_mscount_t { get { var status: mach_port_status = mach_port_status() var size = mach_msg_type_number_t( MemoryLayout.size / MemoryLayout.size ) withUnsafeMutablePointer(to: &status) { [ _name = self._name ] in let status = UnsafeMutableBufferPointer(start: $0, count: 1) status.withMemoryRebound(to: integer_t.self) { let info = $0.baseAddress _machPrecondition( mach_port_get_attributes( mach_task_self_, _name, MACH_PORT_RECEIVE_STATUS, info, &size ) ) } } return status.mps_mscount } set { _machPrecondition(mach_port_set_mscount(mach_task_self_, _name, newValue)) } } } @available(System 1.4.0, *) extension Mach.Port where RightType == Mach.SendRight { /// Transfer ownership of the underlying port right to the caller. /// /// Returns the Mach port name representing the right. /// /// This operation liberates the right from management by the Mach.Port, /// and the underlying right will no longer be automatically deallocated. /// /// After this function completes, the Mach.Port is destroyed and no longer /// usable. @available(System 1.4.0, *) public consuming func relinquish() -> mach_port_name_t { let name = _name discard self return name } /// Create another send right from a given send right. /// /// This does not affect the makeSendCount of the receive right. /// /// If the send right being copied has become a dead name, meaning the /// receiving side has been deallocated, then copySendRight() will throw /// a Mach.PortRightError.deadName error. @inlinable @available(System 1.4.0, *) public func copySendRight() throws -> Mach.Port { let how = MACH_MSG_TYPE_COPY_SEND // name is the same because send rights are coalesced let kr = mach_port_insert_right( mach_task_self_, _name, _name, mach_msg_type_name_t(how) ) if kr == KERN_INVALID_NAME || kr == KERN_INVALID_CAPABILITY { throw Mach.PortRightError.deadName } _machPrecondition(kr) return Mach.Port(name: _name) } } @available(System 1.4.0, *) extension Mach.Port where RightType == Mach.SendOnceRight { /// Transfer ownership of the underlying port right to the caller. /// /// Returns the Mach port name representing the right. /// /// This operation liberates the right from management by the Mach.Port, /// and the underlying right will no longer be automatically deallocated. /// /// After this function completes, the Mach.Port is destroyed and no longer /// usable. @available(System 1.4.0, *) public consuming func relinquish() -> mach_port_name_t { let name = _name discard self return name } } #endif