Skip to content

Commit a70d348

Browse files
adonovangopherbot
authored andcommitted
gopls/internal/util/persistent: add concurrency test
It didn't find any problems. Updates golang/go#72931 Change-Id: Idb65548480af1fd6777dffdcc0e6c6e89b5a06f5 Reviewed-on: https://go-review.googlesource.com/c/tools/+/659015 Auto-Submit: Alan Donovan <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Robert Findley <[email protected]>
1 parent 7042bab commit a70d348

File tree

2 files changed

+68
-0
lines changed

2 files changed

+68
-0
lines changed

gopls/internal/util/persistent/map.go

+2
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,8 @@ func (pm *Map[K, V]) SetAll(other *Map[K, V]) {
203203
// Set updates the value associated with the specified key.
204204
// If release is non-nil, it will be called with entry's key and value once the
205205
// key is no longer contained in the map or any clone.
206+
//
207+
// TODO(adonovan): fix release, which has the wrong type.
206208
func (pm *Map[K, V]) Set(key K, value V, release func(key, value any)) {
207209
first := pm.root
208210
second := newNodeWithRef(key, value, release)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// Copyright 2025 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
//go:build race
6+
7+
package persistent
8+
9+
import (
10+
"context"
11+
"maps"
12+
"testing"
13+
"time"
14+
15+
"golang.org/x/sync/errgroup"
16+
)
17+
18+
// TestConcurrency exercises concurrent map access.
19+
// It doesn't assert anything, but it runs under the race detector.
20+
func TestConcurrency(t *testing.T) {
21+
ctx, cancel := context.WithTimeout(t.Context(), 5*time.Second)
22+
defer cancel()
23+
var orig Map[int, int] // maps subset of [0-10] to itself (values aren't interesting)
24+
for i := range 10 {
25+
orig.Set(i, i, func(k, v any) { /* just for good measure*/ })
26+
}
27+
g, ctx := errgroup.WithContext(ctx)
28+
const N = 10 // concurrency level
29+
g.SetLimit(N)
30+
for range N {
31+
g.Go(func() error {
32+
// Each thread has its own clone of the original,
33+
// sharing internal structures. Each map is accessed
34+
// only by a single thread; the shared data is immutable.
35+
m := orig.Clone()
36+
37+
// Run until the timeout.
38+
for ctx.Err() == nil {
39+
for i := range 1000 {
40+
key := i % 10
41+
42+
switch {
43+
case i%2 == 0:
44+
_, _ = m.Get(key)
45+
case i%11 == 0:
46+
m.Set(key, key, func(key, value any) {})
47+
case i%13 == 0:
48+
_ = maps.Collect(m.All())
49+
case i%17 == 0:
50+
_ = m.Delete(key)
51+
case i%19 == 0:
52+
_ = m.Keys()
53+
case i%31 == 0:
54+
_ = m.String()
55+
case i%23 == 0:
56+
_ = m.Clone()
57+
}
58+
// Don't call m.Clear(), as it would
59+
// disentangle the various maps from each other.
60+
}
61+
}
62+
return nil
63+
})
64+
}
65+
g.Wait() // no errors
66+
}

0 commit comments

Comments
 (0)