Skip to content

Commit

Permalink
cgofuse: refactor file table slice implementation
Browse files Browse the repository at this point in the history
The old version would grow the slice capacity when needed,
but only modify the slice's length when extending the capacity.
So even if the slice had capacity to store an entry, we were only
checking for empty slots up to the length.

We now reslice to length+1 if we have the capacity,
and reallocate if we don't.

The slice type was also abstracted to break the logic up a little.
  • Loading branch information
djdv committed Dec 2, 2022
1 parent c549358 commit f1e481f
Showing 1 changed file with 49 additions and 17 deletions.
66 changes: 49 additions & 17 deletions internal/filesystem/cgofuse/table.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@ import (
"github.com/winfsp/cgofuse/fuse"
)

type fileTable struct {
sync.RWMutex
files []*fileHandle
}
type (
handleSlice []*fileHandle
fileTable struct {
sync.RWMutex
files handleSlice
}
)

const (
errorHandle = math.MaxUint64
Expand All @@ -31,27 +34,56 @@ var (
errFull = errors.New("all slots filled")
)

func newFileTable() *fileTable { return &fileTable{files: make([]*fileHandle, 0)} }
func newFileTable() *fileTable { return new(fileTable) }

func (ft *fileTable) add(f fs.File) (fileDescriptor, error) {
ft.Lock()
defer ft.Unlock()
files := ft.files
func (files handleSlice) lowestAvailable() fileDescriptor {
for i, handle := range files {
if handle == nil {
files[i] = &fileHandle{goFile: f}
return fileDescriptor(i), nil
return fileDescriptor(i)
}
}
const initialSize = 8 // TODO: [review] Bigger? Smaller?
newCap := generic.Max(cap(files)*2, initialSize)
if newCap > handleMax {
return errorHandle, errFull
return errorHandle
}

func (files handleSlice) extend() (handleSlice, error) {
var (
filesLen = len(files)
filesEnd = filesLen + 1
filesCap = cap(files)
)
if filesLen < filesCap {
return files[:filesEnd], nil
}
const (
initialSize = 8 // NOTE: Initial size is chosen arbitrarily.
factor = 2 // If a better average is known, replace this.
)
var (
newTable = make([]*fileHandle, len(files)+1, newCap)
index = (copy(newTable, files))
scaledCap = filesCap * factor
newCap = generic.Max(scaledCap, initialSize)
)
if newCap > handleMax {
return nil, errFull
}
newTable := make([]*fileHandle, filesEnd, newCap)
copy(newTable, files)
return newTable, nil
}

func (ft *fileTable) add(f fs.File) (fileDescriptor, error) {
ft.Lock()
defer ft.Unlock()
files := ft.files
if index := files.lowestAvailable(); index != errorHandle {
files[index] = &fileHandle{goFile: f}
return index, nil
}
newTable, err := files.extend()
if err != nil {
return errorHandle, err
}

index := len(newTable) - 1
newTable[index] = &fileHandle{goFile: f}
ft.files = newTable
return fileDescriptor(index), nil
Expand Down

0 comments on commit f1e481f

Please sign in to comment.