Skip to content

Avoid macOS 15 DevTools attach crash and analytics quit hang#4981

Open
lawrencecchen wants to merge 2 commits into
mainfrom
issue-crash-reports-diagnostics
Open

Avoid macOS 15 DevTools attach crash and analytics quit hang#4981
lawrencecchen wants to merge 2 commits into
mainfrom
issue-crash-reports-diagnostics

Conversation

@lawrencecchen
Copy link
Copy Markdown
Contributor

@lawrencecchen lawrencecchen commented May 29, 2026


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag @codesmith with what you need. Autofix is disabled.


Note

Low Risk
Targeted workarounds for a known WebKit crash and analytics shutdown blocking; behavior changes are version-gated and limited to DevTools presentation and flush timing.

Overview
Adds a macOS version gate so Web Inspector is not side-attached on macOS 15 (WebKit platformAttach crash); those systems prefer detached DevTools and skip the private attach call when revealing tools.

PostHog flush() is now async on the analytics queue (no sync wait), with an injectable flush hook and DEBUG test factory so shutdown does not block on a busy queue.

Tests cover the attach policy (15 vs 26), DevTools show without attach when disallowed, and non-blocking flush.

Reviewed by Cursor Bugbot for commit 1ccac78. Bugbot is set up for automated code reviews on this repo. Configure here.


Summary by cubic

Prevents a crash when opening DevTools on macOS 15 by avoiding side-attached Web Inspector, and fixes a quit hang by making analytics flushing non-blocking. Adds tests to cover both behaviors.

  • Bug Fixes
    • Developer Tools: Introduced BrowserDeveloperToolsAttachmentPolicy to block attached inspector on macOS 15 and open DevTools detached instead.
    • Analytics: Made PostHogAnalytics.flush asynchronous (no sync wait on the analytics queue) and routed all flushes through an injectable implementation to avoid hangs during app termination.
    • Tests: Added unit tests for the attachment policy and for non-blocking flush behavior.

Written for commit 1ccac78. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features

    • Introduced developer tools attachment policy with OS version detection and override capabilities for testing.
  • Bug Fixes

    • Added WebKit crash workaround for affected macOS versions by preventing attached Web Inspector usage on unstable builds.
  • Refactor

    • Updated analytics flushing to use non-blocking asynchronous dispatch for improved responsiveness.
  • Tests

    • Added comprehensive test coverage for developer tools attachment policies and analytics flush behavior.

Review Change Stack

@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@vercel
Copy link
Copy Markdown

vercel Bot commented May 29, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment May 29, 2026 7:14am
cmux-staging Building Building Preview, Comment May 29, 2026 7:14am

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 29, 2026

📝 Walkthrough

Walkthrough

This PR adds OS-version-based gating for WebKit's attached Web Inspector in BrowserPanel to work around a crash on macOS 15.x, and refactors PostHogAnalytics to support testable flush behavior via dependency injection while converting the public flush API from synchronous to asynchronous dispatch.

Changes

Browser Developer Tools Attachment Policy

Layer / File(s) Summary
Attachment policy definition and integration
Sources/Panels/BrowserPanel.swift, cmuxTests/BrowserConfigTests.swift
Introduces BrowserDeveloperToolsAttachmentPolicy enum with OS-version thresholds (disallows on macOS 15.x due to WebKit crash) and DEBUG testing override, integrates policy check into prepareDeveloperToolsForRevealIfNeeded(_:) to force .detached presentation when disallowed, and validates policy logic with unit tests for specific OS versions and integration test showing DevTools visibility without private attach invocation.

PostHog Analytics Flush Dependency Injection

Layer / File(s) Summary
Injection infrastructure and initialization
Sources/PostHogAnalytics.swift
Adds injectable flushImplementation closure to PostHogAnalytics, refactors initialization with private init accepting work queue and flush closure, and introduces DEBUG-only makeForTesting factory for test configuration.
Public flush API and internal routing updates
Sources/PostHogAnalytics.swift
Converts public flush() from synchronous to asynchronous work-queue dispatch guarded by didStart, routes all flush operations through injected flushImplementation in trackActive, trackDailyActiveOnWorkQueue, and trackHourlyActiveOnWorkQueue, and removes the now-unused dispatchSyncOnWorkQueue helper.
Async non-blocking flush behavior validation
cmuxTests/GhosttyConfigTests.swift
Validates that flush() completes promptly without blocking on a busy injected analytics queue using semaphores and XCTWaiter timeout assertion.

Sequence Diagram(s)

Not applicable. The changes introduce dependency-injection infrastructure and a policy-gating mechanism without introducing multi-component sequential flows that would benefit from visualization.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • manaflow-ai/cmux#4182: Both PRs modify the same code path in BrowserPanel.prepareDeveloperToolsForRevealIfNeeded(_:) around attached Web Inspector gating; retrieved PR tightens reveal/attachment conditions while main PR adds OS-version-aware policy control.
  • manaflow-ai/cmux#4244: Main PR adds an attachment-policy gate that can force DevTools into detached state, which directly impacts the developer-tools visibility signals used by retrieved PR to defer or cancel hidden-WebView discard eligibility.

Poem

🐇 The inspector takes a version check,
Async flushes, no more sync wreck—
A rabbit hops through testing glow,
Where policies and queues now flow. 🌿


Caution

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

  • Ignore

❌ Failed checks (2 errors, 2 warnings)

Check name Status Explanation Resolution
Cmux Swift @Concurrent ❌ Error The flush() method dispatches network-heavy PostHog analytics work without @concurrent annotation when called from MainActor context (applicationWillTerminate). Add @concurrent annotation to func flush() to mark work that safely leaves the caller's actor, or alternatively use workQueue.async directly instead of dispatchAsyncOnWorkQueue to guarantee an async boundary without sync-fallback.
Cmux Architecture Rethink ❌ Error Version threshold bug (26 vs 16) disables attached inspector on all current macOS; flush() still potentially synchronous; test incompletely verifies execution. Fix threshold to 16, make flush() directly async, and add flushImplementation execution verification in test.
Description check ⚠️ Warning The description lacks the required Summary, Testing, and Checklist sections from the template, though it contains detailed technical context from automated tools. Add explicit Summary (what/why), Testing (how tested/verified), and complete Checklist sections to match the repository template structure.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (14 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the two main issues addressed: avoiding a DevTools attach crash on macOS 15 and preventing an analytics quit hang.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Cmux Swift Actor Isolation ✅ Passed No actor isolation mistakes. BrowserDeveloperToolsAttachmentPolicy uses nonisolated(unsafe) correctly; PostHogAnalytics manages thread safety via DispatchQueue without Sendable marking.
Cmux Swift Blocking Runtime ✅ Passed Production code introduces no blocking/timing patterns; PostHogAnalytics.flush() uses async dispatch. Test-only DispatchSemaphore usage is deterministic scaffolding allowed by policy.
Cmux No Hacky Sleeps ✅ Passed PR introduces no new fixed sleeps/timers in non-Swift production code. Swift files (not covered by this check) and test-only file removals are allowed.
Cmux Algorithmic Complexity ✅ Passed Code changes only use fixed-size data structures with O(1) operations. No collection scans, nested loops, or batch rescans affecting scalable collections detected.
Cmux Swift Concurrency ✅ Passed PR removes dispatchSyncOnWorkQueue, converts flush to async. Pre-existing DispatchQueue patterns unchanged. Test-only DispatchQueue.global usage is intentionally testing async boundaries, allowed.
Cmux Swift File And Package Boundaries ✅ Passed PostHogAnalytics (290 lines, single responsibility) and BrowserPanel (+34 lines) comply with file boundary rules; no mixed responsibilities or package violations found.
Cmux Swift Logging ✅ Passed No logging violations found. Production code changes add no print, NSLog, debugPrint, dump, file logging, MainActor-incompatible Logger, or secret-exposing statements.
Cmux User-Facing Error Privacy ✅ Passed No user-facing error messages, alerts, or sensitive information exposed. Changes are internal refactoring and test infrastructure improvements with developer-only comments.
Cmux Full Internationalization ✅ Passed PR contains no user-facing strings requiring localization: internal enum/methods for WebKit crash workaround, analytics implementation, and test code (exempt per rules).
Cmux Swiftui State Layout ✅ Passed No new SwiftUI state violations. New BrowserDeveloperToolsAttachmentPolicy is a utility enum. BrowserPanel incidentally touches existing @Published state only.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR adds policy enum and modifies existing developer tools logic without creating or materially changing standalone cmux-owned windows. No NSWindow/NSPanel/WindowGroup creation detected.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-crash-reports-diagnostics

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cmuxTests/GhosttyConfigTests.swift`:
- Around line 3375-3392: The test currently only asserts flush() returns quickly
but doesn't verify the queued task actually ran; update the test by adding an
expectation inside the injected flushImplementation passed to
PostHogAnalytics.makeForTesting (e.g., create an XCTestExpectation in the test
and have flushImplementation fulfill it when executed), then after calling
releaseQueuedWork.signal() wait for that expectation to ensure the enqueued
flush work ran; locate the test that constructs PostHogAnalytics via
PostHogAnalytics.makeForTesting, modify the flushImplementation closure to call
expectation.fulfill(), and add an XCTWaiter/XCTestExpectation wait after
releaseQueuedWork.signal().

In `@Sources/Panels/BrowserPanel.swift`:
- Line 43: The constant firstStableAttachedInspectorMajorVersion is set to 26
which incorrectly disables the attached inspector for all current macOS
releases; change its value to the intended macOS major-version threshold (e.g.,
15) so the attached inspector is only disabled for versions older than the macOS
15 workaround, and update both occurrences of
firstStableAttachedInspectorMajorVersion in BrowserPanel.swift to the corrected
value.

In `@Sources/PostHogAnalytics.swift`:
- Around line 105-109: The flush() method currently calls
dispatchAsyncOnWorkQueue(_:) which may execute inline if the caller is already
on workQueue, making flush synchronous for re-entrant callers; change flush() to
always schedule asynchronously by dispatching directly to workQueue (e.g., use
workQueue.async { [weak self] in ... }) so that the body (guard let self,
self.didStart else { return }; self.flushImplementation()) never runs
synchronously; keep the weak self capture and the same
guard/didStart/flushImplementation logic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8aa9b702-3309-472b-939c-6eb5fce2a3fc

📥 Commits

Reviewing files that changed from the base of the PR and between bc35d13 and 1ccac78.

📒 Files selected for processing (4)
  • Sources/Panels/BrowserPanel.swift
  • Sources/PostHogAnalytics.swift
  • cmuxTests/BrowserConfigTests.swift
  • cmuxTests/GhosttyConfigTests.swift

Comment on lines +3375 to +3392
let analytics = PostHogAnalytics.makeForTesting(
workQueue: queue,
didStart: true,
flushImplementation: {}
)
let flushReturned = expectation(description: "flush returned")
DispatchQueue.global(qos: .userInitiated).async {
analytics.flush()
flushReturned.fulfill()
}

let result = XCTWaiter().wait(for: [flushReturned], timeout: 0.2)
releaseQueuedWork.signal()
XCTAssertEqual(
result,
.completed,
"PostHogAnalytics.flush must not synchronously wait for the analytics queue while the main thread is terminating."
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Assert that the queued flush eventually runs.

This test proves flush() returns quickly, but it would also pass if flush() stopped enqueuing any work at all. Add an expectation in flushImplementation and wait for it after releaseQueuedWork.signal() so the test also covers the injected execution path.

Suggested strengthening
-        let analytics = PostHogAnalytics.makeForTesting(
+        let didExecuteFlush = expectation(description: "flush executed")
+        let analytics = PostHogAnalytics.makeForTesting(
             workQueue: queue,
             didStart: true,
-            flushImplementation: {}
+            flushImplementation: {
+                didExecuteFlush.fulfill()
+            }
         )
         let flushReturned = expectation(description: "flush returned")
         DispatchQueue.global(qos: .userInitiated).async {
             analytics.flush()
             flushReturned.fulfill()
@@
         releaseQueuedWork.signal()
         XCTAssertEqual(
             result,
             .completed,
             "PostHogAnalytics.flush must not synchronously wait for the analytics queue while the main thread is terminating."
         )
+        wait(for: [didExecuteFlush], timeout: 1.0)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmuxTests/GhosttyConfigTests.swift` around lines 3375 - 3392, The test
currently only asserts flush() returns quickly but doesn't verify the queued
task actually ran; update the test by adding an expectation inside the injected
flushImplementation passed to PostHogAnalytics.makeForTesting (e.g., create an
XCTestExpectation in the test and have flushImplementation fulfill it when
executed), then after calling releaseQueuedWork.signal() wait for that
expectation to ensure the enqueued flush work ran; locate the test that
constructs PostHogAnalytics via PostHogAnalytics.makeForTesting, modify the
flushImplementation closure to call expectation.fulfill(), and add an
XCTWaiter/XCTestExpectation wait after releaseQueuedWork.signal().

}

