Skip to content

Instantly share code, notes, and snippets.

@bradfitz
Last active February 17, 2025 03:07
Show Gist options
  • Save bradfitz/901f14f9c6dfd5c9344673a2c9478932 to your computer and use it in GitHub Desktop.
Save bradfitz/901f14f9c6dfd5c9344673a2c9478932 to your computer and use it in GitHub Desktop.
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 {
@bradfitz
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment