Skip to content

Commit 3f371fd

Browse files
authored
Merge pull request #76595 from ktoso/wip-release-dist-execute-return
[Distributed] remote calls over-retain returned values
2 parents 4a51cfa + a12420a commit 3f371fd

File tree

3 files changed

+130
-5
lines changed

3 files changed

+130
-5
lines changed

stdlib/public/Distributed/DistributedActorSystem.swift

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -531,8 +531,8 @@ extension DistributedActorSystem {
531531
}
532532

533533
// Decode the return type
534-
func allocateReturnTypeBuffer<R>(_: R.Type) -> UnsafeRawPointer? {
535-
return UnsafeRawPointer(UnsafeMutablePointer<R>.allocate(capacity: 1))
534+
func allocateReturnTypeBuffer<R>(_: R.Type) -> UnsafeMutableRawPointer? {
535+
return UnsafeMutableRawPointer(UnsafeMutablePointer<R>.allocate(capacity: 1))
536536
}
537537

538538
let maybeReturnTypeFromTypeInfo =
@@ -555,12 +555,14 @@ extension DistributedActorSystem {
555555
errorCode: .typeDeserializationFailure)
556556
}
557557

558-
func destroyReturnTypeBuffer<R>(_: R.Type) {
559-
resultBuffer.assumingMemoryBound(to: R.self).deallocate()
558+
func doDestroyReturnTypeBuffer<R>(_: R.Type) {
559+
let buf = resultBuffer.assumingMemoryBound(to: R.self)
560+
buf.deinitialize(count: 1)
561+
buf.deallocate()
560562
}
561563

562564
defer {
563-
_openExistential(returnTypeFromTypeInfo, do: destroyReturnTypeBuffer)
565+
_openExistential(returnTypeFromTypeInfo, do: doDestroyReturnTypeBuffer)
564566
}
565567

566568
do {

test/Distributed/Inputs/FakeDistributedActorSystems.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,8 @@ public final class FakeRoundtripActorSystem: DistributedActorSystem, @unchecked
324324
handler: resultHandler
325325
)
326326

327+
defer { remoteCallResult = nil }
328+
defer { remoteCallError = nil }
327329
switch (remoteCallResult, remoteCallError) {
328330
case (.some(let value), nil):
329331
print(" << remoteCall return: \(value)")
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend-emit-module -emit-module-path %t/FakeCodableForDistributedTests.swiftmodule -module-name FakeCodableForDistributedTests -disable-availability-checking %S/../Inputs/FakeCodableForDistributedTests.swift
3+
// RUN: %target-swift-frontend-emit-module -emit-module-path %t/FakeDistributedActorSystems.swiftmodule -module-name FakeDistributedActorSystems -disable-availability-checking %S/../Inputs/FakeDistributedActorSystems.swift
4+
// RUN: %target-build-swift -module-name main -enable-experimental-feature Extern -Xfrontend -enable-experimental-distributed -Xfrontend -disable-availability-checking -j2 -parse-as-library -I %t %s %S/../Inputs/FakeCodableForDistributedTests.swift %S/../Inputs/FakeDistributedActorSystems.swift -o %t/a.out
5+
// RUN: %target-codesign %t/a.out
6+
// RUN: %target-run %t/a.out | %FileCheck %s --dump-input=always
7+
8+
// REQUIRES: executable_test
9+
// REQUIRES: concurrency
10+
// REQUIRES: distributed
11+
// REQUIRES: objc_interop
12+
13+
// rdar://76038845
14+
// UNSUPPORTED: use_os_stdlib
15+
// UNSUPPORTED: back_deployment_runtime
16+
17+
import Foundation
18+
import Distributed
19+
import FakeDistributedActorSystems
20+
import FakeCodableForDistributedTests
21+
22+
typealias DefaultDistributedActorSystem = FakeRoundtripActorSystem
23+
24+
@objc
25+
class SentinelNSError:
26+
NSObject,
27+
// NSError,
28+
Codable, @unchecked Sendable {
29+
let str: String
30+
31+
init(_ str: String) {
32+
self.str = str
33+
super.init()
34+
print("\(str).init: \(Unmanaged.passUnretained(self).toOpaque())")
35+
}
36+
37+
required init(from: any Decoder) { fatalError("Not implemented") }
38+
func encode (to: any Decoder) { fatalError("Not implemented") }
39+
40+
deinit {
41+
print("\(str).deinit: \(Unmanaged.passUnretained(self).toOpaque())")
42+
}
43+
}
44+
45+
class Sentinel: Codable {
46+
let str: String
47+
48+
init(_ str: String) {
49+
self.str = str
50+
print("\(str).init: \(Unmanaged.passUnretained(self).toOpaque())")
51+
}
52+
53+
required init(coder: NSCoder) { fatalError() }
54+
required init(from: any Decoder) { fatalError("Not implemented") }
55+
func encode (to: any Decoder) { fatalError("Not implemented") }
56+
57+
deinit {
58+
print("\(str).deinit: \(Unmanaged.passUnretained(self).toOpaque())")
59+
}
60+
}
61+
62+
distributed actor TestActor {
63+
distributed func returnNSSentinel() -> SentinelNSError? {
64+
return .init("returnNSSentinel")
65+
}
66+
67+
distributed func returnSentinel() -> Sentinel? {
68+
return .init("returnSentinel")
69+
}
70+
}
71+
72+
@main
73+
struct Main {
74+
75+
static func main() async throws {
76+
let system = DefaultDistributedActorSystem()
77+
78+
let instance = TestActor(actorSystem: system)
79+
let resolved = try TestActor.resolve(id: instance.id, using: system)
80+
81+
// CHECK: returnSentinel.init: [[P0:0x[0-9]+]]
82+
var s0 = try await instance.returnSentinel()
83+
// CHECK: s0 retain count = 2
84+
print("s0 retain count = \(_getRetainCount(s0!))")
85+
// CHECK: returnSentinel.deinit: [[P0]]
86+
s0 = nil
87+
88+
// CHECK: returnSentinel.init: [[P1:0x[0-9]+]]
89+
var s1 = try await resolved.returnSentinel()
90+
// CHECK: s1 retain count = 2
91+
print("s1 retain count = \(_getRetainCount(s1!))")
92+
// CHECK: returnSentinel.deinit: [[P1]]
93+
s1 = nil
94+
95+
// CHECK: returnNSSentinel.init: [[NS1:0x[0-9]+]]
96+
var ns1 = try await resolved.returnNSSentinel()
97+
// CHECK: returnNSSentinel.deinit: [[NS1]]
98+
ns1 = nil
99+
100+
print("DONE.")
101+
}
102+
}
103+
104+
@_alwaysEmitIntoClient
105+
public func _getRetainCount(_ object: AnyObject) -> UInt {
106+
let count = _withHeapObject(of: object) { _swift_retainCount($0) }
107+
return UInt(bitPattern: count)
108+
}
109+
110+
@_extern(c, "swift_retainCount") @usableFromInline
111+
internal func _swift_retainCount(_: UnsafeMutableRawPointer) -> Int
112+
113+
@_alwaysEmitIntoClient @_transparent
114+
internal func _withHeapObject<R>(
115+
of object: AnyObject,
116+
_ body: (UnsafeMutableRawPointer) -> R
117+
) -> R {
118+
defer { _fixLifetime(object) }
119+
let unmanaged = Unmanaged.passUnretained(object)
120+
return body(unmanaged.toOpaque())
121+
}

0 commit comments

Comments
 (0)