Skip to content

Fix ExtendedContextTests crash: nonisolated deinit on SuggestionSettingsModel#449

Merged
FuJacob merged 1 commit into
mainfrom
fix/settings-model-nonisolated-deinit
May 31, 2026
Merged

Fix ExtendedContextTests crash: nonisolated deinit on SuggestionSettingsModel#449
FuJacob merged 1 commit into
mainfrom
fix/settings-model-nonisolated-deinit

Conversation

@FuJacob

@FuJacob FuJacob commented May 31, 2026

Copy link
Copy Markdown
Owner

Summary

ExtendedContextTests was crashing the test host with malloc: pointer being freed was not allocated on every test that creates and destroys a SuggestionSettingsModel. All 5 settings-model tests failed as a result (the first crash killed the runner; the restart ran 0 tests).

The crash happens in SuggestionSettingsModel.__deallocating_deinit via swift_task_deinitOnExecutorMainActorBackDeploy. Because SuggestionSettingsModel is @MainActor, Swift schedules the deinit to hop to the main actor executor through the macOS 14 back-deployment shim. That shim has a bug on macOS 26 where swift::TaskLocal::StopLookupScope::~StopLookupScope() attempts to free stack memory through malloc, triggering abort().

Fix: add nonisolated deinit {} (SE-0371 / Swift 5.9). This instructs the compiler that the deinit is not actor-isolated and requires no executor hop, bypassing the buggy back-deployment path entirely. All stored state (@Published property wrappers and the UserDefaults reference) is thread-safe to release from any thread.

Validation

xcodebuild test \
  -project Cotabby.xcodeproj \
  -scheme Cotabby \
  -destination 'platform=macOS' \
  -only-testing:CotabbyTests/ExtendedContextTests \
  CODE_SIGNING_ALLOWED=NO
# Executed 12 tests, with 0 failures (0 unexpected)
# ** TEST SUCCEEDED **

swiftlint lint --quiet
# exit 0

Before this change: 5 failures (test_setExtendedContext_*), test host crash.
After: all 12 ExtendedContextTests pass.

Linked issues

Risk / rollout notes

nonisolated deinit is a Swift 5.9 language feature (available in Xcode 15+). The project already builds with Xcode 26. It has no runtime component — it only suppresses compiler-generated executor-hopping in the deinit, which is safe because @Published subjects and UserDefaults have no main-actor deallocation requirement.

Greptile Summary

This PR adds a single nonisolated deinit {} to SuggestionSettingsModel to fix a test-host crash on macOS 26 caused by a Swift back-deployment shim bug in the main-actor teardown path.

  • The empty nonisolated deinit prevents the compiler from generating an executor-hop to the main actor during deallocation, bypassing the StopLookupScope double-free in swift_task_deinitOnExecutorMainActorBackDeploy.
  • The stored state (@Published value-type properties and a UserDefaults reference) has no main-thread deallocation requirement, so the thread-safety claim in the comment is sound.

Confidence Score: 5/5

Safe to merge — the change is a single-line, well-scoped fix with no side effects on production behaviour.

The model holds only value-type @published properties and a UserDefaults reference, none of which require main-thread deallocation. No AnyCancellable sets, NotificationCenter observers, or other cleanup work exists in the deinit, so moving teardown off the main-actor executor cannot introduce new problems. In production the model is effectively a singleton and is rarely deallocated, so the change primarily affects test teardown.

No files require special attention.

Important Files Changed

Filename Overview
Cotabby/Models/SuggestionSettingsModel.swift Adds nonisolated deinit {} to suppress main-actor executor hop during teardown; all stored properties are value types or thread-safe reference types with no main-thread deallocation requirement.

Sequence Diagram

sequenceDiagram
    participant T as Test / ARC
    participant S as SuggestionSettingsModel
    participant MA as MainActor Executor
    participant Shim as Back-Deploy Shim (macOS 26)

    Note over T,Shim: Before fix
    T->>S: last strong reference released
    S->>MA: schedule deinit hop (compiler-generated)
    MA->>Shim: swift_task_deinitOnExecutorMainActorBackDeploy
    Shim-->>T: 💥 malloc: pointer being freed was not allocated

    Note over T,S: After fix (nonisolated deinit)
    T->>S: last strong reference released
    S->>S: deinit runs on current thread (no hop)
    S-->>T: ✅ stored properties released safely
Loading

Reviews (1): Last reviewed commit: "Add nonisolated deinit to SuggestionSett..." | Re-trigger Greptile

@FuJacob FuJacob merged commit f1b3d05 into main May 31, 2026
4 checks passed
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