diff options
| author | Paul Buetow <paul@buetow.org> | 2026-03-06 16:03:00 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-03-06 16:03:00 +0200 |
| commit | a2d253f1e92578ccea95f962bbd1a1aebf190de1 (patch) | |
| tree | 251cb010c0808cbda0e49320436f35f3522ff054 | |
| parent | fcee8baac995b25ffb9ab06567f010df105c3db1 (diff) | |
fix: make flags string cache concurrent-safe (task 386)
| -rw-r--r-- | internal/file/flags.go | 11 | ||||
| -rw-r--r-- | internal/file/flags_test.go | 40 |
2 files changed, 48 insertions, 3 deletions
diff --git a/internal/file/flags.go b/internal/file/flags.go index ca749e1..c06c27b 100644 --- a/internal/file/flags.go +++ b/internal/file/flags.go @@ -3,12 +3,13 @@ package file import ( "os" "strings" + "sync" "syscall" ) type Flags int32 -var flagsToHumanCache = map[Flags]string{} +var flagsToHumanCache sync.Map var unknownFlag = Flags(-1) type tuple struct { @@ -49,12 +50,16 @@ func (f Flags) Is(flag int) bool { } func (f Flags) BuildString(sb *strings.Builder) { - if str, ok := flagsToHumanCache[f]; ok { + if cached, ok := flagsToHumanCache.Load(f); ok { + str, _ := cached.(string) sb.WriteString(str) return } str := f.String() - flagsToHumanCache[f] = str + cached, loaded := flagsToHumanCache.LoadOrStore(f, str) + if loaded { + str, _ = cached.(string) + } sb.WriteString(str) } diff --git a/internal/file/flags_test.go b/internal/file/flags_test.go new file mode 100644 index 0000000..120e432 --- /dev/null +++ b/internal/file/flags_test.go @@ -0,0 +1,40 @@ +package file + +import ( + "strings" + "sync" + "syscall" + "testing" +) + +func TestFlagsBuildStringConcurrent(t *testing.T) { + flagsToHumanCache = sync.Map{} + + const workers = 32 + const iterations = 500 + const want = "O_WRONLY|O_APPEND" + flag := Flags(syscall.O_WRONLY | syscall.O_APPEND) + + var wg sync.WaitGroup + errs := make(chan string, workers) + for i := 0; i < workers; i++ { + wg.Add(1) + go func() { + defer wg.Done() + for j := 0; j < iterations; j++ { + var sb strings.Builder + flag.BuildString(&sb) + if got := sb.String(); got != want { + errs <- got + return + } + } + }() + } + wg.Wait() + close(errs) + + for got := range errs { + t.Fatalf("unexpected BuildString output %q, want %q", got, want) + } +} |
