Skip to content

Commit 222946b

Browse files
authored
Merge pull request #65952 from ktoso/wip-destroy-dist-args-reverse-order
[Distributed] Ensure to swift_release during destroying decoded distributed call arguments
2 parents ad3801f + 0104f2b commit 222946b

File tree

4 files changed

+91
-8
lines changed

4 files changed

+91
-8
lines changed

lib/IRGen/GenDistributed.cpp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,15 @@ void DistributedAccessor::emitLoadOfWitnessTables(llvm::Value *witnessTables,
581581
}
582582

583583
void DistributedAccessor::emitReturn(llvm::Value *errorValue) {
584+
// Destroy loaded arguments.
585+
// This MUST be done before deallocating, as otherwise we'd try to
586+
// swift_release freed memory, which will be a no-op, however that also would
587+
// mean we never drop retain counts to 0 and miss to run deinitializers of
588+
// classes!
589+
llvm::for_each(LoadedArguments, [&](const auto &argInfo) {
590+
emitDestroyCall(IGF, argInfo.second, argInfo.first);
591+
});
592+
584593
// Deallocate all of the copied arguments. Since allocations happened
585594
// on stack they have to be deallocated in reverse order.
586595
{
@@ -590,11 +599,6 @@ void DistributedAccessor::emitReturn(llvm::Value *errorValue) {
590599
}
591600
}
592601

593-
// Destroy loaded arguments.
594-
llvm::for_each(LoadedArguments, [&](const auto &argInfo) {
595-
emitDestroyCall(IGF, argInfo.second, argInfo.first);
596-
});
597-
598602
Explosion voidResult;
599603

600604
Explosion error;

test/Distributed/Inputs/FakeDistributedActorSystems.swift

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,10 @@ public final class FakeRoundtripActorSystem: DistributedActorSystem, @unchecked
226226

227227
public init() {}
228228

229+
public func shutdown() {
230+
self.activeActors = [:]
231+
}
232+
229233
public func resolve<Act>(id: ActorID, as actorType: Act.Type)
230234
throws -> Act? where Act: DistributedActor {
231235
print("| resolve \(id) as remote // this system always resolves as remote")
@@ -394,7 +398,14 @@ public struct FakeInvocationEncoder : DistributedTargetInvocationEncoder {
394398
print(" > done recording")
395399
}
396400

397-
public func makeDecoder() -> FakeInvocationDecoder {
401+
public mutating func makeDecoder() -> FakeInvocationDecoder {
402+
defer {
403+
// reset the decoder; we don't want to keep these values retained by accident here
404+
genericSubs = []
405+
arguments = []
406+
returnType = nil
407+
errorType = nil
408+
}
398409
return .init(
399410
args: arguments,
400411
substitutions: genericSubs,
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend-emit-module -emit-module-path %t/FakeDistributedActorSystems.swiftmodule -module-name FakeDistributedActorSystems -disable-availability-checking %S/../Inputs/FakeDistributedActorSystems.swift
3+
// RUN: %target-build-swift -module-name main -O -Xfrontend -disable-availability-checking -j2 -parse-as-library -I %t %s %S/../Inputs/FakeDistributedActorSystems.swift -o %t/a.out
4+
// RUN: %target-codesign %t/a.out
5+
// RUN: %target-run %t/a.out | %FileCheck %s --color --dump-input=always
6+
7+
// REQUIRES: executable_test
8+
// REQUIRES: concurrency
9+
// REQUIRES: distributed
10+
11+
// rdar://76038845
12+
// UNSUPPORTED: use_os_stdlib
13+
// UNSUPPORTED: back_deployment_runtime
14+
15+
// FIXME(distributed): Distributed actors currently have some issues on windows, isRemote always returns false. rdar://82593574
16+
// UNSUPPORTED: OS=windows-msvc
17+
18+
import Distributed
19+
import FakeDistributedActorSystems
20+
21+
typealias DefaultDistributedActorSystem = FakeRoundtripActorSystem
22+
23+
final class SomeClass<T>: Sendable, Codable {
24+
let file: String
25+
let line: UInt
26+
init(file: String = #fileID, line: UInt = #line) {
27+
self.file = file
28+
self.line = line
29+
print("SomeClass: init @ \(file):\(line)")
30+
}
31+
deinit {
32+
print("SomeClass: deinit @ \(file):\(line)")
33+
}
34+
}
35+
36+
struct S<T> : Codable {
37+
var data: SomeClass<T>
38+
}
39+
40+
distributed actor Greeter {
41+
distributed func test1<T>(_ value: SomeClass<T>) {
42+
}
43+
distributed func test2<T>(_ value: S<T>) {}
44+
}
45+
46+
func test() async throws {
47+
let system = DefaultDistributedActorSystem()
48+
defer { system.shutdown() }
49+
50+
let local = Greeter(actorSystem: system)
51+
let ref = try Greeter.resolve(id: local.id, using: system)
52+
53+
try await ref.test1(SomeClass<Int>())
54+
// CHECK: SomeClass: init
55+
// CHECK: SomeClass: deinit
56+
57+
try await ref.test2(S(data: SomeClass<Int>()))
58+
// CHECK: SomeClass: init
59+
// CHECK: SomeClass: deinit
60+
}
61+
62+
@main struct Main {
63+
static func main() async {
64+
try! await test()
65+
}
66+
}

test/Distributed/distributed_actor_thunk_doesnt_leak_class_arguments.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,20 @@ struct S<T> : Codable {
2525

2626
distributed actor Greeter {
2727
// CHECK-LABEL: define linkonce_odr hidden swifttailcc void @"$s15no_to_arg_leaks7GreeterC5test1yyAA9SomeClassCyxGYaKlFTETF"
28-
// CHECK: [[ARG_ADDR:%.*]] = bitcast i8* {{.*}} to %T15no_to_arg_leaks9SomeClassC**
28+
// CHECK: [[ARG_ADDR:%.*]] = bitcast i8* [[PARAM:%.*]] to %T15no_to_arg_leaks9SomeClassC**
2929
// CHECK: %destroy = bitcast i8* {{.*}} to void (%swift.opaque*, %swift.type*)*
3030
// CHECK-NEXT: [[OPAQUE_ARG_ADDR:%.*]] = bitcast %T15no_to_arg_leaks9SomeClassC** [[ARG_ADDR]] to %swift.opaque*
3131
// CHECK-NEXT: call void %destroy(%swift.opaque* noalias [[OPAQUE_ARG_ADDR]], %swift.type* %arg_type)
32+
// CHECK-NEXT: call swiftcc void @swift_task_dealloc(i8* [[PARAM]])
3233
distributed func test1<T>(_: SomeClass<T>) {
3334
}
3435

3536
// CHECK-LABEL: define linkonce_odr hidden swifttailcc void @"$s15no_to_arg_leaks7GreeterC5test2yyAA1SVyxGYaKlFTETF"
36-
// CHECK: [[ARG_ADDR:%.*]] = bitcast i8* {{.*}} to %T15no_to_arg_leaks1SV*
37+
// CHECK: [[ARG_ADDR:%.*]] = bitcast i8* [[PARAM:%.*]] to %T15no_to_arg_leaks1SV*
3738
// CHECK: %destroy = bitcast i8* {{.*}} to void (%swift.opaque*, %swift.type*)*
3839
// CHECK-NEXT: [[OPAQUE_ARG_ADDR:%.*]] = bitcast %T15no_to_arg_leaks1SV* [[ARG_ADDR]] to %swift.opaque*
3940
// CHECK-NEXT: call void %destroy(%swift.opaque* noalias [[OPAQUE_ARG_ADDR]], %swift.type* %arg_type)
41+
// CHECK-NEXT: call swiftcc void @swift_task_dealloc(i8* [[PARAM]])
4042
distributed func test2<T>(_: S<T>) {}
4143
}
4244

0 commit comments

Comments
 (0)