Skip to content

Commit 480bb08

Browse files
authored
[Distributed] Naive impl of whenLocal for distributed actors (swiftlang#39654)
* [Distributed] Hacky impl of whenLocal for distributed actors * cleanup & fix one test * disable test on windows
1 parent 5fd48a0 commit 480bb08

File tree

6 files changed

+113
-3
lines changed

6 files changed

+113
-3
lines changed

include/swift/AST/KnownIdentifiers.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@ IDENTIFIER(identifier)
264264
IDENTIFIER(_distributedActorRemoteInitialize)
265265
IDENTIFIER(_distributedActorDestroy)
266266
IDENTIFIER(__isRemoteActor)
267+
IDENTIFIER(whenLocal)
267268

268269
#undef IDENTIFIER
269270
#undef IDENTIFIER_

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1432,6 +1432,11 @@ namespace {
14321432
bool isPotentiallyIsolated = false;
14331433
if (!var) {
14341434
isPotentiallyIsolated = false;
1435+
} else if (var->getName().str().equals("__secretlyKnownToBeLocal")) {
1436+
// FIXME(distributed): we did a dynamic check and know that this actor is local,
1437+
// but we can't express that to the type system; the real implementation
1438+
// will have to mark 'self' as "known to be local" after an is-local check.
1439+
isPotentiallyIsolated = true;
14351440
} else if (auto param = dyn_cast<ParamDecl>(var)) {
14361441
isPotentiallyIsolated = param->isIsolated();
14371442
} else if (var->isSelfParamCapture()) {

stdlib/public/Distributed/DistributedActor.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,28 @@ extension DistributedActor {
116116
}
117117
}
118118

119+
// ==== Local actor special handling -------------------------------------------
120+
121+
@available(SwiftStdlib 5.5, *)
122+
extension DistributedActor {
123+
124+
/// Executes the passed 'body' only when the distributed actor is local instance.
125+
///
126+
/// The `Self` passed to the the body closure is isolated, meaning that the
127+
/// closure can be used to call non-distributed functions, or even access actor
128+
/// state.
129+
///
130+
/// When the actor is remote, the closure won't be executed and this function will return nil.
131+
public nonisolated func whenLocal<T>(_ body: @Sendable (isolated Self) async throws -> T)
132+
async rethrows -> T? where T: Sendable {
133+
if __isLocalActor(self) {
134+
return try await body(self)
135+
} else {
136+
return nil
137+
}
138+
}
139+
}
140+
119141
/******************************************************************************/
120142
/***************************** Actor Identity *********************************/
121143
/******************************************************************************/

test/Distributed/Runtime/distributed_actor_deinit.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
// UNSUPPORTED: use_os_stdlib
88
// UNSUPPORTED: back_deployment_runtime
99

10-
// temporary non-support. tracked in rdar://82593574
10+
// FIXME(distributed): Distributed actors currently have some issues on windows, isRemote always returns false. rdar://82593574
1111
// UNSUPPORTED: windows
1212

1313
import _Distributed
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking -Xfrontend -enable-experimental-distributed -parse-as-library) | %FileCheck %s --dump-input=always
2+
3+
// REQUIRES: executable_test
4+
// REQUIRES: concurrency
5+
// REQUIRES: distributed
6+
7+
// rdar://76038845
8+
// UNSUPPORTED: use_os_stdlib
9+
// UNSUPPORTED: back_deployment_runtime
10+
11+
// FIXME(distributed): Distributed actors currently have some issues on windows, isRemote always returns false. rdar://82593574
12+
// UNSUPPORTED: windows
13+
14+
import _Distributed
15+
16+
distributed actor Capybara {
17+
// only the local capybara can do this!
18+
func eat() -> String {
19+
"watermelon"
20+
}
21+
}
22+
23+
24+
// ==== Fake Transport ---------------------------------------------------------
25+
@available(SwiftStdlib 5.5, *)
26+
struct ActorAddress: ActorIdentity {
27+
let address: String
28+
init(parse address: String) {
29+
self.address = address
30+
}
31+
}
32+
33+
@available(SwiftStdlib 5.5, *)
34+
struct FakeTransport: ActorTransport {
35+
func decodeIdentity(from decoder: Decoder) throws -> AnyActorIdentity {
36+
fatalError("not implemented:\(#function)")
37+
}
38+
39+
func resolve<Act>(_ identity: AnyActorIdentity, as actorType: Act.Type)
40+
throws -> Act? where Act: DistributedActor {
41+
return nil
42+
}
43+
44+
func assignIdentity<Act>(_ actorType: Act.Type) -> AnyActorIdentity
45+
where Act: DistributedActor {
46+
let id = ActorAddress(parse: "xxx")
47+
return .init(id)
48+
}
49+
50+
func actorReady<Act>(_ actor: Act) where Act: DistributedActor {
51+
}
52+
53+
func resignIdentity(_ id: AnyActorIdentity) {
54+
}
55+
}
56+
57+
func test() async throws {
58+
let transport = FakeTransport()
59+
60+
let local = Capybara(transport: transport)
61+
// await local.eat() // SHOULD ERROR
62+
let valueWhenLocal: String? = await local.whenLocal { __secretlyKnownToBeLocal in
63+
__secretlyKnownToBeLocal.eat()
64+
}
65+
66+
// CHECK: valueWhenLocal: watermelon
67+
print("valueWhenLocal: \(valueWhenLocal ?? "nil")")
68+
69+
let remote = try Capybara.resolve(local.id, using: transport)
70+
let valueWhenRemote: String? = await remote.whenLocal { __secretlyKnownToBeLocal in
71+
__secretlyKnownToBeLocal.eat()
72+
}
73+
74+
// CHECK: valueWhenRemote: nil
75+
print("valueWhenRemote: \(valueWhenRemote ?? "nil")")
76+
}
77+
78+
@available(SwiftStdlib 5.5, *)
79+
@main struct Main {
80+
static func main() async {
81+
try! await test()
82+
}
83+
}

test/Distributed/actor_protocols.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,8 @@ actor A2: DistributedActor {
5252
}
5353
}
5454

55-
class C2: DistributedActor {
55+
final class C2: DistributedActor {
5656
// expected-error@-1{{non-actor type 'C2' cannot conform to the 'Actor' protocol}}
57-
// expected-error@-2{{non-final class 'C2' cannot conform to 'Sendable'; use '@unchecked Sendable'}}
5857
nonisolated var id: AnyActorIdentity {
5958
fatalError()
6059
}

0 commit comments

Comments
 (0)