diff options
author | Dean Deng <deandeng@google.com> | 2020-11-09 08:30:29 -0800 |
---|---|---|
committer | gVisor bot <gvisor-bot@google.com> | 2020-11-09 08:33:17 -0800 |
commit | 0fb5353e45f166460d5846576c20479072207a06 (patch) | |
tree | f88e4472c07f56e4d34b14743e1a3afaafab62e1 /pkg/refsvfs2 | |
parent | 78cce3a46b953cab00731f8afacf7e9e7f4dc751 (diff) |
Initialize references with a value of 1.
This lets us avoid treating a value of 0 as one reference. All references
using the refsvfs2 template must call InitRefs() before the reference is
incremented/decremented, or else a panic will occur. Therefore, it should be
pretty easy to identify missing InitRef calls during testing.
Updates #1486.
PiperOrigin-RevId: 341411151
Diffstat (limited to 'pkg/refsvfs2')
-rw-r--r-- | pkg/refsvfs2/refs_template.go | 38 |
1 files changed, 22 insertions, 16 deletions
diff --git a/pkg/refsvfs2/refs_template.go b/pkg/refsvfs2/refs_template.go index 8f50b4ee6..f64b6c6ae 100644 --- a/pkg/refsvfs2/refs_template.go +++ b/pkg/refsvfs2/refs_template.go @@ -13,10 +13,7 @@ // limitations under the License. // Package refs_template defines a template that can be used by reference -// counted objects. The "owner" template parameter is used in log messages to -// indicate the type of reference-counted object that exhibited a reference -// leak. As a result, structs that are embedded in other structs should not use -// this template, since it will make tracking down leaks more difficult. +// counted objects. package refs_template import ( @@ -43,9 +40,6 @@ var obj *T // Refs implements refs.RefCounter. It keeps a reference count using atomic // operations and calls the destructor when the count reaches zero. // -// Note that the number of references is actually refCount + 1 so that a default -// zero-value Refs object contains one reference. -// // +stateify savable type Refs struct { // refCount is composed of two fields: @@ -58,6 +52,13 @@ type Refs struct { refCount int64 } +// InitRefs initializes r with one reference and, if enabled, activates leak +// checking. +func (r *Refs) InitRefs() { + atomic.StoreInt64(&r.refCount, 1) + refsvfs2.Register(r) +} + // RefType implements refsvfs2.CheckedObject.RefType. func (r *Refs) RefType() string { return fmt.Sprintf("%T", obj)[1:] @@ -81,8 +82,7 @@ func (r *Refs) EnableLeakCheck() { // ReadRefs returns the current number of references. The returned count is // inherently racy and is unsafe to use without external synchronization. func (r *Refs) ReadRefs() int64 { - // Account for the internal -1 offset on refcounts. - return atomic.LoadInt64(&r.refCount) + 1 + return atomic.LoadInt64(&r.refCount) } // IncRef implements refs.RefCounter.IncRef. @@ -90,8 +90,10 @@ func (r *Refs) ReadRefs() int64 { //go:nosplit func (r *Refs) IncRef() { v := atomic.AddInt64(&r.refCount, 1) - refsvfs2.LogIncRef(r, v+1) - if v <= 0 { + if enableLogging { + refsvfs2.LogIncRef(r, v) + } + if v <= 1 { panic(fmt.Sprintf("Incrementing non-positive count %p on %s", r, r.RefType())) } } @@ -105,7 +107,7 @@ func (r *Refs) IncRef() { //go:nosplit func (r *Refs) TryIncRef() bool { const speculativeRef = 1 << 32 - if v := atomic.AddInt64(&r.refCount, speculativeRef); int32(v) < 0 { + if v := atomic.AddInt64(&r.refCount, speculativeRef); int32(v) == 0 { // This object has already been freed. atomic.AddInt64(&r.refCount, -speculativeRef) return false @@ -113,7 +115,9 @@ func (r *Refs) TryIncRef() bool { // Turn into a real reference. v := atomic.AddInt64(&r.refCount, -speculativeRef+1) - refsvfs2.LogTryIncRef(r, v+1) + if enableLogging { + refsvfs2.LogTryIncRef(r, v) + } return true } @@ -131,12 +135,14 @@ func (r *Refs) TryIncRef() bool { //go:nosplit func (r *Refs) DecRef(destroy func()) { v := atomic.AddInt64(&r.refCount, -1) - refsvfs2.LogDecRef(r, v+1) + if enableLogging { + refsvfs2.LogDecRef(r, v+1) + } switch { - case v < -1: + case v < 0: panic(fmt.Sprintf("Decrementing non-positive ref count %p, owned by %s", r, r.RefType())) - case v == -1: + case v == 0: refsvfs2.Unregister(r) // Call the destructor. if destroy != nil { |