Mach Port API

This looks good! I have three quick items of feedback:

Initializer naming

First, I don't really see an issue with the raw conversion initializers not getting labeled as "unchecked" or "unsafe".

These Swift wrappers need to be able smoothly interoperate with existing code that vends mach_port_name_t / mach_port_context_t etc values. Labeling the core initializers that enable this as unsafe/unchecked would not be very productive -- code that is calling these is actively trying to move away from the current -- inherently & extraordinarily unsafe -- status quo; this is not something we'd want to discourage by such labels.

Some direct precedents:

  • UnsafeBufferPointer.init(start:count:) does not validate that the address range it is given is mapped into the process's address space.
  • UnsafePointer(bitPattern:) does not verify that the given integer corresponds to a valid address.
  • FileDescriptor(rawValue:) does not verify that the given integer addresses a valid entry in the per-process descriptor table.

Granted, FileDescriptor does not attempt to model whether the file descriptor is readable or writable within the Swift type system, but the proposed Mach.Port does do this sort of thing via the MachPortRight protocol. I don't think this makes a material difference (but this is not a strongly held opinion).

Labeled Return Tuples

Unrelated API nit: to aid readability at the point of use, I think functions should return labeled tuples whenever possible; so instead of

__consuming func relinquish() -> (mach_port_name_t, mach_port_context_t)

we should have

__consuming func relinquish() -> (name: mach_port_name_t, context: mach_port_context_t)

(This would align the labels in these return tuples with the labels used in the corresponding initializer.)

Moving Mach Ports into a separate module

The primary practical reason Swift Numerics & Swift Collections have split themselves up into multiple modules is to allow dependents to have better control over their code size -- types in these packages have a tendency to be relatively large, and it seems counterproductive for a space-constrained app that needs a quick Deque to have to build & link the entire Collections package, including, say, the hash-array mapped prefix tree implementation. This practical consideration does not apply to System, even in its package incarnation: these wrappers are comparatively tiny. (Additionally, apps that are most subject to space constraints do have the option to simply import System from the SDK, rather than building it as a package.)

We also do not need to define a separate module for these types just because they are specific to a particular subset of supported platforms. There is no expectation that the APIs exported by System on one platform would be compatible with the APIs exported on any other. To a first approximation, any code that imports System is platform-specific code, and will generally need to be manually ported to support new platforms -- even if it happens to build without errors.

(I like to think of System as a loosely related set of independent packages, one for each platform, that happen to all share the same name and have the same package manager URL. Having the same name for the module everywhere simply gets rid of the awkward import Darwin/Glibc/ucrt dance; it isn't meant to be a signal that this module would specialize on providing cross-platform constructs.)

As it happens, most of the APIs that System has covered so far translate relatively well across supported platforms, because we have ~only wrapped core functionality that's mostly covered by POSIX. However, this is only because of the module's current limited functionality; this is explicitly not a requirement for potential additions. Ideally APIs that are (superficially) the same across platforms should be wrapped consistently in System, but each platform comes with a wealth of uniquely platform specific APIs, and these certainly need not be relegated into secondary modules. (At least certainly not as a general rule.)

Personally, I don't see a reason against simply putting these new APIs directly into the System module. That said, if for whatever reason domain experts would prefer if import System[Package] would not include new Mach Ports constructs, then we should by all means define a separate module for them. Note that Swift has no concept of submodules (beyond rudimentary support for importing them from Clang), so the new module would need to have a distinct top-level module name. Unlike with Numerics & Collections, the new module probably should not be imported & reexported by the main one.

6 Likes