Sorted out mmap-related issues with FileSystem.Flush.
commit
359b82fa8f
|
@ -242,6 +242,12 @@ type FileSystem interface {
|
||||||
// case of close(2), a flush error is returned to the user. For dup2(2), it
|
// case of close(2), a flush error is returned to the user. For dup2(2), it
|
||||||
// is not.
|
// is not.
|
||||||
//
|
//
|
||||||
|
// Note that one potentially significant case where this is *not* called is
|
||||||
|
// munmap(2). (Cf. http://goo.gl/7n1r9X, fuse-devel mailing list thread from
|
||||||
|
// Han-Wen Nienhuys on 2014-10-08.) Even if users close(2) after writing to
|
||||||
|
// an mmap'd file, on OS X the contents are not immediately flushed (cf.
|
||||||
|
// https://github.com/osxfuse/osxfuse/issues/202).
|
||||||
|
//
|
||||||
// Because of cases like dup2(2), calls to FlushFile are not necessarily one
|
// Because of cases like dup2(2), calls to FlushFile are not necessarily one
|
||||||
// to one with calls to OpenFile. They should not be used for reference
|
// to one with calls to OpenFile. They should not be used for reference
|
||||||
// counting, and the handle must remain valid even after the method is called
|
// counting, and the handle must remain valid even after the method is called
|
||||||
|
|
|
@ -569,8 +569,118 @@ func (t *FlushFSTest) Dup2_FlushError() {
|
||||||
ExpectEq(nil, err)
|
ExpectEq(nil, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *FlushFSTest) Mmap() {
|
func (t *FlushFSTest) Mmap_MunmapBeforeClose() {
|
||||||
AssertTrue(false, "TODO")
|
var n int
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// If we run this test with GOMAXPROCS=1 (the default), the program will
|
||||||
|
// deadlock for the reason described here:
|
||||||
|
//
|
||||||
|
// https://groups.google.com/d/msg/golang-nuts/11rdExWP6ac/TzwT6HBOb3wJ
|
||||||
|
//
|
||||||
|
// In summary, the goroutine reading from the mmap'd file is camping on a
|
||||||
|
// scheduler slot while it blocks on a page fault, and the goroutine handling
|
||||||
|
// fuse requests is waiting for the scheduler slot.
|
||||||
|
//
|
||||||
|
// So run with GOMAXPROCS=2.
|
||||||
|
old := runtime.GOMAXPROCS(2)
|
||||||
|
defer runtime.GOMAXPROCS(old)
|
||||||
|
|
||||||
|
// Open the file.
|
||||||
|
t.f1, err = os.OpenFile(path.Join(t.Dir, "foo"), os.O_RDWR, 0)
|
||||||
|
AssertEq(nil, err)
|
||||||
|
|
||||||
|
// Write some contents to the file.
|
||||||
|
n, err = t.f1.Write([]byte("taco"))
|
||||||
|
AssertEq(nil, err)
|
||||||
|
AssertEq(4, n)
|
||||||
|
|
||||||
|
// mmap the file.
|
||||||
|
data, err := syscall.Mmap(
|
||||||
|
int(t.f1.Fd()), 0, 4,
|
||||||
|
syscall.PROT_READ|syscall.PROT_WRITE,
|
||||||
|
syscall.MAP_SHARED)
|
||||||
|
|
||||||
|
AssertEq(nil, err)
|
||||||
|
AssertEq("taco", string(data))
|
||||||
|
|
||||||
|
// Modify then unmap.
|
||||||
|
data[0] = 'p'
|
||||||
|
|
||||||
|
err = syscall.Munmap(data)
|
||||||
|
AssertEq(nil, err)
|
||||||
|
|
||||||
|
// munmap does not cause a flush.
|
||||||
|
ExpectThat(t.getFlushes(), ElementsAre())
|
||||||
|
ExpectThat(t.getFsyncs(), ElementsAre())
|
||||||
|
|
||||||
|
// Close the file. We should see a flush. On Darwin, this will contain out of
|
||||||
|
// date contents (cf. https://github.com/osxfuse/osxfuse/issues/202).
|
||||||
|
err = t.f1.Close()
|
||||||
|
t.f1 = nil
|
||||||
|
AssertEq(nil, err)
|
||||||
|
|
||||||
|
if runtime.GOOS == "darwin" {
|
||||||
|
ExpectThat(t.getFlushes(), ElementsAre("taco"))
|
||||||
|
ExpectThat(t.getFsyncs(), ElementsAre())
|
||||||
|
} else {
|
||||||
|
ExpectThat(t.getFlushes(), ElementsAre("paco"))
|
||||||
|
ExpectThat(t.getFsyncs(), ElementsAre())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *FlushFSTest) Mmap_CloseBeforeMunmap() {
|
||||||
|
var n int
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// If we run this test with GOMAXPROCS=1 (the default), the program will
|
||||||
|
// deadlock for the reason described here:
|
||||||
|
//
|
||||||
|
// https://groups.google.com/d/msg/golang-nuts/11rdExWP6ac/TzwT6HBOb3wJ
|
||||||
|
//
|
||||||
|
// In summary, the goroutine reading from the mmap'd file is camping on a
|
||||||
|
// scheduler slot while it blocks on a page fault, and the goroutine handling
|
||||||
|
// fuse requests is waiting for the scheduler slot.
|
||||||
|
//
|
||||||
|
// So run with GOMAXPROCS=2.
|
||||||
|
old := runtime.GOMAXPROCS(2)
|
||||||
|
defer runtime.GOMAXPROCS(old)
|
||||||
|
|
||||||
|
// Open the file.
|
||||||
|
t.f1, err = os.OpenFile(path.Join(t.Dir, "foo"), os.O_RDWR, 0)
|
||||||
|
AssertEq(nil, err)
|
||||||
|
|
||||||
|
// Write some contents to the file.
|
||||||
|
n, err = t.f1.Write([]byte("taco"))
|
||||||
|
AssertEq(nil, err)
|
||||||
|
AssertEq(4, n)
|
||||||
|
|
||||||
|
// mmap the file.
|
||||||
|
data, err := syscall.Mmap(
|
||||||
|
int(t.f1.Fd()), 0, 4,
|
||||||
|
syscall.PROT_READ|syscall.PROT_WRITE,
|
||||||
|
syscall.MAP_SHARED)
|
||||||
|
|
||||||
|
AssertEq(nil, err)
|
||||||
|
AssertEq("taco", string(data))
|
||||||
|
|
||||||
|
// Close the file. We should see a flush.
|
||||||
|
err = t.f1.Close()
|
||||||
|
t.f1 = nil
|
||||||
|
AssertEq(nil, err)
|
||||||
|
|
||||||
|
AssertThat(t.getFlushes(), ElementsAre("taco"))
|
||||||
|
AssertThat(t.getFsyncs(), ElementsAre())
|
||||||
|
|
||||||
|
// Modify then unmap.
|
||||||
|
data[0] = 'p'
|
||||||
|
|
||||||
|
err = syscall.Munmap(data)
|
||||||
|
AssertEq(nil, err)
|
||||||
|
|
||||||
|
// munmap does not cause a flush.
|
||||||
|
ExpectThat(t.getFlushes(), ElementsAre("taco"))
|
||||||
|
ExpectThat(t.getFsyncs(), ElementsAre())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *FlushFSTest) Directory() {
|
func (t *FlushFSTest) Directory() {
|
||||||
|
|
Loading…
Reference in New Issue