Skip to content

[Swift6] Fix TSan race condition in Swift6 SynchronizedDictionary#23091

Merged
4brunu merged 1 commit intoOpenAPITools:masterfrom
biovolt:fix/swift6-synchronized-dictionary-race
Mar 3, 2026
Merged

[Swift6] Fix TSan race condition in Swift6 SynchronizedDictionary#23091
4brunu merged 1 commit intoOpenAPITools:masterfrom
biovolt:fix/swift6-synchronized-dictionary-race

Conversation

@biovolt
Copy link
Contributor

@biovolt biovolt commented Mar 2, 2026

Problem

SynchronizedDictionary is a struct held as a var property on shared singleton classes (URLSessionRequestBuilderConfiguration and AlamofireRequestBuilderConfiguration). When multiple concurrent network requests mutate credentialStore or managerStore from different
threads, Swift's memory exclusivity rules treat each subscript mutation as a modifying access to the entire struct — causing a real ThreadSanitizer "Swift access race".

Changes

struct → class (SynchronizedDictionary.mustache + 13 sample files)

Changing SynchronizedDictionary from a value type to a reference type makes mutations go through pointer indirection. Concurrent subscript writes now only touch the object's internal state (already protected by NSRecursiveLock), rather than triggering a modifying
access on the owning class's stored property.

var → let (URLSessionImplementations.mustache + 11 sample files, AlamofireImplementations.mustache + 2 sample files)

Since SynchronizedDictionary is now a class, the properties credentialStore and managerStore only need to hold a stable reference — they never get reassigned. Declaring them as let makes this intent explicit and prevents any residual exclusivity issues on the property
itself.
Fixes #23055

@4brunu

PR checklist

  • [x ] Read the contribution guidelines.
  • [ x] Pull Request title clearly describes the work in the pull request and Pull Request description provides details about how to validate the work. Missing information here may result in delayed response from the community.
  • [ x] Run the following to build the project and update samples:
    ./mvnw clean package || exit
    ./bin/generate-samples.sh ./bin/configs/*.yaml || exit
    ./bin/utils/export_docs_generators.sh || exit
    
    (For Windows users, please run the script in WSL)
    Commit all changed files.
    This is important, as CI jobs will verify all generator outputs of your HEAD commit as it would merge with master.
    These must match the expectations made by your contribution.
    You may regenerate an individual generator by passing the relevant config(s) as an argument to the script, for example ./bin/generate-samples.sh bin/configs/java*.
    IMPORTANT: Do NOT purge/delete any folders/files (e.g. tests) when regenerating the samples as manually written tests may be removed.
  • File the PR against the correct branch: master (upcoming 7.x.0 minor release - breaking changes with fallbacks), 8.0.x (breaking changes without fallbacks)
  • If your PR solves a reported issue, reference it using GitHub's linking syntax (e.g., having "fixes #123" present in the PR description)
  • If your PR is targeting a particular programming language, @mention the technical committee members, so they are more likely to review the pull request.

Summary by cubic

Fixes a ThreadSanitizer Swift access race in Swift6 clients by making SynchronizedDictionary a class and storing stable references for credentialStore and managerStore. This removes exclusivity violations during concurrent URLSession and Alamofire requests.

  • Bug Fixes
    • SynchronizedDictionary switched from struct to class so concurrent writes mutate internal state behind NSRecursiveLock rather than the owning property.
    • credentialStore and managerStore are now let on configuration singletons to keep stable references and prevent residual exclusivity issues.

Written for commit 3d4101a. Summary will update on new commits.

  Problem

  SynchronizedDictionary is a struct held as a var property on shared singleton classes (URLSessionRequestBuilderConfiguration and AlamofireRequestBuilderConfiguration). When multiple concurrent network requests mutate credentialStore or managerStore from different
  threads, Swift's memory exclusivity rules treat each subscript mutation as a modifying access to the entire struct — causing a real ThreadSanitizer "Swift access race".

  Changes

  struct → class (SynchronizedDictionary.mustache + 13 sample files)

  Changing SynchronizedDictionary from a value type to a reference type makes mutations go through pointer indirection. Concurrent subscript writes now only touch the object's internal state (already protected by NSRecursiveLock), rather than triggering a modifying
  access on the owning class's stored property.

  var → let (URLSessionImplementations.mustache + 11 sample files, AlamofireImplementations.mustache + 2 sample files)

  Since SynchronizedDictionary is now a class, the properties credentialStore and managerStore only need to hold a stable reference — they never get reassigned. Declaring them as let makes this intent explicit and prevents any residual exclusivity issues on the property
   itself.
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

No issues found across 29 files

@biovolt
Copy link
Contributor Author

biovolt commented Mar 2, 2026

Sorry I have no idea why Build 21254 failed

@4brunu
Copy link
Contributor

4brunu commented Mar 3, 2026

Looks good to me, CI failure is not related to this PR.

@4brunu 4brunu merged commit d9dd07a into OpenAPITools:master Mar 3, 2026
16 of 17 checks passed
@biovolt biovolt deleted the fix/swift6-synchronized-dictionary-race branch March 3, 2026 10:24
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.

[BUG][Swift6] ThreadSanitizer Swift Access Race in OpenAPI Generator Swift Client

2 participants