Skip to content

fix: Thread-safe scope observer iteration and propagation context locking#7897

Merged
antonis merged 1 commit into
mainfrom
fix/thread-safe-scope-observer-iteration
May 13, 2026
Merged

fix: Thread-safe scope observer iteration and propagation context locking#7897
antonis merged 1 commit into
mainfrom
fix/thread-safe-scope-observer-iteration

Conversation

@antonis
Copy link
Copy Markdown
Contributor

@antonis antonis commented May 12, 2026

📜 Description

Fixes race conditions in SentryScope that cause EXC_BREAKPOINT crashes during concurrent scope observer enumeration and mutation.

SentryScope.m:

  1. _observersLock + observerSnapshot — Adds a dedicated lock and snapshot method returning [self.observers copy]. All 21 observer iteration sites are migrated from self.observers to [self observerSnapshot], eliminating mutation-during-enumeration on the NSMutableArray. addScopeObserver: is also protected by the same lock.
  2. _propagationContextLock — Replaces @synchronized(_propagationContext) which locked on the ivar value itself — a bug since the ivar is reassigned inside the block, allowing concurrent calls to lock on different objects. Adds explicit @synthesize + manual getter/setter with a stable lock object.

💡 Motivation and Context

Fixes getsentry/sentry-react-native#6131

A similar fix was applied with #7807 but didn't cover the observer array or propagation context.

💚 How did you test it?

  • Added testAddObserverWhileEnumeratingObservers — stress test that exercises the exact crash scenario: concurrent addScopeObserver: calls while other threads iterate observers via scope setters.
  • All existing scope tests pass, including:
    • testModifyingFromMultipleThreads
    • testModifyingFromMultipleThreads_withObserverAsyncDispatch
    • testScopeObserver_passesDistinctCopyToObservers
    • testScopeObserver_setPropagationContext_UpdatesTraceContext

📝 Checklist

  • I added tests to verify the changes.
  • No new PII added or SDK only sends newly added PII if sendDefaultPII is enabled.
  • I updated the docs if needed.
  • I updated the wizard if needed.
  • Review from the native team if needed.
  • No breaking change or entry added to the changelog.
  • No breaking change for hybrid SDKs or communicated to hybrid SDKs.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 12, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 85.563%. Comparing base (66415de) to head (4eb1b14).
✅ All tests successful. No failed tests found.

Additional details and impacted files

Impacted file tree graph

@@              Coverage Diff              @@
##              main     #7897       +/-   ##
=============================================
- Coverage   86.192%   85.563%   -0.630%     
=============================================
  Files          487       487               
  Lines        29651     29660        +9     
  Branches     12894     12901        +7     
=============================================
- Hits         25557     25378      -179     
- Misses        4046      4232      +186     
- Partials        48        50        +2     
Files with missing lines Coverage Δ
Sources/Sentry/SentryScope.m 96.960% <100.000%> (+0.075%) ⬆️

... and 16 files with indirect coverage changes


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 66415de...4eb1b14. Read the comment docs.

Comment thread Sources/Sentry/SentryScope.m Outdated
Comment thread Sources/Sentry/SentryScope.m
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit fa0890b. Configure here.

@antonis antonis force-pushed the fix/thread-safe-scope-observer-iteration branch from 38e1d2e to 960f0c0 Compare May 12, 2026 14:36
@antonis antonis marked this pull request as ready for review May 12, 2026 14:55
@itaybre itaybre added the ready-to-merge Use this label to trigger all PR workflows label May 12, 2026
@sentry
Copy link
Copy Markdown

sentry Bot commented May 12, 2026

📲 Install Builds

iOS

🔗 App Name App ID Version Configuration
SDK-Size io.sentry.sample.SDK-Size 9.13.0 (1) Release

⚙️ sentry-cocoa Build Distribution Settings

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 12, 2026

Performance metrics 🚀

  Plain With Sentry Diff
Startup time 1211.21 ms 1245.13 ms 33.91 ms
Size 24.14 KiB 1.16 MiB 1.13 MiB

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
29d546e 1224.06 ms 1257.05 ms 32.98 ms
e56c394 1229.37 ms 1245.86 ms 16.49 ms
a7c42d9 1217.25 ms 1253.98 ms 36.73 ms
787537a 1218.35 ms 1251.72 ms 33.38 ms
7814719 1220.98 ms 1254.14 ms 33.16 ms
1155f0c 1222.41 ms 1251.18 ms 28.76 ms
6b08499 1231.61 ms 1241.90 ms 10.29 ms
9b154ad 1218.94 ms 1253.60 ms 34.66 ms
bdd8e0e 1233.35 ms 1266.96 ms 33.60 ms
670aba7 1219.98 ms 1257.20 ms 37.23 ms

App size

Revision Plain With Sentry Diff
29d546e 24.14 KiB 1.15 MiB 1.13 MiB
e56c394 24.14 KiB 1.16 MiB 1.13 MiB
a7c42d9 24.14 KiB 1.15 MiB 1.13 MiB
787537a 24.14 KiB 1.15 MiB 1.12 MiB
7814719 24.14 KiB 1.15 MiB 1.13 MiB
1155f0c 24.14 KiB 1.16 MiB 1.13 MiB
6b08499 24.14 KiB 1.15 MiB 1.13 MiB
9b154ad 24.14 KiB 1.16 MiB 1.13 MiB
bdd8e0e 24.14 KiB 1.16 MiB 1.13 MiB
670aba7 24.14 KiB 1.16 MiB 1.13 MiB

Previous results on branch: fix/thread-safe-scope-observer-iteration

Startup times

Revision Plain With Sentry Diff
41847b2 1226.00 ms 1250.88 ms 24.88 ms

App size

Revision Plain With Sentry Diff
41847b2 24.14 KiB 1.16 MiB 1.13 MiB

@itaybre
Copy link
Copy Markdown
Contributor

itaybre commented May 12, 2026

@antonis looks like tests are failing with this change:

Tests/SentryTests/Integrations/Screenshot/SentryScreenshotIntegrationTests.swift:1 | /Users/admin/actions-runner/_work/sentry-cocoa/sentry-cocoa/Tests/SentryTests/Integrations/Screenshot/SentryScreenshotIntegrationTests.swift:253 - Asynchronous wait failed: Exceeded timeout of 1 seconds, with unfulfilled expectations: "Attachment Added".

@antonis antonis marked this pull request as draft May 13, 2026 09:15
…king

Protect observer array with _observersLock and snapshot copy to
prevent mutation-during-enumeration crashes. Replace broken
@synchronized(_propagationContext) with dedicated lock object.
@antonis antonis force-pushed the fix/thread-safe-scope-observer-iteration branch from 960f0c0 to 4eb1b14 Compare May 13, 2026 09:18
@antonis antonis marked this pull request as ready for review May 13, 2026 09:58
@antonis
Copy link
Copy Markdown
Contributor Author

antonis commented May 13, 2026

Thank you for adding the ready-to-merge label and flagging this @itaybre 🙇 Ready for another pass 🤞

Copy link
Copy Markdown
Contributor

@itaybre itaybre left a comment

Choose a reason for hiding this comment

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

LGTM

@antonis antonis merged commit 8180609 into main May 13, 2026
229 of 234 checks passed
@antonis antonis deleted the fix/thread-safe-scope-observer-iteration branch May 13, 2026 16:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-to-merge Use this label to trigger all PR workflows

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Swift runtime trap in dispatchAsyncOnMainQueueIfNotMainThread: with concurrent SentryFileManagerHelper write

2 participants