Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 847d40d

Browse files
WillAbidesjba
authored andcommitted
log/slog: fix issue with concurrent writes
This causes instances commonHandler created by withAttrs or withGroup to share a mutex with their parent preventing concurrent writes to their shared writer. Fixes #61321 Change-Id: Ieec225e88ad51c84b41bad6c409fac48c90320b2 Reviewed-on: https://go-review.googlesource.com/c/go/+/509196 Reviewed-by: Bryan Mills <bcmills@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Run-TryBot: Jonathan Amsterdam <jba@google.com> Reviewed-by: Jonathan Amsterdam <jba@google.com>
1 parent c30faf9 commit 847d40d

File tree

4 files changed

+53
-1
lines changed

4 files changed

+53
-1
lines changed

‎src/log/slog/handler.go‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ type commonHandler struct {
193193
groupPrefix string
194194
groups []string // all groups started from WithGroup
195195
nOpenGroups int // the number of groups opened in preformattedAttrs
196-
mu sync.Mutex
196+
mu *sync.Mutex
197197
w io.Writer
198198
}
199199

@@ -207,6 +207,7 @@ func (h *commonHandler) clone() *commonHandler {
207207
groups: slices.Clip(h.groups),
208208
nOpenGroups: h.nOpenGroups,
209209
w: h.w,
210+
mu: h.mu, // mutex shared among all clones of this handler
210211
}
211212
}
212213

‎src/log/slog/handler_test.go‎

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"slices"
1717
"strconv"
1818
"strings"
19+
"sync"
1920
"testing"
2021
"time"
2122
)
@@ -106,6 +107,52 @@ func TestDefaultHandle(t *testing.T) {
106107
}
107108
}
108109

110+
func TestConcurrentWrites(t *testing.T) {
111+
ctx := context.Background()
112+
count := 1000
113+
for _, handlerType := range []string{"text", "json"} {
114+
t.Run(handlerType, func(t *testing.T) {
115+
var buf bytes.Buffer
116+
var h Handler
117+
switch handlerType {
118+
case "text":
119+
h = NewTextHandler(&buf, nil)
120+
case "json":
121+
h = NewJSONHandler(&buf, nil)
122+
default:
123+
t.Fatalf("unexpected handlerType %q", handlerType)
124+
}
125+
sub1 := h.WithAttrs([]Attr{Bool("sub1", true)})
126+
sub2 := h.WithAttrs([]Attr{Bool("sub2", true)})
127+
var wg sync.WaitGroup
128+
for i := 0; i < count; i++ {
129+
sub1Record := NewRecord(time.Time{}, LevelInfo, "hello from sub1", 0)
130+
sub1Record.AddAttrs(Int("i", i))
131+
sub2Record := NewRecord(time.Time{}, LevelInfo, "hello from sub2", 0)
132+
sub2Record.AddAttrs(Int("i", i))
133+
wg.Add(1)
134+
go func() {
135+
defer wg.Done()
136+
if err := sub1.Handle(ctx, sub1Record); err != nil {
137+
t.Error(err)
138+
}
139+
if err := sub2.Handle(ctx, sub2Record); err != nil {
140+
t.Error(err)
141+
}
142+
}()
143+
}
144+
wg.Wait()
145+
for i := 1; i <= 2; i++ {
146+
want := "hello from sub" + strconv.Itoa(i)
147+
n := strings.Count(buf.String(), want)
148+
if n != count {
149+
t.Fatalf("want %d occurrences of %q, got %d", count, want, n)
150+
}
151+
}
152+
})
153+
}
154+
}
155+
109156
// Verify the common parts of TextHandler and JSONHandler.
110157
func TestJSONAndTextHandlers(t *testing.T) {
111158
// remove all Attrs

‎src/log/slog/json_handler.go‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"io"
1414
"log/slog/internal/buffer"
1515
"strconv"
16+
"sync"
1617
"time"
1718
"unicode/utf8"
1819
)
@@ -35,6 +36,7 @@ func NewJSONHandler(w io.Writer, opts *HandlerOptions) *JSONHandler {
3536
json: true,
3637
w: w,
3738
opts: *opts,
39+
mu: &sync.Mutex{},
3840
},
3941
}
4042
}

‎src/log/slog/text_handler.go‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"io"
1212
"reflect"
1313
"strconv"
14+
"sync"
1415
"unicode"
1516
"unicode/utf8"
1617
)
@@ -33,6 +34,7 @@ func NewTextHandler(w io.Writer, opts *HandlerOptions) *TextHandler {
3334
json: false,
3435
w: w,
3536
opts: *opts,
37+
mu: &sync.Mutex{},
3638
},
3739
}
3840
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /