Simplified memfs locking.

geesefs-0-30-9
Aaron Jacobs 2015-06-25 21:37:46 +10:00
commit 10b71ca55f
2 changed files with 15 additions and 51 deletions

View File

@ -22,11 +22,12 @@ import (
"github.com/jacobsa/fuse/fuseops"
"github.com/jacobsa/fuse/fuseutil"
"github.com/jacobsa/syncutil"
"github.com/jacobsa/timeutil"
)
// Common attributes for files and directories.
//
// External synchronization is required.
type inode struct {
/////////////////////////
// Dependencies
@ -38,8 +39,6 @@ type inode struct {
// Mutable state
/////////////////////////
mu syncutil.InvariantMutex
// The current attributes of this inode.
//
// INVARIANT: attrs.Mode &^ (os.ModePerm|os.ModeDir|os.ModeSymlink) == 0
@ -93,11 +92,10 @@ func newInode(
attrs: attrs,
}
in.mu = syncutil.NewInvariantMutex(in.checkInvariants)
return
}
func (in *inode) checkInvariants() {
func (in *inode) CheckInvariants() {
// INVARIANT: attrs.Mode &^ (os.ModePerm|os.ModeDir|os.ModeSymlink) == 0
if !(in.attrs.Mode&^(os.ModePerm|os.ModeDir|os.ModeSymlink) == 0) {
panic(fmt.Sprintf("Unexpected mode: %v", in.attrs.Mode))
@ -153,25 +151,21 @@ func (in *inode) checkInvariants() {
return
}
// LOCKS_REQUIRED(in.mu)
func (in *inode) isDir() bool {
return in.attrs.Mode&os.ModeDir != 0
}
// LOCKS_REQUIRED(in.mu)
func (in *inode) isSymlink() bool {
return in.attrs.Mode&os.ModeSymlink != 0
}
// LOCKS_REQUIRED(in.mu)
func (in *inode) isFile() bool {
return !(in.isDir() || in.isSymlink())
}
// Return the index of the child within in.entries, if it exists.
//
// REQUIRES: in.dir
// LOCKS_REQUIRED(in.mu)
// REQUIRES: in.isDir()
func (in *inode) findChild(name string) (i int, ok bool) {
if !in.isDir() {
panic("findChild called on non-directory.")
@ -195,7 +189,6 @@ func (in *inode) findChild(name string) (i int, ok bool) {
// Return the number of children of the directory.
//
// REQUIRES: in.isDir()
// LOCKS_REQUIRED(in.mu)
func (in *inode) Len() (n int) {
for _, e := range in.entries {
if e.Type != fuseutil.DT_Unknown {
@ -209,7 +202,6 @@ func (in *inode) Len() (n int) {
// Find an entry for the given child name and return its inode ID.
//
// REQUIRES: in.isDir()
// LOCKS_REQUIRED(in.mu)
func (in *inode) LookUpChild(name string) (id fuseops.InodeID, ok bool) {
index, ok := in.findChild(name)
if ok {
@ -223,7 +215,6 @@ func (in *inode) LookUpChild(name string) (id fuseops.InodeID, ok bool) {
//
// REQUIRES: in.isDir()
// REQUIRES: dt != fuseutil.DT_Unknown
// LOCKS_REQUIRED(in.mu)
func (in *inode) AddChild(
id fuseops.InodeID,
name string,
@ -263,7 +254,6 @@ func (in *inode) AddChild(
//
// REQUIRES: in.isDir()
// REQUIRES: An entry for the given name exists.
// LOCKS_REQUIRED(in.mu)
func (in *inode) RemoveChild(name string) {
// Update the modification time.
in.attrs.Mtime = in.clock.Now()
@ -284,7 +274,6 @@ func (in *inode) RemoveChild(name string) {
// Serve a ReadDir request.
//
// REQUIRES: in.isDir()
// LOCKS_REQUIRED(in.mu)
func (in *inode) ReadDir(offset int, size int) (data []byte, err error) {
if !in.isDir() {
panic("ReadDir called on non-directory.")
@ -313,7 +302,6 @@ func (in *inode) ReadDir(offset int, size int) (data []byte, err error) {
// Read from the file's contents. See documentation for ioutil.ReaderAt.
//
// REQUIRES: in.isFile()
// LOCKS_REQUIRED(in.mu)
func (in *inode) ReadAt(p []byte, off int64) (n int, err error) {
if !in.isFile() {
panic("ReadAt called on non-file.")
@ -337,7 +325,6 @@ func (in *inode) ReadAt(p []byte, off int64) (n int, err error) {
// Write to the file's contents. See documentation for ioutil.WriterAt.
//
// REQUIRES: in.isFile()
// LOCKS_REQUIRED(in.mu)
func (in *inode) WriteAt(p []byte, off int64) (n int, err error) {
if !in.isFile() {
panic("WriteAt called on non-file.")
@ -366,8 +353,6 @@ func (in *inode) WriteAt(p []byte, off int64) (n int, err error) {
}
// Update attributes from non-nil parameters.
//
// LOCKS_REQUIRED(in.mu)
func (in *inode) SetAttributes(
size *uint64,
mode *os.FileMode,

View File

@ -40,13 +40,13 @@ type memFS struct {
// Mutable state
/////////////////////////
// When acquiring this lock, the caller must hold no inode locks.
mu syncutil.InvariantMutex
// The collection of live inodes, indexed by ID. IDs of free inodes that may
// be re-used have nil entries. No ID less than fuseops.RootInodeID is ever
// used.
//
// INVARIANT: For each inode in, in.CheckInvariants() does not panic.
// INVARIANT: len(inodes) > fuseops.RootInodeID
// INVARIANT: For all i < fuseops.RootInodeID, inodes[i] == nil
// INVARIANT: inodes[fuseops.RootInodeID] != nil
@ -132,33 +132,32 @@ func (fs *memFS) checkInvariants() {
panic(fmt.Sprintf("Unexected free inode ID: %v", id))
}
}
// INVARIANT: For each inode in, in.CheckInvariants() does not panic.
for _, in := range fs.inodes {
in.CheckInvariants()
}
}
// Find the given inode and return it with its lock held. Panic if it doesn't
// exist.
// Find the given inode. Panic if it doesn't exist.
//
// LOCKS_REQUIRED(fs.mu)
// LOCK_FUNCTION(inode.mu)
func (fs *memFS) getInodeOrDie(id fuseops.InodeID) (inode *inode) {
inode = fs.inodes[id]
if inode == nil {
panic(fmt.Sprintf("Unknown inode: %v", id))
}
inode.mu.Lock()
return
}
// Allocate a new inode, assigning it an ID that is not in use. Return it with
// its lock held.
// Allocate a new inode, assigning it an ID that is not in use.
//
// EXCLUSIVE_LOCKS_REQUIRED(fs.mu)
// EXCLUSIVE_LOCK_FUNCTION(inode.mu)
// LOCKS_REQUIRED(fs.mu)
func (fs *memFS) allocateInode(
attrs fuseops.InodeAttributes) (id fuseops.InodeID, inode *inode) {
// Create and lock the inode.
// Create the inode.
inode = newInode(fs.clock, attrs)
inode.mu.Lock()
// Re-use a free ID if possible. Otherwise mint a new one.
numFree := len(fs.freeInodes)
@ -174,7 +173,7 @@ func (fs *memFS) allocateInode(
return
}
// EXCLUSIVE_LOCKS_REQUIRED(fs.mu)
// LOCKS_REQUIRED(fs.mu)
func (fs *memFS) deallocateInode(id fuseops.InodeID) {
fs.freeInodes = append(fs.freeInodes, id)
fs.inodes[id] = nil
@ -191,7 +190,6 @@ func (fs *memFS) LookUpInode(
// Grab the parent directory.
inode := fs.getInodeOrDie(op.Parent)
defer inode.mu.Unlock()
// Does the directory have an entry with the given name?
childID, ok := inode.LookUpChild(op.Name)
@ -202,7 +200,6 @@ func (fs *memFS) LookUpInode(
// Grab the child.
child := fs.getInodeOrDie(childID)
defer child.mu.Unlock()
// Fill in the response.
op.Entry.Child = childID
@ -223,7 +220,6 @@ func (fs *memFS) GetInodeAttributes(
// Grab the inode.
inode := fs.getInodeOrDie(op.Inode)
defer inode.mu.Unlock()
// Fill in the response.
op.Attributes = inode.attrs
@ -242,7 +238,6 @@ func (fs *memFS) SetInodeAttributes(
// Grab the inode.
inode := fs.getInodeOrDie(op.Inode)
defer inode.mu.Unlock()
// Handle the request.
inode.SetAttributes(op.Size, op.Mode, op.Mtime)
@ -264,7 +259,6 @@ func (fs *memFS) MkDir(
// Grab the parent, which we will update shortly.
parent := fs.getInodeOrDie(op.Parent)
defer parent.mu.Unlock()
// Ensure that the name doesn't already exist, so we don't wind up with a
// duplicate.
@ -285,7 +279,6 @@ func (fs *memFS) MkDir(
// Allocate a child.
childID, child := fs.allocateInode(childAttrs)
defer child.mu.Unlock()
// Add an entry in the parent.
parent.AddChild(childID, op.Name, fuseutil.DT_Directory)
@ -309,7 +302,6 @@ func (fs *memFS) CreateFile(
// Grab the parent, which we will update shortly.
parent := fs.getInodeOrDie(op.Parent)
defer parent.mu.Unlock()
// Ensure that the name doesn't already exist, so we don't wind up with a
// duplicate.
@ -335,7 +327,6 @@ func (fs *memFS) CreateFile(
// Allocate a child.
childID, child := fs.allocateInode(childAttrs)
defer child.mu.Unlock()
// Add an entry in the parent.
parent.AddChild(childID, op.Name, fuseutil.DT_File)
@ -361,7 +352,6 @@ func (fs *memFS) CreateSymlink(
// Grab the parent, which we will update shortly.
parent := fs.getInodeOrDie(op.Parent)
defer parent.mu.Unlock()
// Ensure that the name doesn't already exist, so we don't wind up with a
// duplicate.
@ -387,7 +377,6 @@ func (fs *memFS) CreateSymlink(
// Allocate a child.
childID, child := fs.allocateInode(childAttrs)
defer child.mu.Unlock()
// Set up its target.
child.target = op.Target
@ -414,7 +403,6 @@ func (fs *memFS) RmDir(
// Grab the parent, which we will update shortly.
parent := fs.getInodeOrDie(op.Parent)
defer parent.mu.Unlock()
// Find the child within the parent.
childID, ok := parent.LookUpChild(op.Name)
@ -425,7 +413,6 @@ func (fs *memFS) RmDir(
// Grab the child.
child := fs.getInodeOrDie(childID)
defer child.mu.Unlock()
// Make sure the child is empty.
if child.Len() != 0 {
@ -449,7 +436,6 @@ func (fs *memFS) Unlink(
// Grab the parent, which we will update shortly.
parent := fs.getInodeOrDie(op.Parent)
defer parent.mu.Unlock()
// Find the child within the parent.
childID, ok := parent.LookUpChild(op.Name)
@ -460,7 +446,6 @@ func (fs *memFS) Unlink(
// Grab the child.
child := fs.getInodeOrDie(childID)
defer child.mu.Unlock()
// Remove the entry within the parent.
parent.RemoveChild(op.Name)
@ -480,7 +465,6 @@ func (fs *memFS) OpenDir(
// inode that doesn't exist, something screwed up earlier (a lookup, a
// cache invalidation, etc.).
inode := fs.getInodeOrDie(op.Inode)
defer inode.mu.Unlock()
if !inode.isDir() {
panic("Found non-dir.")
@ -496,7 +480,6 @@ func (fs *memFS) ReadDir(
// Grab the directory.
inode := fs.getInodeOrDie(op.Inode)
defer inode.mu.Unlock()
// Serve the request.
op.Data, err = inode.ReadDir(int(op.Offset), op.Size)
@ -517,7 +500,6 @@ func (fs *memFS) OpenFile(
// inode that doesn't exist, something screwed up earlier (a lookup, a
// cache invalidation, etc.).
inode := fs.getInodeOrDie(op.Inode)
defer inode.mu.Unlock()
if !inode.isFile() {
panic("Found non-file.")
@ -533,7 +515,6 @@ func (fs *memFS) ReadFile(
// Find the inode in question.
inode := fs.getInodeOrDie(op.Inode)
defer inode.mu.Unlock()
// Serve the request.
op.Data = make([]byte, op.Size)
@ -555,7 +536,6 @@ func (fs *memFS) WriteFile(
// Find the inode in question.
inode := fs.getInodeOrDie(op.Inode)
defer inode.mu.Unlock()
// Serve the request.
_, err = inode.WriteAt(op.Data, op.Offset)
@ -570,7 +550,6 @@ func (fs *memFS) ReadSymlink(
// Find the inode in question.
inode := fs.getInodeOrDie(op.Inode)
defer inode.mu.Unlock()
// Serve the request.
op.Target = inode.target