Skip to content

fix(coderd/util/syncmap): match sync.Map semantics in the typed wrapper - #27582

Merged
mafredri merged 3 commits into
mainfrom
mathias/codagt-869-syncmap-loadorstore
Jul 28, 2026
Merged

fix(coderd/util/syncmap): match sync.Map semantics in the typed wrapper#27582
mafredri merged 3 commits into
mainfrom
mathias/codagt-869-syncmap-loadorstore

Conversation

@mafredri

@mafredri mafredri commented Jul 28, 2026

Copy link
Copy Markdown
Member

Fixes CODAGT-869.

syncmap.Map wraps sync.Map to keep untyped values off callers, and three defects broke that contract in different directions.

LoadOrStore threw away the value it stored and returned the zero V, so the load-or-create pattern that the stdlib contract guarantees (v, _ := m.LoadOrStore(k, new(T)); v.Use()) nil-dereferenced on the first call. Swap declared previous any, making callers assert a type they had already supplied. And every read path panicked with interface conversion: interface is nil when V was an interface type holding nil, because sync.Map returns that as a nil any, which cannot be asserted. All five read paths now share one cast[T] helper that maps nil to the zero T, matching what sync.Map returned; that also subsumes the not-found early returns, since a miss yields nil too.

Neither LoadOrStore nor Swap has an in-tree caller today (the LoadOrStore call sites in chatdebug, reconnectingpty, and aibridge/circuitbreaker are all on stdlib sync.Map), so nothing behavioural changes elsewhere and the signature change breaks nothing. The bug surfaced while writing a test in #27547, where the workaround was load-then-store under a mutex.

The package had no tests. The new ones pin every method to the stdlib contract, including the Swap return type, which only compiles as V. They were checked against the old implementation: three fail on LoadOrStore, TestSwapTyped fails to compile, and the nil-interface subtests panic.

Clear is still missing versus Go 1.23 sync.Map; left alone since nothing needs it.

🤖 This PR was created with the help of Coder Agents, and will be reviewed by a human. 🏂🏻

@linear-code

linear-code Bot commented Jul 28, 2026

Copy link
Copy Markdown

CODAGT-869

mafredri added 2 commits July 28, 2026 11:42
sync.Map.LoadOrStore guarantees that actual is usable whether the value
was loaded or stored, which is what makes the load-or-create pattern
work. The wrapper discarded the value on the store path and returned the
zero V, so a caller doing `v, _ := m.LoadOrStore(k, new(T)); v.Use()`
dereferenced nil on the first call for every pointer value type.

No caller used the method yet, so nothing else changes. The new tests
pin every wrapper method to the stdlib contract; three of them fail
against the old LoadOrStore.

Fixes CODAGT-869
The wrapper exists to keep sync.Map's untyped values off callers, and
Swap leaked one back: it declared previous as any, so a caller had to
type-assert the value it just handed in. It has no callers, so this
breaks nothing.
@mafredri
mafredri force-pushed the mathias/codagt-869-syncmap-loadorstore branch from a82d82b to 21d288e Compare July 28, 2026 11:45

Copilot AI 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.

Pull request overview

This PR fixes the coderd/util/syncmap typed wrapper to better match sync.Map semantics, specifically ensuring LoadOrStore returns the stored value (not V’s zero) and making Swap return the correctly typed previous value.

Changes:

  • Fix LoadOrStore to return the actual stored value, matching the stdlib load-or-create contract.
  • Change Swap’s return type from any to V, removing the need for redundant type assertions by callers.
  • Add a new test suite that pins wrapper behavior to sync.Map semantics across all methods.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
coderd/util/syncmap/map.go Adjusts LoadOrStore and Swap behavior/signatures to match sync.Map semantics.
coderd/util/syncmap/map_test.go Adds coverage asserting the wrapper matches stdlib sync.Map behavior, including concurrency and typed returns.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread coderd/util/syncmap/map.go
Comment thread coderd/util/syncmap/map.go
Comment thread coderd/util/syncmap/map_test.go
sync.Map stores a nil interface as a nil `any`, and a nil `any` cannot be
type-asserted, so every read path panicked with "interface conversion:
interface is nil" when V was an interface type holding nil. Copilot
flagged LoadOrStore and Swap on this PR; Load, LoadAndDelete and Range
had the same hole, so all five now go through one cast helper that maps
nil to the zero V, which is what sync.Map handed back.

The helper subsumes the not-found early returns, since a miss also yields
nil. TestLoadOrStoreConcurrent now asserts exactly one goroutine stores,
an invariant sequential execution cannot satisfy.
@mafredri mafredri changed the title fix(coderd/util/syncmap): match sync.Map semantics in LoadOrStore and Swap fix(coderd/util/syncmap): match sync.Map semantics in the typed wrapper Jul 28, 2026
@mafredri
mafredri requested a review from Copilot July 28, 2026 12:41
@mafredri
mafredri marked this pull request as ready for review July 28, 2026 12:43
@mafredri
mafredri requested a review from Emyrk July 28, 2026 12:44

Copilot AI 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.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

@mafredri

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. What shall we delve into next?

Reviewed commit: ca209d075c

ℹ️ 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".

@mafredri
mafredri merged commit eb90570 into main Jul 28, 2026
37 of 38 checks passed
@mafredri
mafredri deleted the mathias/codagt-869-syncmap-loadorstore branch July 28, 2026 16:06
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 28, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants