summaryrefslogtreecommitdiffhomepage
path: root/pkg/refsvfs2
diff options
context:
space:
mode:
authorDean Deng <deandeng@google.com>2020-11-09 08:30:29 -0800
committergVisor bot <gvisor-bot@google.com>2020-11-09 08:33:17 -0800
commit0fb5353e45f166460d5846576c20479072207a06 (patch)
treef88e4472c07f56e4d34b14743e1a3afaafab62e1 /pkg/refsvfs2
parent78cce3a46b953cab00731f8afacf7e9e7f4dc751 (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.go38
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 {