Repository navigation
Expand file tree
/
Copy pathPackage.swift
More file actions
371 lines (363 loc) · 16.6 KB
/
Copy pathPackage.swift
File metadata and controls
371 lines (363 loc) · 16.6 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
// swift-tools-version: 6.3
//
// Package.swift
// SynchronizationKit
//
// Copyright (c) 2026 Kang Jaehong
// SPDX-License-Identifier: Apache-2.0
//
import PackageDescription
var commonSwiftSettings: [PackageDescription.SwiftSetting] = [
.enableUpcomingFeature("ApproachableConcurrency"),
.strictMemorySafety(),
// Imports default to `internal`, so every module that leaks into this
// package's ABI has to say so with `public import`. Much of what this
// package declares inlines into its callers, carrying its body across the
// module boundary, so the line between an implementation detail and part of
// the interface is not where it looks — the C shim behind `Atomic` is on
// the wrong side of it. Making that explicit also makes it checkable: the
// compiler warns when a `public import` stops being reachable from
// inlinable code, and errors when an internal one starts.
//
// Member visibility follows the same principle one level down: a member is
// in scope only where its defining module is imported outright, never by
// way of something else that happens to import it.
.enableUpcomingFeature("InternalImportsByDefault"),
.enableUpcomingFeature("MemberImportVisibility"),
// `Mutex` and `Atomic` store their payload inline, with no heap allocation
// and no separate box, which is what `@_rawLayout` provides. The compiler
// back-deploys the metadata initialization for these types on its own: it
// picks `swift_initRawStructMetadata2`, `swift_initRawStructMetadata`, or a
// `swift_initStructMetadata` fallback based on the deployment target, so
// this reaches back as far as the Swift 5.0 runtime.
//
// `StaticExclusiveOnly` forbids declaring these types as `var`, which is
// what makes an inline lock or atomic safe to expose by borrow.
.enableExperimentalFeature("RawLayout"),
.enableExperimentalFeature("StaticExclusiveOnly"),
// `@_lifetime`, which ties the slot token a published reader carries from
// the read lock to the read unlock to the borrow of the lock that issued
// it. The token is `~Escapable`, so a path that would keep it past the
// read section — storing it in a property, returning it, handing it to an
// escaping closure — is a compile error rather than a pointer into a slot
// another reader has since taken.
//
// The non-underscored `@lifetime` belongs to a different flag,
// `LifetimeDependence`, which warns at every annotation that the
// underscored spelling is the one to write, and which breaks this one when
// both are on. Revisit the spelling once the attribute is not underscored.
.enableExperimentalFeature("Lifetimes"),
// `@available(anyAppleOS 26.0, *)` in place of the five-platform list.
// Swift 6.4 accepts the spelling on its own and ignores this flag; 6.3
// needs the flag. `#if os(anyAppleOS)` is a different matter: 6.3 quietly
// evaluates it to false, flag or no flag, so the `#if` conditions keep
// naming their platforms. Drop the flag once the package's minimum
// toolchain is 6.4.
.enableExperimentalFeature("AnyAppleOSAvailability"),
]
// `associatedtype State: ~Copyable`, which the asynchronous wait queue's
// protocols declare so that its state cannot be copied. Swift 6.4 accepts it
// on its own and warns that this flag is deprecated; 6.3 needs the flag, and
// knows it by this name only. Drop the block once the package's minimum
// toolchain is 6.4.
#if compiler(<6.4)
commonSwiftSettings.append(.enableExperimentalFeature("SuppressedAssociatedTypes"))
#endif
let package = Package(
name: "SynchronizationKit",
// The standard library's own Synchronization module starts here, which is
// exactly what `Atomic` and `Mutex` exist to reach below.
//
// Non-Apple platforms bundle the Swift runtime with the application instead
// of shipping it in the OS, so `Synchronization` is already available to
// them regardless of OS version, and those two targets forward to it.
// `RWLock` has no standard-library counterpart to forward to, so it is a
// real implementation on every platform, Apple or not.
platforms: [
.macOS(.v12),
.iOS(.v15),
.tvOS(.v15),
.watchOS(.v8),
.visionOS(.v1),
],
products: [
.library(
name: "SynchronizationKit",
targets: ["SynchronizationKit"]
),
],
traits: [
.trait(name: "Atomic"),
.trait(name: "Mutex"),
.trait(name: "RWLock"),
.trait(name: "Semaphore"),
.trait(name: "AsyncMutex"),
.trait(name: "AsyncRWLock"),
.trait(name: "AsyncSemaphore"),
// Aggregates, so a client can pick a whole family without naming each
// primitive: `Sync` is everything that blocks or spins a thread,
// `Async` everything that suspends a task.
.trait(name: "Sync", enabledTraits: ["Atomic", "Mutex", "RWLock", "Semaphore"]),
.trait(name: "Async", enabledTraits: ["AsyncMutex", "AsyncRWLock", "AsyncSemaphore"]),
.default(enabledTraits: ["Sync", "Async"]),
],
targets: [
.target(
name: "SynchronizationKit",
dependencies: [
.target(name: "SynchronizationKitAtomic", condition: .when(traits: ["Atomic"])),
.target(name: "SynchronizationKitMutex", condition: .when(traits: ["Mutex"])),
.target(name: "SynchronizationKitRWLock", condition: .when(traits: ["RWLock"])),
.target(name: "SynchronizationKitSemaphore", condition: .when(traits: ["Semaphore"])),
.target(name: "SynchronizationKitAsyncMutex", condition: .when(traits: ["AsyncMutex"])),
.target(name: "SynchronizationKitAsyncRWLock", condition: .when(traits: ["AsyncRWLock"])),
.target(name: "SynchronizationKitAsyncSemaphore", condition: .when(traits: ["AsyncSemaphore"])),
],
swiftSettings: commonSwiftSettings,
),
.target(
name: "CSynchronizationKitAtomic",
),
// Darwin's address-based wait and wake. They are public API that the
// SDK's `os` module map happens not to list, so Swift cannot see them
// without a shim. Only the Semaphore target needs them — RWLock waits
// through its semaphores — and `Mutex` does not: it is an unfair lock,
// whose priority donation these calls do not offer.
.target(
name: "CSynchronizationKitSemaphore",
),
// The per-thread record of held `RWLock`s that a build with
// assertions enabled keeps, to trap on a thread nesting its locking
// of one instance. Thread-local storage has to be C's: Swift has none,
// and the header says why a task local will not do.
.target(
name: "CSynchronizationKitRWLock",
),
// The ThreadSanitizer annotations for a handoff the sanitizer cannot
// see, which have to be compiled as C to know whether the sanitizer is
// in play; the header says why the runtime's own annotations are not
// enough. Two targets record such a handoff — Semaphore on its Mach
// backend, the asynchronous wait queue on every platform — and this is
// the one place the pair is defined.
.target(
name: "CSynchronizationKitCore",
),
// Internal plumbing shared by the lock targets: inline raw-layout
// storage. `package` access keeps it invisible to clients, so it needs
// no trait and never appears in the umbrella.
.target(
name: "SynchronizationKitCore",
swiftSettings: commonSwiftSettings,
),
.target(
name: "SynchronizationKitAtomic",
dependencies: ["CSynchronizationKitAtomic"],
swiftSettings: commonSwiftSettings,
),
.target(
name: "SynchronizationKitMutex",
dependencies: ["SynchronizationKitCore"],
swiftSettings: commonSwiftSettings,
),
// A Semaphore is one atomic word on Darwin, waited on by address
// through the shim, and the platform's own semaphore elsewhere. The
// sanitizer annotations are for the Mach backend, so they too are
// reached only from the Darwin side, but the target that carries them
// builds everywhere and needs no condition.
.target(
name: "SynchronizationKitSemaphore",
dependencies: [
"CSynchronizationKitCore",
"SynchronizationKitAtomic",
"SynchronizationKitCore",
.target(
name: "CSynchronizationKitSemaphore",
condition: .when(platforms: [
.macOS, .macCatalyst, .iOS, .tvOS, .watchOS, .visionOS,
]),
),
],
swiftSettings: commonSwiftSettings,
),
// An RWLock embeds a mutex for its writer-side exclusion and two
// semaphores for its handoffs, so the dependencies point at those
// targets rather than duplicating their handles.
.target(
name: "SynchronizationKitRWLock",
dependencies: [
"CSynchronizationKitRWLock",
"SynchronizationKitAtomic",
"SynchronizationKitCore",
"SynchronizationKitMutex",
"SynchronizationKitSemaphore",
.target(
name: "SynchronizationKitRWLockPrivacyManifest",
condition: .when(platforms: [
.macOS, .macCatalyst, .iOS, .tvOS, .watchOS, .visionOS,
]),
),
],
swiftSettings: commonSwiftSettings,
),
// `RWLock` reads `mach_absolute_time` on Apple platforms, which App
// Store submission requires a privacy manifest to give a reason for,
// and the manifest has to ride along as a package resource. A resource
// costs its target a generated `Bundle.module` accessor, which imports
// Foundation — a module nothing else in this package touches — so the
// manifest has a target of its own, holding nothing else, that only
// builds for Apple platforms depend on. Judged by the destination,
// not the host: a build for any other platform drops the dependency,
// and the accessor with it.
.target(
name: "SynchronizationKitRWLockPrivacyManifest",
resources: [.copy("PrivacyInfo.xcprivacy")],
),
// Internal plumbing shared by the asynchronous targets: the queue of
// waiting tasks, how a task joins it, suspends, and leaves it on
// cancellation, and how the tasks holding what the queue waits for
// are escalated to the queue's priority. The queue is bookkeeping
// under a synchronous mutex, so the dependency points at the Mutex
// target the same way RWLock's does. `package` access, like
// `SynchronizationKitCore`.
//
// A thread may wait in the queue too — that is AsyncSemaphore's
// blocking `wait()` — and blocks on a Semaphore while it does, which
// is what the Semaphore dependency is for. It is a build dependency
// only: the umbrella still exports Semaphore on its own trait.
//
// The C dependency is for the ThreadSanitizer annotations on the
// queue's handoff, on every platform: the wait queue is the same
// everywhere.
.target(
name: "SynchronizationKitAsyncCore",
dependencies: [
"CSynchronizationKitCore",
"SynchronizationKitMutex",
"SynchronizationKitSemaphore",
],
swiftSettings: commonSwiftSettings,
),
// An AsyncMutex adds a holder to the shared wait queue, and stores its
// value inline the way Mutex does.
.target(
name: "SynchronizationKitAsyncMutex",
dependencies: [
"SynchronizationKitAsyncCore",
"SynchronizationKitCore",
"SynchronizationKitMutex",
],
swiftSettings: commonSwiftSettings,
),
// An AsyncRWLock adds a writer and a set of readers to the shared wait
// queue, and stores its value inline the way RWLock does.
.target(
name: "SynchronizationKitAsyncRWLock",
dependencies: [
"SynchronizationKitAsyncCore",
"SynchronizationKitCore",
"SynchronizationKitMutex",
],
swiftSettings: commonSwiftSettings,
),
// An AsyncSemaphore adds a count to the shared wait queue, and is the
// one owner that lets a thread wait in it.
.target(
name: "SynchronizationKitAsyncSemaphore",
dependencies: [
"SynchronizationKitAsyncCore",
"SynchronizationKitMutex",
],
swiftSettings: commonSwiftSettings,
),
// What more than one suite has to agree about: which implementation is
// under test, whether a sanitizer is watching, and how to wait for a
// task to reach a queue. Neither is in a product, so neither reaches a
// client; `package` access keeps it that way.
//
// A target rather than a file, because SwiftPM will not let two suites
// share one — and the copies that restriction forced had already begun
// to drift, one of them carrying a memory-safety warning the other did
// not.
//
// The asynchronous dependencies are for the helpers that reach into the
// wait queue; Atomic is for the measurement harness's counter; Mutex
// guards the gate's waiters. The synchronous suites pay for the first
// in build time and nothing else: what they import from here is a pair
// of globals, the stress dial, and the harness.
.target(
name: "SynchronizationKitTestUtils",
dependencies: [
"SynchronizationKitAsyncCore",
"SynchronizationKitAsyncMutex",
"SynchronizationKitAsyncRWLock",
"SynchronizationKitAtomic",
"SynchronizationKitMutex",
],
swiftSettings: commonSwiftSettings,
),
.testTarget(
name: "SynchronizationKitMutexTests",
dependencies: [
"SynchronizationKitAtomic",
"SynchronizationKitMutex",
"SynchronizationKitTestUtils",
],
swiftSettings: commonSwiftSettings,
),
.testTarget(
name: "SynchronizationKitAtomicTests",
dependencies: ["SynchronizationKitAtomic", "SynchronizationKitTestUtils"],
swiftSettings: commonSwiftSettings,
),
.testTarget(
name: "SynchronizationKitSemaphoreTests",
dependencies: [
"SynchronizationKitAtomic",
"SynchronizationKitSemaphore",
"SynchronizationKitTestUtils",
],
swiftSettings: commonSwiftSettings,
),
.testTarget(
name: "SynchronizationKitRWLockTests",
dependencies: [
"SynchronizationKitAtomic",
"SynchronizationKitMutex",
"SynchronizationKitRWLock",
"SynchronizationKitSemaphore",
"SynchronizationKitTestUtils",
],
swiftSettings: commonSwiftSettings,
),
.testTarget(
name: "SynchronizationKitAsyncMutexTests",
dependencies: [
"SynchronizationKitAsyncCore",
"SynchronizationKitAsyncMutex",
"SynchronizationKitMutex",
"SynchronizationKitTestUtils",
],
swiftSettings: commonSwiftSettings,
),
.testTarget(
name: "SynchronizationKitAsyncRWLockTests",
dependencies: [
"SynchronizationKitAsyncCore",
"SynchronizationKitAsyncRWLock",
"SynchronizationKitMutex",
"SynchronizationKitTestUtils",
],
swiftSettings: commonSwiftSettings,
),
.testTarget(
name: "SynchronizationKitAsyncSemaphoreTests",
dependencies: [
"SynchronizationKitAsyncCore",
"SynchronizationKitAsyncSemaphore",
"SynchronizationKitMutex",
"SynchronizationKitTestUtils",
],
swiftSettings: commonSwiftSettings,
),
]
)