Skip to content

Commit f54dee1

Browse files
authored
Merge pull request #65941 from ktoso/pick-rdar-109207043
2 parents 919cf8e + 1803771 commit f54dee1

File tree

4 files changed

+147
-1
lines changed

4 files changed

+147
-1
lines changed

lib/IRGen/GenDistributed.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,9 @@ class DistributedAccessor {
134134
/// The list of all arguments that were allocated on the stack.
135135
SmallVector<StackAddress, 4> AllocatedArguments;
136136

137+
/// The list of all the arguments that were loaded.
138+
SmallVector<std::pair<Address, /*type=*/llvm::Value *>, 4> LoadedArguments;
139+
137140
public:
138141
DistributedAccessor(IRGenFunction &IGF, SILFunction *target,
139142
CanSILFunctionType accessorTy);
@@ -498,13 +501,16 @@ void DistributedAccessor::decodeArgument(unsigned argumentIdx,
498501
resultValue.getAddress(), IGM.getStorageType(paramTy));
499502

500503
cast<LoadableTypeInfo>(paramInfo).loadAsTake(IGF, eltPtr, arguments);
504+
LoadedArguments.push_back(std::make_pair(eltPtr, argumentType));
501505
break;
502506
}
503507

504508
case ParameterConvention::Direct_Owned: {
505509
// Copy the value out at +1.
506510
cast<LoadableTypeInfo>(paramInfo).loadAsCopy(IGF, resultValue.getAddress(),
507511
arguments);
512+
LoadedArguments.push_back(
513+
std::make_pair(resultValue.getAddress(), argumentType));
508514
break;
509515
}
510516
}
@@ -575,6 +581,15 @@ void DistributedAccessor::emitLoadOfWitnessTables(llvm::Value *witnessTables,
575581
}
576582

577583
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+
578593
// Deallocate all of the copied arguments. Since allocations happened
579594
// on stack they have to be deallocated in reverse order.
580595
{

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+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
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-swift-frontend -module-name no_to_arg_leaks -emit-irgen -disable-availability-checking -I %t 2>&1 %s | %IRGenFileCheck %s -check-prefix CHECK-%target-import-type
4+
5+
// UNSUPPORTED: back_deploy_concurrency
6+
// REQUIRES: concurrency
7+
// REQUIRES: distributed
8+
9+
// REQUIRES: CPU=x86_64 || CPU=arm64
10+
11+
// UNSUPPORTED: OS=windows-msvc
12+
13+
import Distributed
14+
import FakeDistributedActorSystems
15+
16+
@available(SwiftStdlib 5.5, *)
17+
typealias DefaultDistributedActorSystem = FakeActorSystem
18+
19+
final class SomeClass<T>: Sendable, Codable {
20+
init() {}
21+
}
22+
23+
struct S<T> : Codable {
24+
var data: SomeClass<T>
25+
}
26+
27+
distributed actor Greeter {
28+
// CHECK-LABEL: define linkonce_odr hidden swifttailcc void @"$s15no_to_arg_leaks7GreeterC5test1yyAA9SomeClassCyxGYaKlFTETF"
29+
// CHECK: [[ARG_ADDR:%.*]] = bitcast i8* [[PARAM:%.*]] to %T15no_to_arg_leaks9SomeClassC**
30+
// CHECK: %destroy = bitcast i8* {{.*}} to void (%swift.opaque*, %swift.type*)*
31+
// CHECK-NEXT: [[OPAQUE_ARG_ADDR:%.*]] = bitcast %T15no_to_arg_leaks9SomeClassC** [[ARG_ADDR]] to %swift.opaque*
32+
// CHECK-NEXT: call void %destroy(%swift.opaque* noalias [[OPAQUE_ARG_ADDR]], %swift.type* %arg_type)
33+
// CHECK-NEXT: call swiftcc void @swift_task_dealloc(i8* [[PARAM]])
34+
distributed func test1<T>(_: SomeClass<T>) {
35+
}
36+
37+
// CHECK-LABEL: define linkonce_odr hidden swifttailcc void @"$s15no_to_arg_leaks7GreeterC5test2yyAA1SVyxGYaKlFTETF"
38+
// CHECK: [[ARG_ADDR:%.*]] = bitcast i8* [[PARAM:%.*]] to %T15no_to_arg_leaks1SV*
39+
// CHECK: %destroy = bitcast i8* {{.*}} to void (%swift.opaque*, %swift.type*)*
40+
// CHECK-NEXT: [[OPAQUE_ARG_ADDR:%.*]] = bitcast %T15no_to_arg_leaks1SV* [[ARG_ADDR]] to %swift.opaque*
41+
// CHECK-NEXT: call void %destroy(%swift.opaque* noalias [[OPAQUE_ARG_ADDR]], %swift.type* %arg_type)
42+
// CHECK-NEXT: call swiftcc void @swift_task_dealloc(i8* [[PARAM]])
43+
distributed func test2<T>(_: S<T>) {}
44+
}
45+
46+
func test() async throws {
47+
let system = DefaultDistributedActorSystem()
48+
49+
let local = Greeter(actorSystem: system)
50+
let ref = try Greeter.resolve(id: local.id, using: system)
51+
52+
try await ref.test1(SomeClass<Int>())
53+
try await ref.test2(S(data: SomeClass<Int>()))
54+
}

0 commit comments

Comments
 (0)