Skip to content

fix: race condition in Tree.FindEntry() and Tree.entry()#1922

Open
majiayu000 wants to merge 2 commits intogo-git:mainfrom
majiayu000:fix/issue-1917-tree-findentry-race
Open

fix: race condition in Tree.FindEntry() and Tree.entry()#1922
majiayu000 wants to merge 2 commits intogo-git:mainfrom
majiayu000:fix/issue-1917-tree-findentry-race

Conversation

@majiayu000
Copy link
Copy Markdown

Fixes #1917

Summary

Three data races exist on the Tree struct when FindEntry() is called concurrently:

  1. t.t field — concurrent goroutines in FindEntry() read t.t == nil and write t.t = make(...) without synchronization
  2. t.m field — concurrent goroutines in entry() race on the nil-check and buildMap() call
  3. t.m map contents — one goroutine reads from t.m while another is still populating it via buildMap()

Approach

  • sync.Once for t.m (Races 2 & 3): Replace the nil-check-then-buildMap pattern in entry() with t.mOnce.Do(t.buildMap). buildMap is a one-time lazy init from immutable Entries, so sync.Once is the idiomatic solution with zero overhead after first call. Reset in Decode() where t.m is set to nil.

  • sync.Mutex for t.t (Race 1): Protect accesses to the tree path cache with fine-grained locking. I/O operations (tree.dir()) happen outside the lock to preserve concurrency.

Both caches are preserved — no performance regression (ref: #1238 was rejected for removing caches, causing 24x regression).

Test Plan

  • Added TestFindEntryConcurrent — spawns 10 goroutines calling FindEntry concurrently on the same Tree instance, verifying both same-entry and different-entry access patterns
  • All existing tests pass with -race flag

Add sync.Once for the entry name map (t.m) in entry() and sync.Mutex
for the tree path cache (t.t) in FindEntry() to make concurrent access
to the same Tree instance thread-safe.

Fixes go-git#1917

Signed-off-by: majiayu000 <1835304752@qq.com>
Remove pre-warming that populated caches before the concurrent phase,
making the race detector ineffective. Use top-level entry names and a
start barrier (sync.WaitGroup gate) so all goroutines begin
simultaneously, exercising concurrent t.m and t.t cache initialization.

Signed-off-by: majiayu000 <1835304752@qq.com>
@majiayu000 majiayu000 marked this pull request as ready for review March 24, 2026 11:14
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.

Race condition in v6/plumbing/object.(*Tree).FindEntry()

1 participant