-
Notifications
You must be signed in to change notification settings - Fork 149
Expand file tree
/
Copy pathMachPort.swift
More file actions
376 lines (336 loc) · 12.1 KB
/
Copy pathMachPort.swift
File metadata and controls
376 lines (336 loc) · 12.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
/*
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
@preconcurrency import Darwin.Mach
@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