Source file src/unique/handle.go
1 // Copyright 2024 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package unique 6 7 import ( 8 "internal/abi" 9 isync "internal/sync" 10 "unsafe" 11 ) 12 13 var zero uintptr 14 15 // Handle is a globally unique identity for some value of type T. 16 // 17 // Two handles compare equal exactly if the two values used to create the handles 18 // would have also compared equal. The comparison of two handles is trivial and 19 // typically much more efficient than comparing the values used to create them. 20 type Handle[T comparable] struct { 21 value *T 22 } 23 24 // Value returns a shallow copy of the T value that produced the Handle. 25 // Value is safe for concurrent use by multiple goroutines. 26 func (h Handle[T]) Value() T { 27 return *h.value 28 } 29 30 // Make returns a globally unique handle for a value of type T. Handles 31 // are equal if and only if the values used to produce them are equal. 32 // Make is safe for concurrent use by multiple goroutines. 33 func Make[T comparable](value T) Handle[T] { 34 // Find the map for type T. 35 typ := abi.TypeFor[T]() 36 if typ.Size() == 0 { 37 return Handle[T]{(*T)(unsafe.Pointer(&zero))} 38 } 39 ma, ok := uniqueMaps.Load(typ) 40 if !ok { 41 m := &uniqueMap[T]{canonMap: newCanonMap[T](), cloneSeq: makeCloneSeq(typ)} 42 ma, _ = uniqueMaps.LoadOrStore(typ, m) 43 } 44 m := ma.(*uniqueMap[T]) 45 46 // Find the value in the map. 47 ptr := m.Load(value) 48 if ptr == nil { 49 // Insert a new value into the map. 50 ptr = m.LoadOrStore(clone(value, &m.cloneSeq)) 51 } 52 return Handle[T]{ptr} 53 } 54 55 // uniqueMaps is an index of type-specific concurrent maps used for unique.Make. 56 // 57 // The two-level map might seem odd at first since the HashTrieMap could have "any" 58 // as its key type, but the issue is escape analysis. We do not want to force lookups 59 // to escape the argument, and using a type-specific map allows us to avoid that where 60 // possible (for example, for strings and plain-ol'-data structs). We also get the 61 // benefit of not cramming every different type into a single map, but that's certainly 62 // not enough to outweigh the cost of two map lookups. What is worth it though, is saving 63 // on those allocations. 64 var uniqueMaps isync.HashTrieMap[*abi.Type, any] // any is always a *uniqueMap[T]. 65 66 type uniqueMap[T comparable] struct { 67 *canonMap[T] 68 cloneSeq 69 } 70