Skip to content

Commit 9066f51

Browse files
committed
[WIP] Dependency traits
1 parent bc74ac0 commit 9066f51

File tree

6 files changed

+451
-13
lines changed

6 files changed

+451
-13
lines changed

Sources/Testing/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ add_library(Testing
5858
Running/Runner.Plan+Dumping.swift
5959
Running/Runner.RuntimeState.swift
6060
Running/Runner.swift
61+
Running/Serializer.swift
6162
Running/SkipInfo.swift
6263
SourceAttribution/Backtrace.swift
6364
SourceAttribution/Backtrace+Symbolication.swift
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
//
2+
// This source file is part of the Swift.org open source project
3+
//
4+
// Copyright (c) 2025 Apple Inc. and the Swift project authors
5+
// Licensed under Apache License v2.0 with Runtime Library Exception
6+
//
7+
// See https://swift.org/LICENSE.txt for license information
8+
// See https://swift.org/CONTRIBUTORS.txt for Swift project authors
9+
//
10+
11+
/// A type whose instances can run a series of work items in strict order.
12+
///
13+
/// When a work item is scheduled on an instance of this type, it runs after any
14+
/// previously-scheduled work items. If it suspends, subsequently-scheduled work
15+
/// items do not start running; they must wait until the suspended work item
16+
/// either returns or throws an error.
17+
final actor Serializer {
18+
/// The number of scheduled work items, including (possibly) the one currently
19+
/// running.
20+
private var scheduledCount = 0
21+
22+
/// Continuations for any scheduled work items that haven't started yet.
23+
private var continuations = [CheckedContinuation<Void, Never>]()
24+
25+
/// Run a work item serially after any previously-scheduled work items.
26+
///
27+
/// - Parameters:
28+
/// - workItem: A closure to run.
29+
///
30+
/// - Returns: Whatever is returned from `workItem`.
31+
///
32+
/// - Throws: Whatever is thrown by `workItem`.
33+
func run<R>(_ workItem: @Sendable @isolated(any) () async throws -> sending R) async rethrows -> R {
34+
scheduledCount += 1
35+
defer {
36+
// Resume the next scheduled closure.
37+
if !continuations.isEmpty {
38+
let continuation = continuations.removeFirst()
39+
continuation.resume()
40+
}
41+
42+
scheduledCount -= 1
43+
}
44+
45+
await withCheckedContinuation { continuation in
46+
if scheduledCount == 1 {
47+
// Nothing else was scheduled, so we can resume immediately.
48+
continuation.resume()
49+
} else {
50+
// Something was scheduled, so add the continuation to the list. When it
51+
// resumes, we can run.
52+
continuations.append(continuation)
53+
}
54+
}
55+
56+
return try await workItem()
57+
}
58+
}
59+

Sources/Testing/Support/Environment.swift

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -89,24 +89,41 @@ enum Environment {
8989
}()
9090
#endif
9191

92+
/// The address of the environment block, if available.
93+
///
94+
/// The value of this property is always `nil` on Windows and on platforms
95+
/// that do not support environment variables.
96+
static var unsafeAddress: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>? {
97+
#if SWT_NO_ENVIRONMENT_VARIABLES
98+
nil
99+
#elseif SWT_TARGET_OS_APPLE
100+
_NSGetEnviron()?.pointee
101+
#elseif os(Linux) || os(FreeBSD) || os(OpenBSD) || os(Android)
102+
swt_environ()
103+
#elseif os(WASI)
104+
__wasilibc_get_environ()
105+
#elseif os(Windows)
106+
nil
107+
#else
108+
#warning("Platform-specific implementation missing: environment variables unavailable")
109+
nil
110+
#endif
111+
}
112+
92113
/// Get all environment variables in the current process.
93114
///
94115
/// - Returns: A copy of the current process' environment dictionary.
95116
static func get() -> [String: String] {
96117
#if SWT_NO_ENVIRONMENT_VARIABLES
97118
simulatedEnvironment.rawValue
98-
#elseif SWT_TARGET_OS_APPLE
99-
#if !SWT_NO_DYNAMIC_LINKING
119+
#elseif SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(OpenBSD) || os(Android) || os(WASI)
120+
#if SWT_TARGET_OS_APPLE && !SWT_NO_DYNAMIC_LINKING
100121
_environ_lock_np?()
101122
defer {
102123
_environ_unlock_np?()
103124
}
104125
#endif
105-
return _get(fromEnviron: _NSGetEnviron()!.pointee!)
106-
#elseif os(Linux) || os(FreeBSD) || os(OpenBSD) || os(Android)
107-
_get(fromEnviron: swt_environ())
108-
#elseif os(WASI)
109-
_get(fromEnviron: __wasilibc_get_environ())
126+
return _get(fromEnviron: Self.unsafeAddress!)
110127
#elseif os(Windows)
111128
guard let environ = GetEnvironmentStringsW() else {
112129
return [:]
@@ -153,7 +170,9 @@ enum Environment {
153170
defer {
154171
_environ_unlock_np?()
155172
}
156-
let environ = _NSGetEnviron()!.pointee!
173+
guard let environ = Self.unsafeAddress else {
174+
return nil
175+
}
157176

158177
return name.withCString { name in
159178
for i in 0... {

Sources/Testing/Testing.docc/Parallelization.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ accomplished by the testing library using task groups, and tests generally all
1919
run in the same process. The number of tests that run concurrently is controlled
2020
by the Swift runtime.
2121

22+
<!-- TODO: discuss .serialized(for:) -->
23+
2224
## Disabling parallelization
2325

2426
Parallelization can be disabled on a per-function or per-suite basis using the

0 commit comments

Comments
 (0)