Skip to content

fix: Avoid keeping strong reference to self instance in delegates#1123

Merged
mykola-mokhnach merged 9 commits intomasterfrom
leak
Apr 12, 2026
Merged

fix: Avoid keeping strong reference to self instance in delegates#1123
mykola-mokhnach merged 9 commits intomasterfrom
leak

Conversation

@mykola-mokhnach
Copy link
Copy Markdown

This PR adds explicit MJPEG stop/run-state handling, breaks retention chains during shutdown (delegate/server/broadcaster cleanup), and improves load behavior by bounding MJPEG write timeouts and coalescing image-scaling work. It also updates route blocks to avoid strong self capture where applicable.

Result: cleaner teardown, lower risk of retained streaming objects, and more stable memory usage during long-running/slow-client streaming sessions.

@Dan-Maor
Copy link
Copy Markdown
Collaborator

Dan-Maor commented Apr 9, 2026

Interesting.

I'll take a look at it and run some tests on Sunday once I'm back at work.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR aims to reduce retention/teardown issues in the MJPEG screenshot streaming pipeline and web server shutdown flow by adding explicit stop handling, weakening delegate references, bounding streaming write timeouts, and coalescing image scaling work.

Changes:

  • Add stopStreaming + run-state tracking to FBMjpegServer, and enforce bounded socket write timeouts with periodic stats logging.
  • Coalesce scaling work in FBImageProcessor so repeated submissions don’t schedule redundant scaling blocks.
  • Break potential retention chains during shutdown by weakening FBTCPSocket.delegate, clearing delegates on stop, and avoiding strong self capture in route blocks.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
WebDriverAgentLib/Utilities/FBMjpegServer.m Adds streaming lifecycle flag, bounded write timeout, periodic stats, and explicit shutdown behavior.
WebDriverAgentLib/Utilities/FBMjpegServer.h Exposes stopStreaming API for orderly shutdown.
WebDriverAgentLib/Utilities/FBImageProcessor.m Coalesces/drains scaling work via a scheduled flag and a loop on the scaling queue.
WebDriverAgentLib/Routing/FBWebServer.m Ensures broadcaster shutdown on dealloc/stop and avoids strong self capture in route blocks.
WebDriverAgentLib/Routing/FBTCPSocket.m Makes delegate callbacks nil-safe and clears delegate on stop.
WebDriverAgentLib/Routing/FBTCPSocket.h Changes delegate to weak (when supported) to prevent retention.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread WebDriverAgentLib/Routing/FBWebServer.m
Comment thread WebDriverAgentLib/Utilities/FBMjpegServer.m
Comment thread WebDriverAgentLib/Utilities/FBImageProcessor.m Outdated
Comment thread WebDriverAgentLib/Utilities/FBImageProcessor.m Outdated
Comment on lines 55 to +63
self.nextImage = image;
BOOL shouldSchedule = !self.isScalingScheduled;
if (shouldSchedule) {
self.isScalingScheduled = YES;
}
[self.nextImageLock unlock];
if (!shouldSchedule) {
return;
}
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

submitImageData: now coalesces/schedules scaling work via isScalingScheduled and a draining while loop. Existing FBImageProcessorTests cover scaling correctness but don’t appear to exercise the new scheduling/coalescing behavior (e.g., multiple rapid submitImageData: calls resulting in expected number/order of completion callbacks). Adding a test for the new queueing/coalescing semantics would help prevent regressions.

Copilot uses AI. Check for mistakes.
Comment thread WebDriverAgentLib/Utilities/FBMjpegServer.m Outdated
@KazuCocoa
Copy link
Copy Markdown
Member

I haven't dug into it deeply yet, but this branch didn't generate any images for 9100 when I opened it on my browser, while the current master did.

@mykola-mokhnach
Copy link
Copy Markdown
Author

I haven't dug into it deeply yet, but this branch didn't generate any images for 9100 when I opened it on my browser, while the current master did.

yes, there was a bug related to weak referencing - should be ok now

@KazuCocoa
Copy link
Copy Markdown
Member

(I'll test out again tonight)

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread WebDriverAgentLib/Utilities/FBMjpegServer.m
Comment thread WebDriverAgentLib/Utilities/FBMjpegServer.m
Comment on lines +154 to +156
id<FBTCPSocketDelegate> delegate = self.screenshotsBroadcaster.delegate;
if ([(NSObject *)delegate respondsToSelector:@selector(stopStreaming)]) {
[(FBMjpegServer *)delegate stopStreaming];
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

delegate is typed as id<FBTCPSocketDelegate>, but this code checks respondsToSelector: via an NSObject * cast and then force-casts to FBMjpegServer * to call stopStreaming. This is brittle (and will crash if a different delegate type is ever used). Prefer calling stopStreaming on the strongly-held self.mjpegServer, or narrow the type check/cast safely before invoking the method.

Suggested change
id<FBTCPSocketDelegate> delegate = self.screenshotsBroadcaster.delegate;
if ([(NSObject *)delegate respondsToSelector:@selector(stopStreaming)]) {
[(FBMjpegServer *)delegate stopStreaming];
if (nil != self.mjpegServer) {
[self.mjpegServer stopStreaming];

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's ok

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lib/xcodebuild.ts
Comment on lines 463 to 476
const proxyTimeout = noSessionProxy.timeout;
noSessionProxy.timeout = 1000;
(noSessionProxy as any).timeout = 1000;
try {
currentStatus = (await noSessionProxy.command('/status', 'GET')) as StringRecord;
if (currentStatus && currentStatus.ios && (currentStatus.ios as any).ip) {
this.agentUrl = (currentStatus.ios as any).ip;
if (currentStatus?.ios?.ip) {
this.agentUrl = currentStatus.ios.ip as string | undefined;
}
this.log.debug(`WebDriverAgent information:`);
this.log.debug(JSON.stringify(currentStatus, null, 2));
} catch (err: any) {
throw new Error(`Unable to connect to running WebDriverAgent: ${err.message}`);
} finally {
noSessionProxy.timeout = proxyTimeout;
(noSessionProxy as any).timeout = proxyTimeout;
}
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The timeout mutation is now done via (noSessionProxy as any).timeout, which bypasses the type system and could mask a real API change (e.g., if timeout becomes a getter-only property, this assignment may throw at runtime or silently shadow the intended value). Prefer a typed approach here, such as creating a short-timeout NoSessionProxy instance just for the /status polling, or extending NoSessionProxy/JWProxy typings with a supported way to set the request timeout.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread WebDriverAgentLib/Routing/FBTCPSocket.m
Copy link
Copy Markdown
Collaborator

@Dan-Maor Dan-Maor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Went through the code and tested on the devices I have available with Xcode 16.2 and 26.3.
Looks good 👍

@mykola-mokhnach mykola-mokhnach merged commit dd15f48 into master Apr 12, 2026
46 of 47 checks passed
@mykola-mokhnach mykola-mokhnach deleted the leak branch April 12, 2026 07:57
github-actions Bot pushed a commit that referenced this pull request Apr 12, 2026
## [11.4.2](v11.4.1...v11.4.2) (2026-04-12)

### Bug Fixes

* Avoid keeping strong reference to self instance in delegates ([#1123](#1123)) ([dd15f48](dd15f48))
@github-actions
Copy link
Copy Markdown

🎉 This PR is included in version 11.4.2 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants