Skip to content

Commit bcd0f64

Browse files
committed
[Distributed] workaround for LocalTestingDAS crashes;
This happens to work, but is not a real fix; we are not handling well types declared in a library evolution enabled library, which this DAS is. Without this, at runtime, we crash in LocalTestingDAS using actor initializers
1 parent 223d73c commit bcd0f64

File tree

6 files changed

+802
-753
lines changed

6 files changed

+802
-753
lines changed

lib/AST/DistributedDecl.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -472,7 +472,6 @@ bool AbstractFunctionDecl::isDistributedActorSystemRemoteCall(bool isVoidReturn)
472472
if (!invocationParam->isInOut()) {
473473
return false;
474474
}
475-
476475
// --- Check parameter: throwing: Err.Type
477476
auto thrownTypeParam = params->get(3);
478477
if (thrownTypeParam->getArgumentName() != C.Id_throwing) {

stdlib/public/Distributed/DistributedActorSystem.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,12 @@
1212
import Swift
1313
import _Concurrency
1414

15-
/// A distributed actor system
15+
/// A distributed actor system is what enables all functionality of distributed actors.
16+
///
17+
/// Distributed actors must always be associated with a concrete distributed actor system,
18+
/// and do so by declaring a `typealias ActorSystem = ...`, or by having a module wide
19+
/// `typealias DefaultDistributedActorSystem` declared which then applies to all distributed
20+
/// actors that do not declare a specific type alias in their bodies.
1621
@available(SwiftStdlib 5.7, *)
1722
public protocol DistributedActorSystem: Sendable {
1823
/// The identity used by actors that communicate via this transport

stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift

Lines changed: 57 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
import Swift
14+
import _Concurrency
1415

1516
#if canImport(Darwin)
1617
import Darwin
@@ -26,25 +27,33 @@ import WinSDK
2627
/// for learning about `distributed actor` isolation, as well as early
2728
/// prototyping stages of development where a real system is not necessary yet.
2829
@available(SwiftStdlib 5.7, *)
29-
public final class LocalTestingDistributedActorSystem: DistributedActorSystem, @unchecked Sendable {
30+
public struct LocalTestingDistributedActorSystem: DistributedActorSystem, @unchecked Sendable {
3031
public typealias ActorID = LocalTestingActorAddress
3132
public typealias ResultHandler = LocalTestingInvocationResultHandler
3233
public typealias InvocationEncoder = LocalTestingInvocationEncoder
3334
public typealias InvocationDecoder = LocalTestingInvocationDecoder
34-
public typealias SerializationRequirement = Codable
35+
public typealias SerializationRequirement = any Codable
36+
3537

36-
private var activeActors: [ActorID: any DistributedActor] = [:]
37-
private let activeActorsLock = _Lock()
38+
@usableFromInline
39+
final class _Storage {
3840

39-
private var idProvider: ActorIDProvider = ActorIDProvider()
40-
private var assignedIDs: Set<ActorID> = []
41-
private let assignedIDsLock = _Lock()
41+
var activeActors: [ActorID: any DistributedActor] = [:]
42+
let activeActorsLock = _Lock()
4243

43-
public init() {}
44+
var assignedIDs: Set<ActorID> = []
45+
let assignedIDsLock = _Lock()
46+
}
47+
let storage: _Storage
48+
var idProvider: ActorIDProvider = ActorIDProvider()
49+
50+
public init() {
51+
storage = .init()
52+
}
4453

4554
public func resolve<Act>(id: ActorID, as actorType: Act.Type)
46-
throws -> Act? where Act: DistributedActor {
47-
guard let anyActor = self.activeActorsLock.withLock({ self.activeActors[id] }) else {
55+
throws -> Act? where Act: DistributedActor, Act.ID == ActorID {
56+
guard let anyActor = storage.activeActorsLock.withLock({ self.storage.activeActors[id] }) else {
4857
throw LocalTestingDistributedActorSystemError(message: "Unable to locate id '\(id)' locally")
4958
}
5059
guard let actor = anyActor as? Act else {
@@ -54,28 +63,25 @@ public final class LocalTestingDistributedActorSystem: DistributedActorSystem, @
5463
}
5564

5665
public func assignID<Act>(_ actorType: Act.Type) -> ActorID
57-
where Act: DistributedActor {
66+
where Act: DistributedActor, Act.ID == ActorID {
5867
let id = self.idProvider.next()
59-
self.assignedIDsLock.withLock {
60-
self.assignedIDs.insert(id)
68+
storage.assignedIDsLock.withLock {
69+
self.storage.assignedIDs.insert(id)
6170
}
6271
return id
6372
}
6473

6574
public func actorReady<Act>(_ actor: Act)
66-
where Act: DistributedActor,
67-
Act.ID == ActorID {
68-
guard self.assignedIDsLock.withLock({ self.assignedIDs.contains(actor.id) }) else {
75+
where Act: DistributedActor, Act.ID == ActorID {
76+
guard storage.assignedIDsLock.withLock({ self.storage.assignedIDs.contains(actor.id) }) else {
6977
fatalError("Attempted to mark an unknown actor '\(actor.id)' ready")
7078
}
71-
self.activeActorsLock.withLock {
72-
self.activeActors[actor.id] = actor
73-
}
79+
self.storage.activeActors[actor.id] = actor
7480
}
7581

7682
public func resignID(_ id: ActorID) {
77-
self.activeActorsLock.withLock {
78-
self.activeActors.removeValue(forKey: id)
83+
storage.activeActorsLock.withLock {
84+
self.storage.activeActors.removeValue(forKey: id)
7985
}
8086
}
8187

@@ -93,7 +99,7 @@ public final class LocalTestingDistributedActorSystem: DistributedActorSystem, @
9399
where Act: DistributedActor,
94100
Act.ID == ActorID,
95101
Err: Error,
96-
Res: SerializationRequirement {
102+
Res: Codable {
97103
fatalError("Attempted to make remote call to \(target) on actor \(actor) using a local-only actor system")
98104
}
99105

@@ -109,38 +115,40 @@ public final class LocalTestingDistributedActorSystem: DistributedActorSystem, @
109115
fatalError("Attempted to make remote call to \(target) on actor \(actor) using a local-only actor system")
110116
}
111117

112-
private struct ActorIDProvider {
118+
@usableFromInline
119+
final class ActorIDProvider {
113120
private var counter: Int = 0
114121
private let counterLock = _Lock()
115122

123+
@usableFromInline
116124
init() {}
117125

118-
mutating func next() -> LocalTestingActorAddress {
126+
func next() -> LocalTestingActorAddress {
119127
let id: Int = self.counterLock.withLock {
120128
self.counter += 1
121129
return self.counter
122130
}
123-
return LocalTestingActorAddress(parse: "\(id)")
131+
return LocalTestingActorAddress(id)
124132
}
125133
}
126134
}
127135

128136
@available(SwiftStdlib 5.7, *)
129137
public struct LocalTestingActorAddress: Hashable, Sendable, Codable {
130-
public let address: String
138+
public let uuid: String
131139

132-
public init(parse address: String) {
133-
self.address = address
140+
public init(_ uuid: Int) {
141+
self.uuid = "\(uuid)"
134142
}
135143

136144
public init(from decoder: Decoder) throws {
137145
let container = try decoder.singleValueContainer()
138-
self.address = try container.decode(String.self)
146+
self.uuid = try container.decode(String.self)
139147
}
140148

141149
public func encode(to encoder: Encoder) throws {
142150
var container = encoder.singleValueContainer()
143-
try container.encode(self.address)
151+
try container.encode(self.uuid)
144152
}
145153
}
146154

@@ -220,7 +228,7 @@ public struct LocalTestingDistributedActorSystemError: DistributedActorSystemErr
220228
// === lock ----------------------------------------------------------------
221229

222230
@available(SwiftStdlib 5.7, *)
223-
fileprivate class _Lock {
231+
internal class _Lock {
224232
#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS)
225233
private let underlying: UnsafeMutablePointer<os_unfair_lock>
226234
#elseif os(Windows)
@@ -233,10 +241,27 @@ fileprivate class _Lock {
233241
private let underlying: UnsafeMutablePointer<pthread_mutex_t>
234242
#endif
235243

244+
init() {
245+
#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS)
246+
self.underlying = UnsafeMutablePointer.allocate(capacity: 1)
247+
self.underlying.initialize(to: os_unfair_lock())
248+
#elseif os(Windows)
249+
self.underlying = UnsafeMutablePointer.allocate(capacity: 1)
250+
InitializeSRWLock(self.underlying)
251+
#elseif os(WASI)
252+
// WASI environment has only a single thread
253+
#else
254+
self.underlying = UnsafeMutablePointer.allocate(capacity: 1)
255+
guard pthread_mutex_init(self.underlying, nil) == 0 else {
256+
fatalError("pthread_mutex_init failed")
257+
}
258+
#endif
259+
}
260+
236261
deinit {
237262
#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS)
238263
// `os_unfair_lock`s do not need to be explicitly destroyed
239-
#elseif os(Windows)
264+
#elseif os(Windows)
240265
// `SRWLOCK`s do not need to be explicitly destroyed
241266
#elseif os(WASI)
242267
// WASI environment has only a single thread
@@ -252,22 +277,6 @@ fileprivate class _Lock {
252277
#endif
253278
}
254279

255-
init() {
256-
#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS)
257-
self.underlying = UnsafeMutablePointer.allocate(capacity: 1)
258-
#elseif os(Windows)
259-
self.underlying = UnsafeMutablePointer.allocate(capacity: 1)
260-
InitializeSRWLock(self.underlying)
261-
#elseif os(WASI)
262-
// WASI environment has only a single thread
263-
#else
264-
self.underlying = UnsafeMutablePointer.allocate(capacity: 1)
265-
guard pthread_mutex_init(self.underlying, nil) == 0 else {
266-
fatalError("pthread_mutex_init failed")
267-
}
268-
#endif
269-
}
270-
271280
@discardableResult
272281
func withLock<T>(_ body: () -> T) -> T {
273282
#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS)

test/Distributed/Runtime/distributed_actor_init_local.swift

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,26 @@ distributed actor MaybeAfterAssign {
9090
}
9191
}
9292

93+
distributed actor LocalTestingSystemDA {
94+
typealias ActorSystem = LocalTestingDistributedActorSystem
95+
var x: Int
96+
init() {
97+
actorSystem = .init()
98+
x = 100
99+
}
100+
}
101+
102+
distributed actor LocalTestingDA_Int {
103+
typealias ActorSystem = LocalTestingDistributedActorSystem
104+
var int: Int
105+
init() {
106+
actorSystem = .init()
107+
int = 12
108+
// CRASH
109+
}
110+
}
111+
112+
93113
// ==== Fake Transport ---------------------------------------------------------
94114

95115
struct ActorAddress: Sendable, Hashable, Codable {
@@ -262,6 +282,10 @@ func test() async {
262282
// CHECK: assign type:MaybeAfterAssign, id:ActorAddress(address: "[[ID10:.*]]")
263283
// CHECK-NEXT: ready actor:main.MaybeAfterAssign, id:ActorAddress(address: "[[ID10]]")
264284

285+
let localDA = LocalTestingDA_Int()
286+
print("localDA = \(localDA.id)")
287+
// CHECK: localDA = LocalTestingActorAddress(uuid: "1")
288+
265289
// the following tests fail to initialize the actor's identity.
266290
print("-- start of no-assign tests --")
267291
test.append(MaybeSystem(nil))
@@ -271,7 +295,6 @@ func test() async {
271295
// CHECK-NOT: assign
272296
// CHECK: -- end of no-assign tests --
273297

274-
275298
// resigns that come out of the deinits:
276299

277300
// CHECK-DAG: resign id:ActorAddress(address: "[[ID1]]")

test/Distributed/distributed_actor_layout.swift

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ import FakeDistributedActorSystems
1212
@available(SwiftStdlib 5.7, *)
1313
typealias DefaultDistributedActorSystem = FakeActorSystem
1414

15-
class MyClass { }
16-
1715
// Ensure that the actor layout is (metadata pointer, default actor, id, system,
1816
// <user fields>)
1917

@@ -23,12 +21,27 @@ protocol HasActorSystem {
2321

2422
extension MyActor: HasActorSystem { }
2523

26-
// CHECK: %T27distributed_actor_accessors7MyActorC = type <{ %swift.refcounted, %swift.defaultactor, %T27FakeDistributedActorSystems0C7AddressV, %T27FakeDistributedActorSystems0aC6SystemV, %T27distributed_actor_accessors7MyClassC* }>
24+
// CHECK: %T27distributed_actor_accessors7MyActorC = type <{ %swift.refcounted, %swift.defaultactor, %T27FakeDistributedActorSystems0C7AddressV, %T27FakeDistributedActorSystems0aC6SystemV, %TSS }>
2725
@available(SwiftStdlib 5.7, *)
2826
public distributed actor MyActor {
29-
var field: MyClass = MyClass()
27+
var field: String = ""
3028

3129
init(actorSystem: FakeActorSystem) {
3230
self.actorSystem = actorSystem
3331
}
3432
}
33+
34+
// CHECK: %T27distributed_actor_accessors10MyActorIntC = type <{ %swift.refcounted, %swift.defaultactor }>
35+
//
36+
// This does not have the concrete fields in the IR type because the LocalTestingDistributedActorSystem
37+
// is declared in Distributed, which means that it is compiled with library evolution turned on,
38+
// which causes the type to be laid out at runtime.
39+
@available(SwiftStdlib 5.7, *)
40+
public distributed actor MyActorInt {
41+
public typealias ActorSystem = LocalTestingDistributedActorSystem
42+
var field: String = ""
43+
44+
init(actorSystem: LocalTestingDistributedActorSystem) {
45+
self.actorSystem = actorSystem
46+
}
47+
}

0 commit comments

Comments
 (0)