Last active
February 17, 2025 03:07
-
-
Save bradfitz/901f14f9c6dfd5c9344673a2c9478932 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/src/unique/handle.go b/src/unique/handle.go | |
index abc620f60f..95395a6b85 100644 | |
--- a/src/unique/handle.go | |
+++ b/src/unique/handle.go | |
@@ -10,6 +10,7 @@ import ( | |
"internal/weak" | |
"runtime" | |
"sync" | |
+ "sync/atomic" | |
_ "unsafe" | |
) | |
@@ -63,6 +64,9 @@ func Make[T comparable](value T) Handle[T] { | |
// Check the map. | |
wp, ok := m.Load(value) | |
if !ok { | |
+ // If we're making a new handle (because the Load failed), apply | |
+ // some back pressure and wait for any ongoing cleanup to complete. | |
+ m.awaitCleanup() | |
// Try to insert a new value into the map. | |
k, v := newValue() | |
wp, _ = m.LoadOrStore(k, v) | |
@@ -109,6 +113,23 @@ var ( | |
type uniqueMap[T comparable] struct { | |
*concurrent.HashTrieMap[T, weak.Pointer[T]] | |
cloneSeq | |
+ | |
+ // cleaning is true while the map is being cleaned up. | |
+ // It's always set while holding cleanMu. | |
+ cleaning atomic.Bool | |
+ | |
+ // cleanMu protects cleanCond and is held while cleaning the map. | |
+ cleanMu sync.Mutex | |
+} | |
+ | |
+// awaitCleanup blocks until any ongoing cleanup is finished. | |
+// If there's no ongoing cleanup, it returns immediately | |
+// without acquiring any mutex. | |
+func (m *uniqueMap[T]) awaitCleanup() { | |
+ for m.cleaning.Load() { | |
+ m.cleanMu.Lock() | |
+ m.cleanMu.Unlock() | |
+ } | |
} | |
func addUniqueMap[T comparable](typ *abi.Type) *uniqueMap[T] { | |
@@ -120,11 +141,18 @@ func addUniqueMap[T comparable](typ *abi.Type) *uniqueMap[T] { | |
HashTrieMap: concurrent.NewHashTrieMap[T, weak.Pointer[T]](), | |
cloneSeq: makeCloneSeq(typ), | |
} | |
+ | |
a, loaded := uniqueMaps.LoadOrStore(typ, m) | |
if !loaded { | |
// Add a cleanup function for the new map. | |
cleanupFuncsMu.Lock() | |
cleanupFuncs = append(cleanupFuncs, func() { | |
+ m.cleanMu.Lock() | |
+ defer m.cleanMu.Unlock() | |
+ | |
+ m.cleaning.Store(true) | |
+ defer m.cleaning.Store(false) | |
+ | |
// Delete all the entries whose weak references are nil and clean up | |
// deleted entries. | |
m.All()(func(key T, wp weak.Pointer[T]) bool { |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
for golang/go#71772