| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380 |
- /*
- 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<RightType: MachPortRight>: ~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<ReturnType>(
- 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<T>
- /// 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<Mach.ReceiveRight> 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<ReturnType>(
- 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<Mach.SendOnceRight> {
- // 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<Mach.SendRight> {
- 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<mach_port_status>.size / MemoryLayout<natural_t>.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<Mach.SendRight> {
- 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
|