Skip to content

Commit 680698e

Browse files
authored
[Distributed] Fix _executeDistributedFunction resume and result buffer (#40814)
* [Distributed] Adjust interface of `swift_distributed_execute_target` Since this is a special function, `calleeContext` doesn't point to a direct parent but instead both parent context (uninitialized) and resume function are passed as last arguments which means that `callContext` has to act as an intermediate context in call to accessor. * [Distributed] Drop optionality from result buffer in `_executeDistributedTarget` `RawPointer?` is lowered into a two arguments since it's a struct, to make it easy let's just allocate an empty pointer for `Void` result. * [Distributed] NFC: Update _remoteCall test-case to check multiple different result types
1 parent add1bbb commit 680698e

File tree

3 files changed

+104
-22
lines changed

3 files changed

+104
-22
lines changed

stdlib/public/Concurrency/Actor.cpp

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1714,7 +1714,12 @@ findDistributedAccessor(const char *targetNameStart, size_t targetNameLength) {
17141714
/// argumentBuffer: Builtin.RawPointer,
17151715
/// resultBuffer: Builtin.RawPointer) async throws
17161716
using TargetExecutorSignature =
1717-
AsyncSignature<void(DefaultActor *, const char *, size_t, void *, void *),
1717+
AsyncSignature<void(/*on=*/DefaultActor *,
1718+
/*targetName=*/const char *, /*targetNameSize=*/size_t,
1719+
/*argumentBuffer=*/void *,
1720+
/*resultBuffer=*/void *,
1721+
/*resumeFunc=*/TaskContinuationFunction *,
1722+
/*callContext=*/AsyncContext *),
17181723
/*throws=*/true>;
17191724

17201725
SWIFT_CC(swiftasync)
@@ -1743,7 +1748,9 @@ static void ::swift_distributed_execute_target_resume(
17431748
reinterpret_cast<TargetExecutorSignature::ContinuationType *>(
17441749
parentCtx->ResumeParent);
17451750
swift_task_dealloc(context);
1746-
return resumeInParent(parentCtx, error);
1751+
// See `swift_distributed_execute_target` - `parentCtx` in this case
1752+
// is `callContext` which should be completely transparent on resume.
1753+
return resumeInParent(parentCtx->Parent, error);
17471754
}
17481755

17491756
SWIFT_CC(swiftasync)
@@ -1752,7 +1759,9 @@ void ::swift_distributed_execute_target(
17521759
DefaultActor *actor,
17531760
const char *targetNameStart, size_t targetNameLength,
17541761
void *argumentBuffer,
1755-
void *resultBuffer) {
1762+
void *resultBuffer,
1763+
TaskContinuationFunction *resumeFunc,
1764+
AsyncContext *callContext) {
17561765
auto *accessor = findDistributedAccessor(targetNameStart, targetNameLength);
17571766
if (!accessor) {
17581767
assert(false && "no distributed accessor accessor");
@@ -1770,7 +1779,17 @@ void ::swift_distributed_execute_target(
17701779
AsyncContext *calleeContext = reinterpret_cast<AsyncContext *>(
17711780
swift_task_alloc(asyncFnPtr->ExpectedContextSize));
17721781

1773-
calleeContext->Parent = callerContext;
1782+
// TODO(concurrency): Special functions like this one are currently set-up
1783+
// to pass "caller" context and resume function as extra parameters due to
1784+
// how they are declared in C. But this particular function behaves exactly
1785+
// like a regular `async throws`, which means that we need to initialize
1786+
// intermediate `callContext` using parent `callerContext`. A better fix for
1787+
// this situation would be to adjust IRGen and handle function like this
1788+
// like regular `async` functions even though they are classified as special.
1789+
callContext->Parent = callerContext;
1790+
callContext->ResumeParent = resumeFunc;
1791+
1792+
calleeContext->Parent = callContext;
17741793
calleeContext->ResumeParent = reinterpret_cast<TaskContinuationFunction *>(
17751794
swift_distributed_execute_target_resume);
17761795

stdlib/public/Distributed/DistributedActorSystem.swift

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -222,18 +222,20 @@ extension DistributedActorSystem {
222222

223223
// Decode the return type
224224
func allocateReturnTypeBuffer<R>(_: R.Type) -> UnsafeRawPointer? {
225-
if R.self == Void.self {
226-
return nil
227-
}
228225
return UnsafeRawPointer(UnsafeMutablePointer<R>.allocate(capacity: 1))
229226
}
230227
guard let returnType: Any.Type = _getReturnTypeInfo(mangledMethodName: mangledTargetName) else {
231228
throw ExecuteDistributedTargetError(
232229
message: "Failed to decode distributed target return type")
233230
}
234-
let resultBuffer = _openExistential(returnType, do: allocateReturnTypeBuffer)
231+
232+
guard let resultBuffer = _openExistential(returnType, do: allocateReturnTypeBuffer) else {
233+
throw ExecuteDistributedTargetError(
234+
message: "Failed to allocate buffer for distributed target return type")
235+
}
236+
235237
func destroyReturnTypeBuffer<R>(_: R.Type) {
236-
resultBuffer?.assumingMemoryBound(to: R.self).deallocate()
238+
resultBuffer.assumingMemoryBound(to: R.self).deallocate()
237239
}
238240
defer {
239241
_openExistential(returnType, do: destroyReturnTypeBuffer)
@@ -252,16 +254,11 @@ extension DistributedActorSystem {
252254
on: actor,
253255
mangledTargetName, UInt(mangledTargetName.count),
254256
argumentBuffer: hargs.buffer._rawValue,
255-
resultBuffer: resultBuffer?._rawValue
257+
resultBuffer: resultBuffer._rawValue
256258
)
257259

258-
// Get the result out of the buffer and invoke onReturn with the right type
259-
guard let resultBuffer = resultBuffer else {
260-
try await handler.onReturn(value: ())
261-
return
262-
}
263-
func onReturn<R>(_: R.Type) async throws {
264-
try await handler.onReturn/*<R>*/(value: resultBuffer)
260+
func onReturn<R>(_ resultTy: R.Type) async throws {
261+
try await handler.onReturn/*<R>*/(value: resultBuffer.load(as: resultTy))
265262
}
266263

267264
try await _openExistential(returnType, do: onReturn)
@@ -277,7 +274,7 @@ func _executeDistributedTarget(
277274
on actor: AnyObject, // DistributedActor
278275
_ targetName: UnsafePointer<UInt8>, _ targetNameLength: UInt,
279276
argumentBuffer: Builtin.RawPointer, // HeterogeneousBuffer of arguments
280-
resultBuffer: Builtin.RawPointer?
277+
resultBuffer: Builtin.RawPointer
281278
) async throws
282279

283280
// ==== ----------------------------------------------------------------------------------------------------------------

test/Distributed/Runtime/distributed_actor_remoteCall.swift

Lines changed: 70 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,37 @@
1818
import _Distributed
1919

2020
final class Obj: @unchecked Sendable, Codable {}
21+
2122
struct LargeStruct: Sendable, Codable {
23+
var q: String
24+
var a: Int
25+
var b: Int64
26+
var c: Double
27+
var d: String
28+
}
29+
30+
enum E : Sendable, Codable {
31+
case foo, bar
2232
}
2333

2434
distributed actor Greeter {
25-
distributed func hello() {
26-
print("EXECUTING HELLO")
35+
distributed func empty() {
36+
}
37+
38+
distributed func hello() -> String {
39+
return "Hello, World!"
40+
}
41+
42+
distributed func answer() -> Int {
43+
return 42
44+
}
45+
46+
distributed func largeResult() -> LargeStruct {
47+
.init(q: "question", a: 42, b: 1, c: 2.0, d: "Lorum ipsum")
48+
}
49+
50+
distributed func enumResult() -> E {
51+
.bar
2752
}
2853
}
2954

@@ -102,7 +127,11 @@ struct FakeResultHandler: DistributedTargetInvocationResultHandler {
102127
typealias DefaultDistributedActorSystem = FakeActorSystem
103128

104129
// actual mangled name:
105-
let helloName = "$s4main7GreeterC5helloyyFTE"
130+
let emptyName = "$s4main7GreeterC5emptyyyFTE"
131+
let helloName = "$s4main7GreeterC5helloSSyFTE"
132+
let answerName = "$s4main7GreeterC6answerSiyFTE"
133+
let largeResultName = "$s4main7GreeterC11largeResultAA11LargeStructVyFTE"
134+
let enumResult = "$s4main7GreeterC10enumResultAA1EOyFTE"
106135

107136
func test() async throws {
108137
let system = FakeActorSystem()
@@ -112,14 +141,51 @@ func test() async throws {
112141
// act as if we decoded an Invocation:
113142
let invocation = FakeInvocation()
114143

144+
try await system.executeDistributedTarget(
145+
on: local,
146+
mangledTargetName: emptyName,
147+
invocation: invocation,
148+
handler: FakeResultHandler()
149+
)
150+
151+
// CHECK: RETURN: ()
152+
115153
try await system.executeDistributedTarget(
116154
on: local,
117155
mangledTargetName: helloName,
118156
invocation: invocation,
119157
handler: FakeResultHandler()
120158
)
121159

122-
// CHECK: done
160+
// CHECK: RETURN: Hello, World!
161+
162+
try await system.executeDistributedTarget(
163+
on: local,
164+
mangledTargetName: answerName,
165+
invocation: invocation,
166+
handler: FakeResultHandler()
167+
)
168+
169+
// CHECK: RETURN: 42
170+
171+
try await system.executeDistributedTarget(
172+
on: local,
173+
mangledTargetName: largeResultName,
174+
invocation: invocation,
175+
handler: FakeResultHandler()
176+
)
177+
178+
// CHECK: RETURN: LargeStruct(q: "question", a: 42, b: 1, c: 2.0, d: "Lorum ipsum")
179+
180+
try await system.executeDistributedTarget(
181+
on: local,
182+
mangledTargetName: enumResult,
183+
invocation: invocation,
184+
handler: FakeResultHandler()
185+
)
186+
// CHECK: RETURN: bar
187+
188+
// CHECK-NEXT: done
123189
print("done")
124190
}
125191

0 commit comments

Comments
 (0)