Skip to content

Commit 17a8ffe

Browse files
committed
Release stream callback, once the stream has finished
1 parent ff11e2a commit 17a8ffe

File tree

1 file changed

+13
-1
lines changed

1 file changed

+13
-1
lines changed

Sources/GRPC/ClientCalls/ResponseContainers.swift

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,9 @@ internal class StreamingResponseParts<Response> {
9898
private let eventLoop: EventLoop
9999

100100
/// A callback for response messages.
101-
private let responseCallback: (Response) -> Void
101+
private var responseCallback: (Response) -> Void
102+
103+
private static let noopCallback: (Response) -> Void = { _ in }
102104

103105
/// Lazy promises for the status, initial-, and trailing-metadata.
104106
private var initialMetadataPromise: LazyEventLoopPromise<HPACKHeaders>
@@ -142,13 +144,23 @@ internal class StreamingResponseParts<Response> {
142144
self.responseCallback(response)
143145

144146
case let .end(status, trailers):
147+
// Once the stream has finished, we must release the callback, to make sure don't
148+
// break potential retain cycles (the callback may reference other object's that in
149+
// turn reference `StreamingResponseParts`).
150+
self.responseCallback = Self.noopCallback
145151
self.initialMetadataPromise.fail(status)
146152
self.trailingMetadataPromise.succeed(trailers)
147153
self.statusPromise.succeed(status)
148154
}
149155
}
150156

151157
internal func handleError(_ error: Error) {
158+
self.eventLoop.assertInEventLoop()
159+
160+
// Once the stream has finished, we must release the callback, to make sure don't
161+
// break potential retain cycles (the callback may reference other object's that in
162+
// turn reference `StreamingResponseParts`).
163+
self.responseCallback = Self.noopCallback
152164
let withoutContext = error.removingContext()
153165
let status = withoutContext.makeGRPCStatus()
154166
self.initialMetadataPromise.fail(withoutContext)

0 commit comments

Comments
 (0)