1//go:build unix23package stress45import (6 "errors"7 "io/fs"8 "os"9 "syscall"10 "time"11)1213// BenchLockPath is the lock timed runs on this machine share, as the14// speed gate takes it (internal/ui/speedlock_unix_test.go): a file15// locked with flock(2), or a directory made and removed after where16// there is no flock command (macOS).17const BenchLockPath = "/tmp/012-bench.lock"1819// Logger is what BenchLock says why it times without the lock through:20// a testing.TB.21type Logger interface {22 Logf(format string, args ...any)23 Fatalf(format string, args ...any)24}2526// BenchLock waits up to ten minutes for the lock and returns its27// release; past that, or when it can't be taken, it times without it.28func BenchLock(tb Logger) func() {29 deadline := time.Now().Add(10 * time.Minute)30 for {31 err := os.Mkdir(BenchLockPath, 0o755)32 if err == nil {33 return func() { os.Remove(BenchLockPath) }34 }35 info, serr := os.Stat(BenchLockPath)36 switch {37 case !errors.Is(err, fs.ErrExist) || serr != nil:38 tb.Logf("timing without the lock: %v", err)39 return func() {}40 case info.Mode().IsRegular():41 return flockBench(tb)42 case time.Now().After(deadline):43 tb.Logf("timing without the lock: %s held too long", BenchLockPath)44 return func() {}45 }46 time.Sleep(200 * time.Millisecond)47 }48}4950// flockBench takes the lock as a file, with flock(2).51func flockBench(tb Logger) func() {52 f, err := os.OpenFile(BenchLockPath, os.O_RDWR, 0)53 if err != nil {54 tb.Logf("timing without the lock: %v", err)55 return func() {}56 }57 if err := syscall.Flock(int(f.Fd()), syscall.LOCK_EX); err != nil {58 f.Close()59 tb.Fatalf("locking %s: %v", BenchLockPath, err)60 }61 return func() {62 syscall.Flock(int(f.Fd()), syscall.LOCK_UN)63 f.Close()64 }65}