9
9
//
10
10
//===----------------------------------------------------------------------===//
11
11
12
- import _CAsyncSequenceValidationSupport
12
+ @ preconcurrency import _CAsyncSequenceValidationSupport
13
13
import AsyncAlgorithms
14
14
15
15
@_silgen_name ( " swift_job_run " )
@@ -48,17 +48,17 @@ extension AsyncSequenceValidationDiagram {
48
48
do {
49
49
if let pastEnd = try await iterator. next ( ) {
50
50
let failure = ExpectationFailure (
51
- when: Context . clock!. now,
51
+ when: Context . clock. value !. now,
52
52
kind: . specificationViolationGotValueAfterIteration( pastEnd) ,
53
53
specification: output)
54
- Context . specificationFailures. append ( failure)
54
+ Context . specificationFailures. withValue { $0 . append ( failure) }
55
55
}
56
56
} catch {
57
57
let failure = ExpectationFailure (
58
- when: Context . clock!. now,
58
+ when: Context . clock. value !. now,
59
59
kind: . specificationViolationGotFailureAfterIteration( error) ,
60
60
specification: output)
61
- Context . specificationFailures. append ( failure)
61
+ Context . specificationFailures. withValue { $0 . append ( failure) }
62
62
}
63
63
} catch {
64
64
throw error
@@ -107,7 +107,7 @@ extension AsyncSequenceValidationDiagram {
107
107
}
108
108
}
109
109
110
- private static let _executor : AnyObject = {
110
+ private static let _executor : any SerialExecutor = {
111
111
if #available( macOS 14 . 0 , iOS 17 . 0 , watchOS 10 . 0 , tvOS 17 . 0 , * ) {
112
112
return ClockExecutor_5_9 ( )
113
113
} else {
@@ -116,19 +116,19 @@ extension AsyncSequenceValidationDiagram {
116
116
} ( )
117
117
118
118
static var unownedExecutor : UnownedSerialExecutor {
119
- ( _executor as! any SerialExecutor ) . asUnownedSerialExecutor ( )
119
+ _executor. asUnownedSerialExecutor ( )
120
120
}
121
121
#endif
122
122
123
- static var clock : Clock ?
124
-
125
-
126
-
127
- static var driver : TaskDriver ?
123
+ static let clock = LockIsolated < Clock ? > ( nil )
124
+
128
125
129
- static var currentJob : Job ?
130
126
131
- static var specificationFailures = [ ExpectationFailure] ( )
127
+ static let driver = LockIsolated < TaskDriver ? > ( nil )
128
+
129
+ static let currentJob = LockIsolated < Job ? > ( nil )
130
+
131
+ static let specificationFailures = LockIsolated < [ ExpectationFailure ] > ( [ ] )
132
132
}
133
133
134
134
enum ActualResult {
@@ -158,9 +158,9 @@ extension AsyncSequenceValidationDiagram {
158
158
actual: [ ( Clock . Instant , Result < String ? , Error > ) ]
159
159
) -> ( ExpectationResult , [ ExpectationFailure ] ) {
160
160
let result = ExpectationResult ( expected: expected, actual: actual)
161
- var failures = Context . specificationFailures
162
- Context . specificationFailures. removeAll ( )
163
-
161
+ var failures = Context . specificationFailures. value
162
+ Context . specificationFailures. withValue { $0 . removeAll ( ) }
163
+
164
164
let actualTimes = actual. map { when, _ in when }
165
165
let expectedTimes = expected. map { $0. when }
166
166
@@ -349,14 +349,14 @@ extension AsyncSequenceValidationDiagram {
349
349
}
350
350
351
351
let actual = ManagedCriticalState ( [ ( Clock . Instant , Result < String ? , Error > ) ] ( ) )
352
- Context . clock = clock
353
- Context . specificationFailures. removeAll ( )
352
+ Context . clock. setValue ( clock)
353
+ Context . specificationFailures. withValue { $0 . removeAll ( ) }
354
354
// This all needs to be isolated from potential Tasks (the caller function might be async!)
355
- Context . driver = TaskDriver ( queue: diagram. queue) { driver in
355
+ Context . driver. setValue ( TaskDriver ( queue: diagram. queue) { driver in
356
356
swift_task_enqueueGlobal_hook = { job, original in
357
- Context . driver? . enqueue ( job)
357
+ Context . driver. value ? . enqueue ( job)
358
358
}
359
-
359
+
360
360
let runner = Task {
361
361
do {
362
362
try await test. test ( with: clock, activeTicks: activeTicks, output: test. output) { event in
@@ -389,15 +389,15 @@ extension AsyncSequenceValidationDiagram {
389
389
}
390
390
391
391
runner. cancel ( )
392
- Context . clock = nil
392
+ Context . clock. setValue ( nil )
393
393
swift_task_enqueueGlobal_hook = nil
394
- }
395
- Context . driver? . start ( )
394
+ } )
395
+ Context . driver. value ? . start ( )
396
396
// This is only valid since we are doing tests here
397
397
// else wise this would cause QoS inversions
398
- Context . driver? . join ( )
399
- Context . driver = nil
400
-
398
+ Context . driver. value ? . join ( )
399
+ Context . driver. setValue ( nil )
400
+
401
401
return validate (
402
402
inputs: test. inputs,
403
403
output: test. output,
@@ -412,3 +412,32 @@ extension AsyncSequenceValidationDiagram {
412
412
try self . test ( theme: . ascii, build)
413
413
}
414
414
}
415
+
416
+ final class LockIsolated < Value> : @unchecked Sendable {
417
+ private var _value : Value
418
+ private let lock = Lock . allocate ( )
419
+
420
+ init ( _ value: @autoclosure @Sendable ( ) throws -> Value ) rethrows {
421
+ self . _value = try value ( )
422
+ }
423
+
424
+ func setValue( _ newValue: @autoclosure @Sendable ( ) throws -> Value ) rethrows {
425
+ try self . lock. withLock {
426
+ self . _value = try newValue ( )
427
+ }
428
+ }
429
+
430
+ var value : Value {
431
+ self . lock. withLock {
432
+ self . _value
433
+ }
434
+ }
435
+
436
+ func withValue< T: Sendable > ( _ operation: @Sendable ( inout Value ) throws -> T ) rethrows -> T {
437
+ try self . lock. withLock {
438
+ var value = self . _value
439
+ defer { self . _value = value }
440
+ return try operation ( & value)
441
+ }
442
+ }
443
+ }
0 commit comments