github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/refsvfs2/refs_template.go (about) 1 // Copyright 2020 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package refs_template defines a template that can be used by reference 16 // counted objects. The template comes with leak checking capabilities. 17 package refs_template 18 19 import ( 20 "fmt" 21 "sync/atomic" 22 23 "github.com/SagerNet/gvisor/pkg/refsvfs2" 24 ) 25 26 // enableLogging indicates whether reference-related events should be logged (with 27 // stack traces). This is false by default and should only be set to true for 28 // debugging purposes, as it can generate an extremely large amount of output 29 // and drastically degrade performance. 30 const enableLogging = false 31 32 // T is the type of the reference counted object. It is only used to customize 33 // debug output when leak checking. 34 type T interface{} 35 36 // obj is used to customize logging. Note that we use a pointer to T so that 37 // we do not copy the entire object when passed as a format parameter. 38 var obj *T 39 40 // Refs implements refs.RefCounter. It keeps a reference count using atomic 41 // operations and calls the destructor when the count reaches zero. 42 // 43 // NOTE: Do not introduce additional fields to the Refs struct. It is used by 44 // many filesystem objects, and we want to keep it as small as possible (i.e., 45 // the same size as using an int64 directly) to avoid taking up extra cache 46 // space. In general, this template should not be extended at the cost of 47 // performance. If it does not offer enough flexibility for a particular object 48 // (example: b/187877947), we should implement the RefCounter/CheckedObject 49 // interfaces manually. 50 // 51 // +stateify savable 52 type Refs struct { 53 // refCount is composed of two fields: 54 // 55 // [32-bit speculative references]:[32-bit real references] 56 // 57 // Speculative references are used for TryIncRef, to avoid a CompareAndSwap 58 // loop. See IncRef, DecRef and TryIncRef for details of how these fields are 59 // used. 60 refCount int64 61 } 62 63 // InitRefs initializes r with one reference and, if enabled, activates leak 64 // checking. 65 func (r *Refs) InitRefs() { 66 atomic.StoreInt64(&r.refCount, 1) 67 refsvfs2.Register(r) 68 } 69 70 // RefType implements refsvfs2.CheckedObject.RefType. 71 func (r *Refs) RefType() string { 72 return fmt.Sprintf("%T", obj)[1:] 73 } 74 75 // LeakMessage implements refsvfs2.CheckedObject.LeakMessage. 76 func (r *Refs) LeakMessage() string { 77 return fmt.Sprintf("[%s %p] reference count of %d instead of 0", r.RefType(), r, r.ReadRefs()) 78 } 79 80 // LogRefs implements refsvfs2.CheckedObject.LogRefs. 81 func (r *Refs) LogRefs() bool { 82 return enableLogging 83 } 84 85 // ReadRefs returns the current number of references. The returned count is 86 // inherently racy and is unsafe to use without external synchronization. 87 func (r *Refs) ReadRefs() int64 { 88 return atomic.LoadInt64(&r.refCount) 89 } 90 91 // IncRef implements refs.RefCounter.IncRef. 92 // 93 //go:nosplit 94 func (r *Refs) IncRef() { 95 v := atomic.AddInt64(&r.refCount, 1) 96 if enableLogging { 97 refsvfs2.LogIncRef(r, v) 98 } 99 if v <= 1 { 100 panic(fmt.Sprintf("Incrementing non-positive count %p on %s", r, r.RefType())) 101 } 102 } 103 104 // TryIncRef implements refs.RefCounter.TryIncRef. 105 // 106 // To do this safely without a loop, a speculative reference is first acquired 107 // on the object. This allows multiple concurrent TryIncRef calls to distinguish 108 // other TryIncRef calls from genuine references held. 109 // 110 //go:nosplit 111 func (r *Refs) TryIncRef() bool { 112 const speculativeRef = 1 << 32 113 if v := atomic.AddInt64(&r.refCount, speculativeRef); int32(v) == 0 { 114 // This object has already been freed. 115 atomic.AddInt64(&r.refCount, -speculativeRef) 116 return false 117 } 118 119 // Turn into a real reference. 120 v := atomic.AddInt64(&r.refCount, -speculativeRef+1) 121 if enableLogging { 122 refsvfs2.LogTryIncRef(r, v) 123 } 124 return true 125 } 126 127 // DecRef implements refs.RefCounter.DecRef. 128 // 129 // Note that speculative references are counted here. Since they were added 130 // prior to real references reaching zero, they will successfully convert to 131 // real references. In other words, we see speculative references only in the 132 // following case: 133 // 134 // A: TryIncRef [speculative increase => sees non-negative references] 135 // B: DecRef [real decrease] 136 // A: TryIncRef [transform speculative to real] 137 // 138 //go:nosplit 139 func (r *Refs) DecRef(destroy func()) { 140 v := atomic.AddInt64(&r.refCount, -1) 141 if enableLogging { 142 refsvfs2.LogDecRef(r, v) 143 } 144 switch { 145 case v < 0: 146 panic(fmt.Sprintf("Decrementing non-positive ref count %p, owned by %s", r, r.RefType())) 147 148 case v == 0: 149 refsvfs2.Unregister(r) 150 // Call the destructor. 151 if destroy != nil { 152 destroy() 153 } 154 } 155 } 156 157 func (r *Refs) afterLoad() { 158 if r.ReadRefs() > 0 { 159 refsvfs2.Register(r) 160 } 161 }