diff --git a/samples/memfs/inode.go b/samples/memfs/inode.go index 79582bc..587fd3e 100644 --- a/samples/memfs/inode.go +++ b/samples/memfs/inode.go @@ -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, diff --git a/samples/memfs/memfs.go b/samples/memfs/memfs.go index 0ee6d7f..ef511d5 100644 --- a/samples/memfs/memfs.go +++ b/samples/memfs/memfs.go @@ -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