Skip to content

Make sure RumAppStartupDetector.destroy is called on main thread#3397

Merged
aleksandr-gringauz merged 3 commits into
developfrom
aleksandr-gringauz/CalledFromWrongThreadException
Apr 27, 2026
Merged

Make sure RumAppStartupDetector.destroy is called on main thread#3397
aleksandr-gringauz merged 3 commits into
developfrom
aleksandr-gringauz/CalledFromWrongThreadException

Conversation

@aleksandr-gringauz

@aleksandr-gringauz aleksandr-gringauz commented Apr 24, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Addressing this comment #3373 (comment)

Observations:

  1. I tried to reproduce CalledFromWrongThreadException when calling removeOnDrawListener and removeOnAttachStateChangeListener on a background thread in a UI test, it doesn't crash. Nevertheless, looking at ViewRootImpl code, it seems it requires main thread for a lot of its methods. So, I would say we should be extra careful when interacting with "view related things" from background threads.
  2. RumAppStartupDetectorImpl by itself doesn't support calling destroy from non-main threads. All fields aren't volatile, there is no synchronization.

Current PR:

  1. Adds UiThread annotation to RumAppStartupDetector.destroy to establish explicit contract for this class.
  2. In RumFeature.onStop make sure RumAppStartupDetector.destroy is called from the main thread.

Motivation

What inspired you to submit this pull request?

Additional Notes

Anything else we should know when reviewing?

Review checklist (to be filled by reviewers)

  • Feature or bugfix MUST have appropriate tests (unit, integration, e2e)
  • Make sure you discussed the feature or bugfix with the maintaining team in an Issue
  • Make sure each commit and the PR mention the Issue number (cf the CONTRIBUTING doc)

@aleksandr-gringauz
aleksandr-gringauz force-pushed the aleksandr-gringauz/CalledFromWrongThreadException branch from 0b8d0d4 to 14ad681 Compare April 24, 2026 11:51
@aleksandr-gringauz
aleksandr-gringauz force-pushed the aleksandr-gringauz/CalledFromWrongThreadException branch from 14ad681 to 856a87b Compare April 24, 2026 12:08
@codecov-commenter

codecov-commenter commented Apr 24, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 77.77778% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 72.08%. Comparing base (6bc1364) to head (213407a).
⚠️ Report is 2 commits behind head on develop.

Files with missing lines Patch % Lines
...lin/com/datadog/android/rum/internal/RumFeature.kt 75.00% 0 Missing and 2 partials ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #3397      +/-   ##
===========================================
+ Coverage    72.06%   72.08%   +0.02%     
===========================================
  Files          957      958       +1     
  Lines        35333    35340       +7     
  Branches      5863     5865       +2     
===========================================
+ Hits         25461    25474      +13     
- Misses        8261     8265       +4     
+ Partials      1611     1601      -10     
Files with missing lines Coverage Δ
...com/datadog/android/internal/thread/ThreadUtils.kt 100.00% <100.00%> (ø)
...roid/rum/internal/startup/RumAppStartupDetector.kt 100.00% <ø> (ø)
...lin/com/datadog/android/rum/internal/RumFeature.kt 92.79% <75.00%> (+0.34%) ⬆️

... and 31 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@aleksandr-gringauz

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Can't wait for the next one!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@aleksandr-gringauz
aleksandr-gringauz marked this pull request as ready for review April 24, 2026 13:50
@aleksandr-gringauz
aleksandr-gringauz requested review from a team as code owners April 24, 2026 13:50

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 856a87bc7b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +364 to +367
handler.post {
@Suppress("ThreadSafety") // handler posts to the main looper
detector?.destroy()
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep startup detector teardown synchronous off-main

When onStop() is called from a worker thread, this posts destroy() and then immediately returns after nulling the field. SdkFeature.stop() treats wrappedFeature.onStop() as the teardown barrier and immediately clears the feature storage/context afterward, while RumAppStartupDetectorImpl.destroy() is the code that unregisters the application lifecycle callback and removes the first-frame view/window listeners. If Datadog.stopInstance() runs off-main while the main queue is busy, those callbacks remain active after the SDK has been stopped and can still run against a torn-down feature until this runnable is eventually processed; dispatching to main should wait for completion before onStop() returns.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I don't know... Seems like an overkill tbh.


rumAppStartupDetector?.destroy()
val detector = rumAppStartupDetector
if (Looper.myLooper() == Looper.getMainLooper()) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Maybe worth to move this logic into some extension method for further use?

@aleksandr-gringauz aleksandr-gringauz Apr 27, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done

added isMainThread function in dd-sdk-android-internal

hamorillo
hamorillo previously approved these changes Apr 27, 2026

@hamorillo hamorillo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM! 🚀

hamorillo
hamorillo previously approved these changes Apr 27, 2026
@aleksandr-gringauz
aleksandr-gringauz force-pushed the aleksandr-gringauz/CalledFromWrongThreadException branch from ca280e9 to dadf7b9 Compare April 27, 2026 09:45

Mockito.mockStatic(Looper::class.java).use { mockedLooper ->
// When
mockedLooper.`when`<Looper> { Looper.getMainLooper() }.thenReturn(mockMainLooper)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: you can use whenever to avoid adding `

@aleksandr-gringauz
aleksandr-gringauz merged commit 06e2d10 into develop Apr 27, 2026
26 checks passed
@aleksandr-gringauz
aleksandr-gringauz deleted the aleksandr-gringauz/CalledFromWrongThreadException branch April 27, 2026 12:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants