Skip to content

Config.parse races on concurrent first use #15

Closed

Description

@andrew

Config.parse() runs lazily from For and Enabled without any locking, and it sets c.parsed = true before it fills defaultDuration, ecosystemDurations and packageDurations. When two goroutines make the first call on the same Config at once, one can see parsed already set and read zero durations or nil maps, so cooldown comes back as off. It can also read packageDurations or ecosystemDurations while the other goroutine is still writing them, and Go can abort the program with "concurrent map read and map write" when that happens.

go test -race reports it with eight goroutines calling For on a fresh Config:

c := &cooldown.Config{Default: "3d", Ecosystems: map[string]string{"npm": "7d"}}
var wg sync.WaitGroup
for i := 0; i < 8; i++ {
	wg.Add(1)
	go func() { defer wg.Done(); _ = c.For("npm", "pkg:npm/lodash") }()
}
wg.Wait()

The races are between cooldown.go:59/62 (the parsed check and set) and between the map writes at 66-72 and the reads in For at 84-87.

NewPolicy calls base.Enabled(), so a Config built through it is parsed before it is shared, and the proxy server doesn't hit this. A bare Config used from several goroutines does: the proxy's npm streaming tests in git-pkgs/proxy#640 had to switch to NewPolicy to avoid it. Wrapping the body of parse in a sync.Once field would fix it.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions