Split the ring implementation into its own package.

The ring data structure's requires it's own implementation of Entry. With
the new version you can more easily be part of multiple rings at once. Also,
a user who wants to use ilist probably doesn't need ring as well, so it makes
sense to separate the two.

PiperOrigin-RevId: 563151782
This commit is contained in:
Lucas Manning
2023-09-06 10:54:41 -07:00
committed by gVisor bot
parent c584a1a435
commit d30c39364d
8 changed files with 240 additions and 120 deletions
+47
View File
@@ -57,3 +57,50 @@ go_template(
],
visibility = ["//visibility:public"],
)
go_template(
name = "generic_ring",
srcs = [
"ring.go",
],
opt_types = [
"Container",
],
visibility = ["//visibility:public"],
)
go_template_instance(
name = "test_ring",
out = "test_ring.go",
package = "ring",
prefix = "test",
template = "//pkg/ilist:generic_ring",
types = {
"Container": "*testContainer",
},
)
go_library(
name = "ring",
srcs = [
"interface_ring.go",
],
visibility = ["//visibility:public"],
)
go_template_instance(
name = "interface_ring",
out = "interface_ring.go",
package = "ring",
template = ":generic_ring",
types = {},
)
go_test(
name = "ring_test",
srcs = [
"ring_test.go",
"test_ring.go",
],
library = ":ring",
)
-45
View File
@@ -271,48 +271,3 @@ func (e *Entry) SetNext(elem Element) {
func (e *Entry) SetPrev(elem Element) {
e.prev = elem
}
// RingInit instantiates an Element to be an item in a ring (circularly-linked
// list).
//
//go:nosplit
func RingInit(e Element) {
linker := ElementMapper{}.linkerFor(e)
linker.SetNext(e)
linker.SetPrev(e)
}
// RingAdd adds new to old's ring.
//
//go:nosplit
func RingAdd(old Element, new Element) {
oldLinker := ElementMapper{}.linkerFor(old)
newLinker := ElementMapper{}.linkerFor(new)
next := oldLinker.Next()
prev := old
next.SetPrev(new)
newLinker.SetNext(next)
newLinker.SetPrev(prev)
oldLinker.SetNext(new)
}
// RingRemove removes e from its ring.
//
//go:nosplit
func RingRemove(e Element) {
eLinker := ElementMapper{}.linkerFor(e)
next := eLinker.Next()
prev := eLinker.Prev()
next.SetPrev(prev)
prev.SetNext(next)
RingInit(e)
}
// RingEmpty returns true if there are no other elements in the list.
//
//go:nosplit
func RingEmpty(e Element) bool {
linker := ElementMapper{}.linkerFor(e)
return linker.Next() == e
}
-57
View File
@@ -183,63 +183,6 @@ func TestReset(t *testing.T) {
}
}
func TestRingAdd(t *testing.T) {
e1 := &testEntry{value: 1}
e2 := &testEntry{value: 2}
e3 := &testEntry{value: 3}
RingInit(e1)
RingAdd(e1, e2)
RingAdd(e1, e3)
sum := 0
for e := e1.Next(); e != e1; e = e.Next() {
sum += e.(*testEntry).value
}
if sum != 5 {
t.Errorf("wrong sum: want 5, got %d", sum)
}
}
func TestRingRemove(t *testing.T) {
e1 := &testEntry{value: 1}
e2 := &testEntry{value: 2}
e3 := &testEntry{value: 3}
RingInit(e1)
RingAdd(e1, e2)
RingAdd(e2, e3)
RingRemove(e2)
sum := 0
for e := e1.Next(); e != e1; e = e.Next() {
sum += e.(*testEntry).value
}
if sum != 3 {
t.Errorf("wrong sum: want 3, got %d", sum)
}
}
func TestRingEmpty(t *testing.T) {
e1 := &testEntry{value: 1}
e2 := &testEntry{value: 2}
e3 := &testEntry{value: 3}
RingInit(e1)
RingAdd(e1, e2)
RingAdd(e2, e3)
RingRemove(e3)
RingRemove(e2)
sum := 0
for e := e1.Next(); e != e1; e = e.Next() {
sum += e.(*testEntry).value
}
if sum != 0 {
t.Errorf("wrong sum: want 0, got %d", sum)
}
}
func BenchmarkIterateForward(b *testing.B) {
var l List
for i := 0; i < 1000000; i++ {
+84
View File
@@ -0,0 +1,84 @@
// Copyright 2023 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package ring is an implementation of an intrusive circular linked list.
package ring
// Container is the type that holds the list entries.
type Container any
// Entry is an element in the circular linked list.
//
// +stateify savable
type Entry struct {
next *Entry
prev *Entry
container Container
}
// Init instantiates an Element to be an item in a ring (circularly-linked
// list).
//
//go:nosplit
func (e *Entry) Init(container Container) {
e.next = e
e.prev = e
e.container = container
}
// Add adds new to old's ring.
//
//go:nosplit
func (e *Entry) Add(new *Entry) {
next := e.next
prev := e
next.prev = new
new.next = next
new.prev = prev
e.next = new
}
// Remove removes e from its ring and reinitializes it.
//
//go:nosplit
func (e *Entry) Remove() {
next := e.next
prev := e.prev
next.prev = prev
prev.next = next
e.Init(e.container)
}
// Empty returns true if there are no other elements in the ring.
//
//go:nosplit
func (e *Entry) Empty() bool {
return e.next == e
}
// Next returns the next containing object pointed to by the list.
//
//go:nosplit
func (e *Entry) Next() Container {
return e.next.container
}
// Prev returns the previous containing object pointed to by the list.
//
//go:nosplit
func (e *Entry) Prev() Container {
return e.prev.container
}
+97
View File
@@ -0,0 +1,97 @@
// Copyright 2023 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ring
import (
"testing"
)
type testContainer struct {
value int
entry testEntry
}
func newContainer(value int) *testContainer {
c := &testContainer{value: value}
c.entry.Init(c)
return c
}
func TestAdd(t *testing.T) {
e1 := newContainer(1)
e2 := newContainer(2)
e3 := newContainer(3)
e1.entry.Add(&e2.entry)
e1.entry.Add(&e3.entry)
sum := 0
want := 6
e := e1
for {
sum += e.value
e = e.entry.Next()
if e == e1 {
break
}
}
if sum != want {
t.Errorf("wrong sum: want %d, got %d", want, sum)
}
}
func TestRemove(t *testing.T) {
e1 := newContainer(1)
e2 := newContainer(2)
e3 := newContainer(3)
e1.entry.Add(&e2.entry)
e2.entry.Add(&e3.entry)
e2.entry.Remove()
sum := 0
want := 4
e := e1
for {
sum += e.value
e = e.entry.Next()
if e == e1 {
break
}
}
if sum != want {
t.Errorf("wrong sum: want %d, got %d", want, sum)
}
}
func TestEmpty(t *testing.T) {
head := newContainer(1)
e2 := newContainer(2)
e3 := newContainer(3)
head.entry.Add(&e2.entry)
e2.entry.Add(&e3.entry)
e3.entry.Remove()
e2.entry.Remove()
sum := 0
want := 0
for e := head.entry.Next(); e != head; e = e.entry.Next() {
sum += e.value
}
if sum != want {
t.Errorf("wrong sum: want %d, got %d", want, sum)
}
}
+6 -8
View File
@@ -54,15 +54,13 @@ go_template_instance(
)
go_template_instance(
name = "shared_list",
out = "shared_list.go",
name = "mount_ring",
out = "mount_ring.go",
package = "vfs",
prefix = "shared",
template = "//pkg/ilist:generic_list",
prefix = "mount",
template = "//pkg/ilist:generic_ring",
types = {
"Element": "*Mount",
"Linker": "*sharedEntry",
"ElementMapper": "sharedMapper",
"Container": "*Mount",
},
)
@@ -144,6 +142,7 @@ go_library(
"lock.go",
"mount.go",
"mount_namespace_refs.go",
"mount_ring.go",
"mount_unsafe.go",
"namespace.go",
"opath.go",
@@ -153,7 +152,6 @@ go_library(
"propagation.go",
"resolving_path.go",
"save_restore.go",
"shared_list.go",
"vfs.go",
"virtual_filesystem_mutex.go",
],
+2 -6
View File
@@ -92,7 +92,7 @@ type Mount struct {
// sharedEntry represents an entry in a circular list (ring) of mounts in a
// shared peer group.
sharedEntry
sharedEntry mountEntry
// groupID is the ID for this mount's shared peer group. If the mount is not
// in a peer group, this is 0.
@@ -110,10 +110,6 @@ type Mount struct {
writers atomicbitops.Int64
}
type sharedMapper struct{}
func (sharedMapper) linkerFor(mnt *Mount) *sharedEntry { return &mnt.sharedEntry }
func newMount(vfs *VirtualFilesystem, fs *Filesystem, root *Dentry, mntns *MountNamespace, opts *MountOptions) *Mount {
mnt := &Mount{
ID: vfs.lastMountID.Add(1),
@@ -128,7 +124,7 @@ func newMount(vfs *VirtualFilesystem, fs *Filesystem, root *Dentry, mntns *Mount
if opts.ReadOnly {
mnt.setReadOnlyLocked(true)
}
sharedRingInit(mnt)
mnt.sharedEntry.Init(mnt)
refs.Register(mnt)
return mnt
}
+4 -4
View File
@@ -62,15 +62,15 @@ func (vfs *VirtualFilesystem) setPropagation(mnt *Mount, pflag uint32) error {
return err
}
mnt.groupID = id
sharedRingInit(mnt)
mnt.sharedEntry.Init(mnt)
mnt.isShared = true
}
case linux.MS_PRIVATE:
if mnt.isShared {
if sharedRingEmpty(mnt) {
if mnt.sharedEntry.Empty() {
vfs.freeGroupID(mnt.groupID)
}
sharedRingRemove(mnt)
mnt.sharedEntry.Remove()
mnt.groupID = 0
mnt.isShared = false
}
@@ -85,7 +85,7 @@ func (vfs *VirtualFilesystem) setPropagation(mnt *Mount, pflag uint32) error {
//
// +checklocks:vfs.mountMu
func (vfs *VirtualFilesystem) addPeer(mnt *Mount, new *Mount) {
sharedRingAdd(mnt, new)
mnt.sharedEntry.Add(&new.sharedEntry)
new.isShared = true
new.groupID = mnt.groupID
}