12
12
13
13
import Swift
14
14
15
+ #if canImport(Glibc)
16
+ import Glibc
17
+ #elseif os(Windows)
18
+ import WinSDK
19
+ #endif
20
+
15
21
public struct LocalTestingActorAddress : Hashable , Sendable , Codable {
16
22
public let address : String
17
23
@@ -30,7 +36,6 @@ public struct LocalTestingActorAddress: Hashable, Sendable, Codable {
30
36
}
31
37
}
32
38
33
- // TODO(distributed): not thread safe...
34
39
@available ( SwiftStdlib 5 . 7 , * )
35
40
public final class LocalTestingDistributedActorSystem : DistributedActorSystem , @unchecked Sendable {
36
41
public typealias ActorID = LocalTestingActorAddress
@@ -39,15 +44,17 @@ public final class LocalTestingDistributedActorSystem: DistributedActorSystem, @
39
44
public typealias SerializationRequirement = Codable
40
45
41
46
private var activeActors : [ ActorID : DistributedActor ] = [ : ]
47
+ private let activeActorsLock = _Lock ( )
42
48
43
49
private var idProvider : ActorIDProvider = ActorIDProvider ( )
44
50
private var assignedIDs : Set < ActorID > = [ ]
51
+ private let assignedIDsLock = _Lock ( )
45
52
46
53
public init ( ) { }
47
54
48
55
public func resolve< Act> ( id: ActorID , as actorType: Act . Type )
49
56
throws -> Act ? where Act: DistributedActor {
50
- guard let anyActor = self . activeActors [ id] else {
57
+ guard let anyActor = self . activeActorsLock . withLock ( { self . activeActors [ id] } ) else {
51
58
throw LocalTestingDistributedActorSystemError ( message: " Unable to locate id ' \( id) ' locally " )
52
59
}
53
60
guard let actor = anyActor as? Act else {
@@ -59,24 +66,30 @@ public final class LocalTestingDistributedActorSystem: DistributedActorSystem, @
59
66
public func assignID< Act> ( _ actorType: Act . Type ) -> ActorID
60
67
where Act: DistributedActor {
61
68
let id = self . idProvider. next ( )
62
- self . assignedIDs. insert ( id)
69
+ self . assignedIDsLock. withLock {
70
+ self . assignedIDs. insert ( id)
71
+ }
63
72
return id
64
73
}
65
74
66
75
public func actorReady< Act> ( _ actor : Act )
67
76
where Act: DistributedActor ,
68
77
Act. ID == ActorID {
69
- guard self . assignedIDs. contains ( actor . id) else {
78
+ guard self . assignedIDsLock . withLock ( { self . assignedIDs. contains ( actor . id) } ) else {
70
79
fatalError ( " Attempted to mark an unknown actor ' \( actor . id) ' ready " )
71
80
}
72
- self . activeActors [ actor . id] = actor
81
+ self . activeActorsLock. withLock {
82
+ self . activeActors [ actor . id] = actor
83
+ }
73
84
}
74
85
75
86
public func resignID( _ id: ActorID ) {
76
- guard self . assignedIDs. contains ( id) else {
87
+ guard self . assignedIDsLock . withLock ( { self . assignedIDs. contains ( id) } ) else {
77
88
fatalError ( " Attempted to resign unknown id ' \( id) ' " )
78
89
}
79
- self . activeActors. removeValue ( forKey: id)
90
+ self . activeActorsLock. withLock {
91
+ self . activeActors. removeValue ( forKey: id)
92
+ }
80
93
}
81
94
82
95
public func makeInvocationEncoder( ) -> InvocationEncoder {
@@ -109,15 +122,18 @@ public final class LocalTestingDistributedActorSystem: DistributedActorSystem, @
109
122
fatalError ( " Attempted to make remote call on actor \( actor ) in a local-only actor system " )
110
123
}
111
124
112
- // TODO(distributed): not thread safe...
113
125
private struct ActorIDProvider {
114
126
private var counter : Int = 0
127
+ private let counterLock = _Lock ( )
115
128
116
129
init ( ) { }
117
130
118
131
mutating func next( ) -> LocalTestingActorAddress {
119
- self . counter += 1
120
- return LocalTestingActorAddress ( parse: " \( self . counter) " )
132
+ let id : Int = self . counterLock. withLock {
133
+ self . counter += 1
134
+ return self . counter
135
+ }
136
+ return LocalTestingActorAddress ( parse: " \( id) " )
121
137
}
122
138
}
123
139
}
@@ -176,3 +192,75 @@ public struct LocalTestingDistributedActorSystemError: DistributedActorSystemErr
176
192
self . message = message
177
193
}
178
194
}
195
+
196
+ // === lock ----------------------------------------------------------------
197
+
198
+ fileprivate class _Lock {
199
+ #if os(Windows)
200
+ private let underlying : UnsafeMutablePointer < SRWLOCK >
201
+ #elseif os(Cygwin) || os(FreeBSD) || os(OpenBSD)
202
+ private let underlying : UnsafeMutablePointer < pthread_mutex_t ? >
203
+ #elseif os(WASI)
204
+ // pthread is currently not available on WASI
205
+ #else
206
+ private let underlying : UnsafeMutablePointer < pthread_mutex_t >
207
+ #endif
208
+
209
+ deinit {
210
+ #if os(Windows)
211
+ // Mutexes do not need to be explicitly destroyed
212
+ #elseif os(WASI)
213
+ // WASI environment has only a single thread
214
+ #else
215
+ guard pthread_mutex_destroy ( self . underlying) == 0 else {
216
+ fatalError ( " pthread_mutex_destroy failed " )
217
+ }
218
+ #endif
219
+
220
+ #if !os(WASI)
221
+ self . underlying. deinitialize ( count: 1 )
222
+ self . underlying. deallocate ( )
223
+ #endif
224
+ }
225
+
226
+ init ( ) {
227
+ #if os(Windows)
228
+ self . underlying = UnsafeMutablePointer . allocate ( capacity: 1 )
229
+ InitializeSRWLock ( self . underlying)
230
+ #elseif os(WASI)
231
+ // WASI environment has only a single thread
232
+ #else
233
+ self . underlying = UnsafeMutablePointer . allocate ( capacity: 1 )
234
+ guard pthread_mutex_init ( self . underlying, nil ) == 0 else {
235
+ fatalError ( " pthread_mutex_init failed " )
236
+ }
237
+ #endif
238
+ }
239
+
240
+ @discardableResult
241
+ func withLock< T> ( _ body: ( ) -> T ) -> T {
242
+ #if os(Windows)
243
+ AcquireSRWLockExclusive ( self . underlying)
244
+ #elseif os(WASI)
245
+ // WASI environment has only a single thread
246
+ #else
247
+ guard pthread_mutex_lock ( self . underlying) == 0 else {
248
+ fatalError ( " pthread_mutex_lock failed " )
249
+ }
250
+ #endif
251
+
252
+ defer {
253
+ #if os(Windows)
254
+ ReleaseSRWLockExclusive ( self . underlying)
255
+ #elseif os(WASI)
256
+ // WASI environment has only a single thread
257
+ #else
258
+ guard pthread_mutex_unlock ( self . underlying) == 0 else {
259
+ fatalError ( " pthread_mutex_unlock failed " )
260
+ }
261
+ #endif
262
+ }
263
+
264
+ return body ( )
265
+ }
266
+ }
0 commit comments