Conversation
Copilot
AI
changed the title
[WIP] Fix checkptr error triggered by race flag in tests
Fix Mar 18, 2026
-race checkptr fatal: replace global unsafe.Pointer expunged sentinel with per-entry typed field
af31628 to
9cd6261
Compare
9cd6261 to
bcc8f55
Compare
Owner
|
/diff |
Benchmark Comparison (vs main) |
Owner
|
@copilot can you please fix /diff chatbot failure? |
Contributor
Author
The PR diff was noisy due to unrelated changes (Makefile, go.mod, go.sum, counter refactoring) that were accidentally bundled into the commit. Cleaned up in |
874e7c5 to
bcc8f55
Compare
…per-entry typed expunged field Signed-off-by: kpango <kpango@vdaas.org>
bcc8f55 to
3ae43e4
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
var expunged = unsafe.Pointer(new(any))expunged atomic.Pointer[V]field toentry[V]structexpungedwithnew(V)innewEntryandnewEntryPointerisExpunged(p *V) boolhelper method onentry[V](*V)(expunged)casts withe.expunged.Load()/e.isExpunged(p)-raceflagmap.gochanged (removed unrelated Makefile/go.mod/go.sum/counter changes)Original prompt
Problem
Resolves #181
Since v2.1.5, running tests with
-raceflag triggers a fatalcheckptrerror:The root cause is the use of a global untyped sentinel value:
This is then cast to
(*V)(expunged)in many places throughoutmap.go. WhenVis a non-pointer value type (e.g. a struct), the cast(*V)(expunged)creates an unsafe pointer conversion across different memory allocations, violating Go's pointer safety rules that are checked when-raceis enabled.Required Fix
Replace the global
unsafe.Pointersentinel with a per-entry typedexpungedpointer stored as anatomic.Pointer[V]field in theentry[V]struct. This makes all expunged comparisons type-safe.Changes needed in
map.go:Remove the global variable:
Add an
expungedfield to theentry[V]struct:Initialize
expungedinnewEntryandnewEntryPointer:Add a helper method
isExpungedonentry[V]:Replace all occurrences of
(*V)(expunged)withe.expunged.Load()and allp == (*V)(expunged)comparisons withe.isExpunged(p). This affects the following methods:entry[V].load()(line 131)entry[V].loadPointer()(line 139)entry[V].unexpungeLocked()(line 174-175)entry[V].tryLoadOrStorePointer()(lines 295, 307)entry[V].deletePointer()(line 339)entry[V].trySwap()(line 355)entry[V].tryCompareAndSwap()(lines 412, 422)entry[V].tryCompareAndSwapPointer()(line 430)entry[V].tryExpungeLocked()(lines 528, 533)entry[V].Size()(line 564)Map[K,V].CompareAndDelete()(line 454)For
unexpungeLocked, change:For
tryExpungeLocked, change:Remove the
import "unsafe"if it's no longer needed (it is still used inSize()methods viaunsafe.Sizeof, so keep it).Important: The
import "unsafe"must be kept because it's still used inSize()andmapSize().Make sure all existing tests pass, especially with
-raceflag. The key insight is that everyentry[V]now carries its own properly-typed expunged sentinel, so no cross-allocation pointer casts occur.The following is the prior conversation context from the user's chat exploration (may be truncated):
User: Can you resolve issue @kpango/gache/issues/181 ?
Assistant:
Let me start by examining the current codebase to understand the issue and the relevant code.
Assistant: Now I have a clear understanding of the issue and the codebase. Let me create a PR to fix this.
Assistant: I've initiated the creation of a pull request to resolve issue #181 in
kpango/gache. The coding agent is now working on implementing the fix. Here's a summary of what the PR will do:Root Cause
The global untyped sentinel
var expunged = unsafe.Pointer(new(any))is cast to(*V)(expunged)throughoutmap.go. WhenVis a non-pointer value type (like a struct), this cast creates an unsafe pointer conversion that straddles multiple allocations — which Go'scheckptrinstrumentation (enabled with-race) correctly flags as a fatal error.Fix Approach
The PR will replace the global
unsafe.Pointersentinel with a per-entry typed expunged pointer stored as anatomic.Pointer[V]field in theentry[V]struct:var expunged = unsafe.Pointer(new(any))expunged atomic.Pointer[V]field toentry[V]new(V)innewEntry/newEntryPointer(type-safe allocation)isExpunged(p *V) boolhelper method(*V)(expunged)casts withe.expunged.Load()across ~12 locations in the fileThis ensures all expunged comparisons are type-safe — no
unsafe.Pointercasts crossing allocation boundaries — while preserving the exact same concurrent map semantics.The agent wil...
This pull request was created from Copilot chat.
📍 Connect Copilot coding agent with Jira, Azure Boards or Linear to delegate work to Copilot in one click without leaving your project management tool.