enum BrowserDeveloperToolsAttachmentPolicy {
private static let firstStableAttachedInspectorMajorVersion = 26
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fix the OS major-version threshold; 26 effectively disables attached inspector for all current macOS releases.

ProcessInfo.processInfo.operatingSystemVersion.majorVersion returns macOS major versions (15/16/…), so >= 26 keeps attached inspector disabled far beyond the intended macOS 15 workaround. This broadens the fallback behavior and likely regresses attach support on unaffected versions.

Suggested fix
 enum BrowserDeveloperToolsAttachmentPolicy {
-    private static let firstStableAttachedInspectorMajorVersion = 26
+    private static let firstStableAttachedInspectorMajorVersion = 16

Also applies to: 68-68

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/Panels/BrowserPanel.swift` at line 43, The constant
firstStableAttachedInspectorMajorVersion is set to 26 which incorrectly disables
the attached inspector for all current macOS releases; change its value to the
intended macOS major-version threshold (e.g., 15) so the attached inspector is
only disabled for versions older than the macOS 15 workaround, and update both
occurrences of firstStableAttachedInspectorMajorVersion in BrowserPanel.swift to
the corrected value.

Comment on lines 105 to 109
func flush() {
dispatchSyncOnWorkQueue {
guard didStart else { return }
PostHogSDK.shared.flush()
dispatchAsyncOnWorkQueue { [weak self] in
guard let self, self.didStart else { return }
self.flushImplementation()
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Make flush() unconditionally async.

Line 106 still goes through dispatchAsyncOnWorkQueue(_:), and that helper runs inline when the caller is already on workQueue. That means flush() is still synchronous for re-entrant callers, which weakens the new “return promptly” contract this PR is introducing.

Suggested fix
 func flush() {
-    dispatchAsyncOnWorkQueue { [weak self] in
+    workQueue.async { [weak self] in
         guard let self, self.didStart else { return }
         self.flushImplementation()
     }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func flush() {
dispatchSyncOnWorkQueue {
guard didStart else { return }
PostHogSDK.shared.flush()
dispatchAsyncOnWorkQueue { [weak self] in
guard let self, self.didStart else { return }
self.flushImplementation()
}
func flush() {
workQueue.async { [weak self] in
guard let self, self.didStart else { return }
self.flushImplementation()
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/PostHogAnalytics.swift` around lines 105 - 109, The flush() method
currently calls dispatchAsyncOnWorkQueue(_:) which may execute inline if the
caller is already on workQueue, making flush synchronous for re-entrant callers;
change flush() to always schedule asynchronously by dispatching directly to
workQueue (e.g., use workQueue.async { [weak self] in ... }) so that the body
(guard let self, self.didStart else { return }; self.flushImplementation())
never runs synchronously; keep the weak self capture and the same
guard/didStart/flushImplementation logic.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 29, 2026

Greptile Summary

This PR fixes two independent macOS regressions: a crash when the Web Inspector attach API is called on macOS 15 WebKit builds, and a quit-time hang caused by PostHogAnalytics.flush() blocking the main thread on the analytics work queue.

  • BrowserPanel.swift: Introduces BrowserDeveloperToolsAttachmentPolicy, a version-gated check that forces the DevTools presentation to .detached on macOS < 26 (where WebInspectorUIProxy::platformAttach crashes), skipping the private attach selector call entirely. The preference field is in-memory, so it resets on the next panel creation.
  • PostHogAnalytics.swift: Replaces the synchronous dispatchSyncOnWorkQueue in flush() with an async dispatch, eliminating the main-thread block during applicationWillTerminate. The flushImplementation closure is injected through a new testing-only constructor, making the behaviour unit-testable without touching the live PostHog SDK.
  • Both fixes are covered by new XCTest cases that reproduce the failure scenario (OS-version gating, queue-blocked flush timing).

Confidence Score: 4/5

The changes are narrowly scoped, well-tested, and address concrete crash/hang reports. The async flush trade-off and the DEBUG-global race are minor concerns that don't block shipping.

Both changes are backed by reproducing unit tests. The async flush() intentionally trades guaranteed quit-time delivery for non-blocking behaviour — acceptable for telemetry, but the delivery semantics shift is undocumented at the call-site. The nonisolated(unsafe) test global is DEBUG-only and low-risk in practice. No production logic path is left in an incorrect state.

Sources/PostHogAnalytics.swift — the quit-time delivery semantics of flush() changed; Sources/Panels/BrowserPanel.swift — the nonisolated(unsafe) test override global.

Important Files Changed

Filename Overview
Sources/Panels/BrowserPanel.swift Adds BrowserDeveloperToolsAttachmentPolicy enum to gate WebInspector attach calls on macOS < 26, preventing the WebKit platformAttach crash on macOS 15.
Sources/PostHogAnalytics.swift Changes flush() from synchronous to async dispatch, fixing the quit-time deadlock. New flush() is fire-and-forget from the caller's perspective.
cmuxTests/BrowserConfigTests.swift Adds tests for the attachment policy and behaviour when attach is disallowed.
cmuxTests/GhosttyConfigTests.swift Adds test verifying flush() returns immediately when the work queue is blocked.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["showDeveloperTools()"] --> B["prepareDeveloperToolsForRevealIfNeeded(inspector)"]
    B --> C{"allowsAttachedInspector()\nosVersion.major >= 26?"}
    C -- "No (macOS 15–25)" --> D["setPreferredPresentation(.detached)\nreturn — no attach call"]
    C -- "Yes (macOS 26+)" --> E{"preferredPresentation\n== .unknown?"}
    E -- "Yes" --> H["inspector.attach()"]
    E -- "No" --> F{"preferredPresentation\n== .attached?"}
    F -- "No (.detached)" --> G["return — skip attach"]
    F -- "Yes" --> I{"webView in window?\ninspector not already attached?"}
    I -- "No" --> G
    I -- "Yes" --> H
    H --> J["inspector.show()"]
    D --> J
Loading

Reviews (1): Last reviewed commit: "fix: prevent crash report diagnostics" | Re-trigger Greptile

Comment on lines 105 to 110
func flush() {
dispatchSyncOnWorkQueue {
guard didStart else { return }
PostHogSDK.shared.flush()
dispatchAsyncOnWorkQueue { [weak self] in
guard let self, self.didStart else { return }
self.flushImplementation()
}
}
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.

P2 Async flush no longer guarantees delivery before process exit

The quit-time caller in applicationWillTerminate now dispatches flushImplementation() asynchronously and returns immediately. If the run loop drains before the workQueue block executes, events captured in the final moments of the session will not be sent in this launch. Whether PostHog's SDK persists those events to disk for the next launch depends on whether the SDK's own flush cycle fires before exit — there's no guarantee. The fix is correct for eliminating the hang, but it's worth noting explicitly (e.g. in a comment near the applicationWillTerminate call-site or here) that quit-time delivery is now best-effort.

Comment on lines +42 to +70
enum BrowserDeveloperToolsAttachmentPolicy {
private static let firstStableAttachedInspectorMajorVersion = 26

#if DEBUG
private nonisolated(unsafe) static var attachedInspectorAllowedOverrideForTesting: Bool?

static func withAttachedInspectorAllowedForTesting<T>(
_ allowed: Bool?,
_ body: () throws -> T
) rethrows -> T {
let previous = attachedInspectorAllowedOverrideForTesting
attachedInspectorAllowedOverrideForTesting = allowed
defer { attachedInspectorAllowedOverrideForTesting = previous }
return try body()
}
#endif

static func allowsAttachedInspector(
osVersion: OperatingSystemVersion = ProcessInfo.processInfo.operatingSystemVersion
) -> Bool {
#if DEBUG
if let attachedInspectorAllowedOverrideForTesting {
return attachedInspectorAllowedOverrideForTesting
}
#endif
// macOS 15 WebKit 20621 crashes inside WebInspectorUIProxy::platformAttach.
return osVersion.majorVersion >= firstStableAttachedInspectorMajorVersion
}
}
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.

P2 nonisolated(unsafe) test-override global is not concurrency-safe across parallel test suites

attachedInspectorAllowedOverrideForTesting is a mutable static written and read in withAttachedInspectorAllowedForTesting without any lock. If Xcode runs test classes in parallel (e.g. BrowserDeveloperToolsConfigurationTests alongside BrowserDeveloperToolsVisibilityPersistenceTests), concurrent mutation of this global is a data race under Swift 6's memory model. Since the scope is DEBUG-only and the existing test runner is likely serial, this is low risk in practice, but OSAllocatedUnfairLock or @MainActor isolation would eliminate the hazard.

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.

1 participant