From 73df946d56c74384511a194dd01dbe099584fd1a Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 28 Apr 2024 15:14:23 +0200 Subject: Adding upstream version 1.16.10. Signed-off-by: Daniel Baumann --- src/cmd/go/internal/renameio/renameio_test.go | 160 ++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 src/cmd/go/internal/renameio/renameio_test.go (limited to 'src/cmd/go/internal/renameio/renameio_test.go') diff --git a/src/cmd/go/internal/renameio/renameio_test.go b/src/cmd/go/internal/renameio/renameio_test.go new file mode 100644 index 0000000..5b2ed83 --- /dev/null +++ b/src/cmd/go/internal/renameio/renameio_test.go @@ -0,0 +1,160 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !plan9 + +package renameio + +import ( + "encoding/binary" + "errors" + "internal/testenv" + "math/rand" + "os" + "path/filepath" + "runtime" + "strings" + "sync" + "sync/atomic" + "syscall" + "testing" + "time" + + "cmd/go/internal/robustio" +) + +func TestConcurrentReadsAndWrites(t *testing.T) { + if runtime.GOOS == "darwin" && strings.HasSuffix(testenv.Builder(), "-10_14") { + testenv.SkipFlaky(t, 33041) + } + + dir, err := os.MkdirTemp("", "renameio") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + path := filepath.Join(dir, "blob.bin") + + const chunkWords = 8 << 10 + buf := make([]byte, 2*chunkWords*8) + for i := uint64(0); i < 2*chunkWords; i++ { + binary.LittleEndian.PutUint64(buf[i*8:], i) + } + + var attempts int64 = 128 + if !testing.Short() { + attempts *= 16 + } + const parallel = 32 + + var sem = make(chan bool, parallel) + + var ( + writeSuccesses, readSuccesses int64 // atomic + writeErrnoSeen, readErrnoSeen sync.Map + ) + + for n := attempts; n > 0; n-- { + sem <- true + go func() { + defer func() { <-sem }() + + time.Sleep(time.Duration(rand.Intn(100)) * time.Microsecond) + offset := rand.Intn(chunkWords) + chunk := buf[offset*8 : (offset+chunkWords)*8] + if err := WriteFile(path, chunk, 0666); err == nil { + atomic.AddInt64(&writeSuccesses, 1) + } else if robustio.IsEphemeralError(err) { + var ( + errno syscall.Errno + dup bool + ) + if errors.As(err, &errno) { + _, dup = writeErrnoSeen.LoadOrStore(errno, true) + } + if !dup { + t.Logf("ephemeral error: %v", err) + } + } else { + t.Errorf("unexpected error: %v", err) + } + + time.Sleep(time.Duration(rand.Intn(100)) * time.Microsecond) + data, err := ReadFile(path) + if err == nil { + atomic.AddInt64(&readSuccesses, 1) + } else if robustio.IsEphemeralError(err) { + var ( + errno syscall.Errno + dup bool + ) + if errors.As(err, &errno) { + _, dup = readErrnoSeen.LoadOrStore(errno, true) + } + if !dup { + t.Logf("ephemeral error: %v", err) + } + return + } else { + t.Errorf("unexpected error: %v", err) + return + } + + if len(data) != 8*chunkWords { + t.Errorf("read %d bytes, but each write is a %d-byte file", len(data), 8*chunkWords) + return + } + + u := binary.LittleEndian.Uint64(data) + for i := 1; i < chunkWords; i++ { + next := binary.LittleEndian.Uint64(data[i*8:]) + if next != u+1 { + t.Errorf("wrote sequential integers, but read integer out of sequence at offset %d", i) + return + } + u = next + } + }() + } + + for n := parallel; n > 0; n-- { + sem <- true + } + + var minWriteSuccesses int64 = attempts + if runtime.GOOS == "windows" { + // Windows produces frequent "Access is denied" errors under heavy rename load. + // As long as those are the only errors and *some* of the writes succeed, we're happy. + minWriteSuccesses = attempts / 4 + } + + if writeSuccesses < minWriteSuccesses { + t.Errorf("%d (of %d) writes succeeded; want ≥ %d", writeSuccesses, attempts, minWriteSuccesses) + } else { + t.Logf("%d (of %d) writes succeeded (ok: ≥ %d)", writeSuccesses, attempts, minWriteSuccesses) + } + + var minReadSuccesses int64 = attempts + + switch runtime.GOOS { + case "windows": + // Windows produces frequent "Access is denied" errors under heavy rename load. + // As long as those are the only errors and *some* of the reads succeed, we're happy. + minReadSuccesses = attempts / 4 + + case "darwin", "ios": + // The filesystem on certain versions of macOS (10.14) and iOS (affected + // versions TBD) occasionally fail with "no such file or directory" errors. + // See https://golang.org/issue/33041 and https://golang.org/issue/42066. + // The flake rate is fairly low, so ensure that at least 75% of attempts + // succeed. + minReadSuccesses = attempts - (attempts / 4) + } + + if readSuccesses < minReadSuccesses { + t.Errorf("%d (of %d) reads succeeded; want ≥ %d", readSuccesses, attempts, minReadSuccesses) + } else { + t.Logf("%d (of %d) reads succeeded (ok: ≥ %d)", readSuccesses, attempts, minReadSuccesses) + } +} -- cgit v1.2.3