For that range to be correct, it needs to hold a lock around s, like subscope does:
func (r *scopeRegistry) ForEachScope(f func(*scope)) {
for _, subscopeBucket := range r.subscopes {
subscopeBucket.mu.RLock()
for _, s := range subscopeBucket.s {
f(s)
}
subscopeBucket.mu.RUnlock()
}
}
I can probably make a PR for this, but I figured I'd document it first so I don't forget before I get around to it (and open it up for others to do so, if I do forget). Making sure there isn't a bad perf regression and setting up a regression test will take a bit of time.
ForEachScope does not protect
scopedbucket.s
with the mutex thatsubscope
holds while mutating, leading to crashes like this (for an older version):The cause is pretty clear, reads of line 128 here: https://github.com/uber-go/tally/blob/007e844c400c1a16420fe6019d12f010acb11f73/scope_registry.go#L126-L134 race with writes at the end of
subscope
: https://github.com/uber-go/tally/blob/007e844c400c1a16420fe6019d12f010acb11f73/scope_registry.go#L199-L205For that
range
to be correct, it needs to hold a lock arounds
, likesubscope
does:I can probably make a PR for this, but I figured I'd document it first so I don't forget before I get around to it (and open it up for others to do so, if I do forget). Making sure there isn't a bad perf regression and setting up a regression test will take a bit of time.