Gave mkdir and symlink the same concurrency issues treatment.
commit
6777b1d99f
|
@ -301,6 +301,14 @@ func (fs *memFS) MkDir(
|
|||
parent := fs.getInodeForModifyingOrDie(op.Parent)
|
||||
defer parent.mu.Unlock()
|
||||
|
||||
// Ensure that the name doesn't already exist, so we don't wind up with a
|
||||
// duplicate.
|
||||
_, exists := parent.LookUpChild(op.Name)
|
||||
if exists {
|
||||
err = fuse.EEXIST
|
||||
return
|
||||
}
|
||||
|
||||
// Set up attributes from the child, using the credentials of the calling
|
||||
// process as owner (matching inode_init_owner, cf. http://goo.gl/5qavg8).
|
||||
childAttrs := fuseops.InodeAttributes{
|
||||
|
@ -341,7 +349,7 @@ func (fs *memFS) CreateFile(
|
|||
parent := fs.getInodeForModifyingOrDie(op.Parent)
|
||||
defer parent.mu.Unlock()
|
||||
|
||||
// Ensure that the name doesn't alread exist, so we don't wind up with a
|
||||
// Ensure that the name doesn't already exist, so we don't wind up with a
|
||||
// duplicate.
|
||||
_, exists := parent.LookUpChild(op.Name)
|
||||
if exists {
|
||||
|
@ -396,6 +404,14 @@ func (fs *memFS) CreateSymlink(
|
|||
parent := fs.getInodeForModifyingOrDie(op.Parent)
|
||||
defer parent.mu.Unlock()
|
||||
|
||||
// Ensure that the name doesn't already exist, so we don't wind up with a
|
||||
// duplicate.
|
||||
_, exists := parent.LookUpChild(op.Name)
|
||||
if exists {
|
||||
err = fuse.EEXIST
|
||||
return
|
||||
}
|
||||
|
||||
// Set up attributes from the child, using the credentials of the calling
|
||||
// process as owner (matching inode_init_owner, cf. http://goo.gl/5qavg8).
|
||||
now := fs.clock.Now()
|
||||
|
|
|
@ -1263,3 +1263,11 @@ func (t *MemFSTest) CreateInParallel_Truncate() {
|
|||
func (t *MemFSTest) CreateInParallel_Exclusive() {
|
||||
runCreateInParallelTest_Exclusive(t.Ctx, t.Dir)
|
||||
}
|
||||
|
||||
func (t *MemFSTest) MkdirInParallel() {
|
||||
runMkdirInParallelTest(t.Ctx, t.Dir)
|
||||
}
|
||||
|
||||
func (t *MemFSTest) SymlinkInParallel() {
|
||||
runSymlinkInParallelTest(t.Ctx, t.Dir)
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ import (
|
|||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/jacobsa/fuse/fusetesting"
|
||||
"github.com/jacobsa/gcloud/syncutil"
|
||||
. "github.com/jacobsa/oglematchers"
|
||||
. "github.com/jacobsa/ogletest"
|
||||
|
@ -268,6 +269,114 @@ func runCreateInParallelTest_Exclusive(
|
|||
}
|
||||
}
|
||||
|
||||
func runMkdirInParallelTest(
|
||||
ctx context.Context,
|
||||
dir string) {
|
||||
// Ensure that we get parallelism for this test.
|
||||
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(runtime.NumCPU()))
|
||||
|
||||
// Try for awhile to see if anything breaks.
|
||||
const duration = 500 * time.Millisecond
|
||||
startTime := time.Now()
|
||||
for time.Since(startTime) < duration {
|
||||
filename := path.Join(dir, "foo")
|
||||
|
||||
// Set up a function that creates the directory, ignoring EEXIST errors.
|
||||
worker := func(id byte) (err error) {
|
||||
err = os.Mkdir(filename, 0700)
|
||||
|
||||
if os.IsExist(err) {
|
||||
err = nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Worker %d: Mkdir: %v", id, err)
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Run several workers in parallel.
|
||||
const numWorkers = 16
|
||||
b := syncutil.NewBundle(ctx)
|
||||
for i := 0; i < numWorkers; i++ {
|
||||
id := byte(i)
|
||||
b.Add(func(ctx context.Context) (err error) {
|
||||
err = worker(id)
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
err := b.Join()
|
||||
AssertEq(nil, err)
|
||||
|
||||
// The directory should have been created, once.
|
||||
entries, err := fusetesting.ReadDirPicky(dir)
|
||||
AssertEq(nil, err)
|
||||
AssertEq(1, len(entries))
|
||||
AssertEq("foo", entries[0].Name())
|
||||
|
||||
// Delete the directory.
|
||||
err = os.Remove(filename)
|
||||
AssertEq(nil, err)
|
||||
}
|
||||
}
|
||||
|
||||
func runSymlinkInParallelTest(
|
||||
ctx context.Context,
|
||||
dir string) {
|
||||
// Ensure that we get parallelism for this test.
|
||||
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(runtime.NumCPU()))
|
||||
|
||||
// Try for awhile to see if anything breaks.
|
||||
const duration = 500 * time.Millisecond
|
||||
startTime := time.Now()
|
||||
for time.Since(startTime) < duration {
|
||||
filename := path.Join(dir, "foo")
|
||||
|
||||
// Set up a function that creates the symlink, ignoring EEXIST errors.
|
||||
worker := func(id byte) (err error) {
|
||||
err = os.Symlink("blah", filename)
|
||||
|
||||
if os.IsExist(err) {
|
||||
err = nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Worker %d: Symlink: %v", id, err)
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Run several workers in parallel.
|
||||
const numWorkers = 16
|
||||
b := syncutil.NewBundle(ctx)
|
||||
for i := 0; i < numWorkers; i++ {
|
||||
id := byte(i)
|
||||
b.Add(func(ctx context.Context) (err error) {
|
||||
err = worker(id)
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
err := b.Join()
|
||||
AssertEq(nil, err)
|
||||
|
||||
// The symlink should have been created, once.
|
||||
entries, err := fusetesting.ReadDirPicky(dir)
|
||||
AssertEq(nil, err)
|
||||
AssertEq(1, len(entries))
|
||||
AssertEq("foo", entries[0].Name())
|
||||
|
||||
// Delete the directory.
|
||||
err = os.Remove(filename)
|
||||
AssertEq(nil, err)
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Boilerplate
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
@ -661,3 +770,11 @@ func (t *PosixTest) CreateInParallel_Truncate() {
|
|||
func (t *PosixTest) CreateInParallel_Exclusive() {
|
||||
runCreateInParallelTest_Exclusive(t.ctx, t.dir)
|
||||
}
|
||||
|
||||
func (t *PosixTest) MkdirInParallel() {
|
||||
runMkdirInParallelTest(t.ctx, t.dir)
|
||||
}
|
||||
|
||||
func (t *PosixTest) SymlinkInParallel() {
|
||||
runSymlinkInParallelTest(t.ctx, t.dir)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue