|  | // Copyright 2020 The Go Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style | 
|  | // license that can be found in the LICENSE file. | 
|  |  | 
|  | // Package intern lets you make smaller comparable values by boxing | 
|  | // a larger comparable value (such as a 16 byte string header) down | 
|  | // into a globally unique 8 byte pointer. | 
|  | // | 
|  | // The globally unique pointers are garbage collected with weak | 
|  | // references and finalizers. This package hides that. | 
|  | package intern | 
|  |  | 
|  | import ( | 
|  | "internal/godebug" | 
|  | "runtime" | 
|  | "sync" | 
|  | "unsafe" | 
|  | ) | 
|  |  | 
|  | // A Value pointer is the handle to an underlying comparable value. | 
|  | // See func Get for how Value pointers may be used. | 
|  | type Value struct { | 
|  | _      [0]func() // prevent people from accidentally using value type as comparable | 
|  | cmpVal any | 
|  | // resurrected is guarded by mu (for all instances of Value). | 
|  | // It is set true whenever v is synthesized from a uintptr. | 
|  | resurrected bool | 
|  | } | 
|  |  | 
|  | // Get returns the comparable value passed to the Get func | 
|  | // that returned v. | 
|  | func (v *Value) Get() any { return v.cmpVal } | 
|  |  | 
|  | // key is a key in our global value map. | 
|  | // It contains type-specialized fields to avoid allocations | 
|  | // when converting common types to empty interfaces. | 
|  | type key struct { | 
|  | s      string | 
|  | cmpVal any | 
|  | // isString reports whether key contains a string. | 
|  | // Without it, the zero value of key is ambiguous. | 
|  | isString bool | 
|  | } | 
|  |  | 
|  | // keyFor returns a key to use with cmpVal. | 
|  | func keyFor(cmpVal any) key { | 
|  | if s, ok := cmpVal.(string); ok { | 
|  | return key{s: s, isString: true} | 
|  | } | 
|  | return key{cmpVal: cmpVal} | 
|  | } | 
|  |  | 
|  | // Value returns a *Value built from k. | 
|  | func (k key) Value() *Value { | 
|  | if k.isString { | 
|  | return &Value{cmpVal: k.s} | 
|  | } | 
|  | return &Value{cmpVal: k.cmpVal} | 
|  | } | 
|  |  | 
|  | var ( | 
|  | // mu guards valMap, a weakref map of *Value by underlying value. | 
|  | // It also guards the resurrected field of all *Values. | 
|  | mu      sync.Mutex | 
|  | valMap  = map[key]uintptr{} // to uintptr(*Value) | 
|  | valSafe = safeMap()         // non-nil in safe+leaky mode | 
|  | ) | 
|  |  | 
|  | // safeMap returns a non-nil map if we're in safe-but-leaky mode, | 
|  | // as controlled by GODEBUG=intern=leaky | 
|  | func safeMap() map[key]*Value { | 
|  | if godebug.Get("intern") == "leaky" { | 
|  | return map[key]*Value{} | 
|  | } | 
|  | return nil | 
|  | } | 
|  |  | 
|  | // Get returns a pointer representing the comparable value cmpVal. | 
|  | // | 
|  | // The returned pointer will be the same for Get(v) and Get(v2) | 
|  | // if and only if v == v2, and can be used as a map key. | 
|  | func Get(cmpVal any) *Value { | 
|  | return get(keyFor(cmpVal)) | 
|  | } | 
|  |  | 
|  | // GetByString is identical to Get, except that it is specialized for strings. | 
|  | // This avoids an allocation from putting a string into an interface{} | 
|  | // to pass as an argument to Get. | 
|  | func GetByString(s string) *Value { | 
|  | return get(key{s: s, isString: true}) | 
|  | } | 
|  |  | 
|  | // We play unsafe games that violate Go's rules (and assume a non-moving | 
|  | // collector). So we quiet Go here. | 
|  | // See the comment below Get for more implementation details. | 
|  | //go:nocheckptr | 
|  | func get(k key) *Value { | 
|  | mu.Lock() | 
|  | defer mu.Unlock() | 
|  |  | 
|  | var v *Value | 
|  | if valSafe != nil { | 
|  | v = valSafe[k] | 
|  | } else if addr, ok := valMap[k]; ok { | 
|  | v = (*Value)(unsafe.Pointer(addr)) | 
|  | v.resurrected = true | 
|  | } | 
|  | if v != nil { | 
|  | return v | 
|  | } | 
|  | v = k.Value() | 
|  | if valSafe != nil { | 
|  | valSafe[k] = v | 
|  | } else { | 
|  | // SetFinalizer before uintptr conversion (theoretical concern; | 
|  | // see https://github.com/go4org/intern/issues/13) | 
|  | runtime.SetFinalizer(v, finalize) | 
|  | valMap[k] = uintptr(unsafe.Pointer(v)) | 
|  | } | 
|  | return v | 
|  | } | 
|  |  | 
|  | func finalize(v *Value) { | 
|  | mu.Lock() | 
|  | defer mu.Unlock() | 
|  | if v.resurrected { | 
|  | // We lost the race. Somebody resurrected it while we | 
|  | // were about to finalize it. Try again next round. | 
|  | v.resurrected = false | 
|  | runtime.SetFinalizer(v, finalize) | 
|  | return | 
|  | } | 
|  | delete(valMap, keyFor(v.cmpVal)) | 
|  | } | 
|  |  | 
|  | // Interning is simple if you don't require that unused values be | 
|  | // garbage collectable. But we do require that; we don't want to be | 
|  | // DOS vector. We do this by using a uintptr to hide the pointer from | 
|  | // the garbage collector, and using a finalizer to eliminate the | 
|  | // pointer when no other code is using it. | 
|  | // | 
|  | // The obvious implementation of this is to use a | 
|  | // map[interface{}]uintptr-of-*interface{}, and set up a finalizer to | 
|  | // delete from the map. Unfortunately, this is racy. Because pointers | 
|  | // are being created in violation of Go's unsafety rules, it's | 
|  | // possible to create a pointer to a value concurrently with the GC | 
|  | // concluding that the value can be collected. There are other races | 
|  | // that break the equality invariant as well, but the use-after-free | 
|  | // will cause a runtime crash. | 
|  | // | 
|  | // To make this work, the finalizer needs to know that no references | 
|  | // have been unsafely created since the finalizer was set up. To do | 
|  | // this, values carry a "resurrected" sentinel, which gets set | 
|  | // whenever a pointer is unsafely created. If the finalizer encounters | 
|  | // the sentinel, it clears the sentinel and delays collection for one | 
|  | // additional GC cycle, by re-installing itself as finalizer. This | 
|  | // ensures that the unsafely created pointer is visible to the GC, and | 
|  | // will correctly prevent collection. | 
|  | // | 
|  | // This technique does mean that interned values that get reused take | 
|  | // at least 3 GC cycles to fully collect (1 to clear the sentinel, 1 | 
|  | // to clean up the unsafe map, 1 to be actually deleted). | 
|  | // | 
|  | // @ianlancetaylor commented in | 
|  | // https://github.com/golang/go/issues/41303#issuecomment-717401656 | 
|  | // that it is possible to implement weak references in terms of | 
|  | // finalizers without unsafe. Unfortunately, the approach he outlined | 
|  | // does not work here, for two reasons. First, there is no way to | 
|  | // construct a strong pointer out of a weak pointer; our map stores | 
|  | // weak pointers, but we must return strong pointers to callers. | 
|  | // Second, and more fundamentally, we must return not just _a_ strong | 
|  | // pointer to callers, but _the same_ strong pointer to callers. In | 
|  | // order to return _the same_ strong pointer to callers, we must track | 
|  | // it, which is exactly what we cannot do with strong pointers. | 
|  | // | 
|  | // See https://github.com/inetaf/netaddr/issues/53 for more | 
|  | // discussion, and https://github.com/go4org/intern/issues/2 for an | 
|  | // illustration of the subtleties at play. |