aboutsummaryrefslogtreecommitdiff
path: root/src/sync
diff options
context:
space:
mode:
authorJoe Tsai <joetsai@digital-static.net>2022-08-26 15:10:46 -0700
committerGopher Robot <gobot@golang.org>2022-08-29 20:09:02 +0000
commitb0144b384318e17b7b4facdbbd2fff3712085eab (patch)
tree396fd030844f124b0b936a360d0a491d7021c8e3 /src/sync
parent36d1f236610a436bcbceca11e040a65593f1f253 (diff)
downloadgo-b0144b384318e17b7b4facdbbd2fff3712085eab.tar.xz
sync: switch Map to use atomic.Pointer
There was no noticeable change in performance. Change-Id: I9c57bf836c8b6066e0620afb3d536ce99e4b9d87 Reviewed-on: https://go-review.googlesource.com/c/go/+/426074 Reviewed-by: Bryan Mills <bcmills@google.com> Reviewed-by: Heschi Kreinick <heschi@google.com> Auto-Submit: Joseph Tsai <joetsai@digital-static.net> Run-TryBot: Joseph Tsai <joetsai@digital-static.net> TryBot-Result: Gopher Robot <gobot@golang.org>
Diffstat (limited to 'src/sync')
-rw-r--r--src/sync/map.go39
1 files changed, 23 insertions, 16 deletions
diff --git a/src/sync/map.go b/src/sync/map.go
index ec529e056b..bde78b8878 100644
--- a/src/sync/map.go
+++ b/src/sync/map.go
@@ -43,7 +43,7 @@ type Map struct {
// Entries stored in read may be updated concurrently without mu, but updating
// a previously-expunged entry requires that the entry be copied to the dirty
// map and unexpunged with mu held.
- read atomic.Value // readOnly
+ read atomic.Pointer[readOnly]
// dirty contains the portion of the map's contents that require mu to be
// held. To ensure that the dirty map can be promoted to the read map quickly,
@@ -104,18 +104,25 @@ func newEntry(i any) *entry {
return &entry{p: unsafe.Pointer(&i)}
}
+func (m *Map) loadReadOnly() readOnly {
+ if p := m.read.Load(); p != nil {
+ return *p
+ }
+ return readOnly{}
+}
+
// Load returns the value stored in the map for a key, or nil if no
// value is present.
// The ok result indicates whether value was found in the map.
func (m *Map) Load(key any) (value any, ok bool) {
- read, _ := m.read.Load().(readOnly)
+ read := m.loadReadOnly()
e, ok := read.m[key]
if !ok && read.amended {
m.mu.Lock()
// Avoid reporting a spurious miss if m.dirty got promoted while we were
// blocked on m.mu. (If further loads of the same key will not miss, it's
// not worth copying the dirty map for this key.)
- read, _ = m.read.Load().(readOnly)
+ read = m.loadReadOnly()
e, ok = read.m[key]
if !ok && read.amended {
e, ok = m.dirty[key]
@@ -142,13 +149,13 @@ func (e *entry) load() (value any, ok bool) {
// Store sets the value for a key.
func (m *Map) Store(key, value any) {
- read, _ := m.read.Load().(readOnly)
+ read := m.loadReadOnly()
if e, ok := read.m[key]; ok && e.tryStore(&value) {
return
}
m.mu.Lock()
- read, _ = m.read.Load().(readOnly)
+ read = m.loadReadOnly()
if e, ok := read.m[key]; ok {
if e.unexpungeLocked() {
// The entry was previously expunged, which implies that there is a
@@ -163,7 +170,7 @@ func (m *Map) Store(key, value any) {
// We're adding the first new key to the dirty map.
// Make sure it is allocated and mark the read-only map as incomplete.
m.dirtyLocked()
- m.read.Store(readOnly{m: read.m, amended: true})
+ m.read.Store(&readOnly{m: read.m, amended: true})
}
m.dirty[key] = newEntry(value)
}
@@ -206,7 +213,7 @@ func (e *entry) storeLocked(i *any) {
// The loaded result is true if the value was loaded, false if stored.
func (m *Map) LoadOrStore(key, value any) (actual any, loaded bool) {
// Avoid locking if it's a clean hit.
- read, _ := m.read.Load().(readOnly)
+ read := m.loadReadOnly()
if e, ok := read.m[key]; ok {
actual, loaded, ok := e.tryLoadOrStore(value)
if ok {
@@ -215,7 +222,7 @@ func (m *Map) LoadOrStore(key, value any) (actual any, loaded bool) {
}
m.mu.Lock()
- read, _ = m.read.Load().(readOnly)
+ read = m.loadReadOnly()
if e, ok := read.m[key]; ok {
if e.unexpungeLocked() {
m.dirty[key] = e
@@ -229,7 +236,7 @@ func (m *Map) LoadOrStore(key, value any) (actual any, loaded bool) {
// We're adding the first new key to the dirty map.
// Make sure it is allocated and mark the read-only map as incomplete.
m.dirtyLocked()
- m.read.Store(readOnly{m: read.m, amended: true})
+ m.read.Store(&readOnly{m: read.m, amended: true})
}
m.dirty[key] = newEntry(value)
actual, loaded = value, false
@@ -274,11 +281,11 @@ func (e *entry) tryLoadOrStore(i any) (actual any, loaded, ok bool) {
// LoadAndDelete deletes the value for a key, returning the previous value if any.
// The loaded result reports whether the key was present.
func (m *Map) LoadAndDelete(key any) (value any, loaded bool) {
- read, _ := m.read.Load().(readOnly)
+ read := m.loadReadOnly()
e, ok := read.m[key]
if !ok && read.amended {
m.mu.Lock()
- read, _ = m.read.Load().(readOnly)
+ read = m.loadReadOnly()
e, ok = read.m[key]
if !ok && read.amended {
e, ok = m.dirty[key]
@@ -329,17 +336,17 @@ func (m *Map) Range(f func(key, value any) bool) {
// present at the start of the call to Range.
// If read.amended is false, then read.m satisfies that property without
// requiring us to hold m.mu for a long time.
- read, _ := m.read.Load().(readOnly)
+ read := m.loadReadOnly()
if read.amended {
// m.dirty contains keys not in read.m. Fortunately, Range is already O(N)
// (assuming the caller does not break out early), so a call to Range
// amortizes an entire copy of the map: we can promote the dirty copy
// immediately!
m.mu.Lock()
- read, _ = m.read.Load().(readOnly)
+ read = m.loadReadOnly()
if read.amended {
read = readOnly{m: m.dirty}
- m.read.Store(read)
+ m.read.Store(&read)
m.dirty = nil
m.misses = 0
}
@@ -362,7 +369,7 @@ func (m *Map) missLocked() {
if m.misses < len(m.dirty) {
return
}
- m.read.Store(readOnly{m: m.dirty})
+ m.read.Store(&readOnly{m: m.dirty})
m.dirty = nil
m.misses = 0
}
@@ -372,7 +379,7 @@ func (m *Map) dirtyLocked() {
return
}
- read, _ := m.read.Load().(readOnly)
+ read := m.loadReadOnly()
m.dirty = make(map[any]*entry, len(read.m))
for k, e := range read.m {
if !e.tryExpungeLocked() {