blob: 827791a78dc7b8d086338bc3e14adc87d3133ad0 [file] [log] [blame]
// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build race
package persistent
import (
"context"
"maps"
"testing"
"time"
"golang.org/x/sync/errgroup"
)
// TestConcurrency exercises concurrent map access.
// It doesn't assert anything, but it runs under the race detector.
func TestConcurrency(t *testing.T) {
ctx, cancel := context.WithTimeout(t.Context(), 5*time.Second)
defer cancel()
var orig Map[int, int] // maps subset of [0-10] to itself (values aren't interesting)
for i := range 10 {
orig.Set(i, i, func(k, v any) { /* just for good measure*/ })
}
g, ctx := errgroup.WithContext(ctx)
const N = 10 // concurrency level
g.SetLimit(N)
for range N {
g.Go(func() error {
// Each thread has its own clone of the original,
// sharing internal structures. Each map is accessed
// only by a single thread; the shared data is immutable.
m := orig.Clone()
// Run until the timeout.
for ctx.Err() == nil {
for i := range 1000 {
key := i % 10
switch {
case i%2 == 0:
_, _ = m.Get(key)
case i%11 == 0:
m.Set(key, key, func(key, value any) {})
case i%13 == 0:
_ = maps.Collect(m.All())
case i%17 == 0:
_ = m.Delete(key)
case i%19 == 0:
_ = m.Keys()
case i%31 == 0:
_ = m.String()
case i%23 == 0:
_ = m.Clone()
}
// Don't call m.Clear(), as it would
// disentangle the various maps from each other.
}
}
return nil
})
}
g.Wait() // no errors
}