From f5f2674ed517f99a6cd6e5028ef9e63dae363cb1 Mon Sep 17 00:00:00 2001 From: Travis Redman Date: Fri, 23 Nov 2018 13:44:47 -0800 Subject: [PATCH] [trace] Fix race between CreateAsyncChild and span.Send `CreateChild` adds a new span to the current span's list of children. `CreateAsyncChild` calls `CreateChild`, then sets the isAsync bool on the span. This can lead to a race caused by simultaneous read of the child span's `isAsync` bool by `Send` as it's being written by `CreateAsyncChild`. To fix this, I refactored the code a bit to allow us to set `isAsync` before the span is added to the list of child spans. This doesn't seem to be the same race reported in https://github.com/honeycombio/beeline-go/issues/38 but it should be fixed either way. --- trace/trace.go | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/trace/trace.go b/trace/trace.go index 2872ffad..77516e46 100644 --- a/trace/trace.go +++ b/trace/trace.go @@ -270,24 +270,13 @@ func (s *Span) GetParent() *Span { // outlive the current span (and trace). Async spans are not automatically sent // when their parent finishes, but are otherwise identical to synchronous spans. func (s *Span) CreateAsyncChild(ctx context.Context) (context.Context, *Span) { - ctx, newSpan := s.CreateChild(ctx) - newSpan.isAsync = true - return ctx, newSpan + return s.createChildSpan(ctx, true) } // Span creates a synchronous child of the current span. Spans must finish // before their parents. func (s *Span) CreateChild(ctx context.Context) (context.Context, *Span) { - newSpan := newSpan() - newSpan.parent = s - newSpan.parentID = s.spanID - newSpan.trace = s.trace - newSpan.ev = s.trace.builder.NewEvent() - s.childrenLock.Lock() - s.children = append(s.children, newSpan) - s.childrenLock.Unlock() - ctx = PutSpanInContext(ctx, newSpan) - return ctx, newSpan + return s.createChildSpan(ctx, false) } // SerializeHeaders returns the trace ID, current span ID as parent ID, and an @@ -370,3 +359,17 @@ func (s *Span) send() { s.ev.SendPresampled() } } + +func (s *Span) createChildSpan(ctx context.Context, async bool) (context.Context, *Span) { + newSpan := newSpan() + newSpan.parent = s + newSpan.parentID = s.spanID + newSpan.trace = s.trace + newSpan.ev = s.trace.builder.NewEvent() + newSpan.isAsync = async + s.childrenLock.Lock() + s.children = append(s.children, newSpan) + s.childrenLock.Unlock() + ctx = PutSpanInContext(ctx, newSpan) + return ctx, newSpan +}