Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 0 additions & 12 deletions pkg/buffer/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,6 @@ package(
licenses = ["notice"],
)

go_template_instance(
name = "chunk_refs",
out = "chunk_refs.go",
package = "buffer",
prefix = "chunk",
template = "//pkg/refs:refs_template",
types = {
"T": "chunk",
},
)

go_template_instance(
name = "view_list",
out = "view_list.go",
Expand All @@ -35,7 +24,6 @@ go_library(
"buffer.go",
"buffer_state.go",
"chunk.go",
"chunk_refs.go",
"view.go",
"view_list.go",
"view_unsafe.go",
Expand Down
4 changes: 4 additions & 0 deletions pkg/buffer/chunk.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"fmt"

"gvisor.dev/gvisor/pkg/bits"
"gvisor.dev/gvisor/pkg/refs"
"gvisor.dev/gvisor/pkg/sync"
)

Expand Down Expand Up @@ -78,6 +79,9 @@ type chunk struct {
data []byte
}

// +stateify transparent
type chunkRefs struct{ refs.Refs[chunk] }

func newChunk(size int) *chunk {
var c *chunk
if size > MaxChunkSize {
Expand Down
2 changes: 1 addition & 1 deletion pkg/buffer/view.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ func (v *View) Reset() {
}

func (v *View) sharesChunk() bool {
return v.chunk.refCount.Load() > 1
return v.chunk.ReadRefs() > 1
}

// Full indicates the chunk is full.
Expand Down
8 changes: 4 additions & 4 deletions pkg/buffer/view_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,12 @@ func TestClone(t *testing.T) {
if orig.chunk != clone.chunk {
t.Errorf("orig.Clone().chunk = %p, want %p", clone.chunk, orig.chunk)
}
if orig.chunk.refCount.Load() != 2 {
t.Errorf("got orig.chunk.chunkRefs.Load() = %d, want 2", orig.chunk.refCount.Load())
if want, got := int64(2), orig.chunk.ReadRefs(); got != want {
t.Errorf("got orig.chunk.ReadRefs() = %d, want %d", got, want)
}
orig.Release()
if clone.chunk.refCount.Load() != 1 {
t.Errorf("got clone.chunk.chunkRefs.Load() = %d, want 1", clone.chunk.refCount.Load())
if want, got := int64(1), clone.chunk.ReadRefs(); got != want {
t.Errorf("got clone.chunk.ReadRefs() = %d, want %d", got, want)
}
clone.Release()
}
Expand Down
48 changes: 0 additions & 48 deletions pkg/lisafs/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -7,50 +7,6 @@ package(
licenses = ["notice"],
)

go_template_instance(
name = "control_fd_refs",
out = "control_fd_refs.go",
package = "lisafs",
prefix = "controlFD",
template = "//pkg/refs:refs_template",
types = {
"T": "ControlFD",
},
)

go_template_instance(
name = "open_fd_refs",
out = "open_fd_refs.go",
package = "lisafs",
prefix = "openFD",
template = "//pkg/refs:refs_template",
types = {
"T": "OpenFD",
},
)

go_template_instance(
name = "bound_socket_fd_refs",
out = "bound_socket_fd_refs.go",
package = "lisafs",
prefix = "boundSocketFD",
template = "//pkg/refs:refs_template",
types = {
"T": "BoundSocketFD",
},
)

go_template_instance(
name = "node_fd_refs",
out = "node_fd_refs.go",
package = "lisafs",
prefix = "node",
template = "//pkg/refs:refs_template",
types = {
"T": "Node",
},
)

go_template_instance(
name = "control_fd_list",
out = "control_fd_list.go",
Expand Down Expand Up @@ -78,22 +34,18 @@ go_template_instance(
go_library(
name = "lisafs",
srcs = [
"bound_socket_fd_refs.go",
"channel.go",
"client.go",
"client_file.go",
"communicator.go",
"connection.go",
"control_fd_list.go",
"control_fd_refs.go",
"fd.go",
"handlers.go",
"lisafs.go",
"message.go",
"node.go",
"node_fd_refs.go",
"open_fd_list.go",
"open_fd_refs.go",
"sample_message.go",
"server.go",
"sock.go",
Expand Down
9 changes: 9 additions & 0 deletions pkg/lisafs/fd.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,15 @@ type genericFD interface {
refs.RefCounter
}

// +stateify transparent
type controlFDRefs struct{ refs.Refs[ControlFD] }

// +stateify transparent
type openFDRefs struct{ refs.Refs[OpenFD] }

// +stateify transparent
type boundSocketFDRefs struct{ refs.Refs[BoundSocketFD] }

// A ControlFD is the gateway to the backing filesystem tree node. It is an
// unusual concept. This exists to provide a safe way to do path-based
// operations on the file. It performs operations that can modify the
Expand Down
4 changes: 4 additions & 0 deletions pkg/lisafs/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"gvisor.dev/gvisor/pkg/atomicbitops"
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/fspath"
"gvisor.dev/gvisor/pkg/refs"
"gvisor.dev/gvisor/pkg/sync"
)

Expand Down Expand Up @@ -101,6 +102,9 @@ type Node struct {
dynamicChildren map[string]*Node
}

// +stateify transparent
type nodeRefs struct{ refs.Refs[Node] }

// DecRef implements refs.RefCounter.DecRef. Note that the context
// parameter should never be used. It exists solely to comply with the
// refs.RefCounter interface.
Expand Down
20 changes: 1 addition & 19 deletions pkg/refs/BUILD
Original file line number Diff line number Diff line change
@@ -1,34 +1,16 @@
load("//tools:defs.bzl", "go_library")
load("//tools/go_generics:defs.bzl", "go_template")

package(
default_applicable_licenses = ["//:license"],
licenses = ["notice"],
)

go_template(
name = "refs_template",
srcs = [
"refs_template.go",
],
opt_consts = [
"enableLogging",
],
types = [
"T",
],
visibility = ["//:sandbox"],
deps = [
"//pkg/log",
"//pkg/refs",
],
)

go_library(
name = "refs",
srcs = [
"refcounter.go",
"refs_map.go",
"refs_template.go",
],
visibility = ["//:sandbox"],
deps = [
Expand Down
27 changes: 13 additions & 14 deletions pkg/refs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ appropriately. For example, the filesystem has many reference-counted objects
object persists while anything holds a reference on it and is destroyed once all
references are dropped.

We provide a template in `refs_template.go` that can be applied to most objects
in need of reference counting. It contains a simple `Refs` struct that can be
incremented and decremented, and once the reference count reaches zero, a
destructor can be called. Note that there are some objects (e.g. `gofer.dentry`,
`overlay.dentry`) that should not immediately be destroyed upon reaching zero
references; in these cases, this template cannot be applied.
We provide a generic `Refs` type in `refs_template.go` that can be applied to
most objects in need of reference counting. It contains a simple `Refs` struct
that can be incremented and decremented, and once the reference count reaches
zero, a destructor can be called. Note that there are some objects (e.g.
`gofer.dentry`, `overlay.dentry`) that should not immediately be destroyed upon
reaching zero references; in these cases, this helper cannot be applied.

# Reference Checking

Expand Down Expand Up @@ -44,9 +44,9 @@ report everything left in the map as having leaked. Leak-checking objects
implement the `CheckedObject` interface, which allows us to print informative
warnings for each of the leaked objects.

Leak checking is provided by `refs_template`, but objects that do not use the
template will also need to implement `CheckedObject` and be manually
registered/unregistered from the map in order to be checked.
Leak checking is provided by `Refs`, but objects that do not use it will also
need to implement `CheckedObject` and be manually registered/unregistered from
the map in order to be checked.

Note that leak checking affects performance and memory usage, so it should only
be enabled in testing environments.
Expand All @@ -56,11 +56,10 @@ be enabled in testing environments.
Even with the checks described above, it can be difficult to track down the
exact source of a reference counting error. The error may occur far before it is
discovered (for instance, a missing `IncRef` may not be discovered until a
future `DecRef` makes the count negative). To aid in debugging, `refs_template`
provides the `enableLogging` option to log every `IncRef`, `DecRef`, and leak
future `DecRef` makes the count negative). To aid in debugging, `Refs` can be
instantiated with `refs.LoggingEnabled` to log every `IncRef`, `DecRef`, and leak
check registration/unregistration, along with the object address and a call
stack. This allows us to search a log for all of the changes to a particular
object's reference count, which makes it much easier to identify the absent or
extraneous operation(s). The reference-counted objects that do not use
`refs_template` also provide logging, and others defined in the future should do
so as well.
extraneous operation(s). The reference-counted objects that do not use `Refs`
also provide logging, and others defined in the future should do so as well.
4 changes: 2 additions & 2 deletions pkg/refs/refcounter.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ type RefCounter interface {
// IncRef increments the reference counter on the object.
IncRef()

// DecRef decrements the object's reference count. Users of refs_template.Refs
// may specify a destructor to be called once the reference count reaches zero.
// DecRef decrements the object's reference count. Users of Refs may specify
// a destructor to be called once the reference count reaches zero.
DecRef(ctx context.Context)
}

Expand Down
Loading
Loading