os: fix race condition in readdir by atomically initializing dirinfo

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: 1e1f619143
GitHub-Pull-Request: golang/go#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>
This commit is contained in:
Amirhossein Akhlaghpour 2025-02-03 05:56:31 +00:00 committed by Gopher Robot
parent a1ea78c470
commit be2b809e5b
2 changed files with 24 additions and 9 deletions

View file

@ -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()

View file

@ -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 {