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  

View as plain text