Skip to content

Commit

Permalink
os: fix race condition in readdir by atomically initializing dirinfo
Browse files Browse the repository at this point in the history
This change ensures that dirinfo in the File struct is initialized atomically,
avoiding redundant allocations when multiple goroutines access it concurrently.
Instead of creating separate buffers, we now use CompareAndSwap to guarantee
thread-safe initialization and reduce unnecessary memory usage.

Although this is not a strict race condition, the update enhances efficiency by
eliminating duplicate allocations and ensuring safer concurrent access.

Fixes #71496.

Change-Id: If08699a94afa05611cdf67e82a5957a8d8f9d5c8
GitHub-Last-Rev: 1e1f619
GitHub-Pull-Request: #71501
Reviewed-on: https://go-review.googlesource.com/c/go/+/645720
Auto-Submit: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
  • Loading branch information
Mehrbod2002 authored and gopherbot committed Feb 5, 2025
1 parent a1ea78c commit be2b809
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 9 deletions.
17 changes: 12 additions & 5 deletions src/os/dir_plan9.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,19 @@ import (
)

func (file *File) readdir(n int, mode readdirMode) (names []string, dirents []DirEntry, infos []FileInfo, err error) {
// If this file has no dirinfo, create one.
d := file.dirinfo.Load()
if d == nil {
d = new(dirInfo)
file.dirinfo.Store(d)
var d *dirInfo
for {
d = file.dirinfo.Load()
if d != nil {
break
}
newD := new(dirInfo)
if file.dirinfo.CompareAndSwap(nil, newD) {
d = newD
break
}
}

d.mu.Lock()
defer d.mu.Unlock()

Expand Down
16 changes: 12 additions & 4 deletions src/os/dir_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,19 @@ func (d *dirInfo) close() {

func (f *File) readdir(n int, mode readdirMode) (names []string, dirents []DirEntry, infos []FileInfo, err error) {
// If this file has no dirInfo, create one.
d := f.dirinfo.Load()
if d == nil {
d = new(dirInfo)
f.dirinfo.Store(d)
var d *dirInfo
for {
d = f.dirinfo.Load()
if d != nil {
break
}
newD := new(dirInfo)
if f.dirinfo.CompareAndSwap(nil, newD) {
d = newD
break
}
}

d.mu.Lock()
defer d.mu.Unlock()
if d.buf == nil {
Expand Down

0 comments on commit be2b809

Please sign in to comment.