Fixes #61696
Change-Id: I0a31afd3bc433fc84280d56f2798bda10da61eba
GitHub-Last-Rev:
17bedc864f1685178a42b59f7083677a6124f831
GitHub-Pull-Request: golang/go#61702
Reviewed-on: https://go-review.googlesource.com/c/go/+/515015
Auto-Submit: Bryan Mills <bcmills@google.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Reviewed-by: qiulaidongfeng <2645477756@qq.com>
Reviewed-by: Bryan Mills <bcmills@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
--- /dev/null
+pkg sync, method (*Map) Clear() #61696
_, _ = m.Swap(key, value)
}
+// Clear deletes all the keys.
+func (m *Map) Clear() {
+ read := m.loadReadOnly()
+ if len(read.m) == 0 && !read.amended {
+ // Avoid allocating a new readOnly when the map is already clear.
+ return
+ }
+
+ m.mu.Lock()
+ defer m.mu.Unlock()
+
+ read = m.loadReadOnly()
+ if len(read.m) > 0 || read.amended {
+ m.read.Store(&readOnly{})
+ }
+
+ clear(m.dirty)
+ m.misses = 0 // Don't immediately promote the newly-cleared dirty map on the next operation
+}
+
// tryCompareAndSwap compare the entry with the given old value and swaps
// it with a new value if the entry is equal to the old value, and the entry
// has not been expunged.
},
})
}
+
+func BenchmarkClear(b *testing.B) {
+ benchMap(b, bench{
+ perG: func(b *testing.B, pb *testing.PB, i int, m mapInterface) {
+ for ; pb.Next(); i++ {
+ k, v := i%256, i%256
+ m.Clear()
+ m.Store(k, v)
+ }
+ },
+ })
+}
// mapInterface is the interface Map implements.
type mapInterface interface {
- Load(any) (any, bool)
+ Load(key any) (value any, ok bool)
Store(key, value any)
LoadOrStore(key, value any) (actual any, loaded bool)
LoadAndDelete(key any) (value any, loaded bool)
CompareAndSwap(key, old, new any) (swapped bool)
CompareAndDelete(key, old any) (deleted bool)
Range(func(key, value any) (shouldContinue bool))
+ Clear()
}
var (
}
}
+func (m *RWMutexMap) Clear() {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+
+ clear(m.dirty)
+}
+
// DeepCopyMap is an implementation of mapInterface using a Mutex and
// atomic.Value. It makes deep copies of the map on every write to avoid
// acquiring the Mutex in Load.
}
return dirty
}
+
+func (m *DeepCopyMap) Clear() {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+
+ m.clean.Store((map[any]any)(nil))
+}
opSwap = mapOp("Swap")
opCompareAndSwap = mapOp("CompareAndSwap")
opCompareAndDelete = mapOp("CompareAndDelete")
+ opClear = mapOp("Clear")
)
var mapOps = [...]mapOp{
opSwap,
opCompareAndSwap,
opCompareAndDelete,
+ opClear,
}
// mapCall is a quick.Generator for calls on mapInterface.
}
}
return nil, false
+ case opClear:
+ m.Clear()
+ return nil, false
default:
panic("invalid mapOp")
}
t.Errorf("AllocsPerRun of m.Range = %v; want 0", allocs)
}
}
+
+// TestConcurrentClear tests concurrent behavior of sync.Map properties to ensure no data races.
+// Checks for proper synchronization between Clear, Store, Load operations.
+func TestConcurrentClear(t *testing.T) {
+ var m sync.Map
+
+ wg := sync.WaitGroup{}
+ wg.Add(30) // 10 goroutines for writing, 10 goroutines for reading, 10 goroutines for waiting
+
+ // Writing data to the map concurrently
+ for i := 0; i < 10; i++ {
+ go func(k, v int) {
+ defer wg.Done()
+ m.Store(k, v)
+ }(i, i*10)
+ }
+
+ // Reading data from the map concurrently
+ for i := 0; i < 10; i++ {
+ go func(k int) {
+ defer wg.Done()
+ if value, ok := m.Load(k); ok {
+ t.Logf("Key: %v, Value: %v\n", k, value)
+ } else {
+ t.Logf("Key: %v not found\n", k)
+ }
+ }(i)
+ }
+
+ // Clearing data from the map concurrently
+ for i := 0; i < 10; i++ {
+ go func() {
+ defer wg.Done()
+ m.Clear()
+ }()
+ }
+
+ wg.Wait()
+
+ m.Clear()
+
+ m.Range(func(k, v any) bool {
+ t.Errorf("after Clear, Map contains (%v, %v); expected to be empty", k, v)
+
+ return true
+ })
+}
+
+func TestMapClearNoAllocations(t *testing.T) {
+ testenv.SkipIfOptimizationOff(t)
+ var m sync.Map
+ allocs := testing.AllocsPerRun(10, func() {
+ m.Clear()
+ })
+ if allocs > 0 {
+ t.Errorf("AllocsPerRun of m.Clear = %v; want 0", allocs)
+ }
+}