99//
1010//===----------------------------------------------------------------------===//
1111
12- import _CAsyncSequenceValidationSupport
12+ @ preconcurrency import _CAsyncSequenceValidationSupport
1313import AsyncAlgorithms
1414
1515@_silgen_name ( " swift_job_run " )
@@ -48,17 +48,17 @@ extension AsyncSequenceValidationDiagram {
4848 do {
4949 if let pastEnd = try await iterator. next ( ) {
5050 let failure = ExpectationFailure (
51- when: Context . clock!. now,
51+ when: Context . clock. value !. now,
5252 kind: . specificationViolationGotValueAfterIteration( pastEnd) ,
5353 specification: output)
54- Context . specificationFailures. append ( failure)
54+ Context . specificationFailures. withValue { $0 . append ( failure) }
5555 }
5656 } catch {
5757 let failure = ExpectationFailure (
58- when: Context . clock!. now,
58+ when: Context . clock. value !. now,
5959 kind: . specificationViolationGotFailureAfterIteration( error) ,
6060 specification: output)
61- Context . specificationFailures. append ( failure)
61+ Context . specificationFailures. withValue { $0 . append ( failure) }
6262 }
6363 } catch {
6464 throw error
@@ -107,7 +107,7 @@ extension AsyncSequenceValidationDiagram {
107107 }
108108 }
109109
110- private static let _executor : AnyObject = {
110+ private static let _executor : any SerialExecutor = {
111111 if #available( macOS 14 . 0 , iOS 17 . 0 , watchOS 10 . 0 , tvOS 17 . 0 , * ) {
112112 return ClockExecutor_5_9 ( )
113113 } else {
@@ -116,19 +116,19 @@ extension AsyncSequenceValidationDiagram {
116116 } ( )
117117
118118 static var unownedExecutor : UnownedSerialExecutor {
119- ( _executor as! any SerialExecutor ) . asUnownedSerialExecutor ( )
119+ _executor. asUnownedSerialExecutor ( )
120120 }
121121#endif
122122
123- static var clock : Clock ?
124-
125-
126-
127- static var driver : TaskDriver ?
123+ static let clock = LockIsolated < Clock ? > ( nil )
124+
128125
129- static var currentJob : Job ?
130126
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 ] > ( [ ] )
132132 }
133133
134134 enum ActualResult {
@@ -158,9 +158,9 @@ extension AsyncSequenceValidationDiagram {
158158 actual: [ ( Clock . Instant , Result < String ? , Error > ) ]
159159 ) -> ( ExpectationResult , [ ExpectationFailure ] ) {
160160 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+
164164 let actualTimes = actual. map { when, _ in when }
165165 let expectedTimes = expected. map { $0. when }
166166
@@ -349,14 +349,14 @@ extension AsyncSequenceValidationDiagram {
349349 }
350350
351351 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 ( ) }
354354 // 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
356356 swift_task_enqueueGlobal_hook = { job, original in
357- Context . driver? . enqueue ( job)
357+ Context . driver. value ? . enqueue ( job)
358358 }
359-
359+
360360 let runner = Task {
361361 do {
362362 try await test. test ( with: clock, activeTicks: activeTicks, output: test. output) { event in
@@ -389,15 +389,15 @@ extension AsyncSequenceValidationDiagram {
389389 }
390390
391391 runner. cancel ( )
392- Context . clock = nil
392+ Context . clock. setValue ( nil )
393393 swift_task_enqueueGlobal_hook = nil
394- }
395- Context . driver? . start ( )
394+ } )
395+ Context . driver. value ? . start ( )
396396 // This is only valid since we are doing tests here
397397 // 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+
401401 return validate (
402402 inputs: test. inputs,
403403 output: test. output,
@@ -412,3 +412,32 @@ extension AsyncSequenceValidationDiagram {
412412 try self . test ( theme: . ascii, build)
413413 }
414414}
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