Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

apm: fix race in DefaultTracer #1248

Merged
merged 1 commit into from
May 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 25 additions & 14 deletions tracer.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,30 +50,41 @@ var (
)

// DefaultTracer returns the default global Tracer, set the first time the
// function is called. It is configured via environment variables.
// function is called, or after calling SetDefaultTracer(nil).
//
// This will always be initialized to a non-nil value. If any of the
// environment variables are invalid, the corresponding errors will be logged
// to stderr and the default values will be used instead.
// The default tracer is configured via environment variables, and will always
// be non-nil. If any of the environment variables are invalid, the
// corresponding errors will be logged to stderr and the default values will be
// used instead.
func DefaultTracer() *Tracer {
tracerMu.RLock()
tracer := defaultTracer
tracerMu.RUnlock()
if tracer != nil {
if defaultTracer != nil {
tracer := defaultTracer
tracerMu.RUnlock()
return tracer
}
tracerMu.RUnlock()

tracerMu.Lock()
defer tracerMu.Unlock()
if defaultTracer != nil {
return defaultTracer
}

var opts TracerOptions
opts.initDefaults(true)
tracer = newTracer(opts)
SetDefaultTracer(tracer)
return tracer
defaultTracer = newTracer(opts)
return defaultTracer
}

// SetDefaultTracer sets the tracer returned by DefaultTracer(). If another
// tracer has already been initialized, it is closed. Any queued events are not
// flushed; it is the responsibility of the caller to call
// DefaultTracer().Flush().
// SetDefaultTracer sets the tracer returned by DefaultTracer.
//
// If a default tracer has already been initialized, it is closed.
// Any queued events are not flushed; it is the responsibility of the
// caller to call the default tracer's Flush method first, if needed.
//
// Calling SetDefaultTracer(nil) will clear the default tracer,
// causing DefaultTracer to initialize a new default tracer.
func SetDefaultTracer(t *Tracer) {
tracerMu.Lock()
defer tracerMu.Unlock()
Expand Down
18 changes: 18 additions & 0 deletions tracer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,24 @@ import (
"go.elastic.co/apm/v2/transport/transporttest"
)

func TestDefaultTracer(t *testing.T) {
defer apm.SetDefaultTracer(nil)

// Call DefaultTracer concurrently to ensure there are
// no races in creating the default tracer.
tracers := make(chan *apm.Tracer, 1000)
for i := 0; i < cap(tracers); i++ {
go func() {
tracers <- apm.DefaultTracer()
}()
}

tracer0 := <-tracers
for i := 1; i < cap(tracers); i++ {
assert.Same(t, tracer0, <-tracers)
}
}

func TestTracerStats(t *testing.T) {
tracer := apmtest.NewDiscardTracer()
defer tracer.Close()
Expand Down